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 { |
