diff options
author | Dov Levenglick <dovl@codeaurora.org> | 2015-06-24 19:51:58 +0300 |
---|---|---|
committer | Subhash Jadavani <subhashj@codeaurora.org> | 2016-05-31 15:26:49 -0700 |
commit | fda56078bbf3b05a9bb1c2ffe3d3616dd15714d5 (patch) | |
tree | 1baf3fcd0d9f512dc5dc6b7b889cc3cabd935c2b | |
parent | 37927a22fe1c03a9ec3f410af4ffae1707330918 (diff) |
mmc: add support for bkops
Add support for manual and auto bkops for
legacy (not command-queue) mode.
Change-Id: I1333e1d4330393e446ba48a9474c83295744c050
Signed-off-by: Dov Levenglick <dovl@codeaurora.org>
[subhashj@codeaurora.org: fixed merge conflicts and compilation
errors]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
-rw-r--r-- | drivers/mmc/card/block.c | 20 | ||||
-rw-r--r-- | drivers/mmc/core/bus.c | 12 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 156 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 16 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 54 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 29 | ||||
-rw-r--r-- | include/linux/mmc/core.h | 21 | ||||
-rw-r--r-- | include/linux/mmc/host.h | 2 | ||||
-rw-r--r-- | include/linux/mmc/mmc.h | 3 |
9 files changed, 247 insertions, 66 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5d95402c5fb2..006d112f11db 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -43,6 +43,7 @@ #include <linux/mmc/ioctl.h> #include <linux/mmc/card.h> +#include <linux/mmc/core.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> @@ -3405,10 +3406,17 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) unsigned long flags; unsigned int cmd_flags = req ? req->cmd_flags : 0; - if (req && !mq->mqrq_prev->req) + if (req && !mq->mqrq_prev->req) { /* claim host only for the first request */ mmc_get_card(card); + if (mmc_card_doing_bkops(host->card)) { + ret = mmc_stop_bkops(host->card); + if (ret) + goto out; + } + } + ret = mmc_blk_part_switch(card, md); if (ret) { if (req) { @@ -3951,8 +3959,16 @@ static int mmc_blk_probe(struct mmc_card *card) goto out; } - pm_runtime_set_autosuspend_delay(&card->dev, 3000); pm_runtime_use_autosuspend(&card->dev); + pm_runtime_set_autosuspend_delay(&card->dev, MMC_AUTOSUSPEND_DELAY_MS); + /* + * If there is a runtime_idle function, it should take care of + * suspending the card + */ + if (card->host->bus_ops->runtime_idle) + pm_runtime_dont_use_autosuspend(&card->dev); + else + pm_runtime_use_autosuspend(&card->dev); /* * Don't enable runtime PM for SD-combo cards here. Leave that diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index af884a552d5e..be7773f40a90 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -200,10 +200,20 @@ static int mmc_runtime_resume(struct device *dev) return host->bus_ops->runtime_resume(host); } + +static int mmc_runtime_idle(struct device *dev) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + + return host->bus_ops->runtime_idle(host); +} + #endif /* !CONFIG_PM */ static const struct dev_pm_ops mmc_bus_pm_ops = { - SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, NULL) + SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, + mmc_runtime_idle) SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume) }; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ed4e383c0482..8db21b61a2ae 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -913,24 +913,70 @@ static void mmc_start_cmdq_request(struct mmc_host *host, } /** - * mmc_start_bkops - start BKOPS for supported cards + * mmc_set_auto_bkops - set auto BKOPS for supported cards * @card: MMC card to start BKOPS - * @form_exception: A flag to indicate if this function was - * called due to an exception raised by the card + * @enable: enable/disable flag + * Configure the card to run automatic BKOPS. * - * Start background operations whenever requested. - * When the urgent BKOPS bit is set in a R1 command response - * then background operations should be started immediately. + * Should be called when host is claimed. */ -void mmc_start_bkops(struct mmc_card *card, bool from_exception) +int mmc_set_auto_bkops(struct mmc_card *card, bool enable) +{ + int ret = 0; + u8 bkops_en; + + BUG_ON(!card); + enable = !!enable; + + if (unlikely(!mmc_card_support_auto_bkops(card))) { + pr_err("%s: %s: card doesn't support auto bkops\n", + mmc_hostname(card->host), __func__); + return -EPERM; + } + + if (enable) { + if (mmc_card_doing_auto_bkops(card)) + goto out; + bkops_en = card->ext_csd.man_bkops_en | EXT_CSD_BKOPS_AUTO_EN; + } else { + if (!mmc_card_doing_auto_bkops(card)) + goto out; + bkops_en = card->ext_csd.man_bkops_en & ~EXT_CSD_BKOPS_AUTO_EN; + } + + ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, + bkops_en, 0); + if (ret) { + pr_err("%s: %s: error in setting auto bkops to %d (%d)\n", + mmc_hostname(card->host), __func__, enable, ret); + } else { + if (enable) + mmc_card_set_auto_bkops(card); + else + mmc_card_clr_auto_bkops(card); + card->ext_csd.man_bkops_en = bkops_en; + } +out: + return ret; +} +EXPORT_SYMBOL(mmc_set_auto_bkops); + +/** + * mmc_check_bkops - check BKOPS for supported cards + * @card: MMC card to check BKOPS + * + * Read the BKOPS status in order to determine whether the + * card requires bkops to be started. +*/ +void mmc_check_bkops(struct mmc_card *card) { int err; - int timeout; - bool use_busy_signal; BUG_ON(!card); - if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card)) + if (unlikely(!mmc_card_configured_manual_bkops(card))) + return; + if (mmc_card_doing_bkops(card) || mmc_card_doing_auto_bkops(card)) return; err = mmc_read_bkops_status(card); @@ -940,47 +986,48 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) return; } - if (!card->ext_csd.raw_bkops_status) + if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2) + return; + + card->bkops.needs_manual = true; +} +EXPORT_SYMBOL(mmc_check_bkops); + +/** + * mmc_start_manual_bkops - start BKOPS for supported cards + * @card: MMC card to start BKOPS + * + * Send START_BKOPS to the card. +*/ +void mmc_start_manual_bkops(struct mmc_card *card) +{ + int err; + + BUG_ON(!card); + + if (unlikely(!mmc_card_configured_manual_bkops(card))) return; - if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2 && - from_exception) + if (mmc_card_doing_bkops(card)) return; mmc_claim_host(card->host); - if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) { - timeout = MMC_BKOPS_MAX_TIMEOUT; - use_busy_signal = true; - } else { - timeout = 0; - 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); + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_START, + 1, 0, false, true, false); if (err) { - pr_warn("%s: Error %d starting bkops\n", - mmc_hostname(card->host), err); - mmc_retune_release(card->host); - goto out; + pr_err("%s: Error %d starting manual bkops\n", + mmc_hostname(card->host), err); + } else { + mmc_card_set_doing_bkops(card); + card->bkops.needs_manual = false; } - /* - * For urgent bkops status (LEVEL_2 and more) - * bkops executed synchronously, otherwise - * the operation is in progress - */ - if (!use_busy_signal) - mmc_card_set_doing_bkops(card); - else - mmc_retune_release(card->host); -out: + mmc_retune_release(card->host); mmc_release_host(card->host); } -EXPORT_SYMBOL(mmc_start_bkops); +EXPORT_SYMBOL(mmc_start_manual_bkops); /* * mmc_wait_data_done() - done callback for data request @@ -1347,7 +1394,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (areq) mmc_post_req(host, areq->mrq, -EINVAL); - mmc_start_bkops(host->card, true); + mmc_check_bkops(host->card); /* prepare the request again */ if (areq) @@ -1509,6 +1556,11 @@ int mmc_stop_bkops(struct mmc_card *card) int err = 0; BUG_ON(!card); + if (unlikely(!mmc_card_configured_manual_bkops(card))) + goto out; + if (!mmc_card_doing_bkops(card)) + goto out; + err = mmc_interrupt_hpi(card); /* @@ -1520,7 +1572,7 @@ int mmc_stop_bkops(struct mmc_card *card) mmc_retune_release(card->host); err = 0; } - +out: return err; } EXPORT_SYMBOL(mmc_stop_bkops); @@ -1777,6 +1829,19 @@ void mmc_get_card(struct mmc_card *card) EXPORT_SYMBOL(mmc_get_card); /* + * This is a helper function, which drops the runtime + * pm reference for the card device. + */ +static void __mmc_put_card(struct mmc_card *card) +{ + /* In case of runtime_idle, it will handle the suspend */ + if (card->host->bus_ops->runtime_idle) + pm_runtime_put(&card->dev); + else + pm_runtime_put_autosuspend(&card->dev); +} + +/* * This is a helper function, which releases the host and drops the runtime * pm reference for the card device. */ @@ -1784,7 +1849,7 @@ void mmc_put_card(struct mmc_card *card) { mmc_release_host(card->host); pm_runtime_mark_last_busy(&card->dev); - pm_runtime_put_autosuspend(&card->dev); + __mmc_put_card(card); } EXPORT_SYMBOL(mmc_put_card); @@ -1796,6 +1861,13 @@ void mmc_set_ios(struct mmc_host *host) { struct mmc_ios *ios = &host->ios; + if (unlikely(ios->power_mode == MMC_POWER_OFF && + host->card && mmc_card_doing_auto_bkops(host->card))) { + pr_err("%s: %s: illegal to disable power to card when running auto bkops\n", + mmc_hostname(host), __func__); + return; + } + pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u " "width %u timing %u\n", mmc_hostname(host), ios->clock, ios->bus_mode, diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index c4ab8ce9fd90..af0b13a492a5 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -15,22 +15,6 @@ #define MMC_CMD_RETRIES 3 -struct mmc_bus_ops { - void (*remove)(struct mmc_host *); - void (*detect)(struct mmc_host *); - int (*pre_suspend)(struct mmc_host *); - int (*suspend)(struct mmc_host *); - int (*resume)(struct mmc_host *); - int (*runtime_suspend)(struct mmc_host *); - int (*runtime_resume)(struct mmc_host *); - int (*power_save)(struct mmc_host *); - int (*power_restore)(struct mmc_host *); - int (*alive)(struct mmc_host *); - int (*shutdown)(struct mmc_host *); - int (*reset)(struct mmc_host *); - int (*change_bus_speed)(struct mmc_host *, unsigned long *); -}; - void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); void mmc_detach_bus(struct mmc_host *host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fe96eda51d72..3f68765323ea 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -518,6 +518,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) */ card->ext_csd.out_of_int_time = ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10; + pr_info("%s: Out-of-interrupt timeout is %d[ms]\n", + mmc_hostname(card->host), + card->ext_csd.out_of_int_time); } if (card->ext_csd.rev >= 5) { @@ -535,8 +538,11 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; if (!card->ext_csd.man_bkops_en) - pr_info("%s: MAN_BKOPS_EN bit is not set\n", - mmc_hostname(card->host)); + pr_info("%s: BKOPS_EN equals 0x%x\n", + mmc_hostname(card->host), + card->ext_csd.man_bkops_en); + + } /* check whether the eMMC card supports HPI */ @@ -2132,6 +2138,23 @@ reinit: } } + /* + * Start auto bkops, if supported. + * + * Note: This leaves the possibility of having both manual and + * auto bkops running in parallel. The runtime implementation + * will allow this, but ignore bkops exceptions on the premises + * that auto bkops will eventually kick in and the device will + * handle bkops without START_BKOPS from the host. + */ + if (mmc_card_support_auto_bkops(card)) { + /* + * Ignore the return value of setting auto bkops. + * If it failed, will run in backward compatible mode. + */ + (void)mmc_set_auto_bkops(card, true); + } + return 0; free_card: @@ -2335,6 +2358,12 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) goto out; } + if (mmc_card_doing_auto_bkops(host->card)) { + err = mmc_set_auto_bkops(host->card, false); + if (err) + goto out; + } + err = mmc_flush_cache(host->card); if (err) goto out; @@ -2547,6 +2576,26 @@ static int mmc_reset(struct mmc_host *host) return mmc_init_card(host, card->ocr, card); } +#define NO_AUTOSUSPEND (-1) +static int mmc_runtime_idle(struct mmc_host *host) +{ + BUG_ON(!host->card); + + if (host->card->bkops.needs_manual) + mmc_start_manual_bkops(host->card); + + pm_runtime_mark_last_busy(&host->card->dev); + /* + * TODO: consider prolonging suspend when bkops + * is running in order to allow a longer time for + * bkops to complete + * */ + pm_schedule_suspend(&host->card->dev, MMC_AUTOSUSPEND_DELAY_MS); + + /* return negative value in order to avoid autosuspend */ + return NO_AUTOSUSPEND; +} + static const struct mmc_bus_ops mmc_ops = { .remove = mmc_remove, .detect = mmc_detect, @@ -2554,6 +2603,7 @@ static const struct mmc_bus_ops mmc_ops = { .resume = mmc_resume, .runtime_suspend = mmc_runtime_suspend, .runtime_resume = mmc_runtime_resume, + .runtime_idle = mmc_runtime_idle, .alive = mmc_alive, .shutdown = mmc_shutdown, .change_bus_speed = mmc_change_bus_speed, diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 8289add95151..80ce6cc36b88 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -12,6 +12,7 @@ #include <linux/device.h> #include <linux/mmc/core.h> +#include <linux/mmc/mmc.h> #include <linux/mod_devicetable.h> #include <linux/notifier.h> @@ -86,7 +87,7 @@ struct mmc_ext_csd { bool hpi; /* HPI support bit */ unsigned int hpi_cmd; /* cmd used as HPI */ bool bkops; /* background support bit */ - bool man_bkops_en; /* manual bkops enable bit */ + u8 man_bkops_en; /* manual bkops enable */ unsigned int data_sector_size; /* 512 bytes or 4KB */ unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ @@ -274,6 +275,15 @@ struct mmc_part { #define MMC_BLK_DATA_AREA_RPMB (1<<3) }; +/** + * struct mmc_bkops_info - BKOPS data + * @need_manual: indication whether have to send START_BKOPS + * to the device + */ +struct mmc_bkops_info { + bool needs_manual; +}; + /* * MMC device */ @@ -297,9 +307,10 @@ struct mmc_card { #define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */ #define MMC_CARD_SDXC (1<<3) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<4) /* card has been removed */ -#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */ +#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing manual BKOPS */ #define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */ #define MMC_STATE_CMDQ (1<<12) /* card is in cmd queue mode */ +#define MMC_STATE_AUTO_BKOPS (1<<13) /* card is doing auto BKOPS */ 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 */ @@ -363,6 +374,7 @@ struct mmc_card { bool issue_long_pon; u8 *cached_ext_csd; bool cmdq_init; + struct mmc_bkops_info bkops; }; /* @@ -512,6 +524,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) #define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED) #define mmc_card_cmdq(c) ((c)->state & MMC_STATE_CMDQ) +#define mmc_card_doing_auto_bkops(c) ((c)->state & MMC_STATE_AUTO_BKOPS) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -524,6 +537,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED) #define mmc_card_set_cmdq(c) ((c)->state |= MMC_STATE_CMDQ) #define mmc_card_clr_cmdq(c) ((c)->state &= ~MMC_STATE_CMDQ) +#define mmc_card_set_auto_bkops(c) ((c)->state |= MMC_STATE_AUTO_BKOPS) +#define mmc_card_clr_auto_bkops(c) ((c)->state &= ~MMC_STATE_AUTO_BKOPS) /* * Quirk add/remove for MMC products. @@ -594,6 +609,16 @@ static inline int mmc_card_broken_irq_polling(const struct mmc_card *c) return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING; } +static inline bool mmc_card_support_auto_bkops(const struct mmc_card *c) +{ + return c->ext_csd.rev >= MMC_V5_1; +} + +static inline bool mmc_card_configured_manual_bkops(const struct mmc_card *c) +{ + return c->ext_csd.man_bkops_en & EXT_CSD_BKOPS_MANUAL_EN; +} + #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) (dev_name(&(c)->dev)) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 22c610609a25..446b56765dbb 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -112,6 +112,23 @@ struct mmc_request { struct request *req; }; +struct mmc_bus_ops { + void (*remove)(struct mmc_host *); + void (*detect)(struct mmc_host *); + int (*pre_suspend)(struct mmc_host *); + int (*suspend)(struct mmc_host *); + int (*resume)(struct mmc_host *); + int (*runtime_suspend)(struct mmc_host *); + int (*runtime_resume)(struct mmc_host *); + int (*runtime_idle)(struct mmc_host *); + int (*power_save)(struct mmc_host *); + int (*power_restore)(struct mmc_host *); + int (*alive)(struct mmc_host *); + int (*shutdown)(struct mmc_host *); + int (*reset)(struct mmc_host *); + int (*change_bus_speed)(struct mmc_host *, unsigned long *); +}; + struct mmc_card; struct mmc_async_req; struct mmc_cmdq_req; @@ -139,13 +156,15 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); -extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); +extern void mmc_check_bkops(struct mmc_card *card); +extern void mmc_start_manual_bkops(struct mmc_card *card); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); extern int __mmc_switch_cmdq_mode(struct mmc_command *cmd, u8 set, u8 index, u8 value, unsigned int timeout_ms, bool use_busy_signal, bool ignore_timeout); extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); +extern int mmc_set_auto_bkops(struct mmc_card *card, bool enable); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 42e7b412e6f1..cb46cdf8dcf3 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -22,6 +22,8 @@ #include <linux/mmc/card.h> #include <linux/mmc/pm.h> +#define MMC_AUTOSUSPEND_DELAY_MS 3000 + struct mmc_ios { unsigned int clock; /* clock rate */ unsigned int old_rate; /* saved clock rate */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index aa1db902745f..1cde2e5eb283 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -370,6 +370,9 @@ struct _mmc_csd { #define EXT_CSD_PACKED_EVENT_EN BIT(3) +#define EXT_CSD_BKOPS_MANUAL_EN BIT(0) +#define EXT_CSD_BKOPS_AUTO_EN BIT(1) + /* * EXCEPTION_EVENT_STATUS field */ |