From 2c4967f741e87cdd63de7271b97807041dccbf3b Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Sat, 4 Feb 2012 16:14:50 -0500 Subject: mmc: core: Ensure clocks are always enabled before host interaction Ensure clocks are always enabled before any interaction with the host controller driver. This makes sure that there is no race between host execution and the core layer turning off clocks in different context with clock gating framework. Signed-off-by: Sujit Reddy Thumma Acked-by: Linus Walleij Acked-by: Per Forlin Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/core/core.c') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f545a3e6eb8..b3063b741df 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -290,8 +290,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 +309,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); + } } /** @@ -620,7 +626,9 @@ int mmc_host_enable(struct mmc_host *host) int err; host->en_dis_recurs = 1; + mmc_host_clk_hold(host); err = host->ops->enable(host); + mmc_host_clk_release(host); host->en_dis_recurs = 0; if (err) { @@ -640,7 +648,9 @@ static int mmc_host_do_disable(struct mmc_host *host, int lazy) int err; host->en_dis_recurs = 1; + mmc_host_clk_hold(host); err = host->ops->disable(host, lazy); + mmc_host_clk_release(host); host->en_dis_recurs = 0; if (err < 0) { @@ -1203,8 +1213,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; } -- cgit v1.2.3 From 6e8201f57c9359c9c5dc8f9805c15a4392492a10 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 16 Jan 2012 17:49:01 +0900 Subject: mmc: core: add the capability for broken voltage There is an understood mismatch between the voltage the host controller is set to and the voltage supplied to the card by a fixed voltage regulator. Teaching the driver to accept the mismatch is overly complicated. Instead just accept the regulator's voltage. This patch adds MMC_CAP2_BROKEN_VOLTAGE. If the voltage didn't satisfy between min_uV and max_uV, try to change the voltage in core.c. When changing the voltage, maybe use regulator_set_voltage(). In regulator_set_voltage(), check the below condition. /* sanity check */ if (!rdev->desc->ops->set_voltage && !rdev->desc->ops->set_voltage_sel) { ret = -EINVAL; goto out; } If some board should use the fixed-regulator, always return -EINVAL. Then, eMMC didn't initialize always. So if use a fixed-regulator, we need to add the MMC_CAP2_BROKEN_VOLTAGE. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 4 ++++ include/linux/mmc/host.h | 1 + 2 files changed, 5 insertions(+) (limited to 'drivers/mmc/core/core.c') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b3063b741df..18661554e79 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1131,6 +1131,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) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 73e5ee8e9ca..ee2b0363c04 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -257,6 +257,7 @@ struct mmc_host { #define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */ #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ MMC_CAP2_HS200_1_2V_SDR) +#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ mmc_pm_flag_t pm_caps; /* supported pm features */ unsigned int power_notify_type; -- cgit v1.2.3 From 3e73c36b4dc224529d0b0c0d5d69c0dacd793c42 Mon Sep 17 00:00:00 2001 From: Girish K S Date: Tue, 31 Jan 2012 15:44:03 +0530 Subject: mmc: core: Fix PowerOff Notify suspend/resume Modified the mmc_poweroff to resume before sending the poweroff notification command. In sleep mode only AWAKE and RESET commands are allowed, so before sending the poweroff notification command resume from sleep mode and then send the notification command. PowerOff Notify is tested on a Synopsis Designware Host Controller (eMMC 4.5). The suspend to RAM and resume works fine. Signed-off-by: Girish K S Tested-by: Girish K S Reviewed-by: Saugata Das Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 26 +++++++++++++++++++------- drivers/mmc/core/mmc.c | 17 ++++++++++++----- include/linux/mmc/card.h | 4 ++++ 3 files changed, 35 insertions(+), 12 deletions(-) (limited to 'drivers/mmc/core/core.c') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 18661554e79..690255c7d4d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1256,6 +1256,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 @@ -1286,6 +1287,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); } /* @@ -1344,12 +1346,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 @@ -2403,12 +2421,6 @@ 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); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index dd3fcac684a..9be031934e3 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1316,11 +1316,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; @@ -1340,7 +1342,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; @@ -1350,7 +1356,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/include/linux/mmc/card.h b/include/linux/mmc/card.h index 9f22ba572de..19a41d1737a 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -217,6 +217,7 @@ struct mmc_card { #define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<7) /* card has been removed */ #define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ +#define MMC_STATE_SLEEP (1<<9) /* card is in sleep state */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -382,6 +383,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -393,7 +395,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) +#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP) +#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP) /* * Quirk add/remove for MMC products. */ -- cgit v1.2.3