diff options
author | Wolfgang Muees <wolfgang.mues@auerswald.de> | 2010-03-05 16:43:41 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-06 14:26:43 -0500 |
commit | 86ee26f5b0a889bf7e9f6351bbf01516d0686461 (patch) | |
tree | 59c9cc05be531a10a09d7e4f005be274c34fa068 /drivers/mmc | |
parent | 3780d90602dfef6df3d8b39b203d6bf7fb99f22a (diff) |
mmc: at91_mci: use DMA buffer for read
Convert the read to use the DMA buffer as well. The old code was doing
double-buffering DMA with the PDC; no way to make it work. Replace it
with a single-PDC approach. It also simplify things removing the need for
a pre_dma_read() function.
[nicolas.ferre@atmel.com coding style modifications]
Signed-off-by: Wolfgang Muees <wolfgang.mues@auerswald.de>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Andrew Victor <avictor.za@gmail.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/at91_mci.c | 128 |
1 files changed, 32 insertions, 96 deletions
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 76cb05ab9341..818b4e75708a 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c | |||
@@ -251,80 +251,14 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data | |||
251 | } | 251 | } |
252 | 252 | ||
253 | /* | 253 | /* |
254 | * Prepare a dma read | ||
255 | */ | ||
256 | static void at91_mci_pre_dma_read(struct at91mci_host *host) | ||
257 | { | ||
258 | int i; | ||
259 | struct scatterlist *sg; | ||
260 | struct mmc_command *cmd; | ||
261 | struct mmc_data *data; | ||
262 | |||
263 | pr_debug("pre dma read\n"); | ||
264 | |||
265 | cmd = host->cmd; | ||
266 | if (!cmd) { | ||
267 | pr_debug("no command\n"); | ||
268 | return; | ||
269 | } | ||
270 | |||
271 | data = cmd->data; | ||
272 | if (!data) { | ||
273 | pr_debug("no data\n"); | ||
274 | return; | ||
275 | } | ||
276 | |||
277 | for (i = 0; i < 2; i++) { | ||
278 | /* nothing left to transfer */ | ||
279 | if (host->transfer_index >= data->sg_len) { | ||
280 | pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index); | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | /* Check to see if this needs filling */ | ||
285 | if (i == 0) { | ||
286 | if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) { | ||
287 | pr_debug("Transfer active in current\n"); | ||
288 | continue; | ||
289 | } | ||
290 | } | ||
291 | else { | ||
292 | if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) { | ||
293 | pr_debug("Transfer active in next\n"); | ||
294 | continue; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | /* Setup the next transfer */ | ||
299 | pr_debug("Using transfer index %d\n", host->transfer_index); | ||
300 | |||
301 | sg = &data->sg[host->transfer_index++]; | ||
302 | pr_debug("sg = %p\n", sg); | ||
303 | |||
304 | sg->dma_address = dma_map_page(NULL, sg_page(sg), sg->offset, sg->length, DMA_FROM_DEVICE); | ||
305 | |||
306 | pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); | ||
307 | |||
308 | if (i == 0) { | ||
309 | at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); | ||
310 | at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4); | ||
311 | } | ||
312 | else { | ||
313 | at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); | ||
314 | at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | pr_debug("pre dma read done\n"); | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * Handle after a dma read | 254 | * Handle after a dma read |
323 | */ | 255 | */ |
324 | static void at91_mci_post_dma_read(struct at91mci_host *host) | 256 | static void at91_mci_post_dma_read(struct at91mci_host *host) |
325 | { | 257 | { |
326 | struct mmc_command *cmd; | 258 | struct mmc_command *cmd; |
327 | struct mmc_data *data; | 259 | struct mmc_data *data; |
260 | unsigned int len, i, size; | ||
261 | unsigned *dmabuf = host->buffer; | ||
328 | 262 | ||
329 | pr_debug("post dma read\n"); | 263 | pr_debug("post dma read\n"); |
330 | 264 | ||
@@ -340,42 +274,39 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) | |||
340 | return; | 274 | return; |
341 | } | 275 | } |
342 | 276 | ||
343 | while (host->in_use_index < host->transfer_index) { | 277 | size = data->blksz * data->blocks; |
344 | struct scatterlist *sg; | 278 | len = data->sg_len; |
345 | 279 | ||
346 | pr_debug("finishing index %d\n", host->in_use_index); | 280 | at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); |
281 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); | ||
347 | 282 | ||
348 | sg = &data->sg[host->in_use_index++]; | 283 | for (i = 0; i < len; i++) { |
284 | struct scatterlist *sg; | ||
285 | int amount; | ||
286 | unsigned int *sgbuffer; | ||
349 | 287 | ||
350 | pr_debug("Unmapping page %08X\n", sg->dma_address); | 288 | sg = &data->sg[i]; |
351 | 289 | ||
352 | dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); | 290 | sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; |
291 | amount = min(size, sg->length); | ||
292 | size -= amount; | ||
353 | 293 | ||
354 | if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ | 294 | if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ |
355 | unsigned int *buffer; | ||
356 | int index; | 295 | int index; |
357 | 296 | for (index = 0; index < (amount / 4); index++) | |
358 | /* Swap the contents of the buffer */ | 297 | sgbuffer[index] = swab32(*dmabuf++); |
359 | buffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; | 298 | } else { |
360 | pr_debug("buffer = %p, length = %d\n", buffer, sg->length); | 299 | char *tmpv = (char *)dmabuf; |
361 | 300 | memcpy(sgbuffer, tmpv, amount); | |
362 | for (index = 0; index < (sg->length / 4); index++) | 301 | tmpv += amount; |
363 | buffer[index] = swab32(buffer[index]); | 302 | dmabuf = (unsigned *)tmpv; |
364 | |||
365 | kunmap_atomic(buffer, KM_BIO_SRC_IRQ); | ||
366 | } | 303 | } |
367 | 304 | ||
368 | flush_dcache_page(sg_page(sg)); | 305 | kunmap_atomic(((void *)sgbuffer)-sg->offset, KM_BIO_SRC_IRQ); |
369 | 306 | dmac_flush_range((void *)sgbuffer, ((void *)sgbuffer) + amount); | |
370 | data->bytes_xfered += sg->length; | 307 | data->bytes_xfered += amount; |
371 | } | 308 | if (size == 0) |
372 | 309 | break; | |
373 | /* Is there another transfer to trigger? */ | ||
374 | if (host->transfer_index < data->sg_len) | ||
375 | at91_mci_pre_dma_read(host); | ||
376 | else { | ||
377 | at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); | ||
378 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); | ||
379 | } | 310 | } |
380 | 311 | ||
381 | pr_debug("post dma read done\n"); | 312 | pr_debug("post dma read done\n"); |
@@ -610,7 +541,12 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command | |||
610 | */ | 541 | */ |
611 | host->total_length = 0; | 542 | host->total_length = 0; |
612 | 543 | ||
613 | at91_mci_pre_dma_read(host); | 544 | at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address); |
545 | at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? | ||
546 | (blocks * block_length) : (blocks * block_length) / 4); | ||
547 | at91_mci_write(host, ATMEL_PDC_RNPR, 0); | ||
548 | at91_mci_write(host, ATMEL_PDC_RNCR, 0); | ||
549 | |||
614 | ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; | 550 | ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; |
615 | } | 551 | } |
616 | else { | 552 | else { |