Discussion:
[PATCH 0/4] omap4: hsmmc: Adding ADMA support
Kishore Kadiyala
2011-01-18 17:26:27 UTC
Permalink
This patch series adds support for ADMA on MMC1 & MMC2 controllers on OMAP4.
There is no performance improvement observed using ADMA over SDMA.
Advantage using ADMA could be reducing contention over SDMA.
Also the series includes some cleanup.

The series is based on 2.6.37-rc8 and tested on OMAP4430SDP & OMAP3430SDP.


Kishore Kadiyala (4):
omap: hsmmc: Rename use_dma to xfer_type and define possible
transfers
omap: hsmmc: Rename and cleanup omap_hsmmc_dma_cleanup
omap4: hsmmc: Adding ADMA support for MMC1 & MMC2 controllers
omap4: hsmmc: enable ADMA for MMC1 & MMC2

arch/arm/mach-omap2/hsmmc.c | 13 ++-
arch/arm/plat-omap/include/plat/mmc.h | 1 +
drivers/mmc/host/omap_hsmmc.c | 241 +++++++++++++++++++++++++++------
3 files changed, 210 insertions(+), 45 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Kishore Kadiyala
2011-01-18 17:26:29 UTC
Permalink
Renaming omap_hsmmc_dma_cleanup as omap_hsmmc_xfer_cleanup and doing some
cleanup by handling the error & success scenarios during a transfer for
different xfer_type.

Signed-off-by: Kishore Kadiyala <***@ti.com>
Reviewed-by: Sukumar Ghorai <s-***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 52 ++++++++++++++++++++++------------------
1 files changed, 29 insertions(+), 23 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 8fb8586..7cf0383 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -935,25 +935,37 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
}

/*
- * DMA clean up for command errors
+ * SDMA clean up during SDMA transfers.
+ * Also unmapping of sg list in case of error/transfer done during
+ * SDMA/ADMA transfers.
*/
-static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
+static void omap_hsmmc_xfer_cleanup(struct omap_hsmmc_host *host, int errno)
{
int dma_ch;
+ struct mmc_data *data = errno ? host->data : host->mrq->data;

- host->data->error = errno;
-
- spin_lock(&host->irq_lock);
- dma_ch = host->dma_ch;
- host->dma_ch = -1;
- spin_unlock(&host->irq_lock);
-
- if (host->xfer_type && dma_ch != -1) {
- dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
- omap_hsmmc_get_dma_dir(host, host->data));
+ switch (host->xfer_type) {
+ case OMAP_HSMMC_USE_SDMA_XFER:
+ spin_lock(&host->irq_lock);
+ dma_ch = host->dma_ch;
+ host->dma_ch = -1;
+ spin_unlock(&host->irq_lock);
+ if (dma_ch != -1)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ host->dma_len,
+ omap_hsmmc_get_dma_dir(host, data));
omap_free_dma(dma_ch);
+ break;
+ case OMAP_HSMMC_USE_PIO_XFER:
+ /* TODO */
+ break;
+ default:
+ dev_dbg(mmc_dev(host->mmc), "Unknown xfer_type\n");
+ }
+ if (errno) {
+ host->data->error = errno;
+ host->data = NULL;
}
- host->data = NULL;
}

