2 * Qualcomm SDHCI driver - SD/eMMC controller
4 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 * Based on Linux driver
8 * SPDX-License-Identifier: GPL-2.0+
17 #include <linux/bitops.h>
19 /* Non-standard registers needed for SDHCI startup */
20 #define SDCC_MCI_POWER 0x0
21 #define SDCC_MCI_POWER_SW_RST BIT(7)
23 /* This is undocumented register */
24 #define SDCC_MCI_VERSION 0x50
25 #define SDCC_MCI_VERSION_MAJOR_SHIFT 28
26 #define SDCC_MCI_VERSION_MAJOR_MASK (0xf << SDCC_MCI_VERSION_MAJOR_SHIFT)
27 #define SDCC_MCI_VERSION_MINOR_MASK 0xff
29 #define SDCC_MCI_STATUS2 0x6C
30 #define SDCC_MCI_STATUS2_MCI_ACT 0x1
31 #define SDCC_MCI_HC_MODE 0x78
33 /* Offset to SDHCI registers */
34 #define SDCC_SDHCI_OFFSET 0x900
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 DECLARE_GLOBAL_DATA_PTR
;
51 static int msm_sdc_clk_init(struct udevice
*dev
)
53 int node
= dev_of_offset(dev
);
54 uint clk_rate
= fdtdec_get_uint(gd
->fdt_blob
, node
, "clock-frequency",
56 uint clkd
[2]; /* clk_id and clk_no */
58 struct udevice
*clk_dev
;
62 ret
= fdtdec_get_int_array(gd
->fdt_blob
, node
, "clock", clkd
, 2);
66 clk_offset
= fdt_node_offset_by_phandle(gd
->fdt_blob
, clkd
[0]);
70 ret
= uclass_get_device_by_of_offset(UCLASS_CLK
, clk_offset
, &clk_dev
);
75 ret
= clk_request(clk_dev
, &clk
);
79 ret
= clk_set_rate(&clk
, clk_rate
);
87 static int msm_sdc_probe(struct udevice
*dev
)
89 struct mmc_uclass_priv
*upriv
= dev_get_uclass_priv(dev
);
90 struct msm_sdhc_plat
*plat
= dev_get_platdata(dev
);
91 struct msm_sdhc
*prv
= dev_get_priv(dev
);
92 struct sdhci_host
*host
= &prv
->host
;
93 u32 core_version
, core_minor
, core_major
;
97 host
->quirks
= SDHCI_QUIRK_WAIT_SEND_CMD
| SDHCI_QUIRK_BROKEN_R1B
;
102 ret
= msm_sdc_clk_init(dev
);
106 /* Reset the core and Enable SDHC mode */
107 writel(readl(prv
->base
+ SDCC_MCI_POWER
) | SDCC_MCI_POWER_SW_RST
,
108 prv
->base
+ SDCC_MCI_POWER
);
111 /* Wait for reset to be written to register */
112 if (wait_for_bit_le32(prv
->base
+ SDCC_MCI_STATUS2
,
113 SDCC_MCI_STATUS2_MCI_ACT
, false, 10, false)) {
114 printf("msm_sdhci: reset request failed\n");
118 /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
119 if (wait_for_bit_le32(prv
->base
+ SDCC_MCI_POWER
,
120 SDCC_MCI_POWER_SW_RST
, false, 2, false)) {
121 printf("msm_sdhci: stuck in reset\n");
125 /* Enable host-controller mode */
126 writel(1, prv
->base
+ SDCC_MCI_HC_MODE
);
128 core_version
= readl(prv
->base
+ SDCC_MCI_VERSION
);
130 core_major
= (core_version
& SDCC_MCI_VERSION_MAJOR_MASK
);
131 core_major
>>= SDCC_MCI_VERSION_MAJOR_SHIFT
;
133 core_minor
= core_version
& SDCC_MCI_VERSION_MINOR_MASK
;
136 * Support for some capabilities is not advertised by newer
137 * controller versions and must be explicitly enabled.
139 if (core_major
>= 1 && core_minor
!= 0x11 && core_minor
!= 0x12) {
140 caps
= readl(host
->ioaddr
+ SDHCI_CAPABILITIES
);
141 caps
|= SDHCI_CAN_VDD_300
| SDHCI_CAN_DO_8BIT
;
142 writel(caps
, host
->ioaddr
+ SDHCI_VENDOR_SPEC_CAPABILITIES0
);
145 ret
= sdhci_setup_cfg(&plat
->cfg
, host
, 0, 0);
146 host
->mmc
= &plat
->mmc
;
149 host
->mmc
->priv
= &prv
->host
;
150 host
->mmc
->dev
= dev
;
151 upriv
->mmc
= host
->mmc
;
153 return sdhci_probe(dev
);
156 static int msm_sdc_remove(struct udevice
*dev
)
158 struct msm_sdhc
*priv
= dev_get_priv(dev
);
160 /* Disable host-controller mode */
161 writel(0, priv
->base
+ SDCC_MCI_HC_MODE
);
166 static int msm_ofdata_to_platdata(struct udevice
*dev
)
168 struct udevice
*parent
= dev
->parent
;
169 struct msm_sdhc
*priv
= dev_get_priv(dev
);
170 struct sdhci_host
*host
= &priv
->host
;
171 int node
= dev_of_offset(dev
);
173 host
->name
= strdup(dev
->name
);
174 host
->ioaddr
= (void *)devfdt_get_addr(dev
);
175 host
->bus_width
= fdtdec_get_int(gd
->fdt_blob
, node
, "bus-width", 4);
176 host
->index
= fdtdec_get_uint(gd
->fdt_blob
, node
, "index", 0);
177 priv
->base
= (void *)fdtdec_get_addr_size_auto_parent(gd
->fdt_blob
,
178 dev_of_offset(parent
), node
, "reg", 1, NULL
, false);
179 if (priv
->base
== (void *)FDT_ADDR_T_NONE
||
180 host
->ioaddr
== (void *)FDT_ADDR_T_NONE
)
186 static int msm_sdc_bind(struct udevice
*dev
)
188 struct msm_sdhc_plat
*plat
= dev_get_platdata(dev
);
190 return sdhci_bind(dev
, &plat
->mmc
, &plat
->cfg
);
193 static const struct udevice_id msm_mmc_ids
[] = {
194 { .compatible
= "qcom,sdhci-msm-v4" },
198 U_BOOT_DRIVER(msm_sdc_drv
) = {
201 .of_match
= msm_mmc_ids
,
202 .ofdata_to_platdata
= msm_ofdata_to_platdata
,
204 .bind
= msm_sdc_bind
,
205 .probe
= msm_sdc_probe
,
206 .remove
= msm_sdc_remove
,
207 .priv_auto_alloc_size
= sizeof(struct msm_sdhc
),
208 .platdata_auto_alloc_size
= sizeof(struct msm_sdhc_plat
),