@@ -209,6 +209,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;
@@ -217,6 +219,30 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
mmc_start_request(host, 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");
+ }
}
EXPORT_SYMBOL(mmc_wait_for_req);
@@ -24,6 +24,7 @@ struct mmc_bus_ops {
int (*resume)(struct mmc_host *);
int (*power_save)(struct mmc_host *);
int (*power_restore)(struct mmc_host *);
+ int (*reinit)(struct mmc_host *);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -237,6 +237,8 @@ static int mmc_read_ext_csd(struct mmc_card *card)
}
}
+ 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 revision %d\n",
@@ -484,6 +486,32 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * 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) {
+ /* FIXME should driver set this register? */
+ 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;
+ }
+
+ /*
* Activate high speed (if supported)
*/
if ((card->ext_csd.hs_max_dtr != 0) &&
@@ -699,6 +727,23 @@ 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);
+
+ host->ios.clock = host->f_min;
+ 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,
@@ -707,6 +752,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 = {
@@ -717,6 +763,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)
@@ -1266,11 +1266,34 @@ out:
spin_unlock_irqrestore(&host->lock, flags);
}
+/*
+ * HW reset callback for eMMC4.4 card
+ * In this function, HC will do the real HW reset
+ * for eMMC4.4 card
+ *
+ * RETURN VALUE:
+ * 0: reset emmc successfully
+ * 1: reset emmc failed
+ * */
+static int sdhci_hardware_reset(struct mmc_host *mmc)
+{
+ int err = 1;
+ struct sdhci_host *host;
+
+ host = mmc_priv(mmc);
+
+ if (host->ops && host->ops->reset_emmc)
+ err = host->ops->reset_emmc(host);
+
+ return err;
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
.enable_sdio_irq = sdhci_enable_sdio_irq,
+ .hardware_reset = sdhci_hardware_reset,
};
/*****************************************************************************\
@@ -215,6 +215,7 @@ struct sdhci_ops {
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host);
+ int (*reset_emmc)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@@ -54,6 +54,7 @@ struct mmc_ext_csd {
unsigned int sec_trim_mult; /* Secure trim multiplier */
unsigned int sec_erase_mult; /* Secure erase multiplier */
unsigned int trim_timeout; /* In milliseconds */
+ unsigned int rst;
};
struct sd_scr {
@@ -117,6 +117,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;
@@ -251,6 +251,7 @@ struct _mmc_csd {
* EXT_CSD fields
*/
+#define EXT_CSD_RST 162 /* R/W */
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
#define EXT_CSD_BUS_WIDTH 183 /* R/W */