Commit | Line | Data |
---|---|---|
7657c3a7 AH |
1 | /* |
2 | * Freescale eSDHC controller driver. | |
3 | * | |
f060bc9c | 4 | * Copyright (c) 2007, 2010, 2012 Freescale Semiconductor, Inc. |
7657c3a7 AH |
5 | * Copyright (c) 2009 MontaVista Software, Inc. |
6 | * | |
7 | * Authors: Xiaobo Xie <X.Xie@freescale.com> | |
8 | * Anton Vorontsov <avorontsov@ru.mvista.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or (at | |
13 | * your option) any later version. | |
14 | */ | |
15 | ||
66b50a00 | 16 | #include <linux/err.h> |
7657c3a7 | 17 | #include <linux/io.h> |
f060bc9c | 18 | #include <linux/of.h> |
7657c3a7 | 19 | #include <linux/delay.h> |
88b47679 | 20 | #include <linux/module.h> |
7657c3a7 | 21 | #include <linux/mmc/host.h> |
38576af1 | 22 | #include "sdhci-pltfm.h" |
80872e21 | 23 | #include "sdhci-esdhc.h" |
7657c3a7 | 24 | |
137ccd46 | 25 | #define VENDOR_V_22 0x12 |
a4071fbb | 26 | #define VENDOR_V_23 0x13 |
f4932cfd | 27 | |
28 | struct sdhci_esdhc { | |
29 | u8 vendor_ver; | |
30 | u8 spec_ver; | |
31 | }; | |
32 | ||
33 | /** | |
34 | * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register | |
35 | * to make it compatible with SD spec. | |
36 | * | |
37 | * @host: pointer to sdhci_host | |
38 | * @spec_reg: SD spec register address | |
39 | * @value: 32bit eSDHC register value on spec_reg address | |
40 | * | |
41 | * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC | |
42 | * registers are 32 bits. There are differences in register size, register | |
43 | * address, register function, bit position and function between eSDHC spec | |
44 | * and SD spec. | |
45 | * | |
46 | * Return a fixed up register value | |
47 | */ | |
48 | static u32 esdhc_readl_fixup(struct sdhci_host *host, | |
49 | int spec_reg, u32 value) | |
137ccd46 | 50 | { |
f4932cfd | 51 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
8605e7ae | 52 | struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); |
137ccd46 JH |
53 | u32 ret; |
54 | ||
137ccd46 JH |
55 | /* |
56 | * The bit of ADMA flag in eSDHC is not compatible with standard | |
57 | * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is | |
58 | * supported by eSDHC. | |
59 | * And for many FSL eSDHC controller, the reset value of field | |
f4932cfd | 60 | * SDHCI_CAN_DO_ADMA1 is 1, but some of them can't support ADMA, |
137ccd46 | 61 | * only these vendor version is greater than 2.2/0x12 support ADMA. |
137ccd46 | 62 | */ |
f4932cfd | 63 | if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) { |
64 | if (esdhc->vendor_ver > VENDOR_V_22) { | |
65 | ret = value | SDHCI_CAN_DO_ADMA2; | |
66 | return ret; | |
67 | } | |
137ccd46 | 68 | } |
f4932cfd | 69 | ret = value; |
137ccd46 JH |
70 | return ret; |
71 | } | |
72 | ||
f4932cfd | 73 | static u16 esdhc_readw_fixup(struct sdhci_host *host, |
74 | int spec_reg, u32 value) | |
7657c3a7 AH |
75 | { |
76 | u16 ret; | |
f4932cfd | 77 | int shift = (spec_reg & 0x2) * 8; |
7657c3a7 | 78 | |
f4932cfd | 79 | if (spec_reg == SDHCI_HOST_VERSION) |
80 | ret = value & 0xffff; | |
7657c3a7 | 81 | else |
f4932cfd | 82 | ret = (value >> shift) & 0xffff; |
e51cbc9e X |
83 | return ret; |
84 | } | |
85 | ||
f4932cfd | 86 | static u8 esdhc_readb_fixup(struct sdhci_host *host, |
87 | int spec_reg, u32 value) | |
e51cbc9e | 88 | { |
f4932cfd | 89 | u8 ret; |
90 | u8 dma_bits; | |
91 | int shift = (spec_reg & 0x3) * 8; | |
92 | ||
93 | ret = (value >> shift) & 0xff; | |
ba8c4dc9 RZ |
94 | |
95 | /* | |
96 | * "DMA select" locates at offset 0x28 in SD specification, but on | |
97 | * P5020 or P3041, it locates at 0x29. | |
98 | */ | |
f4932cfd | 99 | if (spec_reg == SDHCI_HOST_CONTROL) { |
ba8c4dc9 | 100 | /* DMA select is 22,23 bits in Protocol Control Register */ |
f4932cfd | 101 | dma_bits = (value >> 5) & SDHCI_CTRL_DMA_MASK; |
ba8c4dc9 RZ |
102 | /* fixup the result */ |
103 | ret &= ~SDHCI_CTRL_DMA_MASK; | |
104 | ret |= dma_bits; | |
105 | } | |
7657c3a7 AH |
106 | return ret; |
107 | } | |
108 | ||
f4932cfd | 109 | /** |
110 | * esdhc_write*_fixup - Fixup the SD spec register value so that it could be | |
111 | * written into eSDHC register. | |
112 | * | |
113 | * @host: pointer to sdhci_host | |
114 | * @spec_reg: SD spec register address | |
115 | * @value: 8/16/32bit SD spec register value that would be written | |
116 | * @old_value: 32bit eSDHC register value on spec_reg address | |
117 | * | |
118 | * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC | |
119 | * registers are 32 bits. There are differences in register size, register | |
120 | * address, register function, bit position and function between eSDHC spec | |
121 | * and SD spec. | |
122 | * | |
123 | * Return a fixed up register value | |
124 | */ | |
125 | static u32 esdhc_writel_fixup(struct sdhci_host *host, | |
126 | int spec_reg, u32 value, u32 old_value) | |
a4071fbb | 127 | { |
f4932cfd | 128 | u32 ret; |
129 | ||
a4071fbb | 130 | /* |
f4932cfd | 131 | * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] |
132 | * when SYSCTL[RSTD] is set for some special operations. | |
133 | * No any impact on other operation. | |
a4071fbb | 134 | */ |
f4932cfd | 135 | if (spec_reg == SDHCI_INT_ENABLE) |
136 | ret = value | SDHCI_INT_BLK_GAP; | |
137 | else | |
138 | ret = value; | |
139 | ||
140 | return ret; | |
a4071fbb HZ |
141 | } |
142 | ||
f4932cfd | 143 | static u32 esdhc_writew_fixup(struct sdhci_host *host, |
144 | int spec_reg, u16 value, u32 old_value) | |
7657c3a7 | 145 | { |
f4932cfd | 146 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
147 | int shift = (spec_reg & 0x2) * 8; | |
148 | u32 ret; | |
149 | ||
150 | switch (spec_reg) { | |
151 | case SDHCI_TRANSFER_MODE: | |
152 | /* | |
153 | * Postpone this write, we must do it together with a | |
154 | * command write that is down below. Return old value. | |
155 | */ | |
156 | pltfm_host->xfer_mode_shadow = value; | |
157 | return old_value; | |
158 | case SDHCI_COMMAND: | |
159 | ret = (value << 16) | pltfm_host->xfer_mode_shadow; | |
160 | return ret; | |
161 | } | |
162 | ||
163 | ret = old_value & (~(0xffff << shift)); | |
164 | ret |= (value << shift); | |
165 | ||
166 | if (spec_reg == SDHCI_BLOCK_SIZE) { | |
7657c3a7 AH |
167 | /* |
168 | * Two last DMA bits are reserved, and first one is used for | |
169 | * non-standard blksz of 4096 bytes that we don't support | |
170 | * yet. So clear the DMA boundary bits. | |
171 | */ | |
f4932cfd | 172 | ret &= (~SDHCI_MAKE_BLKSZ(0x7, 0)); |
7657c3a7 | 173 | } |
f4932cfd | 174 | return ret; |
7657c3a7 AH |
175 | } |
176 | ||
f4932cfd | 177 | static u32 esdhc_writeb_fixup(struct sdhci_host *host, |
178 | int spec_reg, u8 value, u32 old_value) | |
7657c3a7 | 179 | { |
f4932cfd | 180 | u32 ret; |
181 | u32 dma_bits; | |
182 | u8 tmp; | |
183 | int shift = (spec_reg & 0x3) * 8; | |
184 | ||
9e4703df | 185 | /* |
186 | * eSDHC doesn't have a standard power control register, so we do | |
187 | * nothing here to avoid incorrect operation. | |
188 | */ | |
189 | if (spec_reg == SDHCI_POWER_CONTROL) | |
190 | return old_value; | |
ba8c4dc9 RZ |
191 | /* |
192 | * "DMA select" location is offset 0x28 in SD specification, but on | |
193 | * P5020 or P3041, it's located at 0x29. | |
194 | */ | |
f4932cfd | 195 | if (spec_reg == SDHCI_HOST_CONTROL) { |
dcaff04d OG |
196 | /* |
197 | * If host control register is not standard, exit | |
198 | * this function | |
199 | */ | |
200 | if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL) | |
f4932cfd | 201 | return old_value; |
dcaff04d | 202 | |
ba8c4dc9 | 203 | /* DMA select is 22,23 bits in Protocol Control Register */ |
f4932cfd | 204 | dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5; |
205 | ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits; | |
206 | tmp = (value & (~SDHCI_CTRL_DMA_MASK)) | | |
207 | (old_value & SDHCI_CTRL_DMA_MASK); | |
208 | ret = (ret & (~0xff)) | tmp; | |
209 | ||
210 | /* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */ | |
211 | ret &= ~ESDHC_HOST_CONTROL_RES; | |
212 | return ret; | |
ba8c4dc9 RZ |
213 | } |
214 | ||
f4932cfd | 215 | ret = (old_value & (~(0xff << shift))) | (value << shift); |
216 | return ret; | |
217 | } | |
218 | ||
219 | static u32 esdhc_be_readl(struct sdhci_host *host, int reg) | |
220 | { | |
221 | u32 ret; | |
222 | u32 value; | |
223 | ||
224 | value = ioread32be(host->ioaddr + reg); | |
225 | ret = esdhc_readl_fixup(host, reg, value); | |
226 | ||
227 | return ret; | |
228 | } | |
229 | ||
230 | static u32 esdhc_le_readl(struct sdhci_host *host, int reg) | |
231 | { | |
232 | u32 ret; | |
233 | u32 value; | |
234 | ||
235 | value = ioread32(host->ioaddr + reg); | |
236 | ret = esdhc_readl_fixup(host, reg, value); | |
237 | ||
238 | return ret; | |
239 | } | |
240 | ||
241 | static u16 esdhc_be_readw(struct sdhci_host *host, int reg) | |
242 | { | |
243 | u16 ret; | |
244 | u32 value; | |
245 | int base = reg & ~0x3; | |
246 | ||
247 | value = ioread32be(host->ioaddr + base); | |
248 | ret = esdhc_readw_fixup(host, reg, value); | |
249 | return ret; | |
250 | } | |
251 | ||
252 | static u16 esdhc_le_readw(struct sdhci_host *host, int reg) | |
253 | { | |
254 | u16 ret; | |
255 | u32 value; | |
256 | int base = reg & ~0x3; | |
257 | ||
258 | value = ioread32(host->ioaddr + base); | |
259 | ret = esdhc_readw_fixup(host, reg, value); | |
260 | return ret; | |
261 | } | |
262 | ||
263 | static u8 esdhc_be_readb(struct sdhci_host *host, int reg) | |
264 | { | |
265 | u8 ret; | |
266 | u32 value; | |
267 | int base = reg & ~0x3; | |
268 | ||
269 | value = ioread32be(host->ioaddr + base); | |
270 | ret = esdhc_readb_fixup(host, reg, value); | |
271 | return ret; | |
272 | } | |
273 | ||
274 | static u8 esdhc_le_readb(struct sdhci_host *host, int reg) | |
275 | { | |
276 | u8 ret; | |
277 | u32 value; | |
278 | int base = reg & ~0x3; | |
279 | ||
280 | value = ioread32(host->ioaddr + base); | |
281 | ret = esdhc_readb_fixup(host, reg, value); | |
282 | return ret; | |
283 | } | |
284 | ||
285 | static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg) | |
286 | { | |
287 | u32 value; | |
288 | ||
289 | value = esdhc_writel_fixup(host, reg, val, 0); | |
290 | iowrite32be(value, host->ioaddr + reg); | |
291 | } | |
292 | ||
293 | static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg) | |
294 | { | |
295 | u32 value; | |
296 | ||
297 | value = esdhc_writel_fixup(host, reg, val, 0); | |
298 | iowrite32(value, host->ioaddr + reg); | |
299 | } | |
300 | ||
301 | static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg) | |
302 | { | |
303 | int base = reg & ~0x3; | |
304 | u32 value; | |
305 | u32 ret; | |
306 | ||
307 | value = ioread32be(host->ioaddr + base); | |
308 | ret = esdhc_writew_fixup(host, reg, val, value); | |
309 | if (reg != SDHCI_TRANSFER_MODE) | |
310 | iowrite32be(ret, host->ioaddr + base); | |
311 | } | |
312 | ||
313 | static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg) | |
314 | { | |
315 | int base = reg & ~0x3; | |
316 | u32 value; | |
317 | u32 ret; | |
318 | ||
319 | value = ioread32(host->ioaddr + base); | |
320 | ret = esdhc_writew_fixup(host, reg, val, value); | |
321 | if (reg != SDHCI_TRANSFER_MODE) | |
322 | iowrite32(ret, host->ioaddr + base); | |
323 | } | |
324 | ||
325 | static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg) | |
326 | { | |
327 | int base = reg & ~0x3; | |
328 | u32 value; | |
329 | u32 ret; | |
330 | ||
331 | value = ioread32be(host->ioaddr + base); | |
332 | ret = esdhc_writeb_fixup(host, reg, val, value); | |
333 | iowrite32be(ret, host->ioaddr + base); | |
334 | } | |
335 | ||
336 | static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg) | |
337 | { | |
338 | int base = reg & ~0x3; | |
339 | u32 value; | |
340 | u32 ret; | |
341 | ||
342 | value = ioread32(host->ioaddr + base); | |
343 | ret = esdhc_writeb_fixup(host, reg, val, value); | |
344 | iowrite32(ret, host->ioaddr + base); | |
7657c3a7 AH |
345 | } |
346 | ||
a4071fbb HZ |
347 | /* |
348 | * For Abort or Suspend after Stop at Block Gap, ignore the ADMA | |
349 | * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC]) | |
350 | * and Block Gap Event(IRQSTAT[BGE]) are also set. | |
351 | * For Continue, apply soft reset for data(SYSCTL[RSTD]); | |
352 | * and re-issue the entire read transaction from beginning. | |
353 | */ | |
f4932cfd | 354 | static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask) |
a4071fbb | 355 | { |
f4932cfd | 356 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
8605e7ae | 357 | struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); |
a4071fbb HZ |
358 | bool applicable; |
359 | dma_addr_t dmastart; | |
360 | dma_addr_t dmanow; | |
361 | ||
a4071fbb | 362 | applicable = (intmask & SDHCI_INT_DATA_END) && |
f4932cfd | 363 | (intmask & SDHCI_INT_BLK_GAP) && |
364 | (esdhc->vendor_ver == VENDOR_V_23); | |
a4071fbb HZ |
365 | if (!applicable) |
366 | return; | |
367 | ||
368 | host->data->error = 0; | |
369 | dmastart = sg_dma_address(host->data->sg); | |
370 | dmanow = dmastart + host->data->bytes_xfered; | |
371 | /* | |
372 | * Force update to the next DMA block boundary. | |
373 | */ | |
374 | dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + | |
375 | SDHCI_DEFAULT_BOUNDARY_SIZE; | |
376 | host->data->bytes_xfered = dmanow - dmastart; | |
377 | sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); | |
378 | } | |
379 | ||
80872e21 | 380 | static int esdhc_of_enable_dma(struct sdhci_host *host) |
7657c3a7 | 381 | { |
f4932cfd | 382 | u32 value; |
383 | ||
384 | value = sdhci_readl(host, ESDHC_DMA_SYSCTL); | |
385 | value |= ESDHC_DMA_SNOOP; | |
386 | sdhci_writel(host, value, ESDHC_DMA_SYSCTL); | |
7657c3a7 AH |
387 | return 0; |
388 | } | |
389 | ||
80872e21 | 390 | static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host) |
7657c3a7 | 391 | { |
e307148f | 392 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
7657c3a7 | 393 | |
e307148f | 394 | return pltfm_host->clock; |
7657c3a7 AH |
395 | } |
396 | ||
80872e21 | 397 | static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) |
7657c3a7 | 398 | { |
e307148f | 399 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
7657c3a7 | 400 | |
e307148f | 401 | return pltfm_host->clock / 256 / 16; |
7657c3a7 AH |
402 | } |
403 | ||
f060bc9c JH |
404 | static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) |
405 | { | |
f4932cfd | 406 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
8605e7ae | 407 | struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); |
bd455029 | 408 | int pre_div = 1; |
d31fc00a DA |
409 | int div = 1; |
410 | u32 temp; | |
411 | ||
1650d0c7 RK |
412 | host->mmc->actual_clock = 0; |
413 | ||
d31fc00a | 414 | if (clock == 0) |
373073ef | 415 | return; |
d31fc00a | 416 | |
77bd2f6f | 417 | /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ |
f4932cfd | 418 | if (esdhc->vendor_ver < VENDOR_V_23) |
77bd2f6f YL |
419 | pre_div = 2; |
420 | ||
f060bc9c JH |
421 | /* Workaround to reduce the clock frequency for p1010 esdhc */ |
422 | if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { | |
423 | if (clock > 20000000) | |
424 | clock -= 5000000; | |
425 | if (clock > 40000000) | |
426 | clock -= 5000000; | |
427 | } | |
428 | ||
d31fc00a DA |
429 | temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); |
430 | temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | |
431 | | ESDHC_CLOCK_MASK); | |
432 | sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); | |
433 | ||
434 | while (host->max_clk / pre_div / 16 > clock && pre_div < 256) | |
435 | pre_div *= 2; | |
436 | ||
437 | while (host->max_clk / pre_div / div > clock && div < 16) | |
438 | div++; | |
439 | ||
440 | dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", | |
e76b8559 | 441 | clock, host->max_clk / pre_div / div); |
bd455029 | 442 | host->mmc->actual_clock = host->max_clk / pre_div / div; |
d31fc00a DA |
443 | pre_div >>= 1; |
444 | div--; | |
445 | ||
446 | temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); | |
447 | temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | |
448 | | (div << ESDHC_DIVIDER_SHIFT) | |
449 | | (pre_div << ESDHC_PREDIV_SHIFT)); | |
450 | sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); | |
451 | mdelay(1); | |
f060bc9c JH |
452 | } |
453 | ||
2317f56c | 454 | static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) |
66b50a00 OG |
455 | { |
456 | u32 ctrl; | |
457 | ||
f4932cfd | 458 | ctrl = sdhci_readl(host, ESDHC_PROCTL); |
459 | ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK); | |
66b50a00 OG |
460 | switch (width) { |
461 | case MMC_BUS_WIDTH_8: | |
f4932cfd | 462 | ctrl |= ESDHC_CTRL_8BITBUS; |
66b50a00 OG |
463 | break; |
464 | ||
465 | case MMC_BUS_WIDTH_4: | |
f4932cfd | 466 | ctrl |= ESDHC_CTRL_4BITBUS; |
66b50a00 OG |
467 | break; |
468 | ||
469 | default: | |
66b50a00 OG |
470 | break; |
471 | } | |
472 | ||
f4932cfd | 473 | sdhci_writel(host, ctrl, ESDHC_PROCTL); |
66b50a00 OG |
474 | } |
475 | ||
304f0a98 AIB |
476 | static void esdhc_reset(struct sdhci_host *host, u8 mask) |
477 | { | |
478 | sdhci_reset(host, mask); | |
479 | ||
480 | sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); | |
481 | sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); | |
482 | } | |
483 | ||
723f7924 | 484 | #ifdef CONFIG_PM |
723f7924 RK |
485 | static u32 esdhc_proctl; |
486 | static int esdhc_of_suspend(struct device *dev) | |
487 | { | |
488 | struct sdhci_host *host = dev_get_drvdata(dev); | |
489 | ||
f4932cfd | 490 | esdhc_proctl = sdhci_readl(host, SDHCI_HOST_CONTROL); |
723f7924 RK |
491 | |
492 | return sdhci_suspend_host(host); | |
493 | } | |
494 | ||
06732b84 | 495 | static int esdhc_of_resume(struct device *dev) |
723f7924 RK |
496 | { |
497 | struct sdhci_host *host = dev_get_drvdata(dev); | |
498 | int ret = sdhci_resume_host(host); | |
499 | ||
500 | if (ret == 0) { | |
501 | /* Isn't this already done by sdhci_resume_host() ? --rmk */ | |
502 | esdhc_of_enable_dma(host); | |
f4932cfd | 503 | sdhci_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); |
723f7924 | 504 | } |
723f7924 RK |
505 | return ret; |
506 | } | |
507 | ||
508 | static const struct dev_pm_ops esdhc_pmops = { | |
06732b84 UH |
509 | .suspend = esdhc_of_suspend, |
510 | .resume = esdhc_of_resume, | |
723f7924 RK |
511 | }; |
512 | #define ESDHC_PMOPS (&esdhc_pmops) | |
513 | #else | |
514 | #define ESDHC_PMOPS NULL | |
515 | #endif | |
516 | ||
f4932cfd | 517 | static const struct sdhci_ops sdhci_esdhc_be_ops = { |
518 | .read_l = esdhc_be_readl, | |
519 | .read_w = esdhc_be_readw, | |
520 | .read_b = esdhc_be_readb, | |
521 | .write_l = esdhc_be_writel, | |
522 | .write_w = esdhc_be_writew, | |
523 | .write_b = esdhc_be_writeb, | |
524 | .set_clock = esdhc_of_set_clock, | |
525 | .enable_dma = esdhc_of_enable_dma, | |
526 | .get_max_clock = esdhc_of_get_max_clock, | |
527 | .get_min_clock = esdhc_of_get_min_clock, | |
528 | .adma_workaround = esdhc_of_adma_workaround, | |
529 | .set_bus_width = esdhc_pltfm_set_bus_width, | |
530 | .reset = esdhc_reset, | |
531 | .set_uhs_signaling = sdhci_set_uhs_signaling, | |
532 | }; | |
533 | ||
534 | static const struct sdhci_ops sdhci_esdhc_le_ops = { | |
535 | .read_l = esdhc_le_readl, | |
536 | .read_w = esdhc_le_readw, | |
537 | .read_b = esdhc_le_readb, | |
538 | .write_l = esdhc_le_writel, | |
539 | .write_w = esdhc_le_writew, | |
540 | .write_b = esdhc_le_writeb, | |
541 | .set_clock = esdhc_of_set_clock, | |
542 | .enable_dma = esdhc_of_enable_dma, | |
543 | .get_max_clock = esdhc_of_get_max_clock, | |
544 | .get_min_clock = esdhc_of_get_min_clock, | |
545 | .adma_workaround = esdhc_of_adma_workaround, | |
546 | .set_bus_width = esdhc_pltfm_set_bus_width, | |
547 | .reset = esdhc_reset, | |
548 | .set_uhs_signaling = sdhci_set_uhs_signaling, | |
549 | }; | |
550 | ||
551 | static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { | |
552 | .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION | |
553 | | SDHCI_QUIRK_NO_CARD_NO_RESET | |
554 | | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | |
555 | .ops = &sdhci_esdhc_be_ops, | |
556 | }; | |
557 | ||
558 | static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = { | |
e481e45d | 559 | .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION |
137ccd46 JH |
560 | | SDHCI_QUIRK_NO_CARD_NO_RESET |
561 | | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | |
f4932cfd | 562 | .ops = &sdhci_esdhc_le_ops, |
7657c3a7 | 563 | }; |
38576af1 | 564 | |
f4932cfd | 565 | static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) |
566 | { | |
567 | struct sdhci_pltfm_host *pltfm_host; | |
568 | struct sdhci_esdhc *esdhc; | |
569 | u16 host_ver; | |
570 | ||
571 | pltfm_host = sdhci_priv(host); | |
8605e7ae | 572 | esdhc = sdhci_pltfm_priv(pltfm_host); |
f4932cfd | 573 | |
574 | host_ver = sdhci_readw(host, SDHCI_HOST_VERSION); | |
575 | esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >> | |
576 | SDHCI_VENDOR_VER_SHIFT; | |
577 | esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK; | |
f4932cfd | 578 | } |
579 | ||
c3be1efd | 580 | static int sdhci_esdhc_probe(struct platform_device *pdev) |
38576af1 | 581 | { |
66b50a00 | 582 | struct sdhci_host *host; |
dcaff04d | 583 | struct device_node *np; |
1ef5e49e | 584 | struct sdhci_pltfm_host *pltfm_host; |
585 | struct sdhci_esdhc *esdhc; | |
66b50a00 OG |
586 | int ret; |
587 | ||
f4932cfd | 588 | np = pdev->dev.of_node; |
589 | ||
590 | if (of_get_property(np, "little-endian", NULL)) | |
8605e7ae JZ |
591 | host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, |
592 | sizeof(struct sdhci_esdhc)); | |
f4932cfd | 593 | else |
8605e7ae JZ |
594 | host = sdhci_pltfm_init(pdev, &sdhci_esdhc_be_pdata, |
595 | sizeof(struct sdhci_esdhc)); | |
f4932cfd | 596 | |
66b50a00 OG |
597 | if (IS_ERR(host)) |
598 | return PTR_ERR(host); | |
599 | ||
f4932cfd | 600 | esdhc_init(pdev, host); |
601 | ||
66b50a00 OG |
602 | sdhci_get_of_property(pdev); |
603 | ||
1ef5e49e | 604 | pltfm_host = sdhci_priv(host); |
8605e7ae | 605 | esdhc = sdhci_pltfm_priv(pltfm_host); |
1ef5e49e | 606 | if (esdhc->vendor_ver == VENDOR_V_22) |
607 | host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; | |
608 | ||
609 | if (esdhc->vendor_ver > VENDOR_V_22) | |
610 | host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; | |
611 | ||
74fd5e30 YL |
612 | if (of_device_is_compatible(np, "fsl,p5040-esdhc") || |
613 | of_device_is_compatible(np, "fsl,p5020-esdhc") || | |
614 | of_device_is_compatible(np, "fsl,p4080-esdhc") || | |
615 | of_device_is_compatible(np, "fsl,p1020-esdhc") || | |
aaa58d0e YL |
616 | of_device_is_compatible(np, "fsl,t1040-esdhc") || |
617 | of_device_is_compatible(np, "fsl,ls1021a-esdhc")) | |
74fd5e30 YL |
618 | host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; |
619 | ||
a22950c8 | 620 | if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) |
621 | host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; | |
622 | ||
dcaff04d OG |
623 | if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { |
624 | /* | |
625 | * Freescale messed up with P2020 as it has a non-standard | |
626 | * host control register | |
627 | */ | |
628 | host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; | |
629 | } | |
630 | ||
66b50a00 | 631 | /* call to generic mmc_of_parse to support additional capabilities */ |
f0991408 UH |
632 | ret = mmc_of_parse(host->mmc); |
633 | if (ret) | |
634 | goto err; | |
635 | ||
490104ac | 636 | mmc_of_parse_voltage(np, &host->ocr_mask); |
66b50a00 OG |
637 | |
638 | ret = sdhci_add_host(host); | |
639 | if (ret) | |
f0991408 | 640 | goto err; |
66b50a00 | 641 | |
f0991408 UH |
642 | return 0; |
643 | err: | |
644 | sdhci_pltfm_free(pdev); | |
66b50a00 | 645 | return ret; |
38576af1 SG |
646 | } |
647 | ||
38576af1 SG |
648 | static const struct of_device_id sdhci_esdhc_of_match[] = { |
649 | { .compatible = "fsl,mpc8379-esdhc" }, | |
650 | { .compatible = "fsl,mpc8536-esdhc" }, | |
651 | { .compatible = "fsl,esdhc" }, | |
652 | { } | |
653 | }; | |
654 | MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); | |
655 | ||
656 | static struct platform_driver sdhci_esdhc_driver = { | |
657 | .driver = { | |
658 | .name = "sdhci-esdhc", | |
38576af1 | 659 | .of_match_table = sdhci_esdhc_of_match, |
723f7924 | 660 | .pm = ESDHC_PMOPS, |
38576af1 SG |
661 | }, |
662 | .probe = sdhci_esdhc_probe, | |
caebcae9 | 663 | .remove = sdhci_pltfm_unregister, |
38576af1 SG |
664 | }; |
665 | ||
d1f81a64 | 666 | module_platform_driver(sdhci_esdhc_driver); |
38576af1 SG |
667 | |
668 | MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC"); | |
669 | MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, " | |
670 | "Anton Vorontsov <avorontsov@ru.mvista.com>"); | |
671 | MODULE_LICENSE("GPL v2"); |