[PATCH v2 11/18] MMC: dw_mmc: Add support for PIO mode and Rockchip variant of this hardware
Andrey Panov
rockford at yandex.ru
Wed Mar 4 12:11:39 PST 2015
Signed-off-by: Andrey Panov <rockford at yandex.ru>
---
drivers/mci/Kconfig | 6 ++
drivers/mci/dw_mmc.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 173 insertions(+), 7 deletions(-)
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index dbcc38d..17bf0d3 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -42,6 +42,12 @@ config MCI_DW
block, this provides host support for SD and MMC interfaces, in both
PIO and external DMA modes.
+config MCI_DW_PIO
+ bool "Use PIO mode on Synopsys DesignWare MCI"
+ depends on MCI_DW
+ help
+ Use PIO mode (instead of IDMAC) in DW MMC driver.
+
config MCI_MXS
bool "i.MX23/i.MX28"
depends on ARCH_MXS
diff --git a/drivers/mci/dw_mmc.c b/drivers/mci/dw_mmc.c
index 365b60d..1232951 100644
--- a/drivers/mci/dw_mmc.c
+++ b/drivers/mci/dw_mmc.c
@@ -125,6 +125,8 @@
#define DWMCI_CTYPE_8BIT (1 << 16)
/* Status Register */
+#define DWMCI_STATUS_FIFO_EMPTY (1 << 2)
+#define DWMCI_STATUS_FIFO_FULL (1 << 3)
#define DWMCI_STATUS_BUSY (1 << 9)
/* FIFOTH Register */
@@ -153,6 +155,8 @@ struct dwmci_host {
struct dwmci_idmac *idmac;
unsigned long clkrate;
int ciu_div;
+ u32 fifoth_val;
+ u32 pwren_value;
};
struct dwmci_idmac {
@@ -201,6 +205,8 @@ static int dwmci_prepare_data(struct dwmci_host *host,
struct mci_data *data)
{
unsigned long ctrl;
+
+#ifndef CONFIG_MCI_DW_PIO
unsigned int i = 0, flags, cnt, blk_cnt;
unsigned long data_start, start_addr;
struct dwmci_idmac *desc = host->idmac;
@@ -256,7 +262,26 @@ static int dwmci_prepare_data(struct dwmci_host *host,
dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize);
dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks);
+#else
+ /* PIO MODE */
+ dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
+ dwmci_writel(host, DWMCI_RINTSTS,
+ DWMCI_INTMSK_TXDR | DWMCI_INTMSK_RXDR);
+
+ ctrl = dwmci_readl(host, DWMCI_INTMASK);
+ ctrl |= DWMCI_INTMSK_TXDR | DWMCI_INTMSK_RXDR;
+ dwmci_writel(host, DWMCI_INTMASK, ctrl);
+
+ ctrl = dwmci_readl(host, DWMCI_CTRL);
+ ctrl &= ~(DWMCI_IDMAC_EN | DWMCI_DMA_EN);
+ dwmci_writel(host, DWMCI_CTRL, ctrl);
+ dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
+
+ dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF);
+ dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize);
+ dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * data->blocks);
+#endif
return 0;
}
@@ -272,12 +297,108 @@ static int dwmci_set_transfer_mode(struct dwmci_host *host,
return mode;
}
+#ifdef CONFIG_MCI_DW_PIO
+static int dwmci_read_data_pio(struct dwmci_host *host, struct mci_data *data)
+{
+ u32 *pdata = (u32 *)data->dest;
+ u32 val, status, timeout;
+ u32 fcnt, bcnt, rcnt, rlen = 0;
+
+ timeout = 100;
+ status = dwmci_readl(host, DWMCI_RINTSTS);
+ while (--timeout && !(status & DWMCI_INTMSK_RXDR))
+ status = dwmci_readl(host, DWMCI_RINTSTS);
+
+ if (!timeout) {
+ dev_err(host->dev, "%s: RX ready wait timeout\n", __func__);
+ return 0;
+ }
+
+ fcnt = data->blocksize;
+ bcnt = data->blocks;
+
+ do {
+ for (rcnt = fcnt>>2; rcnt; rcnt--) {
+ timeout = 20000;
+ status = dwmci_readl(host, DWMCI_STATUS);
+ while (--timeout
+ && (status & DWMCI_STATUS_FIFO_EMPTY)) {
+ udelay(200);
+ status = dwmci_readl(host, DWMCI_STATUS);
+ }
+ if (!timeout) {
+ dev_err(host->dev, "%s: FIFO underflow timeout\n",
+ __func__);
+ break;
+ }
+
+ val = dwmci_readl(host, DWMCI_DATA);
+
+ *pdata++ = val;
+ rlen += 4;
+ }
+ status = dwmci_readl(host, DWMCI_RINTSTS);
+ dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_RXDR);
+ } while (--bcnt && (status & DWMCI_INTMSK_RXDR));
+
+ return rlen;
+}
+
+static int dwmci_write_data_pio(struct dwmci_host *host, struct mci_data *data)
+{
+ u32 *pdata = (u32 *)data->src;
+ u32 status, timeout;
+ u32 fcnt, bcnt, wcnt, wlen = 0;
+
+ fcnt = host->fifo_size_bytes;
+
+ bcnt = (data->blocks*data->blocksize)/fcnt;
+
+ timeout = 100;
+ status = dwmci_readl(host, DWMCI_RINTSTS);
+
+ while (--timeout && !(status & DWMCI_INTMSK_TXDR))
+ status = dwmci_readl(host, DWMCI_RINTSTS);
+
+ if (!timeout) {
+ dev_err(host->dev, "%s: TX ready wait timeout\n", __func__);
+ return 0;
+ }
+
+ do {
+ for (wcnt = fcnt>>2; wcnt; wcnt--) {
+ timeout = 20000;
+ status = dwmci_readl(host, DWMCI_STATUS);
+ while (--timeout
+ && (status & DWMCI_STATUS_FIFO_FULL)) {
+ udelay(200);
+ status = dwmci_readl(host, DWMCI_STATUS);
+ }
+ if (!timeout) {
+ dev_err(host->dev, "%s: FIFO overflow timeout\n",
+ __func__);
+ break;
+ }
+ dwmci_writel(host, DWMCI_DATA, *pdata++);
+ wlen += 4;
+ }
+ status = dwmci_readl(host, DWMCI_RINTSTS);
+ dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_TXDR);
+ } while (--bcnt && (status & DWMCI_INTMSK_TXDR));
+
+ return wlen;
+}
+#endif
+
static int
dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
{
struct dwmci_host *host = to_dwmci_host(mci);
int flags = 0;
- uint32_t mask, ctrl;
+ uint32_t mask;
+#ifndef CONFIG_MCI_DW_PIO
+ uint32_t ctrl;
+#endif
uint64_t start;
int ret;
unsigned int num_bytes = 0;
@@ -347,8 +468,10 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
dwmci_writel(host, DWMCI_RINTSTS, mask);
break;
}
- if (is_timeout(start, 100 * MSECOND))
+ if (is_timeout(start, 100 * MSECOND)) {
+ dev_dbg(host->dev, "Send command timeout..\n");
return -ETIMEDOUT;
+ }
}
if (mask & DWMCI_INTMSK_RTO) {
@@ -374,16 +497,34 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
start = get_time_ns();
do {
mask = dwmci_readl(host, DWMCI_RINTSTS);
+#ifdef CONFIG_MCI_DW_PIO
+ if (mask & (DWMCI_DATA_ERR)) {
+#else
if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) {
+#endif
dev_dbg(host->dev, "DATA ERROR!\n");
return -EIO;
}
- if (is_timeout(start, SECOND))
+#ifndef CONFIG_MCI_DW_PIO
+ if (is_timeout(start, SECOND)) {
+#else
+ if (is_timeout(start, SECOND*100)) {
+ /* In PIO end transfer can take more than a second */
+#endif
+ dev_dbg(host->dev, "Data timeout\n");
return -ETIMEDOUT;
+ }
+#ifdef CONFIG_MCI_DW_PIO
+ if (mask & DWMCI_INTMSK_RXDR)
+ dwmci_read_data_pio(host, data);
+ if (mask & DWMCI_INTMSK_TXDR)
+ dwmci_write_data_pio(host, data);
+#endif
} while (!(mask & DWMCI_INTMSK_DTO));
dwmci_writel(host, DWMCI_RINTSTS, mask);
+#ifndef CONFIG_MCI_DW_PIO
ctrl = dwmci_readl(host, DWMCI_CTRL);
ctrl &= ~(DWMCI_DMA_EN);
dwmci_writel(host, DWMCI_CTRL, ctrl);
@@ -392,6 +533,7 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
dma_inv_range((unsigned long)data->dest,
(unsigned long)(data->dest + data->blocks * 512));
}
+#endif
}
udelay(100);
@@ -480,9 +622,9 @@ static int dwmci_card_present(struct mci_host *mci)
static int dwmci_init(struct mci_host *mci, struct device_d *dev)
{
struct dwmci_host *host = to_dwmci_host(mci);
- uint32_t fifo_size, fifoth_val;
+ uint32_t fifo_size;
- dwmci_writel(host, DWMCI_PWREN, 1);
+ dwmci_writel(host, DWMCI_PWREN, host->pwren_value);
if (dwmci_wait_reset(host, DWMCI_RESET_ALL)) {
dev_err(host->dev, "reset failed\n");
@@ -506,11 +648,21 @@ static int dwmci_init(struct mci_host *mci, struct device_d *dev)
fifo_size = DWMCI_FIFOTH_FIFO_DEPTH(fifo_size);
host->fifo_size_bytes = fifo_size * 4;
- fifoth_val = DWMCI_FIFOTH_MSIZE(0x2) |
+ /*
+ * If fifo-depth property is set, use this value
+ */
+ if (!of_property_read_u32(host->dev->device_node,
+ "fifo-depth", &fifo_size)) {
+ host->fifo_size_bytes = fifo_size;
+ dev_dbg(host->dev, "Using fifo-depth=%u\n",
+ host->fifo_size_bytes);
+ }
+
+ host->fifoth_val = DWMCI_FIFOTH_MSIZE(0x2) |
DWMCI_FIFOTH_RX_WMARK(fifo_size / 2 - 1) |
DWMCI_FIFOTH_TX_WMARK(fifo_size / 2);
- dwmci_writel(host, DWMCI_FIFOTH, fifoth_val);
+ dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
dwmci_writel(host, DWMCI_CLKENA, 0);
dwmci_writel(host, DWMCI_CLKSRC, 0);
@@ -574,6 +726,12 @@ static int dw_mmc_probe(struct device_d *dev)
host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
host->mci.host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
+ if (of_device_is_compatible(dev->device_node,
+ "rockchip,rk2928-dw-mshc"))
+ host->pwren_value = 0;
+ else
+ host->pwren_value = 1;
+
dev->detect = dw_mmc_detect;
host->clkrate = clk_get_rate(host->clk_ciu);
@@ -593,6 +751,8 @@ static __maybe_unused struct of_device_id dw_mmc_compatible[] = {
{
.compatible = "altr,socfpga-dw-mshc",
}, {
+ .compatible = "rockchip,rk2928-dw-mshc",
+ }, {
/* sentinel */
}
};
--
2.1.4
More information about the barebox
mailing list