diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/cd-gpio.c | 13 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 296 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 5 | ||||
-rw-r--r-- | drivers/mmc/core/host.h | 22 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 84 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 12 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 30 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 15 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_irq.c | 10 |
9 files changed, 212 insertions, 275 deletions
diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c index 082202ae4a0..29de31e260d 100644 --- a/drivers/mmc/core/cd-gpio.c +++ b/drivers/mmc/core/cd-gpio.c @@ -28,13 +28,17 @@ static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id) return IRQ_HANDLED; } -int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, - unsigned int irq, unsigned long flags) +int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio) { size_t len = strlen(dev_name(host->parent)) + 4; - struct mmc_cd_gpio *cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL); + struct mmc_cd_gpio *cd; + int irq = gpio_to_irq(gpio); int ret; + if (irq < 0) + return irq; + + cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL); if (!cd) return -ENOMEM; @@ -45,7 +49,8 @@ int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio, goto egpioreq; ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt, - flags, cd->label, host); + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + cd->label, host); if (ret < 0) goto eirqreq; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f545a3e6eb8..14f262e9246 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -188,6 +188,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) struct scatterlist *sg; #endif + if (mrq->sbc) { + pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n", + mmc_hostname(host), mrq->sbc->opcode, + mrq->sbc->arg, mrq->sbc->flags); + } + pr_debug("%s: starting CMD%u arg %08x flags %08x\n", mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); @@ -243,16 +249,17 @@ static void mmc_wait_done(struct mmc_request *mrq) complete(&mrq->completion); } -static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) +static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { init_completion(&mrq->completion); mrq->done = mmc_wait_done; if (mmc_card_removed(host->card)) { mrq->cmd->error = -ENOMEDIUM; complete(&mrq->completion); - return; + return -ENOMEDIUM; } mmc_start_request(host, mrq); + return 0; } static void mmc_wait_for_req_done(struct mmc_host *host, @@ -290,8 +297,11 @@ 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) + if (host->ops->pre_req) { + mmc_host_clk_hold(host); host->ops->pre_req(host, mrq, is_first_req); + mmc_host_clk_release(host); + } } /** @@ -306,8 +316,11 @@ 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) + if (host->ops->post_req) { + mmc_host_clk_hold(host); host->ops->post_req(host, mrq, err); + mmc_host_clk_release(host); + } } /** @@ -330,6 +343,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, struct mmc_async_req *areq, int *error) { int err = 0; + int start_err = 0; struct mmc_async_req *data = host->areq; /* Prepare a new request */ @@ -339,30 +353,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (host->areq) { mmc_wait_for_req_done(host, host->areq->mrq); err = host->areq->err_check(host->card, host->areq); - if (err) { - /* post process the completed failed request */ - mmc_post_req(host, host->areq->mrq, 0); - if (areq) - /* - * Cancel the new prepared request, because - * it can't run until the failed - * request has been properly handled. - */ - mmc_post_req(host, areq->mrq, -EINVAL); - - host->areq = NULL; - goto out; - } } - if (areq) - __mmc_start_req(host, areq->mrq); + if (!err && areq) + start_err = __mmc_start_req(host, areq->mrq); if (host->areq) mmc_post_req(host, host->areq->mrq, 0); - host->areq = areq; - out: + /* Cancel a prepared request if it was not started. */ + if ((err || start_err) && areq) + mmc_post_req(host, areq->mrq, -EINVAL); + + if (err) + host->areq = NULL; + else + host->areq = areq; + if (error) *error = err; return data; @@ -593,101 +600,6 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) EXPORT_SYMBOL(mmc_align_data_size); /** - * mmc_host_enable - enable a host. - * @host: mmc host to enable - * - * Hosts that support power saving can use the 'enable' and 'disable' - * methods to exit and enter power saving states. For more information - * see comments for struct mmc_host_ops. - */ -int mmc_host_enable(struct mmc_host *host) -{ - if (!(host->caps & MMC_CAP_DISABLE)) - return 0; - - if (host->en_dis_recurs) - return 0; - - if (host->nesting_cnt++) - return 0; - - cancel_delayed_work_sync(&host->disable); - - if (host->enabled) - return 0; - - if (host->ops->enable) { - int err; - - host->en_dis_recurs = 1; - err = host->ops->enable(host); - host->en_dis_recurs = 0; - - if (err) { - pr_debug("%s: enable error %d\n", - mmc_hostname(host), err); - return err; - } - } - host->enabled = 1; - return 0; -} -EXPORT_SYMBOL(mmc_host_enable); - -static int mmc_host_do_disable(struct mmc_host *host, int lazy) -{ - if (host->ops->disable) { - int err; - - host->en_dis_recurs = 1; - err = host->ops->disable(host, lazy); - host->en_dis_recurs = 0; - - if (err < 0) { - pr_debug("%s: disable error %d\n", - mmc_hostname(host), err); - return err; - } - if (err > 0) { - unsigned long delay = msecs_to_jiffies(err); - - mmc_schedule_delayed_work(&host->disable, delay); - } - } - host->enabled = 0; - return 0; -} - -/** - * mmc_host_disable - disable a host. - * @host: mmc host to disable - * - * Hosts that support power saving can use the 'enable' and 'disable' - * methods to exit and enter power saving states. For more information - * see comments for struct mmc_host_ops. - */ -int mmc_host_disable(struct mmc_host *host) -{ - int err; - - if (!(host->caps & MMC_CAP_DISABLE)) - return 0; - - if (host->en_dis_recurs) - return 0; - - if (--host->nesting_cnt) - return 0; - - if (!host->enabled) - return 0; - - err = mmc_host_do_disable(host, 0); - return err; -} -EXPORT_SYMBOL(mmc_host_disable); - -/** * __mmc_claim_host - exclusively claim a host * @host: mmc host to claim * @abort: whether or not the operation should be aborted @@ -725,8 +637,8 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) wake_up(&host->wq); spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); - if (!stop) - mmc_host_enable(host); + if (host->ops->enable && !stop && host->claim_cnt == 1) + host->ops->enable(host); return stop; } @@ -751,21 +663,28 @@ int mmc_try_claim_host(struct mmc_host *host) claimed_host = 1; } spin_unlock_irqrestore(&host->lock, flags); + if (host->ops->enable && claimed_host && host->claim_cnt == 1) + host->ops->enable(host); return claimed_host; } EXPORT_SYMBOL(mmc_try_claim_host); /** - * mmc_do_release_host - release a claimed host + * mmc_release_host - release a host * @host: mmc host to release * - * If you successfully claimed a host, this function will - * release it again. + * Release a MMC host, allowing others to claim the host + * for their operations. */ -void mmc_do_release_host(struct mmc_host *host) +void mmc_release_host(struct mmc_host *host) { unsigned long flags; + WARN_ON(!host->claimed); + + if (host->ops->disable && host->claim_cnt == 1) + host->ops->disable(host); + spin_lock_irqsave(&host->lock, flags); if (--host->claim_cnt) { /* Release for nested claim */ @@ -777,67 +696,6 @@ void mmc_do_release_host(struct mmc_host *host) wake_up(&host->wq); } } -EXPORT_SYMBOL(mmc_do_release_host); - -void mmc_host_deeper_disable(struct work_struct *work) -{ - struct mmc_host *host = - container_of(work, struct mmc_host, disable.work); - - /* If the host is claimed then we do not want to disable it anymore */ - if (!mmc_try_claim_host(host)) - return; - mmc_host_do_disable(host, 1); - mmc_do_release_host(host); -} - -/** - * mmc_host_lazy_disable - lazily disable a host. - * @host: mmc host to disable - * - * Hosts that support power saving can use the 'enable' and 'disable' - * methods to exit and enter power saving states. For more information - * see comments for struct mmc_host_ops. - */ -int mmc_host_lazy_disable(struct mmc_host *host) -{ - if (!(host->caps & MMC_CAP_DISABLE)) - return 0; - - if (host->en_dis_recurs) - return 0; - - if (--host->nesting_cnt) - return 0; - - if (!host->enabled) - return 0; - - if (host->disable_delay) { - mmc_schedule_delayed_work(&host->disable, - msecs_to_jiffies(host->disable_delay)); - return 0; - } else - return mmc_host_do_disable(host, 1); -} -EXPORT_SYMBOL(mmc_host_lazy_disable); - -/** - * mmc_release_host - release a host - * @host: mmc host to release - * - * Release a MMC host, allowing others to claim the host - * for their operations. - */ -void mmc_release_host(struct mmc_host *host) -{ - WARN_ON(!host->claimed); - - mmc_host_lazy_disable(host); - - mmc_do_release_host(host); -} - EXPORT_SYMBOL(mmc_release_host); /* @@ -1121,6 +979,10 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, * might not allow this operation */ voltage = regulator_get_voltage(supply); + + if (mmc->caps2 & MMC_CAP2_BROKEN_VOLTAGE) + min_uV = max_uV = voltage; + if (voltage < 0) result = voltage; else if (voltage < min_uV || voltage > max_uV) @@ -1203,8 +1065,11 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11 host->ios.signal_voltage = signal_voltage; - if (host->ops->start_signal_voltage_switch) + if (host->ops->start_signal_voltage_switch) { + mmc_host_clk_hold(host); err = host->ops->start_signal_voltage_switch(host, &host->ios); + mmc_host_clk_release(host); + } return err; } @@ -1239,6 +1104,7 @@ static void mmc_poweroff_notify(struct mmc_host *host) int err = 0; card = host->card; + mmc_claim_host(host); /* * Send power notify command only if card @@ -1269,6 +1135,7 @@ static void mmc_poweroff_notify(struct mmc_host *host) /* Set the card state to no notification after the poweroff */ card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION; } + mmc_release_host(host); } /* @@ -1327,12 +1194,28 @@ static void mmc_power_up(struct mmc_host *host) void mmc_power_off(struct mmc_host *host) { + int err = 0; mmc_host_clk_hold(host); host->ios.clock = 0; host->ios.vdd = 0; - mmc_poweroff_notify(host); + /* + * For eMMC 4.5 device send AWAKE command before + * POWER_OFF_NOTIFY command, because in sleep state + * eMMC 4.5 devices respond to only RESET and AWAKE cmd + */ + if (host->card && mmc_card_is_sleep(host->card) && + host->bus_ops->resume) { + err = host->bus_ops->resume(host); + + if (!err) + mmc_poweroff_notify(host); + else + pr_warning("%s: error %d during resume " + "(continue with poweroff sequence)\n", + mmc_hostname(host), err); + } /* * Reset ocr mask to be the highest possible voltage supported for @@ -2033,6 +1916,9 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) */ mmc_hw_reset_for_init(host); + /* Initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + /* * sdio_reset sends CMD52 to reset card. Since we do not know * if the card is being re-initialized, just send it. CMD52 @@ -2077,18 +1963,36 @@ int _mmc_detect_card_removed(struct mmc_host *host) int mmc_detect_card_removed(struct mmc_host *host) { struct mmc_card *card = host->card; + int ret; WARN_ON(!host->claimed); + + if (!card) + return 1; + + ret = mmc_card_removed(card); /* * The card will be considered unchanged unless we have been asked to * detect a change or host requires polling to provide card detection. */ - if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL)) - return mmc_card_removed(card); + if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) && + !(host->caps2 & MMC_CAP2_DETECT_ON_ERR)) + return ret; host->detect_change = 0; + if (!ret) { + ret = _mmc_detect_card_removed(host); + if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) { + /* + * Schedule a detect work as soon as possible to let a + * rescan handle the card removal. + */ + cancel_delayed_work(&host->detect); + mmc_detect_change(host, 0); + } + } - return _mmc_detect_card_removed(host); + return ret; } EXPORT_SYMBOL(mmc_detect_card_removed); @@ -2165,8 +2069,6 @@ void mmc_stop_host(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); #endif - if (host->caps & MMC_CAP_DISABLE) - cancel_delayed_work(&host->disable); cancel_delayed_work_sync(&host->detect); mmc_flush_scheduled_work(); @@ -2361,13 +2263,11 @@ int mmc_suspend_host(struct mmc_host *host) { int err = 0; - if (host->caps & MMC_CAP_DISABLE) - cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); if (mmc_try_claim_host(host)) { err = mmc_cache_ctrl(host, 0); - mmc_do_release_host(host); + mmc_release_host(host); } else { err = -EBUSY; } @@ -2386,15 +2286,9 @@ int mmc_suspend_host(struct mmc_host *host) */ if (mmc_try_claim_host(host)) { if (host->bus_ops->suspend) { - /* - * For eMMC 4.5 device send notify command - * before sleep, because in sleep state eMMC 4.5 - * devices respond to only RESET and AWAKE cmd - */ - mmc_poweroff_notify(host); err = host->bus_ops->suspend(host); } - mmc_do_release_host(host); + mmc_release_host(host); if (err == -ENOSYS || !host->bus_ops->resume) { /* diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 30055f2b0d4..91c84c7a182 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -238,10 +238,10 @@ static inline void mmc_host_clk_init(struct mmc_host *host) /* Hold MCI clock for 8 cycles by default */ host->clk_delay = 8; /* - * Default clock gating delay is 200ms. + * Default clock gating delay is 0ms to avoid wasting power. * This value can be tuned by writing into sysfs entry. */ - host->clkgate_delay = 200; + 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); @@ -330,7 +330,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); - INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; #endif diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index fb8a5cd2e4a..f2ab9e57812 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -15,27 +15,5 @@ int mmc_register_host_class(void); void mmc_unregister_host_class(void); -#ifdef CONFIG_MMC_CLKGATE -void mmc_host_clk_hold(struct mmc_host *host); -void mmc_host_clk_release(struct mmc_host *host); -unsigned int mmc_host_clk_rate(struct mmc_host *host); - -#else -static inline void mmc_host_clk_hold(struct mmc_host *host) -{ -} - -static inline void mmc_host_clk_release(struct mmc_host *host) -{ -} - -static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) -{ - return host->ios.clock; -} -#endif - -void mmc_host_deeper_disable(struct work_struct *work); - #endif diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 59b9ba52e66..02914d609a9 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -376,7 +376,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) } card->ext_csd.raw_hc_erase_gap_size = - ext_csd[EXT_CSD_PARTITION_ATTRIBUTE]; + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; card->ext_csd.raw_sec_trim_mult = ext_csd[EXT_CSD_SEC_TRIM_MULT]; card->ext_csd.raw_sec_erase_mult = @@ -519,6 +519,20 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_CACHE_SIZE + 1] << 8 | ext_csd[EXT_CSD_CACHE_SIZE + 2] << 16 | ext_csd[EXT_CSD_CACHE_SIZE + 3] << 24; + + if (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 1) + card->ext_csd.data_sector_size = 4096; + else + card->ext_csd.data_sector_size = 512; + + if ((ext_csd[EXT_CSD_DATA_TAG_SUPPORT] & 1) && + (ext_csd[EXT_CSD_TAG_UNIT_SIZE] <= 8)) { + card->ext_csd.data_tag_unit_size = + ((unsigned int) 1 << ext_csd[EXT_CSD_TAG_UNIT_SIZE]) * + (card->ext_csd.data_sector_size); + } else { + card->ext_csd.data_tag_unit_size = 0; + } } out: @@ -551,7 +565,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) goto out; /* only compare read only fields */ - err = (!(card->ext_csd.raw_partition_support == + err = !((card->ext_csd.raw_partition_support == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && (card->ext_csd.raw_erased_mem_count == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && @@ -816,6 +830,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (!mmc_host_is_spi(host)) mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); + /* Initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle @@ -935,7 +952,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF * bit. This bit will be lost every time after a reset or power off. */ - if (card->ext_csd.enhanced_area_en) { + if (card->ext_csd.enhanced_area_en || + (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1, card->ext_csd.generic_cmd6_time); @@ -1006,7 +1024,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_select_hs200(card); else if (host->caps & MMC_CAP_MMC_HIGHSPEED) err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1, 0); + EXT_CSD_HS_TIMING, 1, + card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; @@ -1029,22 +1048,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* - * Enable HPI feature (if supported) - */ - if (card->ext_csd.hpi) { - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HPI_MGMT, 1, 0); - if (err && err != -EBADMSG) - goto free_card; - if (err) { - pr_warning("%s: Enabling HPI failed\n", - mmc_hostname(card->host)); - err = 0; - } else - card->ext_csd.hpi_en = 1; - } - - /* * Compute bus speed. */ max_dtr = (unsigned int)-1; @@ -1093,9 +1096,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * 4. execute tuning for HS200 */ if ((host->caps2 & MMC_CAP2_HS200) && - card->host->ops->execute_tuning) + card->host->ops->execute_tuning) { + mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK_HS200); + mmc_host_clk_release(card->host); + } if (err) { pr_warning("%s: tuning execution failed\n", mmc_hostname(card->host)); @@ -1116,7 +1122,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * Activate wide bus and DDR (if supported). */ if (!mmc_card_hs200(card) && - (card->csd.mmca_vsn >= CSD_SPEC_VER_3) && + (card->csd.mmca_vsn >= CSD_SPEC_VER_4) && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { static unsigned ext_csd_bits[][2] = { { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, @@ -1215,6 +1221,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* + * Enable HPI feature (if supported) + */ + if (card->ext_csd.hpi) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HPI_MGMT, 1, + card->ext_csd.generic_cmd6_time); + if (err && err != -EBADMSG) + goto free_card; + if (err) { + pr_warning("%s: Enabling HPI failed\n", + mmc_hostname(card->host)); + err = 0; + } else + card->ext_csd.hpi_en = 1; + } + + /* * If cache size is higher than 0, this indicates * the existence of cache and it can be turned on. */ @@ -1315,11 +1338,13 @@ static int mmc_suspend(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - if (mmc_card_can_sleep(host)) + if (mmc_card_can_sleep(host)) { err = mmc_card_sleep(host); - else if (!mmc_host_is_spi(host)) + if (!err) + mmc_card_set_sleep(host->card); + } else if (!mmc_host_is_spi(host)) mmc_deselect_cards(host); - host->card->state &= ~MMC_STATE_HIGHSPEED; + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_release_host(host); return err; @@ -1339,7 +1364,11 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - err = mmc_init_card(host, host->ocr, host->card); + if (mmc_card_is_sleep(host->card)) { + err = mmc_card_awake(host); + mmc_card_clr_sleep(host->card); + } else + err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); return err; @@ -1349,7 +1378,8 @@ static int mmc_power_restore(struct mmc_host *host) { int ret; - host->card->state &= ~MMC_STATE_HIGHSPEED; + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); + mmc_card_clr_sleep(host->card); mmc_claim_host(host); ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 4d41fa984c9..69370f494e0 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -553,18 +553,22 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) { struct mmc_command cmd = {0}; unsigned int opcode; - unsigned int flags; int err; + if (!card->ext_csd.hpi) { + pr_warning("%s: Card didn't support HPI command\n", + mmc_hostname(card->host)); + return -EINVAL; + } + opcode = card->ext_csd.hpi_cmd; if (opcode == MMC_STOP_TRANSMISSION) - flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; else if (opcode == MMC_SEND_STATUS) - flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.opcode = opcode; cmd.arg = card->rca << 16 | 1; - cmd.flags = flags; cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time; err = mmc_wait_for_cmd(card->host, &cmd, 0); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index c63ad03c29c..c272c6868ec 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -451,9 +451,11 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) * 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) @@ -660,9 +662,12 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) goto out; /* SPI mode doesn't define CMD19 */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) { + mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK); + mmc_host_clk_release(card->host); + } out: kfree(status); @@ -850,8 +855,11 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, if (!reinit) { int ro = -1; - if (host->ops->get_ro) + if (host->ops->get_ro) { + mmc_host_clk_hold(card->host); ro = host->ops->get_ro(host); + mmc_host_clk_release(card->host); + } if (ro < 0) { pr_warning("%s: host does not " @@ -903,6 +911,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, BUG_ON(!host); WARN_ON(!host->claimed); + /* The initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + err = mmc_sd_get_cid(host, ocr, cid, &rocr); if (err) return err; @@ -967,8 +978,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, * Since initialization is now complete, enable preset * value registers for UHS-I cards. */ - if (host->ops->enable_preset_value) + if (host->ops->enable_preset_value) { + mmc_host_clk_hold(card->host); host->ops->enable_preset_value(host, true); + mmc_host_clk_release(card->host); + } } else { /* * Attempt to change to high-speed (if supported) @@ -1145,14 +1159,12 @@ int mmc_attach_sd(struct mmc_host *host) BUG_ON(!host); WARN_ON(!host->claimed); - /* Make sure we are at 3.3V signalling voltage */ - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false); - if (err) - return err; - /* Disable preset value enable if already set since last time */ - if (host->ops->enable_preset_value) + if (host->ops->enable_preset_value) { + mmc_host_clk_hold(host); host->ops->enable_preset_value(host, false); + mmc_host_clk_release(host); + } err = mmc_send_app_op_cond(host, 0, &ocr); if (err) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index bd7bacc950d..2c7c83f832d 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -98,10 +98,11 @@ fail: return ret; } -static int sdio_read_cccr(struct mmc_card *card) +static int sdio_read_cccr(struct mmc_card *card, u32 ocr) { int ret; int cccr_vsn; + int uhs = ocr & R4_18V_PRESENT; unsigned char data; unsigned char speed; @@ -149,7 +150,7 @@ static int sdio_read_cccr(struct mmc_card *card) card->scr.sda_spec3 = 0; card->sw_caps.sd3_bus_mode = 0; card->sw_caps.sd3_drv_type = 0; - if (cccr_vsn >= SDIO_CCCR_REV_3_00) { + if (cccr_vsn >= SDIO_CCCR_REV_3_00 && uhs) { card->scr.sda_spec3 = 1; ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_UHS, 0, &data); @@ -584,6 +585,9 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, * Inform the card of the voltage */ if (!powered_resume) { + /* The initialization should be done at 3.3 V I/O voltage. */ + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + err = mmc_send_io_op_cond(host, host->ocr, &ocr); if (err) goto err; @@ -712,7 +716,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * Read the common registers. */ - err = sdio_read_cccr(card); + err = sdio_read_cccr(card, ocr); if (err) goto remove; @@ -995,6 +999,11 @@ static int mmc_sdio_power_restore(struct mmc_host *host) * With these steps taken, mmc_select_voltage() is also required to * restore the correct voltage setting of the card. */ + + /* The initialization should be done at 3.3 V I/O voltage. */ + if (!mmc_card_keep_power(host)) + mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + sdio_reset(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 68f81b9ee0f..f573e7f9f74 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -146,15 +146,21 @@ static int sdio_irq_thread(void *_host) } set_current_state(TASK_INTERRUPTIBLE); - if (host->caps & MMC_CAP_SDIO_IRQ) + if (host->caps & MMC_CAP_SDIO_IRQ) { + mmc_host_clk_hold(host); 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) + if (host->caps & MMC_CAP_SDIO_IRQ) { + mmc_host_clk_hold(host); 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); |