/*
@@ -1059,7 +1071,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
}
if (host->data || host->response_busy) {
if (host->data)
- omap_hsmmc_dma_cleanup(host,
+ omap_hsmmc_xfer_cleanup(host,
-ETIMEDOUT);
host->response_busy = 0;
omap_hsmmc_reset_controller_fsm(host, SRD);
@@ -1072,7 +1084,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
-ETIMEDOUT : -EILSEQ;

if (host->data)
- omap_hsmmc_dma_cleanup(host, err);
+ omap_hsmmc_xfer_cleanup(host, err);
else
host->mrq->cmd->error = err;
host->response_busy = 0;
@@ -1310,7 +1322,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
{
struct omap_hsmmc_host *host = cb_data;
struct mmc_data *data = host->mrq->data;
- int dma_ch, req_in_progress;
+ int req_in_progress;

if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
@@ -1332,16 +1344,10 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
spin_unlock(&host->irq_lock);
return;
}
-
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
- omap_hsmmc_get_dma_dir(host, data));
-
req_in_progress = host->req_in_progress;
- dma_ch = host->dma_ch;
- host->dma_ch = -1;
spin_unlock(&host->irq_lock);

- omap_free_dma(dma_ch);
+ omap_hsmmc_xfer_cleanup(host, 0);

/* If DMA has finished after TC, complete the request */
if (!req_in_progress) {
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Kishore Kadiyala
2011-01-18 17:26:28 UTC
Permalink
OMAP4 introduces dedicated internal DMA which is ADMA for its MMC
controllers HSMMC1 & HSMMC2.
Renaming "use_dma" member of the struct omap_hsmmc_host to "xfer_type"
and defining the transfer modes PIO/SDMA/ADMA that can be used by the
MMC controller.

Signed-off-by: Kishore Kadiyala <***@ti.com>
Reviewed-by: Sukumar Ghorai <s-***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 20 +++++++++++++-------
1 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 5d46021..8fb8586 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -125,6 +125,11 @@
#define OMAP_MMC_SLEEP_TIMEOUT 1000
#define OMAP_MMC_OFF_TIMEOUT 8000

+/* Transfer Modes used by MMC controller */
+#define OMAP_HSMMC_USE_PIO_XFER 0
+#define OMAP_HSMMC_USE_SDMA_XFER 1
+#define OMAP_HSMMC_USE_ADMA_XFER 2
+
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@@ -172,7 +177,8 @@ struct omap_hsmmc_host {
u32 bytesleft;
int suspended;
int irq;
- int use_dma, dma_ch;
+ int dma_ch;
+ int xfer_type; /* Transfer can be PIO/SDMA/ADMA */
int dma_line_tx, dma_line_rx;
int slot_id;
int got_dbclk;
@@ -545,7 +551,7 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
{
unsigned int irq_mask;

- if (host->use_dma)
+ if (host->xfer_type != OMAP_HSMMC_USE_PIO_XFER)
irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE);
else
irq_mask = INT_EN_MASK;
@@ -835,7 +841,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
cmdreg &= ~(DDIR);
}

- if (host->use_dma)
+ if (host->xfer_type != OMAP_HSMMC_USE_PIO_XFER)
cmdreg |= DMA_EN;

host->req_in_progress = 1;
@@ -864,7 +870,7 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req

omap_hsmmc_disable_irq(host);
/* Do not complete the request if DMA is still in progress */
- if (mrq->data && host->use_dma && dma_ch != -1)
+ if (mrq->data && host->xfer_type && dma_ch != -1)
return;
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
@@ -942,7 +948,7 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
host->dma_ch = -1;
spin_unlock(&host->irq_lock);

- if (host->use_dma && dma_ch != -1) {
+ if (host->xfer_type && dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
omap_hsmmc_get_dma_dir(host, host->data));
omap_free_dma(dma_ch);
@@ -1451,7 +1457,7 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
| (req->data->blocks << 16));
set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);

