1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2010 Marvell International Ltd. |
4 | * Zhangfei Gao <zhangfei.gao@marvell.com> |
5 | * Kevin Wang <dwang4@marvell.com> |
6 | * Mingwei Wang <mwwang@marvell.com> |
7 | * Philip Rakity <prakity@marvell.com> |
8 | * Mark Brown <markb@marvell.com> |
9 | */ |
10 | #include <linux/err.h> |
11 | #include <linux/init.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/clk.h> |
14 | #include <linux/io.h> |
15 | #include <linux/mmc/card.h> |
16 | #include <linux/mmc/host.h> |
17 | #include <linux/platform_data/pxa_sdhci.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/delay.h> |
20 | #include <linux/module.h> |
21 | #include <linux/of.h> |
22 | #include <linux/of_device.h> |
23 | #include <linux/pm.h> |
24 | #include <linux/pm_runtime.h> |
25 | #include <linux/mbus.h> |
26 | |
27 | #include "sdhci.h" |
28 | #include "sdhci-pltfm.h" |
29 | |
30 | #define PXAV3_RPM_DELAY_MS 50 |
31 | |
32 | #define SD_CLOCK_BURST_SIZE_SETUP 0x10A |
33 | #define SDCLK_SEL 0x100 |
34 | #define SDCLK_DELAY_SHIFT 9 |
35 | #define SDCLK_DELAY_MASK 0x1f |
36 | |
37 | #define SD_CFG_FIFO_PARAM 0x100 |
38 | #define SDCFG_GEN_PAD_CLK_ON (1<<6) |
39 | #define SDCFG_GEN_PAD_CLK_CNT_MASK 0xFF |
40 | #define SDCFG_GEN_PAD_CLK_CNT_SHIFT 24 |
41 | |
42 | #define SD_SPI_MODE 0x108 |
43 | #define SD_CE_ATA_1 0x10C |
44 | |
45 | #define SD_CE_ATA_2 0x10E |
46 | #define SDCE_MISC_INT (1<<2) |
47 | #define SDCE_MISC_INT_EN (1<<1) |
48 | |
49 | struct sdhci_pxa { |
50 | struct clk *clk_core; |
51 | struct clk *clk_io; |
52 | u8 power_mode; |
53 | void __iomem *sdio3_conf_reg; |
54 | }; |
55 | |
56 | /* |
57 | * These registers are relative to the second register region, for the |
58 | * MBus bridge. |
59 | */ |
60 | #define SDHCI_WINDOW_CTRL(i) (0x80 + ((i) << 3)) |
61 | #define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3)) |
62 | #define SDHCI_MAX_WIN_NUM 8 |
63 | |
64 | /* |
65 | * Fields below belong to SDIO3 Configuration Register (third register |
66 | * region for the Armada 38x flavor) |
67 | */ |
68 | |
69 | #define SDIO3_CONF_CLK_INV BIT(0) |
70 | #define SDIO3_CONF_SD_FB_CLK BIT(2) |
71 | |
72 | static int mv_conf_mbus_windows(struct platform_device *pdev, |
73 | const struct mbus_dram_target_info *dram) |
74 | { |
75 | int i; |
76 | void __iomem *regs; |
77 | struct resource *res; |
78 | |
79 | if (!dram) { |
80 | dev_err(&pdev->dev, "no mbus dram info\n" ); |
81 | return -EINVAL; |
82 | } |
83 | |
84 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
85 | if (!res) { |
86 | dev_err(&pdev->dev, "cannot get mbus registers\n" ); |
87 | return -EINVAL; |
88 | } |
89 | |
90 | regs = ioremap(offset: res->start, size: resource_size(res)); |
91 | if (!regs) { |
92 | dev_err(&pdev->dev, "cannot map mbus registers\n" ); |
93 | return -ENOMEM; |
94 | } |
95 | |
96 | for (i = 0; i < SDHCI_MAX_WIN_NUM; i++) { |
97 | writel(val: 0, addr: regs + SDHCI_WINDOW_CTRL(i)); |
98 | writel(val: 0, addr: regs + SDHCI_WINDOW_BASE(i)); |
99 | } |
100 | |
101 | for (i = 0; i < dram->num_cs; i++) { |
102 | const struct mbus_dram_window *cs = dram->cs + i; |
103 | |
104 | /* Write size, attributes and target id to control register */ |
105 | writel(val: ((cs->size - 1) & 0xffff0000) | |
106 | (cs->mbus_attr << 8) | |
107 | (dram->mbus_dram_target_id << 4) | 1, |
108 | addr: regs + SDHCI_WINDOW_CTRL(i)); |
109 | /* Write base address to base register */ |
110 | writel(val: cs->base, addr: regs + SDHCI_WINDOW_BASE(i)); |
111 | } |
112 | |
113 | iounmap(addr: regs); |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | static int armada_38x_quirks(struct platform_device *pdev, |
119 | struct sdhci_host *host) |
120 | { |
121 | struct device_node *np = pdev->dev.of_node; |
122 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
123 | struct sdhci_pxa *pxa = sdhci_pltfm_priv(host: pltfm_host); |
124 | struct resource *res; |
125 | |
126 | host->quirks &= ~SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; |
127 | |
128 | sdhci_read_caps(host); |
129 | |
130 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
131 | "conf-sdio3" ); |
132 | if (res) { |
133 | pxa->sdio3_conf_reg = devm_ioremap_resource(dev: &pdev->dev, res); |
134 | if (IS_ERR(ptr: pxa->sdio3_conf_reg)) |
135 | return PTR_ERR(ptr: pxa->sdio3_conf_reg); |
136 | } else { |
137 | /* |
138 | * According to erratum 'FE-2946959' both SDR50 and DDR50 |
139 | * modes require specific clock adjustments in SDIO3 |
140 | * Configuration register, if the adjustment is not done, |
141 | * remove them from the capabilities. |
142 | */ |
143 | host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); |
144 | |
145 | dev_warn(&pdev->dev, "conf-sdio3 register not found: disabling SDR50 and DDR50 modes.\nConsider updating your dtb\n" ); |
146 | } |
147 | |
148 | /* |
149 | * According to erratum 'ERR-7878951' Armada 38x SDHCI |
150 | * controller has different capabilities than the ones shown |
151 | * in its registers |
152 | */ |
153 | if (of_property_read_bool(np, propname: "no-1-8-v" )) { |
154 | host->caps &= ~SDHCI_CAN_VDD_180; |
155 | host->mmc->caps &= ~MMC_CAP_1_8V_DDR; |
156 | } else { |
157 | host->caps &= ~SDHCI_CAN_VDD_330; |
158 | } |
159 | host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_USE_SDR50_TUNING); |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | static void pxav3_reset(struct sdhci_host *host, u8 mask) |
165 | { |
166 | struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); |
167 | struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; |
168 | |
169 | sdhci_reset(host, mask); |
170 | |
171 | if (mask == SDHCI_RESET_ALL) { |
172 | /* |
173 | * tune timing of read data/command when crc error happen |
174 | * no performance impact |
175 | */ |
176 | if (pdata && 0 != pdata->clk_delay_cycles) { |
177 | u16 tmp; |
178 | |
179 | tmp = readw(addr: host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); |
180 | tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) |
181 | << SDCLK_DELAY_SHIFT; |
182 | tmp |= SDCLK_SEL; |
183 | writew(val: tmp, addr: host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); |
184 | } |
185 | } |
186 | } |
187 | |
188 | #define MAX_WAIT_COUNT 5 |
189 | static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode) |
190 | { |
191 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
192 | struct sdhci_pxa *pxa = sdhci_pltfm_priv(host: pltfm_host); |
193 | u16 tmp; |
194 | int count; |
195 | |
196 | if (pxa->power_mode == MMC_POWER_UP |
197 | && power_mode == MMC_POWER_ON) { |
198 | |
199 | dev_dbg(mmc_dev(host->mmc), |
200 | "%s: slot->power_mode = %d," |
201 | "ios->power_mode = %d\n" , |
202 | __func__, |
203 | pxa->power_mode, |
204 | power_mode); |
205 | |
206 | /* set we want notice of when 74 clocks are sent */ |
207 | tmp = readw(addr: host->ioaddr + SD_CE_ATA_2); |
208 | tmp |= SDCE_MISC_INT_EN; |
209 | writew(val: tmp, addr: host->ioaddr + SD_CE_ATA_2); |
210 | |
211 | /* start sending the 74 clocks */ |
212 | tmp = readw(addr: host->ioaddr + SD_CFG_FIFO_PARAM); |
213 | tmp |= SDCFG_GEN_PAD_CLK_ON; |
214 | writew(val: tmp, addr: host->ioaddr + SD_CFG_FIFO_PARAM); |
215 | |
216 | /* slowest speed is about 100KHz or 10usec per clock */ |
217 | udelay(740); |
218 | count = 0; |
219 | |
220 | while (count++ < MAX_WAIT_COUNT) { |
221 | if ((readw(addr: host->ioaddr + SD_CE_ATA_2) |
222 | & SDCE_MISC_INT) == 0) |
223 | break; |
224 | udelay(10); |
225 | } |
226 | |
227 | if (count == MAX_WAIT_COUNT) |
228 | dev_warn(mmc_dev(host->mmc), "74 clock interrupt not cleared\n" ); |
229 | |
230 | /* clear the interrupt bit if posted */ |
231 | tmp = readw(addr: host->ioaddr + SD_CE_ATA_2); |
232 | tmp |= SDCE_MISC_INT; |
233 | writew(val: tmp, addr: host->ioaddr + SD_CE_ATA_2); |
234 | } |
235 | pxa->power_mode = power_mode; |
236 | } |
237 | |
238 | static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) |
239 | { |
240 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
241 | struct sdhci_pxa *pxa = sdhci_pltfm_priv(host: pltfm_host); |
242 | u16 ctrl_2; |
243 | |
244 | /* |
245 | * Set V18_EN -- UHS modes do not work without this. |
246 | * does not change signaling voltage |
247 | */ |
248 | ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); |
249 | |
250 | /* Select Bus Speed Mode for host */ |
251 | ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; |
252 | switch (uhs) { |
253 | case MMC_TIMING_UHS_SDR12: |
254 | ctrl_2 |= SDHCI_CTRL_UHS_SDR12; |
255 | break; |
256 | case MMC_TIMING_UHS_SDR25: |
257 | ctrl_2 |= SDHCI_CTRL_UHS_SDR25; |
258 | break; |
259 | case MMC_TIMING_UHS_SDR50: |
260 | ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180; |
261 | break; |
262 | case MMC_TIMING_UHS_SDR104: |
263 | ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180; |
264 | break; |
265 | case MMC_TIMING_MMC_DDR52: |
266 | case MMC_TIMING_UHS_DDR50: |
267 | ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180; |
268 | break; |
269 | } |
270 | |
271 | /* |
272 | * Update SDIO3 Configuration register according to erratum |
273 | * FE-2946959 |
274 | */ |
275 | if (pxa->sdio3_conf_reg) { |
276 | u8 reg_val = readb(addr: pxa->sdio3_conf_reg); |
277 | |
278 | if (uhs == MMC_TIMING_UHS_SDR50 || |
279 | uhs == MMC_TIMING_UHS_DDR50) { |
280 | reg_val &= ~SDIO3_CONF_CLK_INV; |
281 | reg_val |= SDIO3_CONF_SD_FB_CLK; |
282 | } else if (uhs == MMC_TIMING_MMC_HS) { |
283 | reg_val &= ~SDIO3_CONF_CLK_INV; |
284 | reg_val &= ~SDIO3_CONF_SD_FB_CLK; |
285 | } else { |
286 | reg_val |= SDIO3_CONF_CLK_INV; |
287 | reg_val &= ~SDIO3_CONF_SD_FB_CLK; |
288 | } |
289 | writeb(val: reg_val, addr: pxa->sdio3_conf_reg); |
290 | } |
291 | |
292 | sdhci_writew(host, val: ctrl_2, SDHCI_HOST_CONTROL2); |
293 | dev_dbg(mmc_dev(host->mmc), |
294 | "%s uhs = %d, ctrl_2 = %04X\n" , |
295 | __func__, uhs, ctrl_2); |
296 | } |
297 | |
298 | static void pxav3_set_power(struct sdhci_host *host, unsigned char mode, |
299 | unsigned short vdd) |
300 | { |
301 | struct mmc_host *mmc = host->mmc; |
302 | u8 pwr = host->pwr; |
303 | |
304 | sdhci_set_power_noreg(host, mode, vdd); |
305 | |
306 | if (host->pwr == pwr) |
307 | return; |
308 | |
309 | if (host->pwr == 0) |
310 | vdd = 0; |
311 | |
312 | if (!IS_ERR(ptr: mmc->supply.vmmc)) |
313 | mmc_regulator_set_ocr(mmc, supply: mmc->supply.vmmc, vdd_bit: vdd); |
314 | } |
315 | |
316 | static const struct sdhci_ops pxav3_sdhci_ops = { |
317 | .set_clock = sdhci_set_clock, |
318 | .set_power = pxav3_set_power, |
319 | .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, |
320 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
321 | .set_bus_width = sdhci_set_bus_width, |
322 | .reset = pxav3_reset, |
323 | .set_uhs_signaling = pxav3_set_uhs_signaling, |
324 | }; |
325 | |
326 | static const struct sdhci_pltfm_data sdhci_pxav3_pdata = { |
327 | .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
328 | | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
329 | | SDHCI_QUIRK_32BIT_ADMA_SIZE |
330 | | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, |
331 | .ops = &pxav3_sdhci_ops, |
332 | }; |
333 | |
334 | #ifdef CONFIG_OF |
335 | static const struct of_device_id sdhci_pxav3_of_match[] = { |
336 | { |
337 | .compatible = "mrvl,pxav3-mmc" , |
338 | }, |
339 | { |
340 | .compatible = "marvell,armada-380-sdhci" , |
341 | }, |
342 | {}, |
343 | }; |
344 | MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match); |
345 | |
346 | static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) |
347 | { |
348 | struct sdhci_pxa_platdata *pdata; |
349 | struct device_node *np = dev->of_node; |
350 | u32 clk_delay_cycles; |
351 | |
352 | pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL); |
353 | if (!pdata) |
354 | return NULL; |
355 | |
356 | if (!of_property_read_u32(np, propname: "mrvl,clk-delay-cycles" , |
357 | out_value: &clk_delay_cycles)) |
358 | pdata->clk_delay_cycles = clk_delay_cycles; |
359 | |
360 | return pdata; |
361 | } |
362 | #else |
363 | static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) |
364 | { |
365 | return NULL; |
366 | } |
367 | #endif |
368 | |
369 | static int sdhci_pxav3_probe(struct platform_device *pdev) |
370 | { |
371 | struct sdhci_pltfm_host *pltfm_host; |
372 | struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; |
373 | struct device *dev = &pdev->dev; |
374 | struct device_node *np = pdev->dev.of_node; |
375 | struct sdhci_host *host = NULL; |
376 | struct sdhci_pxa *pxa = NULL; |
377 | const struct of_device_id *match; |
378 | int ret; |
379 | |
380 | host = sdhci_pltfm_init(pdev, pdata: &sdhci_pxav3_pdata, priv_size: sizeof(*pxa)); |
381 | if (IS_ERR(ptr: host)) |
382 | return PTR_ERR(ptr: host); |
383 | |
384 | pltfm_host = sdhci_priv(host); |
385 | pxa = sdhci_pltfm_priv(host: pltfm_host); |
386 | |
387 | pxa->clk_io = devm_clk_get(dev, id: "io" ); |
388 | if (IS_ERR(ptr: pxa->clk_io)) |
389 | pxa->clk_io = devm_clk_get(dev, NULL); |
390 | if (IS_ERR(ptr: pxa->clk_io)) { |
391 | dev_err(dev, "failed to get io clock\n" ); |
392 | ret = PTR_ERR(ptr: pxa->clk_io); |
393 | goto err_clk_get; |
394 | } |
395 | pltfm_host->clk = pxa->clk_io; |
396 | clk_prepare_enable(clk: pxa->clk_io); |
397 | |
398 | pxa->clk_core = devm_clk_get(dev, id: "core" ); |
399 | if (!IS_ERR(ptr: pxa->clk_core)) |
400 | clk_prepare_enable(clk: pxa->clk_core); |
401 | |
402 | /* enable 1/8V DDR capable */ |
403 | host->mmc->caps |= MMC_CAP_1_8V_DDR; |
404 | |
405 | if (of_device_is_compatible(device: np, "marvell,armada-380-sdhci" )) { |
406 | ret = armada_38x_quirks(pdev, host); |
407 | if (ret < 0) |
408 | goto err_mbus_win; |
409 | ret = mv_conf_mbus_windows(pdev, dram: mv_mbus_dram_info()); |
410 | if (ret < 0) |
411 | goto err_mbus_win; |
412 | } |
413 | |
414 | match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), dev: &pdev->dev); |
415 | if (match) { |
416 | ret = mmc_of_parse(host: host->mmc); |
417 | if (ret) |
418 | goto err_of_parse; |
419 | sdhci_get_of_property(pdev); |
420 | pdata = pxav3_get_mmc_pdata(dev); |
421 | pdev->dev.platform_data = pdata; |
422 | } else if (pdata) { |
423 | /* on-chip device */ |
424 | if (pdata->flags & PXA_FLAG_CARD_PERMANENT) |
425 | host->mmc->caps |= MMC_CAP_NONREMOVABLE; |
426 | |
427 | /* If slot design supports 8 bit data, indicate this to MMC. */ |
428 | if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) |
429 | host->mmc->caps |= MMC_CAP_8_BIT_DATA; |
430 | |
431 | if (pdata->quirks) |
432 | host->quirks |= pdata->quirks; |
433 | if (pdata->quirks2) |
434 | host->quirks2 |= pdata->quirks2; |
435 | if (pdata->host_caps) |
436 | host->mmc->caps |= pdata->host_caps; |
437 | if (pdata->host_caps2) |
438 | host->mmc->caps2 |= pdata->host_caps2; |
439 | if (pdata->pm_caps) |
440 | host->mmc->pm_caps |= pdata->pm_caps; |
441 | } |
442 | |
443 | pm_runtime_get_noresume(dev: &pdev->dev); |
444 | pm_runtime_set_active(dev: &pdev->dev); |
445 | pm_runtime_set_autosuspend_delay(dev: &pdev->dev, PXAV3_RPM_DELAY_MS); |
446 | pm_runtime_use_autosuspend(dev: &pdev->dev); |
447 | pm_runtime_enable(dev: &pdev->dev); |
448 | pm_suspend_ignore_children(dev: &pdev->dev, enable: 1); |
449 | |
450 | ret = sdhci_add_host(host); |
451 | if (ret) |
452 | goto err_add_host; |
453 | |
454 | if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) |
455 | device_init_wakeup(dev: &pdev->dev, enable: 1); |
456 | |
457 | pm_runtime_put_autosuspend(dev: &pdev->dev); |
458 | |
459 | return 0; |
460 | |
461 | err_add_host: |
462 | pm_runtime_disable(dev: &pdev->dev); |
463 | pm_runtime_put_noidle(dev: &pdev->dev); |
464 | err_of_parse: |
465 | err_mbus_win: |
466 | clk_disable_unprepare(clk: pxa->clk_io); |
467 | clk_disable_unprepare(clk: pxa->clk_core); |
468 | err_clk_get: |
469 | sdhci_pltfm_free(pdev); |
470 | return ret; |
471 | } |
472 | |
473 | static void sdhci_pxav3_remove(struct platform_device *pdev) |
474 | { |
475 | struct sdhci_host *host = platform_get_drvdata(pdev); |
476 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
477 | struct sdhci_pxa *pxa = sdhci_pltfm_priv(host: pltfm_host); |
478 | |
479 | pm_runtime_get_sync(dev: &pdev->dev); |
480 | pm_runtime_disable(dev: &pdev->dev); |
481 | pm_runtime_put_noidle(dev: &pdev->dev); |
482 | |
483 | sdhci_remove_host(host, dead: 1); |
484 | |
485 | clk_disable_unprepare(clk: pxa->clk_io); |
486 | clk_disable_unprepare(clk: pxa->clk_core); |
487 | |
488 | sdhci_pltfm_free(pdev); |
489 | } |
490 | |
491 | #ifdef CONFIG_PM_SLEEP |
492 | static int sdhci_pxav3_suspend(struct device *dev) |
493 | { |
494 | int ret; |
495 | struct sdhci_host *host = dev_get_drvdata(dev); |
496 | |
497 | pm_runtime_get_sync(dev); |
498 | if (host->tuning_mode != SDHCI_TUNING_MODE_3) |
499 | mmc_retune_needed(host: host->mmc); |
500 | ret = sdhci_suspend_host(host); |
501 | pm_runtime_mark_last_busy(dev); |
502 | pm_runtime_put_autosuspend(dev); |
503 | |
504 | return ret; |
505 | } |
506 | |
507 | static int sdhci_pxav3_resume(struct device *dev) |
508 | { |
509 | int ret; |
510 | struct sdhci_host *host = dev_get_drvdata(dev); |
511 | |
512 | pm_runtime_get_sync(dev); |
513 | ret = sdhci_resume_host(host); |
514 | pm_runtime_mark_last_busy(dev); |
515 | pm_runtime_put_autosuspend(dev); |
516 | |
517 | return ret; |
518 | } |
519 | #endif |
520 | |
521 | #ifdef CONFIG_PM |
522 | static int sdhci_pxav3_runtime_suspend(struct device *dev) |
523 | { |
524 | struct sdhci_host *host = dev_get_drvdata(dev); |
525 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
526 | struct sdhci_pxa *pxa = sdhci_pltfm_priv(host: pltfm_host); |
527 | int ret; |
528 | |
529 | ret = sdhci_runtime_suspend_host(host); |
530 | if (ret) |
531 | return ret; |
532 | |
533 | if (host->tuning_mode != SDHCI_TUNING_MODE_3) |
534 | mmc_retune_needed(host: host->mmc); |
535 | |
536 | clk_disable_unprepare(clk: pxa->clk_io); |
537 | if (!IS_ERR(ptr: pxa->clk_core)) |
538 | clk_disable_unprepare(clk: pxa->clk_core); |
539 | |
540 | return 0; |
541 | } |
542 | |
543 | static int sdhci_pxav3_runtime_resume(struct device *dev) |
544 | { |
545 | struct sdhci_host *host = dev_get_drvdata(dev); |
546 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
547 | struct sdhci_pxa *pxa = sdhci_pltfm_priv(host: pltfm_host); |
548 | |
549 | clk_prepare_enable(clk: pxa->clk_io); |
550 | if (!IS_ERR(ptr: pxa->clk_core)) |
551 | clk_prepare_enable(clk: pxa->clk_core); |
552 | |
553 | return sdhci_runtime_resume_host(host, soft_reset: 0); |
554 | } |
555 | #endif |
556 | |
557 | static const struct dev_pm_ops sdhci_pxav3_pmops = { |
558 | SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume) |
559 | SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, |
560 | sdhci_pxav3_runtime_resume, NULL) |
561 | }; |
562 | |
563 | static struct platform_driver sdhci_pxav3_driver = { |
564 | .driver = { |
565 | .name = "sdhci-pxav3" , |
566 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
567 | .of_match_table = of_match_ptr(sdhci_pxav3_of_match), |
568 | .pm = &sdhci_pxav3_pmops, |
569 | }, |
570 | .probe = sdhci_pxav3_probe, |
571 | .remove_new = sdhci_pxav3_remove, |
572 | }; |
573 | |
574 | module_platform_driver(sdhci_pxav3_driver); |
575 | |
576 | MODULE_DESCRIPTION("SDHCI driver for pxav3" ); |
577 | MODULE_AUTHOR("Marvell International Ltd." ); |
578 | MODULE_LICENSE("GPL v2" ); |
579 | |
580 | |