]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
9d11d12a MK |
2 | /* |
3 | * Qualcomm SDHCI driver - SD/eMMC controller | |
4 | * | |
5 | * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> | |
6 | * | |
7 | * Based on Linux driver | |
9d11d12a MK |
8 | */ |
9 | ||
d678a59d | 10 | #include <common.h> |
9d11d12a MK |
11 | #include <clk.h> |
12 | #include <dm.h> | |
336d4615 | 13 | #include <malloc.h> |
9d11d12a MK |
14 | #include <sdhci.h> |
15 | #include <wait_bit.h> | |
401d1c4f | 16 | #include <asm/global_data.h> |
9d11d12a MK |
17 | #include <asm/io.h> |
18 | #include <linux/bitops.h> | |
19 | ||
20 | /* Non-standard registers needed for SDHCI startup */ | |
21 | #define SDCC_MCI_POWER 0x0 | |
22 | #define SDCC_MCI_POWER_SW_RST BIT(7) | |
23 | ||
24 | /* This is undocumented register */ | |
364c22a8 SG |
25 | #define SDCC_MCI_VERSION 0x50 |
26 | #define SDCC_V5_VERSION 0x318 | |
27 | ||
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 | |
9d11d12a MK |
31 | |
32 | #define SDCC_MCI_STATUS2 0x6C | |
33 | #define SDCC_MCI_STATUS2_MCI_ACT 0x1 | |
34 | #define SDCC_MCI_HC_MODE 0x78 | |
35 | ||
12293f6d SG |
36 | struct msm_sdhc_plat { |
37 | struct mmc_config cfg; | |
38 | struct mmc mmc; | |
39 | }; | |
40 | ||
9d11d12a MK |
41 | struct msm_sdhc { |
42 | struct sdhci_host host; | |
43 | void *base; | |
5f3c4cca | 44 | struct clk_bulk clks; |
9d11d12a MK |
45 | }; |
46 | ||
364c22a8 SG |
47 | struct msm_sdhc_variant_info { |
48 | bool mci_removed; | |
a535d717 CC |
49 | |
50 | u32 core_vendor_spec_capabilities0; | |
364c22a8 SG |
51 | }; |
52 | ||
9d11d12a MK |
53 | DECLARE_GLOBAL_DATA_PTR; |
54 | ||
55 | static int msm_sdc_clk_init(struct udevice *dev) | |
56 | { | |
5f3c4cca CC |
57 | struct msm_sdhc *prv = dev_get_priv(dev); |
58 | ofnode node = dev_ofnode(dev); | |
59 | ulong clk_rate; | |
60 | int ret, i = 0, n_clks; | |
61 | const char *clk_name; | |
9d11d12a | 62 | |
5f3c4cca | 63 | ret = ofnode_read_u32(node, "clock-frequency", (uint *)(&clk_rate)); |
9d11d12a | 64 | if (ret) |
86c60933 | 65 | clk_rate = 201500000; |
9d11d12a | 66 | |
5f3c4cca CC |
67 | ret = clk_get_bulk(dev, &prv->clks); |
68 | if (ret) { | |
69 | log_warning("Couldn't get mmc clocks: %d\n", ret); | |
9d11d12a | 70 | return ret; |
5f3c4cca | 71 | } |
9d11d12a | 72 | |
5f3c4cca CC |
73 | ret = clk_enable_bulk(&prv->clks); |
74 | if (ret) { | |
75 | log_warning("Couldn't enable mmc clocks: %d\n", ret); | |
135aa950 | 76 | return ret; |
5f3c4cca | 77 | } |
135aa950 | 78 | |
5f3c4cca CC |
79 | /* If clock-names is unspecified, then the first clock is the core clock */ |
80 | if (!ofnode_get_property(node, "clock-names", &n_clks)) { | |
81 | if (!clk_set_rate(&prv->clks.clks[0], clk_rate)) { | |
82 | log_warning("Couldn't set core clock rate: %d\n", ret); | |
83 | return -EINVAL; | |
84 | } | |
85 | } | |
86 | ||
87 | /* Find the index of the "core" clock */ | |
88 | while (i < n_clks) { | |
89 | ofnode_read_string_index(node, "clock-names", i, &clk_name); | |
90 | if (!strcmp(clk_name, "core")) | |
91 | break; | |
92 | i++; | |
93 | } | |
94 | ||
95 | if (i >= prv->clks.count) { | |
96 | log_warning("Couldn't find core clock (index %d but only have %d clocks)\n", i, | |
97 | prv->clks.count); | |
98 | return -EINVAL; | |
99 | } | |
100 | ||
101 | /* The clock is already enabled by the clk_bulk above */ | |
102 | clk_rate = clk_set_rate(&prv->clks.clks[i], clk_rate); | |
103 | /* If we get a rate of 0 then something has probably gone wrong. */ | |
104 | if (clk_rate == 0 || IS_ERR((void *)clk_rate)) { | |
105 | log_warning("Couldn't set MMC core clock rate: %dE\n", clk_rate ? (int)PTR_ERR((void *)clk_rate) : 0); | |
106 | return -EINVAL; | |
107 | } | |
9d11d12a MK |
108 | |
109 | return 0; | |
110 | } | |
111 | ||
364c22a8 | 112 | static int msm_sdc_mci_init(struct msm_sdhc *prv) |
9d11d12a | 113 | { |
9d11d12a MK |
114 | /* Reset the core and Enable SDHC mode */ |
115 | writel(readl(prv->base + SDCC_MCI_POWER) | SDCC_MCI_POWER_SW_RST, | |
116 | prv->base + SDCC_MCI_POWER); | |
117 | ||
118 | ||
119 | /* Wait for reset to be written to register */ | |
48263504 ÁFR |
120 | if (wait_for_bit_le32(prv->base + SDCC_MCI_STATUS2, |
121 | SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { | |
9d11d12a MK |
122 | printf("msm_sdhci: reset request failed\n"); |
123 | return -EIO; | |
124 | } | |
125 | ||
126 | /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ | |
48263504 ÁFR |
127 | if (wait_for_bit_le32(prv->base + SDCC_MCI_POWER, |
128 | SDCC_MCI_POWER_SW_RST, false, 2, false)) { | |
9d11d12a MK |
129 | printf("msm_sdhci: stuck in reset\n"); |
130 | return -ETIMEDOUT; | |
131 | } | |
132 | ||
133 | /* Enable host-controller mode */ | |
134 | writel(1, prv->base + SDCC_MCI_HC_MODE); | |
135 | ||
364c22a8 SG |
136 | return 0; |
137 | } | |
9d11d12a | 138 | |
364c22a8 SG |
139 | static int msm_sdc_probe(struct udevice *dev) |
140 | { | |
141 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); | |
142 | struct msm_sdhc_plat *plat = dev_get_plat(dev); | |
143 | struct msm_sdhc *prv = dev_get_priv(dev); | |
144 | const struct msm_sdhc_variant_info *var_info; | |
145 | struct sdhci_host *host = &prv->host; | |
146 | u32 core_version, core_minor, core_major; | |
147 | u32 caps; | |
148 | int ret; | |
9d11d12a | 149 | |
364c22a8 SG |
150 | host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B; |
151 | ||
152 | host->max_clk = 0; | |
153 | ||
154 | /* Init clocks */ | |
155 | ret = msm_sdc_clk_init(dev); | |
156 | if (ret) | |
157 | return ret; | |
158 | ||
159 | var_info = (void *)dev_get_driver_data(dev); | |
160 | if (!var_info->mci_removed) { | |
161 | ret = msm_sdc_mci_init(prv); | |
162 | if (ret) | |
163 | return ret; | |
164 | } | |
165 | ||
166 | if (!var_info->mci_removed) | |
167 | core_version = readl(prv->base + SDCC_MCI_VERSION); | |
168 | else | |
169 | core_version = readl(host->ioaddr + SDCC_V5_VERSION); | |
170 | ||
171 | core_major = (core_version & SDCC_VERSION_MAJOR_MASK); | |
172 | core_major >>= SDCC_VERSION_MAJOR_SHIFT; | |
173 | ||
174 | core_minor = core_version & SDCC_VERSION_MINOR_MASK; | |
9d11d12a | 175 | |
c2c1b03b CC |
176 | log_debug("SDCC version %d.%d\n", core_major, core_minor); |
177 | ||
9d11d12a MK |
178 | /* |
179 | * Support for some capabilities is not advertised by newer | |
180 | * controller versions and must be explicitly enabled. | |
181 | */ | |
182 | if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) { | |
12293f6d | 183 | caps = readl(host->ioaddr + SDHCI_CAPABILITIES); |
9d11d12a | 184 | caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; |
a535d717 | 185 | writel(caps, host->ioaddr + var_info->core_vendor_spec_capabilities0); |
9d11d12a MK |
186 | } |
187 | ||
85051474 MS |
188 | ret = mmc_of_parse(dev, &plat->cfg); |
189 | if (ret) | |
190 | return ret; | |
191 | ||
12293f6d | 192 | host->mmc = &plat->mmc; |
6904e1b2 PF |
193 | host->mmc->dev = dev; |
194 | ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); | |
eb9d3ca3 MK |
195 | if (ret) |
196 | return ret; | |
12293f6d | 197 | host->mmc->priv = &prv->host; |
12293f6d | 198 | upriv->mmc = host->mmc; |
eb9d3ca3 | 199 | |
12293f6d | 200 | return sdhci_probe(dev); |
9d11d12a MK |
201 | } |
202 | ||
203 | static int msm_sdc_remove(struct udevice *dev) | |
204 | { | |
205 | struct msm_sdhc *priv = dev_get_priv(dev); | |
364c22a8 SG |
206 | const struct msm_sdhc_variant_info *var_info; |
207 | ||
208 | var_info = (void *)dev_get_driver_data(dev); | |
9d11d12a | 209 | |
364c22a8 | 210 | /* Disable host-controller mode */ |
f67f224a | 211 | if (!var_info->mci_removed && priv->base) |
364c22a8 | 212 | writel(0, priv->base + SDCC_MCI_HC_MODE); |
9d11d12a | 213 | |
5f3c4cca CC |
214 | clk_release_bulk(&priv->clks); |
215 | ||
9d11d12a MK |
216 | return 0; |
217 | } | |
218 | ||
d1998a9f | 219 | static int msm_of_to_plat(struct udevice *dev) |
9d11d12a | 220 | { |
9d11d12a | 221 | struct msm_sdhc *priv = dev_get_priv(dev); |
f67f224a | 222 | const struct msm_sdhc_variant_info *var_info; |
9d11d12a | 223 | struct sdhci_host *host = &priv->host; |
f67f224a CC |
224 | int ret; |
225 | ||
226 | var_info = (void*)dev_get_driver_data(dev); | |
9d11d12a MK |
227 | |
228 | host->name = strdup(dev->name); | |
8613c8d8 | 229 | host->ioaddr = dev_read_addr_ptr(dev); |
f67f224a CC |
230 | ret = dev_read_u32(dev, "bus-width", &host->bus_width); |
231 | if (ret) | |
232 | host->bus_width = 4; | |
233 | ret = dev_read_u32(dev, "index", &host->index); | |
234 | if (ret) | |
235 | host->index = 0; | |
236 | priv->base = dev_read_addr_index_ptr(dev, 1); | |
237 | ||
238 | if (!host->ioaddr) | |
9d11d12a MK |
239 | return -EINVAL; |
240 | ||
f67f224a CC |
241 | if (!var_info->mci_removed && !priv->base) { |
242 | printf("msm_sdhci: MCI base address not found\n"); | |
243 | return -EINVAL; | |
244 | } | |
245 | ||
9d11d12a MK |
246 | return 0; |
247 | } | |
248 | ||
12293f6d SG |
249 | static int msm_sdc_bind(struct udevice *dev) |
250 | { | |
c69cda25 | 251 | struct msm_sdhc_plat *plat = dev_get_plat(dev); |
12293f6d | 252 | |
24f5aec3 | 253 | return sdhci_bind(dev, &plat->mmc, &plat->cfg); |
12293f6d SG |
254 | } |
255 | ||
364c22a8 SG |
256 | static const struct msm_sdhc_variant_info msm_sdhc_mci_var = { |
257 | .mci_removed = false, | |
a535d717 | 258 | |
0bfbd404 | 259 | .core_vendor_spec_capabilities0 = 0x11c, |
364c22a8 SG |
260 | }; |
261 | ||
262 | static const struct msm_sdhc_variant_info msm_sdhc_v5_var = { | |
263 | .mci_removed = true, | |
a535d717 | 264 | |
0bfbd404 | 265 | .core_vendor_spec_capabilities0 = 0x21c, |
364c22a8 SG |
266 | }; |
267 | ||
9d11d12a | 268 | static const struct udevice_id msm_mmc_ids[] = { |
364c22a8 SG |
269 | { .compatible = "qcom,sdhci-msm-v4", .data = (ulong)&msm_sdhc_mci_var }, |
270 | { .compatible = "qcom,sdhci-msm-v5", .data = (ulong)&msm_sdhc_v5_var }, | |
9d11d12a MK |
271 | { } |
272 | }; | |
273 | ||
274 | U_BOOT_DRIVER(msm_sdc_drv) = { | |
275 | .name = "msm_sdc", | |
276 | .id = UCLASS_MMC, | |
277 | .of_match = msm_mmc_ids, | |
d1998a9f | 278 | .of_to_plat = msm_of_to_plat, |
12293f6d | 279 | .ops = &sdhci_ops, |
12293f6d | 280 | .bind = msm_sdc_bind, |
9d11d12a MK |
281 | .probe = msm_sdc_probe, |
282 | .remove = msm_sdc_remove, | |
41575d8e | 283 | .priv_auto = sizeof(struct msm_sdhc), |
caa4daa2 | 284 | .plat_auto = sizeof(struct msm_sdhc_plat), |
9d11d12a | 285 | }; |