linux-mmc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v9] mmc: support BKOPS feature for eMMC
@ 2012-06-08  4:39 Jaehoon Chung
  2012-06-13 12:40 ` Dong, Chuanxiao
                   ` (4 more replies)
  0 siblings, 5 replies; 23+ messages in thread
From: Jaehoon Chung @ 2012-06-08  4:39 UTC (permalink / raw)
  To: linux-mmc
  Cc: Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Maya Erez,
	Ulf Hansson

Enable eMMC background operations (BKOPS) feature.

If URGENT_BKOPS is set after a response, note that BKOPS
are required. After all I/O requests are finished, run
BKOPS if required. Should read/write operations be requested
during BKOPS, first issue HPI to interrupt the ongoing BKOPS
and then service the request.
If BKOPS-STATUS is upper than LEVEL2, need to check until clear
the BKOPS-STATUS vaule. 

If you want to enable this feature, set MMC_CAP2_BKOPS.
And if you want to set the BKOPS_EN bit in ext_csd register,
use the MMC_CAP2_INIT_BKOPS.

Future considerations
 * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
 * Interrupt ongoing BKOPS before powering off the card.
 * How get BKOPS_STATUS value.(periodically send ext_csd command?)

Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
Signed-off-by: Maya Erez <merez@codeaurora.org>
---
Changelog V9:
	- Rebased on patch-v7.
	- Added Konstantin and Maya's patch
		: mmc:core: Define synchronous BKOPS timeout
		: mmc:core: eMMC4.5 BKOPS fixes
	- Removed periodic bkops
		: This feature will do in future work
	- Add __mmc_switch() for waiting_for_prod_done.
	
Changelog V8:
	- Remove host->lock spin lock reviewed by Adrian
	- Support periodic start bkops
	- when bkops_status is level-3, if timeout is set to 0, send hpi.
	- Move the start-bkops point
Changelog V7:
	- Use HPI command when issued URGENT_BKOPS
	- Recheck until clearing the bkops-status bit.
Changelog V6:
	- Add the flag of check-bkops-status.
	  (For fixing async_req problem)
	- Add the capability for MMC_CAP2_INIT_BKOPS.
	  (When unset the bkops_en bit in ext_csd register)
	- modify the wrong condition.
Changelog V5:
	- Rebase based on the latest mmc-next.
	- modify codes based-on Chris's comment
Changelog V4:
	- Add mmc_read_bkops_status
	- When URGENT_BKOPS(level2-3), didn't use HPI command.
	- In mmc_switch(), use R1B/R1 according to level.
Changelog V3:
	- move the bkops setting's location in mmc_blk_issue_rw_rq
	- modify condition checking
	- bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
	- remove the unused code
	- change pr_debug instead of pr_warn in mmc_send_hpi_cmd
	- Add the Future consideration suggested by Per
Changelog V2:
	- Use EXCEPTION_STATUS instead of URGENT_BKOPS
	- Add function to check Exception_status(for eMMC4.5)
---
 drivers/mmc/card/queue.c   |    2 +
 drivers/mmc/core/core.c    |  161 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/mmc/core/mmc.c     |   18 +++++
 drivers/mmc/core/mmc_ops.c |   27 ++++++-
 include/linux/mmc/card.h   |   16 +++++
 include/linux/mmc/core.h   |    5 ++
 include/linux/mmc/host.h   |    2 +
 include/linux/mmc/mmc.h    |   19 +++++
 8 files changed, 243 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index e360a97..e4a2cde 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)
 		spin_unlock_irq(q->queue_lock);
 
 		if (req || mq->mqrq_prev->req) {
+			if (mmc_card_doing_bkops(mq->card))
+				mmc_stop_bkops(mq->card);
 			set_current_state(TASK_RUNNING);
 			mq->issue_fn(mq, req);
 		} else {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 0b6141d..9adb004 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -41,6 +41,12 @@
 #include "sd_ops.h"
 #include "sdio_ops.h"
 
+/*
+ * The Background operation can take a long time, depends on the house keeping
+ * operations the card has to peform
+ */
+#define MMC_BKOPS_MAX_TIMEOUT	(4 * 80 * 1000) /* max time to wait in ms */
+
 static struct workqueue_struct *workqueue;
 static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
 
@@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
 		if (mrq->done)
 			mrq->done(mrq);
 
+		/*
+		 * Check BKOPS urgency from each R1 response
+		 */
+		if (host->card && mmc_card_mmc(host->card) &&
+			((mmc_resp_type(cmd) == MMC_RSP_R1) ||
+			 (mmc_resp_type(cmd) == MMC_RSP_R1B)) &&
+			(cmd->resp[0] & R1_EXCEPTION_EVENT)) {
+			mmc_card_set_check_bkops(host->card);
+		}
+
 		mmc_host_clk_release(host);
 	}
 }
@@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 	host->ops->request(host, mrq);
 }
 
+/**
+ *	mmc_start_bkops - start BKOPS for supported cards
+ *	@card: MMC card to start BKOPS
+ *
+ *	Start background operations whenever requested.
+ *	when the urgent BKOPS bit is set in a R1 command response
+ *	then background operations should be started immediately.
+*/
+void mmc_start_bkops(struct mmc_card *card)
+{
+	int err;
+	int timeout;
+	u8 wait_for_prog_done;
+
+	BUG_ON(!card);
+	if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
+		return;
+
+	if (mmc_card_check_bkops(card)) {
+		mmc_card_clr_check_bkops(card);
+		if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
+			if (card->ext_csd.raw_bkops_status)
+				mmc_card_set_need_bkops(card);
+	}
+
+	/*
+	 * If card is already doing bkops or need for
+	 * bkops flag is not set, then do nothing just
+	 * return
+	 */
+	if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
+		return;
+
+	mmc_claim_host(card->host);
+	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
+		timeout = MMC_BKOPS_MAX_TIMEOUT;
+		wait_for_prog_done = 1;
+	} else {
+		timeout = 0;
+		wait_for_prog_done = 0;
+	}
+
+	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+			EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
+	if (err) {
+		pr_warn("%s: error %d starting bkops\n",
+			   mmc_hostname(card->host), err);
+		mmc_card_clr_need_bkops(card);
+		goto out;
+	}
+
+	mmc_card_clr_need_bkops(card);
+
+	/*
+	 * For urgent bkops status (LEVEL_2 and more)
+	 * bkops executed synchronously, otherwise
+	 * the operation is in progress
+	 */
+	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
+		mmc_card_set_check_bkops(card);
+	else
+		mmc_card_set_doing_bkops(card);
+
+out:
+	mmc_release_host(card->host);
+}
+EXPORT_SYMBOL(mmc_start_bkops);
+
 static void mmc_wait_done(struct mmc_request *mrq)
 {
 	complete(&mrq->completion);
@@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
 	if (!err && areq)
 		start_err = __mmc_start_req(host, areq->mrq);
 
-	if (host->areq)
+	if (host->areq) {
+		if (!areq && host->areq && mmc_card_mmc(host->card))
+			mmc_start_bkops(host->card);
 		mmc_post_req(host, host->areq->mrq, 0);
+	}
 
 	 /* Cancel a prepared request if it was not started. */
 	if ((err || start_err) && areq)
-			mmc_post_req(host, areq->mrq, -EINVAL);
+		mmc_post_req(host, areq->mrq, -EINVAL);
 
 	if (err)
 		host->areq = NULL;
@@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
 EXPORT_SYMBOL(mmc_wait_for_cmd);
 
 /**
+ *	mmc_stop_bkops - stop ongoing BKOPS
+ *	@card: MMC card to check BKOPS
+ *
+ *	Send HPI command to stop ongoing background operations,
+ *	to allow rapid servicing of foreground operations,e.g. read/
+ *	writes. Wait until the card comes out of the programming state
+ *	to avoid errors in servicing read/write requests.
+ */
+int mmc_stop_bkops(struct mmc_card *card)
+{
+	int err = 0;
+
+	BUG_ON(!card);
+
+	err = mmc_interrupt_hpi(card);
+
+	mmc_card_clr_doing_bkops(card);
+
+	return err;
+}
+EXPORT_SYMBOL(mmc_stop_bkops);
+
+int mmc_read_bkops_status(struct mmc_card *card)
+{
+	int err;
+	u8 ext_csd[512];
+
+	mmc_claim_host(card->host);
+	err = mmc_send_ext_csd(card, ext_csd);
+	mmc_release_host(card->host);
+	if (err)
+		return err;
+
+	card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
+	card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXCEPTION_STATUS];
+
+	return 0;
+}
+EXPORT_SYMBOL(mmc_read_bkops_status);
+
+int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
+{
+	int err;
+
+	err = mmc_read_bkops_status(card);
+	if (err) {
+		pr_err("%s: Didn't read bkops status : %d\n",
+		       mmc_hostname(card->host), err);
+		return 0;
+	}
+
+	/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
+	if (card->ext_csd.rev == 5)
+		return 1;
+
+	return (card->ext_csd.raw_exception_status & value) ? 1 : 0;
+}
+EXPORT_SYMBOL(mmc_is_exception_event);
+
+/**
  *	mmc_set_data_timeout - set the timeout for a data command
  *	@data: data phase for command
  *	@card: the MMC card associated with the data transfer
@@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
 
-		if (host->bus_ops->suspend)
+		if (host->bus_ops->suspend) {
+			if (mmc_card_doing_bkops(host->card))
+				mmc_stop_bkops(host->card);
 			err = host->bus_ops->suspend(host);
+		}
 
 		if (err == -ENOSYS || !host->bus_ops->resume) {
 			/*
@@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
 	switch (mode) {
 	case PM_HIBERNATION_PREPARE:
 	case PM_SUSPEND_PREPARE:
+		if (host->card && mmc_card_mmc(host->card) &&
+				mmc_card_doing_bkops(host->card)) {
+			mmc_interrupt_hpi(host->card);
+			mmc_card_clr_doing_bkops(host->card);
+		}
 
 		spin_lock_irqsave(&host->lock, flags);
 		host->rescan_disable = 1;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 258b203..9e036e5 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
 	}
 
 	if (card->ext_csd.rev >= 5) {
+		/* check whether the eMMC card support BKOPS */
+		if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
+			card->ext_csd.bkops = 1;
+			card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
+			card->ext_csd.raw_bkops_status =
+				ext_csd[EXT_CSD_BKOPS_STATUS];
+			if (!card->ext_csd.bkops_en &&
+				card->host->caps2 & MMC_CAP2_INIT_BKOPS) {
+				err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+					EXT_CSD_BKOPS_EN, 1, 0);
+				if (err)
+					pr_warn("%s: Enabling BKOPS failed\n",
+						mmc_hostname(card->host));
+				else
+					card->ext_csd.bkops_en = 1;
+			}
+		}
+
 		/* check whether the eMMC card supports HPI */
 		if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
 			card->ext_csd.hpi = 1;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 69370f4..f0bebd9 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
 }
 
 /**
- *	mmc_switch - modify EXT_CSD register
+ *	__mmc_switch - modify EXT_CSD register
  *	@card: the MMC card associated with the data transfer
  *	@set: cmd set values
  *	@index: EXT_CSD register index
  *	@value: value to program into EXT_CSD register
  *	@timeout_ms: timeout (ms) for operation performed by register write,
  *                   timeout of zero implies maximum possible timeout
+ *      @wait_for_prod_done: is waiting for program done
  *
  *	Modifies the EXT_CSD register for selected card.
  */
-int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
-	       unsigned int timeout_ms)
+int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
+	       unsigned int timeout_ms, u8 wait_for_prog_done)
+
 {
 	int err;
 	struct mmc_command cmd = {0};
@@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
 		  (index << 16) |
 		  (value << 8) |
 		  set;
-	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+
+	cmd.flags = MMC_CMD_AC;
+	if (wait_for_prog_done)
+		cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
+	else
+		cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
+
 	cmd.cmd_timeout_ms = timeout_ms;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err)
 		return err;
 
