summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShridhar Rasal <srasal@nvidia.com>2012-01-25 17:54:30 +0530
committerSimone Willett <swillett@nvidia.com>2012-02-10 13:12:17 -0800
commit506e63fcfb563581c4a0417d424e36a75cf51acf (patch)
treee9bf67c45323f25b73c60418c8a2b68b7355182d
parent9c5ce68dd28bb7d750c47181ebcca6ac6c071eab (diff)
mmc: core: Add support for BKOPS and HPI interrupt
Added support for starting BKOPS and issuing HPI commands which are supported by eMMC v4.41 and eMMC v4.5 cards. Enable BKOPS and HPI interrupt if the host and card support it. Originally reviewed on: http://git-master/r/69778 Bug 919232 Change-Id: I09b33ddc18013e2eeb505fdb28dd8357fa75b569 Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com> Signed-off-by: Shridhar Rasal <srasal@nvidia.com> Reviewed-on: http://git-master/r/77319 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Sachin Nikam <snikam@nvidia.com> Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
-rw-r--r--drivers/mmc/core/core.c109
-rw-r--r--drivers/mmc/core/mmc.c58
-rw-r--r--drivers/mmc/core/mmc_ops.c70
-rw-r--r--drivers/mmc/core/mmc_ops.h3
-rw-r--r--include/linux/mmc/card.h11
-rw-r--r--include/linux/mmc/core.h3
-rw-r--r--include/linux/mmc/host.h1
-rw-r--r--include/linux/mmc/mmc.h13
8 files changed, 266 insertions, 2 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 344d2414f05c..2a288e936a84 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -5,6 +5,7 @@
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
+ * Copyright (c) 2012 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -325,6 +326,110 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
EXPORT_SYMBOL(mmc_wait_for_req);
/**
+ * mmc_bkops_start - Issue start for mmc background ops
+ * @card: the MMC card associated with bkops
+ * @is_synchronous: is the backops synchronous
+ *
+ * Issued background ops without the busy wait.
+ */
+int mmc_bkops_start(struct mmc_card *card, bool is_synchronous)
+{
+ int err;
+ unsigned long flags;
+
+ BUG_ON(!card);
+
+ if (!card->ext_csd.bk_ops_en || mmc_card_doing_bkops(card))
+ return 1;
+
+ mmc_claim_host(card->host);
+ err = mmc_send_bk_ops_cmd(card, is_synchronous);
+ if (err)
+ pr_err("%s: abort bk ops (%d error)\n",
+ mmc_hostname(card->host), err);
+
+ /*
+ * Incase of asynchronous backops, set card state
+ * to doing bk ops to ensure that HPI is issued before
+ * handling any new request in the queue.
+ */
+ spin_lock_irqsave(&card->host->lock, flags);
+ mmc_card_clr_need_bkops(card);
+ if (!is_synchronous)
+ mmc_card_set_doing_bkops(card);
+ spin_unlock_irqrestore(&card->host->lock, flags);
+
+ mmc_release_host(card->host);
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_bkops_start);
+
+/**
+ * mmc_interrupt_hpi - Issue for High priority Interrupt
+ * @card: the MMC card associated with the HPI transfer
+ *
+ * Issued High Priority Interrupt, and check for card status
+ * util out-of prg-state.
+ */
+int mmc_interrupt_hpi(struct mmc_card *card)
+{
+ int err;
+ u32 status;
+ unsigned long flags;
+
+ BUG_ON(!card);
+
+ if (!mmc_card_mmc(card))
+ return 1;
+
+ if (!card->ext_csd.hpi_en) {
+ pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host));
+ return 1;
+ }
+
+ mmc_claim_host(card->host);
+ err = mmc_send_status(card, &status);
+ if (err) {
+ pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
+ goto out;
+ }
+
+ /*
+ * If the card status is in PRG-state, we can send the HPI command.
+ */
+ if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
+ do {
+ /*
+ * We don't know when the HPI command will finish
+ * processing, so we need to resend HPI until out
+ * of prg-state, and keep checking the card status
+ * with SEND_STATUS. If a timeout error occurs when
+ * sending the HPI command, we are already out of
+ * prg-state.
+ */
+ err = mmc_send_hpi_cmd(card, &status);
+ if (err)
+ pr_debug("%s: abort HPI (%d error)\n",
+ mmc_hostname(card->host), err);
+
+ err = mmc_send_status(card, &status);
+ if (err)
+ break;
+ } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
+ } else
+ pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
+
+out:
+ spin_lock_irqsave(&card->host->lock, flags);
+ mmc_card_clr_doing_bkops(card);
+ spin_unlock_irqrestore(&card->host->lock, flags);
+ mmc_release_host(card->host);
+ return err;
+}
+EXPORT_SYMBOL(mmc_interrupt_hpi);
+
+/**
* mmc_wait_for_cmd - start a command and wait for completion
* @host: MMC host to start command
* @cmd: MMC command to start
@@ -2010,6 +2115,10 @@ int mmc_suspend_host(struct mmc_host *host)
if (mmc_bus_needs_resume(host))
return 0;
+ if (mmc_card_mmc(host->card) && mmc_card_doing_bkops(host->card))
+ mmc_interrupt_hpi(host->card);
+ mmc_card_clr_need_bkops(host->card);
+
if (host->caps & MMC_CAP_DISABLE)
cancel_delayed_work(&host->disable);
if (cancel_delayed_work(&host->detect))
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 6952f778c294..69fb2275845c 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -4,6 +4,7 @@
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
+ * Copyright (c) 2012 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -403,10 +404,29 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_TRIM_MULT];
}
- if (card->ext_csd.rev >= 5)
+ card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
+ if (card->ext_csd.rev >= 5) {
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
+ /* check whether the eMMC card supports HPI */
+ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
+ card->ext_csd.hpi = 1;
+ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
+ card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
+ else
+ card->ext_csd.hpi_cmd = MMC_SEND_STATUS;
+ /*
+ * Indicate the maximum timeout to close
+ * a command interrupted by HPI
+ */
+ card->ext_csd.out_of_int_time =
+ ext_csd[EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10;
+ }
+
+ /* Check whether the eMMC card supports background ops */
+ if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)
+ card->ext_csd.bk_ops = 1;
+ }
- card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT];
if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
card->erased_byte = 0xFF;
else
@@ -728,6 +748,40 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Enable HPI feature (if supported)
+ */
+ if (card->ext_csd.hpi && (card->host->caps & MMC_CAP_BKOPS)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HPI_MGMT, 1, 0);
+ if (err && err != -EBADMSG)
+ goto free_card;
+ if (err) {
+ pr_warning("%s: Enabling HPI failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ } else {
+ card->ext_csd.hpi_en = 1;
+ }
+ }
+
+ /*
+ * Enable Background ops feature (if supported)
+ */
+ if (card->ext_csd.bk_ops && (card->host->caps & MMC_CAP_BKOPS)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BKOPS_EN, 1, 0);
+ if (err && err != -EBADMSG)
+ goto free_card;
+ if (err) {
+ pr_warning("%s: Enabling BK ops failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ } else {
+ card->ext_csd.bk_ops_en = 1;
+ }
+ }
+
+ /*
* Compute bus speed.
*/
max_dtr = (unsigned int)-1;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 770c3d06f5dc..330b968393d6 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -547,3 +547,73 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width)
err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width);
return err;
}
+
+int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
+{
+ struct mmc_command cmd = {0};
+ unsigned int opcode;
+ unsigned int flags;
+ int err;
+
+ opcode = card->ext_csd.hpi_cmd;
+ flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ cmd.opcode = opcode;
+ cmd.arg = card->rca << 16 | 1;
+ cmd.flags = flags;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ pr_warn("%s: error %d interrupting operation. "
+ "HPI command response %#x\n", mmc_hostname(card->host),
+ err, cmd.resp[0]);
+ return err;
+ }
+ if (status)
+ *status = cmd.resp[0];
+
+ return 0;
+}
+
+int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous)
+{
+ int err;
+ struct mmc_command cmd;
+ u32 status;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SWITCH;
+ cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (EXT_CSD_BKOPS_EN << 16) |
+ (1 << 8) |
+ EXT_CSD_CMD_SET_NORMAL;
+ if (is_synchronous)
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ else
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ /* Must check status to be sure of no errors */
+ do {
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+ if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ break;
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ if (status & 0xFDFFA000)
+ printk(KERN_ERR "%s: unexpected status %#x after "
+ "switch", mmc_hostname(card->host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+
+ return 0;
+}
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 9276946fa5b7..d8f157dee147 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -2,6 +2,7 @@
* linux/drivers/mmc/core/mmc_ops.h
*
* Copyright 2006-2007 Pierre Ossman
+ * Copyright (c) 2012 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,6 +27,8 @@ int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
+int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
+int mmc_send_bk_ops_cmd(struct mmc_card *card, bool is_synchronous);
#endif
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index b6a1fcf4ffd1..9178aa48209a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -77,6 +77,12 @@ struct mmc_ext_csd {
u8 raw_sec_feature_support;/* 231 */
u8 raw_trim_mult; /* 232 */
u8 raw_sectors[4]; /* 212 - 4 bytes */
+ bool hpi_en; /* HPI enablebit */
+ bool hpi; /* HPI support bit */
+ unsigned int hpi_cmd; /* cmd used as HPI */
+ u8 out_of_int_time; /* out of int time */
+ bool bk_ops; /* BK ops support bit */
+ bool bk_ops_en; /* BK ops enable bit */
};
struct sd_scr {
@@ -359,6 +365,11 @@ static inline void __maybe_unused remove_quirk_sd(struct mmc_card *card,
if (mmc_card_sd(card))
card->quirks &= ~data;
}
+#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
+#define mmc_card_set_need_bkops(c) ((c)->state |= MMC_STATE_NEED_BKOPS)
+
+#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
+#define mmc_card_clr_need_bkops(c) ((c)->state &= ~MMC_STATE_NEED_BKOPS)
static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
{
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index b8b1b7a311f1..c3e55fa63fb6 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -136,6 +136,9 @@ struct mmc_async_req;
extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
struct mmc_async_req *, int *);
+extern int mmc_interrupt_hpi(struct mmc_card *);
+extern int mmc_bkops_start(struct mmc_card *card, bool is_synchronous);
+
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_app_cmd(struct mmc_host *, struct mmc_card *);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 51128f8a5775..8c0bf3f2a36f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -230,6 +230,7 @@ struct mmc_host {
#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
+#define MMC_CAP_BKOPS (1 << 31) /* Host supports BKOPS */
mmc_pm_flag_t pm_caps; /* supported pm features */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 5a794cb503ea..6c5fc36827fe 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -138,6 +138,7 @@ static inline bool mmc_op_multi(u32 opcode)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_SWITCH_ERROR (1 << 7) /* sx, c */
+#define R1_URGENT_BKOPS (1 << 6) /* sr, a */
#define R1_APP_CMD (1 << 5) /* sr, c */
#define R1_STATE_IDLE 0
@@ -151,6 +152,11 @@ static inline bool mmc_op_multi(u32 opcode)
#define R1_STATE_DIS 8
/*
+ * MMC/SD card state
+ */
+#define R1_STATE_PRG 0x7
+
+/*
* MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
* R1 is the low order byte; R2 is the next highest byte, when present.
*/
@@ -272,6 +278,9 @@ struct _mmc_csd {
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
+#define EXT_CSD_HPI_MGMT 161 /* R/W */
+#define EXT_CSD_BKOPS_EN 163 /* R/W */
+#define EXT_CSD_BKOPS_START 164 /* R/W */
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
#define EXT_CSD_PART_CONFIG 179 /* R/W */
@@ -281,6 +290,7 @@ struct _mmc_csd {
#define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_STRUCTURE 194 /* RO */
#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */
#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
#define EXT_CSD_S_A_TIMEOUT 217 /* RO */
@@ -293,6 +303,9 @@ struct _mmc_csd {
#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */
#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
#define EXT_CSD_TRIM_MULT 232 /* RO */
+#define EXT_CSD_BKOPS_STATUS 246 /* RO */
+#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */
+#define EXT_CSD_HPI_FEATURES 503 /* RO */
/*
* EXT_CSD field definitions