linux-mmc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] mmc: support BKOPS feature for eMMC
@ 2011-11-17  0:50 Jaehoon Chung
  2011-11-17 12:37 ` S, Venkatraman
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Jaehoon Chung @ 2011-11-17  0:50 UTC (permalink / raw)
  To: linux-mmc
  Cc: Chris Ball, Kyungmin Park, Hanumath Prasad, Per Forlin,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com

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 you want to enable this feature, set MMC_CAP2_BKOPS.

Future considerations
 * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
 * Interrupt ongoing BKOPS before powering off the card.

Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Hanumath Prasad <hanumath.prasad@stericsson.com>
---
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)
	- remove unnecessary code.

 drivers/mmc/card/block.c   |   10 +++++
 drivers/mmc/card/queue.c   |    4 ++
 drivers/mmc/core/core.c    |   87 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/mmc.c     |    8 ++++
 drivers/mmc/core/mmc_ops.c |    6 +++-
 include/linux/mmc/card.h   |   12 ++++++
 include/linux/mmc/core.h   |    3 ++
 include/linux/mmc/host.h   |    1 +
 include/linux/mmc/mmc.h    |   14 +++++++
 9 files changed, 144 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index c80bb6d..9d19ebb 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1188,6 +1188,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
 		type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
 		mmc_queue_bounce_post(mq_rq);
 
+		/*
+		 * Check BKOPS urgency from each R1 response
+		 */
+		if (mmc_card_mmc(card) &&
+			(brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) {
+			if (mmc_is_exception_event(card,
+					EXT_CSD_URGENT_BKOPS))
+				mmc_card_set_need_bkops(card);
+		}
+
 		switch (status) {
 		case MMC_BLK_SUCCESS:
 		case MMC_BLK_PARTIAL:
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index dcad59c..20bb4a5 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -61,6 +61,9 @@ 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_interrupt_bkops(mq->card);
+
 			set_current_state(TASK_RUNNING);
 			mq->issue_fn(mq, req);
 		} else {
@@ -68,6 +71,7 @@ static int mmc_queue_thread(void *d)
 				set_current_state(TASK_RUNNING);
 				break;
 			}
+			mmc_start_bkops(mq->card);
 			up(&mq->thread_sem);
 			schedule();
 			down(&mq->thread_sem);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 7ee2e07..2d2f1d5 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -238,6 +238,50 @@ 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;
+	unsigned long flags;
+
+	BUG_ON(!card);
+	if ((!card->ext_csd.bkops_en) ||
+			!(card->host->caps2 & MMC_CAP2_BKOPS))
+		return;
+
+	/*
+	 * 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);
+	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+			EXT_CSD_BKOPS_START, 1, 0);
+	if (err) {
+		pr_warning("error %d starting bkops\n", err);
+		mmc_card_clr_need_bkops(card);
+		goto out;
+	}
+	spin_lock_irqsave(&card->host->lock, flags);
+	mmc_card_clr_need_bkops(card);
+	mmc_card_set_doing_bkops(card);
+	spin_unlock_irqrestore(&card->host->lock, flags);
+out:
+	mmc_release_host(card->host);
+}
+EXPORT_SYMBOL(mmc_start_bkops);
+
 static void mmc_wait_done(struct mmc_request *mrq)
 {
 	complete(&mrq->completion);
@@ -466,6 +510,49 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
 EXPORT_SYMBOL(mmc_wait_for_cmd);
 
 /**
+ *	mmc_interrupt_bkops - interrupt ongoing BKOPS
+ *	@card: MMC card to check BKOPS
+ *
+ *	Send HPI command to interrupt 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_interrupt_bkops(struct mmc_card *card)
+{
+	int err = 0;
+	unsigned long flags;
+
+	BUG_ON(!card);
+
+	err = mmc_interrupt_hpi(card);
+
+	spin_lock_irqsave(&card->host->lock, flags);
+	mmc_card_clr_doing_bkops(card);
+	spin_unlock_irqrestore(&card->host->lock, flags);
+
+	return err;
+}
+EXPORT_SYMBOL(mmc_interrupt_bkops);
+
+int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
+{
+	int err;
+	u8 ext_csd[512];
+
+	/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
+	if (card->ext_csd.rev == 5)
+		return 1;
+
+	err = mmc_send_ext_csd(card, ext_csd);
+	if (err)
+		return err;
+
+	return (ext_csd[EXT_CSD_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
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a1223bd..514ad0f 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -448,6 +448,14 @@ 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];
+		}
+
 		/* 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 007863e..2e397d5 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -398,6 +398,10 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
 	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);
@@ -568,7 +572,7 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
 
 	err = mmc_wait_for_cmd(card->host, &cmd, 0);
 	if (err) {
-		pr_warn("%s: error %d interrupting operation. "
+		pr_debug("%s: error %d interrupting operation. "
 			"HPI command response %#x\n", mmc_hostname(card->host),
 			err, cmd.resp[0]);
 		return err;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 534974c..3c983d7 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -70,6 +70,8 @@ struct mmc_ext_csd {
 	unsigned int		cache_size;		/* Units: KB */
 	bool			hpi_en;			/* HPI enablebit */
 	bool			hpi;			/* HPI support bit */
+	bool			bkops;		/* background support bit */
+	bool			bkops_en;	/* background enable bit */
 	unsigned int		hpi_cmd;		/* cmd used as HPI */
 	u8			raw_partition_support;	/* 160 */
 	u8			raw_erased_mem_count;	/* 181 */
@@ -84,6 +86,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;
@@ -209,6 +212,8 @@ struct mmc_card {
 #define MMC_STATE_HIGHSPEED_DDR (1<<4)		/* card is in high speed mode */
 #define MMC_STATE_ULTRAHIGHSPEED (1<<5)		/* card is in ultra high speed mode */
 #define MMC_CARD_SDXC		(1<<6)		/* card is SDXC */
+#define MMC_STATE_NEED_BKOPS	(1<<7)		/* card need to do BKOPS */
+#define MMC_STATE_DOING_BKOPS	(1<<8)		/* card is doing 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 */
@@ -370,6 +375,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_uhs(c)		((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_sd_card_uhs(c)	((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
+#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_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -379,6 +386,11 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
+#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_clr_need_bkops(c)	((c)->state &= ~MMC_STATE_NEED_BKOPS)
+#define mmc_card_clr_doing_bkops(c)	((c)->state &= ~MMC_STATE_DOING_BKOPS)
 
 /*
  * Quirk add/remove for MMC products.
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 174a844..f6b997a 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -134,6 +134,8 @@ struct mmc_host;
 struct mmc_card;
 struct mmc_async_req;
 
+extern int mmc_interrupt_bkops(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 *);
@@ -163,6 +165,7 @@ extern int mmc_can_sanitize(struct mmc_card *card);
 extern int mmc_can_secure_erase_trim(struct mmc_card *card);
 extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
 				   unsigned int nr);
+extern void mmc_start_bkops(struct mmc_card *card);
 extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
 
 extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 706f722..4629e84 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -242,6 +242,7 @@ struct mmc_host {
 #define MMC_CAP2_CACHE_CTRL	(1 << 1)	/* Allow cache control */
 #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)	/* Notify poweroff supported */
 #define MMC_CAP2_NO_MULTI_READ	(1 << 3)	/* Multiblock reads don't work */
+#define MMC_CAP2_BKOPS		(1 << 4)	/* 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 0e71356..20b1420 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -138,6 +138,7 @@ static inline bool mmc_op_multi(u32 opcode)
 #define R1_CURRENT_STATE(x)	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
 #define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
 #define R1_SWITCH_ERROR		(1 << 7)	/* sx, c */
+#define R1_EXCEPTION_EVENT	(1 << 6)	/* sx, a */
 #define R1_APP_CMD		(1 << 5)	/* sr, c */
 
 #define R1_STATE_IDLE	0
@@ -273,11 +274,14 @@ 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_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_ERASE_GROUP_DEF		175	/* R/W */
@@ -310,9 +314,11 @@ 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_BKOPS_SUPPORT		502	/* RO */
 #define EXT_CSD_HPI_FEATURES		503	/* RO */
 
 /*
@@ -373,4 +379,12 @@ 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 */
 
+/*
+ * 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 related	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-17  0:50 [PATCH v3] mmc: support BKOPS feature for eMMC Jaehoon Chung
@ 2011-11-17 12:37 ` S, Venkatraman
  2011-11-17 15:01 ` Konstantin Dorfman
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 14+ messages in thread
From: S, Venkatraman @ 2011-11-17 12:37 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per Forlin,
	Sebastian Rasmussen, Dong, Chuanxiao

On Thu, Nov 17, 2011 at 6:20 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 you want to enable this feature, set MMC_CAP2_BKOPS.
>
> Future considerations
>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>  * Interrupt ongoing BKOPS before powering off the card.
>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> CC: Hanumath Prasad <hanumath.prasad@stericsson.com>
> ---
> 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)
>        - remove unnecessary code.
>
>  drivers/mmc/card/block.c   |   10 +++++
>  drivers/mmc/card/queue.c   |    4 ++
>  drivers/mmc/core/core.c    |   87 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/core/mmc.c     |    8 ++++
>  drivers/mmc/core/mmc_ops.c |    6 +++-
>  include/linux/mmc/card.h   |   12 ++++++
>  include/linux/mmc/core.h   |    3 ++
>  include/linux/mmc/host.h   |    1 +
>  include/linux/mmc/mmc.h    |   14 +++++++
>  9 files changed, 144 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index c80bb6d..9d19ebb 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -1188,6 +1188,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
>                type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE;
>                mmc_queue_bounce_post(mq_rq);
>
> +               /*
> +                * Check BKOPS urgency from each R1 response
> +                */
> +               if (mmc_card_mmc(card) &&
> +                       (brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) {
> +                       if (mmc_is_exception_event(card,
> +                                       EXT_CSD_URGENT_BKOPS))
> +                               mmc_card_set_need_bkops(card);
> +               }
> +
>                switch (status) {
>                case MMC_BLK_SUCCESS:
>                case MMC_BLK_PARTIAL:
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index dcad59c..20bb4a5 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -61,6 +61,9 @@ 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_interrupt_bkops(mq->card);

Should you interrupt bkops for any request or just for read /write requests ?

> +
>                        set_current_state(TASK_RUNNING);
>                        mq->issue_fn(mq, req);
>                } else {
> @@ -68,6 +71,7 @@ static int mmc_queue_thread(void *d)
>                                set_current_state(TASK_RUNNING);
>                                break;
>                        }
> +                       mmc_start_bkops(mq->card);
>                        up(&mq->thread_sem);
>                        schedule();
>                        down(&mq->thread_sem);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 7ee2e07..2d2f1d5 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -238,6 +238,50 @@ 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;
> +       unsigned long flags;
> +
> +       BUG_ON(!card);
> +       if ((!card->ext_csd.bkops_en) ||
> +                       !(card->host->caps2 & MMC_CAP2_BKOPS))
> +               return;
> +
> +       /*
> +        * 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);
> +       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +                       EXT_CSD_BKOPS_START, 1, 0);
> +       if (err) {
> +               pr_warning("error %d starting bkops\n", err);
> +               mmc_card_clr_need_bkops(card);
> +               goto out;
> +       }
> +       spin_lock_irqsave(&card->host->lock, flags);
> +       mmc_card_clr_need_bkops(card);
> +       mmc_card_set_doing_bkops(card);
> +       spin_unlock_irqrestore(&card->host->lock, flags);
> +out:
> +       mmc_release_host(card->host);
> +}
> +EXPORT_SYMBOL(mmc_start_bkops);
> +
>  static void mmc_wait_done(struct mmc_request *mrq)
>  {
>        complete(&mrq->completion);
> @@ -466,6 +510,49 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
>  EXPORT_SYMBOL(mmc_wait_for_cmd);
>
>  /**
> + *     mmc_interrupt_bkops - interrupt ongoing BKOPS
> + *     @card: MMC card to check BKOPS
> + *
> + *     Send HPI command to interrupt 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_interrupt_bkops(struct mmc_card *card)
> +{
> +       int err = 0;
> +       unsigned long flags;
> +
> +       BUG_ON(!card);
> +
> +       err = mmc_interrupt_hpi(card);
> +
> +       spin_lock_irqsave(&card->host->lock, flags);
> +       mmc_card_clr_doing_bkops(card);
> +       spin_unlock_irqrestore(&card->host->lock, flags);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL(mmc_interrupt_bkops);
> +
> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
> +{
> +       int err;
> +       u8 ext_csd[512];
> +
> +       /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
> +       if (card->ext_csd.rev == 5)
> +               return 1;
> +
> +       err = mmc_send_ext_csd(card, ext_csd);
> +       if (err)
> +               return err;
If reading ext_csd fails, this will still trigger bkops by returning a
non-zero value.
I think it should abort if ext_csd can't be read.

> +
> +       return (ext_csd[EXT_CSD_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
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index a1223bd..514ad0f 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -448,6 +448,14 @@ 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];
> +               }
> +
>                /* 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 007863e..2e397d5 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -398,6 +398,10 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>        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);
> @@ -568,7 +572,7 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
>
>        err = mmc_wait_for_cmd(card->host, &cmd, 0);
>        if (err) {
> -               pr_warn("%s: error %d interrupting operation. "
> +               pr_debug("%s: error %d interrupting operation. "
>                        "HPI command response %#x\n", mmc_hostname(card->host),
>                        err, cmd.resp[0]);
>                return err;
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 534974c..3c983d7 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -70,6 +70,8 @@ struct mmc_ext_csd {
>        unsigned int            cache_size;             /* Units: KB */
>        bool                    hpi_en;                 /* HPI enablebit */
>        bool                    hpi;                    /* HPI support bit */
> +       bool                    bkops;          /* background support bit */
> +       bool                    bkops_en;       /* background enable bit */
>        unsigned int            hpi_cmd;                /* cmd used as HPI */
>        u8                      raw_partition_support;  /* 160 */
>        u8                      raw_erased_mem_count;   /* 181 */
> @@ -84,6 +86,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;
> @@ -209,6 +212,8 @@ struct mmc_card {
>  #define MMC_STATE_HIGHSPEED_DDR (1<<4)         /* card is in high speed mode */
>  #define MMC_STATE_ULTRAHIGHSPEED (1<<5)                /* card is in ultra high speed mode */
>  #define MMC_CARD_SDXC          (1<<6)          /* card is SDXC */
> +#define MMC_STATE_NEED_BKOPS   (1<<7)          /* card need to do BKOPS */
> +#define MMC_STATE_DOING_BKOPS  (1<<8)          /* card is doing 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 */
> @@ -370,6 +375,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>  #define mmc_card_uhs(c)                ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_sd_card_uhs(c)     ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
> +#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_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
> @@ -379,6 +386,11 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
>  #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
> +#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_clr_need_bkops(c)     ((c)->state &= ~MMC_STATE_NEED_BKOPS)
> +#define mmc_card_clr_doing_bkops(c)    ((c)->state &= ~MMC_STATE_DOING_BKOPS)
>
>  /*
>  * Quirk add/remove for MMC products.
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 174a844..f6b997a 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -134,6 +134,8 @@ struct mmc_host;
>  struct mmc_card;
>  struct mmc_async_req;
>
> +extern int mmc_interrupt_bkops(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 *);
> @@ -163,6 +165,7 @@ extern int mmc_can_sanitize(struct mmc_card *card);
>  extern int mmc_can_secure_erase_trim(struct mmc_card *card);
>  extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
>                                   unsigned int nr);
> +extern void mmc_start_bkops(struct mmc_card *card);
>  extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
>
>  extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 706f722..4629e84 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -242,6 +242,7 @@ struct mmc_host {
>  #define MMC_CAP2_CACHE_CTRL    (1 << 1)        /* Allow cache control */
>  #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)      /* Notify poweroff supported */
>  #define MMC_CAP2_NO_MULTI_READ (1 << 3)        /* Multiblock reads don't work */
> +#define MMC_CAP2_BKOPS         (1 << 4)        /* 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 0e71356..20b1420 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -138,6 +138,7 @@ static inline bool mmc_op_multi(u32 opcode)
>  #define R1_CURRENT_STATE(x)    ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
>  #define R1_READY_FOR_DATA      (1 << 8)        /* sx, a */
>  #define R1_SWITCH_ERROR                (1 << 7)        /* sx, c */
> +#define R1_EXCEPTION_EVENT     (1 << 6)        /* sx, a */
>  #define R1_APP_CMD             (1 << 5)        /* sr, c */
>
>  #define R1_STATE_IDLE  0
> @@ -273,11 +274,14 @@ 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_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_ERASE_GROUP_DEF                175     /* R/W */
> @@ -310,9 +314,11 @@ 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_BKOPS_SUPPORT          502     /* RO */
>  #define EXT_CSD_HPI_FEATURES           503     /* RO */
>
>  /*
> @@ -373,4 +379,12 @@ 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 */
>
> +/*
> + * 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] 14+ messages in thread

* RE: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-17  0:50 [PATCH v3] mmc: support BKOPS feature for eMMC Jaehoon Chung
  2011-11-17 12:37 ` S, Venkatraman
