summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/Kconfig7
-rw-r--r--drivers/mmc/bcm2835_sdhost.c265
-rw-r--r--drivers/mmc/ftsdc010_mci.c2
-rw-r--r--drivers/mmc/renesas-sdhi.c1
-rw-r--r--drivers/mmc/sdhci.c9
-rw-r--r--drivers/mmc/stm32_sdmmc2.c4
-rw-r--r--drivers/mmc/zynq_sdhci.c47
7 files changed, 90 insertions, 245 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 3f15f85efda..693b3ceaf00 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -71,6 +71,13 @@ config MMC_HW_PARTITIONING
This adds a command and an API to do hardware partitioning on eMMC
devices.
+config SUPPORT_EMMC_RPMB
+ bool "Support eMMC replay protected memory block (RPMB)"
+ imply CMD_MMC_RPMB
+ help
+ Enable support for reading, writing and programming the
+ key for the Replay Protection Memory Block partition in eMMC.
+
config MMC_IO_VOLTAGE
bool "Support IO voltage configuration"
help
diff --git a/drivers/mmc/bcm2835_sdhost.c b/drivers/mmc/bcm2835_sdhost.c
index 96428333b0a..1ce019af579 100644
--- a/drivers/mmc/bcm2835_sdhost.c
+++ b/drivers/mmc/bcm2835_sdhost.c
@@ -163,7 +163,6 @@ struct bcm2835_host {
int clock; /* Current clock speed */
unsigned int max_clk; /* Max possible freq */
unsigned int blocks; /* remaining PIO blocks */
- int irq; /* Device IRQ */
u32 ns_per_fifo_word;
@@ -173,14 +172,7 @@ struct bcm2835_host {
struct mmc_cmd *cmd; /* Current command */
struct mmc_data *data; /* Current data request */
- bool data_complete:1;/* Data finished before cmd */
bool use_busy:1; /* Wait for busy interrupt */
- bool wait_data_complete:1; /* Wait for data */
-
- /* for threaded irq handler */
- bool irq_block;
- bool irq_busy;
- bool irq_data;
struct udevice *dev;
struct mmc *mmc;
@@ -240,17 +232,9 @@ static void bcm2835_reset_internal(struct bcm2835_host *host)
writel(host->cdiv, host->ioaddr + SDCDIV);
}
-static int bcm2835_finish_command(struct bcm2835_host *host);
-
-static void bcm2835_wait_transfer_complete(struct bcm2835_host *host)
+static int bcm2835_wait_transfer_complete(struct bcm2835_host *host)
{
- int timediff;
- u32 alternate_idle;
-
- alternate_idle = (host->data->flags & MMC_DATA_READ) ?
- SDEDM_FSM_READWAIT : SDEDM_FSM_WRITESTART1;
-
- timediff = 0;
+ int timediff = 0;
while (1) {
u32 edm, fsm;
@@ -261,7 +245,10 @@ static void bcm2835_wait_transfer_complete(struct bcm2835_host *host)
if ((fsm == SDEDM_FSM_IDENTMODE) ||
(fsm == SDEDM_FSM_DATAMODE))
break;
- if (fsm == alternate_idle) {
+
+ if ((fsm == SDEDM_FSM_READWAIT) ||
+ (fsm == SDEDM_FSM_WRITESTART1) ||
+ (fsm == SDEDM_FSM_READDATA)) {
writel(edm | SDEDM_FORCE_DATA_MODE,
host->ioaddr + SDEDM);
break;
@@ -273,9 +260,11 @@ static void bcm2835_wait_transfer_complete(struct bcm2835_host *host)
"wait_transfer_complete - still waiting after %d retries\n",
timediff);
bcm2835_dumpregs(host);
- return;
+ return -ETIMEDOUT;
}
}
+
+ return 0;
}
static int bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
@@ -322,6 +311,9 @@ static int bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
fsm_state != SDEDM_FSM_READCRC)) ||
(!is_read &&
(fsm_state != SDEDM_FSM_WRITEDATA &&
+ fsm_state != SDEDM_FSM_WRITEWAIT1 &&
+ fsm_state != SDEDM_FSM_WRITEWAIT2 &&
+ fsm_state != SDEDM_FSM_WRITECRC &&
fsm_state != SDEDM_FSM_WRITESTART1 &&
fsm_state != SDEDM_FSM_WRITESTART2))) {
hsts = readl(host->ioaddr + SDHSTS);
@@ -358,9 +350,8 @@ static int bcm2835_transfer_pio(struct bcm2835_host *host)
is_read = (host->data->flags & MMC_DATA_READ) != 0;
ret = bcm2835_transfer_block_pio(host, is_read);
-
- if (host->wait_data_complete)
- bcm2835_wait_transfer_complete(host);
+ if (ret)
+ return ret;
sdhsts = readl(host->ioaddr + SDHSTS);
if (sdhsts & (SDHSTS_CRC16_ERROR |
@@ -379,21 +370,8 @@ static int bcm2835_transfer_pio(struct bcm2835_host *host)
return ret;
}
-static void bcm2835_set_transfer_irqs(struct bcm2835_host *host)
-{
- u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN |
- SDHCFG_BUSY_IRPT_EN;
-
- host->hcfg = (host->hcfg & ~all_irqs) |
- SDHCFG_DATA_IRPT_EN |
- SDHCFG_BUSY_IRPT_EN;
-
- writel(host->hcfg, host->ioaddr + SDHCFG);
-}
-
-static
-void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_cmd *cmd,
- struct mmc_data *data)
+static void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_cmd *cmd,
+ struct mmc_data *data)
{
WARN_ON(host->data);
@@ -401,14 +379,9 @@ void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_cmd *cmd,
if (!data)
return;
- host->wait_data_complete = cmd->cmdidx != MMC_CMD_READ_MULTIPLE_BLOCK;
- host->data_complete = false;
-
/* Use PIO */
host->blocks = data->blocks;
- bcm2835_set_transfer_irqs(host);
-
writel(data->blocksize, host->ioaddr + SDHBCT);
writel(data->blocks, host->ioaddr + SDHBLC);
}
@@ -483,36 +456,6 @@ static int bcm2835_send_command(struct bcm2835_host *host, struct mmc_cmd *cmd,
return 0;
}
-static int bcm2835_transfer_complete(struct bcm2835_host *host)
-{
- int ret = 0;
-
- WARN_ON(!host->data_complete);
-
- host->data = NULL;
-
- return ret;
-}
-
-static void bcm2835_finish_data(struct bcm2835_host *host)
-{
- host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
- writel(host->hcfg, host->ioaddr + SDHCFG);
-
- host->data_complete = true;
-
- if (host->cmd) {
- /* Data managed to finish before the
- * command completed. Make sure we do
- * things in the proper order.
- */
- dev_dbg(dev, "Finished early - HSTS %08x\n",
- readl(host->ioaddr + SDHSTS));
- } else {
- bcm2835_transfer_complete(host);
- }
-}
-
static int bcm2835_finish_command(struct bcm2835_host *host)
{
struct mmc_cmd *cmd = host->cmd;
@@ -562,8 +505,6 @@ static int bcm2835_finish_command(struct bcm2835_host *host)
/* Processed actual command. */
host->cmd = NULL;
- if (host->data && host->data_complete)
- ret = bcm2835_transfer_complete(host);
return ret;
}
@@ -608,159 +549,44 @@ static int bcm2835_check_data_error(struct bcm2835_host *host, u32 intmask)
return ret;
}
-static void bcm2835_busy_irq(struct bcm2835_host *host)
-{
- if (WARN_ON(!host->cmd)) {
- bcm2835_dumpregs(host);
- return;
- }
-
- if (WARN_ON(!host->use_busy)) {
- bcm2835_dumpregs(host);
- return;
- }
- host->use_busy = false;
-
- bcm2835_finish_command(host);
-}
-
-static void bcm2835_data_irq(struct bcm2835_host *host, u32 intmask)
+static int bcm2835_transmit(struct bcm2835_host *host)
{
+ u32 intmask = readl(host->ioaddr + SDHSTS);
int ret;
- /*
- * There are no dedicated data/space available interrupt
- * status bits, so it is necessary to use the single shared
- * data/space available FIFO status bits. It is therefore not
- * an error to get here when there is no data transfer in
- * progress.
- */
- if (!host->data)
- return;
-
+ /* Check for errors */
ret = bcm2835_check_data_error(host, intmask);
if (ret)
- goto finished;
-
- if (host->data->flags & MMC_DATA_WRITE) {
- /* Use the block interrupt for writes after the first block */
- host->hcfg &= ~(SDHCFG_DATA_IRPT_EN);
- host->hcfg |= SDHCFG_BLOCK_IRPT_EN;
- writel(host->hcfg, host->ioaddr + SDHCFG);
- bcm2835_transfer_pio(host);
- } else {
- bcm2835_transfer_pio(host);
- host->blocks--;
- if ((host->blocks == 0))
- goto finished;
- }
- return;
+ return ret;
-finished:
- host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
- writel(host->hcfg, host->ioaddr + SDHCFG);
-}
-
-static void bcm2835_data_threaded_irq(struct bcm2835_host *host)
-{
- if (!host->data)
- return;
- if ((host->blocks == 0))
- bcm2835_finish_data(host);
-}
-
-static void bcm2835_block_irq(struct bcm2835_host *host)
-{
- if (WARN_ON(!host->data)) {
- bcm2835_dumpregs(host);
- return;
- }
-
- WARN_ON(!host->blocks);
- if ((--host->blocks == 0))
- bcm2835_finish_data(host);
- else
- bcm2835_transfer_pio(host);
-}
+ ret = bcm2835_check_cmd_error(host, intmask);
+ if (ret)
+ return ret;
-static irqreturn_t bcm2835_irq(int irq, void *dev_id)
-{
- irqreturn_t result = IRQ_NONE;
- struct bcm2835_host *host = dev_id;
- u32 intmask;
-
- intmask = readl(host->ioaddr + SDHSTS);
-
- writel(SDHSTS_BUSY_IRPT |
- SDHSTS_BLOCK_IRPT |
- SDHSTS_SDIO_IRPT |
- SDHSTS_DATA_FLAG,
- host->ioaddr + SDHSTS);
-
- if (intmask & SDHSTS_BLOCK_IRPT) {
- bcm2835_check_data_error(host, intmask);
- host->irq_block = true;
- result = IRQ_WAKE_THREAD;
+ /* Handle wait for busy end */
+ if (host->use_busy && (intmask & SDHSTS_BUSY_IRPT)) {
+ writel(SDHSTS_BUSY_IRPT, host->ioaddr + SDHSTS);
+ host->use_busy = false;
+ bcm2835_finish_command(host);
}
- if (intmask & SDHSTS_BUSY_IRPT) {
- if (!bcm2835_check_cmd_error(host, intmask)) {
- host->irq_busy = true;
- result = IRQ_WAKE_THREAD;
- } else {
- result = IRQ_HANDLED;
+ /* Handle PIO data transfer */
+ if (host->data) {
+ ret = bcm2835_transfer_pio(host);
+ if (ret)
+ return ret;
+ host->blocks--;
+ if (host->blocks == 0) {
+ /* Wait for command to complete for real */
+ ret = bcm2835_wait_transfer_complete(host);
+ if (ret)
+ return ret;
+ /* Transfer complete */
+ host->data = NULL;
}
}
- /* There is no true data interrupt status bit, so it is
- * necessary to qualify the data flag with the interrupt
- * enable bit.
- */
- if ((intmask & SDHSTS_DATA_FLAG) &&
- (host->hcfg & SDHCFG_DATA_IRPT_EN)) {
- bcm2835_data_irq(host, intmask);
- host->irq_data = true;
- result = IRQ_WAKE_THREAD;
- }
-
- return result;
-}
-
-static irqreturn_t bcm2835_threaded_irq(int irq, void *dev_id)
-{
- struct bcm2835_host *host = dev_id;
-
- if (host->irq_block) {
- host->irq_block = false;
- bcm2835_block_irq(host);
- }
-
- if (host->irq_busy) {
- host->irq_busy = false;
- bcm2835_busy_irq(host);
- }
-
- if (host->irq_data) {
- host->irq_data = false;
- bcm2835_data_threaded_irq(host);
- }
-
- return IRQ_HANDLED;
-}
-
-static void bcm2835_irq_poll(struct bcm2835_host *host)
-{
- u32 intmask;
-
- while (1) {
- intmask = readl(host->ioaddr + SDHSTS);
- if (intmask & (SDHSTS_BUSY_IRPT | SDHSTS_BLOCK_IRPT |
- SDHSTS_SDIO_IRPT | SDHSTS_DATA_FLAG)) {
- bcm2835_irq(0, host);
- bcm2835_threaded_irq(0, host);
- return;
- }
- }
+ return 0;
}
static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
@@ -864,8 +690,11 @@ static int bcm2835_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
}
/* Wait for completion of busy signal or data transfer */
- while (host->use_busy || host->data)
- bcm2835_irq_poll(host);
+ while (host->use_busy || host->data) {
+ ret = bcm2835_transmit(host);
+ if (ret)
+ break;
+ }
return ret;
}
diff --git a/drivers/mmc/ftsdc010_mci.c b/drivers/mmc/ftsdc010_mci.c
index 00209e33889..9c15eb36d64 100644
--- a/drivers/mmc/ftsdc010_mci.c
+++ b/drivers/mmc/ftsdc010_mci.c
@@ -463,7 +463,7 @@ int ftsdc010_mmc_bind(struct udevice *dev)
}
static const struct udevice_id ftsdc010_mmc_ids[] = {
- { .compatible = "andestech,atsdc010" },
+ { .compatible = "andestech,atfsdc010" },
{ }
};
diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c
index 62577895eee..ecdb088ac4d 100644
--- a/drivers/mmc/renesas-sdhi.c
+++ b/drivers/mmc/renesas-sdhi.c
@@ -323,6 +323,7 @@ static const struct udevice_id renesas_sdhi_match[] = {
{ .compatible = "renesas,sdhi-r8a7796", .data = RENESAS_GEN3_QUIRKS },
{ .compatible = "renesas,sdhi-r8a77965", .data = RENESAS_GEN3_QUIRKS },
{ .compatible = "renesas,sdhi-r8a77970", .data = RENESAS_GEN3_QUIRKS },
+ { .compatible = "renesas,sdhi-r8a77990", .data = RENESAS_GEN3_QUIRKS },
{ .compatible = "renesas,sdhi-r8a77995", .data = RENESAS_GEN3_QUIRKS },
{ /* sentinel */ }
};
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 400f87e134d..40e28abda67 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -161,7 +161,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
/* We shouldn't wait for data inihibit for stop commands, even
though they might use busy signaling */
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION ||
- cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK)
+ cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
+ cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
mask &= ~SDHCI_DATA_INHIBIT;
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
@@ -183,7 +184,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
mask = SDHCI_INT_RESPONSE;
- if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK)
+ if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
+ cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
mask = SDHCI_INT_DATA_AVAIL;
if (!(cmd->resp_type & MMC_RSP_PRESENT))
@@ -201,7 +203,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
flags |= SDHCI_CMD_CRC;
if (cmd->resp_type & MMC_RSP_OPCODE)
flags |= SDHCI_CMD_INDEX;
- if (data || cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK)
+ if (data || cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
+ cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
flags |= SDHCI_CMD_DATA;
/* Set Transfer mode regarding to data flag */
diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c
index 11cc438ce6a..e8292c438d9 100644
--- a/drivers/mmc/stm32_sdmmc2.c
+++ b/drivers/mmc/stm32_sdmmc2.c
@@ -235,8 +235,8 @@ static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
static void stm32_sdmmc2_start_cmd(struct stm32_sdmmc2_priv *priv,
struct mmc_cmd *cmd, u32 cmd_param)
{
- if (readl(priv->base + SDMMC_ARG) & SDMMC_CMD_CPSMEN)
- writel(0, priv->base + SDMMC_ARG);
+ if (readl(priv->base + SDMMC_CMD) & SDMMC_CMD_CPSMEN)
+ writel(0, priv->base + SDMMC_CMD);
cmd_param |= cmd->cmdidx | SDMMC_CMD_CPSMEN;
if (cmd->resp_type & MMC_RSP_PRESENT) {
diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c
index f99731fb1ff..5b6d5256084 100644
--- a/drivers/mmc/zynq_sdhci.c
+++ b/drivers/mmc/zynq_sdhci.c
@@ -32,12 +32,21 @@ struct arasan_sdhci_priv {
};
#if defined(CONFIG_ARCH_ZYNQMP)
+#define MMC_HS200_BUS_SPEED 5
+
static const u8 mode2timing[] = {
- [UHS_SDR12] = UHS_SDR12_BUS_SPEED,
- [UHS_SDR25] = UHS_SDR25_BUS_SPEED,
- [UHS_SDR50] = UHS_SDR50_BUS_SPEED,
- [UHS_SDR104] = UHS_SDR104_BUS_SPEED,
- [UHS_DDR50] = UHS_DDR50_BUS_SPEED,
+ [MMC_LEGACY] = UHS_SDR12_BUS_SPEED,
+ [SD_LEGACY] = UHS_SDR12_BUS_SPEED,
+ [MMC_HS] = HIGH_SPEED_BUS_SPEED,
+ [SD_HS] = HIGH_SPEED_BUS_SPEED,
+ [MMC_HS_52] = HIGH_SPEED_BUS_SPEED,
+ [MMC_DDR_52] = HIGH_SPEED_BUS_SPEED,
+ [UHS_SDR12] = UHS_SDR12_BUS_SPEED,
+ [UHS_SDR25] = UHS_SDR25_BUS_SPEED,
+ [UHS_SDR50] = UHS_SDR50_BUS_SPEED,
+ [UHS_DDR50] = UHS_DDR50_BUS_SPEED,
+ [UHS_SDR104] = UHS_SDR104_BUS_SPEED,
+ [MMC_HS_200] = MMC_HS200_BUS_SPEED,
};
#define SDHCI_HOST_CTRL2 0x3E
@@ -160,9 +169,6 @@ static void arasan_sdhci_set_tapdelay(struct sdhci_host *host)
struct mmc *mmc = (struct mmc *)host->mmc;
u8 uhsmode;
- if (!IS_SD(mmc))
- return;
-
uhsmode = mode2timing[mmc->selected_mode];
if (uhsmode >= UHS_SDR25_BUS_SPEED)
@@ -175,6 +181,9 @@ static void arasan_sdhci_set_control_reg(struct sdhci_host *host)
struct mmc *mmc = (struct mmc *)host->mmc;
u32 reg;
+ if (!IS_SD(mmc))
+ return;
+
if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
reg = sdhci_readw(host, SDHCI_HOST_CTRL2);
reg |= SDHCI_18V_SIGNAL;
@@ -283,25 +292,21 @@ static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev)
return -1;
priv->host->name = dev->name;
- priv->host->ioaddr = (void *)devfdt_get_addr(dev);
-
- priv->deviceid = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
- "xlnx,device_id", -1);
- priv->bank = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
- "xlnx,mio_bank", -1);
- if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev),
- "no-1-8-v", NULL))
- priv->no_1p8 = 1;
- else
- priv->no_1p8 = 0;
#if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP)
priv->host->ops = &arasan_ops;
#endif
- plat->f_max = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
- "max-frequency", CONFIG_ZYNQ_SDHCI_MAX_FREQ);
+ priv->host->ioaddr = (void *)dev_read_addr(dev);
+ if (IS_ERR(priv->host->ioaddr))
+ return PTR_ERR(priv->host->ioaddr);
+
+ priv->deviceid = dev_read_u32_default(dev, "xlnx,device_id", -1);
+ priv->bank = dev_read_u32_default(dev, "xlnx,mio_bank", -1);
+ priv->no_1p8 = dev_read_bool(dev, "no-1-8-v");
+ plat->f_max = dev_read_u32_default(dev, "max-frequency",
+ CONFIG_ZYNQ_SDHCI_MAX_FREQ);
return 0;
}