]>
Commit | Line | Data |
---|---|---|
aaa449fb LV |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
a94a4071 | 3 | * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ |
aaa449fb LV |
4 | * |
5 | * Texas Instruments' K3 SD Host Controller Interface | |
6 | */ | |
7 | ||
8 | #include <clk.h> | |
aaa449fb LV |
9 | #include <dm.h> |
10 | #include <malloc.h> | |
a759abf5 | 11 | #include <mmc.h> |
aaa449fb | 12 | #include <power-domain.h> |
ce142ff0 | 13 | #include <regmap.h> |
aaa449fb | 14 | #include <sdhci.h> |
8c32b5f3 | 15 | #include <soc.h> |
336d4615 | 16 | #include <dm/device_compat.h> |
cd93d625 | 17 | #include <linux/bitops.h> |
61b29b82 | 18 | #include <linux/err.h> |
aaa449fb | 19 | |
ce142ff0 FA |
20 | /* CTL_CFG Registers */ |
21 | #define CTL_CFG_2 0x14 | |
22 | ||
23 | #define SLOTTYPE_MASK GENMASK(31, 30) | |
24 | #define SLOTTYPE_EMBEDDED BIT(30) | |
25 | ||
26 | /* PHY Registers */ | |
27 | #define PHY_CTRL1 0x100 | |
28 | #define PHY_CTRL2 0x104 | |
29 | #define PHY_CTRL3 0x108 | |
30 | #define PHY_CTRL4 0x10C | |
31 | #define PHY_CTRL5 0x110 | |
32 | #define PHY_CTRL6 0x114 | |
33 | #define PHY_STAT1 0x130 | |
34 | #define PHY_STAT2 0x134 | |
35 | ||
36 | #define IOMUX_ENABLE_SHIFT 31 | |
37 | #define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT) | |
38 | #define OTAPDLYENA_SHIFT 20 | |
39 | #define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT) | |
40 | #define OTAPDLYSEL_SHIFT 12 | |
41 | #define OTAPDLYSEL_MASK GENMASK(15, 12) | |
42 | #define STRBSEL_SHIFT 24 | |
a20008ea FA |
43 | #define STRBSEL_4BIT_MASK GENMASK(27, 24) |
44 | #define STRBSEL_8BIT_MASK GENMASK(31, 24) | |
ce142ff0 FA |
45 | #define SEL50_SHIFT 8 |
46 | #define SEL50_MASK BIT(SEL50_SHIFT) | |
47 | #define SEL100_SHIFT 9 | |
48 | #define SEL100_MASK BIT(SEL100_SHIFT) | |
a20008ea FA |
49 | #define FREQSEL_SHIFT 8 |
50 | #define FREQSEL_MASK GENMASK(10, 8) | |
194c3756 FA |
51 | #define CLKBUFSEL_SHIFT 0 |
52 | #define CLKBUFSEL_MASK GENMASK(2, 0) | |
ce142ff0 FA |
53 | #define DLL_TRIM_ICP_SHIFT 4 |
54 | #define DLL_TRIM_ICP_MASK GENMASK(7, 4) | |
55 | #define DR_TY_SHIFT 20 | |
56 | #define DR_TY_MASK GENMASK(22, 20) | |
57 | #define ENDLL_SHIFT 1 | |
58 | #define ENDLL_MASK BIT(ENDLL_SHIFT) | |
59 | #define DLLRDY_SHIFT 0 | |
60 | #define DLLRDY_MASK BIT(DLLRDY_SHIFT) | |
61 | #define PDB_SHIFT 0 | |
62 | #define PDB_MASK BIT(PDB_SHIFT) | |
63 | #define CALDONE_SHIFT 1 | |
64 | #define CALDONE_MASK BIT(CALDONE_SHIFT) | |
65 | #define RETRIM_SHIFT 17 | |
66 | #define RETRIM_MASK BIT(RETRIM_SHIFT) | |
c964447e FA |
67 | #define SELDLYTXCLK_SHIFT 17 |
68 | #define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT) | |
69 | #define SELDLYRXCLK_SHIFT 16 | |
70 | #define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT) | |
71 | #define ITAPDLYSEL_SHIFT 0 | |
72 | #define ITAPDLYSEL_MASK GENMASK(4, 0) | |
73 | #define ITAPDLYENA_SHIFT 8 | |
74 | #define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT) | |
75 | #define ITAPCHGWIN_SHIFT 9 | |
76 | #define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT) | |
ce142ff0 FA |
77 | |
78 | #define DRIVER_STRENGTH_50_OHM 0x0 | |
79 | #define DRIVER_STRENGTH_33_OHM 0x1 | |
80 | #define DRIVER_STRENGTH_66_OHM 0x2 | |
81 | #define DRIVER_STRENGTH_100_OHM 0x3 | |
82 | #define DRIVER_STRENGTH_40_OHM 0x4 | |
83 | ||
3a1a0dfc | 84 | #define AM654_SDHCI_MIN_FREQ 400000 |
c964447e | 85 | #define CLOCK_TOO_SLOW_HZ 50000000 |
aaa449fb | 86 | |
056af04a JM |
87 | #define ENABLE 0x1 |
88 | ||
3a1a0dfc | 89 | struct am654_sdhci_plat { |
aaa449fb LV |
90 | struct mmc_config cfg; |
91 | struct mmc mmc; | |
ce142ff0 FA |
92 | struct regmap *base; |
93 | bool non_removable; | |
7d6f45a2 | 94 | u32 otap_del_sel[MMC_MODES_END]; |
c964447e | 95 | u32 itap_del_sel[MMC_MODES_END]; |
056af04a | 96 | u32 itap_del_ena[MMC_MODES_END]; |
ce142ff0 FA |
97 | u32 trm_icp; |
98 | u32 drv_strength; | |
a20008ea | 99 | u32 strb_sel; |
194c3756 | 100 | u32 clkbuf_sel; |
794453f9 | 101 | u32 flags; |
6b8dd9ca | 102 | bool dll_enable; |
144e131d FA |
103 | #define DLL_PRESENT BIT(0) |
104 | #define IOMUX_PRESENT BIT(1) | |
105 | #define FREQSEL_2_BIT BIT(2) | |
106 | #define STRBSEL_4_BIT BIT(3) | |
5b29fd4a | 107 | #define DLL_CALIB BIT(4) |
ce142ff0 FA |
108 | }; |
109 | ||
c7d106b4 | 110 | struct timing_data { |
c964447e FA |
111 | const char *otap_binding; |
112 | const char *itap_binding; | |
c7d106b4 FA |
113 | u32 capability; |
114 | }; | |
115 | ||
6b8dd9ca JM |
116 | struct window { |
117 | u8 start; | |
118 | u8 end; | |
119 | u8 length; | |
120 | }; | |
121 | ||
c7d106b4 | 122 | static const struct timing_data td[] = { |
c964447e FA |
123 | [MMC_LEGACY] = {"ti,otap-del-sel-legacy", |
124 | "ti,itap-del-sel-legacy", | |
125 | 0}, | |
126 | [MMC_HS] = {"ti,otap-del-sel-mmc-hs", | |
127 | "ti,itap-del-sel-mms-hs", | |
128 | MMC_CAP(MMC_HS)}, | |
129 | [SD_HS] = {"ti,otap-del-sel-sd-hs", | |
130 | "ti,itap-del-sel-sd-hs", | |
131 | MMC_CAP(SD_HS)}, | |
132 | [UHS_SDR12] = {"ti,otap-del-sel-sdr12", | |
133 | "ti,itap-del-sel-sdr12", | |
134 | MMC_CAP(UHS_SDR12)}, | |
135 | [UHS_SDR25] = {"ti,otap-del-sel-sdr25", | |
136 | "ti,itap-del-sel-sdr25", | |
137 | MMC_CAP(UHS_SDR25)}, | |
138 | [UHS_SDR50] = {"ti,otap-del-sel-sdr50", | |
139 | NULL, | |
140 | MMC_CAP(UHS_SDR50)}, | |
141 | [UHS_SDR104] = {"ti,otap-del-sel-sdr104", | |
142 | NULL, | |
143 | MMC_CAP(UHS_SDR104)}, | |
144 | [UHS_DDR50] = {"ti,otap-del-sel-ddr50", | |
145 | NULL, | |
146 | MMC_CAP(UHS_DDR50)}, | |
147 | [MMC_DDR_52] = {"ti,otap-del-sel-ddr52", | |
148 | "ti,itap-del-sel-ddr52", | |
149 | MMC_CAP(MMC_DDR_52)}, | |
150 | [MMC_HS_200] = {"ti,otap-del-sel-hs200", | |
151 | NULL, | |
152 | MMC_CAP(MMC_HS_200)}, | |
153 | [MMC_HS_400] = {"ti,otap-del-sel-hs400", | |
154 | NULL, | |
155 | MMC_CAP(MMC_HS_400)}, | |
c7d106b4 FA |
156 | }; |
157 | ||
a20008ea FA |
158 | struct am654_driver_data { |
159 | const struct sdhci_ops *ops; | |
160 | u32 flags; | |
161 | }; | |
162 | ||
c964447e FA |
163 | static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat, |
164 | unsigned int speed) | |
165 | { | |
166 | int sel50, sel100, freqsel; | |
167 | u32 mask, val; | |
168 | int ret; | |
169 | ||
170 | /* Disable delay chain mode */ | |
171 | regmap_update_bits(plat->base, PHY_CTRL5, | |
172 | SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0); | |
173 | ||
174 | if (plat->flags & FREQSEL_2_BIT) { | |
175 | switch (speed) { | |
176 | case 200000000: | |
177 | sel50 = 0; | |
178 | sel100 = 0; | |
179 | break; | |
180 | case 100000000: | |
181 | sel50 = 0; | |
182 | sel100 = 1; | |
183 | break; | |
184 | default: | |
185 | sel50 = 1; | |
186 | sel100 = 0; | |
187 | } | |
188 | ||
189 | /* Configure PHY DLL frequency */ | |
190 | mask = SEL50_MASK | SEL100_MASK; | |
191 | val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT); | |
192 | regmap_update_bits(plat->base, PHY_CTRL5, mask, val); | |
193 | } else { | |
194 | switch (speed) { | |
195 | case 200000000: | |
196 | freqsel = 0x0; | |
197 | break; | |
198 | default: | |
199 | freqsel = 0x4; | |
200 | } | |
201 | regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK, | |
202 | freqsel << FREQSEL_SHIFT); | |
203 | } | |
204 | ||
205 | /* Configure DLL TRIM */ | |
206 | mask = DLL_TRIM_ICP_MASK; | |
207 | val = plat->trm_icp << DLL_TRIM_ICP_SHIFT; | |
208 | ||
209 | /* Configure DLL driver strength */ | |
210 | mask |= DR_TY_MASK; | |
211 | val |= plat->drv_strength << DR_TY_SHIFT; | |
212 | regmap_update_bits(plat->base, PHY_CTRL1, mask, val); | |
213 | ||
214 | /* Enable DLL */ | |
215 | regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, | |
216 | 0x1 << ENDLL_SHIFT); | |
217 | /* | |
218 | * Poll for DLL ready. Use a one second timeout. | |
219 | * Works in all experiments done so far | |
220 | */ | |
221 | ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val, | |
222 | val & DLLRDY_MASK, 1000, 1000000); | |
223 | ||
224 | return ret; | |
225 | } | |
226 | ||
227 | static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat, | |
056af04a | 228 | u32 itapdly, u32 enable) |
c964447e | 229 | { |
056af04a JM |
230 | regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYENA_MASK, |
231 | enable << ITAPDLYENA_SHIFT); | |
c964447e FA |
232 | /* Set ITAPCHGWIN before writing to ITAPDLY */ |
233 | regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, | |
234 | 1 << ITAPCHGWIN_SHIFT); | |
235 | regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK, | |
236 | itapdly << ITAPDLYSEL_SHIFT); | |
237 | regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0); | |
238 | } | |
239 | ||
240 | static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat, | |
241 | int mode) | |
242 | { | |
243 | u32 mask, val; | |
244 | ||
245 | val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT; | |
246 | mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK; | |
247 | regmap_update_bits(plat->base, PHY_CTRL5, mask, val); | |
248 | ||
056af04a JM |
249 | am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode], |
250 | plat->itap_del_ena[mode]); | |
c964447e FA |
251 | } |
252 | ||
ce142ff0 FA |
253 | static int am654_sdhci_set_ios_post(struct sdhci_host *host) |
254 | { | |
255 | struct udevice *dev = host->mmc->dev; | |
c69cda25 | 256 | struct am654_sdhci_plat *plat = dev_get_plat(dev); |
ce142ff0 | 257 | unsigned int speed = host->mmc->clock; |
c964447e | 258 | int mode = host->mmc->selected_mode; |
c7d106b4 | 259 | u32 otap_del_sel; |
ce142ff0 FA |
260 | u32 mask, val; |
261 | int ret; | |
262 | ||
263 | /* Reset SD Clock Enable */ | |
264 | val = sdhci_readw(host, SDHCI_CLOCK_CONTROL); | |
265 | val &= ~SDHCI_CLOCK_CARD_EN; | |
266 | sdhci_writew(host, val, SDHCI_CLOCK_CONTROL); | |
267 | ||
c604e204 | 268 | regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, 0); |
ce142ff0 FA |
269 | |
270 | /* restart clock */ | |
271 | sdhci_set_clock(host->mmc, speed); | |
272 | ||
273 | /* switch phy back on */ | |
c964447e FA |
274 | otap_del_sel = plat->otap_del_sel[mode]; |
275 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; | |
276 | val = (1 << OTAPDLYENA_SHIFT) | | |
277 | (otap_del_sel << OTAPDLYSEL_SHIFT); | |
ce142ff0 | 278 | |
c964447e FA |
279 | /* Write to STRBSEL for HS400 speed mode */ |
280 | if (host->mmc->selected_mode == MMC_HS_400) { | |
281 | if (plat->flags & STRBSEL_4_BIT) | |
282 | mask |= STRBSEL_4BIT_MASK; | |
283 | else | |
284 | mask |= STRBSEL_8BIT_MASK; | |
285 | ||
286 | val |= plat->strb_sel << STRBSEL_SHIFT; | |
287 | } | |
288 | ||
289 | regmap_update_bits(plat->base, PHY_CTRL4, mask, val); | |
ce142ff0 | 290 | |
a124e31a | 291 | if ((mode > UHS_SDR25 || mode == MMC_DDR_52) && speed >= CLOCK_TOO_SLOW_HZ) { |
c964447e | 292 | ret = am654_sdhci_setup_dll(plat, speed); |
ce142ff0 FA |
293 | if (ret) |
294 | return ret; | |
6b8dd9ca JM |
295 | |
296 | plat->dll_enable = true; | |
f13a830e JM |
297 | if (mode == MMC_HS_400) { |
298 | plat->itap_del_ena[mode] = ENABLE; | |
299 | plat->itap_del_sel[mode] = plat->itap_del_sel[mode - 1]; | |
300 | } | |
301 | ||
a124e31a JM |
302 | am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode], |
303 | plat->itap_del_ena[mode]); | |
c964447e FA |
304 | } else { |
305 | am654_sdhci_setup_delay_chain(plat, mode); | |
6b8dd9ca | 306 | plat->dll_enable = false; |
ce142ff0 FA |
307 | } |
308 | ||
194c3756 FA |
309 | regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK, |
310 | plat->clkbuf_sel); | |
311 | ||
ce142ff0 FA |
312 | return 0; |
313 | } | |
314 | ||
ce142ff0 FA |
315 | int am654_sdhci_init(struct am654_sdhci_plat *plat) |
316 | { | |
317 | u32 ctl_cfg_2 = 0; | |
318 | u32 mask, val; | |
319 | int ret; | |
320 | ||
321 | /* Reset OTAP to default value */ | |
322 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; | |
323 | regmap_update_bits(plat->base, PHY_CTRL4, mask, 0x0); | |
324 | ||
5b29fd4a | 325 | if (plat->flags & DLL_CALIB) { |
794453f9 FA |
326 | regmap_read(plat->base, PHY_STAT1, &val); |
327 | if (~val & CALDONE_MASK) { | |
328 | /* Calibrate IO lines */ | |
329 | regmap_update_bits(plat->base, PHY_CTRL1, PDB_MASK, | |
330 | PDB_MASK); | |
331 | ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, | |
332 | val, val & CALDONE_MASK, | |
333 | 1, 20); | |
334 | if (ret) | |
335 | return ret; | |
336 | } | |
794453f9 | 337 | } |
ce142ff0 FA |
338 | |
339 | /* Enable pins by setting IO mux to 0 */ | |
a20008ea FA |
340 | if (plat->flags & IOMUX_PRESENT) |
341 | regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0); | |
ce142ff0 FA |
342 | |
343 | /* Set slot type based on SD or eMMC */ | |
344 | if (plat->non_removable) | |
345 | ctl_cfg_2 = SLOTTYPE_EMBEDDED; | |
346 | ||
347 | regmap_update_bits(plat->base, CTL_CFG_2, SLOTTYPE_MASK, ctl_cfg_2); | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
a8512139 FA |
352 | #define MAX_SDCD_DEBOUNCE_TIME 2000 |
353 | static int am654_sdhci_deferred_probe(struct sdhci_host *host) | |
354 | { | |
355 | struct udevice *dev = host->mmc->dev; | |
c69cda25 | 356 | struct am654_sdhci_plat *plat = dev_get_plat(dev); |
a8512139 FA |
357 | unsigned long start; |
358 | int val; | |
359 | ||
360 | /* | |
361 | * The controller takes about 1 second to debounce the card detect line | |
362 | * and doesn't let us power on until that time is up. Instead of waiting | |
363 | * for 1 second at every stage, poll on the CARD_PRESENT bit upto a | |
364 | * maximum of 2 seconds to be safe.. | |
365 | */ | |
366 | start = get_timer(0); | |
367 | do { | |
368 | if (get_timer(start) > MAX_SDCD_DEBOUNCE_TIME) | |
369 | return -ENOMEDIUM; | |
370 | ||
371 | val = mmc_getcd(host->mmc); | |
372 | } while (!val); | |
373 | ||
374 | am654_sdhci_init(plat); | |
375 | ||
376 | return sdhci_probe(dev); | |
377 | } | |
378 | ||
27a87c83 FA |
379 | static void am654_sdhci_write_b(struct sdhci_host *host, u8 val, int reg) |
380 | { | |
381 | if (reg == SDHCI_HOST_CONTROL) { | |
382 | switch (host->mmc->selected_mode) { | |
383 | /* | |
384 | * According to the data manual, HISPD bit | |
385 | * should not be set in these speed modes. | |
386 | */ | |
387 | case SD_HS: | |
388 | case MMC_HS: | |
389 | case UHS_SDR12: | |
390 | case UHS_SDR25: | |
391 | val &= ~SDHCI_CTRL_HISPD; | |
392 | default: | |
393 | break; | |
394 | } | |
395 | } | |
396 | ||
397 | writeb(val, host->ioaddr + reg); | |
398 | } | |
03de305e | 399 | #if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING) |
6b8dd9ca JM |
400 | #define ITAPDLY_LENGTH 32 |
401 | #define ITAPDLY_LAST_INDEX (ITAPDLY_LENGTH - 1) | |
402 | ||
403 | static u32 am654_sdhci_calculate_itap(struct udevice *dev, struct window | |
404 | *fail_window, u8 num_fails, bool circular_buffer) | |
405 | { | |
406 | u8 itap = 0, start_fail = 0, end_fail = 0, pass_length = 0; | |
407 | u8 first_fail_start = 0, last_fail_end = 0; | |
408 | struct window pass_window = {0, 0, 0}; | |
409 | int prev_fail_end = -1; | |
410 | u8 i; | |
411 | ||
412 | if (!num_fails) | |
413 | return ITAPDLY_LAST_INDEX >> 1; | |
414 | ||
415 | if (fail_window->length == ITAPDLY_LENGTH) { | |
416 | dev_err(dev, "No passing ITAPDLY, return 0\n"); | |
417 | return 0; | |
418 | } | |
419 | ||
420 | first_fail_start = fail_window->start; | |
421 | last_fail_end = fail_window[num_fails - 1].end; | |
422 | ||
423 | for (i = 0; i < num_fails; i++) { | |
424 | start_fail = fail_window[i].start; | |
425 | end_fail = fail_window[i].end; | |
426 | pass_length = start_fail - (prev_fail_end + 1); | |
427 | ||
428 | if (pass_length > pass_window.length) { | |
429 | pass_window.start = prev_fail_end + 1; | |
430 | pass_window.length = pass_length; | |
431 | } | |
432 | prev_fail_end = end_fail; | |
433 | } | |
434 | ||
435 | if (!circular_buffer) | |
436 | pass_length = ITAPDLY_LAST_INDEX - last_fail_end; | |
437 | else | |
438 | pass_length = ITAPDLY_LAST_INDEX - last_fail_end + first_fail_start; | |
439 | ||
440 | if (pass_length > pass_window.length) { | |
441 | pass_window.start = last_fail_end + 1; | |
442 | pass_window.length = pass_length; | |
443 | } | |
444 | ||
445 | if (!circular_buffer) | |
446 | itap = pass_window.start + (pass_window.length >> 1); | |
447 | else | |
448 | itap = (pass_window.start + (pass_window.length >> 1)) % ITAPDLY_LENGTH; | |
449 | ||
450 | return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap; | |
451 | } | |
452 | ||
a759abf5 FA |
453 | static int am654_sdhci_execute_tuning(struct mmc *mmc, u8 opcode) |
454 | { | |
455 | struct udevice *dev = mmc->dev; | |
456 | struct am654_sdhci_plat *plat = dev_get_plat(dev); | |
6b8dd9ca | 457 | struct window fail_window[ITAPDLY_LENGTH]; |
056af04a | 458 | int mode = mmc->selected_mode; |
6b8dd9ca JM |
459 | u8 curr_pass, itap; |
460 | u8 fail_index = 0; | |
461 | u8 prev_pass = 1; | |
462 | ||
463 | memset(fail_window, 0, sizeof(fail_window)); | |
a759abf5 FA |
464 | |
465 | /* Enable ITAPDLY */ | |
056af04a | 466 | plat->itap_del_ena[mode] = ENABLE; |
a759abf5 | 467 | |
6b8dd9ca | 468 | for (itap = 0; itap < ITAPDLY_LENGTH; itap++) { |
056af04a | 469 | am654_sdhci_write_itapdly(plat, itap, plat->itap_del_ena[mode]); |
a759abf5 | 470 | |
6b8dd9ca JM |
471 | curr_pass = !mmc_send_tuning(mmc, opcode); |
472 | ||
473 | if (!curr_pass && prev_pass) | |
474 | fail_window[fail_index].start = itap; | |
a759abf5 | 475 | |
6b8dd9ca JM |
476 | if (!curr_pass) { |
477 | fail_window[fail_index].end = itap; | |
478 | fail_window[fail_index].length++; | |
479 | } | |
a759abf5 | 480 | |
6b8dd9ca JM |
481 | if (curr_pass && !prev_pass) |
482 | fail_index++; | |
483 | ||
484 | prev_pass = curr_pass; | |
a759abf5 | 485 | } |
6b8dd9ca JM |
486 | |
487 | if (fail_window[fail_index].length != 0) | |
488 | fail_index++; | |
489 | ||
490 | itap = am654_sdhci_calculate_itap(dev, fail_window, fail_index, | |
491 | plat->dll_enable); | |
492 | ||
f13a830e JM |
493 | /* Save ITAPDLY */ |
494 | plat->itap_del_sel[mode] = itap; | |
495 | ||
056af04a | 496 | am654_sdhci_write_itapdly(plat, itap, plat->itap_del_ena[mode]); |
a759abf5 FA |
497 | |
498 | return 0; | |
499 | } | |
500 | #endif | |
a8512139 | 501 | const struct sdhci_ops am654_sdhci_ops = { |
03de305e | 502 | #if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING) |
a759abf5 FA |
503 | .platform_execute_tuning = am654_sdhci_execute_tuning, |
504 | #endif | |
a8512139 FA |
505 | .deferred_probe = am654_sdhci_deferred_probe, |
506 | .set_ios_post = &am654_sdhci_set_ios_post, | |
e9fbbba4 | 507 | .set_control_reg = sdhci_set_control_reg, |
27a87c83 | 508 | .write_b = am654_sdhci_write_b, |
a8512139 FA |
509 | }; |
510 | ||
511 | const struct am654_driver_data am654_drv_data = { | |
8c32b5f3 FA |
512 | .ops = &am654_sdhci_ops, |
513 | .flags = DLL_PRESENT | IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT, | |
514 | }; | |
515 | ||
516 | const struct am654_driver_data am654_sr1_drv_data = { | |
a8512139 | 517 | .ops = &am654_sdhci_ops, |
5b29fd4a FA |
518 | .flags = IOMUX_PRESENT | FREQSEL_2_BIT | DLL_PRESENT | DLL_CALIB | |
519 | STRBSEL_4_BIT, | |
a8512139 FA |
520 | }; |
521 | ||
522 | const struct am654_driver_data j721e_8bit_drv_data = { | |
523 | .ops = &am654_sdhci_ops, | |
5b29fd4a | 524 | .flags = DLL_PRESENT | DLL_CALIB, |
a8512139 FA |
525 | }; |
526 | ||
527 | static int j721e_4bit_sdhci_set_ios_post(struct sdhci_host *host) | |
528 | { | |
529 | struct udevice *dev = host->mmc->dev; | |
c69cda25 | 530 | struct am654_sdhci_plat *plat = dev_get_plat(dev); |
5048b5c6 NY |
531 | int mode = host->mmc->selected_mode; |
532 | u32 otap_del_sel; | |
056af04a | 533 | u32 itap_del_ena; |
5048b5c6 NY |
534 | u32 itap_del_sel; |
535 | u32 mask, val; | |
536 | ||
537 | otap_del_sel = plat->otap_del_sel[mode]; | |
a8512139 | 538 | |
a8512139 | 539 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; |
5048b5c6 NY |
540 | val = (1 << OTAPDLYENA_SHIFT) | |
541 | (otap_del_sel << OTAPDLYSEL_SHIFT); | |
542 | ||
056af04a | 543 | itap_del_ena = plat->itap_del_ena[mode]; |
5048b5c6 NY |
544 | itap_del_sel = plat->itap_del_sel[mode]; |
545 | ||
546 | mask |= ITAPDLYENA_MASK | ITAPDLYSEL_MASK; | |
056af04a | 547 | val |= (itap_del_ena << ITAPDLYENA_SHIFT) | |
5048b5c6 NY |
548 | (itap_del_sel << ITAPDLYSEL_SHIFT); |
549 | ||
550 | regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, | |
551 | 1 << ITAPCHGWIN_SHIFT); | |
a8512139 | 552 | regmap_update_bits(plat->base, PHY_CTRL4, mask, val); |
5048b5c6 | 553 | regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0); |
a8512139 | 554 | |
194c3756 FA |
555 | regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK, |
556 | plat->clkbuf_sel); | |
557 | ||
a8512139 FA |
558 | return 0; |
559 | } | |
560 | ||
561 | const struct sdhci_ops j721e_4bit_sdhci_ops = { | |
03de305e | 562 | #if CONFIG_IS_ENABLED(MMC_SUPPORTS_TUNING) |
a759abf5 FA |
563 | .platform_execute_tuning = am654_sdhci_execute_tuning, |
564 | #endif | |
a8512139 FA |
565 | .deferred_probe = am654_sdhci_deferred_probe, |
566 | .set_ios_post = &j721e_4bit_sdhci_set_ios_post, | |
e9fbbba4 | 567 | .set_control_reg = sdhci_set_control_reg, |
27a87c83 | 568 | .write_b = am654_sdhci_write_b, |
a8512139 FA |
569 | }; |
570 | ||
571 | const struct am654_driver_data j721e_4bit_drv_data = { | |
572 | .ops = &j721e_4bit_sdhci_ops, | |
573 | .flags = IOMUX_PRESENT, | |
574 | }; | |
575 | ||
7288beaa DG |
576 | static const struct am654_driver_data sdhci_am64_8bit_drvdata = { |
577 | .ops = &am654_sdhci_ops, | |
578 | .flags = DLL_PRESENT | DLL_CALIB, | |
579 | }; | |
580 | ||
581 | static const struct am654_driver_data sdhci_am64_4bit_drvdata = { | |
582 | .ops = &j721e_4bit_sdhci_ops, | |
583 | .flags = IOMUX_PRESENT, | |
584 | }; | |
585 | ||
8c32b5f3 FA |
586 | const struct soc_attr am654_sdhci_soc_attr[] = { |
587 | { .family = "AM65X", .revision = "SR1.0", .data = &am654_sr1_drv_data}, | |
588 | {/* sentinel */} | |
589 | }; | |
590 | ||
c7d106b4 FA |
591 | static int sdhci_am654_get_otap_delay(struct udevice *dev, |
592 | struct mmc_config *cfg) | |
593 | { | |
c69cda25 | 594 | struct am654_sdhci_plat *plat = dev_get_plat(dev); |
c7d106b4 FA |
595 | int ret; |
596 | int i; | |
597 | ||
598 | /* ti,otap-del-sel-legacy is mandatory */ | |
599 | ret = dev_read_u32(dev, "ti,otap-del-sel-legacy", | |
600 | &plat->otap_del_sel[0]); | |
601 | if (ret) | |
602 | return ret; | |
603 | /* | |
604 | * Remove the corresponding capability if an otap-del-sel | |
605 | * value is not found | |
606 | */ | |
5048b5c6 | 607 | for (i = MMC_LEGACY; i <= MMC_HS_400; i++) { |
c964447e FA |
608 | ret = dev_read_u32(dev, td[i].otap_binding, |
609 | &plat->otap_del_sel[i]); | |
c7d106b4 | 610 | if (ret) { |
c964447e | 611 | dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding); |
c7d106b4 FA |
612 | /* |
613 | * Remove the corresponding capability | |
614 | * if an otap-del-sel value is not found | |
615 | */ | |
616 | cfg->host_caps &= ~td[i].capability; | |
617 | } | |
c964447e | 618 | |
056af04a JM |
619 | if (td[i].itap_binding) { |
620 | ret = dev_read_u32(dev, td[i].itap_binding, | |
621 | &plat->itap_del_sel[i]); | |
622 | ||
623 | if (!ret) | |
624 | plat->itap_del_ena[i] = ENABLE; | |
625 | } | |
c7d106b4 FA |
626 | } |
627 | ||
628 | return 0; | |
629 | } | |
630 | ||
3a1a0dfc | 631 | static int am654_sdhci_probe(struct udevice *dev) |
aaa449fb | 632 | { |
a20008ea FA |
633 | struct am654_driver_data *drv_data = |
634 | (struct am654_driver_data *)dev_get_driver_data(dev); | |
c69cda25 | 635 | struct am654_sdhci_plat *plat = dev_get_plat(dev); |
aaa449fb LV |
636 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
637 | struct sdhci_host *host = dev_get_priv(dev); | |
ce142ff0 | 638 | struct mmc_config *cfg = &plat->cfg; |
8c32b5f3 FA |
639 | const struct soc_attr *soc; |
640 | const struct am654_driver_data *soc_drv_data; | |
aaa449fb LV |
641 | struct clk clk; |
642 | unsigned long clock; | |
643 | int ret; | |
644 | ||
fe0e30c7 | 645 | ret = clk_get_by_name(dev, "clk_xin", &clk); |
aaa449fb LV |
646 | if (ret) { |
647 | dev_err(dev, "failed to get clock\n"); | |
648 | return ret; | |
649 | } | |
650 | ||
651 | clock = clk_get_rate(&clk); | |
652 | if (IS_ERR_VALUE(clock)) { | |
653 | dev_err(dev, "failed to get rate\n"); | |
654 | return clock; | |
655 | } | |
656 | ||
aaa449fb | 657 | host->max_clk = clock; |
aaa449fb | 658 | host->mmc = &plat->mmc; |
ce142ff0 | 659 | host->mmc->dev = dev; |
27a87c83 | 660 | host->ops = drv_data->ops; |
ce142ff0 FA |
661 | ret = sdhci_setup_cfg(cfg, host, cfg->f_max, |
662 | AM654_SDHCI_MIN_FREQ); | |
aaa449fb LV |
663 | if (ret) |
664 | return ret; | |
a20008ea | 665 | |
c7d106b4 FA |
666 | ret = sdhci_am654_get_otap_delay(dev, cfg); |
667 | if (ret) | |
668 | return ret; | |
669 | ||
8c32b5f3 FA |
670 | /* Update ops based on SoC revision */ |
671 | soc = soc_device_match(am654_sdhci_soc_attr); | |
672 | if (soc && soc->data) { | |
673 | soc_drv_data = soc->data; | |
674 | host->ops = soc_drv_data->ops; | |
675 | } | |
676 | ||
aaa449fb | 677 | host->mmc->priv = host; |
aaa449fb LV |
678 | upriv->mmc = host->mmc; |
679 | ||
ce142ff0 FA |
680 | regmap_init_mem_index(dev_ofnode(dev), &plat->base, 1); |
681 | ||
a8512139 | 682 | return 0; |
aaa449fb LV |
683 | } |
684 | ||
d1998a9f | 685 | static int am654_sdhci_of_to_plat(struct udevice *dev) |
aaa449fb | 686 | { |
c69cda25 | 687 | struct am654_sdhci_plat *plat = dev_get_plat(dev); |
aaa449fb | 688 | struct sdhci_host *host = dev_get_priv(dev); |
ce142ff0 FA |
689 | struct mmc_config *cfg = &plat->cfg; |
690 | u32 drv_strength; | |
691 | int ret; | |
aaa449fb LV |
692 | |
693 | host->name = dev->name; | |
a12a73b6 | 694 | host->ioaddr = dev_read_addr_ptr(dev); |
ce142ff0 FA |
695 | plat->non_removable = dev_read_bool(dev, "non-removable"); |
696 | ||
794453f9 FA |
697 | if (plat->flags & DLL_PRESENT) { |
698 | ret = dev_read_u32(dev, "ti,trm-icp", &plat->trm_icp); | |
699 | if (ret) | |
700 | return ret; | |
701 | ||
702 | ret = dev_read_u32(dev, "ti,driver-strength-ohm", | |
703 | &drv_strength); | |
704 | if (ret) | |
705 | return ret; | |
ce142ff0 | 706 | |
794453f9 FA |
707 | switch (drv_strength) { |
708 | case 50: | |
709 | plat->drv_strength = DRIVER_STRENGTH_50_OHM; | |
710 | break; | |
711 | case 33: | |
712 | plat->drv_strength = DRIVER_STRENGTH_33_OHM; | |
713 | break; | |
714 | case 66: | |
715 | plat->drv_strength = DRIVER_STRENGTH_66_OHM; | |
716 | break; | |
717 | case 100: | |
718 | plat->drv_strength = DRIVER_STRENGTH_100_OHM; | |
719 | break; | |
720 | case 40: | |
721 | plat->drv_strength = DRIVER_STRENGTH_40_OHM; | |
722 | break; | |
723 | default: | |
724 | dev_err(dev, "Invalid driver strength\n"); | |
725 | return -EINVAL; | |
726 | } | |
ce142ff0 FA |
727 | } |
728 | ||
46077ef2 | 729 | dev_read_u32(dev, "ti,strobe-sel", &plat->strb_sel); |
194c3756 FA |
730 | dev_read_u32(dev, "ti,clkbuf-sel", &plat->clkbuf_sel); |
731 | ||
ce142ff0 FA |
732 | ret = mmc_of_parse(dev, cfg); |
733 | if (ret) | |
734 | return ret; | |
aaa449fb LV |
735 | |
736 | return 0; | |
737 | } | |
738 | ||
3a1a0dfc | 739 | static int am654_sdhci_bind(struct udevice *dev) |
aaa449fb | 740 | { |
a20008ea FA |
741 | struct am654_driver_data *drv_data = |
742 | (struct am654_driver_data *)dev_get_driver_data(dev); | |
c69cda25 | 743 | struct am654_sdhci_plat *plat = dev_get_plat(dev); |
8c32b5f3 FA |
744 | const struct soc_attr *soc; |
745 | const struct am654_driver_data *soc_drv_data; | |
aaa449fb | 746 | |
a20008ea FA |
747 | plat->flags = drv_data->flags; |
748 | ||
8c32b5f3 FA |
749 | /* Update flags based on SoC revision */ |
750 | soc = soc_device_match(am654_sdhci_soc_attr); | |
751 | if (soc && soc->data) { | |
752 | soc_drv_data = soc->data; | |
753 | plat->flags = soc_drv_data->flags; | |
754 | } | |
755 | ||
aaa449fb LV |
756 | return sdhci_bind(dev, &plat->mmc, &plat->cfg); |
757 | } | |
758 | ||
3a1a0dfc | 759 | static const struct udevice_id am654_sdhci_ids[] = { |
794453f9 FA |
760 | { |
761 | .compatible = "ti,am654-sdhci-5.1", | |
a20008ea | 762 | .data = (ulong)&am654_drv_data, |
794453f9 FA |
763 | }, |
764 | { | |
765 | .compatible = "ti,j721e-sdhci-8bit", | |
a20008ea | 766 | .data = (ulong)&j721e_8bit_drv_data, |
794453f9 FA |
767 | }, |
768 | { | |
769 | .compatible = "ti,j721e-sdhci-4bit", | |
a20008ea | 770 | .data = (ulong)&j721e_4bit_drv_data, |
794453f9 | 771 | }, |
7288beaa DG |
772 | { |
773 | .compatible = "ti,am64-sdhci-8bit", | |
774 | .data = (ulong)&sdhci_am64_8bit_drvdata, | |
775 | }, | |
776 | { | |
777 | .compatible = "ti,am64-sdhci-4bit", | |
778 | .data = (ulong)&sdhci_am64_4bit_drvdata, | |
779 | }, | |
ed6d7814 AG |
780 | { |
781 | .compatible = "ti,am62-sdhci", | |
782 | .data = (ulong)&sdhci_am64_4bit_drvdata, | |
783 | }, | |
aaa449fb LV |
784 | { } |
785 | }; | |
786 | ||
3a1a0dfc FA |
787 | U_BOOT_DRIVER(am654_sdhci_drv) = { |
788 | .name = "am654_sdhci", | |
aaa449fb | 789 | .id = UCLASS_MMC, |
3a1a0dfc | 790 | .of_match = am654_sdhci_ids, |
d1998a9f | 791 | .of_to_plat = am654_sdhci_of_to_plat, |
aaa449fb | 792 | .ops = &sdhci_ops, |
3a1a0dfc FA |
793 | .bind = am654_sdhci_bind, |
794 | .probe = am654_sdhci_probe, | |
41575d8e | 795 | .priv_auto = sizeof(struct sdhci_host), |
caa4daa2 | 796 | .plat_auto = sizeof(struct am654_sdhci_plat), |
aaa449fb | 797 | }; |