Commit | Line | Data |
---|---|---|
95f25efe WS |
1 | /* |
2 | * Freescale eSDHC i.MX controller driver for the platform bus. | |
3 | * | |
4 | * derived from the OF-version. | |
5 | * | |
6 | * Copyright (c) 2010 Pengutronix e.K. | |
7 | * Author: Wolfram Sang <w.sang@pengutronix.de> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License. | |
12 | */ | |
13 | ||
14 | #include <linux/io.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/clk.h> | |
0c6d49ce | 18 | #include <linux/gpio.h> |
66506f76 | 19 | #include <linux/module.h> |
e149860d | 20 | #include <linux/slab.h> |
95f25efe | 21 | #include <linux/mmc/host.h> |
58ac8177 RZ |
22 | #include <linux/mmc/mmc.h> |
23 | #include <linux/mmc/sdio.h> | |
abfafc2d SG |
24 | #include <linux/of.h> |
25 | #include <linux/of_device.h> | |
26 | #include <linux/of_gpio.h> | |
0c6d49ce | 27 | #include <mach/esdhc.h> |
95f25efe WS |
28 | #include "sdhci-pltfm.h" |
29 | #include "sdhci-esdhc.h" | |
30 | ||
0d58864b | 31 | #define SDHCI_CTRL_D3CD 0x08 |
58ac8177 RZ |
32 | /* VENDOR SPEC register */ |
33 | #define SDHCI_VENDOR_SPEC 0xC0 | |
34 | #define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002 | |
f750ba9b | 35 | #define SDHCI_WTMK_LVL 0x44 |
95a2482a | 36 | #define SDHCI_MIX_CTRL 0x48 |
58ac8177 | 37 | |
97e4ba6a RZ |
38 | /* |
39 | * There is an INT DMA ERR mis-match between eSDHC and STD SDHC SPEC: | |
40 | * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design, | |
41 | * but bit28 is used as the INT DMA ERR in fsl eSDHC design. | |
42 | * Define this macro DMA error INT for fsl eSDHC | |
43 | */ | |
44 | #define SDHCI_INT_VENDOR_SPEC_DMA_ERR 0x10000000 | |
45 | ||
58ac8177 RZ |
46 | /* |
47 | * The CMDTYPE of the CMD register (offset 0xE) should be set to | |
48 | * "11" when the STOP CMD12 is issued on imx53 to abort one | |
49 | * open ended multi-blk IO. Otherwise the TC INT wouldn't | |
50 | * be generated. | |
51 | * In exact block transfer, the controller doesn't complete the | |
52 | * operations automatically as required at the end of the | |
53 | * transfer and remains on hold if the abort command is not sent. | |
54 | * As a result, the TC flag is not asserted and SW received timeout | |
55 | * exeception. Bit1 of Vendor Spec registor is used to fix it. | |
56 | */ | |
57 | #define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1) | |
e149860d | 58 | |
57ed3314 SG |
59 | enum imx_esdhc_type { |
60 | IMX25_ESDHC, | |
61 | IMX35_ESDHC, | |
62 | IMX51_ESDHC, | |
63 | IMX53_ESDHC, | |
95a2482a | 64 | IMX6Q_USDHC, |
57ed3314 SG |
65 | }; |
66 | ||
e149860d RZ |
67 | struct pltfm_imx_data { |
68 | int flags; | |
69 | u32 scratchpad; | |
57ed3314 | 70 | enum imx_esdhc_type devtype; |
842afc02 | 71 | struct esdhc_platform_data boarddata; |
e149860d RZ |
72 | }; |
73 | ||
57ed3314 SG |
74 | static struct platform_device_id imx_esdhc_devtype[] = { |
75 | { | |
76 | .name = "sdhci-esdhc-imx25", | |
77 | .driver_data = IMX25_ESDHC, | |
78 | }, { | |
79 | .name = "sdhci-esdhc-imx35", | |
80 | .driver_data = IMX35_ESDHC, | |
81 | }, { | |
82 | .name = "sdhci-esdhc-imx51", | |
83 | .driver_data = IMX51_ESDHC, | |
84 | }, { | |
85 | .name = "sdhci-esdhc-imx53", | |
86 | .driver_data = IMX53_ESDHC, | |
95a2482a SG |
87 | }, { |
88 | .name = "sdhci-usdhc-imx6q", | |
89 | .driver_data = IMX6Q_USDHC, | |
57ed3314 SG |
90 | }, { |
91 | /* sentinel */ | |
92 | } | |
93 | }; | |
94 | MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype); | |
95 | ||
abfafc2d SG |
96 | static const struct of_device_id imx_esdhc_dt_ids[] = { |
97 | { .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], }, | |
98 | { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], }, | |
99 | { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], }, | |
100 | { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], }, | |
95a2482a | 101 | { .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], }, |
abfafc2d SG |
102 | { /* sentinel */ } |
103 | }; | |
104 | MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); | |
105 | ||
57ed3314 SG |
106 | static inline int is_imx25_esdhc(struct pltfm_imx_data *data) |
107 | { | |
108 | return data->devtype == IMX25_ESDHC; | |
109 | } | |
110 | ||
111 | static inline int is_imx35_esdhc(struct pltfm_imx_data *data) | |
112 | { | |
113 | return data->devtype == IMX35_ESDHC; | |
114 | } | |
115 | ||
116 | static inline int is_imx51_esdhc(struct pltfm_imx_data *data) | |
117 | { | |
118 | return data->devtype == IMX51_ESDHC; | |
119 | } | |
120 | ||
121 | static inline int is_imx53_esdhc(struct pltfm_imx_data *data) | |
122 | { | |
123 | return data->devtype == IMX53_ESDHC; | |
124 | } | |
125 | ||
95a2482a SG |
126 | static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) |
127 | { | |
128 | return data->devtype == IMX6Q_USDHC; | |
129 | } | |
130 | ||
95f25efe WS |
131 | static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) |
132 | { | |
133 | void __iomem *base = host->ioaddr + (reg & ~0x3); | |
134 | u32 shift = (reg & 0x3) * 8; | |
135 | ||
136 | writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); | |
137 | } | |
138 | ||
7e29c306 WS |
139 | static u32 esdhc_readl_le(struct sdhci_host *host, int reg) |
140 | { | |
842afc02 SG |
141 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
142 | struct pltfm_imx_data *imx_data = pltfm_host->priv; | |
143 | struct esdhc_platform_data *boarddata = &imx_data->boarddata; | |
e149860d | 144 | |
913413c3 | 145 | /* fake CARD_PRESENT flag */ |
7e29c306 WS |
146 | u32 val = readl(host->ioaddr + reg); |
147 | ||
e149860d | 148 | if (unlikely((reg == SDHCI_PRESENT_STATE) |
913413c3 SG |
149 | && gpio_is_valid(boarddata->cd_gpio))) { |
150 | if (gpio_get_value(boarddata->cd_gpio)) | |
7e29c306 | 151 | /* no card, if a valid gpio says so... */ |
803862a6 | 152 | val &= ~SDHCI_CARD_PRESENT; |
7e29c306 WS |
153 | else |
154 | /* ... in all other cases assume card is present */ | |
155 | val |= SDHCI_CARD_PRESENT; | |
156 | } | |
157 | ||
97e4ba6a RZ |
158 | if (unlikely(reg == SDHCI_CAPABILITIES)) { |
159 | /* In FSL esdhc IC module, only bit20 is used to indicate the | |
160 | * ADMA2 capability of esdhc, but this bit is messed up on | |
161 | * some SOCs (e.g. on MX25, MX35 this bit is set, but they | |
162 | * don't actually support ADMA2). So set the BROKEN_ADMA | |
163 | * uirk on MX25/35 platforms. | |
164 | */ | |
165 | ||
166 | if (val & SDHCI_CAN_DO_ADMA1) { | |
167 | val &= ~SDHCI_CAN_DO_ADMA1; | |
168 | val |= SDHCI_CAN_DO_ADMA2; | |
169 | } | |
170 | } | |
171 | ||
172 | if (unlikely(reg == SDHCI_INT_STATUS)) { | |
173 | if (val & SDHCI_INT_VENDOR_SPEC_DMA_ERR) { | |
174 | val &= ~SDHCI_INT_VENDOR_SPEC_DMA_ERR; | |
175 | val |= SDHCI_INT_ADMA_ERROR; | |
176 | } | |
177 | } | |
178 | ||
7e29c306 WS |
179 | return val; |
180 | } | |
181 | ||
182 | static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) | |
183 | { | |
e149860d RZ |
184 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
185 | struct pltfm_imx_data *imx_data = pltfm_host->priv; | |
842afc02 | 186 | struct esdhc_platform_data *boarddata = &imx_data->boarddata; |
0d58864b TL |
187 | u32 data; |
188 | ||
189 | if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) { | |
190 | if (boarddata->cd_type == ESDHC_CD_GPIO) | |
191 | /* | |
192 | * These interrupts won't work with a custom | |
193 | * card_detect gpio (only applied to mx25/35) | |
194 | */ | |
195 | val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); | |
196 | ||
197 | if (val & SDHCI_INT_CARD_INT) { | |
198 | /* | |
199 | * Clear and then set D3CD bit to avoid missing the | |
200 | * card interrupt. This is a eSDHC controller problem | |
201 | * so we need to apply the following workaround: clear | |
202 | * and set D3CD bit will make eSDHC re-sample the card | |
203 | * interrupt. In case a card interrupt was lost, | |
204 | * re-sample it by the following steps. | |
205 | */ | |
206 | data = readl(host->ioaddr + SDHCI_HOST_CONTROL); | |
207 | data &= ~SDHCI_CTRL_D3CD; | |
208 | writel(data, host->ioaddr + SDHCI_HOST_CONTROL); | |
209 | data |= SDHCI_CTRL_D3CD; | |
210 | writel(data, host->ioaddr + SDHCI_HOST_CONTROL); | |
211 | } | |
212 | } | |
7e29c306 | 213 | |
58ac8177 RZ |
214 | if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) |
215 | && (reg == SDHCI_INT_STATUS) | |
216 | && (val & SDHCI_INT_DATA_END))) { | |
217 | u32 v; | |
218 | v = readl(host->ioaddr + SDHCI_VENDOR_SPEC); | |
219 | v &= ~SDHCI_VENDOR_SPEC_SDIO_QUIRK; | |
220 | writel(v, host->ioaddr + SDHCI_VENDOR_SPEC); | |
221 | } | |
222 | ||
97e4ba6a RZ |
223 | if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) { |
224 | if (val & SDHCI_INT_ADMA_ERROR) { | |
225 | val &= ~SDHCI_INT_ADMA_ERROR; | |
226 | val |= SDHCI_INT_VENDOR_SPEC_DMA_ERR; | |
227 | } | |
228 | } | |
229 | ||
7e29c306 WS |
230 | writel(val, host->ioaddr + reg); |
231 | } | |
232 | ||
95f25efe WS |
233 | static u16 esdhc_readw_le(struct sdhci_host *host, int reg) |
234 | { | |
95a2482a SG |
235 | if (unlikely(reg == SDHCI_HOST_VERSION)) { |
236 | u16 val = readw(host->ioaddr + (reg ^ 2)); | |
237 | /* | |
238 | * uSDHC supports SDHCI v3.0, but it's encoded as value | |
239 | * 0x3 in host controller version register, which violates | |
240 | * SDHCI_SPEC_300 definition. Work it around here. | |
241 | */ | |
242 | if ((val & SDHCI_SPEC_VER_MASK) == 3) | |
243 | return --val; | |
244 | } | |
95f25efe WS |
245 | |
246 | return readw(host->ioaddr + reg); | |
247 | } | |
248 | ||
249 | static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) | |
250 | { | |
251 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
e149860d | 252 | struct pltfm_imx_data *imx_data = pltfm_host->priv; |
95f25efe WS |
253 | |
254 | switch (reg) { | |
255 | case SDHCI_TRANSFER_MODE: | |
256 | /* | |
257 | * Postpone this write, we must do it together with a | |
258 | * command write that is down below. | |
259 | */ | |
58ac8177 RZ |
260 | if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) |
261 | && (host->cmd->opcode == SD_IO_RW_EXTENDED) | |
262 | && (host->cmd->data->blocks > 1) | |
263 | && (host->cmd->data->flags & MMC_DATA_READ)) { | |
264 | u32 v; | |
265 | v = readl(host->ioaddr + SDHCI_VENDOR_SPEC); | |
266 | v |= SDHCI_VENDOR_SPEC_SDIO_QUIRK; | |
267 | writel(v, host->ioaddr + SDHCI_VENDOR_SPEC); | |
268 | } | |
e149860d | 269 | imx_data->scratchpad = val; |
95f25efe WS |
270 | return; |
271 | case SDHCI_COMMAND: | |
5b6b0ad6 SH |
272 | if ((host->cmd->opcode == MMC_STOP_TRANSMISSION || |
273 | host->cmd->opcode == MMC_SET_BLOCK_COUNT) && | |
274 | (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) | |
58ac8177 | 275 | val |= SDHCI_CMD_ABORTCMD; |
95a2482a SG |
276 | |
277 | if (is_imx6q_usdhc(imx_data)) { | |
278 | u32 m = readl(host->ioaddr + SDHCI_MIX_CTRL); | |
279 | m = imx_data->scratchpad | (m & 0xffff0000); | |
280 | writel(m, host->ioaddr + SDHCI_MIX_CTRL); | |
281 | writel(val << 16, | |
282 | host->ioaddr + SDHCI_TRANSFER_MODE); | |
283 | } else { | |
284 | writel(val << 16 | imx_data->scratchpad, | |
285 | host->ioaddr + SDHCI_TRANSFER_MODE); | |
286 | } | |
95f25efe WS |
287 | return; |
288 | case SDHCI_BLOCK_SIZE: | |
289 | val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); | |
290 | break; | |
291 | } | |
292 | esdhc_clrset_le(host, 0xffff, val, reg); | |
293 | } | |
294 | ||
295 | static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) | |
296 | { | |
297 | u32 new_val; | |
298 | ||
299 | switch (reg) { | |
300 | case SDHCI_POWER_CONTROL: | |
301 | /* | |
302 | * FSL put some DMA bits here | |
303 | * If your board has a regulator, code should be here | |
304 | */ | |
305 | return; | |
306 | case SDHCI_HOST_CONTROL: | |
0d58864b TL |
307 | /* FSL messed up here, so we can just keep those three */ |
308 | new_val = val & (SDHCI_CTRL_LED | \ | |
309 | SDHCI_CTRL_4BITBUS | \ | |
310 | SDHCI_CTRL_D3CD); | |
95f25efe WS |
311 | /* ensure the endianess */ |
312 | new_val |= ESDHC_HOST_CONTROL_LE; | |
313 | /* DMA mode bits are shifted */ | |
314 | new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5; | |
315 | ||
316 | esdhc_clrset_le(host, 0xffff, new_val, reg); | |
317 | return; | |
318 | } | |
319 | esdhc_clrset_le(host, 0xff, val, reg); | |
913413c3 SG |
320 | |
321 | /* | |
322 | * The esdhc has a design violation to SDHC spec which tells | |
323 | * that software reset should not affect card detection circuit. | |
324 | * But esdhc clears its SYSCTL register bits [0..2] during the | |
325 | * software reset. This will stop those clocks that card detection | |
326 | * circuit relies on. To work around it, we turn the clocks on back | |
327 | * to keep card detection circuit functional. | |
328 | */ | |
329 | if ((reg == SDHCI_SOFTWARE_RESET) && (val & 1)) | |
330 | esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL); | |
95f25efe WS |
331 | } |
332 | ||
333 | static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host) | |
334 | { | |
335 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
336 | ||
337 | return clk_get_rate(pltfm_host->clk); | |
338 | } | |
339 | ||
340 | static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) | |
341 | { | |
342 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
343 | ||
344 | return clk_get_rate(pltfm_host->clk) / 256 / 16; | |
345 | } | |
346 | ||
913413c3 SG |
347 | static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) |
348 | { | |
842afc02 SG |
349 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
350 | struct pltfm_imx_data *imx_data = pltfm_host->priv; | |
351 | struct esdhc_platform_data *boarddata = &imx_data->boarddata; | |
913413c3 SG |
352 | |
353 | switch (boarddata->wp_type) { | |
354 | case ESDHC_WP_GPIO: | |
355 | if (gpio_is_valid(boarddata->wp_gpio)) | |
356 | return gpio_get_value(boarddata->wp_gpio); | |
357 | case ESDHC_WP_CONTROLLER: | |
358 | return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) & | |
359 | SDHCI_WRITE_PROTECT); | |
360 | case ESDHC_WP_NONE: | |
361 | break; | |
362 | } | |
363 | ||
364 | return -ENOSYS; | |
365 | } | |
366 | ||
0c6d49ce | 367 | static struct sdhci_ops sdhci_esdhc_ops = { |
e149860d | 368 | .read_l = esdhc_readl_le, |
0c6d49ce | 369 | .read_w = esdhc_readw_le, |
e149860d | 370 | .write_l = esdhc_writel_le, |
0c6d49ce WS |
371 | .write_w = esdhc_writew_le, |
372 | .write_b = esdhc_writeb_le, | |
373 | .set_clock = esdhc_set_clock, | |
374 | .get_max_clock = esdhc_pltfm_get_max_clock, | |
375 | .get_min_clock = esdhc_pltfm_get_min_clock, | |
913413c3 | 376 | .get_ro = esdhc_pltfm_get_ro, |
0c6d49ce WS |
377 | }; |
378 | ||
85d6509d | 379 | static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { |
97e4ba6a RZ |
380 | .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT |
381 | | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | |
382 | | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | |
85d6509d | 383 | | SDHCI_QUIRK_BROKEN_CARD_DETECTION, |
85d6509d SG |
384 | .ops = &sdhci_esdhc_ops, |
385 | }; | |
386 | ||
7e29c306 WS |
387 | static irqreturn_t cd_irq(int irq, void *data) |
388 | { | |
389 | struct sdhci_host *sdhost = (struct sdhci_host *)data; | |
390 | ||
391 | tasklet_schedule(&sdhost->card_tasklet); | |
392 | return IRQ_HANDLED; | |
393 | }; | |
394 | ||
abfafc2d SG |
395 | #ifdef CONFIG_OF |
396 | static int __devinit | |
397 | sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, | |
398 | struct esdhc_platform_data *boarddata) | |
399 | { | |
400 | struct device_node *np = pdev->dev.of_node; | |
401 | ||
402 | if (!np) | |
403 | return -ENODEV; | |
404 | ||
405 | if (of_get_property(np, "fsl,card-wired", NULL)) | |
406 | boarddata->cd_type = ESDHC_CD_PERMANENT; | |
407 | ||
408 | if (of_get_property(np, "fsl,cd-controller", NULL)) | |
409 | boarddata->cd_type = ESDHC_CD_CONTROLLER; | |
410 | ||
411 | if (of_get_property(np, "fsl,wp-controller", NULL)) | |
412 | boarddata->wp_type = ESDHC_WP_CONTROLLER; | |
413 | ||
414 | boarddata->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); | |
415 | if (gpio_is_valid(boarddata->cd_gpio)) | |
416 | boarddata->cd_type = ESDHC_CD_GPIO; | |
417 | ||
418 | boarddata->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); | |
419 | if (gpio_is_valid(boarddata->wp_gpio)) | |
420 | boarddata->wp_type = ESDHC_WP_GPIO; | |
421 | ||
422 | return 0; | |
423 | } | |
424 | #else | |
425 | static inline int | |
426 | sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, | |
427 | struct esdhc_platform_data *boarddata) | |
428 | { | |
429 | return -ENODEV; | |
430 | } | |
431 | #endif | |
432 | ||
85d6509d | 433 | static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) |
95f25efe | 434 | { |
abfafc2d SG |
435 | const struct of_device_id *of_id = |
436 | of_match_device(imx_esdhc_dt_ids, &pdev->dev); | |
85d6509d SG |
437 | struct sdhci_pltfm_host *pltfm_host; |
438 | struct sdhci_host *host; | |
439 | struct esdhc_platform_data *boarddata; | |
95f25efe | 440 | struct clk *clk; |
0c6d49ce | 441 | int err; |
e149860d | 442 | struct pltfm_imx_data *imx_data; |
95f25efe | 443 | |
85d6509d SG |
444 | host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata); |
445 | if (IS_ERR(host)) | |
446 | return PTR_ERR(host); | |
447 | ||
448 | pltfm_host = sdhci_priv(host); | |
449 | ||
450 | imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL); | |
abfafc2d SG |
451 | if (!imx_data) { |
452 | err = -ENOMEM; | |
453 | goto err_imx_data; | |
454 | } | |
57ed3314 | 455 | |
abfafc2d SG |
456 | if (of_id) |
457 | pdev->id_entry = of_id->data; | |
57ed3314 | 458 | imx_data->devtype = pdev->id_entry->driver_data; |
85d6509d SG |
459 | pltfm_host->priv = imx_data; |
460 | ||
95f25efe WS |
461 | clk = clk_get(mmc_dev(host->mmc), NULL); |
462 | if (IS_ERR(clk)) { | |
463 | dev_err(mmc_dev(host->mmc), "clk err\n"); | |
85d6509d SG |
464 | err = PTR_ERR(clk); |
465 | goto err_clk_get; | |
95f25efe | 466 | } |
5ef97faa | 467 | clk_prepare_enable(clk); |
95f25efe WS |
468 | pltfm_host->clk = clk; |
469 | ||
b8915282 | 470 | host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; |
37865fe9 | 471 | |
57ed3314 | 472 | if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) |
0c6d49ce | 473 | /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ |
97e4ba6a RZ |
474 | host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK |
475 | | SDHCI_QUIRK_BROKEN_ADMA; | |
0c6d49ce | 476 | |
57ed3314 | 477 | if (is_imx53_esdhc(imx_data)) |
58ac8177 RZ |
478 | imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT; |
479 | ||
f750ba9b SG |
480 | /* |
481 | * The imx6q ROM code will change the default watermark level setting | |
482 | * to something insane. Change it back here. | |
483 | */ | |
484 | if (is_imx6q_usdhc(imx_data)) | |
485 | writel(0x08100810, host->ioaddr + SDHCI_WTMK_LVL); | |
486 | ||
842afc02 | 487 | boarddata = &imx_data->boarddata; |
abfafc2d SG |
488 | if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) { |
489 | if (!host->mmc->parent->platform_data) { | |
490 | dev_err(mmc_dev(host->mmc), "no board data!\n"); | |
491 | err = -EINVAL; | |
492 | goto no_board_data; | |
493 | } | |
494 | imx_data->boarddata = *((struct esdhc_platform_data *) | |
495 | host->mmc->parent->platform_data); | |
496 | } | |
913413c3 SG |
497 | |
498 | /* write_protect */ | |
499 | if (boarddata->wp_type == ESDHC_WP_GPIO) { | |
0c6d49ce WS |
500 | err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP"); |
501 | if (err) { | |
502 | dev_warn(mmc_dev(host->mmc), | |
913413c3 SG |
503 | "no write-protect pin available!\n"); |
504 | boarddata->wp_gpio = -EINVAL; | |
0c6d49ce | 505 | } |
913413c3 SG |
506 | } else { |
507 | boarddata->wp_gpio = -EINVAL; | |
508 | } | |
509 | ||
510 | /* card_detect */ | |
511 | if (boarddata->cd_type != ESDHC_CD_GPIO) | |
512 | boarddata->cd_gpio = -EINVAL; | |
7e29c306 | 513 | |
913413c3 SG |
514 | switch (boarddata->cd_type) { |
515 | case ESDHC_CD_GPIO: | |
7e29c306 WS |
516 | err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD"); |
517 | if (err) { | |
913413c3 | 518 | dev_err(mmc_dev(host->mmc), |
7e29c306 WS |
519 | "no card-detect pin available!\n"); |
520 | goto no_card_detect_pin; | |
521 | } | |
522 | ||
7e29c306 WS |
523 | err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq, |
524 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | |
525 | mmc_hostname(host->mmc), host); | |
526 | if (err) { | |
913413c3 | 527 | dev_err(mmc_dev(host->mmc), "request irq error\n"); |
7e29c306 WS |
528 | goto no_card_detect_irq; |
529 | } | |
913413c3 | 530 | /* fall through */ |
7e29c306 | 531 | |
913413c3 SG |
532 | case ESDHC_CD_CONTROLLER: |
533 | /* we have a working card_detect back */ | |
7e29c306 | 534 | host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; |
913413c3 SG |
535 | break; |
536 | ||
537 | case ESDHC_CD_PERMANENT: | |
538 | host->mmc->caps = MMC_CAP_NONREMOVABLE; | |
539 | break; | |
540 | ||
541 | case ESDHC_CD_NONE: | |
542 | break; | |
0c6d49ce | 543 | } |
16a790bc | 544 | |
85d6509d SG |
545 | err = sdhci_add_host(host); |
546 | if (err) | |
547 | goto err_add_host; | |
548 | ||
95f25efe | 549 | return 0; |
7e29c306 | 550 | |
913413c3 SG |
551 | err_add_host: |
552 | if (gpio_is_valid(boarddata->cd_gpio)) | |
553 | free_irq(gpio_to_irq(boarddata->cd_gpio), host); | |
554 | no_card_detect_irq: | |
555 | if (gpio_is_valid(boarddata->cd_gpio)) | |
556 | gpio_free(boarddata->cd_gpio); | |
557 | if (gpio_is_valid(boarddata->wp_gpio)) | |
558 | gpio_free(boarddata->wp_gpio); | |
559 | no_card_detect_pin: | |
560 | no_board_data: | |
5ef97faa | 561 | clk_disable_unprepare(pltfm_host->clk); |
85d6509d | 562 | clk_put(pltfm_host->clk); |
913413c3 SG |
563 | err_clk_get: |
564 | kfree(imx_data); | |
abfafc2d | 565 | err_imx_data: |
85d6509d SG |
566 | sdhci_pltfm_free(pdev); |
567 | return err; | |
95f25efe WS |
568 | } |
569 | ||
85d6509d | 570 | static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev) |
95f25efe | 571 | { |
85d6509d | 572 | struct sdhci_host *host = platform_get_drvdata(pdev); |
95f25efe | 573 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
e149860d | 574 | struct pltfm_imx_data *imx_data = pltfm_host->priv; |
842afc02 | 575 | struct esdhc_platform_data *boarddata = &imx_data->boarddata; |
85d6509d SG |
576 | int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); |
577 | ||
578 | sdhci_remove_host(host, dead); | |
0c6d49ce | 579 | |
913413c3 | 580 | if (gpio_is_valid(boarddata->wp_gpio)) |
0c6d49ce | 581 | gpio_free(boarddata->wp_gpio); |
95f25efe | 582 | |
913413c3 SG |
583 | if (gpio_is_valid(boarddata->cd_gpio)) { |
584 | free_irq(gpio_to_irq(boarddata->cd_gpio), host); | |
7e29c306 | 585 | gpio_free(boarddata->cd_gpio); |
7e29c306 WS |
586 | } |
587 | ||
5ef97faa | 588 | clk_disable_unprepare(pltfm_host->clk); |
95f25efe | 589 | clk_put(pltfm_host->clk); |
e149860d | 590 | kfree(imx_data); |
85d6509d SG |
591 | |
592 | sdhci_pltfm_free(pdev); | |
593 | ||
594 | return 0; | |
95f25efe WS |
595 | } |
596 | ||
85d6509d SG |
597 | static struct platform_driver sdhci_esdhc_imx_driver = { |
598 | .driver = { | |
599 | .name = "sdhci-esdhc-imx", | |
600 | .owner = THIS_MODULE, | |
abfafc2d | 601 | .of_match_table = imx_esdhc_dt_ids, |
29495aa0 | 602 | .pm = SDHCI_PLTFM_PMOPS, |
85d6509d | 603 | }, |
57ed3314 | 604 | .id_table = imx_esdhc_devtype, |
85d6509d SG |
605 | .probe = sdhci_esdhc_imx_probe, |
606 | .remove = __devexit_p(sdhci_esdhc_imx_remove), | |
95f25efe | 607 | }; |
85d6509d | 608 | |
d1f81a64 | 609 | module_platform_driver(sdhci_esdhc_imx_driver); |
85d6509d SG |
610 | |
611 | MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC"); | |
612 | MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>"); | |
613 | MODULE_LICENSE("GPL v2"); |