* [PATCH 0/5 v2] MMC: DMA for SH MMCIF
@ 2010-11-24 10:05 Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 1/5 v2] mmc: sh_mmcif: switch to completion, fix flags Guennadi Liakhovetski
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Guennadi Liakhovetski @ 2010-11-24 10:05 UTC (permalink / raw)
To: linux-sh; +Cc: linux-mmc, Yusuke Goda
This patch series slightly cleans up the sh_mmcif MMC driver and adds a
DMA capability to it. One of the patches is not directly related, but is
required for a proper DMA functionality. V2 limits the number of request
segments, instead of raising the number of DMA descriptors in shdma.c.
This drops one patch from this series and modifies one further patch, the
rest remain unchanged.
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/5 v2] mmc: sh_mmcif: switch to completion, fix flags
2010-11-24 10:05 [PATCH 0/5 v2] MMC: DMA for SH MMCIF Guennadi Liakhovetski
@ 2010-11-24 10:05 ` Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 2/5 v2] ARM: mach-shmobile: add DMA defines for MMCIF on Guennadi Liakhovetski
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Guennadi Liakhovetski @ 2010-11-24 10:05 UTC (permalink / raw)
To: linux-sh; +Cc: linux-mmc, Yusuke Goda
In sh_mmcif.c an event is used as a completion, switch over. When a
wait_for_completion*_timeout() returns, it suffices to check the remaining
time, setting an additional flag before waking up the waiting task only reduces
the race window, but does not eliminate it. This patch switches the driver to
use a completion to signal an interrupt, the only case, when an interrupt
should not wake up the waiter, is when an automatic CMD12 completes. Also fix
MODULE_ALIAS.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
v2: no change
drivers/mmc/host/sh_mmcif.c | 117 +++++++++++++++++--------------------------
1 files changed, 46 insertions(+), 71 deletions(-)
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index ddd0984..c9eaab2 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -16,6 +16,8 @@
*
*/
+#include <linux/clk.h>
+#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@@ -24,7 +26,6 @@
#include <linux/mmc/sdio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/clk.h>
#include <linux/mmc/sh_mmcif.h>
#define DRIVER_NAME "sh_mmcif"
@@ -181,11 +182,10 @@ struct sh_mmcif_host {
struct clk *hclk;
unsigned int clk;
int bus_width;
- u16 wait_int;
- u16 sd_error;
+ bool sd_error;
long timeout;
void __iomem *addr;
- wait_queue_head_t intr_wait;
+ struct completion intr_wait;
};
@@ -239,8 +239,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
u32 state1, state2;
int ret, timeout = 10000000;
- host->sd_error = 0;
- host->wait_int = 0;
+ host->sd_error = false;
state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1);
state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2);
@@ -287,17 +286,13 @@ static int sh_mmcif_single_read(struct sh_mmcif_host *host,
long time;
u32 blocksize, i, *p = sg_virt(data->sg);
- host->wait_int = 0;
-
/* buf read enable */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 ||
- host->sd_error = 1, host->timeout);
- if (host->wait_int != 1 && (time = 0 || host->sd_error != 0))
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
+ if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
- host->wait_int = 0;
blocksize = (BLOCK_SIZE_MASK &
sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3;
for (i = 0; i < blocksize / 4; i++)
@@ -305,13 +300,11 @@ static int sh_mmcif_single_read(struct sh_mmcif_host *host,
/* buffer read end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 ||
- host->sd_error = 1, host->timeout);
- if (host->wait_int != 1 && (time = 0 || host->sd_error != 0))
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
+ if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
- host->wait_int = 0;
return 0;
}
@@ -326,19 +319,15 @@ static int sh_mmcif_multi_read(struct sh_mmcif_host *host,
MMCIF_CE_BLOCK_SET);
for (j = 0; j < data->sg_len; j++) {
p = sg_virt(data->sg);
- host->wait_int = 0;
for (sec = 0; sec < data->sg->length / blocksize; sec++) {
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
/* buf read enable */
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 ||
- host->sd_error = 1, host->timeout);
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
- if (host->wait_int != 1 &&
- (time = 0 || host->sd_error != 0))
+ if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
- host->wait_int = 0;
for (i = 0; i < blocksize / 4; i++)
*p++ = sh_mmcif_readl(host->addr,
MMCIF_CE_DATA);
@@ -356,17 +345,14 @@ static int sh_mmcif_single_write(struct sh_mmcif_host *host,
long time;
u32 blocksize, i, *p = sg_virt(data->sg);
- host->wait_int = 0;
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
/* buf write enable */
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 ||
- host->sd_error = 1, host->timeout);
- if (host->wait_int != 1 && (time = 0 || host->sd_error != 0))
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
+ if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
- host->wait_int = 0;
blocksize = (BLOCK_SIZE_MASK &
sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3;
for (i = 0; i < blocksize / 4; i++)
@@ -375,13 +361,11 @@ static int sh_mmcif_single_write(struct sh_mmcif_host *host,
/* buffer write end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 ||
- host->sd_error = 1, host->timeout);
- if (host->wait_int != 1 && (time = 0 || host->sd_error != 0))
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
+ if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
- host->wait_int = 0;
return 0;
}
@@ -397,19 +381,15 @@ static int sh_mmcif_multi_write(struct sh_mmcif_host *host,
for (j = 0; j < data->sg_len; j++) {
p = sg_virt(data->sg);
- host->wait_int = 0;
for (sec = 0; sec < data->sg->length / blocksize; sec++) {
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
/* buf write enable*/
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 ||
- host->sd_error = 1, host->timeout);
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
- if (host->wait_int != 1 &&
- (time = 0 || host->sd_error != 0))
+ if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
- host->wait_int = 0;
for (i = 0; i < blocksize / 4; i++)
sh_mmcif_writel(host->addr,
MMCIF_CE_DATA, *p++);
@@ -579,13 +559,12 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask);
/* set arg */
sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg);
- host->wait_int = 0;
/* set cmd */
sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc);
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 || host->sd_error = 1, host->timeout);
- if (host->wait_int != 1 && time = 0) {
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
+ if (time <= 0) {
cmd->error = sh_mmcif_error_manage(host);
return;
}
@@ -602,19 +581,14 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
cmd->error = sh_mmcif_error_manage(host);
break;
}
- host->sd_error = 0;
- host->wait_int = 0;
+ host->sd_error = false;
return;
}
if (!(cmd->flags & MMC_RSP_PRESENT)) {
cmd->error = ret;
- host->wait_int = 0;
return;
}
- if (host->wait_int = 1) {
- sh_mmcif_get_response(host, cmd);
- host->wait_int = 0;
- }
+ sh_mmcif_get_response(host, cmd);
if (host->data) {
ret = sh_mmcif_data_trans(host, mrq, cmd->opcode);
if (ret < 0)
@@ -641,15 +615,13 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
return;
}
- time = wait_event_interruptible_timeout(host->intr_wait,
- host->wait_int = 1 ||
- host->sd_error = 1, host->timeout);
- if (host->wait_int != 1 && (time = 0 || host->sd_error != 0)) {
+ time = wait_for_completion_interruptible_timeout(&host->intr_wait,
+ host->timeout);
+ if (time <= 0 || host->sd_error) {
cmd->error = sh_mmcif_error_manage(host);
return;
}
sh_mmcif_get_cmd12response(host, cmd);
- host->wait_int = 0;
cmd->error = 0;
}
@@ -735,7 +707,7 @@ static void sh_mmcif_detect(struct mmc_host *mmc)
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
- u32 state = 0;
+ u32 state;
int err = 0;
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
@@ -780,11 +752,13 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
err = 1;
}
if (err) {
- host->sd_error = 1;
+ host->sd_error = true;
pr_debug("%s: int err state = %08x\n", DRIVER_NAME, state);
}
- host->wait_int = 1;
- wake_up(&host->intr_wait);
+ if (state & ~(INT_CMD12RBE | INT_CMD12CRE))
+ complete(&host->intr_wait);
+ else
+ dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state);
return IRQ_HANDLED;
}
@@ -842,7 +816,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
host->clk = clk_get_rate(host->hclk);
host->pd = pdev;
- init_waitqueue_head(&host->intr_wait);
+ init_completion(&host->intr_wait);
mmc->ops = &sh_mmcif_ops;
mmc->f_max = host->clk;
@@ -903,20 +877,21 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
int irq[2];
- sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
-
- irq[0] = platform_get_irq(pdev, 0);
- irq[1] = platform_get_irq(pdev, 1);
+ mmc_remove_host(host->mmc);
if (host->addr)
iounmap(host->addr);
- platform_set_drvdata(pdev, NULL);
- mmc_remove_host(host->mmc);
+ sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
+
+ irq[0] = platform_get_irq(pdev, 0);
+ irq[1] = platform_get_irq(pdev, 1);
free_irq(irq[0], host);
free_irq(irq[1], host);
+ platform_set_drvdata(pdev, NULL);
+
clk_disable(host->hclk);
mmc_free_host(host->mmc);
@@ -947,5 +922,5 @@ module_exit(sh_mmcif_exit);
MODULE_DESCRIPTION("SuperH on-chip MMC/eMMC interface driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS(DRIVER_NAME);
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Yusuke Goda <yusuke.goda.sx@renesas.com>");
--
1.7.2.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/5 v2] ARM: mach-shmobile: add DMA defines for MMCIF on
2010-11-24 10:05 [PATCH 0/5 v2] MMC: DMA for SH MMCIF Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 1/5 v2] mmc: sh_mmcif: switch to completion, fix flags Guennadi Liakhovetski
@ 2010-11-24 10:05 ` Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 3/5 v2] mmc: sh_mmcif: cosmetic clean up Guennadi Liakhovetski
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Guennadi Liakhovetski @ 2010-11-24 10:05 UTC (permalink / raw)
To: linux-sh; +Cc: linux-mmc, Yusuke Goda
Add DMA slave IDs and slave definitions for MMCIF on sh7372.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
v2: no change
arch/arm/mach-shmobile/include/mach/sh7372.h | 2 ++
arch/arm/mach-shmobile/setup-sh7372.c | 10 ++++++++++
2 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-shmobile/include/mach/sh7372.h b/arch/arm/mach-shmobile/include/mach/sh7372.h
index e4f9004..5736efc 100644
--- a/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ b/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -455,6 +455,8 @@ enum {
SHDMA_SLAVE_SDHI1_TX,
SHDMA_SLAVE_SDHI2_RX,
SHDMA_SLAVE_SDHI2_TX,
+ SHDMA_SLAVE_MMCIF_RX,
+ SHDMA_SLAVE_MMCIF_TX,
};
extern struct clk sh7372_extal1_clk;
diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c
index 564a6d0..2e3e11e 100644
--- a/arch/arm/mach-shmobile/setup-sh7372.c
+++ b/arch/arm/mach-shmobile/setup-sh7372.c
@@ -416,6 +416,16 @@ static const struct sh_dmae_slave_config sh7372_dmae_slaves[] = {
.addr = 0xe6870030,
.chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
.mid_rid = 0xce,
+ }, {
+ .slave_id = SHDMA_SLAVE_MMCIF_TX,
+ .addr = 0xe6bd0034,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+ .mid_rid = 0xd1,
+ }, {
+ .slave_id = SHDMA_SLAVE_MMCIF_RX,
+ .addr = 0xe6bd0034,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+ .mid_rid = 0xd2,
},
};
--
1.7.2.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/5 v2] mmc: sh_mmcif: cosmetic clean up
2010-11-24 10:05 [PATCH 0/5 v2] MMC: DMA for SH MMCIF Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 1/5 v2] mmc: sh_mmcif: switch to completion, fix flags Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 2/5 v2] ARM: mach-shmobile: add DMA defines for MMCIF on Guennadi Liakhovetski
@ 2010-11-24 10:05 ` Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 4/5 v2] mmc: sh_mmcif: add DMA support Guennadi Liakhovetski
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Guennadi Liakhovetski @ 2010-11-24 10:05 UTC (permalink / raw)
To: linux-sh; +Cc: linux-mmc, Yusuke Goda
Replace pr_* with respective dev_*, sort headers alphabetically, remove an
unused struct member, superfluous variable initialisations and type-casts.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
v2: no change
drivers/mmc/host/sh_mmcif.c | 63 ++++++++++++++++++++----------------------
1 files changed, 30 insertions(+), 33 deletions(-)
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index c9eaab2..51a0160 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -18,15 +18,15 @@
#include <linux/clk.h>
#include <linux/completion.h>
+#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
#include <linux/mmc/sh_mmcif.h>
+#include <linux/platform_device.h>
#define DRIVER_NAME "sh_mmcif"
#define DRIVER_VERSION "2010-04-28"
@@ -177,7 +177,6 @@
struct sh_mmcif_host {
struct mmc_host *mmc;
struct mmc_data *data;
- struct mmc_command *cmd;
struct platform_device *pd;
struct clk *hclk;
unsigned int clk;
@@ -243,8 +242,8 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1);
state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2);
- pr_debug("%s: ERR HOST_STS1 = %08x\n", DRIVER_NAME, state1);
- pr_debug("%s: ERR HOST_STS2 = %08x\n", DRIVER_NAME, state2);
+ dev_dbg(&host->pd->dev, "ERR HOST_STS1 = %08x\n", state1);
+ dev_dbg(&host->pd->dev, "ERR HOST_STS2 = %08x\n", state2);
if (state1 & STS1_CMDSEQ) {
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
@@ -252,8 +251,8 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
while (1) {
timeout--;
if (timeout < 0) {
- pr_err(DRIVER_NAME": Forceed end of " \
- "command sequence timeout err\n");
+ dev_err(&host->pd->dev,
+ "Forceed end of command sequence timeout err\n");
return -EIO;
}
if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1)
@@ -262,18 +261,18 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
mdelay(1);
}
sh_mmcif_sync_reset(host);
- pr_debug(DRIVER_NAME": Forced end of command sequence\n");
+ dev_dbg(&host->pd->dev, "Forced end of command sequence\n");
return -EIO;
}
if (state2 & STS2_CRC_ERR) {
- pr_debug(DRIVER_NAME": Happened CRC error\n");
+ dev_dbg(&host->pd->dev, ": Happened CRC error\n");
ret = -EIO;
} else if (state2 & STS2_TIMEOUT_ERR) {
- pr_debug(DRIVER_NAME": Happened Timeout error\n");
+ dev_dbg(&host->pd->dev, ": Happened Timeout error\n");
ret = -ETIMEDOUT;
} else {
- pr_debug(DRIVER_NAME": Happened End/Index error\n");
+ dev_dbg(&host->pd->dev, ": Happened End/Index error\n");
ret = -EIO;
}
return ret;
@@ -437,7 +436,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
tmp |= CMD_SET_RTYP_17B;
break;
default:
- pr_err(DRIVER_NAME": Not support type response.\n");
+ dev_err(&host->pd->dev, "Unsupported response type.\n");
break;
}
switch (opc) {
@@ -465,7 +464,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
tmp |= CMD_SET_DATW_8;
break;
default:
- pr_err(DRIVER_NAME": Not support bus width.\n");
+ dev_err(&host->pd->dev, "Unsupported bus width.\n");
break;
}
}
@@ -493,10 +492,10 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
return opc = ((opc << 24) | tmp);
}
-static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host,
+static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
struct mmc_request *mrq, u32 opc)
{
- u32 ret;
+ int ret;
switch (opc) {
case MMC_READ_MULTIPLE_BLOCK:
@@ -513,7 +512,7 @@ static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host,
ret = sh_mmcif_single_read(host, mrq);
break;
default:
- pr_err(DRIVER_NAME": NOT SUPPORT CMD = d'%08d\n", opc);
+ dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc);
ret = -EINVAL;
break;
}
@@ -527,8 +526,6 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
int ret = 0, mask = 0;
u32 opc = cmd->opcode;
- host->cmd = cmd;
-
switch (opc) {
/* respons busy check */
case MMC_SWITCH:
@@ -576,8 +573,8 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
cmd->error = -ETIMEDOUT;
break;
default:
- pr_debug("%s: Cmd(d'%d) err\n",
- DRIVER_NAME, cmd->opcode);
+ dev_dbg(&host->pd->dev, "Cmd(d'%d) err\n",
+ cmd->opcode);
cmd->error = sh_mmcif_error_manage(host);
break;
}
@@ -585,7 +582,7 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
return;
}
if (!(cmd->flags & MMC_RSP_PRESENT)) {
- cmd->error = ret;
+ cmd->error = 0;
return;
}
sh_mmcif_get_response(host, cmd);
@@ -610,7 +607,7 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
else if (mrq->cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK)
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
else {
- pr_err(DRIVER_NAME": not support stop cmd\n");
+ dev_err(&host->pd->dev, "unsupported stop cmd\n");
cmd->error = sh_mmcif_error_manage(host);
return;
}
@@ -746,14 +743,14 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
err = 1;
} else {
- pr_debug("%s: Not support int\n", DRIVER_NAME);
+ dev_dbg(&host->pd->dev, "Not support int\n");
sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
err = 1;
}
if (err) {
host->sd_error = true;
- pr_debug("%s: int err state = %08x\n", DRIVER_NAME, state);
+ dev_dbg(&host->pd->dev, "int err state = %08x\n", state);
}
if (state & ~(INT_CMD12RBE | INT_CMD12CRE))
complete(&host->intr_wait);
@@ -767,8 +764,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
{
int ret = 0, irq[2];
struct mmc_host *mmc;
- struct sh_mmcif_host *host = NULL;
- struct sh_mmcif_plat_data *pd = NULL;
+ struct sh_mmcif_host *host;
+ struct sh_mmcif_plat_data *pd;
struct resource *res;
void __iomem *reg;
char clk_name[8];
@@ -776,7 +773,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
irq[0] = platform_get_irq(pdev, 0);
irq[1] = platform_get_irq(pdev, 1);
if (irq[0] < 0 || irq[1] < 0) {
- pr_err(DRIVER_NAME": Get irq error\n");
+ dev_err(&pdev->dev, "Get irq error\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -789,7 +786,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "ioremap error.\n");
return -ENOMEM;
}
- pd = (struct sh_mmcif_plat_data *)(pdev->dev.platform_data);
+ pd = pdev->dev.platform_data;
if (!pd) {
dev_err(&pdev->dev, "sh_mmcif plat data error.\n");
ret = -ENXIO;
@@ -844,21 +841,21 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host);
if (ret) {
- pr_err(DRIVER_NAME": request_irq error (sh_mmc:error)\n");
+ dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n");
goto clean_up2;
}
ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host);
if (ret) {
free_irq(irq[0], host);
- pr_err(DRIVER_NAME": request_irq error (sh_mmc:int)\n");
+ dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
goto clean_up2;
}
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
sh_mmcif_detect(host->mmc);
- pr_info("%s: driver version %s\n", DRIVER_NAME, DRIVER_VERSION);
- pr_debug("%s: chip ver H'%04x\n", DRIVER_NAME,
+ dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
+ dev_dbg(&pdev->dev, "chip ver H'%04x\n",
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
return ret;
--
1.7.2.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/5 v2] mmc: sh_mmcif: add DMA support
2010-11-24 10:05 [PATCH 0/5 v2] MMC: DMA for SH MMCIF Guennadi Liakhovetski
` (2 preceding siblings ...)
2010-11-24 10:05 ` [PATCH 3/5 v2] mmc: sh_mmcif: cosmetic clean up Guennadi Liakhovetski
@ 2010-11-24 10:05 ` Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 5/5 v2] ARM: mach-shmobile: enable MMCIF DMA on ap4evb Guennadi Liakhovetski
2010-11-25 7:29 ` [PATCH 0/5 v2] MMC: DMA for SH MMCIF Paul Mundt
5 siblings, 0 replies; 7+ messages in thread
From: Guennadi Liakhovetski @ 2010-11-24 10:05 UTC (permalink / raw)
To: linux-sh; +Cc: linux-mmc, Yusuke Goda
The MMCIF controller on sh-mobile platforms can use the DMA controller for data
transfers. Interface to the SH dmaengine driver to enable DMA. We also have to
lower the maximum number of segments to match with the number od DMA
descriptors on SuperH, this doesn't significantly affect driver's PIO
performance.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
v2: reduce the maximum number of segments per request from 128 to 32 to go
with the dma engine driver, calculate .max_req_size and .max_blk_count
based on PAGE_CACHE_SIZE. Performance changes seem to be negligible and
would require an intensive benchmarking to be evaluated more precisely.
drivers/mmc/host/Kconfig | 6 +
drivers/mmc/host/sh_mmcif.c | 246 +++++++++++++++++++++++++++++++++++++++++-
include/linux/mmc/sh_mmcif.h | 15 ++-
3 files changed, 258 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index d618e86..859e352 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -466,6 +466,12 @@ config MMC_SH_MMCIF
This driver supports MMCIF in sh7724/sh7757/sh7372.
+config SH_MMCIF_DMA
+ bool "Use DMA for MMCIF"
+ depends on MMC_SH_MMCIF
+ help
+ Use SH dma-engine driver for data transfer
+
config MMC_JZ4740
tristate "JZ4740 SD/Multimedia Card Interface support"
depends on MACH_JZ4740
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 51a0160..7dc035b 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -20,12 +20,14 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sh_mmcif.h>
+#include <linux/pagemap.h>
#include <linux/platform_device.h>
#define DRIVER_NAME "sh_mmcif"
@@ -185,8 +187,13 @@ struct sh_mmcif_host {
long timeout;
void __iomem *addr;
struct completion intr_wait;
-};
+ /* DMA support */
+ struct dma_chan *chan_rx;
+ struct dma_chan *chan_tx;
+ struct completion dma_complete;
+ unsigned int dma_sglen;
+};
static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
unsigned int reg, u32 val)
@@ -200,6 +207,208 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
writel(~val & readl(host->addr + reg), host->addr + reg);
}
+#ifdef CONFIG_SH_MMCIF_DMA
+static void mmcif_dma_complete(void *arg)
+{
+ struct sh_mmcif_host *host = arg;
+ dev_dbg(&host->pd->dev, "Command completed\n");
+
+ if (WARN(!host->data, "%s: NULL data in DMA completion!\n",
+ dev_name(&host->pd->dev)))
+ return;
+
+ if (host->data->flags & MMC_DATA_READ)
+ dma_unmap_sg(&host->pd->dev, host->data->sg, host->dma_sglen,
+ DMA_FROM_DEVICE);
+ else
+ dma_unmap_sg(&host->pd->dev, host->data->sg, host->dma_sglen,
+ DMA_TO_DEVICE);
+
+ complete(&host->dma_complete);
+}
+
+static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
+{
+ struct scatterlist *sg = host->data->sg;
+ struct dma_async_tx_descriptor *desc = NULL;
+ struct dma_chan *chan = host->chan_rx;
+ dma_cookie_t cookie = -EINVAL;
+ int ret;
+
+ ret = dma_map_sg(&host->pd->dev, sg, host->data->sg_len, DMA_FROM_DEVICE);
+ if (ret > 0) {
+ host->dma_sglen = ret;
+ desc = chan->device->device_prep_slave_sg(chan, sg, ret,
+ DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ }
+
+ if (desc) {
+ desc->callback = mmcif_dma_complete;
+ desc->callback_param = host;
+ cookie = desc->tx_submit(desc);
+ if (cookie < 0) {
+ desc = NULL;
+ ret = cookie;
+ } else {
+ sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN);
+ chan->device->device_issue_pending(chan);
+ }
+ }
+ dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
+ __func__, host->data->sg_len, ret, cookie);
+
+ if (!desc) {
+ /* DMA failed, fall back to PIO */
+ if (ret >= 0)
+ ret = -EIO;
+ host->chan_rx = NULL;
+ host->dma_sglen = 0;
+ dma_release_channel(chan);
+ /* Free the Tx channel too */
+ chan = host->chan_tx;
+ if (chan) {
+ host->chan_tx = NULL;
+ dma_release_channel(chan);
+ }
+ dev_warn(&host->pd->dev,
+ "DMA failed: %d, falling back to PIO\n", ret);
+ sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
+ }
+
+ dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
+ desc, cookie, host->data->sg_len);
+}
+
+static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
+{
+ struct scatterlist *sg = host->data->sg;
+ struct dma_async_tx_descriptor *desc = NULL;
+ struct dma_chan *chan = host->chan_tx;
+ dma_cookie_t cookie = -EINVAL;
+ int ret;
+
+ ret = dma_map_sg(&host->pd->dev, sg, host->data->sg_len, DMA_TO_DEVICE);
+ if (ret > 0) {
+ host->dma_sglen = ret;
+ desc = chan->device->device_prep_slave_sg(chan, sg, ret,
+ DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ }
+
+ if (desc) {
+ desc->callback = mmcif_dma_complete;
+ desc->callback_param = host;
+ cookie = desc->tx_submit(desc);
+ if (cookie < 0) {
+ desc = NULL;
+ ret = cookie;
+ } else {
+ sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN);
+ chan->device->device_issue_pending(chan);
+ }
+ }
+ dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
+ __func__, host->data->sg_len, ret, cookie);
+
+ if (!desc) {
+ /* DMA failed, fall back to PIO */
+ if (ret >= 0)
+ ret = -EIO;
+ host->chan_tx = NULL;
+ host->dma_sglen = 0;
+ dma_release_channel(chan);
+ /* Free the Rx channel too */
+ chan = host->chan_rx;
+ if (chan) {
+ host->chan_rx = NULL;
+ dma_release_channel(chan);
+ }
+ dev_warn(&host->pd->dev,
+ "DMA failed: %d, falling back to PIO\n", ret);
+ sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
+ }
+
+ dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d\n", __func__,
+ desc, cookie);
+}
+
+static bool sh_mmcif_filter(struct dma_chan *chan, void *arg)
+{
+ dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg);
+ chan->private = arg;
+ return true;
+}
+
+static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
+ struct sh_mmcif_plat_data *pdata)
+{
+ host->dma_sglen = 0;
+
+ /* We can only either use DMA for both Tx and Rx or not use it at all */
+ if (pdata->dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ host->chan_tx = dma_request_channel(mask, sh_mmcif_filter,
+ &pdata->dma->chan_priv_tx);
+ dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
+ host->chan_tx);
+
+ if (!host->chan_tx)
+ return;
+
+ host->chan_rx = dma_request_channel(mask, sh_mmcif_filter,
+ &pdata->dma->chan_priv_rx);
+ dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
+ host->chan_rx);
+
+ if (!host->chan_rx) {
+ dma_release_channel(host->chan_tx);
+ host->chan_tx = NULL;
+ return;
+ }
+
+ init_completion(&host->dma_complete);
+ }
+}
+
+static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
+{
+ sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
+ /* Descriptors are freed automatically */
+ if (host->chan_tx) {
+ struct dma_chan *chan = host->chan_tx;
+ host->chan_tx = NULL;
+ dma_release_channel(chan);
+ }
+ if (host->chan_rx) {
+ struct dma_chan *chan = host->chan_rx;
+ host->chan_rx = NULL;
+ dma_release_channel(chan);
+ }
+
+ host->dma_sglen = 0;
+}
+#else
+static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
+{
+}
+
+static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
+{
+}
+
+static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
+ struct sh_mmcif_plat_data *pdata)
+{
+ /* host->chan_tx, host->chan_tx and host->dma_sglen are all zero */
+}
+
+static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
+{
+}
+#endif
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
{
@@ -587,7 +796,20 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
}
sh_mmcif_get_response(host, cmd);
if (host->data) {
- ret = sh_mmcif_data_trans(host, mrq, cmd->opcode);
+ if (!host->dma_sglen) {
+ ret = sh_mmcif_data_trans(host, mrq, cmd->opcode);
+ } else {
+ long time + wait_for_completion_interruptible_timeout(&host->dma_complete,
+ host->timeout);
+ if (!time)
+ ret = -ETIMEDOUT;
+ else if (time < 0)
+ ret = time;
+ sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC,
+ BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
+ host->dma_sglen = 0;
+ }
if (ret < 0)
mrq->data->bytes_xfered = 0;
else
@@ -645,6 +867,15 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
break;
}
host->data = mrq->data;
+ if (mrq->data) {
+ if (mrq->data->flags & MMC_DATA_READ) {
+ if (host->chan_rx)
+ sh_mmcif_start_dma_rx(host);
+ } else {
+ if (host->chan_tx)
+ sh_mmcif_start_dma_tx(host);
+ }
+ }
sh_mmcif_start_cmd(host, mrq, mrq->cmd);
host->data = NULL;
@@ -829,14 +1060,18 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
mmc->caps = MMC_CAP_MMC_HIGHSPEED;
if (pd->caps)
mmc->caps |= pd->caps;
- mmc->max_segs = 128;
+ mmc->max_segs = 32;
mmc->max_blk_size = 512;
- mmc->max_blk_count = 65535;
- mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ mmc->max_req_size = PAGE_CACHE_SIZE * mmc->max_segs;
+ mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size;
mmc->max_seg_size = mmc->max_req_size;
sh_mmcif_sync_reset(host);
platform_set_drvdata(pdev, host);
+
+ /* See if we also get DMA */
+ sh_mmcif_request_dma(host, pd);
+
mmc_add_host(mmc);
ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host);
@@ -875,6 +1110,7 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
int irq[2];
mmc_remove_host(host->mmc);
+ sh_mmcif_release_dma(host);
if (host->addr)
iounmap(host->addr);
diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h
index 5c99da1..d999171 100644
--- a/include/linux/mmc/sh_mmcif.h
+++ b/include/linux/mmc/sh_mmcif.h
@@ -14,8 +14,9 @@
#ifndef __SH_MMCIF_H__
#define __SH_MMCIF_H__
-#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sh_dma.h>
/*
* MMCIF : CE_CLK_CTRL [19:16]
@@ -31,13 +32,19 @@
* 1111 : Peripheral clock (sup_pclk set '1')
*/
+struct sh_mmcif_dma {
+ struct sh_dmae_slave chan_priv_tx;
+ struct sh_dmae_slave chan_priv_rx;
+};
+
struct sh_mmcif_plat_data {
void (*set_pwr)(struct platform_device *pdev, int state);
void (*down_pwr)(struct platform_device *pdev);
int (*get_cd)(struct platform_device *pdef);
- u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
- unsigned long caps;
- u32 ocr;
+ struct sh_mmcif_dma *dma;
+ u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
+ unsigned long caps;
+ u32 ocr;
};
#define MMCIF_CE_CMD_SET 0x00000000
--
1.7.2.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 5/5 v2] ARM: mach-shmobile: enable MMCIF DMA on ap4evb
2010-11-24 10:05 [PATCH 0/5 v2] MMC: DMA for SH MMCIF Guennadi Liakhovetski
` (3 preceding siblings ...)
2010-11-24 10:05 ` [PATCH 4/5 v2] mmc: sh_mmcif: add DMA support Guennadi Liakhovetski
@ 2010-11-24 10:05 ` Guennadi Liakhovetski
2010-11-25 7:29 ` [PATCH 0/5 v2] MMC: DMA for SH MMCIF Paul Mundt
5 siblings, 0 replies; 7+ messages in thread
From: Guennadi Liakhovetski @ 2010-11-24 10:05 UTC (permalink / raw)
To: linux-sh; +Cc: linux-mmc, Yusuke Goda
Have to add DMA slave configuration to enable DMA for the sh7372 MMCIF
controller.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
v2: no change
arch/arm/mach-shmobile/board-ap4evb.c | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 1443d2e..67a2f34 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -271,6 +271,15 @@ static struct resource sh_mmcif_resources[] = {
},
};
+static struct sh_mmcif_dma sh_mmcif_dma = {
+ .chan_priv_rx = {
+ .slave_id = SHDMA_SLAVE_MMCIF_RX,
+ },
+ .chan_priv_tx = {
+ .slave_id = SHDMA_SLAVE_MMCIF_TX,
+ },
+};
+
static struct sh_mmcif_plat_data sh_mmcif_plat = {
.sup_pclk = 0,
.ocr = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
@@ -278,6 +287,7 @@ static struct sh_mmcif_plat_data sh_mmcif_plat = {
MMC_CAP_8_BIT_DATA |
MMC_CAP_NEEDS_POLL,
.get_cd = slot_cn7_get_cd,
+ .dma = &sh_mmcif_dma,
};
static struct platform_device sh_mmcif_device = {
--
1.7.2.3
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 0/5 v2] MMC: DMA for SH MMCIF
2010-11-24 10:05 [PATCH 0/5 v2] MMC: DMA for SH MMCIF Guennadi Liakhovetski
` (4 preceding siblings ...)
2010-11-24 10:05 ` [PATCH 5/5 v2] ARM: mach-shmobile: enable MMCIF DMA on ap4evb Guennadi Liakhovetski
@ 2010-11-25 7:29 ` Paul Mundt
5 siblings, 0 replies; 7+ messages in thread
From: Paul Mundt @ 2010-11-25 7:29 UTC (permalink / raw)
To: Guennadi Liakhovetski; +Cc: linux-sh, linux-mmc, Yusuke Goda
On Wed, Nov 24, 2010 at 11:05:08AM +0100, Guennadi Liakhovetski wrote:
> This patch series slightly cleans up the sh_mmcif MMC driver and adds a
> DMA capability to it. One of the patches is not directly related, but is
> required for a proper DMA functionality. V2 limits the number of request
> segments, instead of raising the number of DMA descriptors in shdma.c.
> This drops one patch from this series and modifies one further patch, the
> rest remain unchanged.
>
Looks better! All applied, thanks.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2010-11-25 7:29 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-24 10:05 [PATCH 0/5 v2] MMC: DMA for SH MMCIF Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 1/5 v2] mmc: sh_mmcif: switch to completion, fix flags Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 2/5 v2] ARM: mach-shmobile: add DMA defines for MMCIF on Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 3/5 v2] mmc: sh_mmcif: cosmetic clean up Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 4/5 v2] mmc: sh_mmcif: add DMA support Guennadi Liakhovetski
2010-11-24 10:05 ` [PATCH 5/5 v2] ARM: mach-shmobile: enable MMCIF DMA on ap4evb Guennadi Liakhovetski
2010-11-25 7:29 ` [PATCH 0/5 v2] MMC: DMA for SH MMCIF Paul Mundt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).