>From b08076caeb121150feefd5b02103cc7af11c96d7 Mon Sep 17 00:00:00 2001
From: Chuanxiao Dong <[email protected]>
Date: Thu, 28 Oct 2010 11:23:24 +0800
Subject: [PATCH 1/3] mmc: implemented eMMC4.4 hardware reset feature in mmc 
core layer

When core layer detect a data timeout error, core driver need to
first reset eMMC card and then do a reinit for card. After that,
posts the original timeout error to mmc block layer.

1. enable hardware reset feature: to enable hardware reset
feature, mmc driver need to set RST_n_function bit in ext_csd
register. But this bit is onetime programmable, not sure whether
it is suitable for driver to set this bit.

2. The reset part was resvered for host controller to implement.

3. The reinit card part was implemented.

Signed-off-by: Chuanxiao Dong <[email protected]>
---
 drivers/mmc/core/core.c  |   27 +++++++++++++++++++++
 drivers/mmc/core/core.h  |    1 +
 drivers/mmc/core/mmc.c   |   59 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/card.h |    1 +
 include/linux/mmc/host.h |    5 ++++
 include/linux/mmc/mmc.h  |    1 +
 6 files changed, 94 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 41a3ce0..7b0fcb8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -208,6 +208,8 @@ static void mmc_wait_done(struct mmc_request *mrq)
  */
 void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
 {
+       struct mmc_card *card = host->card;
+
        DECLARE_COMPLETION_ONSTACK(complete);
 
        mrq->done_data = &complete;
@@ -220,6 +222,31 @@ void mmc_wait_for_req(struct mmc_host *host, struct 
mmc_request *mrq)
 
        wait_for_completion(&complete);
 
+       /* as MMC4.4 standard says, when some data timeout conditions
+        * occur, HC need to do a hardware reset for eMMC4.4 card.
+        * If the card is eMMC4.4 card && data error is timeout,
+        * do the following things:
+        * 1. let host controller do a specific hardware reset for eMMC
+        *    card (trigger RST_n signal).
+        * 2. after reset done, reinit eMMC4.4 card.
+        * */
+       if (mrq->data && card &&
+               mrq->data->error == -ETIMEDOUT &&
+               card->ext_csd.rst == 1) {
+               int err = 1;
+               if (host->ops->hardware_reset &&
+                               host->bus_ops->reinit) {
+                       err = host->ops->hardware_reset(host);
+                       if (err)
+                               pr_warn("MMC card reset failed, no reinit\n");
+                       else
+                               err = host->bus_ops->reinit(host);
+               }
+
+               if (err)
+                       pr_warn("cannot reset and reinit eMMC4.4 card\n");
+       }
+
        if (host->port_mutex)
                mutex_unlock(host->port_mutex);
 }
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index a811c52..f9ba278 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -24,6 +24,7 @@ struct mmc_bus_ops {
        int (*resume)(struct mmc_host *);
        void (*power_save)(struct mmc_host *);
        void (*power_restore)(struct mmc_host *);
+       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 fc6c8a7..4772a04 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -209,6 +209,8 @@ static int mmc_read_ext_csd(struct mmc_card *card)
                goto out;
        }
 
+       card->ext_csd.rst = ext_csd[EXT_CSD_RST];
+
        card->ext_csd.rev = ext_csd[EXT_CSD_REV];
        if (card->ext_csd.rev > 5) {
                printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
@@ -439,6 +441,31 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                        goto free_card;
        }
 
+       /*
+        * eMMC4.4 version card has HW reset capbility.
+        * Enable this feature here:
+        * RST_N_FUNCTION register is W/R, one time programmable
+        * or readable.
+        * So need to enable this register only once after power on
+        * */
+       if (card->csd.mmca_vsn >= CSD_SPEC_VER_4 &&
+                       card->ext_csd.rev >= 4 &&
+                       card->ext_csd.rst != 1) {
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                               EXT_CSD_RST, 1);
+
+               if (err && err != -EBADMSG)
+                       goto free_card;
+
+               if (err) {
+                       printk(KERN_WARNING "%s: switch to rst enable "
+                                  "failed %d\n",
+                                  mmc_hostname(card->host), err);
+                       err = 0;
+               } else
+                       card->ext_csd.rst = 1;
+       }
+
        /* XXX: for debugging */
        pr_info("ext_csd.hs_max_dtr: %u\n", card->ext_csd.hs_max_dtr);
        pr_info("csd.max_dtr: %u\n", card->csd.max_dtr);
@@ -646,6 +673,36 @@ static int mmc_awake(struct mmc_host *host)
        return err;
 }
 
+static int mmc_reinit_card(struct mmc_host *host)
+{
+       int err;
+
+       BUG_ON(!host);
+       BUG_ON(!host->card);
+
+       if (host->f_min > 400000) {
+               pr_warning("%s: Minimum clock frequency too high for "
+                               "identification mode\n", mmc_hostname(host));
+               host->ios.clock = host->f_min;
+       } else
+               /*
+                * according to mmca 4.4 or sd 2.0 spec, the clock frequency of
+                * identification mode can be less than 400KHz.
+                *
+                * lower down the clock frequency to 200KHz because some
+                * e.MMC devices (e.g. Micron e.MMC) don't work in clock
+                * frequency above 200KHz.
+                */
+               host->ios.clock = 200000;
+       mmc_set_clock(host, host->ios.clock);
+
+       err = mmc_init_card(host, host->ocr, host->card);
+       if (err)
+               printk(KERN_ERR "%s: err %d\n", __func__, err);
+
+       return err;
+}
+
 static const struct mmc_bus_ops mmc_ops = {
        .awake = mmc_awake,
        .sleep = mmc_sleep,
@@ -654,6 +711,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 = {
@@ -664,6 +722,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/card.h b/include/linux/mmc/card.h
index 8336bd9..7cb2901 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -44,6 +44,7 @@ struct mmc_ext_csd {
        unsigned int            sa_timeout;             /* Units: 100ns */
        unsigned int            hs_max_dtr;
        unsigned int            sectors;
+       unsigned char           rst;
 };
 
 struct sd_scr {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 45e47ff..e0cf3c2 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -112,6 +112,11 @@ struct mmc_host_ops {
 
        /* optional callback for HC quirks */
        void    (*init_card)(struct mmc_host *host, struct mmc_card *card);
+
+       /* HW reset callback, used for eMMC 4.4 new feature.
+        * when occurs data timeout, HC will need to reset eMMC4.4 card
+        * */
+       int (*hardware_reset)(struct mmc_host *host);
 };
 
 struct mmc_card;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 8a49cbf..b964b07 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -251,6 +251,7 @@ struct _mmc_csd {
  * EXT_CSD fields
  */
 
+#define EXT_CSD_RST            162             /* R/W */
 #define EXT_CSD_BUS_WIDTH      183     /* R/W */
 #define EXT_CSD_HS_TIMING      185     /* R/W */
 #define EXT_CSD_CARD_TYPE      196     /* RO */
-- 
1.6.6.1

Attachment: 0001-mmc-implemented-eMMC4.4-hardware-reset-feature-in-mm.patch
Description: 0001-mmc-implemented-eMMC4.4-hardware-reset-feature-in-mm.patch

_______________________________________________
Meego-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to