+	/* No need to check card status in case of BKOPS switch*/
+	if (index == EXT_CSD_BKOPS_START)
+		return 0;
+
 	/* Must check status to be sure of no errors */
 	do {
 		err = mmc_send_status(card, &status);
@@ -423,6 +435,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(__mmc_switch);
+
+int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
+		unsigned int timeout_ms)
+{
+	return __mmc_switch(card, set, index, value, timeout_ms, 1);
+}
 EXPORT_SYMBOL_GPL(mmc_switch);
 
 int mmc_send_status(struct mmc_card *card, u32 *status)
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index d76513b..50b3539 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -76,10 +76,13 @@ struct mmc_ext_csd {
 	bool			hpi_en;			/* HPI enablebit */
 	bool			hpi;			/* HPI support bit */
 	unsigned int		hpi_cmd;		/* cmd used as HPI */
+	bool			bkops;		/* background support bit */
+	bool			bkops_en;	/* background enable bit */
 	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
 	unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size */
 	unsigned int		boot_ro_lock;		/* ro lock support */
 	bool			boot_ro_lockable;
+	u8			raw_exception_status;	/* 53 */
 	u8			raw_partition_support;	/* 160 */
 	u8			raw_erased_mem_count;	/* 181 */
 	u8			raw_ext_csd_structure;	/* 194 */
@@ -93,6 +96,7 @@ struct mmc_ext_csd {
 	u8			raw_sec_erase_mult;	/* 230 */
 	u8			raw_sec_feature_support;/* 231 */
 	u8			raw_trim_mult;		/* 232 */
+	u8			raw_bkops_status;	/* 246 */
 	u8			raw_sectors[4];		/* 212 - 4 bytes */
 
 	unsigned int            feature_support;
@@ -225,6 +229,9 @@ struct mmc_card {
 #define MMC_CARD_REMOVED	(1<<7)		/* card has been removed */
 #define MMC_STATE_HIGHSPEED_200	(1<<8)		/* card is in HS200 mode */
 #define MMC_STATE_SLEEP		(1<<9)		/* card is in sleep state */
+#define MMC_STATE_NEED_BKOPS	(1<<10)		/* card need to do BKOPS */
+#define MMC_STATE_DOING_BKOPS	(1<<11)		/* card is doing BKOPS */
+#define MMC_STATE_CHECK_BKOPS	(1<<12)		/* card need to check BKOPS */
 	unsigned int		quirks; 	/* card quirks */
 #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes outside of the VS CCCR range */
 #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func->cur_blksize */
@@ -391,6 +398,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
 #define mmc_card_removed(c)	((c) && ((c)->state & MMC_CARD_REMOVED))
 #define mmc_card_is_sleep(c)	((c)->state & MMC_STATE_SLEEP)
+#define mmc_card_need_bkops(c)	((c)->state & MMC_STATE_NEED_BKOPS)
+#define mmc_card_doing_bkops(c)	((c)->state & MMC_STATE_DOING_BKOPS)
+#define mmc_card_check_bkops(c) ((c)->state & MMC_STATE_CHECK_BKOPS)
 
 #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -403,7 +413,13 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
 #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
 #define mmc_card_set_sleep(c)	((c)->state |= MMC_STATE_SLEEP)
+#define mmc_card_set_need_bkops(c)	((c)->state |= MMC_STATE_NEED_BKOPS)
+#define mmc_card_set_doing_bkops(c)	((c)->state |= MMC_STATE_DOING_BKOPS)
+#define mmc_card_set_check_bkops(c) ((c)->state |= MMC_STATE_CHECK_BKOPS)
 
+#define mmc_card_clr_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_check_bkops(c) ((c)->state &= ~MMC_STATE_CHECK_BKOPS)
 #define mmc_card_clr_sleep(c)	((c)->state &= ~MMC_STATE_SLEEP)
 /*
  * Quirk add/remove for MMC products.
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 1b431c7..bda3d86 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -134,6 +134,9 @@ struct mmc_host;
 struct mmc_card;
 struct mmc_async_req;
 
+extern int mmc_stop_bkops(struct mmc_card *);
+extern int mmc_read_bkops_status(struct mmc_card *);
+extern int mmc_is_exception_event(struct mmc_card *, unsigned int);
 extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
 					   struct mmc_async_req *, int *);
 extern int mmc_interrupt_hpi(struct mmc_card *);
@@ -142,6 +145,8 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
 extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
 	struct mmc_command *, int);
+extern void mmc_start_bkops(struct mmc_card *card);
+extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
 extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
 
 #define MMC_ERASE_ARG		0x00000000
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0707d22..d23caf2 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -238,6 +238,8 @@ struct mmc_host {
 #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
 #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card removal */
 #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
+#define MMC_CAP2_INIT_BKOPS	(1 << 10)	/* To enable BKOPS */
+#define MMC_CAP2_BKOPS		(1 << 11)	/* BKOPS supported */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 	unsigned int        power_notify_type;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index d425cab..80cfbf1 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -139,6 +139,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_EXCEPTION_EVENT	(1 << 6)	/* sx, a */
 #define R1_APP_CMD		(1 << 5)	/* sr, c */
 
 #define R1_STATE_IDLE	0
@@ -274,12 +275,15 @@ struct _mmc_csd {
 #define EXT_CSD_FLUSH_CACHE		32      /* W */
 #define EXT_CSD_CACHE_CTRL		33      /* R/W */
 #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
+#define EXT_CSD_EXCEPTION_STATUS	54	/* RO */
 #define EXT_CSD_DATA_SECTOR_SIZE	61	/* R */
 #define EXT_CSD_GP_SIZE_MULT		143	/* R/W */
 #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_RST_N_FUNCTION		162	/* R/W */
+#define EXT_CSD_BKOPS_EN		163	/* R/W */
+#define EXT_CSD_BKOPS_START		164	/* W */
 #define EXT_CSD_SANITIZE_START		165     /* W */
 #define EXT_CSD_WR_REL_PARAM		166	/* RO */
 #define EXT_CSD_BOOT_WP			173	/* R/W */
@@ -313,11 +317,13 @@ struct _mmc_csd {
 #define EXT_CSD_PWR_CL_200_360		237	/* RO */
 #define EXT_CSD_PWR_CL_DDR_52_195	238	/* RO */
 #define EXT_CSD_PWR_CL_DDR_52_360	239	/* RO */
+#define EXT_CSD_BKOPS_STATUS		246	/* RO */
 #define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
 #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
 #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
 #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
 #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
+#define EXT_CSD_BKOPS_SUPPORT		502	/* RO */
 #define EXT_CSD_HPI_FEATURES		503	/* RO */
 
 /*
@@ -386,4 +392,17 @@ struct _mmc_csd {
 #define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1 in value */
 #define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */
 
+/*
+ * BKOPS status level
+ */
+#define EXT_CSD_BKOPS_LEVEL_2		0x2
+
+/*
+ * EXCEPTION_EVENT_STATUS field (eMMC4.5)
+ */
+#define EXT_CSD_URGENT_BKOPS		BIT(0)
+#define EXT_CSD_DYNCAP_NEEDED		BIT(1)
+#define EXT_CSD_SYSPOOL_EXHAUSTED	BIT(2)
+#define EXT_CSD_PACKED_FAILURE		BIT(3)
+
 #endif /* LINUX_MMC_MMC_H */
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
@ 2012-06-12 10:58 merez
  2012-06-13  3:07 ` Jaehoon Chung
  0 siblings, 1 reply; 23+ messages in thread
From: merez @ 2012-06-12 10:58 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Maya Erez,
	Ulf Hansson

> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
mmc_host
> *host,
>  	if (!err && areq)
>  		start_err = __mmc_start_req(host, areq->mrq);
>
> -	if (host->areq)
> +	if (host->areq) {
> +		if (!areq && host->areq && mmc_card_mmc(host->card))
> +			mmc_start_bkops(host->card);
I think it would be better to start tey BKOPs in the queue thread when it
becomes idle.
We have seen cases where there are several requests fetched and then NULL
but by the time the previous request ends there are more requests to be
fetched. Starting the BKOPs here instead of in the queue thread increases
the probability of using HPI to stop the BKOPs due to incoming request,
which is not a desirable action.

>  		mmc_post_req(host, host->areq->mrq, 0);
> +	}
>
>  	 /* Cancel a prepared request if it was not started. */
>  	if ((err || start_err) && areq)
> -			mmc_post_req(host, areq->mrq, -EINVAL);
> +		mmc_post_req(host, areq->mrq, -EINVAL);
Remove this change, it is not related to this patch.

Thanks,
Maya Erez
Consultant for Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum





^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-12 10:58 merez
@ 2012-06-13  3:07 ` Jaehoon Chung
  2012-06-13  6:18   ` merez
  0 siblings, 1 reply; 23+ messages in thread
From: Jaehoon Chung @ 2012-06-13  3:07 UTC (permalink / raw)
  To: merez
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com, Saugata Das, Konstantin Dorfman, Adrian Hunter,
	Ulf Hansson

On 06/12/2012 07:58 PM, merez@codeaurora.org wrote:

>> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
> mmc_host
>> *host,
>>  	if (!err && areq)
>>  		start_err = __mmc_start_req(host, areq->mrq);
>>
>> -	if (host->areq)
>> +	if (host->areq) {
>> +		if (!areq && host->areq && mmc_card_mmc(host->card))
>> +			mmc_start_bkops(host->card);
> I think it would be better to start tey BKOPs in the queue thread when it
> becomes idle.
> We have seen cases where there are several requests fetched and then NULL
> but by the time the previous request ends there are more requests to be
> fetched. Starting the BKOPs here instead of in the queue thread increases
> the probability of using HPI to stop the BKOPs due to incoming request,
> which is not a desirable action.

I will change this point.

> 
>>  		mmc_post_req(host, host->areq->mrq, 0);
>> +	}
>>
>>  	 /* Cancel a prepared request if it was not started. */
>>  	if ((err || start_err) && areq)
>> -			mmc_post_req(host, areq->mrq, -EINVAL);
>> +		mmc_post_req(host, areq->mrq, -EINVAL);
> Remove this change, it is not related to this patch.

Ok.
Anything else?

Best Regards,
Jaehoon Chung

> 
> Thanks,
> Maya Erez
> Consultant for Qualcomm Innovation Center, Inc.
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
> 
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-13  3:07 ` Jaehoon Chung
@ 2012-06-13  6:18   ` merez
  0 siblings, 0 replies; 23+ messages in thread
From: merez @ 2012-06-13  6:18 UTC (permalink / raw)
  Cc: merez, Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com, Saugata Das, Konstantin Dorfman, Adrian Hunter,
	Ulf Hansson


On Tue, June 12, 2012 8:07 pm, Jaehoon Chung wrote:
> On 06/12/2012 07:58 PM, merez@codeaurora.org wrote:
>
>>> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
>> mmc_host
>>> *host,
>>>  	if (!err && areq)
>>>  		start_err = __mmc_start_req(host, areq->mrq);
>>>
>>> -	if (host->areq)
>>> +	if (host->areq) {
>>> +		if (!areq && host->areq && mmc_card_mmc(host->card))
>>> +			mmc_start_bkops(host->card);
>> I think it would be better to start tey BKOPs in the queue thread when
>> it
>> becomes idle.
>> We have seen cases where there are several requests fetched and then
>> NULL
>> but by the time the previous request ends there are more requests to be
>> fetched. Starting the BKOPs here instead of in the queue thread
>> increases
>> the probability of using HPI to stop the BKOPs due to incoming request,
>> which is not a desirable action.
>
> I will change this point.
>
>>
>>>  		mmc_post_req(host, host->areq->mrq, 0);
>>> +	}
>>>
>>>  	 /* Cancel a prepared request if it was not started. */
>>>  	if ((err || start_err) && areq)
>>> -			mmc_post_req(host, areq->mrq, -EINVAL);
>>> +		mmc_post_req(host, areq->mrq, -EINVAL);
>> Remove this change, it is not related to this patch.
>
> Ok.
> Anything else?
>
> Best Regards,
> Jaehoon Chung
We have no further comments.

Thanks,
Maya Erez

-- 
Sent by consultant of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum


^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-08  4:39 [PATCH v9] mmc: support BKOPS feature for eMMC Jaehoon Chung
@ 2012-06-13 12:40 ` Dong, Chuanxiao
       [not found]   ` <17296D9F8FF2234F831FC3DF505A87A90FEA11E5@SHSMSX102.ccr.corp.intel.com >
  2012-06-15  3:00   ` Jaehoon Chung
  2012-06-13 13:53 ` S, Venkatraman
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 23+ messages in thread
From: Dong, Chuanxiao @ 2012-06-13 12:40 UTC (permalink / raw)
  To: Jaehoon Chung, linux-mmc
  Cc: Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, svenkatr@ti.com, Saugata Das,
	Konstantin Dorfman, Hunter, Adrian, Maya Erez, Ulf Hansson

Hi Jaehoon,

> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index
> 0b6141d..9adb004 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -41,6 +41,12 @@
>  #include "sd_ops.h"
>  #include "sdio_ops.h"
> 
> +/*
> + * The Background operation can take a long time, depends on the house
> +keeping
> + * operations the card has to peform
> + */
> +#define MMC_BKOPS_MAX_TIMEOUT	(4 * 80 * 1000) /* max time to wait in
> ms */
> +
Just go through the eMMC specification, didn't find any clue about the max timeout value for background operations. So, could you please tell why you choose 320s?
This value will used to calculate TIMEOUT_CONTROL registers for SDHC host controller. And as far as I know, this value is too large for some host.

>  static struct workqueue_struct *workqueue;  static const unsigned freqs[] =
> { 400000, 300000, 200000, 100000 };
> 
> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct
> mmc_request *mrq)
>  		if (mrq->done)
>  			mrq->done(mrq);
> 
> +		/*
> +		 * Check BKOPS urgency from each R1 response
> +		 */
> +		if (host->card && mmc_card_mmc(host->card) &&
> +			((mmc_resp_type(cmd) == MMC_RSP_R1) ||
> +			 (mmc_resp_type(cmd) == MMC_RSP_R1B)) &&
> +			(cmd->resp[0] & R1_EXCEPTION_EVENT)) {
> +			mmc_card_set_check_bkops(host->card);
> +		}
> +
>  		mmc_host_clk_release(host);
>  	}
>  }
> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct
> mmc_request *mrq)
>  	host->ops->request(host, mrq);
>  }
> 
> +/**
> + *	mmc_start_bkops - start BKOPS for supported cards
> + *	@card: MMC card to start BKOPS
> + *
> + *	Start background operations whenever requested.
> + *	when the urgent BKOPS bit is set in a R1 command response
> + *	then background operations should be started immediately.
> +*/
> +void mmc_start_bkops(struct mmc_card *card) {
> +	int err;
> +	int timeout;
> +	u8 wait_for_prog_done;
> +
> +	BUG_ON(!card);
> +	if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
> +		return;
> +
> +	if (mmc_card_check_bkops(card)) {
> +		mmc_card_clr_check_bkops(card);
> +		if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
eMMC4.5 specification has said that "EXCEPTION_EVENTS_STATUS is set whenever the levels is either 2 or 3." To my understanding, EXCEPTION_EVENTS_STATUS will be set only when level-2 or level-3. For level-0 and level-1, EXCEPTION_EVENTS_STATUS won't be set. Regarding this, checking EXCEPTION_EVENTS_CTRL register during mmc card initialization phase can help to save the ext_csd reading commands.

I don't have eMMC4.5 chip on hand, not sure how the real device is implemented. It is better if you can provide a testing result with your EXCEPTION_EVENTS_STATUS register and BKOPS_STATUS register.

> +			if (card->ext_csd.raw_bkops_status)
> +				mmc_card_set_need_bkops(card);
> +	}
> +
> +	/*
> +	 * If card is already doing bkops or need for
> +	 * bkops flag is not set, then do nothing just
> +	 * return
> +	 */
> +	if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
> +		return;
> +
> +	mmc_claim_host(card->host);
> +	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
> +		timeout = MMC_BKOPS_MAX_TIMEOUT;
> +		wait_for_prog_done = 1;
> +	} else {
> +		timeout = 0;
> +		wait_for_prog_done = 0;
> +	}
As specification, CMD6 needs R1B response. Is it really good to use R1 response for it?
To my understanding, the BUSY signal is indicate whether the CMD6 switching is done, but not whether the background operation is done. After CMD6, card will stay in PRG state which means card is still not ready for data transferring. Once the card state change to transfer state, background operation is really finished. So the signal for whether background operations finished is not the BUSY signal but the card state. Please correct me if I am wrong on this point. Of cause, you can have a test with your device.

Thanks
Chuanxiao

> +
> +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +			EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
> +	if (err) {
> +		pr_warn("%s: error %d starting bkops\n",
> +			   mmc_hostname(card->host), err);
> +		mmc_card_clr_need_bkops(card);
> +		goto out;
> +	}
> +
> +	mmc_card_clr_need_bkops(card);
> +
> +	/*
> +	 * For urgent bkops status (LEVEL_2 and more)
> +	 * bkops executed synchronously, otherwise
> +	 * the operation is in progress
> +	 */
> +	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
> +		mmc_card_set_check_bkops(card);
> +	else
> +		mmc_card_set_doing_bkops(card);
> +
> +out:
> +	mmc_release_host(card->host);
> +}
> +EXPORT_SYMBOL(mmc_start_bkops);
> +
>  static void mmc_wait_done(struct mmc_request *mrq)  {
>  	complete(&mrq->completion);
> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
> mmc_host *host,
>  	if (!err && areq)
>  		start_err = __mmc_start_req(host, areq->mrq);
> 
> -	if (host->areq)
> +	if (host->areq) {
> +		if (!areq && host->areq && mmc_card_mmc(host->card))
> +			mmc_start_bkops(host->card);
>  		mmc_post_req(host, host->areq->mrq, 0);
> +	}
> 
>  	 /* Cancel a prepared request if it was not started. */
>  	if ((err || start_err) && areq)
> -			mmc_post_req(host, areq->mrq, -EINVAL);
> +		mmc_post_req(host, areq->mrq, -EINVAL);
> 
>  	if (err)
>  		host->areq = NULL;
> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
> mmc_command *cmd, int retries  EXPORT_SYMBOL(mmc_wait_for_cmd);
> 
>  /**
> + *	mmc_stop_bkops - stop ongoing BKOPS
> + *	@card: MMC card to check BKOPS
> + *
> + *	Send HPI command to stop ongoing background operations,
> + *	to allow rapid servicing of foreground operations,e.g. read/
> + *	writes. Wait until the card comes out of the programming state
> + *	to avoid errors in servicing read/write requests.
> + */
> +int mmc_stop_bkops(struct mmc_card *card) {
> +	int err = 0;
> +
> +	BUG_ON(!card);
> +
> +	err = mmc_interrupt_hpi(card);
> +
> +	mmc_card_clr_doing_bkops(card);
> +
> +	return err;
> +}
> +EXPORT_SYMBOL(mmc_stop_bkops);
> +
> +int mmc_read_bkops_status(struct mmc_card *card) {
> +	int err;
> +	u8 ext_csd[512];
> +
> +	mmc_claim_host(card->host);
> +	err = mmc_send_ext_csd(card, ext_csd);
> +	mmc_release_host(card->host);
> +	if (err)
> +		return err;
> +
> +	card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
> +	card->ext_csd.raw_exception_status =
> +ext_csd[EXT_CSD_EXCEPTION_STATUS];
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mmc_read_bkops_status);
> +
> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value) {
> +	int err;
> +
> +	err = mmc_read_bkops_status(card);
> +	if (err) {
> +		pr_err("%s: Didn't read bkops status : %d\n",
> +		       mmc_hostname(card->host), err);
> +		return 0;
> +	}
> +
> +	/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
> +	if (card->ext_csd.rev == 5)
> +		return 1;
> +
> +	return (card->ext_csd.raw_exception_status & value) ? 1 : 0; }
> +EXPORT_SYMBOL(mmc_is_exception_event);
> +
> +/**
>   *	mmc_set_data_timeout - set the timeout for a data command
>   *	@data: data phase for command
>   *	@card: the MMC card associated with the data transfer
> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
>  	mmc_bus_get(host);
>  	if (host->bus_ops && !host->bus_dead) {
> 
> -		if (host->bus_ops->suspend)
> +		if (host->bus_ops->suspend) {
> +			if (mmc_card_doing_bkops(host->card))
> +				mmc_stop_bkops(host->card);
>  			err = host->bus_ops->suspend(host);
> +		}
> 
>  		if (err == -ENOSYS || !host->bus_ops->resume) {
>  			/*
> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
>  	switch (mode) {
>  	case PM_HIBERNATION_PREPARE:
>  	case PM_SUSPEND_PREPARE:
> +		if (host->card && mmc_card_mmc(host->card) &&
> +				mmc_card_doing_bkops(host->card)) {
> +			mmc_interrupt_hpi(host->card);
> +			mmc_card_clr_doing_bkops(host->card);
> +		}
> 
>  		spin_lock_irqsave(&host->lock, flags);
>  		host->rescan_disable = 1;
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index
> 258b203..9e036e5 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8
> *ext_csd)
>  	}
> 
>  	if (card->ext_csd.rev >= 5) {
> +		/* check whether the eMMC card support BKOPS */
> +		if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
> +			card->ext_csd.bkops = 1;
> +			card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
> +			card->ext_csd.raw_bkops_status =
> +				ext_csd[EXT_CSD_BKOPS_STATUS];
> +			if (!card->ext_csd.bkops_en &&
> +				card->host->caps2 & MMC_CAP2_INIT_BKOPS) {
> +				err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +					EXT_CSD_BKOPS_EN, 1, 0);
> +				if (err)
> +					pr_warn("%s: Enabling BKOPS failed\n",
> +						mmc_hostname(card->host));
> +				else
> +					card->ext_csd.bkops_en = 1;
> +			}
> +		}
> +
>  		/* check whether the eMMC card supports HPI */
>  		if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
>  			card->ext_csd.hpi = 1;
> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index
> 69370f4..f0bebd9 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int
> use_crc)  }
> 
>  /**
> - *	mmc_switch - modify EXT_CSD register
> + *	__mmc_switch - modify EXT_CSD register
>   *	@card: the MMC card associated with the data transfer
>   *	@set: cmd set values
>   *	@index: EXT_CSD register index
>   *	@value: value to program into EXT_CSD register
>   *	@timeout_ms: timeout (ms) for operation performed by register write,
>   *                   timeout of zero implies maximum possible timeout
> + *      @wait_for_prod_done: is waiting for program done
>   *
>   *	Modifies the EXT_CSD register for selected card.
>   */
> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> -	       unsigned int timeout_ms)
> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> +	       unsigned int timeout_ms, u8 wait_for_prog_done)
> +
>  {
>  	int err;
>  	struct mmc_command cmd = {0};
> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8
> index, u8 value,
>  		  (index << 16) |
>  		  (value << 8) |
>  		  set;
> -	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
> +
> +	cmd.flags = MMC_CMD_AC;
> +	if (wait_for_prog_done)
> +		cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
> +	else
> +		cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
> +
>  	cmd.cmd_timeout_ms = timeout_ms;
> 
>  	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
>  	if (err)
>  		return err;
> 
> +	/* No need to check card status in case of BKOPS switch*/
> +	if (index == EXT_CSD_BKOPS_START)
> +		return 0;
> +
>  	/* Must check status to be sure of no errors */
>  	do {
>  		err = mmc_send_status(card, &status); @@ -423,6 +435,13 @@ int
> mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> 
>  	return 0;
>  }
> +EXPORT_SYMBOL_GPL(__mmc_switch);
> +
> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> +		unsigned int timeout_ms)
> +{
> +	return __mmc_switch(card, set, index, value, timeout_ms, 1); }
>  EXPORT_SYMBOL_GPL(mmc_switch);
> 
>  int mmc_send_status(struct mmc_card *card, u32 *status) diff --git
> a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d76513b..50b3539
> 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
>  	bool			hpi_en;			/* HPI enablebit */
>  	bool			hpi;			/* HPI support bit */
>  	unsigned int		hpi_cmd;		/* cmd used as HPI */
> +	bool			bkops;		/* background support bit */
> +	bool			bkops_en;	/* background enable bit */
>  	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>  	unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size */
>  	unsigned int		boot_ro_lock;		/* ro lock support */
>  	bool			boot_ro_lockable;
> +	u8			raw_exception_status;	/* 53 */
>  	u8			raw_partition_support;	/* 160 */
>  	u8			raw_erased_mem_count;	/* 181 */
>  	u8			raw_ext_csd_structure;	/* 194 */
> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
>  	u8			raw_sec_erase_mult;	/* 230 */
>  	u8			raw_sec_feature_support;/* 231 */
>  	u8			raw_trim_mult;		/* 232 */
> +	u8			raw_bkops_status;	/* 246 */
>  	u8			raw_sectors[4];		/* 212 - 4 bytes */
> 
>  	unsigned int            feature_support;
> @@ -225,6 +229,9 @@ struct mmc_card {
>  #define MMC_CARD_REMOVED	(1<<7)		/* card has been removed */
>  #define MMC_STATE_HIGHSPEED_200	(1<<8)		/* card is in HS200
> mode */
>  #define MMC_STATE_SLEEP		(1<<9)		/* card is in sleep state */
> +#define MMC_STATE_NEED_BKOPS	(1<<10)		/* card need to do BKOPS */
> +#define MMC_STATE_DOING_BKOPS	(1<<11)		/* card is doing BKOPS */
> +#define MMC_STATE_CHECK_BKOPS	(1<<12)		/* card need to check BKOPS
> */
>  	unsigned int		quirks; 	/* card quirks */
>  #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes
> outside of the VS CCCR range */
>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func->cur_blksize
> */
> @@ -391,6 +398,9 @@ static inline void __maybe_unused remove_quirk(struct
> mmc_card *card, int data)  #define mmc_card_ext_capacity(c) ((c)->state &
> MMC_CARD_SDXC)
>  #define mmc_card_removed(c)	((c) && ((c)->state & MMC_CARD_REMOVED))
>  #define mmc_card_is_sleep(c)	((c)->state & MMC_STATE_SLEEP)
> +#define mmc_card_need_bkops(c)	((c)->state & MMC_STATE_NEED_BKOPS)
> +#define mmc_card_doing_bkops(c)	((c)->state & MMC_STATE_DOING_BKOPS)
> +#define mmc_card_check_bkops(c) ((c)->state & MMC_STATE_CHECK_BKOPS)
> 
>  #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@
> -403,7 +413,13 @@ static inline void __maybe_unused remove_quirk(struct
> mmc_card *card, int data)  #define mmc_card_set_ext_capacity(c) ((c)->state |=
> MMC_CARD_SDXC)  #define mmc_card_set_removed(c) ((c)->state |=
> MMC_CARD_REMOVED)
>  #define mmc_card_set_sleep(c)	((c)->state |= MMC_STATE_SLEEP)
> +#define mmc_card_set_need_bkops(c)	((c)->state |=
> MMC_STATE_NEED_BKOPS)
> +#define mmc_card_set_doing_bkops(c)	((c)->state |=
> MMC_STATE_DOING_BKOPS)
> +#define mmc_card_set_check_bkops(c) ((c)->state |=
> +MMC_STATE_CHECK_BKOPS)
> 
> +#define mmc_card_clr_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_check_bkops(c) ((c)->state &=
> +~MMC_STATE_CHECK_BKOPS)
>  #define mmc_card_clr_sleep(c)	((c)->state &= ~MMC_STATE_SLEEP)
>  /*
>   * Quirk add/remove for MMC products.
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index
> 1b431c7..bda3d86 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -134,6 +134,9 @@ struct mmc_host;
>  struct mmc_card;
>  struct mmc_async_req;
> 
> +extern int mmc_stop_bkops(struct mmc_card *); extern int
> +mmc_read_bkops_status(struct mmc_card *); extern int
> +mmc_is_exception_event(struct mmc_card *, unsigned int);
>  extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>  					   struct mmc_async_req *, int *);  extern int
> mmc_interrupt_hpi(struct mmc_card *); @@ -142,6 +145,8 @@ extern int
> mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);  extern int
> mmc_app_cmd(struct mmc_host *, struct mmc_card *);  extern int
> mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>  	struct mmc_command *, int);
> +extern void mmc_start_bkops(struct mmc_card *card); extern int
> +__mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
> 
>  #define MMC_ERASE_ARG		0x00000000
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index
> 0707d22..d23caf2 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -238,6 +238,8 @@ struct mmc_host {
>  #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
>  #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card
> removal */
>  #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
> +#define MMC_CAP2_INIT_BKOPS	(1 << 10)	/* To enable BKOPS */
> +#define MMC_CAP2_BKOPS		(1 << 11)	/* BKOPS supported */
> 
>  	mmc_pm_flag_t		pm_caps;	/* supported pm features */
>  	unsigned int        power_notify_type;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index
> d425cab..80cfbf1 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -139,6 +139,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_EXCEPTION_EVENT	(1 << 6)	/* sx, a */
>  #define R1_APP_CMD		(1 << 5)	/* sr, c */
> 
>  #define R1_STATE_IDLE	0
> @@ -274,12 +275,15 @@ struct _mmc_csd {
>  #define EXT_CSD_FLUSH_CACHE		32      /* W */
>  #define EXT_CSD_CACHE_CTRL		33      /* R/W */
>  #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
> +#define EXT_CSD_EXCEPTION_STATUS	54	/* RO */
>  #define EXT_CSD_DATA_SECTOR_SIZE	61	/* R */
>  #define EXT_CSD_GP_SIZE_MULT		143	/* R/W */
>  #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_RST_N_FUNCTION		162	/* R/W */
> +#define EXT_CSD_BKOPS_EN		163	/* R/W */
> +#define EXT_CSD_BKOPS_START		164	/* W */
>  #define EXT_CSD_SANITIZE_START		165     /* W */
>  #define EXT_CSD_WR_REL_PARAM		166	/* RO */
>  #define EXT_CSD_BOOT_WP			173	/* R/W */
> @@ -313,11 +317,13 @@ struct _mmc_csd {
>  #define EXT_CSD_PWR_CL_200_360		237	/* RO */
>  #define EXT_CSD_PWR_CL_DDR_52_195	238	/* RO */
>  #define EXT_CSD_PWR_CL_DDR_52_360	239	/* RO */
> +#define EXT_CSD_BKOPS_STATUS		246	/* RO */
>  #define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
>  #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
>  #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
>  #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
>  #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
> +#define EXT_CSD_BKOPS_SUPPORT		502	/* RO */
>  #define EXT_CSD_HPI_FEATURES		503	/* RO */
> 
>  /*
> @@ -386,4 +392,17 @@ struct _mmc_csd {
>  #define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1
> in value */
>  #define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */
> 
> +/*
> + * BKOPS status level
> + */
> +#define EXT_CSD_BKOPS_LEVEL_2		0x2
> +
> +/*
> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)  */
> +#define EXT_CSD_URGENT_BKOPS		BIT(0)
> +#define EXT_CSD_DYNCAP_NEEDED		BIT(1)
> +#define EXT_CSD_SYSPOOL_EXHAUSTED	BIT(2)
> +#define EXT_CSD_PACKED_FAILURE		BIT(3)
> +
>  #endif /* LINUX_MMC_MMC_H */
> --
> 1.7.4.1

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-08  4:39 [PATCH v9] mmc: support BKOPS feature for eMMC Jaehoon Chung
  2012-06-13 12:40 ` Dong, Chuanxiao
@ 2012-06-13 13:53 ` S, Venkatraman
  2012-06-14  8:44   ` merez
  2012-06-15  4:29   ` Jaehoon Chung
  2012-06-14 13:46 ` Adrian Hunter
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 23+ messages in thread
From: S, Venkatraman @ 2012-06-13 13:53 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, Saugata Das,
	Konstantin Dorfman, Adrian Hunter, Maya Erez, Ulf Hansson

On Fri, Jun 8, 2012 at 10:09 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Enable eMMC background operations (BKOPS) feature.
>
> If URGENT_BKOPS is set after a response, note that BKOPS
> are required. After all I/O requests are finished, run
> BKOPS if required. Should read/write operations be requested
> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
> and then service the request.
> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
> the BKOPS-STATUS vaule.
>
> If you want to enable this feature, set MMC_CAP2_BKOPS.
> And if you want to set the BKOPS_EN bit in ext_csd register,
> use the MMC_CAP2_INIT_BKOPS.
>
> Future considerations
>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>  * Interrupt ongoing BKOPS before powering off the card.
>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
> Signed-off-by: Maya Erez <merez@codeaurora.org>
> ---
> Changelog V9:
>        - Rebased on patch-v7.
>        - Added Konstantin and Maya's patch
>                : mmc:core: Define synchronous BKOPS timeout
>                : mmc:core: eMMC4.5 BKOPS fixes
>        - Removed periodic bkops
>                : This feature will do in future work
>        - Add __mmc_switch() for waiting_for_prod_done.
>
Jaehoon,
  (I've been trying to sync up with you offline - but our timings dont
seem to match.)
In essence, you've fixed most of the issue, except for the need for
periodic BKOPS. In my view, BKOPS should have little or no overhead
during read writes. Here is the simple workflow that I think of (and
was discussed with some experts during Linaro connect in HK)

1) Check the status of BKOPS need when the queue has been idle for,
say, ~5 seconds
2) If it's >0, start BKOPS
3) If a request arrives during the above execution of BKOPS, issue HPI
and continue with the request
4) Else let it run indefinitely

There should be no need or procedure to check BKOPS during transactions.
If this is done, in normal circumstances, the card wouldn't need to
set URGENT_BKOPS. Even if it is set, the above sequence would handle
it gracefully.
Apart from this, I have just one clarification required on the implementation.
See below..

> Changelog V8:
>        - Remove host->lock spin lock reviewed by Adrian
>        - Support periodic start bkops
>        - when bkops_status is level-3, if timeout is set to 0, send hpi.
>        - Move the start-bkops point
> Changelog V7:
>        - Use HPI command when issued URGENT_BKOPS
>        - Recheck until clearing the bkops-status bit.
> Changelog V6:
>        - Add the flag of check-bkops-status.
>          (For fixing async_req problem)
>        - Add the capability for MMC_CAP2_INIT_BKOPS.
>          (When unset the bkops_en bit in ext_csd register)
>        - modify the wrong condition.
> Changelog V5:
>        - Rebase based on the latest mmc-next.
>        - modify codes based-on Chris's comment
> Changelog V4:
>        - Add mmc_read_bkops_status
>        - When URGENT_BKOPS(level2-3), didn't use HPI command.
>        - In mmc_switch(), use R1B/R1 according to level.
> Changelog V3:
>        - move the bkops setting's location in mmc_blk_issue_rw_rq
>        - modify condition checking
>        - bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
>        - remove the unused code
>        - change pr_debug instead of pr_warn in mmc_send_hpi_cmd
>        - Add the Future consideration suggested by Per
> Changelog V2:
>        - Use EXCEPTION_STATUS instead of URGENT_BKOPS
>        - Add function to check Exception_status(for eMMC4.5)
> ---
>  drivers/mmc/card/queue.c   |    2 +
>  drivers/mmc/core/core.c    |  161 +++++++++++++++++++++++++++++++++++++++++++-
>  drivers/mmc/core/mmc.c     |   18 +++++
>  drivers/mmc/core/mmc_ops.c |   27 ++++++-
>  include/linux/mmc/card.h   |   16 +++++
>  include/linux/mmc/core.h   |    5 ++
>  include/linux/mmc/host.h   |    2 +
>  include/linux/mmc/mmc.h    |   19 +++++
>  8 files changed, 243 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index e360a97..e4a2cde 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)
>                spin_unlock_irq(q->queue_lock);
>
>                if (req || mq->mqrq_prev->req) {
> +                       if (mmc_card_doing_bkops(mq->card))
> +                               mmc_stop_bkops(mq->card);
>                        set_current_state(TASK_RUNNING);
>                        mq->issue_fn(mq, req);
>                } else {
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 0b6141d..9adb004 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -41,6 +41,12 @@
>  #include "sd_ops.h"
>  #include "sdio_ops.h"
>
> +/*
> + * The Background operation can take a long time, depends on the house keeping
> + * operations the card has to peform
> + */
> +#define MMC_BKOPS_MAX_TIMEOUT  (4 * 80 * 1000) /* max time to wait in ms */
> +
>  static struct workqueue_struct *workqueue;
>  static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
>
> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
>                if (mrq->done)
>                        mrq->done(mrq);
>
> +               /*
> +                * Check BKOPS urgency from each R1 response
> +                */
> +               if (host->card && mmc_card_mmc(host->card) &&
> +                       ((mmc_resp_type(cmd) == MMC_RSP_R1) ||
> +                        (mmc_resp_type(cmd) == MMC_RSP_R1B)) &&
> +                       (cmd->resp[0] & R1_EXCEPTION_EVENT)) {
> +                       mmc_card_set_check_bkops(host->card);
> +               }
> +
>                mmc_host_clk_release(host);
>        }
>  }
> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
>        host->ops->request(host, mrq);
>  }
>
> +/**
> + *     mmc_start_bkops - start BKOPS for supported cards
> + *     @card: MMC card to start BKOPS
> + *
> + *     Start background operations whenever requested.
> + *     when the urgent BKOPS bit is set in a R1 command response
> + *     then background operations should be started immediately.
> +*/
> +void mmc_start_bkops(struct mmc_card *card)
> +{
> +       int err;
> +       int timeout;
> +       u8 wait_for_prog_done;
> +
> +       BUG_ON(!card);
> +       if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
> +               return;
> +
> +       if (mmc_card_check_bkops(card)) {
> +               mmc_card_clr_check_bkops(card);
> +               if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
> +                       if (card->ext_csd.raw_bkops_status)
> +                               mmc_card_set_need_bkops(card);
> +       }
> +
> +       /*
> +        * If card is already doing bkops or need for
> +        * bkops flag is not set, then do nothing just
> +        * return
> +        */
> +       if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
> +               return;
> +
> +       mmc_claim_host(card->host);
> +       if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
> +               timeout = MMC_BKOPS_MAX_TIMEOUT;

I don't think the timeout should be any different for BKOPS_START.
IIUC, the CMD to set BKOPS_START would return immediately, and BKOPS
will continue in the background. So the time to actually do the device
management is not applicable to the CMD execution time..

> +               wait_for_prog_done = 1;
> +       } else {
> +               timeout = 0;
> +               wait_for_prog_done = 0;
> +       }
> +
> +       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +                       EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
> +       if (err) {
> +               pr_warn("%s: error %d starting bkops\n",
> +                          mmc_hostname(card->host), err);
> +               mmc_card_clr_need_bkops(card);
> +               goto out;
> +       }
> +
> +       mmc_card_clr_need_bkops(card);
> +
> +       /*
> +        * For urgent bkops status (LEVEL_2 and more)
> +        * bkops executed synchronously, otherwise
> +        * the operation is in progress
> +        */
> +       if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
> +               mmc_card_set_check_bkops(card);
> +       else
> +               mmc_card_set_doing_bkops(card);
> +
> +out:
> +       mmc_release_host(card->host);
> +}
> +EXPORT_SYMBOL(mmc_start_bkops);
> +
>  static void mmc_wait_done(struct mmc_request *mrq)
>  {
>        complete(&mrq->completion);
> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
>        if (!err && areq)
>                start_err = __mmc_start_req(host, areq->mrq);
>
> -       if (host->areq)
> +       if (host->areq) {
> +               if (!areq && host->areq && mmc_card_mmc(host->card))
> +                       mmc_start_bkops(host->card);
>                mmc_post_req(host, host->areq->mrq, 0);
> +       }
>
>         /* Cancel a prepared request if it was not started. */
>        if ((err || start_err) && areq)
> -                       mmc_post_req(host, areq->mrq, -EINVAL);
> +               mmc_post_req(host, areq->mrq, -EINVAL);
>
>        if (err)
>                host->areq = NULL;
> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
>  EXPORT_SYMBOL(mmc_wait_for_cmd);
>
>  /**
> + *     mmc_stop_bkops - stop ongoing BKOPS
> + *     @card: MMC card to check BKOPS
> + *
> + *     Send HPI command to stop ongoing background operations,
> + *     to allow rapid servicing of foreground operations,e.g. read/
> + *     writes. Wait until the card comes out of the programming state
> + *     to avoid errors in servicing read/write requests.
> + */
> +int mmc_stop_bkops(struct mmc_card *card)
> +{
> +       int err = 0;
> +
> +       BUG_ON(!card);
> +
> +       err = mmc_interrupt_hpi(card);
> +
> +       mmc_card_clr_doing_bkops(card);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL(mmc_stop_bkops);
> +
> +int mmc_read_bkops_status(struct mmc_card *card)
> +{
> +       int err;
> +       u8 ext_csd[512];
> +
> +       mmc_claim_host(card->host);
> +       err = mmc_send_ext_csd(card, ext_csd);
> +       mmc_release_host(card->host);
> +       if (err)
> +               return err;
> +
> +       card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
> +       card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXCEPTION_STATUS];
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(mmc_read_bkops_status);
> +
> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
> +{
> +       int err;
> +
> +       err = mmc_read_bkops_status(card);
> +       if (err) {
> +               pr_err("%s: Didn't read bkops status : %d\n",
> +                      mmc_hostname(card->host), err);
> +               return 0;
> +       }
> +
> +       /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
> +       if (card->ext_csd.rev == 5)
> +               return 1;
> +
> +       return (card->ext_csd.raw_exception_status & value) ? 1 : 0;
> +}
> +EXPORT_SYMBOL(mmc_is_exception_event);
> +
> +/**
>  *     mmc_set_data_timeout - set the timeout for a data command
>  *     @data: data phase for command
>  *     @card: the MMC card associated with the data transfer
> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
>        mmc_bus_get(host);
>        if (host->bus_ops && !host->bus_dead) {
>
> -               if (host->bus_ops->suspend)
> +               if (host->bus_ops->suspend) {
> +                       if (mmc_card_doing_bkops(host->card))
> +                               mmc_stop_bkops(host->card);
>                        err = host->bus_ops->suspend(host);
> +               }
>
>                if (err == -ENOSYS || !host->bus_ops->resume) {
>                        /*
> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
>        switch (mode) {
>        case PM_HIBERNATION_PREPARE:
>        case PM_SUSPEND_PREPARE:
> +               if (host->card && mmc_card_mmc(host->card) &&
> +                               mmc_card_doing_bkops(host->card)) {
> +                       mmc_interrupt_hpi(host->card);
> +                       mmc_card_clr_doing_bkops(host->card);
> +               }
>
>                spin_lock_irqsave(&host->lock, flags);
>                host->rescan_disable = 1;
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 258b203..9e036e5 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
>        }
>
>        if (card->ext_csd.rev >= 5) {
> +               /* check whether the eMMC card support BKOPS */
> +               if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
> +                       card->ext_csd.bkops = 1;
> +                       card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
> +                       card->ext_csd.raw_bkops_status =
> +                               ext_csd[EXT_CSD_BKOPS_STATUS];
> +                       if (!card->ext_csd.bkops_en &&
> +                               card->host->caps2 & MMC_CAP2_INIT_BKOPS) {
> +                               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +                                       EXT_CSD_BKOPS_EN, 1, 0);
> +                               if (err)
> +                                       pr_warn("%s: Enabling BKOPS failed\n",
> +                                               mmc_hostname(card->host));
> +                               else
> +                                       card->ext_csd.bkops_en = 1;
> +                       }
> +               }
> +
>                /* check whether the eMMC card supports HPI */
>                if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
>                        card->ext_csd.hpi = 1;
> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> index 69370f4..f0bebd9 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
>  }
>
>  /**
> - *     mmc_switch - modify EXT_CSD register
> + *     __mmc_switch - modify EXT_CSD register
>  *     @card: the MMC card associated with the data transfer
>  *     @set: cmd set values
>  *     @index: EXT_CSD register index
>  *     @value: value to program into EXT_CSD register
>  *     @timeout_ms: timeout (ms) for operation performed by register write,
>  *                   timeout of zero implies maximum possible timeout
> + *      @wait_for_prod_done: is waiting for program done
>  *
>  *     Modifies the EXT_CSD register for selected card.
>  */
> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> -              unsigned int timeout_ms)
> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> +              unsigned int timeout_ms, u8 wait_for_prog_done)
> +
>  {
>        int err;
>        struct mmc_command cmd = {0};
> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>                  (index << 16) |
>                  (value << 8) |
>                  set;
> -       cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
> +
> +       cmd.flags = MMC_CMD_AC;
> +       if (wait_for_prog_done)
> +               cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
> +       else
> +               cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
> +
>        cmd.cmd_timeout_ms = timeout_ms;
>
>        err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
>        if (err)
>                return err;
>
> +       /* No need to check card status in case of BKOPS switch*/
> +       if (index == EXT_CSD_BKOPS_START)
> +               return 0;
> +
>        /* Must check status to be sure of no errors */
>        do {
>                err = mmc_send_status(card, &status);
> @@ -423,6 +435,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>
>        return 0;
>  }
> +EXPORT_SYMBOL_GPL(__mmc_switch);
> +
> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> +               unsigned int timeout_ms)
> +{
> +       return __mmc_switch(card, set, index, value, timeout_ms, 1);
> +}
>  EXPORT_SYMBOL_GPL(mmc_switch);
>
>  int mmc_send_status(struct mmc_card *card, u32 *status)
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index d76513b..50b3539 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
>        bool                    hpi_en;                 /* HPI enablebit */
>        bool                    hpi;                    /* HPI support bit */
>        unsigned int            hpi_cmd;                /* cmd used as HPI */
> +       bool                    bkops;          /* background support bit */
> +       bool                    bkops_en;       /* background enable bit */
>        unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>        unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size */
>        unsigned int            boot_ro_lock;           /* ro lock support */
>        bool                    boot_ro_lockable;
> +       u8                      raw_exception_status;   /* 53 */
>        u8                      raw_partition_support;  /* 160 */
>        u8                      raw_erased_mem_count;   /* 181 */
>        u8                      raw_ext_csd_structure;  /* 194 */
> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
>        u8                      raw_sec_erase_mult;     /* 230 */
>        u8                      raw_sec_feature_support;/* 231 */
>        u8                      raw_trim_mult;          /* 232 */
> +       u8                      raw_bkops_status;       /* 246 */
>        u8                      raw_sectors[4];         /* 212 - 4 bytes */
>
>        unsigned int            feature_support;
> @@ -225,6 +229,9 @@ struct mmc_card {
>  #define MMC_CARD_REMOVED       (1<<7)          /* card has been removed */
>  #define MMC_STATE_HIGHSPEED_200        (1<<8)          /* card is in HS200 mode */
>  #define MMC_STATE_SLEEP                (1<<9)          /* card is in sleep state */
> +#define MMC_STATE_NEED_BKOPS   (1<<10)         /* card need to do BKOPS */
> +#define MMC_STATE_DOING_BKOPS  (1<<11)         /* card is doing BKOPS */
> +#define MMC_STATE_CHECK_BKOPS  (1<<12)         /* card need to check BKOPS */
>        unsigned int            quirks;         /* card quirks */
>  #define MMC_QUIRK_LENIENT_FN0  (1<<0)          /* allow SDIO FN0 writes outside of the VS CCCR range */
>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)   /* use func->cur_blksize */
> @@ -391,6 +398,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>  #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
>  #define mmc_card_removed(c)    ((c) && ((c)->state & MMC_CARD_REMOVED))
>  #define mmc_card_is_sleep(c)   ((c)->state & MMC_STATE_SLEEP)
> +#define mmc_card_need_bkops(c) ((c)->state & MMC_STATE_NEED_BKOPS)
> +#define mmc_card_doing_bkops(c)        ((c)->state & MMC_STATE_DOING_BKOPS)
> +#define mmc_card_check_bkops(c) ((c)->state & MMC_STATE_CHECK_BKOPS)
>
>  #define mmc_card_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
> @@ -403,7 +413,13 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>  #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
>  #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
>  #define mmc_card_set_sleep(c)  ((c)->state |= MMC_STATE_SLEEP)
> +#define mmc_card_set_need_bkops(c)     ((c)->state |= MMC_STATE_NEED_BKOPS)
> +#define mmc_card_set_doing_bkops(c)    ((c)->state |= MMC_STATE_DOING_BKOPS)
> +#define mmc_card_set_check_bkops(c) ((c)->state |= MMC_STATE_CHECK_BKOPS)
>
> +#define mmc_card_clr_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_check_bkops(c) ((c)->state &= ~MMC_STATE_CHECK_BKOPS)
>  #define mmc_card_clr_sleep(c)  ((c)->state &= ~MMC_STATE_SLEEP)
>  /*
>  * Quirk add/remove for MMC products.
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 1b431c7..bda3d86 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -134,6 +134,9 @@ struct mmc_host;
>  struct mmc_card;
>  struct mmc_async_req;
>
> +extern int mmc_stop_bkops(struct mmc_card *);
> +extern int mmc_read_bkops_status(struct mmc_card *);
> +extern int mmc_is_exception_event(struct mmc_card *, unsigned int);
>  extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>                                           struct mmc_async_req *, int *);
>  extern int mmc_interrupt_hpi(struct mmc_card *);
> @@ -142,6 +145,8 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
>  extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
>  extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>        struct mmc_command *, int);
> +extern void mmc_start_bkops(struct mmc_card *card);
> +extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>
>  #define MMC_ERASE_ARG          0x00000000
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 0707d22..d23caf2 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -238,6 +238,8 @@ struct mmc_host {
>  #define MMC_CAP2_BROKEN_VOLTAGE        (1 << 7)        /* Use the broken voltage */
>  #define MMC_CAP2_DETECT_ON_ERR (1 << 8)        /* On I/O err check card removal */
>  #define MMC_CAP2_HC_ERASE_SZ   (1 << 9)        /* High-capacity erase size */
> +#define MMC_CAP2_INIT_BKOPS    (1 << 10)       /* To enable BKOPS */
> +#define MMC_CAP2_BKOPS         (1 << 11)       /* BKOPS supported */
>
>        mmc_pm_flag_t           pm_caps;        /* supported pm features */
>        unsigned int        power_notify_type;
> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
> index d425cab..80cfbf1 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -139,6 +139,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_EXCEPTION_EVENT     (1 << 6)        /* sx, a */
>  #define R1_APP_CMD             (1 << 5)        /* sr, c */
>
>  #define R1_STATE_IDLE  0
> @@ -274,12 +275,15 @@ struct _mmc_csd {
>  #define EXT_CSD_FLUSH_CACHE            32      /* W */
>  #define EXT_CSD_CACHE_CTRL             33      /* R/W */
>  #define EXT_CSD_POWER_OFF_NOTIFICATION 34      /* R/W */
> +#define EXT_CSD_EXCEPTION_STATUS       54      /* RO */
>  #define EXT_CSD_DATA_SECTOR_SIZE       61      /* R */
>  #define EXT_CSD_GP_SIZE_MULT           143     /* R/W */
>  #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_RST_N_FUNCTION         162     /* R/W */
> +#define EXT_CSD_BKOPS_EN               163     /* R/W */
> +#define EXT_CSD_BKOPS_START            164     /* W */
>  #define EXT_CSD_SANITIZE_START         165     /* W */
>  #define EXT_CSD_WR_REL_PARAM           166     /* RO */
>  #define EXT_CSD_BOOT_WP                        173     /* R/W */
> @@ -313,11 +317,13 @@ struct _mmc_csd {
>  #define EXT_CSD_PWR_CL_200_360         237     /* RO */
>  #define EXT_CSD_PWR_CL_DDR_52_195      238     /* RO */
>  #define EXT_CSD_PWR_CL_DDR_52_360      239     /* RO */
> +#define EXT_CSD_BKOPS_STATUS           246     /* RO */
>  #define EXT_CSD_POWER_OFF_LONG_TIME    247     /* RO */
>  #define EXT_CSD_GENERIC_CMD6_TIME      248     /* RO */
>  #define EXT_CSD_CACHE_SIZE             249     /* RO, 4 bytes */
>  #define EXT_CSD_TAG_UNIT_SIZE          498     /* RO */
>  #define EXT_CSD_DATA_TAG_SUPPORT       499     /* RO */
> +#define EXT_CSD_BKOPS_SUPPORT          502     /* RO */
>  #define EXT_CSD_HPI_FEATURES           503     /* RO */
>
>  /*
> @@ -386,4 +392,17 @@ struct _mmc_csd {
>  #define MMC_SWITCH_MODE_CLEAR_BITS     0x02    /* Clear bits which are 1 in value */
>  #define MMC_SWITCH_MODE_WRITE_BYTE     0x03    /* Set target to value */
>
> +/*
> + * BKOPS status level
> + */
> +#define EXT_CSD_BKOPS_LEVEL_2          0x2
> +
> +/*
> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)
> + */
> +#define EXT_CSD_URGENT_BKOPS           BIT(0)
> +#define EXT_CSD_DYNCAP_NEEDED          BIT(1)
> +#define EXT_CSD_SYSPOOL_EXHAUSTED      BIT(2)
> +#define EXT_CSD_PACKED_FAILURE         BIT(3)
> +
>  #endif /* LINUX_MMC_MMC_H */
> --
> 1.7.4.1

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-13 13:53 ` S, Venkatraman
@ 2012-06-14  8:44   ` merez
  2012-06-15  4:29   ` Jaehoon Chung
  1 sibling, 0 replies; 23+ messages in thread
From: merez @ 2012-06-14  8:44 UTC (permalink / raw)
  To: S, Venkatraman
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Maya Erez,
	Ulf Hansson


On Wed, June 13, 2012 6:53 am, S, Venkatraman wrote:
> On Fri, Jun 8, 2012 at 10:09 AM, Jaehoon Chung <jh80.chung@samsung.com>
> wrote:
>> Enable eMMC background operations (BKOPS) feature.
>>
>> If URGENT_BKOPS is set after a response, note that BKOPS
>> are required. After all I/O requests are finished, run
>> BKOPS if required. Should read/write operations be requested
>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>> and then service the request.
>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>> the BKOPS-STATUS vaule.
>>
>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>> And if you want to set the BKOPS_EN bit in ext_csd register,
>> use the MMC_CAP2_INIT_BKOPS.
>>
>> Future considerations
>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>  * Interrupt ongoing BKOPS before powering off the card.
>>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>> Signed-off-by: Maya Erez <merez@codeaurora.org>
>> ---
>> Changelog V9:
>>        - Rebased on patch-v7.
>>        - Added Konstantin and Maya's patch
>>                : mmc:core: Define synchronous BKOPS timeout
>>                : mmc:core: eMMC4.5 BKOPS fixes
>>        - Removed periodic bkops
>>                : This feature will do in future work
>>        - Add __mmc_switch() for waiting_for_prod_done.
>>
> Jaehoon,
>   (I've been trying to sync up with you offline - but our timings dont
> seem to match.)
> In essence, you've fixed most of the issue, except for the need for
> periodic BKOPS. In my view, BKOPS should have little or no overhead
> during read writes. Here is the simple workflow that I think of (and
> was discussed with some experts during Linaro connect in HK)
>
> 1) Check the status of BKOPS need when the queue has been idle for,
> say, ~5 seconds
> 2) If it's >0, start BKOPS
> 3) If a request arrives during the above execution of BKOPS, issue HPI
> and continue with the request
> 4) Else let it run indefinitely
>
> There should be no need or procedure to check BKOPS during transactions.
> If this is done, in normal circumstances, the card wouldn't need to
> set URGENT_BKOPS. Even if it is set, the above sequence would handle
> it gracefully.

We have seen write latency improvement of 6ms per block with this BKOPs
patch. In all our tests, the card didn't report of a need for a BKOPs
operation, so it is not clear if periodic BKOPs will make a real
difference.
Therefore, we think that this patch can be approved as is and periodic
BKOPs can be released as a second phase.

> Apart from this, I have just one clarification required on the
> implementation.
> See below..
>
>> Changelog V8:
>>        - Remove host->lock spin lock reviewed by Adrian
>>        - Support periodic start bkops
>>        - when bkops_status is level-3, if timeout is set to 0, send hpi.
>>        - Move the start-bkops point
>> Changelog V7:
>>        - Use HPI command when issued URGENT_BKOPS
>>        - Recheck until clearing the bkops-status bit.
>> Changelog V6:
>>        - Add the flag of check-bkops-status.
>>          (For fixing async_req problem)
>>        - Add the capability for MMC_CAP2_INIT_BKOPS.
>>          (When unset the bkops_en bit in ext_csd register)
>>        - modify the wrong condition.
>> Changelog V5:
>>        - Rebase based on the latest mmc-next.
>>        - modify codes based-on Chris's comment
>> Changelog V4:
>>        - Add mmc_read_bkops_status
>>        - When URGENT_BKOPS(level2-3), didn't use HPI command.
>>        - In mmc_switch(), use R1B/R1 according to level.
>> Changelog V3:
>>        - move the bkops setting's location in mmc_blk_issue_rw_rq
>>        - modify condition checking
>>        - bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
>>        - remove the unused code
>>        - change pr_debug instead of pr_warn in mmc_send_hpi_cmd
>>        - Add the Future consideration suggested by Per
>> Changelog V2:
>>        - Use EXCEPTION_STATUS instead of URGENT_BKOPS
>>        - Add function to check Exception_status(for eMMC4.5)
>> ---
>>  drivers/mmc/card/queue.c   |    2 +
>>  drivers/mmc/core/core.c    |  161
>> +++++++++++++++++++++++++++++++++++++++++++-
>>  drivers/mmc/core/mmc.c     |   18 +++++
>>  drivers/mmc/core/mmc_ops.c |   27 ++++++-
>>  include/linux/mmc/card.h   |   16 +++++
>>  include/linux/mmc/core.h   |    5 ++
>>  include/linux/mmc/host.h   |    2 +
>>  include/linux/mmc/mmc.h    |   19 +++++
>>  8 files changed, 243 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..e4a2cde 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)
>>                spin_unlock_irq(q->queue_lock);
>>
>>                if (req || mq->mqrq_prev->req) {
>> +                       if (mmc_card_doing_bkops(mq->card))
>> +                               mmc_stop_bkops(mq->card);
>>                        set_current_state(TASK_RUNNING);
>>                        mq->issue_fn(mq, req);
>>                } else {
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 0b6141d..9adb004 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -41,6 +41,12 @@
>>  #include "sd_ops.h"
>>  #include "sdio_ops.h"
>>
>> +/*
>> + * The Background operation can take a long time, depends on the house
>> keeping
>> + * operations the card has to peform
>> + */
>> +#define MMC_BKOPS_MAX_TIMEOUT  (4 * 80 * 1000) /* max time to wait in
>> ms */
>> +
>>  static struct workqueue_struct *workqueue;
>>  static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
>>
>> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct
>> mmc_request *mrq)
>>                if (mrq->done)
>>                        mrq->done(mrq);
>>
>> +               /*
>> +                * Check BKOPS urgency from each R1 response
>> +                */
>> +               if (host->card && mmc_card_mmc(host->card) &&
>> +                       ((mmc_resp_type(cmd) == MMC_RSP_R1) ||
>> +                        (mmc_resp_type(cmd) == MMC_RSP_R1B)) &&
>> +                       (cmd->resp[0] & R1_EXCEPTION_EVENT)) {
>> +                       mmc_card_set_check_bkops(host->card);
>> +               }
>> +
>>                mmc_host_clk_release(host);
>>        }
>>  }
>> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct
>> mmc_request *mrq)
>>        host->ops->request(host, mrq);
>>  }
>>
>> +/**
>> + *     mmc_start_bkops - start BKOPS for supported cards
>> + *     @card: MMC card to start BKOPS
>> + *
>> + *     Start background operations whenever requested.
>> + *     when the urgent BKOPS bit is set in a R1 command response
>> + *     then background operations should be started immediately.
>> +*/
>> +void mmc_start_bkops(struct mmc_card *card)
>> +{
>> +       int err;
>> +       int timeout;
>> +       u8 wait_for_prog_done;
>> +
>> +       BUG_ON(!card);
>> +       if (!card->ext_csd.bkops_en || !(card->host->caps2 &
>> MMC_CAP2_BKOPS))
>> +               return;
>> +
>> +       if (mmc_card_check_bkops(card)) {
>> +               mmc_card_clr_check_bkops(card);
>> +               if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
>> +                       if (card->ext_csd.raw_bkops_status)
>> +                               mmc_card_set_need_bkops(card);
>> +       }
>> +
>> +       /*
>> +        * If card is already doing bkops or need for
>> +        * bkops flag is not set, then do nothing just
>> +        * return
>> +        */
>> +       if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
>> +               return;
>> +
>> +       mmc_claim_host(card->host);
>> +       if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
>> +               timeout = MMC_BKOPS_MAX_TIMEOUT;
>
> I don't think the timeout should be any different for BKOPS_START.
> IIUC, the CMD to set BKOPS_START would return immediately, and BKOPS
> will continue in the background. So the time to actually do the device
> management is not applicable to the CMD execution time..
In case of urgent BKOPs the BKOPs operation is synchronous. Therefore we
need to set the timeout to a bigger value, waiting for the BKOPs operation
to complete.
>
>> +               wait_for_prog_done = 1;
>> +       } else {
>> +               timeout = 0;
>> +               wait_for_prog_done = 0;
>> +       }
>> +
>> +       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                       EXT_CSD_BKOPS_START, 1, timeout,
>> wait_for_prog_done);
>> +       if (err) {
>> +               pr_warn("%s: error %d starting bkops\n",
>> +                          mmc_hostname(card->host), err);
>> +               mmc_card_clr_need_bkops(card);
>> +               goto out;
>> +       }
>> +
>> +       mmc_card_clr_need_bkops(card);
>> +
>> +       /*
>> +        * For urgent bkops status (LEVEL_2 and more)
>> +        * bkops executed synchronously, otherwise
>> +        * the operation is in progress
>> +        */
>> +       if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
>> +               mmc_card_set_check_bkops(card);
>> +       else
>> +               mmc_card_set_doing_bkops(card);
>> +
>> +out:
>> +       mmc_release_host(card->host);
>> +}
>> +EXPORT_SYMBOL(mmc_start_bkops);
>> +
>>  static void mmc_wait_done(struct mmc_request *mrq)
>>  {
>>        complete(&mrq->completion);
>> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
>> mmc_host *host,
>>        if (!err && areq)
>>                start_err = __mmc_start_req(host, areq->mrq);
>>
>> -       if (host->areq)
>> +       if (host->areq) {
>> +               if (!areq && host->areq && mmc_card_mmc(host->card))
>> +                       mmc_start_bkops(host->card);
>>                mmc_post_req(host, host->areq->mrq, 0);
>> +       }
>>
>>         /* Cancel a prepared request if it was not started. */
>>        if ((err || start_err) && areq)
>> -                       mmc_post_req(host, areq->mrq, -EINVAL);
>> +               mmc_post_req(host, areq->mrq, -EINVAL);
>>
>>        if (err)
>>                host->areq = NULL;
>> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
>> mmc_command *cmd, int retries
>>  EXPORT_SYMBOL(mmc_wait_for_cmd);
>>
>>  /**
>> + *     mmc_stop_bkops - stop ongoing BKOPS
>> + *     @card: MMC card to check BKOPS
>> + *
>> + *     Send HPI command to stop ongoing background operations,
>> + *     to allow rapid servicing of foreground operations,e.g. read/
>> + *     writes. Wait until the card comes out of the programming state
>> + *     to avoid errors in servicing read/write requests.
>> + */
>> +int mmc_stop_bkops(struct mmc_card *card)
>> +{
>> +       int err = 0;
>> +
>> +       BUG_ON(!card);
>> +
>> +       err = mmc_interrupt_hpi(card);
>> +
>> +       mmc_card_clr_doing_bkops(card);
>> +
>> +       return err;
>> +}
>> +EXPORT_SYMBOL(mmc_stop_bkops);
>> +
>> +int mmc_read_bkops_status(struct mmc_card *card)
>> +{
>> +       int err;
>> +       u8 ext_csd[512];
>> +
>> +       mmc_claim_host(card->host);
>> +       err = mmc_send_ext_csd(card, ext_csd);
>> +       mmc_release_host(card->host);
>> +       if (err)
>> +               return err;
>> +
>> +       card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
>> +       card->ext_csd.raw_exception_status =
>> ext_csd[EXT_CSD_EXCEPTION_STATUS];
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(mmc_read_bkops_status);
>> +
>> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
>> +{
>> +       int err;
>> +
>> +       err = mmc_read_bkops_status(card);
>> +       if (err) {
>> +               pr_err("%s: Didn't read bkops status : %d\n",
>> +                      mmc_hostname(card->host), err);
>> +               return 0;
>> +       }
>> +
>> +       /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
>> +       if (card->ext_csd.rev == 5)
>> +               return 1;
>> +
>> +       return (card->ext_csd.raw_exception_status & value) ? 1 : 0;
>> +}
>> +EXPORT_SYMBOL(mmc_is_exception_event);
>> +
>> +/**
>>  *     mmc_set_data_timeout - set the timeout for a data command
>>  *     @data: data phase for command
>>  *     @card: the MMC card associated with the data transfer
>> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
>>        mmc_bus_get(host);
>>        if (host->bus_ops && !host->bus_dead) {
>>
>> -               if (host->bus_ops->suspend)
>> +               if (host->bus_ops->suspend) {
>> +                       if (mmc_card_doing_bkops(host->card))
>> +                               mmc_stop_bkops(host->card);
>>                        err = host->bus_ops->suspend(host);
>> +               }
>>
>>                if (err == -ENOSYS || !host->bus_ops->resume) {
>>                        /*
>> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block
>> *notify_block,
>>        switch (mode) {
>>        case PM_HIBERNATION_PREPARE:
>>        case PM_SUSPEND_PREPARE:
>> +               if (host->card && mmc_card_mmc(host->card) &&
>> +                               mmc_card_doing_bkops(host->card)) {
>> +                       mmc_interrupt_hpi(host->card);
>> +                       mmc_card_clr_doing_bkops(host->card);
>> +               }
>>
>>                spin_lock_irqsave(&host->lock, flags);
>>                host->rescan_disable = 1;
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 258b203..9e036e5 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card,
>> u8 *ext_csd)
>>        }
>>
>>        if (card->ext_csd.rev >= 5) {
>> +               /* check whether the eMMC card support BKOPS */
>> +               if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
>> +                       card->ext_csd.bkops = 1;
>> +                       card->ext_csd.bkops_en =
>> ext_csd[EXT_CSD_BKOPS_EN];
>> +                       card->ext_csd.raw_bkops_status =
>> +                               ext_csd[EXT_CSD_BKOPS_STATUS];
>> +                       if (!card->ext_csd.bkops_en &&
>> +                               card->host->caps2 & MMC_CAP2_INIT_BKOPS)
>> {
>> +                               err = mmc_switch(card,
>> EXT_CSD_CMD_SET_NORMAL,
>> +                                       EXT_CSD_BKOPS_EN, 1, 0);
>> +                               if (err)
>> +                                       pr_warn("%s: Enabling BKOPS
>> failed\n",
>> +                                              
>> mmc_hostname(card->host));
>> +                               else
>> +                                       card->ext_csd.bkops_en = 1;
>> +                       }
>> +               }
>> +
>>                /* check whether the eMMC card supports HPI */
>>                if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
>>                        card->ext_csd.hpi = 1;
>> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
>> index 69370f4..f0bebd9 100644
>> --- a/drivers/mmc/core/mmc_ops.c
>> +++ b/drivers/mmc/core/mmc_ops.c
>> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int
>> use_crc)
>>  }
>>
>>  /**
>> - *     mmc_switch - modify EXT_CSD register
>> + *     __mmc_switch - modify EXT_CSD register
>>  *     @card: the MMC card associated with the data transfer
>>  *     @set: cmd set values
>>  *     @index: EXT_CSD register index
>>  *     @value: value to program into EXT_CSD register
>>  *     @timeout_ms: timeout (ms) for operation performed by register
>> write,
>>  *                   timeout of zero implies maximum possible timeout
>> + *      @wait_for_prod_done: is waiting for program done
>>  *
>>  *     Modifies the EXT_CSD register for selected card.
>>  */
>> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> -              unsigned int timeout_ms)
>> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +              unsigned int timeout_ms, u8 wait_for_prog_done)
>> +
>>  {
>>        int err;
>>        struct mmc_command cmd = {0};
>> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8
>> index, u8 value,
>>                  (index << 16) |
>>                  (value << 8) |
>>                  set;
>> -       cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
>> +
>> +       cmd.flags = MMC_CMD_AC;
>> +       if (wait_for_prog_done)
>> +               cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
>> +       else
>> +               cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
>> +
>>        cmd.cmd_timeout_ms = timeout_ms;
>>
>>        err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
>>        if (err)
>>                return err;
>>
>> +       /* No need to check card status in case of BKOPS switch*/
>> +       if (index == EXT_CSD_BKOPS_START)
>> +               return 0;
>> +
>>        /* Must check status to be sure of no errors */
>>        do {
>>                err = mmc_send_status(card, &status);
>> @@ -423,6 +435,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8
>> index, u8 value,
>>
>>        return 0;
>>  }
>> +EXPORT_SYMBOL_GPL(__mmc_switch);
>> +
>> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +               unsigned int timeout_ms)
>> +{
>> +       return __mmc_switch(card, set, index, value, timeout_ms, 1);
>> +}
>>  EXPORT_SYMBOL_GPL(mmc_switch);
>>
>>  int mmc_send_status(struct mmc_card *card, u32 *status)
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index d76513b..50b3539 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
>>        bool                    hpi_en;                 /* HPI enablebit
>> */
>>        bool                    hpi;                    /* HPI support
>> bit */
>>        unsigned int            hpi_cmd;                /* cmd used as
>> HPI */
>> +       bool                    bkops;          /* background support
>> bit */
>> +       bool                    bkops_en;       /* background enable bit
>> */
>>        unsigned int            data_sector_size;       /* 512 bytes or
>> 4KB */
>>        unsigned int            data_tag_unit_size;     /* DATA TAG UNIT
>> size */
>>        unsigned int            boot_ro_lock;           /* ro lock
>> support */
>>        bool                    boot_ro_lockable;
>> +       u8                      raw_exception_status;   /* 53 */
>>        u8                      raw_partition_support;  /* 160 */
>>        u8                      raw_erased_mem_count;   /* 181 */
>>        u8                      raw_ext_csd_structure;  /* 194 */
>> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
>>        u8                      raw_sec_erase_mult;     /* 230 */
>>        u8                      raw_sec_feature_support;/* 231 */
>>        u8                      raw_trim_mult;          /* 232 */
>> +       u8                      raw_bkops_status;       /* 246 */
>>        u8                      raw_sectors[4];         /* 212 - 4 bytes
>> */
>>
>>        unsigned int            feature_support;
>> @@ -225,6 +229,9 @@ struct mmc_card {
>>  #define MMC_CARD_REMOVED       (1<<7)          /* card has been removed
>> */
>>  #define MMC_STATE_HIGHSPEED_200        (1<<8)          /* card is in
>> HS200 mode */
>>  #define MMC_STATE_SLEEP                (1<<9)          /* card is in
>> sleep state */
>> +#define MMC_STATE_NEED_BKOPS   (1<<10)         /* card need to do BKOPS
>> */
>> +#define MMC_STATE_DOING_BKOPS  (1<<11)         /* card is doing BKOPS
>> */
>> +#define MMC_STATE_CHECK_BKOPS  (1<<12)         /* card need to check
>> BKOPS */
>>        unsigned int            quirks;         /* card quirks */
>>  #define MMC_QUIRK_LENIENT_FN0  (1<<0)          /* allow SDIO FN0 writes
>> outside of the VS CCCR range */
>>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)   /* use func->cur_blksize
>> */
>> @@ -391,6 +398,9 @@ static inline void __maybe_unused
>> remove_quirk(struct mmc_card *card, int data)
>>  #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
>>  #define mmc_card_removed(c)    ((c) && ((c)->state & MMC_CARD_REMOVED))
>>  #define mmc_card_is_sleep(c)   ((c)->state & MMC_STATE_SLEEP)
>> +#define mmc_card_need_bkops(c) ((c)->state & MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_doing_bkops(c)        ((c)->state &
>> MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_check_bkops(c) ((c)->state & MMC_STATE_CHECK_BKOPS)
>>
>>  #define mmc_card_set_present(c)        ((c)->state |=
>> MMC_STATE_PRESENT)
>>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
>> @@ -403,7 +413,13 @@ static inline void __maybe_unused
>> remove_quirk(struct mmc_card *card, int data)
>>  #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
>>  #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
>>  #define mmc_card_set_sleep(c)  ((c)->state |= MMC_STATE_SLEEP)
>> +#define mmc_card_set_need_bkops(c)     ((c)->state |=
>> MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_set_doing_bkops(c)    ((c)->state |=
>> MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_set_check_bkops(c) ((c)->state |=
>> MMC_STATE_CHECK_BKOPS)
>>
>> +#define mmc_card_clr_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_check_bkops(c) ((c)->state &=
>> ~MMC_STATE_CHECK_BKOPS)
>>  #define mmc_card_clr_sleep(c)  ((c)->state &= ~MMC_STATE_SLEEP)
>>  /*
>>  * Quirk add/remove for MMC products.
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 1b431c7..bda3d86 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -134,6 +134,9 @@ struct mmc_host;
>>  struct mmc_card;
>>  struct mmc_async_req;
>>
>> +extern int mmc_stop_bkops(struct mmc_card *);
>> +extern int mmc_read_bkops_status(struct mmc_card *);
>> +extern int mmc_is_exception_event(struct mmc_card *, unsigned int);
>>  extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>>                                           struct mmc_async_req *, int
>> *);
>>  extern int mmc_interrupt_hpi(struct mmc_card *);
>> @@ -142,6 +145,8 @@ extern int mmc_wait_for_cmd(struct mmc_host *,
>> struct mmc_command *, int);
>>  extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
>>  extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>>        struct mmc_command *, int);
>> +extern void mmc_start_bkops(struct mmc_card *card);
>> +extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int,
>> u8);
>>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>>
>>  #define MMC_ERASE_ARG          0x00000000
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index 0707d22..d23caf2 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -238,6 +238,8 @@ struct mmc_host {
>>  #define MMC_CAP2_BROKEN_VOLTAGE        (1 << 7)        /* Use the
>> broken voltage */
>>  #define MMC_CAP2_DETECT_ON_ERR (1 << 8)        /* On I/O err check card
>> removal */
>>  #define MMC_CAP2_HC_ERASE_SZ   (1 << 9)        /* High-capacity erase
>> size */
>> +#define MMC_CAP2_INIT_BKOPS    (1 << 10)       /* To enable BKOPS */
>> +#define MMC_CAP2_BKOPS         (1 << 11)       /* BKOPS supported */
>>
>>        mmc_pm_flag_t           pm_caps;        /* supported pm features
>> */
>>        unsigned int        power_notify_type;
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> index d425cab..80cfbf1 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -139,6 +139,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_EXCEPTION_EVENT     (1 << 6)        /* sx, a */
>>  #define R1_APP_CMD             (1 << 5)        /* sr, c */
>>
>>  #define R1_STATE_IDLE  0
>> @@ -274,12 +275,15 @@ struct _mmc_csd {
>>  #define EXT_CSD_FLUSH_CACHE            32      /* W */
>>  #define EXT_CSD_CACHE_CTRL             33      /* R/W */
>>  #define EXT_CSD_POWER_OFF_NOTIFICATION 34      /* R/W */
>> +#define EXT_CSD_EXCEPTION_STATUS       54      /* RO */
>>  #define EXT_CSD_DATA_SECTOR_SIZE       61      /* R */
>>  #define EXT_CSD_GP_SIZE_MULT           143     /* R/W */
>>  #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_RST_N_FUNCTION         162     /* R/W */
>> +#define EXT_CSD_BKOPS_EN               163     /* R/W */
>> +#define EXT_CSD_BKOPS_START            164     /* W */
>>  #define EXT_CSD_SANITIZE_START         165     /* W */
>>  #define EXT_CSD_WR_REL_PARAM           166     /* RO */
>>  #define EXT_CSD_BOOT_WP                        173     /* R/W */
>> @@ -313,11 +317,13 @@ struct _mmc_csd {
>>  #define EXT_CSD_PWR_CL_200_360         237     /* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_195      238     /* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_360      239     /* RO */
>> +#define EXT_CSD_BKOPS_STATUS           246     /* RO */
>>  #define EXT_CSD_POWER_OFF_LONG_TIME    247     /* RO */
>>  #define EXT_CSD_GENERIC_CMD6_TIME      248     /* RO */
>>  #define EXT_CSD_CACHE_SIZE             249     /* RO, 4 bytes */
>>  #define EXT_CSD_TAG_UNIT_SIZE          498     /* RO */
>>  #define EXT_CSD_DATA_TAG_SUPPORT       499     /* RO */
>> +#define EXT_CSD_BKOPS_SUPPORT          502     /* RO */
>>  #define EXT_CSD_HPI_FEATURES           503     /* RO */
>>
>>  /*
>> @@ -386,4 +392,17 @@ struct _mmc_csd {
>>  #define MMC_SWITCH_MODE_CLEAR_BITS     0x02    /* Clear bits which are
>> 1 in value */
>>  #define MMC_SWITCH_MODE_WRITE_BYTE     0x03    /* Set target to value
>> */
>>
>> +/*
>> + * BKOPS status level
>> + */
>> +#define EXT_CSD_BKOPS_LEVEL_2          0x2
>> +
>> +/*
>> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)
>> + */
>> +#define EXT_CSD_URGENT_BKOPS           BIT(0)
>> +#define EXT_CSD_DYNCAP_NEEDED          BIT(1)
>> +#define EXT_CSD_SYSPOOL_EXHAUSTED      BIT(2)
>> +#define EXT_CSD_PACKED_FAILURE         BIT(3)
>> +
>>  #endif /* LINUX_MMC_MMC_H */
>> --
>> 1.7.4.1
>

Thanks,
Maya

-- 
Sent by consultant of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-08  4:39 [PATCH v9] mmc: support BKOPS feature for eMMC Jaehoon Chung
  2012-06-13 12:40 ` Dong, Chuanxiao
  2012-06-13 13:53 ` S, Venkatraman
@ 2012-06-14 13:46 ` Adrian Hunter
  2012-06-14 14:58   ` Konstantin Dorfman
  2012-07-12  5:02   ` Jaehoon Chung
       [not found] ` <4FD88B7D.3000600@codeaurora.org>
  2012-07-06 16:47 ` Chris Ball
  4 siblings, 2 replies; 23+ messages in thread
From: Adrian Hunter @ 2012-06-14 13:46 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Maya Erez, Ulf Hansson

On 08/06/12 07:39, Jaehoon Chung wrote:
> Enable eMMC background operations (BKOPS) feature.
> 
> If URGENT_BKOPS is set after a response, note that BKOPS
> are required. After all I/O requests are finished, run
> BKOPS if required. Should read/write operations be requested
> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
> and then service the request.
> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
> the BKOPS-STATUS vaule. 
> 
> If you want to enable this feature, set MMC_CAP2_BKOPS.
> And if you want to set the BKOPS_EN bit in ext_csd register,
> use the MMC_CAP2_INIT_BKOPS.
> 
> Future considerations
>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>  * Interrupt ongoing BKOPS before powering off the card.
>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
> 
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
> Signed-off-by: Maya Erez <merez@codeaurora.org>
> ---

I would not expect this to work nicely with runtime PM.  I expect that BKOPS
would need to be stopped beforehand.  But that would limit the time
available for BKOPS since runtime PM would always kick in and stop it.  How
is runtime PM to be handled?

^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [PATCH v9] mmc: support BKOPS feature for eMMC
       [not found]   ` <17296D9F8FF2234F831FC3DF505A87A90FEA11E5@SHSMSX102.ccr.corp.intel.com >
@ 2012-06-14 14:49     ` Konstantin Dorfman
  0 siblings, 0 replies; 23+ messages in thread
From: Konstantin Dorfman @ 2012-06-14 14:49 UTC (permalink / raw)
  To: Dong, Chuanxiao
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Hunter, Adrian, Maya Erez,
	Ulf Hansson

Hello Dong,

On Wed, June 13, 2012 3:40 pm, Dong, Chuanxiao wrote:
> Hi Jaehoon,
>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index
>> 0b6141d..9adb004 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -41,6 +41,12 @@
>>  #include "sd_ops.h"
>>  #include "sdio_ops.h"
>>
>> +/*
>> + * The Background operation can take a long time, depends on the house
>> +keeping
>> + * operations the card has to peform
>> + */
>> +#define MMC_BKOPS_MAX_TIMEOUT	(4 * 80 * 1000) /* max time to wait in
>> ms */
>> +
> Just go through the eMMC specification, didn't find any clue about the max
> timeout value for background operations. So, could you please tell why you
> choose 320s?
> This value will used to calculate TIMEOUT_CONTROL registers for SDHC host
> controller. And as far as I know, this value is too large for some host.

Let me answer you on this point:
from protocol:
7.4.56 BKOPS_START [164]
Writing any value to this field shall manually start background operations.
Device shall stay busy till no more background operations are needed.

In case of urgent BKOPs the BKOPs operation is synchronous. Therefore we
need to set the timeout to a bigger value, waiting for the BKOPs operation
to complete. I've tested this flow with msm_sdcc host, the host default
timeout is 10 sec. Without increasing the timeout I've seen host timeout
occurs during long BKOPS operation.
The host controller logic should be setting maximum timeout between its
default and client (in our case mmc_switch) timeout.

>>  static struct workqueue_struct *workqueue;  static const unsigned
>> freqs[] =
>> { 400000, 300000, 200000, 100000 };
>>
>> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct
>> mmc_request *mrq)
>>  		if (mrq->done)
>>  			mrq->done(mrq);
>>
>> +		/*
>> +		 * Check BKOPS urgency from each R1 response
>> +		 */
>> +		if (host->card && mmc_card_mmc(host->card) &&
>> +			((mmc_resp_type(cmd) == MMC_RSP_R1) ||
>> +			 (mmc_resp_type(cmd) == MMC_RSP_R1B)) &&
>> +			(cmd->resp[0] & R1_EXCEPTION_EVENT)) {
>> +			mmc_card_set_check_bkops(host->card);
>> +		}
>> +
>>  		mmc_host_clk_release(host);
>>  	}
>>  }
>> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct
>> mmc_request *mrq)
>>  	host->ops->request(host, mrq);
>>  }
>>
>> +/**
>> + *	mmc_start_bkops - start BKOPS for supported cards
>> + *	@card: MMC card to start BKOPS
>> + *
>> + *	Start background operations whenever requested.
>> + *	when the urgent BKOPS bit is set in a R1 command response
>> + *	then background operations should be started immediately.
>> +*/
>> +void mmc_start_bkops(struct mmc_card *card) {
>> +	int err;
>> +	int timeout;
>> +	u8 wait_for_prog_done;
>> +
>> +	BUG_ON(!card);
>> +	if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
>> +		return;
>> +
>> +	if (mmc_card_check_bkops(card)) {
>> +		mmc_card_clr_check_bkops(card);
>> +		if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
> eMMC4.5 specification has said that "EXCEPTION_EVENTS_STATUS is set
> whenever the levels is either 2 or 3." To my understanding,
> EXCEPTION_EVENTS_STATUS will be set only when level-2 or level-3. For
> level-0 and level-1, EXCEPTION_EVENTS_STATUS won't be set. Regarding this,
> checking EXCEPTION_EVENTS_CTRL register during mmc card initialization
> phase can help to save the ext_csd reading commands.
>
> I don't have eMMC4.5 chip on hand, not sure how the real device is
> implemented. It is better if you can provide a testing result with your
> EXCEPTION_EVENTS_STATUS register and BKOPS_STATUS register.
>
>> +			if (card->ext_csd.raw_bkops_status)
>> +				mmc_card_set_need_bkops(card);
>> +	}
>> +
>> +	/*
>> +	 * If card is already doing bkops or need for
>> +	 * bkops flag is not set, then do nothing just
>> +	 * return
>> +	 */
>> +	if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
>> +		return;
>> +
>> +	mmc_claim_host(card->host);
>> +	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
>> +		timeout = MMC_BKOPS_MAX_TIMEOUT;
>> +		wait_for_prog_done = 1;
>> +	} else {
>> +		timeout = 0;
>> +		wait_for_prog_done = 0;
>> +	}
> As specification, CMD6 needs R1B response. Is it really good to use R1
> response for it?
> To my understanding, the BUSY signal is indicate whether the CMD6
> switching is done, but not whether the background operation is done. After
> CMD6, card will stay in PRG state which means card is still not ready for
> data transferring. Once the card state change to transfer state,
> background operation is really finished. So the signal for whether
> background operations finished is not the BUSY signal but the card state.
> Please correct me if I am wrong on this point. Of cause, you can have a
> test with your device.