@ 2011-11-17 15:01 ` Konstantin Dorfman
  2011-11-17 16:32   ` Per Forlin
  2011-11-22 12:44 ` Konstantin Dorfman
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Konstantin Dorfman @ 2011-11-17 15:01 UTC (permalink / raw)
  To: 'Jaehoon Chung', 'linux-mmc'
  Cc: 'Chris Ball', 'Kyungmin Park',
	'Hanumath Prasad', 'Per Forlin',
	'Sebastian Rasmussen', 'Dong, Chuanxiao',
	svenkatr

Hello Jaenhoon,

I have a few suggestions regarding the situation when Host starts BKOPS:

1) Host should start BKOPS (based on BKOPS_STATUS or URGENT_BKOPS event)
when going to mmc_sleep, which means that the bus is in idle state (this
also covers the case in mmc_queue_thread, because in this case no I/O
request exists). It seems like checking the status periodically in addition
to mmc_suspend is not needed. Since if the device was in idle and we
performed a single BKOPS then there is no point in performing another BKOPS
unless there was actually another I/O request.

2) Also this implies an answer to the question about need to interrupt BKOPS
before powering off card - the answer is no.

3) Based on statistical data we have (day long test) it looks like we do not
need to do any preventive BKOPS caused by BKOPS_STATUS less than critical
(0x3).

Thanks,
Konstantin Dorfman
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

-----Original Message-----
From: linux-mmc-owner@vger.kernel.org
[mailto:linux-mmc-owner@vger.kernel.org] On Behalf Of Jaehoon Chung
Sent: Thursday, November 17, 2011 2:50 AM
To: linux-mmc
Cc: Chris Ball; Kyungmin Park; Hanumath Prasad; Per Forlin; Sebastian
Rasmussen; Dong, Chuanxiao; svenkatr@ti.com
Subject: [PATCH v3] mmc: support BKOPS feature for eMMC

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 you want to enable this feature, set MMC_CAP2_BKOPS.

Future considerations
 * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
 * Interrupt ongoing BKOPS before powering off the card.

Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Hanumath Prasad <hanumath.prasad@stericsson.com>
---
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)
	- remove unnecessary code.

 drivers/mmc/card/block.c   |   10 +++++
 drivers/mmc/card/queue.c   |    4 ++
 drivers/mmc/core/core.c    |   87
++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/mmc.c     |    8 ++++
 drivers/mmc/core/mmc_ops.c |    6 +++-
 include/linux/mmc/card.h   |   12 ++++++
 include/linux/mmc/core.h   |    3 ++
 include/linux/mmc/host.h   |    1 +
 include/linux/mmc/mmc.h    |   14 +++++++
 9 files changed, 144 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index c80bb6d..9d19ebb 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1188,6 +1188,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq,
struct request *rqc)
 		type = rq_data_dir(req) == READ ? MMC_BLK_READ :
MMC_BLK_WRITE;
 		mmc_queue_bounce_post(mq_rq);
 
+		/*
+		 * Check BKOPS urgency from each R1 response
+		 */
+		if (mmc_card_mmc(card) &&
+			(brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) {
+			if (mmc_is_exception_event(card,
+					EXT_CSD_URGENT_BKOPS))
+				mmc_card_set_need_bkops(card);
+		}
+
 		switch (status) {
 		case MMC_BLK_SUCCESS:
 		case MMC_BLK_PARTIAL:
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index dcad59c..20bb4a5 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -61,6 +61,9 @@ 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_interrupt_bkops(mq->card);
+
 			set_current_state(TASK_RUNNING);
 			mq->issue_fn(mq, req);
 		} else {
@@ -68,6 +71,7 @@ static int mmc_queue_thread(void *d)
 				set_current_state(TASK_RUNNING);
 				break;
 			}
+			mmc_start_bkops(mq->card);
 			up(&mq->thread_sem);
 			schedule();
 			down(&mq->thread_sem);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 7ee2e07..2d2f1d5 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -238,6 +238,50 @@ 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;
+	unsigned long flags;
+
+	BUG_ON(!card);
+	if ((!card->ext_csd.bkops_en) ||
+			!(card->host->caps2 & MMC_CAP2_BKOPS))
+		return;
+
+	/*
+	 * 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);
+	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+			EXT_CSD_BKOPS_START, 1, 0);
+	if (err) {
+		pr_warning("error %d starting bkops\n", err);
+		mmc_card_clr_need_bkops(card);
+		goto out;
+	}
+	spin_lock_irqsave(&card->host->lock, flags);
+	mmc_card_clr_need_bkops(card);
+	mmc_card_set_doing_bkops(card);
+	spin_unlock_irqrestore(&card->host->lock, flags);
+out:
+	mmc_release_host(card->host);
+}
+EXPORT_SYMBOL(mmc_start_bkops);
+
 static void mmc_wait_done(struct mmc_request *mrq)
 {
 	complete(&mrq->completion);
@@ -466,6 +510,49 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
mmc_command *cmd, int retries
 EXPORT_SYMBOL(mmc_wait_for_cmd);
 
 /**
+ *	mmc_interrupt_bkops - interrupt ongoing BKOPS
+ *	@card: MMC card to check BKOPS
+ *
+ *	Send HPI command to interrupt 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_interrupt_bkops(struct mmc_card *card)
+{
+	int err = 0;
+	unsigned long flags;
+
+	BUG_ON(!card);
+
+	err = mmc_interrupt_hpi(card);
+
+	spin_lock_irqsave(&card->host->lock, flags);
+	mmc_card_clr_doing_bkops(card);
+	spin_unlock_irqrestore(&card->host->lock, flags);
+
+	return err;
+}
+EXPORT_SYMBOL(mmc_interrupt_bkops);
+
+int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
+{
+	int err;
+	u8 ext_csd[512];
+
+	/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
+	if (card->ext_csd.rev == 5)
+		return 1;
+
+	err = mmc_send_ext_csd(card, ext_csd);
+	if (err)
+		return err;
+
+	return (ext_csd[EXT_CSD_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
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a1223bd..514ad0f 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -448,6 +448,14 @@ 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];
+		}
+
 		/* 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 007863e..2e397d5 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -398,6 +398,10 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index,
u8 value,
 	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);
@@ -568,7 +572,7 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
 
 	err = mmc_wait_for_cmd(card->host, &cmd, 0);
 	if (err) {
-		pr_warn("%s: error %d interrupting operation. "
+		pr_debug("%s: error %d interrupting operation. "
 			"HPI command response %#x\n",
mmc_hostname(card->host),
 			err, cmd.resp[0]);
 		return err;
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 534974c..3c983d7 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -70,6 +70,8 @@ struct mmc_ext_csd {
 	unsigned int		cache_size;		/* Units: KB */
 	bool			hpi_en;			/* HPI enablebit */
 	bool			hpi;			/* HPI support bit
*/
+	bool			bkops;		/* background support bit */
+	bool			bkops_en;	/* background enable bit */
 	unsigned int		hpi_cmd;		/* cmd used as HPI
*/
 	u8			raw_partition_support;	/* 160 */
 	u8			raw_erased_mem_count;	/* 181 */
@@ -84,6 +86,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;
@@ -209,6 +212,8 @@ struct mmc_card {
 #define MMC_STATE_HIGHSPEED_DDR (1<<4)		/* card is in high speed
mode */
 #define MMC_STATE_ULTRAHIGHSPEED (1<<5)		/* card is in ultra
high speed mode */
 #define MMC_CARD_SDXC		(1<<6)		/* card is SDXC */
+#define MMC_STATE_NEED_BKOPS	(1<<7)		/* card need to do BKOPS */
+#define MMC_STATE_DOING_BKOPS	(1<<8)		/* card is doing 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 */
@@ -370,6 +375,8 @@ static inline void __maybe_unused remove_quirk(struct
mmc_card *card, int data)
 #define mmc_card_uhs(c)		((c)->state &
MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_sd_card_uhs(c)	((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
+#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_set_present(c)	((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -379,6 +386,11 @@ static inline void __maybe_unused remove_quirk(struct
mmc_card *card, int data)
 #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
+#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_clr_need_bkops(c)	((c)->state &=
~MMC_STATE_NEED_BKOPS)
+#define mmc_card_clr_doing_bkops(c)	((c)->state &=
~MMC_STATE_DOING_BKOPS)
 
 /*
  * Quirk add/remove for MMC products.
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 174a844..f6b997a 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -134,6 +134,8 @@ struct mmc_host;
 struct mmc_card;
 struct mmc_async_req;
 
+extern int mmc_interrupt_bkops(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 *);
@@ -163,6 +165,7 @@ extern int mmc_can_sanitize(struct mmc_card *card);
 extern int mmc_can_secure_erase_trim(struct mmc_card *card);
 extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int
from,
 				   unsigned int nr);
+extern void mmc_start_bkops(struct mmc_card *card);
 extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
 
 extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 706f722..4629e84 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -242,6 +242,7 @@ struct mmc_host {
 #define MMC_CAP2_CACHE_CTRL	(1 << 1)	/* Allow cache control */
 #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)	/* Notify poweroff supported
*/
 #define MMC_CAP2_NO_MULTI_READ	(1 << 3)	/* Multiblock reads don't
work */
+#define MMC_CAP2_BKOPS		(1 << 4)	/* 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 0e71356..20b1420 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -138,6 +138,7 @@ static inline bool mmc_op_multi(u32 opcode)
 #define R1_CURRENT_STATE(x)	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
 #define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
 #define R1_SWITCH_ERROR		(1 << 7)	/* sx, c */
+#define R1_EXCEPTION_EVENT	(1 << 6)	/* sx, a */
 #define R1_APP_CMD		(1 << 5)	/* sr, c */
 
 #define R1_STATE_IDLE	0
@@ -273,11 +274,14 @@ 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_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_ERASE_GROUP_DEF		175	/* R/W */
@@ -310,9 +314,11 @@ 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_BKOPS_SUPPORT		502	/* RO */
 #define EXT_CSD_HPI_FEATURES		503	/* RO */
 
 /*
@@ -373,4 +379,12 @@ 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 */
 
+/*
+ * 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 */
--
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 related	[flat|nested] 14+ messages in thread

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-17 15:01 ` Konstantin Dorfman
@ 2011-11-17 16:32   ` Per Forlin
  2011-11-20 16:12     ` Konstantin Dorfman
  0 siblings, 1 reply; 14+ messages in thread
