[PATCH 1/5] mmc: core: Add support to read command queue parameters
eMMC cards with EXT_CSD version >= 7, optionally support command queuing feature as defined by Jedec eMMC5.1. Add support for probing command queue feature for such type of cards. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Asutosh Das --- drivers/mmc/core/mmc.c | 15 +++ include/linux/mmc/card.h | 7 +++ include/linux/mmc/mmc.h | 6 ++ 3 files changed, 28 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1ab5f3a..0ef3af5 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -571,6 +571,21 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.data_sector_size = 512; } + if (card->ext_csd.rev >= 7) { + card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT]; + if (card->ext_csd.cmdq_support) + card->ext_csd.cmdq_depth = ext_csd[EXT_CSD_CMDQ_DEPTH]; + } + + if (card->ext_csd.rev >= 7) { + card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT]; + if (card->ext_csd.cmdq_support) + card->ext_csd.cmdq_depth = ext_csd[EXT_CSD_CMDQ_DEPTH]; + } else { + card->ext_csd.cmdq_support = 0; + card->ext_csd.cmdq_depth = 0; + } + out: return err; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index b730272..b87a27c 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -112,6 +112,9 @@ struct mmc_ext_csd { u8 raw_pwr_cl_ddr_52_360; /* 239 */ u8 raw_bkops_status; /* 246 */ u8 raw_sectors[4]; /* 212 - 4 bytes */ + u8 cmdq_mode_en; /* 15 */ + u8 cmdq_depth; /* 307 */ + u8 cmdq_support; /* 308 */ unsigned intfeature_support; #define MMC_DISCARD_FEATUREBIT(0) /* CMD38 feature */ @@ -259,6 +262,7 @@ struct mmc_card { #define MMC_STATE_HIGHSPEED_200(1<<8) /* card is in HS200 mode */ #define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */ #define MMC_STATE_SUSPENDED(1<<11) /* card is suspended */ +#define MMC_STATE_CMDQ (1<<12) /* card is in cmd queue mode */ unsigned intquirks; /* 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 */ @@ -427,6 +431,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_removed(c)((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_doing_bkops(c)((c)->state & MMC_STATE_DOING_BKOPS) #define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED) +#define mmc_card_cmdq(c) ((c)->state & MMC_STATE_CMDQ) #define mmc_card_set_present(c)((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -441,6 +446,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_clr_doing_bkops(c)((c)->state &= ~MMC_STATE_DOING_BKOPS) #define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED) #define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED) +#define mmc_card_set_cmdq(c) ((c)->state |= MMC_STATE_CMDQ) +#define mmc_card_clr_cmdq(c) ((c)->state &= ~MMC_STATE_CMDQ) /* * Quirk add/remove for MMC products. diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 50bcde3..a893c84 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -84,6 +84,9 @@ #define MMC_APP_CMD 55 /* ac [31:16] RCAR1 */ #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ +/* MMC 5.1 - class 11: Command Queueing */ +#define MMC_CMDQ_TASK_MGMT48 /* ac [31:0] task ID R1b */ + static inline bool mmc_op_multi(u32 opcode) { return opcode == MMC_WRITE_MULTIPLE_BLOCK || @@ -272,6 +275,7 @@ struct _mmc_csd { * EXT_CSD fields */ +#define EXT_CSD_CMDQ_MODE 15 /* R/W */ #define EXT_CSD_FLUSH_CACHE32 /* W */ #define EXT_CSD_CACHE_CTRL 33 /* R/W */ #define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ @@ -325,6 +329,8 @@ struct _mmc_csd { #define EXT_CSD_POWER_OFF_LONG_TIME247 /* RO */ #define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ #define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ +#define EXT_CSD_CMDQ_DEPTH 307 /* RO */ +#define EXT_CSD_CMDQ_SUPPORT
Re: [PATCH V7 1/6] scsi: ufs: fix endianness sparse warnings
On 11/18/2013 2:34 PM, vinayak holikatti wrote: On Mon, Sep 23, 2013 at 5:44 PM, vinayak holikatti wrote: On Thu, Sep 19, 2013 at 4:44 PM, Sujit Reddy Thumma wrote: Fix many warnings with incorrect endian assumptions which makes the code unportable to new architectures. The UFS specification defines the byte order as big-endian for UPIU structure and little-endian for the host controller transfer/task management descriptors. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 36 ++-- drivers/scsi/ufs/ufshcd.c | 42 -- drivers/scsi/ufs/ufshci.h | 32 3 files changed, 42 insertions(+), 68 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 7210500..f42d1ce 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -196,9 +196,9 @@ enum { * @dword_2: UPIU header DW-2 */ struct utp_upiu_header { - u32 dword_0; - u32 dword_1; - u32 dword_2; + __be32 dword_0; + __be32 dword_1; + __be32 dword_2; }; /** @@ -207,7 +207,7 @@ struct utp_upiu_header { * @cdb: Command Descriptor Block CDB DW-4 to DW-7 */ struct utp_upiu_cmd { - u32 exp_data_transfer_len; + __be32 exp_data_transfer_len; u8 cdb[MAX_CDB_SIZE]; }; @@ -228,10 +228,10 @@ struct utp_upiu_query { u8 idn; u8 index; u8 selector; - u16 reserved_osf; - u16 length; - u32 value; - u32 reserved[2]; + __be16 reserved_osf; + __be16 length; + __be32 value; + __be32 reserved[2]; }; /** @@ -256,9 +256,9 @@ struct utp_upiu_req { * @sense_data: Sense data field DW-8 to DW-12 */ struct utp_cmd_rsp { - u32 residual_transfer_count; - u32 reserved[4]; - u16 sense_data_len; + __be32 residual_transfer_count; + __be32 reserved[4]; + __be16 sense_data_len; u8 sense_data[18]; }; @@ -286,10 +286,10 @@ struct utp_upiu_rsp { */ struct utp_upiu_task_req { struct utp_upiu_header header; - u32 input_param1; - u32 input_param2; - u32 input_param3; - u32 reserved[2]; + __be32 input_param1; + __be32 input_param2; + __be32 input_param3; + __be32 reserved[2]; }; /** @@ -301,9 +301,9 @@ struct utp_upiu_task_req { */ struct utp_upiu_task_rsp { struct utp_upiu_header header; - u32 output_param1; - u32 output_param2; - u32 reserved[3]; + __be32 output_param1; + __be32 output_param2; + __be32 reserved[3]; }; /** diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 04884d6..064c9d9 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -163,7 +163,7 @@ static inline int ufshcd_is_device_present(u32 reg_hcs) */ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) { - return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS; + return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS; } /** @@ -176,7 +176,7 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) static inline int ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) { - return task_req_descp->header.dword_2 & MASK_OCS; + return le32_to_cpu(task_req_descp->header.dword_2) & MASK_OCS; } /** @@ -390,26 +390,6 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp) } /** - * ufshcd_query_to_cpu() - formats the buffer to native cpu endian - * @response: upiu query response to convert - */ -static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response) -{ - response->length = be16_to_cpu(response->length); - response->value = be32_to_cpu(response->value); -} - -/** - * ufshcd_query_to_be() - formats the buffer to big endian - * @request: upiu query request to convert - */ -static inline void ufshcd_query_to_be(struct utp_upiu_query *request) -{ - request->length = cpu_to_be16(request->length); - request->value = cpu_to_be32(request->value); -} - -/** * ufshcd_copy_query_response() - Copy the Query Response and the data * descriptor * @hba: per adapter instance @@ -425,7 +405,6 @@ void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) UPIU_RSP_CODE_OFFSET; memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE); - ufshcd_query_to_cpu(&query_res->upiu_res); /* Get the descriptor */ @@ -749,7 +728,7 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, { struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr; struct ufs_query *query = &hba->dev_cmd.query; - u16 len = query->request.upiu_req.length; + u16
[RESEND PATCH V7 1/6] scsi: ufs: fix endianness sparse warnings
Fix many warnings with incorrect endian assumptions which makes the code unportable to new architectures. The UFS specification defines the byte order as big-endian for UPIU structure and little-endian for the host controller transfer/task management descriptors. Signed-off-by: Sujit Reddy Thumma Acked-by: Vinayak Holikatti --- drivers/scsi/ufs/ufs.h| 36 ++-- drivers/scsi/ufs/ufshcd.c | 42 -- drivers/scsi/ufs/ufshci.h | 32 3 files changed, 42 insertions(+), 68 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 7210500..f42d1ce 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -196,9 +196,9 @@ enum { * @dword_2: UPIU header DW-2 */ struct utp_upiu_header { - u32 dword_0; - u32 dword_1; - u32 dword_2; + __be32 dword_0; + __be32 dword_1; + __be32 dword_2; }; /** @@ -207,7 +207,7 @@ struct utp_upiu_header { * @cdb: Command Descriptor Block CDB DW-4 to DW-7 */ struct utp_upiu_cmd { - u32 exp_data_transfer_len; + __be32 exp_data_transfer_len; u8 cdb[MAX_CDB_SIZE]; }; @@ -228,10 +228,10 @@ struct utp_upiu_query { u8 idn; u8 index; u8 selector; - u16 reserved_osf; - u16 length; - u32 value; - u32 reserved[2]; + __be16 reserved_osf; + __be16 length; + __be32 value; + __be32 reserved[2]; }; /** @@ -256,9 +256,9 @@ struct utp_upiu_req { * @sense_data: Sense data field DW-8 to DW-12 */ struct utp_cmd_rsp { - u32 residual_transfer_count; - u32 reserved[4]; - u16 sense_data_len; + __be32 residual_transfer_count; + __be32 reserved[4]; + __be16 sense_data_len; u8 sense_data[18]; }; @@ -286,10 +286,10 @@ struct utp_upiu_rsp { */ struct utp_upiu_task_req { struct utp_upiu_header header; - u32 input_param1; - u32 input_param2; - u32 input_param3; - u32 reserved[2]; + __be32 input_param1; + __be32 input_param2; + __be32 input_param3; + __be32 reserved[2]; }; /** @@ -301,9 +301,9 @@ struct utp_upiu_task_req { */ struct utp_upiu_task_rsp { struct utp_upiu_header header; - u32 output_param1; - u32 output_param2; - u32 reserved[3]; + __be32 output_param1; + __be32 output_param2; + __be32 reserved[3]; }; /** diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 04884d6..064c9d9 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -163,7 +163,7 @@ static inline int ufshcd_is_device_present(u32 reg_hcs) */ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) { - return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS; + return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS; } /** @@ -176,7 +176,7 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) static inline int ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) { - return task_req_descp->header.dword_2 & MASK_OCS; + return le32_to_cpu(task_req_descp->header.dword_2) & MASK_OCS; } /** @@ -390,26 +390,6 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp) } /** - * ufshcd_query_to_cpu() - formats the buffer to native cpu endian - * @response: upiu query response to convert - */ -static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response) -{ - response->length = be16_to_cpu(response->length); - response->value = be32_to_cpu(response->value); -} - -/** - * ufshcd_query_to_be() - formats the buffer to big endian - * @request: upiu query request to convert - */ -static inline void ufshcd_query_to_be(struct utp_upiu_query *request) -{ - request->length = cpu_to_be16(request->length); - request->value = cpu_to_be32(request->value); -} - -/** * ufshcd_copy_query_response() - Copy the Query Response and the data * descriptor * @hba: per adapter instance @@ -425,7 +405,6 @@ void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) UPIU_RSP_CODE_OFFSET; memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE); - ufshcd_query_to_cpu(&query_res->upiu_res); /* Get the descriptor */ @@ -749,7 +728,7 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, { struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr; struct ufs_query *query = &hba->dev_cmd.query; - u16 len = query->request.upiu_req.length; + u16 len = be16_to_cpu(query->request.upiu_req.length); u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE; /* Query request header */ @@ -766,7 +745,6 @@ static void u
[RESEND PATCH V7 0/6] scsi: ufs: TM, fatal-error handling and other fixes
This patch version fixes various static checker warnings along with fixing usage of deprecated flush_work_sync() API that led James drop the patchset from scsi-misc tree. The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches are rebased on scsi-misc and are ack'ed by UFS maintainer. Changes from v6: - Two new patches to fix static checker warnings - remove usage of deprecated flush_work_sync() API. Changes from v5: - correct typo in [PATCH V5 1/4] scsi: ufs: Fix broken task ... - correct poll wait in [PATCH V5 2/4] scsi: ufs: Fix hardware ... - Included Tested-by and Reviewed-by ack's. Changes from v4: - Addressed comments from Seungwon Jeon on V3 - Updated with proper locking while ufshcd state changes - Retained LUN reset instead of device reset with DME_END_POINT_RESET - Removed error handling decisions dependent on OCS value - Simplified fatal error handling to abort the requests first and then carry out reset. Changes from v3: - Rebased. Changes from v2: - [PATCH V3 1/4]: Make the task management command task tag unique across SCSI/NOP/QUERY request tags. - [PATCH V3 3/4]: While handling device/host reset, wait for pending fatal handler to return if running. Changes from v1: - [PATCH V2 1/4]: Fix a race condition because of overloading outstanding_tasks variable to lock the slots. A new variable tm_slots_in_use will track which slots are in use by the driver. - [PATCH V2 2/4]: Commit text update to clarify the hardware race with more details. - [PATCH V2 3/4]: Minor cleanup and rebase - [PATCH V2 4/4]: Fix a bug - sleeping in atomic context Sujit Reddy Thumma (6): scsi: ufs: fix endianness sparse warnings scsi: ufs: make undeclared functions static scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufs.h| 36 +-- drivers/scsi/ufs/ufshcd.c | 722 +++--- drivers/scsi/ufs/ufshcd.h | 22 +- drivers/scsi/ufs/ufshci.h | 32 +- 4 files changed, 545 insertions(+), 267 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[RESEND PATCH V7 3/6] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: Problem: If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. Problem: Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. Problem: If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. Problem: While preparing the TM command descriptor, the task tag used should be unique across SCSI/NOP/QUERY/TM commands and not the task tag of the command which the TM command is trying to manage. Fix: Use a unique task tag instead of task tag of SCSI command. Problem: Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv Acked-by: Vinayak Holikatti --- drivers/scsi/ufs/ufshcd.c | 169 +++--- drivers/scsi/ufs/ufshcd.h | 8 ++- 2 files changed, 122 insertions(+), 55 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d476cc3..c3acadc 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -55,6 +55,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + /* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF @@ -182,13 +185,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + int tag; + bool ret = false; + + if (!free_slot) + goto out; + + do { + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); + if (tag >= hba->nutmrs) + goto out; + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); + + *free_slot = tag; + ret = true; +out: + return ret; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) +{ + clear_bit_unlock(slot, &hba->tm_slots_in_use); } /** @@ -1912,10 +1937,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1936,19 +1962,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", + __func__, ocs_value); } spin_unlock_irqrestore(hba->host->host_lock, flags); - return task_result; + + return ocs_value; } /
[RESEND PATCH V7 6/6] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Processed requests which are completed w/wo error are reported to SCSI layer and any pending commands that are not started are aborted in the controller and re-queued into scsi mid-layer queue. o Upon determining fatal error condition the host controller may hang forever until a reset is applied. Block SCSI layer for sending new requests and apply reset in a separate error handling work. o SCSI is informed about the expected Unit-Attention exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv Acked-by: Vinayak Holikatti --- drivers/scsi/ufs/ufshcd.c | 229 -- drivers/scsi/ufs/ufshcd.h | 10 +- 2 files changed, 149 insertions(+), 90 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 5462310..0c28772 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -84,6 +84,14 @@ enum { UFSHCD_EH_IN_PROGRESS = (1 << 0), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -100,6 +108,8 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); +static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag); /* * ufshcd_wait_for_register - wait for register value to change @@ -1735,9 +1745,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1863,66 +1870,6 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < hba->nutrs; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - clear_bit_unlock(tag, &hba->lrb_in_use); - } - } - } - - /* complete device management command */ - if (hba->dev_cmd.complete) - complete(hba->dev_cmd.complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1939,6 +1886,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ + sdev->allow_restart = 1; + /* * Inform SCSI Midlayer that the LUN queue depth is same as the * controller queue depth. If a LUN queue depth is les
[RESEND PATCH V7 5/6] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv Acked-by: Vinayak Holikatti --- drivers/scsi/ufs/ufshcd.c | 240 -- drivers/scsi/ufs/ufshcd.h | 2 + 2 files changed, 189 insertions(+), 53 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 52f66e4..5462310 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -74,9 +74,14 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_IN_PROGRESS = (1 << 0), }; /* Interrupt configuration options */ @@ -86,6 +91,16 @@ enum { UFSHCD_INT_CLEAR, }; +#define ufshcd_set_eh_in_progress(h) \ + (h->eh_flags |= UFSHCD_EH_IN_PROGRESS) +#define ufshcd_eh_in_progress(h) \ + (h->eh_flags & UFSHCD_EH_IN_PROGRESS) +#define ufshcd_clear_eh_in_progress(h) \ + (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -856,10 +871,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + spin_lock_irqsave(hba->host->host_lock, flags); + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; - goto out; + goto out_unlock; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out_unlock; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + goto out_unlock; } + spin_unlock_irqrestore(hba->host->host_lock, flags); /* acquire the tag to make sure device cmds don't use it */ if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) { @@ -896,6 +926,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) /* issue command to the controller */ spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_send_command(hba, tag); +out_unlock: spin_unlock_irqrestore(hba->host->host_lock, flags); out: return err; @@ -1707,8 +1738,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2455,8 +2484,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba) } return; fatal_eh: - hba->ufshcd_state = UFSHCD_STATE_ERROR; - schedule_work(&hba->feh_workq); + /* handle fatal errors only when link is functional */ + if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { + /* block commands at driver layer until error is handled */ + hba->ufshcd_state = UFSHCD_STATE_ERROR; + schedule_work(&hba->feh_workq); + } } /** @@ -2621,12 +2654,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, } /** - * ufshcd_device_reset - reset device and abort all the pending commands + * ufshcd_eh_device_reset_handler - device reset handler registered to + *scsi layer. * @cmd: SCSI command pointer * * Returns SUCCESS/FAILED */ -static int ufshcd_device_reset(struct scsi_cmnd *cmd) +static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) { struct Scsi_Host *host; struct ufs_hba *hba; @@ -2635,6 +2669,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) int err; u8 resp = 0xF; struct ufshcd_lrb *lrbp; + unsigned long flags; host = cmd->device->host; hba = shost_priv(host); @@ -2643,55 +2678,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) lrbp = &hba->lrb[tag]; err = ufsh
[RESEND PATCH V7 4/6] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv Acked-by: Vinayak Holikatti --- drivers/scsi/ufs/ufshcd.c | 70 +-- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c3acadc..52f66e4 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2695,6 +2695,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2703,7 +2709,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt; u8 resp = 0xF; struct ufshcd_lrb *lrbp; @@ -2711,33 +2718,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) + goto out; - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + lrbp = &hba->lrb[tag]; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 200us to stabilize */ + usleep_range(100, 200); + continue; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = resp; /* service response error */ + goto out; + } + } + + if (!poll_cnt) { + err = -EBUSY; goto out; } - spin_unlock_irqrestore(host->host_lock, flags); - lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = resp; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &a
[RESEND PATCH V7 2/6] scsi: ufs: make undeclared functions static
Make undeclared functions static and declare exported symbols to suppress warnings from sparse tool. Signed-off-by: Sujit Reddy Thumma Acked-by: Vinayak Holikatti --- drivers/scsi/ufs/ufshcd.c | 4 ++-- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 064c9d9..d476cc3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1148,7 +1148,7 @@ out_unlock: * * Returns 0 for success, non-zero in case of failure */ -int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, +static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) { struct ufs_query_req *request; @@ -1459,7 +1459,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr); * * Returns 0 on success, non-zero value on failure */ -int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) +static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) { struct uic_command uic_cmd = {0}; struct completion pwr_done; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 577679a..767ee9e 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -263,6 +263,8 @@ static inline void check_upiu_size(void) GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE); } +extern int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state); +extern int ufshcd_resume(struct ufs_hba *hba); extern int ufshcd_runtime_suspend(struct ufs_hba *hba); extern int ufshcd_runtime_resume(struct ufs_hba *hba); extern int ufshcd_runtime_idle(struct ufs_hba *hba); -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH V2 1/3] scsi: ufs: Allow vendor specific initialization
On 9/9/2013 5:03 PM, Seungwon Jeon wrote: > Hi Sujit, > > On Tue, August 27, 2013, Sujit Reddy Thumma wrote: >> Some vendor specific controller versions might need to configure >> vendor specific - registers, clocks, voltage regulators etc. to >> initialize the host controller UTP layer and Uni-Pro stack. >> Provide some common initialization operations that can be used >> to configure vendor specifics. The methods can be extended in >> future, for example, for power mode transitions. >> >> The operations are vendor/board specific and hence determined with >> the help of compatible property in device tree. >> >> Signed-off-by: Sujit Reddy Thumma >> --- >> drivers/scsi/ufs/ufshcd-pci.c| 8 +- >> drivers/scsi/ufs/ufshcd-pltfrm.c | 24 +- >> drivers/scsi/ufs/ufshcd.c| 157 >> +++ >> drivers/scsi/ufs/ufshcd.h| 34 - >> 4 files changed, 187 insertions(+), 36 deletions(-) >> >> diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c >> index a823cf4..6b0d299 100644 >> --- a/drivers/scsi/ufs/ufshcd-pci.c >> +++ b/drivers/scsi/ufs/ufshcd-pci.c >> @@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct >> pci_device_id *id) >> return err; >> } >> >> -err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq); >> +err = ufshcd_alloc_host(&pdev->dev, &hba); >> +if (err) { >> +dev_err(&pdev->dev, "Allocation failed\n"); >> +return err; >> +} >> + >> +err = ufshcd_init(hba, mmio_base, pdev->irq); >> if (err) { >> dev_err(&pdev->dev, "Initialization failed\n"); >> return err; >> diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c >> b/drivers/scsi/ufs/ufshcd-pltfrm.c >> index 5e46232..9c94052 100644 >> --- a/drivers/scsi/ufs/ufshcd-pltfrm.c >> +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c >> @@ -35,9 +35,23 @@ >> >> #include >> #include >> +#include >> >> #include "ufshcd.h" >> >> +static const struct of_device_id ufs_of_match[]; >> +static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) >> +{ >> +if (dev->of_node) { >> +const struct of_device_id *match; >> +match = of_match_node(ufs_of_match, dev->of_node); >> +if (match) >> +return (struct ufs_hba_variant_ops *)match->data; >> +} >> + >> +return NULL; >> +} >> + >> #ifdef CONFIG_PM >> /** >>* ufshcd_pltfrm_suspend - suspend power management function >> @@ -150,10 +164,18 @@ static int ufshcd_pltfrm_probe(struct platform_device >> *pdev) >> goto out; >> } >> >> +err = ufshcd_alloc_host(dev, &hba); >> +if (err) { >> +dev_err(&pdev->dev, "Allocation failed\n"); >> +goto out; >> +} >> + >> +hba->vops = get_variant_ops(&pdev->dev); >> + >> pm_runtime_set_active(&pdev->dev); >> pm_runtime_enable(&pdev->dev); >> >> -err = ufshcd_init(dev, &hba, mmio_base, irq); >> +err = ufshcd_init(hba, mmio_base, irq); >> if (err) { >> dev_err(dev, "Intialization failed\n"); >> goto out_disable_rpm; >> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >> index a0f5ac2..743696a 100644 >> --- a/drivers/scsi/ufs/ufshcd.c >> +++ b/drivers/scsi/ufs/ufshcd.c >> @@ -174,13 +174,14 @@ static inline u32 ufshcd_get_ufs_version(struct >> ufs_hba *hba) >> /** >>* ufshcd_is_device_present - Check if any device connected to >>* the host controller >> - * @reg_hcs - host controller status register value >> + * @hba: pointer to adapter instance >>* >>* Returns 1 if device present, 0 if no device detected >>*/ >> -static inline int ufshcd_is_device_present(u32 reg_hcs) >> +static inline int ufshcd_is_device_present(struct ufs_hba *hba) >> { >> -return (DEVICE_PRESENT & reg_hcs) ? 1 : 0; >> +return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) & >> +DEVICE_PRESENT) ? 1 : 0; >> } >> >> /** >> @@ -1483,11 +1484,10 @@ out: >>* @hba: per adapter i
[PATCH V7 4/6] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 70 +-- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c3acadc..52f66e4 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2695,6 +2695,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2703,7 +2709,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt; u8 resp = 0xF; struct ufshcd_lrb *lrbp; @@ -2711,33 +2718,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) + goto out; - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + lrbp = &hba->lrb[tag]; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 200us to stabilize */ + usleep_range(100, 200); + continue; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = resp; /* service response error */ + goto out; + } + } + + if (!poll_cnt) { + err = -EBUSY; goto out; } - spin_unlock_irqrestore(host->host_lock, flags); - lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = resp; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &hba->outstanding_reqs);
[PATCH V7 6/6] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Processed requests which are completed w/wo error are reported to SCSI layer and any pending commands that are not started are aborted in the controller and re-queued into scsi mid-layer queue. o Upon determining fatal error condition the host controller may hang forever until a reset is applied. Block SCSI layer for sending new requests and apply reset in a separate error handling work. o SCSI is informed about the expected Unit-Attention exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 229 -- drivers/scsi/ufs/ufshcd.h | 10 +- 2 files changed, 149 insertions(+), 90 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 5462310..0c28772 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -84,6 +84,14 @@ enum { UFSHCD_EH_IN_PROGRESS = (1 << 0), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -100,6 +108,8 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); +static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag); /* * ufshcd_wait_for_register - wait for register value to change @@ -1735,9 +1745,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1863,66 +1870,6 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < hba->nutrs; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - clear_bit_unlock(tag, &hba->lrb_in_use); - } - } - } - - /* complete device management command */ - if (hba->dev_cmd.complete) - complete(hba->dev_cmd.complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1939,6 +1886,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ + sdev->allow_restart = 1; + /* * Inform SCSI Midlayer that the LUN queue depth is same as the * controller queue depth. If a LUN queue depth is less than the @@ -2134
[PATCH V7 5/6] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 240 -- drivers/scsi/ufs/ufshcd.h | 2 + 2 files changed, 189 insertions(+), 53 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 52f66e4..5462310 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -74,9 +74,14 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_IN_PROGRESS = (1 << 0), }; /* Interrupt configuration options */ @@ -86,6 +91,16 @@ enum { UFSHCD_INT_CLEAR, }; +#define ufshcd_set_eh_in_progress(h) \ + (h->eh_flags |= UFSHCD_EH_IN_PROGRESS) +#define ufshcd_eh_in_progress(h) \ + (h->eh_flags & UFSHCD_EH_IN_PROGRESS) +#define ufshcd_clear_eh_in_progress(h) \ + (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -856,10 +871,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + spin_lock_irqsave(hba->host->host_lock, flags); + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; - goto out; + goto out_unlock; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out_unlock; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + goto out_unlock; } + spin_unlock_irqrestore(hba->host->host_lock, flags); /* acquire the tag to make sure device cmds don't use it */ if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) { @@ -896,6 +926,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) /* issue command to the controller */ spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_send_command(hba, tag); +out_unlock: spin_unlock_irqrestore(hba->host->host_lock, flags); out: return err; @@ -1707,8 +1738,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2455,8 +2484,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba) } return; fatal_eh: - hba->ufshcd_state = UFSHCD_STATE_ERROR; - schedule_work(&hba->feh_workq); + /* handle fatal errors only when link is functional */ + if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { + /* block commands at driver layer until error is handled */ + hba->ufshcd_state = UFSHCD_STATE_ERROR; + schedule_work(&hba->feh_workq); + } } /** @@ -2621,12 +2654,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, } /** - * ufshcd_device_reset - reset device and abort all the pending commands + * ufshcd_eh_device_reset_handler - device reset handler registered to + *scsi layer. * @cmd: SCSI command pointer * * Returns SUCCESS/FAILED */ -static int ufshcd_device_reset(struct scsi_cmnd *cmd) +static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) { struct Scsi_Host *host; struct ufs_hba *hba; @@ -2635,6 +2669,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) int err; u8 resp = 0xF; struct ufshcd_lrb *lrbp; + unsigned long flags; host = cmd->device->host; hba = shost_priv(host); @@ -2643,55 +2678,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->
[PATCH V7 3/6] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: Problem: If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. Problem: Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. Problem: If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. Problem: While preparing the TM command descriptor, the task tag used should be unique across SCSI/NOP/QUERY/TM commands and not the task tag of the command which the TM command is trying to manage. Fix: Use a unique task tag instead of task tag of SCSI command. Problem: Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 169 +++--- drivers/scsi/ufs/ufshcd.h | 8 ++- 2 files changed, 122 insertions(+), 55 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d476cc3..c3acadc 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -55,6 +55,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + /* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF @@ -182,13 +185,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + int tag; + bool ret = false; + + if (!free_slot) + goto out; + + do { + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); + if (tag >= hba->nutmrs) + goto out; + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); + + *free_slot = tag; + ret = true; +out: + return ret; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) +{ + clear_bit_unlock(slot, &hba->tm_slots_in_use); } /** @@ -1912,10 +1937,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1936,19 +1962,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", + __func__, ocs_value); } spin_unlock_irqrestore(hba->host->host_lock, flags); - return task_result; + + return ocs_value; } /** @@ -2447,7 +2469
[PATCH V7 2/6] scsi: ufs: make undeclared functions static
Make undeclared functions static and declare exported symbols to suppress warnings from sparse tool. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 4 ++-- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 064c9d9..d476cc3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1148,7 +1148,7 @@ out_unlock: * * Returns 0 for success, non-zero in case of failure */ -int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, +static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) { struct ufs_query_req *request; @@ -1459,7 +1459,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr); * * Returns 0 on success, non-zero value on failure */ -int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) +static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) { struct uic_command uic_cmd = {0}; struct completion pwr_done; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 577679a..767ee9e 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -263,6 +263,8 @@ static inline void check_upiu_size(void) GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE); } +extern int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state); +extern int ufshcd_resume(struct ufs_hba *hba); extern int ufshcd_runtime_suspend(struct ufs_hba *hba); extern int ufshcd_runtime_resume(struct ufs_hba *hba); extern int ufshcd_runtime_idle(struct ufs_hba *hba); -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V7 1/6] scsi: ufs: fix endianness sparse warnings
Fix many warnings with incorrect endian assumptions which makes the code unportable to new architectures. The UFS specification defines the byte order as big-endian for UPIU structure and little-endian for the host controller transfer/task management descriptors. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 36 ++-- drivers/scsi/ufs/ufshcd.c | 42 -- drivers/scsi/ufs/ufshci.h | 32 3 files changed, 42 insertions(+), 68 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 7210500..f42d1ce 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -196,9 +196,9 @@ enum { * @dword_2: UPIU header DW-2 */ struct utp_upiu_header { - u32 dword_0; - u32 dword_1; - u32 dword_2; + __be32 dword_0; + __be32 dword_1; + __be32 dword_2; }; /** @@ -207,7 +207,7 @@ struct utp_upiu_header { * @cdb: Command Descriptor Block CDB DW-4 to DW-7 */ struct utp_upiu_cmd { - u32 exp_data_transfer_len; + __be32 exp_data_transfer_len; u8 cdb[MAX_CDB_SIZE]; }; @@ -228,10 +228,10 @@ struct utp_upiu_query { u8 idn; u8 index; u8 selector; - u16 reserved_osf; - u16 length; - u32 value; - u32 reserved[2]; + __be16 reserved_osf; + __be16 length; + __be32 value; + __be32 reserved[2]; }; /** @@ -256,9 +256,9 @@ struct utp_upiu_req { * @sense_data: Sense data field DW-8 to DW-12 */ struct utp_cmd_rsp { - u32 residual_transfer_count; - u32 reserved[4]; - u16 sense_data_len; + __be32 residual_transfer_count; + __be32 reserved[4]; + __be16 sense_data_len; u8 sense_data[18]; }; @@ -286,10 +286,10 @@ struct utp_upiu_rsp { */ struct utp_upiu_task_req { struct utp_upiu_header header; - u32 input_param1; - u32 input_param2; - u32 input_param3; - u32 reserved[2]; + __be32 input_param1; + __be32 input_param2; + __be32 input_param3; + __be32 reserved[2]; }; /** @@ -301,9 +301,9 @@ struct utp_upiu_task_req { */ struct utp_upiu_task_rsp { struct utp_upiu_header header; - u32 output_param1; - u32 output_param2; - u32 reserved[3]; + __be32 output_param1; + __be32 output_param2; + __be32 reserved[3]; }; /** diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 04884d6..064c9d9 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -163,7 +163,7 @@ static inline int ufshcd_is_device_present(u32 reg_hcs) */ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) { - return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS; + return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS; } /** @@ -176,7 +176,7 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp) static inline int ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) { - return task_req_descp->header.dword_2 & MASK_OCS; + return le32_to_cpu(task_req_descp->header.dword_2) & MASK_OCS; } /** @@ -390,26 +390,6 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp) } /** - * ufshcd_query_to_cpu() - formats the buffer to native cpu endian - * @response: upiu query response to convert - */ -static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response) -{ - response->length = be16_to_cpu(response->length); - response->value = be32_to_cpu(response->value); -} - -/** - * ufshcd_query_to_be() - formats the buffer to big endian - * @request: upiu query request to convert - */ -static inline void ufshcd_query_to_be(struct utp_upiu_query *request) -{ - request->length = cpu_to_be16(request->length); - request->value = cpu_to_be32(request->value); -} - -/** * ufshcd_copy_query_response() - Copy the Query Response and the data * descriptor * @hba: per adapter instance @@ -425,7 +405,6 @@ void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) UPIU_RSP_CODE_OFFSET; memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE); - ufshcd_query_to_cpu(&query_res->upiu_res); /* Get the descriptor */ @@ -749,7 +728,7 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, { struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr; struct ufs_query *query = &hba->dev_cmd.query; - u16 len = query->request.upiu_req.length; + u16 len = be16_to_cpu(query->request.upiu_req.length); u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE; /* Query request header */ @@ -766,7 +745,6 @@ static void ufshcd_prepare_utp_query_req_upiu(st
[PATCH V7 0/6] scsi: ufs: TM, fatal-error handling and other fixes
This patch version fixes various static checker warnings along with fixing usage of deprecated flush_work_sync() API that led James drop the patchset from scsi-misc tree. The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches are rebased on scsi-misc. Changes from v6: - Two new patches to fix static checker warnings - remove usage of deprecated flush_work_sync() API. Changes from v5: - correct typo in [PATCH V5 1/4] scsi: ufs: Fix broken task ... - correct poll wait in [PATCH V5 2/4] scsi: ufs: Fix hardware ... - Included Tested-by and Reviewed-by ack's. Changes from v4: - Addressed comments from Seungwon Jeon on V3 - Updated with proper locking while ufshcd state changes - Retained LUN reset instead of device reset with DME_END_POINT_RESET - Removed error handling decisions dependent on OCS value - Simplified fatal error handling to abort the requests first and then carry out reset. Changes from v3: - Rebased. Changes from v2: - [PATCH V3 1/4]: Make the task management command task tag unique across SCSI/NOP/QUERY request tags. - [PATCH V3 3/4]: While handling device/host reset, wait for pending fatal handler to return if running. Changes from v1: - [PATCH V2 1/4]: Fix a race condition because of overloading outstanding_tasks variable to lock the slots. A new variable tm_slots_in_use will track which slots are in use by the driver. - [PATCH V2 2/4]: Commit text update to clarify the hardware race with more details. - [PATCH V2 3/4]: Minor cleanup and rebase - [PATCH V2 4/4]: Fix a bug - sleeping in atomic context Sujit Reddy Thumma (6): scsi: ufs: fix endianness sparse warnings scsi: ufs: make undeclared functions static scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufs.h| 36 +-- drivers/scsi/ufs/ufshcd.c | 722 +++--- drivers/scsi/ufs/ufshcd.h | 22 +- drivers/scsi/ufs/ufshci.h | 32 +- 4 files changed, 545 insertions(+), 267 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V2 2/3] scsi: ufs: Add regulator enable support
UFS devices are powered by at most three external power supplies - - VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V - VCCQ - The controller and I/O power supply, 1.1V to 1.3V - VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V For some devices VCCQ or VCCQ2 are optional as they can be generated using internal LDO inside the UFS device. Add DT bindings for voltage regulators that can be controlled from host driver. Signed-off-by: Sujit Reddy Thumma --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 24 +++ drivers/scsi/ufs/ufs.h | 25 +++ drivers/scsi/ufs/ufshcd-pltfrm.c | 100 +++ drivers/scsi/ufs/ufshcd.c | 193 - drivers/scsi/ufs/ufshcd.h | 3 + 5 files changed, 342 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 20468b2..65e3117 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -8,9 +8,33 @@ Required properties: - interrupts: - reg : +Optional properties: +- vcc-supply: phandle to VCC supply regulator node +- vccq-supply : phandle to VCCQ supply regulator node +- vccq2-supply : phandle to VCCQ2 supply regulator node +- vcc-supply-1p8: For embedded UFS devices, valid VCC range is 1.7-1.95V + or 2.7-3.6V. This boolean property when set, specifies + to use low voltage range of 1.7-1.95V. Note for external + UFS cards this property is invalid and valid VCC range is + always 2.7-3.6V. +- vcc-max-microamp : specifies max. load that can be drawn from vcc supply +- vccq-max-microamp : specifies max. load that can be drawn from vccq supply +- vccq2-max-microamp: specifies max. load that can be drawn from vccq2 supply + +Note: If above properties are not defined it can be assumed that the supply +regulators are always on. + Example: ufshc@0xfc598000 { compatible = "jedec,ufs-1.1"; reg = <0xfc598000 0x800>; interrupts = <0 28 0>; + + vcc-supply = <&xxx_reg1>; + vcc-supply-1p8; + vccq-supply = <&xxx_reg2>; + vccq2-supply = <&xxx_reg3>; + vcc-max-microamp = 50; + vccq-max-microamp = 20; + vccq2-max-microamp = 20; }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index bce09a6..7db080e 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -325,4 +325,29 @@ struct ufs_query_res { struct utp_upiu_query upiu_res; }; +#define UFS_VREG_VCC_MIN_UV 270 /* uV */ +#define UFS_VREG_VCC_MAX_UV 360 /* uV */ +#define UFS_VREG_VCC_1P8_MIN_UV170 /* uV */ +#define UFS_VREG_VCC_1P8_MAX_UV195 /* uV */ +#define UFS_VREG_VCCQ_MIN_UV 110 /* uV */ +#define UFS_VREG_VCCQ_MAX_UV 130 /* uV */ +#define UFS_VREG_VCCQ2_MIN_UV 165 /* uV */ +#define UFS_VREG_VCCQ2_MAX_UV 195 /* uV */ + +struct ufs_vreg { + struct regulator *reg; + const char *name; + bool enabled; + int min_uV; + int max_uV; + int min_uA; + int max_uA; +}; + +struct ufs_vreg_info { + struct ufs_vreg *vcc; + struct ufs_vreg *vccq; + struct ufs_vreg *vccq2; +}; + #endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 9c94052..cbdf5f3 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -52,6 +52,99 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) return NULL; } +#define MAX_PROP_SIZE 32 +static int ufshcd_populate_vreg(struct device *dev, const char *name, + struct ufs_vreg **out_vreg) +{ + int ret = 0; + char prop_name[MAX_PROP_SIZE]; + struct ufs_vreg *vreg = NULL; + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "%s: non DT initialization\n", __func__); + goto out; + } + + snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); + if (!of_parse_phandle(np, prop_name, 0)) { + dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", + __func__, prop_name); + goto out; + } + + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + dev_err(dev, "No memory for %s regulator\n", name); + goto out; + } + + vreg->name
[PATCH V2 0/3] scsi: ufs: Add support for clock and regulator initializaiton
UFS HCI specificaiton allows the hardware vendors to have vendor specific configurations using a dedicated register space. Most of these register configurations are unique to each controller. The generic UFSHCD exports a set of useful vendor operations that can be used to configure the host controller. In addition, the vendor specific operations can also be used to control the power supply of controller and Uni-Pro h/w blocks. This patchset adds support for vendor specific initialization, regulator and clock initialization based on device-tree properties. Changes from v1: - Rebased on scsi-misc. - Minor code fixes. Sujit Reddy Thumma (3): scsi: ufs: Allow vendor specific initialization scsi: ufs: Add regulator enable support scsi: ufs: Add clock initialization support .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 37 ++ drivers/scsi/ufs/ufs.h | 25 ++ drivers/scsi/ufs/ufshcd-pci.c | 8 +- drivers/scsi/ufs/ufshcd-pltfrm.c | 195 +- drivers/scsi/ufs/ufshcd.c | 429 +++-- drivers/scsi/ufs/ufshcd.h | 55 ++- 6 files changed, 713 insertions(+), 36 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V2 3/3] scsi: ufs: Add clock initialization support
Add generic clock initialization support for UFSHCD platform driver. The clock info is read from device tree using standard clock bindings. A generic max-clock-frequency-hz property is defined to save information on maximum operating clock frequency the h/w supports. Signed-off-by: Sujit Reddy Thumma --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 15 +++- drivers/scsi/ufs/ufshcd-pltfrm.c | 71 + drivers/scsi/ufs/ufshcd.c | 89 +- drivers/scsi/ufs/ufshcd.h | 18 + 4 files changed, 190 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 65e3117..b0f791a 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -21,8 +21,17 @@ Optional properties: - vccq-max-microamp : specifies max. load that can be drawn from vccq supply - vccq2-max-microamp: specifies max. load that can be drawn from vccq2 supply +- clocks: List of phandle and clock specifier pairs +- clock-names : List of clock input name strings sorted in the same + order as the clocks property. +- max-clock-frequency-hz : List of maximum operating frequency stored in the same + order as the clocks property. If this property is not + defined or a value in the array is "0" then it is assumed + that the frequency is set by the parent clock or a + fixed rate clock source. + Note: If above properties are not defined it can be assumed that the supply -regulators are always on. +regulators or clocks are always on. Example: ufshc@0xfc598000 { @@ -37,4 +46,8 @@ Example: vcc-max-microamp = 50; vccq-max-microamp = 20; vccq2-max-microamp = 20; + + clocks = <&core 0>, <&ref 0>, <&iface 0>; + clock-names = "core_clk", "ref_clk", "iface_clk"; + max-clock-frequency-hz = <1 1920 0>; }; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index cbdf5f3..15c8086 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -52,6 +52,71 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) return NULL; } +static int ufshcd_parse_clock_info(struct ufs_hba *hba) +{ + int ret = 0; + int cnt; + int i; + struct device *dev = hba->dev; + struct device_node *np = dev->of_node; + char *name; + u32 *clkfreq = NULL; + struct ufs_clk_info *clki; + + if (!np) + goto out; + + INIT_LIST_HEAD(&hba->clk_list_head); + + cnt = of_property_count_strings(np, "clock-names"); + if (!cnt || (cnt == -EINVAL)) { + dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", + __func__); + } else if (cnt < 0) { + dev_err(dev, "%s: count clock strings failed, err %d\n", + __func__, cnt); + ret = cnt; + } + + if (cnt <= 0) + goto out; + + clkfreq = kzalloc(cnt * sizeof(*clkfreq), GFP_KERNEL); + if (!clkfreq) { + ret = -ENOMEM; + dev_err(dev, "%s: memory alloc failed\n", __func__); + goto out; + } + + ret = of_property_read_u32_array(np, + "max-clock-frequency-hz", clkfreq, cnt); + if (ret && (ret != -EINVAL)) { + dev_err(dev, "%s: invalid max-clock-frequency-hz property, %d\n", + __func__, ret); + goto out; + } + + for (i = 0; i < cnt; i++) { + ret = of_property_read_string_index(np, + "clock-names", i, (const char **)&name); + if (ret) + goto out; + + clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); + if (!clki) { + ret = -ENOMEM; + goto out; + } + + clki->max_freq = clkfreq[i]; + clki->name = kstrdup(name, GFP_KERNEL); + list_add_tail(&clki->list, &hba->clk_list_head); + } +out: + kfree(clkfreq); + return ret; +} + #define MAX_PROP_SIZE 32 static int ufshcd_populate_vreg(struct device *dev, const char *name, struct ufs_vreg **out_vreg) @@ -265,6 +330,12 @@ static int ufshcd_pltfrm_probe(struct
[PATCH V2 1/3] scsi: ufs: Allow vendor specific initialization
Some vendor specific controller versions might need to configure vendor specific - registers, clocks, voltage regulators etc. to initialize the host controller UTP layer and Uni-Pro stack. Provide some common initialization operations that can be used to configure vendor specifics. The methods can be extended in future, for example, for power mode transitions. The operations are vendor/board specific and hence determined with the help of compatible property in device tree. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd-pci.c| 8 +- drivers/scsi/ufs/ufshcd-pltfrm.c | 24 +- drivers/scsi/ufs/ufshcd.c| 157 +++ drivers/scsi/ufs/ufshcd.h| 34 - 4 files changed, 187 insertions(+), 36 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index a823cf4..6b0d299 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq); + err = ufshcd_alloc_host(&pdev->dev, &hba); + if (err) { + dev_err(&pdev->dev, "Allocation failed\n"); + return err; + } + + err = ufshcd_init(hba, mmio_base, pdev->irq); if (err) { dev_err(&pdev->dev, "Initialization failed\n"); return err; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 5e46232..9c94052 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -35,9 +35,23 @@ #include #include +#include #include "ufshcd.h" +static const struct of_device_id ufs_of_match[]; +static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) +{ + if (dev->of_node) { + const struct of_device_id *match; + match = of_match_node(ufs_of_match, dev->of_node); + if (match) + return (struct ufs_hba_variant_ops *)match->data; + } + + return NULL; +} + #ifdef CONFIG_PM /** * ufshcd_pltfrm_suspend - suspend power management function @@ -150,10 +164,18 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) goto out; } + err = ufshcd_alloc_host(dev, &hba); + if (err) { + dev_err(&pdev->dev, "Allocation failed\n"); + goto out; + } + + hba->vops = get_variant_ops(&pdev->dev); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - err = ufshcd_init(dev, &hba, mmio_base, irq); + err = ufshcd_init(hba, mmio_base, irq); if (err) { dev_err(dev, "Intialization failed\n"); goto out_disable_rpm; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a0f5ac2..743696a 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -174,13 +174,14 @@ static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) /** * ufshcd_is_device_present - Check if any device connected to * the host controller - * @reg_hcs - host controller status register value + * @hba: pointer to adapter instance * * Returns 1 if device present, 0 if no device detected */ -static inline int ufshcd_is_device_present(u32 reg_hcs) +static inline int ufshcd_is_device_present(struct ufs_hba *hba) { - return (DEVICE_PRESENT & reg_hcs) ? 1 : 0; + return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) & + DEVICE_PRESENT) ? 1 : 0; } /** @@ -1483,11 +1484,10 @@ out: * @hba: per adapter instance * * To bring UFS host controller to operational state, - * 1. Check if device is present - * 2. Enable required interrupts - * 3. Configure interrupt aggregation - * 4. Program UTRL and UTMRL base addres - * 5. Configure run-stop-registers + * 1. Enable required interrupts + * 2. Configure interrupt aggregation + * 3. Program UTRL and UTMRL base addres + * 4. Configure run-stop-registers * * Returns 0 on success, non-zero value on failure */ @@ -1496,14 +1496,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) int err = 0; u32 reg; - /* check if device present */ - reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS); - if (!ufshcd_is_device_present(reg)) { - dev_err(hba->dev, "cc: Device not present\n"); - err = -ENXIO; - goto out; - } - /* Enable required interrupts */ ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS); @@ -1524,6 +1516,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
[PATCH V6 0/4] scsi: ufs: Improve UFS error handling
The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches are rebased on: [PATCH 9/9] drivers/scsi/ufs: don't check resource with devm_ioremap_resource Changes from v5: - correct typo in [PATCH V5 1/4] scsi: ufs: Fix broken task ... - correct poll wait in [PATCH V5 2/4] scsi: ufs: Fix hardware ... - Included Tested-by and Reviewed-by ack's. Changes from v4: - Addressed comments from Seungwon Jeon on V3 - Updated with proper locking while ufshcd state changes - Retained LUN reset instead of device reset with DME_END_POINT_RESET - Removed error handling decisions dependent on OCS value - Simplified fatal error handling to abort the requests first and then carry out reset. Changes from v3: - Rebased. Changes from v2: - [PATCH V3 1/4]: Make the task management command task tag unique across SCSI/NOP/QUERY request tags. - [PATCH V3 3/4]: While handling device/host reset, wait for pending fatal handler to return if running. Changes from v1: - [PATCH V2 1/4]: Fix a race condition because of overloading outstanding_tasks variable to lock the slots. A new variable tm_slots_in_use will track which slots are in use by the driver. - [PATCH V2 2/4]: Commit text update to clarify the hardware race with more details. - [PATCH V2 3/4]: Minor cleanup and rebase - [PATCH V2 4/4]: Fix a bug - sleeping in atomic context Sujit Reddy Thumma (4): scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufshcd.c | 684 - drivers/scsi/ufs/ufshcd.h | 20 +- 2 files changed, 501 insertions(+), 203 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V6 3/4] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 240 +++-- drivers/scsi/ufs/ufshcd.h |2 + 2 files changed, 189 insertions(+), 53 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 24c5ab3..dfa61be 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -69,9 +69,14 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_IN_PROGRESS = (1 << 0), }; /* Interrupt configuration options */ @@ -87,6 +92,16 @@ enum { INT_AGGR_CONFIG, }; +#define ufshcd_set_eh_in_progress(h) \ + (h->eh_flags |= UFSHCD_EH_IN_PROGRESS) +#define ufshcd_eh_in_progress(h) \ + (h->eh_flags & UFSHCD_EH_IN_PROGRESS) +#define ufshcd_clear_eh_in_progress(h) \ + (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -840,10 +855,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + spin_lock_irqsave(hba->host->host_lock, flags); + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; - goto out; + goto out_unlock; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out_unlock; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + goto out_unlock; } + spin_unlock_irqrestore(hba->host->host_lock, flags); /* acquire the tag to make sure device cmds don't use it */ if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) { @@ -880,6 +910,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) /* issue command to the controller */ spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_send_command(hba, tag); +out_unlock: spin_unlock_irqrestore(hba->host->host_lock, flags); out: return err; @@ -1495,8 +1526,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2245,8 +2274,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba) } return; fatal_eh: - hba->ufshcd_state = UFSHCD_STATE_ERROR; - schedule_work(&hba->feh_workq); + /* handle fatal errors only when link is functional */ + if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { + /* block commands at driver layer until error is handled */ + hba->ufshcd_state = UFSHCD_STATE_ERROR; + schedule_work(&hba->feh_workq); + } } /** @@ -2411,12 +2444,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, } /** - * ufshcd_device_reset - reset device and abort all the pending commands + * ufshcd_eh_device_reset_handler - device reset handler registered to + *scsi layer. * @cmd: SCSI command pointer * * Returns SUCCESS/FAILED */ -static int ufshcd_device_reset(struct scsi_cmnd *cmd) +static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) { struct Scsi_Host *host; struct ufs_hba *hba; @@ -2425,6 +2459,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) int err; u8 resp = 0xF; struct ufshcd_lrb *lrbp; + unsigned long flags; host = cmd->device->host; hba = shost_priv(host); @@ -2433,55 +2468,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->
[PATCH V6 1/4] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: Problem: If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. Problem: Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. Problem: If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. Problem: While preparing the TM command descriptor, the task tag used should be unique across SCSI/NOP/QUERY/TM commands and not the task tag of the command which the TM command is trying to manage. Fix: Use a unique task tag instead of task tag of SCSI command. Problem: Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 173 ++--- drivers/scsi/ufs/ufshcd.h |8 ++- 2 files changed, 122 insertions(+), 59 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b36ca9a..e0810a3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -53,6 +53,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + /* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF @@ -183,13 +186,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + int tag; + bool ret = false; + + if (!free_slot) + goto out; + + do { + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); + if (tag >= hba->nutmrs) + goto out; + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); + + *free_slot = tag; + ret = true; +out: + return ret; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) +{ + clear_bit_unlock(slot, &hba->tm_slots_in_use); } /** @@ -1700,10 +1725,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1724,19 +1750,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", + __func__, ocs_value); } spin_unlock_irqrestore(hba->host->host_lock, flags); - return task_result; + + return ocs_value; } /** @@ -2237,7 +2259
[PATCH V6 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 70 +++- 1 files changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index e0810a3..24c5ab3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2485,6 +2485,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2493,7 +2499,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt; u8 resp = 0xF; struct ufshcd_lrb *lrbp; @@ -2501,33 +2508,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) + goto out; - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + lrbp = &hba->lrb[tag]; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 200us to stabilize */ + usleep_range(100, 200); + continue; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = resp; /* service response error */ + goto out; + } + } + + if (!poll_cnt) { + err = -EBUSY; goto out; } - spin_unlock_irqrestore(host->host_lock, flags); - lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = resp; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &hba->outstanding_reqs);
[PATCH V6 4/4] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Processed requests which are completed w/wo error are reported to SCSI layer and any pending commands that are not started are aborted in the controller and re-queued into scsi mid-layer queue. o Upon determining fatal error condition the host controller may hang forever until a reset is applied. Block SCSI layer for sending new requests and apply reset in a separate error handling work. o SCSI is informed about the expected Unit-Attention exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma Reviewed-by: Yaniv Gardi Tested-by: Dolev Raviv --- drivers/scsi/ufs/ufshcd.c | 229 - drivers/scsi/ufs/ufshcd.h | 10 ++- 2 files changed, 149 insertions(+), 90 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index dfa61be..a0f5ac2 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -79,6 +79,14 @@ enum { UFSHCD_EH_IN_PROGRESS = (1 << 0), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -101,6 +109,8 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); +static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag); /* * ufshcd_wait_for_register - wait for register value to change @@ -1523,9 +1533,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1651,66 +1658,6 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < hba->nutrs; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - clear_bit_unlock(tag, &hba->lrb_in_use); - } - } - } - - /* complete device management command */ - if (hba->dev_cmd.complete) - complete(hba->dev_cmd.complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1727,6 +1674,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ + sdev->allow_restart = 1; + /* * Inform SCSI Midlayer that the LUN queue depth is same as the * controller queue depth. If a LUN queue depth is less than the @@ -1930
Re: [PATCH 1/3] scsi: ufs: Allow vendor specific initialization
On 8/13/2013 7:23 PM, Josh Cartwright wrote: On Tue, Aug 13, 2013 at 04:30:18PM +0530, Sujit Reddy Thumma wrote: [..] diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index a823cf4..829f7a4 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq); + err = ufshcd_alloc_host(&pdev->dev, &hba); + if (err) { + dev_err(&pdev->dev, "Allocation failed\n"); + goto out_iounmap You seem to be missing a semicolon here. I should have compiled the with pci config enabled :) Also, which tree were these patches generated against? They fail to apply at least on the last few 3.11-rc's. I have rebased on top of scsi-misc branch along with few UFS patches that aren't merged ([PATCH 0/9] scsi:ufs: query, bkops support and other fixes + [PATCH V5 0/4] scsi: ufs: Improve UFS error handling patch series). -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/3] scsi: ufs: Add clock initialization support
Add generic clock initialization support for UFSHCD platform driver. The clock info is read from device tree using standard clock bindings. A generic max-clock-frequency-hz property is defined to save information on maximum operating clock frequency the h/w supports. Signed-off-by: Sujit Reddy Thumma --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 15 +++- drivers/scsi/ufs/ufshcd-pltfrm.c | 64 ++ drivers/scsi/ufs/ufshcd.c | 90 +++- drivers/scsi/ufs/ufshcd.h | 18 4 files changed, 184 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 39ee11f..d82c54c 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -16,8 +16,17 @@ Optional properties: - vccq-max-microamp : specifies max. load that can be drawn from vccq supply - vccq2-max-microamp: specifies max. load that can be drawn from vccq2 supply +- clocks: List of phandle and clock specifier pairs +- clock-names : List of clock input name strings sorted in the same + order as the clocks property. +- max-clock-frequency-hz : List of maximum operating frequency stored in the same + order as the clocks property. If this property is not + defined or a value in the array is "0" then it is assumed + that the frequency is set by the parent clock or a + fixed rate clock source. + Note: If above properties are not defined it can be assumed that the supply -regulators are always on. +regulators or clocks are always on. Example: ufshc@0xfc598000 { @@ -31,4 +40,8 @@ Example: vcc-max-microamp = 50; vccq-max-microamp = 20; vccq2-max-microamp = 20; + + clocks = <&core 0>, <&ref 0>, <&iface 0>; + clock-names = "core_clk", "ref_clk", "iface_clk"; + max-clock-frequency-hz = <1 1920 0>; }; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 1c5290d..9bbcbcb 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -38,6 +38,7 @@ #include #include "ufshcd.h" +#define MAX_CLK_CNT100 static const struct of_device_id ufs_of_match[]; static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) @@ -51,6 +52,63 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) return NULL; } +static int ufshcd_parse_clock_info(struct ufs_hba *hba) +{ + int ret = 0; + int cnt; + int i; + struct device *dev = hba->dev; + struct device_node *np = dev->of_node; + char *name; + u32 clkfreq[MAX_CLK_CNT] = {0}; + struct ufs_clk_info *clki; + + if (!np) + goto out; + + INIT_LIST_HEAD(&hba->clk_list_head); + + cnt = of_property_count_strings(np, "clock-names"); + if (!cnt || (cnt == -EINVAL)) { + dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", + __func__); + } else if (cnt < 0) { + dev_err(dev, "%s: count clock strings failed, err %d\n", + __func__, cnt); + ret = cnt; + } + + if (cnt <= 0) + goto out; + + ret = of_property_read_u32_array(np, + "max-clock-frequency-hz", clkfreq, cnt); + if (ret && (ret != -EINVAL)) { + dev_err(dev, "%s: invalid max-clock-frequency-hz property, %d\n", + __func__, ret); + goto out; + } + + for (i = 0; i < cnt; i++) { + ret = of_property_read_string_index(np, + "clock-names", i, (const char **)&name); + if (ret) + goto out; + + clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); + if (!clki) { + ret = -ENOMEM; + goto out; + } + + clki->max_freq = clkfreq[i]; + clki->name = kstrdup(name, GFP_KERNEL); + list_add_tail(&clki->list, &hba->clk_list_head); + } +out: + return ret; +} + #define MAX_PROP_SIZE 32 static int ufshcd_populate_vreg(struct device *dev, const char *name, struct ufs_vreg **out_vreg) @@ -253,6 +311,12 @@ static int ufshcd_pltfrm_probe(struct plat
[PATCH 1/3] scsi: ufs: Allow vendor specific initialization
Some vendor specific controller versions might need to configure vendor specific - registers, clocks, voltage regulators etc. to initialize the host controller UTP layer and Uni-Pro stack. Provide some common initialization operations that can be used to configure vendor specifics. The methods can be extended in future, for example, for power mode transitions. The operations are vendor/board specific and hence determined with the help of compatible property in device tree. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd-pci.c|8 ++- drivers/scsi/ufs/ufshcd-pltfrm.c | 23 +- drivers/scsi/ufs/ufshcd.c| 157 ++ drivers/scsi/ufs/ufshcd.h| 34 - 4 files changed, 185 insertions(+), 37 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index a823cf4..829f7a4 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -191,7 +191,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; } - err = ufshcd_init(&pdev->dev, &hba, mmio_base, pdev->irq); + err = ufshcd_alloc_host(&pdev->dev, &hba); + if (err) { + dev_err(&pdev->dev, "Allocation failed\n"); + goto out_iounmap + } + + err = ufshcd_init(hba, mmio_base, pdev->irq); if (err) { dev_err(&pdev->dev, "Initialization failed\n"); return err; diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 5e46232..3e0a14f 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -35,9 +35,22 @@ #include #include +#include #include "ufshcd.h" +static const struct of_device_id ufs_of_match[]; +static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) +{ + if (dev->of_node) { + const struct of_device_id *match; + match = of_match_node(ufs_of_match, dev->of_node); + return (struct ufs_hba_variant_ops *)match->data; + } + + return NULL; +} + #ifdef CONFIG_PM /** * ufshcd_pltfrm_suspend - suspend power management function @@ -150,10 +163,18 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) goto out; } + err = ufshcd_alloc_host(dev, &hba); + if (err) { + dev_err(&pdev->dev, "Allocation failed\n"); + goto out; + } + + hba->vops = get_variant_ops(&pdev->dev); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - err = ufshcd_init(dev, &hba, mmio_base, irq); + err = ufshcd_init(hba, mmio_base, irq); if (err) { dev_err(dev, "Intialization failed\n"); goto out_disable_rpm; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 4dca9b4..c539347 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -174,13 +174,14 @@ static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) /** * ufshcd_is_device_present - Check if any device connected to * the host controller - * @reg_hcs - host controller status register value + * @hba: pointer to adapter instance * * Returns 1 if device present, 0 if no device detected */ -static inline int ufshcd_is_device_present(u32 reg_hcs) +static inline int ufshcd_is_device_present(struct ufs_hba *hba) { - return (DEVICE_PRESENT & reg_hcs) ? 1 : 0; + return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) & + DEVICE_PRESENT) ? 1 : 0; } /** @@ -1483,11 +1484,10 @@ out: * @hba: per adapter instance * * To bring UFS host controller to operational state, - * 1. Check if device is present - * 2. Enable required interrupts - * 3. Configure interrupt aggregation - * 4. Program UTRL and UTMRL base addres - * 5. Configure run-stop-registers + * 1. Enable required interrupts + * 2. Configure interrupt aggregation + * 3. Program UTRL and UTMRL base addres + * 4. Configure run-stop-registers * * Returns 0 on success, non-zero value on failure */ @@ -1496,14 +1496,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) int err = 0; u32 reg; - /* check if device present */ - reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS); - if (!ufshcd_is_device_present(reg)) { - dev_err(hba->dev, "cc: Device not present\n"); - err = -ENXIO; - goto out; - } - /* Enable required interrupts */ ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS); @@ -1524,6 +1516,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) * UCRDY, UTMRLDY and UT
[PATCH 0/3] scsi: ufs: Add support for clock and regulator initializaiton
UFS HCI specificaiton allows the hardware vendors to have vendor specific configurations using a dedicated register space. Most of these register configurations are unique to each controller. The generic UFSHCD exports a set of useful vendor operations that can be used to configure the host controller. In addition, the vendor specific operations can also be used to control the power supply of controller and Uni-Pro h/w blocks. This patchset adds support for vendor specific initialization, regulator and clock initialization based on device-tree properties. Sujit Reddy Thumma (3): scsi: ufs: Allow vendor specific initialization scsi: ufs: Add regulator enable support scsi: ufs: Add clock initialization support .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 31 ++ drivers/scsi/ufs/ufs.h | 23 + drivers/scsi/ufs/ufshcd-pci.c |8 +- drivers/scsi/ufs/ufshcd-pltfrm.c | 176 - drivers/scsi/ufs/ufshcd.c | 433 ++-- drivers/scsi/ufs/ufshcd.h | 55 +++- 6 files changed, 689 insertions(+), 37 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/3] scsi: ufs: Add regulator enable support
UFS devices are powered by at most three external power supplies - - VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V - VCCQ - The controller and I/O power supply, 1.1V to 1.3V - VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V For some devices VCCQ or VCCQ2 are optional as they can be generated using internal LDO inside the UFS device. Add DT bindings for voltage regulators that can be controlled from host driver. Signed-off-by: Sujit Reddy Thumma --- .../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 18 ++ drivers/scsi/ufs/ufs.h | 23 +++ drivers/scsi/ufs/ufshcd-pltfrm.c | 89 + drivers/scsi/ufs/ufshcd.c | 194 +++- drivers/scsi/ufs/ufshcd.h |3 + 5 files changed, 325 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index 20468b2..39ee11f 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -8,9 +8,27 @@ Required properties: - interrupts: - reg : +Optional properties: +- vcc-supply: phandle to VCC supply regulator node +- vccq-supply : phandle to VCCQ supply regulator node +- vccq2-supply : phandle to VCCQ2 supply regulator node +- vcc-max-microamp : specifies max. load that can be drawn from vcc supply +- vccq-max-microamp : specifies max. load that can be drawn from vccq supply +- vccq2-max-microamp: specifies max. load that can be drawn from vccq2 supply + +Note: If above properties are not defined it can be assumed that the supply +regulators are always on. + Example: ufshc@0xfc598000 { compatible = "jedec,ufs-1.1"; reg = <0xfc598000 0x800>; interrupts = <0 28 0>; + + vcc-supply = <&xxx_reg1>; + vccq-supply = <&xxx_reg2>; + vccq2-supply = <&xxx_reg3>; + vcc-max-microamp = 50; + vccq-max-microamp = 20; + vccq2-max-microamp = 20; }; diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index bce09a6..f8e37bf 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -325,4 +325,27 @@ struct ufs_query_res { struct utp_upiu_query upiu_res; }; +#define UFS_VREG_VCC_MIN 270 /* uV */ +#define UFS_VREG_VCC_MAX 360 /* uV */ +#define UFS_VREG_VCCQ_MIN 115 /* uV */ +#define UFS_VREG_VCCQ_MAX 130 /* uV */ +#define UFS_VREG_VCCQ2_MIN 170 /* uV */ +#define UFS_VREG_VCCQ2_MAX 195 /* uV */ + +struct ufs_vreg { + struct regulator *reg; + const char *name; + bool enabled; + int min_uV; + int max_uV; + int min_uA; + int max_uA; +}; + +struct ufs_vreg_info { + struct ufs_vreg *vcc; + struct ufs_vreg *vccq; + struct ufs_vreg *vccq2; +}; + #endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 3e0a14f..1c5290d 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -51,6 +51,88 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) return NULL; } +#define MAX_PROP_SIZE 32 +static int ufshcd_populate_vreg(struct device *dev, const char *name, + struct ufs_vreg **out_vreg) +{ + int ret = 0; + u32 temp = 0; + char prop_name[MAX_PROP_SIZE]; + struct ufs_vreg *vreg = NULL; + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "%s: non DT initialization\n", __func__); + goto out; + } + + snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); + if (!of_parse_phandle(np, prop_name, 0)) { + dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", + __func__, name); + goto out; + } + + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + dev_err(dev, "No memory for %s regulator\n", name); + goto out; + } + + vreg->name = kstrdup(name, GFP_KERNEL); + + snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); + ret = of_property_read_u32_array(np, prop_name, &temp, 1); + if (!ret) { + vreg->max_uA = temp; + } else { + dev_err(dev, "%s: unable to find %s err %d\n", + __func__, prop_name, ret); + goto out_free; + } + + vreg->min_uA = 0; + if (!strcmp(name, "vcc"))
[PATCH V5 4/4] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Processed requests which are completed w/wo error are reported to SCSI layer and any pending commands that are not started are aborted in the controller and re-queued into scsi mid-layer queue. o Upon determining fatal error condition the host controller may hang forever until a reset is applied. Block SCSI layer for sending new requests and apply reset in a separate error handling work. o SCSI is informed about the expected Unit-Attention exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 229 - drivers/scsi/ufs/ufshcd.h | 10 ++- 2 files changed, 149 insertions(+), 90 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c577e6e..4dca9b4 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -79,6 +79,14 @@ enum { UFSHCD_EH_IN_PROGRESS = (1 << 0), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -101,6 +109,8 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); +static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag); /* * ufshcd_wait_for_register - wait for register value to change @@ -1523,9 +1533,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1651,66 +1658,6 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < hba->nutrs; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - clear_bit_unlock(tag, &hba->lrb_in_use); - } - } - } - - /* complete device management command */ - if (hba->dev_cmd.complete) - complete(hba->dev_cmd.complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1727,6 +1674,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ + sdev->allow_restart = 1; + /* * Inform SCSI Midlayer that the LUN queue depth is same as the * controller queue depth. If a LUN queue depth is less than the @@ -1930,6 +1880,9 @@ ufshcd_transfer_rsp_sta
[PATCH V5 0/4] scsi: ufs: Improve UFS error handling
The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches are rebased on: [PATCH 9/9] drivers/scsi/ufs: don't check resource with devm_ioremap_resource Changes from v4: - Addressed comments from Seungwon Jeon on V3 - Updated with proper locking while ufshcd state changes - Retained LUN reset instead of device reset with DME_END_POINT_RESET - Removed error handling decisions dependent on OCS value - Simplified fatal error handling to abort the requests first and then carry out reset. Changes from v3: - Rebased. Changes from v2: - [PATCH V3 1/4]: Make the task management command task tag unique across SCSI/NOP/QUERY request tags. - [PATCH V3 3/4]: While handling device/host reset, wait for pending fatal handler to return if running. Changes from v1: - [PATCH V2 1/4]: Fix a race condition because of overloading outstanding_tasks variable to lock the slots. A new variable tm_slots_in_use will track which slots are in use by the driver. - [PATCH V2 2/4]: Commit text update to clarify the hardware race with more details. - [PATCH V2 3/4]: Minor cleanup and rebase - [PATCH V2 4/4]: Fix a bug - sleeping in atomic context Sujit Reddy Thumma (4): scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufshcd.c | 684 - drivers/scsi/ufs/ufshcd.h | 20 +- 2 files changed, 501 insertions(+), 203 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V5 3/4] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 240 +++-- drivers/scsi/ufs/ufshcd.h |2 + 2 files changed, 189 insertions(+), 53 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d4ee48d..c577e6e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -69,9 +69,14 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_IN_PROGRESS = (1 << 0), }; /* Interrupt configuration options */ @@ -87,6 +92,16 @@ enum { INT_AGGR_CONFIG, }; +#define ufshcd_set_eh_in_progress(h) \ + (h->eh_flags |= UFSHCD_EH_IN_PROGRESS) +#define ufshcd_eh_in_progress(h) \ + (h->eh_flags & UFSHCD_EH_IN_PROGRESS) +#define ufshcd_clear_eh_in_progress(h) \ + (h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -840,10 +855,25 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + spin_lock_irqsave(hba->host->host_lock, flags); + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; - goto out; + goto out_unlock; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out_unlock; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + goto out_unlock; } + spin_unlock_irqrestore(hba->host->host_lock, flags); /* acquire the tag to make sure device cmds don't use it */ if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) { @@ -880,6 +910,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) /* issue command to the controller */ spin_lock_irqsave(hba->host->host_lock, flags); ufshcd_send_command(hba, tag); +out_unlock: spin_unlock_irqrestore(hba->host->host_lock, flags); out: return err; @@ -1495,8 +1526,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2245,8 +2274,12 @@ static void ufshcd_err_handler(struct ufs_hba *hba) } return; fatal_eh: - hba->ufshcd_state = UFSHCD_STATE_ERROR; - schedule_work(&hba->feh_workq); + /* handle fatal errors only when link is functional */ + if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { + /* block commands at driver layer until error is handled */ + hba->ufshcd_state = UFSHCD_STATE_ERROR; + schedule_work(&hba->feh_workq); + } } /** @@ -2411,12 +2444,13 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, } /** - * ufshcd_device_reset - reset device and abort all the pending commands + * ufshcd_eh_device_reset_handler - device reset handler registered to + *scsi layer. * @cmd: SCSI command pointer * * Returns SUCCESS/FAILED */ -static int ufshcd_device_reset(struct scsi_cmnd *cmd) +static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) { struct Scsi_Host *host; struct ufs_hba *hba; @@ -2425,6 +2459,7 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) int err; u8 resp = 0xF; struct ufshcd_lrb *lrbp; + unsigned long flags; host = cmd->device->host; hba = shost_priv(host); @@ -2433,55 +2468,33 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd) lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
[PATCH V5 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 70 +++- 1 files changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d7f3746..d4ee48d 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2485,6 +2485,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2493,7 +2499,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt; u8 resp = 0xF; struct ufshcd_lrb *lrbp; @@ -2501,33 +2508,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) + goto out; - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + lrbp = &hba->lrb[tag]; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 2ms to stabilize */ + usleep_range(1000, 2000); + continue; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = resp; /* service response error */ + goto out; + } + } + + if (!poll_cnt) { + err = -EBUSY; goto out; } - spin_unlock_irqrestore(host->host_lock, flags); - lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = resp; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &hba->outstanding_reqs); hba->lrb[tag].cmd = NULL; spin_unlo
[PATCH V5 1/4] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: Problem: If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. Problem: Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. Problem: If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. Problem: While preparing the TM command descriptor, the task tag used should be unique across SCSI/NOP/QUERY/TM commands and not the task tag of the command which the TM command is trying to manage. Fix: Use a unique task tag instead of task tag of SCSI command. Problem: Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 173 ++--- drivers/scsi/ufs/ufshcd.h |8 ++- 2 files changed, 122 insertions(+), 59 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b36ca9a..d7f3746 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -53,6 +53,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + /* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF @@ -183,13 +186,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + int tag; + bool ret = false; + + if (!free_slot) + goto out; + + do { + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); + if (tag >= hba->nutmrs) + goto out; + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); + + *free_slot = tag; + ret = true; +out: + return ret; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) +{ + clear_bit_unlock(slot, &hba->tm_slots_in_use); } /** @@ -1700,10 +1725,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1724,19 +1750,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", + __func__, ocs_value); } spin_unlock_irqrestore(hba->host->host_lock, flags); - return task_result; + + return ocs_value; } /** @@ -2237,7 +2259,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba
Re: [PATCH V3 3/4] scsi: ufs: Fix device and host reset methods
On 7/24/2013 7:09 PM, Seungwon Jeon wrote: > On Wed, July 24, 2013, Sujit Reddy Thumma wrote: >> On 7/23/2013 1:57 PM, Seungwon Jeon wrote: >>> On Sat, July 20, 2013, Sujit Reddy Thumma wrote: >>>> On 7/19/2013 7:27 PM, Seungwon Jeon wrote: >>>>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >>>>>> As of now SCSI initiated error handling is broken because, >>>>>> the reset APIs don't try to bring back the device initialized and >>>>>> ready for further transfers. >>>>>> >>>>>> In case of timeouts, the scsi error handler takes care of handling aborts >>>>>> and resets. Improve the error handling in such scenario by resetting the >>>>>> device and host and re-initializing them in proper manner. >>>>>> >>>>>> Signed-off-by: Sujit Reddy Thumma >>>>>> --- >>>>>> drivers/scsi/ufs/ufshcd.c | 467 >>>>>> +++-- >>>>>> drivers/scsi/ufs/ufshcd.h |2 + >>>>>> 2 files changed, 411 insertions(+), 58 deletions(-) >>>>>> >>>>>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >>>>>> index 51ce096..b4c9910 100644 >>>>>> --- a/drivers/scsi/ufs/ufshcd.c >>>>>> +++ b/drivers/scsi/ufs/ufshcd.c >>>>>> @@ -69,9 +69,15 @@ enum { >>>>>> >>>>>> /* UFSHCD states */ >>>>>> enum { >>>>>> -UFSHCD_STATE_OPERATIONAL, >>>>>> UFSHCD_STATE_RESET, >>>>>> UFSHCD_STATE_ERROR, >>>>>> +UFSHCD_STATE_OPERATIONAL, >>>>>> +}; >>>>>> + >>>>>> +/* UFSHCD error handling flags */ >>>>>> +enum { >>>>>> +UFSHCD_EH_HOST_RESET_PENDING = (1 << 0), >>>>>> +UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), >>>>>> }; >>>>>> >>>>>> /* Interrupt configuration options */ >>>>>> @@ -87,6 +93,22 @@ enum { >>>>>> INT_AGGR_CONFIG, >>>>>> }; >>>>>> >>>>>> +#define ufshcd_set_device_reset_pending(h) \ >>>>>> +(h->eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING) >>>>>> +#define ufshcd_set_host_reset_pending(h) \ >>>>>> +(h->eh_flags |= UFSHCD_EH_HOST_RESET_PENDING) >>>>>> +#define ufshcd_device_reset_pending(h) \ >>>>>> +(h->eh_flags & UFSHCD_EH_DEVICE_RESET_PENDING) >>>>>> +#define ufshcd_host_reset_pending(h) \ >>>>>> +(h->eh_flags & UFSHCD_EH_HOST_RESET_PENDING) >>>>>> +#define ufshcd_clear_device_reset_pending(h) \ >>>>>> +(h->eh_flags &= ~UFSHCD_EH_DEVICE_RESET_PENDING) >>>>>> +#define ufshcd_clear_host_reset_pending(h) \ >>>>>> +(h->eh_flags &= ~UFSHCD_EH_HOST_RESET_PENDING) >>>>>> + >>>>>> +static void ufshcd_tmc_handler(struct ufs_hba *hba); >>>>>> +static void ufshcd_async_scan(void *data, async_cookie_t cookie); >>>>>> + >>>>>> /* >>>>>> * ufshcd_wait_for_register - wait for register value to change >>>>>> * @hba - per-adapter interface >>>>>> @@ -851,9 +873,22 @@ static int ufshcd_queuecommand(struct Scsi_Host >>>>>> *host, struct scsi_cmnd *cmd) >>>>>> >>>>>> tag = cmd->request->tag; >>>>>> >>>>>> -if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { >>>>>> +switch (hba->ufshcd_state) { >>>>> Lock is no needed for ufshcd_state? >>> Please check? >> >> Yes, it is needed. Thanks for catching this. >> >>> >>>>> >>>>>> +case UFSHCD_STATE_OPERATIONAL: >>>>>> +break; >>>>>> +case UFSHCD_STATE_RESET: >>>>>> err = SCSI_MLQUEUE_HOST_BUSY; >>>>>> goto out; >>>>>> +case UFSHCD_STATE_ERROR: >>>>>> +set_host_byte(cmd, DID_ERROR); >>>>>> +
Re: [PATCH V3 4/4] scsi: ufs: Improve UFS fatal error handling
On 7/24/2013 7:09 PM, Seungwon Jeon wrote: > On Wed, July 24, 2013, Sujit Reddy Thumma wrote: >> On 7/23/2013 2:04 PM, Seungwon Jeon wrote: >>> On Sat, July 20, 2013, Sujit Reddy Thumma wrote: >>>> On 7/19/2013 7:28 PM, Seungwon Jeon wrote: >>>>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >>>>>> Error handling in UFS driver is broken and resets the host controller >>>>>> for fatal errors without re-initialization. Correct the fatal error >>>>>> handling sequence according to UFS Host Controller Interface (HCI) >>>>>> v1.1 specification. >>>>>> >>>>>> o Upon determining fatal error condition the host controller may hang >>>>>> forever until a reset is applied, so just retrying the command >>>>>> doesn't >>>>>> work without a reset. So, the reset is applied in the driver context >>>>>> in a separate work and SCSI mid-layer isn't informed until reset is >>>>>> applied. >>>>>> >>>>>> o Processed requests which are completed without error are reported to >>>>>> SCSI layer as successful and any pending commands that are not >>>>>> started >>>>>> yet or are not cause of the error are re-queued into scsi midlayer >>>>>> queue. >>>>>> For the command that caused error, host controller or device is >>>>>> reset >>>>>> and DID_ERROR is returned for command retry after applying reset. >>>>>> >>>>>> o SCSI is informed about the expected Unit-Attentioni exception from the >>>>> Attention'i', typo. >>>> Okay. >>>> >>>>> >>>>>> device for the immediate command after a reset so that the SCSI >>>>>> layer >>>>>> take necessary steps to establish communication with the device. >>>>>> >>>>>> Signed-off-by: Sujit Reddy Thumma >>>>>> --- >>>>>> drivers/scsi/ufs/ufshcd.c | 349 >>>>>> +++- >>>>>> drivers/scsi/ufs/ufshcd.h |2 + >>>>>> drivers/scsi/ufs/ufshci.h | 19 ++- >>>>>> 3 files changed, 295 insertions(+), 75 deletions(-) >>>>>> >>>>>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >>>>>> index b4c9910..2a3874f 100644 >>>>>> --- a/drivers/scsi/ufs/ufshcd.c >>>>>> +++ b/drivers/scsi/ufs/ufshcd.c >>>>>> @@ -80,6 +80,14 @@ enum { >>>>>> UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), >>>>>> }; >>>>>> >>>>>> +/* UFSHCD UIC layer error flags */ >>>>>> +enum { >>>>>> +UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer >>>>>> error */ >>>>>> +UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ >>>>>> +UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ >>>>>> +UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ >>>>>> +}; >>>>>> + >>>>>> /* Interrupt configuration options */ >>>>>> enum { >>>>>> UFSHCD_INT_DISABLE, >>>>>> @@ -108,6 +116,7 @@ enum { >>>>>> >>>>>> static void ufshcd_tmc_handler(struct ufs_hba *hba); >>>>>> static void ufshcd_async_scan(void *data, async_cookie_t cookie); >>>>>> +static int ufshcd_reset_and_restore(struct ufs_hba *hba); >>>>>> >>>>>> /* >>>>>> * ufshcd_wait_for_register - wait for register value to change >>>>>> @@ -1605,9 +1614,6 @@ static int ufshcd_make_hba_operational(struct >>>>>> ufs_hba *hba) >>>>>> goto out; >>>>>> } >>>>>> >>>>>> -if (hba->ufshcd_state == UFSHCD_STATE_RESET) >>>>>> -scsi_unblock_requests(hba->host); >>>>>> - >>>>>> out: >>>>>> return err; >>>>>> } >>>>>> @@ -1733
Re: [PATCH V3 4/4] scsi: ufs: Improve UFS fatal error handling
On 7/23/2013 2:04 PM, Seungwon Jeon wrote: > On Sat, July 20, 2013, Sujit Reddy Thumma wrote: >> On 7/19/2013 7:28 PM, Seungwon Jeon wrote: >>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >>>> Error handling in UFS driver is broken and resets the host controller >>>> for fatal errors without re-initialization. Correct the fatal error >>>> handling sequence according to UFS Host Controller Interface (HCI) >>>> v1.1 specification. >>>> >>>> o Upon determining fatal error condition the host controller may hang >>>> forever until a reset is applied, so just retrying the command doesn't >>>> work without a reset. So, the reset is applied in the driver context >>>> in a separate work and SCSI mid-layer isn't informed until reset is >>>> applied. >>>> >>>> o Processed requests which are completed without error are reported to >>>> SCSI layer as successful and any pending commands that are not started >>>> yet or are not cause of the error are re-queued into scsi midlayer >>>> queue. >>>> For the command that caused error, host controller or device is reset >>>> and DID_ERROR is returned for command retry after applying reset. >>>> >>>> o SCSI is informed about the expected Unit-Attentioni exception from the >>> Attention'i', typo. >> Okay. >> >>> >>>> device for the immediate command after a reset so that the SCSI layer >>>> take necessary steps to establish communication with the device. >>>> >>>> Signed-off-by: Sujit Reddy Thumma >>>> --- >>>>drivers/scsi/ufs/ufshcd.c | 349 >>>> +++- >>>>drivers/scsi/ufs/ufshcd.h |2 + >>>>drivers/scsi/ufs/ufshci.h | 19 ++- >>>>3 files changed, 295 insertions(+), 75 deletions(-) >>>> >>>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >>>> index b4c9910..2a3874f 100644 >>>> --- a/drivers/scsi/ufs/ufshcd.c >>>> +++ b/drivers/scsi/ufs/ufshcd.c >>>> @@ -80,6 +80,14 @@ enum { >>>>UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), >>>>}; >>>> >>>> +/* UFSHCD UIC layer error flags */ >>>> +enum { >>>> + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ >>>> + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ >>>> + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ >>>> + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ >>>> +}; >>>> + >>>>/* Interrupt configuration options */ >>>>enum { >>>>UFSHCD_INT_DISABLE, >>>> @@ -108,6 +116,7 @@ enum { >>>> >>>>static void ufshcd_tmc_handler(struct ufs_hba *hba); >>>>static void ufshcd_async_scan(void *data, async_cookie_t cookie); >>>> +static int ufshcd_reset_and_restore(struct ufs_hba *hba); >>>> >>>>/* >>>> * ufshcd_wait_for_register - wait for register value to change >>>> @@ -1605,9 +1614,6 @@ static int ufshcd_make_hba_operational(struct >>>> ufs_hba *hba) >>>>goto out; >>>>} >>>> >>>> - if (hba->ufshcd_state == UFSHCD_STATE_RESET) >>>> - scsi_unblock_requests(hba->host); >>>> - >>>>out: >>>>return err; >>>>} >>>> @@ -1733,66 +1739,6 @@ static int ufshcd_validate_dev_connection(struct >>>> ufs_hba *hba) >>>>} >>>> >>>>/** >>>> - * ufshcd_do_reset - reset the host controller >>>> - * @hba: per adapter instance >>>> - * >>>> - * Returns SUCCESS/FAILED >>>> - */ >>>> -static int ufshcd_do_reset(struct ufs_hba *hba) >>>> -{ >>>> - struct ufshcd_lrb *lrbp; >>>> - unsigned long flags; >>>> - int tag; >>>> - >>>> - /* block commands from midlayer */ >>>> - scsi_block_requests(hba->host); >>>> - >>>> - spin_lock_irqsave(hba->host->host_lock, flags); >>>> - hba->ufshcd_state = UFSHCD_STATE_RESET; >>>> - >>>> - /* send controller to reset state */ >>>>
Re: [PATCH V3 1/4] scsi: ufs: Fix broken task management command implementation
On 7/23/2013 1:54 PM, Seungwon Jeon wrote: > On Sat, July 20, 2013, Sujit Reddy Thumma wrote: >> On 7/19/2013 7:26 PM, Seungwon Jeon wrote: >>> On Tue, July 09, 2013 Sujit Reddy Thumma wrote: >>>> Currently, sending Task Management (TM) command to the card might >>>> be broken in some scenarios as listed below: >>>> >>>> Problem: If there are more than 8 TM commands the implementation >>>>returns error to the caller. >>>> Fix: Wait for one of the slots to be emptied and send the command. >>>> >>>> Problem: Sometimes it is necessary for the caller to know the TM service >>>>response code to determine the task status. >>>> Fix: Propogate the service response to the caller. >>>> >>>> Problem: If the TM command times out no proper error recovery is >>>>implemented. >>>> Fix: Clear the command in the controller door-bell register, so that >>>>further commands for the same slot don't fail. >>>> >>>> Problem: While preparing the TM command descriptor, the task tag used >>>>should be unique across SCSI/NOP/QUERY/TM commands and not the >>>> task tag of the command which the TM command is trying to manage. >>>> Fix: Use a unique task tag instead of task tag of SCSI command. >>>> >>>> Problem: Since the TM command involves H/W communication, abruptly ending >>>>the request on kill interrupt signal might cause h/w >>>> malfunction. >>>> Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE >>>>set. >>>> >>>> Signed-off-by: Sujit Reddy Thumma >>>> --- >>>>drivers/scsi/ufs/ufshcd.c | 177 >>>> ++--- >>>>drivers/scsi/ufs/ufshcd.h |8 ++- >>>>2 files changed, 126 insertions(+), 59 deletions(-) >>>> >>>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >>>> index af7d01d..a176421 100644 >>>> --- a/drivers/scsi/ufs/ufshcd.c >>>> +++ b/drivers/scsi/ufs/ufshcd.c >>>> @@ -53,6 +53,9 @@ >>>>/* Query request timeout */ >>>>#define QUERY_REQ_TIMEOUT 30 /* msec */ >>>> >>>> +/* Task management command timeout */ >>>> +#define TM_CMD_TIMEOUT100 /* msecs */ >>>> + >>>>/* Expose the flag value from utp_upiu_query.value */ >>>>#define MASK_QUERY_UPIU_FLAG_LOC 0xFF >>>> >>>> @@ -190,13 +193,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc >>>> *task_req_descp) >>>>/** >>>> * ufshcd_get_tm_free_slot - get a free slot for task management request >>>> * @hba: per adapter instance >>>> + * @free_slot: pointer to variable with available slot value >>>> * >>>> - * Returns maximum number of task management request slots in case of >>>> - * task management queue full or returns the free slot number >>>> + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. >>>> + * Returns 0 if free slot is not available, else return 1 with tag value >>>> + * in @free_slot. >>>> */ >>>> -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) >>>> +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) >>>> +{ >>>> + int tag; >>>> + bool ret = false; >>>> + >>>> + if (!free_slot) >>>> + goto out; >>>> + >>>> + do { >>>> + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); >>>> + if (tag >= hba->nutmrs) >>>> + goto out; >>>> + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); >>>> + >>>> + *free_slot = tag; >>>> + ret = true; >>>> +out: >>>> + return ret; >>>> +} >>>> + >>>> +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) >>>>{ >>>> - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); >>>> + clear_bit_unlock(slot, &hba->tm_slots_in_use); >>>>} >>>> >>>>/** >>>> @@ -1778,10 +1803,11 @@ static void ufshcd_sl
Re: [PATCH V3 3/4] scsi: ufs: Fix device and host reset methods
On 7/23/2013 1:57 PM, Seungwon Jeon wrote: > On Sat, July 20, 2013, Sujit Reddy Thumma wrote: >> On 7/19/2013 7:27 PM, Seungwon Jeon wrote: >>> On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >>>> As of now SCSI initiated error handling is broken because, >>>> the reset APIs don't try to bring back the device initialized and >>>> ready for further transfers. >>>> >>>> In case of timeouts, the scsi error handler takes care of handling aborts >>>> and resets. Improve the error handling in such scenario by resetting the >>>> device and host and re-initializing them in proper manner. >>>> >>>> Signed-off-by: Sujit Reddy Thumma >>>> --- >>>>drivers/scsi/ufs/ufshcd.c | 467 >>>> +++-- >>>>drivers/scsi/ufs/ufshcd.h |2 + >>>>2 files changed, 411 insertions(+), 58 deletions(-) >>>> >>>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >>>> index 51ce096..b4c9910 100644 >>>> --- a/drivers/scsi/ufs/ufshcd.c >>>> +++ b/drivers/scsi/ufs/ufshcd.c >>>> @@ -69,9 +69,15 @@ enum { >>>> >>>>/* UFSHCD states */ >>>>enum { >>>> - UFSHCD_STATE_OPERATIONAL, >>>>UFSHCD_STATE_RESET, >>>>UFSHCD_STATE_ERROR, >>>> + UFSHCD_STATE_OPERATIONAL, >>>> +}; >>>> + >>>> +/* UFSHCD error handling flags */ >>>> +enum { >>>> + UFSHCD_EH_HOST_RESET_PENDING = (1 << 0), >>>> + UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), >>>>}; >>>> >>>>/* Interrupt configuration options */ >>>> @@ -87,6 +93,22 @@ enum { >>>>INT_AGGR_CONFIG, >>>>}; >>>> >>>> +#define ufshcd_set_device_reset_pending(h) \ >>>> + (h->eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING) >>>> +#define ufshcd_set_host_reset_pending(h) \ >>>> + (h->eh_flags |= UFSHCD_EH_HOST_RESET_PENDING) >>>> +#define ufshcd_device_reset_pending(h) \ >>>> + (h->eh_flags & UFSHCD_EH_DEVICE_RESET_PENDING) >>>> +#define ufshcd_host_reset_pending(h) \ >>>> + (h->eh_flags & UFSHCD_EH_HOST_RESET_PENDING) >>>> +#define ufshcd_clear_device_reset_pending(h) \ >>>> + (h->eh_flags &= ~UFSHCD_EH_DEVICE_RESET_PENDING) >>>> +#define ufshcd_clear_host_reset_pending(h) \ >>>> + (h->eh_flags &= ~UFSHCD_EH_HOST_RESET_PENDING) >>>> + >>>> +static void ufshcd_tmc_handler(struct ufs_hba *hba); >>>> +static void ufshcd_async_scan(void *data, async_cookie_t cookie); >>>> + >>>>/* >>>> * ufshcd_wait_for_register - wait for register value to change >>>> * @hba - per-adapter interface >>>> @@ -851,9 +873,22 @@ static int ufshcd_queuecommand(struct Scsi_Host >>>> *host, struct scsi_cmnd *cmd) >>>> >>>>tag = cmd->request->tag; >>>> >>>> - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { >>>> + switch (hba->ufshcd_state) { >>> Lock is no needed for ufshcd_state? > Please check? Yes, it is needed. Thanks for catching this. > >>> >>>> + case UFSHCD_STATE_OPERATIONAL: >>>> + break; >>>> + case UFSHCD_STATE_RESET: >>>>err = SCSI_MLQUEUE_HOST_BUSY; >>>>goto out; >>>> + case UFSHCD_STATE_ERROR: >>>> + set_host_byte(cmd, DID_ERROR); >>>> + cmd->scsi_done(cmd); >>>> + goto out; >>>> + default: >>>> + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", >>>> + __func__, hba->ufshcd_state); >>>> + set_host_byte(cmd, DID_BAD_TARGET); >>>> + cmd->scsi_done(cmd); >>>> + goto out; >>>>} >>>> >>>>/* acquire the tag to make sure device cmds don't use it */ >>>> @@ -1573,8 +1608,6 @@ static int ufshcd_make_hba_operational(struct >>>> ufs_hba *hba) >>>>if (hba->ufshcd_state == UFSHCD_STATE_RESET) >>>>scsi_unblock_requests(hba->host); >>>> >>>> - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
[PATCH V4 1/4] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: Problem: If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. Problem: Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. Problem: If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. Problem: While preparing the TM command descriptor, the task tag used should be unique across SCSI/NOP/QUERY/TM commands and not the task tag of the command which the TM command is trying to manage. Fix: Use a unique task tag instead of task tag of SCSI command. Problem: Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 174 ++--- drivers/scsi/ufs/ufshcd.h |8 ++- 2 files changed, 123 insertions(+), 59 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 1f2caa0..1d7e027 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -53,6 +53,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + /* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF @@ -183,13 +186,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + int tag; + bool ret = false; + + if (!free_slot) + goto out; + + do { + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); + if (tag >= hba->nutmrs) + goto out; + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); + + *free_slot = tag; + ret = true; +out: + return ret; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) +{ + clear_bit_unlock(slot, &hba->tm_slots_in_use); } /** @@ -1700,10 +1725,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1724,19 +1750,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", + __func__, ocs_value); } spin_unlock_irqrestore(hba->host->host_lock, flags); - return task_result; + + return ocs_value; } /** @@ -2237,7 +2259,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba
[PATCH V4 4/4] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Upon determining fatal error condition the host controller may hang forever until a reset is applied, so just retrying the command doesn't work without a reset. So, the reset is applied in the driver context in a separate work and SCSI mid-layer isn't informed until reset is applied. o Processed requests which are completed without error are reported to SCSI layer as successful and any pending commands that are not started yet or are not cause of the error are re-queued into scsi midlayer queue. For the command that caused error, host controller or device is reset and DID_ERROR is returned for command retry after applying reset. o SCSI is informed about the expected Unit-Attention exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 350 +++- drivers/scsi/ufs/ufshcd.h |2 + drivers/scsi/ufs/ufshci.h | 19 ++- 3 files changed, 296 insertions(+), 75 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 1a0ceb2..82600d6 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -80,6 +80,14 @@ enum { UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -108,6 +116,7 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); /* * ufshcd_wait_for_register - wait for register value to change @@ -1527,9 +1536,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1655,66 +1661,6 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < hba->nutrs; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - clear_bit_unlock(tag, &hba->lrb_in_use); - } - } - } - - /* complete device management command */ - if (hba->dev_cmd.complete) - complete(hba->dev_cmd.complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1731,6 +1677,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ + sdev-&
[PATCH V4 3/4] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 437 +++-- drivers/scsi/ufs/ufshcd.h |2 + 2 files changed, 381 insertions(+), 58 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 3f80396..1a0ceb2 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -69,9 +69,15 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_HOST_RESET_PENDING = (1 << 0), + UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; /* Interrupt configuration options */ @@ -87,6 +93,22 @@ enum { INT_AGGR_CONFIG, }; +#define ufshcd_set_device_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_set_host_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_device_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_host_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_clear_device_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_clear_host_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_HOST_RESET_PENDING) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -840,9 +862,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; goto out; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + goto out; } /* acquire the tag to make sure device cmds don't use it */ @@ -1495,8 +1530,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2212,6 +2245,100 @@ out: } /** + * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_complete_pending_tasks - complete outstanding tasks + * @hba: per adapter instance + * + * Abort in-progress task management commands and wakeup + * waiting threads. + * + * Returns non-zero error value when failed to clear all the commands. + */ +static int ufshcd_complete_pending_tasks(struct ufs_hba *hba) +{ + int err = 0; + unsigned long flags; + + if (!hba->outstanding_tasks) + goto out; + + /* Clear UTMRL only when run-stop is enabled */ + if (ufshcd_utmrl_is_rsr_enabled(hba)) + ufshcd_writel(hba, ~hba->outstanding_tasks, + REG_UTP_TASK_REQ_LIST_CLEAR); + + /* poll for max. 1 sec to clear door bell register by h/w */ + err = ufshcd_wait_for_register(hba, REG_UTP_TASK_REQ_DOOR_BELL, + hba->outstanding_tasks, ~hba->outstanding_tasks, + 1000, 1000); + + spin_lock_irqsave(hba->host->host_lock, flags); + /* complete commands that were cleared out */ + ufshcd_tmc_handler(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); +out: + if (err) +
[PATCH V4 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 70 +++- 1 files changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 1d7e027..3f80396 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2486,6 +2486,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2494,7 +2500,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt; u8 resp; struct ufshcd_lrb *lrbp; @@ -2502,33 +2509,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) + goto out; - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + lrbp = &hba->lrb[tag]; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 2ms to stabilize */ + usleep_range(1000, 2000); + continue; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = resp; /* service response error */ + goto out; + } + } + + if (!poll_cnt) { + err = -EBUSY; goto out; } - spin_unlock_irqrestore(host->host_lock, flags); - lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = resp; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &hba->outstanding_reqs); hba->lrb[tag].cmd = NULL; spin_unlock_ir
[PATCH V4 0/4] scsi: ufs: Improve UFS error handling
The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches depends on: [PATCH V4 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH V4 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization [PATCH V4 1/2] scsi: ufs: Add support for host assisted background operations [PATCH V4 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver Changes from v3: - Rebased. Changes from v2: - [PATCH V3 1/4]: Make the task management command task tag unique across SCSI/NOP/QUERY request tags. - [PATCH V3 3/4]: While handling device/host reset, wait for pending fatal handler to return if running. Changes from v1: - [PATCH V2 1/4]: Fix a race condition because of overloading outstanding_tasks variable to lock the slots. A new variable tm_slots_in_use will track which slots are in use by the driver. - [PATCH V2 2/4]: Commit text update to clarify the hardware race with more details. - [PATCH V2 3/4]: Minor cleanup and rebase - [PATCH V2 4/4]: Fix a bug - sleeping in atomic context Sujit Reddy Thumma (4): scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufshcd.c | 1003 - drivers/scsi/ufs/ufshcd.h | 12 +- drivers/scsi/ufs/ufshci.h | 19 +- 3 files changed, 841 insertions(+), 193 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V4 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver
Add runtime PM helpers to suspend/resume UFS controller at runtime. Enable runtime PM by default for pci and platform drivers as the initialized hardware can suspend if it is not used after bootup. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd-pci.c| 62 ++ drivers/scsi/ufs/ufshcd-pltfrm.c | 48 - drivers/scsi/ufs/ufshcd.c|8 + 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 48be39a..57ea9dd 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -35,6 +35,7 @@ #include "ufshcd.h" #include +#include #ifdef CONFIG_PM /** @@ -44,7 +45,7 @@ * * Returns -ENOSYS */ -static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int ufshcd_pci_suspend(struct device *dev) { /* * TODO: @@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) * * Returns -ENOSYS */ -static int ufshcd_pci_resume(struct pci_dev *pdev) +static int ufshcd_pci_resume(struct device *dev) { /* * TODO: @@ -71,8 +72,45 @@ static int ufshcd_pci_resume(struct pci_dev *pdev) return -ENOSYS; } +#else +#define ufshcd_pci_suspend NULL +#define ufshcd_pci_resume NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pci_runtime_suspend(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + if (!hba) + return 0; + + return ufshcd_runtime_suspend(hba); +} +static int ufshcd_pci_runtime_resume(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + if (!hba) + return 0; + + return ufshcd_runtime_resume(hba); +} +static int ufshcd_pci_runtime_idle(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + if (!hba) + return 0; + + return ufshcd_runtime_idle(hba); +} +#else /* !CONFIG_PM_RUNTIME */ +#define ufshcd_pci_runtime_suspend NULL +#define ufshcd_pci_runtime_resume NULL +#define ufshcd_pci_runtime_idleNULL +#endif /* CONFIG_PM_RUNTIME */ + /** * ufshcd_pci_shutdown - main function to put the controller in reset state * @pdev: pointer to PCI device handle @@ -91,6 +129,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev) { struct ufs_hba *hba = pci_get_drvdata(pdev); + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + disable_irq(pdev->irq); ufshcd_remove(hba); pci_release_regions(pdev); @@ -168,6 +209,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) } pci_set_drvdata(pdev, hba); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); return 0; @@ -182,6 +225,14 @@ out_error: return err; } +static const struct dev_pm_ops ufshcd_pci_pm_ops = { + .suspend= ufshcd_pci_suspend, + .resume = ufshcd_pci_resume, + .runtime_suspend = ufshcd_pci_runtime_suspend, + .runtime_resume = ufshcd_pci_runtime_resume, + .runtime_idle= ufshcd_pci_runtime_idle, +}; + static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = { { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { } /* terminate list */ @@ -195,10 +246,9 @@ static struct pci_driver ufshcd_pci_driver = { .probe = ufshcd_pci_probe, .remove = ufshcd_pci_remove, .shutdown = ufshcd_pci_shutdown, -#ifdef CONFIG_PM - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, -#endif + .driver = { + .pm = &ufshcd_pci_pm_ops + }, }; module_pci_driver(ufshcd_pci_driver); diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index c42db40..cc957fc 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -34,6 +34,7 @@ */ #include +#include #include "ufshcd.h" @@ -87,6 +88,40 @@ static int ufshcd_pltfrm_resume(struct device *dev) #define ufshcd_pltfrm_resume NULL #endif +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pltfrm_runtime_suspend(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + if (!hba) + return 0; + + return ufshcd_runtime_suspend(hba); +} +static int ufshcd_pltfrm_runtime_resume(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + if (!hba) + return 0; + + return ufshcd_runtime_resume(hba); +} +static int ufshcd_pltfrm_runtime_idle(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + if (!hba) + return 0; + + return ufshcd_runtime_idle(hba); +} +#else /* !CONFIG_PM_RUNTIME *
[PATCH V4 1/2] scsi: ufs: Add support for host assisted background operations
Background operations in the UFS device can be disabled by the host to reduce the response latency of transfer requests. Add support for enabling/disabling the background operations during runtime suspend/resume of the device. If the device is in critical need of BKOPS it will raise an URGENT_BKOPS exception which should be handled by the host to make sure the device performs as expected. During bootup, the BKOPS is enabled in the device by default. The disable of BKOPS is supported only when the driver supports runtime suspend/resume operations as the runtime PM framework provides a way to determine the device idleness and hence BKOPS can be managed effectively. During runtime resume the BKOPS is disabled to reduce latency and during runtime suspend the BKOPS is enabled to allow device to carry out idle time BKOPS. In some cases where the BKOPS is disabled during runtime resume and due to continuous data transfers the runtime suspend is not triggered, the BKOPS is enabled when the device raises a level-2 exception (outstanding operations - performance impact). Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 23 +++ drivers/scsi/ufs/ufshcd.c | 343 + drivers/scsi/ufs/ufshcd.h | 10 ++ 3 files changed, 376 insertions(+), 0 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 50d32f1..bce09a6 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -107,6 +107,28 @@ enum { /* Flag idn for Query Requests*/ enum flag_idn { QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_BKOPS_EN = 0x04, +}; + +/* Attribute idn for Query requests */ +enum attr_idn { + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + QUERY_ATTR_IDN_EE_STATUS= 0x0E, +}; + +/* Exception event mask values */ +enum { + MASK_EE_STATUS = 0x, + MASK_EE_URGENT_BKOPS= (1 << 2), +}; + +/* Background operation status */ +enum { + BKOPS_STATUS_NO_OP = 0x0, + BKOPS_STATUS_NON_CRITICAL= 0x1, + BKOPS_STATUS_PERF_IMPACT = 0x2, + BKOPS_STATUS_CRITICAL= 0x3, }; /* UTP QUERY Transaction Specific Fields OpCode */ @@ -155,6 +177,7 @@ enum { MASK_TASK_RESPONSE = 0xFF00, MASK_RSP_UPIU_RESULT= 0x, MASK_QUERY_DATA_SEG_LEN = 0x, + MASK_RSP_EXCEPTION_EVENT= 0x1, }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 7b581f7..4267246 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -261,6 +261,21 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr) } /** + * ufshcd_is_exception_event - Check if the device raised an exception event + * @ucd_rsp_ptr: pointer to response UPIU + * + * The function checks if the device raised an exception event indicated in + * the Device Information field of response UPIU. + * + * Returns true if exception is raised, false otherwise. + */ +static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr) +{ + return be32_to_cpu(ucd_rsp_ptr->header.dword_2) & + MASK_RSP_EXCEPTION_EVENT ? true : false; +} + +/** * ufshcd_config_int_aggr - Configure interrupt aggregation values. * Currently there is no use case where we want to configure * interrupt aggregation dynamically. So to configure interrupt @@ -1107,6 +1122,77 @@ out_unlock: } /** + * ufshcd_query_attr - API function for sending attribute requests + * hba: per-adapter instance + * opcode: attribute opcode + * idn: attribute idn to access + * index: index field + * selector: selector field + * attr_val: the attribute value after the query request completes + * + * Returns 0 for success, non-zero in case of failure +*/ +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) +{ + struct ufs_query_req *request; + struct ufs_query_res *response; + int err; + + BUG_ON(!hba); + + if (!attr_val) { + dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n", + __func__, opcode); + err = -EINVAL; + goto out; + } + + mutex_lock(&hba->dev_cmd.lock); + request = &hba->dev_cmd.query.request; + response = &hba->dev_cmd.query.response; + memset(request, 0, sizeof(struct ufs_query_req)); + memset(response, 0, sizeof(struct ufs_query_res)); + + switch (opcode) { + case UPIU_QUERY_OPCODE_WRITE_ATTR: + request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST; + request->upiu_req.value = *attr_val; +
[PATCH V4 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
As part of device initialization sequence, sending NOP OUT UPIU and waiting for NOP IN UPIU response is mandatory. This confirms that the device UFS Transport (UTP) layer is functional and the host can configure the device with further commands. Add support for sending NOP OUT UPIU to check the device connection path and test whether the UTP layer on the device side is functional during initialization. A tag is acquired from the SCSI tag map space in order to send the device management command. When the tag is acquired by internal command the scsi command is rejected with host busy flag in order to requeue the request. To avoid frequent collisions between internal commands and scsi commands the device management command tag is allocated in the opposite direction w.r.t block layer tag allocation. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Dolev Raviv --- drivers/scsi/ufs/ufs.h| 40 +++- drivers/scsi/ufs/ufshcd.c | 585 + drivers/scsi/ufs/ufshcd.h | 29 ++- 3 files changed, 539 insertions(+), 115 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 139bc06..51b5e3f 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -36,10 +36,13 @@ #ifndef _UFS_H #define _UFS_H +#include +#include + #define MAX_CDB_SIZE 16 #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ - ((byte3 << 24) | (byte2 << 16) |\ + cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ (byte1 << 8) | (byte0)) /* @@ -73,6 +76,7 @@ enum { UPIU_TRANSACTION_TASK_RSP = 0x24, UPIU_TRANSACTION_READY_XFER = 0x31, UPIU_TRANSACTION_QUERY_RSP = 0x36, + UPIU_TRANSACTION_REJECT_UPIU= 0x3F, }; /* UPIU Read/Write flags */ @@ -110,6 +114,12 @@ enum { UPIU_COMMAND_SET_TYPE_QUERY = 0x2, }; +/* UTP Transfer Request Command Offset */ +#define UPIU_COMMAND_TYPE_OFFSET 28 + +/* Offset of the response code in the UPIU header */ +#define UPIU_RSP_CODE_OFFSET 8 + enum { MASK_SCSI_STATUS= 0xFF, MASK_TASK_RESPONSE = 0xFF00, @@ -138,26 +148,32 @@ struct utp_upiu_header { /** * struct utp_upiu_cmd - Command UPIU structure - * @header: UPIU header structure DW-0 to DW-2 * @data_transfer_len: Data Transfer Length DW-3 * @cdb: Command Descriptor Block CDB DW-4 to DW-7 */ struct utp_upiu_cmd { - struct utp_upiu_header header; u32 exp_data_transfer_len; u8 cdb[MAX_CDB_SIZE]; }; /** - * struct utp_upiu_rsp - Response UPIU structure - * @header: UPIU header DW-0 to DW-2 + * struct utp_upiu_req - general upiu request structure + * @header:UPIU header structure DW-0 to DW-2 + * @sc: fields structure for scsi command DW-3 to DW-7 + */ +struct utp_upiu_req { + struct utp_upiu_header header; + struct utp_upiu_cmd sc; +}; + +/** + * struct utp_cmd_rsp - Response UPIU structure * @residual_transfer_count: Residual transfer count DW-3 * @reserved: Reserved double words DW-4 to DW-7 * @sense_data_len: Sense data length DW-8 U16 * @sense_data: Sense data field DW-8 to DW-12 */ -struct utp_upiu_rsp { - struct utp_upiu_header header; +struct utp_cmd_rsp { u32 residual_transfer_count; u32 reserved[4]; u16 sense_data_len; @@ -165,6 +181,16 @@ struct utp_upiu_rsp { }; /** + * struct utp_upiu_rsp - general upiu response structure + * @header: UPIU header structure DW-0 to DW-2 + * @sr: fields structure for scsi command DW-3 to DW-12 + */ +struct utp_upiu_rsp { + struct utp_upiu_header header; + struct utp_cmd_rsp sr; +}; + +/** * struct utp_upiu_task_req - Task request UPIU structure * @header - UPIU header structure DW0 to DW-2 * @input_param1: Input parameter 1 DW-3 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b743bd6..ed20156 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -43,6 +43,11 @@ /* UIC command timeout, unit: ms */ #define UIC_CMD_TIMEOUT500 +/* NOP OUT retries waiting for NOP IN response */ +#define NOP_OUT_RETRIES10 +/* Timeout after 30 msecs if NOP OUT hangs without response */ +#define NOP_OUT_TIMEOUT30 /* msecs */ + enum { UFSHCD_MAX_CHANNEL = 0, UFSHCD_MAX_ID = 1, @@ -71,6 +76,40 @@ enum { INT_AGGR_CONFIG, }; +/* + * ufshcd_wait_for_register - wait for register value to change + * @hba - per-adapter interface + * @reg - mmio register offset + * @mask - mask to apply to read register value + * @val - wait condition + * @interval_us - polling interval in microsecs + * @timeout_ms - timeout in millisecs + * + * Returns -ETIMEDOUT on error, zero on success + */ +static int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, + u32 val, unsigned long interval_us, unsigned long timeout_ms) +{ + int
[PATCH V4 0/2] scsi: ufs: Add support to control UFS device background operations
Add host assisted background operations for UFS device and runtime PM helpers for ufshcd platform and pci glue drivers. The background operations are disabled during runtime resume and enabled when the device is idle and runtime suspended. These patches depends on: [PATCH V4 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH V4 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization Changes from v3: - Addressed some of the comments by Seungwon Jeon on runtime PM. Changes from v2: - Enable auto bkops by default explicitly during bootup so that we may not assume it as enabled after reset. - Enable runtime PM support for contexts that are not part of SCSI, so that the host is not suspended while running other contexts like bkops exception handling. Changes from v1: - Minor cleanup and rebase - Forced enable of auto bkops during initialization to make sure device and driver state are matched. Sujit Reddy Thumma (2): scsi: ufs: Add support for host assisted background operations scsi: ufs: Add runtime PM support for UFS host controller driver drivers/scsi/ufs/ufs.h | 23 +++ drivers/scsi/ufs/ufshcd-pci.c| 62 ++- drivers/scsi/ufs/ufshcd-pltfrm.c | 48 +- drivers/scsi/ufs/ufshcd.c| 351 ++ drivers/scsi/ufs/ufshcd.h| 10 + 5 files changed, 487 insertions(+), 7 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V4 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
From: Dolev Raviv Allow UFS device to complete its initialization and accept SCSI commands by setting fDeviceInit flag. The device may take time for this operation and hence the host should poll until fDeviceInit flag is toggled to zero. This step is mandated by UFS device specification for device initialization completion. Signed-off-by: Dolev Raviv Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 96 +-- drivers/scsi/ufs/ufshcd.c | 238 - drivers/scsi/ufs/ufshcd.h | 20 drivers/scsi/ufs/ufshci.h |2 +- 4 files changed, 345 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 51b5e3f..50d32f1 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -40,6 +40,10 @@ #include #define MAX_CDB_SIZE 16 +#define GENERAL_UPIU_REQUEST_SIZE 32 +#define QUERY_DESC_MAX_SIZE 256 +#define QUERY_OSF_SIZE(GENERAL_UPIU_REQUEST_SIZE - \ + (sizeof(struct utp_upiu_header))) #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ @@ -65,7 +69,7 @@ enum { UPIU_TRANSACTION_COMMAND= 0x01, UPIU_TRANSACTION_DATA_OUT = 0x02, UPIU_TRANSACTION_TASK_REQ = 0x04, - UPIU_TRANSACTION_QUERY_REQ = 0x26, + UPIU_TRANSACTION_QUERY_REQ = 0x16, }; /* UTP UPIU Transaction Codes Target to Initiator */ @@ -94,8 +98,19 @@ enum { UPIU_TASK_ATTR_ACA = 0x03, }; -/* UTP QUERY Transaction Specific Fields OpCode */ +/* UPIU Query request function */ enum { + UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01, + UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81, +}; + +/* Flag idn for Query Requests*/ +enum flag_idn { + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, +}; + +/* UTP QUERY Transaction Specific Fields OpCode */ +enum query_opcode { UPIU_QUERY_OPCODE_NOP = 0x0, UPIU_QUERY_OPCODE_READ_DESC = 0x1, UPIU_QUERY_OPCODE_WRITE_DESC= 0x2, @@ -107,6 +122,21 @@ enum { UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, }; +/* Query response result code */ +enum { + QUERY_RESULT_SUCCESS= 0x00, + QUERY_RESULT_NOT_READABLE = 0xF6, + QUERY_RESULT_NOT_WRITEABLE = 0xF7, + QUERY_RESULT_ALREADY_WRITTEN= 0xF8, + QUERY_RESULT_INVALID_LENGTH = 0xF9, + QUERY_RESULT_INVALID_VALUE = 0xFA, + QUERY_RESULT_INVALID_SELECTOR = 0xFB, + QUERY_RESULT_INVALID_INDEX = 0xFC, + QUERY_RESULT_INVALID_IDN= 0xFD, + QUERY_RESULT_INVALID_OPCODE = 0xFE, + QUERY_RESULT_GENERAL_FAILURE= 0xFF, +}; + /* UTP Transfer Request Command Type (CT) */ enum { UPIU_COMMAND_SET_TYPE_SCSI = 0x0, @@ -121,9 +151,10 @@ enum { #define UPIU_RSP_CODE_OFFSET 8 enum { - MASK_SCSI_STATUS= 0xFF, - MASK_TASK_RESPONSE = 0xFF00, - MASK_RSP_UPIU_RESULT= 0x, + MASK_SCSI_STATUS= 0xFF, + MASK_TASK_RESPONSE = 0xFF00, + MASK_RSP_UPIU_RESULT= 0x, + MASK_QUERY_DATA_SEG_LEN = 0x, }; /* Task management service response */ @@ -157,13 +188,40 @@ struct utp_upiu_cmd { }; /** + * struct utp_upiu_query - upiu request buffer structure for + * query request. + * @opcode: command to perform B-0 + * @idn: a value that indicates the particular type of data B-1 + * @index: Index to further identify data B-2 + * @selector: Index to further identify data B-3 + * @reserved_osf: spec reserved field B-4,5 + * @length: number of descriptor bytes to read/write B-6,7 + * @value: Attribute value to be written DW-5 + * @reserved: spec reserved DW-6,7 + */ +struct utp_upiu_query { + u8 opcode; + u8 idn; + u8 index; + u8 selector; + u16 reserved_osf; + u16 length; + u32 value; + u32 reserved[2]; +}; + +/** * struct utp_upiu_req - general upiu request structure * @header:UPIU header structure DW-0 to DW-2 * @sc: fields structure for scsi command DW-3 to DW-7 + * @qr: fields structure for query request DW-3 to DW-7 */ struct utp_upiu_req { struct utp_upiu_header header; - struct utp_upiu_cmd sc; + union { + struct utp_upiu_cmd sc; + struct utp_upiu_query qr; + }; }; /** @@ -184,10 +242,14 @@ struct utp_cmd_rsp { * struct utp_upiu_rsp - general upiu response structure * @header: UPIU header structure DW-0 to DW-2 * @sr: fields structure for scsi command DW-3 to DW-12 + * @qr: fields structure for query request DW-3 to DW-7 */ struct utp_upiu_rsp { struct utp_upiu_header header; - struct utp_cmd_rsp sr; +
[PATCH V4 0/2] Add suport for internal request (NOP and Query Request)
This patch series replace the previous Query Request and NOP patches: [PATCH 1/8] scsi: ufs: add support for query [PATCH 6/8] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH 7/8] scsi: ufs: Set fDeviceInit flag to initiate device initialization Major difference - Sending the query request via the SCSI vendor specific command can cause a deadlock in case the SCSI command queue is blocked and we would like to send a query request (for example fDeviceInit in case of re-initialization). In addition, usage of a vendor specific SCSI command for UFS can cause future conflicts if this vendor specific command will be allocated for a different usage. The new patch series gets an internal tag for NOP and query requests and do not involve the SCSI layer in UFS specific requests transfers. This design also resolves the possible deadlock mentioned above. Changes from v3: - Addressed some of the comments by Seungwon Jeon. o Changed return value for ufshcd_wait_for_register(). o Function name changes to ufshcd_verify_dev_init(). o Bootup allocation for query structures. o Remove overloading of flag_res while polling in fDeviceInit. Changes from v2: - Rebased on scsi-misc branche (commit 8c0eb596baa5) - Minor addition to structure documentation in ufshcd.h Changes from v1 - Allocate a tag for device management commands dynamically instead of reserving tag[MAX_QUEUE - 1]. - Changed the "internal_cmd" naming to "dev_cmd" to avoid confusion with other type of internal commands other than NOP and QUERY. Dolev Raviv (1): scsi: ufs: Set fDeviceInit flag to initiate device initialization Sujit Reddy Thumma (1): scsi: ufs: Add support for sending NOP OUT UPIU drivers/scsi/ufs/ufs.h| 132 +++- drivers/scsi/ufs/ufshcd.c | 817 +++-- drivers/scsi/ufs/ufshcd.h | 49 +++- drivers/scsi/ufs/ufshci.h |2 +- 4 files changed, 879 insertions(+), 121 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations
I'm not sure that BKOPS with runtime-pm associates. Do you think it's helpful for power management? How about hibernation scheme for runtime-pm? I'm testing and I can introduce soon. Well, I am thinking on following approach when we introduce power management. ufshcd_runtime_suspend() { if (bkops_status >= NON_CRITICAL) { /* 0x1 */ ufshcd_enable_auto_bkops(); hibernate(); /* only the link and the device should be able to cary out bkops */ } else { hibernate(); /* Link and the device for more savings */ } } Let me know if this is okay. I still consider whether BKOPS is proper behavior with runtime-pm or not. The BKOPS is something that host allows the card to carry out when the host knows it is idle and not expecting back to back requests. Runtime PM idle is the only way to know whether the device is idle (unless we want to reinvent the wheel to detect the idleness of host and trigger bkops). There was a discussion on this in MMC mailing list as well, and folks have agreed to move idle time bkops to runtime PM (http://thread.gmane.org/gmane.linux.kernel.mmc/19444/) It looks like different. eMMC cannot execute BKOPS itself unlike UFS. That's the way eMMC's host should trigger the BKOPS manually. I guess it is not much of a difference for UFS as far as we concern only about idle time bkops. In UFS one can still disallow the device to not carry out BKOPS and hence the case where UFS device also cannot execute BKOPS itself and a idle timer is needed to allow BKOPS sporadically so that device doesn't go into URGENT_BKOPS state. How about this scenario? It seems more simple. > > If we concern a response latency of transfer requests, BKOPS can be disabled by default. And then BKOPS can be enabled whenever device requests in some exception. If you have any idea, let me know. Exceptions are raised only when the device is in critical need for bkops. Also the spec. recommends, host should ensure that the device doesn't go into such states. With your suggestion, when we disable bkops, the exception is raised and we enable bkops after which there is no way to disable it again? Yes, it's difficult to find proper time. Maybe, BKOPS can be disabled when request comes up. In cases where there are back-to-back heavy data write requests then it is not proper to enable/disable BKOPS as soon as the new request comes up. It may happen that the card will then move from PERFORMANCE_IMPACT state to CRITICAL and ultimately start failing the requests. The point is that we should never get into state where we need URGENT_BKOPS. -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH V3 3/4] scsi: ufs: Fix device and host reset methods
On 7/19/2013 7:27 PM, Seungwon Jeon wrote: > On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >> As of now SCSI initiated error handling is broken because, >> the reset APIs don't try to bring back the device initialized and >> ready for further transfers. >> >> In case of timeouts, the scsi error handler takes care of handling aborts >> and resets. Improve the error handling in such scenario by resetting the >> device and host and re-initializing them in proper manner. >> >> Signed-off-by: Sujit Reddy Thumma >> --- >> drivers/scsi/ufs/ufshcd.c | 467 >> +++-- >> drivers/scsi/ufs/ufshcd.h |2 + >> 2 files changed, 411 insertions(+), 58 deletions(-) >> >> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >> index 51ce096..b4c9910 100644 >> --- a/drivers/scsi/ufs/ufshcd.c >> +++ b/drivers/scsi/ufs/ufshcd.c >> @@ -69,9 +69,15 @@ enum { >> >> /* UFSHCD states */ >> enum { >> -UFSHCD_STATE_OPERATIONAL, >> UFSHCD_STATE_RESET, >> UFSHCD_STATE_ERROR, >> +UFSHCD_STATE_OPERATIONAL, >> +}; >> + >> +/* UFSHCD error handling flags */ >> +enum { >> +UFSHCD_EH_HOST_RESET_PENDING = (1 << 0), >> +UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), >> }; >> >> /* Interrupt configuration options */ >> @@ -87,6 +93,22 @@ enum { >> INT_AGGR_CONFIG, >> }; >> >> +#define ufshcd_set_device_reset_pending(h) \ >> +(h->eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING) >> +#define ufshcd_set_host_reset_pending(h) \ >> +(h->eh_flags |= UFSHCD_EH_HOST_RESET_PENDING) >> +#define ufshcd_device_reset_pending(h) \ >> +(h->eh_flags & UFSHCD_EH_DEVICE_RESET_PENDING) >> +#define ufshcd_host_reset_pending(h) \ >> +(h->eh_flags & UFSHCD_EH_HOST_RESET_PENDING) >> +#define ufshcd_clear_device_reset_pending(h) \ >> +(h->eh_flags &= ~UFSHCD_EH_DEVICE_RESET_PENDING) >> +#define ufshcd_clear_host_reset_pending(h) \ >> +(h->eh_flags &= ~UFSHCD_EH_HOST_RESET_PENDING) >> + >> +static void ufshcd_tmc_handler(struct ufs_hba *hba); >> +static void ufshcd_async_scan(void *data, async_cookie_t cookie); >> + >> /* >>* ufshcd_wait_for_register - wait for register value to change >>* @hba - per-adapter interface >> @@ -851,9 +873,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, >> struct scsi_cmnd *cmd) >> >> tag = cmd->request->tag; >> >> -if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { >> +switch (hba->ufshcd_state) { > Lock is no needed for ufshcd_state? > >> +case UFSHCD_STATE_OPERATIONAL: >> +break; >> +case UFSHCD_STATE_RESET: >> err = SCSI_MLQUEUE_HOST_BUSY; >> goto out; >> +case UFSHCD_STATE_ERROR: >> +set_host_byte(cmd, DID_ERROR); >> +cmd->scsi_done(cmd); >> +goto out; >> +default: >> +dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", >> +__func__, hba->ufshcd_state); >> +set_host_byte(cmd, DID_BAD_TARGET); >> +cmd->scsi_done(cmd); >> +goto out; >> } >> >> /* acquire the tag to make sure device cmds don't use it */ >> @@ -1573,8 +1608,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba >> *hba) >> if (hba->ufshcd_state == UFSHCD_STATE_RESET) >> scsi_unblock_requests(hba->host); >> >> -hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; >> - >> out: >> return err; >> } >> @@ -2273,6 +2306,106 @@ out: >> } >> >> /** >> + * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled >> + * @hba: per-adapter instance >> + */ >> +static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba) >> +{ >> +return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP) & 0x1; >> +} >> + >> +/** >> + * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled >> + * @hba: per-adapter instance >> + */ >> +static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba) >> +{ >> +return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP) & 0x1; >> +} >> + >> +/** >> + * ufshcd_complete_pending_tasks - complete outstanding tasks >> + * @hba: per adapter instance >> +
Re: [PATCH V3 1/4] scsi: ufs: Fix broken task management command implementation
On 7/19/2013 7:26 PM, Seungwon Jeon wrote: > On Tue, July 09, 2013 Sujit Reddy Thumma wrote: >> Currently, sending Task Management (TM) command to the card might >> be broken in some scenarios as listed below: >> >> Problem: If there are more than 8 TM commands the implementation >> returns error to the caller. >> Fix: Wait for one of the slots to be emptied and send the command. >> >> Problem: Sometimes it is necessary for the caller to know the TM service >> response code to determine the task status. >> Fix: Propogate the service response to the caller. >> >> Problem: If the TM command times out no proper error recovery is >> implemented. >> Fix: Clear the command in the controller door-bell register, so that >> further commands for the same slot don't fail. >> >> Problem: While preparing the TM command descriptor, the task tag used >> should be unique across SCSI/NOP/QUERY/TM commands and not the >> task tag of the command which the TM command is trying to manage. >> Fix: Use a unique task tag instead of task tag of SCSI command. >> >> Problem: Since the TM command involves H/W communication, abruptly ending >> the request on kill interrupt signal might cause h/w malfunction. >> Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE >> set. >> >> Signed-off-by: Sujit Reddy Thumma >> --- >> drivers/scsi/ufs/ufshcd.c | 177 >> ++--- >> drivers/scsi/ufs/ufshcd.h |8 ++- >> 2 files changed, 126 insertions(+), 59 deletions(-) >> >> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >> index af7d01d..a176421 100644 >> --- a/drivers/scsi/ufs/ufshcd.c >> +++ b/drivers/scsi/ufs/ufshcd.c >> @@ -53,6 +53,9 @@ >> /* Query request timeout */ >> #define QUERY_REQ_TIMEOUT 30 /* msec */ >> >> +/* Task management command timeout */ >> +#define TM_CMD_TIMEOUT 100 /* msecs */ >> + >> /* Expose the flag value from utp_upiu_query.value */ >> #define MASK_QUERY_UPIU_FLAG_LOC 0xFF >> >> @@ -190,13 +193,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc >> *task_req_descp) >> /** >>* ufshcd_get_tm_free_slot - get a free slot for task management request >>* @hba: per adapter instance >> + * @free_slot: pointer to variable with available slot value >>* >> - * Returns maximum number of task management request slots in case of >> - * task management queue full or returns the free slot number >> + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. >> + * Returns 0 if free slot is not available, else return 1 with tag value >> + * in @free_slot. >>*/ >> -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) >> +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) >> +{ >> +int tag; >> +bool ret = false; >> + >> +if (!free_slot) >> +goto out; >> + >> +do { >> +tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); >> +if (tag >= hba->nutmrs) >> +goto out; >> +} while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); >> + >> +*free_slot = tag; >> +ret = true; >> +out: >> +return ret; >> +} >> + >> +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) >> { >> -return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); >> +clear_bit_unlock(slot, &hba->tm_slots_in_use); >> } >> >> /** >> @@ -1778,10 +1803,11 @@ static void ufshcd_slave_destroy(struct scsi_device >> *sdev) >>* ufshcd_task_req_compl - handle task management request completion >>* @hba: per adapter instance >>* @index: index of the completed request >> + * @resp: task management service response >>* >> - * Returns SUCCESS/FAILED >> + * Returns non-zero value on error, zero on success >>*/ >> -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) >> +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) >> { >> struct utp_task_req_desc *task_req_descp; >> struct utp_upiu_task_rsp *task_rsp_upiup; >> @@ -1802,19 +1828,15 @@ static int ufshcd_task_req_compl(struct ufs_hba >> *hba, u32 index) >> task_req_descp[index].task_rsp_upi
Re: [PATCH V3 4/4] scsi: ufs: Improve UFS fatal error handling
On 7/19/2013 7:28 PM, Seungwon Jeon wrote: > On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >> Error handling in UFS driver is broken and resets the host controller >> for fatal errors without re-initialization. Correct the fatal error >> handling sequence according to UFS Host Controller Interface (HCI) >> v1.1 specification. >> >> o Upon determining fatal error condition the host controller may hang >>forever until a reset is applied, so just retrying the command doesn't >>work without a reset. So, the reset is applied in the driver context >>in a separate work and SCSI mid-layer isn't informed until reset is >>applied. >> >> o Processed requests which are completed without error are reported to >>SCSI layer as successful and any pending commands that are not started >>yet or are not cause of the error are re-queued into scsi midlayer queue. >>For the command that caused error, host controller or device is reset >>and DID_ERROR is returned for command retry after applying reset. >> >> o SCSI is informed about the expected Unit-Attentioni exception from the > Attention'i', typo. Okay. > >>device for the immediate command after a reset so that the SCSI layer >>take necessary steps to establish communication with the device. >> >> Signed-off-by: Sujit Reddy Thumma >> --- >> drivers/scsi/ufs/ufshcd.c | 349 >> +++- >> drivers/scsi/ufs/ufshcd.h |2 + >> drivers/scsi/ufs/ufshci.h | 19 ++- >> 3 files changed, 295 insertions(+), 75 deletions(-) >> >> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >> index b4c9910..2a3874f 100644 >> --- a/drivers/scsi/ufs/ufshcd.c >> +++ b/drivers/scsi/ufs/ufshcd.c >> @@ -80,6 +80,14 @@ enum { >> UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), >> }; >> >> +/* UFSHCD UIC layer error flags */ >> +enum { >> +UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ >> +UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ >> +UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ >> +UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ >> +}; >> + >> /* Interrupt configuration options */ >> enum { >> UFSHCD_INT_DISABLE, >> @@ -108,6 +116,7 @@ enum { >> >> static void ufshcd_tmc_handler(struct ufs_hba *hba); >> static void ufshcd_async_scan(void *data, async_cookie_t cookie); >> +static int ufshcd_reset_and_restore(struct ufs_hba *hba); >> >> /* >>* ufshcd_wait_for_register - wait for register value to change >> @@ -1605,9 +1614,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba >> *hba) >> goto out; >> } >> >> -if (hba->ufshcd_state == UFSHCD_STATE_RESET) >> -scsi_unblock_requests(hba->host); >> - >> out: >> return err; >> } >> @@ -1733,66 +1739,6 @@ static int ufshcd_validate_dev_connection(struct >> ufs_hba *hba) >> } >> >> /** >> - * ufshcd_do_reset - reset the host controller >> - * @hba: per adapter instance >> - * >> - * Returns SUCCESS/FAILED >> - */ >> -static int ufshcd_do_reset(struct ufs_hba *hba) >> -{ >> -struct ufshcd_lrb *lrbp; >> -unsigned long flags; >> -int tag; >> - >> -/* block commands from midlayer */ >> -scsi_block_requests(hba->host); >> - >> -spin_lock_irqsave(hba->host->host_lock, flags); >> -hba->ufshcd_state = UFSHCD_STATE_RESET; >> - >> -/* send controller to reset state */ >> -ufshcd_hba_stop(hba); >> -spin_unlock_irqrestore(hba->host->host_lock, flags); >> - >> -/* abort outstanding commands */ >> -for (tag = 0; tag < hba->nutrs; tag++) { >> -if (test_bit(tag, &hba->outstanding_reqs)) { >> -lrbp = &hba->lrb[tag]; >> -if (lrbp->cmd) { >> -scsi_dma_unmap(lrbp->cmd); >> -lrbp->cmd->result = DID_RESET << 16; >> -lrbp->cmd->scsi_done(lrbp->cmd); >> -lrbp->cmd = NULL; >> -clear_bit_unlock(tag, &hba->lrb_in_use); >> -} >> -} >> -} >> - >> -/* complete device managem
Re: [PATCH V3 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
On 7/19/2013 7:26 PM, Seungwon Jeon wrote: > On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >> There is a possible race condition in the hardware when the abort >> command is issued to terminate the ongoing SCSI command as described >> below: >> >> - A bit in the door-bell register is set in the controller for a >>new SCSI command. >> - In some rare situations, before controller get a chance to issue >>the command to the device, the software issued an abort command. > It's interesting. > I wonder when we can meet this situation. > Is it possible if SCSI mid layer should send abort command as soon as the > transfer command is issued? > AFAIK abort command is followed if one command has timed out. > That means command have been already issued and no response? > If you had some problem, could you share? You are right. This is a very rare case and probably don't happen at all until and unless the SCSI error handling is changed. We found it just by static analysis. Probably, at some point I may push a patch that tries to reduce the read latencies while aborting a large write transfer when I come across a UFS device that has command per LU as 1 :-) I guess this is good to have change. The path chosen is according to SCSI SAM-5 architecture specification, so I don't expect any issues here. > >> - If the device recieves abort command first then it returns success > receives > >>because the command itself is not present. >> - Now if the controller commits the command to device it will be >>processed. >> - Software thinks that command is aborted and proceed while still >>the device is processing it. >> - The software, controller and device may go out of sync because of >>this race condition. >> >> To avoid this, query task presence in the device before sending abort >> task command so that after the abort operation, the command is guaranteed >> to be non-existent in both controller and the device. >> >> Signed-off-by: Sujit Reddy Thumma >> --- >> drivers/scsi/ufs/ufshcd.c | 70 >> +++- >> 1 files changed, 55 insertions(+), 15 deletions(-) >> >> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c >> index a176421..51ce096 100644 >> --- a/drivers/scsi/ufs/ufshcd.c >> +++ b/drivers/scsi/ufs/ufshcd.c >> @@ -2550,6 +2550,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) >>* ufshcd_abort - abort a specific command >>* @cmd: SCSI command pointer >>* >> + * Abort the pending command in device by sending UFS_ABORT_TASK task >> management >> + * command, and in host controller by clearing the door-bell register. >> There can >> + * be race between controller sending the command to the device while abort >> is >> + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the >> command is >> + * really issued and then try to abort it. >> + * >>* Returns SUCCESS/FAILED >>*/ >> static int ufshcd_abort(struct scsi_cmnd *cmd) >> @@ -2558,7 +2564,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) >> struct ufs_hba *hba; >> unsigned long flags; >> unsigned int tag; >> -int err; >> +int err = 0; >> +int poll_cnt; >> u8 resp; >> struct ufshcd_lrb *lrbp; >> >> @@ -2566,33 +2573,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) >> hba = shost_priv(host); >> tag = cmd->request->tag; >> >> -spin_lock_irqsave(host->host_lock, flags); >> +/* If command is already aborted/completed, return SUCCESS */ >> +if (!(test_bit(tag, &hba->outstanding_reqs))) >> +goto out; >> >> -/* check if command is still pending */ >> -if (!(test_bit(tag, &hba->outstanding_reqs))) { >> -err = FAILED; >> -spin_unlock_irqrestore(host->host_lock, flags); >> +lrbp = &hba->lrb[tag]; >> +for (poll_cnt = 100; poll_cnt; poll_cnt--) { >> +err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, >> +UFS_QUERY_TASK, &resp); >> +if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { >> +/* cmd pending in the device */ >> +break; >> +} else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { >> +u32 reg; >> + >> +/* >> + * cmd not pending in the device, check if
Re: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
On 7/19/2013 7:17 PM, Seungwon Jeon wrote: On Thu, July 18, 2013, Sujit Reddy Thumma wrote: + * ufshcd_wait_for_register - wait for register value to change + * @hba - per-adapter interface + * @reg - mmio register offset + * @mask - mask to apply to read register value + * @val - wait condition + * @interval_us - polling interval in microsecs + * @timeout_ms - timeout in millisecs + * + * Returns final register value after iteration + */ +static u32 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, + u32 val, unsigned long interval_us, unsigned long timeout_ms) I feel like this function's role is to wait for clearing register (specially, doorbells). If you really don't intend to apply other all register, I think it would better to change the function name. ex> ufshcd_wait_for_clear_doorbell or ufshcd_wait_for_clear_reg? Although, this API is introduced in this patch and used only for clearing the doorbell, I have intentionally made it generic to avoid duplication of wait condition code in future (not just clearing but also for setting a bit). For example, when we write to HCE.ENABLE we wait for it turn to 1. And if you like it, it could be more simple like below static bool ufshcd_wait_for_clear_reg(struct ufs_hba *hba, u32 reg, u32 mask, unsigned long interval_us, unsigned int timeout_ms) { unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); Jiffies on 100Hz systems have minimum granularity of 10ms. So we really wait for more 10ms even though the timeout_ms < 10ms. Yes. Real timeout depends on system. But normally actual wait will be maintained until 'ufshcd_readl' is done. Timeout is valid for failure case. If we don't need to apply a strict timeout value, it's not bad. Hmm.. make sense. Will take care in the next patchset. /* wakeup within 50us of expiry */ const unsigned int expiry = 50; while (ufshcd_readl(hba, reg) & mask) { usleep_range(interval_us, interval_us + expiry); if (time_after(jiffies, timeout)) { if (ufshcd_readl(hba, reg) & mask) return false; I really want the caller to decide on what to do after the timeout. This helps in error handling cases where we try to clear off the entire door-bell register but only a few of the bits are cleared after timeout. I don't know what we can do with the report of the partial success on clearing bit. It's just failure case. Isn't enough with false/true? The point is, if a bit can't be cleared it can be regarded as a permanent failure (only for that request), otherwise, it can be retried with the same tag value. else return true; } } return true; } +{ + u32 tmp; + ktime_t start; + unsigned long diff; + + tmp = ufshcd_readl(hba, reg); + + if ((val & mask) != val) { + dev_err(hba->dev, "%s: Invalid wait condition 0x%x\n", + __func__, val); + goto out; + } + + start = ktime_get(); + while ((tmp & mask) != val) { + /* wakeup within 50us of expiry */ + usleep_range(interval_us, interval_us + 50); + tmp = ufshcd_readl(hba, reg); + diff = ktime_to_ms(ktime_sub(ktime_get(), start)); + if (diff > timeout_ms) { + tmp = ufshcd_readl(hba, reg); + break; + } + } +out: + return tmp; +} + .. +static int +ufshcd_clear_cmd(struct ufs_hba *hba, int tag) +{ + int err = 0; + unsigned long flags; + u32 reg; + u32 mask = 1 << tag; + + /* clear outstanding transaction before retry */ + spin_lock_irqsave(hba->host->host_lock, flags); + ufshcd_utrl_clear(hba, tag); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + /* +* wait for for h/w to clear corresponding bit in door-bell. +* max. wait is 1 sec. +*/ + reg = ufshcd_wait_for_register(hba, + REG_UTP_TRANSFER_REQ_DOOR_BELL, + mask, 0, 1000, 1000); 4th argument should be (~mask) instead of '0', right? True, but not really for this implementation. It breaks the check in in wait_for_register - if ((val & mask) != val) dev_err(...); Hmm, it seems complicated to use. Ok. Is right the following about val as 4th argument? - clear: val should be '0' regardless corresponding bit. - set: val should be same with mask. If the related comment is added, it will be helpful. Thinking again it looks l
Re: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
+ * ufshcd_wait_for_register - wait for register value to change + * @hba - per-adapter interface + * @reg - mmio register offset + * @mask - mask to apply to read register value + * @val - wait condition + * @interval_us - polling interval in microsecs + * @timeout_ms - timeout in millisecs + * + * Returns final register value after iteration + */ +static u32 ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, + u32 val, unsigned long interval_us, unsigned long timeout_ms) I feel like this function's role is to wait for clearing register (specially, doorbells). If you really don't intend to apply other all register, I think it would better to change the function name. ex> ufshcd_wait_for_clear_doorbell or ufshcd_wait_for_clear_reg? Although, this API is introduced in this patch and used only for clearing the doorbell, I have intentionally made it generic to avoid duplication of wait condition code in future (not just clearing but also for setting a bit). For example, when we write to HCE.ENABLE we wait for it turn to 1. And if you like it, it could be more simple like below static bool ufshcd_wait_for_clear_reg(struct ufs_hba *hba, u32 reg, u32 mask, unsigned long interval_us, unsigned int timeout_ms) { unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); Jiffies on 100Hz systems have minimum granularity of 10ms. So we really wait for more 10ms even though the timeout_ms < 10ms. Yes. Real timeout depends on system. But normally actual wait will be maintained until 'ufshcd_readl' is done. Timeout is valid for failure case. If we don't need to apply a strict timeout value, it's not bad. Hmm.. make sense. Will take care in the next patchset. /* wakeup within 50us of expiry */ const unsigned int expiry = 50; while (ufshcd_readl(hba, reg) & mask) { usleep_range(interval_us, interval_us + expiry); if (time_after(jiffies, timeout)) { if (ufshcd_readl(hba, reg) & mask) return false; I really want the caller to decide on what to do after the timeout. This helps in error handling cases where we try to clear off the entire door-bell register but only a few of the bits are cleared after timeout. I don't know what we can do with the report of the partial success on clearing bit. It's just failure case. Isn't enough with false/true? The point is, if a bit can't be cleared it can be regarded as a permanent failure (only for that request), otherwise, it can be retried with the same tag value. else return true; } } return true; } +{ + u32 tmp; + ktime_t start; + unsigned long diff; + + tmp = ufshcd_readl(hba, reg); + + if ((val & mask) != val) { + dev_err(hba->dev, "%s: Invalid wait condition 0x%x\n", + __func__, val); + goto out; + } + + start = ktime_get(); + while ((tmp & mask) != val) { + /* wakeup within 50us of expiry */ + usleep_range(interval_us, interval_us + 50); + tmp = ufshcd_readl(hba, reg); + diff = ktime_to_ms(ktime_sub(ktime_get(), start)); + if (diff > timeout_ms) { + tmp = ufshcd_readl(hba, reg); + break; + } + } +out: + return tmp; +} + .. +static int +ufshcd_clear_cmd(struct ufs_hba *hba, int tag) +{ + int err = 0; + unsigned long flags; + u32 reg; + u32 mask = 1 << tag; + + /* clear outstanding transaction before retry */ + spin_lock_irqsave(hba->host->host_lock, flags); + ufshcd_utrl_clear(hba, tag); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + /* +* wait for for h/w to clear corresponding bit in door-bell. +* max. wait is 1 sec. +*/ + reg = ufshcd_wait_for_register(hba, + REG_UTP_TRANSFER_REQ_DOOR_BELL, + mask, 0, 1000, 1000); 4th argument should be (~mask) instead of '0', right? True, but not really for this implementation. It breaks the check in in wait_for_register - if ((val & mask) != val) dev_err(...); Hmm, it seems complicated to use. Ok. Is right the following about val as 4th argument? - clear: val should be '0' regardless corresponding bit. - set: val should be same with mask. If the related comment is added, it will be helpful. Thinking again it looks like it is complicated. How about changing the check to - val = val & mask; /* ignore the bits we don't intend to wait on */ while (ufshcd_readl() & mask != val) { sleep } Usage will be ~mask for clearing the bits, mask for setting the bits in
Re: [PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations
> >I'm not sure that BKOPS with runtime-pm associates. > >Do you think it's helpful for power management? > >How about hibernation scheme for runtime-pm? > >I'm testing and I can introduce soon. > >Well, I am thinking on following approach when we introduce >power management. > >ufshcd_runtime_suspend() { >if (bkops_status >= NON_CRITICAL) { /* 0x1 */ >ufshcd_enable_auto_bkops(); >hibernate(); /* only the link and the device >should be able to cary out bkops */ >} else { >hibernate(); /* Link and the device for more savings */ >} >} > >Let me know if this is okay. I still consider whether BKOPS is proper behavior with runtime-pm or not. The BKOPS is something that host allows the card to carry out when the host knows it is idle and not expecting back to back requests. Runtime PM idle is the only way to know whether the device is idle (unless we want to reinvent the wheel to detect the idleness of host and trigger bkops). There was a discussion on this in MMC mailing list as well, and folks have agreed to move idle time bkops to runtime PM (http://thread.gmane.org/gmane.linux.kernel.mmc/19444/) How about this scenario? It seems more simple. If we concern a response latency of transfer requests, BKOPS can be disabled by default. And then BKOPS can be enabled whenever device requests in some exception. If you have any idea, let me know. Exceptions are raised only when the device is in critical need for bkops. Also the spec. recommends, host should ensure that the device doesn't go into such states. With your suggestion, when we disable bkops, the exception is raised and we enable bkops after which there is no way to disable it again? -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH V3 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver
On 7/10/2013 7:01 PM, Seungwon Jeon wrote: On Tue, July 09, 2013, Sujit Reddy Thumma wrote: Add runtime PM helpers to suspend/resume UFS controller at runtime. Enable runtime PM by default for pci and platform drivers as the initialized hardware can suspend if it is not used after bootup. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd-pci.c| 65 ++--- drivers/scsi/ufs/ufshcd-pltfrm.c | 51 +- drivers/scsi/ufs/ufshcd.c|8 + 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 48be39a..7bd8faa 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -35,6 +35,7 @@ #include "ufshcd.h" #include +#include #ifdef CONFIG_PM /** @@ -44,7 +45,7 @@ * * Returns -ENOSYS */ -static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int ufshcd_pci_suspend(struct device *dev) { /* * TODO: @@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) * * Returns -ENOSYS */ -static int ufshcd_pci_resume(struct pci_dev *pdev) +static int ufshcd_pci_resume(struct device *dev) { /* * TODO: @@ -71,8 +72,48 @@ static int ufshcd_pci_resume(struct pci_dev *pdev) return -ENOSYS; } +#else +#define ufshcd_pci_suspend NULL +#define ufshcd_pci_resume NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pci_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); Can be replaced: struct ufs_hba *hba = dev_get_drvdata(dev); Yes. + + if (!hba) + return 0; + + return ufshcd_runtime_suspend(hba); +} +static int ufshcd_pci_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); Same as above. Okay. + + if (!hba) + return 0; + + return ufshcd_runtime_resume(hba); +} +static int ufshcd_pci_runtime_idle(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); Same as above. Okay. + + if (!hba) + return 0; + + return ufshcd_runtime_idle(hba); +} +#else /* !CONFIG_PM_RUNTIME */ +#define ufshcd_pci_runtime_suspend NULL +#define ufshcd_pci_runtime_resume NULL +#define ufshcd_pci_runtime_idleNULL +#endif /* CONFIG_PM_RUNTIME */ + /** * ufshcd_pci_shutdown - main function to put the controller in reset state * @pdev: pointer to PCI device handle @@ -91,6 +132,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev) { struct ufs_hba *hba = pci_get_drvdata(pdev); + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + disable_irq(pdev->irq); ufshcd_remove(hba); pci_release_regions(pdev); @@ -168,6 +212,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) } pci_set_drvdata(pdev, hba); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); return 0; @@ -182,6 +228,14 @@ out_error: return err; } +static const struct dev_pm_ops ufshcd_pci_pm_ops = { + .suspend= ufshcd_pci_suspend, + .resume = ufshcd_pci_resume, + .runtime_suspend = ufshcd_pci_runtime_suspend, + .runtime_resume = ufshcd_pci_runtime_resume, + .runtime_idle= ufshcd_pci_runtime_idle, +}; + static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = { { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { } /* terminate list */ @@ -195,10 +249,9 @@ static struct pci_driver ufshcd_pci_driver = { .probe = ufshcd_pci_probe, .remove = ufshcd_pci_remove, .shutdown = ufshcd_pci_shutdown, -#ifdef CONFIG_PM - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, -#endif + .driver = { + .pm = &ufshcd_pci_pm_ops + }, }; module_pci_driver(ufshcd_pci_driver); diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index c42db40..b1f2605 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -34,6 +34,7 @@ */ #include +#include #include "ufshcd.h" @@ -87,6 +88,43 @@ static int ufshcd_pltfrm_resume(struct device *dev) #define ufshcd_pltfrm_resume NULL #endif +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pltfrm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ufs_hba *hba = platform_get_drvdata(pdev); Same as above.
Re: [PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations
On 7/10/2013 7:01 PM, Seungwon Jeon wrote: I'm not sure that BKOPS with runtime-pm associates. Do you think it's helpful for power management? How about hibernation scheme for runtime-pm? I'm testing and I can introduce soon. Well, I am thinking on following approach when we introduce power management. ufshcd_runtime_suspend() { if (bkops_status >= NON_CRITICAL) { /* 0x1 */ ufshcd_enable_auto_bkops(); hibernate(); /* only the link and the device should be able to cary out bkops */ } else { hibernate(); /* Link and the device for more savings */ } } Let me know if this is okay. On Tue, July 09, 2013, Sujit Reddy Thumma wrote: Background operations in the UFS device can be disabled by the host to reduce the response latency of transfer requests. Add support for enabling/disabling the background operations during runtime suspend/resume of the device. If the device is in critical need of BKOPS it will raise an URGENT_BKOPS exception which should be handled by the host to make sure the device performs as expected. During bootup, the BKOPS is enabled in the device by default. The disable of BKOPS is supported only when the driver supports runtime suspend/resume operations as the runtime PM framework provides a way to determine the device idleness and hence BKOPS can be managed effectively. During runtime resume the BKOPS is disabled to reduce latency and during runtime suspend the BKOPS is enabled to allow device to carry out idle time BKOPS. In some cases where the BKOPS is disabled during runtime resume and due to continuous data transfers the runtime suspend is not triggered, the BKOPS is enabled when the device raises a level-2 exception (outstanding operations - performance impact). Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 25 - drivers/scsi/ufs/ufshcd.c | 338 + drivers/scsi/ufs/ufshcd.h | 10 ++ 3 files changed, 372 insertions(+), 1 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index db5bde4..549a652 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -107,7 +107,29 @@ enum { /* Flag idn for Query Requests*/ enum flag_idn { - QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_BKOPS_EN = 0x04, +}; + +/* Attribute idn for Query requests */ +enum attr_idn { + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + QUERY_ATTR_IDN_EE_STATUS= 0x0E, +}; + +/* Exception event mask values */ +enum { + MASK_EE_STATUS = 0x, + MASK_EE_URGENT_BKOPS= (1 << 2), +}; + +/* Background operation status */ +enum { + BKOPS_STATUS_NO_OP = 0x0, + BKOPS_STATUS_NON_CRITICAL= 0x1, + BKOPS_STATUS_PERF_IMPACT = 0x2, + BKOPS_STATUS_CRITICAL= 0x3, }; /* UTP QUERY Transaction Specific Fields OpCode */ @@ -156,6 +178,7 @@ enum { MASK_TASK_RESPONSE = 0xFF00, MASK_RSP_UPIU_RESULT= 0x, MASK_QUERY_DATA_SEG_LEN = 0x, + MASK_RSP_EXCEPTION_EVENT = 0x1, }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 96ccb28..a25de66 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -268,6 +268,21 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr) } /** + * ufshcd_is_exception_event - Check if the device raised an exception event + * @ucd_rsp_ptr: pointer to response UPIU + * + * The function checks if the device raised an exception event indicated in + * the Device Information field of response UPIU. + * + * Returns true if exception is raised, false otherwise. + */ +static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr) +{ + return be32_to_cpu(ucd_rsp_ptr->header.dword_2) & + MASK_RSP_EXCEPTION_EVENT ? true : false; +} + +/** * ufshcd_config_int_aggr - Configure interrupt aggregation values. *Currently there is no use case where we want to configure *interrupt aggregation dynamically. So to configure interrupt @@ -1174,6 +1189,86 @@ out_no_mem: } /** + * ufshcd_query_attr - Helper function for composing attribute requests + * hba: per-adapter instance + * opcode: attribute opcode + * idn: attribute idn to access + * index: index field + * selector: selector field + * attr_val: the attribute value after the query request completes + * + * Returns 0 for success, non-zero in case of failure +*/ +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) +{ + struct ufs_query_req *query; + struct ufs_query_res *resp
Re: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
On 7/10/2013 6:58 PM, Seungwon Jeon wrote: > On Tue, July 09, 2013, Sujit Reddy Thumma wrote: >> As part of device initialization sequence, sending NOP OUT UPIU and >> waiting for NOP IN UPIU response is mandatory. This confirms that the >> device UFS Transport (UTP) layer is functional and the host can configure >> the device with further commands. Add support for sending NOP OUT UPIU to >> check the device connection path and test whether the UTP layer on the >> device side is functional during initialization. >> >> A tag is acquired from the SCSI tag map space in order to send the device >> management command. When the tag is acquired by internal command the scsi >> command is rejected with host busy flag in order to requeue the request. >> To avoid frequent collisions between internal commands and scsi commands >> the device management command tag is allocated in the opposite direction >> w.r.t block layer tag allocation. >> >> Signed-off-by: Sujit Reddy Thumma >> Signed-off-by: Dolev Raviv >> --- >> drivers/scsi/ufs/ufs.h| 43 +++- >> drivers/scsi/ufs/ufshcd.c | 596 >> + >> drivers/scsi/ufs/ufshcd.h | 29 ++- >> 3 files changed, 552 insertions(+), 116 deletions(-) >> >> diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h >> index 139bc06..14c0a4e 100644 >> --- a/drivers/scsi/ufs/ufs.h >> +++ b/drivers/scsi/ufs/ufs.h >> @@ -36,10 +36,16 @@ >> #ifndef _UFS_H >> #define _UFS_H >> >> +#include >> +#include >> + >> #define MAX_CDB_SIZE 16 >> +#define GENERAL_UPIU_REQUEST_SIZE 32 >> +#define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \ >> +(GENERAL_UPIU_REQUEST_SIZE)) >> >> #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ >> -((byte3 << 24) | (byte2 << 16) |\ >> +cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ >> (byte1 << 8) | (byte0)) >> >> /* >> @@ -73,6 +79,7 @@ enum { >> UPIU_TRANSACTION_TASK_RSP = 0x24, >> UPIU_TRANSACTION_READY_XFER = 0x31, >> UPIU_TRANSACTION_QUERY_RSP = 0x36, >> +UPIU_TRANSACTION_REJECT_UPIU= 0x3F, >> }; >> >> /* UPIU Read/Write flags */ >> @@ -110,6 +117,12 @@ enum { >> UPIU_COMMAND_SET_TYPE_QUERY = 0x2, >> }; >> >> +/* UTP Transfer Request Command Offset */ >> +#define UPIU_COMMAND_TYPE_OFFSET28 >> + >> +/* Offset of the response code in the UPIU header */ >> +#define UPIU_RSP_CODE_OFFSET8 >> + >> enum { >> MASK_SCSI_STATUS= 0xFF, >> MASK_TASK_RESPONSE = 0xFF00, >> @@ -138,26 +151,32 @@ struct utp_upiu_header { >> >> /** >>* struct utp_upiu_cmd - Command UPIU structure >> - * @header: UPIU header structure DW-0 to DW-2 >>* @data_transfer_len: Data Transfer Length DW-3 >>* @cdb: Command Descriptor Block CDB DW-4 to DW-7 >>*/ >> struct utp_upiu_cmd { >> -struct utp_upiu_header header; >> u32 exp_data_transfer_len; >> u8 cdb[MAX_CDB_SIZE]; >> }; >> >> /** >> - * struct utp_upiu_rsp - Response UPIU structure >> - * @header: UPIU header DW-0 to DW-2 >> + * struct utp_upiu_req - general upiu request structure >> + * @header:UPIU header structure DW-0 to DW-2 >> + * @sc: fields structure for scsi command DW-3 to DW-7 >> + */ >> +struct utp_upiu_req { >> +struct utp_upiu_header header; >> +struct utp_upiu_cmd sc; >> +}; >> + >> +/** >> + * struct utp_cmd_rsp - Response UPIU structure >>* @residual_transfer_count: Residual transfer count DW-3 >>* @reserved: Reserved double words DW-4 to DW-7 >>* @sense_data_len: Sense data length DW-8 U16 >>* @sense_data: Sense data field DW-8 to DW-12 >>*/ >> -struct utp_upiu_rsp { >> -struct utp_upiu_header header; >> +struct utp_cmd_rsp { >> u32 residual_transfer_count; >> u32 reserved[4]; >> u16 sense_data_len; >> @@ -165,6 +184,16 @@ struct utp_upiu_rsp { >> }; >> >> /** >> + * struct utp_upiu_rsp - general upiu response structure >> + * @header: UPIU header structure DW-0 to DW-2 >> + * @sr: fields structure for scsi command DW-3 to DW-12 >> + */ >> +struct utp_upiu_rsp { >> +struct utp_upiu_header header; >> +struct utp_cmd_rsp sr; >
[PATCH V3 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver
Add runtime PM helpers to suspend/resume UFS controller at runtime. Enable runtime PM by default for pci and platform drivers as the initialized hardware can suspend if it is not used after bootup. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd-pci.c| 65 ++--- drivers/scsi/ufs/ufshcd-pltfrm.c | 51 +- drivers/scsi/ufs/ufshcd.c|8 + 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 48be39a..7bd8faa 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -35,6 +35,7 @@ #include "ufshcd.h" #include +#include #ifdef CONFIG_PM /** @@ -44,7 +45,7 @@ * * Returns -ENOSYS */ -static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int ufshcd_pci_suspend(struct device *dev) { /* * TODO: @@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) * * Returns -ENOSYS */ -static int ufshcd_pci_resume(struct pci_dev *pdev) +static int ufshcd_pci_resume(struct device *dev) { /* * TODO: @@ -71,8 +72,48 @@ static int ufshcd_pci_resume(struct pci_dev *pdev) return -ENOSYS; } +#else +#define ufshcd_pci_suspend NULL +#define ufshcd_pci_resume NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pci_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_suspend(hba); +} +static int ufshcd_pci_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_resume(hba); +} +static int ufshcd_pci_runtime_idle(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_idle(hba); +} +#else /* !CONFIG_PM_RUNTIME */ +#define ufshcd_pci_runtime_suspend NULL +#define ufshcd_pci_runtime_resume NULL +#define ufshcd_pci_runtime_idleNULL +#endif /* CONFIG_PM_RUNTIME */ + /** * ufshcd_pci_shutdown - main function to put the controller in reset state * @pdev: pointer to PCI device handle @@ -91,6 +132,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev) { struct ufs_hba *hba = pci_get_drvdata(pdev); + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + disable_irq(pdev->irq); ufshcd_remove(hba); pci_release_regions(pdev); @@ -168,6 +212,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) } pci_set_drvdata(pdev, hba); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); return 0; @@ -182,6 +228,14 @@ out_error: return err; } +static const struct dev_pm_ops ufshcd_pci_pm_ops = { + .suspend= ufshcd_pci_suspend, + .resume = ufshcd_pci_resume, + .runtime_suspend = ufshcd_pci_runtime_suspend, + .runtime_resume = ufshcd_pci_runtime_resume, + .runtime_idle= ufshcd_pci_runtime_idle, +}; + static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = { { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { } /* terminate list */ @@ -195,10 +249,9 @@ static struct pci_driver ufshcd_pci_driver = { .probe = ufshcd_pci_probe, .remove = ufshcd_pci_remove, .shutdown = ufshcd_pci_shutdown, -#ifdef CONFIG_PM - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, -#endif + .driver = { + .pm = &ufshcd_pci_pm_ops + }, }; module_pci_driver(ufshcd_pci_driver); diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index c42db40..b1f2605 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -34,6 +34,7 @@ */ #include +#include #include "ufshcd.h" @@ -87,6 +88,43 @@ static int ufshcd_pltfrm_resume(struct device *dev) #define ufshcd_pltfrm_resume NULL #endif +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pltfrm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ufs_hba *hba = platform_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_suspend(hba); +} +static int ufshcd_pltfrm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ufs_hba
[PATCH V3 4/4] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Upon determining fatal error condition the host controller may hang forever until a reset is applied, so just retrying the command doesn't work without a reset. So, the reset is applied in the driver context in a separate work and SCSI mid-layer isn't informed until reset is applied. o Processed requests which are completed without error are reported to SCSI layer as successful and any pending commands that are not started yet or are not cause of the error are re-queued into scsi midlayer queue. For the command that caused error, host controller or device is reset and DID_ERROR is returned for command retry after applying reset. o SCSI is informed about the expected Unit-Attentioni exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 349 +++- drivers/scsi/ufs/ufshcd.h |2 + drivers/scsi/ufs/ufshci.h | 19 ++- 3 files changed, 295 insertions(+), 75 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b4c9910..2a3874f 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -80,6 +80,14 @@ enum { UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -108,6 +116,7 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); /* * ufshcd_wait_for_register - wait for register value to change @@ -1605,9 +1614,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1733,66 +1739,6 @@ static int ufshcd_validate_dev_connection(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < hba->nutrs; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - clear_bit_unlock(tag, &hba->lrb_in_use); - } - } - } - - /* complete device management command */ - if (hba->dev_cmd.complete) - complete(hba->dev_cmd.complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1809,6 +1755,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ +
[PATCH V3 3/4] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 467 +++-- drivers/scsi/ufs/ufshcd.h |2 + 2 files changed, 411 insertions(+), 58 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 51ce096..b4c9910 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -69,9 +69,15 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_HOST_RESET_PENDING = (1 << 0), + UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; /* Interrupt configuration options */ @@ -87,6 +93,22 @@ enum { INT_AGGR_CONFIG, }; +#define ufshcd_set_device_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_set_host_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_device_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_host_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_clear_device_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_clear_host_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_HOST_RESET_PENDING) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -851,9 +873,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; goto out; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + goto out; } /* acquire the tag to make sure device cmds don't use it */ @@ -1573,8 +1608,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2273,6 +2306,106 @@ out: } /** + * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_complete_pending_tasks - complete outstanding tasks + * @hba: per adapter instance + * + * Abort in-progress task management commands and wakeup + * waiting threads. + * + * Returns non-zero error value when failed to clear all the commands. + */ +static int ufshcd_complete_pending_tasks(struct ufs_hba *hba) +{ + u32 reg; + int err = 0; + unsigned long flags; + + if (!hba->outstanding_tasks) + goto out; + + /* Clear UTMRL only when run-stop is enabled */ + if (ufshcd_utmrl_is_rsr_enabled(hba)) + ufshcd_writel(hba, ~hba->outstanding_tasks, + REG_UTP_TASK_REQ_LIST_CLEAR); + + /* poll for max. 1 sec to clear door bell register by h/w */ + reg = ufshcd_wait_for_register(hba, + REG_UTP_TASK_REQ_DOOR_BELL, + hba->outstanding_tasks, 0, 1000, 1000); + if (reg & hba->outstanding_tasks) + err = -ETIMEDOUT; + + spin_lock_irqsave(hba->host->host_lock, flags); + /* complete commands that were cleared out */ + ufshcd_tmc_handler(hba); + spin_unlock_irqres
[PATCH V3 1/4] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: Problem: If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. Problem: Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. Problem: If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. Problem: While preparing the TM command descriptor, the task tag used should be unique across SCSI/NOP/QUERY/TM commands and not the task tag of the command which the TM command is trying to manage. Fix: Use a unique task tag instead of task tag of SCSI command. Problem: Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 177 ++--- drivers/scsi/ufs/ufshcd.h |8 ++- 2 files changed, 126 insertions(+), 59 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index af7d01d..a176421 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -53,6 +53,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + /* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF @@ -190,13 +193,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) +{ + int tag; + bool ret = false; + + if (!free_slot) + goto out; + + do { + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); + if (tag >= hba->nutmrs) + goto out; + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); + + *free_slot = tag; + ret = true; +out: + return ret; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + clear_bit_unlock(slot, &hba->tm_slots_in_use); } /** @@ -1778,10 +1803,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1802,19 +1828,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", + __func__, ocs_value); } spin_unlock_irqrestore(hba->host->host_lock, flags); - return task_result; + + return ocs_value; } /** @@ -2298,7 +2320,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba
[PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations
Background operations in the UFS device can be disabled by the host to reduce the response latency of transfer requests. Add support for enabling/disabling the background operations during runtime suspend/resume of the device. If the device is in critical need of BKOPS it will raise an URGENT_BKOPS exception which should be handled by the host to make sure the device performs as expected. During bootup, the BKOPS is enabled in the device by default. The disable of BKOPS is supported only when the driver supports runtime suspend/resume operations as the runtime PM framework provides a way to determine the device idleness and hence BKOPS can be managed effectively. During runtime resume the BKOPS is disabled to reduce latency and during runtime suspend the BKOPS is enabled to allow device to carry out idle time BKOPS. In some cases where the BKOPS is disabled during runtime resume and due to continuous data transfers the runtime suspend is not triggered, the BKOPS is enabled when the device raises a level-2 exception (outstanding operations - performance impact). Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 25 - drivers/scsi/ufs/ufshcd.c | 338 + drivers/scsi/ufs/ufshcd.h | 10 ++ 3 files changed, 372 insertions(+), 1 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index db5bde4..549a652 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -107,7 +107,29 @@ enum { /* Flag idn for Query Requests*/ enum flag_idn { - QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_BKOPS_EN = 0x04, +}; + +/* Attribute idn for Query requests */ +enum attr_idn { + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + QUERY_ATTR_IDN_EE_STATUS= 0x0E, +}; + +/* Exception event mask values */ +enum { + MASK_EE_STATUS = 0x, + MASK_EE_URGENT_BKOPS= (1 << 2), +}; + +/* Background operation status */ +enum { + BKOPS_STATUS_NO_OP = 0x0, + BKOPS_STATUS_NON_CRITICAL= 0x1, + BKOPS_STATUS_PERF_IMPACT = 0x2, + BKOPS_STATUS_CRITICAL= 0x3, }; /* UTP QUERY Transaction Specific Fields OpCode */ @@ -156,6 +178,7 @@ enum { MASK_TASK_RESPONSE = 0xFF00, MASK_RSP_UPIU_RESULT= 0x, MASK_QUERY_DATA_SEG_LEN = 0x, + MASK_RSP_EXCEPTION_EVENT = 0x1, }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 96ccb28..a25de66 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -268,6 +268,21 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr) } /** + * ufshcd_is_exception_event - Check if the device raised an exception event + * @ucd_rsp_ptr: pointer to response UPIU + * + * The function checks if the device raised an exception event indicated in + * the Device Information field of response UPIU. + * + * Returns true if exception is raised, false otherwise. + */ +static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr) +{ + return be32_to_cpu(ucd_rsp_ptr->header.dword_2) & + MASK_RSP_EXCEPTION_EVENT ? true : false; +} + +/** * ufshcd_config_int_aggr - Configure interrupt aggregation values. * Currently there is no use case where we want to configure * interrupt aggregation dynamically. So to configure interrupt @@ -1174,6 +1189,86 @@ out_no_mem: } /** + * ufshcd_query_attr - Helper function for composing attribute requests + * hba: per-adapter instance + * opcode: attribute opcode + * idn: attribute idn to access + * index: index field + * selector: selector field + * attr_val: the attribute value after the query request completes + * + * Returns 0 for success, non-zero in case of failure +*/ +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) +{ + struct ufs_query_req *query; + struct ufs_query_res *response; + int err = -ENOMEM; + + if (!attr_val) { + dev_err(hba->dev, "%s: attribute value required for write request\n", + __func__); + err = -EINVAL; + goto out; + } + + query = kzalloc(sizeof(struct ufs_query_req), GFP_KERNEL); + if (!query) { + dev_err(hba->dev, + "%s: Failed allocating ufs_query_req instance\n", + __func__); + goto out; + } + + response = kzalloc(sizeof(struct ufs_query_res), GFP_KERNEL); + if (!response) { + dev_err(hba->dev, + "%s: Failed allocating ufs_query_re
[PATCH V3 0/4] scsi: ufs: Improve UFS error handling
The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches depends on: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization [PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations [PATCH V3 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver Changes from v2: - [PATCH V3 1/4]: Make the task management command task tag unique across SCSI/NOP/QUERY request tags. - [PATCH V3 3/4]: While handling device/host reset, wait for pending fatal handler to return if running. Changes from v1: - [PATCH V2 1/4]: Fix a race condition because of overloading outstanding_tasks variable to lock the slots. A new variable tm_slots_in_use will track which slots are in use by the driver. - [PATCH V2 2/4]: Commit text update to clarify the hardware race with more details. - [PATCH V2 3/4]: Minor cleanup and rebase - [PATCH V2 4/4]: Fix a bug - sleeping in atomic context Sujit Reddy Thumma (4): scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufshcd.c | 1035 + drivers/scsi/ufs/ufshcd.h | 12 +- drivers/scsi/ufs/ufshci.h | 19 +- 3 files changed, 873 insertions(+), 193 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V3 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 70 +++- 1 files changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a176421..51ce096 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2550,6 +2550,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2558,7 +2564,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt; u8 resp; struct ufshcd_lrb *lrbp; @@ -2566,33 +2573,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) + goto out; - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + lrbp = &hba->lrb[tag]; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 2ms to stabilize */ + usleep_range(1000, 2000); + continue; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = resp; /* service response error */ + goto out; + } + } + + if (!poll_cnt) { + err = -EBUSY; goto out; } - spin_unlock_irqrestore(host->host_lock, flags); - lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = resp; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &hba->outstanding_reqs); hba->lrb[tag].cmd = NULL; spin_unlock_ir
[PATCH V3 0/2] scsi: ufs: Add support to control UFS device background operations
Add host assisted background operations for UFS device and runtime PM helpers for ufshcd platform and pci glue drivers. The background operations are disabled during runtime resume and enabled when the device is idle and runtime suspended. These patches depends on: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization Changes from v2: - Enable auto bkops by default explicitly during bootup so that we may not assume it as enabled after reset. - Enable runtime PM support for contexts that are not part of SCSI, so that the host is not suspended while running other contexts like bkops exception handling. Changes from v1: - Minor cleanup and rebase - Forced enable of auto bkops during initialization to make sure device and driver state are matched. Sujit Reddy Thumma (2): scsi: ufs: Add support for host assisted background operations scsi: ufs: Add runtime PM support for UFS host controller driver drivers/scsi/ufs/ufs.h | 25 +++- drivers/scsi/ufs/ufshcd-pci.c| 65 +++- drivers/scsi/ufs/ufshcd-pltfrm.c | 51 ++- drivers/scsi/ufs/ufshcd.c| 346 ++ drivers/scsi/ufs/ufshcd.h| 10 + 5 files changed, 489 insertions(+), 8 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
From: Dolev Raviv Allow UFS device to complete its initialization and accept SCSI commands by setting fDeviceInit flag. The device may take time for this operation and hence the host should poll until fDeviceInit flag is toggled to zero. This step is mandated by UFS device specification for device initialization completion. Signed-off-by: Dolev Raviv Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 88 +- drivers/scsi/ufs/ufshcd.c | 292 - drivers/scsi/ufs/ufshcd.h | 14 ++ drivers/scsi/ufs/ufshci.h |2 +- 4 files changed, 390 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 14c0a4e..db5bde4 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -43,6 +43,8 @@ #define GENERAL_UPIU_REQUEST_SIZE 32 #define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \ (GENERAL_UPIU_REQUEST_SIZE)) +#define QUERY_OSF_SIZE ((GENERAL_UPIU_REQUEST_SIZE) - \ + (sizeof(struct utp_upiu_header))) #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ @@ -68,7 +70,7 @@ enum { UPIU_TRANSACTION_COMMAND= 0x01, UPIU_TRANSACTION_DATA_OUT = 0x02, UPIU_TRANSACTION_TASK_REQ = 0x04, - UPIU_TRANSACTION_QUERY_REQ = 0x26, + UPIU_TRANSACTION_QUERY_REQ = 0x16, }; /* UTP UPIU Transaction Codes Target to Initiator */ @@ -97,8 +99,19 @@ enum { UPIU_TASK_ATTR_ACA = 0x03, }; -/* UTP QUERY Transaction Specific Fields OpCode */ +/* UPIU Query request function */ enum { + UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01, + UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81, +}; + +/* Flag idn for Query Requests*/ +enum flag_idn { + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, +}; + +/* UTP QUERY Transaction Specific Fields OpCode */ +enum query_opcode { UPIU_QUERY_OPCODE_NOP = 0x0, UPIU_QUERY_OPCODE_READ_DESC = 0x1, UPIU_QUERY_OPCODE_WRITE_DESC= 0x2, @@ -110,6 +123,21 @@ enum { UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, }; +/* Query response result code */ +enum { + QUERY_RESULT_SUCCESS= 0x00, + QUERY_RESULT_NOT_READABLE = 0xF6, + QUERY_RESULT_NOT_WRITEABLE = 0xF7, + QUERY_RESULT_ALREADY_WRITTEN= 0xF8, + QUERY_RESULT_INVALID_LENGTH = 0xF9, + QUERY_RESULT_INVALID_VALUE = 0xFA, + QUERY_RESULT_INVALID_SELECTOR = 0xFB, + QUERY_RESULT_INVALID_INDEX = 0xFC, + QUERY_RESULT_INVALID_IDN= 0xFD, + QUERY_RESULT_INVALID_OPCODE = 0xFE, + QUERY_RESULT_GENERAL_FAILURE= 0xFF, +}; + /* UTP Transfer Request Command Type (CT) */ enum { UPIU_COMMAND_SET_TYPE_SCSI = 0x0, @@ -127,6 +155,7 @@ enum { MASK_SCSI_STATUS= 0xFF, MASK_TASK_RESPONSE = 0xFF00, MASK_RSP_UPIU_RESULT= 0x, + MASK_QUERY_DATA_SEG_LEN = 0x, }; /* Task management service response */ @@ -160,13 +189,40 @@ struct utp_upiu_cmd { }; /** + * struct utp_upiu_query - upiu request buffer structure for + * query request. + * @opcode: command to perform B-0 + * @idn: a value that indicates the particular type of data B-1 + * @index: Index to further identify data B-2 + * @selector: Index to further identify data B-3 + * @reserved_osf: spec reserved field B-4,5 + * @length: number of descriptor bytes to read/write B-6,7 + * @value: Attribute value to be written DW-5 + * @reserved: spec reserved DW-6,7 + */ +struct utp_upiu_query { + u8 opcode; + u8 idn; + u8 index; + u8 selector; + u16 reserved_osf; + u16 length; + u32 value; + u32 reserved[2]; +}; + +/** * struct utp_upiu_req - general upiu request structure * @header:UPIU header structure DW-0 to DW-2 * @sc: fields structure for scsi command DW-3 to DW-7 + * @qr: fields structure for query request DW-3 to DW-7 */ struct utp_upiu_req { struct utp_upiu_header header; - struct utp_upiu_cmd sc; + union { + struct utp_upiu_cmd sc; + struct utp_upiu_query qr; + }; }; /** @@ -187,10 +243,14 @@ struct utp_cmd_rsp { * struct utp_upiu_rsp - general upiu response structure * @header: UPIU header structure DW-0 to DW-2 * @sr: fields structure for scsi command DW-3 to DW-12 + * @qr: fields structure for query request DW-3 to DW-7 */ struct utp_upiu_rsp { struct utp_upiu_header header; - struct utp_cmd_rsp sr; + union { + struct utp_cmd_rsp sr; + struct utp_upiu_query qr; + }; }; /** @@ -223,4 +283,24 @@ struct utp_upiu_task_rsp { u3
[PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
As part of device initialization sequence, sending NOP OUT UPIU and waiting for NOP IN UPIU response is mandatory. This confirms that the device UFS Transport (UTP) layer is functional and the host can configure the device with further commands. Add support for sending NOP OUT UPIU to check the device connection path and test whether the UTP layer on the device side is functional during initialization. A tag is acquired from the SCSI tag map space in order to send the device management command. When the tag is acquired by internal command the scsi command is rejected with host busy flag in order to requeue the request. To avoid frequent collisions between internal commands and scsi commands the device management command tag is allocated in the opposite direction w.r.t block layer tag allocation. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Dolev Raviv --- drivers/scsi/ufs/ufs.h| 43 +++- drivers/scsi/ufs/ufshcd.c | 596 + drivers/scsi/ufs/ufshcd.h | 29 ++- 3 files changed, 552 insertions(+), 116 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 139bc06..14c0a4e 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -36,10 +36,16 @@ #ifndef _UFS_H #define _UFS_H +#include +#include + #define MAX_CDB_SIZE 16 +#define GENERAL_UPIU_REQUEST_SIZE 32 +#define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \ + (GENERAL_UPIU_REQUEST_SIZE)) #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ - ((byte3 << 24) | (byte2 << 16) |\ + cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ (byte1 << 8) | (byte0)) /* @@ -73,6 +79,7 @@ enum { UPIU_TRANSACTION_TASK_RSP = 0x24, UPIU_TRANSACTION_READY_XFER = 0x31, UPIU_TRANSACTION_QUERY_RSP = 0x36, + UPIU_TRANSACTION_REJECT_UPIU= 0x3F, }; /* UPIU Read/Write flags */ @@ -110,6 +117,12 @@ enum { UPIU_COMMAND_SET_TYPE_QUERY = 0x2, }; +/* UTP Transfer Request Command Offset */ +#define UPIU_COMMAND_TYPE_OFFSET 28 + +/* Offset of the response code in the UPIU header */ +#define UPIU_RSP_CODE_OFFSET 8 + enum { MASK_SCSI_STATUS= 0xFF, MASK_TASK_RESPONSE = 0xFF00, @@ -138,26 +151,32 @@ struct utp_upiu_header { /** * struct utp_upiu_cmd - Command UPIU structure - * @header: UPIU header structure DW-0 to DW-2 * @data_transfer_len: Data Transfer Length DW-3 * @cdb: Command Descriptor Block CDB DW-4 to DW-7 */ struct utp_upiu_cmd { - struct utp_upiu_header header; u32 exp_data_transfer_len; u8 cdb[MAX_CDB_SIZE]; }; /** - * struct utp_upiu_rsp - Response UPIU structure - * @header: UPIU header DW-0 to DW-2 + * struct utp_upiu_req - general upiu request structure + * @header:UPIU header structure DW-0 to DW-2 + * @sc: fields structure for scsi command DW-3 to DW-7 + */ +struct utp_upiu_req { + struct utp_upiu_header header; + struct utp_upiu_cmd sc; +}; + +/** + * struct utp_cmd_rsp - Response UPIU structure * @residual_transfer_count: Residual transfer count DW-3 * @reserved: Reserved double words DW-4 to DW-7 * @sense_data_len: Sense data length DW-8 U16 * @sense_data: Sense data field DW-8 to DW-12 */ -struct utp_upiu_rsp { - struct utp_upiu_header header; +struct utp_cmd_rsp { u32 residual_transfer_count; u32 reserved[4]; u16 sense_data_len; @@ -165,6 +184,16 @@ struct utp_upiu_rsp { }; /** + * struct utp_upiu_rsp - general upiu response structure + * @header: UPIU header structure DW-0 to DW-2 + * @sr: fields structure for scsi command DW-3 to DW-12 + */ +struct utp_upiu_rsp { + struct utp_upiu_header header; + struct utp_cmd_rsp sr; +}; + +/** * struct utp_upiu_task_req - Task request UPIU structure * @header - UPIU header structure DW0 to DW-2 * @input_param1: Input parameter 1 DW-3 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b743bd6..3f482b6 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -43,6 +43,11 @@ /* UIC command timeout, unit: ms */ #define UIC_CMD_TIMEOUT500 +/* NOP OUT retries waiting for NOP IN response */ +#define NOP_OUT_RETRIES10 +/* Timeout after 30 msecs if NOP OUT hangs without response */ +#define NOP_OUT_TIMEOUT30 /* msecs */ + enum { UFSHCD_MAX_CHANNEL = 0, UFSHCD_MAX_ID = 1, @@ -71,6 +76,47 @@ enum { INT_AGGR_CONFIG, }; +/* + * ufshcd_wait_for_register - wait for register value to change + * @hba - per-adapter interface + * @reg - mmio register offset + * @mask - mask to apply to read register value + * @val - wait condition + * @interval_us - polling interval in microsecs + * @timeout_ms - timeout in millisecs + * + * Returns final register value af
[PATCH V3 0/2] Add suport for internal request (NOP and Query Request)
This patch series replace the previous Query Request and NOP patches: [PATCH 1/8] scsi: ufs: add support for query [PATCH 6/8] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH 7/8] scsi: ufs: Set fDeviceInit flag to initiate device initialization Major difference - Sending the query request via the SCSI vendor specific command can cause a deadlock in case the SCSI command queue is blocked and we would like to send a query request (for example fDeviceInit in case of re-initialization). In addition, usage of a vendor specific SCSI command for UFS can cause future conflicts if this vendor specific command will be allocated for a different usage. The new patch series gets an internal tag for NOP and query requests and do not involve the SCSI layer in UFS specific requests transfers. This design also resolves the possible deadlock mentioned above. Changes from v2: - Rebased on scsi-misc branche (commit 8c0eb596baa5) - Minor addition to structure documentation in ufshcd.h Changes from v1 - Allocate a tag for device management commands dynamically instead of reserving tag[MAX_QUEUE - 1]. - Changed the "internal_cmd" naming to "dev_cmd" to avoid confusion with other type of internal commands other than NOP and QUERY. Dolev Raviv (1): scsi: ufs: Set fDeviceInit flag to initiate device initialization Sujit Reddy Thumma (1): scsi: ufs: Add support for sending NOP OUT UPIU drivers/scsi/ufs/ufs.h| 127 ++- drivers/scsi/ufs/ufshcd.c | 886 +++-- drivers/scsi/ufs/ufshcd.h | 43 ++- drivers/scsi/ufs/ufshci.h |2 +- 4 files changed, 939 insertions(+), 119 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH V2 4/4] scsi: ufs: Improve UFS fatal error handling
On 7/3/2013 10:22 PM, Santosh Y wrote: + +/** + * ufshcd_fatal_err_handler - handle fatal errors + * @work: pointer to work structure */ static void ufshcd_fatal_err_handler(struct work_struct *work) { struct ufs_hba *hba; + unsigned long flags; + u32 err_xfer = 0; + u32 err_tm = 0; + int err; + hba = container_of(work, struct ufs_hba, feh_workq); - /* check if reset is already in progress */ - if (hba->ufshcd_state != UFSHCD_STATE_RESET) - ufshcd_do_reset(hba); + spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->ufshcd_state == UFSHCD_STATE_RESET) { + /* complete processed requests and exit */ + ufshcd_transfer_req_compl(hba); + ufshcd_tmc_handler(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); + return; I could not go through this patch yet, please check if it needs to wait here until the state is 'operational' or 'error'. The 'reset' state might be due to the device reset also. As of now reset is scheduled only in two contexts - 1) Fatal error handling 2) SCSI error handling If scsi error handling is in progress it changes the state to UFSHCD_STATE_RESET. If fatal error interrupt is raised it checks whether the state is operational or not before scheduling the work. So in any case, there is no race between fatal error handler and the scsi error handler and hence there is no need any wait here. static void ufshcd_err_handler(struct ufs_hba *hba) { ... fatal_eh: - hba->ufshcd_state = UFSHCD_STATE_ERROR; - schedule_work(&hba->feh_workq); + /* handle fatal errors only when link is functional */ + if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { + /* block commands from midlayer */ + scsi_block_requests(hba->host); + + /* block commands at driver layer until error is handled */ + hba->ufshcd_state = UFSHCD_STATE_ERROR; + schedule_work(&hba->feh_workq); + } } + } + + hba->ufshcd_state = UFSHCD_STATE_RESET; + ufshcd_error_autopsy_transfer_req(hba, &err_xfer); + ufshcd_error_autopsy_task_req(hba, &err_tm); + -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH V2 3/4] scsi: ufs: Fix device and host reset methods
On 7/3/2013 11:19 AM, Santosh Y wrote: + >+/** >+ * ufshcd_eh_device_reset_handler - device reset handler registered to >+ *scsi layer. >+ * @cmd - SCSI command pointer >+ * >+ * Returns SUCCESS/FAILED >+ */ >+static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) >+{ >+ struct ufs_hba *hba; >+ int err; >+ unsigned long flags; >+ >+ hba = shost_priv(cmd->device->host); >+ >+ spin_lock_irqsave(hba->host->host_lock, flags); >+ if (hba->ufshcd_state == UFSHCD_STATE_RESET) { >+ dev_warn(hba->dev, "%s: reset in progress\n", __func__); >+ err = SUCCESS; >+ spin_unlock_irqrestore(hba->host->host_lock, flags); >+ goto out; It is better to wait here until the state changes to 'operational' or 'error' before returning success. Okay. Sounds good. -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/4] scsi: ufs: Fix broken task management command implementation
On 7/3/2013 9:53 PM, Santosh Y wrote: On Wed, Jul 3, 2013 at 9:22 PM, Sujit Reddy Thumma wrote: On 7/2/2013 9:21 PM, Santosh Y wrote: On Fri, Jun 28, 2013 at 5:02 PM, Sujit Reddy Thumma wrote: On 6/27/2013 4:49 PM, Santosh Y wrote: + spin_lock_irqsave(host->host_lock, flags); task_req_descp = hba->utmrdl_base_addr; task_req_descp += free_slot; @@ -2353,38 +2387,39 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba, (struct utp_upiu_task_req *) task_req_descp->task_req_upiu; task_req_upiup->header.dword_0 = UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0, - lrbp->lun, lrbp->task_tag); + lun_id, free_slot); Actually it still doesn't fix the problem. The*task tag* used here should be unique across the SCSI/Query and Task Managment UPIUs. I am sorry, I didn't get that. Why should it be unique across the SCSI/Query? For example, if a machine supports 32 request slots and 8 task management slots, then the task management command tag can be anything out of 8 slots. The spec(ufs 1.1) has the requirement under '10.5.2 Basic Header Format'->'Task Tag'. Couple of devices I came across had similar behavior. The tracking of UPIUs --even belonging to a separate group-- seemed to be based on the 'task tag' value rather than 'type of UPIU'->'task tag'. Thanks for the clarification. So to make the task tag unique we should do something like below: @@ -2940,9 +2941,10 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, /* Configure task request UPIU */ task_req_upiup = (struct utp_upiu_task_req *) task_req_descp->task_req_upiu; + task_tag = hba->nutrs + free_slot; Yes, this was exactly my internal fix at the time :-) Okay. I will update the patch with this. Thanks. task_req_upiup->header.dword_0 = UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0, - lun_id, free_slot); + lun_id, task_tag); Will this work for the devices you came across? -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/4] scsi: ufs: Fix broken task management command implementation
On 7/2/2013 9:21 PM, Santosh Y wrote: On Fri, Jun 28, 2013 at 5:02 PM, Sujit Reddy Thumma wrote: On 6/27/2013 4:49 PM, Santosh Y wrote: + spin_lock_irqsave(host->host_lock, flags); task_req_descp = hba->utmrdl_base_addr; task_req_descp += free_slot; @@ -2353,38 +2387,39 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba, (struct utp_upiu_task_req *) task_req_descp->task_req_upiu; task_req_upiup->header.dword_0 = UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0, - lrbp->lun, lrbp->task_tag); + lun_id, free_slot); Actually it still doesn't fix the problem. The*task tag* used here should be unique across the SCSI/Query and Task Managment UPIUs. I am sorry, I didn't get that. Why should it be unique across the SCSI/Query? For example, if a machine supports 32 request slots and 8 task management slots, then the task management command tag can be anything out of 8 slots. The spec(ufs 1.1) has the requirement under '10.5.2 Basic Header Format'->'Task Tag'. Couple of devices I came across had similar behavior. The tracking of UPIUs --even belonging to a separate group-- seemed to be based on the 'task tag' value rather than 'type of UPIU'->'task tag'. Thanks for the clarification. So to make the task tag unique we should do something like below: @@ -2940,9 +2941,10 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, /* Configure task request UPIU */ task_req_upiup = (struct utp_upiu_task_req *) task_req_descp->task_req_upiu; + task_tag = hba->nutrs + free_slot; task_req_upiup->header.dword_0 = UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0, - lun_id, free_slot); + lun_id, task_tag); Will this work for the devices you came across? -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] block: Fix possible sleep in invalid context
On 7/2/2013 8:34 AM, Aaron Lu wrote: Fix this by releasing spin_lock_irq() before calling >pm_runtime_autosuspend() in blk_post_runtime_resume(). Hi Sujit, Thanks for testing out block layer runtime PM! As for the problem here, it is already fixed by: commit c60855cdb976c632b3bf8922eeab8a0e78edfc04 Author: Aaron Lu Date: Fri May 17 15:47:20 2013 +0800 blkpm: avoid sleep when holding queue lock Thanks Aaron. I see that is merged in 3.10-rc6. Please ignore this patch. -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] block: Fix possible sleep in invalid context
When block runtime PM is enabled following warning is seen while resuming the device. BUG: sleeping function called from invalid context at .../drivers/base/power/runtime.c:923 in_atomic(): 1, irqs_disabled(): 128, pid: 12, name: kworker/0:1 [] (unwind_backtrace+0x0/0x120) from [] (__pm_runtime_suspend+0x34/0xa0) from [] (blk_post_runtime_resume+0x4c/0x5c) from [] (scsi_runtime_resume+0x90/0xb4) from [] (__rpm_callback+0x30/0x58) from [] (rpm_callback+0x18/0x28) from [] (rpm_resume+0x3dc/0x540) from [] (pm_runtime_work+0x8c/0x98) from [] (process_one_work+0x238/0x3e4) from [] (worker_thread+0x1ac/0x2ac) from [] (kthread+0x88/0x94) from [] (kernel_thread_exit+0x0/0x8) Fix this by releasing spin_lock_irq() before calling pm_runtime_autosuspend() in blk_post_runtime_resume(). Signed-off-by: Sujit Reddy Thumma Cc: sta...@vger.kernel.org --- block/blk-core.c |6 -- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 33c33bc..2456116 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -3159,16 +3159,18 @@ EXPORT_SYMBOL(blk_pre_runtime_resume); */ void blk_post_runtime_resume(struct request_queue *q, int err) { - spin_lock_irq(q->queue_lock); if (!err) { + spin_lock_irq(q->queue_lock); q->rpm_status = RPM_ACTIVE; __blk_run_queue(q); pm_runtime_mark_last_busy(q->dev); + spin_unlock_irq(q->queue_lock); pm_runtime_autosuspend(q->dev); } else { + spin_lock_irq(q->queue_lock); q->rpm_status = RPM_SUSPENDED; + spin_unlock_irq(q->queue_lock); } - spin_unlock_irq(q->queue_lock); } EXPORT_SYMBOL(blk_post_runtime_resume); #endif -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V2 1/4] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: Problem: If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. Problem: Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. Problem: If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. Problem: While preparing the TM command descriptor, the tag used should be the free slot of TM command and not the task tag of the command which the TM command is trying to manage. Fix: Use free slot tag instead of task tag of SCSI command. Problem: Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 175 ++--- drivers/scsi/ufs/ufshcd.h |8 ++- 2 files changed, 124 insertions(+), 59 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index ac323a8..4781b00 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -53,6 +53,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + /* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF @@ -190,13 +193,35 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) +{ + int tag; + bool ret = false; + + if (!free_slot) + goto out; + + do { + tag = find_first_zero_bit(&hba->tm_slots_in_use, hba->nutmrs); + if (tag >= hba->nutmrs) + goto out; + } while (test_and_set_bit_lock(tag, &hba->tm_slots_in_use)); + + *free_slot = tag; + ret = true; +out: + return ret; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + clear_bit_unlock(slot, &hba->tm_slots_in_use); } /** @@ -1809,10 +1834,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1833,19 +1859,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", + __func__, ocs_value); } spin_unlock_irqrestore(hba->host->host_lock, flags); - return task_result; + + return ocs_value; } /** @@ -2325,7 +2347,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba) tm_doorb
[PATCH V2 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 70 +++- 1 files changed, 55 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 4781b00..6bfe927 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2575,6 +2575,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2583,7 +2589,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt; u8 resp; struct ufshcd_lrb *lrbp; @@ -2591,33 +2598,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) + goto out; - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + lrbp = &hba->lrb[tag]; + for (poll_cnt = 100; poll_cnt; poll_cnt--) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 2ms to stabilize */ + usleep_range(1000, 2000); + continue; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = resp; /* service response error */ + goto out; + } + } + + if (!poll_cnt) { + err = -EBUSY; goto out; } - spin_unlock_irqrestore(host->host_lock, flags); - lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = resp; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &hba->outstanding_reqs); hba->lrb[tag].cmd = NULL; spin_unlock_ir
[PATCH V2 4/4] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Upon determining fatal error condition the host controller may hang forever until a reset is applied, so just retrying the command doesn't work without a reset. So, the reset is applied in the driver context in a separate work and SCSI mid-layer isn't informed until reset is applied. o Processed requests which are completed without error are reported to SCSI layer as successful and any pending commands that are not started yet or are not cause of the error are re-queued into scsi midlayer queue. For the command that caused error, host controller or device is reset and DID_ERROR is returned for command retry after applying reset. o SCSI is informed about the expected Unit-Attentioni exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 357 +++-- drivers/scsi/ufs/ufshcd.h |2 + drivers/scsi/ufs/ufshci.h | 19 ++- 3 files changed, 301 insertions(+), 77 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 2829a42..3788518 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -80,6 +80,14 @@ enum { UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -108,6 +116,7 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); /* * ufshcd_wait_for_register - wait for register value to change @@ -1636,9 +1645,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1764,66 +1770,6 @@ static int ufshcd_validate_dev_connection(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < hba->nutrs; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - clear_bit_unlock(tag, &hba->lrb_in_use); - } - } - } - - /* complete device management command */ - if (hba->dev_cmd.complete) - complete(hba->dev_cmd.complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1840,6 +1786,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ +
[PATCH V2 3/4] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 446 +++-- drivers/scsi/ufs/ufshcd.h |2 + 2 files changed, 393 insertions(+), 55 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6bfe927..2829a42 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -69,9 +69,15 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_HOST_RESET_PENDING = (1 << 0), + UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; /* Interrupt configuration options */ @@ -87,6 +93,22 @@ enum { INT_AGGR_CONFIG, }; +#define ufshcd_set_device_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_set_host_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_device_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_host_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_clear_device_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_clear_host_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_HOST_RESET_PENDING) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -883,9 +905,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; goto out; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + goto out; } /* acquire the tag to make sure device cmds don't use it */ @@ -1604,8 +1639,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2302,6 +2335,106 @@ out: } /** + * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_complete_pending_tasks - complete outstanding tasks + * @hba: per adapter instance + * + * Abort in-progress task management commands and wakeup + * waiting threads. + * + * Returns non-zero error value when failed to clear all the commands. + */ +static int ufshcd_complete_pending_tasks(struct ufs_hba *hba) +{ + u32 reg; + int err = 0; + unsigned long flags; + + if (!hba->outstanding_tasks) + goto out; + + /* Clear UTMRL only when run-stop is enabled */ + if (ufshcd_utmrl_is_rsr_enabled(hba)) + ufshcd_writel(hba, ~hba->outstanding_tasks, + REG_UTP_TASK_REQ_LIST_CLEAR); + + /* poll for max. 1 sec to clear door bell register by h/w */ + reg = ufshcd_wait_for_register(hba, + REG_UTP_TASK_REQ_DOOR_BELL, + hba->outstanding_tasks, 0, 1000, 1000); + if (reg & hba->outstanding_tasks) + err = -ETIMEDOUT; + + spin_lock_irqsave(hba->host->host_lock, flags); + /* complete commands that were cleared out */ + ufshcd_tmc_handler(hba); + spin_unlock_irqres
[PATCH V2 0/4] scsi: ufs: Improve UFS error handling
The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches depends on: [PATCH 01/10] scsi: ufs: wrap the i/o access operations [PATCH 02/10] scsi: ufs: amend interrupt configuration [PATCH 03/10] scsi: ufs: remove version check before IS reg clear [PATCH 04/10] scsi: ufs: rework link start-up process [PATCH 05/10] scsi: ufs: Fix the response UPIU length setting [PATCH 06/10] scsi: ufs: use devres functions for ufshcd [PATCH 07/10] ufshcd-pltfrm: add missing empty slot in ufs_of_match[] [PATCH 08/10] ufs: fix register address in UIC error interrupt handling [PATCH 09/10] ufshcd-pltfrm: remove unnecessary dma_set_coherent_mask() call [PATCH 10/10] ufs: fix DMA mask setting [PATCH V2 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH V2 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization [PATCH V2 1/2] scsi: ufs: Add support for host assisted background operations [PATCH V2 2/2] scsi: ufs: Add runtime PM helpers for UFS host driver Changes from v1: - [PATCH V2 1/4]: Fix a race condition because of overloading outstanding_tasks variable to lock the slots. A new variable tm_slots_in_use will track which slots are in use by the driver. - [PATCH V2 2/4]: Commit text update to clarify the hardware race with more details. - [PATCH V2 3/4]: Minor cleanup and rebase - [PATCH V2 4/4]: Fix a bug - sleeping in atomic context Sujit Reddy Thumma (4): scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufshcd.c | 1020 - drivers/scsi/ufs/ufshcd.h | 12 +- drivers/scsi/ufs/ufshci.h | 19 +- 3 files changed, 859 insertions(+), 192 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V2 0/2] scsi: ufs: Add support to control UFS device background operations
Add host assisted background operations for UFS device and runtime PM helpers for ufshcd platform and pci glue drivers. The background operations are disabled during runtime resume and enabled when the device is idle and runtime suspended. These patches depends on: [PATCH 01/10] scsi: ufs: wrap the i/o access operations [PATCH 02/10] scsi: ufs: amend interrupt configuration [PATCH 03/10] scsi: ufs: remove version check before IS reg clear [PATCH 04/10] scsi: ufs: rework link start-up process [PATCH 05/10] scsi: ufs: Fix the response UPIU length setting [PATCH 06/10] scsi: ufs: use devres functions for ufshcd [PATCH 07/10] ufshcd-pltfrm: add missing empty slot in ufs_of_match[] [PATCH 08/10] ufs: fix register address in UIC error interrupt handling [PATCH 09/10] ufshcd-pltfrm: remove unnecessary dma_set_coherent_mask() call [PATCH 10/10] ufs: fix DMA mask setting [PATCH V2 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH V2 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization Changes from v1: - Minor cleanup and rebase - Forced enable of auto bkops during initialization to make sure device and driver state are matched. Sujit Reddy Thumma (2): scsi: ufs: Add support for host assisted background operations scsi: ufs: Add runtime PM helpers for UFS host driver drivers/scsi/ufs/ufs.h | 25 +++- drivers/scsi/ufs/ufshcd-pci.c| 60 ++- drivers/scsi/ufs/ufshcd-pltfrm.c | 41 + drivers/scsi/ufs/ufshcd.c| 337 ++ drivers/scsi/ufs/ufshcd.h| 10 + 5 files changed, 466 insertions(+), 7 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V2 2/2] scsi: ufs: Add runtime PM helpers for UFS host driver
Add runtime PM helpers to suspend/resume the UFS controller device at runtime. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd-pci.c| 60 ++ drivers/scsi/ufs/ufshcd-pltfrm.c | 41 ++ 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 64d36eb..f669e9a2 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -35,6 +35,7 @@ #include "ufshcd.h" #include +#include #ifdef CONFIG_PM /** @@ -44,7 +45,7 @@ * * Returns -ENOSYS */ -static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int ufshcd_pci_suspend(struct device *dev) { /* * TODO: @@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) * * Returns -ENOSYS */ -static int ufshcd_pci_resume(struct pci_dev *pdev) +static int ufshcd_pci_resume(struct device *dev) { /* * TODO: @@ -71,8 +72,48 @@ static int ufshcd_pci_resume(struct pci_dev *pdev) return -ENOSYS; } +#else +#define ufshcd_pci_suspend NULL +#define ufshcd_pci_resume NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pci_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_suspend(hba); +} +static int ufshcd_pci_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_resume(hba); +} +static int ufshcd_pci_runtime_idle(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct ufs_hba *hba = pci_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_idle(hba); +} +#else /* !CONFIG_PM_RUNTIME */ +#define ufshcd_pci_runtime_suspend NULL +#define ufshcd_pci_runtime_resume NULL +#define ufshcd_pci_runtime_idleNULL +#endif /* CONFIG_PM_RUNTIME */ + /** * ufshcd_pci_shutdown - main function to put the controller in reset state * @pdev: pointer to PCI device handle @@ -156,6 +197,14 @@ out_error: return err; } +static const struct dev_pm_ops ufshcd_pci_pm_ops = { + .suspend= ufshcd_pci_suspend, + .resume = ufshcd_pci_resume, + .runtime_suspend = ufshcd_pci_runtime_suspend, + .runtime_resume = ufshcd_pci_runtime_resume, + .runtime_idle= ufshcd_pci_runtime_idle, +}; + static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = { { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { } /* terminate list */ @@ -169,10 +218,9 @@ static struct pci_driver ufshcd_pci_driver = { .probe = ufshcd_pci_probe, .remove = ufshcd_pci_remove, .shutdown = ufshcd_pci_shutdown, -#ifdef CONFIG_PM - .suspend = ufshcd_pci_suspend, - .resume = ufshcd_pci_resume, -#endif + .driver = { + .pm = &ufshcd_pci_pm_ops + }, }; module_pci_driver(ufshcd_pci_driver); diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index c42db40..7541054 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -34,6 +34,7 @@ */ #include +#include #include "ufshcd.h" @@ -87,6 +88,43 @@ static int ufshcd_pltfrm_resume(struct device *dev) #define ufshcd_pltfrm_resume NULL #endif +#ifdef CONFIG_PM_RUNTIME +static int ufshcd_pltfrm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ufs_hba *hba = platform_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_suspend(hba); +} +static int ufshcd_pltfrm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ufs_hba *hba = platform_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_resume(hba); +} +static int ufshcd_pltfrm_runtime_idle(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ufs_hba *hba = platform_get_drvdata(pdev); + + if (!hba) + return 0; + + return ufshcd_runtime_idle(hba); +} +#else /* !CONFIG_PM_RUNTIME */ +#define ufshcd_pltfrm_runtime_suspend NULL +#define ufshcd_pltfrm_runtime_resume NULL +#define ufshcd_pltfrm_runtime_idle NULL +#endif /* CONFIG_PM_RUNTIME */ + /** * ufshcd_pltfrm_probe - probe routine of the driver * @pdev: pointer to Platform device handle @@ -157,6 +195,9 @@ static const struct of_device_id uf
[PATCH V2 1/2] scsi: ufs: Add support for host assisted background operations
Background operations in the UFS device can be disabled by the host to reduce the response latency of transfer requests. Add support for enabling/disabling the background operations during runtime suspend/resume of the device. If the device is in critical need of BKOPS it will raise an URGENT_BKOPS exception which should be handled by the host to make sure the device performs as expected. During bootup, the BKOPS is enabled in the device by default. The disable of BKOPS is supported only when the driver supports runtime suspend/resume operations as the runtime PM framework provides a way to determine the device idleness and hence BKOPS can be managed effectively. During runtime resume the BKOPS is disabled to reduce latency and during runtime suspend the BKOPS is enabled to allow device to carry out idle time BKOPS. In some cases where the BKOPS is disabled during runtime resume and due to continuous data transfers the runtime suspend is not triggered, the BKOPS is enabled when the device raises a level-2 exception (outstanding operations - performance impact). Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 25 - drivers/scsi/ufs/ufshcd.c | 337 + drivers/scsi/ufs/ufshcd.h | 10 ++ 3 files changed, 371 insertions(+), 1 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 5484c59..24e589c 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -107,7 +107,29 @@ enum { /* Flag idn for Query Requests*/ enum flag_idn { - QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_BKOPS_EN = 0x04, +}; + +/* Attribute idn for Query requests */ +enum attr_idn { + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + QUERY_ATTR_IDN_EE_STATUS= 0x0E, +}; + +/* Exception event mask values */ +enum { + MASK_EE_STATUS = 0x, + MASK_EE_URGENT_BKOPS= (1 << 2), +}; + +/* Background operation status */ +enum { + BKOPS_STATUS_NO_OP = 0x0, + BKOPS_STATUS_NON_CRITICAL= 0x1, + BKOPS_STATUS_PERF_IMPACT = 0x2, + BKOPS_STATUS_CRITICAL= 0x3, }; /* UTP QUERY Transaction Specific Fields OpCode */ @@ -156,6 +178,7 @@ enum { MASK_TASK_RESPONSE = 0xFF00, MASK_RSP_UPIU_RESULT= 0x, MASK_QUERY_DATA_SEG_LEN = 0x, + MASK_RSP_EXCEPTION_EVENT = 0x1, }; /* Task management service response */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index f8278be..ac323a8 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -300,6 +300,21 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr) } /** + * ufshcd_is_exception_event - Check if the device raised an exception event + * @ucd_rsp_ptr: pointer to response UPIU + * + * The function checks if the device raised an exception event indicated in + * the Device Information field of response UPIU. + * + * Returns true if exception is raised, false otherwise. + */ +static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr) +{ + return be32_to_cpu(ucd_rsp_ptr->header.dword_2) & + MASK_RSP_EXCEPTION_EVENT ? true : false; +} + +/** * ufshcd_config_int_aggr - Configure interrupt aggregation values. * Currently there is no use case where we want to configure * interrupt aggregation dynamically. So to configure interrupt @@ -1206,6 +1221,86 @@ out_no_mem: } /** + * ufshcd_query_attr - Helper function for composing attribute requests + * hba: per-adapter instance + * opcode: attribute opcode + * idn: attribute idn to access + * index: index field + * selector: selector field + * attr_val: the attribute value after the query request completes + * + * Returns 0 for success, non-zero in case of failure +*/ +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) +{ + struct ufs_query_req *query; + struct ufs_query_res *response; + int err = -ENOMEM; + + if (!attr_val) { + dev_err(hba->dev, "%s: attribute value required for write request\n", + __func__); + err = -EINVAL; + goto out; + } + + query = kzalloc(sizeof(struct ufs_query_req), GFP_KERNEL); + if (!query) { + dev_err(hba->dev, + "%s: Failed allocating ufs_query_req instance\n", + __func__); + goto out; + } + + response = kzalloc(sizeof(struct ufs_query_res), GFP_KERNEL); + if (!response) { + dev_err(hba->dev, + "%s: Failed allocating ufs_query_re
[PATCH V2 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
From: Dolev Raviv Allow UFS device to complete its initialization and accept SCSI commands by setting fDeviceInit flag. The device may take time for this operation and hence the host should poll until fDeviceInit flag is toggled to zero. This step is mandated by UFS device specification for device initialization completion. Signed-off-by: Dolev Raviv Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufs.h| 88 +- drivers/scsi/ufs/ufshcd.c | 292 - drivers/scsi/ufs/ufshcd.h | 14 ++ drivers/scsi/ufs/ufshci.h |2 +- 4 files changed, 390 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 4737fad..5484c59 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -43,6 +43,8 @@ #define GENERAL_UPIU_REQUEST_SIZE 32 #define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \ (GENERAL_UPIU_REQUEST_SIZE)) +#define QUERY_OSF_SIZE ((GENERAL_UPIU_REQUEST_SIZE) - \ + (sizeof(struct utp_upiu_header))) #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ @@ -68,7 +70,7 @@ enum { UPIU_TRANSACTION_COMMAND= 0x01, UPIU_TRANSACTION_DATA_OUT = 0x02, UPIU_TRANSACTION_TASK_REQ = 0x04, - UPIU_TRANSACTION_QUERY_REQ = 0x26, + UPIU_TRANSACTION_QUERY_REQ = 0x16, }; /* UTP UPIU Transaction Codes Target to Initiator */ @@ -97,8 +99,19 @@ enum { UPIU_TASK_ATTR_ACA = 0x03, }; -/* UTP QUERY Transaction Specific Fields OpCode */ +/* UPIU Query request function */ enum { + UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01, + UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81, +}; + +/* Flag idn for Query Requests*/ +enum flag_idn { + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, +}; + +/* UTP QUERY Transaction Specific Fields OpCode */ +enum query_opcode { UPIU_QUERY_OPCODE_NOP = 0x0, UPIU_QUERY_OPCODE_READ_DESC = 0x1, UPIU_QUERY_OPCODE_WRITE_DESC= 0x2, @@ -110,6 +123,21 @@ enum { UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, }; +/* Query response result code */ +enum { + QUERY_RESULT_SUCCESS= 0x00, + QUERY_RESULT_NOT_READABLE = 0xF6, + QUERY_RESULT_NOT_WRITEABLE = 0xF7, + QUERY_RESULT_ALREADY_WRITTEN= 0xF8, + QUERY_RESULT_INVALID_LENGTH = 0xF9, + QUERY_RESULT_INVALID_VALUE = 0xFA, + QUERY_RESULT_INVALID_SELECTOR = 0xFB, + QUERY_RESULT_INVALID_INDEX = 0xFC, + QUERY_RESULT_INVALID_IDN= 0xFD, + QUERY_RESULT_INVALID_OPCODE = 0xFE, + QUERY_RESULT_GENERAL_FAILURE= 0xFF, +}; + /* UTP Transfer Request Command Type (CT) */ enum { UPIU_COMMAND_SET_TYPE_SCSI = 0x0, @@ -127,6 +155,7 @@ enum { MASK_SCSI_STATUS= 0xFF, MASK_TASK_RESPONSE = 0xFF00, MASK_RSP_UPIU_RESULT= 0x, + MASK_QUERY_DATA_SEG_LEN = 0x, }; /* Task management service response */ @@ -160,13 +189,40 @@ struct utp_upiu_cmd { }; /** + * struct utp_upiu_query - upiu request buffer structure for + * query request. + * @opcode: command to perform B-0 + * @idn: a value that indicates the particular type of data B-1 + * @index: Index to further identify data B-2 + * @selector: Index to further identify data B-3 + * @reserved_osf: spec reserved field B-4,5 + * @length: number of descriptor bytes to read/write B-6,7 + * @value: Attribute value to be written DW-6 + * @reserved: spec reserved DW-7,8 + */ +struct utp_upiu_query { + u8 opcode; + u8 idn; + u8 index; + u8 selector; + u16 reserved_osf; + u16 length; + u32 value; + u32 reserved[2]; +}; + +/** * struct utp_upiu_req - general upiu request structure * @header:UPIU header structure DW-0 to DW-2 * @sc: fields structure for scsi command + * @qr: fields structure for query request */ struct utp_upiu_req { struct utp_upiu_header header; - struct utp_upiu_cmd sc; + union { + struct utp_upiu_cmd sc; + struct utp_upiu_query qr; + }; }; /** @@ -187,10 +243,14 @@ struct utp_cmd_rsp { * struct utp_upiu_rsp - general upiu response structure * @header: UPIU header structure DW-0 to DW-2 * @sr: fields structure for scsi command + * @qr: fields structure for query request */ struct utp_upiu_rsp { struct utp_upiu_header header; - struct utp_cmd_rsp sr; + union { + struct utp_cmd_rsp sr; + struct utp_upiu_query qr; + }; }; /** @@ -223,4 +283,24 @@ struct utp_upiu_task_rsp { u32 reserved[3]; }; +/** + * struct ufs_query_req - pa
[PATCH V2 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
As part of device initialization sequence, sending NOP OUT UPIU and waiting for NOP IN UPIU response is mandatory. This confirms that the device UFS Transport (UTP) layer is functional and the host can configure the device with further commands. Add support for sending NOP OUT UPIU to check the device connection path and test whether the UTP layer on the device side is functional during initialization. A tag is acquired from the SCSI tag map space in order to send the device management command. When the tag is acquired by internal command the scsi command is rejected with host busy flag in order to requeue the request. To avoid frequent collisions between internal commands and scsi commands the device management command tag is allocated in the opposite direction w.r.t block layer tag allocation. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Dolev Raviv --- drivers/scsi/ufs/ufs.h| 43 +++- drivers/scsi/ufs/ufshcd.c | 596 + drivers/scsi/ufs/ufshcd.h | 29 ++- 3 files changed, 552 insertions(+), 116 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 139bc06..4737fad 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -36,10 +36,16 @@ #ifndef _UFS_H #define _UFS_H +#include +#include + #define MAX_CDB_SIZE 16 +#define GENERAL_UPIU_REQUEST_SIZE 32 +#define UPIU_HEADER_DATA_SEGMENT_MAX_SIZE ((ALIGNED_UPIU_SIZE) - \ + (GENERAL_UPIU_REQUEST_SIZE)) #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ - ((byte3 << 24) | (byte2 << 16) |\ + cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ (byte1 << 8) | (byte0)) /* @@ -73,6 +79,7 @@ enum { UPIU_TRANSACTION_TASK_RSP = 0x24, UPIU_TRANSACTION_READY_XFER = 0x31, UPIU_TRANSACTION_QUERY_RSP = 0x36, + UPIU_TRANSACTION_REJECT_UPIU= 0x3F, }; /* UPIU Read/Write flags */ @@ -110,6 +117,12 @@ enum { UPIU_COMMAND_SET_TYPE_QUERY = 0x2, }; +/* UTP Transfer Request Command Offset */ +#define UPIU_COMMAND_TYPE_OFFSET 28 + +/* Offset of the response code in the UPIU header */ +#define UPIU_RSP_CODE_OFFSET 8 + enum { MASK_SCSI_STATUS= 0xFF, MASK_TASK_RESPONSE = 0xFF00, @@ -138,26 +151,32 @@ struct utp_upiu_header { /** * struct utp_upiu_cmd - Command UPIU structure - * @header: UPIU header structure DW-0 to DW-2 * @data_transfer_len: Data Transfer Length DW-3 * @cdb: Command Descriptor Block CDB DW-4 to DW-7 */ struct utp_upiu_cmd { - struct utp_upiu_header header; u32 exp_data_transfer_len; u8 cdb[MAX_CDB_SIZE]; }; /** - * struct utp_upiu_rsp - Response UPIU structure - * @header: UPIU header DW-0 to DW-2 + * struct utp_upiu_req - general upiu request structure + * @header:UPIU header structure DW-0 to DW-2 + * @sc: fields structure for scsi command + */ +struct utp_upiu_req { + struct utp_upiu_header header; + struct utp_upiu_cmd sc; +}; + +/** + * struct utp_cmd_rsp - Response UPIU structure * @residual_transfer_count: Residual transfer count DW-3 * @reserved: Reserved double words DW-4 to DW-7 * @sense_data_len: Sense data length DW-8 U16 * @sense_data: Sense data field DW-8 to DW-12 */ -struct utp_upiu_rsp { - struct utp_upiu_header header; +struct utp_cmd_rsp { u32 residual_transfer_count; u32 reserved[4]; u16 sense_data_len; @@ -165,6 +184,16 @@ struct utp_upiu_rsp { }; /** + * struct utp_upiu_rsp - general upiu response structure + * @header: UPIU header structure DW-0 to DW-2 + * @sr: fields structure for scsi command + */ +struct utp_upiu_rsp { + struct utp_upiu_header header; + struct utp_cmd_rsp sr; +}; + +/** * struct utp_upiu_task_req - Task request UPIU structure * @header - UPIU header structure DW0 to DW-2 * @input_param1: Input parameter 1 DW-3 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 431ddb2..d613ae4 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -43,6 +43,11 @@ /* UIC command timeout, unit: ms */ #define UIC_CMD_TIMEOUT500 +/* NOP OUT retries waiting for NOP IN response */ +#define NOP_OUT_RETRIES10 +/* Timeout after 30 msecs if NOP OUT hangs without response */ +#define NOP_OUT_TIMEOUT30 /* msecs */ + enum { UFSHCD_MAX_CHANNEL = 0, UFSHCD_MAX_ID = 1, @@ -71,6 +76,47 @@ enum { INT_AGGR_CONFIG, }; +/* + * ufshcd_wait_for_register - wait for register value to change + * @hba - per-adapter interface + * @reg - mmio register offset + * @mask - mask to apply to read register value + * @val - wait condition + * @interval_us - polling interval in microsecs + * @timeout_ms - timeout in millisecs + * + * Returns final register value after iteration + */ +static u32 ufshcd_wa
[PATCH V2 0/2] Add suport for internal request (NOP and Query Request)
This patch series replace the previous Query Request and NOP patches: [PATCH 1/8] scsi: ufs: add support for query [PATCH 6/8] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH 7/8] scsi: ufs: Set fDeviceInit flag to initiate device initialization Major difference - Sending the query request via the SCSI vendor specific command can cause a deadlock in case the SCSI command queue is blocked and we would like to send a query request (for example fDeviceInit in case of re-initialization). In addition, usage of a vendor specific SCSI command for UFS can cause future conflicts if this vendor specific command will be allocated for a different usage. The new patch series gets an internal tag for NOP and query requests and do not involve the SCSI layer in UFS specific requests transfers. This design also resolves the possible deadlock mentioned above. Depends on: [PATCH 01/10] scsi: ufs: wrap the i/o access operations [PATCH 02/10] scsi: ufs: amend interrupt configuration [PATCH 03/10] scsi: ufs: remove version check before IS reg clear [PATCH 04/10] scsi: ufs: rework link start-up process [PATCH 05/10] scsi: ufs: Fix the response UPIU length setting [PATCH 06/10] scsi: ufs: use devres functions for ufshcd [PATCH 07/10] ufshcd-pltfrm: add missing empty slot in ufs_of_match[] [PATCH 08/10] ufs: fix register address in UIC error interrupt handling [PATCH 09/10] ufshcd-pltfrm: remove unnecessary dma_set_coherent_mask() call [PATCH 10/10] ufs: fix DMA mask setting Changes from v1 - Allocate a tag for device management commands dynamically instead of reserving tag[MAX_QUEUE - 1]. - Changed the "internal_cmd" naming to "dev_cmd" to avoid confusion with other type of internal commands other than NOP and QUERY. Dolev Raviv (1): scsi: ufs: Set fDeviceInit flag to initiate device initialization Sujit Reddy Thumma (1): scsi: ufs: Add support for sending NOP OUT UPIU drivers/scsi/ufs/ufs.h| 127 ++- drivers/scsi/ufs/ufshcd.c | 886 +++-- drivers/scsi/ufs/ufshcd.h | 43 ++- drivers/scsi/ufs/ufshci.h |2 +- 4 files changed, 939 insertions(+), 119 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/2] scsi: ufs: Add support for host assisted background operations
On 6/26/2013 11:06 PM, Santosh Y wrote: +/** > * ufshcd_memory_alloc - allocate memory for host memory space data >structures > * @hba: per adapter instance > * >@@ -1803,6 +1904,9 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, >struct ufshcd_lrb *lrbp) > */ > scsi_status = result & MASK_SCSI_STATUS; > result = ufshcd_scsi_cmd_status(lrbp, >scsi_status); >+ >+ if (ufshcd_is_exception_event(lrbp->ucd_rsp_ptr)) ^ This condition will not satisfy until a runtime suspend/resume cycle completes. Is there any specific reason it is only being enabled in runtime-resume routine? As the fBackgroundOpsEn = 1 by default, shouldn't the sequence be 1. ufshcd_disable_auto_bkops() 2. ufshcd_enable_ee() during the startup. According to the specification, the host should put in all measures to ensure that the device doesn't enter into URGENT_BKOPS need because the device may not operate optimally in that case. If your sequence is used then we expect a case where URGENT_BKOPS is needed if the runtime PM is disabled or prohibited soon after startup. We wouldn't want to disable auto bkops by default without knowing that the runtime PM is enabled, since the device/host/software idleness is determined by runtime PM operation as explained in the commit text. -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/4] scsi: ufs: Fix broken task management command implementation
On 6/27/2013 4:49 PM, Santosh Y wrote: >+ spin_lock_irqsave(host->host_lock, flags); > task_req_descp = hba->utmrdl_base_addr; > task_req_descp += free_slot; > >@@ -2353,38 +2387,39 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba, > (struct utp_upiu_task_req *) task_req_descp->task_req_upiu; > task_req_upiup->header.dword_0 = > UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0, >- lrbp->lun, lrbp->task_tag); >+ lun_id, free_slot); Actually it still doesn't fix the problem. The*task tag* used here should be unique across the SCSI/Query and Task Managment UPIUs. I am sorry, I didn't get that. Why should it be unique across the SCSI/Query? For example, if a machine supports 32 request slots and 8 task management slots, then the task management command tag can be anything out of 8 slots. -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
On 6/13/2013 10:03 AM, Sujit Reddy Thumma wrote: static struct scsi_host_template ufshcd_driver_template = { @@ -1771,8 +2064,8 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, /* Configure LRB */ ufshcd_host_memory_configure(hba); - host->can_queue = hba->nutrs; - host->cmd_per_lun = hba->nutrs; + host->can_queue = SCSI_CMD_QUEUE_SIZE; I don't think this is appropriate. Reserving a slot exclusively for query/DM requests is not optimal. can_queue should be changed dynamically, scsi_adjust_queue_depth() maybe? The motivation to change the design for this patch is that routing query command through SCSI layer raised problems when we are trying to improve the fatal error handling as we need to block the SCSI layer and recover the link. Hence, the need to have the query/DM command send as internal commands. Which probably makes sense. One possible option was to look for a free command slot whenever we try to send an internal command, but getting a free slot is not always guaranteed. Even if we get hold of a tag, there is no way we can explain this to SCSI/block layer that particular tag is in use internally (case where normal query requests are sent in tandem with SCSI requests). In which case, other option is to use tag[31] as you have said on need basis and change the queue depth to 31. This again has problems - one changing queue depth doesn't take effect immediately but for the next command. Second, if the command in tag[31] is the root cause of the fatal error and is stuck, then the internal command has to wait forever (until scsi timesout) to plan recovery. Considering, all these factors it is better to reserve a tag and use it for internal commands instead of playing around with the tags internally without/partially informing SCSI/block layers. I am open for discussion, if there are any suggestions to handle such LLD requirements in the SCSI layer optimally. Coming to how optimal is to work with 31 slots instead of h/w defined 32 is something which we can answer when we have true multi queueing. AFAIK, there may not exist real-world applications which utilize full 32 tags at particular instant. SATA AHCI controller driver which is ubiquitous in modern systems also reserves a slot for internal commands which is used only in case of error handling and AFAIK, no one has ever reported performance problems with this (its about 7 years the commit to reserve a slot is merged into Linux tree). I hope this explains the intent. Please let me know what do you think. -- Regards, Sujit -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 1/4] scsi: ufs: Fix broken task management command implementation
Currently, sending Task Management (TM) command to the card might be broken in some scenarios as listed below: - If there are more than 8 TM commands the implementation returns error to the caller. Fix: Wait for one of the slots to be emptied and send the command. - Sometimes it is necessary for the caller to know the TM service response code to determine the task status. Fix: Propogate the service response to the caller. - If the TM command times out no proper error recovery is implemented. Fix: Clear the command in the controller door-bell register, so that further commands for the same slot don't fail. - While preparing the TM command descriptor the tag used should be the free slot of TM command and not the task tag of the command which the TM command is trying to manage. Fix: Use free slot tag instead of task tag of SCSI command - Since the TM command involves H/W communication, abruptly ending the request on kill interrupt signal might cause h/w malfunction. Fix: Wait for hardware completion interrupt with TASK_UNINTERRUPTIBLE set. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 174 + drivers/scsi/ufs/ufshcd.h |6 +- 2 files changed, 117 insertions(+), 63 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index aa1f11e..7d043f4 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -53,6 +53,9 @@ /* Query request timeout */ #define QUERY_REQ_TIMEOUT 30 /* msec */ +/* Task management command timeout */ +#define TM_CMD_TIMEOUT 100 /* msecs */ + #define SCSI_CMD_QUEUE_SIZE (hba->nutrs - 1) /* Reserved tag for internal commands */ #define INTERNAL_CMD_TAG (hba->nutrs - 1) @@ -188,13 +191,32 @@ ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp) /** * ufshcd_get_tm_free_slot - get a free slot for task management request * @hba: per adapter instance + * @free_slot: pointer to variable with available slot value * - * Returns maximum number of task management request slots in case of - * task management queue full or returns the free slot number + * Get a free tag and lock it until ufshcd_put_tm_slot() is called. + * Returns 0 if free slot is not available, else return 1 with tag value + * in @free_slot. */ -static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) +static bool ufshcd_get_tm_free_slot(struct ufs_hba *hba, int *free_slot) +{ + int tag; + + do { + tag = find_first_zero_bit( + &hba->outstanding_tasks, hba->nutmrs); + if (tag >= hba->nutmrs) + return false; + } while (test_and_set_bit_lock(tag, &hba->outstanding_tasks)); + + if (free_slot) + *free_slot = tag; + + return true; +} + +static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) { - return find_first_zero_bit(&hba->outstanding_tasks, hba->nutmrs); + clear_bit_unlock(slot, &hba->outstanding_tasks); } /** @@ -1745,10 +1767,11 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * ufshcd_task_req_compl - handle task management request completion * @hba: per adapter instance * @index: index of the completed request + * @resp: task management service response * - * Returns SUCCESS/FAILED + * Returns non-zero value on error, zero on success */ -static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index, u8 *resp) { struct utp_task_req_desc *task_req_descp; struct utp_upiu_task_rsp *task_rsp_upiup; @@ -1758,9 +1781,6 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) spin_lock_irqsave(hba->host->host_lock, flags); - /* Clear completed tasks from outstanding_tasks */ - __clear_bit(index, &hba->outstanding_tasks); - task_req_descp = hba->utmrdl_base_addr; ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]); @@ -1769,19 +1789,15 @@ static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index) task_req_descp[index].task_rsp_upiu; task_result = be32_to_cpu(task_rsp_upiup->header.dword_1); task_result = ((task_result & MASK_TASK_RESPONSE) >> 8); - - if (task_result != UPIU_TASK_MANAGEMENT_FUNC_COMPL && - task_result != UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) - task_result = FAILED; - else - task_result = SUCCESS; + if (resp) + *resp = (u8)task_result; } else { - task_result = FAILED; - dev_err(hba->dev, - "trc: Invalid ocs = %x\n", ocs_value); + dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", +
[PATCH 4/4] scsi: ufs: Improve UFS fatal error handling
Error handling in UFS driver is broken and resets the host controller for fatal errors without re-initialization. Correct the fatal error handling sequence according to UFS Host Controller Interface (HCI) v1.1 specification. o Upon determining fatal error condition the host controller may hang forever until a reset is applied, so just retrying the command doesn't work without a reset. So, the reset is applied in the driver context in a separate work and SCSI mid-layer isn't informed until reset is applied. o Processed requests which are completed without error are reported to SCSI layer as successful and any pending commands that are not started yet or are not cause of the error are re-queued into scsi midlayer queue. For the command that caused error, host controller or device is reset and DID_ERROR is returned for command retry after applying reset. o SCSI is informed about the expected Unit-Attentioni exception from the device for the immediate command after a reset so that the SCSI layer take necessary steps to establish communication with the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 348 +++-- drivers/scsi/ufs/ufshcd.h |2 + drivers/scsi/ufs/ufshci.h | 19 ++- 3 files changed, 293 insertions(+), 76 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index e368bb0..cca774e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -84,6 +84,14 @@ enum { UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; +/* UFSHCD UIC layer error flags */ +enum { + UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */ + UFSHCD_UIC_NL_ERROR = (1 << 1), /* Network layer error */ + UFSHCD_UIC_TL_ERROR = (1 << 2), /* Transport Layer error */ + UFSHCD_UIC_DME_ERROR = (1 << 3), /* DME error */ +}; + /* Interrupt configuration options */ enum { UFSHCD_INT_DISABLE, @@ -112,6 +120,7 @@ enum { static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); +static int ufshcd_reset_and_restore(struct ufs_hba *hba); /* * ufshcd_wait_for_register - wait for register value to change @@ -1570,9 +1579,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - if (hba->ufshcd_state == UFSHCD_STATE_RESET) - scsi_unblock_requests(hba->host); - out: return err; } @@ -1698,65 +1704,6 @@ static int ufshcd_validate_dev_connection(struct ufs_hba *hba) } /** - * ufshcd_do_reset - reset the host controller - * @hba: per adapter instance - * - * Returns SUCCESS/FAILED - */ -static int ufshcd_do_reset(struct ufs_hba *hba) -{ - struct ufshcd_lrb *lrbp; - unsigned long flags; - int tag; - - /* block commands from midlayer */ - scsi_block_requests(hba->host); - - spin_lock_irqsave(hba->host->host_lock, flags); - hba->ufshcd_state = UFSHCD_STATE_RESET; - - /* send controller to reset state */ - ufshcd_hba_stop(hba); - spin_unlock_irqrestore(hba->host->host_lock, flags); - - /* abort outstanding commands */ - for (tag = 0; tag < SCSI_CMD_QUEUE_SIZE; tag++) { - if (test_bit(tag, &hba->outstanding_reqs)) { - lrbp = &hba->lrb[tag]; - if (lrbp->cmd) { - scsi_dma_unmap(lrbp->cmd); - lrbp->cmd->result = DID_RESET << 16; - lrbp->cmd->scsi_done(lrbp->cmd); - lrbp->cmd = NULL; - } - } - } - - /* complete internal command */ - if (hba->i_cmd.dev_cmd_complete) - complete(hba->i_cmd.dev_cmd_complete); - - /* clear outstanding request/task bit maps */ - hba->outstanding_reqs = 0; - hba->outstanding_tasks = 0; - - /* Host controller enable */ - if (ufshcd_hba_enable(hba)) { - dev_err(hba->dev, - "Reset: Controller initialization failed\n"); - return FAILED; - } - - if (ufshcd_link_startup(hba)) { - dev_err(hba->dev, - "Reset: Link start-up failed\n"); - return FAILED; - } - - return SUCCESS; -} - -/** * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * @@ -1773,6 +1720,9 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) sdev->use_10_for_ms = 1; scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + /* allow SCSI layer to restart the device in case of errors */ + sdev->allow_restart = 1; + /*
[PATCH 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command. It can happen that when the abort command is issued, the device doesn't have the command pending but just before the command is cleared in controller the command is comitted to the device by h/w. In this case, the command is still pending in the device but the host controller and s/w assume it is aborted which can confuse the device h/w and further operations might fail. To avoid this, query task presence in the device before sending abort task, task managment command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 71 ++-- 1 files changed, 55 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 7d043f4..d576df2 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2502,6 +2502,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd) * ufshcd_abort - abort a specific command * @cmd: SCSI command pointer * + * Abort the pending command in device by sending UFS_ABORT_TASK task management + * command, and in host controller by clearing the door-bell register. There can + * be race between controller sending the command to the device while abort is + * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is + * really issued and then try to abort it. + * * Returns SUCCESS/FAILED */ static int ufshcd_abort(struct scsi_cmnd *cmd) @@ -2510,7 +2516,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) struct ufs_hba *hba; unsigned long flags; unsigned int tag; - int err; + int err = 0; + int poll_cnt = 100; u8 resp; struct ufshcd_lrb *lrbp; @@ -2518,37 +2525,69 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - spin_lock_irqsave(host->host_lock, flags); - - /* check if command is still pending */ - if (!(test_bit(tag, &hba->outstanding_reqs))) { - err = FAILED; - spin_unlock_irqrestore(host->host_lock, flags); + /* If command is already aborted/completed, return SUCCESS */ + if (!(test_bit(tag, &hba->outstanding_reqs))) goto out; - } - spin_unlock_irqrestore(host->host_lock, flags); lrbp = &hba->lrb[tag]; + for (;;) { + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, + UFS_QUERY_TASK, &resp); + if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) { + /* cmd pending in the device */ + break; + } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) { + u32 reg; + + /* +* cmd not pending in the device, check if it is +* in transition. +*/ + reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); + if (reg & (1 << tag)) { + /* sleep for max. 2ms to stabilize */ + usleep_range(1000, 2000); + if (poll_cnt) { + poll_cnt--; + continue; + } + err = -EBUSY; + } + /* command completed already */ + goto out; + } else { + if (!err) + err = -EPERM; /* service response error */ + goto out; + } + } + err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, UFS_ABORT_TASK, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { - err = FAILED; + if (!err) + err = -EPERM; /* service response error */ goto out; - } else { - err = SUCCESS; } + err = ufshcd_clear_cmd(hba, tag); + if (err) + goto out; + scsi_dma_unmap(cmd); spin_lock_irqsave(host->host_lock, flags); - - /* clear the respective UTRLCLR register bit */ - ufshcd_utrl_clear(hba, tag); - __clear_bit(tag, &hba->outstanding_reqs); hba->lrb[tag].cmd = NULL; spin_unlock_irqrestore(host->host_lock, flags); out: + if (!err) { + err = SUCCESS; + } else { + dev_err(hba->dev, "%s: failed with err %d\n&quo
[PATCH 3/4] scsi: ufs: Fix device and host reset methods
As of now SCSI initiated error handling is broken because, the reset APIs don't try to bring back the device initialized and ready for further transfers. In case of timeouts, the scsi error handler takes care of handling aborts and resets. Improve the error handling in such scenario by resetting the device and host and re-initializing them in proper manner. Signed-off-by: Sujit Reddy Thumma --- drivers/scsi/ufs/ufshcd.c | 446 +++-- drivers/scsi/ufs/ufshcd.h |2 + 2 files changed, 391 insertions(+), 57 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d576df2..e368bb0 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -73,9 +73,15 @@ enum { /* UFSHCD states */ enum { - UFSHCD_STATE_OPERATIONAL, UFSHCD_STATE_RESET, UFSHCD_STATE_ERROR, + UFSHCD_STATE_OPERATIONAL, +}; + +/* UFSHCD error handling flags */ +enum { + UFSHCD_EH_HOST_RESET_PENDING = (1 << 0), + UFSHCD_EH_DEVICE_RESET_PENDING = (1 << 1), }; /* Interrupt configuration options */ @@ -91,6 +97,22 @@ enum { INT_AGGR_CONFIG, }; +#define ufshcd_set_device_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_set_host_reset_pending(h) \ + (h->eh_flags |= UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_device_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_host_reset_pending(h) \ + (h->eh_flags & UFSHCD_EH_HOST_RESET_PENDING) +#define ufshcd_clear_device_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_DEVICE_RESET_PENDING) +#define ufshcd_clear_host_reset_pending(h) \ + (h->eh_flags &= ~UFSHCD_EH_HOST_RESET_PENDING) + +static void ufshcd_tmc_handler(struct ufs_hba *hba); +static void ufshcd_async_scan(void *data, async_cookie_t cookie); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -879,9 +901,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) tag = cmd->request->tag; - if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) { + switch (hba->ufshcd_state) { + case UFSHCD_STATE_OPERATIONAL: + break; + case UFSHCD_STATE_RESET: err = SCSI_MLQUEUE_HOST_BUSY; goto out; + case UFSHCD_STATE_ERROR: + set_host_byte(cmd, DID_ERROR); + cmd->scsi_done(cmd); + goto out; + default: + dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n", + __func__, hba->ufshcd_state); + set_host_byte(cmd, DID_BAD_TARGET); + cmd->scsi_done(cmd); + break; } lrbp = &hba->lrb[tag]; @@ -1538,8 +1573,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); - hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - out: return err; } @@ -2229,6 +2262,106 @@ out: } /** + * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled + * @hba: per-adapter instance + */ +static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP) & 0x1; +} + +/** + * ufshcd_complete_pending_tasks - complete outstanding tasks + * @hba: per adapter instance + * + * Abort in-progress task management commands and wakeup + * waiting threads. + * + * Returns non-zero error value when failed to clear all the commands. + */ +static int ufshcd_complete_pending_tasks(struct ufs_hba *hba) +{ + u32 reg; + int err = 0; + unsigned long flags; + + if (!hba->outstanding_tasks) + goto out; + + /* Clear UTMRL only when run-stop is enabled */ + if (ufshcd_utmrl_is_rsr_enabled(hba)) + ufshcd_writel(hba, ~hba->outstanding_tasks, + REG_UTP_TASK_REQ_LIST_CLEAR); + + /* poll for max. 1 sec to clear door bell register by h/w */ + reg = ufshcd_wait_for_register(hba, + REG_UTP_TASK_REQ_DOOR_BELL, + hba->outstanding_tasks, 0, 1000, 1000); + if (reg & hba->outstanding_tasks) + err = -ETIMEDOUT; + + spin_lock_irqsave(hba->host->host_lock, flags); + /* complete commands that were cleared out */ + ufshcd_tmc_handler(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); +out:
[PATCH 0/4] scsi: ufs: Improve UFS error handling
The first patch fixes many issues with current task management handling in UFSHCD driver. Others improve error handling in various scenarios. These patches depends on: [PATCH 2/8] scsi: ufs: wrap the i/o access operations [PATCH 3/8] scsi: ufs: amend interrupt configuration [PATCH 4/8] scsi: ufs: remove version check before IS reg clear [PATCH 5/8] scsi: ufs: rework link start-up process [PATCH 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization [PATCH 1/2] scsi: ufs: Add support for host assisted background operations [PATCH 2/2] scsi: ufs: Add runtime PM helpers for UFS host driver Sujit Reddy Thumma (4): scsi: ufs: Fix broken task management command implementation scsi: ufs: Fix hardware race conditions while aborting a command scsi: ufs: Fix device and host reset methods scsi: ufs: Improve UFS fatal error handling drivers/scsi/ufs/ufshcd.c | 1009 - drivers/scsi/ufs/ufshcd.h | 10 +- drivers/scsi/ufs/ufshci.h | 19 +- 3 files changed, 841 insertions(+), 197 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/2] scsi: ufs: Add support to control UFS device background operations
Add host assisted background operations for UFS device and runtime PM helpers for ufshcd platform and pci glue drivers. The background operations are disabled during runtime resume and enabled when the device is idle and runtime suspended. These patches depends on: [PATCH 2/8] scsi: ufs: wrap the i/o access operations [PATCH 3/8] scsi: ufs: amend interrupt configuration [PATCH 4/8] scsi: ufs: remove version check before IS reg clear [PATCH 5/8] scsi: ufs: rework link start-up process [PATCH 1/2] scsi: ufs: Add support for sending NOP OUT UPIU [PATCH 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization Sujit Reddy Thumma (2): scsi: ufs: Add support for host assisted background operations scsi: ufs: Add runtime PM helpers for UFS host driver drivers/scsi/ufs/ufs.h | 25 +++- drivers/scsi/ufs/ufshcd-pci.c| 60 ++- drivers/scsi/ufs/ufshcd-pltfrm.c | 41 + drivers/scsi/ufs/ufshcd.c| 343 ++ drivers/scsi/ufs/ufshcd.h| 10 + 5 files changed, 472 insertions(+), 7 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation. -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html