summaryrefslogtreecommitdiffstats
path: root/drivers/mci/dw_mmc.c
diff options
context:
space:
mode:
authorAndrey Panov <rockford@yandex.ru>2015-03-04 23:11:39 +0300
committerSascha Hauer <s.hauer@pengutronix.de>2015-03-05 15:19:28 +0100
commit84fcb04367ccf9c89f442c8a5a716cceb34413e6 (patch)
treef2b16854c69f254855238156c89209d1c4b6b8f2 /drivers/mci/dw_mmc.c
parent63ea110f1f5314d3622e85b1948fd20c30303f95 (diff)
downloadbarebox-84fcb04367ccf9c89f442c8a5a716cceb34413e6.tar.gz
barebox-84fcb04367ccf9c89f442c8a5a716cceb34413e6.tar.xz
MMC: dw_mmc: Add support for PIO mode and Rockchip variant of this hardware
Signed-off-by: Andrey Panov <rockford@yandex.ru> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/mci/dw_mmc.c')
-rw-r--r--drivers/mci/dw_mmc.c203
1 files changed, 188 insertions, 15 deletions
diff --git a/drivers/mci/dw_mmc.c b/drivers/mci/dw_mmc.c
index 365b60d6d4..076f99de0e 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 {
@@ -162,6 +166,11 @@ struct dwmci_idmac {
uint32_t next_addr;
};
+static inline int dwmci_use_pio(struct dwmci_host *host)
+{
+ return IS_ENABLED(CONFIG_MCI_DW_PIO);
+}
+
static inline void dwmci_writel(struct dwmci_host *host, int reg, uint32_t val)
{
writel(val, host->ioaddr + reg);
@@ -197,7 +206,33 @@ static int dwmci_wait_reset(struct dwmci_host *host, uint32_t value)
return -EIO;
}
-static int dwmci_prepare_data(struct dwmci_host *host,
+static int dwmci_prepare_data_pio(struct dwmci_host *host,
+ struct mci_data *data)
+{
+ unsigned long ctrl;
+
+ 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);
+
+ return 0;
+}
+
+static int dwmci_prepare_data_dma(struct dwmci_host *host,
struct mci_data *data)
{
unsigned long ctrl;
@@ -260,6 +295,15 @@ static int dwmci_prepare_data(struct dwmci_host *host,
return 0;
}
+static int dwmci_prepare_data(struct dwmci_host *host,
+ struct mci_data *data)
+{
+ if (dwmci_use_pio(host))
+ return dwmci_prepare_data_pio(host, data);
+ else
+ return dwmci_prepare_data_dma(host, data);
+}
+
static int dwmci_set_transfer_mode(struct dwmci_host *host,
struct mci_data *data)
{
@@ -272,12 +316,104 @@ static int dwmci_set_transfer_mode(struct dwmci_host *host,
return mode;
}
+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;
+}
+
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;
+ uint32_t ctrl;
uint64_t start;
int ret;
unsigned int num_bytes = 0;
@@ -347,8 +483,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,23 +512,40 @@ 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);
- if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) {
+
+ if (mask & (DWMCI_DATA_ERR)) {
dev_dbg(host->dev, "DATA ERROR!\n");
return -EIO;
}
- if (is_timeout(start, SECOND))
+
+ if (!dwmci_use_pio(host) && (mask & DWMCI_DATA_TOUT)) {
+ dev_dbg(host->dev, "DATA TIMEOUT!\n");
+ return -EIO;
+ }
+
+ if (is_timeout(start, SECOND * 10)) {
+ dev_dbg(host->dev, "Data timeout\n");
return -ETIMEDOUT;
+ }
+
+ if (dwmci_use_pio(host) && (mask & DWMCI_INTMSK_RXDR))
+ dwmci_read_data_pio(host, data);
+ if (dwmci_use_pio(host) && (mask & DWMCI_INTMSK_TXDR))
+ dwmci_write_data_pio(host, data);
} while (!(mask & DWMCI_INTMSK_DTO));
dwmci_writel(host, DWMCI_RINTSTS, mask);
- ctrl = dwmci_readl(host, DWMCI_CTRL);
- ctrl &= ~(DWMCI_DMA_EN);
- dwmci_writel(host, DWMCI_CTRL, ctrl);
+ if (!dwmci_use_pio(host)) {
+ ctrl = dwmci_readl(host, DWMCI_CTRL);
+ ctrl &= ~(DWMCI_DMA_EN);
+ dwmci_writel(host, DWMCI_CTRL, ctrl);
- if (data->flags & MMC_DATA_READ) {
- dma_inv_range((unsigned long)data->dest,
- (unsigned long)(data->dest + data->blocks * 512));
+ if (data->flags & MMC_DATA_READ) {
+ dma_inv_range((unsigned long)data->dest,
+ (unsigned long)(data->dest +
+ data->blocks * 512));
+ }
}
}
@@ -480,9 +635,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 +661,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 +739,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 +764,8 @@ static __maybe_unused struct of_device_id dw_mmc_compatible[] = {
{
.compatible = "altr,socfpga-dw-mshc",
}, {
+ .compatible = "rockchip,rk2928-dw-mshc",
+ }, {
/* sentinel */
}
};