From: Per Forlin @ 2011-11-17 16:32 UTC (permalink / raw)
  To: Konstantin Dorfman
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Sebastian Rasmussen, Dong, Chuanxiao, svenkatr

On Thu, Nov 17, 2011 at 4:01 PM, Konstantin Dorfman
<kdorfman@codeaurora.org> wrote:
> Hello Jaenhoon,
>
> I have a few suggestions regarding the situation when Host starts BKOPS:
>
> 1) Host should start BKOPS (based on BKOPS_STATUS or URGENT_BKOPS event)
> when going to mmc_sleep, which means that the bus is in idle state (this
> also covers the case in mmc_queue_thread, because in this case no I/O
> request exists). It seems like checking the status periodically in addition
> to mmc_suspend is not needed. Since if the device was in idle and we
> performed a single BKOPS then there is no point in performing another BKOPS
> unless there was actually another I/O request.
>
> 2) Also this implies an answer to the question about need to interrupt BKOPS
> before powering off card - the answer is no.
By the answer no you mean there is no risk of data corruption if
cutting power in the middle of a BKOPS. When the card is powered up
next time it will verify that bkops is in a defined state, and do
recovery if necessary. An interesting comment I got from Sebastian is
if this recovery mechanism affects card boot time.
The question is: May the card boot up slower (due to recovery) next
time if BKOPS was ongoing at power off?
I assume this recovery time should be insignificant, but I don't know for sure.

