summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/Makefile1
-rw-r--r--drivers/mmc/ca_dw_mmc.c2
-rw-r--r--drivers/mmc/dw_mmc.c547
-rw-r--r--drivers/mmc/exynos_dw_mmc.c357
-rw-r--r--drivers/mmc/ftsdc010_mci.h1
-rw-r--r--drivers/mmc/hi6220_dw_mmc.c7
-rw-r--r--drivers/mmc/mmc.c87
-rw-r--r--drivers/mmc/msm_sdhci.c11
-rw-r--r--drivers/mmc/nexell_dw_mmc.c5
-rw-r--r--drivers/mmc/rockchip_dw_mmc.c5
-rw-r--r--drivers/mmc/s5p_sdhci.c2
-rw-r--r--drivers/mmc/sdhci-cadence.c63
-rw-r--r--drivers/mmc/sdhci-cadence.h69
-rw-r--r--drivers/mmc/sdhci-cadence6.c293
-rw-r--r--drivers/mmc/sdhci.c52
-rw-r--r--drivers/mmc/snps_dw_mmc.c15
-rw-r--r--drivers/mmc/socfpga_dw_mmc.c4
17 files changed, 1087 insertions, 434 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 72c3fb66ce0..235c477c2e0 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_MMC_SDHCI_ATMEL) += atmel_sdhci.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += bcm2835_sdhci.o
obj-$(CONFIG_MMC_SDHCI_BCMSTB) += bcmstb_sdhci.o
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
+obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence6.o
obj-$(CONFIG_MMC_SDHCI_CV1800B) += cv1800b_sdhci.o
obj-$(CONFIG_MMC_SDHCI_AM654) += am654_sdhci.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += iproc_sdhci.o
diff --git a/drivers/mmc/ca_dw_mmc.c b/drivers/mmc/ca_dw_mmc.c
index 54a2ba4795e..1af5ec0532e 100644
--- a/drivers/mmc/ca_dw_mmc.c
+++ b/drivers/mmc/ca_dw_mmc.c
@@ -86,7 +86,7 @@ unsigned int ca_dwmci_get_mmc_clock(struct dwmci_host *host, uint freq)
clk_div = 1;
}
- return SD_SCLK_MAX / clk_div / (host->div + 1);
+ return SD_SCLK_MAX / clk_div;
}
static int ca_dwmmc_of_to_plat(struct udevice *dev)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index f4ecd7422ce..8551eac7018 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -20,6 +20,47 @@
#define PAGE_SIZE 4096
+/* Internal DMA Controller (IDMAC) descriptor for 32-bit addressing mode */
+struct dwmci_idmac32 {
+ u32 des0; /* Control descriptor */
+ u32 des1; /* Buffer size */
+ u32 des2; /* Buffer physical address */
+ u32 des3; /* Next descriptor physical address */
+} __aligned(ARCH_DMA_MINALIGN);
+
+/* Internal DMA Controller (IDMAC) descriptor for 64-bit addressing mode */
+struct dwmci_idmac64 {
+ u32 des0; /* Control descriptor */
+ u32 des1; /* Reserved */
+ u32 des2; /* Buffer sizes */
+ u32 des3; /* Reserved */
+ u32 des4; /* Lower 32-bits of Buffer Address Pointer 1 */
+ u32 des5; /* Upper 32-bits of Buffer Address Pointer 1 */
+ u32 des6; /* Lower 32-bits of Next Descriptor Address */
+ u32 des7; /* Upper 32-bits of Next Descriptor Address */
+} __aligned(ARCH_DMA_MINALIGN);
+
+/* Register offsets for DW MMC blocks with 32-bit IDMAC */
+static const struct dwmci_idmac_regs dwmci_idmac_regs32 = {
+ .dbaddrl = DWMCI_DBADDR,
+ .idsts = DWMCI_IDSTS,
+ .idinten = DWMCI_IDINTEN,
+ .dscaddrl = DWMCI_DSCADDR,
+ .bufaddrl = DWMCI_BUFADDR,
+};
+
+/* Register offsets for DW MMC blocks with 64-bit IDMAC */
+static const struct dwmci_idmac_regs dwmci_idmac_regs64 = {
+ .dbaddrl = DWMCI_DBADDRL,
+ .dbaddru = DWMCI_DBADDRU,
+ .idsts = DWMCI_IDSTS64,
+ .idinten = DWMCI_IDINTEN64,
+ .dscaddrl = DWMCI_DSCADDRL,
+ .dscaddru = DWMCI_DSCADDRU,
+ .bufaddrl = DWMCI_BUFADDRL,
+ .bufaddru = DWMCI_BUFADDRU,
+};
+
static int dwmci_wait_reset(struct dwmci_host *host, u32 value)
{
unsigned long timeout = 1000;
@@ -35,57 +76,98 @@ static int dwmci_wait_reset(struct dwmci_host *host, u32 value)
return 0;
}
-static void dwmci_set_idma_desc(struct dwmci_idmac *idmac,
- u32 desc0, u32 desc1, u32 desc2)
+static void dwmci_set_idma_desc32(struct dwmci_idmac32 *desc, u32 control,
+ u32 buf_size, u32 buf_addr)
{
- struct dwmci_idmac *desc = idmac;
+ phys_addr_t desc_phys = virt_to_phys(desc);
+ u32 next_desc_phys = desc_phys + sizeof(struct dwmci_idmac32);
- desc->flags = desc0;
- desc->cnt = desc1;
- desc->addr = desc2;
- desc->next_addr = (ulong)desc + sizeof(struct dwmci_idmac);
+ desc->des0 = control;
+ desc->des1 = buf_size;
+ desc->des2 = buf_addr;
+ desc->des3 = next_desc_phys;
}
-static void dwmci_prepare_data(struct dwmci_host *host,
- struct mmc_data *data,
- struct dwmci_idmac *cur_idmac,
- void *bounce_buffer)
+static void dwmci_set_idma_desc64(struct dwmci_idmac64 *desc, u32 control,
+ u32 buf_size, u64 buf_addr)
{
- unsigned long ctrl;
- unsigned int i = 0, flags, cnt, blk_cnt;
+ phys_addr_t desc_phys = virt_to_phys(desc);
+ u64 next_desc_phys = desc_phys + sizeof(struct dwmci_idmac64);
+
+ desc->des0 = control;
+ desc->des1 = 0;
+ desc->des2 = buf_size;
+ desc->des3 = 0;
+ desc->des4 = buf_addr & 0xffffffff;
+ desc->des5 = buf_addr >> 32;
+ desc->des6 = next_desc_phys & 0xffffffff;
+ desc->des7 = next_desc_phys >> 32;
+}
+
+static void dwmci_prepare_desc(struct dwmci_host *host, struct mmc_data *data,
+ void *cur_idmac, void *bounce_buffer)
+{
+ struct dwmci_idmac32 *desc32 = cur_idmac;
+ struct dwmci_idmac64 *desc64 = cur_idmac;
ulong data_start, data_end;
+ unsigned int blk_cnt, i;
+ data_start = (ulong)cur_idmac;
blk_cnt = data->blocks;
- dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
-
- /* Clear IDMAC interrupt */
- dwmci_writel(host, DWMCI_IDSTS, 0xFFFFFFFF);
+ for (i = 0;; i++) {
+ phys_addr_t buf_phys = virt_to_phys(bounce_buffer);
+ unsigned int flags, cnt;
- data_start = (ulong)cur_idmac;
- dwmci_writel(host, DWMCI_DBADDR, (ulong)cur_idmac);
-
- do {
- flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH ;
- flags |= (i == 0) ? DWMCI_IDMAC_FS : 0;
+ flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH;
+ if (i == 0)
+ flags |= DWMCI_IDMAC_FS;
if (blk_cnt <= 8) {
flags |= DWMCI_IDMAC_LD;
cnt = data->blocksize * blk_cnt;
- } else
+ } else {
cnt = data->blocksize * 8;
+ }
- dwmci_set_idma_desc(cur_idmac, flags, cnt,
- (ulong)bounce_buffer + (i * PAGE_SIZE));
+ if (host->dma_64bit_address) {
+ dwmci_set_idma_desc64(desc64, flags, cnt,
+ buf_phys + i * PAGE_SIZE);
+ desc64++;
+ } else {
+ dwmci_set_idma_desc32(desc32, flags, cnt,
+ buf_phys + i * PAGE_SIZE);
+ desc32++;
+ }
- cur_idmac++;
if (blk_cnt <= 8)
break;
blk_cnt -= 8;
- i++;
- } while(1);
+ }
- data_end = (ulong)cur_idmac;
+ if (host->dma_64bit_address)
+ data_end = (ulong)desc64;
+ else
+ data_end = (ulong)desc32;
flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN));
+}
+
+static void dwmci_prepare_data(struct dwmci_host *host, struct mmc_data *data,
+ void *cur_idmac, void *bounce_buffer)
+{
+ const u32 idmacl = virt_to_phys(cur_idmac) & 0xffffffff;
+ const u32 idmacu = (u64)virt_to_phys(cur_idmac) >> 32;
+ unsigned long ctrl;
+
+ dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
+
+ /* Clear IDMAC interrupt */
+ dwmci_writel(host, host->regs->idsts, 0xffffffff);
+
+ dwmci_writel(host, host->regs->dbaddrl, idmacl);
+ if (host->dma_64bit_address)
+ dwmci_writel(host, host->regs->dbaddru, idmacu);
+
+ dwmci_prepare_desc(host, data, cur_idmac, bounce_buffer);
ctrl = dwmci_readl(host, DWMCI_CTRL);
ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN;
@@ -132,90 +214,86 @@ static unsigned int dwmci_get_timeout(struct mmc *mmc, const unsigned int size)
return timeout;
}
-static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
+static int dwmci_data_transfer_fifo(struct dwmci_host *host,
+ struct mmc_data *data, u32 mask)
{
- struct mmc *mmc = host->mmc;
+ const u32 int_rx = mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO);
+ const u32 int_tx = mask & DWMCI_INTMSK_TXDR;
int ret = 0;
- u32 timeout, mask, size, i, len = 0;
- u32 *buf = NULL;
- ulong start = get_timer(0);
- u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >>
- RX_WMARK_SHIFT) + 1) * 2;
+ u32 len = 0, size, i;
+ u32 *buf;
+
+ size = (data->blocksize * data->blocks) / 4;
+ if (!host->fifo_mode || !size)
+ return 0;
- size = data->blocksize * data->blocks;
if (data->flags == MMC_DATA_READ)
buf = (unsigned int *)data->dest;
else
buf = (unsigned int *)data->src;
- timeout = dwmci_get_timeout(mmc, size);
+ if (data->flags == MMC_DATA_READ && int_rx) {
+ dwmci_writel(host, DWMCI_RINTSTS, int_rx);
+ while (size) {
+ ret = dwmci_fifo_ready(host, DWMCI_FIFO_EMPTY, &len);
+ if (ret < 0)
+ break;
+
+ len = (len >> DWMCI_FIFO_SHIFT) & DWMCI_FIFO_MASK;
+ len = min(size, len);
+ for (i = 0; i < len; i++)
+ *buf++ = dwmci_readl(host, DWMCI_DATA);
+ size = size > len ? (size - len) : 0;
+ }
+ } else if (data->flags == MMC_DATA_WRITE && int_tx) {
+ while (size) {
+ ret = dwmci_fifo_ready(host, DWMCI_FIFO_FULL, &len);
+ if (ret < 0)
+ break;
+
+ len = host->fifo_depth - ((len >> DWMCI_FIFO_SHIFT) &
+ DWMCI_FIFO_MASK);
+ len = min(size, len);
+ for (i = 0; i < len; i++)
+ dwmci_writel(host, DWMCI_DATA, *buf++);
+ size = size > len ? (size - len) : 0;
+ }
+ dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_TXDR);
+ }
- size /= 4;
+ return ret;
+}
+
+static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
+{
+ struct mmc *mmc = host->mmc;
+ int ret = 0;
+ u32 timeout, mask, size;
+ ulong start = get_timer(0);
+
+ size = data->blocksize * data->blocks;
+ timeout = dwmci_get_timeout(mmc, size);
for (;;) {
mask = dwmci_readl(host, DWMCI_RINTSTS);
- /* Error during data transfer. */
+ /* Error during data transfer */
if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) {
debug("%s: DATA ERROR!\n", __func__);
ret = -EINVAL;
break;
}
- if (host->fifo_mode && size) {
- len = 0;
- if (data->flags == MMC_DATA_READ &&
- (mask & (DWMCI_INTMSK_RXDR | DWMCI_INTMSK_DTO))) {
- dwmci_writel(host, DWMCI_RINTSTS,
- mask & (DWMCI_INTMSK_RXDR |
- DWMCI_INTMSK_DTO));
- while (size) {
- ret = dwmci_fifo_ready(host,
- DWMCI_FIFO_EMPTY,
- &len);
- if (ret < 0)
- break;
-
- len = (len >> DWMCI_FIFO_SHIFT) &
- DWMCI_FIFO_MASK;
- len = min(size, len);
- for (i = 0; i < len; i++)
- *buf++ =
- dwmci_readl(host, DWMCI_DATA);
- size = size > len ? (size - len) : 0;
- }
- } else if (data->flags == MMC_DATA_WRITE &&
- (mask & DWMCI_INTMSK_TXDR)) {
- while (size) {
- ret = dwmci_fifo_ready(host,
- DWMCI_FIFO_FULL,
- &len);
- if (ret < 0)
- break;
-
- len = fifo_depth - ((len >>
- DWMCI_FIFO_SHIFT) &
- DWMCI_FIFO_MASK);
- len = min(size, len);
- for (i = 0; i < len; i++)
- dwmci_writel(host, DWMCI_DATA,
- *buf++);
- size = size > len ? (size - len) : 0;
- }
- dwmci_writel(host, DWMCI_RINTSTS,
- DWMCI_INTMSK_TXDR);
- }
- }
+ ret = dwmci_data_transfer_fifo(host, data, mask);
- /* Data arrived correctly. */
+ /* Data arrived correctly */
if (mask & DWMCI_INTMSK_DTO) {
ret = 0;
break;
}
- /* Check for timeout. */
+ /* Check for timeout */
if (get_timer(start) > timeout) {
- debug("%s: Timeout waiting for data!\n",
- __func__);
+ debug("%s: Timeout waiting for data!\n", __func__);
ret = -ETIMEDOUT;
break;
}
@@ -226,8 +304,35 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data)
return ret;
}
+static int dwmci_dma_transfer(struct dwmci_host *host, uint flags,
+ struct bounce_buffer *bbstate)
+{
+ int ret;
+ u32 mask, ctrl;
+
+ if (flags == MMC_DATA_READ)
+ mask = DWMCI_IDINTEN_RI;
+ else
+ mask = DWMCI_IDINTEN_TI;
+
+ ret = wait_for_bit_le32(host->ioaddr + host->regs->idsts, mask, true,
+ 1000, false);
+ if (ret)
+ debug("%s: DWMCI_IDINTEN mask 0x%x timeout\n", __func__, mask);
+
+ /* Clear interrupts */
+ dwmci_writel(host, host->regs->idsts, DWMCI_IDINTEN_MASK);
+
+ ctrl = dwmci_readl(host, DWMCI_CTRL);
+ ctrl &= ~DWMCI_DMA_EN;
+ dwmci_writel(host, DWMCI_CTRL, ctrl);
+
+ bounce_buffer_stop(bbstate);
+ return ret;
+}
+
static int dwmci_set_transfer_mode(struct dwmci_host *host,
- struct mmc_data *data)
+ struct mmc_data *data)
{
unsigned long mode;
@@ -238,33 +343,30 @@ static int dwmci_set_transfer_mode(struct dwmci_host *host,
return mode;
}
-#ifdef CONFIG_DM_MMC
-static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
- struct mmc_data *data)
+static void dwmci_wait_while_busy(struct dwmci_host *host, struct mmc_cmd *cmd)
{
- struct mmc *mmc = mmc_get_mmc_dev(dev);
-#else
-static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
- struct mmc_data *data)
-{
-#endif
- struct dwmci_host *host = mmc->priv;
- ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac,
- data ? DIV_ROUND_UP(data->blocks, 8) : 0);
- int ret = 0, flags = 0, i;
- unsigned int timeout = 500;
- u32 retry = 100000;
- u32 mask, ctrl;
- ulong start = get_timer(0);
- struct bounce_buffer bbstate;
+ unsigned int timeout = 500; /* msec */
+ ulong start;
+ start = get_timer(0);
while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) {
if (get_timer(start) > timeout) {
- debug("%s: Timeout on data busy, continue anyway\n", __func__);
+ debug("%s: Timeout on data busy, continue anyway\n",
+ __func__);
break;
}
}
+}
+static int dwmci_send_cmd_common(struct dwmci_host *host, struct mmc_cmd *cmd,
+ struct mmc_data *data, void *cur_idmac)
+{
+ int ret, flags = 0, i;
+ u32 retry = 100000;
+ u32 mask;
+ struct bounce_buffer bbstate;
+
+ dwmci_wait_while_busy(host, cmd);
dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);
if (data) {
@@ -276,12 +378,12 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
} else {
if (data->flags == MMC_DATA_READ) {
ret = bounce_buffer_start(&bbstate,
- (void*)data->dest,
+ (void *)data->dest,
data->blocksize *
data->blocks, GEN_BB_WRITE);
} else {
ret = bounce_buffer_start(&bbstate,
- (void*)data->src,
+ (void *)data->src,
data->blocksize *
data->blocks, GEN_BB_READ);
}
@@ -316,9 +418,9 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
if (cmd->resp_type & MMC_RSP_CRC)
flags |= DWMCI_CMD_CHECK_CRC;
- flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG);
+ flags |= cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG;
- debug("Sending CMD%d\n",cmd->cmdidx);
+ debug("Sending CMD%d\n", cmd->cmdidx);
dwmci_writel(host, DWMCI_CMD, flags);
@@ -332,7 +434,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
}
if (i == retry) {
- debug("%s: Timeout.\n", __func__);
+ debug("%s: Timeout\n", __func__);
return -ETIMEDOUT;
}
@@ -345,14 +447,14 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
* below shall be debug(). eMMC cards also do not favor
* CMD8, please keep that in mind.
*/
- debug("%s: Response Timeout.\n", __func__);
+ debug("%s: Response Timeout\n", __func__);
return -ETIMEDOUT;
} else if (mask & DWMCI_INTMSK_RE) {
- debug("%s: Response Error.\n", __func__);
+ debug("%s: Response Error\n", __func__);
return -EIO;
} else if ((cmd->resp_type & MMC_RSP_CRC) &&
(mask & DWMCI_INTMSK_RCRC)) {
- debug("%s: Response CRC Error.\n", __func__);
+ debug("%s: Response CRC Error\n", __func__);
return -EIO;
}
@@ -369,26 +471,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
if (data) {
ret = dwmci_data_transfer(host, data);
-
- /* only dma mode need it */
- if (!host->fifo_mode) {
- if (data->flags == MMC_DATA_READ)
- mask = DWMCI_IDINTEN_RI;
- else
- mask = DWMCI_IDINTEN_TI;
- ret = wait_for_bit_le32(host->ioaddr + DWMCI_IDSTS,
- mask, true, 1000, false);
- if (ret)
- debug("%s: DWMCI_IDINTEN mask 0x%x timeout.\n",
- __func__, mask);
- /* clear interrupts */
- dwmci_writel(host, DWMCI_IDSTS, DWMCI_IDINTEN_MASK);
-
- ctrl = dwmci_readl(host, DWMCI_CTRL);
- ctrl &= ~(DWMCI_DMA_EN);
- dwmci_writel(host, DWMCI_CTRL, ctrl);
- bounce_buffer_stop(&bbstate);
- }
+ if (!host->fifo_mode)
+ ret = dwmci_dma_transfer(host, data->flags, &bbstate);
}
udelay(100);
@@ -396,40 +480,39 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
return ret;
}
-static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
+#ifdef CONFIG_DM_MMC
+static int dwmci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+ struct mmc_data *data)
{
- u32 div, status;
- int timeout = 10000;
- unsigned long sclk;
+ struct mmc *mmc = mmc_get_mmc_dev(dev);
+#else
+static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+#endif
+ struct dwmci_host *host = mmc->priv;
+ const size_t buf_size = data ? DIV_ROUND_UP(data->blocks, 8) : 0;
- if ((freq == host->clock) || (freq == 0))
- return 0;
- /*
- * If host->get_mmc_clk isn't defined,
- * then assume that host->bus_hz is source clock value.
- * host->bus_hz should be set by user.
- */
- if (host->get_mmc_clk)
- sclk = host->get_mmc_clk(host, freq);
- else if (host->bus_hz)
- sclk = host->bus_hz;
- else {
- debug("%s: Didn't get source clock value.\n", __func__);
- return -EINVAL;
+ if (host->dma_64bit_address) {
+ ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac64, idmac, buf_size);
+ return dwmci_send_cmd_common(host, cmd, data, idmac);
+ } else {
+ ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac32, idmac, buf_size);
+ return dwmci_send_cmd_common(host, cmd, data, idmac);
}
+}
- if (sclk == freq)
- div = 0; /* bypass mode */
- else
- div = DIV_ROUND_UP(sclk, 2 * freq);
-
- dwmci_writel(host, DWMCI_CLKENA, 0);
- dwmci_writel(host, DWMCI_CLKSRC, 0);
+static int dwmci_control_clken(struct dwmci_host *host, bool on)
+{
+ const u32 val = on ? DWMCI_CLKEN_ENABLE | DWMCI_CLKEN_LOW_PWR : 0;
+ const u32 cmd_only_clk = DWMCI_CMD_PRV_DAT_WAIT | DWMCI_CMD_UPD_CLK;
+ int timeout = 10000;
+ u32 status;
- dwmci_writel(host, DWMCI_CLKDIV, div);
- dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
- DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
+ dwmci_writel(host, DWMCI_CLKENA, val);
+ /* Inform CIU */
+ dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_START | cmd_only_clk);
do {
status = dwmci_readl(host, DWMCI_CMD);
if (timeout-- < 0) {
@@ -438,20 +521,62 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
}
} while (status & DWMCI_CMD_START);
- dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE |
- DWMCI_CLKEN_LOW_PWR);
+ return 0;
+}
- dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
- DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
+/*
+ * Update the clock divider.
+ *
+ * To prevent a clock glitch keep the clock stopped during the update of
+ * clock divider and clock source.
+ */
+static int dwmci_update_div(struct dwmci_host *host, u32 div)
+{
+ int ret;
- timeout = 10000;
- do {
- status = dwmci_readl(host, DWMCI_CMD);
- if (timeout-- < 0) {
- debug("%s: Timeout!\n", __func__);
- return -ETIMEDOUT;
- }
- } while (status & DWMCI_CMD_START);
+ /* Disable clock */
+ ret = dwmci_control_clken(host, false);
+ if (ret)
+ return ret;
+
+ /* Set clock to desired speed */
+ dwmci_writel(host, DWMCI_CLKDIV, div);
+ dwmci_writel(host, DWMCI_CLKSRC, 0);
+
+ /* Enable clock */
+ return dwmci_control_clken(host, true);
+}
+
+static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
+{
+ u32 div;
+ unsigned long sclk;
+ int ret;
+
+ if (freq == host->clock || freq == 0)
+ return 0;
+
+ /*
+ * If host->get_mmc_clk isn't defined, then assume that host->bus_hz is
+ * source clock value. host->bus_hz should be set by user.
+ */
+ if (host->get_mmc_clk) {
+ sclk = host->get_mmc_clk(host, freq);
+ } else if (host->bus_hz) {
+ sclk = host->bus_hz;
+ } else {
+ debug("%s: Didn't get source clock value\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sclk == freq)
+ div = 0; /* bypass mode */
+ else
+ div = DIV_ROUND_UP(sclk, 2 * freq);
+
+ ret = dwmci_update_div(host, div);
+ if (ret)
+ return ret;
host->clock = freq;
@@ -469,7 +594,7 @@ static int dwmci_set_ios(struct mmc *mmc)
struct dwmci_host *host = (struct dwmci_host *)mmc->priv;
u32 ctype, regs;
- debug("Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock);
+ debug("Bus width = %d, clock: %d\n", mmc->bus_width, mmc->clock);
dwmci_setup_bus(host, mmc->clock);
switch (mmc->bus_width) {
@@ -524,6 +649,48 @@ static int dwmci_set_ios(struct mmc *mmc)
return 0;
}
+static void dwmci_init_fifo(struct dwmci_host *host)
+{
+ u32 fifo_thr, fifoth_val;
+
+ if (!host->fifo_depth) {
+ u32 fifo_size;
+
+ /*
+ * Automatically detect FIFO depth from FIFOTH register.
+ * Power-on value of RX_WMark is FIFO_DEPTH-1.
+ */
+ fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
+ fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
+ host->fifo_depth = fifo_size;
+ }
+
+ fifo_thr = host->fifo_depth / 2;
+ fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_thr - 1) | TX_WMARK(fifo_thr);
+ dwmci_writel(host, DWMCI_FIFOTH, fifoth_val);
+}
+
+static void dwmci_init_dma(struct dwmci_host *host)
+{
+ int addr_config;
+
+ if (host->fifo_mode)
+ return;
+
+ addr_config = (dwmci_readl(host, DWMCI_HCON) >> 27) & 0x1;
+ if (addr_config == 1) {
+ host->dma_64bit_address = true;
+ host->regs = &dwmci_idmac_regs64;
+ debug("%s: IDMAC supports 64-bit address mode\n", __func__);
+ } else {
+ host->dma_64bit_address = false;
+ host->regs = &dwmci_idmac_regs32;
+ debug("%s: IDMAC supports 32-bit address mode\n", __func__);
+ }
+
+ dwmci_writel(host, host->regs->idinten, DWMCI_IDINTEN_MASK);
+}
+
static int dwmci_init(struct mmc *mmc)
{
struct dwmci_host *host = mmc->priv;
@@ -541,30 +708,18 @@ static int dwmci_init(struct mmc *mmc)
/* Enumerate at 400KHz */
dwmci_setup_bus(host, mmc->cfg->f_min);
- dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF);
+ dwmci_writel(host, DWMCI_RINTSTS, 0xffffffff);
dwmci_writel(host, DWMCI_INTMASK, 0);
- dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF);
+ dwmci_writel(host, DWMCI_TMOUT, 0xffffffff);
- dwmci_writel(host, DWMCI_IDINTEN, 0);
dwmci_writel(host, DWMCI_BMOD, 1);
-
- if (!host->fifoth_val) {
- uint32_t fifo_size;
-
- fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
- fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
- host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
- TX_WMARK(fifo_size / 2);
- }
- dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
+ dwmci_init_fifo(host);
+ dwmci_init_dma(host);
dwmci_writel(host, DWMCI_CLKENA, 0);
dwmci_writel(host, DWMCI_CLKSRC, 0);
- if (!host->fifo_mode)
- dwmci_writel(host, DWMCI_IDINTEN, DWMCI_IDINTEN_MASK);
-
return 0;
}
@@ -590,7 +745,7 @@ static const struct mmc_ops dwmci_ops = {
#endif
void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host,
- u32 max_clk, u32 min_clk)
+ u32 max_clk, u32 min_clk)
{
cfg->name = host->name;
#ifndef CONFIG_DM_MMC
@@ -626,7 +781,7 @@ int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk)
dwmci_setup_cfg(&host->cfg, host, max_clk, min_clk);
host->mmc = mmc_create(&host->cfg, host);
- if (host->mmc == NULL)
+ if (!host->mmc)
return -1;
return 0;
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index a51f762988d..c8bf89d6d35 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -4,10 +4,9 @@
* Jaehoon Chung <jh80.chung@samsung.com>
*/
+#include <clk.h>
#include <dwmmc.h>
-#include <fdtdec.h>
#include <asm/global_data.h>
-#include <linux/libfdt.h>
#include <malloc.h>
#include <errno.h>
#include <asm/arch/dwmmc.h>
@@ -15,6 +14,7 @@
#include <asm/arch/pinmux.h>
#include <asm/arch/power.h>
#include <asm/gpio.h>
+#include <linux/err.h>
#include <linux/printk.h>
#define DWMMC_MAX_CH_NUM 4
@@ -23,6 +23,11 @@
#define DWMMC_MMC0_SDR_TIMING_VAL 0x03030001
#define DWMMC_MMC2_SDR_TIMING_VAL 0x03020001
+#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
+
+/* Quirks */
+#define DWMCI_QUIRK_DISABLE_SMU BIT(0)
+
#ifdef CONFIG_DM_MMC
#include <dm.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -33,35 +38,117 @@ struct exynos_mmc_plat {
};
#endif
-/* Exynos implmentation specific drver private data */
+/* Chip specific data */
+struct exynos_dwmmc_variant {
+ u32 clksel; /* CLKSEL register offset */
+ u8 div; /* (optional) fixed clock divider value: 0..7 */
+ u32 quirks; /* quirk flags - see DWMCI_QUIRK_... */
+};
+
+/* Exynos implementation specific driver private data */
struct dwmci_exynos_priv_data {
#ifdef CONFIG_DM_MMC
struct dwmci_host host;
#endif
+ struct clk clk;
u32 sdr_timing;
+ u32 ddr_timing;
+ const struct exynos_dwmmc_variant *chip;
};
-/*
- * Function used as callback function to initialise the
- * CLKSEL register for every mmc channel.
- */
-static int exynos_dwmci_clksel(struct dwmci_host *host)
+static struct dwmci_exynos_priv_data *exynos_dwmmc_get_priv(
+ struct dwmci_host *host)
{
#ifdef CONFIG_DM_MMC
- struct dwmci_exynos_priv_data *priv =
- container_of(host, struct dwmci_exynos_priv_data, host);
+ return container_of(host, struct dwmci_exynos_priv_data, host);
#else
- struct dwmci_exynos_priv_data *priv = host->priv;
+ return host->priv;
#endif
- dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing);
+}
+
+/**
+ * exynos_dwmmc_get_sclk - Get source clock (SDCLKIN) rate
+ * @host: MMC controller object
+ * @rate: Will contain clock rate, Hz
+ *
+ * Return: 0 on success or negative value on error
+ */
+static int exynos_dwmmc_get_sclk(struct dwmci_host *host, unsigned long *rate)
+{
+#ifdef CONFIG_CPU_V7A
+ *rate = get_mmc_clk(host->dev_index);
+#else
+ struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+
+ *rate = clk_get_rate(&priv->clk);
+#endif
+
+ if (IS_ERR_VALUE(*rate))
+ return *rate;
return 0;
}
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
+/**
+ * exynos_dwmmc_set_sclk - Set source clock (SDCLKIN) rate
+ * @host: MMC controller object
+ * @rate: Desired clock rate, Hz
+ *
+ * Return: 0 on success or negative value on error
+ */
+static int exynos_dwmmc_set_sclk(struct dwmci_host *host, unsigned long rate)
{
+ int err;
+
+#ifdef CONFIG_CPU_V7A
unsigned long sclk;
- int8_t clk_div;
+ unsigned int div;
+
+ err = exynos_dwmmc_get_sclk(host, &sclk);
+ if (err)
+ return err;
+
+ div = DIV_ROUND_UP(sclk, rate);
+ set_mmc_clk(host->dev_index, div);
+#else
+ struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+
+ err = clk_set_rate(&priv->clk, rate);
+ if (err < 0)
+ return err;
+#endif
+
+ return 0;
+}
+
+/* Configure CLKSEL register with chosen timing values */
+static int exynos_dwmci_clksel(struct dwmci_host *host)
+{
+ struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+ u32 timing;
+
+ if (host->mmc->selected_mode == MMC_DDR_52)
+ timing = priv->ddr_timing;
+ else
+ timing = priv->sdr_timing;
+
+ dwmci_writel(host, priv->chip->clksel, timing);
+
+ return 0;
+}
+
+/**
+ * exynos_dwmmc_get_ciu_div - Get internal clock divider value
+ * @host: MMC controller object
+ *
+ * Returns: Divider value, in range of 1..8
+ */
+static u8 exynos_dwmmc_get_ciu_div(struct dwmci_host *host)
+{
+ struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
+
+ if (priv->chip->div)
+ return priv->chip->div + 1;
/*
* Since SDCLKIN is divided inside controller by the DIVRATIO
@@ -69,22 +156,42 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
* clock value to calculate the CLKDIV value.
* as per user manual:cclk_in = SDCLKIN / (DIVRATIO + 1)
*/
- clk_div = ((dwmci_readl(host, DWMCI_CLKSEL) >> DWMCI_DIVRATIO_BIT)
- & DWMCI_DIVRATIO_MASK) + 1;
- sclk = get_mmc_clk(host->dev_index);
+ return ((dwmci_readl(host, priv->chip->clksel) >> DWMCI_DIVRATIO_BIT)
+ & DWMCI_DIVRATIO_MASK) + 1;
+}
- /*
- * Assume to know divider value.
- * When clock unit is broken, need to set "host->div"
- */
- return sclk / clk_div / (host->div + 1);
+static unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
+{
+ unsigned long sclk;
+ u8 clk_div;
+ int err;
+
+ /* Should be double rate for DDR mode */
+ if (host->mmc->selected_mode == MMC_DDR_52 && host->mmc->bus_width == 8)
+ freq *= 2;
+
+ clk_div = exynos_dwmmc_get_ciu_div(host);
+ err = exynos_dwmmc_set_sclk(host, freq * clk_div);
+ if (err) {
+ printf("DWMMC%d: failed to set clock rate (%d); "
+ "continue anyway\n", host->dev_index, err);
+ }
+
+ err = exynos_dwmmc_get_sclk(host, &sclk);
+ if (err) {
+ printf("DWMMC%d: failed to get clock rate (%d)\n",
+ host->dev_index, err);
+ return 0;
+ }
+
+ return sclk / clk_div;
}
static void exynos_dwmci_board_init(struct dwmci_host *host)
{
- struct dwmci_exynos_priv_data *priv = host->priv;
+ struct dwmci_exynos_priv_data *priv = exynos_dwmmc_get_priv(host);
- if (host->quirks & DWMCI_QUIRK_DISABLE_SMU) {
+ if (priv->chip->quirks & DWMCI_QUIRK_DISABLE_SMU) {
dwmci_writel(host, EMMCP_MPSBEGIN0, 0);
dwmci_writel(host, EMMCP_SEND0, 0);
dwmci_writel(host, EMMCP_CTRL0,
@@ -94,73 +201,27 @@ static void exynos_dwmci_board_init(struct dwmci_host *host)
MPSCTRL_NON_SECURE_WRITE_BIT | MPSCTRL_VALID);
}
- /* Set to timing value at initial time */
if (priv->sdr_timing)
exynos_dwmci_clksel(host);
}
-static int exynos_dwmci_core_init(struct dwmci_host *host)
-{
- unsigned int div;
- unsigned long freq, sclk;
-
- if (host->bus_hz)
- freq = host->bus_hz;
- else
- freq = DWMMC_MAX_FREQ;
-
- /* request mmc clock vlaue of 52MHz. */
- sclk = get_mmc_clk(host->dev_index);
- div = DIV_ROUND_UP(sclk, freq);
- /* set the clock divisor for mmc */
- set_mmc_clk(host->dev_index, div);
-
- host->name = "EXYNOS DWMMC";
-#ifdef CONFIG_EXYNOS5420
- host->quirks = DWMCI_QUIRK_DISABLE_SMU;
-#endif
- host->board_init = exynos_dwmci_board_init;
-
- host->caps = MMC_MODE_DDR_52MHz;
- host->clksel = exynos_dwmci_clksel;
- host->get_mmc_clk = exynos_dwmci_get_clk;
-
-#ifndef CONFIG_DM_MMC
- /* Add the mmc channel to be registered with mmc core */
- if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) {
- printf("DWMMC%d registration failed\n", host->dev_index);
- return -1;
- }
-#endif
-
- return 0;
-}
-
-static int do_dwmci_init(struct dwmci_host *host)
+#ifdef CONFIG_DM_MMC
+static int exynos_dwmmc_of_to_plat(struct udevice *dev)
{
- int flag, err;
-
- flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
- err = exynos_pinmux_config(host->dev_id, flag);
- if (err) {
- printf("DWMMC%d not configure\n", host->dev_index);
- return err;
- }
+ struct dwmci_exynos_priv_data *priv = dev_get_priv(dev);
+ struct dwmci_host *host = &priv->host;
+ u32 div, timing[2];
+ int err;
- return exynos_dwmci_core_init(host);
-}
+ priv->chip = (struct exynos_dwmmc_variant *)dev_get_driver_data(dev);
-static int exynos_dwmci_get_config(const void *blob, int node,
- struct dwmci_host *host,
- struct dwmci_exynos_priv_data *priv)
-{
- int err = 0;
- u32 base, timing[3];
+#ifdef CONFIG_CPU_V7A
+ const void *blob = gd->fdt_blob;
+ int node = dev_of_offset(dev);
- /* Extract device id for each mmc channel */
+ /* Obtain device ID for current MMC channel */
host->dev_id = pinmux_decode_periph_id(blob, node);
-
- host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id);
+ host->dev_index = dev_read_u32_default(dev, "index", host->dev_id);
if (host->dev_index == host->dev_id)
host->dev_index = host->dev_id - PERIPH_ID_SDMMC0;
@@ -168,31 +229,34 @@ static int exynos_dwmci_get_config(const void *blob, int node,
printf("DWMMC%d: Can't get the dev index\n", host->dev_index);
return -EINVAL;
}
+#else
+ if (dev_read_bool(dev, "non-removable"))
+ host->dev_index = 0; /* eMMC */
+ else
+ host->dev_index = 2; /* SD card */
+#endif
- /* Get the bus width from the device node (Default is 4bit buswidth) */
- host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 4);
-
- /* Set the base address from the device node */
- base = fdtdec_get_addr(blob, node, "reg");
- if (!base) {
+ host->ioaddr = dev_read_addr_ptr(dev);
+ if (!host->ioaddr) {
printf("DWMMC%d: Can't get base address\n", host->dev_index);
return -EINVAL;
}
- host->ioaddr = (void *)base;
- /* Extract the timing info from the node */
- err = fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3);
+ if (priv->chip->div)
+ div = priv->chip->div;
+ else
+ div = dev_read_u32_default(dev, "samsung,dw-mshc-ciu-div", 0);
+
+ err = dev_read_u32_array(dev, "samsung,dw-mshc-sdr-timing", timing, 2);
if (err) {
- printf("DWMMC%d: Can't get sdr-timings for devider\n",
- host->dev_index);
+ printf("DWMMC%d: Can't get sdr-timings\n", host->dev_index);
return -EINVAL;
}
+ priv->sdr_timing = DWMCI_SET_SAMPLE_CLK(timing[0]) |
+ DWMCI_SET_DRV_CLK(timing[1]) |
+ DWMCI_SET_DIV_RATIO(div);
- priv->sdr_timing = (DWMCI_SET_SAMPLE_CLK(timing[0]) |
- DWMCI_SET_DRV_CLK(timing[1]) |
- DWMCI_SET_DIV_RATIO(timing[2]));
-
- /* sdr_timing didn't assigned anything, use the default value */
+ /* sdr_timing wasn't set, use the default value */
if (!priv->sdr_timing) {
if (host->dev_index == 0)
priv->sdr_timing = DWMMC_MMC0_SDR_TIMING_VAL;
@@ -200,35 +264,82 @@ static int exynos_dwmci_get_config(const void *blob, int node,
priv->sdr_timing = DWMMC_MMC2_SDR_TIMING_VAL;
}
- host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0);
- host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0);
- host->div = fdtdec_get_int(blob, node, "div", 0);
+ err = dev_read_u32_array(dev, "samsung,dw-mshc-ddr-timing", timing, 2);
+ if (err) {
+ debug("DWMMC%d: Can't get ddr-timings, using sdr-timings\n",
+ host->dev_index);
+ priv->ddr_timing = priv->sdr_timing;
+ } else {
+ priv->ddr_timing = DWMCI_SET_SAMPLE_CLK(timing[0]) |
+ DWMCI_SET_DRV_CLK(timing[1]) |
+ DWMCI_SET_DIV_RATIO(div);
+ }
+
+ host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
+ host->fifo_depth = dev_read_u32_default(dev, "fifo-depth", 0);
+ host->bus_hz = dev_read_u32_default(dev, "clock-frequency", 0);
return 0;
}
-#ifdef CONFIG_DM_MMC
static int exynos_dwmmc_probe(struct udevice *dev)
{
struct exynos_mmc_plat *plat = dev_get_plat(dev);
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct dwmci_exynos_priv_data *priv = dev_get_priv(dev);
struct dwmci_host *host = &priv->host;
+ unsigned long freq;
int err;
- err = exynos_dwmci_get_config(gd->fdt_blob, dev_of_offset(dev), host,
- priv);
+#ifndef CONFIG_CPU_V7A
+ err = clk_get_by_index(dev, 1, &priv->clk); /* ciu */
if (err)
return err;
- err = do_dwmci_init(host);
- if (err)
+#endif
+
+#ifdef CONFIG_CPU_V7A
+ int flag;
+
+ flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
+ err = exynos_pinmux_config(host->dev_id, flag);
+ if (err) {
+ printf("DWMMC%d not configure\n", host->dev_index);
return err;
+ }
+#endif
+
+ if (host->bus_hz)
+ freq = host->bus_hz;
+ else
+ freq = DWMMC_MAX_FREQ;
+ err = exynos_dwmmc_set_sclk(host, freq);
+ if (err) {
+ printf("DWMMC%d: failed to set clock rate on probe (%d); "
+ "continue anyway\n", host->dev_index, err);
+ }
+
+ host->name = dev->name;
+ host->board_init = exynos_dwmci_board_init;
+ host->caps = MMC_MODE_DDR_52MHz;
+ host->clksel = exynos_dwmci_clksel;
+ host->get_mmc_clk = exynos_dwmci_get_clk;
+
+#ifdef CONFIG_BLK
dwmci_setup_cfg(&plat->cfg, host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ);
host->mmc = &plat->mmc;
+#else
+ err = add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ);
+ if (err) {
+ printf("DWMMC%d registration failed\n", host->dev_index);
+ return err;
+ }
+#endif
+
host->mmc->priv = &priv->host;
- host->priv = dev;
upriv->mmc = host->mmc;
+ host->mmc->dev = dev;
+ host->priv = dev;
return dwmci_probe(dev);
}
@@ -240,9 +351,34 @@ static int exynos_dwmmc_bind(struct udevice *dev)
return dwmci_bind(dev, &plat->mmc, &plat->cfg);
}
+static const struct exynos_dwmmc_variant exynos4_drv_data = {
+ .clksel = DWMCI_CLKSEL,
+ .div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1,
+};
+
+static const struct exynos_dwmmc_variant exynos5_drv_data = {
+ .clksel = DWMCI_CLKSEL,
+#ifdef CONFIG_EXYNOS5420
+ .quirks = DWMCI_QUIRK_DISABLE_SMU,
+#endif
+};
+
+static const struct exynos_dwmmc_variant exynos7_smu_drv_data = {
+ .clksel = DWMCI_CLKSEL64,
+ .quirks = DWMCI_QUIRK_DISABLE_SMU,
+};
+
static const struct udevice_id exynos_dwmmc_ids[] = {
- { .compatible = "samsung,exynos4412-dw-mshc" },
- { .compatible = "samsung,exynos-dwmmc" },
+ {
+ .compatible = "samsung,exynos4412-dw-mshc",
+ .data = (ulong)&exynos4_drv_data,
+ }, {
+ .compatible = "samsung,exynos-dwmmc",
+ .data = (ulong)&exynos5_drv_data,
+ }, {
+ .compatible = "samsung,exynos7-dw-mshc-smu",
+ .data = (ulong)&exynos7_smu_drv_data,
+ },
{ }
};
@@ -250,9 +386,10 @@ U_BOOT_DRIVER(exynos_dwmmc_drv) = {
.name = "exynos_dwmmc",
.id = UCLASS_MMC,
.of_match = exynos_dwmmc_ids,
+ .of_to_plat = exynos_dwmmc_of_to_plat,
.bind = exynos_dwmmc_bind,
- .ops = &dm_dwmci_ops,
.probe = exynos_dwmmc_probe,
+ .ops = &dm_dwmci_ops,
.priv_auto = sizeof(struct dwmci_exynos_priv_data),
.plat_auto = sizeof(struct exynos_mmc_plat),
};
diff --git a/drivers/mmc/ftsdc010_mci.h b/drivers/mmc/ftsdc010_mci.h
index 782d92be2f5..36187cfa04f 100644
--- a/drivers/mmc/ftsdc010_mci.h
+++ b/drivers/mmc/ftsdc010_mci.h
@@ -28,7 +28,6 @@ struct ftsdc010_chip {
int dev_index;
int dev_id;
int buswidth;
- u32 fifoth_val;
struct mmc *mmc;
void *priv;
bool fifo_mode;
diff --git a/drivers/mmc/hi6220_dw_mmc.c b/drivers/mmc/hi6220_dw_mmc.c
index c68a9157bfc..0302f5c296b 100644
--- a/drivers/mmc/hi6220_dw_mmc.c
+++ b/drivers/mmc/hi6220_dw_mmc.c
@@ -36,7 +36,7 @@ struct hi6220_dwmmc_priv_data {
struct hisi_mmc_data {
unsigned int clock;
bool use_fifo;
- u32 fifoth_val;
+ u32 fifo_depth;
};
static int hi6220_dwmmc_of_to_plat(struct udevice *dev)
@@ -125,7 +125,7 @@ static int hi6220_dwmmc_probe(struct udevice *dev)
host->mmc = &plat->mmc;
host->fifo_mode = mmc_data->use_fifo;
- host->fifoth_val = mmc_data->fifoth_val;
+ host->fifo_depth = mmc_data->fifo_depth;
host->mmc->priv = &priv->host;
upriv->mmc = host->mmc;
host->mmc->dev = dev;
@@ -158,8 +158,7 @@ static const struct hisi_mmc_data hi6220_mmc_data = {
static const struct hisi_mmc_data hi3798mv2x_mmc_data = {
.clock = 50000000,
.use_fifo = false,
- // FIFO depth is 256
- .fifoth_val = MSIZE(4) | RX_WMARK(0x7f) | TX_WMARK(0x80),
+ .fifo_depth = 256,
};
static const struct udevice_id hi6220_dwmmc_ids[] = {
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index cf8277cbed8..7e702c3ae85 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -30,6 +30,41 @@
#define DEFAULT_CMD6_TIMEOUT_MS 500
+/**
+ * names of emmc BOOT_PARTITION_ENABLE values
+ *
+ * Boot Area Partitions - name consistent with Linux
+ */
+const char *emmc_boot_part_names[] = {
+ "default", /* EMMC_BOOT_PART_DEFAULT */
+ "boot0", /* EMMC_BOOT_PART_BOOT1 */
+ "boot1", /* EMMC_BOOT_PART_BOOT2 */
+ "",
+ "",
+ "",
+ "",
+ "user", /* EMMC_BOOT_PART_USER */
+};
+
+/**
+ * names of emmc 'hardware partitions' consistent with:
+ * - value used in mmc_switch()
+ * - value used by PARTITION_CONFIG PARTITION_ACCESS field
+ *
+ * Boot Area Partitions - name consistent with Linux
+ * General Perpose Partitions - name consistent with 'mmc hwpartition' usage
+ */
+const char *emmc_hwpart_names[] = {
+ "user", /* EMMC_HWPART_DEFAULT */
+ "boot0", /* EMMC_HWPART_BOOT1 */
+ "boot1", /* EMMC_HWPART_BOOT2 */
+ "rpmb", /* EMMC_HWPART_RPMB */
+ "gp1", /* EMMC_HWPART_GP1 */
+ "gp2", /* EMMC_HWPART_GP2 */
+ "gp3", /* EMMC_HWPART_GP3 */
+ "gp4", /* EMMC_HWPART_GP4 */
+};
+
static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage);
#if !CONFIG_IS_ENABLED(DM_MMC)
@@ -294,7 +329,7 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms)
if (status & MMC_STATUS_MASK) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("Status Error: 0x%08x\n", status);
+ log_err("Status Error: %#08x\n", status);
#endif
return -ECOMM;
}
@@ -307,7 +342,7 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms)
if (timeout_ms <= 0) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("Timeout waiting card ready\n");
+ log_err("Timeout waiting card ready\n");
#endif
return -ETIMEDOUT;
}
@@ -449,7 +484,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
if (blkcnt > 1) {
if (mmc_send_stop_transmission(mmc, false)) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("mmc fail to send stop cmd\n");
+ log_err("mmc fail to send stop cmd\n");
#endif
return 0;
}
@@ -500,8 +535,8 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
if ((start + blkcnt) > block_dev->lba) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
- start + blkcnt, block_dev->lba);
+ log_err("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
+ start + blkcnt, block_dev->lba);
#endif
return 0;
}
@@ -962,8 +997,8 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode,
* Extended CSD. Reconfigure the controller to run at HS mode.
*/
if (hsdowngrade) {
- mmc_select_mode(mmc, MMC_HS);
- mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS), false);
+ mmc_select_mode(mmc, MMC_HS_52);
+ mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS_52), false);
}
#endif
@@ -996,7 +1031,7 @@ static int mmc_get_capabilities(struct mmc *mmc)
return 0;
if (!ext_csd) {
- pr_err("No ext_csd found!\n"); /* this should enver happen */
+ log_err("No ext_csd found!\n"); /* this should never happen */
return -ENOTSUPP;
}
@@ -1108,17 +1143,17 @@ int mmc_hwpart_config(struct mmc *mmc,
return -EINVAL;
if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
- pr_err("eMMC >= 4.4 required for enhanced user data area\n");
+ log_err("eMMC >= 4.4 required for enhanced user data area\n");
return -EMEDIUMTYPE;
}
if (!(mmc->part_support & PART_SUPPORT)) {
- pr_err("Card does not support partitioning\n");
+ log_err("Card does not support partitioning\n");
return -EMEDIUMTYPE;
}
if (!mmc->hc_wp_grp_size) {
- pr_err("Card does not define HC WP group size\n");
+ log_err("Card does not define HC WP group size\n");
return -EMEDIUMTYPE;
}
@@ -1126,8 +1161,7 @@ int mmc_hwpart_config(struct mmc *mmc,
if (conf->user.enh_size) {
if (conf->user.enh_size % mmc->hc_wp_grp_size ||
conf->user.enh_start % mmc->hc_wp_grp_size) {
- pr_err("User data enhanced area not HC WP group "
- "size aligned\n");
+ log_err("User data enhanced area not HC WP group size aligned\n");
return -EINVAL;
}
part_attrs |= EXT_CSD_ENH_USR;
@@ -1145,8 +1179,8 @@ int mmc_hwpart_config(struct mmc *mmc,
for (pidx = 0; pidx < 4; pidx++) {
if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
- pr_err("GP%i partition not HC WP group size "
- "aligned\n", pidx+1);
+ log_err("GP%i partition not HC WP group-size aligned\n",
+ pidx + 1);
return -EINVAL;
}
gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size;
@@ -1157,7 +1191,7 @@ int mmc_hwpart_config(struct mmc *mmc,
}
if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
- pr_err("Card does not support enhanced attribute\n");
+ log_err("Card does not support enhanced attribute\n");
return -EMEDIUMTYPE;
}
@@ -1170,8 +1204,8 @@ int mmc_hwpart_config(struct mmc *mmc,
(ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
if (tot_enh_size_mult > max_enh_size_mult) {
- pr_err("Total enhanced size exceeds maximum (%u > %u)\n",
- tot_enh_size_mult, max_enh_size_mult);
+ log_err("Total enhanced size exceeds maximum (%#x > %#x)\n",
+ tot_enh_size_mult, max_enh_size_mult);
return -EMEDIUMTYPE;
}
@@ -1204,7 +1238,7 @@ int mmc_hwpart_config(struct mmc *mmc,
if (ext_csd[EXT_CSD_PARTITION_SETTING] &
EXT_CSD_PARTITION_SETTING_COMPLETED) {
- pr_err("Card already partitioned\n");
+ log_err("Card already partitioned\n");
return -EPERM;
}
@@ -1875,7 +1909,7 @@ error:
}
}
- pr_err("unable to select a mode\n");
+ log_err("unable to select a mode\n");
return -ENOTSUPP;
}
@@ -2043,7 +2077,7 @@ static int mmc_select_hs400(struct mmc *mmc)
}
/* Set back to HS */
- mmc_set_card_speed(mmc, MMC_HS, true);
+ mmc_set_card_speed(mmc, MMC_HS_52, true);
err = mmc_hs400_prepare_ddr(mmc);
if (err)
@@ -2253,7 +2287,7 @@ error:
}
}
- pr_err("unable to select a mode : %d\n", err);
+ log_err("unable to select a mode: %d\n", err);
return -ENOTSUPP;
}
@@ -2921,7 +2955,8 @@ retry:
if (err) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
if (!quiet)
- pr_err("Card did not respond to voltage select! : %d\n", err);
+ log_err("Card did not respond to voltage select! : %d\n",
+ err);
#endif
return -EOPNOTSUPP;
}
@@ -2954,7 +2989,7 @@ int mmc_start_init(struct mmc *mmc)
| MMC_CAP(MMC_LEGACY) |
MMC_MODE_1BIT);
} else {
- pr_err("bus_mode requested is not supported\n");
+ log_err("bus_mode requested is not supported\n");
return -EINVAL;
}
}
@@ -2974,7 +3009,7 @@ int mmc_start_init(struct mmc *mmc)
if (no_card) {
mmc->has_init = 0;
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("MMC: no card present\n");
+ log_err("MMC: no card present\n");
#endif
return -ENOMEDIUM;
}
@@ -3103,7 +3138,7 @@ static int mmc_probe(struct bd_info *bis)
uclass_foreach_dev(dev, uc) {
ret = device_probe(dev);
if (ret)
- pr_err("%s - probe failed: %d\n", dev->name, ret);
+ log_err("%s - probe failed: %d\n", dev->name, ret);
}
return 0;
diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c
index f5e9930c799..4e5c932c071 100644
--- a/drivers/mmc/msm_sdhci.c
+++ b/drivers/mmc/msm_sdhci.c
@@ -32,6 +32,8 @@
#define SDCC_MCI_STATUS2_MCI_ACT 0x1
#define SDCC_MCI_HC_MODE 0x78
+#define CORE_VENDOR_SPEC_POR_VAL 0xa9c
+
struct msm_sdhc_plat {
struct mmc_config cfg;
struct mmc mmc;
@@ -46,6 +48,7 @@ struct msm_sdhc {
struct msm_sdhc_variant_info {
bool mci_removed;
+ u32 core_vendor_spec;
u32 core_vendor_spec_capabilities0;
};
@@ -54,11 +57,14 @@ DECLARE_GLOBAL_DATA_PTR;
static int msm_sdc_clk_init(struct udevice *dev)
{
struct msm_sdhc *prv = dev_get_priv(dev);
+ const struct msm_sdhc_variant_info *var_info;
ofnode node = dev_ofnode(dev);
ulong clk_rate;
int ret, i = 0, n_clks;
const char *clk_name;
+ var_info = (void *)dev_get_driver_data(dev);
+
ret = ofnode_read_u32(node, "clock-frequency", (uint *)(&clk_rate));
if (ret)
clk_rate = 201500000;
@@ -105,6 +111,9 @@ static int msm_sdc_clk_init(struct udevice *dev)
return -EINVAL;
}
+ writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
+ prv->host.ioaddr + var_info->core_vendor_spec);
+
return 0;
}
@@ -254,12 +263,14 @@ static int msm_sdc_bind(struct udevice *dev)
static const struct msm_sdhc_variant_info msm_sdhc_mci_var = {
.mci_removed = false,
+ .core_vendor_spec = 0x10c,
.core_vendor_spec_capabilities0 = 0x11c,
};
static const struct msm_sdhc_variant_info msm_sdhc_v5_var = {
.mci_removed = true,
+ .core_vendor_spec = 0x20c,
.core_vendor_spec_capabilities0 = 0x21c,
};
diff --git a/drivers/mmc/nexell_dw_mmc.c b/drivers/mmc/nexell_dw_mmc.c
index 2e1ce54c7d5..80df617e07e 100644
--- a/drivers/mmc/nexell_dw_mmc.c
+++ b/drivers/mmc/nexell_dw_mmc.c
@@ -186,10 +186,7 @@ static int nexell_dwmmc_probe(struct udevice *dev)
struct dwmci_host *host = &priv->host;
struct udevice *pwr_dev __maybe_unused;
- host->fifoth_val = MSIZE(0x2) |
- RX_WMARK(priv->fifo_size / 2 - 1) |
- TX_WMARK(priv->fifo_size / 2);
-
+ host->fifo_depth = priv->fifo_size;
host->fifo_mode = priv->fifo_mode;
dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq);
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c
index 549fb80f198..fb77b049834 100644
--- a/drivers/mmc/rockchip_dw_mmc.c
+++ b/drivers/mmc/rockchip_dw_mmc.c
@@ -138,10 +138,7 @@ static int rockchip_dwmmc_probe(struct udevice *dev)
if (ret < 0)
return ret;
#endif
- host->fifoth_val = MSIZE(0x2) |
- RX_WMARK(priv->fifo_depth / 2 - 1) |
- TX_WMARK(priv->fifo_depth / 2);
-
+ host->fifo_depth = priv->fifo_depth;
host->fifo_mode = priv->fifo_mode;
#if CONFIG_IS_ENABLED(MMC_PWRSEQ)
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
index 80dbb38c9b3..278019f02ab 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -166,7 +166,7 @@ static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
host->index = dev_id - PERIPH_ID_SDMMC0;
/* Get bus width */
- bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
+ bus_width = fdtdec_get_int(blob, node, "bus-width", 0);
if (bus_width <= 0) {
debug("MMC: Can't get bus-width\n");
return -EINVAL;
diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
index 07ec35a0463..7d169efa476 100644
--- a/drivers/mmc/sdhci-cadence.c
+++ b/drivers/mmc/sdhci-cadence.c
@@ -16,56 +16,7 @@
#include <linux/libfdt.h>
#include <mmc.h>
#include <sdhci.h>
-
-/* HRS - Host Register Set (specific to Cadence) */
-#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
-#define SDHCI_CDNS_HRS04_ACK BIT(26)
-#define SDHCI_CDNS_HRS04_RD BIT(25)
-#define SDHCI_CDNS_HRS04_WR BIT(24)
-#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
-#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
-#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
-
-#define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
-#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
-#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
-#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
-#define SDHCI_CDNS_HRS06_MODE_SD 0x0
-#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
-#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
-#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
-#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
-#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
-
-/* SRS - Slot Register Set (SDHCI-compatible) */
-#define SDHCI_CDNS_SRS_BASE 0x200
-
-/* PHY */
-#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
-#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
-#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
-#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
-#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
-#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
-#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
-#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
-#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
-#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
-#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
-#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
-
-/*
- * The tuned val register is 6 bit-wide, but not the whole of the range is
- * available. The range 0-42 seems to be available (then 43 wraps around to 0)
- * but I am not quite sure if it is official. Use only 0 to 39 for safety.
- */
-#define SDHCI_CDNS_MAX_TUNING_LOOP 40
-
-struct sdhci_cdns_plat {
- struct mmc_config cfg;
- struct mmc mmc;
- void __iomem *hrs_addr;
-};
+#include "sdhci-cadence.h"
struct sdhci_cdns_phy_cfg {
const char *property;
@@ -162,6 +113,9 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host)
tmp &= ~SDHCI_CDNS_HRS06_MODE;
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06);
+
+ if (device_is_compatible(mmc->dev, "cdns,sd6hc"))
+ sdhci_cdns6_phy_adj(mmc->dev, plat, mode);
}
static const struct sdhci_ops sdhci_cdns_ops = {
@@ -175,6 +129,9 @@ static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
u32 tmp;
int i, ret;
+ if (device_is_compatible(plat->mmc.dev, "cdns,sd6hc"))
+ return sdhci_cdns6_set_tune_val(plat, val);
+
if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
return -EINVAL;
@@ -281,7 +238,10 @@ static int sdhci_cdns_probe(struct udevice *dev)
if (ret)
return ret;
- ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
+ if (device_is_compatible(dev, "cdns,sd6hc"))
+ ret = sdhci_cdns6_phy_init(dev, plat);
+ else
+ ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
if (ret)
return ret;
@@ -300,6 +260,7 @@ static int sdhci_cdns_probe(struct udevice *dev)
static const struct udevice_id sdhci_cdns_match[] = {
{ .compatible = "socionext,uniphier-sd4hc" },
{ .compatible = "cdns,sd4hc" },
+ { .compatible = "cdns,sd6hc" },
{ /* sentinel */ }
};
diff --git a/drivers/mmc/sdhci-cadence.h b/drivers/mmc/sdhci-cadence.h
new file mode 100644
index 00000000000..7101f00b75b
--- /dev/null
+++ b/drivers/mmc/sdhci-cadence.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#ifndef SDHCI_CADENCE_H_
+#define SDHCI_CADENCE_H_
+
+/* HRS - Host Register Set (specific to Cadence) */
+/* PHY access port */
+#define SDHCI_CDNS_HRS04 0x10
+/* Cadence V4 HRS04 Description*/
+#define SDHCI_CDNS_HRS04_ACK BIT(26)
+#define SDHCI_CDNS_HRS04_RD BIT(25)
+#define SDHCI_CDNS_HRS04_WR BIT(24)
+#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
+#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
+#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
+
+#define SDHCI_CDNS_HRS05 0x14
+
+/* eMMC control */
+#define SDHCI_CDNS_HRS06 0x18
+#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
+#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
+#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
+#define SDHCI_CDNS_HRS06_MODE_SD 0x0
+#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
+#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
+
+/* SRS - Slot Register Set (SDHCI-compatible) */
+#define SDHCI_CDNS_SRS_BASE 0x200
+
+/* Cadence V4 PHY Setting*/
+#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
+#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
+#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
+#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
+#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
+#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
+#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
+#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
+#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
+
+/*
+ * The tuned val register is 6 bit-wide, but not the whole of the range is
+ * available. The range 0-42 seems to be available (then 43 wraps around to 0)
+ * but I am not quite sure if it is official. Use only 0 to 39 for safety.
+ */
+#define SDHCI_CDNS_MAX_TUNING_LOOP 40
+
+struct sdhci_cdns_plat {
+ struct mmc_config cfg;
+ struct mmc mmc;
+ void __iomem *hrs_addr;
+};
+
+int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode);
+int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat);
+int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val);
+
+#endif
diff --git a/drivers/mmc/sdhci-cadence6.c b/drivers/mmc/sdhci-cadence6.c
new file mode 100644
index 00000000000..a5ed87321ab
--- /dev/null
+++ b/drivers/mmc/sdhci-cadence6.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0-or-platform_driver
+/*
+ * Copyright (C) 2023 Starfive.
+ * Author: Kuan Lim Lee <kuanlim.lee@starfivetech.com>
+ */
+
+#include <dm.h>
+#include <asm/global_data.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <linux/libfdt.h>
+#include <mmc.h>
+#include <sdhci.h>
+#include "sdhci-cadence.h"
+
+/* IO Delay Information */
+#define SDHCI_CDNS_HRS07 0X1C
+#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20, 16)
+#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4, 0)
+
+/* PHY Control and Status */
+#define SDHCI_CDNS_HRS09 0x24
+#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16)
+#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15)
+#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3)
+#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2)
+#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1)
+#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0)
+
+/* SDCLK adjustment */
+#define SDHCI_CDNS_HRS10 0x28
+#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19, 16)
+
+/* CMD/DAT output delay */
+#define SDHCI_CDNS_HRS16 0x40
+
+/* PHY Special Function Registers */
+/* register to control the DQ related timing */
+#define PHY_DQ_TIMING_REG_ADDR 0x2000
+
+/* register to control the DQS related timing */
+#define PHY_DQS_TIMING_REG_ADDR 0x2004
+
+/* register to control the gate and loopback control related timing */
+#define PHY_GATE_LPBK_CTRL_REG_ADDR 0x2008
+
+/* register to control the Master DLL logic */
+#define PHY_DLL_MASTER_CTRL_REG_ADDR 0x200C
+
+/* register to control the Slave DLL logic */
+#define PHY_DLL_SLAVE_CTRL_REG_ADDR 0x2010
+#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY GENMASK(31, 24)
+#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY GENMASK(7, 0)
+
+#define SDHCI_CDNS6_PHY_CFG_NUM 4
+#define SDHCI_CDNS6_CTRL_CFG_NUM 4
+
+struct sdhci_cdns6_phy_cfg {
+ const char *property;
+ u32 val;
+};
+
+struct sdhci_cdns6_ctrl_cfg {
+ const char *property;
+ u32 val;
+};
+
+static struct sdhci_cdns6_phy_cfg sd_ds_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-sd-ds", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-sd-ds", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-sd-ds", 0x00000000, },
+ { "cdns,phy-dq-timing-delay-sd-ds", 0x00000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_sdr_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-semmc-sdr", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-sdr", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-sdr", 0x00000000, },
+ { "cdns,phy-dq-timing-delay-emmc-sdr", 0x00000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_ddr_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-emmc-ddr", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-ddr", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-ddr", 0x00000000, },
+ { "cdns,phy-dq-timing-delay-emmc-ddr", 0x10000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_hs200_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-emmc-hs200", 0x00380004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs200", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-hs200", 0x00DADA00, },
+ { "cdns,phy-dq-timing-delay-emmc-hs200", 0x00000001, },
+};
+
+static struct sdhci_cdns6_phy_cfg emmc_hs400_phy_cfgs[] = {
+ { "cdns,phy-dqs-timing-delay-emmc-hs400", 0x00280004, },
+ { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs400", 0x01A00040, },
+ { "cdns,phy-dll-slave-ctrl-emmc-hs400", 0x00DAD800, },
+ { "cdns,phy-dq-timing-delay-emmc-hs400", 0x00000001, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg sd_ds_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-sd-ds", 0x0001800C, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-sd-ds", 0x00020000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-sd-ds", 0x00000000, },
+ { "cdns,ctrl-hrs07-timing-delay-sd-ds", 0x00080000, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_sdr_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-sdr", 0x0001800C, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-sdr", 0x00030000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-sdr", 0x00000000, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-sdr", 0x00080000, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_ddr_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-ddr", 0x0001800C, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-ddr", 0x00020000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-ddr", 0x11000001, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-ddr", 0x00090001, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_hs200_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-hs200", 0x00018000, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs200", 0x00080000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs200", 0x00000000, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-hs200", 0x00090000, },
+};
+
+static struct sdhci_cdns6_ctrl_cfg emmc_hs400_ctrl_cfgs[] = {
+ { "cdns,ctrl-hrs09-timing-delay-emmc-hs400", 0x00018000, },
+ { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs400", 0x00080000, },
+ { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs400", 0x11000000, },
+ { "cdns,ctrl-hrs07-timing-delay-emmc-hs400", 0x00080000, },
+};
+
+static u32 sdhci_cdns6_read_phy_reg(struct sdhci_cdns_plat *plat, u32 addr)
+{
+ writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
+ return readl(plat->hrs_addr + SDHCI_CDNS_HRS05);
+}
+
+static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat, u32 addr, u32 val)
+{
+ writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
+ writel(val, plat->hrs_addr + SDHCI_CDNS_HRS05);
+}
+
+static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat, bool reset)
+{
+ void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
+ u32 tmp;
+ int ret;
+
+ tmp = readl(reg);
+ tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
+
+ /* Switch On DLL Reset */
+ if (reset)
+ tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 0);
+ else
+ tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 1);
+
+ writel(tmp, reg);
+
+ /* After reset, wait until HRS09.PHY_INIT_COMPLETE is set to 1 within 3000us*/
+ if (!reset) {
+ ret = readl_poll_timeout(reg, tmp, (tmp & SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE),
+ 3000);
+ }
+
+ return ret;
+}
+
+int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ struct sdhci_cdns6_phy_cfg *sdhci_cdns6_phy_cfgs;
+ struct sdhci_cdns6_ctrl_cfg *sdhci_cdns6_ctrl_cfgs;
+ const fdt32_t *prop;
+ u32 tmp;
+ int i, ret;
+
+ switch (mode) {
+ case SDHCI_CDNS_HRS06_MODE_SD:
+ sdhci_cdns6_phy_cfgs = sd_ds_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = sd_ds_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_SDR:
+ sdhci_cdns6_phy_cfgs = emmc_sdr_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_sdr_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_DDR:
+ sdhci_cdns6_phy_cfgs = emmc_ddr_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_ddr_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_HS200:
+ sdhci_cdns6_phy_cfgs = emmc_hs200_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_hs200_ctrl_cfgs;
+ break;
+
+ case SDHCI_CDNS_HRS06_MODE_MMC_HS400:
+ sdhci_cdns6_phy_cfgs = emmc_hs400_phy_cfgs;
+ sdhci_cdns6_ctrl_cfgs = emmc_hs400_ctrl_cfgs;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDHCI_CDNS6_PHY_CFG_NUM; i++) {
+ prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ sdhci_cdns6_phy_cfgs[i].property, NULL);
+ if (prop)
+ sdhci_cdns6_phy_cfgs[i].val = *prop;
+ }
+
+ for (i = 0; i < SDHCI_CDNS6_CTRL_CFG_NUM; i++) {
+ prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ sdhci_cdns6_ctrl_cfgs[i].property, NULL);
+ if (prop)
+ sdhci_cdns6_ctrl_cfgs[i].val = *prop;
+ }
+
+ /* Switch On the DLL Reset */
+ sdhci_cdns6_reset_phy_dll(plat, true);
+
+ sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[0].val);
+ sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[1].val);
+ sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val);
+
+ /* Switch Off the DLL Reset */
+ ret = sdhci_cdns6_reset_phy_dll(plat, false);
+ if (ret) {
+ printf("sdhci_cdns6_reset_phy is not completed\n");
+ return ret;
+ }
+
+ /* Set PHY DQ TIMING control register */
+ sdhci_cdns6_write_phy_reg(plat, PHY_DQ_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[3].val);
+
+ /* Set HRS09 register */
+ tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS09);
+ tmp &= ~(SDHCI_CDNS_HRS09_EXTENDED_WR_MODE |
+ SDHCI_CDNS_HRS09_EXTENDED_RD_MODE |
+ SDHCI_CDNS_HRS09_RDDATA_EN |
+ SDHCI_CDNS_HRS09_RDCMD_EN);
+ tmp |= sdhci_cdns6_ctrl_cfgs[0].val;
+ writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS09);
+
+ /* Set HRS10 register */
+ tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS10);
+ tmp &= ~SDHCI_CDNS_HRS10_HCSDCLKADJ;
+ tmp |= sdhci_cdns6_ctrl_cfgs[1].val;
+ writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS10);
+
+ /* Set HRS16 register */
+ writel(sdhci_cdns6_ctrl_cfgs[2].val, plat->hrs_addr + SDHCI_CDNS_HRS16);
+
+ /* Set HRS07 register */
+ writel(sdhci_cdns6_ctrl_cfgs[3].val, plat->hrs_addr + SDHCI_CDNS_HRS07);
+
+ return 0;
+}
+
+int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat)
+{
+ return sdhci_cdns6_phy_adj(dev, plat, SDHCI_CDNS_HRS06_MODE_SD);
+}
+
+int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
+{
+ u32 tmp, tuneval;
+
+ tuneval = (val * 256) / SDHCI_CDNS_MAX_TUNING_LOOP;
+
+ tmp = sdhci_cdns6_read_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR);
+ tmp &= ~(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY |
+ PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY);
+ tmp |= FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY, tuneval) |
+ FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY, tuneval);
+ sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp);
+
+ return 0;
+}
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 560b7e889c7..4833b5158c7 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -32,8 +32,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
- printf("%s: Reset 0x%x never completed.\n",
- __func__, (int)mask);
+ log_warning("Reset %#x never completed\n", mask);
return;
}
timeout--;
@@ -139,8 +138,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data)
do {
stat = sdhci_readl(host, SDHCI_INT_STATUS);
if (stat & SDHCI_INT_ERROR) {
- pr_debug("%s: Error detected in status(0x%X)!\n",
- __func__, stat);
+ log_debug("Error detected in status(%#x)!\n", stat);
return -EIO;
}
if (!transfer_done && (stat & rdy)) {
@@ -173,7 +171,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data)
if (timeout-- > 0)
udelay(10);
else {
- printf("%s: Transfer data timeout\n", __func__);
+ log_err("Transfer data timeout\n");
return -ETIMEDOUT;
}
} while (!(stat & SDHCI_INT_DATA_END));
@@ -232,13 +230,13 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (time >= cmd_timeout) {
- printf("%s: MMC: %d busy ", __func__, mmc_dev);
+ log_warning("mmc%d busy ", mmc_dev);
if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) {
cmd_timeout += cmd_timeout;
- printf("timeout increasing to: %u ms.\n",
- cmd_timeout);
+ log_warning("timeout increasing to: %u ms\n",
+ cmd_timeout);
} else {
- puts("timeout.\n");
+ log_warning("timeout\n");
return -ECOMM;
}
}
@@ -316,8 +314,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
}
if (get_timer(start) >= SDHCI_READ_STATUS_TIMEOUT) {
- printf("%s: Timeout for status update: %08x %08x\n",
- __func__, stat, mask);
+ log_warning("Timeout for status update: %08x %08x\n",
+ stat, mask);
return -ETIMEDOUT;
}
} while ((stat & mask) != mask);
@@ -358,7 +356,7 @@ static int sdhci_execute_tuning(struct udevice *dev, uint opcode)
struct mmc *mmc = mmc_get_mmc_dev(dev);
struct sdhci_host *host = mmc->priv;
- debug("%s\n", __func__);
+ log_debug("sdhci tuning\n");
if (host->ops && host->ops->platform_execute_tuning) {
err = host->ops->platform_execute_tuning(mmc, opcode);
@@ -380,8 +378,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
while (sdhci_readl(host, SDHCI_PRESENT_STATE) &
(SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
if (timeout == 0) {
- printf("%s: Timeout to wait cmd & data inhibit\n",
- __func__);
+ log_err("Timeout waiting for cmd & data inhibit\n");
return -EBUSY;
}
@@ -397,7 +394,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if (host->ops && host->ops->set_delay) {
ret = host->ops->set_delay(host);
if (ret) {
- printf("%s: Error while setting tap delay\n", __func__);
+ log_err("Error while setting tap delay\n");
return ret;
}
}
@@ -405,7 +402,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if (host->ops && host->ops->config_dll) {
ret = host->ops->config_dll(host, clock, false);
if (ret) {
- printf("%s: Error while configuring dll\n", __func__);
+ log_err("Error configuring dll\n");
return ret;
}
}
@@ -456,7 +453,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if (host->ops && host->ops->config_dll) {
ret = host->ops->config_dll(host, clock, true);
if (ret) {
- printf("%s: Error while configuring dll\n", __func__);
+ log_err("Error while configuring dll\n");
return ret;
}
}
@@ -472,8 +469,7 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
- printf("%s: Internal clock never stabilised.\n",
- __func__);
+ log_err("Internal clock never stabilised.\n");
return -EBUSY;
}
timeout--;
@@ -738,8 +734,7 @@ static int sdhci_init(struct mmc *mmc)
if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) {
host->align_buffer = memalign(8, 512 * 1024);
if (!host->align_buffer) {
- printf("%s: Aligned buffer alloc failed!!!\n",
- __func__);
+ log_err("Aligned buffer alloc failed\n");
return -ENOMEM;
}
}
@@ -881,20 +876,18 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
#else
caps = sdhci_readl(host, SDHCI_CAPABILITIES);
#endif
- debug("%s, caps: 0x%x\n", __func__, caps);
+ log_debug("caps: %#x\n", caps);
#if CONFIG_IS_ENABLED(MMC_SDHCI_SDMA)
if ((caps & SDHCI_CAN_DO_SDMA)) {
host->flags |= USE_SDMA;
} else {
- debug("%s: Your controller doesn't support SDMA!!\n",
- __func__);
+ log_debug("Controller doesn't support SDMA\n");
}
#endif
#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)
if (!(caps & SDHCI_CAN_DO_ADMA2)) {
- printf("%s: Your controller doesn't support ADMA!!\n",
- __func__);
+ log_err("Controller doesn't support ADMA\n");
return -EINVAL;
}
if (!host->adma_desc_table) {
@@ -927,7 +920,7 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
#else
caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
#endif
- debug("%s, caps_1: 0x%x\n", __func__, caps_1);
+ log_debug("caps_1: %#x\n", caps_1);
host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
SDHCI_CLOCK_MUL_SHIFT;
@@ -953,8 +946,7 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
host->max_clk *= host->clk_mul;
}
if (host->max_clk == 0) {
- printf("%s: Hardware doesn't specify base clock frequency\n",
- __func__);
+ log_err("Hardware doesn't specify base clock frequency\n");
return -EINVAL;
}
if (f_max && (f_max < host->max_clk))
@@ -1047,7 +1039,7 @@ int add_sdhci(struct sdhci_host *host, u32 f_max, u32 f_min)
host->mmc = mmc_create(&host->cfg, host);
if (host->mmc == NULL) {
- printf("%s: mmc create fail!\n", __func__);
+ log_err("mmc create fail\n");
return -ENOMEM;
}
diff --git a/drivers/mmc/snps_dw_mmc.c b/drivers/mmc/snps_dw_mmc.c
index 9bdbe5070b1..47ab5654bd6 100644
--- a/drivers/mmc/snps_dw_mmc.c
+++ b/drivers/mmc/snps_dw_mmc.c
@@ -12,6 +12,7 @@
#include <dwmmc.h>
#include <errno.h>
#include <fdtdec.h>
+#include <asm/gpio.h>
#include <dm/device_compat.h>
#include <linux/libfdt.h>
#include <linux/err.h>
@@ -29,6 +30,7 @@ struct snps_dwmci_plat {
struct snps_dwmci_priv_data {
struct dwmci_host host;
u32 f_max;
+ struct gpio_desc cd_gpio;
};
static int snps_dwmmc_clk_setup(struct udevice *dev)
@@ -81,7 +83,7 @@ static int snps_dwmmc_of_to_plat(struct udevice *dev)
host->ioaddr = dev_read_addr_ptr(dev);
/*
- * If fifo-depth is unset don't set fifoth_val - we will try to
+ * If fifo-depth is unset don't set fifo_depth - we will try to
* auto detect it.
*/
ret = dev_read_u32(dev, "fifo-depth", &fifo_depth);
@@ -89,9 +91,7 @@ static int snps_dwmmc_of_to_plat(struct udevice *dev)
if (fifo_depth < FIFO_MIN || fifo_depth > FIFO_MAX)
return -EINVAL;
- host->fifoth_val = MSIZE(0x2) |
- RX_WMARK(fifo_depth / 2 - 1) |
- TX_WMARK(fifo_depth / 2);
+ host->fifo_depth = fifo_depth;
}
host->buswidth = dev_read_u32_default(dev, "bus-width", 4);
@@ -106,6 +106,10 @@ static int snps_dwmmc_of_to_plat(struct udevice *dev)
if (!ret && priv->f_max < CLOCK_MIN)
return -EINVAL;
+ if (CONFIG_IS_ENABLED(DM_GPIO))
+ gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
+ GPIOD_IS_IN);
+
host->fifo_mode = dev_read_bool(dev, "fifo-mode");
host->name = dev->name;
host->dev_index = 0;
@@ -119,6 +123,9 @@ int snps_dwmmc_getcd(struct udevice *dev)
struct snps_dwmci_priv_data *priv = dev_get_priv(dev);
struct dwmci_host *host = &priv->host;
+ if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&priv->cd_gpio))
+ return dm_gpio_get_value(&priv->cd_gpio);
+
return !(dwmci_readl(host, DWMCI_CDETECT) & 1);
}
diff --git a/drivers/mmc/socfpga_dw_mmc.c b/drivers/mmc/socfpga_dw_mmc.c
index f738019b835..3147d3019c0 100644
--- a/drivers/mmc/socfpga_dw_mmc.c
+++ b/drivers/mmc/socfpga_dw_mmc.c
@@ -134,8 +134,8 @@ static int socfpga_dwmmc_of_to_plat(struct udevice *dev)
* We only have one dwmmc block on gen5 SoCFPGA.
*/
host->dev_index = 0;
- host->fifoth_val = MSIZE(0x2) |
- RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2);
+
+ host->fifo_depth = fifo_depth;
priv->drvsel = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
"drvsel", 3);
priv->smplsel = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),