1 // SPDX-License-Identifier: GPL-2.0+
3 * Qualcomm SDHCI driver - SD/eMMC controller
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
7 * Based on Linux driver
16 #include <asm/global_data.h>
18 #include <linux/bitops.h>
20 /* Non-standard registers needed for SDHCI startup */
21 #define SDCC_MCI_POWER 0x0
22 #define SDCC_MCI_POWER_SW_RST BIT(7)
24 /* This is undocumented register */
25 #define SDCC_MCI_VERSION 0x50
26 #define SDCC_V5_VERSION 0x318
28 #define SDCC_VERSION_MAJOR_SHIFT 28
29 #define SDCC_VERSION_MAJOR_MASK (0xf << SDCC_VERSION_MAJOR_SHIFT)
30 #define SDCC_VERSION_MINOR_MASK 0xff
32 #define SDCC_MCI_STATUS2 0x6C
33 #define SDCC_MCI_STATUS2_MCI_ACT 0x1
34 #define SDCC_MCI_HC_MODE 0x78
36 /* Non standard (?) SDHCI register */
37 #define SDHCI_VENDOR_SPEC_CAPABILITIES0 0x11c
39 struct msm_sdhc_plat
{
40 struct mmc_config cfg
;
45 struct sdhci_host host
;
49 struct msm_sdhc_variant_info
{
53 DECLARE_GLOBAL_DATA_PTR
;
55 static int msm_sdc_clk_init(struct udevice
*dev
)
57 int node
= dev_of_offset(dev
);
58 uint clk_rate
= fdtdec_get_uint(gd
->fdt_blob
, node
, "clock-frequency",
60 uint clkd
[2]; /* clk_id and clk_no */
62 struct udevice
*clk_dev
;
66 ret
= fdtdec_get_int_array(gd
->fdt_blob
, node
, "clock", clkd
, 2);
70 clk_offset
= fdt_node_offset_by_phandle(gd
->fdt_blob
, clkd
[0]);
74 ret
= uclass_get_device_by_of_offset(UCLASS_CLK
, clk_offset
, &clk_dev
);
79 ret
= clk_request(clk_dev
, &clk
);
83 ret
= clk_set_rate(&clk
, clk_rate
);
90 static int msm_sdc_mci_init(struct msm_sdhc
*prv
)
92 /* Reset the core and Enable SDHC mode */
93 writel(readl(prv
->base
+ SDCC_MCI_POWER
) | SDCC_MCI_POWER_SW_RST
,
94 prv
->base
+ SDCC_MCI_POWER
);
97 /* Wait for reset to be written to register */
98 if (wait_for_bit_le32(prv
->base
+ SDCC_MCI_STATUS2
,
99 SDCC_MCI_STATUS2_MCI_ACT
, false, 10, false)) {
100 printf("msm_sdhci: reset request failed\n");
104 /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
105 if (wait_for_bit_le32(prv
->base
+ SDCC_MCI_POWER
,
106 SDCC_MCI_POWER_SW_RST
, false, 2, false)) {
107 printf("msm_sdhci: stuck in reset\n");
111 /* Enable host-controller mode */
112 writel(1, prv
->base
+ SDCC_MCI_HC_MODE
);
117 static int msm_sdc_probe(struct udevice
*dev
)
119 struct mmc_uclass_priv
*upriv
= dev_get_uclass_priv(dev
);
120 struct msm_sdhc_plat
*plat
= dev_get_plat(dev
);
121 struct msm_sdhc
*prv
= dev_get_priv(dev
);
122 const struct msm_sdhc_variant_info
*var_info
;
123 struct sdhci_host
*host
= &prv
->host
;
124 u32 core_version
, core_minor
, core_major
;
128 host
->quirks
= SDHCI_QUIRK_WAIT_SEND_CMD
| SDHCI_QUIRK_BROKEN_R1B
;
133 ret
= msm_sdc_clk_init(dev
);
137 var_info
= (void *)dev_get_driver_data(dev
);
138 if (!var_info
->mci_removed
) {
139 ret
= msm_sdc_mci_init(prv
);
144 if (!var_info
->mci_removed
)
145 core_version
= readl(prv
->base
+ SDCC_MCI_VERSION
);
147 core_version
= readl(host
->ioaddr
+ SDCC_V5_VERSION
);
149 core_major
= (core_version
& SDCC_VERSION_MAJOR_MASK
);
150 core_major
>>= SDCC_VERSION_MAJOR_SHIFT
;
152 core_minor
= core_version
& SDCC_VERSION_MINOR_MASK
;
155 * Support for some capabilities is not advertised by newer
156 * controller versions and must be explicitly enabled.
158 if (core_major
>= 1 && core_minor
!= 0x11 && core_minor
!= 0x12) {
159 caps
= readl(host
->ioaddr
+ SDHCI_CAPABILITIES
);
160 caps
|= SDHCI_CAN_VDD_300
| SDHCI_CAN_DO_8BIT
;
161 writel(caps
, host
->ioaddr
+ SDHCI_VENDOR_SPEC_CAPABILITIES0
);
164 ret
= mmc_of_parse(dev
, &plat
->cfg
);
168 host
->mmc
= &plat
->mmc
;
169 host
->mmc
->dev
= dev
;
170 ret
= sdhci_setup_cfg(&plat
->cfg
, host
, 0, 0);
173 host
->mmc
->priv
= &prv
->host
;
174 upriv
->mmc
= host
->mmc
;
176 return sdhci_probe(dev
);
179 static int msm_sdc_remove(struct udevice
*dev
)
181 struct msm_sdhc
*priv
= dev_get_priv(dev
);
182 const struct msm_sdhc_variant_info
*var_info
;
184 var_info
= (void *)dev_get_driver_data(dev
);
186 /* Disable host-controller mode */
187 if (!var_info
->mci_removed
)
188 writel(0, priv
->base
+ SDCC_MCI_HC_MODE
);
193 static int msm_of_to_plat(struct udevice
*dev
)
195 struct udevice
*parent
= dev
->parent
;
196 struct msm_sdhc
*priv
= dev_get_priv(dev
);
197 struct sdhci_host
*host
= &priv
->host
;
198 int node
= dev_of_offset(dev
);
200 host
->name
= strdup(dev
->name
);
201 host
->ioaddr
= dev_read_addr_ptr(dev
);
202 host
->bus_width
= fdtdec_get_int(gd
->fdt_blob
, node
, "bus-width", 4);
203 host
->index
= fdtdec_get_uint(gd
->fdt_blob
, node
, "index", 0);
204 priv
->base
= (void *)fdtdec_get_addr_size_auto_parent(gd
->fdt_blob
,
205 dev_of_offset(parent
), node
, "reg", 1, NULL
, false);
206 if (priv
->base
== (void *)FDT_ADDR_T_NONE
||
207 host
->ioaddr
== (void *)FDT_ADDR_T_NONE
)
213 static int msm_sdc_bind(struct udevice
*dev
)
215 struct msm_sdhc_plat
*plat
= dev_get_plat(dev
);
217 return sdhci_bind(dev
, &plat
->mmc
, &plat
->cfg
);
220 static const struct msm_sdhc_variant_info msm_sdhc_mci_var
= {
221 .mci_removed
= false,
224 static const struct msm_sdhc_variant_info msm_sdhc_v5_var
= {
228 static const struct udevice_id msm_mmc_ids
[] = {
229 { .compatible
= "qcom,sdhci-msm-v4", .data
= (ulong
)&msm_sdhc_mci_var
},
230 { .compatible
= "qcom,sdhci-msm-v5", .data
= (ulong
)&msm_sdhc_v5_var
},
234 U_BOOT_DRIVER(msm_sdc_drv
) = {
237 .of_match
= msm_mmc_ids
,
238 .of_to_plat
= msm_of_to_plat
,
240 .bind
= msm_sdc_bind
,
241 .probe
= msm_sdc_probe
,
242 .remove
= msm_sdc_remove
,
243 .priv_auto
= sizeof(struct msm_sdhc
),
244 .plat_auto
= sizeof(struct msm_sdhc_plat
),