> 3) Based on statistical data we have (day long test) it looks like we do not
> need to do any preventive BKOPS caused by BKOPS_STATUS less than critical
> (0x3).
I proposed this "preventive action" but at that time I didn't have any
data to back it up with. I've run some day long tests and what I could
see is when BKOPS_LEVEL goes from 0 to 1, it goes back to 0 without
having to start BKOPS. Can you confirm this with your tests as well?
One explanation I got for this is that level of 1 only means the eMMC
internal cache is fragmented and not the actual memory.

Thanks,
Per

> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org
> [mailto:linux-mmc-owner@vger.kernel.org] On Behalf Of Jaehoon Chung
> Sent: Thursday, November 17, 2011 2:50 AM
> To: linux-mmc
> Cc: Chris Ball; Kyungmin Park; Hanumath Prasad; Per Forlin; Sebastian
> Rasmussen; Dong, Chuanxiao; svenkatr@ti.com
> Subject: [PATCH v3] mmc: support BKOPS feature for eMMC
>
> 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 you want to enable this feature, set MMC_CAP2_BKOPS.
>
> Future considerations
>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>  * Interrupt ongoing BKOPS before powering off the card.
>
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> CC: Hanumath Prasad <hanumath.prasad@stericsson.com>
> ---
> 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)
>        - remove unnecessary code.
>
>  drivers/mmc/card/block.c   |   10 +++++
>  drivers/mmc/card/queue.c   |    4 ++
>  drivers/mmc/core/core.c    |   87
> ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/core/mmc.c     |    8 ++++
>  drivers/mmc/core/mmc_ops.c |    6 +++-
>  include/linux/mmc/card.h   |   12 ++++++
>  include/linux/mmc/core.h   |    3 ++
>  include/linux/mmc/host.h   |    1 +
>  include/linux/mmc/mmc.h    |   14 +++++++
>  9 files changed, 144 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
> index c80bb6d..9d19ebb 100644
> --- a/drivers/mmc/card/block.c
> +++ b/drivers/mmc/card/block.c
> @@ -1188,6 +1188,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq,
> struct request *rqc)
>                type = rq_data_dir(req) == READ ? MMC_BLK_READ :
> MMC_BLK_WRITE;
>                mmc_queue_bounce_post(mq_rq);
>
> +               /*
> +                * Check BKOPS urgency from each R1 response
> +                */
> +               if (mmc_card_mmc(card) &&
> +                       (brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) {
> +                       if (mmc_is_exception_event(card,
> +                                       EXT_CSD_URGENT_BKOPS))
> +                               mmc_card_set_need_bkops(card);
> +               }
> +
>                switch (status) {
>                case MMC_BLK_SUCCESS:
>                case MMC_BLK_PARTIAL:
> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
> index dcad59c..20bb4a5 100644
> --- a/drivers/mmc/card/queue.c
> +++ b/drivers/mmc/card/queue.c
> @@ -61,6 +61,9 @@ 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_interrupt_bkops(mq->card);
> +
>                        set_current_state(TASK_RUNNING);
>                        mq->issue_fn(mq, req);
>                } else {
> @@ -68,6 +71,7 @@ static int mmc_queue_thread(void *d)
>                                set_current_state(TASK_RUNNING);
>                                break;
>                        }
> +                       mmc_start_bkops(mq->card);
>                        up(&mq->thread_sem);
>                        schedule();
>                        down(&mq->thread_sem);
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index 7ee2e07..2d2f1d5 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -238,6 +238,50 @@ 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;
> +       unsigned long flags;
> +
> +       BUG_ON(!card);
> +       if ((!card->ext_csd.bkops_en) ||
> +                       !(card->host->caps2 & MMC_CAP2_BKOPS))
> +               return;
> +
> +       /*
> +        * 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);
> +       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +                       EXT_CSD_BKOPS_START, 1, 0);
> +       if (err) {
> +               pr_warning("error %d starting bkops\n", err);
> +               mmc_card_clr_need_bkops(card);
> +               goto out;
> +       }
> +       spin_lock_irqsave(&card->host->lock, flags);
> +       mmc_card_clr_need_bkops(card);
> +       mmc_card_set_doing_bkops(card);
> +       spin_unlock_irqrestore(&card->host->lock, flags);
> +out:
> +       mmc_release_host(card->host);
> +}
> +EXPORT_SYMBOL(mmc_start_bkops);
> +
>  static void mmc_wait_done(struct mmc_request *mrq)
>  {
>        complete(&mrq->completion);
> @@ -466,6 +510,49 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
> mmc_command *cmd, int retries
>  EXPORT_SYMBOL(mmc_wait_for_cmd);
>
>  /**
> + *     mmc_interrupt_bkops - interrupt ongoing BKOPS
> + *     @card: MMC card to check BKOPS
> + *
> + *     Send HPI command to interrupt 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_interrupt_bkops(struct mmc_card *card)
> +{
> +       int err = 0;
> +       unsigned long flags;
> +
> +       BUG_ON(!card);
> +
> +       err = mmc_interrupt_hpi(card);
> +
> +       spin_lock_irqsave(&card->host->lock, flags);
> +       mmc_card_clr_doing_bkops(card);
> +       spin_unlock_irqrestore(&card->host->lock, flags);
> +
> +       return err;
> +}
> +EXPORT_SYMBOL(mmc_interrupt_bkops);
> +
> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
> +{
> +       int err;
> +       u8 ext_csd[512];
> +
> +       /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
> +       if (card->ext_csd.rev == 5)
> +               return 1;
> +
> +       err = mmc_send_ext_csd(card, ext_csd);
> +       if (err)
> +               return err;
> +
> +       return (ext_csd[EXT_CSD_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
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index a1223bd..514ad0f 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -448,6 +448,14 @@ 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];
> +               }
> +
>                /* 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 007863e..2e397d5 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -398,6 +398,10 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index,
> u8 value,
>        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);
> @@ -568,7 +572,7 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
>
>        err = mmc_wait_for_cmd(card->host, &cmd, 0);
>        if (err) {
> -               pr_warn("%s: error %d interrupting operation. "
> +               pr_debug("%s: error %d interrupting operation. "
>                        "HPI command response %#x\n",
> mmc_hostname(card->host),
>                        err, cmd.resp[0]);
>                return err;
> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
> index 534974c..3c983d7 100644
> --- a/include/linux/mmc/card.h
> +++ b/include/linux/mmc/card.h
> @@ -70,6 +70,8 @@ struct mmc_ext_csd {
>        unsigned int            cache_size;             /* Units: KB */
>        bool                    hpi_en;                 /* HPI enablebit */
>        bool                    hpi;                    /* HPI support bit
> */
> +       bool                    bkops;          /* background support bit */
> +       bool                    bkops_en;       /* background enable bit */
>        unsigned int            hpi_cmd;                /* cmd used as HPI
> */
>        u8                      raw_partition_support;  /* 160 */
>        u8                      raw_erased_mem_count;   /* 181 */
> @@ -84,6 +86,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;
> @@ -209,6 +212,8 @@ struct mmc_card {
>  #define MMC_STATE_HIGHSPEED_DDR (1<<4)         /* card is in high speed
> mode */
>  #define MMC_STATE_ULTRAHIGHSPEED (1<<5)                /* card is in ultra
> high speed mode */
>  #define MMC_CARD_SDXC          (1<<6)          /* card is SDXC */
> +#define MMC_STATE_NEED_BKOPS   (1<<7)          /* card need to do BKOPS */
> +#define MMC_STATE_DOING_BKOPS  (1<<8)          /* card is doing 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 */
> @@ -370,6 +375,8 @@ static inline void __maybe_unused remove_quirk(struct
> mmc_card *card, int data)
>  #define mmc_card_uhs(c)                ((c)->state &
> MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_sd_card_uhs(c)     ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
> +#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_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
> @@ -379,6 +386,11 @@ static inline void __maybe_unused remove_quirk(struct
> mmc_card *card, int data)
>  #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
>  #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
> +#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_clr_need_bkops(c)     ((c)->state &=
> ~MMC_STATE_NEED_BKOPS)
> +#define mmc_card_clr_doing_bkops(c)    ((c)->state &=
> ~MMC_STATE_DOING_BKOPS)
>
>  /*
>  * Quirk add/remove for MMC products.
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index 174a844..f6b997a 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -134,6 +134,8 @@ struct mmc_host;
>  struct mmc_card;
>  struct mmc_async_req;
>
> +extern int mmc_interrupt_bkops(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 *);
> @@ -163,6 +165,7 @@ extern int mmc_can_sanitize(struct mmc_card *card);
>  extern int mmc_can_secure_erase_trim(struct mmc_card *card);
>  extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int
> from,
>                                   unsigned int nr);
> +extern void mmc_start_bkops(struct mmc_card *card);
>  extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
>
>  extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 706f722..4629e84 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -242,6 +242,7 @@ struct mmc_host {
>  #define MMC_CAP2_CACHE_CTRL    (1 << 1)        /* Allow cache control */
>  #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)      /* Notify poweroff supported
> */
>  #define MMC_CAP2_NO_MULTI_READ (1 << 3)        /* Multiblock reads don't
> work */
> +#define MMC_CAP2_BKOPS         (1 << 4)        /* 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 0e71356..20b1420 100644
> --- a/include/linux/mmc/mmc.h
> +++ b/include/linux/mmc/mmc.h
> @@ -138,6 +138,7 @@ static inline bool mmc_op_multi(u32 opcode)
>  #define R1_CURRENT_STATE(x)    ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
>  #define R1_READY_FOR_DATA      (1 << 8)        /* sx, a */
>  #define R1_SWITCH_ERROR                (1 << 7)        /* sx, c */
> +#define R1_EXCEPTION_EVENT     (1 << 6)        /* sx, a */
>  #define R1_APP_CMD             (1 << 5)        /* sr, c */
>
>  #define R1_STATE_IDLE  0
> @@ -273,11 +274,14 @@ 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_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_ERASE_GROUP_DEF                175     /* R/W */
> @@ -310,9 +314,11 @@ 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_BKOPS_SUPPORT          502     /* RO */
>  #define EXT_CSD_HPI_FEATURES           503     /* RO */
>
>  /*
> @@ -373,4 +379,12 @@ 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 */
>
> +/*
> + * 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 */
> --
> 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] 14+ messages in thread

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-17 16:32   ` Per Forlin
@ 2011-11-20 16:12     ` Konstantin Dorfman
  2011-11-20 22:42       ` Per Forlin
  0 siblings, 1 reply; 14+ messages in thread
From: Konstantin Dorfman @ 2011-11-20 16:12 UTC (permalink / raw)
  To: Per Forlin
  Cc: Konstantin Dorfman, Jaehoon Chung, linux-mmc, Chris Ball,
	Kyungmin Park, Hanumath Prasad, Sebastian Rasmussen,
	Dong, Chuanxiao, svenkatr

Hello Per,

> On Thu, Nov 17, 2011 at 4:01 PM, Konstantin Dorfman
> <kdorfman@codeaurora.org> wrote:
>> Hello Jaenhoon,
>>
>> I have a few suggestions regarding the situation when Host starts BKOPS:
>>
>> 1) Host should start BKOPS (based on BKOPS_STATUS or URGENT_BKOPS event)
>> when going to mmc_sleep, which means that the bus is in idle state (this
>> also covers the case in mmc_queue_thread, because in this case no I/O
>> request exists). It seems like checking the status periodically in
>> addition
>> to mmc_suspend is not needed. Since if the device was in idle and we
>> performed a single BKOPS then there is no point in performing another
>> BKOPS
>> unless there was actually another I/O request.
>>
>> 2) Also this implies an answer to the question about need to interrupt
>> BKOPS
>> before powering off card - the answer is no.
> By the answer no you mean there is no risk of data corruption if
> cutting power in the middle of a BKOPS. When the card is powered up
> next time it will verify that bkops is in a defined state, and do
> recovery if necessary. An interesting comment I got from Sebastian is
> if this recovery mechanism affects card boot time.
> The question is: May the card boot up slower (due to recovery) next
> time if BKOPS was ongoing at power off?
> I assume this recovery time should be insignificant, but I don't know for
> sure.
>
Let me explain proposed flow:
The only trigger to start BKOPS command should be mmc_power_off() function,
just before sending POWER_OFF_NOTIFICATION[34] and only when BKOPS is needed
(by needed I understand situation, when URGENT_BKOPS event arrived or
BKOPS_STATUS 0x2 or 0x3).
The flow will wait till BKOPS successfully completed and than continue to
powering off the card. Power off will never occur in the middle of BKOPS.
Also do not need to start BKOPS when mmc_queue_thread() is in IDLE state
(no requests exists), because in such case power off should be done to card.

>> 3) Based on statistical data we have (day long test) it looks like we do
>> not
>> need to do any preventive BKOPS caused by BKOPS_STATUS less than
>> critical
>> (0x3).
> I proposed this "preventive action" but at that time I didn't have any
> data to back it up with. I've run some day long tests and what I could
> see is when BKOPS_LEVEL goes from 0 to 1, it goes back to 0 without
> having to start BKOPS. Can you confirm this with your tests as well?
> One explanation I got for this is that level of 1 only means the eMMC
> internal cache is fragmented and not the actual memory.
We have such data from card vendors, but I plan to do similar tests to
confirm.
I will update about the results.

Thanks,
Kostya

>
> Thanks,
> Per
>
>> -----Original Message-----
>> From: linux-mmc-owner@vger.kernel.org
>> [mailto:linux-mmc-owner@vger.kernel.org] On Behalf Of Jaehoon Chung
>> Sent: Thursday, November 17, 2011 2:50 AM
>> To: linux-mmc
>> Cc: Chris Ball; Kyungmin Park; Hanumath Prasad; Per Forlin; Sebastian
>> Rasmussen; Dong, Chuanxiao; svenkatr@ti.com
>> Subject: [PATCH v3] mmc: support BKOPS feature for eMMC
>>
>> 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 you want to enable this feature, set MMC_CAP2_BKOPS.
>>
>> Future considerations
>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>  * Interrupt ongoing BKOPS before powering off the card.
>>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> CC: Hanumath Prasad <hanumath.prasad@stericsson.com>
>> ---
>> 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)
>>        - remove unnecessary code.
>>
>>  drivers/mmc/card/block.c   |   10 +++++
>>  drivers/mmc/card/queue.c   |    4 ++
>>  drivers/mmc/core/core.c    |   87
>> ++++++++++++++++++++++++++++++++++++++++++++
>>  drivers/mmc/core/mmc.c     |    8 ++++
>>  drivers/mmc/core/mmc_ops.c |    6 +++-
>>  include/linux/mmc/card.h   |   12 ++++++
>>  include/linux/mmc/core.h   |    3 ++
>>  include/linux/mmc/host.h   |    1 +
>>  include/linux/mmc/mmc.h    |   14 +++++++
>>  9 files changed, 144 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
>> index c80bb6d..9d19ebb 100644
>> --- a/drivers/mmc/card/block.c
>> +++ b/drivers/mmc/card/block.c
>> @@ -1188,6 +1188,16 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue
>> *mq,
>> struct request *rqc)
>>                type = rq_data_dir(req) == READ ? MMC_BLK_READ :
>> MMC_BLK_WRITE;
>>                mmc_queue_bounce_post(mq_rq);
>>
>> +               /*
>> +                * Check BKOPS urgency from each R1 response
>> +                */
>> +               if (mmc_card_mmc(card) &&
>> +                       (brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) {
>> +                       if (mmc_is_exception_event(card,
>> +                                       EXT_CSD_URGENT_BKOPS))
>> +                               mmc_card_set_need_bkops(card);
>> +               }
>> +
>>                switch (status) {
>>                case MMC_BLK_SUCCESS:
>>                case MMC_BLK_PARTIAL:
>> diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
>> index dcad59c..20bb4a5 100644
>> --- a/drivers/mmc/card/queue.c
>> +++ b/drivers/mmc/card/queue.c
>> @@ -61,6 +61,9 @@ 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_interrupt_bkops(mq->card);
>> +
>>                        set_current_state(TASK_RUNNING);
>>                        mq->issue_fn(mq, req);
>>                } else {
>> @@ -68,6 +71,7 @@ static int mmc_queue_thread(void *d)
>>                                set_current_state(TASK_RUNNING);
>>                                break;
>>                        }
>> +                       mmc_start_bkops(mq->card);
>>                        up(&mq->thread_sem);
>>                        schedule();
>>                        down(&mq->thread_sem);
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 7ee2e07..2d2f1d5 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -238,6 +238,50 @@ 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;
>> +       unsigned long flags;
>> +
>> +       BUG_ON(!card);
>> +       if ((!card->ext_csd.bkops_en) ||
>> +                       !(card->host->caps2 & MMC_CAP2_BKOPS))
>> +               return;
>> +
>> +       /*
>> +        * 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);
>> +       err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                       EXT_CSD_BKOPS_START, 1, 0);
>> +       if (err) {
>> +               pr_warning("error %d starting bkops\n", err);
>> +               mmc_card_clr_need_bkops(card);
>> +               goto out;
>> +       }
>> +       spin_lock_irqsave(&card->host->lock, flags);
>> +       mmc_card_clr_need_bkops(card);
>> +       mmc_card_set_doing_bkops(card);
>> +       spin_unlock_irqrestore(&card->host->lock, flags);
>> +out:
>> +       mmc_release_host(card->host);
>> +}
>> +EXPORT_SYMBOL(mmc_start_bkops);
>> +
>>  static void mmc_wait_done(struct mmc_request *mrq)
>>  {
>>        complete(&mrq->completion);
>> @@ -466,6 +510,49 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
>> mmc_command *cmd, int retries
>>  EXPORT_SYMBOL(mmc_wait_for_cmd);
>>
>>  /**
>> + *     mmc_interrupt_bkops - interrupt ongoing BKOPS
>> + *     @card: MMC card to check BKOPS
>> + *
>> + *     Send HPI command to interrupt 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_interrupt_bkops(struct mmc_card *card)
>> +{
>> +       int err = 0;
>> +       unsigned long flags;
>> +
>> +       BUG_ON(!card);
>> +
>> +       err = mmc_interrupt_hpi(card);
>> +
>> +       spin_lock_irqsave(&card->host->lock, flags);
>> +       mmc_card_clr_doing_bkops(card);
>> +       spin_unlock_irqrestore(&card->host->lock, flags);
>> +
>> +       return err;
>> +}
>> +EXPORT_SYMBOL(mmc_interrupt_bkops);
>> +
>> +int mmc_is_exception_event(struct mmc_card *card, unsigned int value)
>> +{
>> +       int err;
>> +       u8 ext_csd[512];
>> +
>> +       /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */
>> +       if (card->ext_csd.rev == 5)
>> +               return 1;
>> +
>> +       err = mmc_send_ext_csd(card, ext_csd);
>> +       if (err)
>> +               return err;
>> +
>> +       return (ext_csd[EXT_CSD_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
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index a1223bd..514ad0f 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -448,6 +448,14 @@ 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];
>> +               }
>> +
>>                /* 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 007863e..2e397d5 100644
>> --- a/drivers/mmc/core/mmc_ops.c
>> +++ b/drivers/mmc/core/mmc_ops.c
>> @@ -398,6 +398,10 @@ int mmc_switch(struct mmc_card *card, u8 set, u8
>> index,
>> u8 value,
>>        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);
>> @@ -568,7 +572,7 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32
>> *status)
>>
>>        err = mmc_wait_for_cmd(card->host, &cmd, 0);
>>        if (err) {
>> -               pr_warn("%s: error %d interrupting operation. "
>> +               pr_debug("%s: error %d interrupting operation. "
>>                        "HPI command response %#x\n",
>> mmc_hostname(card->host),
>>                        err, cmd.resp[0]);
>>                return err;
>> diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
>> index 534974c..3c983d7 100644
>> --- a/include/linux/mmc/card.h
>> +++ b/include/linux/mmc/card.h
>> @@ -70,6 +70,8 @@ struct mmc_ext_csd {
>>        unsigned int            cache_size;             /* Units: KB */
>>        bool                    hpi_en;                 /* HPI enablebit
>> */
>>        bool                    hpi;                    /* HPI support
>> bit
>> */
>> +       bool                    bkops;          /* background support
>> bit */
>> +       bool                    bkops_en;       /* background enable bit
>> */
>>        unsigned int            hpi_cmd;                /* cmd used as
>> HPI
>> */
>>        u8                      raw_partition_support;  /* 160 */
>>        u8                      raw_erased_mem_count;   /* 181 */
>> @@ -84,6 +86,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;
>> @@ -209,6 +212,8 @@ struct mmc_card {
>>  #define MMC_STATE_HIGHSPEED_DDR (1<<4)         /* card is in high speed
>> mode */
>>  #define MMC_STATE_ULTRAHIGHSPEED (1<<5)                /* card is in
>> ultra
>> high speed mode */
>>  #define MMC_CARD_SDXC          (1<<6)          /* card is SDXC */
>> +#define MMC_STATE_NEED_BKOPS   (1<<7)          /* card need to do BKOPS
>> */
>> +#define MMC_STATE_DOING_BKOPS  (1<<8)          /* card is doing 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
>> */
>> @@ -370,6 +375,8 @@ static inline void __maybe_unused
>> remove_quirk(struct
>> mmc_card *card, int data)
>>  #define mmc_card_uhs(c)                ((c)->state &
>> MMC_STATE_ULTRAHIGHSPEED)
>>  #define mmc_sd_card_uhs(c)     ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
>>  #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
>> +#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_set_present(c)        ((c)->state |=
>> MMC_STATE_PRESENT)
>>  #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
>> @@ -379,6 +386,11 @@ static inline void __maybe_unused
>> remove_quirk(struct
>> mmc_card *card, int data)
>>  #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
>>  #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
>>  #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
>> +#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_clr_need_bkops(c)     ((c)->state &=
>> ~MMC_STATE_NEED_BKOPS)
>> +#define mmc_card_clr_doing_bkops(c)    ((c)->state &=
>> ~MMC_STATE_DOING_BKOPS)
>>
>>  /*
>>  * Quirk add/remove for MMC products.
>> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
>> index 174a844..f6b997a 100644
>> --- a/include/linux/mmc/core.h
>> +++ b/include/linux/mmc/core.h
>> @@ -134,6 +134,8 @@ struct mmc_host;
>>  struct mmc_card;
>>  struct mmc_async_req;
>>
>> +extern int mmc_interrupt_bkops(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 *);
>> @@ -163,6 +165,7 @@ extern int mmc_can_sanitize(struct mmc_card *card);
>>  extern int mmc_can_secure_erase_trim(struct mmc_card *card);
>>  extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int
>> from,
>>                                   unsigned int nr);
>> +extern void mmc_start_bkops(struct mmc_card *card);
>>  extern unsigned int mmc_calc_max_discard(struct mmc_card *card);
>>
>>  extern int mmc_set_blocklen(struct mmc_card *card, unsigned int
>> blocklen);
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index 706f722..4629e84 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -242,6 +242,7 @@ struct mmc_host {
>>  #define MMC_CAP2_CACHE_CTRL    (1 << 1)        /* Allow cache control
>> */
>>  #define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)      /* Notify poweroff
>> supported
>> */
>>  #define MMC_CAP2_NO_MULTI_READ (1 << 3)        /* Multiblock reads
>> don't
>> work */
>> +#define MMC_CAP2_BKOPS         (1 << 4)        /* 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 0e71356..20b1420 100644
>> --- a/include/linux/mmc/mmc.h
>> +++ b/include/linux/mmc/mmc.h
>> @@ -138,6 +138,7 @@ static inline bool mmc_op_multi(u32 opcode)
>>  #define R1_CURRENT_STATE(x)    ((x & 0x00001E00) >> 9) /* sx, b (4
>> bits) */
>>  #define R1_READY_FOR_DATA      (1 << 8)        /* sx, a */
>>  #define R1_SWITCH_ERROR                (1 << 7)        /* sx, c */
>> +#define R1_EXCEPTION_EVENT     (1 << 6)        /* sx, a */
>>  #define R1_APP_CMD             (1 << 5)        /* sr, c */
>>
>>  #define R1_STATE_IDLE  0
>> @@ -273,11 +274,14 @@ 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_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_ERASE_GROUP_DEF                175     /* R/W */
>> @@ -310,9 +314,11 @@ 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_BKOPS_SUPPORT          502     /* RO */
>>  #define EXT_CSD_HPI_FEATURES           503     /* RO */
>>
>>  /*
>> @@ -373,4 +379,12 @@ 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
>> */
>>
>> +/*
>> + * 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 */
>> --
>> 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
>>
>>
> --
> 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] 14+ messages in thread

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-20 16:12     ` Konstantin Dorfman
@ 2011-11-20 22:42       ` Per Forlin
  0 siblings, 0 replies; 14+ messages in thread
From: Per Forlin @ 2011-11-20 22:42 UTC (permalink / raw)
  To: Konstantin Dorfman
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Sebastian Rasmussen, Dong, Chuanxiao, svenkatr

Hi Konstantin,

On Sun, Nov 20, 2011 at 5:12 PM, Konstantin Dorfman
<kdorfman@codeaurora.org> wrote:
> Hello Per,
>
>> On Thu, Nov 17, 2011 at 4:01 PM, Konstantin Dorfman
>> <kdorfman@codeaurora.org> wrote:
>>> Hello Jaenhoon,
>>>
>>> I have a few suggestions regarding the situation when Host starts BKOPS:
>>>
>>> 1) Host should start BKOPS (based on BKOPS_STATUS or URGENT_BKOPS event)
>>> when going to mmc_sleep, which means that the bus is in idle state (this
>>> also covers the case in mmc_queue_thread, because in this case no I/O
>>> request exists). It seems like checking the status periodically in
>>> addition
>>> to mmc_suspend is not needed. Since if the device was in idle and we
>>> performed a single BKOPS then there is no point in performing another
>>> BKOPS
>>> unless there was actually another I/O request.
>>>
>>> 2) Also this implies an answer to the question about need to interrupt
>>> BKOPS
>>> before powering off card - the answer is no.
>> By the answer no you mean there is no risk of data corruption if
>> cutting power in the middle of a BKOPS. When the card is powered up
>> next time it will verify that bkops is in a defined state, and do
>> recovery if necessary. An interesting comment I got from Sebastian is
>> if this recovery mechanism affects card boot time.
>> The question is: May the card boot up slower (due to recovery) next
>> time if BKOPS was ongoing at power off?
>> I assume this recovery time should be insignificant, but I don't know for
>> sure.
>>
> Let me explain proposed flow:
> The only trigger to start BKOPS command should be mmc_power_off() function,
> just before sending POWER_OFF_NOTIFICATION[34] and only when BKOPS is needed
> (by needed I understand situation, when URGENT_BKOPS event arrived or
> BKOPS_STATUS 0x2 or 0x3).
> The flow will wait till BKOPS successfully completed and than continue to
> powering off the card. Power off will never occur in the middle of BKOPS.
> Also do not need to start BKOPS when mmc_queue_thread() is in IDLE state
> (no requests exists), because in such case power off should be done to card.
>
I wonder how this works with suspend.
If suspend is called on the system, MMC should suspend quickly and not
wait for the BKOPS to finish.
For pm_runtime_suspend it's fine to return -EBUSY and defer
pm_runtime_suspend until BKOPS is completed.
mmc_power_on/off is too low level I think. What about adding this at
the suspend/resume level instead?

>>> 3) Based on statistical data we have (day long test) it looks like we do
>>> not
>>> need to do any preventive BKOPS caused by BKOPS_STATUS less than
>>> critical
>>> (0x3).
>> I proposed this "preventive action" but at that time I didn't have any
>> data to back it up with. I've run some day long tests and what I could
>> see is when BKOPS_LEVEL goes from 0 to 1, it goes back to 0 without
>> having to start BKOPS. Can you confirm this with your tests as well?
>> One explanation I got for this is that level of 1 only means the eMMC
>> internal cache is fragmented and not the actual memory.
> We have such data from card vendors, but I plan to do similar tests to
> confirm.
> I will update about the results.
Looking forward to see your results.

Thanks,
Per

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

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-17  0:50 [PATCH v3] mmc: support BKOPS feature for eMMC Jaehoon Chung
  2011-11-17 12:37 ` S, Venkatraman
  2011-11-17 15:01 ` Konstantin Dorfman
@ 2011-11-22 12:44 ` Konstantin Dorfman
  2011-11-22 14:00   ` Konstantin Dorfman
  2011-11-22 23:54   ` Jaehoon Chung
  2011-11-29 12:20 ` Adrian Hunter
  2011-12-01 16:13 ` Konstantin Dorfman
  4 siblings, 2 replies; 14+ messages in thread
