diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2006-07-02 11:50:59 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-07-02 11:50:59 -0400 |
commit | a406f5a3b68ee1db2306a2ba1c9b00dbd3505d05 (patch) | |
tree | 47434d59bab5732b7154522e14923c591c666360 /drivers/mmc/sdhci.c | |
parent | 3192a28f7d34ea8f1d0fef8ca5bc0314b5b5bb19 (diff) |
[MMC] Fix sdhci PIO routines
The sdhci controllers operate with blocks, not bytes. The PIO routines must
therefore make sure that the minimum unit transfered is a complete block.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/mmc/sdhci.c')
-rw-r--r-- | drivers/mmc/sdhci.c | 158 |
1 files changed, 98 insertions, 60 deletions
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index b9aa60aed7f0..8e480140cd28 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c | |||
@@ -8,12 +8,6 @@ | |||
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | /* | ||
12 | * Note that PIO transfer is rather crappy atm. The buffer full/empty | ||
13 | * interrupts aren't reliable so we currently transfer the entire buffer | ||
14 | * directly. Patches to solve the problem are welcome. | ||
15 | */ | ||
16 | |||
17 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
18 | #include <linux/highmem.h> | 12 | #include <linux/highmem.h> |
19 | #include <linux/pci.h> | 13 | #include <linux/pci.h> |
@@ -128,7 +122,7 @@ static void sdhci_init(struct sdhci_host *host) | |||
128 | SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | | 122 | SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | |
129 | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | | 123 | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | |
130 | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | | 124 | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | |
131 | SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL | | 125 | SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | |
132 | SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; | 126 | SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; |
133 | 127 | ||
134 | writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); | 128 | writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); |
@@ -189,79 +183,96 @@ static inline int sdhci_next_sg(struct sdhci_host* host) | |||
189 | return host->num_sg; | 183 | return host->num_sg; |
190 | } | 184 | } |
191 | 185 | ||
192 | static void sdhci_transfer_pio(struct sdhci_host *host) | 186 | static void sdhci_read_block_pio(struct sdhci_host *host) |
193 | { | 187 | { |
188 | int blksize, chunk_remain; | ||
189 | u32 data; | ||
194 | char *buffer; | 190 | char *buffer; |
195 | u32 mask; | 191 | int size; |
196 | int bytes, size; | ||
197 | unsigned long max_jiffies; | ||
198 | 192 | ||
199 | BUG_ON(!host->data); | 193 | DBG("PIO reading\n"); |
200 | 194 | ||
201 | if (host->num_sg == 0) | 195 | blksize = host->data->blksz; |
202 | return; | 196 | chunk_remain = 0; |
203 | 197 | data = 0; | |
204 | bytes = 0; | ||
205 | if (host->data->flags & MMC_DATA_READ) | ||
206 | mask = SDHCI_DATA_AVAILABLE; | ||
207 | else | ||
208 | mask = SDHCI_SPACE_AVAILABLE; | ||
209 | 198 | ||
210 | buffer = sdhci_kmap_sg(host) + host->offset; | 199 | buffer = sdhci_kmap_sg(host) + host->offset; |
211 | 200 | ||
212 | /* Transfer shouldn't take more than 5 s */ | 201 | while (blksize) { |
213 | max_jiffies = jiffies + HZ * 5; | 202 | if (chunk_remain == 0) { |
203 | data = readl(host->ioaddr + SDHCI_BUFFER); | ||
204 | chunk_remain = min(blksize, 4); | ||
205 | } | ||
214 | 206 | ||
215 | while (host->size > 0) { | 207 | size = min(host->size, host->remain); |
216 | if (time_after(jiffies, max_jiffies)) { | 208 | size = min(size, chunk_remain); |
217 | printk(KERN_ERR "%s: PIO transfer stalled. " | ||
218 | "Please report this to " | ||
219 | BUGMAIL ".\n", mmc_hostname(host->mmc)); | ||
220 | sdhci_dumpregs(host); | ||
221 | 209 | ||
222 | sdhci_kunmap_sg(host); | 210 | chunk_remain -= size; |
211 | blksize -= size; | ||
212 | host->offset += size; | ||
213 | host->remain -= size; | ||
214 | host->size -= size; | ||
215 | while (size) { | ||
216 | *buffer = data & 0xFF; | ||
217 | buffer++; | ||
218 | data >>= 8; | ||
219 | size--; | ||
220 | } | ||
223 | 221 | ||
224 | host->data->error = MMC_ERR_FAILED; | 222 | if (host->remain == 0) { |
225 | sdhci_finish_data(host); | 223 | sdhci_kunmap_sg(host); |
226 | return; | 224 | if (sdhci_next_sg(host) == 0) { |
225 | BUG_ON(blksize != 0); | ||
226 | return; | ||
227 | } | ||
228 | buffer = sdhci_kmap_sg(host); | ||
227 | } | 229 | } |
230 | } | ||
228 | 231 | ||
229 | if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask)) | 232 | sdhci_kunmap_sg(host); |
230 | continue; | 233 | } |
231 | 234 | ||
232 | size = min(host->size, host->remain); | 235 | static void sdhci_write_block_pio(struct sdhci_host *host) |
236 | { | ||
237 | int blksize, chunk_remain; | ||
238 | u32 data; | ||
239 | char *buffer; | ||
240 | int bytes, size; | ||
233 | 241 | ||
234 | if (size >= 4) { | 242 | DBG("PIO writing\n"); |
235 | if (host->data->flags & MMC_DATA_READ) | 243 | |
236 | *(u32*)buffer = readl(host->ioaddr + SDHCI_BUFFER); | 244 | blksize = host->data->blksz; |
237 | else | 245 | chunk_remain = 4; |
238 | writel(*(u32*)buffer, host->ioaddr + SDHCI_BUFFER); | 246 | data = 0; |
239 | size = 4; | 247 | |
240 | } else if (size >= 2) { | 248 | bytes = 0; |
241 | if (host->data->flags & MMC_DATA_READ) | 249 | buffer = sdhci_kmap_sg(host) + host->offset; |
242 | *(u16*)buffer = readw(host->ioaddr + SDHCI_BUFFER); | 250 | |
243 | else | 251 | while (blksize) { |
244 | writew(*(u16*)buffer, host->ioaddr + SDHCI_BUFFER); | 252 | size = min(host->size, host->remain); |
245 | size = 2; | 253 | size = min(size, chunk_remain); |
246 | } else { | ||
247 | if (host->data->flags & MMC_DATA_READ) | ||
248 | *(u8*)buffer = readb(host->ioaddr + SDHCI_BUFFER); | ||
249 | else | ||
250 | writeb(*(u8*)buffer, host->ioaddr + SDHCI_BUFFER); | ||
251 | size = 1; | ||
252 | } | ||
253 | 254 | ||
254 | buffer += size; | 255 | chunk_remain -= size; |
256 | blksize -= size; | ||
255 | host->offset += size; | 257 | host->offset += size; |
256 | host->remain -= size; | 258 | host->remain -= size; |
257 | |||
258 | bytes += size; | ||
259 | host->size -= size; | 259 | host->size -= size; |
260 | while (size) { | ||
261 | data >>= 8; | ||
262 | data |= (u32)*buffer << 24; | ||
263 | buffer++; | ||
264 | size--; | ||
265 | } | ||
266 | |||
267 | if (chunk_remain == 0) { | ||
268 | writel(data, host->ioaddr + SDHCI_BUFFER); | ||
269 | chunk_remain = min(blksize, 4); | ||
270 | } | ||
260 | 271 | ||
261 | if (host->remain == 0) { | 272 | if (host->remain == 0) { |
262 | sdhci_kunmap_sg(host); | 273 | sdhci_kunmap_sg(host); |
263 | if (sdhci_next_sg(host) == 0) { | 274 | if (sdhci_next_sg(host) == 0) { |
264 | DBG("PIO transfer: %d bytes\n", bytes); | 275 | BUG_ON(blksize != 0); |
265 | return; | 276 | return; |
266 | } | 277 | } |
267 | buffer = sdhci_kmap_sg(host); | 278 | buffer = sdhci_kmap_sg(host); |
@@ -269,8 +280,35 @@ static void sdhci_transfer_pio(struct sdhci_host *host) | |||
269 | } | 280 | } |
270 | 281 | ||
271 | sdhci_kunmap_sg(host); | 282 | sdhci_kunmap_sg(host); |
283 | } | ||
284 | |||
285 | static void sdhci_transfer_pio(struct sdhci_host *host) | ||
286 | { | ||
287 | u32 mask; | ||
288 | |||
289 | BUG_ON(!host->data); | ||
290 | |||
291 | if (host->size == 0) | ||
292 | return; | ||
293 | |||
294 | if (host->data->flags & MMC_DATA_READ) | ||
295 | mask = SDHCI_DATA_AVAILABLE; | ||
296 | else | ||
297 | mask = SDHCI_SPACE_AVAILABLE; | ||
298 | |||
299 | while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { | ||
300 | if (host->data->flags & MMC_DATA_READ) | ||
301 | sdhci_read_block_pio(host); | ||
302 | else | ||
303 | sdhci_write_block_pio(host); | ||
304 | |||
305 | if (host->size == 0) | ||
306 | break; | ||
307 | |||
308 | BUG_ON(host->num_sg == 0); | ||
309 | } | ||
272 | 310 | ||
273 | DBG("PIO transfer: %d bytes\n", bytes); | 311 | DBG("PIO transfer complete.\n"); |
274 | } | 312 | } |
275 | 313 | ||
276 | static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | 314 | static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) |
@@ -863,7 +901,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) | |||
863 | if (host->data->error != MMC_ERR_NONE) | 901 | if (host->data->error != MMC_ERR_NONE) |
864 | sdhci_finish_data(host); | 902 | sdhci_finish_data(host); |
865 | else { | 903 | else { |
866 | if (intmask & (SDHCI_INT_BUF_FULL | SDHCI_INT_BUF_EMPTY)) | 904 | if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) |
867 | sdhci_transfer_pio(host); | 905 | sdhci_transfer_pio(host); |
868 | 906 | ||
869 | if (intmask & SDHCI_INT_DATA_END) | 907 | if (intmask & SDHCI_INT_DATA_END) |