BUSY signal in case of writing to BKOPS_START[164] has another meaning,
it will be released in the end of BKOPS (when R1B used),
from protocol:

7.4.13 GENERIC_CMD6_TIME [248]
This field indicates the default maximum timeout for a SWITCH command (CMD6)
unless a specific timeout is defined when accessing a specific field.
Additionally, this field doesn&#146;t define the timeout when starting a
background operation
 (writing to field BKOPS_START[164])

>
> Thanks
> Chuanxiao
>
>> +
>> +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +			EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
>> +	if (err) {
>> +		pr_warn("%s: error %d starting bkops\n",
>> +			   mmc_hostname(card->host), err);
>> +		mmc_card_clr_need_bkops(card);
>> +		goto out;
>> +	}
>> +
>> +	mmc_card_clr_need_bkops(card);
>> +
>> +	/*
>> +	 * For urgent bkops status (LEVEL_2 and more)
>> +	 * bkops executed synchronously, otherwise
>> +	 * the operation is in progress
>> +	 */
>> +	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
>> +		mmc_card_set_check_bkops(card);
>> +	else
>> +		mmc_card_set_doing_bkops(card);
>> +
>> +out:
>> +	mmc_release_host(card->host);
>> +}
>> +EXPORT_SYMBOL(mmc_start_bkops);
>> +
>>  static void mmc_wait_done(struct mmc_request *mrq)  {
>>  	complete(&mrq->completion);
>> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
>> mmc_host *host,
>>  	if (!err && areq)
>>  		start_err = __mmc_start_req(host, areq->mrq);
>>
>> -	if (host->areq)
>> +	if (host->areq) {
>> +		if (!areq && host->areq && mmc_card_mmc(host->card))
>> +			mmc_start_bkops(host->card);
>>  		mmc_post_req(host, host->areq->mrq, 0);
>> +	}
>>
>>  	 /* Cancel a prepared request if it was not started. */
>>  	if ((err || start_err) && areq)
>> -			mmc_post_req(host, areq->mrq, -EINVAL);
>> +		mmc_post_req(host, areq->mrq, -EINVAL);
>>
>>  	if (err)
>>  		host->areq = NULL;
>> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
>> mmc_command *cmd, int retries  EXPORT_SYMBOL(mmc_wait_for_cmd);
>>
>>  /**
>> + *	mmc_stop_bkops - stop ongoing BKOPS
>> + *	@card: MMC card to check BKOPS
>> + *
>> + *	Send HPI command to stop ongoing background operations,
>> + *	to allow rapid servicing of foreground operations,e.g. read/
>> + *	writes. Wait until the card comes out of the programming state
>> + *	to avoid errors in servicing read/write requests.
>> + */
>> +int mmc_stop_bkops(struct mmc_card *card) {
>> +	int err = 0;
>> +
>> +	BUG_ON(!card);
>> +
>> +	err = mmc_interrupt_hpi(card);
>> +
>> +	mmc_card_clr_doing_bkops(card);
>> +
>> +	return err;
>> +}
>> +EXPORT_SYMBOL(mmc_stop_bkops);
>> +
>> +int mmc_read_bkops_status(struct mmc_card *card) {
>> +	int err;
>> +	u8 ext_csd[512];
>> +
>> +	mmc_claim_host(card->host);
>> +	err = mmc_send_ext_csd(card, ext_csd);
>> +	mmc_release_host(card->host);
>> +	if (err)
>> +		return err;
>> +
>> +	card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
>> +	card->ext_csd.raw_exception_status =
>> +ext_csd[EXT_CSD_EXCEPTION_STATUS];
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(mmc_read_bkops_status);
>> +
>> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value) {
>> +	int err;
>> +
>> +	err = mmc_read_bkops_status(card);
>> +	if (err) {
>> +		pr_err("%s: Didn't read bkops status : %d\n",
>> +		       mmc_hostname(card->host), err);
>> +		return 0;
>> +	}
>> +
>> +	/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
>> +	if (card->ext_csd.rev == 5)
>> +		return 1;
>> +
>> +	return (card->ext_csd.raw_exception_status & value) ? 1 : 0; }
>> +EXPORT_SYMBOL(mmc_is_exception_event);
>> +
>> +/**
>>   *	mmc_set_data_timeout - set the timeout for a data command
>>   *	@data: data phase for command
>>   *	@card: the MMC card associated with the data transfer
>> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
>>  	mmc_bus_get(host);
>>  	if (host->bus_ops && !host->bus_dead) {
>>
>> -		if (host->bus_ops->suspend)
>> +		if (host->bus_ops->suspend) {
>> +			if (mmc_card_doing_bkops(host->card))
>> +				mmc_stop_bkops(host->card);
>>  			err = host->bus_ops->suspend(host);
>> +		}
>>
>>  		if (err == -ENOSYS || !host->bus_ops->resume) {
>>  			/*
>> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block
>> *notify_block,
>>  	switch (mode) {
>>  	case PM_HIBERNATION_PREPARE:
>>  	case PM_SUSPEND_PREPARE:
>> +		if (host->card && mmc_card_mmc(host->card) &&
>> +				mmc_card_doing_bkops(host->card)) {
>> +			mmc_interrupt_hpi(host->card);
>> +			mmc_card_clr_doing_bkops(host->card);
>> +		}
>>
>>  		spin_lock_irqsave(&host->lock, flags);
>>  		host->rescan_disable = 1;
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index
>> 258b203..9e036e5 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card,
>> u8
>> *ext_csd)
>>  	}
>>
>>  	if (card->ext_csd.rev >= 5) {
>> +		/* check whether the eMMC card support BKOPS */
>> +		if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
>> +			card->ext_csd.bkops = 1;
>> +			card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
>> +			card->ext_csd.raw_bkops_status =
>> +				ext_csd[EXT_CSD_BKOPS_STATUS];
>> +			if (!card->ext_csd.bkops_en &&
>> +				card->host->caps2 & MMC_CAP2_INIT_BKOPS) {
>> +				err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +					EXT_CSD_BKOPS_EN, 1, 0);
>> +				if (err)
>> +					pr_warn("%s: Enabling BKOPS failed\n",
>> +						mmc_hostname(card->host));
>> +				else
>> +					card->ext_csd.bkops_en = 1;
>> +			}
>> +		}
>> +
>>  		/* check whether the eMMC card supports HPI */
>>  		if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
>>  			card->ext_csd.hpi = 1;
>> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
>> index
>> 69370f4..f0bebd9 100644
>> --- a/drivers/mmc/core/mmc_ops.c
>> +++ b/drivers/mmc/core/mmc_ops.c
>> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int
>> use_crc)  }
>>
>>  /**
>> - *	mmc_switch - modify EXT_CSD register
>> + *	__mmc_switch - modify EXT_CSD register
>>   *	@card: the MMC card associated with the data transfer
>>   *	@set: cmd set values
>>   *	@index: EXT_CSD register index
>>   *	@value: value to program into EXT_CSD register
>>   *	@timeout_ms: timeout (ms) for operation performed by register write,
>>   *                   timeout of zero implies maximum possible timeout
>> + *      @wait_for_prod_done: is waiting for program done
>>   *
>>   *	Modifies the EXT_CSD register for selected card.
>>   */
>> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> -	       unsigned int timeout_ms)
>> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +	       unsigned int timeout_ms, u8 wait_for_prog_done)
>> +
>>  {
>>  	int err;
>>  	struct mmc_command cmd = {0};
>> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8
>> index, u8 value,
>>  		  (index << 16) |
>>  		  (value << 8) |
>>  		  set;
>> -	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
>> +
>> +	cmd.flags = MMC_CMD_AC;
>> +	if (wait_for_prog_done)
>> +		cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
>> +	else
>> +		cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
>> +
>>  	cmd.cmd_timeout_ms = timeout_ms;
>>
>>  	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
>>  	if (err)
>>  		return err;
>>
>> +	/* No need to check card status in case of BKOPS switch*/
>> +	if (index == EXT_CSD_BKOPS_START)
>> +		return 0;
>> +
>>  	/* Must check status to be sure of no errors */
>>  	do {
>>  		err = mmc_send_status(card, &status); @@ -423,6 +435,13 @@ int
>> mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>>
>>  	return 0;
>>  }
>> +EXPORT_SYMBOL_GPL(__mmc_switch);
>> +
>> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +		unsigned int timeout_ms)
>> +{
>> +	return __mmc_switch(card, set, index, value, timeout_ms, 1); }
>>  EXPORT_SYMBOL_GPL(mmc_switch);
>>
>>  int mmc_send_status(struct mmc_card *card, u32 *status) diff --git
>> a/include/linux/mmc/card.h b/include/linux/mmc/card.h index
>> d76513b..50b3539
>> 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
>>  	bool			hpi_en;			/* HPI enablebit */
>>  	bool			hpi;			/* HPI support bit */
>>  	unsigned int		hpi_cmd;		/* cmd used as HPI */
>> +	bool			bkops;		/* background support bit */
>> +	bool			bkops_en;	/* background enable bit */
>>  	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>>  	unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size
>> */
>>  	unsigned int		boot_ro_lock;		/* ro lock support */
>>  	bool			boot_ro_lockable;
>> +	u8			raw_exception_status;	/* 53 */
>>  	u8			raw_partition_support;	/* 160 */
>>  	u8			raw_erased_mem_count;	/* 181 */
>>  	u8			raw_ext_csd_structure;	/* 194 */
>> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
>>  	u8			raw_sec_erase_mult;	/* 230 */
>>  	u8			raw_sec_feature_support;/* 231 */
>>  	u8			raw_trim_mult;		/* 232 */
>> +	u8			raw_bkops_status;	/* 246 */
>>  	u8			raw_sectors[4];		/* 212 - 4 bytes */
>>
>>  	unsigned int            feature_support;
>> @@ -225,6 +229,9 @@ struct mmc_card {
>>  #define MMC_CARD_REMOVED	(1<<7)		/* card has been removed */
>>  #define MMC_STATE_HIGHSPEED_200	(1<<8)		/* card is in HS200
>> mode */
>>  #define MMC_STATE_SLEEP		(1<<9)		/* card is in sleep state */
>> +#define MMC_STATE_NEED_BKOPS	(1<<10)		/* card need to do BKOPS */
>> +#define MMC_STATE_DOING_BKOPS	(1<<11)		/* card is doing BKOPS */
>> +#define MMC_STATE_CHECK_BKOPS	(1<<12)		/* card need to check BKOPS
>> */
>>  	unsigned int		quirks; 	/* card quirks */
>>  #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes
>> outside of the VS CCCR range */
>>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func->cur_blksize
>> */
>> @@ -391,6 +398,9 @@ static inline void __maybe_unused
>> remove_quirk(struct
>> mmc_card *card, int data)  #define mmc_card_ext_capacity(c) ((c)->state
>> &
>> MMC_CARD_SDXC)
>>  #define mmc_card_removed(c)	((c) && ((c)->state & MMC_CARD_REMOVED))
>>  #define mmc_card_is_sleep(c)	((c)->state & MMC_STATE_SLEEP)
>> +#define mmc_card_need_bkops(c)	((c)->state & MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_doing_bkops(c)	((c)->state & MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_check_bkops(c) ((c)->state & MMC_STATE_CHECK_BKOPS)
>>
>>  #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
>>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@
>> -403,7 +413,13 @@ static inline void __maybe_unused remove_quirk(struct
>> mmc_card *card, int data)  #define mmc_card_set_ext_capacity(c)
>> ((c)->state |=
>> MMC_CARD_SDXC)  #define mmc_card_set_removed(c) ((c)->state |=
>> MMC_CARD_REMOVED)
>>  #define mmc_card_set_sleep(c)	((c)->state |= MMC_STATE_SLEEP)
>> +#define mmc_card_set_need_bkops(c)	((c)->state |=
>> MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_set_doing_bkops(c)	((c)->state |=
>> MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_set_check_bkops(c) ((c)->state |=
>> +MMC_STATE_CHECK_BKOPS)
>>
>> +#define mmc_card_clr_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_check_bkops(c) ((c)->state &=
>> +~MMC_STATE_CHECK_BKOPS)
>>  #define mmc_card_clr_sleep(c)	((c)->state &= ~MMC_STATE_SLEEP)
>>  /*
>>   * Quirk add/remove for MMC products.
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index
>> 1b431c7..bda3d86 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -134,6 +134,9 @@ struct mmc_host;
>>  struct mmc_card;
>>  struct mmc_async_req;
>>
>> +extern int mmc_stop_bkops(struct mmc_card *); extern int
>> +mmc_read_bkops_status(struct mmc_card *); extern int
>> +mmc_is_exception_event(struct mmc_card *, unsigned int);
>>  extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>>  					   struct mmc_async_req *, int *);  extern int
>> mmc_interrupt_hpi(struct mmc_card *); @@ -142,6 +145,8 @@ extern int
>> mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);  extern
>> int
>> mmc_app_cmd(struct mmc_host *, struct mmc_card *);  extern int
>> mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>>  	struct mmc_command *, int);
>> +extern void mmc_start_bkops(struct mmc_card *card); extern int
>> +__mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
>>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>>
>>  #define MMC_ERASE_ARG		0x00000000
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index
>> 0707d22..d23caf2 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -238,6 +238,8 @@ struct mmc_host {
>>  #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
>>  #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card
>> removal */
>>  #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
>> +#define MMC_CAP2_INIT_BKOPS	(1 << 10)	/* To enable BKOPS */
>> +#define MMC_CAP2_BKOPS		(1 << 11)	/* BKOPS supported */
>>
>>  	mmc_pm_flag_t		pm_caps;	/* supported pm features */
>>  	unsigned int        power_notify_type;
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index
>> d425cab..80cfbf1 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -139,6 +139,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_EXCEPTION_EVENT	(1 << 6)	/* sx, a */
>>  #define R1_APP_CMD		(1 << 5)	/* sr, c */
>>
>>  #define R1_STATE_IDLE	0
>> @@ -274,12 +275,15 @@ struct _mmc_csd {
>>  #define EXT_CSD_FLUSH_CACHE		32      /* W */
>>  #define EXT_CSD_CACHE_CTRL		33      /* R/W */
>>  #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
>> +#define EXT_CSD_EXCEPTION_STATUS	54	/* RO */
>>  #define EXT_CSD_DATA_SECTOR_SIZE	61	/* R */
>>  #define EXT_CSD_GP_SIZE_MULT		143	/* R/W */
>>  #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_RST_N_FUNCTION		162	/* R/W */
>> +#define EXT_CSD_BKOPS_EN		163	/* R/W */
>> +#define EXT_CSD_BKOPS_START		164	/* W */
>>  #define EXT_CSD_SANITIZE_START		165     /* W */
>>  #define EXT_CSD_WR_REL_PARAM		166	/* RO */
>>  #define EXT_CSD_BOOT_WP			173	/* R/W */
>> @@ -313,11 +317,13 @@ struct _mmc_csd {
>>  #define EXT_CSD_PWR_CL_200_360		237	/* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_195	238	/* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_360	239	/* RO */
>> +#define EXT_CSD_BKOPS_STATUS		246	/* RO */
>>  #define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
>>  #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
>>  #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
>>  #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
>>  #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
>> +#define EXT_CSD_BKOPS_SUPPORT		502	/* RO */
>>  #define EXT_CSD_HPI_FEATURES		503	/* RO */
>>
>>  /*
>> @@ -386,4 +392,17 @@ struct _mmc_csd {
>>  #define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1
>> in value */
>>  #define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */
>>
>> +/*
>> + * BKOPS status level
>> + */
>> +#define EXT_CSD_BKOPS_LEVEL_2		0x2
>> +
>> +/*
>> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)  */
>> +#define EXT_CSD_URGENT_BKOPS		BIT(0)
>> +#define EXT_CSD_DYNCAP_NEEDED		BIT(1)
>> +#define EXT_CSD_SYSPOOL_EXHAUSTED	BIT(2)
>> +#define EXT_CSD_PACKED_FAILURE		BIT(3)
>> +
>>  #endif /* LINUX_MMC_MMC_H */
>> --
>> 1.7.4.1
>


-- 
Kostya Dorfman,
Consultant for Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-14 13:46 ` Adrian Hunter
@ 2012-06-14 14:58   ` Konstantin Dorfman
  2012-07-12  5:02   ` Jaehoon Chung
  1 sibling, 0 replies; 23+ messages in thread
From: Konstantin Dorfman @ 2012-06-14 14:58 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com, Saugata Das, Konstantin Dorfman, Maya Erez,
	Ulf Hansson

Hello Adrian,

On Thu, June 14, 2012 4:46 pm, Adrian Hunter wrote:
> On 08/06/12 07:39, Jaehoon Chung wrote:
>> Enable eMMC background operations (BKOPS) feature.
>>
>> If URGENT_BKOPS is set after a response, note that BKOPS
>> are required. After all I/O requests are finished, run
>> BKOPS if required. Should read/write operations be requested
>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>> and then service the request.
>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>> the BKOPS-STATUS vaule.
>>
>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>> And if you want to set the BKOPS_EN bit in ext_csd register,
>> use the MMC_CAP2_INIT_BKOPS.
>>
>> Future considerations
>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>  * Interrupt ongoing BKOPS before powering off the card.
>>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>> Signed-off-by: Maya Erez <merez@codeaurora.org>
>> ---
>
> I would not expect this to work nicely with runtime PM.  I expect that
> BKOPS
> would need to be stopped beforehand.  But that would limit the time
> available for BKOPS since runtime PM would always kick in and stop it.
> How
> is runtime PM to be handled?
>
Indeed, it is handled in mmc_suspend_host(), HPI issued before CMD5, when
background BKOPS in progress.
We expect to start BKOPS when idle period starts (no block requests
incoming). Then we have 2 cases:
 1) URGENT level reached => BKOPS started synchronously and host will wait
    till no more BKOPS needed
 2) BKOPS level is not URGENT, there is host controller driver defined
    timeout after last host command, before runtime suspend flow triggerred.
    For example for msm_sdcc it is 5 second.

This implementation was tested and we did not see URGENT levels and
performance degradation.
-- 
Kostya Dorfman,
Consultant for Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-13 12:40 ` Dong, Chuanxiao
       [not found]   ` <17296D9F8FF2234F831FC3DF505A87A90FEA11E5@SHSMSX102.ccr.corp.intel.com >
@ 2012-06-15  3:00   ` Jaehoon Chung
  1 sibling, 0 replies; 23+ messages in thread
From: Jaehoon Chung @ 2012-06-15  3:00 UTC (permalink / raw)
  To: Dong, Chuanxiao
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, svenkatr@ti.com, Saugata Das,
	Konstantin Dorfman, Hunter, Adrian, Maya Erez, Ulf Hansson

Hi, Dong,

On 06/13/2012 09:40 PM, Dong, Chuanxiao wrote:

> Hi Jaehoon,
> 
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index
>> 0b6141d..9adb004 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -41,6 +41,12 @@
>>  #include "sd_ops.h"
>>  #include "sdio_ops.h"
>>
>> +/*
>> + * The Background operation can take a long time, depends on the house
>> +keeping
>> + * operations the card has to peform
>> + */
>> +#define MMC_BKOPS_MAX_TIMEOUT	(4 * 80 * 1000) /* max time to wait in
>> ms */
>> +
> Just go through the eMMC specification, didn't find any clue about the max timeout value for background operations. So, could you please tell why you choose 320s?
> This value will used to calculate TIMEOUT_CONTROL registers for SDHC host controller. And as far as I know, this value is too large for some host.
> 
>>  static struct workqueue_struct *workqueue;  static const unsigned freqs[] =
>> { 400000, 300000, 200000, 100000 };
>>
>> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct
>> mmc_request *mrq)
>>  		if (mrq->done)
>>  			mrq->done(mrq);
>>
>> +		/*
>> +		 * Check BKOPS urgency from each R1 response
>> +		 */
>> +		if (host->card && mmc_card_mmc(host->card) &&
>> +			((mmc_resp_type(cmd) == MMC_RSP_R1) ||
>> +			 (mmc_resp_type(cmd) == MMC_RSP_R1B)) &&
>> +			(cmd->resp[0] & R1_EXCEPTION_EVENT)) {
>> +			mmc_card_set_check_bkops(host->card);
>> +		}
>> +
>>  		mmc_host_clk_release(host);
>>  	}
>>  }
>> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct
>> mmc_request *mrq)
>>  	host->ops->request(host, mrq);
>>  }
>>
>> +/**
>> + *	mmc_start_bkops - start BKOPS for supported cards
>> + *	@card: MMC card to start BKOPS
>> + *
>> + *	Start background operations whenever requested.
>> + *	when the urgent BKOPS bit is set in a R1 command response
>> + *	then background operations should be started immediately.
>> +*/
>> +void mmc_start_bkops(struct mmc_card *card) {
>> +	int err;
>> +	int timeout;
>> +	u8 wait_for_prog_done;
>> +
>> +	BUG_ON(!card);
>> +	if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
>> +		return;
>> +
>> +	if (mmc_card_check_bkops(card)) {
>> +		mmc_card_clr_check_bkops(card);
>> +		if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
> eMMC4.5 specification has said that "EXCEPTION_EVENTS_STATUS is set whenever the levels is either 2 or 3." To my understanding, EXCEPTION_EVENTS_STATUS will be set only when level-2 or level-3. For level-0 and level-1, EXCEPTION_EVENTS_STATUS won't be set. Regarding this, checking EXCEPTION_EVENTS_CTRL register during mmc card initialization phase can help to save the ext_csd reading commands.
> 
> I don't have eMMC4.5 chip on hand, not sure how the real device is implemented. It is better if you can provide a testing result with your EXCEPTION_EVENTS_STATUS register and BKOPS_STATUS register.

Well, in my case(eMMC4.5), When Urgent bkops is set, i can see Level-0 or level-1. I think it's dependent on card's implementation.
And In eMMC4.5, we didn't know what exception_event is. At results, we must read the ext_csd register, then we can also read the bkops_status.
It's not waste.
As you mentioned, if didn't occur the level-0 and level-1, didn't check EXCEPTION_EVENT_STATUS.

> 
>> +			if (card->ext_csd.raw_bkops_status)
>> +				mmc_card_set_need_bkops(card);
>> +	}
>> +
>> +	/*
>> +	 * If card is already doing bkops or need for
>> +	 * bkops flag is not set, then do nothing just
>> +	 * return
>> +	 */
>> +	if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
>> +		return;
>> +
>> +	mmc_claim_host(card->host);
>> +	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
>> +		timeout = MMC_BKOPS_MAX_TIMEOUT;
>> +		wait_for_prog_done = 1;
>> +	} else {
>> +		timeout = 0;
>> +		wait_for_prog_done = 0;
>> +	}
> As specification, CMD6 needs R1B response. Is it really good to use R1 response for it?
> To my understanding, the BUSY signal is indicate whether the CMD6 switching is done, but not whether the background operation is done. After CMD6, card will stay in PRG state which means card is still not ready for data transferring. Once the card state change to transfer state, background operation is really finished. So the signal for whether background operations finished is not the BUSY signal but the card state. Please correct me if I am wrong on this point. Of cause, you can have a test with your device.

