Commit | Line | Data |
---|---|---|
fb8bd90f CZ |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Secure Digital Host Controller | |
4 | // | |
5 | // Copyright (C) 2018 Spreadtrum, Inc. | |
6 | // Author: Chunyan Zhang <chunyan.zhang@unisoc.com> | |
7 | ||
8 | #include <linux/delay.h> | |
9 | #include <linux/dma-mapping.h> | |
10 | #include <linux/highmem.h> | |
7f00917a | 11 | #include <linux/iopoll.h> |
d83d251b WC |
12 | #include <linux/mmc/host.h> |
13 | #include <linux/mmc/mmc.h> | |
fb8bd90f CZ |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> | |
fb8bd90f | 16 | #include <linux/of_gpio.h> |
29ca763f | 17 | #include <linux/pinctrl/consumer.h> |
fb8bd90f CZ |
18 | #include <linux/platform_device.h> |
19 | #include <linux/pm_runtime.h> | |
20 | #include <linux/regulator/consumer.h> | |
21 | #include <linux/slab.h> | |
22 | ||
23 | #include "sdhci-pltfm.h" | |
f4498549 | 24 | #include "mmc_hsq.h" |
fb8bd90f CZ |
25 | |
26 | /* SDHCI_ARGUMENT2 register high 16bit */ | |
27 | #define SDHCI_SPRD_ARG2_STUFF GENMASK(31, 16) | |
28 | ||
87a395c2 BW |
29 | #define SDHCI_SPRD_REG_32_DLL_CFG 0x200 |
30 | #define SDHCI_SPRD_DLL_ALL_CPST_EN (BIT(18) | BIT(24) | BIT(25) | BIT(26) | BIT(27)) | |
31 | #define SDHCI_SPRD_DLL_EN BIT(21) | |
32 | #define SDHCI_SPRD_DLL_SEARCH_MODE BIT(16) | |
33 | #define SDHCI_SPRD_DLL_INIT_COUNT 0xc00 | |
34 | #define SDHCI_SPRD_DLL_PHASE_INTERNAL 0x3 | |
35 | ||
5f2f4e0d BW |
36 | #define SDHCI_SPRD_REG_32_DLL_DLY 0x204 |
37 | ||
fb8bd90f CZ |
38 | #define SDHCI_SPRD_REG_32_DLL_DLY_OFFSET 0x208 |
39 | #define SDHCIBSPRD_IT_WR_DLY_INV BIT(5) | |
40 | #define SDHCI_SPRD_BIT_CMD_DLY_INV BIT(13) | |
41 | #define SDHCI_SPRD_BIT_POSRD_DLY_INV BIT(21) | |
42 | #define SDHCI_SPRD_BIT_NEGRD_DLY_INV BIT(29) | |
43 | ||
7f00917a ZL |
44 | #define SDHCI_SPRD_REG_32_DLL_STS0 0x210 |
45 | #define SDHCI_SPRD_DLL_LOCKED BIT(18) | |
46 | ||
fb8bd90f CZ |
47 | #define SDHCI_SPRD_REG_32_BUSY_POSI 0x250 |
48 | #define SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN BIT(25) | |
49 | #define SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN BIT(24) | |
50 | ||
51 | #define SDHCI_SPRD_REG_DEBOUNCE 0x28C | |
52 | #define SDHCI_SPRD_BIT_DLL_BAK BIT(0) | |
53 | #define SDHCI_SPRD_BIT_DLL_VAL BIT(1) | |
54 | ||
55 | #define SDHCI_SPRD_INT_SIGNAL_MASK 0x1B7F410B | |
56 | ||
57 | /* SDHCI_HOST_CONTROL2 */ | |
58 | #define SDHCI_SPRD_CTRL_HS200 0x0005 | |
59 | #define SDHCI_SPRD_CTRL_HS400 0x0006 | |
494c11e1 | 60 | #define SDHCI_SPRD_CTRL_HS400ES 0x0007 |
fb8bd90f CZ |
61 | |
62 | /* | |
63 | * According to the standard specification, BIT(3) of SDHCI_SOFTWARE_RESET is | |
64 | * reserved, and only used on Spreadtrum's design, the hardware cannot work | |
65 | * if this bit is cleared. | |
66 | * 1 : normal work | |
67 | * 0 : hardware reset | |
68 | */ | |
69 | #define SDHCI_HW_RESET_CARD BIT(3) | |
70 | ||
71 | #define SDHCI_SPRD_MAX_CUR 0xFFFFFF | |
72 | #define SDHCI_SPRD_CLK_MAX_DIV 1023 | |
73 | ||
74 | #define SDHCI_SPRD_CLK_DEF_RATE 26000000 | |
87a395c2 | 75 | #define SDHCI_SPRD_PHY_DLL_CLK 52000000 |
fb8bd90f | 76 | |
d83d251b WC |
77 | #define SDHCI_SPRD_MAX_RANGE 0xff |
78 | #define SDHCI_SPRD_CMD_DLY_MASK GENMASK(15, 8) | |
79 | #define SDHCI_SPRD_POSRD_DLY_MASK GENMASK(23, 16) | |
80 | #define SDHCI_SPRD_CPST_EN GENMASK(27, 24) | |
81 | ||
fb8bd90f CZ |
82 | struct sdhci_sprd_host { |
83 | u32 version; | |
84 | struct clk *clk_sdio; | |
85 | struct clk *clk_enable; | |
ebd88a38 | 86 | struct clk *clk_2x_enable; |
29ca763f BW |
87 | struct pinctrl *pinctrl; |
88 | struct pinctrl_state *pins_uhs; | |
89 | struct pinctrl_state *pins_default; | |
fb8bd90f CZ |
90 | u32 base_rate; |
91 | int flags; /* backup of host attribute */ | |
5f2f4e0d BW |
92 | u32 phy_delay[MMC_TIMING_MMC_HS400 + 2]; |
93 | }; | |
94 | ||
d83d251b WC |
95 | enum sdhci_sprd_tuning_type { |
96 | SDHCI_SPRD_TUNING_SD_HS_CMD, | |
97 | SDHCI_SPRD_TUNING_SD_HS_DATA, | |
98 | }; | |
99 | ||
5f2f4e0d BW |
100 | struct sdhci_sprd_phy_cfg { |
101 | const char *property; | |
102 | u8 timing; | |
103 | }; | |
104 | ||
105 | static const struct sdhci_sprd_phy_cfg sdhci_sprd_phy_cfgs[] = { | |
106 | { "sprd,phy-delay-legacy", MMC_TIMING_LEGACY, }, | |
107 | { "sprd,phy-delay-sd-highspeed", MMC_TIMING_SD_HS, }, | |
108 | { "sprd,phy-delay-sd-uhs-sdr50", MMC_TIMING_UHS_SDR50, }, | |
109 | { "sprd,phy-delay-sd-uhs-sdr104", MMC_TIMING_UHS_SDR104, }, | |
110 | { "sprd,phy-delay-mmc-highspeed", MMC_TIMING_MMC_HS, }, | |
111 | { "sprd,phy-delay-mmc-ddr52", MMC_TIMING_MMC_DDR52, }, | |
112 | { "sprd,phy-delay-mmc-hs200", MMC_TIMING_MMC_HS200, }, | |
113 | { "sprd,phy-delay-mmc-hs400", MMC_TIMING_MMC_HS400, }, | |
114 | { "sprd,phy-delay-mmc-hs400es", MMC_TIMING_MMC_HS400 + 1, }, | |
fb8bd90f CZ |
115 | }; |
116 | ||
117 | #define TO_SPRD_HOST(host) sdhci_pltfm_priv(sdhci_priv(host)) | |
118 | ||
119 | static void sdhci_sprd_init_config(struct sdhci_host *host) | |
120 | { | |
121 | u16 val; | |
122 | ||
123 | /* set dll backup mode */ | |
124 | val = sdhci_readl(host, SDHCI_SPRD_REG_DEBOUNCE); | |
125 | val |= SDHCI_SPRD_BIT_DLL_BAK | SDHCI_SPRD_BIT_DLL_VAL; | |
126 | sdhci_writel(host, val, SDHCI_SPRD_REG_DEBOUNCE); | |
127 | } | |
128 | ||
129 | static inline u32 sdhci_sprd_readl(struct sdhci_host *host, int reg) | |
130 | { | |
131 | if (unlikely(reg == SDHCI_MAX_CURRENT)) | |
132 | return SDHCI_SPRD_MAX_CUR; | |
133 | ||
134 | return readl_relaxed(host->ioaddr + reg); | |
135 | } | |
136 | ||
137 | static inline void sdhci_sprd_writel(struct sdhci_host *host, u32 val, int reg) | |
138 | { | |
139 | /* SDHCI_MAX_CURRENT is reserved on Spreadtrum's platform */ | |
140 | if (unlikely(reg == SDHCI_MAX_CURRENT)) | |
141 | return; | |
142 | ||
143 | if (unlikely(reg == SDHCI_SIGNAL_ENABLE || reg == SDHCI_INT_ENABLE)) | |
144 | val = val & SDHCI_SPRD_INT_SIGNAL_MASK; | |
145 | ||
146 | writel_relaxed(val, host->ioaddr + reg); | |
147 | } | |
148 | ||
149 | static inline void sdhci_sprd_writew(struct sdhci_host *host, u16 val, int reg) | |
150 | { | |
151 | /* SDHCI_BLOCK_COUNT is Read Only on Spreadtrum's platform */ | |
152 | if (unlikely(reg == SDHCI_BLOCK_COUNT)) | |
153 | return; | |
154 | ||
155 | writew_relaxed(val, host->ioaddr + reg); | |
156 | } | |
157 | ||
158 | static inline void sdhci_sprd_writeb(struct sdhci_host *host, u8 val, int reg) | |
159 | { | |
160 | /* | |
161 | * Since BIT(3) of SDHCI_SOFTWARE_RESET is reserved according to the | |
162 | * standard specification, sdhci_reset() write this register directly | |
163 | * without checking other reserved bits, that will clear BIT(3) which | |
164 | * is defined as hardware reset on Spreadtrum's platform and clearing | |
165 | * it by mistake will lead the card not work. So here we need to work | |
166 | * around it. | |
167 | */ | |
168 | if (unlikely(reg == SDHCI_SOFTWARE_RESET)) { | |
169 | if (readb_relaxed(host->ioaddr + reg) & SDHCI_HW_RESET_CARD) | |
170 | val |= SDHCI_HW_RESET_CARD; | |
171 | } | |
172 | ||
173 | writeb_relaxed(val, host->ioaddr + reg); | |
174 | } | |
175 | ||
176 | static inline void sdhci_sprd_sd_clk_off(struct sdhci_host *host) | |
177 | { | |
178 | u16 ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); | |
179 | ||
180 | ctrl &= ~SDHCI_CLOCK_CARD_EN; | |
181 | sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); | |
182 | } | |
183 | ||
494c11e1 BW |
184 | static inline void sdhci_sprd_sd_clk_on(struct sdhci_host *host) |
185 | { | |
186 | u16 ctrl; | |
187 | ||
188 | ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); | |
189 | ctrl |= SDHCI_CLOCK_CARD_EN; | |
190 | sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); | |
191 | } | |
192 | ||
fb8bd90f CZ |
193 | static inline void |
194 | sdhci_sprd_set_dll_invert(struct sdhci_host *host, u32 mask, bool en) | |
195 | { | |
196 | u32 dll_dly_offset; | |
197 | ||
198 | dll_dly_offset = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET); | |
199 | if (en) | |
200 | dll_dly_offset |= mask; | |
201 | else | |
202 | dll_dly_offset &= ~mask; | |
203 | sdhci_writel(host, dll_dly_offset, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET); | |
204 | } | |
205 | ||
206 | static inline u32 sdhci_sprd_calc_div(u32 base_clk, u32 clk) | |
207 | { | |
208 | u32 div; | |
209 | ||
210 | /* select 2x clock source */ | |
211 | if (base_clk <= clk * 2) | |
212 | return 0; | |
213 | ||
214 | div = (u32) (base_clk / (clk * 2)); | |
215 | ||
216 | if ((base_clk / div) > (clk * 2)) | |
217 | div++; | |
218 | ||
fb8bd90f CZ |
219 | if (div % 2) |
220 | div = (div + 1) / 2; | |
221 | else | |
222 | div = div / 2; | |
223 | ||
d252e9b1 WC |
224 | if (div > SDHCI_SPRD_CLK_MAX_DIV) |
225 | div = SDHCI_SPRD_CLK_MAX_DIV; | |
226 | ||
fb8bd90f CZ |
227 | return div; |
228 | } | |
229 | ||
230 | static inline void _sdhci_sprd_set_clock(struct sdhci_host *host, | |
231 | unsigned int clk) | |
232 | { | |
233 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
234 | u32 div, val, mask; | |
235 | ||
efdaf275 | 236 | sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); |
fb8bd90f | 237 | |
efdaf275 CZ |
238 | div = sdhci_sprd_calc_div(sprd_host->base_rate, clk); |
239 | div = ((div & 0x300) >> 2) | ((div & 0xFF) << 8); | |
240 | sdhci_enable_clk(host, div); | |
fb8bd90f | 241 | |
ff874dbc WC |
242 | /* Enable CLK_AUTO when the clock is greater than 400K. */ |
243 | if (clk > 400000) { | |
244 | val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI); | |
245 | mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN | | |
246 | SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN; | |
247 | if (mask != (val & mask)) { | |
248 | val |= mask; | |
249 | sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI); | |
250 | } | |
fb8bd90f CZ |
251 | } |
252 | } | |
253 | ||
87a395c2 BW |
254 | static void sdhci_sprd_enable_phy_dll(struct sdhci_host *host) |
255 | { | |
256 | u32 tmp; | |
257 | ||
258 | tmp = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG); | |
259 | tmp &= ~(SDHCI_SPRD_DLL_EN | SDHCI_SPRD_DLL_ALL_CPST_EN); | |
260 | sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG); | |
261 | /* wait 1ms */ | |
262 | usleep_range(1000, 1250); | |
263 | ||
264 | tmp = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG); | |
265 | tmp |= SDHCI_SPRD_DLL_ALL_CPST_EN | SDHCI_SPRD_DLL_SEARCH_MODE | | |
266 | SDHCI_SPRD_DLL_INIT_COUNT | SDHCI_SPRD_DLL_PHASE_INTERNAL; | |
267 | sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG); | |
268 | /* wait 1ms */ | |
269 | usleep_range(1000, 1250); | |
270 | ||
271 | tmp = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG); | |
272 | tmp |= SDHCI_SPRD_DLL_EN; | |
273 | sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG); | |
274 | /* wait 1ms */ | |
275 | usleep_range(1000, 1250); | |
7f00917a ZL |
276 | |
277 | if (read_poll_timeout(sdhci_readl, tmp, (tmp & SDHCI_SPRD_DLL_LOCKED), | |
278 | 2000, USEC_PER_SEC, false, host, SDHCI_SPRD_REG_32_DLL_STS0)) { | |
279 | pr_err("%s: DLL locked fail!\n", mmc_hostname(host->mmc)); | |
280 | pr_info("%s: DLL_STS0 : 0x%x, DLL_CFG : 0x%x\n", | |
281 | mmc_hostname(host->mmc), | |
282 | sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_STS0), | |
283 | sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG)); | |
284 | } | |
87a395c2 BW |
285 | } |
286 | ||
fb8bd90f CZ |
287 | static void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock) |
288 | { | |
87a395c2 | 289 | bool en = false, clk_changed = false; |
fb8bd90f CZ |
290 | |
291 | if (clock == 0) { | |
292 | sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); | |
293 | } else if (clock != host->clock) { | |
294 | sdhci_sprd_sd_clk_off(host); | |
295 | _sdhci_sprd_set_clock(host, clock); | |
296 | ||
297 | if (clock <= 400000) | |
298 | en = true; | |
299 | sdhci_sprd_set_dll_invert(host, SDHCI_SPRD_BIT_CMD_DLY_INV | | |
300 | SDHCI_SPRD_BIT_POSRD_DLY_INV, en); | |
87a395c2 | 301 | clk_changed = true; |
fb8bd90f CZ |
302 | } else { |
303 | _sdhci_sprd_set_clock(host, clock); | |
304 | } | |
87a395c2 BW |
305 | |
306 | /* | |
307 | * According to the Spreadtrum SD host specification, when we changed | |
308 | * the clock to be more than 52M, we should enable the PHY DLL which | |
309 | * is used to track the clock frequency to make the clock work more | |
310 | * stable. Otherwise deviation may occur of the higher clock. | |
311 | */ | |
312 | if (clk_changed && clock > SDHCI_SPRD_PHY_DLL_CLK) | |
313 | sdhci_sprd_enable_phy_dll(host); | |
fb8bd90f CZ |
314 | } |
315 | ||
316 | static unsigned int sdhci_sprd_get_max_clock(struct sdhci_host *host) | |
317 | { | |
318 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
319 | ||
320 | return clk_round_rate(sprd_host->clk_sdio, ULONG_MAX); | |
321 | } | |
322 | ||
323 | static unsigned int sdhci_sprd_get_min_clock(struct sdhci_host *host) | |
324 | { | |
6e141772 | 325 | return 100000; |
fb8bd90f CZ |
326 | } |
327 | ||
328 | static void sdhci_sprd_set_uhs_signaling(struct sdhci_host *host, | |
329 | unsigned int timing) | |
330 | { | |
5f2f4e0d BW |
331 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); |
332 | struct mmc_host *mmc = host->mmc; | |
333 | u32 *p = sprd_host->phy_delay; | |
fb8bd90f CZ |
334 | u16 ctrl_2; |
335 | ||
336 | if (timing == host->timing) | |
337 | return; | |
338 | ||
339 | ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); | |
340 | /* Select Bus Speed Mode for host */ | |
341 | ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; | |
342 | switch (timing) { | |
343 | case MMC_TIMING_UHS_SDR12: | |
344 | ctrl_2 |= SDHCI_CTRL_UHS_SDR12; | |
345 | break; | |
346 | case MMC_TIMING_MMC_HS: | |
347 | case MMC_TIMING_SD_HS: | |
348 | case MMC_TIMING_UHS_SDR25: | |
349 | ctrl_2 |= SDHCI_CTRL_UHS_SDR25; | |
350 | break; | |
351 | case MMC_TIMING_UHS_SDR50: | |
352 | ctrl_2 |= SDHCI_CTRL_UHS_SDR50; | |
353 | break; | |
354 | case MMC_TIMING_UHS_SDR104: | |
355 | ctrl_2 |= SDHCI_CTRL_UHS_SDR104; | |
356 | break; | |
357 | case MMC_TIMING_UHS_DDR50: | |
358 | case MMC_TIMING_MMC_DDR52: | |
359 | ctrl_2 |= SDHCI_CTRL_UHS_DDR50; | |
360 | break; | |
361 | case MMC_TIMING_MMC_HS200: | |
362 | ctrl_2 |= SDHCI_SPRD_CTRL_HS200; | |
363 | break; | |
364 | case MMC_TIMING_MMC_HS400: | |
365 | ctrl_2 |= SDHCI_SPRD_CTRL_HS400; | |
366 | break; | |
367 | default: | |
368 | break; | |
369 | } | |
370 | ||
371 | sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); | |
5f2f4e0d BW |
372 | |
373 | if (!mmc->ios.enhanced_strobe) | |
374 | sdhci_writel(host, p[timing], SDHCI_SPRD_REG_32_DLL_DLY); | |
fb8bd90f CZ |
375 | } |
376 | ||
377 | static void sdhci_sprd_hw_reset(struct sdhci_host *host) | |
378 | { | |
379 | int val; | |
380 | ||
381 | /* | |
382 | * Note: don't use sdhci_writeb() API here since it is redirected to | |
383 | * sdhci_sprd_writeb() in which we have a workaround for | |
384 | * SDHCI_SOFTWARE_RESET which would make bit SDHCI_HW_RESET_CARD can | |
385 | * not be cleared. | |
386 | */ | |
387 | val = readb_relaxed(host->ioaddr + SDHCI_SOFTWARE_RESET); | |
388 | val &= ~SDHCI_HW_RESET_CARD; | |
389 | writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET); | |
390 | /* wait for 10 us */ | |
391 | usleep_range(10, 20); | |
392 | ||
393 | val |= SDHCI_HW_RESET_CARD; | |
394 | writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET); | |
395 | usleep_range(300, 500); | |
396 | } | |
397 | ||
7486831d BW |
398 | static unsigned int sdhci_sprd_get_max_timeout_count(struct sdhci_host *host) |
399 | { | |
400 | /* The Spredtrum controller actual maximum timeout count is 1 << 31 */ | |
401 | return 1 << 31; | |
402 | } | |
403 | ||
4eae8cbd CZ |
404 | static unsigned int sdhci_sprd_get_ro(struct sdhci_host *host) |
405 | { | |
406 | return 0; | |
407 | } | |
408 | ||
f4498549 BW |
409 | static void sdhci_sprd_request_done(struct sdhci_host *host, |
410 | struct mmc_request *mrq) | |
411 | { | |
412 | /* Validate if the request was from software queue firstly. */ | |
413 | if (mmc_hsq_finalize_request(host->mmc, mrq)) | |
414 | return; | |
415 | ||
0cb231f1 | 416 | mmc_request_done(host->mmc, mrq); |
f4498549 BW |
417 | } |
418 | ||
fb8bd90f CZ |
419 | static struct sdhci_ops sdhci_sprd_ops = { |
420 | .read_l = sdhci_sprd_readl, | |
421 | .write_l = sdhci_sprd_writel, | |
96147082 | 422 | .write_w = sdhci_sprd_writew, |
fb8bd90f CZ |
423 | .write_b = sdhci_sprd_writeb, |
424 | .set_clock = sdhci_sprd_set_clock, | |
425 | .get_max_clock = sdhci_sprd_get_max_clock, | |
426 | .get_min_clock = sdhci_sprd_get_min_clock, | |
427 | .set_bus_width = sdhci_set_bus_width, | |
428 | .reset = sdhci_reset, | |
429 | .set_uhs_signaling = sdhci_sprd_set_uhs_signaling, | |
430 | .hw_reset = sdhci_sprd_hw_reset, | |
7486831d | 431 | .get_max_timeout_count = sdhci_sprd_get_max_timeout_count, |
4eae8cbd | 432 | .get_ro = sdhci_sprd_get_ro, |
f4498549 | 433 | .request_done = sdhci_sprd_request_done, |
fb8bd90f CZ |
434 | }; |
435 | ||
61ab64e2 BW |
436 | static void sdhci_sprd_check_auto_cmd23(struct mmc_host *mmc, |
437 | struct mmc_request *mrq) | |
fb8bd90f CZ |
438 | { |
439 | struct sdhci_host *host = mmc_priv(mmc); | |
440 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
441 | ||
442 | host->flags |= sprd_host->flags & SDHCI_AUTO_CMD23; | |
443 | ||
444 | /* | |
445 | * From version 4.10 onward, ARGUMENT2 register is also as 32-bit | |
446 | * block count register which doesn't support stuff bits of | |
447 | * CMD23 argument on Spreadtrum's sd host controller. | |
448 | */ | |
449 | if (host->version >= SDHCI_SPEC_410 && | |
450 | mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) && | |
451 | (host->flags & SDHCI_AUTO_CMD23)) | |
452 | host->flags &= ~SDHCI_AUTO_CMD23; | |
61ab64e2 BW |
453 | } |
454 | ||
455 | static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq) | |
456 | { | |
457 | sdhci_sprd_check_auto_cmd23(mmc, mrq); | |
fb8bd90f CZ |
458 | |
459 | sdhci_request(mmc, mrq); | |
460 | } | |
461 | ||
61ab64e2 | 462 | static int sdhci_sprd_request_atomic(struct mmc_host *mmc, |
0cb231f1 | 463 | struct mmc_request *mrq) |
61ab64e2 BW |
464 | { |
465 | sdhci_sprd_check_auto_cmd23(mmc, mrq); | |
466 | ||
467 | return sdhci_request_atomic(mmc, mrq); | |
468 | } | |
469 | ||
eef9e0a6 BW |
470 | static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) |
471 | { | |
29ca763f BW |
472 | struct sdhci_host *host = mmc_priv(mmc); |
473 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
eef9e0a6 BW |
474 | int ret; |
475 | ||
476 | if (!IS_ERR(mmc->supply.vqmmc)) { | |
477 | ret = mmc_regulator_set_vqmmc(mmc, ios); | |
9cbe0fc8 | 478 | if (ret < 0) { |
eef9e0a6 BW |
479 | pr_err("%s: Switching signalling voltage failed\n", |
480 | mmc_hostname(mmc)); | |
481 | return ret; | |
482 | } | |
483 | } | |
484 | ||
29ca763f | 485 | if (IS_ERR(sprd_host->pinctrl)) |
dd30dcfa | 486 | goto reset; |
29ca763f BW |
487 | |
488 | switch (ios->signal_voltage) { | |
489 | case MMC_SIGNAL_VOLTAGE_180: | |
490 | ret = pinctrl_select_state(sprd_host->pinctrl, | |
491 | sprd_host->pins_uhs); | |
492 | if (ret) { | |
493 | pr_err("%s: failed to select uhs pin state\n", | |
494 | mmc_hostname(mmc)); | |
495 | return ret; | |
496 | } | |
497 | break; | |
498 | ||
499 | default: | |
df561f66 | 500 | fallthrough; |
29ca763f BW |
501 | case MMC_SIGNAL_VOLTAGE_330: |
502 | ret = pinctrl_select_state(sprd_host->pinctrl, | |
503 | sprd_host->pins_default); | |
504 | if (ret) { | |
505 | pr_err("%s: failed to select default pin state\n", | |
506 | mmc_hostname(mmc)); | |
507 | return ret; | |
508 | } | |
509 | break; | |
510 | } | |
511 | ||
512 | /* Wait for 300 ~ 500 us for pin state stable */ | |
513 | usleep_range(300, 500); | |
dd30dcfa WC |
514 | |
515 | reset: | |
29ca763f BW |
516 | sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); |
517 | ||
eef9e0a6 BW |
518 | return 0; |
519 | } | |
520 | ||
494c11e1 BW |
521 | static void sdhci_sprd_hs400_enhanced_strobe(struct mmc_host *mmc, |
522 | struct mmc_ios *ios) | |
523 | { | |
524 | struct sdhci_host *host = mmc_priv(mmc); | |
5f2f4e0d BW |
525 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); |
526 | u32 *p = sprd_host->phy_delay; | |
494c11e1 BW |
527 | u16 ctrl_2; |
528 | ||
529 | if (!ios->enhanced_strobe) | |
530 | return; | |
531 | ||
532 | sdhci_sprd_sd_clk_off(host); | |
533 | ||
534 | /* Set HS400 enhanced strobe mode */ | |
535 | ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); | |
536 | ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; | |
537 | ctrl_2 |= SDHCI_SPRD_CTRL_HS400ES; | |
538 | sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); | |
539 | ||
540 | sdhci_sprd_sd_clk_on(host); | |
5f2f4e0d BW |
541 | |
542 | /* Set the PHY DLL delay value for HS400 enhanced strobe mode */ | |
543 | sdhci_writel(host, p[MMC_TIMING_MMC_HS400 + 1], | |
544 | SDHCI_SPRD_REG_32_DLL_DLY); | |
545 | } | |
546 | ||
d83d251b WC |
547 | static int mmc_send_tuning_cmd(struct mmc_card *card) |
548 | { | |
549 | return mmc_send_status(card, NULL); | |
550 | } | |
551 | ||
552 | static int mmc_send_tuning_data(struct mmc_card *card) | |
553 | { | |
554 | u8 *status; | |
555 | int ret; | |
556 | ||
557 | status = kmalloc(64, GFP_KERNEL); | |
558 | if (!status) | |
559 | return -ENOMEM; | |
560 | ||
561 | ret = mmc_sd_switch(card, 0, 0, 0, status); | |
562 | ||
563 | kfree(status); | |
564 | ||
565 | return ret; | |
566 | } | |
567 | ||
568 | static int sdhci_sprd_get_best_clk_sample(struct mmc_host *mmc, u8 *value) | |
569 | { | |
570 | int range_end = SDHCI_SPRD_MAX_RANGE; | |
571 | int range_length = 0; | |
572 | int middle_range = 0; | |
573 | int count = 0; | |
574 | int i; | |
575 | ||
576 | for (i = 0; i <= SDHCI_SPRD_MAX_RANGE; i++) { | |
577 | if (value[i]) { | |
578 | pr_debug("%s: tuning ok: %d\n", mmc_hostname(mmc), i); | |
579 | count++; | |
580 | } else { | |
581 | pr_debug("%s: tuning fail: %d\n", mmc_hostname(mmc), i); | |
582 | if (range_length < count) { | |
583 | range_length = count; | |
584 | range_end = i - 1; | |
585 | count = 0; | |
586 | } | |
587 | } | |
588 | } | |
589 | ||
590 | if (!count) | |
591 | return -EIO; | |
592 | ||
593 | if (count > range_length) { | |
594 | range_length = count; | |
595 | range_end = i - 1; | |
596 | } | |
597 | ||
598 | middle_range = range_end - (range_length - 1) / 2; | |
599 | ||
600 | return middle_range; | |
601 | } | |
602 | ||
603 | static int sdhci_sprd_tuning(struct mmc_host *mmc, struct mmc_card *card, | |
604 | enum sdhci_sprd_tuning_type type) | |
605 | { | |
606 | struct sdhci_host *host = mmc_priv(mmc); | |
607 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
608 | u32 *p = sprd_host->phy_delay; | |
609 | u32 dll_cfg, dll_dly; | |
610 | int best_clk_sample; | |
611 | int err = 0; | |
612 | u8 *value; | |
613 | int i; | |
614 | ||
615 | value = kmalloc(SDHCI_SPRD_MAX_RANGE + 1, GFP_KERNEL); | |
616 | if (!value) | |
617 | return -ENOMEM; | |
618 | ||
619 | sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | |
620 | ||
621 | dll_cfg = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG); | |
622 | dll_cfg &= ~SDHCI_SPRD_CPST_EN; | |
623 | sdhci_writel(host, dll_cfg, SDHCI_SPRD_REG_32_DLL_CFG); | |
624 | ||
625 | dll_dly = p[mmc->ios.timing]; | |
626 | ||
627 | for (i = 0; i <= SDHCI_SPRD_MAX_RANGE; i++) { | |
628 | if (type == SDHCI_SPRD_TUNING_SD_HS_CMD) { | |
629 | dll_dly &= ~SDHCI_SPRD_CMD_DLY_MASK; | |
630 | dll_dly |= ((i << 8) & SDHCI_SPRD_CMD_DLY_MASK); | |
631 | } else { | |
632 | dll_dly &= ~SDHCI_SPRD_POSRD_DLY_MASK; | |
633 | dll_dly |= ((i << 16) & SDHCI_SPRD_POSRD_DLY_MASK); | |
634 | } | |
635 | ||
636 | sdhci_writel(host, dll_dly, SDHCI_SPRD_REG_32_DLL_DLY); | |
637 | ||
638 | if (type == SDHCI_SPRD_TUNING_SD_HS_CMD) | |
639 | value[i] = !mmc_send_tuning_cmd(card); | |
640 | else | |
641 | value[i] = !mmc_send_tuning_data(card); | |
642 | } | |
643 | ||
644 | best_clk_sample = sdhci_sprd_get_best_clk_sample(mmc, value); | |
645 | if (best_clk_sample < 0) { | |
646 | dev_err(mmc_dev(host->mmc), "all tuning phase fail!\n"); | |
168054ca | 647 | err = best_clk_sample; |
d83d251b WC |
648 | goto out; |
649 | } | |
650 | ||
651 | if (type == SDHCI_SPRD_TUNING_SD_HS_CMD) { | |
652 | p[mmc->ios.timing] &= ~SDHCI_SPRD_CMD_DLY_MASK; | |
653 | p[mmc->ios.timing] |= ((best_clk_sample << 8) & SDHCI_SPRD_CMD_DLY_MASK); | |
654 | } else { | |
655 | p[mmc->ios.timing] &= ~(SDHCI_SPRD_POSRD_DLY_MASK); | |
656 | p[mmc->ios.timing] |= ((best_clk_sample << 16) & SDHCI_SPRD_POSRD_DLY_MASK); | |
657 | } | |
658 | ||
659 | pr_debug("%s: the best clk sample %d, delay value 0x%08x\n", | |
660 | mmc_hostname(host->mmc), best_clk_sample, p[mmc->ios.timing]); | |
661 | ||
662 | out: | |
663 | sdhci_writel(host, p[mmc->ios.timing], SDHCI_SPRD_REG_32_DLL_DLY); | |
664 | ||
665 | kfree(value); | |
666 | ||
667 | return err; | |
668 | } | |
669 | ||
670 | static int sdhci_sprd_prepare_sd_hs_cmd_tuning(struct mmc_host *mmc, struct mmc_card *card) | |
671 | { | |
672 | return sdhci_sprd_tuning(mmc, card, SDHCI_SPRD_TUNING_SD_HS_CMD); | |
673 | } | |
674 | ||
675 | static int sdhci_sprd_execute_sd_hs_data_tuning(struct mmc_host *mmc, struct mmc_card *card) | |
676 | { | |
677 | return sdhci_sprd_tuning(mmc, card, SDHCI_SPRD_TUNING_SD_HS_DATA); | |
678 | } | |
679 | ||
5f2f4e0d BW |
680 | static void sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host, |
681 | struct device_node *np) | |
682 | { | |
683 | u32 *p = sprd_host->phy_delay; | |
684 | int ret, i, index; | |
685 | u32 val[4]; | |
686 | ||
687 | for (i = 0; i < ARRAY_SIZE(sdhci_sprd_phy_cfgs); i++) { | |
688 | ret = of_property_read_u32_array(np, | |
689 | sdhci_sprd_phy_cfgs[i].property, val, 4); | |
690 | if (ret) | |
691 | continue; | |
692 | ||
693 | index = sdhci_sprd_phy_cfgs[i].timing; | |
694 | p[index] = val[0] | (val[1] << 8) | (val[2] << 16) | (val[3] << 24); | |
695 | } | |
494c11e1 BW |
696 | } |
697 | ||
fb8bd90f | 698 | static const struct sdhci_pltfm_data sdhci_sprd_pdata = { |
4324e54b | 699 | .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | |
924ea310 | 700 | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, |
fb8bd90f | 701 | .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 | |
6a526f66 CZ |
702 | SDHCI_QUIRK2_USE_32BIT_BLK_CNT | |
703 | SDHCI_QUIRK2_PRESET_VALUE_BROKEN, | |
fb8bd90f CZ |
704 | .ops = &sdhci_sprd_ops, |
705 | }; | |
706 | ||
707 | static int sdhci_sprd_probe(struct platform_device *pdev) | |
708 | { | |
709 | struct sdhci_host *host; | |
710 | struct sdhci_sprd_host *sprd_host; | |
f4498549 | 711 | struct mmc_hsq *hsq; |
fb8bd90f CZ |
712 | struct clk *clk; |
713 | int ret = 0; | |
714 | ||
715 | host = sdhci_pltfm_init(pdev, &sdhci_sprd_pdata, sizeof(*sprd_host)); | |
716 | if (IS_ERR(host)) | |
717 | return PTR_ERR(host); | |
718 | ||
719 | host->dma_mask = DMA_BIT_MASK(64); | |
720 | pdev->dev.dma_mask = &host->dma_mask; | |
721 | host->mmc_host_ops.request = sdhci_sprd_request; | |
494c11e1 BW |
722 | host->mmc_host_ops.hs400_enhanced_strobe = |
723 | sdhci_sprd_hs400_enhanced_strobe; | |
d83d251b WC |
724 | host->mmc_host_ops.prepare_sd_hs_tuning = |
725 | sdhci_sprd_prepare_sd_hs_cmd_tuning; | |
726 | host->mmc_host_ops.execute_sd_hs_tuning = | |
727 | sdhci_sprd_execute_sd_hs_data_tuning; | |
728 | ||
eef9e0a6 BW |
729 | /* |
730 | * We can not use the standard ops to change and detect the voltage | |
731 | * signal for Spreadtrum SD host controller, since our voltage regulator | |
732 | * for I/O is fixed in hardware, that means we do not need control | |
733 | * the standard SD host controller to change the I/O voltage. | |
734 | */ | |
735 | host->mmc_host_ops.start_signal_voltage_switch = | |
736 | sdhci_sprd_voltage_switch; | |
fb8bd90f CZ |
737 | |
738 | host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | | |
a049b5ae UH |
739 | MMC_CAP_WAIT_WHILE_BUSY; |
740 | ||
fb8bd90f CZ |
741 | ret = mmc_of_parse(host->mmc); |
742 | if (ret) | |
743 | goto pltfm_free; | |
744 | ||
61ab64e2 BW |
745 | if (!mmc_card_is_removable(host->mmc)) |
746 | host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic; | |
747 | else | |
748 | host->always_defer_done = true; | |
749 | ||
fb8bd90f | 750 | sprd_host = TO_SPRD_HOST(host); |
5f2f4e0d | 751 | sdhci_sprd_phy_param_parse(sprd_host, pdev->dev.of_node); |
fb8bd90f | 752 | |
29ca763f BW |
753 | sprd_host->pinctrl = devm_pinctrl_get(&pdev->dev); |
754 | if (!IS_ERR(sprd_host->pinctrl)) { | |
755 | sprd_host->pins_uhs = | |
756 | pinctrl_lookup_state(sprd_host->pinctrl, "state_uhs"); | |
757 | if (IS_ERR(sprd_host->pins_uhs)) { | |
758 | ret = PTR_ERR(sprd_host->pins_uhs); | |
759 | goto pltfm_free; | |
760 | } | |
761 | ||
762 | sprd_host->pins_default = | |
763 | pinctrl_lookup_state(sprd_host->pinctrl, "default"); | |
764 | if (IS_ERR(sprd_host->pins_default)) { | |
765 | ret = PTR_ERR(sprd_host->pins_default); | |
766 | goto pltfm_free; | |
767 | } | |
768 | } | |
769 | ||
fb8bd90f CZ |
770 | clk = devm_clk_get(&pdev->dev, "sdio"); |
771 | if (IS_ERR(clk)) { | |
772 | ret = PTR_ERR(clk); | |
773 | goto pltfm_free; | |
774 | } | |
775 | sprd_host->clk_sdio = clk; | |
776 | sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio); | |
777 | if (!sprd_host->base_rate) | |
778 | sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE; | |
779 | ||
780 | clk = devm_clk_get(&pdev->dev, "enable"); | |
781 | if (IS_ERR(clk)) { | |
782 | ret = PTR_ERR(clk); | |
783 | goto pltfm_free; | |
784 | } | |
785 | sprd_host->clk_enable = clk; | |
786 | ||
ebd88a38 BW |
787 | clk = devm_clk_get(&pdev->dev, "2x_enable"); |
788 | if (!IS_ERR(clk)) | |
789 | sprd_host->clk_2x_enable = clk; | |
790 | ||
fb8bd90f CZ |
791 | ret = clk_prepare_enable(sprd_host->clk_sdio); |
792 | if (ret) | |
793 | goto pltfm_free; | |
794 | ||
1d94717d | 795 | ret = clk_prepare_enable(sprd_host->clk_enable); |
fb8bd90f CZ |
796 | if (ret) |
797 | goto clk_disable; | |
798 | ||
ebd88a38 BW |
799 | ret = clk_prepare_enable(sprd_host->clk_2x_enable); |
800 | if (ret) | |
801 | goto clk_disable2; | |
802 | ||
fb8bd90f CZ |
803 | sdhci_sprd_init_config(host); |
804 | host->version = sdhci_readw(host, SDHCI_HOST_VERSION); | |
805 | sprd_host->version = ((host->version & SDHCI_VENDOR_VER_MASK) >> | |
806 | SDHCI_VENDOR_VER_SHIFT); | |
807 | ||
808 | pm_runtime_get_noresume(&pdev->dev); | |
809 | pm_runtime_set_active(&pdev->dev); | |
810 | pm_runtime_enable(&pdev->dev); | |
811 | pm_runtime_set_autosuspend_delay(&pdev->dev, 50); | |
812 | pm_runtime_use_autosuspend(&pdev->dev); | |
813 | pm_suspend_ignore_children(&pdev->dev, 1); | |
814 | ||
815 | sdhci_enable_v4_mode(host); | |
816 | ||
2f765c17 CZ |
817 | /* |
818 | * Supply the existing CAPS, but clear the UHS-I modes. This | |
819 | * will allow these modes to be specified only by device | |
820 | * tree properties through mmc_of_parse(). | |
821 | */ | |
924ea310 | 822 | sdhci_read_caps(host); |
2f765c17 CZ |
823 | host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | |
824 | SDHCI_SUPPORT_DDR50); | |
825 | ||
fb8bd90f CZ |
826 | ret = sdhci_setup_host(host); |
827 | if (ret) | |
828 | goto pm_runtime_disable; | |
829 | ||
830 | sprd_host->flags = host->flags; | |
831 | ||
f4498549 BW |
832 | hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); |
833 | if (!hsq) { | |
834 | ret = -ENOMEM; | |
835 | goto err_cleanup_host; | |
836 | } | |
837 | ||
838 | ret = mmc_hsq_init(hsq, host->mmc); | |
839 | if (ret) | |
840 | goto err_cleanup_host; | |
841 | ||
fb8bd90f CZ |
842 | ret = __sdhci_add_host(host); |
843 | if (ret) | |
844 | goto err_cleanup_host; | |
845 | ||
846 | pm_runtime_mark_last_busy(&pdev->dev); | |
847 | pm_runtime_put_autosuspend(&pdev->dev); | |
848 | ||
849 | return 0; | |
850 | ||
851 | err_cleanup_host: | |
852 | sdhci_cleanup_host(host); | |
853 | ||
854 | pm_runtime_disable: | |
fc62113b | 855 | pm_runtime_put_noidle(&pdev->dev); |
fb8bd90f CZ |
856 | pm_runtime_disable(&pdev->dev); |
857 | pm_runtime_set_suspended(&pdev->dev); | |
858 | ||
ebd88a38 BW |
859 | clk_disable_unprepare(sprd_host->clk_2x_enable); |
860 | ||
861 | clk_disable2: | |
fb8bd90f CZ |
862 | clk_disable_unprepare(sprd_host->clk_enable); |
863 | ||
864 | clk_disable: | |
865 | clk_disable_unprepare(sprd_host->clk_sdio); | |
866 | ||
867 | pltfm_free: | |
868 | sdhci_pltfm_free(pdev); | |
869 | return ret; | |
870 | } | |
871 | ||
c618ba0f | 872 | static void sdhci_sprd_remove(struct platform_device *pdev) |
fb8bd90f CZ |
873 | { |
874 | struct sdhci_host *host = platform_get_drvdata(pdev); | |
875 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
fb8bd90f | 876 | |
c9c256a8 CJ |
877 | sdhci_remove_host(host, 0); |
878 | ||
fb8bd90f CZ |
879 | clk_disable_unprepare(sprd_host->clk_sdio); |
880 | clk_disable_unprepare(sprd_host->clk_enable); | |
ebd88a38 | 881 | clk_disable_unprepare(sprd_host->clk_2x_enable); |
fb8bd90f | 882 | |
c9c256a8 | 883 | sdhci_pltfm_free(pdev); |
fb8bd90f CZ |
884 | } |
885 | ||
886 | static const struct of_device_id sdhci_sprd_of_match[] = { | |
887 | { .compatible = "sprd,sdhci-r11", }, | |
888 | { } | |
889 | }; | |
890 | MODULE_DEVICE_TABLE(of, sdhci_sprd_of_match); | |
891 | ||
892 | #ifdef CONFIG_PM | |
893 | static int sdhci_sprd_runtime_suspend(struct device *dev) | |
894 | { | |
895 | struct sdhci_host *host = dev_get_drvdata(dev); | |
896 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
897 | ||
f4498549 | 898 | mmc_hsq_suspend(host->mmc); |
fb8bd90f CZ |
899 | sdhci_runtime_suspend_host(host); |
900 | ||
901 | clk_disable_unprepare(sprd_host->clk_sdio); | |
902 | clk_disable_unprepare(sprd_host->clk_enable); | |
ebd88a38 | 903 | clk_disable_unprepare(sprd_host->clk_2x_enable); |
fb8bd90f CZ |
904 | |
905 | return 0; | |
906 | } | |
907 | ||
908 | static int sdhci_sprd_runtime_resume(struct device *dev) | |
909 | { | |
910 | struct sdhci_host *host = dev_get_drvdata(dev); | |
911 | struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); | |
912 | int ret; | |
913 | ||
ebd88a38 | 914 | ret = clk_prepare_enable(sprd_host->clk_2x_enable); |
fb8bd90f CZ |
915 | if (ret) |
916 | return ret; | |
917 | ||
ebd88a38 BW |
918 | ret = clk_prepare_enable(sprd_host->clk_enable); |
919 | if (ret) | |
920 | goto clk_2x_disable; | |
921 | ||
fb8bd90f | 922 | ret = clk_prepare_enable(sprd_host->clk_sdio); |
ebd88a38 BW |
923 | if (ret) |
924 | goto clk_disable; | |
fb8bd90f | 925 | |
c6303c5d | 926 | sdhci_runtime_resume_host(host, 1); |
f4498549 BW |
927 | mmc_hsq_resume(host->mmc); |
928 | ||
fb8bd90f | 929 | return 0; |
ebd88a38 BW |
930 | |
931 | clk_disable: | |
932 | clk_disable_unprepare(sprd_host->clk_enable); | |
933 | ||
934 | clk_2x_disable: | |
935 | clk_disable_unprepare(sprd_host->clk_2x_enable); | |
936 | ||
937 | return ret; | |
fb8bd90f CZ |
938 | } |
939 | #endif | |
940 | ||
941 | static const struct dev_pm_ops sdhci_sprd_pm_ops = { | |
942 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | |
943 | pm_runtime_force_resume) | |
944 | SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend, | |
945 | sdhci_sprd_runtime_resume, NULL) | |
946 | }; | |
947 | ||
948 | static struct platform_driver sdhci_sprd_driver = { | |
949 | .probe = sdhci_sprd_probe, | |
c618ba0f | 950 | .remove_new = sdhci_sprd_remove, |
fb8bd90f CZ |
951 | .driver = { |
952 | .name = "sdhci_sprd_r11", | |
d86472ae | 953 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
a96e6523 | 954 | .of_match_table = sdhci_sprd_of_match, |
fb8bd90f CZ |
955 | .pm = &sdhci_sprd_pm_ops, |
956 | }, | |
957 | }; | |
958 | module_platform_driver(sdhci_sprd_driver); | |
959 | ||
960 | MODULE_DESCRIPTION("Spreadtrum sdio host controller r11 driver"); | |
961 | MODULE_LICENSE("GPL v2"); | |
962 | MODULE_ALIAS("platform:sdhci-sprd-r11"); |