]>
Commit | Line | Data |
---|---|---|
aaa449fb LV |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ | |
4 | * | |
5 | * Texas Instruments' K3 SD Host Controller Interface | |
6 | */ | |
7 | ||
8 | #include <clk.h> | |
9 | #include <common.h> | |
10 | #include <dm.h> | |
11 | #include <malloc.h> | |
12 | #include <power-domain.h> | |
ce142ff0 | 13 | #include <regmap.h> |
aaa449fb | 14 | #include <sdhci.h> |
336d4615 | 15 | #include <dm/device_compat.h> |
cd93d625 | 16 | #include <linux/bitops.h> |
61b29b82 | 17 | #include <linux/err.h> |
aaa449fb | 18 | |
ce142ff0 FA |
19 | /* CTL_CFG Registers */ |
20 | #define CTL_CFG_2 0x14 | |
21 | ||
22 | #define SLOTTYPE_MASK GENMASK(31, 30) | |
23 | #define SLOTTYPE_EMBEDDED BIT(30) | |
24 | ||
25 | /* PHY Registers */ | |
26 | #define PHY_CTRL1 0x100 | |
27 | #define PHY_CTRL2 0x104 | |
28 | #define PHY_CTRL3 0x108 | |
29 | #define PHY_CTRL4 0x10C | |
30 | #define PHY_CTRL5 0x110 | |
31 | #define PHY_CTRL6 0x114 | |
32 | #define PHY_STAT1 0x130 | |
33 | #define PHY_STAT2 0x134 | |
34 | ||
35 | #define IOMUX_ENABLE_SHIFT 31 | |
36 | #define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT) | |
37 | #define OTAPDLYENA_SHIFT 20 | |
38 | #define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT) | |
39 | #define OTAPDLYSEL_SHIFT 12 | |
40 | #define OTAPDLYSEL_MASK GENMASK(15, 12) | |
41 | #define STRBSEL_SHIFT 24 | |
a20008ea FA |
42 | #define STRBSEL_4BIT_MASK GENMASK(27, 24) |
43 | #define STRBSEL_8BIT_MASK GENMASK(31, 24) | |
ce142ff0 FA |
44 | #define SEL50_SHIFT 8 |
45 | #define SEL50_MASK BIT(SEL50_SHIFT) | |
46 | #define SEL100_SHIFT 9 | |
47 | #define SEL100_MASK BIT(SEL100_SHIFT) | |
a20008ea FA |
48 | #define FREQSEL_SHIFT 8 |
49 | #define FREQSEL_MASK GENMASK(10, 8) | |
ce142ff0 FA |
50 | #define DLL_TRIM_ICP_SHIFT 4 |
51 | #define DLL_TRIM_ICP_MASK GENMASK(7, 4) | |
52 | #define DR_TY_SHIFT 20 | |
53 | #define DR_TY_MASK GENMASK(22, 20) | |
54 | #define ENDLL_SHIFT 1 | |
55 | #define ENDLL_MASK BIT(ENDLL_SHIFT) | |
56 | #define DLLRDY_SHIFT 0 | |
57 | #define DLLRDY_MASK BIT(DLLRDY_SHIFT) | |
58 | #define PDB_SHIFT 0 | |
59 | #define PDB_MASK BIT(PDB_SHIFT) | |
60 | #define CALDONE_SHIFT 1 | |
61 | #define CALDONE_MASK BIT(CALDONE_SHIFT) | |
62 | #define RETRIM_SHIFT 17 | |
63 | #define RETRIM_MASK BIT(RETRIM_SHIFT) | |
64 | ||
65 | #define DRIVER_STRENGTH_50_OHM 0x0 | |
66 | #define DRIVER_STRENGTH_33_OHM 0x1 | |
67 | #define DRIVER_STRENGTH_66_OHM 0x2 | |
68 | #define DRIVER_STRENGTH_100_OHM 0x3 | |
69 | #define DRIVER_STRENGTH_40_OHM 0x4 | |
70 | ||
3a1a0dfc | 71 | #define AM654_SDHCI_MIN_FREQ 400000 |
aaa449fb | 72 | |
3a1a0dfc | 73 | struct am654_sdhci_plat { |
aaa449fb LV |
74 | struct mmc_config cfg; |
75 | struct mmc mmc; | |
ce142ff0 FA |
76 | struct regmap *base; |
77 | bool non_removable; | |
c7d106b4 | 78 | u32 otap_del_sel[11]; |
ce142ff0 FA |
79 | u32 trm_icp; |
80 | u32 drv_strength; | |
a20008ea | 81 | u32 strb_sel; |
794453f9 FA |
82 | u32 flags; |
83 | #define DLL_PRESENT (1 << 0) | |
a20008ea FA |
84 | #define IOMUX_PRESENT (1 << 1) |
85 | #define FREQSEL_2_BIT (1 << 2) | |
86 | #define STRBSEL_4_BIT (1 << 3) | |
ce142ff0 FA |
87 | bool dll_on; |
88 | }; | |
89 | ||
c7d106b4 FA |
90 | struct timing_data { |
91 | const char *binding; | |
92 | u32 capability; | |
93 | }; | |
94 | ||
95 | static const struct timing_data td[] = { | |
96 | [MMC_LEGACY] = {"ti,otap-del-sel-legacy", 0}, | |
97 | [MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP(MMC_HS)}, | |
98 | [SD_HS] = {"ti,otap-del-sel-sd-hs", MMC_CAP(SD_HS)}, | |
99 | [UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP(UHS_SDR12)}, | |
100 | [UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP(UHS_SDR25)}, | |
101 | [UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP(UHS_SDR50)}, | |
102 | [UHS_SDR104] = {"ti,otap-del-sel-sdr104", MMC_CAP(UHS_SDR104)}, | |
103 | [UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP(UHS_DDR50)}, | |
104 | [MMC_DDR_52] = {"ti,otap-del-sel-ddr52", MMC_CAP(MMC_DDR_52)}, | |
105 | [MMC_HS_200] = {"ti,otap-del-sel-hs200", MMC_CAP(MMC_HS_200)}, | |
106 | [MMC_HS_400] = {"ti,otap-del-sel-hs400", MMC_CAP(MMC_HS_400)}, | |
107 | }; | |
108 | ||
a20008ea FA |
109 | struct am654_driver_data { |
110 | const struct sdhci_ops *ops; | |
111 | u32 flags; | |
112 | }; | |
113 | ||
f605807f FA |
114 | static void am654_sdhci_set_control_reg(struct sdhci_host *host) |
115 | { | |
116 | struct mmc *mmc = (struct mmc *)host->mmc; | |
117 | u32 reg; | |
118 | ||
119 | if (IS_SD(host->mmc) && | |
120 | mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { | |
121 | reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); | |
122 | reg |= SDHCI_CTRL_VDD_180; | |
123 | sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); | |
124 | } | |
125 | ||
126 | sdhci_set_uhs_timing(host); | |
127 | } | |
128 | ||
ce142ff0 FA |
129 | static int am654_sdhci_set_ios_post(struct sdhci_host *host) |
130 | { | |
131 | struct udevice *dev = host->mmc->dev; | |
132 | struct am654_sdhci_plat *plat = dev_get_platdata(dev); | |
133 | unsigned int speed = host->mmc->clock; | |
a20008ea | 134 | int sel50, sel100, freqsel; |
c7d106b4 | 135 | u32 otap_del_sel; |
ce142ff0 FA |
136 | u32 mask, val; |
137 | int ret; | |
138 | ||
139 | /* Reset SD Clock Enable */ | |
140 | val = sdhci_readw(host, SDHCI_CLOCK_CONTROL); | |
141 | val &= ~SDHCI_CLOCK_CARD_EN; | |
142 | sdhci_writew(host, val, SDHCI_CLOCK_CONTROL); | |
143 | ||
144 | /* power off phy */ | |
145 | if (plat->dll_on) { | |
146 | regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, 0); | |
147 | ||
148 | plat->dll_on = false; | |
149 | } | |
150 | ||
151 | /* restart clock */ | |
152 | sdhci_set_clock(host->mmc, speed); | |
153 | ||
154 | /* switch phy back on */ | |
155 | if (speed > AM654_SDHCI_MIN_FREQ) { | |
c7d106b4 | 156 | otap_del_sel = plat->otap_del_sel[host->mmc->selected_mode]; |
ce142ff0 FA |
157 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; |
158 | val = (1 << OTAPDLYENA_SHIFT) | | |
c7d106b4 | 159 | (otap_del_sel << OTAPDLYSEL_SHIFT); |
a20008ea FA |
160 | |
161 | /* Write to STRBSEL for HS400 speed mode */ | |
162 | if (host->mmc->selected_mode == MMC_HS_400) { | |
163 | if (plat->flags & STRBSEL_4_BIT) | |
164 | mask |= STRBSEL_4BIT_MASK; | |
165 | else | |
166 | mask |= STRBSEL_8BIT_MASK; | |
167 | ||
168 | val |= plat->strb_sel << STRBSEL_SHIFT; | |
ce142ff0 FA |
169 | } |
170 | ||
a20008ea FA |
171 | regmap_update_bits(plat->base, PHY_CTRL4, mask, val); |
172 | ||
173 | if (plat->flags & FREQSEL_2_BIT) { | |
174 | switch (speed) { | |
175 | case 200000000: | |
176 | sel50 = 0; | |
177 | sel100 = 0; | |
178 | break; | |
179 | case 100000000: | |
180 | sel50 = 0; | |
181 | sel100 = 1; | |
182 | break; | |
183 | default: | |
184 | sel50 = 1; | |
185 | sel100 = 0; | |
186 | } | |
187 | ||
188 | /* Configure PHY DLL frequency */ | |
189 | mask = SEL50_MASK | SEL100_MASK; | |
190 | val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT); | |
191 | regmap_update_bits(plat->base, PHY_CTRL5, mask, val); | |
192 | } else { | |
193 | switch (speed) { | |
194 | case 200000000: | |
195 | freqsel = 0x0; | |
196 | break; | |
197 | default: | |
198 | freqsel = 0x4; | |
199 | } | |
200 | regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK, | |
201 | freqsel << FREQSEL_SHIFT); | |
202 | } | |
ce142ff0 FA |
203 | |
204 | /* Enable DLL */ | |
205 | regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, | |
206 | 0x1 << ENDLL_SHIFT); | |
207 | /* | |
208 | * Poll for DLL ready. Use a one second timeout. | |
209 | * Works in all experiments done so far | |
210 | */ | |
211 | ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val, | |
212 | val & DLLRDY_MASK, 1000, 1000000); | |
213 | if (ret) | |
214 | return ret; | |
215 | ||
216 | plat->dll_on = true; | |
217 | } | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
ce142ff0 FA |
222 | int am654_sdhci_init(struct am654_sdhci_plat *plat) |
223 | { | |
224 | u32 ctl_cfg_2 = 0; | |
225 | u32 mask, val; | |
226 | int ret; | |
227 | ||
228 | /* Reset OTAP to default value */ | |
229 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; | |
230 | regmap_update_bits(plat->base, PHY_CTRL4, mask, 0x0); | |
231 | ||
794453f9 FA |
232 | if (plat->flags & DLL_PRESENT) { |
233 | regmap_read(plat->base, PHY_STAT1, &val); | |
234 | if (~val & CALDONE_MASK) { | |
235 | /* Calibrate IO lines */ | |
236 | regmap_update_bits(plat->base, PHY_CTRL1, PDB_MASK, | |
237 | PDB_MASK); | |
238 | ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, | |
239 | val, val & CALDONE_MASK, | |
240 | 1, 20); | |
241 | if (ret) | |
242 | return ret; | |
243 | } | |
ce142ff0 | 244 | |
794453f9 FA |
245 | /* Configure DLL TRIM */ |
246 | mask = DLL_TRIM_ICP_MASK; | |
247 | val = plat->trm_icp << DLL_TRIM_ICP_SHIFT; | |
ce142ff0 | 248 | |
794453f9 FA |
249 | /* Configure DLL driver strength */ |
250 | mask |= DR_TY_MASK; | |
251 | val |= plat->drv_strength << DR_TY_SHIFT; | |
252 | regmap_update_bits(plat->base, PHY_CTRL1, mask, val); | |
253 | } | |
ce142ff0 FA |
254 | |
255 | /* Enable pins by setting IO mux to 0 */ | |
a20008ea FA |
256 | if (plat->flags & IOMUX_PRESENT) |
257 | regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0); | |
ce142ff0 FA |
258 | |
259 | /* Set slot type based on SD or eMMC */ | |
260 | if (plat->non_removable) | |
261 | ctl_cfg_2 = SLOTTYPE_EMBEDDED; | |
262 | ||
263 | regmap_update_bits(plat->base, CTL_CFG_2, SLOTTYPE_MASK, ctl_cfg_2); | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
a8512139 FA |
268 | #define MAX_SDCD_DEBOUNCE_TIME 2000 |
269 | static int am654_sdhci_deferred_probe(struct sdhci_host *host) | |
270 | { | |
271 | struct udevice *dev = host->mmc->dev; | |
272 | struct am654_sdhci_plat *plat = dev_get_platdata(dev); | |
273 | unsigned long start; | |
274 | int val; | |
275 | ||
276 | /* | |
277 | * The controller takes about 1 second to debounce the card detect line | |
278 | * and doesn't let us power on until that time is up. Instead of waiting | |
279 | * for 1 second at every stage, poll on the CARD_PRESENT bit upto a | |
280 | * maximum of 2 seconds to be safe.. | |
281 | */ | |
282 | start = get_timer(0); | |
283 | do { | |
284 | if (get_timer(start) > MAX_SDCD_DEBOUNCE_TIME) | |
285 | return -ENOMEDIUM; | |
286 | ||
287 | val = mmc_getcd(host->mmc); | |
288 | } while (!val); | |
289 | ||
290 | am654_sdhci_init(plat); | |
291 | ||
292 | return sdhci_probe(dev); | |
293 | } | |
294 | ||
295 | const struct sdhci_ops am654_sdhci_ops = { | |
296 | .deferred_probe = am654_sdhci_deferred_probe, | |
297 | .set_ios_post = &am654_sdhci_set_ios_post, | |
298 | .set_control_reg = &am654_sdhci_set_control_reg, | |
299 | }; | |
300 | ||
301 | const struct am654_driver_data am654_drv_data = { | |
302 | .ops = &am654_sdhci_ops, | |
303 | .flags = IOMUX_PRESENT | FREQSEL_2_BIT | DLL_PRESENT | STRBSEL_4_BIT, | |
304 | }; | |
305 | ||
306 | const struct am654_driver_data j721e_8bit_drv_data = { | |
307 | .ops = &am654_sdhci_ops, | |
308 | .flags = DLL_PRESENT, | |
309 | }; | |
310 | ||
311 | static int j721e_4bit_sdhci_set_ios_post(struct sdhci_host *host) | |
312 | { | |
313 | struct udevice *dev = host->mmc->dev; | |
314 | struct am654_sdhci_plat *plat = dev_get_platdata(dev); | |
315 | u32 otap_del_sel, mask, val; | |
316 | ||
317 | otap_del_sel = plat->otap_del_sel[host->mmc->selected_mode]; | |
318 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; | |
319 | val = (1 << OTAPDLYENA_SHIFT) | (otap_del_sel << OTAPDLYSEL_SHIFT); | |
320 | regmap_update_bits(plat->base, PHY_CTRL4, mask, val); | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
325 | const struct sdhci_ops j721e_4bit_sdhci_ops = { | |
326 | .deferred_probe = am654_sdhci_deferred_probe, | |
327 | .set_ios_post = &j721e_4bit_sdhci_set_ios_post, | |
328 | }; | |
329 | ||
330 | const struct am654_driver_data j721e_4bit_drv_data = { | |
331 | .ops = &j721e_4bit_sdhci_ops, | |
332 | .flags = IOMUX_PRESENT, | |
333 | }; | |
334 | ||
c7d106b4 FA |
335 | static int sdhci_am654_get_otap_delay(struct udevice *dev, |
336 | struct mmc_config *cfg) | |
337 | { | |
338 | struct am654_sdhci_plat *plat = dev_get_platdata(dev); | |
339 | int ret; | |
340 | int i; | |
341 | ||
342 | /* ti,otap-del-sel-legacy is mandatory */ | |
343 | ret = dev_read_u32(dev, "ti,otap-del-sel-legacy", | |
344 | &plat->otap_del_sel[0]); | |
345 | if (ret) | |
346 | return ret; | |
347 | /* | |
348 | * Remove the corresponding capability if an otap-del-sel | |
349 | * value is not found | |
350 | */ | |
351 | for (i = MMC_HS; i <= MMC_HS_400; i++) { | |
352 | ret = dev_read_u32(dev, td[i].binding, &plat->otap_del_sel[i]); | |
353 | if (ret) { | |
354 | dev_dbg(dev, "Couldn't find %s\n", td[i].binding); | |
355 | /* | |
356 | * Remove the corresponding capability | |
357 | * if an otap-del-sel value is not found | |
358 | */ | |
359 | cfg->host_caps &= ~td[i].capability; | |
360 | } | |
361 | } | |
362 | ||
363 | return 0; | |
364 | } | |
365 | ||
3a1a0dfc | 366 | static int am654_sdhci_probe(struct udevice *dev) |
aaa449fb | 367 | { |
a20008ea FA |
368 | struct am654_driver_data *drv_data = |
369 | (struct am654_driver_data *)dev_get_driver_data(dev); | |
3a1a0dfc | 370 | struct am654_sdhci_plat *plat = dev_get_platdata(dev); |
aaa449fb LV |
371 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
372 | struct sdhci_host *host = dev_get_priv(dev); | |
ce142ff0 | 373 | struct mmc_config *cfg = &plat->cfg; |
aaa449fb LV |
374 | struct clk clk; |
375 | unsigned long clock; | |
376 | int ret; | |
377 | ||
fe0e30c7 | 378 | ret = clk_get_by_name(dev, "clk_xin", &clk); |
aaa449fb LV |
379 | if (ret) { |
380 | dev_err(dev, "failed to get clock\n"); | |
381 | return ret; | |
382 | } | |
383 | ||
384 | clock = clk_get_rate(&clk); | |
385 | if (IS_ERR_VALUE(clock)) { | |
386 | dev_err(dev, "failed to get rate\n"); | |
387 | return clock; | |
388 | } | |
389 | ||
aaa449fb | 390 | host->max_clk = clock; |
aaa449fb | 391 | host->mmc = &plat->mmc; |
ce142ff0 FA |
392 | host->mmc->dev = dev; |
393 | ret = sdhci_setup_cfg(cfg, host, cfg->f_max, | |
394 | AM654_SDHCI_MIN_FREQ); | |
aaa449fb LV |
395 | if (ret) |
396 | return ret; | |
a20008ea | 397 | |
c7d106b4 FA |
398 | ret = sdhci_am654_get_otap_delay(dev, cfg); |
399 | if (ret) | |
400 | return ret; | |
401 | ||
a20008ea | 402 | host->ops = drv_data->ops; |
aaa449fb | 403 | host->mmc->priv = host; |
aaa449fb LV |
404 | upriv->mmc = host->mmc; |
405 | ||
ce142ff0 FA |
406 | regmap_init_mem_index(dev_ofnode(dev), &plat->base, 1); |
407 | ||
a8512139 | 408 | return 0; |
aaa449fb LV |
409 | } |
410 | ||
3a1a0dfc | 411 | static int am654_sdhci_ofdata_to_platdata(struct udevice *dev) |
aaa449fb | 412 | { |
3a1a0dfc | 413 | struct am654_sdhci_plat *plat = dev_get_platdata(dev); |
aaa449fb | 414 | struct sdhci_host *host = dev_get_priv(dev); |
ce142ff0 FA |
415 | struct mmc_config *cfg = &plat->cfg; |
416 | u32 drv_strength; | |
417 | int ret; | |
aaa449fb LV |
418 | |
419 | host->name = dev->name; | |
420 | host->ioaddr = (void *)dev_read_addr(dev); | |
ce142ff0 FA |
421 | plat->non_removable = dev_read_bool(dev, "non-removable"); |
422 | ||
794453f9 FA |
423 | if (plat->flags & DLL_PRESENT) { |
424 | ret = dev_read_u32(dev, "ti,trm-icp", &plat->trm_icp); | |
425 | if (ret) | |
426 | return ret; | |
427 | ||
428 | ret = dev_read_u32(dev, "ti,driver-strength-ohm", | |
429 | &drv_strength); | |
430 | if (ret) | |
431 | return ret; | |
ce142ff0 | 432 | |
794453f9 FA |
433 | switch (drv_strength) { |
434 | case 50: | |
435 | plat->drv_strength = DRIVER_STRENGTH_50_OHM; | |
436 | break; | |
437 | case 33: | |
438 | plat->drv_strength = DRIVER_STRENGTH_33_OHM; | |
439 | break; | |
440 | case 66: | |
441 | plat->drv_strength = DRIVER_STRENGTH_66_OHM; | |
442 | break; | |
443 | case 100: | |
444 | plat->drv_strength = DRIVER_STRENGTH_100_OHM; | |
445 | break; | |
446 | case 40: | |
447 | plat->drv_strength = DRIVER_STRENGTH_40_OHM; | |
448 | break; | |
449 | default: | |
450 | dev_err(dev, "Invalid driver strength\n"); | |
451 | return -EINVAL; | |
452 | } | |
ce142ff0 FA |
453 | } |
454 | ||
455 | ret = mmc_of_parse(dev, cfg); | |
456 | if (ret) | |
457 | return ret; | |
aaa449fb LV |
458 | |
459 | return 0; | |
460 | } | |
461 | ||
3a1a0dfc | 462 | static int am654_sdhci_bind(struct udevice *dev) |
aaa449fb | 463 | { |
a20008ea FA |
464 | struct am654_driver_data *drv_data = |
465 | (struct am654_driver_data *)dev_get_driver_data(dev); | |
3a1a0dfc | 466 | struct am654_sdhci_plat *plat = dev_get_platdata(dev); |
aaa449fb | 467 | |
a20008ea FA |
468 | plat->flags = drv_data->flags; |
469 | ||
aaa449fb LV |
470 | return sdhci_bind(dev, &plat->mmc, &plat->cfg); |
471 | } | |
472 | ||
3a1a0dfc | 473 | static const struct udevice_id am654_sdhci_ids[] = { |
794453f9 FA |
474 | { |
475 | .compatible = "ti,am654-sdhci-5.1", | |
a20008ea | 476 | .data = (ulong)&am654_drv_data, |
794453f9 FA |
477 | }, |
478 | { | |
479 | .compatible = "ti,j721e-sdhci-8bit", | |
a20008ea | 480 | .data = (ulong)&j721e_8bit_drv_data, |
794453f9 FA |
481 | }, |
482 | { | |
483 | .compatible = "ti,j721e-sdhci-4bit", | |
a20008ea | 484 | .data = (ulong)&j721e_4bit_drv_data, |
794453f9 | 485 | }, |
aaa449fb LV |
486 | { } |
487 | }; | |
488 | ||
3a1a0dfc FA |
489 | U_BOOT_DRIVER(am654_sdhci_drv) = { |
490 | .name = "am654_sdhci", | |
aaa449fb | 491 | .id = UCLASS_MMC, |
3a1a0dfc FA |
492 | .of_match = am654_sdhci_ids, |
493 | .ofdata_to_platdata = am654_sdhci_ofdata_to_platdata, | |
aaa449fb | 494 | .ops = &sdhci_ops, |
3a1a0dfc FA |
495 | .bind = am654_sdhci_bind, |
496 | .probe = am654_sdhci_probe, | |
aaa449fb | 497 | .priv_auto_alloc_size = sizeof(struct sdhci_host), |
3a1a0dfc | 498 | .platdata_auto_alloc_size = sizeof(struct am654_sdhci_plat), |
aaa449fb | 499 | }; |