- if (host->use_dma) {
+ if (host->xfer_type) {
ret = omap_hsmmc_start_dma_transfer(host, req);
if (ret != 0) {
dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
@@ -2050,7 +2056,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
host->mmc = mmc;
host->pdata = pdata;
host->dev = &pdev->dev;
- host->use_dma = 1;
+ host->xfer_type = OMAP_HSMMC_USE_SDMA_XFER;
host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1;
host->irq = irq;
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Kishore Kadiyala
2011-01-18 17:26:31 UTC
Permalink
Enable ADMA support for MMC1 & MMC2 controller on omap4 by updating
"features" of struct omap_mmc_platform_data.

Signed-off-by: Kishore Kadiyala <***@ti.com>
Signed-off-by: Venkatraman S <***@ti.com>
Reviewed-by: Sukumar Ghorai <s-***@ti.com>
---
arch/arm/mach-omap2/hsmmc.c | 13 +++++++++++--
1 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
index 34272e4..2ac7271 100644
--- a/arch/arm/mach-omap2/hsmmc.c
+++ b/arch/arm/mach-omap2/hsmmc.c
@@ -307,8 +307,17 @@ void __init omap2_hsmmc_init(struct omap2_hsmmc_info *controllers)
else
mmc->slots[0].features |= HSMMC_HAS_PBIAS;

- if (cpu_is_omap44xx() && (omap_rev() > OMAP4430_REV_ES1_0))
- mmc->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
+ /*
+ * OMAP4: MMC1 & MMC2 controllers support ADMA.
+ * Default works with SDMA.
+ * For ADMA support, update respective BIT in features.
+ */
+ if (cpu_is_omap44xx()) {
+ mmc->slots[0].features |= HSMMC_HAS_ADMA_SUPPORT;
+ if (omap_rev() > OMAP4430_REV_ES1_0)
+ mmc->slots[0].features |=
+ HSMMC_HAS_UPDATED_RESET;
+ }

switch (c->mmc) {
case 1:
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Kishore Kadiyala
2011-01-18 17:26:30 UTC
Permalink
On OMAP4, MMC1 & MMC2 controllers support ADMA feature which will
provide direct access to internal data.
Basically ADMA is a DMA controller embedded in the MMC controller.
It fetches each descriptor line [address+length+attributes] from a
descriptor table and executes the corresponding action [based on attributes].
Base address of Descriptor table in stored in MMCHS_ADMASAL register.

Signed-off-by: Kishore Kadiyala <***@ti.com>
Signed-off-by: Venkatraman S <***@ti.com>
Signed-off-by: Santosh Shilimkar <***@ti.com>
Reviewed-by: Sukumar Ghorai <s-***@ti.com>
---
arch/arm/plat-omap/include/plat/mmc.h | 1 +
drivers/mmc/host/omap_hsmmc.c | 223 +++++++++++++++++++++++++++------
2 files changed, 184 insertions(+), 40 deletions(-)

diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h
index f57f36a..b13e927 100644
--- a/arch/arm/plat-omap/include/plat/mmc.h
+++ b/arch/arm/plat-omap/include/plat/mmc.h
@@ -110,6 +110,7 @@ struct omap_mmc_platform_data {
/* we can put the features above into this variable */
#define HSMMC_HAS_PBIAS (1 << 0)
#define HSMMC_HAS_UPDATED_RESET (1 << 1)
+#define HSMMC_HAS_ADMA_SUPPORT (1 << 2)
unsigned features;

int switch_pin; /* gpio (card detect) */
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 7cf0383..aaa113b 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -51,12 +51,15 @@
#define OMAP_HSMMC_RSP54 0x0118
#define OMAP_HSMMC_RSP76 0x011C
#define OMAP_HSMMC_DATA 0x0120
+#define OMAP_HSMMC_PSTATE 0x0124
#define OMAP_HSMMC_HCTL 0x0128
#define OMAP_HSMMC_SYSCTL 0x012C
#define OMAP_HSMMC_STAT 0x0130
#define OMAP_HSMMC_IE 0x0134
#define OMAP_HSMMC_ISE 0x0138
#define OMAP_HSMMC_CAPA 0x0140
+#define OMAP_HSMMC_ADMA_ES 0x0154
+#define OMAP_HSMMC_ADMA_SAL 0x0158

#define VS18 (1 << 26)
#define VS30 (1 << 25)
@@ -104,6 +107,25 @@
#define SRD (1 << 26)
#define SOFTRESET (1 << 1)
#define RESETDONE (1 << 0)
+#define DMAS (0x2 << 3)
+#define CAPA_ADMA_SUPPORT (1 << 19)
+#define ADMA_XFER_VALID (1 << 0)
+#define ADMA_XFER_END (1 << 1)
+#define ADMA_XFER_LINK (1 << 4)
+#define ADMA_XFER_DESC (1 << 5)
+#define DMA_MNS_ADMA_MODE (1 << 20)
+#define ADMA_ERR (1 << 25)
+#define ADMA_XFER_INT (1 << 3)
+
+#define ADMA_TABLE_SZ (PAGE_SIZE)
+#define ADMA_TABLE_NUM_ENTRIES (ADMA_TABLE_SZ / sizeof(struct adma_desc_table))
+
+/*
+ * According to TRM, it is possible to transfer upto 64kB per ADMA table entry.
+ * But 64kB = 0x10000 cannot be represented using a 16bit integer
+ * in 1 ADMA table row. Hence rounding it to a lesser value.
+ */
+#define ADMA_MAX_XFER_PER_ROW (63 * 1024)

/*
* FIXME: Most likely all the data using these _DEVID defines should come
@@ -146,6 +168,13 @@
#define OMAP_HSMMC_WRITE(base, reg, val) \
__raw_writel((val), (base) + OMAP_HSMMC_##reg)

+/* ADMA descriptor table entry */
+struct adma_desc_table {
+ u16 attr;
+ u16 length;
+ dma_addr_t addr;
+};
+
struct omap_hsmmc_host {
struct device *dev;
struct mmc_host *mmc;
@@ -179,6 +208,8 @@ struct omap_hsmmc_host {
int irq;
int dma_ch;
int xfer_type; /* Transfer can be PIO/SDMA/ADMA */
+ struct adma_desc_table *adma_table;
+ dma_addr_t phy_adma_table;
int dma_line_tx, dma_line_rx;
int slot_id;
int got_dbclk;
@@ -877,6 +908,44 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req
}

/*
+ * SDMA clean up during SDMA transfers.
+ * Also unmapping of sg list in case of error/transfer done during
+ * SDMA/ADMA transfers.
+ */
+static void omap_hsmmc_xfer_cleanup(struct omap_hsmmc_host *host, int errno)
+{
+ int dma_ch;
+ struct mmc_data *data = errno ? host->data : host->mrq->data;
+
+ switch (host->xfer_type) {
+ case OMAP_HSMMC_USE_SDMA_XFER:
+ spin_lock(&host->irq_lock);
+ dma_ch = host->dma_ch;
+ host->dma_ch = -1;
+ spin_unlock(&host->irq_lock);
+ if (dma_ch != -1)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ host->dma_len,
+ omap_hsmmc_get_dma_dir(host, data));
+ omap_free_dma(dma_ch);
+ break;
+ case OMAP_HSMMC_USE_ADMA_XFER:
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ host->dma_len, omap_hsmmc_get_dma_dir(host, data));
+ break;
+ case OMAP_HSMMC_USE_PIO_XFER:
+ /* TODO */
+ break;
+ default:
+ dev_dbg(mmc_dev(host->mmc), "Unknown xfer_type\n");
+ }
+ if (errno) {
+ host->data->error = errno;
+ host->data = NULL;
+ }
+}
+
+/*
* Notify the transfer complete to MMC core
*/
static void
@@ -898,6 +967,9 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)

host->data = NULL;

+ if (host->xfer_type == OMAP_HSMMC_USE_ADMA_XFER)
+ omap_hsmmc_xfer_cleanup(host, 0);
+
if (!data->error)
data->bytes_xfered += data->blocks * (data->blksz);
else
@@ -935,40 +1007,6 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
}

/*
- * SDMA clean up during SDMA transfers.
- * Also unmapping of sg list in case of error/transfer done during
- * SDMA/ADMA transfers.
- */
-static void omap_hsmmc_xfer_cleanup(struct omap_hsmmc_host *host, int errno)
-{
- int dma_ch;
- struct mmc_data *data = errno ? host->data : host->mrq->data;
-
- switch (host->xfer_type) {
- case OMAP_HSMMC_USE_SDMA_XFER:
- spin_lock(&host->irq_lock);
- dma_ch = host->dma_ch;
- host->dma_ch = -1;
- spin_unlock(&host->irq_lock);
- if (dma_ch != -1)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- host->dma_len,
- omap_hsmmc_get_dma_dir(host, data));
- omap_free_dma(dma_ch);
- break;
- case OMAP_HSMMC_USE_PIO_XFER:
- /* TODO */
- break;
- default:
- dev_dbg(mmc_dev(host->mmc), "Unknown xfer_type\n");
- }
- if (errno) {
- host->data->error = errno;
- host->data = NULL;
- }
-}
-
-/*
* Readable error output
*/
#ifdef CONFIG_MMC_DEBUG
@@ -976,10 +1014,10 @@ static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
{
/* --- means reserved bit without definition at documentation */
static const char *omap_hsmmc_status_bits[] = {
- "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ",
- "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
- "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---",
- "---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---"
+ "CC", "TC", "BGE", "DMA", "BWR", "BRR", "CINS", "CREM", "CIRQ",
+ "OBI", "BSR", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
+ "CEB", "CIE", "DTO", "DCRC", "DEB", "CLE", "ACE", "ADMA",
+ "---", "---", "CERR", "BADA", "---", "---"
};
char res[256];
char *buf = res;
@@ -1100,6 +1138,24 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
if (host->data)
end_trans = 1;
}
+ if (status & ADMA_ERR) {
+ dev_dbg(mmc_dev(host->mmc),
+ "ADMA err: ADMA_ES=%x, SAL=%x; Ignored!\n",
+ OMAP_HSMMC_READ(host->base, ADMA_ES),
+ OMAP_HSMMC_READ(host->base, ADMA_SAL));
+ if (host->cmd)
+ end_cmd = 1;
+ if (host->data)
+ end_trans = 1;
+ }
+ }
+ if (status & ADMA_XFER_INT) {
+ dev_dbg(mmc_dev(host->mmc),
+ "ADMA XFERINT: blk=%x at table=%x pstate=%x\n",
+ OMAP_HSMMC_READ(host->base, BLK),
+ OMAP_HSMMC_READ(host->base, ADMA_SAL),
+ OMAP_HSMMC_READ(host->base, PSTATE));
+
}

