mmc: support BKOPS feature for eMMC
PATCH v5 1/1 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.
* How get BKOPS_STATUS value.(periodically send ext_csd command?)
---
Changelog V5:
- Rebase based on the latest mmc-next.
- modify codes based-on Chris's comment
Changelog V4:
- Add mmc_read_bkops_status
- When URGENT_BKOPS(level2-3), didn't use HPI command.
- In mmc_switch(), use R1B/R1 according to level.
Changelog V3:
- move the bkops setting's location in mmc_blk_issue_rw_rq
- modify condition checking
- bkops_en is assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
- remove the unused code
- change pr_debug instead of pr_warn in mmc_send_hpi_cmd
- Add the Future consideration suggested by Per
Changelog V2:
- Use EXCEPTION_STATUS instead of URGENT_BKOPS
- Add function to check Exception_status(for eMMC4.5)
drivers/mmc/card/block.c | 11 +++++ drivers/mmc/card/queue.c | 3 + drivers/mmc/core/core.c | 105 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/mmc.c | 8 +++ drivers/mmc/core/mmc_ops.c | 12 +++++- include/linux/mmc/card.h | 13 +++++ include/linux/mmc/core.h | 4 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 19 ++++++++ 9 files changed, 175 insertions(+), 1 deletions(-)
drivers/mmc/card/block.c +11 -0
@@ -1273,6 +1273,17 @@ 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.flags ==MMC_RSP_R1B || + brq->cmd.flags ==MMC_RSP_R1 ) && + (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 :
drivers/mmc/card/queue.c +3 -0
@@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) { +if (mmc_card_doing_bkops (mq->card)) +mmc_interrupt_bkops (mq->card);set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); }else {
@@ -73,6 +75,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);
drivers/mmc/core/core.c +105 -0
117 lines not on this page · raw message
drivers/mmc/core/mmc.c +8 -0
@@ -459,6 +459,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 ;
drivers/mmc/core/mmc_ops.c +11 -1
@@ -392,13 +392,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, (index <<16 ) | (value <<8 ) | set; - cmd.flags =MMC_RSP_SPI_R1B |MMC_RSP_R1B |MMC_CMD_AC ; + cmd.flags =MMC_CMD_AC ; +if (index ==EXT_CSD_BKOPS_START && + card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 ) + cmd.flags |=MMC_RSP_SPI_R1 |MMC_RSP_R1 ; +else + cmd.flags |=MMC_RSP_SPI_R1B |MMC_RSP_R1B ; + cmd.cmd_timeout_ms = timeout_ms; err =mmc_wait_for_cmd (card->host, &cmd,MMC_CMD_RETRIES );if (err)return err; +/* No need to check card status in case of BKOPS switch*/ +if (index ==EXT_CSD_BKOPS_START ) +return 0 ; +/* Must check status to be sure of no errors */ do { err = mmc_send_status(card, &status);
include/linux/mmc/card.h +13 -0
@@ -70,9 +70,12 @@ 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 */ unsigned int boot_ro_lock;/* ro lock support */ bool boot_ro_lockable; +u8 raw_exception_status;/* 53 */ u8 raw_partition_support;/* 160 */ u8 raw_erased_mem_count;/* 181 */ u8 raw_ext_csd_structure;/* 194 */
@@ -86,6 +89,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;
@@ -216,6 +220,8 @@ struct mmc_card {#define MMC_STATE_ULTRAHIGHSPEED (1<<5)/* card is in ultra high speed mode */ #define MMC_CARD_SDXC (1<<6)/* card is SDXC */ #define MMC_CARD_REMOVED (1<<7)/* card has been removed */ +#define MMC_STATE_NEED_BKOPS (1<<8)/* card need to do BKOPS */ +#define MMC_STATE_DOING_BKOPS (1<<9)/* 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 */
@@ -380,6 +386,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)#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_removed (c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#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)
@@ -390,6 +398,11 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)#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_removed (c) ((c)->state |= MMC_CARD_REMOVED) +#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.
include/linux/mmc/core.h +4 -0
@@ -134,6 +134,9 @@ struct mmc_host;struct mmc_card ;struct mmc_async_req ; +extern int mmc_interrupt_bkops (struct mmc_card *); +extern int mmc_read_bkops_status (struct mmc_card *); +extern int mmc_is_exception_event (struct mmc_card *,unsigned int );extern struct mmc_async_req *mmc_start_req (struct mmc_host *,struct mmc_async_req *,int *);extern int mmc_interrupt_hpi (struct mmc_card *);
@@ -163,6 +166,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);
include/linux/mmc/host.h +1 -0
@@ -248,6 +248,7 @@ struct mmc_host {#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_NO_SLEEP_CMD (1 << 4)/* Don't allow sleep command */ +#define MMC_CAP2_BKOPS (1 << 5)/* BKOPS supported */ mmc_pm_flag_t pm_caps;/* supported pm features */ unsigned int power_notify_type;
include/linux/mmc/mmc.h +19 -0
@@ -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_BOOT_WP 173/* R/W */
@@ -311,9 +315,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 */ /*
@@ -379,4 +385,17 @@ struct _mmc_csd {#define MMC_SWITCH_MODE_CLEAR_BITS 0x02/* Clear bits which are 1 in value */ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03/* Set target to value */ +/* +* BKOPS status level +*/ +#define EXT_CSD_BKOPS_LEVEL_2 0x2 + +/* +* EXCEPTION_EVENT_STATUS field (eMMC4.5) +*/ +#define EXT_CSD_URGENT_BKOPS BIT(0) +#define EXT_CSD_DYNCAP_NEEDED BIT(1) +#define EXT_CSD_SYSPOOL_EXHAUSTED BIT(2) +#define EXT_CSD_PACKED_FAILURE BIT(3) +#endif /* LINUX_MMC_MMC_H */
Quoted 219 lines · Jaehoon Chung
Enable eMMC background operations (BKOPS) feature.
If
URGENT_BKOPSis 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.
* How getBKOPS_STATUSvalue.(periodically sendext_csdcommand?)Signed-off-by Jaehoon Chung <[email protected]>Signed-off-by Kyungmin Park <[email protected]>---
Changelog V5:
- Rebase based on the latest mmc-next.
- modify codes based-on Chris's comment
Changelog V4:
- Addmmc_read_bkops_status
- WhenURGENT_BKOPS(level2-3), didn't use HPI command.
- Inmmc_switch(), use R1B/R1 according to level. Changelog V3:
- move the bkops setting's location inmmc_blk_issue_rw_rq
- modify condition checking
-bkops_enis assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
- remove the unused code
- changepr_debuginstead ofpr_warninmmc_send_hpi_cmd
- Add the Future consideration suggested by Per Changelog V2:
- UseEXCEPTION_STATUSinstead ofURGENT_BKOPS
- Add function to checkException_status(for eMMC4.5)drivers/mmc/card/block.c | 11 +++++
drivers/mmc/card/queue.c | 3 +
drivers/mmc/core/core.c | 105 ++++++++++++++++++++++++++++++++++++++++++++drivers/mmc/core/mmc.c| 8 +++
drivers/mmc/core/mmc_ops.c | 12 +++++-include/linux/mmc/card.h | 13 +++++ include/linux/mmc/core.h | 4 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 19 ++++++++ 9 files changed, 175 insertions(+), 1 deletions(-)drivers/mmc/card/block.c +11
-0@@ -1273,6 +1273,17 @@ 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.flags == MMC_RSP_R1B || + brq->cmd.flags == MMC_RSP_R1) && + (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:
caseMMC_BLK_PARTIAL:drivers/mmc/card/queue.c +3
-0@@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d) spin_unlock_irq(q->queue_lock); if (req || mq->mqrq_prev->req) { + if (mmc_card_doing_bkops(mq->card)) + mmc_interrupt_bkops(mq->card); set_current_state(TASK_RUNNING); mq->issue_fn(mq, req); } else {@@ -73,6 +75,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);drivers/mmc/core/core.c +105
-0@@ -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("%s: error %d starting bkops\n", + mmc_hostname(card->host), err); + mmc_card_clr_need_bkops(card); + goto out; + } + spin_lock_irqsave(&card->host->lock, flags); + mmc_card_clr_need_bkops(card); + if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2) + 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);@@ -472,6 +516,67 @@ 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_read_bkops_status (struct mmc_card *card) +{ + int err; + u8 ext_csd[512]; + + err = mmc_send_ext_csd(card, ext_csd); + if (err) + return err; + + card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; + card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXCEPTION_STATUS]; + + return 0; +} +EXPORT_SYMBOL (mmc_read_bkops_status); + +int mmc_is_exception_event (struct mmc_card *card,unsigned int value) +{ + int err; + + err = mmc_read_bkops_status(card); + if (err) { + pr_err("%s: Didn't read bkops status : %d\n", + mmc_hostname(card->host), err); + return 0; + } + + /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */ + if (card->ext_csd.rev == 5) + return 1; + + return (card->ext_csd.raw_exception_status & value) ? 1 : 0; +} +EXPORT_SYMBOL (mmc_is_exception_event); + +/** * mmc_set_data_timeout - set the timeout for a data command * @data: data phase for command * @card: the MMC card associated with the data transfer drivers/mmc/core/mmc.c +0
-0@@ -459,6 +459,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) }
drivers/mmc/core/mmc.c static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) · line 469
Quoted 10 lines
drivers/mmc/core/mmc.c +8
-0if (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]; + } +
In my understanding, the EXT_CSD_BKOPS_EN should be _updated_by_ the host controller to 1 at initialization time, to indicate to the device, as per the spec.
Otherwise, the device would decide that the host doesn't support software controlled BKOPS.
Quoted 178 lines
drivers/mmc/core/mmc.c +0
-0/* check whether the eMMC card supports HPI */ if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) { card->ext_csd.hpi = 1;drivers/mmc/core/mmc_ops.c +0
-0@@ -392,13 +392,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,(index << 16) |
(value << 8) |
set;
- cmd.flags =MMC_RSP_SPI_R1B|MMC_RSP_R1B|MMC_CMD_AC;
+ cmd.flags =MMC_CMD_AC;
+ if (index ==EXT_CSD_BKOPS_START&&
+card->ext_csd.raw_bkops_status<EXT_CSD_BKOPS_LEVEL_2)
+ cmd.flags |=MMC_RSP_SPI_R1|MMC_RSP_R1;
+ else
+ cmd.flags |=MMC_RSP_SPI_R1B|MMC_RSP_R1B; +
cmd.cmd_timeout_ms =timeout_ms;err =
mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err)
return err;+ /* No need to check card status in case of BKOPS switch*/
+ if (index ==EXT_CSD_BKOPS_START)
+ return 0;
+
/* Must check status to be sure of no errors */ do {
err =mmc_send_status(card, &status);include/linux/mmc/card.h +0
-0@@ -70,9 +70,12 @@ 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 */ unsigned int boot_ro_lock; /* ro lock support */ bool boot_ro_lockable;
+ u8 raw_exception_status; /* 53 */ u8 raw_partition_support; /* 160 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_structure; /* 194 */include/linux/mmc/card.h +0
-0@@ -86,6 +89,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;
include/linux/mmc/card.h +2
-0@@ -216,6 +220,8 @@ struct mmc_card { #define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */ #define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<7) /* card has been removed */ +#define MMC_STATE_NEED_BKOPS (1<<8)/* card need to do BKOPS */ +#define MMC_STATE_DOING_BKOPS (1<<9)/* card is doing BKOPS */ unsigned int quirks; /* card quirks */
#defineMMC_QUIRK_LENIENT_FN0(1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #defineMMC_QUIRK_BLKSZ_FOR_BYTE_MODE(1<<1) /* usefunc->cur_blksize*/include/linux/mmc/card.h +7
-0@@ -380,6 +386,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #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_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#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)@@ -390,6 +398,11 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #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_removed(c) ((c)->state |= MMC_CARD_REMOVED) +#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. include/linux/mmc/core.h +4
-0@@ -134,6 +134,9 @@ struct mmc_host; struct mmc_card; struct mmc_async_req; +extern int mmc_interrupt_bkops (struct mmc_card *); +extern int mmc_read_bkops_status (struct mmc_card *); +extern int mmc_is_exception_event (struct mmc_card *,unsigned int ); extern struct mmc_async_req *mmc_start_req(struct mmc_host *, struct mmc_async_req *, int *); extern int mmc_interrupt_hpi(struct mmc_card *);@@ -163,6 +166,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);include/linux/mmc/host.h +1
-0@@ -248,6 +248,7 @@ struct mmc_host { #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_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */ +#define MMC_CAP2_BKOPS (1 << 5)/* BKOPS supported */ mmc_pm_flag_t
pm_caps; /* supported pm features */ unsigned int power_notify_type;include/linux/mmc/mmc.h +19
-0@@ -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_BOOT_WP 173 /* R/W */@@ -311,9 +315,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 */ /*@@ -379,4 +385,17 @@ struct _mmc_csd { #define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ +/* +* BKOPS status level +*/ +#define EXT_CSD_BKOPS_LEVEL_2 0x2 + +/* +* EXCEPTION_EVENT_STATUS field (eMMC4.5) +*/ +#define EXT_CSD_URGENT_BKOPS BIT(0) +#define EXT_CSD_DYNCAP_NEEDED BIT(1) +#define EXT_CSD_SYSPOOL_EXHAUSTED BIT(2) +#define EXT_CSD_PACKED_FAILURE BIT(3) + #endif /* LINUX_MMC_MMC_H */
Quoted 254 lines
-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of S, Venkatraman
Sent: Saturday, January 07, 2012 12:42 AM
To: Jaehoon Chung
Cc: linux-mmc; Chris Ball; Kyungmin Park; Hanumath Prasad; [email protected]; Sebastian Rasmussen; Dong, Chuanxiao; Konstantin Dorfman
Subject: Re: [PATCH v5] mmc: support BKOPS feature for eMMCQuoted 36 lines · Jaehoon Chung
Enable eMMC background operations (BKOPS) feature.
If
URGENT_BKOPSis 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.
* How getBKOPS_STATUSvalue.(periodically sendext_csdcommand?)Signed-off-by Jaehoon Chung <[email protected]>Signed-off-by Kyungmin Park <[email protected]>---
Changelog V5:
- Rebase based on the latest mmc-next.
- modify codes based-on Chris's comment
Changelog V4:
- Addmmc_read_bkops_status
- WhenURGENT_BKOPS(level2-3), didn't use HPI command.
- Inmmc_switch(), use R1B/R1 according to level. Changelog V3:
- move the bkops setting's location inmmc_blk_issue_rw_rq
- modify condition checking
-bkops_enis assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
- remove the unused code
- changepr_debuginstead ofpr_warninmmc_send_hpi_cmd
- Add the Future consideration suggested by Per Changelog V2:
- UseEXCEPTION_STATUSinstead ofURGENT_BKOPS
- Add function to checkException_status(for eMMC4.5)Quoted 3 lines
drivers/mmc/card/block.c | 11 +++++
drivers/mmc/card/queue.c | 3 +
drivers/mmc/core/core.c | 105++++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue
Quoted 13 lines
drivers/mmc/core/mmc.c | 8 +++
drivers/mmc/core/mmc_ops.c | 12 +++++-include/linux/mmc/card.h | 13 +++++ include/linux/mmc/core.h | 4 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 19 ++++++++ 9 files changed, 175 insertions(+), 1 deletions(-)drivers/mmc/card/block.c +0
-0@@ -1273,6 +1273,17 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue*mq, struct request *rqc)
Quoted 1 line
type =
rq_data_dir(req)== READ ?MMC_BLK_READ:
MMC_BLK_WRITE;Quoted 10 lines
mmc_queue_bounce_post(mq_rq);
+ /*
+ * Check BKOPS urgency from each R1 response
+ */
+ if (mmc_card_mmc(card)&&
+ (brq->cmd.flags==MMC_RSP_R1B||
+ brq->cmd.flags ==MMC_RSP_R1) &&
+ (brq->cmd.resp[0] &R1_EXCEPTION_EVENT)) {
+ if (mmc_is_exception_event(card,
EXT_CSD_URGENT_BKOPS))drivers/mmc/core/core.c mmc_start_request(struct mmc_host *host, struct
Quoted 32 lines
+
mmc_card_set_need_bkops(card);
+ }
+
switch (status) {
caseMMC_BLK_SUCCESS:
caseMMC_BLK_PARTIAL:drivers/mmc/card/queue.c +3
-0@@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d) spin_unlock_irq(q->queue_lock); if (req || mq->mqrq_prev->req) { + if (mmc_card_doing_bkops(mq->card)) + mmc_interrupt_bkops(mq->card); set_current_state(TASK_RUNNING); mq->issue_fn(mq, req); } else {@@ -73,6 +75,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);drivers/mmc/core/core.c +0
-0@@ -238,6 +238,50 @@ mmc_start_request(struct mmc_host *host, struct
mmc_request*mrq)Quoted 8 lines
drivers/mmc/core/core.c +5
-0host->ops->request(host, mrq); } +/** +* mmc_start_bkops - start BKOPS for supported cards +* @card: MMC card to start BKOPS +* +* Start background operations whenever requested. drivers/mmc/core/core.c mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
Quoted 10 lines
drivers/mmc/core/core.c +10
-0+* 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))drivers/mmc/core/core.c int mmc_wait_for_cmd(struct mmc_host *host, struct
Quoted 33 lines
drivers/mmc/core/core.c +29
-0+ 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("%s: error %d starting bkops\n", + mmc_hostname(card->host), err); + mmc_card_clr_need_bkops(card); + goto out; + } + spin_lock_irqsave(&card->host->lock, flags); + mmc_card_clr_need_bkops(card); + if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2) + 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);@@ -472,6 +516,67 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct
mmc_command*cmd, int retriesQuoted 29 lines
drivers/mmc/core/core.c +26
-0EXPORT_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_read_bkops_status (struct mmc_card *card)drivers/mmc/core/core.c int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
Quoted 10 lines
drivers/mmc/core/core.c +10
-0+{ + int err; + u8 ext_csd[512]; + + err = mmc_send_ext_csd(card, ext_csd); + if (err) + return err; + + card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; + card->ext_csd.raw_exception_status =ext_csd[EXT_CSD_EXCEPTION_STATUS];
drivers/mmc/core/mmc.c static int mmc_read_ext_csd(struct mmc_card *card,
Quoted 33 lines
drivers/mmc/core/core.c +25
-0+ + return 0; +} +EXPORT_SYMBOL (mmc_read_bkops_status); + +int mmc_is_exception_event (struct mmc_card *card,unsigned int value) +{ + int err; + + err = mmc_read_bkops_status(card); + if (err) { + pr_err("%s: Didn't read bkops status : %d\n", + mmc_hostname(card->host), err); + return 0; + } + + /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */ + if (card->ext_csd.rev == 5) + return 1; + + return (card->ext_csd.raw_exception_status & value) ? 1 : 0; +} +EXPORT_SYMBOL (mmc_is_exception_event); + +/** * mmc_set_data_timeout - set the timeout for a data command * @data: data phase for command * @card: the MMC card associated with the data transfer drivers/mmc/core/mmc.c +0
-0@@ -459,6 +459,14 @@ static int mmc_read_ext_csd(struct mmc_card *card,u8
*ext_csd)drivers/mmc/core/mmc.c static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
Quoted 7 lines
drivers/mmc/core/mmc.c +4
-0} 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];
drivers/mmc/core/mmc.c static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) · line 469
Quoted 4 lines
drivers/mmc/core/mmc.c +4
-0+ card->ext_csd.raw_bkops_status = + ext_csd[EXT_CSD_BKOPS_STATUS]; + } +
Quoted 5 lines
In my understanding, the
EXT_CSD_BKOPS_ENshould be _updated_by_ the host controller to 1 at initialization time, to indicate to the device, as per the spec.
Otherwise, the device would decide that the host doesn't support software controlled BKOPS.
EXT_CSD_BKOPS_EN is a onetime programmable register, which is not suitable for driver to touch I think. This can be updated by host controller, but should not happened in driver. This register can be configured by some platform firmware or some eMMC FW from vendor. For what driver needs to do, is just to read this onetime programmable and to know whether BKOPS is enabled. It is like hardware reset feature. RST_n_FUNCTION is only a onetime programmable register which can indicate whether hardware reset feature is enabled. Driver just needs to read it out and to know whether hardware reset feature can be used. So I agree with Jaehoon Chung's implementation.
BR
Chuanxiao
Hi,
Quoted 6 lines · Chuanxiao
Quoted 5 lines
In my understanding, the
EXT_CSD_BKOPS_ENshould be _updated_by_ the host controller to 1 at initialization time, to indicate to the device, as per the spec.
Otherwise, the device would decide that the host doesn't support software controlled BKOPS.
Quoted 10 lines
EXT_CSD_BKOPS_ENis a onetime programmable register, which is not suitable for driver to touch I think. This can be updated by host controller, but should not happened in driver. This register can be configured by some platform firmware or some eMMC FW from vendor. For what driver needs to do, is just to read this onetime programmable and to know whether BKOPS is enabled. It is like hardware reset feature.RST_n_FUNCTIONis only a onetime programmable register which can indicate whether hardware reset feature is enabled. Driver just needs to read it out and to know whether hardware reset feature can be used. So I agree with Jaehoon Chung's implementation.
I agree -- we don't touch fuses in the driver.
Thanks,
- Chris.
Signature · 2 lines
Chris Ball <[email protected]> <http://printf.net/> One Laptop Per Child
A message that is not in the archive (sent off-list or not yet received): [email protected]
Hi,
Quoted 9 lines · Ulf Hansson
Hi,
An overall question: How will you prevent each host driver from doing power save operations while there is an ongoing BKOPS?
Power save operation could be to disable the clock to the card. Is that OK? If not, I think we have two options:
1. do a
pm_runtime_get_syncfor the host device when starting the BKOPS.
2. make sure the host is enabled "mmc_claim_host" or "mmc_host_enable".
Thanks for your comments..i didn't consider the power save operations..
You're right..should be disabled the clock to the card if card is running power-save operation.
I think that condition should be the case for level-1..
Because i know if bkops_status is level-1, should be run the bkops at idle-time. (But now this patch-v5 is controlled URGENT_BKOPS(level2-3))
Right..we must consider the your suggestion.
If my thinking is wrong..plz let me know..
Best Regards,
Jaehoon Chung
Quoted 419 lines
Br
Ulf HanssonQuoted 412 lines · Jaehoon Chung
Enable eMMC background operations (BKOPS) feature.
If
URGENT_BKOPSis 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.
* How getBKOPS_STATUSvalue.(periodically sendext_csdcommand?)Signed-off-by Jaehoon Chung <[email protected]>Signed-off-by Kyungmin Park <[email protected]>---
Changelog V5:
- Rebase based on the latest mmc-next.
- modify codes based-on Chris's comment
Changelog V4:
- Addmmc_read_bkops_status
- WhenURGENT_BKOPS(level2-3), didn't use HPI command.
- Inmmc_switch(), use R1B/R1 according to level.
Changelog V3:
- move the bkops setting's location inmmc_blk_issue_rw_rq
- modify condition checking
-bkops_enis assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
- remove the unused code
- changepr_debuginstead ofpr_warninmmc_send_hpi_cmd
- Add the Future consideration suggested by Per
Changelog V2:
- UseEXCEPTION_STATUSinstead ofURGENT_BKOPS
- Add function to checkException_status(for eMMC4.5)drivers/mmc/card/block.c | 11 +++++ drivers/mmc/card/queue.c | 3 + drivers/mmc/core/core.c | 105 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/mmc.c | 8 +++ drivers/mmc/core/mmc_ops.c | 12 +++++- include/linux/mmc/card.h | 13 +++++ include/linux/mmc/core.h | 4 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 19 ++++++++ 9 files changed, 175 insertions(+), 1 deletions(-)drivers/mmc/card/block.c +11
-0@@ -1273,6 +1273,17 @@ 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.flags ==MMC_RSP_R1B || + brq->cmd.flags ==MMC_RSP_R1 ) && + (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 :drivers/mmc/card/queue.c +3
-0@@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d)spin_unlock_irq (q->queue_lock);if (req || mq->mqrq_prev->req) { +if (mmc_card_doing_bkops (mq->card)) +mmc_interrupt_bkops (mq->card);set_current_state (TASK_RUNNING ); mq->issue_fn (mq, req); }else {@@ -73,6 +75,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);drivers/mmc/core/core.c +105
-0@@ -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 ("%s: error %d starting bkops\n" , +mmc_hostname (card->host), err); +mmc_card_clr_need_bkops (card); + goto out; + } +spin_lock_irqsave (&card->host->lock, flags); +mmc_card_clr_need_bkops (card); +if (card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 ) +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);@@ -472,6 +516,67 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retriesEXPORT_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_read_bkops_status (struct mmc_card *card) +{ +int err; +u8 ext_csd[512 ]; + + err =mmc_send_ext_csd (card, ext_csd); +if (err) +return err; + + card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS ]; + card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXCEPTION_STATUS ]; + +return 0 ; +} +EXPORT_SYMBOL (mmc_read_bkops_status); + +int mmc_is_exception_event (struct mmc_card *card,unsigned int value) +{ +int err; + + err =mmc_read_bkops_status (card); +if (err) { +pr_err ("%s: Didn't read bkops status : %d\n" , +mmc_hostname (card->host), err); +return 0 ; + } + +/* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */ +if (card->ext_csd.rev ==5 ) +return 1 ; + +return (card->ext_csd.raw_exception_status & value) ?1 :0 ; +} +EXPORT_SYMBOL (mmc_is_exception_event); + +/** * mmc_set_data_timeout - set the timeout for a data command * @data: data phase for command * @card: the MMC card associated with the data transfer drivers/mmc/core/mmc.c +8
-0@@ -459,6 +459,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 ;drivers/mmc/core/mmc_ops.c +11
-1@@ -392,13 +392,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, (index <<16 ) | (value <<8 ) | set; - cmd.flags =MMC_RSP_SPI_R1B |MMC_RSP_R1B |MMC_CMD_AC ; + cmd.flags =MMC_CMD_AC ; +if (index ==EXT_CSD_BKOPS_START && + card->ext_csd.raw_bkops_status <EXT_CSD_BKOPS_LEVEL_2 ) + cmd.flags |=MMC_RSP_SPI_R1 |MMC_RSP_R1 ; +else + cmd.flags |=MMC_RSP_SPI_R1B |MMC_RSP_R1B ; + cmd.cmd_timeout_ms = timeout_ms; err =mmc_wait_for_cmd (card->host, &cmd,MMC_CMD_RETRIES );if (err)return err; +/* No need to check card status in case of BKOPS switch*/ +if (index ==EXT_CSD_BKOPS_START ) +return 0 ; +/* Must check status to be sure of no errors */ do { err = mmc_send_status(card, &status);include/linux/mmc/card.h +13
-0@@ -70,9 +70,12 @@ 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 */ unsigned int boot_ro_lock;/* ro lock support */ bool boot_ro_lockable; +u8 raw_exception_status;/* 53 */ u8 raw_partition_support;/* 160 */ u8 raw_erased_mem_count;/* 181 */ u8 raw_ext_csd_structure;/* 194 */ @@ -86,6 +89,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;@@ -216,6 +220,8 @@ struct mmc_card {#define MMC_STATE_ULTRAHIGHSPEED (1<<5)/* card is in ultra high speed mode */ #define MMC_CARD_SDXC (1<<6)/* card is SDXC */ #define MMC_CARD_REMOVED (1<<7)/* card has been removed */ +#define MMC_STATE_NEED_BKOPS (1<<8)/* card need to do BKOPS */ +#define MMC_STATE_DOING_BKOPS (1<<9)/* 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 */ @@ -380,6 +386,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)#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_removed (c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#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)@@ -390,6 +398,11 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)#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_removed (c) ((c)->state |= MMC_CARD_REMOVED) +#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. include/linux/mmc/core.h +4
-0@@ -134,6 +134,9 @@ struct mmc_host;struct mmc_card ;struct mmc_async_req ; +extern int mmc_interrupt_bkops (struct mmc_card *); +extern int mmc_read_bkops_status (struct mmc_card *); +extern int mmc_is_exception_event (struct mmc_card *,unsigned int );extern struct mmc_async_req *mmc_start_req (struct mmc_host *,struct mmc_async_req *,int *);extern int mmc_interrupt_hpi (struct mmc_card *);@@ -163,6 +166,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);include/linux/mmc/host.h +1
-0@@ -248,6 +248,7 @@ struct mmc_host {#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_NO_SLEEP_CMD (1 << 4)/* Don't allow sleep command */ +#define MMC_CAP2_BKOPS (1 << 5)/* BKOPS supported */ mmc_pm_flag_t pm_caps;/* supported pm features */ unsigned int power_notify_type;include/linux/mmc/mmc.h +19
-0@@ -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_BOOT_WP 173/* R/W */ @@ -311,9 +315,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 */ /* @@ -379,4 +385,17 @@ struct _mmc_csd {#define MMC_SWITCH_MODE_CLEAR_BITS 0x02/* Clear bits which are 1 in value */ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03/* Set target to value */ +/* +* BKOPS status level +*/ +#define EXT_CSD_BKOPS_LEVEL_2 0x2 + +/* +* EXCEPTION_EVENT_STATUS field (eMMC4.5) +*/ +#define EXT_CSD_URGENT_BKOPS BIT(0) +#define EXT_CSD_DYNCAP_NEEDED BIT(1) +#define EXT_CSD_SYSPOOL_EXHAUSTED BIT(2) +#define EXT_CSD_PACKED_FAILURE BIT(3) +#endif /* LINUX_MMC_MMC_H */ Signature · 3 lines
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 53 lines · Jaehoon Chung
Enable eMMC background operations (BKOPS) feature.
If
URGENT_BKOPSis 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.
* How getBKOPS_STATUSvalue.(periodically sendext_csdcommand?)Signed-off-by Jaehoon Chung <[email protected]>Signed-off-by Kyungmin Park <[email protected]>---
Changelog V5:
- Rebase based on the latest mmc-next.
- modify codes based-on Chris's comment
Changelog V4:
- Addmmc_read_bkops_status
- WhenURGENT_BKOPS(level2-3), didn't use HPI command.
- Inmmc_switch(), use R1B/R1 according to level. Changelog V3:
- move the bkops setting's location inmmc_blk_issue_rw_rq
- modify condition checking
-bkops_enis assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
- remove the unused code
- changepr_debuginstead ofpr_warninmmc_send_hpi_cmd
- Add the Future consideration suggested by Per Changelog V2:
- UseEXCEPTION_STATUSinstead ofURGENT_BKOPS
- Add function to checkException_status(for eMMC4.5)drivers/mmc/card/block.c | 11 +++++
drivers/mmc/card/queue.c | 3 +
drivers/mmc/core/core.c | 105 ++++++++++++++++++++++++++++++++++++++++++++drivers/mmc/core/mmc.c| 8 +++
drivers/mmc/core/mmc_ops.c | 12 +++++-include/linux/mmc/card.h | 13 +++++ include/linux/mmc/core.h | 4 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 19 ++++++++ 9 files changed, 175 insertions(+), 1 deletions(-)drivers/mmc/card/block.c +0
-0
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) · line 1281
Quoted 9 lines
drivers/mmc/card/block.c +6
-0@@ -1273,6 +1273,17 @@ 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.flags == MMC_RSP_R1B || + brq->cmd.flags == MMC_RSP_R1) &&
I tested this patch on my setup and this condition never hit, as other flags are also set in cmd.flags
It should have been
(brq->cmd.flags & MMC_RSP_R1B ||
brq->cmd.flags & MMC_RSP_R1)
drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) · line 1283
Quoted 2 lines
drivers/mmc/card/block.c +2
-0+ (brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) { + if (mmc_is_exception_event(card, EXT_CSD_URGENT_BKOPS))
This causes a crash on my test setup. The next req is already given to the controller (see the mmc_start_req call a few lines above) and a parallel request to read BKOPS_STATUS is interfering with it. This won't work with any host controller driver which implements async_req functions.
But there is no need to read BKOPS_STATUS at this stage anyway. By checking R1_EXCEPTION_EVENT and the BKOPS bit inside the response, the need to do BKOPS can be determined (as only a urgen_bkops state would trigger a R1_EXCEPTION_EVENT in the first place)
Quoted 344 lines
drivers/mmc/card/block.c +3
-0+ mmc_card_set_need_bkops(card); + } + switch (status) {case
MMC_BLK_SUCCESS:
caseMMC_BLK_PARTIAL:drivers/mmc/card/queue.c +3
-0@@ -66,6 +66,8 @@ static int mmc_queue_thread(void *d) spin_unlock_irq(q->queue_lock); if (req || mq->mqrq_prev->req) { + if (mmc_card_doing_bkops(mq->card)) + mmc_interrupt_bkops(mq->card); set_current_state(TASK_RUNNING); mq->issue_fn(mq, req); } else {@@ -73,6 +75,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);drivers/mmc/core/core.c +105
-0@@ -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("%s: error %d starting bkops\n", + mmc_hostname(card->host), err); + mmc_card_clr_need_bkops(card); + goto out; + } + spin_lock_irqsave(&card->host->lock, flags); + mmc_card_clr_need_bkops(card); + if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2) + 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);@@ -472,6 +516,67 @@ 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_read_bkops_status (struct mmc_card *card) +{ + int err; + u8 ext_csd[512]; + + err = mmc_send_ext_csd(card, ext_csd); + if (err) + return err; + + card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; + card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXCEPTION_STATUS]; + + return 0; +} +EXPORT_SYMBOL (mmc_read_bkops_status); + +int mmc_is_exception_event (struct mmc_card *card,unsigned int value) +{ + int err; + + err = mmc_read_bkops_status(card); + if (err) { + pr_err("%s: Didn't read bkops status : %d\n", + mmc_hostname(card->host), err); + return 0; + } + + /* In eMMC 4.41, R1_EXCEPTION_EVENT is URGENT_BKOPS */ + if (card->ext_csd.rev == 5) + return 1; + + return (card->ext_csd.raw_exception_status & value) ? 1 : 0; +} +EXPORT_SYMBOL (mmc_is_exception_event); + +/** * mmc_set_data_timeout - set the timeout for a data command * @data: data phase for command * @card: the MMC card associated with the data transfer drivers/mmc/core/mmc.c +8
-0@@ -459,6 +459,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;drivers/mmc/core/mmc_ops.c +0
-0@@ -392,13 +392,23 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,(index << 16) |
(value << 8) |
set;
- cmd.flags =MMC_RSP_SPI_R1B|MMC_RSP_R1B|MMC_CMD_AC;
+ cmd.flags =MMC_CMD_AC;
+ if (index ==EXT_CSD_BKOPS_START&&
+card->ext_csd.raw_bkops_status<EXT_CSD_BKOPS_LEVEL_2)
+ cmd.flags |=MMC_RSP_SPI_R1|MMC_RSP_R1;
+ else
+ cmd.flags |=MMC_RSP_SPI_R1B|MMC_RSP_R1B; +
cmd.cmd_timeout_ms =timeout_ms;err =
mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err)
return err;+ /* No need to check card status in case of BKOPS switch*/
+ if (index ==EXT_CSD_BKOPS_START)
+ return 0;
+
/* Must check status to be sure of no errors */ do {
err =mmc_send_status(card, &status);include/linux/mmc/card.h +0
-0@@ -70,9 +70,12 @@ 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 */ unsigned int boot_ro_lock; /* ro lock support */ bool boot_ro_lockable;
+ u8 raw_exception_status; /* 53 */ u8 raw_partition_support; /* 160 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_structure; /* 194 */include/linux/mmc/card.h +0
-0@@ -86,6 +89,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;
include/linux/mmc/card.h +2
-0@@ -216,6 +220,8 @@ struct mmc_card { #define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */ #define MMC_CARD_SDXC (1<<6) /* card is SDXC */ #define MMC_CARD_REMOVED (1<<7) /* card has been removed */ +#define MMC_STATE_NEED_BKOPS (1<<8)/* card need to do BKOPS */ +#define MMC_STATE_DOING_BKOPS (1<<9)/* card is doing BKOPS */ unsigned int quirks; /* card quirks */
#defineMMC_QUIRK_LENIENT_FN0(1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #defineMMC_QUIRK_BLKSZ_FOR_BYTE_MODE(1<<1) /* usefunc->cur_blksize*/include/linux/mmc/card.h +7
-0@@ -380,6 +386,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #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_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#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)@@ -390,6 +398,11 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #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_removed(c) ((c)->state |= MMC_CARD_REMOVED) +#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. include/linux/mmc/core.h +4
-0@@ -134,6 +134,9 @@ struct mmc_host; struct mmc_card; struct mmc_async_req; +extern int mmc_interrupt_bkops (struct mmc_card *); +extern int mmc_read_bkops_status (struct mmc_card *); +extern int mmc_is_exception_event (struct mmc_card *,unsigned int ); extern struct mmc_async_req *mmc_start_req(struct mmc_host *, struct mmc_async_req *, int *); extern int mmc_interrupt_hpi(struct mmc_card *);@@ -163,6 +166,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);include/linux/mmc/host.h +1
-0@@ -248,6 +248,7 @@ struct mmc_host { #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_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */ +#define MMC_CAP2_BKOPS (1 << 5)/* BKOPS supported */ mmc_pm_flag_t
pm_caps; /* supported pm features */ unsigned int power_notify_type;include/linux/mmc/mmc.h +19
-0@@ -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_BOOT_WP 173 /* R/W */@@ -311,9 +315,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 */ /*@@ -379,4 +385,17 @@ struct _mmc_csd { #define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ +/* +* BKOPS status level +*/ +#define EXT_CSD_BKOPS_LEVEL_2 0x2 + +/* +* EXCEPTION_EVENT_STATUS field (eMMC4.5) +*/ +#define EXT_CSD_URGENT_BKOPS BIT(0) +#define EXT_CSD_DYNCAP_NEEDED BIT(1) +#define EXT_CSD_SYSPOOL_EXHAUSTED BIT(2) +#define EXT_CSD_PACKED_FAILURE BIT(3) + #endif /* LINUX_MMC_MMC_H */
Hi,
Quoted 83 lines · Venkatraman
Quoted 54 lines · Jaehoon Chung
Enable eMMC background operations (BKOPS) feature.
If
URGENT_BKOPSis 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.
* How getBKOPS_STATUSvalue.(periodically sendext_csdcommand?)Signed-off-by Jaehoon Chung <[email protected]>Signed-off-by Kyungmin Park <[email protected]>---
Changelog V5:
- Rebase based on the latest mmc-next.
- modify codes based-on Chris's comment
Changelog V4:
- Addmmc_read_bkops_status
- WhenURGENT_BKOPS(level2-3), didn't use HPI command.
- Inmmc_switch(), use R1B/R1 according to level. Changelog V3:
- move the bkops setting's location inmmc_blk_issue_rw_rq
- modify condition checking
-bkops_enis assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
- remove the unused code
- changepr_debuginstead ofpr_warninmmc_send_hpi_cmd
- Add the Future consideration suggested by Per Changelog V2:
- UseEXCEPTION_STATUSinstead ofURGENT_BKOPS
- Add function to checkException_status(for eMMC4.5)drivers/mmc/card/block.c | 11 +++++
drivers/mmc/card/queue.c | 3 +
drivers/mmc/core/core.c | 105
++++++++++++++++++++++++++++++++++++++++++++
drivers/mmc/core/mmc.c | 8 +++
drivers/mmc/core/mmc_ops.c | 12 +++++-include/linux/mmc/card.h | 13 +++++ include/linux/mmc/core.h | 4 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 19 ++++++++ 9 files changed, 175 insertions(+), 1 deletions(-)drivers/mmc/card/block.c +0
-0drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct · line 1282
Quoted 10 lines
drivers/mmc/card/block.c +6
-0@@ -1273,6 +1273,17 @@ 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.flags == MMC_RSP_R1B || + brq->cmd.flags == MMC_RSP_R1) &&I tested this patch on my setup and this condition never hit, as other flags are also set in cmd.flags
It should have been
(brq->cmd.flags&MMC_RSP_R1B||
brq->cmd.flags&MMC_RSP_R1)drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct · line 1285
Quoted 3 lines
drivers/mmc/card/block.c +2
-0+ (brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) { + if (mmc_is_exception_event(card,EXT_CSD_URGENT_BKOPS ))This causes a crash on my test setup. The next req is already given to the controller (see the
mmc_start_reqcall a few lines above) and a parallel request to readBKOPS_STATUSis interfering with it. This won't work with any host controller driver which implementsasync_reqfunctions.
Quoted 4 lines
But there is no need to read
BKOPS_STATUSat this stage anyway. By checkingR1_EXCEPTION_EVENTand the BKOPS bit inside the response, the need to do BKOPS can be determined (as only aurgen_bkopsstate would trigger aR1_EXCEPTION_EVENTin the first place)
Thanks very much for testing! I'll drop this from my 3.3 queue -- we can try it again for 3.4.
- Chris.
Signature · 2 lines
Chris Ball <[email protected]> <http://printf.net/> One Laptop Per Child
Hi Chris & Venkat,
Thanks for testing...i will modify insufficiency part about venkat's testing results. I will resend the patch-v6 at next week.
Best Regards,
Jaehoon Chung
Quoted 95 lines · Chris Ball
Hi,
Quoted 83 lines · Venkatraman
Quoted 54 lines · Jaehoon Chung
Enable eMMC background operations (BKOPS) feature.
If
URGENT_BKOPSis 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.
* How getBKOPS_STATUSvalue.(periodically sendext_csdcommand?)Signed-off-by Jaehoon Chung <[email protected]>Signed-off-by Kyungmin Park <[email protected]>---
Changelog V5:
- Rebase based on the latest mmc-next.
- modify codes based-on Chris's comment
Changelog V4:
- Addmmc_read_bkops_status
- WhenURGENT_BKOPS(level2-3), didn't use HPI command.
- Inmmc_switch(), use R1B/R1 according to level.
Changelog V3:
- move the bkops setting's location inmmc_blk_issue_rw_rq
- modify condition checking
-bkops_enis assigned ext_csd[EXT_CSD_BKOPS_EN] instead of "1"
- remove the unused code
- changepr_debuginstead ofpr_warninmmc_send_hpi_cmd
- Add the Future consideration suggested by Per
Changelog V2:
- UseEXCEPTION_STATUSinstead ofURGENT_BKOPS
- Add function to checkException_status(for eMMC4.5)drivers/mmc/card/block.c | 11 +++++ drivers/mmc/card/queue.c | 3 + drivers/mmc/core/core.c | 105 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/mmc.c | 8 +++ drivers/mmc/core/mmc_ops.c | 12 +++++- include/linux/mmc/card.h | 13 +++++ include/linux/mmc/core.h | 4 ++ include/linux/mmc/host.h | 1 + include/linux/mmc/mmc.h | 19 ++++++++ 9 files changed, 175 insertions(+), 1 deletions(-)drivers/mmc/card/block.c +0
-0drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct · line 1282
Quoted 10 lines
drivers/mmc/card/block.c +6
-0@@ -1273,6 +1273,17 @@ 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.flags == MMC_RSP_R1B || + brq->cmd.flags == MMC_RSP_R1) &&I tested this patch on my setup and this condition never hit, as other flags are also set in cmd.flags
It should have been
(brq->cmd.flags&MMC_RSP_R1B||
brq->cmd.flags&MMC_RSP_R1)drivers/mmc/card/block.c static int mmc_blk_issue_rw_rq(struct · line 1285
Quoted 3 lines
drivers/mmc/card/block.c +2
-0+ (brq->cmd.resp[0] & R1_EXCEPTION_EVENT)) { +if (mmc_is_exception_event (card,EXT_CSD_URGENT_BKOPS ))This causes a crash on my test setup. The next req is already given to the controller (see the
mmc_start_reqcall a few lines above) and a parallel request to readBKOPS_STATUSis interfering with it. This won't work with any host controller driver which implementsasync_reqfunctions.Quoted 4 lines
But there is no need to read
BKOPS_STATUSat this stage anyway. By checkingR1_EXCEPTION_EVENTand the BKOPS bit inside the response, the need to do BKOPS can be determined (as only aurgen_bkopsstate would trigger aR1_EXCEPTION_EVENTin the first place)Thanks very much for testing! I'll drop this from my 3.3 queue -- we can try it again for 3.4.
- Chris.