|
@@ -29,6 +29,7 @@
|
|
#include <linux/irq.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/mmc/host.h>
|
|
#include <linux/mmc/host.h>
|
|
#include <linux/mmc/mmc.h>
|
|
#include <linux/mmc/mmc.h>
|
|
|
|
+#include <linux/mmc/sd.h>
|
|
#include <linux/mmc/sdio.h>
|
|
#include <linux/mmc/sdio.h>
|
|
#include <linux/mmc/dw_mmc.h>
|
|
#include <linux/mmc/dw_mmc.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/bitops.h>
|
|
@@ -81,36 +82,6 @@ struct idmac_desc {
|
|
};
|
|
};
|
|
#endif /* CONFIG_MMC_DW_IDMAC */
|
|
#endif /* CONFIG_MMC_DW_IDMAC */
|
|
|
|
|
|
-static const u8 tuning_blk_pattern_4bit[] = {
|
|
|
|
- 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
|
|
|
|
- 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
|
|
|
|
- 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
|
|
|
|
- 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
|
|
|
|
- 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
|
|
|
|
- 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
|
|
|
|
- 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
|
|
|
|
- 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-static const u8 tuning_blk_pattern_8bit[] = {
|
|
|
|
- 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
|
|
|
|
- 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
|
|
|
|
- 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
|
|
|
|
- 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
|
|
|
|
- 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
|
|
|
|
- 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
|
|
|
|
- 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
|
|
|
|
- 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
|
|
|
|
- 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
|
|
|
|
- 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
|
|
|
|
- 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
|
|
|
|
- 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
|
|
|
|
- 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
|
|
|
|
- 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
|
|
|
|
- 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
|
|
|
|
- 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
static bool dw_mci_reset(struct dw_mci *host);
|
|
static bool dw_mci_reset(struct dw_mci *host);
|
|
|
|
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
@@ -234,10 +205,13 @@ err:
|
|
}
|
|
}
|
|
#endif /* defined(CONFIG_DEBUG_FS) */
|
|
#endif /* defined(CONFIG_DEBUG_FS) */
|
|
|
|
|
|
|
|
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg);
|
|
|
|
+
|
|
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|
{
|
|
{
|
|
struct mmc_data *data;
|
|
struct mmc_data *data;
|
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
|
|
+ struct dw_mci *host = slot->host;
|
|
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
|
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
|
u32 cmdr;
|
|
u32 cmdr;
|
|
cmd->error = -EINPROGRESS;
|
|
cmd->error = -EINPROGRESS;
|
|
@@ -253,6 +227,34 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|
else if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
|
|
else if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
|
|
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
|
|
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
|
|
|
|
|
|
|
|
+ if (cmd->opcode == SD_SWITCH_VOLTAGE) {
|
|
|
|
+ u32 clk_en_a;
|
|
|
|
+
|
|
|
|
+ /* Special bit makes CMD11 not die */
|
|
|
|
+ cmdr |= SDMMC_CMD_VOLT_SWITCH;
|
|
|
|
+
|
|
|
|
+ /* Change state to continue to handle CMD11 weirdness */
|
|
|
|
+ WARN_ON(slot->host->state != STATE_SENDING_CMD);
|
|
|
|
+ slot->host->state = STATE_SENDING_CMD11;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We need to disable low power mode (automatic clock stop)
|
|
|
|
+ * while doing voltage switch so we don't confuse the card,
|
|
|
|
+ * since stopping the clock is a specific part of the UHS
|
|
|
|
+ * voltage change dance.
|
|
|
|
+ *
|
|
|
|
+ * Note that low power mode (SDMMC_CLKEN_LOW_PWR) will be
|
|
|
|
+ * unconditionally turned back on in dw_mci_setup_bus() if it's
|
|
|
|
+ * ever called with a non-zero clock. That shouldn't happen
|
|
|
|
+ * until the voltage change is all done.
|
|
|
|
+ */
|
|
|
|
+ clk_en_a = mci_readl(host, CLKENA);
|
|
|
|
+ clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id);
|
|
|
|
+ mci_writel(host, CLKENA, clk_en_a);
|
|
|
|
+ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
|
|
|
|
+ SDMMC_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
|
/* We expect a response, so set this bit */
|
|
/* We expect a response, so set this bit */
|
|
cmdr |= SDMMC_CMD_RESP_EXP;
|
|
cmdr |= SDMMC_CMD_RESP_EXP;
|
|
@@ -775,11 +777,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|
unsigned int clock = slot->clock;
|
|
unsigned int clock = slot->clock;
|
|
u32 div;
|
|
u32 div;
|
|
u32 clk_en_a;
|
|
u32 clk_en_a;
|
|
|
|
+ u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT;
|
|
|
|
+
|
|
|
|
+ /* We must continue to set bit 28 in CMD until the change is complete */
|
|
|
|
+ if (host->state == STATE_WAITING_CMD11_DONE)
|
|
|
|
+ sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
|
|
|
|
|
|
if (!clock) {
|
|
if (!clock) {
|
|
mci_writel(host, CLKENA, 0);
|
|
mci_writel(host, CLKENA, 0);
|
|
- mci_send_cmd(slot,
|
|
|
|
- SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
|
|
+ mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
|
} else if (clock != host->current_speed || force_clkinit) {
|
|
} else if (clock != host->current_speed || force_clkinit) {
|
|
div = host->bus_hz / clock;
|
|
div = host->bus_hz / clock;
|
|
if (host->bus_hz % clock && host->bus_hz > clock)
|
|
if (host->bus_hz % clock && host->bus_hz > clock)
|
|
@@ -803,15 +809,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|
mci_writel(host, CLKSRC, 0);
|
|
mci_writel(host, CLKSRC, 0);
|
|
|
|
|
|
/* inform CIU */
|
|
/* inform CIU */
|
|
- mci_send_cmd(slot,
|
|
|
|
- SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
|
|
+ mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
|
|
|
|
|
/* set clock to desired speed */
|
|
/* set clock to desired speed */
|
|
mci_writel(host, CLKDIV, div);
|
|
mci_writel(host, CLKDIV, div);
|
|
|
|
|
|
/* inform CIU */
|
|
/* inform CIU */
|
|
- mci_send_cmd(slot,
|
|
|
|
- SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
|
|
+ mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
|
|
|
|
|
/* enable clock; only low power if no SDIO */
|
|
/* enable clock; only low power if no SDIO */
|
|
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
|
|
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
|
|
@@ -820,8 +824,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|
mci_writel(host, CLKENA, clk_en_a);
|
|
mci_writel(host, CLKENA, clk_en_a);
|
|
|
|
|
|
/* inform CIU */
|
|
/* inform CIU */
|
|
- mci_send_cmd(slot,
|
|
|
|
- SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
|
|
|
|
|
|
+ mci_send_cmd(slot, sdmmc_cmd_bits, 0);
|
|
|
|
|
|
/* keep the clock with reflecting clock dividor */
|
|
/* keep the clock with reflecting clock dividor */
|
|
slot->__clk_old = clock << div;
|
|
slot->__clk_old = clock << div;
|
|
@@ -897,6 +900,17 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
|
|
|
|
|
|
slot->mrq = mrq;
|
|
slot->mrq = mrq;
|
|
|
|
|
|
|
|
+ if (host->state == STATE_WAITING_CMD11_DONE) {
|
|
|
|
+ dev_warn(&slot->mmc->class_dev,
|
|
|
|
+ "Voltage change didn't complete\n");
|
|
|
|
+ /*
|
|
|
|
+ * this case isn't expected to happen, so we can
|
|
|
|
+ * either crash here or just try to continue on
|
|
|
|
+ * in the closest possible state
|
|
|
|
+ */
|
|
|
|
+ host->state = STATE_IDLE;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (host->state == STATE_IDLE) {
|
|
if (host->state == STATE_IDLE) {
|
|
host->state = STATE_SENDING_CMD;
|
|
host->state = STATE_SENDING_CMD;
|
|
dw_mci_start_request(host, slot);
|
|
dw_mci_start_request(host, slot);
|
|
@@ -936,6 +950,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
|
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
|
u32 regs;
|
|
u32 regs;
|
|
|
|
+ int ret;
|
|
|
|
|
|
switch (ios->bus_width) {
|
|
switch (ios->bus_width) {
|
|
case MMC_BUS_WIDTH_4:
|
|
case MMC_BUS_WIDTH_4:
|
|
@@ -972,14 +987,43 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
/* Slot specific timing and width adjustment */
|
|
/* Slot specific timing and width adjustment */
|
|
dw_mci_setup_bus(slot, false);
|
|
dw_mci_setup_bus(slot, false);
|
|
|
|
|
|
|
|
+ if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
|
|
|
|
+ slot->host->state = STATE_IDLE;
|
|
|
|
+
|
|
switch (ios->power_mode) {
|
|
switch (ios->power_mode) {
|
|
case MMC_POWER_UP:
|
|
case MMC_POWER_UP:
|
|
|
|
+ if (!IS_ERR(mmc->supply.vmmc)) {
|
|
|
|
+ ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
|
|
|
|
+ ios->vdd);
|
|
|
|
+ if (ret) {
|
|
|
|
+ dev_err(slot->host->dev,
|
|
|
|
+ "failed to enable vmmc regulator\n");
|
|
|
|
+ /*return, if failed turn on vmmc*/
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) {
|
|
|
|
+ ret = regulator_enable(mmc->supply.vqmmc);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ dev_err(slot->host->dev,
|
|
|
|
+ "failed to enable vqmmc regulator\n");
|
|
|
|
+ else
|
|
|
|
+ slot->host->vqmmc_enabled = true;
|
|
|
|
+ }
|
|
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
|
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
|
regs = mci_readl(slot->host, PWREN);
|
|
regs = mci_readl(slot->host, PWREN);
|
|
regs |= (1 << slot->id);
|
|
regs |= (1 << slot->id);
|
|
mci_writel(slot->host, PWREN, regs);
|
|
mci_writel(slot->host, PWREN, regs);
|
|
break;
|
|
break;
|
|
case MMC_POWER_OFF:
|
|
case MMC_POWER_OFF:
|
|
|
|
+ if (!IS_ERR(mmc->supply.vmmc))
|
|
|
|
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
|
|
|
+
|
|
|
|
+ if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) {
|
|
|
|
+ regulator_disable(mmc->supply.vqmmc);
|
|
|
|
+ slot->host->vqmmc_enabled = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
regs = mci_readl(slot->host, PWREN);
|
|
regs = mci_readl(slot->host, PWREN);
|
|
regs &= ~(1 << slot->id);
|
|
regs &= ~(1 << slot->id);
|
|
mci_writel(slot->host, PWREN, regs);
|
|
mci_writel(slot->host, PWREN, regs);
|
|
@@ -989,6 +1033,59 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int dw_mci_card_busy(struct mmc_host *mmc)
|
|
|
|
+{
|
|
|
|
+ struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
|
|
+ u32 status;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Check the busy bit which is low when DAT[3:0]
|
|
|
|
+ * (the data lines) are 0000
|
|
|
|
+ */
|
|
|
|
+ status = mci_readl(slot->host, STATUS);
|
|
|
|
+
|
|
|
|
+ return !!(status & SDMMC_STATUS_BUSY);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
|
+{
|
|
|
|
+ struct dw_mci_slot *slot = mmc_priv(mmc);
|
|
|
|
+ struct dw_mci *host = slot->host;
|
|
|
|
+ u32 uhs;
|
|
|
|
+ u32 v18 = SDMMC_UHS_18V << slot->id;
|
|
|
|
+ int min_uv, max_uv;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Program the voltage. Note that some instances of dw_mmc may use
|
|
|
|
+ * the UHS_REG for this. For other instances (like exynos) the UHS_REG
|
|
|
|
+ * does no harm but you need to set the regulator directly. Try both.
|
|
|
|
+ */
|
|
|
|
+ uhs = mci_readl(host, UHS_REG);
|
|
|
|
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
|
|
|
+ min_uv = 2700000;
|
|
|
|
+ max_uv = 3600000;
|
|
|
|
+ uhs &= ~v18;
|
|
|
|
+ } else {
|
|
|
|
+ min_uv = 1700000;
|
|
|
|
+ max_uv = 1950000;
|
|
|
|
+ uhs |= v18;
|
|
|
|
+ }
|
|
|
|
+ if (!IS_ERR(mmc->supply.vqmmc)) {
|
|
|
|
+ ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
|
|
|
|
+
|
|
|
|
+ if (ret) {
|
|
|
|
+ dev_err(&mmc->class_dev,
|
|
|
|
+ "Regulator set error %d: %d - %d\n",
|
|
|
|
+ ret, min_uv, max_uv);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mci_writel(host, UHS_REG, uhs);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int dw_mci_get_ro(struct mmc_host *mmc)
|
|
static int dw_mci_get_ro(struct mmc_host *mmc)
|
|
{
|
|
{
|
|
int read_only;
|
|
int read_only;
|
|
@@ -1131,6 +1228,9 @@ static const struct mmc_host_ops dw_mci_ops = {
|
|
.get_cd = dw_mci_get_cd,
|
|
.get_cd = dw_mci_get_cd,
|
|
.enable_sdio_irq = dw_mci_enable_sdio_irq,
|
|
.enable_sdio_irq = dw_mci_enable_sdio_irq,
|
|
.execute_tuning = dw_mci_execute_tuning,
|
|
.execute_tuning = dw_mci_execute_tuning,
|
|
|
|
+ .card_busy = dw_mci_card_busy,
|
|
|
|
+ .start_signal_voltage_switch = dw_mci_switch_voltage,
|
|
|
|
+
|
|
};
|
|
};
|
|
|
|
|
|
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
|
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
|
@@ -1154,7 +1254,11 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
|
|
dw_mci_start_request(host, slot);
|
|
dw_mci_start_request(host, slot);
|
|
} else {
|
|
} else {
|
|
dev_vdbg(host->dev, "list empty\n");
|
|
dev_vdbg(host->dev, "list empty\n");
|
|
- host->state = STATE_IDLE;
|
|
|
|
|
|
+
|
|
|
|
+ if (host->state == STATE_SENDING_CMD11)
|
|
|
|
+ host->state = STATE_WAITING_CMD11_DONE;
|
|
|
|
+ else
|
|
|
|
+ host->state = STATE_IDLE;
|
|
}
|
|
}
|
|
|
|
|
|
spin_unlock(&host->lock);
|
|
spin_unlock(&host->lock);
|
|
@@ -1265,8 +1369,10 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|
|
|
|
|
switch (state) {
|
|
switch (state) {
|
|
case STATE_IDLE:
|
|
case STATE_IDLE:
|
|
|
|
+ case STATE_WAITING_CMD11_DONE:
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ case STATE_SENDING_CMD11:
|
|
case STATE_SENDING_CMD:
|
|
case STATE_SENDING_CMD:
|
|
if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
|
|
if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
|
|
&host->pending_events))
|
|
&host->pending_events))
|
|
@@ -1299,6 +1405,14 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|
/* fall through */
|
|
/* fall through */
|
|
|
|
|
|
case STATE_SENDING_DATA:
|
|
case STATE_SENDING_DATA:
|
|
|
|
+ /*
|
|
|
|
+ * We could get a data error and never a transfer
|
|
|
|
+ * complete so we'd better check for it here.
|
|
|
|
+ *
|
|
|
|
+ * Note that we don't really care if we also got a
|
|
|
|
+ * transfer complete; stopping the DMA and sending an
|
|
|
|
+ * abort won't hurt.
|
|
|
|
+ */
|
|
if (test_and_clear_bit(EVENT_DATA_ERROR,
|
|
if (test_and_clear_bit(EVENT_DATA_ERROR,
|
|
&host->pending_events)) {
|
|
&host->pending_events)) {
|
|
dw_mci_stop_dma(host);
|
|
dw_mci_stop_dma(host);
|
|
@@ -1312,7 +1426,29 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|
break;
|
|
break;
|
|
|
|
|
|
set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
|
|
set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Handle an EVENT_DATA_ERROR that might have shown up
|
|
|
|
+ * before the transfer completed. This might not have
|
|
|
|
+ * been caught by the check above because the interrupt
|
|
|
|
+ * could have gone off between the previous check and
|
|
|
|
+ * the check for transfer complete.
|
|
|
|
+ *
|
|
|
|
+ * Technically this ought not be needed assuming we
|
|
|
|
+ * get a DATA_COMPLETE eventually (we'll notice the
|
|
|
|
+ * error and end the request), but it shouldn't hurt.
|
|
|
|
+ *
|
|
|
|
+ * This has the advantage of sending the stop command.
|
|
|
|
+ */
|
|
|
|
+ if (test_and_clear_bit(EVENT_DATA_ERROR,
|
|
|
|
+ &host->pending_events)) {
|
|
|
|
+ dw_mci_stop_dma(host);
|
|
|
|
+ send_stop_abort(host, data);
|
|
|
|
+ state = STATE_DATA_ERROR;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
prev_state = state = STATE_DATA_BUSY;
|
|
prev_state = state = STATE_DATA_BUSY;
|
|
|
|
+
|
|
/* fall through */
|
|
/* fall through */
|
|
|
|
|
|
case STATE_DATA_BUSY:
|
|
case STATE_DATA_BUSY:
|
|
@@ -1335,6 +1471,22 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|
/* stop command for open-ended transfer*/
|
|
/* stop command for open-ended transfer*/
|
|
if (data->stop)
|
|
if (data->stop)
|
|
send_stop_abort(host, data);
|
|
send_stop_abort(host, data);
|
|
|
|
+ } else {
|
|
|
|
+ /*
|
|
|
|
+ * If we don't have a command complete now we'll
|
|
|
|
+ * never get one since we just reset everything;
|
|
|
|
+ * better end the request.
|
|
|
|
+ *
|
|
|
|
+ * If we do have a command complete we'll fall
|
|
|
|
+ * through to the SENDING_STOP command and
|
|
|
|
+ * everything will be peachy keen.
|
|
|
|
+ */
|
|
|
|
+ if (!test_bit(EVENT_CMD_COMPLETE,
|
|
|
|
+ &host->pending_events)) {
|
|
|
|
+ host->cmd = NULL;
|
|
|
|
+ dw_mci_request_end(host, mrq);
|
|
|
|
+ goto unlock;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1821,6 +1973,14 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|
}
|
|
}
|
|
|
|
|
|
if (pending) {
|
|
if (pending) {
|
|
|
|
+ /* Check volt switch first, since it can look like an error */
|
|
|
|
+ if ((host->state == STATE_SENDING_CMD11) &&
|
|
|
|
+ (pending & SDMMC_INT_VOLT_SWITCH)) {
|
|
|
|
+ mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
|
|
|
|
+ pending &= ~SDMMC_INT_VOLT_SWITCH;
|
|
|
|
+ dw_mci_cmd_interrupt(host, pending);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
|
|
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
|
|
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
|
|
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
|
|
host->cmd_status = pending;
|
|
host->cmd_status = pending;
|
|
@@ -1926,7 +2086,9 @@ static void dw_mci_work_routine_card(struct work_struct *work)
|
|
|
|
|
|
switch (host->state) {
|
|
switch (host->state) {
|
|
case STATE_IDLE:
|
|
case STATE_IDLE:
|
|
|
|
+ case STATE_WAITING_CMD11_DONE:
|
|
break;
|
|
break;
|
|
|
|
+ case STATE_SENDING_CMD11:
|
|
case STATE_SENDING_CMD:
|
|
case STATE_SENDING_CMD:
|
|
mrq->cmd->error = -ENOMEDIUM;
|
|
mrq->cmd->error = -ENOMEDIUM;
|
|
if (!mrq->data)
|
|
if (!mrq->data)
|
|
@@ -2028,10 +2190,6 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
|
{
|
|
{
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
-static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
|
|
|
|
-{
|
|
|
|
- return NULL;
|
|
|
|
-}
|
|
|
|
#endif /* CONFIG_OF */
|
|
#endif /* CONFIG_OF */
|
|
|
|
|
|
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
@@ -2064,7 +2222,13 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
mmc->f_max = freq[1];
|
|
mmc->f_max = freq[1];
|
|
}
|
|
}
|
|
|
|
|
|
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
|
|
|
|
+ /*if there are external regulators, get them*/
|
|
|
|
+ ret = mmc_regulator_get_supply(mmc);
|
|
|
|
+ if (ret == -EPROBE_DEFER)
|
|
|
|
+ goto err_host_allocated;
|
|
|
|
+
|
|
|
|
+ if (!mmc->ocr_avail)
|
|
|
|
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
|
|
|
|
if (host->pdata->caps)
|
|
if (host->pdata->caps)
|
|
mmc->caps = host->pdata->caps;
|
|
mmc->caps = host->pdata->caps;
|
|
@@ -2085,7 +2249,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
if (host->pdata->caps2)
|
|
if (host->pdata->caps2)
|
|
mmc->caps2 = host->pdata->caps2;
|
|
mmc->caps2 = host->pdata->caps2;
|
|
|
|
|
|
- mmc_of_parse(mmc);
|
|
|
|
|
|
+ ret = mmc_of_parse(mmc);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto err_host_allocated;
|
|
|
|
|
|
if (host->pdata->blk_settings) {
|
|
if (host->pdata->blk_settings) {
|
|
mmc->max_segs = host->pdata->blk_settings->max_segs;
|
|
mmc->max_segs = host->pdata->blk_settings->max_segs;
|
|
@@ -2117,7 +2283,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
|
|
|
|
ret = mmc_add_host(mmc);
|
|
ret = mmc_add_host(mmc);
|
|
if (ret)
|
|
if (ret)
|
|
- goto err_setup_bus;
|
|
|
|
|
|
+ goto err_host_allocated;
|
|
|
|
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
#if defined(CONFIG_DEBUG_FS)
|
|
dw_mci_init_debugfs(slot);
|
|
dw_mci_init_debugfs(slot);
|
|
@@ -2128,9 +2294,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
-err_setup_bus:
|
|
|
|
|
|
+err_host_allocated:
|
|
mmc_free_host(mmc);
|
|
mmc_free_host(mmc);
|
|
- return -EINVAL;
|
|
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
|
|
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
|
|
@@ -2423,24 +2589,6 @@ int dw_mci_probe(struct dw_mci *host)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- host->vmmc = devm_regulator_get_optional(host->dev, "vmmc");
|
|
|
|
- if (IS_ERR(host->vmmc)) {
|
|
|
|
- ret = PTR_ERR(host->vmmc);
|
|
|
|
- if (ret == -EPROBE_DEFER)
|
|
|
|
- goto err_clk_ciu;
|
|
|
|
-
|
|
|
|
- dev_info(host->dev, "no vmmc regulator found: %d\n", ret);
|
|
|
|
- host->vmmc = NULL;
|
|
|
|
- } else {
|
|
|
|
- ret = regulator_enable(host->vmmc);
|
|
|
|
- if (ret) {
|
|
|
|
- if (ret != -EPROBE_DEFER)
|
|
|
|
- dev_err(host->dev,
|
|
|
|
- "regulator_enable fail: %d\n", ret);
|
|
|
|
- goto err_clk_ciu;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
host->quirks = host->pdata->quirks;
|
|
host->quirks = host->pdata->quirks;
|
|
|
|
|
|
spin_lock_init(&host->lock);
|
|
spin_lock_init(&host->lock);
|
|
@@ -2584,8 +2732,6 @@ err_workqueue:
|
|
err_dmaunmap:
|
|
err_dmaunmap:
|
|
if (host->use_dma && host->dma_ops->exit)
|
|
if (host->use_dma && host->dma_ops->exit)
|
|
host->dma_ops->exit(host);
|
|
host->dma_ops->exit(host);
|
|
- if (host->vmmc)
|
|
|
|
- regulator_disable(host->vmmc);
|
|
|
|
|
|
|
|
err_clk_ciu:
|
|
err_clk_ciu:
|
|
if (!IS_ERR(host->ciu_clk))
|
|
if (!IS_ERR(host->ciu_clk))
|
|
@@ -2621,9 +2767,6 @@ void dw_mci_remove(struct dw_mci *host)
|
|
if (host->use_dma && host->dma_ops->exit)
|
|
if (host->use_dma && host->dma_ops->exit)
|
|
host->dma_ops->exit(host);
|
|
host->dma_ops->exit(host);
|
|
|
|
|
|
- if (host->vmmc)
|
|
|
|
- regulator_disable(host->vmmc);
|
|
|
|
-
|
|
|
|
if (!IS_ERR(host->ciu_clk))
|
|
if (!IS_ERR(host->ciu_clk))
|
|
clk_disable_unprepare(host->ciu_clk);
|
|
clk_disable_unprepare(host->ciu_clk);
|
|
|
|
|
|
@@ -2640,9 +2783,6 @@ EXPORT_SYMBOL(dw_mci_remove);
|
|
*/
|
|
*/
|
|
int dw_mci_suspend(struct dw_mci *host)
|
|
int dw_mci_suspend(struct dw_mci *host)
|
|
{
|
|
{
|
|
- if (host->vmmc)
|
|
|
|
- regulator_disable(host->vmmc);
|
|
|
|
-
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(dw_mci_suspend);
|
|
EXPORT_SYMBOL(dw_mci_suspend);
|
|
@@ -2651,15 +2791,6 @@ int dw_mci_resume(struct dw_mci *host)
|
|
{
|
|
{
|
|
int i, ret;
|
|
int i, ret;
|
|
|
|
|
|
- if (host->vmmc) {
|
|
|
|
- ret = regulator_enable(host->vmmc);
|
|
|
|
- if (ret) {
|
|
|
|
- dev_err(host->dev,
|
|
|
|
- "failed to enable regulator: %d\n", ret);
|
|
|
|
- return ret;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) {
|
|
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) {
|
|
ret = -ENODEV;
|
|
ret = -ENODEV;
|
|
return ret;
|
|
return ret;
|