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