]>
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 |
137ccd46 JH |
27 | static u32 esdhc_readl(struct sdhci_host *host, int reg) |
28 | { | |
29 | u32 ret; | |
30 | ||
31 | ret = in_be32(host->ioaddr + reg); | |
32 | /* | |
33 | * The bit of ADMA flag in eSDHC is not compatible with standard | |
34 | * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is | |
35 | * supported by eSDHC. | |
36 | * And for many FSL eSDHC controller, the reset value of field | |
37 | * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA, | |
38 | * only these vendor version is greater than 2.2/0x12 support ADMA. | |
39 | * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the | |
40 | * the verdor version number, oxFE is SDHCI_HOST_VERSION. | |
41 | */ | |
42 | if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { | |
43 | u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); | |
44 | tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |
45 | if (tmp > VENDOR_V_22) | |
46 | ret |= SDHCI_CAN_DO_ADMA2; | |
47 | } | |
48 | ||
49 | return ret; | |
50 | } | |
51 | ||
7657c3a7 AH |
52 | static u16 esdhc_readw(struct sdhci_host *host, int reg) |
53 | { | |
54 | u16 ret; | |
e51cbc9e X |
55 | int base = reg & ~0x3; |
56 | int shift = (reg & 0x2) * 8; | |
7657c3a7 AH |
57 | |
58 | if (unlikely(reg == SDHCI_HOST_VERSION)) | |
e51cbc9e | 59 | ret = in_be32(host->ioaddr + base) & 0xffff; |
7657c3a7 | 60 | else |
e51cbc9e X |
61 | ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; |
62 | return ret; | |
63 | } | |
64 | ||
65 | static u8 esdhc_readb(struct sdhci_host *host, int reg) | |
66 | { | |
67 | int base = reg & ~0x3; | |
68 | int shift = (reg & 0x3) * 8; | |
69 | u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; | |
ba8c4dc9 RZ |
70 | |
71 | /* | |
72 | * "DMA select" locates at offset 0x28 in SD specification, but on | |
73 | * P5020 or P3041, it locates at 0x29. | |
74 | */ | |
75 | if (reg == SDHCI_HOST_CONTROL) { | |
76 | u32 dma_bits; | |
77 | ||
78 | dma_bits = in_be32(host->ioaddr + reg); | |
79 | /* DMA select is 22,23 bits in Protocol Control Register */ | |
80 | dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; | |
81 | ||
82 | /* fixup the result */ | |
83 | ret &= ~SDHCI_CTRL_DMA_MASK; | |
84 | ret |= dma_bits; | |
85 | } | |
86 | ||
7657c3a7 AH |
87 | return ret; |
88 | } | |
89 | ||
a4071fbb HZ |
90 | static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) |
91 | { | |
92 | /* | |
93 | * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] | |
94 | * when SYSCTL[RSTD]) is set for some special operations. | |
95 | * No any impact other operation. | |
96 | */ | |
97 | if (reg == SDHCI_INT_ENABLE) | |
98 | val |= SDHCI_INT_BLK_GAP; | |
99 | sdhci_be32bs_writel(host, val, reg); | |
100 | } | |
101 | ||
7657c3a7 AH |
102 | static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) |
103 | { | |
104 | if (reg == SDHCI_BLOCK_SIZE) { | |
105 | /* | |
106 | * Two last DMA bits are reserved, and first one is used for | |
107 | * non-standard blksz of 4096 bytes that we don't support | |
108 | * yet. So clear the DMA boundary bits. | |
109 | */ | |
110 | val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); | |
111 | } | |
112 | sdhci_be32bs_writew(host, val, reg); | |
113 | } | |
114 | ||
115 | static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) | |
116 | { | |
ba8c4dc9 RZ |
117 | /* |
118 | * "DMA select" location is offset 0x28 in SD specification, but on | |
119 | * P5020 or P3041, it's located at 0x29. | |
120 | */ | |
121 | if (reg == SDHCI_HOST_CONTROL) { | |
122 | u32 dma_bits; | |
123 | ||
dcaff04d OG |
124 | /* |
125 | * If host control register is not standard, exit | |
126 | * this function | |
127 | */ | |
128 | if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL) | |
129 | return; | |
130 | ||
ba8c4dc9 RZ |
131 | /* DMA select is 22,23 bits in Protocol Control Register */ |
132 | dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; | |
133 | clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, | |
134 | dma_bits); | |
135 | val &= ~SDHCI_CTRL_DMA_MASK; | |
136 | val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; | |
137 | } | |
138 | ||
7657c3a7 AH |
139 | /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ |
140 | if (reg == SDHCI_HOST_CONTROL) | |
141 | val &= ~ESDHC_HOST_CONTROL_RES; | |
142 | sdhci_be32bs_writeb(host, val, reg); | |
143 | } | |
144 | ||
a4071fbb HZ |
145 | /* |
146 | * For Abort or Suspend after Stop at Block Gap, ignore the ADMA | |
147 | * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC]) | |
148 | * and Block Gap Event(IRQSTAT[BGE]) are also set. | |
149 | * For Continue, apply soft reset for data(SYSCTL[RSTD]); | |
150 | * and re-issue the entire read transaction from beginning. | |
151 | */ | |
152 | static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) | |
153 | { | |
154 | u32 tmp; | |
155 | bool applicable; | |
156 | dma_addr_t dmastart; | |
157 | dma_addr_t dmanow; | |
158 | ||
159 | tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); | |
160 | tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |
161 | ||
162 | applicable = (intmask & SDHCI_INT_DATA_END) && | |
163 | (intmask & SDHCI_INT_BLK_GAP) && | |
164 | (tmp == VENDOR_V_23); | |
165 | if (!applicable) | |
166 | return; | |
167 | ||
168 | host->data->error = 0; | |
169 | dmastart = sg_dma_address(host->data->sg); | |
170 | dmanow = dmastart + host->data->bytes_xfered; | |
171 | /* | |
172 | * Force update to the next DMA block boundary. | |
173 | */ | |
174 | dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + | |
175 | SDHCI_DEFAULT_BOUNDARY_SIZE; | |
176 | host->data->bytes_xfered = dmanow - dmastart; | |
177 | sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); | |
178 | } | |
179 | ||
80872e21 | 180 | static int esdhc_of_enable_dma(struct sdhci_host *host) |
7657c3a7 AH |
181 | { |
182 | setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); | |
183 | return 0; | |
184 | } | |
185 | ||
80872e21 | 186 | static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host) |
7657c3a7 | 187 | { |
e307148f | 188 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
7657c3a7 | 189 | |
e307148f | 190 | return pltfm_host->clock; |
7657c3a7 AH |
191 | } |
192 | ||
80872e21 | 193 | static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) |
7657c3a7 | 194 | { |
e307148f | 195 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
7657c3a7 | 196 | |
e307148f | 197 | return pltfm_host->clock / 256 / 16; |
7657c3a7 AH |
198 | } |
199 | ||
f060bc9c JH |
200 | static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) |
201 | { | |
d31fc00a DA |
202 | |
203 | int pre_div = 2; | |
204 | int div = 1; | |
205 | u32 temp; | |
206 | ||
207 | if (clock == 0) | |
208 | goto out; | |
209 | ||
f060bc9c JH |
210 | /* Workaround to reduce the clock frequency for p1010 esdhc */ |
211 | if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { | |
212 | if (clock > 20000000) | |
213 | clock -= 5000000; | |
214 | if (clock > 40000000) | |
215 | clock -= 5000000; | |
216 | } | |
217 | ||
d31fc00a DA |
218 | temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); |
219 | temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | |
220 | | ESDHC_CLOCK_MASK); | |
221 | sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); | |
222 | ||
223 | while (host->max_clk / pre_div / 16 > clock && pre_div < 256) | |
224 | pre_div *= 2; | |
225 | ||
226 | while (host->max_clk / pre_div / div > clock && div < 16) | |
227 | div++; | |
228 | ||
229 | dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", | |
e76b8559 | 230 | clock, host->max_clk / pre_div / div); |
d31fc00a DA |
231 | |
232 | pre_div >>= 1; | |
233 | div--; | |
234 | ||
235 | temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); | |
236 | temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | |
237 | | (div << ESDHC_DIVIDER_SHIFT) | |
238 | | (pre_div << ESDHC_PREDIV_SHIFT)); | |
239 | sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); | |
240 | mdelay(1); | |
241 | out: | |
242 | host->clock = clock; | |
f060bc9c JH |
243 | } |
244 | ||
192b5372 JH |
245 | #ifdef CONFIG_PM |
246 | static u32 esdhc_proctl; | |
247 | static void esdhc_of_suspend(struct sdhci_host *host) | |
248 | { | |
249 | esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); | |
250 | } | |
251 | ||
252 | static void esdhc_of_resume(struct sdhci_host *host) | |
253 | { | |
254 | esdhc_of_enable_dma(host); | |
255 | sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); | |
256 | } | |
257 | #endif | |
258 | ||
63ef5d8c JH |
259 | static void esdhc_of_platform_init(struct sdhci_host *host) |
260 | { | |
261 | u32 vvn; | |
262 | ||
263 | vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); | |
264 | vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |
265 | if (vvn == VENDOR_V_22) | |
266 | host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; | |
3cf38833 JH |
267 | |
268 | if (vvn > VENDOR_V_22) | |
269 | host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; | |
63ef5d8c JH |
270 | } |
271 | ||
2317f56c | 272 | static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) |
66b50a00 OG |
273 | { |
274 | u32 ctrl; | |
275 | ||
276 | switch (width) { | |
277 | case MMC_BUS_WIDTH_8: | |
278 | ctrl = ESDHC_CTRL_8BITBUS; | |
279 | break; | |
280 | ||
281 | case MMC_BUS_WIDTH_4: | |
282 | ctrl = ESDHC_CTRL_4BITBUS; | |
283 | break; | |
284 | ||
285 | default: | |
286 | ctrl = 0; | |
287 | break; | |
288 | } | |
289 | ||
290 | clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, | |
291 | ESDHC_CTRL_BUSWIDTH_MASK, ctrl); | |
66b50a00 OG |
292 | } |
293 | ||
c915568d | 294 | static const struct sdhci_ops sdhci_esdhc_ops = { |
137ccd46 | 295 | .read_l = esdhc_readl, |
e307148f | 296 | .read_w = esdhc_readw, |
e51cbc9e | 297 | .read_b = esdhc_readb, |
a4071fbb | 298 | .write_l = esdhc_writel, |
e307148f SG |
299 | .write_w = esdhc_writew, |
300 | .write_b = esdhc_writeb, | |
f060bc9c | 301 | .set_clock = esdhc_of_set_clock, |
e307148f SG |
302 | .enable_dma = esdhc_of_enable_dma, |
303 | .get_max_clock = esdhc_of_get_max_clock, | |
304 | .get_min_clock = esdhc_of_get_min_clock, | |
63ef5d8c | 305 | .platform_init = esdhc_of_platform_init, |
192b5372 JH |
306 | #ifdef CONFIG_PM |
307 | .platform_suspend = esdhc_of_suspend, | |
308 | .platform_resume = esdhc_of_resume, | |
309 | #endif | |
a4071fbb | 310 | .adma_workaround = esdhci_of_adma_workaround, |
2317f56c | 311 | .set_bus_width = esdhc_pltfm_set_bus_width, |
e307148f SG |
312 | }; |
313 | ||
1db5eebf | 314 | static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { |
137ccd46 JH |
315 | /* |
316 | * card detection could be handled via GPIO | |
317 | * eSDHC cannot support End Attribute in NOP ADMA descriptor | |
318 | */ | |
e481e45d | 319 | .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION |
137ccd46 JH |
320 | | SDHCI_QUIRK_NO_CARD_NO_RESET |
321 | | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | |
e307148f | 322 | .ops = &sdhci_esdhc_ops, |
7657c3a7 | 323 | }; |
38576af1 | 324 | |
c3be1efd | 325 | static int sdhci_esdhc_probe(struct platform_device *pdev) |
38576af1 | 326 | { |
66b50a00 | 327 | struct sdhci_host *host; |
dcaff04d | 328 | struct device_node *np; |
66b50a00 OG |
329 | int ret; |
330 | ||
331 | host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); | |
332 | if (IS_ERR(host)) | |
333 | return PTR_ERR(host); | |
334 | ||
335 | sdhci_get_of_property(pdev); | |
336 | ||
dcaff04d OG |
337 | np = pdev->dev.of_node; |
338 | if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { | |
339 | /* | |
340 | * Freescale messed up with P2020 as it has a non-standard | |
341 | * host control register | |
342 | */ | |
343 | host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; | |
344 | } | |
345 | ||
66b50a00 OG |
346 | /* call to generic mmc_of_parse to support additional capabilities */ |
347 | mmc_of_parse(host->mmc); | |
490104ac | 348 | mmc_of_parse_voltage(np, &host->ocr_mask); |
66b50a00 OG |
349 | |
350 | ret = sdhci_add_host(host); | |
351 | if (ret) | |
352 | sdhci_pltfm_free(pdev); | |
353 | ||
354 | return ret; | |
38576af1 SG |
355 | } |
356 | ||
6e0ee714 | 357 | static int sdhci_esdhc_remove(struct platform_device *pdev) |
38576af1 SG |
358 | { |
359 | return sdhci_pltfm_unregister(pdev); | |
360 | } | |
361 | ||
362 | static const struct of_device_id sdhci_esdhc_of_match[] = { | |
363 | { .compatible = "fsl,mpc8379-esdhc" }, | |
364 | { .compatible = "fsl,mpc8536-esdhc" }, | |
365 | { .compatible = "fsl,esdhc" }, | |
366 | { } | |
367 | }; | |
368 | MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); | |
369 | ||
370 | static struct platform_driver sdhci_esdhc_driver = { | |
371 | .driver = { | |
372 | .name = "sdhci-esdhc", | |
373 | .owner = THIS_MODULE, | |
374 | .of_match_table = sdhci_esdhc_of_match, | |
29495aa0 | 375 | .pm = SDHCI_PLTFM_PMOPS, |
38576af1 SG |
376 | }, |
377 | .probe = sdhci_esdhc_probe, | |
0433c143 | 378 | .remove = sdhci_esdhc_remove, |
38576af1 SG |
379 | }; |
380 | ||
d1f81a64 | 381 | module_platform_driver(sdhci_esdhc_driver); |
38576af1 SG |
382 | |
383 | MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC"); | |
384 | MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, " | |
385 | "Anton Vorontsov <avorontsov@ru.mvista.com>"); | |
386 | MODULE_LICENSE("GPL v2"); |