Right, in spec, CDM6 need R1B response. But i think that Level-0 and Level-1 didn't need waiting for busy signal.

Best Regards,
Jaehoon Chung

> 
> Thanks
> Chuanxiao
> 
>> +
>> +	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +			EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
>> +	if (err) {
>> +		pr_warn("%s: error %d starting bkops\n",
>> +			   mmc_hostname(card->host), err);
>> +		mmc_card_clr_need_bkops(card);
>> +		goto out;
>> +	}
>> +
>> +	mmc_card_clr_need_bkops(card);
>> +
>> +	/*
>> +	 * For urgent bkops status (LEVEL_2 and more)
>> +	 * bkops executed synchronously, otherwise
>> +	 * the operation is in progress
>> +	 */
>> +	if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
>> +		mmc_card_set_check_bkops(card);
>> +	else
>> +		mmc_card_set_doing_bkops(card);
>> +
>> +out:
>> +	mmc_release_host(card->host);
>> +}
>> +EXPORT_SYMBOL(mmc_start_bkops);
>> +
>>  static void mmc_wait_done(struct mmc_request *mrq)  {
>>  	complete(&mrq->completion);
>> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
>> mmc_host *host,
>>  	if (!err && areq)
>>  		start_err = __mmc_start_req(host, areq->mrq);
>>
>> -	if (host->areq)
>> +	if (host->areq) {
>> +		if (!areq && host->areq && mmc_card_mmc(host->card))
>> +			mmc_start_bkops(host->card);
>>  		mmc_post_req(host, host->areq->mrq, 0);
>> +	}
>>
>>  	 /* Cancel a prepared request if it was not started. */
>>  	if ((err || start_err) && areq)
>> -			mmc_post_req(host, areq->mrq, -EINVAL);
>> +		mmc_post_req(host, areq->mrq, -EINVAL);
>>
>>  	if (err)
>>  		host->areq = NULL;
>> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
>> mmc_command *cmd, int retries  EXPORT_SYMBOL(mmc_wait_for_cmd);
>>
>>  /**
>> + *	mmc_stop_bkops - stop ongoing BKOPS
>> + *	@card: MMC card to check BKOPS
>> + *
>> + *	Send HPI command to stop ongoing background operations,
>> + *	to allow rapid servicing of foreground operations,e.g. read/
>> + *	writes. Wait until the card comes out of the programming state
>> + *	to avoid errors in servicing read/write requests.
>> + */
>> +int mmc_stop_bkops(struct mmc_card *card) {
>> +	int err = 0;
>> +
>> +	BUG_ON(!card);
>> +
>> +	err = mmc_interrupt_hpi(card);
>> +
>> +	mmc_card_clr_doing_bkops(card);
>> +
>> +	return err;
>> +}
>> +EXPORT_SYMBOL(mmc_stop_bkops);
>> +
>> +int mmc_read_bkops_status(struct mmc_card *card) {
>> +	int err;
>> +	u8 ext_csd[512];
>> +
>> +	mmc_claim_host(card->host);
>> +	err = mmc_send_ext_csd(card, ext_csd);
>> +	mmc_release_host(card->host);
>> +	if (err)
>> +		return err;
>> +
>> +	card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
>> +	card->ext_csd.raw_exception_status =
>> +ext_csd[EXT_CSD_EXCEPTION_STATUS];
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(mmc_read_bkops_status);
>> +
>> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value) {
>> +	int err;
>> +
>> +	err = mmc_read_bkops_status(card);
>> +	if (err) {
>> +		pr_err("%s: Didn't read bkops status : %d\n",
>> +		       mmc_hostname(card->host), err);
>> +		return 0;
>> +	}
>> +
>> +	/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
>> +	if (card->ext_csd.rev == 5)
>> +		return 1;
>> +
>> +	return (card->ext_csd.raw_exception_status & value) ? 1 : 0; }
>> +EXPORT_SYMBOL(mmc_is_exception_event);
>> +
>> +/**
>>   *	mmc_set_data_timeout - set the timeout for a data command
>>   *	@data: data phase for command
>>   *	@card: the MMC card associated with the data transfer
>> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
>>  	mmc_bus_get(host);
>>  	if (host->bus_ops && !host->bus_dead) {
>>
>> -		if (host->bus_ops->suspend)
>> +		if (host->bus_ops->suspend) {
>> +			if (mmc_card_doing_bkops(host->card))
>> +				mmc_stop_bkops(host->card);
>>  			err = host->bus_ops->suspend(host);
>> +		}
>>
>>  		if (err == -ENOSYS || !host->bus_ops->resume) {
>>  			/*
>> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
>>  	switch (mode) {
>>  	case PM_HIBERNATION_PREPARE:
>>  	case PM_SUSPEND_PREPARE:
>> +		if (host->card && mmc_card_mmc(host->card) &&
>> +				mmc_card_doing_bkops(host->card)) {
>> +			mmc_interrupt_hpi(host->card);
>> +			mmc_card_clr_doing_bkops(host->card);
>> +		}
>>
>>  		spin_lock_irqsave(&host->lock, flags);
>>  		host->rescan_disable = 1;
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index
>> 258b203..9e036e5 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8
>> *ext_csd)
>>  	}
>>
>>  	if (card->ext_csd.rev >= 5) {
>> +		/* check whether the eMMC card support BKOPS */
>> +		if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
>> +			card->ext_csd.bkops = 1;
>> +			card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
>> +			card->ext_csd.raw_bkops_status =
>> +				ext_csd[EXT_CSD_BKOPS_STATUS];
>> +			if (!card->ext_csd.bkops_en &&
>> +				card->host->caps2 & MMC_CAP2_INIT_BKOPS) {
>> +				err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +					EXT_CSD_BKOPS_EN, 1, 0);
>> +				if (err)
>> +					pr_warn("%s: Enabling BKOPS failed\n",
>> +						mmc_hostname(card->host));
>> +				else
>> +					card->ext_csd.bkops_en = 1;
>> +			}
>> +		}
>> +
>>  		/* check whether the eMMC card supports HPI */
>>  		if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
>>  			card->ext_csd.hpi = 1;
>> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index
>> 69370f4..f0bebd9 100644
>> --- a/drivers/mmc/core/mmc_ops.c
>> +++ b/drivers/mmc/core/mmc_ops.c
>> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int
>> use_crc)  }
>>
>>  /**
>> - *	mmc_switch - modify EXT_CSD register
>> + *	__mmc_switch - modify EXT_CSD register
>>   *	@card: the MMC card associated with the data transfer
>>   *	@set: cmd set values
>>   *	@index: EXT_CSD register index
>>   *	@value: value to program into EXT_CSD register
>>   *	@timeout_ms: timeout (ms) for operation performed by register write,
>>   *                   timeout of zero implies maximum possible timeout
>> + *      @wait_for_prod_done: is waiting for program done
>>   *
>>   *	Modifies the EXT_CSD register for selected card.
>>   */
>> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> -	       unsigned int timeout_ms)
>> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +	       unsigned int timeout_ms, u8 wait_for_prog_done)
>> +
>>  {
>>  	int err;
>>  	struct mmc_command cmd = {0};
>> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8
>> index, u8 value,
>>  		  (index << 16) |
>>  		  (value << 8) |
>>  		  set;
>> -	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
>> +
>> +	cmd.flags = MMC_CMD_AC;
>> +	if (wait_for_prog_done)
>> +		cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
>> +	else
>> +		cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
>> +
>>  	cmd.cmd_timeout_ms = timeout_ms;
>>
>>  	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
>>  	if (err)
>>  		return err;
>>
>> +	/* No need to check card status in case of BKOPS switch*/
>> +	if (index == EXT_CSD_BKOPS_START)
>> +		return 0;
>> +
>>  	/* Must check status to be sure of no errors */
>>  	do {
>>  		err = mmc_send_status(card, &status); @@ -423,6 +435,13 @@ int
>> mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>>
>>  	return 0;
>>  }
>> +EXPORT_SYMBOL_GPL(__mmc_switch);
>> +
>> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +		unsigned int timeout_ms)
>> +{
>> +	return __mmc_switch(card, set, index, value, timeout_ms, 1); }
>>  EXPORT_SYMBOL_GPL(mmc_switch);
>>
>>  int mmc_send_status(struct mmc_card *card, u32 *status) diff --git
>> a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d76513b..50b3539
>> 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
>>  	bool			hpi_en;			/* HPI enablebit */
>>  	bool			hpi;			/* HPI support bit */
>>  	unsigned int		hpi_cmd;		/* cmd used as HPI */
>> +	bool			bkops;		/* background support bit */
>> +	bool			bkops_en;	/* background enable bit */
>>  	unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>>  	unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size */
>>  	unsigned int		boot_ro_lock;		/* ro lock support */
>>  	bool			boot_ro_lockable;
>> +	u8			raw_exception_status;	/* 53 */
>>  	u8			raw_partition_support;	/* 160 */
>>  	u8			raw_erased_mem_count;	/* 181 */
>>  	u8			raw_ext_csd_structure;	/* 194 */
>> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
>>  	u8			raw_sec_erase_mult;	/* 230 */
>>  	u8			raw_sec_feature_support;/* 231 */
>>  	u8			raw_trim_mult;		/* 232 */
>> +	u8			raw_bkops_status;	/* 246 */
>>  	u8			raw_sectors[4];		/* 212 - 4 bytes */
>>
>>  	unsigned int            feature_support;
>> @@ -225,6 +229,9 @@ struct mmc_card {
>>  #define MMC_CARD_REMOVED	(1<<7)		/* card has been removed */
>>  #define MMC_STATE_HIGHSPEED_200	(1<<8)		/* card is in HS200
>> mode */
>>  #define MMC_STATE_SLEEP		(1<<9)		/* card is in sleep state */
>> +#define MMC_STATE_NEED_BKOPS	(1<<10)		/* card need to do BKOPS */
>> +#define MMC_STATE_DOING_BKOPS	(1<<11)		/* card is doing BKOPS */
>> +#define MMC_STATE_CHECK_BKOPS	(1<<12)		/* card need to check BKOPS
>> */
>>  	unsigned int		quirks; 	/* card quirks */
>>  #define MMC_QUIRK_LENIENT_FN0	(1<<0)		/* allow SDIO FN0 writes
>> outside of the VS CCCR range */
>>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)	/* use func->cur_blksize
>> */
>> @@ -391,6 +398,9 @@ static inline void __maybe_unused remove_quirk(struct
>> mmc_card *card, int data)  #define mmc_card_ext_capacity(c) ((c)->state &
>> MMC_CARD_SDXC)
>>  #define mmc_card_removed(c)	((c) && ((c)->state & MMC_CARD_REMOVED))
>>  #define mmc_card_is_sleep(c)	((c)->state & MMC_STATE_SLEEP)
>> +#define mmc_card_need_bkops(c)	((c)->state & MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_doing_bkops(c)	((c)->state & MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_check_bkops(c) ((c)->state & MMC_STATE_CHECK_BKOPS)
>>
>>  #define mmc_card_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
>>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@
>> -403,7 +413,13 @@ static inline void __maybe_unused remove_quirk(struct
>> mmc_card *card, int data)  #define mmc_card_set_ext_capacity(c) ((c)->state |=
>> MMC_CARD_SDXC)  #define mmc_card_set_removed(c) ((c)->state |=
>> MMC_CARD_REMOVED)
>>  #define mmc_card_set_sleep(c)	((c)->state |= MMC_STATE_SLEEP)
>> +#define mmc_card_set_need_bkops(c)	((c)->state |=
>> MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_set_doing_bkops(c)	((c)->state |=
>> MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_set_check_bkops(c) ((c)->state |=
>> +MMC_STATE_CHECK_BKOPS)
>>
>> +#define mmc_card_clr_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_check_bkops(c) ((c)->state &=
>> +~MMC_STATE_CHECK_BKOPS)
>>  #define mmc_card_clr_sleep(c)	((c)->state &= ~MMC_STATE_SLEEP)
>>  /*
>>   * Quirk add/remove for MMC products.
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index
>> 1b431c7..bda3d86 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -134,6 +134,9 @@ struct mmc_host;
>>  struct mmc_card;
>>  struct mmc_async_req;
>>
>> +extern int mmc_stop_bkops(struct mmc_card *); extern int
>> +mmc_read_bkops_status(struct mmc_card *); extern int
>> +mmc_is_exception_event(struct mmc_card *, unsigned int);
>>  extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>>  					   struct mmc_async_req *, int *);  extern int
>> mmc_interrupt_hpi(struct mmc_card *); @@ -142,6 +145,8 @@ extern int
>> mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);  extern int
>> mmc_app_cmd(struct mmc_host *, struct mmc_card *);  extern int
>> mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>>  	struct mmc_command *, int);
>> +extern void mmc_start_bkops(struct mmc_card *card); extern int
>> +__mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
>>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>>
>>  #define MMC_ERASE_ARG		0x00000000
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index
>> 0707d22..d23caf2 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -238,6 +238,8 @@ struct mmc_host {
>>  #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
>>  #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card
>> removal */
>>  #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
>> +#define MMC_CAP2_INIT_BKOPS	(1 << 10)	/* To enable BKOPS */
>> +#define MMC_CAP2_BKOPS		(1 << 11)	/* BKOPS supported */
>>
>>  	mmc_pm_flag_t		pm_caps;	/* supported pm features */
>>  	unsigned int        power_notify_type;
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index
>> d425cab..80cfbf1 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -139,6 +139,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_EXCEPTION_EVENT	(1 << 6)	/* sx, a */
>>  #define R1_APP_CMD		(1 << 5)	/* sr, c */
>>
>>  #define R1_STATE_IDLE	0
>> @@ -274,12 +275,15 @@ struct _mmc_csd {
>>  #define EXT_CSD_FLUSH_CACHE		32      /* W */
>>  #define EXT_CSD_CACHE_CTRL		33      /* R/W */
>>  #define EXT_CSD_POWER_OFF_NOTIFICATION	34	/* R/W */
>> +#define EXT_CSD_EXCEPTION_STATUS	54	/* RO */
>>  #define EXT_CSD_DATA_SECTOR_SIZE	61	/* R */
>>  #define EXT_CSD_GP_SIZE_MULT		143	/* R/W */
>>  #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_RST_N_FUNCTION		162	/* R/W */
>> +#define EXT_CSD_BKOPS_EN		163	/* R/W */
>> +#define EXT_CSD_BKOPS_START		164	/* W */
>>  #define EXT_CSD_SANITIZE_START		165     /* W */
>>  #define EXT_CSD_WR_REL_PARAM		166	/* RO */
>>  #define EXT_CSD_BOOT_WP			173	/* R/W */
>> @@ -313,11 +317,13 @@ struct _mmc_csd {
>>  #define EXT_CSD_PWR_CL_200_360		237	/* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_195	238	/* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_360	239	/* RO */
>> +#define EXT_CSD_BKOPS_STATUS		246	/* RO */
>>  #define EXT_CSD_POWER_OFF_LONG_TIME	247	/* RO */
>>  #define EXT_CSD_GENERIC_CMD6_TIME	248	/* RO */
>>  #define EXT_CSD_CACHE_SIZE		249	/* RO, 4 bytes */
>>  #define EXT_CSD_TAG_UNIT_SIZE		498	/* RO */
>>  #define EXT_CSD_DATA_TAG_SUPPORT	499	/* RO */
>> +#define EXT_CSD_BKOPS_SUPPORT		502	/* RO */
>>  #define EXT_CSD_HPI_FEATURES		503	/* RO */
>>
>>  /*
>> @@ -386,4 +392,17 @@ struct _mmc_csd {
>>  #define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1
>> in value */
>>  #define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */
>>
>> +/*
>> + * BKOPS status level
>> + */
>> +#define EXT_CSD_BKOPS_LEVEL_2		0x2
>> +
>> +/*
>> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)  */
>> +#define EXT_CSD_URGENT_BKOPS		BIT(0)
>> +#define EXT_CSD_DYNCAP_NEEDED		BIT(1)
>> +#define EXT_CSD_SYSPOOL_EXHAUSTED	BIT(2)
>> +#define EXT_CSD_PACKED_FAILURE		BIT(3)
>> +
>>  #endif /* LINUX_MMC_MMC_H */
>> --
>> 1.7.4.1
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
       [not found] ` <4FD88B7D.3000600@codeaurora.org>
@ 2012-06-15  4:19   ` Jaehoon Chung
  2012-06-15  6:10     ` Subhash Jadavani
  0 siblings, 1 reply; 23+ messages in thread
From: Jaehoon Chung @ 2012-06-15  4:19 UTC (permalink / raw)
  To: Subhash Jadavani
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Maya Erez,
	Ulf Hansson

Hi Subhash,

On 06/13/2012 09:45 PM, Subhash Jadavani wrote:

> Hi Jaehoon,
> 
> Find few comments inline below. Also, periodic BKOPs support was present in v8 but now you have removed it in v9. Is there any reason for it?

Yes, there is the periodic bkops in v-8. But, i have discussed this with Konstantin and Maya.
1) Submit Basic BKOPS in priority work.
2) after (1), we can add the periodic bkops.
The periodic bkops also need for eMMC's maintenance. but First, we want to merge the basic bkops feature in mainline.
The Periodic bkops should be other patch.

> 
> Regards,
> Subhash
> 
> On 6/8/2012 10:09 AM, Jaehoon Chung wrote:
>> Enable eMMC background operations (BKOPS) feature.
>>
>> If URGENT_BKOPS is set after a response, note that BKOPS
>> are required. After all I/O requests are finished, run
>> BKOPS if required. Should read/write operations be requested
>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>> and then service the request.
>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>> the BKOPS-STATUS vaule.
>>
>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>> And if you want to set the BKOPS_EN bit in ext_csd register,
>> use the MMC_CAP2_INIT_BKOPS.
>>
>> Future considerations
>>   * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>   * Interrupt ongoing BKOPS before powering off the card.
>>   * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>>
>> Signed-off-by: Jaehoon Chung<jh80.chung@samsung.com>
>> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>
>> Signed-off-by: Konstantin Dorfman<kdorfman@codeaurora.org>
>> Signed-off-by: Maya Erez<merez@codeaurora.org>
>> ---
>> Changelog V9:
>>     - Rebased on patch-v7.
>>     - Added Konstantin and Maya's patch
>>         : mmc:core: Define synchronous BKOPS timeout
>>         : mmc:core: eMMC4.5 BKOPS fixes
>>     - Removed periodic bkops
>>         : This feature will do in future work
>>     - Add __mmc_switch() for waiting_for_prod_done.
>>     
>> Changelog V8:
>>     - Remove host->lock spin lock reviewed by Adrian
>>     - Support periodic start bkops
>>     - when bkops_status is level-3, if timeout is set to 0, send hpi.
>>     - Move the start-bkops point
>> Changelog V7:
>>     - Use HPI command when issued URGENT_BKOPS
>>     - Recheck until clearing the bkops-status bit.
>> Changelog V6:
>>     - Add the flag of check-bkops-status.
>>       (For fixing async_req problem)
>>     - Add the capability for MMC_CAP2_INIT_BKOPS.
>>       (When unset the bkops_en bit in ext_csd register)
>>     - modify the wrong condition.
>> Changelog V5:
>>     - Rebase based on the latest mmc-next.
>>     - modify codes based-on Chris's comment
>> Changelog V4:
>>     - Add mmc_read_bkops_status
>>     - When URGENT_BKOPS(level2-3), didn't use HPI command.
>>     - In mmc_switch(), use R1B/R1 according to level.
>> Changelog V3:
>>     - move the bkops setting's location in mmc_blk_issue_rw_rq
>>     - modify condition checking
>>     - bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
>>     - remove the unused code
>>     - change pr_debug instead of pr_warn in mmc_send_hpi_cmd
>>     - Add the Future consideration suggested by Per
>> Changelog V2:
>>     - Use EXCEPTION_STATUS instead of URGENT_BKOPS
>>     - Add function to check Exception_status(for eMMC4.5)
>> ---
>>   drivers/mmc/card/queue.c   |    2 +
>>   drivers/mmc/core/core.c    |  161 +++++++++++++++++++++++++++++++++++++++++++-
>>   drivers/mmc/core/mmc.c     |   18 +++++
>>   drivers/mmc/core/mmc_ops.c |   27 ++++++-
>>   include/linux/mmc/card.h   |   16 +++++
>>   include/linux/mmc/core.h   |    5 ++
>>   include/linux/mmc/host.h   |    2 +
>>   include/linux/mmc/mmc.h    |   19 +++++
>>   8 files changed, 243 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..e4a2cde 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)
>>           spin_unlock_irq(q->queue_lock);
>>
>>           if (req || mq->mqrq_prev->req) {
>> +            if (mmc_card_doing_bkops(mq->card))
>> +                mmc_stop_bkops(mq->card);
> This is what we had discussed last time but i don't see that you are considering error returned by mmc_stop_bkops(). Any reason for it?
> [Subhash] mmc_stop_bkops() may fail and can return an error but we are ignoring it here. What if this function has returned error which means BKOPS may still be running in card and if you go ahead and issue another read/write request?
> [Jaehoon] Great..i didn't think that..I will consider this point. Thanks.

Will do fix..

> 
>>               set_current_state(TASK_RUNNING);
>>               mq->issue_fn(mq, req);
>>           } else {
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 0b6141d..9adb004 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -41,6 +41,12 @@
>>   #include "sd_ops.h"
>>   #include "sdio_ops.h"
>>
>> +/*
>> + * The Background operation can take a long time, depends on the house keeping
>> + * operations the card has to peform
>> + */
>> +#define MMC_BKOPS_MAX_TIMEOUT    (4 * 80 * 1000) /* max time to wait in ms */
>> +
>>   static struct workqueue_struct *workqueue;
>>   static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
>>
>> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
>>           if (mrq->done)
>>               mrq->done(mrq);
>>
>> +        /*
>> +         * Check BKOPS urgency from each R1 response
>> +         */
>> +        if (host->card&&  mmc_card_mmc(host->card)&&
>> +            ((mmc_resp_type(cmd) == MMC_RSP_R1) ||
>> +             (mmc_resp_type(cmd) == MMC_RSP_R1B))&&
>> +            (cmd->resp[0]&  R1_EXCEPTION_EVENT)) {
>> +            mmc_card_set_check_bkops(host->card);
>> +        }
>> +
>>           mmc_host_clk_release(host);
>>       }
>>   }
>> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
>>       host->ops->request(host, mrq);
>>   }
>>
>> +/**
>> + *    mmc_start_bkops - start BKOPS for supported cards
>> + *    @card: MMC card to start BKOPS
>> + *
>> + *    Start background operations whenever requested.
>> + *    when the urgent BKOPS bit is set in a R1 command response
>> + *    then background operations should be started immediately.
>> +*/
>> +void mmc_start_bkops(struct mmc_card *card)
>> +{
>> +    int err;
>> +    int timeout;
>> +    u8 wait_for_prog_done;
>> +
>> +    BUG_ON(!card);
>> +    if (!card->ext_csd.bkops_en || !(card->host->caps2&  MMC_CAP2_BKOPS))
>> +        return;
>> +
>> +    if (mmc_card_check_bkops(card)) {
>> +        mmc_card_clr_check_bkops(card);
>> +        if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
>> +            if (card->ext_csd.raw_bkops_status)
>> +                mmc_card_set_need_bkops(card);
>> +    }
>> +
>> +    /*
>> +     * If card is already doing bkops or need for
>> +     * bkops flag is not set, then do nothing just
>> +     * return
>> +     */
>> +    if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
>> +        return;
>> +
>> +    mmc_claim_host(card->host);
>> +    if (card->ext_csd.raw_bkops_status>= EXT_CSD_BKOPS_LEVEL_2) {
>> +        timeout = MMC_BKOPS_MAX_TIMEOUT;
>> +        wait_for_prog_done = 1;
>> +    } else {
>> +        timeout = 0;
>> +        wait_for_prog_done = 0;
>> +    }
>> +
>> +    err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +            EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
>> +    if (err) {
>> +        pr_warn("%s: error %d starting bkops\n",
>> +               mmc_hostname(card->host), err);
>> +        mmc_card_clr_need_bkops(card);
>> +        goto out;
>> +    }
>> +
>> +    mmc_card_clr_need_bkops(card);
>> +
>> +    /*
>> +     * For urgent bkops status (LEVEL_2 and more)
>> +     * bkops executed synchronously, otherwise
>> +     * the operation is in progress
>> +     */
>> +    if (card->ext_csd.raw_bkops_status>= EXT_CSD_BKOPS_LEVEL_2)
>> +        mmc_card_set_check_bkops(card);
>> +    else
>> +        mmc_card_set_doing_bkops(card);
>> +
>> +out:
>> +    mmc_release_host(card->host);
>> +}
>> +EXPORT_SYMBOL(mmc_start_bkops);
>> +
>>   static void mmc_wait_done(struct mmc_request *mrq)
>>   {
>>       complete(&mrq->completion);
>> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
>>       if (!err&&  areq)
>>           start_err = __mmc_start_req(host, areq->mrq);
>>
>> -    if (host->areq)
>> +    if (host->areq) {
>> +        if (!areq&&  host->areq&&  mmc_card_mmc(host->card))
>> +            mmc_start_bkops(host->card);
>>           mmc_post_req(host, host->areq->mrq, 0);
>> +    }
>>
>>        /* Cancel a prepared request if it was not started. */
>>       if ((err || start_err)&&  areq)
>> -            mmc_post_req(host, areq->mrq, -EINVAL);
>> +        mmc_post_req(host, areq->mrq, -EINVAL);
>>
>>       if (err)
>>           host->areq = NULL;
>> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
>>   EXPORT_SYMBOL(mmc_wait_for_cmd);
>>
>>   /**
>> + *    mmc_stop_bkops - stop ongoing BKOPS
>> + *    @card: MMC card to check BKOPS
>> + *
>> + *    Send HPI command to stop ongoing background operations,
>> + *    to allow rapid servicing of foreground operations,e.g. read/
>> + *    writes. Wait until the card comes out of the programming state
>> + *    to avoid errors in servicing read/write requests.
>> + */
>> +int mmc_stop_bkops(struct mmc_card *card)
>> +{
>> +    int err = 0;
>> +
>> +    BUG_ON(!card);
>> +
>> +    err = mmc_interrupt_hpi(card);
>> +
>> +    mmc_card_clr_doing_bkops(card);
> 
> Here even if mmc_interrupt_hpi() fails, you are clearing the DOING_BKOPS flag which is not correct. Which should clear this flag if HPI issued successfully.

If hpi command is failed, we can consider two case.
1) bkops is already done.
2) send_command error.
I will add the code handled them.

> 
> 
> 
>> +
>> +    return err;
>> +}
>> +EXPORT_SYMBOL(mmc_stop_bkops);
>> +
>> +int mmc_read_bkops_status(struct mmc_card *card)
>> +{
>> +    int err;
>> +    u8 ext_csd[512];
>> +
>> +    mmc_claim_host(card->host);
>> +    err = mmc_send_ext_csd(card, ext_csd);
>> +    mmc_release_host(card->host);
>> +    if (err)
>> +        return err;
>> +
>> +    card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
>> +    card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXCEPTION_STATUS];
>> +
>> +    return 0;
>> +}
>> +EXPORT_SYMBOL(mmc_read_bkops_status);
>> +
>> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
>> +{
>> +    int err;
>> +
>> +    err = mmc_read_bkops_status(card);
>> +    if (err) {
>> +        pr_err("%s: Didn't read bkops status : %d\n",
>> +               mmc_hostname(card->host), err);
>> +        return 0;
>> +    }
>> +
>> +    /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
>> +    if (card->ext_csd.rev == 5)
>> +        return 1;
>> +
>> +    return (card->ext_csd.raw_exception_status&  value) ? 1 : 0;
>> +}
>> +EXPORT_SYMBOL(mmc_is_exception_event);
>> +
>> +/**
>>    *    mmc_set_data_timeout - set the timeout for a data command
>>    *    @data: data phase for command
>>    *    @card: the MMC card associated with the data transfer
>> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
>>       mmc_bus_get(host);
>>       if (host->bus_ops&&  !host->bus_dead) {
>>
>> -        if (host->bus_ops->suspend)
>> +        if (host->bus_ops->suspend) {
>> +            if (mmc_card_doing_bkops(host->card))
>> +                mmc_stop_bkops(host->card);
>>               err = host->bus_ops->suspend(host);
>> +        }
>>
>>           if (err == -ENOSYS || !host->bus_ops->resume) {
>>               /*
>> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
>>       switch (mode) {
>>       case PM_HIBERNATION_PREPARE:
>>       case PM_SUSPEND_PREPARE:
>> +        if (host->card&&  mmc_card_mmc(host->card)&&
>> +                mmc_card_doing_bkops(host->card)) {
>> +            mmc_interrupt_hpi(host->card);
>> +            mmc_card_clr_doing_bkops(host->card);
>> +        }
>>
>>           spin_lock_irqsave(&host->lock, flags);
>>           host->rescan_disable = 1;
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 258b203..9e036e5 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
>>       }
>>
>>       if (card->ext_csd.rev>= 5) {
>> +        /* check whether the eMMC card support BKOPS */
>> +        if (ext_csd[EXT_CSD_BKOPS_SUPPORT]&  0x1) {
>> +            card->ext_csd.bkops = 1;
>> +            card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
>> +            card->ext_csd.raw_bkops_status =
>> +                ext_csd[EXT_CSD_BKOPS_STATUS];
>> +            if (!card->ext_csd.bkops_en&&
>> +                card->host->caps2&  MMC_CAP2_INIT_BKOPS) {
>> +                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                    EXT_CSD_BKOPS_EN, 1, 0);
>> +                if (err)
>> +                    pr_warn("%s: Enabling BKOPS failed\n",
>> +                        mmc_hostname(card->host));
>> +                else
>> +                    card->ext_csd.bkops_en = 1;
>> +            }
>> +        }
>> +
>>           /* check whether the eMMC card supports HPI */
>>           if (ext_csd[EXT_CSD_HPI_FEATURES]&  0x1) {
>>               card->ext_csd.hpi = 1;
>> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
>> index 69370f4..f0bebd9 100644
>> --- a/drivers/mmc/core/mmc_ops.c
>> +++ b/drivers/mmc/core/mmc_ops.c
>> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
>>   }
>>
>>   /**
>> - *    mmc_switch - modify EXT_CSD register
>> + *    __mmc_switch - modify EXT_CSD register
>>    *    @card: the MMC card associated with the data transfer
>>    *    @set: cmd set values
>>    *    @index: EXT_CSD register index
>>    *    @value: value to program into EXT_CSD register
>>    *    @timeout_ms: timeout (ms) for operation performed by register write,
>>    *                   timeout of zero implies maximum possible timeout
>> + *      @wait_for_prod_done: is waiting for program done
>>    *
>>    *    Modifies the EXT_CSD register for selected card.
>>    */
>> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> -           unsigned int timeout_ms)
>> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +           unsigned int timeout_ms, u8 wait_for_prog_done)
>> +
>>   {
>>       int err;
>>       struct mmc_command cmd = {0};
>> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>>             (index<<  16) |
>>             (value<<  8) |
>>             set;
>> -    cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
>> +
>> +    cmd.flags = MMC_CMD_AC;
>> +    if (wait_for_prog_done)
>> +        cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
>> +    else
>> +        cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
> 
> is this typo here? did you mean MMC_RSP_SPI_R1  | MMC_RSP_R1 ??
>> +
>>       cmd.cmd_timeout_ms = timeout_ms;
>>
>>       err = mmc_wait_for_cmd(card->host,&cmd, MMC_CMD_RETRIES);
>>       if (err)
>>           return err;
>>
>> +    /* No need to check card status in case of BKOPS switch*/
>> +    if (index == EXT_CSD_BKOPS_START)
>> +        return 0;
>> +
>>       /* Must check status to be sure of no errors */
>>       do {
>>           err = mmc_send_status(card,&status);
>> @@ -423,6 +435,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>>
>>       return 0;
>>   }
>> +EXPORT_SYMBOL_GPL(__mmc_switch);
>> +
>> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +        unsigned int timeout_ms)
>> +{
>> +    return __mmc_switch(card, set, index, value, timeout_ms, 1);
>> +}
>>   EXPORT_SYMBOL_GPL(mmc_switch);
>>
>>   int mmc_send_status(struct mmc_card *card, u32 *status)
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index d76513b..50b3539 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
>>       bool            hpi_en;            /* HPI enablebit */
>>       bool            hpi;            /* HPI support bit */
>>       unsigned int        hpi_cmd;        /* cmd used as HPI */
>> +    bool            bkops;        /* background support bit */
>> +    bool            bkops_en;    /* background enable bit */
>>       unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>>       unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size */
>>       unsigned int        boot_ro_lock;        /* ro lock support */
>>       bool            boot_ro_lockable;
>> +    u8            raw_exception_status;    /* 53 */
>>       u8            raw_partition_support;    /* 160 */
>>       u8            raw_erased_mem_count;    /* 181 */
>>       u8            raw_ext_csd_structure;    /* 194 */
>> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
>>       u8            raw_sec_erase_mult;    /* 230 */
>>       u8            raw_sec_feature_support;/* 231 */
>>       u8            raw_trim_mult;        /* 232 */
>> +    u8            raw_bkops_status;    /* 246 */
>>       u8            raw_sectors[4];        /* 212 - 4 bytes */
>>
>>       unsigned int            feature_support;
>> @@ -225,6 +229,9 @@ struct mmc_card {
>>   #define MMC_CARD_REMOVED    (1<<7)        /* card has been removed */
>>   #define MMC_STATE_HIGHSPEED_200    (1<<8)        /* card is in HS200 mode */
>>   #define MMC_STATE_SLEEP        (1<<9)        /* card is in sleep state */
>> +#define MMC_STATE_NEED_BKOPS    (1<<10)        /* card need to do BKOPS */
>> +#define MMC_STATE_DOING_BKOPS    (1<<11)        /* card is doing BKOPS */
>> +#define MMC_STATE_CHECK_BKOPS    (1<<12)        /* card need to check BKOPS */
>>       unsigned int        quirks;     /* card quirks */
>>   #define MMC_QUIRK_LENIENT_FN0    (1<<0)        /* allow SDIO FN0 writes outside of the VS CCCR range */
>>   #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)    /* use func->cur_blksize */
>> @@ -391,6 +398,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>>   #define mmc_card_ext_capacity(c) ((c)->state&  MMC_CARD_SDXC)
>>   #define mmc_card_removed(c)    ((c)&&  ((c)->state&  MMC_CARD_REMOVED))
>>   #define mmc_card_is_sleep(c)    ((c)->state&  MMC_STATE_SLEEP)
>> +#define mmc_card_need_bkops(c)    ((c)->state&  MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_doing_bkops(c)    ((c)->state&  MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_check_bkops(c) ((c)->state&  MMC_STATE_CHECK_BKOPS)
>>
>>   #define mmc_card_set_present(c)    ((c)->state |= MMC_STATE_PRESENT)
>>   #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
>> @@ -403,7 +413,13 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>>   #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
>>   #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
>>   #define mmc_card_set_sleep(c)    ((c)->state |= MMC_STATE_SLEEP)
>> +#define mmc_card_set_need_bkops(c)    ((c)->state |= MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_set_doing_bkops(c)    ((c)->state |= MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_set_check_bkops(c) ((c)->state |= MMC_STATE_CHECK_BKOPS)
>>
>> +#define mmc_card_clr_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_check_bkops(c) ((c)->state&= ~MMC_STATE_CHECK_BKOPS)
>>   #define mmc_card_clr_sleep(c)    ((c)->state&= ~MMC_STATE_SLEEP)
>>   /*
>>    * Quirk add/remove for MMC products.
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 1b431c7..bda3d86 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -134,6 +134,9 @@ struct mmc_host;
>>   struct mmc_card;
>>   struct mmc_async_req;
>>
>> +extern int mmc_stop_bkops(struct mmc_card *);
>> +extern int mmc_read_bkops_status(struct mmc_card *);
>> +extern int mmc_is_exception_event(struct mmc_card *, unsigned int);
>>   extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>>                          struct mmc_async_req *, int *);
>>   extern int mmc_interrupt_hpi(struct mmc_card *);
>> @@ -142,6 +145,8 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
>>   extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
>>   extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>>       struct mmc_command *, int);
>> +extern void mmc_start_bkops(struct mmc_card *card);
>> +extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
>>   extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>>
>>   #define MMC_ERASE_ARG        0x00000000
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index 0707d22..d23caf2 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -238,6 +238,8 @@ struct mmc_host {
>>   #define MMC_CAP2_BROKEN_VOLTAGE    (1<<  7)    /* Use the broken voltage */
>>   #define MMC_CAP2_DETECT_ON_ERR    (1<<  8)    /* On I/O err check card removal */
>>   #define MMC_CAP2_HC_ERASE_SZ    (1<<  9)    /* High-capacity erase size */
>> +#define MMC_CAP2_INIT_BKOPS    (1<<  10)    /* To enable BKOPS */
>> +#define MMC_CAP2_BKOPS        (1<<  11)    /* BKOPS supported */
>>
>>       mmc_pm_flag_t        pm_caps;    /* supported pm features */
>>       unsigned int        power_notify_type;
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> index d425cab..80cfbf1 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -139,6 +139,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_EXCEPTION_EVENT    (1<<  6)    /* sx, a */
>>   #define R1_APP_CMD        (1<<  5)    /* sr, c */
>>
>>   #define R1_STATE_IDLE    0
>> @@ -274,12 +275,15 @@ struct _mmc_csd {
>>   #define EXT_CSD_FLUSH_CACHE        32      /* W */
>>   #define EXT_CSD_CACHE_CTRL        33      /* R/W */
>>   #define EXT_CSD_POWER_OFF_NOTIFICATION    34    /* R/W */
>> +#define EXT_CSD_EXCEPTION_STATUS    54    /* RO */
>>   #define EXT_CSD_DATA_SECTOR_SIZE    61    /* R */
>>   #define EXT_CSD_GP_SIZE_MULT        143    /* R/W */
>>   #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_RST_N_FUNCTION        162    /* R/W */
>> +#define EXT_CSD_BKOPS_EN        163    /* R/W */
>> +#define EXT_CSD_BKOPS_START        164    /* W */
>>   #define EXT_CSD_SANITIZE_START        165     /* W */
>>   #define EXT_CSD_WR_REL_PARAM        166    /* RO */
>>   #define EXT_CSD_BOOT_WP            173    /* R/W */
>> @@ -313,11 +317,13 @@ struct _mmc_csd {
>>   #define EXT_CSD_PWR_CL_200_360        237    /* RO */
>>   #define EXT_CSD_PWR_CL_DDR_52_195    238    /* RO */
>>   #define EXT_CSD_PWR_CL_DDR_52_360    239    /* RO */
>> +#define EXT_CSD_BKOPS_STATUS        246    /* RO */
>>   #define EXT_CSD_POWER_OFF_LONG_TIME    247    /* RO */
>>   #define EXT_CSD_GENERIC_CMD6_TIME    248    /* RO */
>>   #define EXT_CSD_CACHE_SIZE        249    /* RO, 4 bytes */
>>   #define EXT_CSD_TAG_UNIT_SIZE        498    /* RO */
>>   #define EXT_CSD_DATA_TAG_SUPPORT    499    /* RO */
>> +#define EXT_CSD_BKOPS_SUPPORT        502    /* RO */
>>   #define EXT_CSD_HPI_FEATURES        503    /* RO */
>>
>>   /*
>> @@ -386,4 +392,17 @@ struct _mmc_csd {
>>   #define MMC_SWITCH_MODE_CLEAR_BITS    0x02    /* Clear bits which are 1 in value */
>>   #define MMC_SWITCH_MODE_WRITE_BYTE    0x03    /* Set target to value */
>>
>> +/*
>> + * BKOPS status level
>> + */
>> +#define EXT_CSD_BKOPS_LEVEL_2        0x2
>> +
>> +/*
>> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)
>> + */
>> +#define EXT_CSD_URGENT_BKOPS        BIT(0)
>> +#define EXT_CSD_DYNCAP_NEEDED        BIT(1)
>> +#define EXT_CSD_SYSPOOL_EXHAUSTED    BIT(2)
>> +#define EXT_CSD_PACKED_FAILURE        BIT(3)
>> +
>>   #endif /* LINUX_MMC_MMC_H */
> 
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-13 13:53 ` S, Venkatraman
  2012-06-14  8:44   ` merez
@ 2012-06-15  4:29   ` Jaehoon Chung
  1 sibling, 0 replies; 23+ messages in thread
From: Jaehoon Chung @ 2012-06-15  4:29 UTC (permalink / raw)
  To: S, Venkatraman
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, Saugata Das,
	Konstantin Dorfman, Adrian Hunter, Maya Erez, Ulf Hansson

On 06/13/2012 10:53 PM, S, Venkatraman wrote:

> On Fri, Jun 8, 2012 at 10:09 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> Enable eMMC background operations (BKOPS) feature.
>>
>> If URGENT_BKOPS is set after a response, note that BKOPS
>> are required. After all I/O requests are finished, run
>> BKOPS if required. Should read/write operations be requested
>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>> and then service the request.
>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>> the BKOPS-STATUS vaule.
>>
>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>> And if you want to set the BKOPS_EN bit in ext_csd register,
>> use the MMC_CAP2_INIT_BKOPS.
>>
>> Future considerations
>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>  * Interrupt ongoing BKOPS before powering off the card.
>>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>> Signed-off-by: Maya Erez <merez@codeaurora.org>
>> ---
>> Changelog V9:
>>        - Rebased on patch-v7.
>>        - Added Konstantin and Maya's patch
>>                : mmc:core: Define synchronous BKOPS timeout
>>                : mmc:core: eMMC4.5 BKOPS fixes
>>        - Removed periodic bkops
>>                : This feature will do in future work
>>        - Add __mmc_switch() for waiting_for_prod_done.
>>
> Jaehoon,
>   (I've been trying to sync up with you offline - but our timings dont
> seem to match.)
> In essence, you've fixed most of the issue, except for the need for
> periodic BKOPS. In my view, BKOPS should have little or no overhead
> during read writes. Here is the simple workflow that I think of (and
> was discussed with some experts during Linaro connect in HK)

Hi Venkat,
Sorry for late reply..

> 
> 1) Check the status of BKOPS need when the queue has been idle for,
> say, ~5 seconds

Is every 5 seconds? 

> 2) If it's >0, start BKOPS
> 3) If a request arrives during the above execution of BKOPS, issue HPI
> and continue with the request
> 4) Else let it run indefinitely
> 
> There should be no need or procedure to check BKOPS during transactions.
> If this is done, in normal circumstances, the card wouldn't need to
> set URGENT_BKOPS. Even if it is set, the above sequence would handle
> it gracefully.

