* [PATCH V8] mmc : Add i.MX21 support to mxcmmc driver
@ 2010-09-09 22:02 ` Martin Fuzzey
0 siblings, 0 replies; 6+ messages in thread
From: Martin Fuzzey @ 2010-09-09 22:02 UTC (permalink / raw)
To: Sascha Hauer, linux-mmc, Pierre Ossman; +Cc: linux-arm-kernel, cjb
Even though the i.MX21 SDHC module has the same revision number as the i.MX27
one there are a few differences!!
Some interrupt enables are inverted.
FIFO is only 16 bits wide.
The argument is written to 2x16bit registers (vs 1x32).
Interrupts must be acknowledged via the INT_CNTR register.
Card clock must be enabled for each request.
For writes DMA must be enabled on command response not before.
Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
---
Changes from V7 (February 2010):
Rebased to 2.6.36-rc3
arch/arm/mach-imx/clock-imx21.c | 4 +
drivers/mmc/host/mxcmmc.c | 153 ++++++++++++++++++++++++++++++---------
2 files changed, 120 insertions(+), 37 deletions(-)
diff --git a/arch/arm/mach-imx/clock-imx21.c b/arch/arm/mach-imx/clock-imx21.c
index dafc271..076cd43 100644
--- a/arch/arm/mach-imx/clock-imx21.c
+++ b/arch/arm/mach-imx/clock-imx21.c
@@ -1170,8 +1170,8 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "gpt1", gpt_clk[1])
_REGISTER_CLOCK(NULL, "gpt1", gpt_clk[2])
_REGISTER_CLOCK(NULL, "pwm", pwm_clk[0])
- _REGISTER_CLOCK(NULL, "sdhc1", sdhc_clk[0])
- _REGISTER_CLOCK(NULL, "sdhc2", sdhc_clk[1])
+ _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc_clk[0])
+ _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc_clk[1])
_REGISTER_CLOCK(NULL, "cspi1", cspi_clk[0])
_REGISTER_CLOCK(NULL, "cspi2", cspi_clk[1])
_REGISTER_CLOCK(NULL, "cspi3", cspi_clk[2])
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 350f78e..88dd31f 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -56,6 +56,8 @@
#define MMC_REG_INT_CNTR 0x24
#define MMC_REG_CMD 0x28
#define MMC_REG_ARG 0x2C
+#define MMC_REG_ARGH 0x2C
+#define MMC_REG_ARGL 0x30
#define MMC_REG_RES_FIFO 0x34
#define MMC_REG_BUFFER_ACCESS 0x38
@@ -145,6 +147,49 @@ struct mxcmci_host {
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
+static void mxcmci_write_arg(struct mxcmci_host *host, u32 arg)
+{
+ if (cpu_is_mx21()) {
+ writel(arg >> 16, host->base + MMC_REG_ARGH);
+ writel(arg & 0xFFFF, host->base + MMC_REG_ARGL);
+ } else {
+ writel(arg, host->base + MMC_REG_ARG);
+ }
+}
+
+static void mxcmci_ack_int(struct mxcmci_host *host, u32 stat)
+{
+ if (cpu_is_mx21()) {
+ u32 intclr = readl(host->base + MMC_REG_INT_CNTR);
+
+ if (stat & STATUS_DATA_TRANS_DONE)
+ intclr |= INT_READ_OP_EN;
+ if (stat & STATUS_WRITE_OP_DONE)
+ intclr |= INT_WRITE_OP_DONE_EN;
+ if (stat & STATUS_END_CMD_RESP)
+ intclr |= INT_END_CMD_RES_EN;
+
+ writel(intclr, host->base + MMC_REG_INT_CNTR);
+ }
+}
+
+static inline void mxcmci_set_int_cntr(struct mxcmci_host *host, u32 enables)
+{
+ if (cpu_is_mx21()) /* some interrupt enables have reverse polarity */
+ enables ^= 0x1F;
+ writel(enables, host->base + MMC_REG_INT_CNTR);
+}
+
+static inline void mxcmci_start_clock(struct mxcmci_host *host)
+{
+ writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
+}
+
+static inline void mxcmci_stop_clock(struct mxcmci_host *host)
+{
+ writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
+}
+
static inline int mxcmci_use_dma(struct mxcmci_host *host)
{
return host->do_dma;
@@ -221,7 +266,10 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
}
wmb();
- imx_dma_enable(host->dma);
+ /* MX21: unreliable writes if dma enabled here - do on command done */
+ if (mxcmci_use_dma(host) &&
+ (!cpu_is_mx21() || host->dma_dir == DMA_FROM_DEVICE))
+ imx_dma_enable(host->dma);
#endif /* HAS_DMA */
return 0;
}
@@ -263,12 +311,16 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
spin_lock_irqsave(&host->lock, flags);
if (host->use_sdio)
int_cntr |= INT_SDIO_IRQ_EN;
- writel(int_cntr, host->base + MMC_REG_INT_CNTR);
+ mxcmci_set_int_cntr(host, int_cntr);
spin_unlock_irqrestore(&host->lock, flags);
writew(cmd->opcode, host->base + MMC_REG_CMD);
- writel(cmd->arg, host->base + MMC_REG_ARG);
+ mxcmci_write_arg(host, cmd->arg);
writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
+ if (cpu_is_mx21()) {
+ /* i.MX21 requires clock start after submitting command */
+ mxcmci_start_clock(host);
+ }
return 0;
}
@@ -282,7 +334,7 @@ static void mxcmci_finish_request(struct mxcmci_host *host,
spin_lock_irqsave(&host->lock, flags);
if (host->use_sdio)
int_cntr |= INT_SDIO_IRQ_EN;
- writel(int_cntr, host->base + MMC_REG_INT_CNTR);
+ mxcmci_set_int_cntr(host, int_cntr);
spin_unlock_irqrestore(&host->lock, flags);
host->req = NULL;
@@ -397,19 +449,29 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
{
unsigned int stat;
- u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = mxcmci_poll_status(host,
+ u16 *buf16 = _buf;
+ u32 *buf32 = _buf;
+ int count = 0;
+ int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
+ int buffer_width = cpu_is_mx21() ? 2 : 4;
+
+ while (bytes >= buffer_width) {
+ if (count % fifo_size == 0) {
+ stat = mxcmci_poll_status(host,
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
- if (stat)
- return stat;
- *buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
- bytes -= 4;
+ if (stat)
+ return stat;
+ }
+ if (buffer_width == 2)
+ *buf16++ = (u16)readl(
+ host->base + MMC_REG_BUFFER_ACCESS);
+ else
+ *buf32++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
+ bytes -= buffer_width;
+ count += buffer_width;
}
if (bytes) {
- u8 *b = (u8 *)buf;
u32 tmp;
stat = mxcmci_poll_status(host,
@@ -417,7 +479,10 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
if (stat)
return stat;
tmp = readl(host->base + MMC_REG_BUFFER_ACCESS);
- memcpy(b, &tmp, bytes);
+ if (buffer_width == 2)
+ memcpy((u8 *)buf16, &tmp, bytes);
+ else
+ memcpy((u8 *)buf32, &tmp, bytes);
}
return 0;
@@ -426,33 +491,41 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
{
unsigned int stat;
- u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
- writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS);
- bytes -= 4;
+ u16 *buf16 = _buf;
+ u32 *buf32 = _buf;
+ int count = 0;
+ int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
+ int buffer_width = cpu_is_mx21() ? 2 : 4;
+
+ while (bytes >= buffer_width) {
+ if (count % fifo_size == 0) {
+ stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
+ if (stat)
+ return stat;
+ }
+ if (buffer_width == 2)
+ writel(*buf16++, host->base + MMC_REG_BUFFER_ACCESS);
+ else
+ writel(*buf32++, host->base + MMC_REG_BUFFER_ACCESS);
+ bytes -= buffer_width;
+ count += buffer_width;
}
if (bytes) {
- u8 *b = (u8 *)buf;
u32 tmp;
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
if (stat)
return stat;
- memcpy(&tmp, b, bytes);
+ if (buffer_width == 2)
+ memcpy(&tmp, (u8 *)buf16, bytes);
+ else
+ memcpy(&tmp, (u8 *)buf32, bytes);
writel(tmp, host->base + MMC_REG_BUFFER_ACCESS);
}
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
-
- return 0;
+ return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
}
static int mxcmci_transfer_data(struct mxcmci_host *host)
@@ -540,13 +613,20 @@ static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
return;
}
+ if (!host->data)
+ return;
+
+#ifdef HAS_DMA
/* For the DMA case the DMA engine handles the data transfer
* automatically. For non DMA we have to do it ourselves.
* Don't do it in interrupt context though.
*/
- if (!mxcmci_use_dma(host) && host->data)
+ if (mxcmci_use_dma(host)) {
+ if (cpu_is_mx21() && host->dma_dir == DMA_TO_DEVICE)
+ imx_dma_enable(host->dma);
+ } else
+#endif
schedule_work(&host->datawork);
-
}
static irqreturn_t mxcmci_irq(int irq, void *devid)
@@ -559,6 +639,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
stat = readl(host->base + MMC_REG_STATUS);
writel(stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
STATUS_WRITE_OP_DONE), host->base + MMC_REG_STATUS);
+ mxcmci_ack_int(host, stat);
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
@@ -689,9 +770,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->clock) {
mxcmci_set_clk_rate(host, ios->clock);
- writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
+ mxcmci_start_clock(host);
} else {
- writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
+ mxcmci_stop_clock(host);
}
host->clock = ios->clock;
@@ -845,7 +926,7 @@ static int mxcmci_probe(struct platform_device *pdev)
/* recommended in data sheet */
writew(0x2db4, host->base + MMC_REG_READ_TO);
- writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
+ mxcmci_set_int_cntr(host, host->default_irq_mask);
#ifdef HAS_DMA
host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
@@ -862,7 +943,9 @@ static int mxcmci_probe(struct platform_device *pdev)
}
ret = imx_dma_config_channel(host->dma,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
+ (cpu_is_mx21() ?
+ IMX_DMA_MEMSIZE_16 : IMX_DMA_MEMSIZE_32)
+ | IMX_DMA_TYPE_FIFO,
IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
r->start, 0);
if (ret) {
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V8] mmc : Add i.MX21 support to mxcmmc driver
@ 2010-09-09 22:02 ` Martin Fuzzey
0 siblings, 0 replies; 6+ messages in thread
From: Martin Fuzzey @ 2010-09-09 22:02 UTC (permalink / raw)
To: linux-arm-kernel
Even though the i.MX21 SDHC module has the same revision number as the i.MX27
one there are a few differences!!
Some interrupt enables are inverted.
FIFO is only 16 bits wide.
The argument is written to 2x16bit registers (vs 1x32).
Interrupts must be acknowledged via the INT_CNTR register.
Card clock must be enabled for each request.
For writes DMA must be enabled on command response not before.
Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
---
Changes from V7 (February 2010):
Rebased to 2.6.36-rc3
arch/arm/mach-imx/clock-imx21.c | 4 +
drivers/mmc/host/mxcmmc.c | 153 ++++++++++++++++++++++++++++++---------
2 files changed, 120 insertions(+), 37 deletions(-)
diff --git a/arch/arm/mach-imx/clock-imx21.c b/arch/arm/mach-imx/clock-imx21.c
index dafc271..076cd43 100644
--- a/arch/arm/mach-imx/clock-imx21.c
+++ b/arch/arm/mach-imx/clock-imx21.c
@@ -1170,8 +1170,8 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "gpt1", gpt_clk[1])
_REGISTER_CLOCK(NULL, "gpt1", gpt_clk[2])
_REGISTER_CLOCK(NULL, "pwm", pwm_clk[0])
- _REGISTER_CLOCK(NULL, "sdhc1", sdhc_clk[0])
- _REGISTER_CLOCK(NULL, "sdhc2", sdhc_clk[1])
+ _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc_clk[0])
+ _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc_clk[1])
_REGISTER_CLOCK(NULL, "cspi1", cspi_clk[0])
_REGISTER_CLOCK(NULL, "cspi2", cspi_clk[1])
_REGISTER_CLOCK(NULL, "cspi3", cspi_clk[2])
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 350f78e..88dd31f 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -56,6 +56,8 @@
#define MMC_REG_INT_CNTR 0x24
#define MMC_REG_CMD 0x28
#define MMC_REG_ARG 0x2C
+#define MMC_REG_ARGH 0x2C
+#define MMC_REG_ARGL 0x30
#define MMC_REG_RES_FIFO 0x34
#define MMC_REG_BUFFER_ACCESS 0x38
@@ -145,6 +147,49 @@ struct mxcmci_host {
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
+static void mxcmci_write_arg(struct mxcmci_host *host, u32 arg)
+{
+ if (cpu_is_mx21()) {
+ writel(arg >> 16, host->base + MMC_REG_ARGH);
+ writel(arg & 0xFFFF, host->base + MMC_REG_ARGL);
+ } else {
+ writel(arg, host->base + MMC_REG_ARG);
+ }
+}
+
+static void mxcmci_ack_int(struct mxcmci_host *host, u32 stat)
+{
+ if (cpu_is_mx21()) {
+ u32 intclr = readl(host->base + MMC_REG_INT_CNTR);
+
+ if (stat & STATUS_DATA_TRANS_DONE)
+ intclr |= INT_READ_OP_EN;
+ if (stat & STATUS_WRITE_OP_DONE)
+ intclr |= INT_WRITE_OP_DONE_EN;
+ if (stat & STATUS_END_CMD_RESP)
+ intclr |= INT_END_CMD_RES_EN;
+
+ writel(intclr, host->base + MMC_REG_INT_CNTR);
+ }
+}
+
+static inline void mxcmci_set_int_cntr(struct mxcmci_host *host, u32 enables)
+{
+ if (cpu_is_mx21()) /* some interrupt enables have reverse polarity */
+ enables ^= 0x1F;
+ writel(enables, host->base + MMC_REG_INT_CNTR);
+}
+
+static inline void mxcmci_start_clock(struct mxcmci_host *host)
+{
+ writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
+}
+
+static inline void mxcmci_stop_clock(struct mxcmci_host *host)
+{
+ writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
+}
+
static inline int mxcmci_use_dma(struct mxcmci_host *host)
{
return host->do_dma;
@@ -221,7 +266,10 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
}
wmb();
- imx_dma_enable(host->dma);
+ /* MX21: unreliable writes if dma enabled here - do on command done */
+ if (mxcmci_use_dma(host) &&
+ (!cpu_is_mx21() || host->dma_dir == DMA_FROM_DEVICE))
+ imx_dma_enable(host->dma);
#endif /* HAS_DMA */
return 0;
}
@@ -263,12 +311,16 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
spin_lock_irqsave(&host->lock, flags);
if (host->use_sdio)
int_cntr |= INT_SDIO_IRQ_EN;
- writel(int_cntr, host->base + MMC_REG_INT_CNTR);
+ mxcmci_set_int_cntr(host, int_cntr);
spin_unlock_irqrestore(&host->lock, flags);
writew(cmd->opcode, host->base + MMC_REG_CMD);
- writel(cmd->arg, host->base + MMC_REG_ARG);
+ mxcmci_write_arg(host, cmd->arg);
writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
+ if (cpu_is_mx21()) {
+ /* i.MX21 requires clock start after submitting command */
+ mxcmci_start_clock(host);
+ }
return 0;
}
@@ -282,7 +334,7 @@ static void mxcmci_finish_request(struct mxcmci_host *host,
spin_lock_irqsave(&host->lock, flags);
if (host->use_sdio)
int_cntr |= INT_SDIO_IRQ_EN;
- writel(int_cntr, host->base + MMC_REG_INT_CNTR);
+ mxcmci_set_int_cntr(host, int_cntr);
spin_unlock_irqrestore(&host->lock, flags);
host->req = NULL;
@@ -397,19 +449,29 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
{
unsigned int stat;
- u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = mxcmci_poll_status(host,
+ u16 *buf16 = _buf;
+ u32 *buf32 = _buf;
+ int count = 0;
+ int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
+ int buffer_width = cpu_is_mx21() ? 2 : 4;
+
+ while (bytes >= buffer_width) {
+ if (count % fifo_size == 0) {
+ stat = mxcmci_poll_status(host,
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
- if (stat)
- return stat;
- *buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
- bytes -= 4;
+ if (stat)
+ return stat;
+ }
+ if (buffer_width == 2)
+ *buf16++ = (u16)readl(
+ host->base + MMC_REG_BUFFER_ACCESS);
+ else
+ *buf32++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
+ bytes -= buffer_width;
+ count += buffer_width;
}
if (bytes) {
- u8 *b = (u8 *)buf;
u32 tmp;
stat = mxcmci_poll_status(host,
@@ -417,7 +479,10 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
if (stat)
return stat;
tmp = readl(host->base + MMC_REG_BUFFER_ACCESS);
- memcpy(b, &tmp, bytes);
+ if (buffer_width == 2)
+ memcpy((u8 *)buf16, &tmp, bytes);
+ else
+ memcpy((u8 *)buf32, &tmp, bytes);
}
return 0;
@@ -426,33 +491,41 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
{
unsigned int stat;
- u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
- writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS);
- bytes -= 4;
+ u16 *buf16 = _buf;
+ u32 *buf32 = _buf;
+ int count = 0;
+ int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
+ int buffer_width = cpu_is_mx21() ? 2 : 4;
+
+ while (bytes >= buffer_width) {
+ if (count % fifo_size == 0) {
+ stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
+ if (stat)
+ return stat;
+ }
+ if (buffer_width == 2)
+ writel(*buf16++, host->base + MMC_REG_BUFFER_ACCESS);
+ else
+ writel(*buf32++, host->base + MMC_REG_BUFFER_ACCESS);
+ bytes -= buffer_width;
+ count += buffer_width;
}
if (bytes) {
- u8 *b = (u8 *)buf;
u32 tmp;
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
if (stat)
return stat;
- memcpy(&tmp, b, bytes);
+ if (buffer_width == 2)
+ memcpy(&tmp, (u8 *)buf16, bytes);
+ else
+ memcpy(&tmp, (u8 *)buf32, bytes);
writel(tmp, host->base + MMC_REG_BUFFER_ACCESS);
}
- stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
- if (stat)
- return stat;
-
- return 0;
+ return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
}
static int mxcmci_transfer_data(struct mxcmci_host *host)
@@ -540,13 +613,20 @@ static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
return;
}
+ if (!host->data)
+ return;
+
+#ifdef HAS_DMA
/* For the DMA case the DMA engine handles the data transfer
* automatically. For non DMA we have to do it ourselves.
* Don't do it in interrupt context though.
*/
- if (!mxcmci_use_dma(host) && host->data)
+ if (mxcmci_use_dma(host)) {
+ if (cpu_is_mx21() && host->dma_dir == DMA_TO_DEVICE)
+ imx_dma_enable(host->dma);
+ } else
+#endif
schedule_work(&host->datawork);
-
}
static irqreturn_t mxcmci_irq(int irq, void *devid)
@@ -559,6 +639,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
stat = readl(host->base + MMC_REG_STATUS);
writel(stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
STATUS_WRITE_OP_DONE), host->base + MMC_REG_STATUS);
+ mxcmci_ack_int(host, stat);
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
@@ -689,9 +770,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->clock) {
mxcmci_set_clk_rate(host, ios->clock);
- writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
+ mxcmci_start_clock(host);
} else {
- writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
+ mxcmci_stop_clock(host);
}
host->clock = ios->clock;
@@ -845,7 +926,7 @@ static int mxcmci_probe(struct platform_device *pdev)
/* recommended in data sheet */
writew(0x2db4, host->base + MMC_REG_READ_TO);
- writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
+ mxcmci_set_int_cntr(host, host->default_irq_mask);
#ifdef HAS_DMA
host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
@@ -862,7 +943,9 @@ static int mxcmci_probe(struct platform_device *pdev)
}
ret = imx_dma_config_channel(host->dma,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
+ (cpu_is_mx21() ?
+ IMX_DMA_MEMSIZE_16 : IMX_DMA_MEMSIZE_32)
+ | IMX_DMA_TYPE_FIFO,
IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
r->start, 0);
if (ret) {
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH V8] mmc : Add i.MX21 support to mxcmmc driver
2010-09-09 22:02 ` Martin Fuzzey
@ 2010-09-14 3:03 ` Chris Ball
-1 siblings, 0 replies; 6+ messages in thread
From: Chris Ball @ 2010-09-14 3:03 UTC (permalink / raw)
To: Martin Fuzzey
Cc: Pavel Pisa, Sascha Hauer, linux-mmc, Pierre Ossman, linux-arm-kernel
Hi Pavel, Sascha,
On Fri, Sep 10, 2010 at 12:02:44AM +0200, Martin Fuzzey wrote:
> Even though the i.MX21 SDHC module has the same revision number as the i.MX27
> one there are a few differences!!
> Some interrupt enables are inverted.
> FIFO is only 16 bits wide.
> The argument is written to 2x16bit registers (vs 1x32).
> Interrupts must be acknowledged via the INT_CNTR register.
> Card clock must be enabled for each request.
> For writes DMA must be enabled on command response not before.
>
> Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
>
> ---
> Changes from V7 (February 2010):
> Rebased to 2.6.36-rc3
>
> arch/arm/mach-imx/clock-imx21.c | 4 +
> drivers/mmc/host/mxcmmc.c | 153 ++++++++++++++++++++++++++++++---------
> 2 files changed, 120 insertions(+), 37 deletions(-)
Any feedback on this patch from Martin?
https://patchwork.kernel.org/patch/178242/
Thanks,
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V8] mmc : Add i.MX21 support to mxcmmc driver
@ 2010-09-14 3:03 ` Chris Ball
0 siblings, 0 replies; 6+ messages in thread
From: Chris Ball @ 2010-09-14 3:03 UTC (permalink / raw)
To: linux-arm-kernel
Hi Pavel, Sascha,
On Fri, Sep 10, 2010 at 12:02:44AM +0200, Martin Fuzzey wrote:
> Even though the i.MX21 SDHC module has the same revision number as the i.MX27
> one there are a few differences!!
> Some interrupt enables are inverted.
> FIFO is only 16 bits wide.
> The argument is written to 2x16bit registers (vs 1x32).
> Interrupts must be acknowledged via the INT_CNTR register.
> Card clock must be enabled for each request.
> For writes DMA must be enabled on command response not before.
>
> Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
>
> ---
> Changes from V7 (February 2010):
> Rebased to 2.6.36-rc3
>
> arch/arm/mach-imx/clock-imx21.c | 4 +
> drivers/mmc/host/mxcmmc.c | 153 ++++++++++++++++++++++++++++++---------
> 2 files changed, 120 insertions(+), 37 deletions(-)
Any feedback on this patch from Martin?
https://patchwork.kernel.org/patch/178242/
Thanks,
--
Chris Ball <cjb@laptop.org> <http://printf.net/>
One Laptop Per Child
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH V8] mmc : Add i.MX21 support to mxcmmc driver
2010-09-09 22:02 ` Martin Fuzzey
@ 2010-09-16 11:07 ` Sascha Hauer
-1 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2010-09-16 11:07 UTC (permalink / raw)
To: Martin Fuzzey; +Cc: linux-mmc, Pierre Ossman, linux-arm-kernel, cjb
On Fri, Sep 10, 2010 at 12:02:44AM +0200, Martin Fuzzey wrote:
> Even though the i.MX21 SDHC module has the same revision number as the i.MX27
> one there are a few differences!!
> Some interrupt enables are inverted.
> FIFO is only 16 bits wide.
> The argument is written to 2x16bit registers (vs 1x32).
> Interrupts must be acknowledged via the INT_CNTR register.
> Card clock must be enabled for each request.
> For writes DMA must be enabled on command response not before.
>
> Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
>
> ---
> Changes from V7 (February 2010):
> Rebased to 2.6.36-rc3
>
> arch/arm/mach-imx/clock-imx21.c | 4 +
> drivers/mmc/host/mxcmmc.c | 153 ++++++++++++++++++++++++++++++---------
> 2 files changed, 120 insertions(+), 37 deletions(-)
>
> diff --git a/arch/arm/mach-imx/clock-imx21.c b/arch/arm/mach-imx/clock-imx21.c
> index dafc271..076cd43 100644
> --- a/arch/arm/mach-imx/clock-imx21.c
> +++ b/arch/arm/mach-imx/clock-imx21.c
> @@ -1170,8 +1170,8 @@ static struct clk_lookup lookups[] = {
> _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[1])
> _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[2])
> _REGISTER_CLOCK(NULL, "pwm", pwm_clk[0])
> - _REGISTER_CLOCK(NULL, "sdhc1", sdhc_clk[0])
> - _REGISTER_CLOCK(NULL, "sdhc2", sdhc_clk[1])
> + _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc_clk[0])
> + _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc_clk[1])
> _REGISTER_CLOCK(NULL, "cspi1", cspi_clk[0])
> _REGISTER_CLOCK(NULL, "cspi2", cspi_clk[1])
> _REGISTER_CLOCK(NULL, "cspi3", cspi_clk[2])
Please make a seperate patch from this hunk. This should go via the i.MX
tree.
I will give this a runtime test on !i.MX21 tomorrow. Otherwise the patch
looks fine to me.
Sascha
> diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
> index 350f78e..88dd31f 100644
> --- a/drivers/mmc/host/mxcmmc.c
> +++ b/drivers/mmc/host/mxcmmc.c
> @@ -56,6 +56,8 @@
> #define MMC_REG_INT_CNTR 0x24
> #define MMC_REG_CMD 0x28
> #define MMC_REG_ARG 0x2C
> +#define MMC_REG_ARGH 0x2C
> +#define MMC_REG_ARGL 0x30
> #define MMC_REG_RES_FIFO 0x34
> #define MMC_REG_BUFFER_ACCESS 0x38
>
> @@ -145,6 +147,49 @@ struct mxcmci_host {
>
> static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
>
> +static void mxcmci_write_arg(struct mxcmci_host *host, u32 arg)
> +{
> + if (cpu_is_mx21()) {
> + writel(arg >> 16, host->base + MMC_REG_ARGH);
> + writel(arg & 0xFFFF, host->base + MMC_REG_ARGL);
> + } else {
> + writel(arg, host->base + MMC_REG_ARG);
> + }
> +}
> +
> +static void mxcmci_ack_int(struct mxcmci_host *host, u32 stat)
> +{
> + if (cpu_is_mx21()) {
> + u32 intclr = readl(host->base + MMC_REG_INT_CNTR);
> +
> + if (stat & STATUS_DATA_TRANS_DONE)
> + intclr |= INT_READ_OP_EN;
> + if (stat & STATUS_WRITE_OP_DONE)
> + intclr |= INT_WRITE_OP_DONE_EN;
> + if (stat & STATUS_END_CMD_RESP)
> + intclr |= INT_END_CMD_RES_EN;
> +
> + writel(intclr, host->base + MMC_REG_INT_CNTR);
> + }
> +}
> +
> +static inline void mxcmci_set_int_cntr(struct mxcmci_host *host, u32 enables)
> +{
> + if (cpu_is_mx21()) /* some interrupt enables have reverse polarity */
> + enables ^= 0x1F;
> + writel(enables, host->base + MMC_REG_INT_CNTR);
> +}
> +
> +static inline void mxcmci_start_clock(struct mxcmci_host *host)
> +{
> + writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
> +}
> +
> +static inline void mxcmci_stop_clock(struct mxcmci_host *host)
> +{
> + writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
> +}
> +
> static inline int mxcmci_use_dma(struct mxcmci_host *host)
> {
> return host->do_dma;
> @@ -221,7 +266,10 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
> }
> wmb();
>
> - imx_dma_enable(host->dma);
> + /* MX21: unreliable writes if dma enabled here - do on command done */
> + if (mxcmci_use_dma(host) &&
> + (!cpu_is_mx21() || host->dma_dir == DMA_FROM_DEVICE))
> + imx_dma_enable(host->dma);
> #endif /* HAS_DMA */
> return 0;
> }
> @@ -263,12 +311,16 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
> spin_lock_irqsave(&host->lock, flags);
> if (host->use_sdio)
> int_cntr |= INT_SDIO_IRQ_EN;
> - writel(int_cntr, host->base + MMC_REG_INT_CNTR);
> + mxcmci_set_int_cntr(host, int_cntr);
> spin_unlock_irqrestore(&host->lock, flags);
>
> writew(cmd->opcode, host->base + MMC_REG_CMD);
> - writel(cmd->arg, host->base + MMC_REG_ARG);
> + mxcmci_write_arg(host, cmd->arg);
> writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
> + if (cpu_is_mx21()) {
> + /* i.MX21 requires clock start after submitting command */
> + mxcmci_start_clock(host);
> + }
>
> return 0;
> }
> @@ -282,7 +334,7 @@ static void mxcmci_finish_request(struct mxcmci_host *host,
> spin_lock_irqsave(&host->lock, flags);
> if (host->use_sdio)
> int_cntr |= INT_SDIO_IRQ_EN;
> - writel(int_cntr, host->base + MMC_REG_INT_CNTR);
> + mxcmci_set_int_cntr(host, int_cntr);
> spin_unlock_irqrestore(&host->lock, flags);
>
> host->req = NULL;
> @@ -397,19 +449,29 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
> static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
> {
> unsigned int stat;
> - u32 *buf = _buf;
> -
> - while (bytes > 3) {
> - stat = mxcmci_poll_status(host,
> + u16 *buf16 = _buf;
> + u32 *buf32 = _buf;
> + int count = 0;
> + int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
> + int buffer_width = cpu_is_mx21() ? 2 : 4;
> +
> + while (bytes >= buffer_width) {
> + if (count % fifo_size == 0) {
> + stat = mxcmci_poll_status(host,
> STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
> - if (stat)
> - return stat;
> - *buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
> - bytes -= 4;
> + if (stat)
> + return stat;
> + }
> + if (buffer_width == 2)
> + *buf16++ = (u16)readl(
> + host->base + MMC_REG_BUFFER_ACCESS);
> + else
> + *buf32++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
> + bytes -= buffer_width;
> + count += buffer_width;
> }
>
> if (bytes) {
> - u8 *b = (u8 *)buf;
> u32 tmp;
>
> stat = mxcmci_poll_status(host,
> @@ -417,7 +479,10 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
> if (stat)
> return stat;
> tmp = readl(host->base + MMC_REG_BUFFER_ACCESS);
> - memcpy(b, &tmp, bytes);
> + if (buffer_width == 2)
> + memcpy((u8 *)buf16, &tmp, bytes);
> + else
> + memcpy((u8 *)buf32, &tmp, bytes);
> }
>
> return 0;
> @@ -426,33 +491,41 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
> static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
> {
> unsigned int stat;
> - u32 *buf = _buf;
> -
> - while (bytes > 3) {
> - stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> - if (stat)
> - return stat;
> - writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS);
> - bytes -= 4;
> + u16 *buf16 = _buf;
> + u32 *buf32 = _buf;
> + int count = 0;
> + int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
> + int buffer_width = cpu_is_mx21() ? 2 : 4;
> +
> + while (bytes >= buffer_width) {
> + if (count % fifo_size == 0) {
> + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> + if (stat)
> + return stat;
> + }
> + if (buffer_width == 2)
> + writel(*buf16++, host->base + MMC_REG_BUFFER_ACCESS);
> + else
> + writel(*buf32++, host->base + MMC_REG_BUFFER_ACCESS);
> + bytes -= buffer_width;
> + count += buffer_width;
> }
>
> if (bytes) {
> - u8 *b = (u8 *)buf;
> u32 tmp;
>
> stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> if (stat)
> return stat;
>
> - memcpy(&tmp, b, bytes);
> + if (buffer_width == 2)
> + memcpy(&tmp, (u8 *)buf16, bytes);
> + else
> + memcpy(&tmp, (u8 *)buf32, bytes);
> writel(tmp, host->base + MMC_REG_BUFFER_ACCESS);
> }
>
> - stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> - if (stat)
> - return stat;
> -
> - return 0;
> + return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> }
>
> static int mxcmci_transfer_data(struct mxcmci_host *host)
> @@ -540,13 +613,20 @@ static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
> return;
> }
>
> + if (!host->data)
> + return;
> +
> +#ifdef HAS_DMA
> /* For the DMA case the DMA engine handles the data transfer
> * automatically. For non DMA we have to do it ourselves.
> * Don't do it in interrupt context though.
> */
> - if (!mxcmci_use_dma(host) && host->data)
> + if (mxcmci_use_dma(host)) {
> + if (cpu_is_mx21() && host->dma_dir == DMA_TO_DEVICE)
> + imx_dma_enable(host->dma);
> + } else
> +#endif
> schedule_work(&host->datawork);
> -
> }
>
> static irqreturn_t mxcmci_irq(int irq, void *devid)
> @@ -559,6 +639,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
> stat = readl(host->base + MMC_REG_STATUS);
> writel(stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
> STATUS_WRITE_OP_DONE), host->base + MMC_REG_STATUS);
> + mxcmci_ack_int(host, stat);
>
> dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
>
> @@ -689,9 +770,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>
> if (ios->clock) {
> mxcmci_set_clk_rate(host, ios->clock);
> - writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
> + mxcmci_start_clock(host);
> } else {
> - writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
> + mxcmci_stop_clock(host);
> }
>
> host->clock = ios->clock;
> @@ -845,7 +926,7 @@ static int mxcmci_probe(struct platform_device *pdev)
> /* recommended in data sheet */
> writew(0x2db4, host->base + MMC_REG_READ_TO);
>
> - writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
> + mxcmci_set_int_cntr(host, host->default_irq_mask);
>
> #ifdef HAS_DMA
> host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
> @@ -862,7 +943,9 @@ static int mxcmci_probe(struct platform_device *pdev)
> }
>
> ret = imx_dma_config_channel(host->dma,
> - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
> + (cpu_is_mx21() ?
> + IMX_DMA_MEMSIZE_16 : IMX_DMA_MEMSIZE_32)
> + | IMX_DMA_TYPE_FIFO,
> IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
> r->start, 0);
> if (ret) {
>
>
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V8] mmc : Add i.MX21 support to mxcmmc driver
@ 2010-09-16 11:07 ` Sascha Hauer
0 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2010-09-16 11:07 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Sep 10, 2010 at 12:02:44AM +0200, Martin Fuzzey wrote:
> Even though the i.MX21 SDHC module has the same revision number as the i.MX27
> one there are a few differences!!
> Some interrupt enables are inverted.
> FIFO is only 16 bits wide.
> The argument is written to 2x16bit registers (vs 1x32).
> Interrupts must be acknowledged via the INT_CNTR register.
> Card clock must be enabled for each request.
> For writes DMA must be enabled on command response not before.
>
> Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
>
> ---
> Changes from V7 (February 2010):
> Rebased to 2.6.36-rc3
>
> arch/arm/mach-imx/clock-imx21.c | 4 +
> drivers/mmc/host/mxcmmc.c | 153 ++++++++++++++++++++++++++++++---------
> 2 files changed, 120 insertions(+), 37 deletions(-)
>
> diff --git a/arch/arm/mach-imx/clock-imx21.c b/arch/arm/mach-imx/clock-imx21.c
> index dafc271..076cd43 100644
> --- a/arch/arm/mach-imx/clock-imx21.c
> +++ b/arch/arm/mach-imx/clock-imx21.c
> @@ -1170,8 +1170,8 @@ static struct clk_lookup lookups[] = {
> _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[1])
> _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[2])
> _REGISTER_CLOCK(NULL, "pwm", pwm_clk[0])
> - _REGISTER_CLOCK(NULL, "sdhc1", sdhc_clk[0])
> - _REGISTER_CLOCK(NULL, "sdhc2", sdhc_clk[1])
> + _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc_clk[0])
> + _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc_clk[1])
> _REGISTER_CLOCK(NULL, "cspi1", cspi_clk[0])
> _REGISTER_CLOCK(NULL, "cspi2", cspi_clk[1])
> _REGISTER_CLOCK(NULL, "cspi3", cspi_clk[2])
Please make a seperate patch from this hunk. This should go via the i.MX
tree.
I will give this a runtime test on !i.MX21 tomorrow. Otherwise the patch
looks fine to me.
Sascha
> diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
> index 350f78e..88dd31f 100644
> --- a/drivers/mmc/host/mxcmmc.c
> +++ b/drivers/mmc/host/mxcmmc.c
> @@ -56,6 +56,8 @@
> #define MMC_REG_INT_CNTR 0x24
> #define MMC_REG_CMD 0x28
> #define MMC_REG_ARG 0x2C
> +#define MMC_REG_ARGH 0x2C
> +#define MMC_REG_ARGL 0x30
> #define MMC_REG_RES_FIFO 0x34
> #define MMC_REG_BUFFER_ACCESS 0x38
>
> @@ -145,6 +147,49 @@ struct mxcmci_host {
>
> static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
>
> +static void mxcmci_write_arg(struct mxcmci_host *host, u32 arg)
> +{
> + if (cpu_is_mx21()) {
> + writel(arg >> 16, host->base + MMC_REG_ARGH);
> + writel(arg & 0xFFFF, host->base + MMC_REG_ARGL);
> + } else {
> + writel(arg, host->base + MMC_REG_ARG);
> + }
> +}
> +
> +static void mxcmci_ack_int(struct mxcmci_host *host, u32 stat)
> +{
> + if (cpu_is_mx21()) {
> + u32 intclr = readl(host->base + MMC_REG_INT_CNTR);
> +
> + if (stat & STATUS_DATA_TRANS_DONE)
> + intclr |= INT_READ_OP_EN;
> + if (stat & STATUS_WRITE_OP_DONE)
> + intclr |= INT_WRITE_OP_DONE_EN;
> + if (stat & STATUS_END_CMD_RESP)
> + intclr |= INT_END_CMD_RES_EN;
> +
> + writel(intclr, host->base + MMC_REG_INT_CNTR);
> + }
> +}
> +
> +static inline void mxcmci_set_int_cntr(struct mxcmci_host *host, u32 enables)
> +{
> + if (cpu_is_mx21()) /* some interrupt enables have reverse polarity */
> + enables ^= 0x1F;
> + writel(enables, host->base + MMC_REG_INT_CNTR);
> +}
> +
> +static inline void mxcmci_start_clock(struct mxcmci_host *host)
> +{
> + writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
> +}
> +
> +static inline void mxcmci_stop_clock(struct mxcmci_host *host)
> +{
> + writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
> +}
> +
> static inline int mxcmci_use_dma(struct mxcmci_host *host)
> {
> return host->do_dma;
> @@ -221,7 +266,10 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
> }
> wmb();
>
> - imx_dma_enable(host->dma);
> + /* MX21: unreliable writes if dma enabled here - do on command done */
> + if (mxcmci_use_dma(host) &&
> + (!cpu_is_mx21() || host->dma_dir == DMA_FROM_DEVICE))
> + imx_dma_enable(host->dma);
> #endif /* HAS_DMA */
> return 0;
> }
> @@ -263,12 +311,16 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
> spin_lock_irqsave(&host->lock, flags);
> if (host->use_sdio)
> int_cntr |= INT_SDIO_IRQ_EN;
> - writel(int_cntr, host->base + MMC_REG_INT_CNTR);
> + mxcmci_set_int_cntr(host, int_cntr);
> spin_unlock_irqrestore(&host->lock, flags);
>
> writew(cmd->opcode, host->base + MMC_REG_CMD);
> - writel(cmd->arg, host->base + MMC_REG_ARG);
> + mxcmci_write_arg(host, cmd->arg);
> writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
> + if (cpu_is_mx21()) {
> + /* i.MX21 requires clock start after submitting command */
> + mxcmci_start_clock(host);
> + }
>
> return 0;
> }
> @@ -282,7 +334,7 @@ static void mxcmci_finish_request(struct mxcmci_host *host,
> spin_lock_irqsave(&host->lock, flags);
> if (host->use_sdio)
> int_cntr |= INT_SDIO_IRQ_EN;
> - writel(int_cntr, host->base + MMC_REG_INT_CNTR);
> + mxcmci_set_int_cntr(host, int_cntr);
> spin_unlock_irqrestore(&host->lock, flags);
>
> host->req = NULL;
> @@ -397,19 +449,29 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
> static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
> {
> unsigned int stat;
> - u32 *buf = _buf;
> -
> - while (bytes > 3) {
> - stat = mxcmci_poll_status(host,
> + u16 *buf16 = _buf;
> + u32 *buf32 = _buf;
> + int count = 0;
> + int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
> + int buffer_width = cpu_is_mx21() ? 2 : 4;
> +
> + while (bytes >= buffer_width) {
> + if (count % fifo_size == 0) {
> + stat = mxcmci_poll_status(host,
> STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
> - if (stat)
> - return stat;
> - *buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
> - bytes -= 4;
> + if (stat)
> + return stat;
> + }
> + if (buffer_width == 2)
> + *buf16++ = (u16)readl(
> + host->base + MMC_REG_BUFFER_ACCESS);
> + else
> + *buf32++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
> + bytes -= buffer_width;
> + count += buffer_width;
> }
>
> if (bytes) {
> - u8 *b = (u8 *)buf;
> u32 tmp;
>
> stat = mxcmci_poll_status(host,
> @@ -417,7 +479,10 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
> if (stat)
> return stat;
> tmp = readl(host->base + MMC_REG_BUFFER_ACCESS);
> - memcpy(b, &tmp, bytes);
> + if (buffer_width == 2)
> + memcpy((u8 *)buf16, &tmp, bytes);
> + else
> + memcpy((u8 *)buf32, &tmp, bytes);
> }
>
> return 0;
> @@ -426,33 +491,41 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
> static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
> {
> unsigned int stat;
> - u32 *buf = _buf;
> -
> - while (bytes > 3) {
> - stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> - if (stat)
> - return stat;
> - writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS);
> - bytes -= 4;
> + u16 *buf16 = _buf;
> + u32 *buf32 = _buf;
> + int count = 0;
> + int fifo_size = host->cmdat & CMD_DAT_CONT_BUS_WIDTH_4 ? 64 : 16;
> + int buffer_width = cpu_is_mx21() ? 2 : 4;
> +
> + while (bytes >= buffer_width) {
> + if (count % fifo_size == 0) {
> + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> + if (stat)
> + return stat;
> + }
> + if (buffer_width == 2)
> + writel(*buf16++, host->base + MMC_REG_BUFFER_ACCESS);
> + else
> + writel(*buf32++, host->base + MMC_REG_BUFFER_ACCESS);
> + bytes -= buffer_width;
> + count += buffer_width;
> }
>
> if (bytes) {
> - u8 *b = (u8 *)buf;
> u32 tmp;
>
> stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> if (stat)
> return stat;
>
> - memcpy(&tmp, b, bytes);
> + if (buffer_width == 2)
> + memcpy(&tmp, (u8 *)buf16, bytes);
> + else
> + memcpy(&tmp, (u8 *)buf32, bytes);
> writel(tmp, host->base + MMC_REG_BUFFER_ACCESS);
> }
>
> - stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> - if (stat)
> - return stat;
> -
> - return 0;
> + return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
> }
>
> static int mxcmci_transfer_data(struct mxcmci_host *host)
> @@ -540,13 +613,20 @@ static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
> return;
> }
>
> + if (!host->data)
> + return;
> +
> +#ifdef HAS_DMA
> /* For the DMA case the DMA engine handles the data transfer
> * automatically. For non DMA we have to do it ourselves.
> * Don't do it in interrupt context though.
> */
> - if (!mxcmci_use_dma(host) && host->data)
> + if (mxcmci_use_dma(host)) {
> + if (cpu_is_mx21() && host->dma_dir == DMA_TO_DEVICE)
> + imx_dma_enable(host->dma);
> + } else
> +#endif
> schedule_work(&host->datawork);
> -
> }
>
> static irqreturn_t mxcmci_irq(int irq, void *devid)
> @@ -559,6 +639,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
> stat = readl(host->base + MMC_REG_STATUS);
> writel(stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
> STATUS_WRITE_OP_DONE), host->base + MMC_REG_STATUS);
> + mxcmci_ack_int(host, stat);
>
> dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
>
> @@ -689,9 +770,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>
> if (ios->clock) {
> mxcmci_set_clk_rate(host, ios->clock);
> - writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
> + mxcmci_start_clock(host);
> } else {
> - writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
> + mxcmci_stop_clock(host);
> }
>
> host->clock = ios->clock;
> @@ -845,7 +926,7 @@ static int mxcmci_probe(struct platform_device *pdev)
> /* recommended in data sheet */
> writew(0x2db4, host->base + MMC_REG_READ_TO);
>
> - writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
> + mxcmci_set_int_cntr(host, host->default_irq_mask);
>
> #ifdef HAS_DMA
> host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
> @@ -862,7 +943,9 @@ static int mxcmci_probe(struct platform_device *pdev)
> }
>
> ret = imx_dma_config_channel(host->dma,
> - IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
> + (cpu_is_mx21() ?
> + IMX_DMA_MEMSIZE_16 : IMX_DMA_MEMSIZE_32)
> + | IMX_DMA_TYPE_FIFO,
> IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
> r->start, 0);
> if (ret) {
>
>
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-09-16 11:07 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-09 22:02 [PATCH V8] mmc : Add i.MX21 support to mxcmmc driver Martin Fuzzey
2010-09-09 22:02 ` Martin Fuzzey
2010-09-14 3:03 ` Chris Ball
2010-09-14 3:03 ` Chris Ball
2010-09-16 11:07 ` Sascha Hauer
2010-09-16 11:07 ` Sascha Hauer
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.