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 | int pre_div = 2; |
203 | int div = 1; | |
204 | u32 temp; | |
205 | ||
1650d0c7 RK |
206 | host->mmc->actual_clock = 0; |
207 | ||
d31fc00a | 208 | if (clock == 0) |
373073ef | 209 | return; |
d31fc00a | 210 | |
f060bc9c JH |
211 | /* Workaround to reduce the clock frequency for p1010 esdhc */ |
212 | if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { | |
213 | if (clock > 20000000) | |
214 | clock -= 5000000; | |
215 | if (clock > 40000000) | |
216 | clock -= 5000000; | |
217 | } | |
218 | ||
d31fc00a DA |
219 | temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); |
220 | temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | |
221 | | ESDHC_CLOCK_MASK); | |
222 | sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); | |
223 | ||
224 | while (host->max_clk / pre_div / 16 > clock && pre_div < 256) | |
225 | pre_div *= 2; | |
226 | ||
227 | while (host->max_clk / pre_div / div > clock && div < 16) | |
228 | div++; | |
229 | ||
230 | dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", | |
e76b8559 | 231 | clock, host->max_clk / pre_div / div); |
d31fc00a DA |
232 | |
233 | pre_div >>= 1; | |
234 | div--; | |
235 | ||
236 | temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); | |
237 | temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | |
238 | | (div << ESDHC_DIVIDER_SHIFT) | |
239 | | (pre_div << ESDHC_PREDIV_SHIFT)); | |
240 | sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); | |
241 | mdelay(1); | |
f060bc9c JH |
242 | } |
243 | ||
63ef5d8c JH |
244 | static void esdhc_of_platform_init(struct sdhci_host *host) |
245 | { | |
246 | u32 vvn; | |
247 | ||
248 | vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); | |
249 | vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |
250 | if (vvn == VENDOR_V_22) | |
251 | host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; | |
3cf38833 JH |
252 | |
253 | if (vvn > VENDOR_V_22) | |
254 | host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; | |
63ef5d8c JH |
255 | } |
256 | ||
2317f56c | 257 | static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) |
66b50a00 OG |
258 | { |
259 | u32 ctrl; | |
260 | ||
261 | switch (width) { | |
262 | case MMC_BUS_WIDTH_8: | |
263 | ctrl = ESDHC_CTRL_8BITBUS; | |
264 | break; | |
265 | ||
266 | case MMC_BUS_WIDTH_4: | |
267 | ctrl = ESDHC_CTRL_4BITBUS; | |
268 | break; | |
269 | ||
270 | default: | |
271 | ctrl = 0; | |
272 | break; | |
273 | } | |
274 | ||
275 | clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, | |
276 | ESDHC_CTRL_BUSWIDTH_MASK, ctrl); | |
66b50a00 OG |
277 | } |
278 | ||
c915568d | 279 | static const struct sdhci_ops sdhci_esdhc_ops = { |
137ccd46 | 280 | .read_l = esdhc_readl, |
e307148f | 281 | .read_w = esdhc_readw, |
e51cbc9e | 282 | .read_b = esdhc_readb, |
a4071fbb | 283 | .write_l = esdhc_writel, |
e307148f SG |
284 | .write_w = esdhc_writew, |
285 | .write_b = esdhc_writeb, | |
f060bc9c | 286 | .set_clock = esdhc_of_set_clock, |
e307148f SG |
287 | .enable_dma = esdhc_of_enable_dma, |
288 | .get_max_clock = esdhc_of_get_max_clock, | |
289 | .get_min_clock = esdhc_of_get_min_clock, | |
63ef5d8c | 290 | .platform_init = esdhc_of_platform_init, |
a4071fbb | 291 | .adma_workaround = esdhci_of_adma_workaround, |
2317f56c | 292 | .set_bus_width = esdhc_pltfm_set_bus_width, |
03231f9b | 293 | .reset = sdhci_reset, |
96d7b78c | 294 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
e307148f SG |
295 | }; |
296 | ||
723f7924 RK |
297 | #ifdef CONFIG_PM |
298 | ||
299 | static u32 esdhc_proctl; | |
300 | static int esdhc_of_suspend(struct device *dev) | |
301 | { | |
302 | struct sdhci_host *host = dev_get_drvdata(dev); | |
303 | ||
304 | esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); | |
305 | ||
306 | return sdhci_suspend_host(host); | |
307 | } | |
308 | ||
06732b84 | 309 | static int esdhc_of_resume(struct device *dev) |
723f7924 RK |
310 | { |
311 | struct sdhci_host *host = dev_get_drvdata(dev); | |
312 | int ret = sdhci_resume_host(host); | |
313 | ||
314 | if (ret == 0) { | |
315 | /* Isn't this already done by sdhci_resume_host() ? --rmk */ | |
316 | esdhc_of_enable_dma(host); | |
317 | sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); | |
318 | } | |
319 | ||
320 | return ret; | |
321 | } | |
322 | ||
323 | static const struct dev_pm_ops esdhc_pmops = { | |
06732b84 UH |
324 | .suspend = esdhc_of_suspend, |
325 | .resume = esdhc_of_resume, | |
723f7924 RK |
326 | }; |
327 | #define ESDHC_PMOPS (&esdhc_pmops) | |
328 | #else | |
329 | #define ESDHC_PMOPS NULL | |
330 | #endif | |
331 | ||
1db5eebf | 332 | static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { |
137ccd46 JH |
333 | /* |
334 | * card detection could be handled via GPIO | |
335 | * eSDHC cannot support End Attribute in NOP ADMA descriptor | |
336 | */ | |
e481e45d | 337 | .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION |
137ccd46 JH |
338 | | SDHCI_QUIRK_NO_CARD_NO_RESET |
339 | | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | |
e307148f | 340 | .ops = &sdhci_esdhc_ops, |
7657c3a7 | 341 | }; |
38576af1 | 342 | |
c3be1efd | 343 | static int sdhci_esdhc_probe(struct platform_device *pdev) |
38576af1 | 344 | { |
66b50a00 | 345 | struct sdhci_host *host; |
dcaff04d | 346 | struct device_node *np; |
66b50a00 OG |
347 | int ret; |
348 | ||
349 | host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); | |
350 | if (IS_ERR(host)) | |
351 | return PTR_ERR(host); | |
352 | ||
353 | sdhci_get_of_property(pdev); | |
354 | ||
dcaff04d OG |
355 | np = pdev->dev.of_node; |
356 | if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { | |
357 | /* | |
358 | * Freescale messed up with P2020 as it has a non-standard | |
359 | * host control register | |
360 | */ | |
361 | host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; | |
362 | } | |
363 | ||
66b50a00 OG |
364 | /* call to generic mmc_of_parse to support additional capabilities */ |
365 | mmc_of_parse(host->mmc); | |
490104ac | 366 | mmc_of_parse_voltage(np, &host->ocr_mask); |
66b50a00 OG |
367 | |
368 | ret = sdhci_add_host(host); | |
369 | if (ret) | |
370 | sdhci_pltfm_free(pdev); | |
371 | ||
372 | return ret; | |
38576af1 SG |
373 | } |
374 | ||
6e0ee714 | 375 | static int sdhci_esdhc_remove(struct platform_device *pdev) |
38576af1 SG |
376 | { |
377 | return sdhci_pltfm_unregister(pdev); | |
378 | } | |
379 | ||
380 | static const struct of_device_id sdhci_esdhc_of_match[] = { | |
381 | { .compatible = "fsl,mpc8379-esdhc" }, | |
382 | { .compatible = "fsl,mpc8536-esdhc" }, | |
383 | { .compatible = "fsl,esdhc" }, | |
384 | { } | |
385 | }; | |
386 | MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); | |
387 | ||
388 | static struct platform_driver sdhci_esdhc_driver = { | |
389 | .driver = { | |
390 | .name = "sdhci-esdhc", | |
38576af1 | 391 | .of_match_table = sdhci_esdhc_of_match, |
723f7924 | 392 | .pm = ESDHC_PMOPS, |
38576af1 SG |
393 | }, |
394 | .probe = sdhci_esdhc_probe, | |
0433c143 | 395 | .remove = sdhci_esdhc_remove, |
38576af1 SG |
396 | }; |
397 | ||
d1f81a64 | 398 | module_platform_driver(sdhci_esdhc_driver); |
38576af1 SG |
399 | |
400 | MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC"); | |
401 | MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, " | |
402 | "Anton Vorontsov <avorontsov@ru.mvista.com>"); | |
403 | MODULE_LICENSE("GPL v2"); |