]>
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 | ||
16 | #include <linux/io.h> | |
f060bc9c | 17 | #include <linux/of.h> |
7657c3a7 | 18 | #include <linux/delay.h> |
88b47679 | 19 | #include <linux/module.h> |
7657c3a7 | 20 | #include <linux/mmc/host.h> |
38576af1 | 21 | #include "sdhci-pltfm.h" |
80872e21 | 22 | #include "sdhci-esdhc.h" |
7657c3a7 | 23 | |
137ccd46 | 24 | #define VENDOR_V_22 0x12 |
a4071fbb | 25 | #define VENDOR_V_23 0x13 |
137ccd46 JH |
26 | static u32 esdhc_readl(struct sdhci_host *host, int reg) |
27 | { | |
28 | u32 ret; | |
29 | ||
30 | ret = in_be32(host->ioaddr + reg); | |
31 | /* | |
32 | * The bit of ADMA flag in eSDHC is not compatible with standard | |
33 | * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is | |
34 | * supported by eSDHC. | |
35 | * And for many FSL eSDHC controller, the reset value of field | |
36 | * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA, | |
37 | * only these vendor version is greater than 2.2/0x12 support ADMA. | |
38 | * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the | |
39 | * the verdor version number, oxFE is SDHCI_HOST_VERSION. | |
40 | */ | |
41 | if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { | |
42 | u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); | |
43 | tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |
44 | if (tmp > VENDOR_V_22) | |
45 | ret |= SDHCI_CAN_DO_ADMA2; | |
46 | } | |
47 | ||
48 | return ret; | |
49 | } | |
50 | ||
7657c3a7 AH |
51 | static u16 esdhc_readw(struct sdhci_host *host, int reg) |
52 | { | |
53 | u16 ret; | |
e51cbc9e X |
54 | int base = reg & ~0x3; |
55 | int shift = (reg & 0x2) * 8; | |
7657c3a7 AH |
56 | |
57 | if (unlikely(reg == SDHCI_HOST_VERSION)) | |
e51cbc9e | 58 | ret = in_be32(host->ioaddr + base) & 0xffff; |
7657c3a7 | 59 | else |
e51cbc9e X |
60 | ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; |
61 | return ret; | |
62 | } | |
63 | ||
64 | static u8 esdhc_readb(struct sdhci_host *host, int reg) | |
65 | { | |
66 | int base = reg & ~0x3; | |
67 | int shift = (reg & 0x3) * 8; | |
68 | u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; | |
ba8c4dc9 RZ |
69 | |
70 | /* | |
71 | * "DMA select" locates at offset 0x28 in SD specification, but on | |
72 | * P5020 or P3041, it locates at 0x29. | |
73 | */ | |
74 | if (reg == SDHCI_HOST_CONTROL) { | |
75 | u32 dma_bits; | |
76 | ||
77 | dma_bits = in_be32(host->ioaddr + reg); | |
78 | /* DMA select is 22,23 bits in Protocol Control Register */ | |
79 | dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; | |
80 | ||
81 | /* fixup the result */ | |
82 | ret &= ~SDHCI_CTRL_DMA_MASK; | |
83 | ret |= dma_bits; | |
84 | } | |
85 | ||
7657c3a7 AH |
86 | return ret; |
87 | } | |
88 | ||
a4071fbb HZ |
89 | static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) |
90 | { | |
91 | /* | |
92 | * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] | |
93 | * when SYSCTL[RSTD]) is set for some special operations. | |
94 | * No any impact other operation. | |
95 | */ | |
96 | if (reg == SDHCI_INT_ENABLE) | |
97 | val |= SDHCI_INT_BLK_GAP; | |
98 | sdhci_be32bs_writel(host, val, reg); | |
99 | } | |
100 | ||
7657c3a7 AH |
101 | static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) |
102 | { | |
103 | if (reg == SDHCI_BLOCK_SIZE) { | |
104 | /* | |
105 | * Two last DMA bits are reserved, and first one is used for | |
106 | * non-standard blksz of 4096 bytes that we don't support | |
107 | * yet. So clear the DMA boundary bits. | |
108 | */ | |
109 | val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); | |
110 | } | |
111 | sdhci_be32bs_writew(host, val, reg); | |
112 | } | |
113 | ||
114 | static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) | |
115 | { | |
ba8c4dc9 RZ |
116 | /* |
117 | * "DMA select" location is offset 0x28 in SD specification, but on | |
118 | * P5020 or P3041, it's located at 0x29. | |
119 | */ | |
120 | if (reg == SDHCI_HOST_CONTROL) { | |
121 | u32 dma_bits; | |
122 | ||
123 | /* DMA select is 22,23 bits in Protocol Control Register */ | |
124 | dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; | |
125 | clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, | |
126 | dma_bits); | |
127 | val &= ~SDHCI_CTRL_DMA_MASK; | |
128 | val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; | |
129 | } | |
130 | ||
7657c3a7 AH |
131 | /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ |
132 | if (reg == SDHCI_HOST_CONTROL) | |
133 | val &= ~ESDHC_HOST_CONTROL_RES; | |
134 | sdhci_be32bs_writeb(host, val, reg); | |
135 | } | |
136 | ||
a4071fbb HZ |
137 | /* |
138 | * For Abort or Suspend after Stop at Block Gap, ignore the ADMA | |
139 | * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC]) | |
140 | * and Block Gap Event(IRQSTAT[BGE]) are also set. | |
141 | * For Continue, apply soft reset for data(SYSCTL[RSTD]); | |
142 | * and re-issue the entire read transaction from beginning. | |
143 | */ | |
144 | static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) | |
145 | { | |
146 | u32 tmp; | |
147 | bool applicable; | |
148 | dma_addr_t dmastart; | |
149 | dma_addr_t dmanow; | |
150 | ||
151 | tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); | |
152 | tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |
153 | ||
154 | applicable = (intmask & SDHCI_INT_DATA_END) && | |
155 | (intmask & SDHCI_INT_BLK_GAP) && | |
156 | (tmp == VENDOR_V_23); | |
157 | if (!applicable) | |
158 | return; | |
159 | ||
160 | host->data->error = 0; | |
161 | dmastart = sg_dma_address(host->data->sg); | |
162 | dmanow = dmastart + host->data->bytes_xfered; | |
163 | /* | |
164 | * Force update to the next DMA block boundary. | |
165 | */ | |
166 | dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + | |
167 | SDHCI_DEFAULT_BOUNDARY_SIZE; | |
168 | host->data->bytes_xfered = dmanow - dmastart; | |
169 | sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); | |
170 | } | |
171 | ||
80872e21 | 172 | static int esdhc_of_enable_dma(struct sdhci_host *host) |
7657c3a7 AH |
173 | { |
174 | setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); | |
175 | return 0; | |
176 | } | |
177 | ||
80872e21 | 178 | static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host) |
7657c3a7 | 179 | { |
e307148f | 180 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
7657c3a7 | 181 | |
e307148f | 182 | return pltfm_host->clock; |
7657c3a7 AH |
183 | } |
184 | ||
80872e21 | 185 | static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) |
7657c3a7 | 186 | { |
e307148f | 187 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
7657c3a7 | 188 | |
e307148f | 189 | return pltfm_host->clock / 256 / 16; |
7657c3a7 AH |
190 | } |
191 | ||
f060bc9c JH |
192 | static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) |
193 | { | |
194 | /* Workaround to reduce the clock frequency for p1010 esdhc */ | |
195 | if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { | |
196 | if (clock > 20000000) | |
197 | clock -= 5000000; | |
198 | if (clock > 40000000) | |
199 | clock -= 5000000; | |
200 | } | |
201 | ||
202 | /* Set the clock */ | |
203 | esdhc_set_clock(host, clock); | |
204 | } | |
205 | ||
192b5372 JH |
206 | #ifdef CONFIG_PM |
207 | static u32 esdhc_proctl; | |
208 | static void esdhc_of_suspend(struct sdhci_host *host) | |
209 | { | |
210 | esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); | |
211 | } | |
212 | ||
213 | static void esdhc_of_resume(struct sdhci_host *host) | |
214 | { | |
215 | esdhc_of_enable_dma(host); | |
216 | sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); | |
217 | } | |
218 | #endif | |
219 | ||
63ef5d8c JH |
220 | static void esdhc_of_platform_init(struct sdhci_host *host) |
221 | { | |
222 | u32 vvn; | |
223 | ||
224 | vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); | |
225 | vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |
226 | if (vvn == VENDOR_V_22) | |
227 | host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; | |
3cf38833 JH |
228 | |
229 | if (vvn > VENDOR_V_22) | |
230 | host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; | |
63ef5d8c JH |
231 | } |
232 | ||
e307148f | 233 | static struct sdhci_ops sdhci_esdhc_ops = { |
137ccd46 | 234 | .read_l = esdhc_readl, |
e307148f | 235 | .read_w = esdhc_readw, |
e51cbc9e | 236 | .read_b = esdhc_readb, |
a4071fbb | 237 | .write_l = esdhc_writel, |
e307148f SG |
238 | .write_w = esdhc_writew, |
239 | .write_b = esdhc_writeb, | |
f060bc9c | 240 | .set_clock = esdhc_of_set_clock, |
e307148f SG |
241 | .enable_dma = esdhc_of_enable_dma, |
242 | .get_max_clock = esdhc_of_get_max_clock, | |
243 | .get_min_clock = esdhc_of_get_min_clock, | |
63ef5d8c | 244 | .platform_init = esdhc_of_platform_init, |
192b5372 JH |
245 | #ifdef CONFIG_PM |
246 | .platform_suspend = esdhc_of_suspend, | |
247 | .platform_resume = esdhc_of_resume, | |
248 | #endif | |
a4071fbb | 249 | .adma_workaround = esdhci_of_adma_workaround, |
e307148f SG |
250 | }; |
251 | ||
38576af1 | 252 | static struct sdhci_pltfm_data sdhci_esdhc_pdata = { |
137ccd46 JH |
253 | /* |
254 | * card detection could be handled via GPIO | |
255 | * eSDHC cannot support End Attribute in NOP ADMA descriptor | |
256 | */ | |
e481e45d | 257 | .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION |
137ccd46 JH |
258 | | SDHCI_QUIRK_NO_CARD_NO_RESET |
259 | | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, | |
e307148f | 260 | .ops = &sdhci_esdhc_ops, |
7657c3a7 | 261 | }; |
38576af1 | 262 | |
c3be1efd | 263 | static int sdhci_esdhc_probe(struct platform_device *pdev) |
38576af1 SG |
264 | { |
265 | return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata); | |
266 | } | |
267 | ||
6e0ee714 | 268 | static int sdhci_esdhc_remove(struct platform_device *pdev) |
38576af1 SG |
269 | { |
270 | return sdhci_pltfm_unregister(pdev); | |
271 | } | |
272 | ||
273 | static const struct of_device_id sdhci_esdhc_of_match[] = { | |
274 | { .compatible = "fsl,mpc8379-esdhc" }, | |
275 | { .compatible = "fsl,mpc8536-esdhc" }, | |
276 | { .compatible = "fsl,esdhc" }, | |
277 | { } | |
278 | }; | |
279 | MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); | |
280 | ||
281 | static struct platform_driver sdhci_esdhc_driver = { | |
282 | .driver = { | |
283 | .name = "sdhci-esdhc", | |
284 | .owner = THIS_MODULE, | |
285 | .of_match_table = sdhci_esdhc_of_match, | |
29495aa0 | 286 | .pm = SDHCI_PLTFM_PMOPS, |
38576af1 SG |
287 | }, |
288 | .probe = sdhci_esdhc_probe, | |
0433c143 | 289 | .remove = sdhci_esdhc_remove, |
38576af1 SG |
290 | }; |
291 | ||
d1f81a64 | 292 | module_platform_driver(sdhci_esdhc_driver); |
38576af1 SG |
293 | |
294 | MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC"); | |
295 | MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, " | |
296 | "Anton Vorontsov <avorontsov@ru.mvista.com>"); | |
297 | MODULE_LICENSE("GPL v2"); |