diff options
author | Andrey Panov <rockford@yandex.ru> | 2015-03-04 23:11:39 +0300 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2015-03-05 15:19:28 +0100 |
commit | 84fcb04367ccf9c89f442c8a5a716cceb34413e6 (patch) | |
tree | f2b16854c69f254855238156c89209d1c4b6b8f2 /drivers/mci/dw_mmc.c | |
parent | 63ea110f1f5314d3622e85b1948fd20c30303f95 (diff) | |
download | barebox-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.c | 203 |
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 */ } }; |