Commit | Line | Data |
---|---|---|
41fd4cae FA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * sdhci_am654.c - SDHCI driver for TI's AM654 SOCs | |
4 | * | |
9481b45c | 5 | * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com |
41fd4cae FA |
6 | * |
7 | */ | |
8 | #include <linux/clk.h> | |
7ca0f166 | 9 | #include <linux/iopoll.h> |
99909b55 | 10 | #include <linux/of.h> |
41fd4cae FA |
11 | #include <linux/module.h> |
12 | #include <linux/pm_runtime.h> | |
13 | #include <linux/property.h> | |
14 | #include <linux/regmap.h> | |
09db9943 | 15 | #include <linux/sys_soc.h> |
41fd4cae | 16 | |
f545702b | 17 | #include "cqhci.h" |
162503fd | 18 | #include "sdhci-cqhci.h" |
41fd4cae FA |
19 | #include "sdhci-pltfm.h" |
20 | ||
21 | /* CTL_CFG Registers */ | |
22 | #define CTL_CFG_2 0x14 | |
764384d0 | 23 | #define CTL_CFG_3 0x18 |
41fd4cae FA |
24 | |
25 | #define SLOTTYPE_MASK GENMASK(31, 30) | |
26 | #define SLOTTYPE_EMBEDDED BIT(30) | |
764384d0 | 27 | #define TUNINGFORSDR50_MASK BIT(13) |
41fd4cae FA |
28 | |
29 | /* PHY Registers */ | |
30 | #define PHY_CTRL1 0x100 | |
31 | #define PHY_CTRL2 0x104 | |
32 | #define PHY_CTRL3 0x108 | |
33 | #define PHY_CTRL4 0x10C | |
34 | #define PHY_CTRL5 0x110 | |
35 | #define PHY_CTRL6 0x114 | |
36 | #define PHY_STAT1 0x130 | |
37 | #define PHY_STAT2 0x134 | |
38 | ||
39 | #define IOMUX_ENABLE_SHIFT 31 | |
40 | #define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT) | |
41 | #define OTAPDLYENA_SHIFT 20 | |
42 | #define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT) | |
43 | #define OTAPDLYSEL_SHIFT 12 | |
44 | #define OTAPDLYSEL_MASK GENMASK(15, 12) | |
45 | #define STRBSEL_SHIFT 24 | |
99909b55 FA |
46 | #define STRBSEL_4BIT_MASK GENMASK(27, 24) |
47 | #define STRBSEL_8BIT_MASK GENMASK(31, 24) | |
41fd4cae FA |
48 | #define SEL50_SHIFT 8 |
49 | #define SEL50_MASK BIT(SEL50_SHIFT) | |
50 | #define SEL100_SHIFT 9 | |
51 | #define SEL100_MASK BIT(SEL100_SHIFT) | |
99909b55 FA |
52 | #define FREQSEL_SHIFT 8 |
53 | #define FREQSEL_MASK GENMASK(10, 8) | |
61d9c4aa FA |
54 | #define CLKBUFSEL_SHIFT 0 |
55 | #define CLKBUFSEL_MASK GENMASK(2, 0) | |
41fd4cae FA |
56 | #define DLL_TRIM_ICP_SHIFT 4 |
57 | #define DLL_TRIM_ICP_MASK GENMASK(7, 4) | |
58 | #define DR_TY_SHIFT 20 | |
59 | #define DR_TY_MASK GENMASK(22, 20) | |
60 | #define ENDLL_SHIFT 1 | |
61 | #define ENDLL_MASK BIT(ENDLL_SHIFT) | |
62 | #define DLLRDY_SHIFT 0 | |
63 | #define DLLRDY_MASK BIT(DLLRDY_SHIFT) | |
64 | #define PDB_SHIFT 0 | |
65 | #define PDB_MASK BIT(PDB_SHIFT) | |
66 | #define CALDONE_SHIFT 1 | |
67 | #define CALDONE_MASK BIT(CALDONE_SHIFT) | |
68 | #define RETRIM_SHIFT 17 | |
69 | #define RETRIM_MASK BIT(RETRIM_SHIFT) | |
0003417d FA |
70 | #define SELDLYTXCLK_SHIFT 17 |
71 | #define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT) | |
a0a62497 FA |
72 | #define SELDLYRXCLK_SHIFT 16 |
73 | #define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT) | |
74 | #define ITAPDLYSEL_SHIFT 0 | |
75 | #define ITAPDLYSEL_MASK GENMASK(4, 0) | |
76 | #define ITAPDLYENA_SHIFT 8 | |
77 | #define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT) | |
78 | #define ITAPCHGWIN_SHIFT 9 | |
79 | #define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT) | |
41fd4cae FA |
80 | |
81 | #define DRIVER_STRENGTH_50_OHM 0x0 | |
82 | #define DRIVER_STRENGTH_33_OHM 0x1 | |
83 | #define DRIVER_STRENGTH_66_OHM 0x2 | |
84 | #define DRIVER_STRENGTH_100_OHM 0x3 | |
85 | #define DRIVER_STRENGTH_40_OHM 0x4 | |
86 | ||
a0a62497 | 87 | #define CLOCK_TOO_SLOW_HZ 50000000 |
9d2e77ff | 88 | #define SDHCI_AM654_AUTOSUSPEND_DELAY -1 |
901d16e4 | 89 | #define RETRY_TUNING_MAX 10 |
41fd4cae | 90 | |
f545702b FA |
91 | /* Command Queue Host Controller Interface Base address */ |
92 | #define SDHCI_AM654_CQE_BASE_ADDR 0x200 | |
93 | ||
1535085f | 94 | static const struct regmap_config sdhci_am654_regmap_config = { |
41fd4cae FA |
95 | .reg_bits = 32, |
96 | .val_bits = 32, | |
97 | .reg_stride = 4, | |
98 | .fast_io = true, | |
99 | }; | |
100 | ||
1e753dbb | 101 | struct timing_data { |
a0a62497 FA |
102 | const char *otap_binding; |
103 | const char *itap_binding; | |
1e753dbb FA |
104 | u32 capability; |
105 | }; | |
106 | ||
107 | static const struct timing_data td[] = { | |
a0a62497 FA |
108 | [MMC_TIMING_LEGACY] = {"ti,otap-del-sel-legacy", |
109 | "ti,itap-del-sel-legacy", | |
110 | 0}, | |
111 | [MMC_TIMING_MMC_HS] = {"ti,otap-del-sel-mmc-hs", | |
112 | "ti,itap-del-sel-mmc-hs", | |
113 | MMC_CAP_MMC_HIGHSPEED}, | |
114 | [MMC_TIMING_SD_HS] = {"ti,otap-del-sel-sd-hs", | |
115 | "ti,itap-del-sel-sd-hs", | |
116 | MMC_CAP_SD_HIGHSPEED}, | |
117 | [MMC_TIMING_UHS_SDR12] = {"ti,otap-del-sel-sdr12", | |
118 | "ti,itap-del-sel-sdr12", | |
119 | MMC_CAP_UHS_SDR12}, | |
120 | [MMC_TIMING_UHS_SDR25] = {"ti,otap-del-sel-sdr25", | |
121 | "ti,itap-del-sel-sdr25", | |
122 | MMC_CAP_UHS_SDR25}, | |
123 | [MMC_TIMING_UHS_SDR50] = {"ti,otap-del-sel-sdr50", | |
124 | NULL, | |
125 | MMC_CAP_UHS_SDR50}, | |
126 | [MMC_TIMING_UHS_SDR104] = {"ti,otap-del-sel-sdr104", | |
127 | NULL, | |
1e753dbb | 128 | MMC_CAP_UHS_SDR104}, |
a0a62497 FA |
129 | [MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50", |
130 | NULL, | |
131 | MMC_CAP_UHS_DDR50}, | |
132 | [MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52", | |
133 | "ti,itap-del-sel-ddr52", | |
134 | MMC_CAP_DDR}, | |
135 | [MMC_TIMING_MMC_HS200] = {"ti,otap-del-sel-hs200", | |
136 | NULL, | |
137 | MMC_CAP2_HS200}, | |
138 | [MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400", | |
139 | NULL, | |
140 | MMC_CAP2_HS400}, | |
1e753dbb FA |
141 | }; |
142 | ||
41fd4cae FA |
143 | struct sdhci_am654_data { |
144 | struct regmap *base; | |
a66db816 JM |
145 | u32 otap_del_sel[ARRAY_SIZE(td)]; |
146 | u32 itap_del_sel[ARRAY_SIZE(td)]; | |
387c1bf7 | 147 | u32 itap_del_ena[ARRAY_SIZE(td)]; |
61d9c4aa | 148 | int clkbuf_sel; |
41fd4cae FA |
149 | int trm_icp; |
150 | int drv_strength; | |
99909b55 FA |
151 | int strb_sel; |
152 | u32 flags; | |
c7666240 | 153 | u32 quirks; |
6231d99d | 154 | bool dll_enable; |
901d16e4 | 155 | u32 tuning_loop; |
c7666240 VR |
156 | |
157 | #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0) | |
71c9475b | 158 | #define SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA BIT(1) |
99909b55 FA |
159 | }; |
160 | ||
6231d99d JM |
161 | struct window { |
162 | u8 start; | |
163 | u8 end; | |
164 | u8 length; | |
165 | }; | |
166 | ||
99909b55 FA |
167 | struct sdhci_am654_driver_data { |
168 | const struct sdhci_pltfm_data *pdata; | |
169 | u32 flags; | |
71c9475b | 170 | u32 quirks; |
99909b55 FA |
171 | #define IOMUX_PRESENT (1 << 0) |
172 | #define FREQSEL_2_BIT (1 << 1) | |
173 | #define STRBSEL_4_BIT (1 << 2) | |
1accbced | 174 | #define DLL_PRESENT (1 << 3) |
23514731 | 175 | #define DLL_CALIB (1 << 4) |
41fd4cae FA |
176 | }; |
177 | ||
a161c45f FA |
178 | static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock) |
179 | { | |
180 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
181 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
182 | int sel50, sel100, freqsel; | |
183 | u32 mask, val; | |
184 | int ret; | |
185 | ||
a0a62497 FA |
186 | /* Disable delay chain mode */ |
187 | regmap_update_bits(sdhci_am654->base, PHY_CTRL5, | |
188 | SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0); | |
189 | ||
a161c45f FA |
190 | if (sdhci_am654->flags & FREQSEL_2_BIT) { |
191 | switch (clock) { | |
192 | case 200000000: | |
193 | sel50 = 0; | |
194 | sel100 = 0; | |
195 | break; | |
196 | case 100000000: | |
197 | sel50 = 0; | |
198 | sel100 = 1; | |
199 | break; | |
200 | default: | |
201 | sel50 = 1; | |
202 | sel100 = 0; | |
203 | } | |
204 | ||
205 | /* Configure PHY DLL frequency */ | |
206 | mask = SEL50_MASK | SEL100_MASK; | |
207 | val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT); | |
208 | regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val); | |
209 | ||
210 | } else { | |
211 | switch (clock) { | |
212 | case 200000000: | |
213 | freqsel = 0x0; | |
214 | break; | |
215 | default: | |
216 | freqsel = 0x4; | |
217 | } | |
218 | ||
219 | regmap_update_bits(sdhci_am654->base, PHY_CTRL5, FREQSEL_MASK, | |
220 | freqsel << FREQSEL_SHIFT); | |
221 | } | |
222 | /* Configure DLL TRIM */ | |
223 | mask = DLL_TRIM_ICP_MASK; | |
224 | val = sdhci_am654->trm_icp << DLL_TRIM_ICP_SHIFT; | |
225 | ||
226 | /* Configure DLL driver strength */ | |
227 | mask |= DR_TY_MASK; | |
228 | val |= sdhci_am654->drv_strength << DR_TY_SHIFT; | |
229 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, mask, val); | |
230 | ||
231 | /* Enable DLL */ | |
232 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, | |
233 | 0x1 << ENDLL_SHIFT); | |
234 | /* | |
235 | * Poll for DLL ready. Use a one second timeout. | |
236 | * Works in all experiments done so far | |
237 | */ | |
238 | ret = regmap_read_poll_timeout(sdhci_am654->base, PHY_STAT1, val, | |
239 | val & DLLRDY_MASK, 1000, 1000000); | |
240 | if (ret) { | |
241 | dev_err(mmc_dev(host->mmc), "DLL failed to relock\n"); | |
242 | return; | |
243 | } | |
a0a62497 FA |
244 | } |
245 | ||
246 | static void sdhci_am654_write_itapdly(struct sdhci_am654_data *sdhci_am654, | |
387c1bf7 | 247 | u32 itapdly, u32 enable) |
a0a62497 FA |
248 | { |
249 | /* Set ITAPCHGWIN before writing to ITAPDLY */ | |
250 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, | |
251 | 1 << ITAPCHGWIN_SHIFT); | |
387c1bf7 JM |
252 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYENA_MASK, |
253 | enable << ITAPDLYENA_SHIFT); | |
a0a62497 FA |
254 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYSEL_MASK, |
255 | itapdly << ITAPDLYSEL_SHIFT); | |
256 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0); | |
257 | } | |
258 | ||
259 | static void sdhci_am654_setup_delay_chain(struct sdhci_am654_data *sdhci_am654, | |
260 | unsigned char timing) | |
261 | { | |
262 | u32 mask, val; | |
a161c45f | 263 | |
a0a62497 FA |
264 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0); |
265 | ||
266 | val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT; | |
267 | mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK; | |
268 | regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val); | |
269 | ||
387c1bf7 JM |
270 | sdhci_am654_write_itapdly(sdhci_am654, sdhci_am654->itap_del_sel[timing], |
271 | sdhci_am654->itap_del_ena[timing]); | |
a161c45f FA |
272 | } |
273 | ||
41fd4cae FA |
274 | static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock) |
275 | { | |
276 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
277 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
8ee5fc0e | 278 | unsigned char timing = host->mmc->ios.timing; |
8ee5fc0e | 279 | u32 otap_del_sel; |
41fd4cae | 280 | u32 mask, val; |
41fd4cae | 281 | |
a0a62497 | 282 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0); |
41fd4cae FA |
283 | |
284 | sdhci_set_clock(host, clock); | |
285 | ||
f4a5dddd | 286 | /* Setup Output TAP delay */ |
5cb2f928 | 287 | otap_del_sel = sdhci_am654->otap_del_sel[timing]; |
8ee5fc0e | 288 | |
fe52e2fb | 289 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; |
387c1bf7 | 290 | val = (0x1 << OTAPDLYENA_SHIFT) | |
fe52e2fb | 291 | (otap_del_sel << OTAPDLYSEL_SHIFT); |
8ee5fc0e | 292 | |
fe52e2fb FA |
293 | /* Write to STRBSEL for HS400 speed mode */ |
294 | if (timing == MMC_TIMING_MMC_HS400) { | |
295 | if (sdhci_am654->flags & STRBSEL_4_BIT) | |
296 | mask |= STRBSEL_4BIT_MASK; | |
297 | else | |
298 | mask |= STRBSEL_8BIT_MASK; | |
99909b55 | 299 | |
fe52e2fb FA |
300 | val |= sdhci_am654->strb_sel << STRBSEL_SHIFT; |
301 | } | |
99909b55 | 302 | |
fe52e2fb | 303 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); |
8ee5fc0e | 304 | |
6231d99d | 305 | if (timing > MMC_TIMING_UHS_SDR25 && clock >= CLOCK_TOO_SLOW_HZ) { |
fe52e2fb | 306 | sdhci_am654_setup_dll(host, clock); |
6231d99d | 307 | sdhci_am654->dll_enable = true; |
d3182932 JM |
308 | |
309 | if (timing == MMC_TIMING_MMC_HS400) { | |
310 | sdhci_am654->itap_del_ena[timing] = 0x1; | |
311 | sdhci_am654->itap_del_sel[timing] = sdhci_am654->itap_del_sel[timing - 1]; | |
312 | } | |
313 | ||
387c1bf7 JM |
314 | sdhci_am654_write_itapdly(sdhci_am654, sdhci_am654->itap_del_sel[timing], |
315 | sdhci_am654->itap_del_ena[timing]); | |
6231d99d | 316 | } else { |
a0a62497 | 317 | sdhci_am654_setup_delay_chain(sdhci_am654, timing); |
6231d99d JM |
318 | sdhci_am654->dll_enable = false; |
319 | } | |
61d9c4aa FA |
320 | |
321 | regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK, | |
322 | sdhci_am654->clkbuf_sel); | |
41fd4cae FA |
323 | } |
324 | ||
8751c8bd Y |
325 | static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host, |
326 | unsigned int clock) | |
1accbced FA |
327 | { |
328 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
329 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
8ee5fc0e FA |
330 | unsigned char timing = host->mmc->ios.timing; |
331 | u32 otap_del_sel; | |
387c1bf7 | 332 | u32 itap_del_ena; |
9dff65bb | 333 | u32 itap_del_sel; |
8ee5fc0e FA |
334 | u32 mask, val; |
335 | ||
f4a5dddd | 336 | /* Setup Output TAP delay */ |
5cb2f928 | 337 | otap_del_sel = sdhci_am654->otap_del_sel[timing]; |
1accbced FA |
338 | |
339 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; | |
8ee5fc0e FA |
340 | val = (0x1 << OTAPDLYENA_SHIFT) | |
341 | (otap_del_sel << OTAPDLYSEL_SHIFT); | |
387c1bf7 | 342 | |
9dff65bb | 343 | /* Setup Input TAP delay */ |
387c1bf7 | 344 | itap_del_ena = sdhci_am654->itap_del_ena[timing]; |
9dff65bb | 345 | itap_del_sel = sdhci_am654->itap_del_sel[timing]; |
387c1bf7 | 346 | |
9dff65bb JM |
347 | mask |= ITAPDLYENA_MASK | ITAPDLYSEL_MASK; |
348 | val |= (itap_del_ena << ITAPDLYENA_SHIFT) | | |
349 | (itap_del_sel << ITAPDLYSEL_SHIFT); | |
387c1bf7 | 350 | |
9dff65bb JM |
351 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, |
352 | 1 << ITAPCHGWIN_SHIFT); | |
1accbced | 353 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); |
9dff65bb | 354 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0); |
61d9c4aa FA |
355 | regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK, |
356 | sdhci_am654->clkbuf_sel); | |
357 | ||
1accbced FA |
358 | sdhci_set_clock(host, clock); |
359 | } | |
360 | ||
71c9475b JM |
361 | static int sdhci_am654_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) |
362 | { | |
363 | struct sdhci_host *host = mmc_priv(mmc); | |
364 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
365 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
366 | int ret; | |
367 | ||
368 | if ((sdhci_am654->quirks & SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA) && | |
369 | ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { | |
370 | if (!IS_ERR(mmc->supply.vqmmc)) { | |
371 | ret = mmc_regulator_set_vqmmc(mmc, ios); | |
372 | if (ret < 0) { | |
373 | pr_err("%s: Switching to 1.8V signalling voltage failed,\n", | |
374 | mmc_hostname(mmc)); | |
375 | return -EIO; | |
376 | } | |
377 | } | |
378 | return 0; | |
379 | } | |
380 | ||
381 | return sdhci_start_signal_voltage_switch(mmc, ios); | |
382 | } | |
383 | ||
7ca0f166 FA |
384 | static u8 sdhci_am654_write_power_on(struct sdhci_host *host, u8 val, int reg) |
385 | { | |
386 | writeb(val, host->ioaddr + reg); | |
387 | usleep_range(1000, 10000); | |
388 | return readb(host->ioaddr + reg); | |
389 | } | |
390 | ||
391 | #define MAX_POWER_ON_TIMEOUT 1500000 /* us */ | |
e374e875 FA |
392 | static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg) |
393 | { | |
394 | unsigned char timing = host->mmc->ios.timing; | |
7ca0f166 FA |
395 | u8 pwr; |
396 | int ret; | |
e374e875 FA |
397 | |
398 | if (reg == SDHCI_HOST_CONTROL) { | |
399 | switch (timing) { | |
400 | /* | |
401 | * According to the data manual, HISPD bit | |
402 | * should not be set in these speed modes. | |
403 | */ | |
404 | case MMC_TIMING_SD_HS: | |
405 | case MMC_TIMING_MMC_HS: | |
e374e875 FA |
406 | val &= ~SDHCI_CTRL_HISPD; |
407 | } | |
408 | } | |
409 | ||
410 | writeb(val, host->ioaddr + reg); | |
7ca0f166 FA |
411 | if (reg == SDHCI_POWER_CONTROL && (val & SDHCI_POWER_ON)) { |
412 | /* | |
413 | * Power on will not happen until the card detect debounce | |
414 | * timer expires. Wait at least 1.5 seconds for the power on | |
415 | * bit to be set | |
416 | */ | |
417 | ret = read_poll_timeout(sdhci_am654_write_power_on, pwr, | |
418 | pwr & SDHCI_POWER_ON, 0, | |
419 | MAX_POWER_ON_TIMEOUT, false, host, val, | |
420 | reg); | |
421 | if (ret) | |
11440da7 | 422 | dev_info(mmc_dev(host->mmc), "Power on failed\n"); |
7ca0f166 | 423 | } |
e374e875 FA |
424 | } |
425 | ||
c7666240 VR |
426 | static void sdhci_am654_reset(struct sdhci_host *host, u8 mask) |
427 | { | |
428 | u8 ctrl; | |
429 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
430 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
431 | ||
162503fd | 432 | sdhci_and_cqhci_reset(host, mask); |
c7666240 VR |
433 | |
434 | if (sdhci_am654->quirks & SDHCI_AM654_QUIRK_FORCE_CDTEST) { | |
435 | ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); | |
436 | ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN; | |
437 | sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); | |
438 | } | |
439 | } | |
440 | ||
de31f6ab FA |
441 | static int sdhci_am654_execute_tuning(struct mmc_host *mmc, u32 opcode) |
442 | { | |
443 | struct sdhci_host *host = mmc_priv(mmc); | |
444 | int err = sdhci_execute_tuning(mmc, opcode); | |
445 | ||
446 | if (err) | |
447 | return err; | |
448 | /* | |
449 | * Tuning data remains in the buffer after tuning. | |
450 | * Do a command and data reset to get rid of it | |
451 | */ | |
452 | sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
27f4e1e9 FA |
457 | static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask) |
458 | { | |
459 | int cmd_error = 0; | |
460 | int data_error = 0; | |
461 | ||
462 | if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) | |
463 | return intmask; | |
464 | ||
465 | cqhci_irq(host->mmc, intmask, cmd_error, data_error); | |
466 | ||
467 | return 0; | |
468 | } | |
469 | ||
6231d99d JM |
470 | #define ITAPDLY_LENGTH 32 |
471 | #define ITAPDLY_LAST_INDEX (ITAPDLY_LENGTH - 1) | |
472 | ||
901d16e4 | 473 | static int sdhci_am654_calculate_itap(struct sdhci_host *host, struct window |
6231d99d JM |
474 | *fail_window, u8 num_fails, bool circular_buffer) |
475 | { | |
476 | u8 itap = 0, start_fail = 0, end_fail = 0, pass_length = 0; | |
477 | u8 first_fail_start = 0, last_fail_end = 0; | |
cf6444ba | 478 | struct device *dev = mmc_dev(host->mmc); |
6231d99d JM |
479 | struct window pass_window = {0, 0, 0}; |
480 | int prev_fail_end = -1; | |
481 | u8 i; | |
482 | ||
901d16e4 JM |
483 | if (!num_fails) { |
484 | /* Retry tuning */ | |
cf6444ba | 485 | dev_dbg(dev, "No failing region found, retry tuning\n"); |
901d16e4 JM |
486 | return -1; |
487 | } | |
6231d99d JM |
488 | |
489 | if (fail_window->length == ITAPDLY_LENGTH) { | |
901d16e4 | 490 | /* Retry tuning */ |
cf6444ba | 491 | dev_dbg(dev, "No passing itapdly, retry tuning\n"); |
901d16e4 | 492 | return -1; |
6231d99d JM |
493 | } |
494 | ||
495 | first_fail_start = fail_window->start; | |
496 | last_fail_end = fail_window[num_fails - 1].end; | |
497 | ||
498 | for (i = 0; i < num_fails; i++) { | |
499 | start_fail = fail_window[i].start; | |
500 | end_fail = fail_window[i].end; | |
501 | pass_length = start_fail - (prev_fail_end + 1); | |
502 | ||
503 | if (pass_length > pass_window.length) { | |
504 | pass_window.start = prev_fail_end + 1; | |
505 | pass_window.length = pass_length; | |
506 | } | |
507 | prev_fail_end = end_fail; | |
508 | } | |
509 | ||
510 | if (!circular_buffer) | |
511 | pass_length = ITAPDLY_LAST_INDEX - last_fail_end; | |
512 | else | |
513 | pass_length = ITAPDLY_LAST_INDEX - last_fail_end + first_fail_start; | |
514 | ||
515 | if (pass_length > pass_window.length) { | |
516 | pass_window.start = last_fail_end + 1; | |
517 | pass_window.length = pass_length; | |
518 | } | |
519 | ||
520 | if (!circular_buffer) | |
521 | itap = pass_window.start + (pass_window.length >> 1); | |
522 | else | |
523 | itap = (pass_window.start + (pass_window.length >> 1)) % ITAPDLY_LENGTH; | |
524 | ||
525 | return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap; | |
526 | } | |
527 | ||
901d16e4 JM |
528 | static int sdhci_am654_do_tuning(struct sdhci_host *host, |
529 | u32 opcode) | |
13ebeae6 FA |
530 | { |
531 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
532 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
387c1bf7 | 533 | unsigned char timing = host->mmc->ios.timing; |
6231d99d | 534 | struct window fail_window[ITAPDLY_LENGTH]; |
cf6444ba | 535 | struct device *dev = mmc_dev(host->mmc); |
6231d99d JM |
536 | u8 curr_pass, itap; |
537 | u8 fail_index = 0; | |
538 | u8 prev_pass = 1; | |
539 | ||
540 | memset(fail_window, 0, sizeof(fail_window)); | |
13ebeae6 FA |
541 | |
542 | /* Enable ITAPDLY */ | |
387c1bf7 | 543 | sdhci_am654->itap_del_ena[timing] = 0x1; |
13ebeae6 | 544 | |
6231d99d | 545 | for (itap = 0; itap < ITAPDLY_LENGTH; itap++) { |
387c1bf7 | 546 | sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]); |
13ebeae6 | 547 | |
6231d99d | 548 | curr_pass = !mmc_send_tuning(host->mmc, opcode, NULL); |
13ebeae6 | 549 | |
6231d99d JM |
550 | if (!curr_pass && prev_pass) |
551 | fail_window[fail_index].start = itap; | |
13ebeae6 | 552 | |
6231d99d JM |
553 | if (!curr_pass) { |
554 | fail_window[fail_index].end = itap; | |
555 | fail_window[fail_index].length++; | |
cf6444ba | 556 | dev_dbg(dev, "Failed itapdly=%d\n", itap); |
6231d99d JM |
557 | } |
558 | ||
559 | if (curr_pass && !prev_pass) | |
560 | fail_index++; | |
561 | ||
562 | prev_pass = curr_pass; | |
13ebeae6 | 563 | } |
6231d99d JM |
564 | |
565 | if (fail_window[fail_index].length != 0) | |
566 | fail_index++; | |
567 | ||
901d16e4 JM |
568 | return sdhci_am654_calculate_itap(host, fail_window, fail_index, |
569 | sdhci_am654->dll_enable); | |
570 | } | |
6231d99d | 571 | |
901d16e4 JM |
572 | static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, |
573 | u32 opcode) | |
574 | { | |
575 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
576 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
577 | unsigned char timing = host->mmc->ios.timing; | |
cf6444ba | 578 | struct device *dev = mmc_dev(host->mmc); |
901d16e4 | 579 | int itapdly; |
13ebeae6 | 580 | |
901d16e4 JM |
581 | do { |
582 | itapdly = sdhci_am654_do_tuning(host, opcode); | |
583 | if (itapdly >= 0) | |
584 | break; | |
585 | } while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX); | |
586 | ||
cf6444ba JM |
587 | if (itapdly < 0) { |
588 | dev_err(dev, "Failed to find itapdly, fail tuning\n"); | |
901d16e4 | 589 | return -1; |
cf6444ba | 590 | } |
901d16e4 | 591 | |
cf6444ba | 592 | dev_dbg(dev, "Passed tuning, final itapdly=%d\n", itapdly); |
901d16e4 | 593 | sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]); |
d3182932 | 594 | /* Save ITAPDLY */ |
901d16e4 | 595 | sdhci_am654->itap_del_sel[timing] = itapdly; |
d3182932 | 596 | |
13ebeae6 FA |
597 | return 0; |
598 | } | |
599 | ||
24922c1a | 600 | static const struct sdhci_ops sdhci_am654_ops = { |
13ebeae6 | 601 | .platform_execute_tuning = sdhci_am654_platform_execute_tuning, |
41fd4cae FA |
602 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
603 | .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, | |
604 | .set_uhs_signaling = sdhci_set_uhs_signaling, | |
605 | .set_bus_width = sdhci_set_bus_width, | |
9d8acdd3 | 606 | .set_power = sdhci_set_power_and_bus_voltage, |
41fd4cae | 607 | .set_clock = sdhci_am654_set_clock, |
e374e875 | 608 | .write_b = sdhci_am654_write_b, |
27f4e1e9 | 609 | .irq = sdhci_am654_cqhci_irq, |
162503fd | 610 | .reset = sdhci_and_cqhci_reset, |
41fd4cae FA |
611 | }; |
612 | ||
613 | static const struct sdhci_pltfm_data sdhci_am654_pdata = { | |
614 | .ops = &sdhci_am654_ops, | |
4d627c88 | 615 | .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, |
41fd4cae FA |
616 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, |
617 | }; | |
618 | ||
09db9943 | 619 | static const struct sdhci_am654_driver_data sdhci_am654_sr1_drvdata = { |
99909b55 | 620 | .pdata = &sdhci_am654_pdata, |
23514731 FA |
621 | .flags = IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT | DLL_PRESENT | |
622 | DLL_CALIB, | |
99909b55 FA |
623 | }; |
624 | ||
09db9943 FA |
625 | static const struct sdhci_am654_driver_data sdhci_am654_drvdata = { |
626 | .pdata = &sdhci_am654_pdata, | |
627 | .flags = IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT | DLL_PRESENT, | |
628 | }; | |
629 | ||
24922c1a | 630 | static const struct sdhci_ops sdhci_j721e_8bit_ops = { |
13ebeae6 | 631 | .platform_execute_tuning = sdhci_am654_platform_execute_tuning, |
99909b55 FA |
632 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
633 | .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, | |
634 | .set_uhs_signaling = sdhci_set_uhs_signaling, | |
635 | .set_bus_width = sdhci_set_bus_width, | |
9d8acdd3 | 636 | .set_power = sdhci_set_power_and_bus_voltage, |
99909b55 FA |
637 | .set_clock = sdhci_am654_set_clock, |
638 | .write_b = sdhci_am654_write_b, | |
f545702b | 639 | .irq = sdhci_am654_cqhci_irq, |
162503fd | 640 | .reset = sdhci_and_cqhci_reset, |
99909b55 FA |
641 | }; |
642 | ||
643 | static const struct sdhci_pltfm_data sdhci_j721e_8bit_pdata = { | |
644 | .ops = &sdhci_j721e_8bit_ops, | |
4d627c88 | 645 | .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, |
99909b55 FA |
646 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, |
647 | }; | |
648 | ||
649 | static const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = { | |
650 | .pdata = &sdhci_j721e_8bit_pdata, | |
23514731 | 651 | .flags = DLL_PRESENT | DLL_CALIB, |
1accbced FA |
652 | }; |
653 | ||
24922c1a | 654 | static const struct sdhci_ops sdhci_j721e_4bit_ops = { |
13ebeae6 | 655 | .platform_execute_tuning = sdhci_am654_platform_execute_tuning, |
1accbced FA |
656 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
657 | .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, | |
658 | .set_uhs_signaling = sdhci_set_uhs_signaling, | |
659 | .set_bus_width = sdhci_set_bus_width, | |
9d8acdd3 | 660 | .set_power = sdhci_set_power_and_bus_voltage, |
1accbced FA |
661 | .set_clock = sdhci_j721e_4bit_set_clock, |
662 | .write_b = sdhci_am654_write_b, | |
f545702b | 663 | .irq = sdhci_am654_cqhci_irq, |
c7666240 | 664 | .reset = sdhci_am654_reset, |
1accbced FA |
665 | }; |
666 | ||
667 | static const struct sdhci_pltfm_data sdhci_j721e_4bit_pdata = { | |
668 | .ops = &sdhci_j721e_4bit_ops, | |
4d627c88 | 669 | .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, |
1accbced | 670 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, |
99909b55 FA |
671 | }; |
672 | ||
1accbced FA |
673 | static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = { |
674 | .pdata = &sdhci_j721e_4bit_pdata, | |
675 | .flags = IOMUX_PRESENT, | |
676 | }; | |
f545702b | 677 | |
71c9475b JM |
678 | static const struct sdhci_am654_driver_data sdhci_am62_4bit_drvdata = { |
679 | .pdata = &sdhci_j721e_4bit_pdata, | |
680 | .flags = IOMUX_PRESENT, | |
681 | .quirks = SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA, | |
682 | }; | |
683 | ||
09db9943 FA |
684 | static const struct soc_device_attribute sdhci_am654_devices[] = { |
685 | { .family = "AM65X", | |
686 | .revision = "SR1.0", | |
687 | .data = &sdhci_am654_sr1_drvdata | |
688 | }, | |
689 | {/* sentinel */} | |
690 | }; | |
691 | ||
f545702b FA |
692 | static void sdhci_am654_dumpregs(struct mmc_host *mmc) |
693 | { | |
694 | sdhci_dumpregs(mmc_priv(mmc)); | |
695 | } | |
696 | ||
697 | static const struct cqhci_host_ops sdhci_am654_cqhci_ops = { | |
698 | .enable = sdhci_cqe_enable, | |
699 | .disable = sdhci_cqe_disable, | |
700 | .dumpregs = sdhci_am654_dumpregs, | |
701 | }; | |
702 | ||
703 | static int sdhci_am654_cqe_add_host(struct sdhci_host *host) | |
704 | { | |
705 | struct cqhci_host *cq_host; | |
f545702b | 706 | |
bac53336 | 707 | cq_host = devm_kzalloc(mmc_dev(host->mmc), sizeof(struct cqhci_host), |
f545702b FA |
708 | GFP_KERNEL); |
709 | if (!cq_host) | |
710 | return -ENOMEM; | |
711 | ||
712 | cq_host->mmio = host->ioaddr + SDHCI_AM654_CQE_BASE_ADDR; | |
713 | cq_host->quirks |= CQHCI_QUIRK_SHORT_TXFR_DESC_SZ; | |
714 | cq_host->caps |= CQHCI_TASK_DESC_SZ_128; | |
715 | cq_host->ops = &sdhci_am654_cqhci_ops; | |
716 | ||
717 | host->mmc->caps2 |= MMC_CAP2_CQE; | |
718 | ||
c0470f43 | 719 | return cqhci_init(cq_host, host->mmc, 1); |
f545702b FA |
720 | } |
721 | ||
8ee5fc0e FA |
722 | static int sdhci_am654_get_otap_delay(struct sdhci_host *host, |
723 | struct sdhci_am654_data *sdhci_am654) | |
724 | { | |
725 | struct device *dev = mmc_dev(host->mmc); | |
726 | int i; | |
727 | int ret; | |
728 | ||
71956d0c | 729 | for (i = MMC_TIMING_LEGACY; i <= MMC_TIMING_MMC_HS400; i++) { |
8ee5fc0e | 730 | |
a0a62497 | 731 | ret = device_property_read_u32(dev, td[i].otap_binding, |
8ee5fc0e FA |
732 | &sdhci_am654->otap_del_sel[i]); |
733 | if (ret) { | |
5cb2f928 VR |
734 | if (i == MMC_TIMING_LEGACY) { |
735 | dev_err(dev, "Couldn't find mandatory ti,otap-del-sel-legacy\n"); | |
736 | return ret; | |
737 | } | |
8ee5fc0e | 738 | dev_dbg(dev, "Couldn't find %s\n", |
a0a62497 | 739 | td[i].otap_binding); |
8ee5fc0e FA |
740 | /* |
741 | * Remove the corresponding capability | |
742 | * if an otap-del-sel value is not found | |
743 | */ | |
744 | if (i <= MMC_TIMING_MMC_DDR52) | |
745 | host->mmc->caps &= ~td[i].capability; | |
746 | else | |
747 | host->mmc->caps2 &= ~td[i].capability; | |
748 | } | |
a0a62497 | 749 | |
387c1bf7 JM |
750 | if (td[i].itap_binding) { |
751 | ret = device_property_read_u32(dev, td[i].itap_binding, | |
752 | &sdhci_am654->itap_del_sel[i]); | |
753 | if (!ret) | |
754 | sdhci_am654->itap_del_ena[i] = 0x1; | |
755 | } | |
8ee5fc0e FA |
756 | } |
757 | ||
758 | return 0; | |
759 | } | |
760 | ||
41fd4cae FA |
761 | static int sdhci_am654_init(struct sdhci_host *host) |
762 | { | |
763 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
764 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
765 | u32 ctl_cfg_2 = 0; | |
766 | u32 mask; | |
767 | u32 val; | |
768 | int ret; | |
769 | ||
770 | /* Reset OTAP to default value */ | |
771 | mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; | |
8023cf26 | 772 | regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, 0x0); |
41fd4cae | 773 | |
23514731 | 774 | if (sdhci_am654->flags & DLL_CALIB) { |
1accbced FA |
775 | regmap_read(sdhci_am654->base, PHY_STAT1, &val); |
776 | if (~val & CALDONE_MASK) { | |
777 | /* Calibrate IO lines */ | |
778 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, | |
779 | PDB_MASK, PDB_MASK); | |
780 | ret = regmap_read_poll_timeout(sdhci_am654->base, | |
781 | PHY_STAT1, val, | |
782 | val & CALDONE_MASK, | |
783 | 1, 20); | |
784 | if (ret) | |
785 | return ret; | |
786 | } | |
41fd4cae FA |
787 | } |
788 | ||
789 | /* Enable pins by setting IO mux to 0 */ | |
99909b55 FA |
790 | if (sdhci_am654->flags & IOMUX_PRESENT) |
791 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, | |
792 | IOMUX_ENABLE_MASK, 0); | |
41fd4cae FA |
793 | |
794 | /* Set slot type based on SD or eMMC */ | |
795 | if (host->mmc->caps & MMC_CAP_NONREMOVABLE) | |
796 | ctl_cfg_2 = SLOTTYPE_EMBEDDED; | |
797 | ||
8023cf26 FA |
798 | regmap_update_bits(sdhci_am654->base, CTL_CFG_2, SLOTTYPE_MASK, |
799 | ctl_cfg_2); | |
41fd4cae | 800 | |
764384d0 FA |
801 | /* Enable tuning for SDR50 */ |
802 | regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK, | |
803 | TUNINGFORSDR50_MASK); | |
804 | ||
901d16e4 JM |
805 | /* Use to re-execute tuning */ |
806 | sdhci_am654->tuning_loop = 0; | |
807 | ||
f545702b FA |
808 | ret = sdhci_setup_host(host); |
809 | if (ret) | |
810 | return ret; | |
811 | ||
812 | ret = sdhci_am654_cqe_add_host(host); | |
813 | if (ret) | |
814 | goto err_cleanup_host; | |
815 | ||
8ee5fc0e FA |
816 | ret = sdhci_am654_get_otap_delay(host, sdhci_am654); |
817 | if (ret) | |
818 | goto err_cleanup_host; | |
819 | ||
f545702b FA |
820 | ret = __sdhci_add_host(host); |
821 | if (ret) | |
822 | goto err_cleanup_host; | |
823 | ||
824 | return 0; | |
825 | ||
826 | err_cleanup_host: | |
827 | sdhci_cleanup_host(host); | |
828 | return ret; | |
41fd4cae FA |
829 | } |
830 | ||
831 | static int sdhci_am654_get_of_property(struct platform_device *pdev, | |
832 | struct sdhci_am654_data *sdhci_am654) | |
833 | { | |
834 | struct device *dev = &pdev->dev; | |
835 | int drv_strength; | |
836 | int ret; | |
837 | ||
1accbced FA |
838 | if (sdhci_am654->flags & DLL_PRESENT) { |
839 | ret = device_property_read_u32(dev, "ti,trm-icp", | |
840 | &sdhci_am654->trm_icp); | |
841 | if (ret) | |
842 | return ret; | |
843 | ||
844 | ret = device_property_read_u32(dev, "ti,driver-strength-ohm", | |
845 | &drv_strength); | |
846 | if (ret) | |
847 | return ret; | |
41fd4cae | 848 | |
1accbced FA |
849 | switch (drv_strength) { |
850 | case 50: | |
851 | sdhci_am654->drv_strength = DRIVER_STRENGTH_50_OHM; | |
852 | break; | |
853 | case 33: | |
854 | sdhci_am654->drv_strength = DRIVER_STRENGTH_33_OHM; | |
855 | break; | |
856 | case 66: | |
857 | sdhci_am654->drv_strength = DRIVER_STRENGTH_66_OHM; | |
858 | break; | |
859 | case 100: | |
860 | sdhci_am654->drv_strength = DRIVER_STRENGTH_100_OHM; | |
861 | break; | |
862 | case 40: | |
863 | sdhci_am654->drv_strength = DRIVER_STRENGTH_40_OHM; | |
864 | break; | |
865 | default: | |
866 | dev_err(dev, "Invalid driver strength\n"); | |
867 | return -EINVAL; | |
868 | } | |
41fd4cae FA |
869 | } |
870 | ||
99909b55 | 871 | device_property_read_u32(dev, "ti,strobe-sel", &sdhci_am654->strb_sel); |
61d9c4aa FA |
872 | device_property_read_u32(dev, "ti,clkbuf-sel", |
873 | &sdhci_am654->clkbuf_sel); | |
99909b55 | 874 | |
c7666240 VR |
875 | if (device_property_read_bool(dev, "ti,fails-without-test-cd")) |
876 | sdhci_am654->quirks |= SDHCI_AM654_QUIRK_FORCE_CDTEST; | |
877 | ||
41fd4cae FA |
878 | sdhci_get_of_property(pdev); |
879 | ||
880 | return 0; | |
881 | } | |
882 | ||
99909b55 FA |
883 | static const struct of_device_id sdhci_am654_of_match[] = { |
884 | { | |
885 | .compatible = "ti,am654-sdhci-5.1", | |
886 | .data = &sdhci_am654_drvdata, | |
887 | }, | |
888 | { | |
889 | .compatible = "ti,j721e-sdhci-8bit", | |
890 | .data = &sdhci_j721e_8bit_drvdata, | |
891 | }, | |
1accbced FA |
892 | { |
893 | .compatible = "ti,j721e-sdhci-4bit", | |
894 | .data = &sdhci_j721e_4bit_drvdata, | |
895 | }, | |
754b7f2f FA |
896 | { |
897 | .compatible = "ti,am64-sdhci-8bit", | |
3b7340f1 | 898 | .data = &sdhci_j721e_8bit_drvdata, |
754b7f2f FA |
899 | }, |
900 | { | |
901 | .compatible = "ti,am64-sdhci-4bit", | |
3b7340f1 | 902 | .data = &sdhci_j721e_4bit_drvdata, |
754b7f2f | 903 | }, |
02538e45 AG |
904 | { |
905 | .compatible = "ti,am62-sdhci", | |
71c9475b | 906 | .data = &sdhci_am62_4bit_drvdata, |
02538e45 | 907 | }, |
99909b55 FA |
908 | { /* sentinel */ } |
909 | }; | |
1e23400f | 910 | MODULE_DEVICE_TABLE(of, sdhci_am654_of_match); |
99909b55 | 911 | |
41fd4cae FA |
912 | static int sdhci_am654_probe(struct platform_device *pdev) |
913 | { | |
99909b55 | 914 | const struct sdhci_am654_driver_data *drvdata; |
09db9943 | 915 | const struct soc_device_attribute *soc; |
41fd4cae FA |
916 | struct sdhci_pltfm_host *pltfm_host; |
917 | struct sdhci_am654_data *sdhci_am654; | |
99909b55 | 918 | const struct of_device_id *match; |
41fd4cae | 919 | struct sdhci_host *host; |
41fd4cae FA |
920 | struct clk *clk_xin; |
921 | struct device *dev = &pdev->dev; | |
922 | void __iomem *base; | |
923 | int ret; | |
924 | ||
99909b55 FA |
925 | match = of_match_node(sdhci_am654_of_match, pdev->dev.of_node); |
926 | drvdata = match->data; | |
09db9943 FA |
927 | |
928 | /* Update drvdata based on SoC revision */ | |
929 | soc = soc_device_match(sdhci_am654_devices); | |
930 | if (soc && soc->data) | |
931 | drvdata = soc->data; | |
932 | ||
99909b55 | 933 | host = sdhci_pltfm_init(pdev, drvdata->pdata, sizeof(*sdhci_am654)); |
41fd4cae FA |
934 | if (IS_ERR(host)) |
935 | return PTR_ERR(host); | |
936 | ||
937 | pltfm_host = sdhci_priv(host); | |
938 | sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
99909b55 | 939 | sdhci_am654->flags = drvdata->flags; |
71c9475b | 940 | sdhci_am654->quirks = drvdata->quirks; |
41fd4cae FA |
941 | |
942 | clk_xin = devm_clk_get(dev, "clk_xin"); | |
943 | if (IS_ERR(clk_xin)) { | |
944 | dev_err(dev, "clk_xin clock not found.\n"); | |
945 | ret = PTR_ERR(clk_xin); | |
946 | goto err_pltfm_free; | |
947 | } | |
948 | ||
949 | pltfm_host->clk = clk_xin; | |
950 | ||
4942ae0e | 951 | base = devm_platform_ioremap_resource(pdev, 1); |
41fd4cae FA |
952 | if (IS_ERR(base)) { |
953 | ret = PTR_ERR(base); | |
9d2e77ff | 954 | goto err_pltfm_free; |
41fd4cae FA |
955 | } |
956 | ||
957 | sdhci_am654->base = devm_regmap_init_mmio(dev, base, | |
958 | &sdhci_am654_regmap_config); | |
959 | if (IS_ERR(sdhci_am654->base)) { | |
960 | dev_err(dev, "Failed to initialize regmap\n"); | |
961 | ret = PTR_ERR(sdhci_am654->base); | |
9d2e77ff | 962 | goto err_pltfm_free; |
41fd4cae FA |
963 | } |
964 | ||
965 | ret = sdhci_am654_get_of_property(pdev, sdhci_am654); | |
966 | if (ret) | |
9d2e77ff | 967 | goto err_pltfm_free; |
41fd4cae FA |
968 | |
969 | ret = mmc_of_parse(host->mmc); | |
970 | if (ret) { | |
654993b3 | 971 | dev_err_probe(dev, ret, "parsing dt failed\n"); |
9d2e77ff | 972 | goto err_pltfm_free; |
41fd4cae FA |
973 | } |
974 | ||
71c9475b | 975 | host->mmc_host_ops.start_signal_voltage_switch = sdhci_am654_start_signal_voltage_switch; |
de31f6ab FA |
976 | host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning; |
977 | ||
9d2e77ff AG |
978 | pm_runtime_get_noresume(dev); |
979 | ret = pm_runtime_set_active(dev); | |
980 | if (ret) | |
981 | goto pm_put; | |
982 | pm_runtime_enable(dev); | |
983 | ret = clk_prepare_enable(pltfm_host->clk); | |
984 | if (ret) | |
985 | goto pm_disable; | |
986 | ||
41fd4cae FA |
987 | ret = sdhci_am654_init(host); |
988 | if (ret) | |
9d2e77ff | 989 | goto clk_disable; |
41fd4cae | 990 | |
9d2e77ff AG |
991 | /* Setting up autosuspend */ |
992 | pm_runtime_set_autosuspend_delay(dev, SDHCI_AM654_AUTOSUSPEND_DELAY); | |
993 | pm_runtime_use_autosuspend(dev); | |
994 | pm_runtime_mark_last_busy(dev); | |
995 | pm_runtime_put_autosuspend(dev); | |
41fd4cae FA |
996 | return 0; |
997 | ||
9d2e77ff AG |
998 | clk_disable: |
999 | clk_disable_unprepare(pltfm_host->clk); | |
1000 | pm_disable: | |
41fd4cae | 1001 | pm_runtime_disable(dev); |
9d2e77ff AG |
1002 | pm_put: |
1003 | pm_runtime_put_noidle(dev); | |
41fd4cae FA |
1004 | err_pltfm_free: |
1005 | sdhci_pltfm_free(pdev); | |
1006 | return ret; | |
1007 | } | |
1008 | ||
de29ade4 | 1009 | static void sdhci_am654_remove(struct platform_device *pdev) |
41fd4cae FA |
1010 | { |
1011 | struct sdhci_host *host = platform_get_drvdata(pdev); | |
9d2e77ff | 1012 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
854034e2 | 1013 | struct device *dev = &pdev->dev; |
41fd4cae FA |
1014 | int ret; |
1015 | ||
854034e2 | 1016 | ret = pm_runtime_get_sync(dev); |
41fd4cae | 1017 | if (ret < 0) |
854034e2 | 1018 | dev_err(dev, "pm_runtime_get_sync() Failed\n"); |
41fd4cae | 1019 | |
9d2e77ff AG |
1020 | sdhci_remove_host(host, true); |
1021 | clk_disable_unprepare(pltfm_host->clk); | |
854034e2 YL |
1022 | pm_runtime_disable(dev); |
1023 | pm_runtime_put_noidle(dev); | |
41fd4cae | 1024 | sdhci_pltfm_free(pdev); |
9d2e77ff AG |
1025 | } |
1026 | ||
1027 | #ifdef CONFIG_PM | |
1028 | static int sdhci_am654_restore(struct sdhci_host *host) | |
1029 | { | |
1030 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
1031 | struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); | |
1032 | u32 ctl_cfg_2 = 0; | |
1033 | u32 val; | |
1034 | int ret; | |
1035 | ||
1036 | if (sdhci_am654->flags & DLL_CALIB) { | |
1037 | regmap_read(sdhci_am654->base, PHY_STAT1, &val); | |
1038 | if (~val & CALDONE_MASK) { | |
1039 | /* Calibrate IO lines */ | |
1040 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, | |
1041 | PDB_MASK, PDB_MASK); | |
1042 | ret = regmap_read_poll_timeout(sdhci_am654->base, | |
1043 | PHY_STAT1, val, | |
1044 | val & CALDONE_MASK, | |
1045 | 1, 20); | |
1046 | if (ret) | |
1047 | return ret; | |
1048 | } | |
1049 | } | |
1050 | ||
1051 | /* Enable pins by setting IO mux to 0 */ | |
1052 | if (sdhci_am654->flags & IOMUX_PRESENT) | |
1053 | regmap_update_bits(sdhci_am654->base, PHY_CTRL1, | |
1054 | IOMUX_ENABLE_MASK, 0); | |
41fd4cae | 1055 | |
9d2e77ff AG |
1056 | /* Set slot type based on SD or eMMC */ |
1057 | if (host->mmc->caps & MMC_CAP_NONREMOVABLE) | |
1058 | ctl_cfg_2 = SLOTTYPE_EMBEDDED; | |
1059 | ||
1060 | regmap_update_bits(sdhci_am654->base, CTL_CFG_2, SLOTTYPE_MASK, | |
1061 | ctl_cfg_2); | |
1062 | ||
1063 | regmap_read(sdhci_am654->base, CTL_CFG_3, &val); | |
1064 | if (~val & TUNINGFORSDR50_MASK) | |
1065 | /* Enable tuning for SDR50 */ | |
1066 | regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK, | |
1067 | TUNINGFORSDR50_MASK); | |
1068 | ||
1069 | return 0; | |
1070 | } | |
1071 | ||
1072 | static int sdhci_am654_runtime_suspend(struct device *dev) | |
1073 | { | |
1074 | struct sdhci_host *host = dev_get_drvdata(dev); | |
1075 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
1076 | int ret; | |
1077 | ||
1078 | if (host->tuning_mode != SDHCI_TUNING_MODE_3) | |
1079 | mmc_retune_needed(host->mmc); | |
1080 | ||
1081 | ret = cqhci_suspend(host->mmc); | |
1082 | if (ret) | |
1083 | return ret; | |
1084 | ||
1085 | ret = sdhci_runtime_suspend_host(host); | |
1086 | if (ret) | |
1087 | return ret; | |
1088 | ||
1089 | /* disable the clock */ | |
1090 | clk_disable_unprepare(pltfm_host->clk); | |
41fd4cae FA |
1091 | return 0; |
1092 | } | |
1093 | ||
9d2e77ff AG |
1094 | static int sdhci_am654_runtime_resume(struct device *dev) |
1095 | { | |
1096 | struct sdhci_host *host = dev_get_drvdata(dev); | |
1097 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
1098 | int ret; | |
1099 | ||
1100 | /* Enable the clock */ | |
1101 | ret = clk_prepare_enable(pltfm_host->clk); | |
1102 | if (ret) | |
1103 | return ret; | |
1104 | ||
1105 | ret = sdhci_am654_restore(host); | |
1106 | if (ret) | |
1107 | return ret; | |
1108 | ||
1109 | ret = sdhci_runtime_resume_host(host, 0); | |
1110 | if (ret) | |
1111 | return ret; | |
1112 | ||
1113 | ret = cqhci_resume(host->mmc); | |
1114 | if (ret) | |
1115 | return ret; | |
1116 | ||
1117 | return 0; | |
1118 | } | |
1119 | #endif | |
1120 | ||
1121 | static const struct dev_pm_ops sdhci_am654_dev_pm_ops = { | |
1122 | SET_RUNTIME_PM_OPS(sdhci_am654_runtime_suspend, | |
1123 | sdhci_am654_runtime_resume, NULL) | |
1124 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
1125 | pm_runtime_force_resume) | |
1126 | }; | |
1127 | ||
41fd4cae FA |
1128 | static struct platform_driver sdhci_am654_driver = { |
1129 | .driver = { | |
1130 | .name = "sdhci-am654", | |
d86472ae | 1131 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
9d2e77ff | 1132 | .pm = &sdhci_am654_dev_pm_ops, |
41fd4cae FA |
1133 | .of_match_table = sdhci_am654_of_match, |
1134 | }, | |
1135 | .probe = sdhci_am654_probe, | |
078e548a | 1136 | .remove = sdhci_am654_remove, |
41fd4cae FA |
1137 | }; |
1138 | ||
1139 | module_platform_driver(sdhci_am654_driver); | |
1140 | ||
1141 | MODULE_DESCRIPTION("Driver for SDHCI Controller on TI's AM654 devices"); | |
1142 | MODULE_AUTHOR("Faiz Abbas <faiz_abbas@ti.com>"); | |
1143 | MODULE_LICENSE("GPL"); |