Signed-off-by: Nicolas Ferre <nicola...@atmel.com>
---
drivers/mmc/host/at91_mci.c | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index 4c30c56..e78cdc8 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -929,7 +929,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc->f_min = 375000;
mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_SDIO_IRQ;
+ mmc->caps = 0;
mmc->max_blk_size = MCI_MAXBLKSIZE;
mmc->max_blk_count = MCI_BLKATONCE;
@@ -958,6 +958,13 @@ static int __init at91_mci_probe(struct platform_device *pdev)
goto fail5;
}
+ /* Add SDIO capability when available */
+ if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) {
+ /* AT91SAM9260/9263 erratum */
+ if (host->board->wire4 || !host->board->slot_b)
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
+ }
+
/*
* Reserve GPIOs ... board init code makes sure these pins are set
* up as GPIOs with the right direction (input, except for vcc)
--
1.5.6.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
This patch is setting some max_ variables for the IO elevator, so the elevator
will put requests for large data blocks to the driver. This is critical for
a) speed
and
b) wear leveling of the flash chip controller: Otherwise the controller will
treat the SD card badly with millions of single 4 KByte write commands. This
will lead to a shorter life time for the SD cards.
Signed-off-by: Wolfgang Muees <wolfga...@auerswald.de>
Signed-off-by: Nicolas Ferre <nicola...@atmel.com>
---
drivers/mmc/host/at91_mci.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index 28fd565..4c30c56 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -934,6 +934,9 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc->max_blk_size = MCI_MAXBLKSIZE;
mmc->max_blk_count = MCI_BLKATONCE;
mmc->max_req_size = MCI_BUFSIZE;
+ mmc->max_phys_segs = MCI_BLKATONCE;
+ mmc->max_hw_segs = MCI_BLKATONCE;
+ mmc->max_seg_size = MCI_BUFSIZE;
host = mmc_priv(mmc);
host->mmc = mmc;
The TX DMA buffer is allocated only once, because the allocation/deallocation
of the buffer for EACH chunk of data is time-consuming and prone to memory
fragmentation.
Using a coherent DMA buffer avoids extra data cache calls.
Signed-off-by: Wolfgang Muees <wolfga...@auerswald.de>
[nicola...@atmel.com: coding style modifications]
Signed-off-by: Nicolas Ferre <nicola...@atmel.com>
---
drivers/mmc/host/at91_mci.c | 49 +++++++++++++++++++++----------------------
1 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index f2cb716..fd01195 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -88,6 +88,10 @@
#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg))
#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg))
+#define MCI_BLKSIZE 512
+#define MCI_MAXBLKSIZE 4095
+#define MCI_BLKATONCE 256
+#define MCI_BUFSIZE (MCI_BLKSIZE * MCI_BLKATONCE)
/*
* Low level type for this driver
@@ -604,7 +608,6 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
/*
* Handle a read
*/
- host->buffer = NULL;
host->total_length = 0;
at91_mci_pre_dma_read(host);
@@ -623,20 +626,8 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
if (host->total_length < 12)
host->total_length = 12;
- host->buffer = kmalloc(host->total_length, GFP_KERNEL);
- if (!host->buffer) {
- pr_debug("Can't alloc tx buffer\n");
- cmd->error = -ENOMEM;
- mmc_request_done(host->mmc, host->request);
- return;
- }
-
at91_mci_sg_to_dma(host, data);
- host->physical_address = dma_map_single(NULL,
- host->buffer, host->total_length,
- DMA_TO_DEVICE);
-
pr_debug("Transmitting %d bytes\n", host->total_length);
at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
@@ -703,14 +694,6 @@ static void at91_mci_completed_command(struct at91mci_host *host, unsigned int s
cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
- if (host->buffer) {
- dma_unmap_single(NULL,
- host->physical_address, host->total_length,
- DMA_TO_DEVICE);
- kfree(host->buffer);
- host->buffer = NULL;
- }
-
pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n",
status, at91_mci_read(host, AT91_MCI_SR),
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
@@ -1012,12 +995,12 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_SDIO_IRQ;
- mmc->max_blk_size = 4095;
- mmc->max_blk_count = mmc->max_req_size;
+ mmc->max_blk_size = MCI_MAXBLKSIZE;
+ mmc->max_blk_count = MCI_BLKATONCE;
+ mmc->max_req_size = MCI_BUFSIZE;
host = mmc_priv(mmc);
host->mmc = mmc;
- host->buffer = NULL;
host->bus_mode = 0;
host->board = pdev->dev.platform_data;
if (host->board->wire4) {
@@ -1028,6 +1011,14 @@ static int __init at91_mci_probe(struct platform_device *pdev)
" - using 1 wire\n");
}
+ host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE,
+ &host->physical_address, GFP_KERNEL);
+ if (!host->buffer) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "Can't allocate transmit buffer\n");
+ goto fail5;
+ }
+
/*
* Reserve GPIOs ... board init code makes sure these pins are set
* up as GPIOs with the right direction (input, except for vcc)
@@ -1036,7 +1027,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
ret = gpio_request(host->board->det_pin, "mmc_detect");
if (ret < 0) {
dev_dbg(&pdev->dev, "couldn't claim card detect pin\n");
- goto fail5;
+ goto fail4b;
}
}
if (host->board->wp_pin) {
@@ -1136,6 +1127,10 @@ fail3:
fail4:
if (host->board->det_pin)
gpio_free(host->board->det_pin);
+fail4b:
+ if (host->buffer)
+ dma_free_coherent(&pdev->dev, MCI_BUFSIZE,
+ host->buffer, host->physical_address);
fail5:
mmc_free_host(mmc);
fail6:
@@ -1158,6 +1153,10 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
host = mmc_priv(mmc);
+ if (host->buffer)
+ dma_free_coherent(&pdev->dev, MCI_BUFSIZE,
+ host->buffer, host->physical_address);
+
if (host->board->det_pin) {
if (device_can_wakeup(&pdev->dev))
free_irq(gpio_to_irq(host->board->det_pin), host);
This patch converts 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.
Signed-off-by: Wolfgang Muees <wolfga...@auerswald.de>
[nicola...@atmel.com coding style modifications]
Signed-off-by: Nicolas Ferre <nicola...@atmel.com>
---
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 fd01195..28fd565 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
}
/*
- * 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");
@@ -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");
@@ -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 {
> + kunmap_atomic(((void *)sgbuffer)-sg->offset, KM_BIO_SRC_IRQ);
It's a feature of kunmap_atomic() that it will accept a pointer to any
location in the page. So the subtraction isn't strictly needed.
And if the subtraction is removed, the (void*) cast can also be removed.
Am Dienstag, 2. M�rz 2010 schrieb Andrew Morton:
> Nicolas Ferre <nicola...@atmel.com> wrote:
> > + kunmap_atomic(((void *)sgbuffer)-sg->offset, KM_BIO_SRC_IRQ);
>
> It's a feature of kunmap_atomic() that it will accept a pointer to any
> location in the page. So the subtraction isn't strictly needed.
>
> And if the subtraction is removed, the (void*) cast can also be removed.
I did not know this. Thank you for pointing this out. So this part of the
patch (which was purely cosmetic because I have seen no error without the
subtraction) can be omitted.
best regards
i. A. Wolfgang M�es
--
Auerswald Gesellschaft f�r Datensysteme mbH
Hardware Development
Telefon: +49 (0)5306 9219 562
Telefax: +49 (0)5306 9219 94
E-Mail: Wolfga...@Auerswald.de
Web: http://www.auerswald.de
--------------------------------------------------------------
Auerswald Gesellschaft f�r Datensysteme mbH
Vor den Grash�fen 1, 38162 Cremlingen
Registriert beim AG Braunschweig HRB 7499
Gesch�ftsf�hrer: Dipl-Ing. Gerhard Auerswald
Signed-off-by: Nicolas Ferre <nicola...@atmel.com>
---
Andrew,
I created a new patch that you can stack at the top of the previous
patch series. I guess that you cannot fold it in other patches as the
kunmap_atomic() correction impacts two different patches.
Thanks.
drivers/mmc/host/at91_mci.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index 55507da..91dc60c 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -248,7 +248,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
dmabuf = (unsigned *)tmpv;
}
- kunmap_atomic(((void *)sgbuffer) - sg->offset, KM_BIO_SRC_IRQ);
+ kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
if (size == 0)
break;
@@ -313,7 +313,7 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
dmabuf = (unsigned *)tmpv;
}
- kunmap_atomic(((void *)sgbuffer)-sg->offset, KM_BIO_SRC_IRQ);
+ kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
dmac_flush_range((void *)sgbuffer, ((void *)sgbuffer) + amount);
data->bytes_xfered += amount;
if (size == 0)
--
1.5.6.5