Skip to content

Commit

Permalink
mmc: at91_mci: use DMA buffer for read
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Wolfgang Muees authored and Linus Torvalds committed Mar 6, 2010
1 parent 3780d90 commit 86ee26f
Showing 1 changed file with 32 additions and 96 deletions.
128 changes: 32 additions & 96 deletions drivers/mmc/host/at91_mci.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,81 +250,15 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
BUG_ON(size != 0);
}

/*
* Prepare a dma read
*/
static void at91_mci_pre_dma_read(struct at91mci_host *host)
{
int i;
struct scatterlist *sg;
struct mmc_command *cmd;
struct mmc_data *data;

pr_debug("pre dma read\n");

cmd = host->cmd;
if (!cmd) {
pr_debug("no command\n");
return;
}

data = cmd->data;
if (!data) {
pr_debug("no data\n");
return;
}

for (i = 0; i < 2; i++) {
/* nothing left to transfer */
if (host->transfer_index >= data->sg_len) {
pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index);
break;
}

/* Check to see if this needs filling */
if (i == 0) {
if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) {
pr_debug("Transfer active in current\n");
continue;
}
}
else {
if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) {
pr_debug("Transfer active in next\n");
continue;
}
}

/* Setup the next transfer */
pr_debug("Using transfer index %d\n", host->transfer_index);

sg = &data->sg[host->transfer_index++];
pr_debug("sg = %p\n", sg);

sg->dma_address = dma_map_page(NULL, sg_page(sg), sg->offset, sg->length, DMA_FROM_DEVICE);

pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length);

if (i == 0) {
at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address);
at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
}
else {
at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address);
at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
}
}

pr_debug("pre dma read done\n");
}

/*
* Handle after a dma read
*/
static void at91_mci_post_dma_read(struct at91mci_host *host)
{
struct mmc_command *cmd;
struct mmc_data *data;
unsigned int len, i, size;
unsigned *dmabuf = host->buffer;

pr_debug("post dma read\n");

Expand All @@ -340,42 +274,39 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
return;
}

while (host->in_use_index < host->transfer_index) {
struct scatterlist *sg;
size = data->blksz * data->blocks;
len = data->sg_len;

pr_debug("finishing index %d\n", host->in_use_index);
at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);

sg = &data->sg[host->in_use_index++];
for (i = 0; i < len; i++) {
struct scatterlist *sg;
int amount;
unsigned int *sgbuffer;

pr_debug("Unmapping page %08X\n", sg->dma_address);
sg = &data->sg[i];

dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
amount = min(size, sg->length);
size -= amount;

if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
unsigned int *buffer;
int index;

/* Swap the contents of the buffer */
buffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
pr_debug("buffer = %p, length = %d\n", buffer, sg->length);

for (index = 0; index < (sg->length / 4); index++)
buffer[index] = swab32(buffer[index]);

kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
for (index = 0; index < (amount / 4); index++)
sgbuffer[index] = swab32(*dmabuf++);
} else {
char *tmpv = (char *)dmabuf;
memcpy(sgbuffer, tmpv, amount);
tmpv += amount;
dmabuf = (unsigned *)tmpv;
}

flush_dcache_page(sg_page(sg));

data->bytes_xfered += sg->length;
}

/* Is there another transfer to trigger? */
if (host->transfer_index < data->sg_len)
at91_mci_pre_dma_read(host);
else {
at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
kunmap_atomic(((void *)sgbuffer)-sg->offset, KM_BIO_SRC_IRQ);
dmac_flush_range((void *)sgbuffer, ((void *)sgbuffer) + amount);
data->bytes_xfered += amount;
if (size == 0)
break;
}

pr_debug("post dma read done\n");
Expand Down Expand Up @@ -610,7 +541,12 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
*/
host->total_length = 0;

at91_mci_pre_dma_read(host);
at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address);
at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ?
(blocks * block_length) : (blocks * block_length) / 4);
at91_mci_write(host, ATMEL_PDC_RNPR, 0);
at91_mci_write(host, ATMEL_PDC_RNCR, 0);

ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
}
else {
Expand Down

0 comments on commit 86ee26f

Please sign in to comment.