I understood your proposal.
If we can use the periodic bkops, maybe the card didn't issue the URGENT_BKOPS.
Because, card is already maintain at idle time.

As i mentioned, we can merge the basic bkops feature, then can add the periodic bkops in future.
So i removed the periodic bkops feature at v9. i will separate them.

Best Regards,
Jaehoon Chung

> Apart from this, I have just one clarification required on the implementation.
> See below..
> 
>> Changelog V8:
>>        - Remove host->lock spin lock reviewed by Adrian
>>        - Support periodic start bkops
>>        - when bkops_status is level-3, if timeout is set to 0, send hpi.
>>        - Move the start-bkops point
>> Changelog V7:
>>        - Use HPI command when issued URGENT_BKOPS
>>        - Recheck until clearing the bkops-status bit.
>> Changelog V6:
>>        - Add the flag of check-bkops-status.
>>          (For fixing async_req problem)
>>        - Add the capability for MMC_CAP2_INIT_BKOPS.
>>          (When unset the bkops_en bit in ext_csd register)
>>        - modify the wrong condition.
>> Changelog V5:
>>        - Rebase based on the latest mmc-next.
>>        - modify codes based-on Chris's comment
>> Changelog V4:
>>        - Add mmc_read_bkops_status
>>        - When URGENT_BKOPS(level2-3), didn't use HPI command.
>>        - In mmc_switch(), use R1B/R1 according to level.
>> Changelog V3:
>>        - move the bkops setting's location in mmc_blk_issue_rw_rq
>>        - modify condition checking
>>        - bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
>>        - remove the unused code
>>        - change pr_debug instead of pr_warn in mmc_send_hpi_cmd
>>        - Add the Future consideration suggested by Per
>> Changelog V2:
>>        - Use EXCEPTION_STATUS instead of URGENT_BKOPS
>>        - Add function to check Exception_status(for eMMC4.5)
>> ---
>>  drivers/mmc/card/queue.c   |    2 +
>>  drivers/mmc/core/core.c    |  161 +++++++++++++++++++++++++++++++++++++++++++-
>>  drivers/mmc/core/mmc.c     |   18 +++++
>>  drivers/mmc/core/mmc_ops.c |   27 ++++++-
>>  include/linux/mmc/card.h   |   16 +++++
>>  include/linux/mmc/core.h   |    5 ++
>>  include/linux/mmc/host.h   |    2 +
>>  include/linux/mmc/mmc.h    |   19 +++++
>>  8 files changed, 243 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index e360a97..e4a2cde 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)
>>                spin_unlock_irq(q->queue_lock);
>>
>>                if (req || mq->mqrq_prev->req) {
>> +                       if (mmc_card_doing_bkops(mq->card))
>> +                               mmc_stop_bkops(mq->card);
>>                        set_current_state(TASK_RUNNING);
>>                        mq->issue_fn(mq, req);
>>                } else {
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 0b6141d..9adb004 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -41,6 +41,12 @@
>>  #include "sd_ops.h"
>>  #include "sdio_ops.h"
>>
>> +/*
>> + * The Background operation can take a long time, depends on the house keeping
>> + * operations the card has to peform
>> + */
>> +#define MMC_BKOPS_MAX_TIMEOUT  (4 * 80 * 1000) /* max time to wait in ms */
>> +
>>  static struct workqueue_struct *workqueue;
>>  static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
>>
>> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
>>                if (mrq->done)
>>                        mrq->done(mrq);
>>
>> +               /*
>> +                * Check BKOPS urgency from each R1 response
>> +                */
>> +               if (host->card && mmc_card_mmc(host->card) &&
>> +                       ((mmc_resp_type(cmd) == MMC_RSP_R1) ||
>> +                        (mmc_resp_type(cmd) == MMC_RSP_R1B)) &&
>> +                       (cmd->resp[0] & R1_EXCEPTION_EVENT)) {
>> +                       mmc_card_set_check_bkops(host->card);
>> +               }
>> +
>>                mmc_host_clk_release(host);
>>        }
>>  }
>> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
>>        host->ops->request(host, mrq);
>>  }
>>
>> +/**
>> + *     mmc_start_bkops - start BKOPS for supported cards
>> + *     @card: MMC card to start BKOPS
>> + *
>> + *     Start background operations whenever requested.
>> + *     when the urgent BKOPS bit is set in a R1 command response
>> + *     then background operations should be started immediately.
>> +*/
>> +void mmc_start_bkops(struct mmc_card *card)
>> +{
>> +       int err;
>> +       int timeout;
>> +       u8 wait_for_prog_done;
>> +
>> +       BUG_ON(!card);
>> +       if (!card->ext_csd.bkops_en || !(card->host->caps2 & MMC_CAP2_BKOPS))
>> +               return;
>> +
>> +       if (mmc_card_check_bkops(card)) {
>> +               mmc_card_clr_check_bkops(card);
>> +               if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
>> +                       if (card->ext_csd.raw_bkops_status)
>> +                               mmc_card_set_need_bkops(card);
>> +       }
>> +
>> +       /*
>> +        * If card is already doing bkops or need for
>> +        * bkops flag is not set, then do nothing just
>> +        * return
>> +        */
>> +       if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
>> +               return;
>> +
>> +       mmc_claim_host(card->host);
>> +       if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
>> +               timeout = MMC_BKOPS_MAX_TIMEOUT;
> 
> I don't think the timeout should be any different for BKOPS_START.
> IIUC, the CMD to set BKOPS_START would return immediately, and BKOPS
> will continue in the background. So the time to actually do the device
> management is not applicable to the CMD execution time..

Konstantin or Maya will reply for this point.

> 
>> +               wait_for_prog_done = 1;
>> +       } else {
>> +               timeout = 0;
>> +               wait_for_prog_done = 0;
>> +       }
>> +
>> +       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                       EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
>> +       if (err) {
>> +               pr_warn("%s: error %d starting bkops\n",
>> +                          mmc_hostname(card->host), err);
>> +               mmc_card_clr_need_bkops(card);
>> +               goto out;
>> +       }
>> +
>> +       mmc_card_clr_need_bkops(card);
>> +
>> +       /*
>> +        * For urgent bkops status (LEVEL_2 and more)
>> +        * bkops executed synchronously, otherwise
>> +        * the operation is in progress
>> +        */
>> +       if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2)
>> +               mmc_card_set_check_bkops(card);
>> +       else
>> +               mmc_card_set_doing_bkops(card);
>> +
>> +out:
>> +       mmc_release_host(card->host);
>> +}
>> +EXPORT_SYMBOL(mmc_start_bkops);
>> +
>>  static void mmc_wait_done(struct mmc_request *mrq)
>>  {
>>        complete(&mrq->completion);
>> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
>>        if (!err && areq)
>>                start_err = __mmc_start_req(host, areq->mrq);
>>
>> -       if (host->areq)
>> +       if (host->areq) {
>> +               if (!areq && host->areq && mmc_card_mmc(host->card))
>> +                       mmc_start_bkops(host->card);
>>                mmc_post_req(host, host->areq->mrq, 0);
>> +       }
>>
>>         /* Cancel a prepared request if it was not started. */
>>        if ((err || start_err) && areq)
>> -                       mmc_post_req(host, areq->mrq, -EINVAL);
>> +               mmc_post_req(host, areq->mrq, -EINVAL);
>>
>>        if (err)
>>                host->areq = NULL;
>> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
>>  EXPORT_SYMBOL(mmc_wait_for_cmd);
>>
>>  /**
>> + *     mmc_stop_bkops - stop ongoing BKOPS
>> + *     @card: MMC card to check BKOPS
>> + *
>> + *     Send HPI command to stop ongoing background operations,
>> + *     to allow rapid servicing of foreground operations,e.g. read/
>> + *     writes. Wait until the card comes out of the programming state
>> + *     to avoid errors in servicing read/write requests.
>> + */
>> +int mmc_stop_bkops(struct mmc_card *card)
>> +{
>> +       int err = 0;
>> +
>> +       BUG_ON(!card);
>> +
>> +       err = mmc_interrupt_hpi(card);
>> +
>> +       mmc_card_clr_doing_bkops(card);
>> +
>> +       return err;
>> +}
>> +EXPORT_SYMBOL(mmc_stop_bkops);
>> +
>> +int mmc_read_bkops_status(struct mmc_card *card)
>> +{
>> +       int err;
>> +       u8 ext_csd[512];
>> +
>> +       mmc_claim_host(card->host);
>> +       err = mmc_send_ext_csd(card, ext_csd);
>> +       mmc_release_host(card->host);
>> +       if (err)
>> +               return err;
>> +
>> +       card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
>> +       card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXCEPTION_STATUS];
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(mmc_read_bkops_status);
>> +
>> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
>> +{
>> +       int err;
>> +
>> +       err = mmc_read_bkops_status(card);
>> +       if (err) {
>> +               pr_err("%s: Didn't read bkops status : %d\n",
>> +                      mmc_hostname(card->host), err);
>> +               return 0;
>> +       }
>> +
>> +       /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
>> +       if (card->ext_csd.rev == 5)
>> +               return 1;
>> +
>> +       return (card->ext_csd.raw_exception_status & value) ? 1 : 0;
>> +}
>> +EXPORT_SYMBOL(mmc_is_exception_event);
>> +
>> +/**
>>  *     mmc_set_data_timeout - set the timeout for a data command
>>  *     @data: data phase for command
>>  *     @card: the MMC card associated with the data transfer
>> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
>>        mmc_bus_get(host);
>>        if (host->bus_ops && !host->bus_dead) {
>>
>> -               if (host->bus_ops->suspend)
>> +               if (host->bus_ops->suspend) {
>> +                       if (mmc_card_doing_bkops(host->card))
>> +                               mmc_stop_bkops(host->card);
>>                        err = host->bus_ops->suspend(host);
>> +               }
>>
>>                if (err == -ENOSYS || !host->bus_ops->resume) {
>>                        /*
>> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
>>        switch (mode) {
>>        case PM_HIBERNATION_PREPARE:
>>        case PM_SUSPEND_PREPARE:
>> +               if (host->card && mmc_card_mmc(host->card) &&
>> +                               mmc_card_doing_bkops(host->card)) {
>> +                       mmc_interrupt_hpi(host->card);
>> +                       mmc_card_clr_doing_bkops(host->card);
>> +               }
>>
>>                spin_lock_irqsave(&host->lock, flags);
>>                host->rescan_disable = 1;
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 258b203..9e036e5 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
>>        }
>>
>>        if (card->ext_csd.rev >= 5) {
>> +               /* check whether the eMMC card support BKOPS */
>> +               if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
>> +                       card->ext_csd.bkops = 1;
>> +                       card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
>> +                       card->ext_csd.raw_bkops_status =
>> +                               ext_csd[EXT_CSD_BKOPS_STATUS];
>> +                       if (!card->ext_csd.bkops_en &&
>> +                               card->host->caps2 & MMC_CAP2_INIT_BKOPS) {
>> +                               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                                       EXT_CSD_BKOPS_EN, 1, 0);
>> +                               if (err)
>> +                                       pr_warn("%s: Enabling BKOPS failed\n",
>> +                                               mmc_hostname(card->host));
>> +                               else
>> +                                       card->ext_csd.bkops_en = 1;
>> +                       }
>> +               }
>> +
>>                /* check whether the eMMC card supports HPI */
>>                if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
>>                        card->ext_csd.hpi = 1;
>> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
>> index 69370f4..f0bebd9 100644
>> --- a/drivers/mmc/core/mmc_ops.c
>> +++ b/drivers/mmc/core/mmc_ops.c
>> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
>>  }
>>
>>  /**
>> - *     mmc_switch - modify EXT_CSD register
>> + *     __mmc_switch - modify EXT_CSD register
>>  *     @card: the MMC card associated with the data transfer
>>  *     @set: cmd set values
>>  *     @index: EXT_CSD register index
>>  *     @value: value to program into EXT_CSD register
>>  *     @timeout_ms: timeout (ms) for operation performed by register write,
>>  *                   timeout of zero implies maximum possible timeout
>> + *      @wait_for_prod_done: is waiting for program done
>>  *
>>  *     Modifies the EXT_CSD register for selected card.
>>  */
>> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> -              unsigned int timeout_ms)
>> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +              unsigned int timeout_ms, u8 wait_for_prog_done)
>> +
>>  {
>>        int err;
>>        struct mmc_command cmd = {0};
>> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>>                  (index << 16) |
>>                  (value << 8) |
>>                  set;
>> -       cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
>> +
>> +       cmd.flags = MMC_CMD_AC;
>> +       if (wait_for_prog_done)
>> +               cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
>> +       else
>> +               cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
>> +
>>        cmd.cmd_timeout_ms = timeout_ms;
>>
>>        err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
>>        if (err)
>>                return err;
>>
>> +       /* No need to check card status in case of BKOPS switch*/
>> +       if (index == EXT_CSD_BKOPS_START)
>> +               return 0;
>> +
>>        /* Must check status to be sure of no errors */
>>        do {
>>                err = mmc_send_status(card, &status);
>> @@ -423,6 +435,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>>
>>        return 0;
>>  }
>> +EXPORT_SYMBOL_GPL(__mmc_switch);
>> +
>> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>> +               unsigned int timeout_ms)
>> +{
>> +       return __mmc_switch(card, set, index, value, timeout_ms, 1);
>> +}
>>  EXPORT_SYMBOL_GPL(mmc_switch);
>>
>>  int mmc_send_status(struct mmc_card *card, u32 *status)
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index d76513b..50b3539 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
>>        bool                    hpi_en;                 /* HPI enablebit */
>>        bool                    hpi;                    /* HPI support bit */
>>        unsigned int            hpi_cmd;                /* cmd used as HPI */
>> +       bool                    bkops;          /* background support bit */
>> +       bool                    bkops_en;       /* background enable bit */
>>        unsigned int            data_sector_size;       /* 512 bytes or 4KB */
>>        unsigned int            data_tag_unit_size;     /* DATA TAG UNIT size */
>>        unsigned int            boot_ro_lock;           /* ro lock support */
>>        bool                    boot_ro_lockable;
>> +       u8                      raw_exception_status;   /* 53 */
>>        u8                      raw_partition_support;  /* 160 */
>>        u8                      raw_erased_mem_count;   /* 181 */
>>        u8                      raw_ext_csd_structure;  /* 194 */
>> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
>>        u8                      raw_sec_erase_mult;     /* 230 */
>>        u8                      raw_sec_feature_support;/* 231 */
>>        u8                      raw_trim_mult;          /* 232 */
>> +       u8                      raw_bkops_status;       /* 246 */
>>        u8                      raw_sectors[4];         /* 212 - 4 bytes */
>>
>>        unsigned int            feature_support;
>> @@ -225,6 +229,9 @@ struct mmc_card {
>>  #define MMC_CARD_REMOVED       (1<<7)          /* card has been removed */
>>  #define MMC_STATE_HIGHSPEED_200        (1<<8)          /* card is in HS200 mode */
>>  #define MMC_STATE_SLEEP                (1<<9)          /* card is in sleep state */
>> +#define MMC_STATE_NEED_BKOPS   (1<<10)         /* card need to do BKOPS */
>> +#define MMC_STATE_DOING_BKOPS  (1<<11)         /* card is doing BKOPS */
>> +#define MMC_STATE_CHECK_BKOPS  (1<<12)         /* card need to check BKOPS */
>>        unsigned int            quirks;         /* card quirks */
>>  #define MMC_QUIRK_LENIENT_FN0  (1<<0)          /* allow SDIO FN0 writes outside of the VS CCCR range */
>>  #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)   /* use func->cur_blksize */
>> @@ -391,6 +398,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>>  #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
>>  #define mmc_card_removed(c)    ((c) && ((c)->state & MMC_CARD_REMOVED))
>>  #define mmc_card_is_sleep(c)   ((c)->state & MMC_STATE_SLEEP)
>> +#define mmc_card_need_bkops(c) ((c)->state & MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_doing_bkops(c)        ((c)->state & MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_check_bkops(c) ((c)->state & MMC_STATE_CHECK_BKOPS)
>>
>>  #define mmc_card_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
>>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
>> @@ -403,7 +413,13 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>>  #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
>>  #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
>>  #define mmc_card_set_sleep(c)  ((c)->state |= MMC_STATE_SLEEP)
>> +#define mmc_card_set_need_bkops(c)     ((c)->state |= MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_set_doing_bkops(c)    ((c)->state |= MMC_STATE_DOING_BKOPS)
>> +#define mmc_card_set_check_bkops(c) ((c)->state |= MMC_STATE_CHECK_BKOPS)
>>
>> +#define mmc_card_clr_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_check_bkops(c) ((c)->state &= ~MMC_STATE_CHECK_BKOPS)
>>  #define mmc_card_clr_sleep(c)  ((c)->state &= ~MMC_STATE_SLEEP)
>>  /*
>>  * Quirk add/remove for MMC products.
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 1b431c7..bda3d86 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -134,6 +134,9 @@ struct mmc_host;
>>  struct mmc_card;
>>  struct mmc_async_req;
>>
>> +extern int mmc_stop_bkops(struct mmc_card *);
>> +extern int mmc_read_bkops_status(struct mmc_card *);
>> +extern int mmc_is_exception_event(struct mmc_card *, unsigned int);
>>  extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
>>                                           struct mmc_async_req *, int *);
>>  extern int mmc_interrupt_hpi(struct mmc_card *);
>> @@ -142,6 +145,8 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
>>  extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
>>  extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
>>        struct mmc_command *, int);
>> +extern void mmc_start_bkops(struct mmc_card *card);
>> +extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
>>  extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
>>
>>  #define MMC_ERASE_ARG          0x00000000
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index 0707d22..d23caf2 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -238,6 +238,8 @@ struct mmc_host {
>>  #define MMC_CAP2_BROKEN_VOLTAGE        (1 << 7)        /* Use the broken voltage */
>>  #define MMC_CAP2_DETECT_ON_ERR (1 << 8)        /* On I/O err check card removal */
>>  #define MMC_CAP2_HC_ERASE_SZ   (1 << 9)        /* High-capacity erase size */
>> +#define MMC_CAP2_INIT_BKOPS    (1 << 10)       /* To enable BKOPS */
>> +#define MMC_CAP2_BKOPS         (1 << 11)       /* BKOPS supported */
>>
>>        mmc_pm_flag_t           pm_caps;        /* supported pm features */
>>        unsigned int        power_notify_type;
>> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
>> index d425cab..80cfbf1 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -139,6 +139,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_EXCEPTION_EVENT     (1 << 6)        /* sx, a */
>>  #define R1_APP_CMD             (1 << 5)        /* sr, c */
>>
>>  #define R1_STATE_IDLE  0
>> @@ -274,12 +275,15 @@ struct _mmc_csd {
>>  #define EXT_CSD_FLUSH_CACHE            32      /* W */
>>  #define EXT_CSD_CACHE_CTRL             33      /* R/W */
>>  #define EXT_CSD_POWER_OFF_NOTIFICATION 34      /* R/W */
>> +#define EXT_CSD_EXCEPTION_STATUS       54      /* RO */
>>  #define EXT_CSD_DATA_SECTOR_SIZE       61      /* R */
>>  #define EXT_CSD_GP_SIZE_MULT           143     /* R/W */
>>  #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_RST_N_FUNCTION         162     /* R/W */
>> +#define EXT_CSD_BKOPS_EN               163     /* R/W */
>> +#define EXT_CSD_BKOPS_START            164     /* W */
>>  #define EXT_CSD_SANITIZE_START         165     /* W */
>>  #define EXT_CSD_WR_REL_PARAM           166     /* RO */
>>  #define EXT_CSD_BOOT_WP                        173     /* R/W */
>> @@ -313,11 +317,13 @@ struct _mmc_csd {
>>  #define EXT_CSD_PWR_CL_200_360         237     /* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_195      238     /* RO */
>>  #define EXT_CSD_PWR_CL_DDR_52_360      239     /* RO */
>> +#define EXT_CSD_BKOPS_STATUS           246     /* RO */
>>  #define EXT_CSD_POWER_OFF_LONG_TIME    247     /* RO */
>>  #define EXT_CSD_GENERIC_CMD6_TIME      248     /* RO */
>>  #define EXT_CSD_CACHE_SIZE             249     /* RO, 4 bytes */
>>  #define EXT_CSD_TAG_UNIT_SIZE          498     /* RO */
>>  #define EXT_CSD_DATA_TAG_SUPPORT       499     /* RO */
>> +#define EXT_CSD_BKOPS_SUPPORT          502     /* RO */
>>  #define EXT_CSD_HPI_FEATURES           503     /* RO */
>>
>>  /*
>> @@ -386,4 +392,17 @@ struct _mmc_csd {
>>  #define MMC_SWITCH_MODE_CLEAR_BITS     0x02    /* Clear bits which are 1 in value */
>>  #define MMC_SWITCH_MODE_WRITE_BYTE     0x03    /* Set target to value */
>>
>> +/*
>> + * BKOPS status level
>> + */
>> +#define EXT_CSD_BKOPS_LEVEL_2          0x2
>> +
>> +/*
>> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)
>> + */
>> +#define EXT_CSD_URGENT_BKOPS           BIT(0)
>> +#define EXT_CSD_DYNCAP_NEEDED          BIT(1)
>> +#define EXT_CSD_SYSPOOL_EXHAUSTED      BIT(2)
>> +#define EXT_CSD_PACKED_FAILURE         BIT(3)
>> +
>>  #endif /* LINUX_MMC_MMC_H */
>> --
>> 1.7.4.1
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-15  4:19   ` Jaehoon Chung
@ 2012-06-15  6:10     ` Subhash Jadavani
  0 siblings, 0 replies; 23+ messages in thread
From: Subhash Jadavani @ 2012-06-15  6:10 UTC (permalink / raw)
  To: 'Jaehoon Chung'
  Cc: 'linux-mmc', 'Chris Ball',
	'Kyungmin Park', 'Hanumath Prasad',
	'Per FORLIN', 'Sebastian Rasmussen',
	'Dong, Chuanxiao', svenkatr, 'Saugata Das',
	'Konstantin Dorfman', 'Adrian Hunter',
	'Maya Erez', 'Ulf Hansson'

Hi Jaehoon,

> -----Original Message-----
> From: Jaehoon Chung [mailto:jh80.chung@samsung.com]
> Sent: Friday, June 15, 2012 9:50 AM
> To: Subhash Jadavani
> Cc: linux-mmc; Chris Ball; Kyungmin Park; Hanumath Prasad; Per FORLIN;
> Sebastian Rasmussen; Dong, Chuanxiao; svenkatr@ti.com; Saugata Das;
> Konstantin Dorfman; Adrian Hunter; Maya Erez; Ulf Hansson
> Subject: Re: [PATCH v9] mmc: support BKOPS feature for eMMC
> 
> Hi Subhash,
> 
> On 06/13/2012 09:45 PM, Subhash Jadavani wrote:
> 
> > Hi Jaehoon,
> >
> > Find few comments inline below. Also, periodic BKOPs support was present
in
> v8 but now you have removed it in v9. Is there any reason for it?
> 
> Yes, there is the periodic bkops in v-8. But, i have discussed this with
Konstantin
> and Maya.
> 1) Submit Basic BKOPS in priority work.
> 2) after (1), we can add the periodic bkops.
> The periodic bkops also need for eMMC's maintenance. but First, we want to
> merge the basic bkops feature in mainline.
> The Periodic bkops should be other patch.

I am fine with having periodic BKOPS support as part of separate patch.
Thanks.

Regards,
Subhash

> 
> >
> > Regards,
> > Subhash
> >
> > On 6/8/2012 10:09 AM, Jaehoon Chung wrote:
> >> Enable eMMC background operations (BKOPS) feature.
> >>
> >> If URGENT_BKOPS is set after a response, note that BKOPS are
> >> required. After all I/O requests are finished, run BKOPS if required.
> >> Should read/write operations be requested during BKOPS, first issue
> >> HPI to interrupt the ongoing BKOPS and then service the request.
> >> If BKOPS-STATUS is upper than LEVEL2, need to check until clear the
> >> BKOPS-STATUS vaule.
> >>
> >> If you want to enable this feature, set MMC_CAP2_BKOPS.
> >> And if you want to set the BKOPS_EN bit in ext_csd register, use the
> >> MMC_CAP2_INIT_BKOPS.
> >>
> >> Future considerations
> >>   * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
> >>   * Interrupt ongoing BKOPS before powering off the card.
> >>   * How get BKOPS_STATUS value.(periodically send ext_csd command?)
> >>
> >> Signed-off-by: Jaehoon Chung<jh80.chung@samsung.com>
> >> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>
> >> Signed-off-by: Konstantin Dorfman<kdorfman@codeaurora.org>
> >> Signed-off-by: Maya Erez<merez@codeaurora.org>
> >> ---
> >> Changelog V9:
> >>     - Rebased on patch-v7.
> >>     - Added Konstantin and Maya's patch
> >>         : mmc:core: Define synchronous BKOPS timeout
> >>         : mmc:core: eMMC4.5 BKOPS fixes
> >>     - Removed periodic bkops
> >>         : This feature will do in future work
> >>     - Add __mmc_switch() for waiting_for_prod_done.
> >>
> >> Changelog V8:
> >>     - Remove host->lock spin lock reviewed by Adrian
> >>     - Support periodic start bkops
> >>     - when bkops_status is level-3, if timeout is set to 0, send hpi.
> >>     - Move the start-bkops point
> >> Changelog V7:
> >>     - Use HPI command when issued URGENT_BKOPS
> >>     - Recheck until clearing the bkops-status bit.
> >> Changelog V6:
> >>     - Add the flag of check-bkops-status.
> >>       (For fixing async_req problem)
> >>     - Add the capability for MMC_CAP2_INIT_BKOPS.
> >>       (When unset the bkops_en bit in ext_csd register)
> >>     - modify the wrong condition.
> >> Changelog V5:
> >>     - Rebase based on the latest mmc-next.
> >>     - modify codes based-on Chris's comment Changelog V4:
> >>     - Add mmc_read_bkops_status
> >>     - When URGENT_BKOPS(level2-3), didn't use HPI command.
> >>     - In mmc_switch(), use R1B/R1 according to level.
> >> Changelog V3:
> >>     - move the bkops setting's location in mmc_blk_issue_rw_rq
> >>     - modify condition checking
> >>     - bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
> >>     - remove the unused code
> >>     - change pr_debug instead of pr_warn in mmc_send_hpi_cmd
> >>     - Add the Future consideration suggested by Per Changelog V2:
> >>     - Use EXCEPTION_STATUS instead of URGENT_BKOPS
> >>     - Add function to check Exception_status(for eMMC4.5)
> >> ---
> >>   drivers/mmc/card/queue.c   |    2 +
> >>   drivers/mmc/core/core.c    |  161
> +++++++++++++++++++++++++++++++++++++++++++-
> >>   drivers/mmc/core/mmc.c     |   18 +++++
> >>   drivers/mmc/core/mmc_ops.c |   27 ++++++-
> >>   include/linux/mmc/card.h   |   16 +++++
> >>   include/linux/mmc/core.h   |    5 ++
> >>   include/linux/mmc/host.h   |    2 +
> >>   include/linux/mmc/mmc.h    |   19 +++++
> >>   8 files changed, 243 insertions(+), 7 deletions(-)
> >>
> >> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> >> index e360a97..e4a2cde 100644
> >> --- a/drivers/mmc/card/queue.c
> >> +++ b/drivers/mmc/card/queue.c
> >> @@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)
> >>           spin_unlock_irq(q->queue_lock);
> >>
> >>           if (req || mq->mqrq_prev->req) {
> >> +            if (mmc_card_doing_bkops(mq->card))
> >> +                mmc_stop_bkops(mq->card);
> > This is what we had discussed last time but i don't see that you are
> considering error returned by mmc_stop_bkops(). Any reason for it?
> > [Subhash] mmc_stop_bkops() may fail and can return an error but we are
> ignoring it here. What if this function has returned error which means
BKOPS
> may still be running in card and if you go ahead and issue another
read/write
> request?
> > [Jaehoon] Great..i didn't think that..I will consider this point.
Thanks.
> 
> Will do fix..
> 
> >
> >>               set_current_state(TASK_RUNNING);
> >>               mq->issue_fn(mq, req);
> >>           } else {
> >> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index
> >> 0b6141d..9adb004 100644
> >> --- a/drivers/mmc/core/core.c
> >> +++ b/drivers/mmc/core/core.c
> >> @@ -41,6 +41,12 @@
> >>   #include "sd_ops.h"
> >>   #include "sdio_ops.h"
> >>
> >> +/*
> >> + * The Background operation can take a long time, depends on the
> >> +house keeping
> >> + * operations the card has to peform  */
> >> +#define MMC_BKOPS_MAX_TIMEOUT    (4 * 80 * 1000) /* max time to
> wait in ms */
> >> +
> >>   static struct workqueue_struct *workqueue;
> >>   static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
> >>
> >> @@ -175,6 +181,16 @@ void mmc_request_done(struct mmc_host *host,
> struct mmc_request *mrq)
> >>           if (mrq->done)
> >>               mrq->done(mrq);
> >>
> >> +        /*
> >> +         * Check BKOPS urgency from each R1 response
> >> +         */
> >> +        if (host->card&&  mmc_card_mmc(host->card)&&
> >> +            ((mmc_resp_type(cmd) == MMC_RSP_R1) ||
> >> +             (mmc_resp_type(cmd) == MMC_RSP_R1B))&&
> >> +            (cmd->resp[0]&  R1_EXCEPTION_EVENT)) {
> >> +            mmc_card_set_check_bkops(host->card);
> >> +        }
> >> +
> >>           mmc_host_clk_release(host);
> >>       }
> >>   }
> >> @@ -245,6 +261,74 @@ mmc_start_request(struct mmc_host *host, struct
> mmc_request *mrq)
> >>       host->ops->request(host, mrq);
> >>   }
> >>
> >> +/**
> >> + *    mmc_start_bkops - start BKOPS for supported cards
> >> + *    @card: MMC card to start BKOPS
> >> + *
> >> + *    Start background operations whenever requested.
> >> + *    when the urgent BKOPS bit is set in a R1 command response
> >> + *    then background operations should be started immediately.
> >> +*/
> >> +void mmc_start_bkops(struct mmc_card *card) {
> >> +    int err;
> >> +    int timeout;
> >> +    u8 wait_for_prog_done;
> >> +
> >> +    BUG_ON(!card);
> >> +    if (!card->ext_csd.bkops_en || !(card->host->caps2&
> MMC_CAP2_BKOPS))
> >> +        return;
> >> +
> >> +    if (mmc_card_check_bkops(card)) {
> >> +        mmc_card_clr_check_bkops(card);
> >> +        if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
> >> +            if (card->ext_csd.raw_bkops_status)
> >> +                mmc_card_set_need_bkops(card);
> >> +    }
> >> +
> >> +    /*
> >> +     * If card is already doing bkops or need for
> >> +     * bkops flag is not set, then do nothing just
> >> +     * return
> >> +     */
> >> +    if (mmc_card_doing_bkops(card) || !mmc_card_need_bkops(card))
> >> +        return;
> >> +
> >> +    mmc_claim_host(card->host);
> >> +    if (card->ext_csd.raw_bkops_status>= EXT_CSD_BKOPS_LEVEL_2) {
> >> +        timeout = MMC_BKOPS_MAX_TIMEOUT;
> >> +        wait_for_prog_done = 1;
> >> +    } else {
> >> +        timeout = 0;
> >> +        wait_for_prog_done = 0;
> >> +    }
> >> +
> >> +    err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> +            EXT_CSD_BKOPS_START, 1, timeout, wait_for_prog_done);
> >> +    if (err) {
> >> +        pr_warn("%s: error %d starting bkops\n",
> >> +               mmc_hostname(card->host), err);
> >> +        mmc_card_clr_need_bkops(card);
> >> +        goto out;
> >> +    }
> >> +
> >> +    mmc_card_clr_need_bkops(card);
> >> +
> >> +    /*
> >> +     * For urgent bkops status (LEVEL_2 and more)
> >> +     * bkops executed synchronously, otherwise
> >> +     * the operation is in progress
> >> +     */
> >> +    if (card->ext_csd.raw_bkops_status>= EXT_CSD_BKOPS_LEVEL_2)
> >> +        mmc_card_set_check_bkops(card);
> >> +    else
> >> +        mmc_card_set_doing_bkops(card);
> >> +
> >> +out:
> >> +    mmc_release_host(card->host);
> >> +}
> >> +EXPORT_SYMBOL(mmc_start_bkops);
> >> +
> >>   static void mmc_wait_done(struct mmc_request *mrq)
> >>   {
> >>       complete(&mrq->completion);
> >> @@ -359,12 +443,15 @@ struct mmc_async_req *mmc_start_req(struct
> mmc_host *host,
> >>       if (!err&&  areq)
> >>           start_err = __mmc_start_req(host, areq->mrq);
> >>
> >> -    if (host->areq)
> >> +    if (host->areq) {
> >> +        if (!areq&&  host->areq&&  mmc_card_mmc(host->card))
> >> +            mmc_start_bkops(host->card);
> >>           mmc_post_req(host, host->areq->mrq, 0);
> >> +    }
> >>
> >>        /* Cancel a prepared request if it was not started. */
> >>       if ((err || start_err)&&  areq)
> >> -            mmc_post_req(host, areq->mrq, -EINVAL);
> >> +        mmc_post_req(host, areq->mrq, -EINVAL);
> >>
> >>       if (err)
> >>           host->areq = NULL;
> >> @@ -480,6 +567,66 @@ int mmc_wait_for_cmd(struct mmc_host *host,
> struct mmc_command *cmd, int retries
> >>   EXPORT_SYMBOL(mmc_wait_for_cmd);
> >>
> >>   /**
> >> + *    mmc_stop_bkops - stop ongoing BKOPS
> >> + *    @card: MMC card to check BKOPS
> >> + *
> >> + *    Send HPI command to stop ongoing background operations,
> >> + *    to allow rapid servicing of foreground operations,e.g. read/
> >> + *    writes. Wait until the card comes out of the programming state
> >> + *    to avoid errors in servicing read/write requests.
> >> + */
> >> +int mmc_stop_bkops(struct mmc_card *card) {
> >> +    int err = 0;
> >> +
> >> +    BUG_ON(!card);
> >> +
> >> +    err = mmc_interrupt_hpi(card);
> >> +
> >> +    mmc_card_clr_doing_bkops(card);
> >
> > Here even if mmc_interrupt_hpi() fails, you are clearing the DOING_BKOPS
> flag which is not correct. Which should clear this flag if HPI issued
successfully.
> 
> If hpi command is failed, we can consider two case.
> 1) bkops is already done.
> 2) send_command error.
> I will add the code handled them.
> 
> >
> >
> >
> >> +
> >> +    return err;
> >> +}
> >> +EXPORT_SYMBOL(mmc_stop_bkops);
> >> +
> >> +int mmc_read_bkops_status(struct mmc_card *card) {
> >> +    int err;
> >> +    u8 ext_csd[512];
> >> +
> >> +    mmc_claim_host(card->host);
> >> +    err = mmc_send_ext_csd(card, ext_csd);
> >> +    mmc_release_host(card->host);
> >> +    if (err)
> >> +        return err;
> >> +
> >> +    card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
> >> +    card->ext_csd.raw_exception_status =
> >> + ext_csd[EXT_CSD_EXCEPTION_STATUS];
> >> +
> >> +    return 0;
> >> +}
> >> +EXPORT_SYMBOL(mmc_read_bkops_status);
> >> +
> >> +int mmc_is_exception_event(struct mmc_card *card, unsigned int
> >> +value) {
> >> +    int err;
> >> +
> >> +    err = mmc_read_bkops_status(card);
> >> +    if (err) {
> >> +        pr_err("%s: Didn't read bkops status : %d\n",
> >> +               mmc_hostname(card->host), err);
> >> +        return 0;
> >> +    }
> >> +
> >> +    /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
> >> +    if (card->ext_csd.rev == 5)
> >> +        return 1;
> >> +
> >> +    return (card->ext_csd.raw_exception_status&  value) ? 1 : 0; }
> >> +EXPORT_SYMBOL(mmc_is_exception_event);
> >> +
> >> +/**
> >>    *    mmc_set_data_timeout - set the timeout for a data command
> >>    *    @data: data phase for command
> >>    *    @card: the MMC card associated with the data transfer
> >> @@ -2294,8 +2441,11 @@ int mmc_suspend_host(struct mmc_host *host)
> >>       mmc_bus_get(host);
> >>       if (host->bus_ops&&  !host->bus_dead) {
> >>
> >> -        if (host->bus_ops->suspend)
> >> +        if (host->bus_ops->suspend) {
> >> +            if (mmc_card_doing_bkops(host->card))
> >> +                mmc_stop_bkops(host->card);
> >>               err = host->bus_ops->suspend(host);
> >> +        }
> >>
> >>           if (err == -ENOSYS || !host->bus_ops->resume) {
> >>               /*
> >> @@ -2382,6 +2532,11 @@ int mmc_pm_notify(struct notifier_block
> *notify_block,
> >>       switch (mode) {
> >>       case PM_HIBERNATION_PREPARE:
> >>       case PM_SUSPEND_PREPARE:
> >> +        if (host->card&&  mmc_card_mmc(host->card)&&
> >> +                mmc_card_doing_bkops(host->card)) {
> >> +            mmc_interrupt_hpi(host->card);
> >> +            mmc_card_clr_doing_bkops(host->card);
> >> +        }
> >>
> >>           spin_lock_irqsave(&host->lock, flags);
> >>           host->rescan_disable = 1;
> >> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index
> >> 258b203..9e036e5 100644
> >> --- a/drivers/mmc/core/mmc.c
> >> +++ b/drivers/mmc/core/mmc.c
> >> @@ -463,6 +463,24 @@ static int mmc_read_ext_csd(struct mmc_card
> *card, u8 *ext_csd)
> >>       }
> >>
> >>       if (card->ext_csd.rev>= 5) {
> >> +        /* check whether the eMMC card support BKOPS */
> >> +        if (ext_csd[EXT_CSD_BKOPS_SUPPORT]&  0x1) {
> >> +            card->ext_csd.bkops = 1;
> >> +            card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
> >> +            card->ext_csd.raw_bkops_status =
> >> +                ext_csd[EXT_CSD_BKOPS_STATUS];
> >> +            if (!card->ext_csd.bkops_en&&
> >> +                card->host->caps2&  MMC_CAP2_INIT_BKOPS) {
> >> +                err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> >> +                    EXT_CSD_BKOPS_EN, 1, 0);
> >> +                if (err)
> >> +                    pr_warn("%s: Enabling BKOPS failed\n",
> >> +                        mmc_hostname(card->host));
> >> +                else
> >> +                    card->ext_csd.bkops_en = 1;
> >> +            }
> >> +        }
> >> +
> >>           /* check whether the eMMC card supports HPI */
> >>           if (ext_csd[EXT_CSD_HPI_FEATURES]&  0x1) {
> >>               card->ext_csd.hpi = 1;
> >> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> >> index 69370f4..f0bebd9 100644
> >> --- a/drivers/mmc/core/mmc_ops.c
> >> +++ b/drivers/mmc/core/mmc_ops.c
> >> @@ -367,18 +367,20 @@ int mmc_spi_set_crc(struct mmc_host *host, int
> use_crc)
> >>   }
> >>
> >>   /**
> >> - *    mmc_switch - modify EXT_CSD register
> >> + *    __mmc_switch - modify EXT_CSD register
> >>    *    @card: the MMC card associated with the data transfer
> >>    *    @set: cmd set values
> >>    *    @index: EXT_CSD register index
> >>    *    @value: value to program into EXT_CSD register
> >>    *    @timeout_ms: timeout (ms) for operation performed by register
> write,
> >>    *                   timeout of zero implies maximum possible timeout
> >> + *      @wait_for_prod_done: is waiting for program done
> >>    *
> >>    *    Modifies the EXT_CSD register for selected card.
> >>    */
> >> -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> >> -           unsigned int timeout_ms)
> >> +int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> >> +           unsigned int timeout_ms, u8 wait_for_prog_done)
> >> +
> >>   {
> >>       int err;
> >>       struct mmc_command cmd = {0};
> >> @@ -392,13 +394,23 @@ int mmc_switch(struct mmc_card *card, u8 set,
> u8 index, u8 value,
> >>             (index<<  16) |
> >>             (value<<  8) |
> >>             set;
> >> -    cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
> >> +
> >> +    cmd.flags = MMC_CMD_AC;
> >> +    if (wait_for_prog_done)
> >> +        cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
> >> +    else
> >> +        cmd.flags |= MMC_RSP_R1 | MMC_RSP_R1;
> >
> > is this typo here? did you mean MMC_RSP_SPI_R1  | MMC_RSP_R1 ??
> >> +
> >>       cmd.cmd_timeout_ms = timeout_ms;
> >>
> >>       err = mmc_wait_for_cmd(card->host,&cmd, MMC_CMD_RETRIES);
> >>       if (err)
> >>           return err;
> >>
> >> +    /* No need to check card status in case of BKOPS switch*/
> >> +    if (index == EXT_CSD_BKOPS_START)
> >> +        return 0;
> >> +
> >>       /* Must check status to be sure of no errors */
> >>       do {
> >>           err = mmc_send_status(card,&status); @@ -423,6 +435,13 @@
> >> int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> >>
> >>       return 0;
> >>   }
> >> +EXPORT_SYMBOL_GPL(__mmc_switch);
> >> +
> >> +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
> >> +        unsigned int timeout_ms)
> >> +{
> >> +    return __mmc_switch(card, set, index, value, timeout_ms, 1); }
> >>   EXPORT_SYMBOL_GPL(mmc_switch);
> >>
> >>   int mmc_send_status(struct mmc_card *card, u32 *status) diff --git
> >> a/include/linux/mmc/card.h b/include/linux/mmc/card.h index
> >> d76513b..50b3539 100644
> >> --- a/include/linux/mmc/card.h
> >> +++ b/include/linux/mmc/card.h
> >> @@ -76,10 +76,13 @@ struct mmc_ext_csd {
> >>       bool            hpi_en;            /* HPI enablebit */
> >>       bool            hpi;            /* HPI support bit */
> >>       unsigned int        hpi_cmd;        /* cmd used as HPI */
> >> +    bool            bkops;        /* background support bit */
> >> +    bool            bkops_en;    /* background enable bit */
> >>       unsigned int            data_sector_size;       /* 512 bytes or
4KB */
> >>       unsigned int            data_tag_unit_size;     /* DATA TAG UNIT
size */
> >>       unsigned int        boot_ro_lock;        /* ro lock support */
> >>       bool            boot_ro_lockable;
> >> +    u8            raw_exception_status;    /* 53 */
> >>       u8            raw_partition_support;    /* 160 */
> >>       u8            raw_erased_mem_count;    /* 181 */
> >>       u8            raw_ext_csd_structure;    /* 194 */
> >> @@ -93,6 +96,7 @@ struct mmc_ext_csd {
> >>       u8            raw_sec_erase_mult;    /* 230 */
> >>       u8            raw_sec_feature_support;/* 231 */
> >>       u8            raw_trim_mult;        /* 232 */
> >> +    u8            raw_bkops_status;    /* 246 */
> >>       u8            raw_sectors[4];        /* 212 - 4 bytes */
> >>
> >>       unsigned int            feature_support;
> >> @@ -225,6 +229,9 @@ struct mmc_card {
> >>   #define MMC_CARD_REMOVED    (1<<7)        /* card has been removed */
> >>   #define MMC_STATE_HIGHSPEED_200    (1<<8)        /* card is in HS200
> mode */
> >>   #define MMC_STATE_SLEEP        (1<<9)        /* card is in sleep
state */
> >> +#define MMC_STATE_NEED_BKOPS    (1<<10)        /* card need to do
> BKOPS */
> >> +#define MMC_STATE_DOING_BKOPS    (1<<11)        /* card is doing BKOPS
> */
> >> +#define MMC_STATE_CHECK_BKOPS    (1<<12)        /* card need to check
> BKOPS */
> >>       unsigned int        quirks;     /* card quirks */
> >>   #define MMC_QUIRK_LENIENT_FN0    (1<<0)        /* allow SDIO FN0
writes
> outside of the VS CCCR range */
> >>   #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)    /* use func-
> >cur_blksize */
> >> @@ -391,6 +398,9 @@ static inline void __maybe_unused
> remove_quirk(struct mmc_card *card, int data)
> >>   #define mmc_card_ext_capacity(c) ((c)->state&  MMC_CARD_SDXC)
> >>   #define mmc_card_removed(c)    ((c)&&  ((c)->state&
> MMC_CARD_REMOVED))
> >>   #define mmc_card_is_sleep(c)    ((c)->state&  MMC_STATE_SLEEP)
> >> +#define mmc_card_need_bkops(c)    ((c)->state&
> MMC_STATE_NEED_BKOPS)
> >> +#define mmc_card_doing_bkops(c)    ((c)->state&
> MMC_STATE_DOING_BKOPS)
> >> +#define mmc_card_check_bkops(c) ((c)->state&
> MMC_STATE_CHECK_BKOPS)
> >>
> >>   #define mmc_card_set_present(c)    ((c)->state |= MMC_STATE_PRESENT)
> >>   #define mmc_card_set_readonly(c) ((c)->state |=
> MMC_STATE_READONLY)
> >> @@ -403,7 +413,13 @@ static inline void __maybe_unused
> remove_quirk(struct mmc_card *card, int data)
> >>   #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
> >>   #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
> >>   #define mmc_card_set_sleep(c)    ((c)->state |= MMC_STATE_SLEEP)
> >> +#define mmc_card_set_need_bkops(c)    ((c)->state |=
> MMC_STATE_NEED_BKOPS)
> >> +#define mmc_card_set_doing_bkops(c)    ((c)->state |=
> MMC_STATE_DOING_BKOPS)
> >> +#define mmc_card_set_check_bkops(c) ((c)->state |=
> >> +MMC_STATE_CHECK_BKOPS)
> >>
> >> +#define mmc_card_clr_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_check_bkops(c) ((c)->state&=
> >> +~MMC_STATE_CHECK_BKOPS)
> >>   #define mmc_card_clr_sleep(c)    ((c)->state&= ~MMC_STATE_SLEEP)
> >>   /*
> >>    * Quirk add/remove for MMC products.
> >> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> >> index 1b431c7..bda3d86 100644
> >> --- a/include/linux/mmc/core.h
> >> +++ b/include/linux/mmc/core.h
> >> @@ -134,6 +134,9 @@ struct mmc_host;
> >>   struct mmc_card;
> >>   struct mmc_async_req;
> >>
> >> +extern int mmc_stop_bkops(struct mmc_card *); extern int
> >> +mmc_read_bkops_status(struct mmc_card *); extern int
> >> +mmc_is_exception_event(struct mmc_card *, unsigned int);
> >>   extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
> >>                          struct mmc_async_req *, int *);
> >>   extern int mmc_interrupt_hpi(struct mmc_card *); @@ -142,6 +145,8
> >> @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct
> mmc_command *, int);
> >>   extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
> >>   extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card
> *,
> >>       struct mmc_command *, int);
> >> +extern void mmc_start_bkops(struct mmc_card *card); extern int
> >> +__mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, u8);
> >>   extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
> >>
> >>   #define MMC_ERASE_ARG        0x00000000
> >> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> >> index 0707d22..d23caf2 100644
> >> --- a/include/linux/mmc/host.h
> >> +++ b/include/linux/mmc/host.h
> >> @@ -238,6 +238,8 @@ struct mmc_host {
> >>   #define MMC_CAP2_BROKEN_VOLTAGE    (1<<  7)    /* Use the broken
> voltage */
> >>   #define MMC_CAP2_DETECT_ON_ERR    (1<<  8)    /* On I/O err check
> card removal */
> >>   #define MMC_CAP2_HC_ERASE_SZ    (1<<  9)    /* High-capacity erase
size
> */
> >> +#define MMC_CAP2_INIT_BKOPS    (1<<  10)    /* To enable BKOPS */
> >> +#define MMC_CAP2_BKOPS        (1<<  11)    /* BKOPS supported */
> >>
> >>       mmc_pm_flag_t        pm_caps;    /* supported pm features */
> >>       unsigned int        power_notify_type;
> >> diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index
> >> d425cab..80cfbf1 100644
> >> --- a/include/linux/mmc/mmc.h
> >> +++ b/include/linux/mmc/mmc.h
> >> @@ -139,6 +139,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_EXCEPTION_EVENT    (1<<  6)    /* sx, a */
> >>   #define R1_APP_CMD        (1<<  5)    /* sr, c */
> >>
> >>   #define R1_STATE_IDLE    0
> >> @@ -274,12 +275,15 @@ struct _mmc_csd {
> >>   #define EXT_CSD_FLUSH_CACHE        32      /* W */
> >>   #define EXT_CSD_CACHE_CTRL        33      /* R/W */
> >>   #define EXT_CSD_POWER_OFF_NOTIFICATION    34    /* R/W */
> >> +#define EXT_CSD_EXCEPTION_STATUS    54    /* RO */
> >>   #define EXT_CSD_DATA_SECTOR_SIZE    61    /* R */
> >>   #define EXT_CSD_GP_SIZE_MULT        143    /* R/W */
> >>   #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_RST_N_FUNCTION        162    /* R/W */
> >> +#define EXT_CSD_BKOPS_EN        163    /* R/W */
> >> +#define EXT_CSD_BKOPS_START        164    /* W */
> >>   #define EXT_CSD_SANITIZE_START        165     /* W */
> >>   #define EXT_CSD_WR_REL_PARAM        166    /* RO */
> >>   #define EXT_CSD_BOOT_WP            173    /* R/W */
> >> @@ -313,11 +317,13 @@ struct _mmc_csd {
> >>   #define EXT_CSD_PWR_CL_200_360        237    /* RO */
> >>   #define EXT_CSD_PWR_CL_DDR_52_195    238    /* RO */
> >>   #define EXT_CSD_PWR_CL_DDR_52_360    239    /* RO */
> >> +#define EXT_CSD_BKOPS_STATUS        246    /* RO */
> >>   #define EXT_CSD_POWER_OFF_LONG_TIME    247    /* RO */
> >>   #define EXT_CSD_GENERIC_CMD6_TIME    248    /* RO */
> >>   #define EXT_CSD_CACHE_SIZE        249    /* RO, 4 bytes */
> >>   #define EXT_CSD_TAG_UNIT_SIZE        498    /* RO */
> >>   #define EXT_CSD_DATA_TAG_SUPPORT    499    /* RO */
> >> +#define EXT_CSD_BKOPS_SUPPORT        502    /* RO */
> >>   #define EXT_CSD_HPI_FEATURES        503    /* RO */
> >>
> >>   /*
> >> @@ -386,4 +392,17 @@ struct _mmc_csd {
> >>   #define MMC_SWITCH_MODE_CLEAR_BITS    0x02    /* Clear bits which
> are 1 in value */
> >>   #define MMC_SWITCH_MODE_WRITE_BYTE    0x03    /* Set target to
> value */
> >>
> >> +/*
> >> + * BKOPS status level
> >> + */
> >> +#define EXT_CSD_BKOPS_LEVEL_2        0x2
> >> +
> >> +/*
> >> + * EXCEPTION_EVENT_STATUS field (eMMC4.5)  */
> >> +#define EXT_CSD_URGENT_BKOPS        BIT(0)
> >> +#define EXT_CSD_DYNCAP_NEEDED        BIT(1)
> >> +#define EXT_CSD_SYSPOOL_EXHAUSTED    BIT(2)
> >> +#define EXT_CSD_PACKED_FAILURE        BIT(3)
> >> +
> >>   #endif /* LINUX_MMC_MMC_H */
> >
> >
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-08  4:39 [PATCH v9] mmc: support BKOPS feature for eMMC Jaehoon Chung
                   ` (3 preceding siblings ...)
       [not found] ` <4FD88B7D.3000600@codeaurora.org>
@ 2012-07-06 16:47 ` Chris Ball
  2012-07-08 10:13   ` merez
  4 siblings, 1 reply; 23+ messages in thread
From: Chris Ball @ 2012-07-06 16:47 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Maya Erez,
	Ulf Hansson

Hi,

On Fri, Jun 08 2012, Jaehoon Chung wrote:
> Enable eMMC background operations (BKOPS) feature.
>
> If URGENT_BKOPS is set after a response, note that BKOPS
> are required. After all I/O requests are finished, run
> BKOPS if required. Should read/write operations be requested
> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
> and then service the request.
> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
> the BKOPS-STATUS vaule. 
>
> If you want to enable this feature, set MMC_CAP2_BKOPS.
> And if you want to set the BKOPS_EN bit in ext_csd register,
> use the MMC_CAP2_INIT_BKOPS.
[..]
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 0707d22..d23caf2 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -238,6 +238,8 @@ struct mmc_host {
>  #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
>  #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card removal */
>  #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
> +#define MMC_CAP2_INIT_BKOPS	(1 << 10)	/* To enable BKOPS */
> +#define MMC_CAP2_BKOPS		(1 << 11)	/* BKOPS supported */

Can we have a better explanation of how these capabilities are supposed
to be used, and why it's necessary to have two of them?  Why would you
set one but not the other?

Thanks,

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-07-06 16:47 ` Chris Ball
@ 2012-07-08 10:13   ` merez
  2012-07-09  2:34     ` Jaehoon Chung
  0 siblings, 1 reply; 23+ messages in thread
From: merez @ 2012-07-08 10:13 UTC (permalink / raw)
  To: Chris Ball
  Cc: Jaehoon Chung, linux-mmc, Kyungmin Park, Hanumath Prasad,
	Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Maya Erez,
	Ulf Hansson


On Fri, July 6, 2012 9:47 am, Chris Ball wrote:
> Hi,
>
> On Fri, Jun 08 2012, Jaehoon Chung wrote:
>> Enable eMMC background operations (BKOPS) feature.
>>
>> If URGENT_BKOPS is set after a response, note that BKOPS
>> are required. After all I/O requests are finished, run
>> BKOPS if required. Should read/write operations be requested
>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>> and then service the request.
>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>> the BKOPS-STATUS vaule.
>>
>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>> And if you want to set the BKOPS_EN bit in ext_csd register,
>> use the MMC_CAP2_INIT_BKOPS.
> [..]
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index 0707d22..d23caf2 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -238,6 +238,8 @@ struct mmc_host {
>>  #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
>>  #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card
>> removal */
>>  #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
>> +#define MMC_CAP2_INIT_BKOPS	(1 << 10)	/* To enable BKOPS */
>> +#define MMC_CAP2_BKOPS		(1 << 11)	/* BKOPS supported */
>
> Can we have a better explanation of how these capabilities are supposed
> to be used, and why it's necessary to have two of them?  Why would you
> set one but not the other?
>
> Thanks,
>
> - Chris.
> --
> Chris Ball   <cjb@laptop.org>   <http://printf.net/>
> One Laptop Per Child
>
I think MMC_CAP2_BKOPS should be removed. If the card BKOPs were already
enabled, the host must support BKOPs. Therefore, in mmc_start_bkops we
should check only if card->ext_csd.bkops_en is set.

Thanks,
Maya


-- 
Sent by consultant of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-07-08 10:13   ` merez
@ 2012-07-09  2:34     ` Jaehoon Chung
  2012-07-09  2:48       ` Chris Ball
  0 siblings, 1 reply; 23+ messages in thread
From: Jaehoon Chung @ 2012-07-09  2:34 UTC (permalink / raw)
  To: merez
  Cc: Chris Ball, Jaehoon Chung, linux-mmc, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com, Saugata Das, Konstantin Dorfman, Adrian Hunter,
	Ulf Hansson

Hi, 

On 07/08/2012 07:13 PM, merez@codeaurora.org wrote:

> 
> On Fri, July 6, 2012 9:47 am, Chris Ball wrote:
>> Hi,
>>
>> On Fri, Jun 08 2012, Jaehoon Chung wrote:
>>> Enable eMMC background operations (BKOPS) feature.
>>>
>>> If URGENT_BKOPS is set after a response, note that BKOPS
>>> are required. After all I/O requests are finished, run
>>> BKOPS if required. Should read/write operations be requested
>>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>>> and then service the request.
>>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>>> the BKOPS-STATUS vaule.
>>>
>>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>>> And if you want to set the BKOPS_EN bit in ext_csd register,
>>> use the MMC_CAP2_INIT_BKOPS.
>> [..]
>>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>>> index 0707d22..d23caf2 100644
>>> --- a/include/linux/mmc/host.h
>>> +++ b/include/linux/mmc/host.h
>>> @@ -238,6 +238,8 @@ struct mmc_host {
>>>  #define MMC_CAP2_BROKEN_VOLTAGE	(1 << 7)	/* Use the broken voltage */
>>>  #define MMC_CAP2_DETECT_ON_ERR	(1 << 8)	/* On I/O err check card
>>> removal */
>>>  #define MMC_CAP2_HC_ERASE_SZ	(1 << 9)	/* High-capacity erase size */
>>> +#define MMC_CAP2_INIT_BKOPS	(1 << 10)	/* To enable BKOPS */
>>> +#define MMC_CAP2_BKOPS		(1 << 11)	/* BKOPS supported */
>>
>> Can we have a better explanation of how these capabilities are supposed
>> to be used, and why it's necessary to have two of them?  Why would you
>> set one but not the other?
>>
>> Thanks,
>>
>> - Chris.
>> --
>> Chris Ball   <cjb@laptop.org>   <http://printf.net/>
>> One Laptop Per Child
>>
> I think MMC_CAP2_BKOPS should be removed. If the card BKOPs were already
> enabled, the host must support BKOPs. Therefore, in mmc_start_bkops we
> should check only if card->ext_csd.bkops_en is set.

If bkops bit is set, it means that use the bkops by default.
If somebody didn't want to use the bkops, then just didn't set the MMC_CAP2_BKOPS.
And eMMC card's BKOPS bit should not be set,
then we can set the BKOPS support with switch command.
For this, MMC_CAP2_INIT_BKOPS is added.
In my case, didn't set the bkops enable bit at first time.
So need to set bkops enable bit with switch command.

As Maya's mentioned, if we use the bkops by default, we can remove the MMC_CAP2_BKOPS2.

Best Regards,
Jaehoon Chung

> 
> Thanks,
> Maya
> 
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-07-09  2:34     ` Jaehoon Chung
@ 2012-07-09  2:48       ` Chris Ball
  2012-07-09  4:14         ` Jaehoon Chung
  0 siblings, 1 reply; 23+ messages in thread
From: Chris Ball @ 2012-07-09  2:48 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: merez, linux-mmc, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Ulf Hansson

Hi,

On Sun, Jul 08 2012, Jaehoon Chung wrote:
>> I think MMC_CAP2_BKOPS should be removed. If the card BKOPs were already
>> enabled, the host must support BKOPs. Therefore, in mmc_start_bkops we
>> should check only if card->ext_csd.bkops_en is set.
>
> If bkops bit is set, it means that use the bkops by default.
> If somebody didn't want to use the bkops, then just didn't set the MMC_CAP2_BKOPS.
> And eMMC card's BKOPS bit should not be set,
> then we can set the BKOPS support with switch command.
> For this, MMC_CAP2_INIT_BKOPS is added.
> In my case, didn't set the bkops enable bit at first time.
> So need to set bkops enable bit with switch command.
>
> As Maya's mentioned, if we use the bkops by default, we can remove the MMC_CAP2_BKOPS2.

I'm not sure I understand.  If someone has advertised MMC_CAP2_BKOPS on
their host, why do we also need to check for MMC_CAP2_INIT_BKOPS before
we enable it in the ext_csd?

Why shouldn't we just use one (MMC_CAP2_BKOPS) capability to mean
"enable bkops if supported, by writing 1 (if it wasn't already set)
to ext_csd[163] to tell the card that we're going to be using bkops"?
Are there any disadvantages to doing this?

Thanks,

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-07-09  2:48       ` Chris Ball
@ 2012-07-09  4:14         ` Jaehoon Chung
  2012-07-09  4:39           ` Chris Ball
  0 siblings, 1 reply; 23+ messages in thread
From: Jaehoon Chung @ 2012-07-09  4:14 UTC (permalink / raw)
  To: Chris Ball
  Cc: Jaehoon Chung, merez, linux-mmc, Kyungmin Park, Hanumath Prasad,
	Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Ulf Hansson

On 07/09/2012 11:48 AM, Chris Ball wrote:

> Hi,
> 
> On Sun, Jul 08 2012, Jaehoon Chung wrote:
>>> I think MMC_CAP2_BKOPS should be removed. If the card BKOPs were already
>>> enabled, the host must support BKOPs. Therefore, in mmc_start_bkops we
>>> should check only if card->ext_csd.bkops_en is set.
>>
>> If bkops bit is set, it means that use the bkops by default.
>> If somebody didn't want to use the bkops, then just didn't set the MMC_CAP2_BKOPS.
>> And eMMC card's BKOPS bit should not be set,
>> then we can set the BKOPS support with switch command.
>> For this, MMC_CAP2_INIT_BKOPS is added.
>> In my case, didn't set the bkops enable bit at first time.
>> So need to set bkops enable bit with switch command.
>>
>> As Maya's mentioned, if we use the bkops by default, we can remove the MMC_CAP2_BKOPS2.
> 
> I'm not sure I understand.  If someone has advertised MMC_CAP2_BKOPS on
> their host, why do we also need to check for MMC_CAP2_INIT_BKOPS before
> we enable it in the ext_csd?

Actually, it didn't need to check the MMC_CAP2_INIT_BKOPS.
Because, if MMC_CAP2_BKOPS is set, it means we want to use the bkops.
Then we could enable in ext_csd by default.
In the initial version, didn't add the MMC_CAP2_INIT_BKOPS.
But some people asked me that card didn't set bkops enable bit in ext_csd why set enable bkops by default?
Also others suggested the bkops enable bit should be set at user space.
I think host didn't consider whether enable bkops or not.
But some people want to enable bkops bit, some people didn't want to enable bkops bit by default.
So i added the MMC_CAP2_INIT_BKOPS.
If you can decide, i will confirm yours.

Best Regards,
Jaehoon Chung

> 
> Why shouldn't we just use one (MMC_CAP2_BKOPS) capability to mean
> "enable bkops if supported, by writing 1 (if it wasn't already set)
> to ext_csd[163] to tell the card that we're going to be using bkops"?
> Are there any disadvantages to doing this?
> 
> Thanks,
> 
> - Chris.



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-07-09  4:14         ` Jaehoon Chung
@ 2012-07-09  4:39           ` Chris Ball
  0 siblings, 0 replies; 23+ messages in thread
From: Chris Ball @ 2012-07-09  4:39 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: merez, linux-mmc, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Adrian Hunter, Ulf Hansson

Hi,

On Mon, Jul 09 2012, Jaehoon Chung wrote:
> On 07/09/2012 11:48 AM, Chris Ball wrote:
>
>> Hi,
>> 
>> On Sun, Jul 08 2012, Jaehoon Chung wrote:
>>>> I think MMC_CAP2_BKOPS should be removed. If the card BKOPs were already
>>>> enabled, the host must support BKOPs. Therefore, in mmc_start_bkops we
>>>> should check only if card->ext_csd.bkops_en is set.
>>>
>>> If bkops bit is set, it means that use the bkops by default.
>>> If somebody didn't want to use the bkops, then just didn't set the MMC_CAP2_BKOPS.
>>> And eMMC card's BKOPS bit should not be set,
>>> then we can set the BKOPS support with switch command.
>>> For this, MMC_CAP2_INIT_BKOPS is added.
>>> In my case, didn't set the bkops enable bit at first time.
>>> So need to set bkops enable bit with switch command.
>>>
>>> As Maya's mentioned, if we use the bkops by default, we can remove
>>> the MMC_CAP2_BKOPS2.
>> 
>> I'm not sure I understand.  If someone has advertised MMC_CAP2_BKOPS on
>> their host, why do we also need to check for MMC_CAP2_INIT_BKOPS before
>> we enable it in the ext_csd?
>
> Actually, it didn't need to check the MMC_CAP2_INIT_BKOPS.
> Because, if MMC_CAP2_BKOPS is set, it means we want to use the bkops.
> Then we could enable in ext_csd by default.
> In the initial version, didn't add the MMC_CAP2_INIT_BKOPS.
> But some people asked me that card didn't set bkops enable bit in
> ext_csd why set enable bkops by default?
> Also others suggested the bkops enable bit should be set at user space.

Oh!  I'd missed that BKOPS_EN is a one-time programmable flag; sorry.

In that case, Sebastian's previous review comments were correct, and
you should not be touching the fuse on BKOPS_EN inside the kernelspace
driver.  It should only be modified from an explicit command inside
mmc-utils.

So, please have the kernel patch only bring up bkops if the flag is
already set in the ext_csd, and please submit a userspace patch to
mmc-utils for a command that sets the flag.

Thanks,

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-06-14 13:46 ` Adrian Hunter
  2012-06-14 14:58   ` Konstantin Dorfman
@ 2012-07-12  5:02   ` Jaehoon Chung
  2012-07-12  6:00     ` Adrian Hunter
  1 sibling, 1 reply; 23+ messages in thread
From: Jaehoon Chung @ 2012-07-12  5:02 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com, Saugata Das, Konstantin Dorfman, Maya Erez,
	Ulf Hansson

Hi, Adrian,

On 06/14/2012 10:46 PM, Adrian Hunter wrote:

> On 08/06/12 07:39, Jaehoon Chung wrote:
>> Enable eMMC background operations (BKOPS) feature.
>>
>> If URGENT_BKOPS is set after a response, note that BKOPS
>> are required. After all I/O requests are finished, run
>> BKOPS if required. Should read/write operations be requested
>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>> and then service the request.
>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>> the BKOPS-STATUS vaule. 
>>
>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>> And if you want to set the BKOPS_EN bit in ext_csd register,
>> use the MMC_CAP2_INIT_BKOPS.
>>
>> Future considerations
>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>  * Interrupt ongoing BKOPS before powering off the card.
>>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>> Signed-off-by: Maya Erez <merez@codeaurora.org>
>> ---
> 
> I would not expect this to work nicely with runtime PM.  I expect that BKOPS
> would need to be stopped beforehand.  But that would limit the time
> available for BKOPS since runtime PM would always kick in and stop it.  How
> is runtime PM to be handled?

I think that add some function like the mmc_runtime_pm_suspend().
int mmc_runtime_pm_suspend()
{
	if running bkops {
		waiting for limit time..=> (when upper than Level2)
		otherwise don't wait
		stop-bkops
	} else
		nothing..
}
Almost runtime PM is controlled at host side.
So it's not very good that add the bkops control code at host side.
How about this? i want to get your opinion.

Best Regards,
Jaehoon Chung

> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-07-12  5:02   ` Jaehoon Chung
@ 2012-07-12  6:00     ` Adrian Hunter
  2012-07-12  6:50       ` Jaehoon Chung
  0 siblings, 1 reply; 23+ messages in thread
From: Adrian Hunter @ 2012-07-12  6:00 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per FORLIN,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com,
	Saugata Das, Konstantin Dorfman, Maya Erez, Ulf Hansson

On 12/07/12 08:02, Jaehoon Chung wrote:
> Hi, Adrian,
> 
> On 06/14/2012 10:46 PM, Adrian Hunter wrote:
> 
>> On 08/06/12 07:39, Jaehoon Chung wrote:
>>> Enable eMMC background operations (BKOPS) feature.
>>>
>>> If URGENT_BKOPS is set after a response, note that BKOPS
>>> are required. After all I/O requests are finished, run
>>> BKOPS if required. Should read/write operations be requested
>>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>>> and then service the request.
>>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>>> the BKOPS-STATUS vaule. 
>>>
>>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>>> And if you want to set the BKOPS_EN bit in ext_csd register,
>>> use the MMC_CAP2_INIT_BKOPS.
>>>
>>> Future considerations
>>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>>  * Interrupt ongoing BKOPS before powering off the card.
>>>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>>>
>>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>>> Signed-off-by: Maya Erez <merez@codeaurora.org>
>>> ---
>>
>> I would not expect this to work nicely with runtime PM.  I expect that BKOPS
>> would need to be stopped beforehand.  But that would limit the time
>> available for BKOPS since runtime PM would always kick in and stop it.  How
>> is runtime PM to be handled?
> 
> I think that add some function like the mmc_runtime_pm_suspend().
> int mmc_runtime_pm_suspend()
> {
> 	if running bkops {
> 		waiting for limit time..=> (when upper than Level2)
> 		otherwise don't wait
> 		stop-bkops
> 	} else
> 		nothing..
> }
> Almost runtime PM is controlled at host side.
> So it's not very good that add the bkops control code at host side.
> How about this? i want to get your opinion.

Possibly, but ideally it should be controlled through the runtime PM API.
For example, sdhci uses pm_runtime_get / put which must be paired.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v9] mmc: support BKOPS feature for eMMC
  2012-07-12  6:00     ` Adrian Hunter
@ 2012-07-12  6:50       ` Jaehoon Chung
  0 siblings, 0 replies; 23+ messages in thread
From: Jaehoon Chung @ 2012-07-12  6:50 UTC (permalink / raw)
  To: Adrian Hunter
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per FORLIN, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com, Saugata Das, Konstantin Dorfman, Maya Erez,
	Ulf Hansson

Thanks for comment.
I will test and send the patch within this week.

Best Regards,
Jaehoon Chung

On 07/12/2012 03:00 PM, Adrian Hunter wrote:

> On 12/07/12 08:02, Jaehoon Chung wrote:
>> Hi, Adrian,
>>
>> On 06/14/2012 10:46 PM, Adrian Hunter wrote:
>>
>>> On 08/06/12 07:39, Jaehoon Chung wrote:
>>>> Enable eMMC background operations (BKOPS) feature.
>>>>
>>>> If URGENT_BKOPS is set after a response, note that BKOPS
>>>> are required. After all I/O requests are finished, run
>>>> BKOPS if required. Should read/write operations be requested
>>>> during BKOPS, first issue HPI to interrupt the ongoing BKOPS
>>>> and then service the request.
>>>> If BKOPS-STATUS is upper than LEVEL2, need to check until clear
>>>> the BKOPS-STATUS vaule. 
>>>>
>>>> If you want to enable this feature, set MMC_CAP2_BKOPS.
>>>> And if you want to set the BKOPS_EN bit in ext_csd register,
>>>> use the MMC_CAP2_INIT_BKOPS.
>>>>
>>>> Future considerations
>>>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>>>  * Interrupt ongoing BKOPS before powering off the card.
>>>>  * How get BKOPS_STATUS value.(periodically send ext_csd command?)
>>>>
>>>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>>> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
>>>> Signed-off-by: Maya Erez <merez@codeaurora.org>
>>>> ---
>>>
>>> I would not expect this to work nicely with runtime PM.  I expect that BKOPS
>>> would need to be stopped beforehand.  But that would limit the time
>>> available for BKOPS since runtime PM would always kick in and stop it.  How
>>> is runtime PM to be handled?
>>
>> I think that add some function like the mmc_runtime_pm_suspend().
>> int mmc_runtime_pm_suspend()
>> {
>> 	if running bkops {
>> 		waiting for limit time..=> (when upper than Level2)
>> 		otherwise don't wait
>> 		stop-bkops
>> 	} else
>> 		nothing..
>> }
>> Almost runtime PM is controlled at host side.
>> So it's not very good that add the bkops control code at host side.
>> How about this? i want to get your opinion.
> 
> Possibly, but ideally it should be controlled through the runtime PM API.
> For example, sdhci uses pm_runtime_get / put which must be paired.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2012-07-12  6:51 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-08  4:39 [PATCH v9] mmc: support BKOPS feature for eMMC Jaehoon Chung
2012-06-13 12:40 ` Dong, Chuanxiao
     [not found]   ` <17296D9F8FF2234F831FC3DF505A87A90FEA11E5@SHSMSX102.ccr.corp.intel.com >
2012-06-14 14:49     ` Konstantin Dorfman
2012-06-15  3:00   ` Jaehoon Chung
2012-06-13 13:53 ` S, Venkatraman
2012-06-14  8:44   ` merez
2012-06-15  4:29   ` Jaehoon Chung
2012-06-14 13:46 ` Adrian Hunter
2012-06-14 14:58   ` Konstantin Dorfman
2012-07-12  5:02   ` Jaehoon Chung
2012-07-12  6:00     ` Adrian Hunter
2012-07-12  6:50       ` Jaehoon Chung
     [not found] ` <4FD88B7D.3000600@codeaurora.org>
2012-06-15  4:19   ` Jaehoon Chung
2012-06-15  6:10     ` Subhash Jadavani
2012-07-06 16:47 ` Chris Ball
2012-07-08 10:13   ` merez
2012-07-09  2:34     ` Jaehoon Chung
2012-07-09  2:48       ` Chris Ball
2012-07-09  4:14         ` Jaehoon Chung
2012-07-09  4:39           ` Chris Ball
  -- strict thread matches above, loose matches on Subject: below --
2012-06-12 10:58 merez
2012-06-13  3:07 ` Jaehoon Chung
2012-06-13  6:18   ` merez

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).