From c321c217836fb08e31b026035a71b8ddad513a52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Krause?= <joerg.krause@embedded.rocks>
Date: Tue, 1 Nov 2016 18:02:46 +0100
Subject: [PATCH] mmc: mxs-mmc: use PIO mode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Krause <joerg.krause@embedded.rocks>
---
drivers/mmc/host/mxs-mmc.c | 365 ++++++++++++++++++++++++++++++++++----------
include/linux/spi/mxs-spi.h | 12 ++
2 files changed, 300 insertions(+), 77 deletions(-)
@@ -47,14 +47,22 @@
#define DRIVER_NAME "mxs-mmc"
-#define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \
- 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_FIFO_UNDERRUN_IRQ | \
- BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
- BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
+#define MXS_MMC_ERR_IRQ_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_FIFO_UNDERRUN_IRQ | \
+ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
+ BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
+
+#define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \
+ MXS_MMC_ERR_IRQ_BITS)
+
+#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)
/* card detect polling timeout */
#define MXS_MMC_DETECT_TIMEOUT (HZ/2)
@@ -71,6 +79,10 @@ struct mxs_mmc_host {
spinlock_t lock;
int sdio_irq_en;
bool broken_cd;
+
+ u32 status;
+ int pio_size;
+ struct completion dma_done;
};
static int mxs_mmc_get_cd(struct mmc_host *mmc)
@@ -256,38 +268,81 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
return desc;
}
+/*
+ * Check for MMC command errors
+ * Returns error code or zero if no errors
+ */
+static inline int mxs_mmc_cmd_error(u32 status)
+{
+ int err = 0;
+
+ if (status & BM_SSP_STATUS_TIMEOUT)
+ err = -ETIMEDOUT;
+ else if (status & BM_SSP_STATUS_RESP_TIMEOUT)
+ err = -ETIMEDOUT;
+ else if (status & BM_SSP_STATUS_RESP_CRC_ERR)
+ err = -EILSEQ;
+ else if (status & BM_SSP_STATUS_RESP_ERR)
+ err = -EIO;
+
+ return err;
+}
+
static void mxs_mmc_bc(struct mxs_mmc_host *host)
{
struct mxs_ssp *ssp = &host->ssp;
struct mmc_command *cmd = host->cmd;
struct dma_async_tx_descriptor *desc;
- u32 ctrl0, cmd0, cmd1;
+ u32 ctrl0, ctrl1, cmd0, cmd1, c1;
ctrl0 = BM_SSP_CTRL0_ENABLE | BM_SSP_CTRL0_IGNORE_CRC;
+ ctrl1 = BM_SSP_CTRL1_DMA_ENABLE |
+ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_DATA_CRC_IRQ_EN |
+ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_ERR_IRQ_EN;
cmd0 = BF_SSP(cmd->opcode, CMD0_CMD) | BM_SSP_CMD0_APPEND_8CYC;
- cmd1 = cmd->arg;
+ cmd1 = BF_SSP(cmd->arg, CMD1_CMD);
if (host->sdio_irq_en) {
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
}
- ssp->ssp_pio_words[0] = ctrl0;
- ssp->ssp_pio_words[1] = cmd0;
- ssp->ssp_pio_words[2] = cmd1;
- ssp->dma_dir = DMA_NONE;
- ssp->slave_dirn = DMA_TRANS_NONE;
- desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
- if (!desc)
- goto out;
-
- dmaengine_submit(desc);
- dma_async_issue_pending(ssp->dmach);
- return;
-
-out:
- dev_warn(mmc_dev(host->mmc),
- "%s: failed to prep dma\n", __func__);
+ /* following IO operations */
+ writel(ctrl0, ssp->base + HW_SSP_CTRL0);
+ writel(cmd0, ssp->base + HW_SSP_CMD0);
+ writel(cmd1, ssp->base + HW_SSP_CMD1);
+
+ /* clear these bits */
+ writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
+ init_completion(&host->dma_done);
+ writel(BM_SSP_CTRL0_RUN,
+ ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+
+ /* wait here as long as SSP is running and busy */
+ while (readl(ssp->base + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN)
+ continue;
+ while (readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_BUSY)
+ continue;
+
+ host->status = readl(ssp->base + HW_SSP_STATUS(ssp));
+ c1 = readl(ssp->base + HW_SSP_CTRL1(ssp));
+
+ /* reset interrupt request status bits */
+ writel(c1 & MXS_MMC_ERR_IRQ_BITS,
+ ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
+
+ /* reenable these bits */
+ writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET);
+ /* end IO operations */
+
+ cmd->error = mxs_mmc_cmd_error(host->status);
+
+ if (cmd->error) {
+ dev_warn(mmc_dev(host->mmc), "BC command error %d\n", cmd->error);
+ }
}
static void mxs_mmc_ac(struct mxs_mmc_host *host)
@@ -296,7 +351,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
struct mmc_command *cmd = host->cmd;
struct dma_async_tx_descriptor *desc;
u32 ignore_crc, get_resp, long_resp;
- u32 ctrl0, cmd0, cmd1;
+ u32 ctrl0, ctrl1, cmd0, cmd1, c1;
ignore_crc = (mmc_resp_type(cmd) & MMC_RSP_CRC) ?
0 : BM_SSP_CTRL0_IGNORE_CRC;
@@ -306,30 +361,76 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
BM_SSP_CTRL0_LONG_RESP : 0;
ctrl0 = BM_SSP_CTRL0_ENABLE | ignore_crc | get_resp | long_resp;
+ ctrl1 = BM_SSP_CTRL1_DMA_ENABLE |
+ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_DATA_CRC_IRQ_EN |
+ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_ERR_IRQ_EN;
cmd0 = BF_SSP(cmd->opcode, CMD0_CMD);
- cmd1 = cmd->arg;
+ cmd1 = BF_SSP(cmd->arg, CMD1_CMD);
if (host->sdio_irq_en) {
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
}
- ssp->ssp_pio_words[0] = ctrl0;
- ssp->ssp_pio_words[1] = cmd0;
- ssp->ssp_pio_words[2] = cmd1;
- ssp->dma_dir = DMA_NONE;
- ssp->slave_dirn = DMA_TRANS_NONE;
- desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
- if (!desc)
- goto out;
-
- dmaengine_submit(desc);
- dma_async_issue_pending(ssp->dmach);
- return;
-
-out:
- dev_warn(mmc_dev(host->mmc),
- "%s: failed to prep dma\n", __func__);
+ /* following IO operations */
+ writel(ctrl0, ssp->base + HW_SSP_CTRL0);
+ writel(cmd0, ssp->base + HW_SSP_CMD0);
+ writel(cmd1, ssp->base + HW_SSP_CMD1);
+
+ /* clear these bits */
+ writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
+ init_completion(&host->dma_done);
+ writel(BM_SSP_CTRL0_RUN,
+ ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+
+ /* wait here as long as SSP is running and busy */
+ while (readl(ssp->base + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN)
+ continue;
+ while (readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_BUSY)
+ continue;
+
+ host->status = readl(ssp->base + HW_SSP_STATUS(ssp));
+ c1 = readl(ssp->base + HW_SSP_CTRL1(ssp));
+
+ /* reset interrupt request status bits */
+ writel(c1 & MXS_MMC_ERR_IRQ_BITS,
+ ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
+
+ /* reenable these bits */
+ writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET);
+ /* end IO operations */
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ while (readl(ssp->base + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN)
+ continue;
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ case MMC_RSP_R3:
+ cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
+ break;
+ case MMC_RSP_R2:
+ cmd->resp[3] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
+ cmd->resp[2] = readl(ssp->base + HW_SSP_SDRESP1(ssp));
+ cmd->resp[1] = readl(ssp->base + HW_SSP_SDRESP2(ssp));
+ cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP3(ssp));
+ break;
+ default:
+ dev_warn(mmc_dev(host->mmc), "Unsupported response type 0x%x\n",
+ mmc_resp_type(cmd));
+ BUG();
+ break;
+ }
+
+ cmd->error = mxs_mmc_cmd_error(host->status);
+
+ if (cmd->error) {
+ dev_warn(mmc_dev(host->mmc), "AC command error %d\n", cmd->error);
+ }
}
static unsigned short mxs_ns_to_ssp_ticks(unsigned clock_rate, unsigned ns)
@@ -357,6 +458,15 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
unsigned int sg_len = data->sg_len;
unsigned int i;
+ int is_reading = 0;
+ int index = 0;
+ int len;
+ struct scatterlist *_sg;
+ int size;
+ char *sgbuf;
+ u8 *p;
+ u32 _data, status;
+
unsigned short dma_data_dir, timeout;
enum dma_transfer_direction slave_dirn;
unsigned int data_size = 0, log2_blksz;
@@ -365,7 +475,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
struct mxs_ssp *ssp = &host->ssp;
u32 ignore_crc, get_resp, long_resp, read;
- u32 ctrl0, cmd0, cmd1, val;
+ u32 ctrl0, ctrl1, cmd0, cmd1, val, c1;
ignore_crc = (mmc_resp_type(cmd) & MMC_RSP_CRC) ?
0 : BM_SSP_CTRL0_IGNORE_CRC;
@@ -378,10 +488,12 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
dma_data_dir = DMA_TO_DEVICE;
slave_dirn = DMA_MEM_TO_DEV;
read = 0;
+ is_reading = 0;
} else {
dma_data_dir = DMA_FROM_DEVICE;
slave_dirn = DMA_DEV_TO_MEM;
read = BM_SSP_CTRL0_READ;
+ is_reading = 1;
}
ctrl0 = BF_SSP(host->bus_width, CTRL0_BUS_WIDTH) |
@@ -428,38 +540,129 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
}
- /* set the timeout count */
- timeout = mxs_ns_to_ssp_ticks(ssp->clk_rate, data->timeout_ns);
- val = readl(ssp->base + HW_SSP_TIMING(ssp));
- val &= ~(BM_SSP_TIMING_TIMEOUT);
- val |= BF_SSP(timeout, TIMING_TIMEOUT);
- writel(val, ssp->base + HW_SSP_TIMING(ssp));
-
- /* pio */
- ssp->ssp_pio_words[0] = ctrl0;
- ssp->ssp_pio_words[1] = cmd0;
- ssp->ssp_pio_words[2] = cmd1;
- ssp->dma_dir = DMA_NONE;
- ssp->slave_dirn = DMA_TRANS_NONE;
- desc = mxs_mmc_prep_dma(host, 0);
- if (!desc)
- goto out;
-
- /* append data sg */
- WARN_ON(host->data != NULL);
- host->data = data;
- ssp->dma_dir = dma_data_dir;
- ssp->slave_dirn = slave_dirn;
- desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc)
- goto out;
-
- dmaengine_submit(desc);
- dma_async_issue_pending(ssp->dmach);
- return;
-out:
- dev_warn(mmc_dev(host->mmc),
- "%s: failed to prep dma\n", __func__);
+ data_size = cmd->data->blksz * cmd->data->blocks;
+ u32 transfer_size = data_size;
+
+ _sg = host->cmd->data->sg;
+ len = host->cmd->data->sg_len;
+
+ ctrl1 = BM_SSP_CTRL1_DMA_ENABLE |
+ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_DATA_CRC_IRQ_EN |
+ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_ERR_IRQ_EN;
+
+ writel(ctrl0, ssp->base + HW_SSP_CTRL0);
+ writel(cmd0, ssp->base + HW_SSP_CMD0);
+ writel(cmd1, ssp->base + HW_SSP_CMD1);
+ /* clear these bits */
+ writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
+ init_completion(&host->dma_done);
+ writel(BM_SSP_CTRL0_RUN, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+
+ while (readl(ssp->base + HW_SSP_CTRL0) & BM_SSP_STATUS_CMD_BUSY)
+ continue;
+
+ while (transfer_size) {
+ sgbuf = kmap_atomic(sg_page(&_sg[index])) + _sg[index].offset;
+
+ p = (u8 *)sgbuf;
+ size = transfer_size < _sg[index].length ? transfer_size : _sg[index].length;
+
+ if (is_reading) {
+ while (size) {
+ status = readl(ssp->base + HW_SSP_STATUS(ssp));
+ if (status & BM_SSP_STATUS_FIFO_EMPTY)
+ continue;
+ _data = readl(ssp->base + HW_SSP_DATA(ssp));
+ if ((u32)p & 0x3) {
+ *p++ = _data & 0xff;
+ *p++ = (_data >> 8) & 0xff;
+ *p++ = (_data >> 16) & 0xff;
+ *p++ = (_data >> 24) & 0xff;
+ } else {
+ *(u32 *)p = _data;
+ p += 4;
+ }
+ transfer_size -= 4;
+ size -= 4;
+ }
+ } else {
+ while (size) {
+ status = readl(ssp->base + HW_SSP_STATUS(ssp));
+ if (status & BM_SSP_STATUS_FIFO_FULL)
+ continue;
+ if ((u32)p & 0x3)
+ _data = p[0] | \
+ (p[1] << 8) | \
+ (p[2] << 16) | \
+ (p[3] << 24);
+ else
+ _data = *(u32 *)p;
+
+ writel(_data, ssp->base + HW_SSP_DATA(ssp));
+ transfer_size -= 4;
+ size -= 4;
+ p += 4;
+ }
+ }
+ kunmap_atomic(sgbuf);
+ index++;
+ }
+
+ while (readl(ssp->base + HW_SSP_STATUS(ssp)) & (BM_SSP_STATUS_BUSY |
+ BM_SSP_STATUS_DATA_BUSY |
+ BM_SSP_STATUS_CMD_BUSY))
+ continue;
+
+ cmd->data->bytes_xfered = data_size;
+
+ host->status = readl(ssp->base + HW_SSP_STATUS(ssp));
+
+ c1 = readl(ssp->base + HW_SSP_CTRL1(ssp));
+
+ /* reset interrupt request status bits */
+ writel(c1 & MXS_MMC_ERR_IRQ_BITS,
+ ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
+
+ /* reenable these bits */
+ writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET);
+ /* end IO operations */
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R3:
+ cmd->resp[0] =
+ readl(ssp->base + HW_SSP_SDRESP0(ssp));
+ break;
+ case MMC_RSP_R2:
+ cmd->resp[3] =
+ readl(ssp->base + HW_SSP_SDRESP0(ssp));
+ cmd->resp[2] =
+ readl(ssp->base + HW_SSP_SDRESP1(ssp));
+ cmd->resp[1] =
+ readl(ssp->base + HW_SSP_SDRESP2(ssp));
+ cmd->resp[0] =
+ readl(ssp->base + HW_SSP_SDRESP3(ssp));
+ break;
+ default:
+ dev_warn(mmc_dev(host->mmc), "Unsupported response type 0x%x\n",
+ mmc_resp_type(cmd));
+ BUG();
+ break;
+ }
+
+ cmd->error = mxs_mmc_cmd_error(host->status);
+
+ if (cmd->error) {
+ dev_warn(mmc_dev(host->mmc), "ADTC command error %d\n", cmd->error);
+ } else {
+ dev_dbg(mmc_dev(host->mmc), "Transferred %u bytes\n",
+ cmd->data->bytes_xfered);
+ }
}
static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
@@ -489,11 +692,18 @@ static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
+ int done;
struct mxs_mmc_host *host = mmc_priv(mmc);
WARN_ON(host->mrq != NULL);
host->mrq = mrq;
mxs_mmc_start_cmd(host, mrq->cmd);
+
+ if (mrq->data && mrq->data->stop)
+ mxs_mmc_start_cmd(host, mrq->data->stop);
+
+ host->mrq = NULL;
+ mmc_request_done(mmc, mrq);
}
static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -603,6 +813,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
host->mmc = mmc;
host->sdio_irq_en = 0;
+ host->pio_size = 0;
reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc");
if (!IS_ERR(reg_vmmc)) {
@@ -53,6 +53,8 @@
#define BP_SSP_CMD0_CMD 0
#define BM_SSP_CMD0_CMD 0xff
#define HW_SSP_CMD1 0x020
+#define BP_SSP_CMD1_CMD 0
+#define BM_SSP_CMD1_CMD 0xFFFFFFFF
#define HW_SSP_XFER_SIZE 0x030
#define HW_SSP_BLOCK_SIZE 0x040
#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT 4
@@ -116,6 +118,16 @@
#define BM_SSP_STATUS_CARD_DETECT (1 << 28)
#define BM_SSP_STATUS_SDIO_IRQ (1 << 17)
#define BM_SSP_STATUS_FIFO_EMPTY (1 << 5)
+#define BM_SSP_STATUS_RESP_CRC_ERR 0x00010000
+#define BM_SSP_STATUS_RESP_ERR 0x00008000
+#define BM_SSP_STATUS_RESP_TIMEOUT 0x00004000
+#define BM_SSP_STATUS_DATA_CRC_ERR 0x00002000
+#define BM_SSP_STATUS_TIMEOUT 0x00001000
+#define BM_SSP_STATUS_FIFO_FULL 0x00000100
+#define BM_SSP_STATUS_CMD_BUSY 0x00000008
+#define BM_SSP_STATUS_DATA_BUSY 0x00000004
+#define BM_SSP_STATUS_RSVD0 0x00000002
+#define BM_SSP_STATUS_BUSY 0x00000001
#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field)
--
2.10.2