Commit | Line | Data |
---|---|---|
6b1baefe | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7d326930 KVA |
2 | /** |
3 | * SDHCI Controller driver for TI's OMAP SoCs | |
4 | * | |
5 | * Copyright (C) 2017 Texas Instruments | |
6 | * Author: Kishon Vijay Abraham I <kishon@ti.com> | |
7d326930 KVA |
7 | */ |
8 | ||
9 | #include <linux/delay.h> | |
5da5e494 | 10 | #include <linux/mmc/mmc.h> |
7d326930 KVA |
11 | #include <linux/mmc/slot-gpio.h> |
12 | #include <linux/module.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/of_device.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/pm_runtime.h> | |
17 | #include <linux/regulator/consumer.h> | |
8d20b2ea | 18 | #include <linux/pinctrl/consumer.h> |
212f4f8a | 19 | #include <linux/sys_soc.h> |
961de0a8 | 20 | #include <linux/thermal.h> |
7d326930 KVA |
21 | |
22 | #include "sdhci-pltfm.h" | |
23 | ||
42b380b6 TL |
24 | /* |
25 | * Note that the register offsets used here are from omap_regs | |
26 | * base which is 0x100 for omap4 and later, and 0 for omap3 and | |
27 | * earlier. | |
28 | */ | |
29 | #define SDHCI_OMAP_SYSCONFIG 0x10 | |
53f9460e | 30 | |
42b380b6 | 31 | #define SDHCI_OMAP_CON 0x2c |
7d326930 KVA |
32 | #define CON_DW8 BIT(5) |
33 | #define CON_DMA_MASTER BIT(20) | |
27ceb7e0 | 34 | #define CON_DDR BIT(19) |
20ea26a1 KVA |
35 | #define CON_CLKEXTFREE BIT(16) |
36 | #define CON_PADEN BIT(15) | |
efde12b2 | 37 | #define CON_CTPL BIT(11) |
7d326930 KVA |
38 | #define CON_INIT BIT(1) |
39 | #define CON_OD BIT(0) | |
40 | ||
42b380b6 | 41 | #define SDHCI_OMAP_DLL 0x34 |
9fc2cd76 KVA |
42 | #define DLL_SWT BIT(20) |
43 | #define DLL_FORCE_SR_C_SHIFT 13 | |
44 | #define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT) | |
45 | #define DLL_FORCE_VALUE BIT(12) | |
46 | #define DLL_CALIB BIT(1) | |
47 | ||
42b380b6 | 48 | #define SDHCI_OMAP_CMD 0x10c |
7d326930 | 49 | |
42b380b6 | 50 | #define SDHCI_OMAP_PSTATE 0x124 |
20ea26a1 KVA |
51 | #define PSTATE_DLEV_DAT0 BIT(20) |
52 | #define PSTATE_DATI BIT(1) | |
53 | ||
42b380b6 | 54 | #define SDHCI_OMAP_HCTL 0x128 |
7d326930 KVA |
55 | #define HCTL_SDBP BIT(8) |
56 | #define HCTL_SDVS_SHIFT 9 | |
57 | #define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT) | |
58 | #define HCTL_SDVS_33 (0x7 << HCTL_SDVS_SHIFT) | |
59 | #define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT) | |
60 | #define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT) | |
61 | ||
42b380b6 | 62 | #define SDHCI_OMAP_SYSCTL 0x12c |
7d326930 KVA |
63 | #define SYSCTL_CEN BIT(2) |
64 | #define SYSCTL_CLKD_SHIFT 6 | |
65 | #define SYSCTL_CLKD_MASK 0x3ff | |
66 | ||
42b380b6 | 67 | #define SDHCI_OMAP_STAT 0x130 |
7d326930 | 68 | |
42b380b6 | 69 | #define SDHCI_OMAP_IE 0x134 |
7d326930 KVA |
70 | #define INT_CC_EN BIT(0) |
71 | ||
42b380b6 | 72 | #define SDHCI_OMAP_ISE 0x138 |
d806e334 | 73 | |
42b380b6 | 74 | #define SDHCI_OMAP_AC12 0x13c |
7d326930 | 75 | #define AC12_V1V8_SIGEN BIT(19) |
9fc2cd76 | 76 | #define AC12_SCLK_SEL BIT(23) |
7d326930 | 77 | |
42b380b6 | 78 | #define SDHCI_OMAP_CAPA 0x140 |
7d326930 KVA |
79 | #define CAPA_VS33 BIT(24) |
80 | #define CAPA_VS30 BIT(25) | |
81 | #define CAPA_VS18 BIT(26) | |
82 | ||
42b380b6 | 83 | #define SDHCI_OMAP_CAPA2 0x144 |
9fc2cd76 KVA |
84 | #define CAPA2_TSDR50 BIT(13) |
85 | ||
7d326930 KVA |
86 | #define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */ |
87 | ||
88 | #define SYSCTL_CLKD_MAX 0x3FF | |
89 | ||
90 | #define IOV_1V8 1800000 /* 180000 uV */ | |
91 | #define IOV_3V0 3000000 /* 300000 uV */ | |
92 | #define IOV_3V3 3300000 /* 330000 uV */ | |
93 | ||
9fc2cd76 KVA |
94 | #define MAX_PHASE_DELAY 0x7C |
95 | ||
8d20b2ea KVA |
96 | /* sdhci-omap controller flags */ |
97 | #define SDHCI_OMAP_REQUIRE_IODELAY BIT(0) | |
9e84a2e6 | 98 | #define SDHCI_OMAP_SPECIAL_RESET BIT(1) |
8d20b2ea | 99 | |
7d326930 | 100 | struct sdhci_omap_data { |
42b380b6 TL |
101 | int omap_offset; /* Offset for omap regs from base */ |
102 | u32 offset; /* Offset for SDHCI regs from base */ | |
8d20b2ea | 103 | u8 flags; |
7d326930 KVA |
104 | }; |
105 | ||
106 | struct sdhci_omap_host { | |
212f4f8a | 107 | char *version; |
7d326930 KVA |
108 | void __iomem *base; |
109 | struct device *dev; | |
110 | struct regulator *pbias; | |
111 | bool pbias_enabled; | |
112 | struct sdhci_host *host; | |
113 | u8 bus_mode; | |
114 | u8 power_mode; | |
8d20b2ea KVA |
115 | u8 timing; |
116 | u8 flags; | |
117 | ||
118 | struct pinctrl *pinctrl; | |
119 | struct pinctrl_state **pinctrl_state; | |
5b0d6210 | 120 | bool is_tuning; |
42b380b6 TL |
121 | |
122 | /* Offset for omap specific registers from base */ | |
123 | int omap_offset; | |
124 | ||
ee0f3092 FA |
125 | /* Omap specific context save */ |
126 | u32 con; | |
127 | u32 hctl; | |
128 | u32 sysctl; | |
129 | u32 capa; | |
d806e334 TL |
130 | u32 ie; |
131 | u32 ise; | |
7d326930 KVA |
132 | }; |
133 | ||
8d20b2ea KVA |
134 | static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host); |
135 | static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host); | |
136 | ||
7d326930 KVA |
137 | static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host, |
138 | unsigned int offset) | |
139 | { | |
42b380b6 | 140 | return readl(host->base + host->omap_offset + offset); |
7d326930 KVA |
141 | } |
142 | ||
143 | static inline void sdhci_omap_writel(struct sdhci_omap_host *host, | |
144 | unsigned int offset, u32 data) | |
145 | { | |
42b380b6 | 146 | writel(data, host->base + host->omap_offset + offset); |
7d326930 KVA |
147 | } |
148 | ||
149 | static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host, | |
150 | bool power_on, unsigned int iov) | |
151 | { | |
152 | int ret; | |
153 | struct device *dev = omap_host->dev; | |
154 | ||
155 | if (IS_ERR(omap_host->pbias)) | |
156 | return 0; | |
157 | ||
158 | if (power_on) { | |
159 | ret = regulator_set_voltage(omap_host->pbias, iov, iov); | |
160 | if (ret) { | |
161 | dev_err(dev, "pbias set voltage failed\n"); | |
162 | return ret; | |
163 | } | |
164 | ||
165 | if (omap_host->pbias_enabled) | |
166 | return 0; | |
167 | ||
168 | ret = regulator_enable(omap_host->pbias); | |
169 | if (ret) { | |
170 | dev_err(dev, "pbias reg enable fail\n"); | |
171 | return ret; | |
172 | } | |
173 | ||
174 | omap_host->pbias_enabled = true; | |
175 | } else { | |
176 | if (!omap_host->pbias_enabled) | |
177 | return 0; | |
178 | ||
179 | ret = regulator_disable(omap_host->pbias); | |
180 | if (ret) { | |
181 | dev_err(dev, "pbias reg disable fail\n"); | |
182 | return ret; | |
183 | } | |
184 | omap_host->pbias_enabled = false; | |
185 | } | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host, | |
de5ccd2a | 191 | unsigned int iov_pbias) |
7d326930 KVA |
192 | { |
193 | int ret; | |
194 | struct sdhci_host *host = omap_host->host; | |
195 | struct mmc_host *mmc = host->mmc; | |
196 | ||
197 | ret = sdhci_omap_set_pbias(omap_host, false, 0); | |
198 | if (ret) | |
199 | return ret; | |
200 | ||
201 | if (!IS_ERR(mmc->supply.vqmmc)) { | |
de5ccd2a TL |
202 | /* Pick the right voltage to allow 3.0V for 3.3V nominal PBIAS */ |
203 | ret = mmc_regulator_set_vqmmc(mmc, &mmc->ios); | |
204 | if (ret < 0) { | |
7d326930 KVA |
205 | dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n"); |
206 | return ret; | |
207 | } | |
208 | } | |
209 | ||
de5ccd2a | 210 | ret = sdhci_omap_set_pbias(omap_host, true, iov_pbias); |
7d326930 KVA |
211 | if (ret) |
212 | return ret; | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host, | |
218 | unsigned char signal_voltage) | |
219 | { | |
de5ccd2a | 220 | u32 reg, capa; |
7d326930 KVA |
221 | ktime_t timeout; |
222 | ||
223 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL); | |
224 | reg &= ~HCTL_SDVS_MASK; | |
225 | ||
de5ccd2a TL |
226 | switch (signal_voltage) { |
227 | case MMC_SIGNAL_VOLTAGE_330: | |
228 | capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); | |
229 | if (capa & CAPA_VS33) | |
230 | reg |= HCTL_SDVS_33; | |
231 | else if (capa & CAPA_VS30) | |
232 | reg |= HCTL_SDVS_30; | |
233 | else | |
234 | dev_warn(omap_host->dev, "misconfigured CAPA: %08x\n", | |
235 | capa); | |
236 | break; | |
237 | case MMC_SIGNAL_VOLTAGE_180: | |
238 | default: | |
7d326930 | 239 | reg |= HCTL_SDVS_18; |
de5ccd2a TL |
240 | break; |
241 | } | |
7d326930 KVA |
242 | |
243 | sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg); | |
244 | ||
245 | reg |= HCTL_SDBP; | |
246 | sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg); | |
247 | ||
248 | /* wait 1ms */ | |
249 | timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT); | |
9f0ea0bd AH |
250 | while (1) { |
251 | bool timedout = ktime_after(ktime_get(), timeout); | |
252 | ||
253 | if (sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP) | |
254 | break; | |
255 | if (WARN_ON(timedout)) | |
7d326930 KVA |
256 | return; |
257 | usleep_range(5, 10); | |
258 | } | |
259 | } | |
260 | ||
efde12b2 KVA |
261 | static void sdhci_omap_enable_sdio_irq(struct mmc_host *mmc, int enable) |
262 | { | |
263 | struct sdhci_host *host = mmc_priv(mmc); | |
264 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
265 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
266 | u32 reg; | |
267 | ||
268 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
269 | if (enable) | |
270 | reg |= (CON_CTPL | CON_CLKEXTFREE); | |
271 | else | |
272 | reg &= ~(CON_CTPL | CON_CLKEXTFREE); | |
273 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
274 | ||
275 | sdhci_enable_sdio_irq(mmc, enable); | |
276 | } | |
277 | ||
9fc2cd76 KVA |
278 | static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host, |
279 | int count) | |
280 | { | |
281 | int i; | |
282 | u32 reg; | |
283 | ||
284 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL); | |
285 | reg |= DLL_FORCE_VALUE; | |
286 | reg &= ~DLL_FORCE_SR_C_MASK; | |
287 | reg |= (count << DLL_FORCE_SR_C_SHIFT); | |
288 | sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg); | |
289 | ||
290 | reg |= DLL_CALIB; | |
291 | sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg); | |
292 | for (i = 0; i < 1000; i++) { | |
293 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL); | |
294 | if (reg & DLL_CALIB) | |
295 | break; | |
296 | } | |
297 | reg &= ~DLL_CALIB; | |
298 | sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg); | |
299 | } | |
300 | ||
301 | static void sdhci_omap_disable_tuning(struct sdhci_omap_host *omap_host) | |
302 | { | |
303 | u32 reg; | |
304 | ||
305 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); | |
306 | reg &= ~AC12_SCLK_SEL; | |
307 | sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg); | |
308 | ||
309 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL); | |
310 | reg &= ~(DLL_FORCE_VALUE | DLL_SWT); | |
311 | sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg); | |
312 | } | |
313 | ||
314 | static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) | |
315 | { | |
316 | struct sdhci_host *host = mmc_priv(mmc); | |
317 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
318 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
961de0a8 | 319 | struct thermal_zone_device *thermal_dev; |
9fc2cd76 KVA |
320 | struct device *dev = omap_host->dev; |
321 | struct mmc_ios *ios = &mmc->ios; | |
322 | u32 start_window = 0, max_window = 0; | |
961de0a8 | 323 | bool single_point_failure = false; |
db2039fc | 324 | bool dcrc_was_enabled = false; |
9fc2cd76 KVA |
325 | u8 cur_match, prev_match = 0; |
326 | u32 length = 0, max_len = 0; | |
327 | u32 phase_delay = 0; | |
961de0a8 | 328 | int temperature; |
9fc2cd76 KVA |
329 | int ret = 0; |
330 | u32 reg; | |
961de0a8 | 331 | int i; |
9fc2cd76 | 332 | |
9fc2cd76 KVA |
333 | /* clock tuning is not needed for upto 52MHz */ |
334 | if (ios->clock <= 52000000) | |
335 | return 0; | |
336 | ||
337 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA2); | |
338 | if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50)) | |
339 | return 0; | |
340 | ||
961de0a8 FA |
341 | thermal_dev = thermal_zone_get_zone_by_name("cpu_thermal"); |
342 | if (IS_ERR(thermal_dev)) { | |
343 | dev_err(dev, "Unable to get thermal zone for tuning\n"); | |
344 | return PTR_ERR(thermal_dev); | |
345 | } | |
346 | ||
347 | ret = thermal_zone_get_temp(thermal_dev, &temperature); | |
348 | if (ret) | |
349 | return ret; | |
350 | ||
9fc2cd76 KVA |
351 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL); |
352 | reg |= DLL_SWT; | |
353 | sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg); | |
354 | ||
7d33c358 KVA |
355 | /* |
356 | * OMAP5/DRA74X/DRA72x Errata i802: | |
357 | * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur | |
358 | * during the tuning procedure. So disable it during the | |
359 | * tuning procedure. | |
360 | */ | |
db2039fc FA |
361 | if (host->ier & SDHCI_INT_DATA_CRC) { |
362 | host->ier &= ~SDHCI_INT_DATA_CRC; | |
363 | dcrc_was_enabled = true; | |
364 | } | |
7d33c358 | 365 | |
5b0d6210 FA |
366 | omap_host->is_tuning = true; |
367 | ||
961de0a8 FA |
368 | /* |
369 | * Stage 1: Search for a maximum pass window ignoring any | |
370 | * any single point failures. If the tuning value ends up | |
371 | * near it, move away from it in stage 2 below | |
372 | */ | |
9fc2cd76 KVA |
373 | while (phase_delay <= MAX_PHASE_DELAY) { |
374 | sdhci_omap_set_dll(omap_host, phase_delay); | |
375 | ||
376 | cur_match = !mmc_send_tuning(mmc, opcode, NULL); | |
377 | if (cur_match) { | |
378 | if (prev_match) { | |
379 | length++; | |
961de0a8 FA |
380 | } else if (single_point_failure) { |
381 | /* ignore single point failure */ | |
382 | length++; | |
9fc2cd76 KVA |
383 | } else { |
384 | start_window = phase_delay; | |
385 | length = 1; | |
386 | } | |
961de0a8 FA |
387 | } else { |
388 | single_point_failure = prev_match; | |
9fc2cd76 KVA |
389 | } |
390 | ||
391 | if (length > max_len) { | |
392 | max_window = start_window; | |
393 | max_len = length; | |
394 | } | |
395 | ||
396 | prev_match = cur_match; | |
397 | phase_delay += 4; | |
398 | } | |
399 | ||
400 | if (!max_len) { | |
401 | dev_err(dev, "Unable to find match\n"); | |
402 | ret = -EIO; | |
403 | goto tuning_error; | |
404 | } | |
405 | ||
961de0a8 FA |
406 | /* |
407 | * Assign tuning value as a ratio of maximum pass window based | |
408 | * on temperature | |
409 | */ | |
410 | if (temperature < -20000) | |
feb40824 | 411 | phase_delay = min(max_window + 4 * (max_len - 1) - 24, |
961de0a8 FA |
412 | max_window + |
413 | DIV_ROUND_UP(13 * max_len, 16) * 4); | |
414 | else if (temperature < 20000) | |
415 | phase_delay = max_window + DIV_ROUND_UP(9 * max_len, 16) * 4; | |
416 | else if (temperature < 40000) | |
417 | phase_delay = max_window + DIV_ROUND_UP(8 * max_len, 16) * 4; | |
418 | else if (temperature < 70000) | |
419 | phase_delay = max_window + DIV_ROUND_UP(7 * max_len, 16) * 4; | |
420 | else if (temperature < 90000) | |
421 | phase_delay = max_window + DIV_ROUND_UP(5 * max_len, 16) * 4; | |
422 | else if (temperature < 120000) | |
423 | phase_delay = max_window + DIV_ROUND_UP(4 * max_len, 16) * 4; | |
424 | else | |
425 | phase_delay = max_window + DIV_ROUND_UP(3 * max_len, 16) * 4; | |
426 | ||
427 | /* | |
428 | * Stage 2: Search for a single point failure near the chosen tuning | |
429 | * value in two steps. First in the +3 to +10 range and then in the | |
430 | * +2 to -10 range. If found, move away from it in the appropriate | |
431 | * direction by the appropriate amount depending on the temperature. | |
432 | */ | |
433 | for (i = 3; i <= 10; i++) { | |
434 | sdhci_omap_set_dll(omap_host, phase_delay + i); | |
435 | ||
436 | if (mmc_send_tuning(mmc, opcode, NULL)) { | |
437 | if (temperature < 10000) | |
438 | phase_delay += i + 6; | |
439 | else if (temperature < 20000) | |
440 | phase_delay += i - 12; | |
441 | else if (temperature < 70000) | |
442 | phase_delay += i - 8; | |
443 | else | |
444 | phase_delay += i - 6; | |
445 | ||
446 | goto single_failure_found; | |
447 | } | |
448 | } | |
449 | ||
450 | for (i = 2; i >= -10; i--) { | |
451 | sdhci_omap_set_dll(omap_host, phase_delay + i); | |
452 | ||
453 | if (mmc_send_tuning(mmc, opcode, NULL)) { | |
454 | if (temperature < 10000) | |
455 | phase_delay += i + 12; | |
456 | else if (temperature < 20000) | |
457 | phase_delay += i + 8; | |
458 | else if (temperature < 70000) | |
459 | phase_delay += i + 8; | |
460 | else if (temperature < 90000) | |
461 | phase_delay += i + 10; | |
462 | else | |
463 | phase_delay += i + 12; | |
464 | ||
465 | goto single_failure_found; | |
466 | } | |
467 | } | |
468 | ||
469 | single_failure_found: | |
9fc2cd76 KVA |
470 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); |
471 | if (!(reg & AC12_SCLK_SEL)) { | |
472 | ret = -EIO; | |
473 | goto tuning_error; | |
474 | } | |
475 | ||
9fc2cd76 KVA |
476 | sdhci_omap_set_dll(omap_host, phase_delay); |
477 | ||
5b0d6210 FA |
478 | omap_host->is_tuning = false; |
479 | ||
9fc2cd76 KVA |
480 | goto ret; |
481 | ||
482 | tuning_error: | |
5b0d6210 | 483 | omap_host->is_tuning = false; |
9fc2cd76 KVA |
484 | dev_err(dev, "Tuning failed\n"); |
485 | sdhci_omap_disable_tuning(omap_host); | |
486 | ||
487 | ret: | |
488 | sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | |
db2039fc FA |
489 | /* Reenable forbidden interrupt */ |
490 | if (dcrc_was_enabled) | |
491 | host->ier |= SDHCI_INT_DATA_CRC; | |
7d33c358 KVA |
492 | sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); |
493 | sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); | |
9fc2cd76 KVA |
494 | return ret; |
495 | } | |
496 | ||
20ea26a1 KVA |
497 | static int sdhci_omap_card_busy(struct mmc_host *mmc) |
498 | { | |
499 | u32 reg, ac12; | |
500 | int ret = false; | |
501 | struct sdhci_host *host = mmc_priv(mmc); | |
502 | struct sdhci_pltfm_host *pltfm_host; | |
503 | struct sdhci_omap_host *omap_host; | |
504 | u32 ier = host->ier; | |
505 | ||
506 | pltfm_host = sdhci_priv(host); | |
507 | omap_host = sdhci_pltfm_priv(pltfm_host); | |
508 | ||
509 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
510 | ac12 = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); | |
511 | reg &= ~CON_CLKEXTFREE; | |
512 | if (ac12 & AC12_V1V8_SIGEN) | |
513 | reg |= CON_CLKEXTFREE; | |
514 | reg |= CON_PADEN; | |
515 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
516 | ||
517 | disable_irq(host->irq); | |
518 | ier |= SDHCI_INT_CARD_INT; | |
519 | sdhci_writel(host, ier, SDHCI_INT_ENABLE); | |
520 | sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); | |
521 | ||
522 | /* | |
523 | * Delay is required for PSTATE to correctly reflect | |
524 | * DLEV/CLEV values after PADEN is set. | |
525 | */ | |
526 | usleep_range(50, 100); | |
527 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_PSTATE); | |
528 | if ((reg & PSTATE_DATI) || !(reg & PSTATE_DLEV_DAT0)) | |
529 | ret = true; | |
530 | ||
531 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
532 | reg &= ~(CON_CLKEXTFREE | CON_PADEN); | |
533 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
534 | ||
535 | sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); | |
536 | sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); | |
537 | enable_irq(host->irq); | |
538 | ||
539 | return ret; | |
540 | } | |
541 | ||
7d326930 KVA |
542 | static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc, |
543 | struct mmc_ios *ios) | |
544 | { | |
545 | u32 reg; | |
546 | int ret; | |
547 | unsigned int iov; | |
548 | struct sdhci_host *host = mmc_priv(mmc); | |
549 | struct sdhci_pltfm_host *pltfm_host; | |
550 | struct sdhci_omap_host *omap_host; | |
551 | struct device *dev; | |
552 | ||
553 | pltfm_host = sdhci_priv(host); | |
554 | omap_host = sdhci_pltfm_priv(pltfm_host); | |
555 | dev = omap_host->dev; | |
556 | ||
557 | if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { | |
558 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); | |
de5ccd2a | 559 | if (!(reg & (CAPA_VS30 | CAPA_VS33))) |
7d326930 KVA |
560 | return -EOPNOTSUPP; |
561 | ||
de5ccd2a TL |
562 | if (reg & CAPA_VS30) |
563 | iov = IOV_3V0; | |
564 | else | |
565 | iov = IOV_3V3; | |
566 | ||
7d326930 KVA |
567 | sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage); |
568 | ||
569 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); | |
570 | reg &= ~AC12_V1V8_SIGEN; | |
571 | sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg); | |
572 | ||
7d326930 KVA |
573 | } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { |
574 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); | |
575 | if (!(reg & CAPA_VS18)) | |
576 | return -EOPNOTSUPP; | |
577 | ||
de5ccd2a TL |
578 | iov = IOV_1V8; |
579 | ||
7d326930 KVA |
580 | sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage); |
581 | ||
582 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12); | |
583 | reg |= AC12_V1V8_SIGEN; | |
584 | sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg); | |
7d326930 KVA |
585 | } else { |
586 | return -EOPNOTSUPP; | |
587 | } | |
588 | ||
589 | ret = sdhci_omap_enable_iov(omap_host, iov); | |
590 | if (ret) { | |
591 | dev_err(dev, "failed to switch IO voltage to %dmV\n", iov); | |
592 | return ret; | |
593 | } | |
594 | ||
595 | dev_dbg(dev, "IO voltage switched to %dmV\n", iov); | |
596 | return 0; | |
597 | } | |
598 | ||
8d20b2ea KVA |
599 | static void sdhci_omap_set_timing(struct sdhci_omap_host *omap_host, u8 timing) |
600 | { | |
601 | int ret; | |
602 | struct pinctrl_state *pinctrl_state; | |
603 | struct device *dev = omap_host->dev; | |
604 | ||
605 | if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY)) | |
606 | return; | |
607 | ||
608 | if (omap_host->timing == timing) | |
609 | return; | |
610 | ||
611 | sdhci_omap_stop_clock(omap_host); | |
612 | ||
613 | pinctrl_state = omap_host->pinctrl_state[timing]; | |
614 | ret = pinctrl_select_state(omap_host->pinctrl, pinctrl_state); | |
615 | if (ret) { | |
616 | dev_err(dev, "failed to select pinctrl state\n"); | |
617 | return; | |
618 | } | |
619 | ||
620 | sdhci_omap_start_clock(omap_host); | |
621 | omap_host->timing = timing; | |
622 | } | |
623 | ||
300df508 KVA |
624 | static void sdhci_omap_set_power_mode(struct sdhci_omap_host *omap_host, |
625 | u8 power_mode) | |
626 | { | |
9fc2cd76 KVA |
627 | if (omap_host->bus_mode == MMC_POWER_OFF) |
628 | sdhci_omap_disable_tuning(omap_host); | |
300df508 KVA |
629 | omap_host->power_mode = power_mode; |
630 | } | |
631 | ||
7d326930 KVA |
632 | static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host, |
633 | unsigned int mode) | |
634 | { | |
635 | u32 reg; | |
636 | ||
637 | if (omap_host->bus_mode == mode) | |
638 | return; | |
639 | ||
640 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
641 | if (mode == MMC_BUSMODE_OPENDRAIN) | |
642 | reg |= CON_OD; | |
643 | else | |
644 | reg &= ~CON_OD; | |
645 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
646 | ||
647 | omap_host->bus_mode = mode; | |
648 | } | |
649 | ||
ddde0e7d | 650 | static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
7d326930 KVA |
651 | { |
652 | struct sdhci_host *host = mmc_priv(mmc); | |
653 | struct sdhci_pltfm_host *pltfm_host; | |
654 | struct sdhci_omap_host *omap_host; | |
655 | ||
656 | pltfm_host = sdhci_priv(host); | |
657 | omap_host = sdhci_pltfm_priv(pltfm_host); | |
658 | ||
659 | sdhci_omap_set_bus_mode(omap_host, ios->bus_mode); | |
8d20b2ea | 660 | sdhci_omap_set_timing(omap_host, ios->timing); |
7d326930 | 661 | sdhci_set_ios(mmc, ios); |
300df508 | 662 | sdhci_omap_set_power_mode(omap_host, ios->power_mode); |
7d326930 KVA |
663 | } |
664 | ||
665 | static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host, | |
666 | unsigned int clock) | |
667 | { | |
668 | u16 dsor; | |
669 | ||
670 | dsor = DIV_ROUND_UP(clk_get_rate(host->clk), clock); | |
671 | if (dsor > SYSCTL_CLKD_MAX) | |
672 | dsor = SYSCTL_CLKD_MAX; | |
673 | ||
674 | return dsor; | |
675 | } | |
676 | ||
677 | static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host) | |
678 | { | |
679 | u32 reg; | |
680 | ||
681 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL); | |
682 | reg |= SYSCTL_CEN; | |
683 | sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg); | |
684 | } | |
685 | ||
686 | static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host) | |
687 | { | |
688 | u32 reg; | |
689 | ||
690 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL); | |
691 | reg &= ~SYSCTL_CEN; | |
692 | sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg); | |
693 | } | |
694 | ||
695 | static void sdhci_omap_set_clock(struct sdhci_host *host, unsigned int clock) | |
696 | { | |
697 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
698 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
699 | unsigned long clkdiv; | |
700 | ||
701 | sdhci_omap_stop_clock(omap_host); | |
702 | ||
703 | if (!clock) | |
704 | return; | |
705 | ||
706 | clkdiv = sdhci_omap_calc_divisor(pltfm_host, clock); | |
707 | clkdiv = (clkdiv & SYSCTL_CLKD_MASK) << SYSCTL_CLKD_SHIFT; | |
708 | sdhci_enable_clk(host, clkdiv); | |
709 | ||
710 | sdhci_omap_start_clock(omap_host); | |
711 | } | |
712 | ||
ddde0e7d | 713 | static void sdhci_omap_set_power(struct sdhci_host *host, unsigned char mode, |
7d326930 KVA |
714 | unsigned short vdd) |
715 | { | |
716 | struct mmc_host *mmc = host->mmc; | |
717 | ||
8e0e7bd3 TL |
718 | if (!IS_ERR(mmc->supply.vmmc)) |
719 | mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); | |
7d326930 KVA |
720 | } |
721 | ||
c66e21fd TL |
722 | /* |
723 | * MMCHS_HL_HWINFO has the MADMA_EN bit set if the controller instance | |
724 | * is connected to L3 interconnect and is bus master capable. Note that | |
725 | * the MMCHS_HL_HWINFO register is in the module registers before the | |
726 | * omap registers and sdhci registers. The offset can vary for omap | |
727 | * registers depending on the SoC. Do not use sdhci_omap_readl() here. | |
728 | */ | |
729 | static bool sdhci_omap_has_adma(struct sdhci_omap_host *omap_host, int offset) | |
730 | { | |
731 | /* MMCHS_HL_HWINFO register is only available on omap4 and later */ | |
732 | if (offset < 0x200) | |
733 | return false; | |
734 | ||
735 | return readl(omap_host->base + 4) & 1; | |
736 | } | |
737 | ||
7d326930 KVA |
738 | static int sdhci_omap_enable_dma(struct sdhci_host *host) |
739 | { | |
740 | u32 reg; | |
741 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
742 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
743 | ||
744 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
195fadb7 CZ |
745 | reg &= ~CON_DMA_MASTER; |
746 | /* Switch to DMA slave mode when using external DMA */ | |
747 | if (!host->use_external_dma) | |
748 | reg |= CON_DMA_MASTER; | |
749 | ||
7d326930 KVA |
750 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); |
751 | ||
752 | return 0; | |
753 | } | |
754 | ||
ddde0e7d | 755 | static unsigned int sdhci_omap_get_min_clock(struct sdhci_host *host) |
7d326930 KVA |
756 | { |
757 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
758 | ||
759 | return clk_get_rate(pltfm_host->clk) / SYSCTL_CLKD_MAX; | |
760 | } | |
761 | ||
762 | static void sdhci_omap_set_bus_width(struct sdhci_host *host, int width) | |
763 | { | |
764 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
765 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
766 | u32 reg; | |
767 | ||
768 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
769 | if (width == MMC_BUS_WIDTH_8) | |
770 | reg |= CON_DW8; | |
771 | else | |
772 | reg &= ~CON_DW8; | |
773 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
774 | ||
775 | sdhci_set_bus_width(host, width); | |
776 | } | |
777 | ||
778 | static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode) | |
779 | { | |
780 | u32 reg; | |
781 | ktime_t timeout; | |
782 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
783 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
784 | ||
785 | if (omap_host->power_mode == power_mode) | |
786 | return; | |
787 | ||
788 | if (power_mode != MMC_POWER_ON) | |
789 | return; | |
790 | ||
791 | disable_irq(host->irq); | |
792 | ||
793 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
794 | reg |= CON_INIT; | |
795 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
796 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CMD, 0x0); | |
797 | ||
798 | /* wait 1ms */ | |
799 | timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT); | |
9f0ea0bd AH |
800 | while (1) { |
801 | bool timedout = ktime_after(ktime_get(), timeout); | |
802 | ||
803 | if (sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN) | |
804 | break; | |
805 | if (WARN_ON(timedout)) | |
7d326930 KVA |
806 | return; |
807 | usleep_range(5, 10); | |
808 | } | |
809 | ||
810 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
811 | reg &= ~CON_INIT; | |
812 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
813 | sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN); | |
814 | ||
815 | enable_irq(host->irq); | |
7d326930 KVA |
816 | } |
817 | ||
27ceb7e0 KVA |
818 | static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host, |
819 | unsigned int timing) | |
820 | { | |
821 | u32 reg; | |
822 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
823 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
824 | ||
825 | sdhci_omap_stop_clock(omap_host); | |
826 | ||
827 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
828 | if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52) | |
829 | reg |= CON_DDR; | |
830 | else | |
831 | reg &= ~CON_DDR; | |
832 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); | |
833 | ||
834 | sdhci_set_uhs_signaling(host, timing); | |
835 | sdhci_omap_start_clock(omap_host); | |
836 | } | |
837 | ||
9e84a2e6 | 838 | #define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */ |
2198eeff | 839 | static void sdhci_omap_reset(struct sdhci_host *host, u8 mask) |
5b0d6210 FA |
840 | { |
841 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
842 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
9e84a2e6 FA |
843 | unsigned long limit = MMC_TIMEOUT_US; |
844 | unsigned long i = 0; | |
53f9460e TL |
845 | u32 sysc; |
846 | ||
847 | /* Save target module sysconfig configured by SoC PM layer */ | |
848 | if (mask & SDHCI_RESET_ALL) | |
849 | sysc = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCONFIG); | |
5b0d6210 FA |
850 | |
851 | /* Don't reset data lines during tuning operation */ | |
852 | if (omap_host->is_tuning) | |
853 | mask &= ~SDHCI_RESET_DATA; | |
854 | ||
9e84a2e6 FA |
855 | if (omap_host->flags & SDHCI_OMAP_SPECIAL_RESET) { |
856 | sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); | |
857 | while ((!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask)) && | |
858 | (i++ < limit)) | |
859 | udelay(1); | |
860 | i = 0; | |
861 | while ((sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) && | |
862 | (i++ < limit)) | |
863 | udelay(1); | |
864 | ||
865 | if (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) | |
866 | dev_err(mmc_dev(host->mmc), | |
867 | "Timeout waiting on controller reset in %s\n", | |
868 | __func__); | |
53f9460e TL |
869 | |
870 | goto restore_sysc; | |
9e84a2e6 FA |
871 | } |
872 | ||
5b0d6210 | 873 | sdhci_reset(host, mask); |
53f9460e TL |
874 | |
875 | restore_sysc: | |
876 | if (mask & SDHCI_RESET_ALL) | |
877 | sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCONFIG, sysc); | |
5b0d6210 FA |
878 | } |
879 | ||
5c41ea6d FA |
880 | #define CMD_ERR_MASK (SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX |\ |
881 | SDHCI_INT_TIMEOUT) | |
882 | #define CMD_MASK (CMD_ERR_MASK | SDHCI_INT_RESPONSE) | |
883 | ||
884 | static u32 sdhci_omap_irq(struct sdhci_host *host, u32 intmask) | |
885 | { | |
886 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
887 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
888 | ||
889 | if (omap_host->is_tuning && host->cmd && !host->data_early && | |
890 | (intmask & CMD_ERR_MASK)) { | |
891 | ||
892 | /* | |
893 | * Since we are not resetting data lines during tuning | |
894 | * operation, data error or data complete interrupts | |
895 | * might still arrive. Mark this request as a failure | |
896 | * but still wait for the data interrupt | |
897 | */ | |
898 | if (intmask & SDHCI_INT_TIMEOUT) | |
899 | host->cmd->error = -ETIMEDOUT; | |
900 | else | |
901 | host->cmd->error = -EILSEQ; | |
902 | ||
903 | host->cmd = NULL; | |
904 | ||
905 | /* | |
906 | * Sometimes command error interrupts and command complete | |
907 | * interrupt will arrive together. Clear all command related | |
908 | * interrupts here. | |
909 | */ | |
910 | sdhci_writel(host, intmask & CMD_MASK, SDHCI_INT_STATUS); | |
911 | intmask &= ~CMD_MASK; | |
912 | } | |
913 | ||
914 | return intmask; | |
915 | } | |
916 | ||
5da5e494 FA |
917 | static void sdhci_omap_set_timeout(struct sdhci_host *host, |
918 | struct mmc_command *cmd) | |
919 | { | |
920 | if (cmd->opcode == MMC_ERASE) | |
921 | sdhci_set_data_timeout_irq(host, false); | |
922 | ||
923 | __sdhci_set_timeout(host, cmd); | |
924 | } | |
925 | ||
7d326930 KVA |
926 | static struct sdhci_ops sdhci_omap_ops = { |
927 | .set_clock = sdhci_omap_set_clock, | |
928 | .set_power = sdhci_omap_set_power, | |
929 | .enable_dma = sdhci_omap_enable_dma, | |
930 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, | |
931 | .get_min_clock = sdhci_omap_get_min_clock, | |
932 | .set_bus_width = sdhci_omap_set_bus_width, | |
933 | .platform_send_init_74_clocks = sdhci_omap_init_74_clocks, | |
5b0d6210 | 934 | .reset = sdhci_omap_reset, |
27ceb7e0 | 935 | .set_uhs_signaling = sdhci_omap_set_uhs_signaling, |
5c41ea6d | 936 | .irq = sdhci_omap_irq, |
5da5e494 | 937 | .set_timeout = sdhci_omap_set_timeout, |
7d326930 KVA |
938 | }; |
939 | ||
de5ccd2a TL |
940 | static unsigned int sdhci_omap_regulator_get_caps(struct device *dev, |
941 | const char *name) | |
7d326930 | 942 | { |
de5ccd2a TL |
943 | struct regulator *reg; |
944 | unsigned int caps = 0; | |
945 | ||
946 | reg = regulator_get(dev, name); | |
947 | if (IS_ERR(reg)) | |
948 | return ~0U; | |
949 | ||
950 | if (regulator_is_supported_voltage(reg, 1700000, 1950000)) | |
951 | caps |= SDHCI_CAN_VDD_180; | |
952 | if (regulator_is_supported_voltage(reg, 2700000, 3150000)) | |
953 | caps |= SDHCI_CAN_VDD_300; | |
954 | if (regulator_is_supported_voltage(reg, 3150000, 3600000)) | |
955 | caps |= SDHCI_CAN_VDD_330; | |
956 | ||
957 | regulator_put(reg); | |
958 | ||
959 | return caps; | |
960 | } | |
961 | ||
962 | static int sdhci_omap_set_capabilities(struct sdhci_host *host) | |
963 | { | |
964 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
965 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
7d326930 | 966 | struct device *dev = omap_host->dev; |
de5ccd2a TL |
967 | const u32 mask = SDHCI_CAN_VDD_180 | SDHCI_CAN_VDD_300 | SDHCI_CAN_VDD_330; |
968 | unsigned int pbias, vqmmc, caps = 0; | |
969 | u32 reg; | |
7d326930 | 970 | |
de5ccd2a TL |
971 | pbias = sdhci_omap_regulator_get_caps(dev, "pbias"); |
972 | vqmmc = sdhci_omap_regulator_get_caps(dev, "vqmmc"); | |
973 | caps = pbias & vqmmc; | |
974 | ||
975 | if (pbias != ~0U && vqmmc == ~0U) | |
976 | dev_warn(dev, "vqmmc regulator missing for pbias\n"); | |
977 | else if (caps == ~0U) | |
978 | return 0; | |
979 | ||
980 | /* | |
981 | * Quirk handling to allow 3.0V vqmmc with a valid 3.3V PBIAS. This is | |
982 | * needed for 3.0V ldo9_reg on omap5 at least. | |
983 | */ | |
984 | if (pbias != ~0U && (pbias & SDHCI_CAN_VDD_330) && | |
985 | (vqmmc & SDHCI_CAN_VDD_300)) | |
986 | caps |= SDHCI_CAN_VDD_330; | |
7d326930 KVA |
987 | |
988 | /* voltage capabilities might be set by boot loader, clear it */ | |
989 | reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); | |
990 | reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33); | |
991 | ||
de5ccd2a | 992 | if (caps & SDHCI_CAN_VDD_180) |
7d326930 KVA |
993 | reg |= CAPA_VS18; |
994 | ||
de5ccd2a TL |
995 | if (caps & SDHCI_CAN_VDD_300) |
996 | reg |= CAPA_VS30; | |
997 | ||
998 | if (caps & SDHCI_CAN_VDD_330) | |
999 | reg |= CAPA_VS33; | |
1000 | ||
7d326930 KVA |
1001 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, reg); |
1002 | ||
de5ccd2a TL |
1003 | host->caps &= ~mask; |
1004 | host->caps |= caps; | |
7d326930 | 1005 | |
de5ccd2a | 1006 | return 0; |
7d326930 KVA |
1007 | } |
1008 | ||
1009 | static const struct sdhci_pltfm_data sdhci_omap_pdata = { | |
1010 | .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | | |
1011 | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | | |
1012 | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | | |
1013 | SDHCI_QUIRK_NO_HISPD_BIT | | |
1014 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, | |
e0b2dbcf KVA |
1015 | .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN | |
1016 | SDHCI_QUIRK2_PRESET_VALUE_BROKEN | | |
25f80d86 KVA |
1017 | SDHCI_QUIRK2_RSP_136_HAS_CRC | |
1018 | SDHCI_QUIRK2_DISABLE_HW_TIMEOUT, | |
7d326930 KVA |
1019 | .ops = &sdhci_omap_ops, |
1020 | }; | |
1021 | ||
42b380b6 TL |
1022 | static const struct sdhci_omap_data omap2430_data = { |
1023 | .omap_offset = 0, | |
1024 | .offset = 0x100, | |
1025 | }; | |
1026 | ||
1027 | static const struct sdhci_omap_data omap3_data = { | |
1028 | .omap_offset = 0, | |
1029 | .offset = 0x100, | |
1030 | }; | |
1031 | ||
de5ccd2a | 1032 | static const struct sdhci_omap_data omap4_data = { |
42b380b6 | 1033 | .omap_offset = 0x100, |
de5ccd2a TL |
1034 | .offset = 0x200, |
1035 | .flags = SDHCI_OMAP_SPECIAL_RESET, | |
1036 | }; | |
1037 | ||
1038 | static const struct sdhci_omap_data omap5_data = { | |
42b380b6 | 1039 | .omap_offset = 0x100, |
de5ccd2a TL |
1040 | .offset = 0x200, |
1041 | .flags = SDHCI_OMAP_SPECIAL_RESET, | |
1042 | }; | |
1043 | ||
6d75df75 | 1044 | static const struct sdhci_omap_data k2g_data = { |
42b380b6 | 1045 | .omap_offset = 0x100, |
6d75df75 KVA |
1046 | .offset = 0x200, |
1047 | }; | |
1048 | ||
d6fe4928 | 1049 | static const struct sdhci_omap_data am335_data = { |
42b380b6 | 1050 | .omap_offset = 0x100, |
d6fe4928 | 1051 | .offset = 0x200, |
9e84a2e6 | 1052 | .flags = SDHCI_OMAP_SPECIAL_RESET, |
d6fe4928 FA |
1053 | }; |
1054 | ||
1055 | static const struct sdhci_omap_data am437_data = { | |
42b380b6 | 1056 | .omap_offset = 0x100, |
d6fe4928 | 1057 | .offset = 0x200, |
9e84a2e6 | 1058 | .flags = SDHCI_OMAP_SPECIAL_RESET, |
d6fe4928 FA |
1059 | }; |
1060 | ||
7d326930 | 1061 | static const struct sdhci_omap_data dra7_data = { |
42b380b6 | 1062 | .omap_offset = 0x100, |
7d326930 | 1063 | .offset = 0x200, |
8d20b2ea | 1064 | .flags = SDHCI_OMAP_REQUIRE_IODELAY, |
7d326930 KVA |
1065 | }; |
1066 | ||
1067 | static const struct of_device_id omap_sdhci_match[] = { | |
42b380b6 TL |
1068 | { .compatible = "ti,omap2430-sdhci", .data = &omap2430_data }, |
1069 | { .compatible = "ti,omap3-sdhci", .data = &omap3_data }, | |
de5ccd2a TL |
1070 | { .compatible = "ti,omap4-sdhci", .data = &omap4_data }, |
1071 | { .compatible = "ti,omap5-sdhci", .data = &omap5_data }, | |
7d326930 | 1072 | { .compatible = "ti,dra7-sdhci", .data = &dra7_data }, |
6d75df75 | 1073 | { .compatible = "ti,k2g-sdhci", .data = &k2g_data }, |
d6fe4928 FA |
1074 | { .compatible = "ti,am335-sdhci", .data = &am335_data }, |
1075 | { .compatible = "ti,am437-sdhci", .data = &am437_data }, | |
7d326930 KVA |
1076 | {}, |
1077 | }; | |
1078 | MODULE_DEVICE_TABLE(of, omap_sdhci_match); | |
1079 | ||
8d20b2ea KVA |
1080 | static struct pinctrl_state |
1081 | *sdhci_omap_iodelay_pinctrl_state(struct sdhci_omap_host *omap_host, char *mode, | |
1082 | u32 *caps, u32 capmask) | |
1083 | { | |
1084 | struct device *dev = omap_host->dev; | |
212f4f8a | 1085 | char *version = omap_host->version; |
8d20b2ea | 1086 | struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV); |
212f4f8a | 1087 | char str[20]; |
8d20b2ea KVA |
1088 | |
1089 | if (!(*caps & capmask)) | |
1090 | goto ret; | |
1091 | ||
212f4f8a KVA |
1092 | if (version) { |
1093 | snprintf(str, 20, "%s-%s", mode, version); | |
1094 | pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, str); | |
1095 | } | |
1096 | ||
1097 | if (IS_ERR(pinctrl_state)) | |
1098 | pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode); | |
1099 | ||
8d20b2ea KVA |
1100 | if (IS_ERR(pinctrl_state)) { |
1101 | dev_err(dev, "no pinctrl state for %s mode", mode); | |
1102 | *caps &= ~capmask; | |
1103 | } | |
1104 | ||
1105 | ret: | |
1106 | return pinctrl_state; | |
1107 | } | |
1108 | ||
1109 | static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host | |
1110 | *omap_host) | |
1111 | { | |
1112 | struct device *dev = omap_host->dev; | |
1113 | struct sdhci_host *host = omap_host->host; | |
1114 | struct mmc_host *mmc = host->mmc; | |
1115 | u32 *caps = &mmc->caps; | |
1116 | u32 *caps2 = &mmc->caps2; | |
1117 | struct pinctrl_state *state; | |
1118 | struct pinctrl_state **pinctrl_state; | |
1119 | ||
1120 | if (!(omap_host->flags & SDHCI_OMAP_REQUIRE_IODELAY)) | |
1121 | return 0; | |
1122 | ||
a86854d0 KC |
1123 | pinctrl_state = devm_kcalloc(dev, |
1124 | MMC_TIMING_MMC_HS200 + 1, | |
1125 | sizeof(*pinctrl_state), | |
1126 | GFP_KERNEL); | |
8d20b2ea KVA |
1127 | if (!pinctrl_state) |
1128 | return -ENOMEM; | |
1129 | ||
1130 | omap_host->pinctrl = devm_pinctrl_get(omap_host->dev); | |
1131 | if (IS_ERR(omap_host->pinctrl)) { | |
1132 | dev_err(dev, "Cannot get pinctrl\n"); | |
1133 | return PTR_ERR(omap_host->pinctrl); | |
1134 | } | |
1135 | ||
1136 | state = pinctrl_lookup_state(omap_host->pinctrl, "default"); | |
1137 | if (IS_ERR(state)) { | |
1138 | dev_err(dev, "no pinctrl state for default mode\n"); | |
1139 | return PTR_ERR(state); | |
1140 | } | |
1141 | pinctrl_state[MMC_TIMING_LEGACY] = state; | |
1142 | ||
1143 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr104", caps, | |
1144 | MMC_CAP_UHS_SDR104); | |
1145 | if (!IS_ERR(state)) | |
1146 | pinctrl_state[MMC_TIMING_UHS_SDR104] = state; | |
1147 | ||
1148 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr50", caps, | |
1149 | MMC_CAP_UHS_DDR50); | |
1150 | if (!IS_ERR(state)) | |
1151 | pinctrl_state[MMC_TIMING_UHS_DDR50] = state; | |
1152 | ||
1153 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr50", caps, | |
1154 | MMC_CAP_UHS_SDR50); | |
1155 | if (!IS_ERR(state)) | |
1156 | pinctrl_state[MMC_TIMING_UHS_SDR50] = state; | |
1157 | ||
1158 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr25", caps, | |
1159 | MMC_CAP_UHS_SDR25); | |
1160 | if (!IS_ERR(state)) | |
1161 | pinctrl_state[MMC_TIMING_UHS_SDR25] = state; | |
1162 | ||
1163 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "sdr12", caps, | |
1164 | MMC_CAP_UHS_SDR12); | |
1165 | if (!IS_ERR(state)) | |
1166 | pinctrl_state[MMC_TIMING_UHS_SDR12] = state; | |
1167 | ||
1168 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps, | |
1169 | MMC_CAP_1_8V_DDR); | |
3f402878 | 1170 | if (!IS_ERR(state)) { |
8d20b2ea | 1171 | pinctrl_state[MMC_TIMING_MMC_DDR52] = state; |
3f402878 KVA |
1172 | } else { |
1173 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_3_3v", | |
1174 | caps, | |
1175 | MMC_CAP_3_3V_DDR); | |
1176 | if (!IS_ERR(state)) | |
1177 | pinctrl_state[MMC_TIMING_MMC_DDR52] = state; | |
1178 | } | |
8d20b2ea KVA |
1179 | |
1180 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps, | |
1181 | MMC_CAP_SD_HIGHSPEED); | |
1182 | if (!IS_ERR(state)) | |
1183 | pinctrl_state[MMC_TIMING_SD_HS] = state; | |
1184 | ||
1185 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps, | |
1186 | MMC_CAP_MMC_HIGHSPEED); | |
1187 | if (!IS_ERR(state)) | |
1188 | pinctrl_state[MMC_TIMING_MMC_HS] = state; | |
1189 | ||
1190 | state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs200_1_8v", caps2, | |
1191 | MMC_CAP2_HS200_1_8V_SDR); | |
1192 | if (!IS_ERR(state)) | |
1193 | pinctrl_state[MMC_TIMING_MMC_HS200] = state; | |
1194 | ||
1195 | omap_host->pinctrl_state = pinctrl_state; | |
1196 | ||
1197 | return 0; | |
1198 | } | |
1199 | ||
212f4f8a KVA |
1200 | static const struct soc_device_attribute sdhci_omap_soc_devices[] = { |
1201 | { | |
1202 | .machine = "DRA7[45]*", | |
1203 | .revision = "ES1.[01]", | |
1204 | }, | |
1205 | { | |
1206 | /* sentinel */ | |
1207 | } | |
1208 | }; | |
1209 | ||
7d326930 KVA |
1210 | static int sdhci_omap_probe(struct platform_device *pdev) |
1211 | { | |
1212 | int ret; | |
1213 | u32 offset; | |
1214 | struct device *dev = &pdev->dev; | |
1215 | struct sdhci_host *host; | |
1216 | struct sdhci_pltfm_host *pltfm_host; | |
1217 | struct sdhci_omap_host *omap_host; | |
1218 | struct mmc_host *mmc; | |
1219 | const struct of_device_id *match; | |
1220 | struct sdhci_omap_data *data; | |
212f4f8a | 1221 | const struct soc_device_attribute *soc; |
195fadb7 | 1222 | struct resource *regs; |
7d326930 KVA |
1223 | |
1224 | match = of_match_device(omap_sdhci_match, dev); | |
1225 | if (!match) | |
1226 | return -EINVAL; | |
1227 | ||
1228 | data = (struct sdhci_omap_data *)match->data; | |
1229 | if (!data) { | |
1230 | dev_err(dev, "no sdhci omap data\n"); | |
1231 | return -EINVAL; | |
1232 | } | |
1233 | offset = data->offset; | |
1234 | ||
195fadb7 CZ |
1235 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1236 | if (!regs) | |
1237 | return -ENXIO; | |
1238 | ||
7d326930 KVA |
1239 | host = sdhci_pltfm_init(pdev, &sdhci_omap_pdata, |
1240 | sizeof(*omap_host)); | |
1241 | if (IS_ERR(host)) { | |
1242 | dev_err(dev, "Failed sdhci_pltfm_init\n"); | |
1243 | return PTR_ERR(host); | |
1244 | } | |
1245 | ||
1246 | pltfm_host = sdhci_priv(host); | |
1247 | omap_host = sdhci_pltfm_priv(pltfm_host); | |
1248 | omap_host->host = host; | |
1249 | omap_host->base = host->ioaddr; | |
1250 | omap_host->dev = dev; | |
300df508 | 1251 | omap_host->power_mode = MMC_POWER_UNDEFINED; |
8d20b2ea KVA |
1252 | omap_host->timing = MMC_TIMING_LEGACY; |
1253 | omap_host->flags = data->flags; | |
42b380b6 | 1254 | omap_host->omap_offset = data->omap_offset; |
7d326930 | 1255 | host->ioaddr += offset; |
195fadb7 | 1256 | host->mapbase = regs->start + offset; |
7d326930 KVA |
1257 | |
1258 | mmc = host->mmc; | |
1d3a2220 | 1259 | sdhci_get_of_property(pdev); |
7d326930 KVA |
1260 | ret = mmc_of_parse(mmc); |
1261 | if (ret) | |
1262 | goto err_pltfm_free; | |
1263 | ||
212f4f8a KVA |
1264 | soc = soc_device_match(sdhci_omap_soc_devices); |
1265 | if (soc) { | |
1266 | omap_host->version = "rev11"; | |
1267 | if (!strcmp(dev_name(dev), "4809c000.mmc")) | |
1268 | mmc->f_max = 96000000; | |
1269 | if (!strcmp(dev_name(dev), "480b4000.mmc")) | |
1270 | mmc->f_max = 48000000; | |
1271 | if (!strcmp(dev_name(dev), "480ad000.mmc")) | |
1272 | mmc->f_max = 48000000; | |
1273 | } | |
1274 | ||
031d2ccc KVA |
1275 | if (!mmc_can_gpio_ro(mmc)) |
1276 | mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; | |
1277 | ||
7d326930 KVA |
1278 | pltfm_host->clk = devm_clk_get(dev, "fck"); |
1279 | if (IS_ERR(pltfm_host->clk)) { | |
1280 | ret = PTR_ERR(pltfm_host->clk); | |
1281 | goto err_pltfm_free; | |
1282 | } | |
1283 | ||
1284 | ret = clk_set_rate(pltfm_host->clk, mmc->f_max); | |
1285 | if (ret) { | |
1286 | dev_err(dev, "failed to set clock to %d\n", mmc->f_max); | |
1287 | goto err_pltfm_free; | |
1288 | } | |
1289 | ||
1290 | omap_host->pbias = devm_regulator_get_optional(dev, "pbias"); | |
1291 | if (IS_ERR(omap_host->pbias)) { | |
1292 | ret = PTR_ERR(omap_host->pbias); | |
1293 | if (ret != -ENODEV) | |
1294 | goto err_pltfm_free; | |
1295 | dev_dbg(dev, "unable to get pbias regulator %d\n", ret); | |
1296 | } | |
1297 | omap_host->pbias_enabled = false; | |
1298 | ||
1299 | /* | |
1300 | * omap_device_pm_domain has callbacks to enable the main | |
1301 | * functional clock, interface clock and also configure the | |
1302 | * SYSCONFIG register of omap devices. The callback will be invoked | |
1303 | * as part of pm_runtime_get_sync. | |
1304 | */ | |
1305 | pm_runtime_enable(dev); | |
809ae4e1 TT |
1306 | ret = pm_runtime_resume_and_get(dev); |
1307 | if (ret) { | |
7d326930 | 1308 | dev_err(dev, "pm_runtime_get_sync failed\n"); |
7d326930 KVA |
1309 | goto err_rpm_disable; |
1310 | } | |
1311 | ||
de5ccd2a | 1312 | ret = sdhci_omap_set_capabilities(host); |
7d326930 KVA |
1313 | if (ret) { |
1314 | dev_err(dev, "failed to set system capabilities\n"); | |
1315 | goto err_put_sync; | |
1316 | } | |
1317 | ||
7d326930 KVA |
1318 | host->mmc_host_ops.start_signal_voltage_switch = |
1319 | sdhci_omap_start_signal_voltage_switch; | |
1320 | host->mmc_host_ops.set_ios = sdhci_omap_set_ios; | |
20ea26a1 | 1321 | host->mmc_host_ops.card_busy = sdhci_omap_card_busy; |
9fc2cd76 | 1322 | host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning; |
efde12b2 | 1323 | host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq; |
7d326930 | 1324 | |
c66e21fd TL |
1325 | /* |
1326 | * Switch to external DMA only if there is the "dmas" property and | |
1327 | * ADMA is not available on the controller instance. | |
1328 | */ | |
1329 | if (device_property_present(dev, "dmas") && | |
1330 | !sdhci_omap_has_adma(omap_host, offset)) | |
195fadb7 CZ |
1331 | sdhci_switch_external_dma(host, true); |
1332 | ||
3781d288 TL |
1333 | if (device_property_read_bool(dev, "ti,non-removable")) { |
1334 | dev_warn_once(dev, "using old ti,non-removable property\n"); | |
1335 | mmc->caps |= MMC_CAP_NONREMOVABLE; | |
1336 | } | |
1337 | ||
055e0483 UH |
1338 | /* R1B responses is required to properly manage HW busy detection. */ |
1339 | mmc->caps |= MMC_CAP_NEED_RSP_BUSY; | |
1340 | ||
0ec4ee3c | 1341 | ret = sdhci_setup_host(host); |
7d326930 KVA |
1342 | if (ret) |
1343 | goto err_put_sync; | |
1344 | ||
0ec4ee3c KVA |
1345 | ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host); |
1346 | if (ret) | |
1347 | goto err_cleanup_host; | |
1348 | ||
1349 | ret = __sdhci_add_host(host); | |
1350 | if (ret) | |
1351 | goto err_cleanup_host; | |
1352 | ||
7d326930 KVA |
1353 | return 0; |
1354 | ||
0ec4ee3c KVA |
1355 | err_cleanup_host: |
1356 | sdhci_cleanup_host(host); | |
1357 | ||
7d326930 KVA |
1358 | err_put_sync: |
1359 | pm_runtime_put_sync(dev); | |
1360 | ||
1361 | err_rpm_disable: | |
1362 | pm_runtime_disable(dev); | |
1363 | ||
1364 | err_pltfm_free: | |
1365 | sdhci_pltfm_free(pdev); | |
1366 | return ret; | |
1367 | } | |
1368 | ||
1369 | static int sdhci_omap_remove(struct platform_device *pdev) | |
1370 | { | |
1371 | struct device *dev = &pdev->dev; | |
1372 | struct sdhci_host *host = platform_get_drvdata(pdev); | |
1373 | ||
1374 | sdhci_remove_host(host, true); | |
1375 | pm_runtime_put_sync(dev); | |
1376 | pm_runtime_disable(dev); | |
1377 | sdhci_pltfm_free(pdev); | |
1378 | ||
1379 | return 0; | |
1380 | } | |
ee0f3092 FA |
1381 | #ifdef CONFIG_PM_SLEEP |
1382 | static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host) | |
1383 | { | |
1384 | omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); | |
1385 | omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL); | |
d806e334 | 1386 | omap_host->sysctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL); |
ee0f3092 | 1387 | omap_host->capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); |
d806e334 TL |
1388 | omap_host->ie = sdhci_omap_readl(omap_host, SDHCI_OMAP_IE); |
1389 | omap_host->ise = sdhci_omap_readl(omap_host, SDHCI_OMAP_ISE); | |
ee0f3092 FA |
1390 | } |
1391 | ||
d806e334 | 1392 | /* Order matters here, HCTL must be restored in two phases */ |
ee0f3092 FA |
1393 | static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host) |
1394 | { | |
ee0f3092 FA |
1395 | sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl); |
1396 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa); | |
d806e334 TL |
1397 | sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl); |
1398 | ||
1399 | sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, omap_host->sysctl); | |
1400 | sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con); | |
1401 | sdhci_omap_writel(omap_host, SDHCI_OMAP_IE, omap_host->ie); | |
1402 | sdhci_omap_writel(omap_host, SDHCI_OMAP_ISE, omap_host->ise); | |
ee0f3092 FA |
1403 | } |
1404 | ||
1405 | static int __maybe_unused sdhci_omap_suspend(struct device *dev) | |
1406 | { | |
1407 | struct sdhci_host *host = dev_get_drvdata(dev); | |
1408 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
1409 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
1410 | ||
1411 | sdhci_suspend_host(host); | |
1412 | ||
1413 | sdhci_omap_context_save(omap_host); | |
1414 | ||
1415 | pinctrl_pm_select_idle_state(dev); | |
1416 | ||
1417 | pm_runtime_force_suspend(dev); | |
1418 | ||
1419 | return 0; | |
1420 | } | |
1421 | ||
1422 | static int __maybe_unused sdhci_omap_resume(struct device *dev) | |
1423 | { | |
1424 | struct sdhci_host *host = dev_get_drvdata(dev); | |
1425 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
1426 | struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); | |
1427 | ||
1428 | pm_runtime_force_resume(dev); | |
1429 | ||
1430 | pinctrl_pm_select_default_state(dev); | |
1431 | ||
1432 | sdhci_omap_context_restore(omap_host); | |
1433 | ||
1434 | sdhci_resume_host(host); | |
1435 | ||
1436 | return 0; | |
1437 | } | |
1438 | #endif | |
1439 | static SIMPLE_DEV_PM_OPS(sdhci_omap_dev_pm_ops, sdhci_omap_suspend, | |
1440 | sdhci_omap_resume); | |
7d326930 KVA |
1441 | |
1442 | static struct platform_driver sdhci_omap_driver = { | |
1443 | .probe = sdhci_omap_probe, | |
1444 | .remove = sdhci_omap_remove, | |
1445 | .driver = { | |
1446 | .name = "sdhci-omap", | |
a1a48919 | 1447 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
ee0f3092 | 1448 | .pm = &sdhci_omap_dev_pm_ops, |
7d326930 KVA |
1449 | .of_match_table = omap_sdhci_match, |
1450 | }, | |
1451 | }; | |
1452 | ||
1453 | module_platform_driver(sdhci_omap_driver); | |
1454 | ||
1455 | MODULE_DESCRIPTION("SDHCI driver for OMAP SoCs"); | |
1456 | MODULE_AUTHOR("Texas Instruments Inc."); | |
1457 | MODULE_LICENSE("GPL v2"); | |
1458 | MODULE_ALIAS("platform:sdhci_omap"); |