OMAP_HSMMC_WRITE(host->base, STAT, status);
@@ -1402,6 +1458,63 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
return 0;
}

+static int mmc_populate_adma_desc_table(struct omap_hsmmc_host *host,
+ struct mmc_request *req, struct adma_desc_table *pdesc)
+{
+ int i, j, dmalen;
+ int splitseg, xferaddr;
+ int numblocks = 0;
+ dma_addr_t dmaaddr;
+ struct mmc_data *data = req->data;
+
+ host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, omap_hsmmc_get_dma_dir(host, data));
+ for (i = 0, j = 0; i < host->dma_len; i++) {
+ dmaaddr = sg_dma_address(data->sg + i);
+ dmalen = sg_dma_len(data->sg + i);
+ numblocks += dmalen / data->blksz;
+
+ if (dmalen <= ADMA_MAX_XFER_PER_ROW) {
+ pdesc[i + j].length = dmalen;
+ pdesc[i + j].addr = dmaaddr;
+ pdesc[i + j].attr = (ADMA_XFER_DESC |
+ ADMA_XFER_VALID);
+ } else {
+ /* Each descriptor row can only support
+ * transfer upto ADMA_MAX_XFER_PER_ROW.
+ * If the current segment is bigger, it has to be
+ * split to multiple ADMA table entries.
+ */
+ xferaddr = 0;
+ do {
+ splitseg = min(dmalen, ADMA_MAX_XFER_PER_ROW);
+ dmalen -= splitseg;
+ pdesc[i + j].length = splitseg;
+ pdesc[i + j].addr = dmaaddr + xferaddr;
+ xferaddr += splitseg;
+ pdesc[i + j].attr = (ADMA_XFER_DESC |
+ ADMA_XFER_VALID);
+ j++;
+ } while (dmalen);
+ j--; /* Compensate for i++ */
+ }
+ }
+ /* Setup last entry to terminate */
+ pdesc[i + j - 1].attr |= ADMA_XFER_END;
+ WARN_ON((i + j - 1) > ADMA_TABLE_NUM_ENTRIES);
+ dev_dbg(mmc_dev(host->mmc),
+ "ADMA table has %d entries from %d sglist\n",
+ i + j, host->dma_len);
+ return numblocks;
+}
+
+static void omap_hsmmc_start_adma_transfer(struct omap_hsmmc_host *host)
+{
+ /* Enforcing ordering in write operations */
+ wmb();
+ OMAP_HSMMC_WRITE(host->base, ADMA_SAL, host->phy_adma_table);
+}
+
static void set_data_timeout(struct omap_hsmmc_host *host,
unsigned int timeout_ns,
unsigned int timeout_clks)
@@ -1446,6 +1559,7 @@ static int
omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
{
int ret;
+ int numblks;
host->data = req->data;

if (req->data == NULL) {
@@ -1463,12 +1577,17 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
| (req->data->blocks << 16));
set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);

