HW reset will reset eMMC card. So after reset, card should also be reinitialized. Add two new callbacks to implement reset and reinitialize. MMC core layer will check whether occurs a timeout error in the new added routine mmc_handle_timeout_error mmc_handle_timeout_error: check whether occurs a timeout error. If occurs,use HW reset callbacks to do reset and reinitialize hardware_reset: trigger a RST_n signal for host to reset card. This callback was just defined in header file, not implemented in this patch. Signed-off-by: Chuanxiao Dong <chuanxiao.dong@xxxxxxxxx> --- drivers/mmc/core/core.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/core.h | 9 +++++++++ drivers/mmc/core/mmc.c | 31 +++++++++++++++++++++++++++++++ include/linux/mmc/core.h | 1 + include/linux/mmc/host.h | 13 +++++++++++++ 5 files changed, 99 insertions(+), 0 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 6286898..530fc35 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -82,6 +82,51 @@ static void mmc_flush_scheduled_work(void) } /** + * mmc_handle_timeout_error - handle command and data timeout + * errors. + * @host: MMC host used to handle error + * @error: error condition + * + * check whether there is a command or data timeout error occured, + * if so, reset and reinit eMMC card if card has such capability. + * 1. let host controller do a specific hardware reset for eMMC + * card (trigger RST_n signal). + * 2. after reset done, reinit eMMC card. + */ +void mmc_handle_timeout_error(struct mmc_host *host, int error) +{ + struct mmc_card *card = host->card; + + /* + * If error condition is not timeout, do nothing + */ + if (error != -ETIMEDOUT) + return; + /* + * make sure mmc_reqeust is not NULL + * make sure mmc_card has HW reset capability + */ + if (!card || !card->ext_csd.rst) + return; + + /* check whether host has such callback */ + if (!host->ops->hardware_reset || + !host->bus_ops->reinit) + return; + + /* + * if there occurs any timeout error, HW reset + * eMMC card and reinit again. + */ + if (host->ops->hardware_reset(host)) + pr_warn("MMC card reset failed\n"); + else + if (host->bus_ops->reinit(host)) + pr_warn("MMC card reinit failed\n"); +} +EXPORT_SYMBOL(mmc_handle_timeout_error); + +/** * mmc_request_done - finish processing an MMC request * @host: MMC host which completed request * @mrq: MMC request which request diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 026c975..4980e2f 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -24,6 +24,15 @@ struct mmc_bus_ops { int (*resume)(struct mmc_host *); int (*power_save)(struct mmc_host *); int (*power_restore)(struct mmc_host *); + /* + * New added callback + * Used to reinit card when HW reset occurs + * + * return value: + * 0: successfully reinit card. + * negative value: failed to reinit + */ + int (*reinit)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1d138b3..b3eb8a6 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -732,6 +732,35 @@ static int mmc_awake(struct mmc_host *host) return err; } +/** + * mmc_reinit_card - reinitialize card after HW reset + * + * this callback is used to reinitialize card after a HW + * reset occurs. + * Reture value: + * 0: successfully reinitialize + * other: failed to reinitialize + * + * claim_host should be called before using this callback. + */ +static int mmc_reinit_card(struct mmc_host *host) +{ + int err; + /* + * Before init card, set the clock to be + * the init frequency + */ + host->ios.clock = host->f_init; + mmc_set_clock(host, host->ios.clock); + + err = mmc_init_card(host, host->ocr, host->card); + if (err) + pr_err("%s: Error %d while reinit card\n", + mmc_hostname(host), err); + + return err; +} + static const struct mmc_bus_ops mmc_ops = { .awake = mmc_awake, .sleep = mmc_sleep, @@ -740,6 +769,7 @@ static const struct mmc_bus_ops mmc_ops = { .suspend = NULL, .resume = NULL, .power_restore = mmc_power_restore, + .reinit = mmc_reinit_card, }; static const struct mmc_bus_ops mmc_ops_unsafe = { @@ -750,6 +780,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = { .suspend = mmc_suspend, .resume = mmc_resume, .power_restore = mmc_power_restore, + .reinit = mmc_reinit_card, }; static void mmc_attach_bus_ops(struct mmc_host *host) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 64e013f..115d589 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -131,6 +131,7 @@ struct mmc_request { struct mmc_host; struct mmc_card; +extern void mmc_handle_timeout_error(struct mmc_host *, int); extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 4a9d9d2..f1dffcb 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -117,6 +117,19 @@ struct mmc_host_ops { /* optional callback for HC quirks */ void (*init_card)(struct mmc_host *host, struct mmc_card *card); + + /* + * eMMC4.4 HW reset feature callback + * + * eMMC4.4 card can be reset if host triggers a RST_n signal + * This callback will be used to for host to trigger such + * signal. + * + * return value: + * 0: successfully reset the eMMC card. + * -ENODEV: no valid hardware to do so. + */ + int (*hardware_reset)(struct mmc_host *host); }; struct mmc_card; -- 1.6.6.1 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html