From: Konstantin Dorfman @ 2011-11-22 12:44 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per Forlin,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com

Hello Jaehoon,

...
> +/**
> + *	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;
> +	unsigned long flags;
> +
> +	BUG_ON(!card);
> +	if ((!card->ext_csd.bkops_en) ||
> +			!(card->host->caps2 & MMC_CAP2_BKOPS))
> +		return;
> +
> +	/*
> +	 * 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);
> +	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +			EXT_CSD_BKOPS_START, 1, 0);
> +	if (err) {
> +		pr_warning("error %d starting bkops\n", err);
> +		mmc_card_clr_need_bkops(card);
> +		goto out;
> +	}
> +	spin_lock_irqsave(&card->host->lock, flags);
> +	mmc_card_clr_need_bkops(card);
> +	mmc_card_set_doing_bkops(card);
Sending CMD6 (by mmc_switch()) will start BKOPS on card,
according to eMMC spec:
-----
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.
-----
Where/when mmc_card_clear_doing_bkops() should called? The only place I
see this happens is
in mmc_interrupt_bkops(). This means, that once BKOPS started, next
read/write request
will _always_(and not when card really busy) send HPI and only then will
clear "doing" flag.

May be it is possible to poll for ack from card about when BKOPS finished?
Another possibility that
mmc_switch() synchronous and after flow returns from it, we can clear
"doing" flag immediately.
Does it make sense?
...
> +	spin_unlock_irqrestore(&card->host->lock, flags);
> +out:
> +	mmc_release_host(card->host);
> +}
> +EXPORT_SYMBOL(mmc_start_bkops);
> +

Thanks,
Kostya


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

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-22 12:44 ` Konstantin Dorfman
@ 2011-11-22 14:00   ` Konstantin Dorfman
  2011-11-22 23:54   ` Jaehoon Chung
  1 sibling, 0 replies; 14+ messages in thread
From: Konstantin Dorfman @ 2011-11-22 14:00 UTC (permalink / raw)
  To: Konstantin Dorfman
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per Forlin, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com

> Hello Jaehoon,
>
> ...
>> +/**
>> + *	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;
>> +	unsigned long flags;
>> +
>> +	BUG_ON(!card);
>> +	if ((!card->ext_csd.bkops_en) ||
>> +			!(card->host->caps2 & MMC_CAP2_BKOPS))
>> +		return;
>> +
>> +	/*
>> +	 * 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);
>> +	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +			EXT_CSD_BKOPS_START, 1, 0);
>> +	if (err) {
>> +		pr_warning("error %d starting bkops\n", err);
>> +		mmc_card_clr_need_bkops(card);
>> +		goto out;
>> +	}
>> +	spin_lock_irqsave(&card->host->lock, flags);
>> +	mmc_card_clr_need_bkops(card);
>> +	mmc_card_set_doing_bkops(card);
> Sending CMD6 (by mmc_switch()) will start BKOPS on card,
> according to eMMC spec:
> -----
> 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.
> -----
> Where/when mmc_card_clear_doing_bkops() should called? The only place I
> see this happens is
> in mmc_interrupt_bkops(). This means, that once BKOPS started, next
> read/write request
> will _always_(and not when card really busy) send HPI and only then will
> clear "doing" flag.
>
> May be it is possible to poll for ack from card about when BKOPS finished?
> Another possibility that
> mmc_switch() synchronous and after flow returns from it, we can clear
> "doing" flag immediately.
> Does it make sense?
> ...

Now I understand that mmc_switch() command was modified for BKOPS_START to
be non-blocking, so the flow never waits for BKOPS to be finished. Because
of this HPI is always sent before next request.
Since start bkops flow is not frequent, it is ok.
Thanks,
Kostya

>> +	spin_unlock_irqrestore(&card->host->lock, flags);
>> +out:
>> +	mmc_release_host(card->host);
>> +}
>> +EXPORT_SYMBOL(mmc_start_bkops);
>> +
>
> Thanks,
> Kostya
>
> --
> 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] 14+ messages in thread

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-22 12:44 ` Konstantin Dorfman
  2011-11-22 14:00   ` Konstantin Dorfman
@ 2011-11-22 23:54   ` Jaehoon Chung
  2011-11-28 11:33     ` Konstantin Dorfman
  1 sibling, 1 reply; 14+ messages in thread
From: Jaehoon Chung @ 2011-11-22 23:54 UTC (permalink / raw)
  To: Konstantin Dorfman
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per Forlin, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com

Hi Konstantin

On 11/22/2011 09:44 PM, Konstantin Dorfman wrote:

> Hello Jaehoon,
> 
> ...
>> +/**
>> + *	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;
>> +	unsigned long flags;
>> +
>> +	BUG_ON(!card);
>> +	if ((!card->ext_csd.bkops_en) ||
>> +			!(card->host->caps2 & MMC_CAP2_BKOPS))
>> +		return;
>> +
>> +	/*
>> +	 * 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);
>> +	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +			EXT_CSD_BKOPS_START, 1, 0);
>> +	if (err) {
>> +		pr_warning("error %d starting bkops\n", err);
>> +		mmc_card_clr_need_bkops(card);
>> +		goto out;
>> +	}
>> +	spin_lock_irqsave(&card->host->lock, flags);
>> +	mmc_card_clr_need_bkops(card);
>> +	mmc_card_set_doing_bkops(card);
> Sending CMD6 (by mmc_switch()) will start BKOPS on card,
> according to eMMC spec:
> -----
> 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.
> -----
> Where/when mmc_card_clear_doing_bkops() should called? The only place I
> see this happens is
> in mmc_interrupt_bkops(). This means, that once BKOPS started, next
> read/write request
> will _always_(and not when card really busy) send HPI and only then will
> clear "doing" flag.


Right, that is called only in mmc_interrupt_bkops. I wonder how did you check
to finish the bkops without hpi command?

> May be it is possible to poll for ack from card about when BKOPS finished?
> Another possibility that
> mmc_switch() synchronous and after flow returns from it, we can clear
> "doing" flag immediately.
> Does it make sense?

i didn't know how poll for ack from card..did you know?
So i implemented that send the hpi command whatever status.

If let me know checking for ack from card, i will consider that.

Thanks

Best Regards,
Jaehoon Chung

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

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-22 23:54   ` Jaehoon Chung
@ 2011-11-28 11:33     ` Konstantin Dorfman
  0 siblings, 0 replies; 14+ messages in thread
From: Konstantin Dorfman @ 2011-11-28 11:33 UTC (permalink / raw)
  Cc: Konstantin Dorfman, Jaehoon Chung, linux-mmc, Chris Ball,
	Kyungmin Park, Hanumath Prasad, Per Forlin, Sebastian Rasmussen,
	Dong, Chuanxiao, svenkatr@ti.com

Hi Jaehoon,

>
> i didn't know how poll for ack from card..did you know?
> So i implemented that send the hpi command whatever status.
>
> If let me know checking for ack from card, i will consider that.

Some of MMC controllers (in my case msm_sdcc) can process automatically
"busy line" and fire interrupt with "prog done" bit asserted, so we can
use handler of this irq to turn off "doing bkops" flag. Otherwise it is
possible to read mmc controller status and check busy line, in case it is
already released we can skip HPI command.

Thanks,
Kostya


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

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-17  0:50 [PATCH v3] mmc: support BKOPS feature for eMMC Jaehoon Chung
                   ` (2 preceding siblings ...)
  2011-11-22 12:44 ` Konstantin Dorfman
@ 2011-11-29 12:20 ` Adrian Hunter
  2011-11-30  4:09   ` Jaehoon Chung
  2011-12-01 16:13 ` Konstantin Dorfman
  4 siblings, 1 reply; 14+ messages in thread
From: Adrian Hunter @ 2011-11-29 12:20 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per Forlin,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com

On 17/11/11 02:50, 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 you want to enable this feature, set MMC_CAP2_BKOPS.
> 
> Future considerations
>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>  * Interrupt ongoing BKOPS before powering off the card.
> 
> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> CC: Hanumath Prasad <hanumath.prasad@stericsson.com>
> ---
> 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)
> 	- remove unnecessary code.
> 

The main problem with bkops is:

	If the status is at level 3 ("critical"), some operations
	may extend beyond their original timeouts due to maintenance
	operations which cannot be delayed anymore.

This means:

	1. at level 3 either bkops are run or the timeout of the next
	(data?) operation is extended

	2. at level 3 either the bkops must not be interrupted or the
	level must be rechecked after interruption and bkops run again
	if the level is still 3

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

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-29 12:20 ` Adrian Hunter
@ 2011-11-30  4:09   ` Jaehoon Chung
  0 siblings, 0 replies; 14+ messages in thread
From: Jaehoon Chung @ 2011-11-30  4:09 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

Hi Adrian.

On 11/29/2011 09:20 PM, Adrian Hunter wrote:

> On 17/11/11 02:50, 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 you want to enable this feature, set MMC_CAP2_BKOPS.
>>
>> Future considerations
>>  * Check BKOPS_LEVEL=1 and start BKOPS in a preventive manner.
>>  * Interrupt ongoing BKOPS before powering off the card.
>>
>> Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> CC: Hanumath Prasad <hanumath.prasad@stericsson.com>
>> ---
>> 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)
>> 	- remove unnecessary code.
>>
> 
> The main problem with bkops is:
> 
> 	If the status is at level 3 ("critical"), some operations
> 	may extend beyond their original timeouts due to maintenance
> 	operations which cannot be delayed anymore.
> 
> This means:
> 
> 	1. at level 3 either bkops are run or the timeout of the next
> 	(data?) operation is extended
> 
> 	2. at level 3 either the bkops must not be interrupted or the
> 	level must be rechecked after interruption and bkops run again
> 	if the level is still 3


Thanks for your comment..I will consider to solve this problem.

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] 14+ messages in thread

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-11-17  0:50 [PATCH v3] mmc: support BKOPS feature for eMMC Jaehoon Chung
                   ` (3 preceding siblings ...)
  2011-11-29 12:20 ` Adrian Hunter
@ 2011-12-01 16:13 ` Konstantin Dorfman
  2011-12-05 16:03   ` Jae hoon Chung
  4 siblings, 1 reply; 14+ messages in thread
From: Konstantin Dorfman @ 2011-12-01 16:13 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: linux-mmc, Chris Ball, Kyungmin Park, Hanumath Prasad, Per Forlin,
	Sebastian Rasmussen, Dong, Chuanxiao, svenkatr@ti.com

Hello Jaehoon,
After applying your patch and debugging it for some time,
I want to share my results and suggests some changes to the patch.
See comments below:

> +void mmc_start_bkops(struct mmc_card *card)
> +{
> +	int err;
> +	unsigned long flags;
> +
> +	BUG_ON(!card);
> +	if ((!card->ext_csd.bkops_en) ||
> +			!(card->host->caps2 & MMC_CAP2_BKOPS))
> +		return;
> +
> +	/*
> +	 * 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);
> +	err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> +			EXT_CSD_BKOPS_START, 1, 0);
> +	if (err) {
> +		pr_warning("error %d starting bkops\n", err);
> +		mmc_card_clr_need_bkops(card);
> +		goto out;
> +	}
> +	spin_lock_irqsave(&card->host->lock, flags);
> +	mmc_card_clr_need_bkops(card);
> +	mmc_card_set_doing_bkops(card);

mmc_switch() will use CMD6 with MMC_RSP_R1B flag (see mmc_switch()
implementation), this flag
causes mmc controler to wait until busy line (DAT0) released:

----from eMMC v4.5 protocol----
6.12 Responses
...
R1b is identical to R1 with an optional busy signal transmitted on the
data line DAT0.
The Device may become busy after receiving these commands based on its
state prior to the command reception.
...
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.
----cut----

This means, that mmc_switch() returns only after BKOPS finished by card
and there is no point
in setting mmc_card_set_doing_bkops(card). Also, HPI command send to the
card in order to interrupt BKOPS in mmc_queue_thread()
will never actually interrupt BKOPS. To resolve this I suggest following:

1)  Define two variants of mmc_switch(...,wait_flag) for BKOPS, where
      wait_flag is true sends CMD6 with MMC_RSP_R1b flag as it happens now.
      wait_flag is false sends CMD6 with MMC_RSP_R1 flag (the only
difference is MMC_RSP_BUSY bit),
    so the flow continues immediately after CMD response arrives and will
not wait for busy line release.

2) The only situation when we want to use synchronous mmc_switch(...,
true) is URGENT bit arrives from card, in mmc_blk_issue_rw_rq().
   There is no point in waiting for idle time check in mmc_queue_thread(),
host should start synchronous BKOPS immediately.

3) When in idle (no requests to issue) we can read BKOPS_STATUS[246] from
card and depending on state decide:
     0: nothing to do go idle
     1: start BKOPS with no wait for busy line ( mmc_switch(..., false))
and mmc_set_doing_bkops()
     3&2: start synchronous BKOPS

Let me describe this by pseudo-code below:

queue.c:  mmc_queue_thread():
...
req = blk_fetch_request(q)
if(req == NULL) {
  state = read_bkops_state(); // read BKOPS_STATUS[246] from card
  switch(state) {
    case 0:
      go_idle()
      break;
    case 1:
      start_asynch_bkops()  // send CMD6 with no wait for busy
      mmc_set_doing_bkops() // update flag
      break;
    case 2:
    case 3:
      start_synch_bkops() // start CMD6 with wait for busy
      break;
    default:
      go_idle()
  }
} else {
   if( mmc_doing_bkops() ) {
     mmc_send_hpi()
     mmc_clear_doing_bkops()
   }
   issue(req)
}

mmc\card\block.c: mmc_blk_issue_rw_rq()
...
  if ( (mmc_is_exception_event(card,EXT_CSD_URGENT_BKOPS)) {
	start_synch_bkops() // start CMD6 with wait for busy
  }
...
-----------------------end of pseudo code-------------

Does this make sense?
Thanks, Kostya




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

* Re: [PATCH v3] mmc: support BKOPS feature for eMMC
  2011-12-01 16:13 ` Konstantin Dorfman
@ 2011-12-05 16:03   ` Jae hoon Chung
  0 siblings, 0 replies; 14+ messages in thread
From: Jae hoon Chung @ 2011-12-05 16:03 UTC (permalink / raw)
  To: Konstantin Dorfman
  Cc: Jaehoon Chung, linux-mmc, Chris Ball, Kyungmin Park,
	Hanumath Prasad, Per Forlin, Sebastian Rasmussen, Dong, Chuanxiao,
	svenkatr@ti.com

Hi Konstantin.

Thanks for your comment..
I will consider your opinion and send path-v4...then also let me know
more comments :)

Best Regards,
Jaehoon Chung

2011/12/2 Konstantin Dorfman <kdorfman@codeaurora.org>:
> Hello Jaehoon,
> After applying your patch and debugging it for some time,
> I want to share my results and suggests some changes to the patch.
> See comments below:
>
>> +void mmc_start_bkops(struct mmc_card *card)
>> +{
>> +     int err;
>> +     unsigned long flags;
>> +
>> +     BUG_ON(!card);
>> +     if ((!card->ext_csd.bkops_en) ||
>> +                     !(card->host->caps2 & MMC_CAP2_BKOPS))
>> +             return;
>> +
>> +     /*
>> +      * 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);
>> +     err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>> +                     EXT_CSD_BKOPS_START, 1, 0);
>> +     if (err) {
>> +             pr_warning("error %d starting bkops\n", err);
>> +             mmc_card_clr_need_bkops(card);
>> +             goto out;
>> +     }
>> +     spin_lock_irqsave(&card->host->lock, flags);
>> +     mmc_card_clr_need_bkops(card);
>> +     mmc_card_set_doing_bkops(card);
>
> mmc_switch() will use CMD6 with MMC_RSP_R1B flag (see mmc_switch()
> implementation), this flag
> causes mmc controler to wait until busy line (DAT0) released:
>
> ----from eMMC v4.5 protocol----
> 6.12 Responses
> ...
> R1b is identical to R1 with an optional busy signal transmitted on the
> data line DAT0.
> The Device may become busy after receiving these commands based on its
> state prior to the command reception.
> ...
> 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.
> ----cut----
>
> This means, that mmc_switch() returns only after BKOPS finished by card
> and there is no point
> in setting mmc_card_set_doing_bkops(card). Also, HPI command send to the
> card in order to interrupt BKOPS in mmc_queue_thread()
> will never actually interrupt BKOPS. To resolve this I suggest following:
>
> 1)  Define two variants of mmc_switch(...,wait_flag) for BKOPS, where
>      wait_flag is true sends CMD6 with MMC_RSP_R1b flag as it happens now.
>      wait_flag is false sends CMD6 with MMC_RSP_R1 flag (the only
> difference is MMC_RSP_BUSY bit),
>    so the flow continues immediately after CMD response arrives and will
> not wait for busy line release.
>
> 2) The only situation when we want to use synchronous mmc_switch(...,
> true) is URGENT bit arrives from card, in mmc_blk_issue_rw_rq().
>   There is no point in waiting for idle time check in mmc_queue_thread(),
> host should start synchronous BKOPS immediately.
>
> 3) When in idle (no requests to issue) we can read BKOPS_STATUS[246] from
> card and depending on state decide:
>     0: nothing to do go idle
>     1: start BKOPS with no wait for busy line ( mmc_switch(..., false))
> and mmc_set_doing_bkops()
>     3&2: start synchronous BKOPS
>
> Let me describe this by pseudo-code below:
>
> queue.c:  mmc_queue_thread():
> ...
> req = blk_fetch_request(q)
> if(req == NULL) {
>  state = read_bkops_state(); // read BKOPS_STATUS[246] from card
>  switch(state) {
>    case 0:
>      go_idle()
>      break;
>    case 1:
>      start_asynch_bkops()  // send CMD6 with no wait for busy
>      mmc_set_doing_bkops() // update flag
>      break;
>    case 2:
>    case 3:
>      start_synch_bkops() // start CMD6 with wait for busy
>      break;
>    default:
>      go_idle()
>  }
> } else {
>   if( mmc_doing_bkops() ) {
>     mmc_send_hpi()
>     mmc_clear_doing_bkops()
>   }
>   issue(req)
> }
>
> mmc\card\block.c: mmc_blk_issue_rw_rq()
> ...
>  if ( (mmc_is_exception_event(card,EXT_CSD_URGENT_BKOPS)) {
>        start_synch_bkops() // start CMD6 with wait for busy
>  }
> ...
> -----------------------end of pseudo code-------------
>
> Does this make sense?
> Thanks, Kostya
>
>
>
> --
> 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] 14+ messages in thread

end of thread, other threads:[~2011-12-05 16:03 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-17  0:50 [PATCH v3] mmc: support BKOPS feature for eMMC Jaehoon Chung
2011-11-17 12:37 ` S, Venkatraman
2011-11-17 15:01 ` Konstantin Dorfman
2011-11-17 16:32   ` Per Forlin
2011-11-20 16:12     ` Konstantin Dorfman
2011-11-20 22:42       ` Per Forlin
2011-11-22 12:44 ` Konstantin Dorfman
2011-11-22 14:00   ` Konstantin Dorfman
2011-11-22 23:54   ` Jaehoon Chung
2011-11-28 11:33     ` Konstantin Dorfman
2011-11-29 12:20 ` Adrian Hunter
2011-11-30  4:09   ` Jaehoon Chung
2011-12-01 16:13 ` Konstantin Dorfman
2011-12-05 16:03   ` Jae hoon Chung

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).