[PATCH 2/4] add two new callback to use HW reset feature

From: Chuanxiao Dong
Date: Wed Dec 01 2010 - 07:14:22 EST


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
occures a timeout error in the new added routine
mmc_handle_timeout_error

mmc_handle_timeout_error: check whether occures a timeout error.
If occures, 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 capbility.
+ * 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 capbility
+ */
+ 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 85cc7f6..bc4611a 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 381c77f..3a7008d 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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/