summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/mmc/core
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/mmc/core
parentf93b97fd65072de626c074dbe099a1fff05ce060 (diff)
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page. During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/mmc/core')
-rw-r--r--kernel/drivers/mmc/core/Kconfig10
-rw-r--r--kernel/drivers/mmc/core/core.c415
-rw-r--r--kernel/drivers/mmc/core/core.h7
-rw-r--r--kernel/drivers/mmc/core/debugfs.c30
-rw-r--r--kernel/drivers/mmc/core/host.c323
-rw-r--r--kernel/drivers/mmc/core/host.h6
-rw-r--r--kernel/drivers/mmc/core/mmc.c233
-rw-r--r--kernel/drivers/mmc/core/mmc_ops.c53
-rw-r--r--kernel/drivers/mmc/core/mmc_ops.h4
-rw-r--r--kernel/drivers/mmc/core/pwrseq_emmc.c8
-rw-r--r--kernel/drivers/mmc/core/pwrseq_simple.c45
-rw-r--r--kernel/drivers/mmc/core/quirks.c18
-rw-r--r--kernel/drivers/mmc/core/sd.c136
-rw-r--r--kernel/drivers/mmc/core/sdio.c121
-rw-r--r--kernel/drivers/mmc/core/sdio_bus.c12
-rw-r--r--kernel/drivers/mmc/core/sdio_irq.c14
-rw-r--r--kernel/drivers/mmc/core/sdio_ops.h7
17 files changed, 764 insertions, 678 deletions
diff --git a/kernel/drivers/mmc/core/Kconfig b/kernel/drivers/mmc/core/Kconfig
index 9ebee72d9..4c33d7690 100644
--- a/kernel/drivers/mmc/core/Kconfig
+++ b/kernel/drivers/mmc/core/Kconfig
@@ -1,13 +1,3 @@
#
# MMC core configuration
#
-
-config MMC_CLKGATE
- bool "MMC host clock gating"
- help
- This will attempt to aggressively gate the clock to the MMC card.
- This is done to save power due to gating off the logic and bus
- noise when the MMC card is not in use. Your host driver has to
- support handling this in order for it to be of any use.
-
- If unsure, say N.
diff --git a/kernel/drivers/mmc/core/core.c b/kernel/drivers/mmc/core/core.c
index 588fb7908..5ae89e48f 100644
--- a/kernel/drivers/mmc/core/core.c
+++ b/kernel/drivers/mmc/core/core.c
@@ -133,6 +133,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd;
int err = cmd->error;
+ /* Flag re-tuning needed on CRC errors */
+ if ((cmd->opcode != MMC_SEND_TUNING_BLOCK &&
+ cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
+ (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
+ (mrq->data && mrq->data->error == -EILSEQ) ||
+ (mrq->stop && mrq->stop->error == -EILSEQ)))
+ mmc_retune_needed(host);
+
if (err && cmd->retries && mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
cmd->retries = 0;
@@ -179,19 +187,51 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
if (mrq->done)
mrq->done(mrq);
-
- mmc_host_clk_release(host);
}
}
EXPORT_SYMBOL(mmc_request_done);
+static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+{
+ int err;
+
+ /* Assumes host controller has been runtime resumed by mmc_claim_host */
+ err = mmc_retune(host);
+ if (err) {
+ mrq->cmd->error = err;
+ mmc_request_done(host, mrq);
+ return;
+ }
+
+ /*
+ * For sdio rw commands we must wait for card busy otherwise some
+ * sdio devices won't work properly.
+ */
+ if (mmc_is_io_op(mrq->cmd->opcode) && host->ops->card_busy) {
+ int tries = 500; /* Wait aprox 500ms at maximum */
+
+ while (host->ops->card_busy(host) && --tries)
+ mmc_delay(1);
+
+ if (tries == 0) {
+ mrq->cmd->error = -EBUSY;
+ mmc_request_done(host, mrq);
+ return;
+ }
+ }
+
+ host->ops->request(host, mrq);
+}
+
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_DEBUG
unsigned int i, sz;
struct scatterlist *sg;
#endif
+ mmc_retune_hold(host);
+
if (mmc_card_removed(host->card))
return -ENOMEDIUM;
@@ -250,9 +290,8 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->mrq = mrq;
}
}
- mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
return 0;
}
@@ -301,12 +340,15 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
use_busy_signal = false;
}
+ mmc_retune_hold(card->host);
+
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BKOPS_START, 1, timeout,
use_busy_signal, true, false);
if (err) {
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
+ mmc_retune_release(card->host);
goto out;
}
@@ -317,6 +359,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
*/
if (!use_busy_signal)
mmc_card_set_doing_bkops(card);
+ else
+ mmc_retune_release(card->host);
out:
mmc_release_host(card->host);
}
@@ -419,22 +463,22 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
host->areq);
break; /* return err */
} else {
+ mmc_retune_recheck(host);
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host),
cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
continue; /* wait for done/new event again */
}
} else if (context_info->is_new_req) {
context_info->is_new_req = false;
- if (!next_req) {
- err = MMC_BLK_NEW_REQUEST;
- break; /* return err */
- }
+ if (!next_req)
+ return MMC_BLK_NEW_REQUEST;
}
}
+ mmc_retune_release(host);
return err;
}
@@ -469,12 +513,16 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
mmc_card_removed(host->card))
break;
+ mmc_retune_recheck(host);
+
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host), cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
- host->ops->request(host, mrq);
+ __mmc_start_request(host, mrq);
}
+
+ mmc_retune_release(host);
}
/**
@@ -491,11 +539,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
bool is_first_req)
{
- if (host->ops->pre_req) {
- mmc_host_clk_hold(host);
+ if (host->ops->pre_req)
host->ops->pre_req(host, mrq, is_first_req);
- mmc_host_clk_release(host);
- }
}
/**
@@ -510,11 +555,8 @@ static void mmc_pre_req(struct mmc_host *host, struct mmc_request *mrq,
static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
int err)
{
- if (host->ops->post_req) {
- mmc_host_clk_hold(host);
+ if (host->ops->post_req)
host->ops->post_req(host, mrq, err);
- mmc_host_clk_release(host);
- }
}
/**
@@ -730,6 +772,7 @@ int mmc_stop_bkops(struct mmc_card *card)
*/
if (!err || (err == -EINVAL)) {
mmc_card_clr_doing_bkops(card);
+ mmc_retune_release(card->host);
err = 0;
}
@@ -798,9 +841,9 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
unsigned int timeout_us, limit_us;
timeout_us = data->timeout_ns / 1000;
- if (mmc_host_clk_rate(card->host))
+ if (card->host->ios.clock)
timeout_us += data->timeout_clks * 1000 /
- (mmc_host_clk_rate(card->host) / 1000);
+ (card->host->ios.clock / 1000);
if (data->flags & MMC_DATA_WRITE)
/*
@@ -998,8 +1041,6 @@ static inline void mmc_set_ios(struct mmc_host *host)
ios->power_mode, ios->chip_select, ios->vdd,
ios->bus_width, ios->timing);
- if (ios->clock > 0)
- mmc_set_ungated(host);
host->ops->set_ios(host, ios);
}
@@ -1008,17 +1049,15 @@ static inline void mmc_set_ios(struct mmc_host *host)
*/
void mmc_set_chip_select(struct mmc_host *host, int mode)
{
- mmc_host_clk_hold(host);
host->ios.chip_select = mode;
mmc_set_ios(host);
- mmc_host_clk_release(host);
}
/*
* Sets the host clock to the highest possible frequency that
* is below "hz".
*/
-static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
+void mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
WARN_ON(hz && hz < host->f_min);
@@ -1029,68 +1068,6 @@ static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
mmc_set_ios(host);
}
-void mmc_set_clock(struct mmc_host *host, unsigned int hz)
-{
- mmc_host_clk_hold(host);
- __mmc_set_clock(host, hz);
- mmc_host_clk_release(host);
-}
-
-#ifdef CONFIG_MMC_CLKGATE
-/*
- * This gates the clock by setting it to 0 Hz.
- */
-void mmc_gate_clock(struct mmc_host *host)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&host->clk_lock, flags);
- host->clk_old = host->ios.clock;
- host->ios.clock = 0;
- host->clk_gated = true;
- spin_unlock_irqrestore(&host->clk_lock, flags);
- mmc_set_ios(host);
-}
-
-/*
- * This restores the clock from gating by using the cached
- * clock value.
- */
-void mmc_ungate_clock(struct mmc_host *host)
-{
- /*
- * We should previously have gated the clock, so the clock shall
- * be 0 here! The clock may however be 0 during initialization,
- * when some request operations are performed before setting
- * the frequency. When ungate is requested in that situation
- * we just ignore the call.
- */
- if (host->clk_old) {
- BUG_ON(host->ios.clock);
- /* This call will also set host->clk_gated to false */
- __mmc_set_clock(host, host->clk_old);
- }
-}
-
-void mmc_set_ungated(struct mmc_host *host)
-{
- unsigned long flags;
-
- /*
- * We've been given a new frequency while the clock is gated,
- * so make sure we regard this as ungating it.
- */
- spin_lock_irqsave(&host->clk_lock, flags);
- host->clk_gated = false;
- spin_unlock_irqrestore(&host->clk_lock, flags);
-}
-
-#else
-void mmc_set_ungated(struct mmc_host *host)
-{
-}
-#endif
-
int mmc_execute_tuning(struct mmc_card *card)
{
struct mmc_host *host = card->host;
@@ -1105,12 +1082,12 @@ int mmc_execute_tuning(struct mmc_card *card)
else
opcode = MMC_SEND_TUNING_BLOCK;
- mmc_host_clk_hold(host);
err = host->ops->execute_tuning(host, opcode);
- mmc_host_clk_release(host);
if (err)
pr_err("%s: tuning execution failed\n", mmc_hostname(host));
+ else
+ mmc_retune_enable(host);
return err;
}
@@ -1120,10 +1097,8 @@ int mmc_execute_tuning(struct mmc_card *card)
*/
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
{
- mmc_host_clk_hold(host);
host->ios.bus_mode = mode;
mmc_set_ios(host);
- mmc_host_clk_release(host);
}
/*
@@ -1131,10 +1106,8 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
*/
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
{
- mmc_host_clk_hold(host);
host->ios.bus_width = width;
mmc_set_ios(host);
- mmc_host_clk_release(host);
}
/*
@@ -1142,6 +1115,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
*/
void mmc_set_initial_state(struct mmc_host *host)
{
+ mmc_retune_disable(host);
+
if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
else
@@ -1149,6 +1124,7 @@ void mmc_set_initial_state(struct mmc_host *host)
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
+ host->ios.drv_type = 0;
mmc_set_ios(host);
}
@@ -1301,6 +1277,40 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host,
#ifdef CONFIG_REGULATOR
/**
+ * mmc_ocrbitnum_to_vdd - Convert a OCR bit number to its voltage
+ * @vdd_bit: OCR bit number
+ * @min_uV: minimum voltage value (mV)
+ * @max_uV: maximum voltage value (mV)
+ *
+ * This function returns the voltage range according to the provided OCR
+ * bit number. If conversion is not possible a negative errno value returned.
+ */
+static int mmc_ocrbitnum_to_vdd(int vdd_bit, int *min_uV, int *max_uV)
+{
+ int tmp;
+
+ if (!vdd_bit)
+ return -EINVAL;
+
+ /*
+ * REVISIT mmc_vddrange_to_ocrmask() may have set some
+ * bits this regulator doesn't quite support ... don't
+ * be too picky, most cards and regulators are OK with
+ * a 0.1V range goof (it's a small error percentage).
+ */
+ tmp = vdd_bit - ilog2(MMC_VDD_165_195);
+ if (tmp == 0) {
+ *min_uV = 1650 * 1000;
+ *max_uV = 1950 * 1000;
+ } else {
+ *min_uV = 1900 * 1000 + tmp * 100 * 1000;
+ *max_uV = *min_uV + 100 * 1000;
+ }
+
+ return 0;
+}
+
+/**
* mmc_regulator_get_ocrmask - return mask of supported voltages
* @supply: regulator to use
*
@@ -1363,22 +1373,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
int min_uV, max_uV;
if (vdd_bit) {
- int tmp;
-
- /*
- * REVISIT mmc_vddrange_to_ocrmask() may have set some
- * bits this regulator doesn't quite support ... don't
- * be too picky, most cards and regulators are OK with
- * a 0.1V range goof (it's a small error percentage).
- */
- tmp = vdd_bit - ilog2(MMC_VDD_165_195);
- if (tmp == 0) {
- min_uV = 1650 * 1000;
- max_uV = 1950 * 1000;
- } else {
- min_uV = 1900 * 1000 + tmp * 100 * 1000;
- max_uV = min_uV + 100 * 1000;
- }
+ mmc_ocrbitnum_to_vdd(vdd_bit, &min_uV, &max_uV);
result = regulator_set_voltage(supply, min_uV, max_uV);
if (result == 0 && !mmc->regulator_enabled) {
@@ -1399,6 +1394,84 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
}
EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr);
+static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
+ int min_uV, int target_uV,
+ int max_uV)
+{
+ /*
+ * Check if supported first to avoid errors since we may try several
+ * signal levels during power up and don't want to show errors.
+ */
+ if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
+ return -EINVAL;
+
+ return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
+ max_uV);
+}
+
+/**
+ * mmc_regulator_set_vqmmc - Set VQMMC as per the ios
+ *
+ * For 3.3V signaling, we try to match VQMMC to VMMC as closely as possible.
+ * That will match the behavior of old boards where VQMMC and VMMC were supplied
+ * by the same supply. The Bus Operating conditions for 3.3V signaling in the
+ * SD card spec also define VQMMC in terms of VMMC.
+ * If this is not possible we'll try the full 2.7-3.6V of the spec.
+ *
+ * For 1.2V and 1.8V signaling we'll try to get as close as possible to the
+ * requested voltage. This is definitely a good idea for UHS where there's a
+ * separate regulator on the card that's trying to make 1.8V and it's best if
+ * we match.
+ *
+ * This function is expected to be used by a controller's
+ * start_signal_voltage_switch() function.
+ */
+int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct device *dev = mmc_dev(mmc);
+ int ret, volt, min_uV, max_uV;
+
+ /* If no vqmmc supply then we can't change the voltage */
+ if (IS_ERR(mmc->supply.vqmmc))
+ return -EINVAL;
+
+ switch (ios->signal_voltage) {
+ case MMC_SIGNAL_VOLTAGE_120:
+ return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ 1100000, 1200000, 1300000);
+ case MMC_SIGNAL_VOLTAGE_180:
+ return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ 1700000, 1800000, 1950000);
+ case MMC_SIGNAL_VOLTAGE_330:
+ ret = mmc_ocrbitnum_to_vdd(mmc->ios.vdd, &volt, &max_uV);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "%s: found vmmc voltage range of %d-%duV\n",
+ __func__, volt, max_uV);
+
+ min_uV = max(volt - 300000, 2700000);
+ max_uV = min(max_uV + 200000, 3600000);
+
+ /*
+ * Due to a limitation in the current implementation of
+ * regulator_set_voltage_triplet() which is taking the lowest
+ * voltage possible if below the target, search for a suitable
+ * voltage in two steps and try to stay close to vmmc
+ * with a 0.3V tolerance at first.
+ */
+ if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ min_uV, volt, max_uV))
+ return 0;
+
+ return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+ 2700000, volt, 3600000);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
+
#endif /* CONFIG_REGULATOR */
int mmc_regulator_get_supply(struct mmc_host *mmc)
@@ -1475,11 +1548,8 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
int old_signal_voltage = host->ios.signal_voltage;
host->ios.signal_voltage = signal_voltage;
- if (host->ops->start_signal_voltage_switch) {
- mmc_host_clk_hold(host);
+ if (host->ops->start_signal_voltage_switch)
err = host->ops->start_signal_voltage_switch(host, &host->ios);
- mmc_host_clk_release(host);
- }
if (err)
host->ios.signal_voltage = old_signal_voltage;
@@ -1513,20 +1583,17 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
pr_warn("%s: cannot verify signal voltage switch\n",
mmc_hostname(host));
- mmc_host_clk_hold(host);
-
cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
- goto err_command;
+ return err;
+
+ if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
+ return -EIO;
- if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) {
- err = -EIO;
- goto err_command;
- }
/*
* The card should drive cmd and dat[0:3] low immediately
* after the response of cmd11, but wait 1 ms to be sure
@@ -1553,8 +1620,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
goto power_cycle;
}
- /* Keep clock gated for at least 5 ms */
- mmc_delay(5);
+ /* Keep clock gated for at least 10 ms, though spec only says 5 ms */
+ mmc_delay(10);
host->ios.clock = clock;
mmc_set_ios(host);
@@ -1575,9 +1642,6 @@ power_cycle:
mmc_power_cycle(host, ocr);
}
-err_command:
- mmc_host_clk_release(host);
-
return err;
}
@@ -1586,10 +1650,8 @@ err_command:
*/
void mmc_set_timing(struct mmc_host *host, unsigned int timing)
{
- mmc_host_clk_hold(host);
host->ios.timing = timing;
mmc_set_ios(host);
- mmc_host_clk_release(host);
}
/*
@@ -1597,10 +1659,41 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
*/
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
{
- mmc_host_clk_hold(host);
host->ios.drv_type = drv_type;
mmc_set_ios(host);
- mmc_host_clk_release(host);
+}
+
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+ int card_drv_type, int *drv_type)
+{
+ struct mmc_host *host = card->host;
+ int host_drv_type = SD_DRIVER_TYPE_B;
+
+ *drv_type = 0;
+
+ if (!host->ops->select_drive_strength)
+ return 0;
+
+ /* Use SD definition of driver strength for hosts */
+ if (host->caps & MMC_CAP_DRIVER_TYPE_A)
+ host_drv_type |= SD_DRIVER_TYPE_A;
+
+ if (host->caps & MMC_CAP_DRIVER_TYPE_C)
+ host_drv_type |= SD_DRIVER_TYPE_C;
+
+ if (host->caps & MMC_CAP_DRIVER_TYPE_D)
+ host_drv_type |= SD_DRIVER_TYPE_D;
+
+ /*
+ * The drive strength that the hardware can support
+ * depends on the board design. Pass the appropriate
+ * information and let the hardware specific code
+ * return what is possible given the options
+ */
+ return host->ops->select_drive_strength(card, max_dtr,
+ host_drv_type,
+ card_drv_type,
+ drv_type);
}
/*
@@ -1619,8 +1712,6 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
if (host->ios.power_mode == MMC_POWER_ON)
return;
- mmc_host_clk_hold(host);
-
mmc_pwrseq_pre_power_on(host);
host->ios.vdd = fls(ocr) - 1;
@@ -1654,8 +1745,6 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
* time required to reach a stable voltage.
*/
mmc_delay(10);
-
- mmc_host_clk_release(host);
}
void mmc_power_off(struct mmc_host *host)
@@ -1663,8 +1752,6 @@ void mmc_power_off(struct mmc_host *host)
if (host->ios.power_mode == MMC_POWER_OFF)
return;
- mmc_host_clk_hold(host);
-
mmc_pwrseq_power_off(host);
host->ios.clock = 0;
@@ -1680,8 +1767,6 @@ void mmc_power_off(struct mmc_host *host)
* can be successfully turned on again.
*/
mmc_delay(1);
-
- mmc_host_clk_release(host);
}
void mmc_power_cycle(struct mmc_host *host, u32 ocr)
@@ -1897,7 +1982,7 @@ static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
*/
timeout_clks <<= 1;
timeout_us += (timeout_clks * 1000) /
- (mmc_host_clk_rate(card->host) / 1000);
+ (card->host->ios.clock / 1000);
erase_timeout = timeout_us / 1000;
@@ -1972,6 +2057,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
unsigned long timeout;
int err;
+ mmc_retune_hold(card->host);
+
/*
* qty is used to calculate the erase timeout which depends on how many
* erase groups (or allocation units in SD terminology) are affected.
@@ -2075,6 +2162,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
out:
+ mmc_retune_release(card->host);
return err;
}
@@ -2091,6 +2179,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
unsigned int arg)
{
unsigned int rem, to = from + nr;
+ int err;
if (!(card->host->caps & MMC_CAP_ERASE) ||
!(card->csd.cmdclass & CCC_ERASE))
@@ -2141,6 +2230,22 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
/* 'from' and 'to' are inclusive */
to -= 1;
+ /*
+ * Special case where only one erase-group fits in the timeout budget:
+ * If the region crosses an erase-group boundary on this particular
+ * case, we will be trimming more than one erase-group which, does not
+ * fit in the timeout budget of the controller, so we need to split it
+ * and call mmc_do_erase() twice if necessary. This special case is
+ * identified by the card->eg_boundary flag.
+ */
+ rem = card->erase_size - (from % card->erase_size);
+ if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && (nr > rem)) {
+ err = mmc_do_erase(card, from, from + rem - 1, arg);
+ from += rem;
+ if ((err) || (to <= from))
+ return err;
+ }
+
return mmc_do_erase(card, from, to, arg);
}
EXPORT_SYMBOL(mmc_erase);
@@ -2156,7 +2261,8 @@ EXPORT_SYMBOL(mmc_can_erase);
int mmc_can_trim(struct mmc_card *card)
{
- if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
+ if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) &&
+ (!(card->quirks & MMC_QUIRK_TRIM_BROKEN)))
return 1;
return 0;
}
@@ -2236,16 +2342,28 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
if (!qty)
return 0;
+ /*
+ * When specifying a sector range to trim, chances are we might cross
+ * an erase-group boundary even if the amount of sectors is less than
+ * one erase-group.
+ * If we can only fit one erase-group in the controller timeout budget,
+ * we have to care that erase-group boundaries are not crossed by a
+ * single trim operation. We flag that special case with "eg_boundary".
+ * In all other cases we can just decrement qty and pretend that we
+ * always touch (qty + 1) erase-groups as a simple optimization.
+ */
if (qty == 1)
- return 1;
+ card->eg_boundary = 1;
+ else
+ qty--;
/* Convert qty to sectors */
if (card->erase_shift)
- max_discard = --qty << card->erase_shift;
+ max_discard = qty << card->erase_shift;
else if (mmc_card_sd(card))
- max_discard = qty;
+ max_discard = qty + 1;
else
- max_discard = --qty * card->erase_size;
+ max_discard = qty * card->erase_size;
return max_discard;
}
@@ -2312,9 +2430,7 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
{
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
return;
- mmc_host_clk_hold(host);
host->ops->hw_reset(host);
- mmc_host_clk_release(host);
}
int mmc_hw_reset(struct mmc_host *host)
@@ -2333,7 +2449,8 @@ int mmc_hw_reset(struct mmc_host *host)
ret = host->bus_ops->reset(host);
mmc_bus_put(host);
- pr_warn("%s: tried to reset card\n", mmc_hostname(host));
+ if (ret != -EOPNOTSUPP)
+ pr_warn("%s: tried to reset card\n", mmc_hostname(host));
return ret;
}
@@ -2521,10 +2638,14 @@ void mmc_start_host(struct mmc_host *host)
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
+
+ mmc_claim_host(host);
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
+ mmc_release_host(host);
+
mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
}
@@ -2562,7 +2683,9 @@ void mmc_stop_host(struct mmc_host *host)
BUG_ON(host->card);
+ mmc_claim_host(host);
mmc_power_off(host);
+ mmc_release_host(host);
}
int mmc_power_save_host(struct mmc_host *host)
diff --git a/kernel/drivers/mmc/core/core.h b/kernel/drivers/mmc/core/core.h
index cfba3c05a..09241e56d 100644
--- a/kernel/drivers/mmc/core/core.h
+++ b/kernel/drivers/mmc/core/core.h
@@ -40,9 +40,6 @@ void mmc_init_erase(struct mmc_card *card);
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
-void mmc_gate_clock(struct mmc_host *host);
-void mmc_ungate_clock(struct mmc_host *host);
-void mmc_set_ungated(struct mmc_host *host);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
@@ -50,6 +47,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+ int card_drv_type, int *drv_type);
void mmc_power_up(struct mmc_host *host, u32 ocr);
void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
@@ -88,6 +87,8 @@ void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
int mmc_execute_tuning(struct mmc_card *card);
+int mmc_hs200_to_hs400(struct mmc_card *card);
+int mmc_hs400_to_hs200(struct mmc_card *card);
#endif
diff --git a/kernel/drivers/mmc/core/debugfs.c b/kernel/drivers/mmc/core/debugfs.c
index e9142108a..154aced0b 100644
--- a/kernel/drivers/mmc/core/debugfs.c
+++ b/kernel/drivers/mmc/core/debugfs.c
@@ -126,6 +126,12 @@ static int mmc_ios_show(struct seq_file *s, void *data)
case MMC_TIMING_SD_HS:
str = "sd high-speed";
break;
+ case MMC_TIMING_UHS_SDR12:
+ str = "sd uhs SDR12";
+ break;
+ case MMC_TIMING_UHS_SDR25:
+ str = "sd uhs SDR25";
+ break;
case MMC_TIMING_UHS_SDR50:
str = "sd uhs SDR50";
break;
@@ -166,6 +172,25 @@ static int mmc_ios_show(struct seq_file *s, void *data)
}
seq_printf(s, "signal voltage:\t%u (%s)\n", ios->chip_select, str);
+ switch (ios->drv_type) {
+ case MMC_SET_DRIVER_TYPE_A:
+ str = "driver type A";
+ break;
+ case MMC_SET_DRIVER_TYPE_B:
+ str = "driver type B";
+ break;
+ case MMC_SET_DRIVER_TYPE_C:
+ str = "driver type C";
+ break;
+ case MMC_SET_DRIVER_TYPE_D:
+ str = "driver type D";
+ break;
+ default:
+ str = "invalid";
+ break;
+ }
+ seq_printf(s, "driver type:\t%u (%s)\n", ios->drv_type, str);
+
return 0;
}
@@ -230,11 +255,6 @@ void mmc_add_host_debugfs(struct mmc_host *host)
&mmc_clock_fops))
goto err_node;
-#ifdef CONFIG_MMC_CLKGATE
- if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
- root, &host->clk_delay))
- goto err_node;
-#endif
#ifdef CONFIG_FAIL_MMC_REQUEST
if (fail_request)
setup_fault_attr(&fail_default_attr, fail_request);
diff --git a/kernel/drivers/mmc/core/host.c b/kernel/drivers/mmc/core/host.c
index 8be0df758..da950c442 100644
--- a/kernel/drivers/mmc/core/host.c
+++ b/kernel/drivers/mmc/core/host.c
@@ -61,245 +61,89 @@ void mmc_unregister_host_class(void)
class_unregister(&mmc_host_class);
}
-#ifdef CONFIG_MMC_CLKGATE
-static ssize_t clkgate_delay_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+void mmc_retune_enable(struct mmc_host *host)
{
- struct mmc_host *host = cls_dev_to_mmc_host(dev);
- return snprintf(buf, PAGE_SIZE, "%lu\n", host->clkgate_delay);
+ host->can_retune = 1;
+ if (host->retune_period)
+ mod_timer(&host->retune_timer,
+ jiffies + host->retune_period * HZ);
}
-static ssize_t clkgate_delay_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+void mmc_retune_disable(struct mmc_host *host)
{
- struct mmc_host *host = cls_dev_to_mmc_host(dev);
- unsigned long flags, value;
-
- if (kstrtoul(buf, 0, &value))
- return -EINVAL;
-
- spin_lock_irqsave(&host->clk_lock, flags);
- host->clkgate_delay = value;
- spin_unlock_irqrestore(&host->clk_lock, flags);
- return count;
+ host->can_retune = 0;
+ del_timer_sync(&host->retune_timer);
+ host->retune_now = 0;
+ host->need_retune = 0;
}
-/*
- * Enabling clock gating will make the core call out to the host
- * once up and once down when it performs a request or card operation
- * intermingled in any fashion. The driver will see this through
- * set_ios() operations with ios.clock field set to 0 to gate (disable)
- * the block clock, and to the old frequency to enable it again.
- */
-static void mmc_host_clk_gate_delayed(struct mmc_host *host)
+void mmc_retune_timer_stop(struct mmc_host *host)
{
- unsigned long tick_ns;
- unsigned long freq = host->ios.clock;
- unsigned long flags;
-
- if (!freq) {
- pr_debug("%s: frequency set to 0 in disable function, "
- "this means the clock is already disabled.\n",
- mmc_hostname(host));
- return;
- }
- /*
- * New requests may have appeared while we were scheduling,
- * then there is no reason to delay the check before
- * clk_disable().
- */
- spin_lock_irqsave(&host->clk_lock, flags);
-
- /*
- * Delay n bus cycles (at least 8 from MMC spec) before attempting
- * to disable the MCI block clock. The reference count may have
- * gone up again after this delay due to rescheduling!
- */
- if (!host->clk_requests) {
- spin_unlock_irqrestore(&host->clk_lock, flags);
- tick_ns = DIV_ROUND_UP(1000000000, freq);
- ndelay(host->clk_delay * tick_ns);
- } else {
- /* New users appeared while waiting for this work */
- spin_unlock_irqrestore(&host->clk_lock, flags);
- return;
- }
- mutex_lock(&host->clk_gate_mutex);
- spin_lock_irqsave(&host->clk_lock, flags);
- if (!host->clk_requests) {
- spin_unlock_irqrestore(&host->clk_lock, flags);
- /* This will set host->ios.clock to 0 */
- mmc_gate_clock(host);
- spin_lock_irqsave(&host->clk_lock, flags);
- pr_debug("%s: gated MCI clock\n", mmc_hostname(host));
- }
- spin_unlock_irqrestore(&host->clk_lock, flags);
- mutex_unlock(&host->clk_gate_mutex);
+ del_timer_sync(&host->retune_timer);
}
+EXPORT_SYMBOL(mmc_retune_timer_stop);
-/*
- * Internal work. Work to disable the clock at some later point.
- */
-static void mmc_host_clk_gate_work(struct work_struct *work)
+void mmc_retune_hold(struct mmc_host *host)
{
- struct mmc_host *host = container_of(work, struct mmc_host,
- clk_gate_work.work);
-
- mmc_host_clk_gate_delayed(host);
+ if (!host->hold_retune)
+ host->retune_now = 1;
+ host->hold_retune += 1;
}
-/**
- * mmc_host_clk_hold - ungate hardware MCI clocks
- * @host: host to ungate.
- *
- * Makes sure the host ios.clock is restored to a non-zero value
- * past this call. Increase clock reference count and ungate clock
- * if we're the first user.
- */
-void mmc_host_clk_hold(struct mmc_host *host)
+void mmc_retune_release(struct mmc_host *host)
{
- unsigned long flags;
-
- /* cancel any clock gating work scheduled by mmc_host_clk_release() */
- cancel_delayed_work_sync(&host->clk_gate_work);
- mutex_lock(&host->clk_gate_mutex);
- spin_lock_irqsave(&host->clk_lock, flags);
- if (host->clk_gated) {
- spin_unlock_irqrestore(&host->clk_lock, flags);
- mmc_ungate_clock(host);
- spin_lock_irqsave(&host->clk_lock, flags);
- pr_debug("%s: ungated MCI clock\n", mmc_hostname(host));
- }
- host->clk_requests++;
- spin_unlock_irqrestore(&host->clk_lock, flags);
- mutex_unlock(&host->clk_gate_mutex);
+ if (host->hold_retune)
+ host->hold_retune -= 1;
+ else
+ WARN_ON(1);
}
-/**
- * mmc_host_may_gate_card - check if this card may be gated
- * @card: card to check.
- */
-static bool mmc_host_may_gate_card(struct mmc_card *card)
+int mmc_retune(struct mmc_host *host)
{
- /* If there is no card we may gate it */
- if (!card)
- return true;
- /*
- * Don't gate SDIO cards! These need to be clocked at all times
- * since they may be independent systems generating interrupts
- * and other events. The clock requests counter from the core will
- * go down to zero since the core does not need it, but we will not
- * gate the clock, because there is somebody out there that may still
- * be using it.
- */
- return !(card->quirks & MMC_QUIRK_BROKEN_CLK_GATING);
-}
+ bool return_to_hs400 = false;
+ int err;
-/**
- * mmc_host_clk_release - gate off hardware MCI clocks
- * @host: host to gate.
- *
- * Calls the host driver with ios.clock set to zero as often as possible
- * in order to gate off hardware MCI clocks. Decrease clock reference
- * count and schedule disabling of clock.
- */
-void mmc_host_clk_release(struct mmc_host *host)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&host->clk_lock, flags);
- host->clk_requests--;
- if (mmc_host_may_gate_card(host->card) &&
- !host->clk_requests)
- schedule_delayed_work(&host->clk_gate_work,
- msecs_to_jiffies(host->clkgate_delay));
- spin_unlock_irqrestore(&host->clk_lock, flags);
-}
+ if (host->retune_now)
+ host->retune_now = 0;
+ else
+ return 0;
-/**
- * mmc_host_clk_rate - get current clock frequency setting
- * @host: host to get the clock frequency for.
- *
- * Returns current clock frequency regardless of gating.
- */
-unsigned int mmc_host_clk_rate(struct mmc_host *host)
-{
- unsigned long freq;
- unsigned long flags;
+ if (!host->need_retune || host->doing_retune || !host->card)
+ return 0;
- spin_lock_irqsave(&host->clk_lock, flags);
- if (host->clk_gated)
- freq = host->clk_old;
- else
- freq = host->ios.clock;
- spin_unlock_irqrestore(&host->clk_lock, flags);
- return freq;
-}
+ host->need_retune = 0;
-/**
- * mmc_host_clk_init - set up clock gating code
- * @host: host with potential clock to control
- */
-static inline void mmc_host_clk_init(struct mmc_host *host)
-{
- host->clk_requests = 0;
- /* Hold MCI clock for 8 cycles by default */
- host->clk_delay = 8;
- /*
- * Default clock gating delay is 0ms to avoid wasting power.
- * This value can be tuned by writing into sysfs entry.
- */
- host->clkgate_delay = 0;
- host->clk_gated = false;
- INIT_DELAYED_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
- spin_lock_init(&host->clk_lock);
- mutex_init(&host->clk_gate_mutex);
-}
+ host->doing_retune = 1;
-/**
- * mmc_host_clk_exit - shut down clock gating code
- * @host: host with potential clock to control
- */
-static inline void mmc_host_clk_exit(struct mmc_host *host)
-{
- /*
- * Wait for any outstanding gate and then make sure we're
- * ungated before exiting.
- */
- if (cancel_delayed_work_sync(&host->clk_gate_work))
- mmc_host_clk_gate_delayed(host);
- if (host->clk_gated)
- mmc_host_clk_hold(host);
- /* There should be only one user now */
- WARN_ON(host->clk_requests > 1);
-}
+ if (host->ios.timing == MMC_TIMING_MMC_HS400) {
+ err = mmc_hs400_to_hs200(host->card);
+ if (err)
+ goto out;
-static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
-{
- host->clkgate_delay_attr.show = clkgate_delay_show;
- host->clkgate_delay_attr.store = clkgate_delay_store;
- sysfs_attr_init(&host->clkgate_delay_attr.attr);
- host->clkgate_delay_attr.attr.name = "clkgate_delay";
- host->clkgate_delay_attr.attr.mode = S_IRUGO | S_IWUSR;
- if (device_create_file(&host->class_dev, &host->clkgate_delay_attr))
- pr_err("%s: Failed to create clkgate_delay sysfs entry\n",
- mmc_hostname(host));
-}
-#else
+ return_to_hs400 = true;
-static inline void mmc_host_clk_init(struct mmc_host *host)
-{
-}
+ if (host->ops->prepare_hs400_tuning)
+ host->ops->prepare_hs400_tuning(host, &host->ios);
+ }
-static inline void mmc_host_clk_exit(struct mmc_host *host)
-{
+ err = mmc_execute_tuning(host->card);
+ if (err)
+ goto out;
+
+ if (return_to_hs400)
+ err = mmc_hs200_to_hs400(host->card);
+out:
+ host->doing_retune = 0;
+
+ return err;
}
-static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
+static void mmc_retune_timer(unsigned long data)
{
-}
+ struct mmc_host *host = (struct mmc_host *)data;
-#endif
+ mmc_retune_needed(host);
+}
/**
* mmc_of_parse() - parse host's device-tree node
@@ -314,7 +158,7 @@ int mmc_of_parse(struct mmc_host *host)
{
struct device_node *np;
u32 bus_width;
- int len, ret;
+ int ret;
bool cd_cap_invert, cd_gpio_invert = false;
bool ro_cap_invert, ro_gpio_invert = false;
@@ -361,19 +205,19 @@ int mmc_of_parse(struct mmc_host *host)
*/
/* Parse Card Detection */
- if (of_find_property(np, "non-removable", &len)) {
+ if (of_property_read_bool(np, "non-removable")) {
host->caps |= MMC_CAP_NONREMOVABLE;
} else {
cd_cap_invert = of_property_read_bool(np, "cd-inverted");
- if (of_find_property(np, "broken-cd", &len))
+ if (of_property_read_bool(np, "broken-cd"))
host->caps |= MMC_CAP_NEEDS_POLL;
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
0, &cd_gpio_invert);
if (!ret)
dev_info(host->parent, "Got CD GPIO\n");
- else if (ret != -ENOENT)
+ else if (ret != -ENOENT && ret != -ENOSYS)
return ret;
/*
@@ -397,48 +241,53 @@ int mmc_of_parse(struct mmc_host *host)
ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
if (!ret)
dev_info(host->parent, "Got WP GPIO\n");
- else if (ret != -ENOENT)
+ else if (ret != -ENOENT && ret != -ENOSYS)
return ret;
+ if (of_property_read_bool(np, "disable-wp"))
+ host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
+
/* See the comment on CD inversion above */
if (ro_cap_invert ^ ro_gpio_invert)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
- if (of_find_property(np, "cap-sd-highspeed", &len))
+ if (of_property_read_bool(np, "cap-sd-highspeed"))
host->caps |= MMC_CAP_SD_HIGHSPEED;
- if (of_find_property(np, "cap-mmc-highspeed", &len))
+ if (of_property_read_bool(np, "cap-mmc-highspeed"))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
- if (of_find_property(np, "sd-uhs-sdr12", &len))
+ if (of_property_read_bool(np, "sd-uhs-sdr12"))
host->caps |= MMC_CAP_UHS_SDR12;
- if (of_find_property(np, "sd-uhs-sdr25", &len))
+ if (of_property_read_bool(np, "sd-uhs-sdr25"))
host->caps |= MMC_CAP_UHS_SDR25;
- if (of_find_property(np, "sd-uhs-sdr50", &len))
+ if (of_property_read_bool(np, "sd-uhs-sdr50"))
host->caps |= MMC_CAP_UHS_SDR50;
- if (of_find_property(np, "sd-uhs-sdr104", &len))
+ if (of_property_read_bool(np, "sd-uhs-sdr104"))
host->caps |= MMC_CAP_UHS_SDR104;
- if (of_find_property(np, "sd-uhs-ddr50", &len))
+ if (of_property_read_bool(np, "sd-uhs-ddr50"))
host->caps |= MMC_CAP_UHS_DDR50;
- if (of_find_property(np, "cap-power-off-card", &len))
+ if (of_property_read_bool(np, "cap-power-off-card"))
host->caps |= MMC_CAP_POWER_OFF_CARD;
- if (of_find_property(np, "cap-sdio-irq", &len))
+ if (of_property_read_bool(np, "cap-mmc-hw-reset"))
+ host->caps |= MMC_CAP_HW_RESET;
+ if (of_property_read_bool(np, "cap-sdio-irq"))
host->caps |= MMC_CAP_SDIO_IRQ;
- if (of_find_property(np, "full-pwr-cycle", &len))
+ if (of_property_read_bool(np, "full-pwr-cycle"))
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
- if (of_find_property(np, "keep-power-in-suspend", &len))
+ if (of_property_read_bool(np, "keep-power-in-suspend"))
host->pm_caps |= MMC_PM_KEEP_POWER;
- if (of_find_property(np, "enable-sdio-wakeup", &len))
+ if (of_property_read_bool(np, "enable-sdio-wakeup"))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
- if (of_find_property(np, "mmc-ddr-1_8v", &len))
+ if (of_property_read_bool(np, "mmc-ddr-1_8v"))
host->caps |= MMC_CAP_1_8V_DDR;
- if (of_find_property(np, "mmc-ddr-1_2v", &len))
+ if (of_property_read_bool(np, "mmc-ddr-1_2v"))
host->caps |= MMC_CAP_1_2V_DDR;
- if (of_find_property(np, "mmc-hs200-1_8v", &len))
+ if (of_property_read_bool(np, "mmc-hs200-1_8v"))
host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
- if (of_find_property(np, "mmc-hs200-1_2v", &len))
+ if (of_property_read_bool(np, "mmc-hs200-1_2v"))
host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
- if (of_find_property(np, "mmc-hs400-1_8v", &len))
+ if (of_property_read_bool(np, "mmc-hs400-1_8v"))
host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
- if (of_find_property(np, "mmc-hs400-1_2v", &len))
+ if (of_property_read_bool(np, "mmc-hs400-1_2v"))
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
@@ -496,14 +345,13 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
return NULL;
}
- mmc_host_clk_init(host);
-
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
+ setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
/*
* By default, hosts do not support SGIO or large requests.
@@ -545,7 +393,6 @@ int mmc_add_host(struct mmc_host *host)
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
- mmc_host_clk_sysfs_init(host);
mmc_start_host(host);
register_pm_notifier(&host->pm_notify);
@@ -575,8 +422,6 @@ void mmc_remove_host(struct mmc_host *host)
device_del(&host->class_dev);
led_trigger_unregister_simple(host->led);
-
- mmc_host_clk_exit(host);
}
EXPORT_SYMBOL(mmc_remove_host);
diff --git a/kernel/drivers/mmc/core/host.h b/kernel/drivers/mmc/core/host.h
index f2ab9e578..992bf5397 100644
--- a/kernel/drivers/mmc/core/host.h
+++ b/kernel/drivers/mmc/core/host.h
@@ -15,5 +15,11 @@
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
+void mmc_retune_hold(struct mmc_host *host);
+void mmc_retune_release(struct mmc_host *host);
+int mmc_retune(struct mmc_host *host);
+
#endif
diff --git a/kernel/drivers/mmc/core/mmc.c b/kernel/drivers/mmc/core/mmc.c
index f36c76f8b..3d5087b03 100644
--- a/kernel/drivers/mmc/core/mmc.c
+++ b/kernel/drivers/mmc/core/mmc.c
@@ -21,6 +21,7 @@
#include <linux/mmc/mmc.h>
#include "core.h"
+#include "host.h"
#include "bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
@@ -266,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
* calculate the enhanced data area offset, in bytes
*/
card->ext_csd.enhanced_area_offset =
- (ext_csd[139] << 24) + (ext_csd[138] << 16) +
- (ext_csd[137] << 8) + ext_csd[136];
+ (((unsigned long long)ext_csd[139]) << 24) +
+ (((unsigned long long)ext_csd[138]) << 16) +
+ (((unsigned long long)ext_csd[137]) << 8) +
+ (((unsigned long long)ext_csd[136]));
if (mmc_card_blockaddr(card))
card->ext_csd.enhanced_area_offset <<= 9;
/*
@@ -434,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.raw_trim_mult =
ext_csd[EXT_CSD_TRIM_MULT];
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
+ card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];
if (card->ext_csd.rev >= 4) {
if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
EXT_CSD_PART_SETTING_COMPLETED)
@@ -1036,10 +1040,26 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
return err;
}
+/* Caller must hold re-tuning */
+static int mmc_switch_status(struct mmc_card *card)
+{
+ u32 status;
+ int err;
+
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+
+ return mmc_switch_status_error(card->host, status);
+}
+
static int mmc_select_hs400(struct mmc_card *card)
{
struct mmc_host *host = card->host;
+ bool send_status = true;
+ unsigned int max_dtr;
int err = 0;
+ u8 val;
/*
* HS400 mode requires 8-bit bus width
@@ -1048,23 +1068,35 @@ static int mmc_select_hs400(struct mmc_card *card)
host->ios.bus_width == MMC_BUS_WIDTH_8))
return 0;
- /*
- * Before switching to dual data rate operation for HS400,
- * it is required to convert from HS200 mode to HS mode.
- */
- mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
- mmc_set_bus_speed(card);
+ if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ send_status = false;
+
+ /* Reduce frequency to HS frequency */
+ max_dtr = card->ext_csd.hs_max_dtr;
+ mmc_set_clock(host, max_dtr);
+ /* Switch card to HS mode */
+ val = EXT_CSD_TIMING_HS;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
+ EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
- true, true, true);
+ true, send_status, true);
if (err) {
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
mmc_hostname(host), err);
return err;
}
+ /* Set host controller to HS timing */
+ mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
+ /* Switch card to DDR */
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
EXT_CSD_DDR_BUS_WIDTH_8,
@@ -1075,20 +1107,130 @@ static int mmc_select_hs400(struct mmc_card *card)
return err;
}
+ /* Switch card to HS400 */
+ val = EXT_CSD_TIMING_HS400 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400,
+ EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
- true, true, true);
+ true, send_status, true);
if (err) {
pr_err("%s: switch to hs400 failed, err:%d\n",
mmc_hostname(host), err);
return err;
}
+ /* Set host controller to HS400 timing and frequency */
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card);
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
return 0;
+
+out_err:
+ pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+ __func__, err);
+ return err;
+}
+
+int mmc_hs200_to_hs400(struct mmc_card *card)
+{
+ return mmc_select_hs400(card);
+}
+
+int mmc_hs400_to_hs200(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ bool send_status = true;
+ unsigned int max_dtr;
+ int err;
+ u8 val;
+
+ if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ send_status = false;
+
+ /* Reduce frequency to HS */
+ max_dtr = card->ext_csd.hs_max_dtr;
+ mmc_set_clock(host, max_dtr);
+
+ /* Switch HS400 to HS DDR */
+ val = EXT_CSD_TIMING_HS;
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+ val, card->ext_csd.generic_cmd6_time,
+ true, send_status, true);
+ if (err)
+ goto out_err;
+
+ mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
+
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
+ /* Switch HS DDR to HS */
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
+ true, send_status, true);
+ if (err)
+ goto out_err;
+
+ mmc_set_timing(host, MMC_TIMING_MMC_HS);
+
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
+ /* Switch HS to HS200 */
+ val = EXT_CSD_TIMING_HS200 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+ val, card->ext_csd.generic_cmd6_time, true,
+ send_status, true);
+ if (err)
+ goto out_err;
+
+ mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+ }
+
+ mmc_set_bus_speed(card);
+
+ return 0;
+
+out_err:
+ pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+ __func__, err);
+ return err;
+}
+
+static void mmc_select_driver_type(struct mmc_card *card)
+{
+ int card_drv_type, drive_strength, drv_type;
+
+ card_drv_type = card->ext_csd.raw_driver_strength |
+ mmc_driver_type_mask(0);
+
+ drive_strength = mmc_select_drive_strength(card,
+ card->ext_csd.hs200_max_dtr,
+ card_drv_type, &drv_type);
+
+ card->drive_strength = drive_strength;
+
+ if (drv_type)
+ mmc_set_driver_type(card->host, drv_type);
}
/*
@@ -1101,7 +1243,10 @@ static int mmc_select_hs400(struct mmc_card *card)
static int mmc_select_hs200(struct mmc_card *card)
{
struct mmc_host *host = card->host;
+ bool send_status = true;
+ unsigned int old_timing;
int err = -EINVAL;
+ u8 val;
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
@@ -1113,20 +1258,41 @@ static int mmc_select_hs200(struct mmc_card *card)
if (err)
goto err;
+ mmc_select_driver_type(card);
+
+ if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ send_status = false;
+
/*
* Set the bus width(4 or 8) with host's support and
* switch to HS200 mode if bus width is set successfully.
*/
err = mmc_select_bus_width(card);
if (!IS_ERR_VALUE(err)) {
+ val = EXT_CSD_TIMING_HS200 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200,
+ EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
- true, true, true);
- if (!err)
- mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+ true, send_status, true);
+ if (err)
+ goto err;
+ old_timing = host->ios.timing;
+ mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+ if (!send_status) {
+ err = mmc_switch_status(card);
+ /*
+ * mmc_select_timing() assumes timing has not changed if
+ * it is a switch error.
+ */
+ if (err == -EBADMSG)
+ mmc_set_timing(host, old_timing);
+ }
}
err:
+ if (err)
+ pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+ __func__, err);
return err;
}
@@ -1511,9 +1677,12 @@ static int mmc_sleep(struct mmc_host *host)
unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
int err;
+ /* Re-tuning can't be done once the card is deselected */
+ mmc_retune_hold(host);
+
err = mmc_deselect_cards(host);
if (err)
- return err;
+ goto out_release;
cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16;
@@ -1534,7 +1703,7 @@ static int mmc_sleep(struct mmc_host *host)
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
- return err;
+ goto out_release;
/*
* If the host does not wait while the card signals busy, then we will
@@ -1545,6 +1714,8 @@ static int mmc_sleep(struct mmc_host *host)
if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(timeout_ms);
+out_release:
+ mmc_retune_release(host);
return err;
}
@@ -1782,17 +1953,6 @@ static int mmc_runtime_resume(struct mmc_host *host)
return 0;
}
-static int mmc_power_restore(struct mmc_host *host)
-{
- int ret;
-
- mmc_claim_host(host);
- ret = mmc_init_card(host, host->card->ocr, host->card);
- mmc_release_host(host);
-
- return ret;
-}
-
int mmc_can_reset(struct mmc_card *card)
{
u8 rst_n_function;
@@ -1807,7 +1967,6 @@ EXPORT_SYMBOL(mmc_can_reset);
static int mmc_reset(struct mmc_host *host)
{
struct mmc_card *card = host->card;
- u32 status;
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
return -EOPNOTSUPP;
@@ -1815,22 +1974,14 @@ static int mmc_reset(struct mmc_host *host)
if (!mmc_can_reset(card))
return -EOPNOTSUPP;
- mmc_host_clk_hold(host);
mmc_set_clock(host, host->f_init);
host->ops->hw_reset(host);
- /* If the reset has happened, then a status command will fail */
- if (!mmc_send_status(card, &status)) {
- mmc_host_clk_release(host);
- return -ENOSYS;
- }
-
/* Set initial state and call mmc_set_ios */
mmc_set_initial_state(host);
- mmc_host_clk_release(host);
- return mmc_power_restore(host);
+ return mmc_init_card(host, card->ocr, card);
}
static const struct mmc_bus_ops mmc_ops = {
@@ -1840,7 +1991,6 @@ static const struct mmc_bus_ops mmc_ops = {
.resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
.runtime_resume = mmc_runtime_resume,
- .power_restore = mmc_power_restore,
.alive = mmc_alive,
.shutdown = mmc_shutdown,
.reset = mmc_reset,
@@ -1897,14 +2047,13 @@ int mmc_attach_mmc(struct mmc_host *host)
mmc_release_host(host);
err = mmc_add_card(host->card);
- mmc_claim_host(host);
if (err)
goto remove_card;
+ mmc_claim_host(host);
return 0;
remove_card:
- mmc_release_host(host);
mmc_remove_card(host->card);
mmc_claim_host(host);
host->card = NULL;
diff --git a/kernel/drivers/mmc/core/mmc_ops.c b/kernel/drivers/mmc/core/mmc_ops.c
index 0ea042dc7..1f444269e 100644
--- a/kernel/drivers/mmc/core/mmc_ops.c
+++ b/kernel/drivers/mmc/core/mmc_ops.c
@@ -19,6 +19,7 @@
#include <linux/mmc/mmc.h>
#include "core.h"
+#include "host.h"
#include "mmc_ops.h"
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
@@ -449,6 +450,21 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
return err;
}
+int mmc_switch_status_error(struct mmc_host *host, u32 status)
+{
+ if (mmc_host_is_spi(host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (status & 0xFDFFA000)
+ pr_warn("%s: unexpected status %#x after switch\n",
+ mmc_hostname(host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+ return 0;
+}
+
/**
* __mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer
@@ -474,6 +490,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
u32 status = 0;
bool use_r1b_resp = use_busy_signal;
+ mmc_retune_hold(host);
+
/*
* If the cmd timeout and the max_busy_timeout of the host are both
* specified, let's validate them. A failure means we need to prevent
@@ -506,11 +524,11 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
- return err;
+ goto out;
/* No need to check card status in case of unblocking command */
if (!use_busy_signal)
- return 0;
+ goto out;
/*
* CRC errors shall only be ignored in cases were CMD13 is used to poll
@@ -529,7 +547,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
if (send_status) {
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
- return err;
+ goto out;
}
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
break;
@@ -543,31 +561,24 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
*/
if (!send_status) {
mmc_delay(timeout_ms);
- return 0;
+ goto out;
}
/* Timeout if the device never leaves the program state. */
if (time_after(jiffies, timeout)) {
pr_err("%s: Card stuck in programming state! %s\n",
mmc_hostname(host), __func__);
- return -ETIMEDOUT;
+ err = -ETIMEDOUT;
+ goto out;
}
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
- if (mmc_host_is_spi(host)) {
- if (status & R1_SPI_ILLEGAL_COMMAND)
- return -EBADMSG;
- } else {
- if (status & 0xFDFFA000)
- pr_warn("%s: unexpected status %#x after switch\n",
- mmc_hostname(host), status);
- if (status & R1_SWITCH_ERROR)
- return -EBADMSG;
- }
+ err = mmc_switch_status_error(host, status);
+out:
+ mmc_retune_release(host);
- return 0;
+ return err;
}
-EXPORT_SYMBOL_GPL(__mmc_switch);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
@@ -577,7 +588,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
}
EXPORT_SYMBOL_GPL(mmc_switch);
-int mmc_send_tuning(struct mmc_host *host)
+int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error)
{
struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
@@ -587,16 +598,13 @@ int mmc_send_tuning(struct mmc_host *host)
const u8 *tuning_block_pattern;
int size, err = 0;
u8 *data_buf;
- u32 opcode;
if (ios->bus_width == MMC_BUS_WIDTH_8) {
tuning_block_pattern = tuning_blk_pattern_8bit;
size = sizeof(tuning_blk_pattern_8bit);
- opcode = MMC_SEND_TUNING_BLOCK_HS200;
} else if (ios->bus_width == MMC_BUS_WIDTH_4) {
tuning_block_pattern = tuning_blk_pattern_4bit;
size = sizeof(tuning_blk_pattern_4bit);
- opcode = MMC_SEND_TUNING_BLOCK;
} else
return -EINVAL;
@@ -627,6 +635,9 @@ int mmc_send_tuning(struct mmc_host *host)
mmc_wait_for_req(host, &mrq);
+ if (cmd_error)
+ *cmd_error = cmd.error;
+
if (cmd.error) {
err = cmd.error;
goto out;
diff --git a/kernel/drivers/mmc/core/mmc_ops.h b/kernel/drivers/mmc/core/mmc_ops.h
index 6f4b00ed9..f1b8e81aa 100644
--- a/kernel/drivers/mmc/core/mmc_ops.h
+++ b/kernel/drivers/mmc/core/mmc_ops.h
@@ -27,6 +27,10 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
int mmc_can_ext_csd(struct mmc_card *card);
+int mmc_switch_status_error(struct mmc_host *host, u32 status);
+int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
+ unsigned int timeout_ms, bool use_busy_signal, bool send_status,
+ bool ignore_crc);
#endif
diff --git a/kernel/drivers/mmc/core/pwrseq_emmc.c b/kernel/drivers/mmc/core/pwrseq_emmc.c
index 9d6d2fb21..ad4f94ec7 100644
--- a/kernel/drivers/mmc/core/pwrseq_emmc.c
+++ b/kernel/drivers/mmc/core/pwrseq_emmc.c
@@ -76,7 +76,7 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
if (!pwrseq)
return ERR_PTR(-ENOMEM);
- pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW);
+ pwrseq->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(pwrseq->reset_gpio)) {
ret = PTR_ERR(pwrseq->reset_gpio);
goto free;
@@ -84,11 +84,11 @@ struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
/*
* register reset handler to ensure emmc reset also from
- * emergency_reboot(), priority 129 schedules it just before
- * system reboot
+ * emergency_reboot(), priority 255 is the highest priority
+ * so it will be executed before any system reboot handler.
*/
pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
- pwrseq->reset_nb.priority = 129;
+ pwrseq->reset_nb.priority = 255;
register_restart_handler(&pwrseq->reset_nb);
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
diff --git a/kernel/drivers/mmc/core/pwrseq_simple.c b/kernel/drivers/mmc/core/pwrseq_simple.c
index 0b14b83a5..d10538bb5 100644
--- a/kernel/drivers/mmc/core/pwrseq_simple.c
+++ b/kernel/drivers/mmc/core/pwrseq_simple.c
@@ -23,18 +23,21 @@ struct mmc_pwrseq_simple {
struct mmc_pwrseq pwrseq;
bool clk_enabled;
struct clk *ext_clk;
- int nr_gpios;
- struct gpio_desc *reset_gpios[0];
+ struct gpio_descs *reset_gpios;
};
static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
int value)
{
int i;
+ struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
+ int values[reset_gpios->ndescs];
- for (i = 0; i < pwrseq->nr_gpios; i++)
- if (!IS_ERR(pwrseq->reset_gpios[i]))
- gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value);
+ for (i = 0; i < reset_gpios->ndescs; i++)
+ values[i] = value;
+
+ gpiod_set_array_value_cansleep(reset_gpios->ndescs, reset_gpios->desc,
+ values);
}
static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
@@ -75,11 +78,8 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host)
{
struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
struct mmc_pwrseq_simple, pwrseq);
- int i;
- for (i = 0; i < pwrseq->nr_gpios; i++)
- if (!IS_ERR(pwrseq->reset_gpios[i]))
- gpiod_put(pwrseq->reset_gpios[i]);
+ gpiod_put_array(pwrseq->reset_gpios);
if (!IS_ERR(pwrseq->ext_clk))
clk_put(pwrseq->ext_clk);
@@ -98,14 +98,9 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
struct device *dev)
{
struct mmc_pwrseq_simple *pwrseq;
- int i, nr_gpios, ret = 0;
-
- nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios");
- if (nr_gpios < 0)
- nr_gpios = 0;
+ int ret = 0;
- pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios *
- sizeof(struct gpio_desc *), GFP_KERNEL);
+ pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);
if (!pwrseq)
return ERR_PTR(-ENOMEM);
@@ -116,22 +111,12 @@ struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
goto free;
}
- for (i = 0; i < nr_gpios; i++) {
- pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i,
- GPIOD_OUT_HIGH);
- if (IS_ERR(pwrseq->reset_gpios[i]) &&
- PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT &&
- PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) {
- ret = PTR_ERR(pwrseq->reset_gpios[i]);
-
- while (i--)
- gpiod_put(pwrseq->reset_gpios[i]);
-
- goto clk_put;
- }
+ pwrseq->reset_gpios = gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(pwrseq->reset_gpios)) {
+ ret = PTR_ERR(pwrseq->reset_gpios);
+ goto clk_put;
}
- pwrseq->nr_gpios = nr_gpios;
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
return &pwrseq->pwrseq;
diff --git a/kernel/drivers/mmc/core/quirks.c b/kernel/drivers/mmc/core/quirks.c
index dd1d1e0fe..fad660b95 100644
--- a/kernel/drivers/mmc/core/quirks.c
+++ b/kernel/drivers/mmc/core/quirks.c
@@ -35,25 +35,7 @@
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
#endif
-/*
- * This hook just adds a quirk for all sdio devices
- */
-static void add_quirk_for_sdio_devices(struct mmc_card *card, int data)
-{
- if (mmc_card_sdio(card))
- card->quirks |= data;
-}
-
static const struct mmc_fixup mmc_fixup_methods[] = {
- /* by default sdio devices are considered CLK_GATING broken */
- /* good cards will be whitelisted as they are tested */
- SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID,
- add_quirk_for_sdio_devices,
- MMC_QUIRK_BROKEN_CLK_GATING),
-
- SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
- remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
-
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
diff --git a/kernel/drivers/mmc/core/sd.c b/kernel/drivers/mmc/core/sd.c
index 31a9ef256..967535d76 100644
--- a/kernel/drivers/mmc/core/sd.c
+++ b/kernel/drivers/mmc/core/sd.c
@@ -357,8 +357,6 @@ int mmc_sd_switch_hs(struct mmc_card *card)
if (card->sw_caps.hs_max_dtr == 0)
return 0;
- err = -EIO;
-
status = kmalloc(64, GFP_KERNEL);
if (!status) {
pr_err("%s: could not allocate a buffer for "
@@ -386,64 +384,31 @@ out:
static int sd_select_driver_type(struct mmc_card *card, u8 *status)
{
- int host_drv_type = SD_DRIVER_TYPE_B;
- int card_drv_type = SD_DRIVER_TYPE_B;
- int drive_strength;
+ int card_drv_type, drive_strength, drv_type;
int err;
- /*
- * If the host doesn't support any of the Driver Types A,C or D,
- * or there is no board specific handler then default Driver
- * Type B is used.
- */
- if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
- | MMC_CAP_DRIVER_TYPE_D)))
- return 0;
-
- if (!card->host->ops->select_drive_strength)
- return 0;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
- host_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
- host_drv_type |= SD_DRIVER_TYPE_C;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
- host_drv_type |= SD_DRIVER_TYPE_D;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
- card_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
- card_drv_type |= SD_DRIVER_TYPE_C;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
- card_drv_type |= SD_DRIVER_TYPE_D;
+ card->drive_strength = 0;
- /*
- * The drive strength that the hardware can support
- * depends on the board design. Pass the appropriate
- * information and let the hardware specific code
- * return what is possible given the options
- */
- mmc_host_clk_hold(card->host);
- drive_strength = card->host->ops->select_drive_strength(
- card->sw_caps.uhs_max_dtr,
- host_drv_type, card_drv_type);
- mmc_host_clk_release(card->host);
+ card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
- err = mmc_sd_switch(card, 1, 2, drive_strength, status);
- if (err)
- return err;
+ drive_strength = mmc_select_drive_strength(card,
+ card->sw_caps.uhs_max_dtr,
+ card_drv_type, &drv_type);
- if ((status[15] & 0xF) != drive_strength) {
- pr_warn("%s: Problem setting drive strength!\n",
- mmc_hostname(card->host));
- return 0;
+ if (drive_strength) {
+ err = mmc_sd_switch(card, 1, 2, drive_strength, status);
+ if (err)
+ return err;
+ if ((status[15] & 0xF) != drive_strength) {
+ pr_warn("%s: Problem setting drive strength!\n",
+ mmc_hostname(card->host));
+ return 0;
+ }
+ card->drive_strength = drive_strength;
}
- mmc_set_driver_type(card->host, drive_strength);
+ if (drv_type)
+ mmc_set_driver_type(card->host, drv_type);
return 0;
}
@@ -661,9 +626,25 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
*/
if (!mmc_host_is_spi(card->host) &&
- (card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
- card->sd_bus_speed == UHS_SDR104_BUS_SPEED))
+ (card->host->ios.timing == MMC_TIMING_UHS_SDR50 ||
+ card->host->ios.timing == MMC_TIMING_UHS_DDR50 ||
+ card->host->ios.timing == MMC_TIMING_UHS_SDR104)) {
err = mmc_execute_tuning(card);
+
+ /*
+ * As SD Specifications Part1 Physical Layer Specification
+ * Version 3.01 says, CMD19 tuning is available for unlocked
+ * cards in transfer state of 1.8V signaling mode. The small
+ * difference between v3.00 and 3.01 spec means that CMD19
+ * tuning is also available for DDR50 mode.
+ */
+ if (err && card->host->ios.timing == MMC_TIMING_UHS_DDR50) {
+ pr_warn("%s: ddr50 tuning failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ }
+ }
+
out:
kfree(status);
@@ -804,6 +785,26 @@ int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
return 0;
}
+static int mmc_sd_get_ro(struct mmc_host *host)
+{
+ int ro;
+
+ /*
+ * Some systems don't feature a write-protect pin and don't need one.
+ * E.g. because they only have micro-SD card slot. For those systems
+ * assume that the SD card is always read-write.
+ */
+ if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
+ return 0;
+
+ if (!host->ops->get_ro)
+ return -1;
+
+ ro = host->ops->get_ro(host);
+
+ return ro;
+}
+
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
bool reinit)
{
@@ -855,13 +856,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
* Check if read-only switch is active.
*/
if (!reinit) {
- int ro = -1;
-
- if (host->ops->get_ro) {
- mmc_host_clk_hold(card->host);
- ro = host->ops->get_ro(host);
- mmc_host_clk_release(card->host);
- }
+ int ro = mmc_sd_get_ro(host);
if (ro < 0) {
pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
@@ -1181,21 +1176,10 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
return 0;
}
-static int mmc_sd_power_restore(struct mmc_host *host)
-{
- int ret;
-
- mmc_claim_host(host);
- ret = mmc_sd_init_card(host, host->card->ocr, host->card);
- mmc_release_host(host);
-
- return ret;
-}
-
static int mmc_sd_reset(struct mmc_host *host)
{
mmc_power_cycle(host, host->card->ocr);
- return mmc_sd_power_restore(host);
+ return mmc_sd_init_card(host, host->card->ocr, host->card);
}
static const struct mmc_bus_ops mmc_sd_ops = {
@@ -1205,7 +1189,6 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.runtime_resume = mmc_sd_runtime_resume,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
- .power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
.shutdown = mmc_sd_suspend,
.reset = mmc_sd_reset,
@@ -1260,14 +1243,13 @@ int mmc_attach_sd(struct mmc_host *host)
mmc_release_host(host);
err = mmc_add_card(host->card);
- mmc_claim_host(host);
if (err)
goto remove_card;
+ mmc_claim_host(host);
return 0;
remove_card:
- mmc_release_host(host);
mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host);
diff --git a/kernel/drivers/mmc/core/sdio.c b/kernel/drivers/mmc/core/sdio.c
index 5bc6c7dbb..467b3cf80 100644
--- a/kernel/drivers/mmc/core/sdio.c
+++ b/kernel/drivers/mmc/core/sdio.c
@@ -402,69 +402,38 @@ static unsigned char host_drive_to_sdio_drive(int host_strength)
static void sdio_select_driver_type(struct mmc_card *card)
{
- int host_drv_type = SD_DRIVER_TYPE_B;
- int card_drv_type = SD_DRIVER_TYPE_B;
- int drive_strength;
+ int card_drv_type, drive_strength, drv_type;
unsigned char card_strength;
int err;
- /*
- * If the host doesn't support any of the Driver Types A,C or D,
- * or there is no board specific handler then default Driver
- * Type B is used.
- */
- if (!(card->host->caps &
- (MMC_CAP_DRIVER_TYPE_A |
- MMC_CAP_DRIVER_TYPE_C |
- MMC_CAP_DRIVER_TYPE_D)))
- return;
-
- if (!card->host->ops->select_drive_strength)
- return;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
- host_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
- host_drv_type |= SD_DRIVER_TYPE_C;
+ card->drive_strength = 0;
- if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
- host_drv_type |= SD_DRIVER_TYPE_D;
+ card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
- card_drv_type |= SD_DRIVER_TYPE_A;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
- card_drv_type |= SD_DRIVER_TYPE_C;
-
- if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
- card_drv_type |= SD_DRIVER_TYPE_D;
-
- /*
- * The drive strength that the hardware can support
- * depends on the board design. Pass the appropriate
- * information and let the hardware specific code
- * return what is possible given the options
- */
- drive_strength = card->host->ops->select_drive_strength(
- card->sw_caps.uhs_max_dtr,
- host_drv_type, card_drv_type);
+ drive_strength = mmc_select_drive_strength(card,
+ card->sw_caps.uhs_max_dtr,
+ card_drv_type, &drv_type);
- /* if error just use default for drive strength B */
- err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
- &card_strength);
- if (err)
- return;
+ if (drive_strength) {
+ /* if error just use default for drive strength B */
+ err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
+ &card_strength);
+ if (err)
+ return;
- card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
- card_strength |= host_drive_to_sdio_drive(drive_strength);
+ card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
+ card_strength |= host_drive_to_sdio_drive(drive_strength);
- err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
- card_strength, NULL);
+ /* if error default to drive strength B */
+ err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
+ card_strength, NULL);
+ if (err)
+ return;
+ card->drive_strength = drive_strength;
+ }
- /* if error default to drive strength B */
- if (!err)
- mmc_set_driver_type(card->host, drive_strength);
+ if (drv_type)
+ mmc_set_driver_type(card->host, drv_type);
}
@@ -566,8 +535,8 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
*/
if (!mmc_host_is_spi(card->host) &&
- ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) ||
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)))
+ ((card->host->ios.timing == MMC_TIMING_UHS_SDR50) ||
+ (card->host->ios.timing == MMC_TIMING_UHS_SDR104)))
err = mmc_execute_tuning(card);
out:
return err;
@@ -661,7 +630,7 @@ try_again:
*/
if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
- ocr);
+ ocr_card);
if (err == -EAGAIN) {
sdio_reset(host);
mmc_go_idle(host);
@@ -928,14 +897,19 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host)
*/
static int mmc_sdio_suspend(struct mmc_host *host)
{
- if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
- mmc_claim_host(host);
+ mmc_claim_host(host);
+
+ if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host))
sdio_disable_wide(host->card);
- mmc_release_host(host);
- }
- if (!mmc_card_keep_power(host))
+ if (!mmc_card_keep_power(host)) {
mmc_power_off(host);
+ } else if (host->retune_period) {
+ mmc_retune_timer_stop(host);
+ mmc_retune_needed(host);
+ }
+
+ mmc_release_host(host);
return 0;
}
@@ -982,13 +956,10 @@ static int mmc_sdio_resume(struct mmc_host *host)
}
if (!err && host->sdio_irqs) {
- if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
+ if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD))
wake_up_process(host->sdio_irq_thread);
- } else if (host->caps & MMC_CAP_SDIO_IRQ) {
- mmc_host_clk_hold(host);
+ else if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 1);
- mmc_host_clk_release(host);
- }
}
mmc_release_host(host);
@@ -1045,14 +1016,29 @@ out:
static int mmc_sdio_runtime_suspend(struct mmc_host *host)
{
/* No references to the card, cut the power to it. */
+ mmc_claim_host(host);
mmc_power_off(host);
+ mmc_release_host(host);
+
return 0;
}
static int mmc_sdio_runtime_resume(struct mmc_host *host)
{
+ int ret;
+
/* Restore power and re-initialize. */
+ mmc_claim_host(host);
mmc_power_up(host, host->card->ocr);
+ ret = mmc_sdio_power_restore(host);
+ mmc_release_host(host);
+
+ return ret;
+}
+
+static int mmc_sdio_reset(struct mmc_host *host)
+{
+ mmc_power_cycle(host, host->card->ocr);
return mmc_sdio_power_restore(host);
}
@@ -1066,6 +1052,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
.runtime_resume = mmc_sdio_runtime_resume,
.power_restore = mmc_sdio_power_restore,
.alive = mmc_sdio_alive,
+ .reset = mmc_sdio_reset,
};
diff --git a/kernel/drivers/mmc/core/sdio_bus.c b/kernel/drivers/mmc/core/sdio_bus.c
index bee02e644..7e327a6dd 100644
--- a/kernel/drivers/mmc/core/sdio_bus.c
+++ b/kernel/drivers/mmc/core/sdio_bus.c
@@ -137,6 +137,10 @@ static int sdio_bus_probe(struct device *dev)
if (!id)
return -ENODEV;
+ ret = dev_pm_domain_attach(dev, false);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
/* Unbound SDIO functions are always suspended.
* During probe, the function is set active and the usage count
* is incremented. If the driver supports runtime PM,
@@ -166,6 +170,7 @@ static int sdio_bus_probe(struct device *dev)
disable_runtimepm:
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_noidle(dev);
+ dev_pm_domain_detach(dev, false);
return ret;
}
@@ -197,6 +202,8 @@ static int sdio_bus_remove(struct device *dev)
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_sync(dev);
+ dev_pm_domain_detach(dev, false);
+
return ret;
}
@@ -316,10 +323,8 @@ int sdio_add_func(struct sdio_func *func)
sdio_set_of_node(func);
sdio_acpi_set_handle(func);
ret = device_add(&func->dev);
- if (ret == 0) {
+ if (ret == 0)
sdio_func_set_present(func);
- dev_pm_domain_attach(&func->dev, false);
- }
return ret;
}
@@ -335,7 +340,6 @@ void sdio_remove_func(struct sdio_func *func)
if (!sdio_func_present(func))
return;
- dev_pm_domain_detach(&func->dev, false);
device_del(&func->dev);
of_node_put(func->dev.of_node);
put_device(&func->dev);
diff --git a/kernel/drivers/mmc/core/sdio_irq.c b/kernel/drivers/mmc/core/sdio_irq.c
index 09cc67d02..91bbbfb29 100644
--- a/kernel/drivers/mmc/core/sdio_irq.c
+++ b/kernel/drivers/mmc/core/sdio_irq.c
@@ -168,21 +168,15 @@ static int sdio_irq_thread(void *_host)
}
set_current_state(TASK_INTERRUPTIBLE);
- if (host->caps & MMC_CAP_SDIO_IRQ) {
- mmc_host_clk_hold(host);
+ if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 1);
- mmc_host_clk_release(host);
- }
if (!kthread_should_stop())
schedule_timeout(period);
set_current_state(TASK_RUNNING);
} while (!kthread_should_stop());
- if (host->caps & MMC_CAP_SDIO_IRQ) {
- mmc_host_clk_hold(host);
+ if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 0);
- mmc_host_clk_release(host);
- }
pr_debug("%s: IRQ thread exiting with code %d\n",
mmc_hostname(host), ret);
@@ -208,9 +202,7 @@ static int sdio_card_irq_get(struct mmc_card *card)
return err;
}
} else if (host->caps & MMC_CAP_SDIO_IRQ) {
- mmc_host_clk_hold(host);
host->ops->enable_sdio_irq(host, 1);
- mmc_host_clk_release(host);
}
}
@@ -229,9 +221,7 @@ static int sdio_card_irq_put(struct mmc_card *card)
atomic_set(&host->sdio_irq_thread_abort, 1);
kthread_stop(host->sdio_irq_thread);
} else if (host->caps & MMC_CAP_SDIO_IRQ) {
- mmc_host_clk_hold(host);
host->ops->enable_sdio_irq(host, 0);
- mmc_host_clk_release(host);
}
}
diff --git a/kernel/drivers/mmc/core/sdio_ops.h b/kernel/drivers/mmc/core/sdio_ops.h
index 12a4d3ab1..5660c7f45 100644
--- a/kernel/drivers/mmc/core/sdio_ops.h
+++ b/kernel/drivers/mmc/core/sdio_ops.h
@@ -12,6 +12,8 @@
#ifndef _MMC_SDIO_OPS_H
#define _MMC_SDIO_OPS_H
+#include <linux/mmc/sdio.h>
+
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8* out);
@@ -19,5 +21,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
int sdio_reset(struct mmc_host *host);
+static inline bool mmc_is_io_op(u32 opcode)
+{
+ return opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED;
+}
+
#endif