Implements DMA on mmci driver for LPC3250 plateform
Subsystems (3)
PATCH 1/1 Implements DMA on mmci driver for LPC3250 plateform
UPDATE: Here is the patch cleaned up and validated with scripts/checkpatch.pl. I also add a check to prevent crashing when DMA is disabled.
ORIGINAL POST:
Hi,
Currently, LPC32xx plateform do not enable DMA on the mmci driver. This makes the driver useless because getting out data from a 64 bytes FIFO by interrupt is not fast enough (at standard SD-card data rate).
DMA is not enabled because LPC32xx has a bug that prevent DMA to work properly with the MMC controller (silicon bug, I guess). NXP did a patch to workaround this, but it has not been commited on the main branch. The patch is for linux 2.6.39.2 and does not use dmaengine.
So, I reworked this patch to make it compatible with the last kernel (3.7). Here it is. Have I any chance to see this patch be commited on the main branch?
Thanks a lot,
Gabriele
---
drivers/dma/amba-pl08x.c | 20 ++++++ drivers/mmc/host/mmci.c | 159 +++++++++++++++++++++++++++++++++++++++++----- drivers/mmc/host/mmci.h | 12 +++- 3 files changed, 174 insertions(+), 17 deletions(-)
drivers/dma/amba-pl08x.c +0 -0
@@ -1758,6 +1758,26 @@ static void pl08x_free_virtual_channels(struct dma_device *dmadev)
} }
+#ifdef CONFIG_ARCH_LPC32XX
+/*
+ * This exported function is used by mmci driver to workaround a bug in the
+ * LPC32xx chip, where the last DMA request is missed and must be simulated by
+ * software.
+ */
+void pl08x_force_dma_burst(struct dma_chan *chan)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct pl08x_driver_data *pl08x = plchan->host;
+
+ dev_dbg(&pl08x->adev->dev,
+ "force burst signal=%d chan=%p plchan=%p\n",
+ plchan->signal, chan, plchan);
+ if (plchan->signal >= 0)
+ writel(1 << plchan->signal, pl08x->base + PL080_SOFT_BREQ);
+}
+EXPORT_SYMBOL_GPL(pl08x_force_dma_burst);
+#endif
+
#ifdef CONFIG_DEBUG_FS
static const char *pl08x_state_str(enum pl08x_dma_chan_state state) {
drivers/mmc/host/mmci.c +1 -0
@@ -3,6 +3,7 @@* * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. * Copyright (C) 2010 ST-Ericsson SA +* Copyright (C) 2012 Precidata Sarl, Gabriele Mondada * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as
@@ -43,6 +44,16 @@
#define DRIVER_NAME "mmci-pl18x"
+#define COOKIE_PREP 0x00000001
+#define COOKIE_SINGLE 0x00000002
+#define COOKIE_ID_MASK 0xFF000000
+#define COOKIE_ID_SHIFT 24
+#define COOKIE_ID(n) (COOKIE_ID_MASK & ((n) << COOKIE_ID_SHIFT))
+
+#ifdef CONFIG_ARCH_LPC32XX
+#define DMA_TX_SIZE SZ_64K
+#endif
+
static unsigned int fmax = 515633;
/**
@@ -133,6 +144,10 @@ static struct variant_data variant_ux500v2 = {
.signal_direction = true,
};
+#ifdef CONFIG_ARCH_LPC32XX
+void pl08x_force_dma_burst(struct dma_chan *chan);
+#endif
+
/*
* This must be called with host->lock held
*/
@@ -267,14 +282,39 @@ static void mmci_dma_setup(struct mmci_host *host)
struct mmci_platform_data *plat = host->plat; const char *rxname, *txname; dma_cap_mask_t mask;
+#ifdef CONFIG_ARCH_LPC32XX
+ int i;
+#endif
if (!plat || !plat->dma_filter) {
dev_info(mmc_dev(host->mmc), "no DMA platform data\n");
return;
}- /* initialize pre request cookie */
- host->next_data.cookie = 1;
+#ifdef CONFIG_ARCH_LPC32XX
+ /*
+ * The LPC32XX do not support sg on TX DMA. So
+ * a temporary bouncing buffer is used if more than 1 sg segment
+ * is passed in the data request. The bouncing buffer will get a
+ * contiguous copy of the TX data and it will be used instead.
+ */
+ for (i = 0; i < 2; i++) {
+ host->dma_v_tx[i] = dma_alloc_coherent(mmc_dev(host->mmc),
+ DMA_TX_SIZE, &host->dma_p_tx[i], GFP_KERNEL);
+ if (host->dma_v_tx[i] == NULL) {
+ dev_err(mmc_dev(host->mmc),
+ "error getting DMA region\n");
+ return;
+ }
+ dev_info(mmc_dev(host->mmc), "DMA buffer: phy:%p, virt:%p\n",
+ (void *)host->dma_p_tx[i], host->dma_v_tx[i]);
+ }
+
+ host->dma_v_tx_current = host->dma_v_tx[0];
+ host->dma_p_tx_current = host->dma_p_tx[0];
+ host->next_data.dma_v_tx = host->dma_v_tx[1];
+ host->next_data.dma_p_tx = host->dma_p_tx[1];
+#endif
/* Try to acquire a generic DMA engine slave channel */
dma_cap_zero(mask);
@@ -344,12 +384,26 @@ static void mmci_dma_setup(struct mmci_host *host)
static inline void mmci_dma_release(struct mmci_host *host)
{
struct mmci_platform_data *plat = host->plat;+#ifdef CONFIG_ARCH_LPC32XX
+ int i;
+#endif
if (host->dma_rx_channel) dma_release_channel(host->dma_rx_channel); if (host->dma_tx_channel && plat->dma_tx_param) dma_release_channel(host->dma_tx_channel); host->dma_rx_channel = host->dma_tx_channel = NULL;
+
+#ifdef CONFIG_ARCH_LPC32XX
+ for (i = 0; i < 2; i++) {
+ if (host->dma_v_tx[i] == NULL)
+ continue;
+ dma_free_coherent(mmc_dev(host->mmc), DMA_TX_SIZE,
+ host->dma_v_tx[i], host->dma_p_tx[i]);
+ host->dma_v_tx[i] = NULL;
+ host->dma_p_tx[i] = 0;
+ }
+#endif
}
static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
@@ -385,8 +439,9 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
dir = DMA_FROM_DEVICE; }
- if (!data->host_cookie)
+ if (data->host_cookie && !(data->host_cookie & COOKIE_SINGLE))
dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+ /* TODO: no data->host_cookie=0 here ? */
/* * Use of DMA with scatter-gather is impossible. @@ -422,6 +477,7 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, struct dma_async_tx_descriptor *desc; enum dma_data_direction buffer_dirn; int nr_sg;
+ bool single = false;
/* Check if next job is already prepared */ if (data->host_cookie && !next && @@ -441,6 +497,9 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, conf.direction = DMA_MEM_TO_DEV; buffer_dirn = DMA_TO_DEVICE; chan = host->dma_tx_channel; +#ifdef CONFIG_ARCH_LPC32XX + conf.device_fc = true; +#endif } /* If there's no DMA channel, fall back to PIO */ @@ -452,13 +511,49 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, return -EINVAL; device = chan->device;
- nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len, buffer_dirn);
- if (nr_sg == 0)
- return -EINVAL;
-
dmaengine_slave_config(chan, &conf);
- desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
- conf.direction, DMA_CTRL_ACK);
+
+#ifdef CONFIG_ARCH_LPC32XX
+ if ((data->flags & MMC_DATA_WRITE) && (data->sg_len > 1))
+ single = true;
+#endif
+
+ if (single) {
+ int i;
+ unsigned char *dst = next ? next->dma_v_tx
+ : host->dma_v_tx_current;
+ size_t len = 0;
+
+ dev_dbg(mmc_dev(host->mmc), "use bounce buffer\n");
+ /* Move data to contiguous buffer first, then transfer it */
+ for (i = 0; i < data->sg_len; i++) {
+ unsigned long flags;
+ struct scatterlist *sg = &data->sg[i];
+ void *src;
+
+ /* Map the current scatter buffer, copy data, unmap */
+ local_irq_save(flags);
+ src = (unsigned char *)kmap_atomic(sg_page(sg)) +
+ sg->offset;
+ memcpy(dst + len, src, sg->length);
+ len += sg->length;
+ kunmap_atomic(src);
+ local_irq_restore(flags);
+ }
+
+ desc = dmaengine_prep_slave_single(chan,
+ next ? next->dma_p_tx : host->dma_p_tx_current,
+ len, buffer_dirn, DMA_CTRL_ACK);
+ } else {
+ nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len,
+ buffer_dirn);
+ if (nr_sg == 0)
+ return -EINVAL;
+
+ desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
+ conf.direction, DMA_CTRL_ACK);
+ }
+
if (!desc) goto unmap_exit;
@@ -470,12 +565,20 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
host->dma_desc_current = desc; }
+ data->host_cookie = COOKIE_PREP;
+ if (single)
+ data->host_cookie |= COOKIE_SINGLE;
+ if (next)
+ data->host_cookie |= COOKIE_ID(++next->cookie_cnt);
+
return 0;
unmap_exit:
if (!next) dmaengine_terminate_all(chan); - dma_unmap_sg(device->dev, data->sg, data->sg_len, buffer_dirn); + if (!single) + dma_unmap_sg(device->dev, data->sg, data->sg_len, buffer_dirn); + data->host_cookie = 0; return -ENOMEM; }
@@ -513,11 +616,16 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
{
struct mmci_host_next *next = &host->next_data;+#ifdef CONFIG_ARCH_LPC32XX
+ void *tmp_v;
+ dma_addr_t tmp_p;
+#endif
- if (data->host_cookie && data->host_cookie != next->cookie) {
- pr_warning("[%s] invalid cookie: data->host_cookie %d"
- " host->next_data.cookie %d\n",
- __func__, data->host_cookie, host->next_data.cookie);
+ if (data->host_cookie && ((data->host_cookie & COOKIE_ID_MASK) !=
+ COOKIE_ID(next->cookie_cnt))) {
+ pr_warn("[%s] invalid cookie: data->host_cookie=0x%08x host->next_data.cookie_cnt=0x%08x\n",
+ __func__, data->host_cookie,
+ host->next_data.cookie_cnt); data->host_cookie = 0; }
@@ -529,6 +637,15 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
next->dma_desc = NULL; next->dma_chan = NULL;
+
+#ifdef CONFIG_ARCH_LPC32XX
+ tmp_v = host->next_data.dma_v_tx;
+ host->next_data.dma_v_tx = host->dma_v_tx_current;
+ host->dma_v_tx_current = tmp_v;
+ tmp_p = host->next_data.dma_p_tx;
+ host->next_data.dma_p_tx = host->dma_p_tx_current;
+ host->dma_p_tx_current = tmp_p;
+#endif
}
static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -552,7 +669,8 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq,
if (mmci_dma_prep_data(host, data, nd)) data->host_cookie = 0; else - data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; + data->host_cookie = COOKIE_ID(++nd->cookie_cnt) | + COOKIE_PREP; } }
@@ -580,7 +698,7 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
if (chan) {
if (err)
dmaengine_terminate_all(chan);
- if (data->host_cookie)
+ if (data->host_cookie && !(data->host_cookie & COOKIE_SINGLE))
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, dir);
mrq->data->host_cookie = 0;
@@ -790,6 +908,15 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n");
if (status & MCI_DATAEND || data->error) {+#ifdef CONFIG_ARCH_LPC32XX
+ /*
+ * On LPC32XX, there is a problem with the DMA flow control and
+ * the last burst transfer is not performed. So we force the
+ * transfer programmatically here.
+ */
+ if ((data->flags & MMC_DATA_READ) && host->dma_rx_channel)
+ pl08x_force_dma_burst(host->dma_rx_channel);
+#endif
if (dma_inprogress(host)) mmci_dma_unmap(host, data); mmci_stop_data(host);
drivers/mmc/host/mmci.h +0 -0
@@ -159,7 +159,11 @@ struct dma_chan;
struct mmci_host_next {
struct dma_async_tx_descriptor *dma_desc;
struct dma_chan *dma_chan;- s32 cookie;
+ s32 cookie_cnt;
+#ifdef CONFIG_ARCH_LPC32XX
+ void *dma_v_tx;
+ dma_addr_t dma_p_tx;
+#endif
};
struct mmci_host {
@@ -206,6 +210,12 @@ struct mmci_host {
struct dma_chan *dma_tx_channel;
struct dma_async_tx_descriptor *dma_desc_current;
struct mmci_host_next next_data;+#ifdef CONFIG_ARCH_LPC32XX
+ void *dma_v_tx[2];
+ dma_addr_t dma_p_tx[2];
+ void *dma_v_tx_current;
+ dma_addr_t dma_p_tx_current;
+#endif
#define dma_inprogress(host) ((host)->dma_current)
#else
Signature · 1 line
1.7.10.4
PATCH 1/1 Implements DMA on mmci driver for LPC3250 plateform
Quoted 8 lines · Gabriele Mondada
Signed-off-by Gabriele Mondada <[email protected]>UPDATE: Here is the patch cleaned up and validated with
scripts/checkpatch.pl. I also add a check to prevent crashing when DMA is disabled.ORIGINAL POST:
Hi,
Currently, LPC32xx plateform do not enable DMA on the mmci driver. This makes the driver useless because getting out data from a 64 bytes FIFO by interrupt is not fast enough (at standard SD-card data rate).
Quoted 7 lines
DMA is not enabled because LPC32xx has a bug that prevent DMA to work properly with the MMC controller (silicon bug, I guess). NXP did a patch to workaround this, but it has not been commited on the main branch. The patch is for linux 2.6.39.2 and does not use dmaengine.
So, I reworked this patch to make it compatible with the last kernel (3.7). Here it is. Have I any chance to see this patch be commited on the main branch?
Thanks a lot,
Gabriele
Please consider that when I got this mail, the source/patch formatting was broken, like this:
drivers/dma/amba-pl08x.c | 20 ++++++drivers/mmc/host/mmci.c | 159 =
+++++++++++++++++++++++++++++++++++++++++-----drivers/mmc/host/mmci.h | 12 +++-
3 files changed, 174 insertions(+), 17 deletions(-)
drivers/dma/amba-pl08x.c +0 -0
@@ -1758,6 +1758,26 @@ static void pl08x_free_virtual_channels(struct =
dma_device *dmadev)
} }
Please use "git send-email" or sth. where code is included unchanged.
Thanks,
Roland
PATCH 1/1 Implements DMA on mmci driver for LPC3250 plateform
In reply to Gabriele MondadaGabriele,
Quoted 4 lines · Gabriele Mondada
Signed-off-by Gabriele Mondada <[email protected]>UPDATE: Here is the patch cleaned up and validated with
scripts/checkpatch.pl. I also add a check to prevent crashing when DMA is disabled.
Quoted 8 lines
ORIGINAL POST:
Hi,
Currently, LPC32xx plateform do not enable DMA on the mmci driver. This makes the driver useless because getting out data from a 64 bytes FIFO by interrupt is not fast enough (at standard SD-card data rate).DMA is not enabled because LPC32xx has a bug that prevent DMA to work properly with the MMC controller (silicon bug, I guess). NXP did a patch to workaround this, but it has not been commited on the main branch. The patch is for linux 2.6.39.2 and does not use dmaengine.
So, I reworked this patch to make it compatible with the last kernel (3.7). Here it is. Have I any chance to see this patch be commited on the main branch?
You can add this sort of description, that's not intended as part of the commit message after the "---" line and before the diff stat (*). It's typically used to add a changelog.
See here:
http://permalink.gmane.org/gmane.linux.ports.arm.omap/92215
Also, given you seem to be having some problems preparing the patch I suggest you to watch Greg's talk on submitting first patch:
http://www.youtube.com/watch?v=LLBrBBImJt4
Quoted 2 lines
---
(*) here!
Quoted 12 lines
drivers/dma/amba-pl08x.c | 20 ++++++ drivers/mmc/host/mmci.c | 159 +++++++++++++++++++++++++++++++++++++++++----- drivers/mmc/host/mmci.h | 12 +++- 3 files changed, 174 insertions(+), 17 deletions(-)drivers/dma/amba-pl08x.c +0
-0@@ -1758,6 +1758,26 @@ static void pl08x_free_virtual_channels(struct dma_device *dmadev) } }
[...]
Signature · 1 line
Ezequiel