diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
commit | e5fd91f1ef340da553f7a79da9540c3db711c937 (patch) | |
tree | b11842027dc6641da63f4bcc524f8678263304a3 /drivers/mmc/core | |
parent | 2a9b0348e685a63d97486f6749622b61e9e3292f (diff) |
Linux-libre 4.2-gnu
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/core.c | 100 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 4 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 88 | ||||
-rw-r--r-- | drivers/mmc/core/host.h | 6 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 156 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 44 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 113 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 90 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 12 |
10 files changed, 438 insertions, 176 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 92e767142..9ad73f30f 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -133,6 +133,12 @@ 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 (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; @@ -186,12 +192,29 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) 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; + } + + 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; @@ -252,7 +275,7 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *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 +324,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 +343,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); } @@ -417,22 +445,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; } @@ -467,12 +495,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); } /** @@ -728,6 +760,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; } @@ -1109,6 +1142,8 @@ int mmc_execute_tuning(struct mmc_card *card) if (err) pr_err("%s: tuning execution failed\n", mmc_hostname(host)); + else + mmc_retune_enable(host); return err; } @@ -1140,6 +1175,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 @@ -1147,6 +1184,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); } @@ -1551,8 +1589,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); @@ -1601,6 +1639,44 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) 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; + int drive_strength; + + *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 + */ + mmc_host_clk_hold(host); + drive_strength = host->ops->select_drive_strength(card, max_dtr, + host_drv_type, + card_drv_type, + drv_type); + mmc_host_clk_release(host); + + return drive_strength; +} + /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. @@ -1970,6 +2046,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. @@ -2073,6 +2151,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; } @@ -2331,7 +2410,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; } diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index cfba3c05a..1a22a8220 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -50,6 +50,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 +90,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/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 8be0df758..99a9c9011 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -301,6 +301,90 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) #endif +void mmc_retune_enable(struct mmc_host *host) +{ + host->can_retune = 1; + if (host->retune_period) + mod_timer(&host->retune_timer, + jiffies + host->retune_period * HZ); +} + +void mmc_retune_disable(struct mmc_host *host) +{ + host->can_retune = 0; + del_timer_sync(&host->retune_timer); + host->retune_now = 0; + host->need_retune = 0; +} + +void mmc_retune_timer_stop(struct mmc_host *host) +{ + del_timer_sync(&host->retune_timer); +} +EXPORT_SYMBOL(mmc_retune_timer_stop); + +void mmc_retune_hold(struct mmc_host *host) +{ + if (!host->hold_retune) + host->retune_now = 1; + host->hold_retune += 1; +} + +void mmc_retune_release(struct mmc_host *host) +{ + if (host->hold_retune) + host->hold_retune -= 1; + else + WARN_ON(1); +} + +int mmc_retune(struct mmc_host *host) +{ + bool return_to_hs400 = false; + int err; + + if (host->retune_now) + host->retune_now = 0; + else + return 0; + + if (!host->need_retune || host->doing_retune || !host->card) + return 0; + + host->need_retune = 0; + + host->doing_retune = 1; + + if (host->ios.timing == MMC_TIMING_MMC_HS400) { + err = mmc_hs400_to_hs200(host->card); + if (err) + goto out; + + return_to_hs400 = true; + + if (host->ops->prepare_hs400_tuning) + host->ops->prepare_hs400_tuning(host, &host->ios); + } + + 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 void mmc_retune_timer(unsigned long data) +{ + struct mmc_host *host = (struct mmc_host *)data; + + mmc_retune_needed(host); +} + /** * mmc_of_parse() - parse host's device-tree node * @host: host whose node should be parsed. @@ -400,6 +484,9 @@ int mmc_of_parse(struct mmc_host *host) else if (ret != -ENOENT) 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; @@ -504,6 +591,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) #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. diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index f2ab9e578..992bf5397 100644 --- a/drivers/mmc/core/host.h +++ b/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/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f36c76f8b..e72690317 100644 --- a/drivers/mmc/core/mmc.c +++ b/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) @@ -1040,6 +1044,7 @@ static int mmc_select_hs400(struct mmc_card *card) { struct mmc_host *host = card->host; int err = 0; + u8 val; /* * HS400 mode requires 8-bit bus width @@ -1055,8 +1060,10 @@ static int mmc_select_hs400(struct mmc_card *card) mmc_set_timing(card->host, MMC_TIMING_MMC_HS); mmc_set_bus_speed(card); + val = EXT_CSD_TIMING_HS | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; 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); if (err) { @@ -1075,8 +1082,10 @@ static int mmc_select_hs400(struct mmc_card *card) return err; } + 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); if (err) { @@ -1091,6 +1100,115 @@ static int mmc_select_hs400(struct mmc_card *card) return 0; } +int mmc_hs200_to_hs400(struct mmc_card *card) +{ + return mmc_select_hs400(card); +} + +/* 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); +} + +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 | + 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_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); +} + /* * For device supporting HS200 mode, the following sequence * should be done before executing the tuning process. @@ -1102,6 +1220,7 @@ static int mmc_select_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; 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,14 +1232,18 @@ static int mmc_select_hs200(struct mmc_card *card) if (err) goto err; + mmc_select_driver_type(card); + /* * 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) @@ -1511,9 +1634,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 +1660,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 +1671,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 +1910,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; @@ -1830,7 +1947,7 @@ static int mmc_reset(struct mmc_host *host) 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 +1957,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, diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 0ea042dc7..0e9ae1c27 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/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,29 +561,23 @@ 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); diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 6f4b00ed9..f498f9ae2 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -27,6 +27,7 @@ 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); #endif diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 31a9ef256..4e7366ab1 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -386,64 +386,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; + card->drive_strength = 0; - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D) - card_drv_type |= SD_DRIVER_TYPE_D; + card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B; - /* - * 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); - - 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; } @@ -804,6 +771,28 @@ 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; + + mmc_host_clk_hold(host); + ro = host->ops->get_ro(host); + mmc_host_clk_release(host); + + return ro; +} + int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, bool reinit) { @@ -855,13 +844,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 +1164,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 +1177,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, diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 5bc6c7dbb..b91abedcf 100644 --- a/drivers/mmc/core/sdio.c +++ b/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); } @@ -934,8 +903,12 @@ static int mmc_sdio_suspend(struct mmc_host *host) 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); + } return 0; } @@ -1056,6 +1029,12 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host) return mmc_sdio_power_restore(host); } +static int mmc_sdio_reset(struct mmc_host *host) +{ + mmc_power_cycle(host, host->card->ocr); + return mmc_sdio_power_restore(host); +} + static const struct mmc_bus_ops mmc_sdio_ops = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, @@ -1066,6 +1045,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/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index bee02e644..7e327a6dd 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/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); |