- if (host->xfer_type) {
+ if (host->xfer_type == OMAP_HSMMC_USE_SDMA_XFER) {
ret = omap_hsmmc_start_dma_transfer(host, req);
if (ret != 0) {
dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
return ret;
}
+ } else if (host->xfer_type == OMAP_HSMMC_USE_ADMA_XFER) {
+ numblks = mmc_populate_adma_desc_table(host, req,
+ host->adma_table);
+ WARN_ON(numblks != req->data->blocks);
+ omap_hsmmc_start_adma_transfer(host);
}
return 0;
}
@@ -1667,6 +1786,11 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
capa = VS18;
}

+ if (host->xfer_type == OMAP_HSMMC_USE_ADMA_XFER) {
+ hctl |= DMAS;
+ value = OMAP_HSMMC_READ(host->base, CON);
+ OMAP_HSMMC_WRITE(host->base, CON, value | DMA_MNS_ADMA_MODE);
+ }
value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK;
OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl);

@@ -2024,7 +2148,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
struct mmc_host *mmc;
struct omap_hsmmc_host *host = NULL;
struct resource *res;
- int ret, irq;
+ int ret, irq, capa;

if (pdata == NULL) {
dev_err(&pdev->dev, "Platform Data is missing\n");
@@ -2162,6 +2286,19 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if (mmc_slot(host).nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;

+ if (mmc_slot(host).features & HSMMC_HAS_ADMA_SUPPORT) {
+ capa = OMAP_HSMMC_READ(host->base, CAPA);
+ if (capa & CAPA_ADMA_SUPPORT) {
+ /* Allocating memory for ADMA Descriptor Table */
+ host->adma_table = dma_alloc_coherent(NULL,
+ ADMA_TABLE_SZ, &host->phy_adma_table, 0);
+ /* If allocation is success go with ADMA else SDMA */
+ if (host->adma_table != NULL)
+ host->xfer_type = OMAP_HSMMC_USE_ADMA_XFER;
+ }
+ }
+ dev_dbg(mmc_dev(host->mmc), "xfer_type=%d\n", host->xfer_type);
+
omap_hsmmc_conf_bus_power(host);

/* Select DMA lines */
@@ -2277,6 +2414,9 @@ err_irq:
clk_put(host->dbclk);
}
err1:
+ if (host->adma_table != NULL)
+ dma_free_coherent(NULL, ADMA_TABLE_SZ,
+ host->adma_table, host->phy_adma_table);
iounmap(host->base);
platform_set_drvdata(pdev, NULL);
mmc_free_host(mmc);
@@ -2304,6 +2444,9 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
free_irq(mmc_slot(host).card_detect_irq, host);
flush_scheduled_work();

+ if (host->adma_table != NULL)
+ dma_free_coherent(NULL, ADMA_TABLE_SZ,
+ host->adma_table, host->phy_adma_table);
mmc_host_disable(host->mmc);
clk_disable(host->iclk);
clk_put(host->fclk);
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Tony Lindgren
2011-01-20 17:42:27 UTC
Permalink
Post by Kishore Kadiyala
This patch series adds support for ADMA on MMC1 & MMC2 controllers on OMAP4.
There is no performance improvement observed using ADMA over SDMA.
Advantage using ADMA could be reducing contention over SDMA.
Also the series includes some cleanup.
To me it seems that the only sane way to deal with multiple different
DMA enginges is to implement them all with drivers/dma/dmaengine.c.
Otherwise we'll end up with multiple custom ADMA implementations in
each driver. We need to also make SDMA use the dmaengine code.

If the current dmaengine code does not support this, we need to
improve it to deal with cases like these.

Regards,

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Kadiyala, Kishore
2011-02-03 06:14:28 UTC
Permalink
Tony,
Post by Tony Lindgren
Post by Kishore Kadiyala
This patch series adds support for ADMA on MMC1 & MMC2 controllers on OMAP4.
There is no performance improvement observed using ADMA over SDMA.
Advantage using ADMA could be reducing contention over SDMA.
Also the series includes some cleanup.
To me it seems that the only sane way to deal with multiple different
DMA enginges is to implement them all with drivers/dma/dmaengine.c.
Otherwise we'll end up with multiple custom ADMA implementations in
each driver. We need to also make SDMA use the dmaengine code.
If the current dmaengine code does not support this, we need to
improve it to deal with cases like these.
Ok, I will check the feasibility of implementing ADMA through dmaengine .
--
Regards,
Kishore
Post by Tony Lindgren
Regards,
Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Loading...