summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/mxs-mmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/mxs-mmc.c')
-rw-r--r--drivers/mmc/host/mxs-mmc.c92
1 files changed, 66 insertions, 26 deletions
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index c60352247c49..b7210b7d7af3 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -49,7 +49,7 @@
#define MXS_MMC_DETECT_TIMEOUT (HZ/2)
/* Max value supported for XFER_COUNT */
-#define SSP_BUFFER_SIZE (65536)
+#define SSP_BUFFER_SIZE (65535)
#ifndef BF
#define BF(value, field) (((value) << BP_##field) & BM_##field)
@@ -93,6 +93,9 @@
#define BF_SSP_BLOCK_SIZE_BLOCK_SIZE(v) \
(((v) << 16) & BM_SSP_BLOCK_SIZE_BLOCK_SIZE)
#endif
+#ifndef BM_SSP_CMD0_DBL_DATA_RATE_EN
+#define BM_SSP_CMD0_DBL_DATA_RATE_EN 0x02000000
+#endif
struct mxs_mmc_host {
struct device *dev;
@@ -159,6 +162,7 @@ static inline int mxs_mmc_is_plugged(struct mxs_mmc_host *host)
return !(status & BM_SSP_STATUS_CARD_DETECT);
}
+static void mxs_mmc_reset(struct mxs_mmc_host *host);
/* Card detection polling function */
static void mxs_mmc_detect_poll(unsigned long arg)
{
@@ -167,6 +171,8 @@ static void mxs_mmc_detect_poll(unsigned long arg)
card_status = mxs_mmc_is_plugged(host);
if (card_status != host->present) {
+ /* Reset MMC block */
+ mxs_mmc_reset(host);
host->present = card_status;
mmc_detect_change(host->mmc, 0);
}
@@ -183,6 +189,12 @@ static void mxs_mmc_detect_poll(unsigned long arg)
BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
+#define MXS_MMC_ERR_BITS (BM_SSP_CTRL1_RESP_ERR_IRQ | \
+ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
+ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ | \
+ BM_SSP_CTRL1_DATA_CRC_IRQ | \
+ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ)
+
/* SSP DMA interrupt handler */
static irqreturn_t mmc_irq_handler(int irq, void *dev_id)
{
@@ -198,19 +210,17 @@ static irqreturn_t mmc_irq_handler(int irq, void *dev_id)
/* STOP the dma transfer here. */
mxs_dma_cooked(host->dmach, NULL);
}
- host->status =
- __raw_readl(host->ssp_base + HW_SSP_STATUS);
- if (host->cmd) /* else it is a bogus interrupt */
- complete(&host->dma_done);
+ if ((irq == host->dmairq) || (c1 & MXS_MMC_ERR_BITS))
+ if (host->cmd) {
+ host->status =
+ __raw_readl(host->ssp_base + HW_SSP_STATUS);
+ complete(&host->dma_done);
+ }
- if ((c1 & BM_SSP_CTRL1_SDIO_IRQ) && (c1 & BM_SSP_CTRL1_SDIO_IRQ_EN)) {
- __raw_writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, host->ssp_base + \
- HW_SSP_CTRL0_CLR);
- __raw_writel(BM_SSP_CTRL1_SDIO_IRQ_EN, host->ssp_base + \
- HW_SSP_CTRL1_CLR);
+ if ((c1 & BM_SSP_CTRL1_SDIO_IRQ) && (c1 & BM_SSP_CTRL1_SDIO_IRQ_EN))
mmc_signal_sdio_irq(host->mmc);
- }
+
return IRQ_HANDLED;
}
@@ -239,6 +249,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
{
struct mmc_command *cmd = host->cmd;
struct mxs_dma_desc *dma_desc = host->dma_desc;
+ unsigned long flags;
dma_desc->cmd.cmd.bits.command = NO_DMA_XFER;
dma_desc->cmd.cmd.bits.irq = 1;
@@ -255,7 +266,8 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
if (host->sdio_irq_en) {
dma_desc->cmd.pio_words[0] |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
- dma_desc->cmd.pio_words[1] |= BM_SSP_CMD0_CONT_CLKING_EN;
+ dma_desc->cmd.pio_words[1] |= BM_SSP_CMD0_CONT_CLKING_EN \
+ | BM_SSP_CMD0_SLOW_CLKING_EN;
}
init_completion(&host->dma_done);
@@ -265,6 +277,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
dev_dbg(host->dev, "%s start DMA.\n", __func__);
if (mxs_dma_enable(host->dmach) < 0)
dev_err(host->dev, "mmc_dma_enable failed\n");
+
wait_for_completion(&host->dma_done);
cmd->error = mxs_mmc_cmd_error(host->status);
@@ -273,6 +286,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
dev_dbg(host->dev, "Command error 0x%x\n", cmd->error);
mxs_dma_reset(host->dmach);
}
+ mxs_dma_disable(host->dmach);
}
/* Send the ac command to the device */
@@ -284,6 +298,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
u32 ssp_ctrl0;
u32 ssp_cmd0;
u32 ssp_cmd1;
+ unsigned long flags;
ignore_crc = (mmc_resp_type(cmd) & MMC_RSP_CRC) ?
0 : BM_SSP_CTRL0_IGNORE_CRC;
@@ -305,7 +320,8 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
if (host->sdio_irq_en) {
ssp_ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
- ssp_cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN;
+ ssp_cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN \
+ | BM_SSP_CMD0_SLOW_CLKING_EN;
}
dma_desc->cmd.pio_words[0] = ssp_ctrl0;
@@ -356,6 +372,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
dev_dbg(host->dev, "Command error 0x%x\n", cmd->error);
mxs_dma_reset(host->dmach);
}
+ mxs_dma_disable(host->dmach);
}
/* Copy data between sg list and dma buffer */
@@ -451,6 +468,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
u32 data_size = cmd->data->blksz * cmd->data->blocks;
u32 log2_block_size;
+ unsigned long flags;
ignore_crc = mmc_resp_type(cmd) & MMC_RSP_CRC ? 0 : 1;
resp = mmc_resp_type(cmd) & MMC_RSP_PRESENT ? 1 : 0;
@@ -540,6 +558,9 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
dev_dbg(host->dev, "%s blksz is 0x%x.\n", __func__, log2_block_size);
if (ssp_ver_major > 3) {
+ /* Configure the CMD0 */
+ ssp_cmd0 = BF(cmd->opcode, SSP_CMD0_CMD);
+
/* Configure the BLOCK SIZE and BLOCK COUNT */
if ((1<<log2_block_size) != cmd->data->blksz) {
BUG_ON(cmd->data->blocks > 1);
@@ -548,20 +569,32 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
val = BF(log2_block_size, SSP_BLOCK_SIZE_BLOCK_SIZE) |
BF(cmd->data->blocks - 1, SSP_BLOCK_SIZE_BLOCK_COUNT);
__raw_writel(val, host->ssp_base + HW_SSP_BLOCK_SIZE);
- }
- /* Configure the CMD0 */
- ssp_cmd0 = BF(cmd->opcode, SSP_CMD0_CMD);
- } else
- ssp_cmd0 =
- BF(log2_block_size, SSP_BLOCK_SIZE_BLOCK_SIZE) |
- BF(cmd->opcode, SSP_CMD0_CMD) |
- BF(cmd->data->blocks - 1, SSP_BLOCK_SIZE_BLOCK_COUNT);
+ if (host->mmc->ios.bus_width & MMC_BUS_WIDTH_DDR)
+ /* Enable the DDR mode */
+ ssp_cmd0 |= BM_SSP_CMD0_DBL_DATA_RATE_EN;
+ else
+ ssp_cmd0 &= ~BM_SSP_CMD0_DBL_DATA_RATE_EN;
+ }
+ } else {
+ if ((1<<log2_block_size) != cmd->data->blksz) {
+ BUG_ON(cmd->data->blocks > 1);
+ ssp_cmd0 =
+ BF(0, SSP_BLOCK_SIZE_BLOCK_SIZE) |
+ BF(cmd->opcode, SSP_CMD0_CMD) |
+ BF(0, SSP_BLOCK_SIZE_BLOCK_COUNT);
+ } else
+ ssp_cmd0 =
+ BF(log2_block_size, SSP_BLOCK_SIZE_BLOCK_SIZE) |
+ BF(cmd->opcode, SSP_CMD0_CMD) |
+ BF(cmd->data->blocks - 1, SSP_BLOCK_SIZE_BLOCK_COUNT);
+ }
if (host->sdio_irq_en) {
ssp_ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
- ssp_cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN;
+ ssp_cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN \
+ | BM_SSP_CMD0_SLOW_CLKING_EN;
}
- if (cmd->opcode == 12)
+ if ((cmd->opcode == 12) || (cmd->opcode == 53))
ssp_cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
ssp_cmd1 = BF(cmd->arg, SSP_CMD1_CMD_ARG);
@@ -628,6 +661,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
dev_dbg(host->dev, "Transferred %u bytes\n",
cmd->data->bytes_xfered);
}
+ mxs_dma_disable(host->dmach);
}
/* Begin sedning a command to the card */
@@ -672,6 +706,13 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
dev_dbg(host->dev, "MMC request\n");
+ if (!host->present) {
+ mrq->cmd->error = -ETIMEDOUT;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ BUG_ON(host->mrq != NULL);
host->mrq = mrq;
mxs_mmc_start_cmd(host, mrq->cmd);
@@ -777,9 +818,9 @@ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
dev_warn(host->dev,
"Platform does not support CMD pin pullup control\n");
- if (ios->bus_width == MMC_BUS_WIDTH_8)
+ if ((ios->bus_width & ~MMC_BUS_WIDTH_DDR) == MMC_BUS_WIDTH_8)
host->bus_width = 2;
- else if (ios->bus_width == MMC_BUS_WIDTH_4)
+ else if ((ios->bus_width & ~MMC_BUS_WIDTH_DDR) == MMC_BUS_WIDTH_4)
host->bus_width = 1;
else
host->bus_width = 0;
@@ -851,7 +892,6 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host)
/* Configure SSP Control Register 1 */
ssp_ctrl1 =
BM_SSP_CTRL1_DMA_ENABLE |
- BM_SSP_CTRL1_POLARITY |
BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
BM_SSP_CTRL1_DATA_CRC_IRQ_EN |
BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |