[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
[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 sthu...@codeaurora.org Signed-off-by: Dolev Raviv dra...@codeaurora.org --- 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 linux/mutex.h +#include linux/types.h + #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
[PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
From: Dolev Raviv dra...@codeaurora.org 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 dra...@codeaurora.org Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org --- 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
[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 sthu...@codeaurora.org --- 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_irqrestore(host-host_lock, flags); @@ -2600,6 +2633,13 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
[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 sthu...@codeaurora.org --- 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_res instance\n, + __func__); +
[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 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 sthu...@codeaurora.org --- 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_irqrestore(hba-host-host_lock, flags); +out: + if (err) + dev_err(hba-dev, %s: failed, still pending = 0x%.8x\n, +
[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 sthu...@codeaurora.org --- 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) tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); hba-tm_condition =
[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 sthu...@codeaurora.org --- 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 linux/pci.h +#include linux/pm_runtime.h #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 linux/platform_device.h +#include linux/pm_runtime.h #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
[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 sthu...@codeaurora.org --- 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 */ + 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 @@
Re: [PATCH V3 2/2] scsi: ufs: Set fDeviceInit flag to initiate device initialization
Tested-by: Maya Erez me...@codeaurora.org From: Dolev Raviv dra...@codeaurora.org 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 dra...@codeaurora.org Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org --- 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; +
Re: [PATCH V3 1/2] scsi: ufs: Add support for sending NOP OUT UPIU
Tested-by: Maya Erez me...@codeaurora.org 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 sthu...@codeaurora.org Signed-off-by: Dolev Raviv dra...@codeaurora.org --- 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 linux/mutex.h +#include linux/types.h + #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_TIMEOUT 500 +/* 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 + *
Re: [PATCH V3 1/2] scsi: ufs: Add support for host assisted background operations
Tested-by: Maya Erez me...@codeaurora.org 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 sthu...@codeaurora.org --- 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:
Re: [PATCH V3 2/2] scsi: ufs: Add runtime PM support for UFS host controller driver
Tested-by: Maya Erez me...@codeaurora.org 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 sthu...@codeaurora.org --- 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 linux/pci.h +#include linux/pm_runtime.h #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_resumeNULL #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_resumeNULL +#define ufshcd_pci_runtime_idle NULL +#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 linux/platform_device.h +#include linux/pm_runtime.h #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
Re: [PATCH V3 2/4] scsi: ufs: Fix hardware race conditions while aborting a command
Tested-by: Maya Erez me...@codeaurora.org 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 sthu...@codeaurora.org --- 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_irqrestore(host-host_lock, flags); @@ -2600,6 +2633,13 @@ static int ufshcd_abort(struct scsi_cmnd
Re: [PATCH V3 1/4] scsi: ufs: Fix broken task management command implementation
Tested-by: Maya Erez me...@codeaurora.org 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 sthu...@codeaurora.org --- 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)
Re: [PATCH V3 3/4] scsi: ufs: Fix device and host reset methods
Tested with error injection. Tested-by: Maya Erez me...@codeaurora.org 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 sthu...@codeaurora.org --- 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_irqrestore(hba-host-host_lock, flags); +out: + if (err) +
Re: [PATCH V3 4/4] scsi: ufs: Improve UFS fatal error handling
Tested with error injection. Tested-by: Maya Erez me...@codeaurora.org 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 sthu...@codeaurora.org --- 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 */ + sdev-allow_restart = 1; + /* * Inform SCSI Midlayer that the LUN queue depth is same
[PATCH v2 0/7] usb: phy: msm: Fixes and cleanups
From: Ivan T. Ivanov iiva...@mm-sol.com Changes since first version. * Extend commit messages a little bit. Following patches make initial cleanup of usb phy found in the Qualcomm chipsets. Changes include: * Build time error fix. * Move driver to Managed Device Resource allocation. * Checkpatch warnings and error fixes * Removed usage of global regulators variables. Ivan T. Ivanov (7): usb: phy: msm: Move mach depndend code to platform data usb: phy: msm: Migrate to Managed Device Resource allocation usb: phy: msm: Move regulator usage to managed resource allocation usb: phy: msm: Remove unnecessarily check for valid regulators. usb: phy: msm: Fix WARNING: quoted string split across lines usb: phy: msm: Fix WARNING: Prefer seq_puts to seq_printf usb: phy: msm: Lindent the code arch/arm/mach-msm/board-msm7x30.c | 35 arch/arm/mach-msm/board-qsd8x50.c | 34 drivers/usb/phy/phy-msm-usb.c | 384 ++--- include/linux/usb/msm_hsusb.h |5 + 4 files changed, 220 insertions(+), 238 deletions(-) -- 1.7.9.5 -- 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/7] usb: phy: msm: Move regulator usage to managed resource allocation
From: Ivan T. Ivanov iiva...@mm-sol.com This patch move global regulators variables to driver state structire and move allocation of the regulators to be devm managed. Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com --- drivers/usb/phy/phy-msm-usb.c | 111 +++-- include/linux/usb/msm_hsusb.h |3 ++ 2 files changed, 53 insertions(+), 61 deletions(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index cc37f5e..8289270 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -58,47 +58,32 @@ #define USB_PHY_VDD_DIG_VOL_MIN100 /* uV */ #define USB_PHY_VDD_DIG_VOL_MAX132 /* uV */ -static struct regulator *hsusb_3p3; -static struct regulator *hsusb_1p8; -static struct regulator *hsusb_vddcx; - static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) { int ret = 0; if (init) { - hsusb_vddcx = regulator_get(motg-phy.dev, HSUSB_VDDCX); - if (IS_ERR(hsusb_vddcx)) { - dev_err(motg-phy.dev, unable to get hsusb vddcx\n); - return PTR_ERR(hsusb_vddcx); - } - - ret = regulator_set_voltage(hsusb_vddcx, + ret = regulator_set_voltage(motg-vddcx, USB_PHY_VDD_DIG_VOL_MIN, USB_PHY_VDD_DIG_VOL_MAX); if (ret) { dev_err(motg-phy.dev, unable to set the voltage for hsusb vddcx\n); - regulator_put(hsusb_vddcx); return ret; } - ret = regulator_enable(hsusb_vddcx); - if (ret) { + ret = regulator_enable(motg-vddcx); + if (ret) dev_err(motg-phy.dev, unable to enable hsusb vddcx\n); - regulator_put(hsusb_vddcx); - } } else { - ret = regulator_set_voltage(hsusb_vddcx, 0, + ret = regulator_set_voltage(motg-vddcx, 0, USB_PHY_VDD_DIG_VOL_MAX); if (ret) dev_err(motg-phy.dev, unable to set the voltage for hsusb vddcx\n); - ret = regulator_disable(hsusb_vddcx); + ret = regulator_disable(motg-vddcx); if (ret) dev_err(motg-phy.dev, unable to disable hsusb vddcx\n); - - regulator_put(hsusb_vddcx); } return ret; @@ -109,59 +94,44 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) int rc = 0; if (init) { - hsusb_3p3 = regulator_get(motg-phy.dev, HSUSB_3p3); - if (IS_ERR(hsusb_3p3)) { - dev_err(motg-phy.dev, unable to get hsusb 3p3\n); - return PTR_ERR(hsusb_3p3); - } - - rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, + rc = regulator_set_voltage(motg-v3p3, USB_PHY_3P3_VOL_MIN, USB_PHY_3P3_VOL_MAX); if (rc) { dev_err(motg-phy.dev, unable to set voltage level for hsusb 3p3\n); - goto put_3p3; + return rc; } - rc = regulator_enable(hsusb_3p3); + rc = regulator_enable(motg-v3p3); if (rc) { dev_err(motg-phy.dev, unable to enable the hsusb 3p3\n); - goto put_3p3; - } - hsusb_1p8 = regulator_get(motg-phy.dev, HSUSB_1p8); - if (IS_ERR(hsusb_1p8)) { - dev_err(motg-phy.dev, unable to get hsusb 1p8\n); - rc = PTR_ERR(hsusb_1p8); - goto disable_3p3; + return rc; } - rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, + + rc = regulator_set_voltage(motg-v1p8, USB_PHY_1P8_VOL_MIN, USB_PHY_1P8_VOL_MAX); if (rc) { dev_err(motg-phy.dev, unable to set voltage level for hsusb 1p8\n); - goto put_1p8; + goto disable_3p3; } - rc = regulator_enable(hsusb_1p8); + rc = regulator_enable(motg-v1p8); if (rc) { dev_err(motg-phy.dev, unable to enable the hsusb 1p8\n); - goto put_1p8; + goto disable_3p3; } return 0; } - regulator_disable(hsusb_1p8); -put_1p8: - regulator_put(hsusb_1p8); + regulator_disable(motg-v1p8);
[PATCH v2 2/7] usb: phy: msm: Migrate to Managed Device Resource allocation
From: Ivan T. Ivanov iiva...@mm-sol.com Use managed device resources to clean up the probe/remove and get DT support for free. Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com --- drivers/usb/phy/phy-msm-usb.c | 78 +++-- 1 file changed, 20 insertions(+), 58 deletions(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index ab1b880..cc37f5e 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1397,13 +1397,14 @@ static int __init msm_otg_probe(struct platform_device *pdev) return -ENODEV; } - motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL); + motg = devm_kzalloc(pdev-dev, sizeof(*motg), GFP_KERNEL); if (!motg) { dev_err(pdev-dev, unable to allocate msm_otg\n); return -ENOMEM; } - motg-phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + motg-phy.otg = devm_kzalloc(pdev-dev, sizeof(*motg-phy.otg), +GFP_KERNEL); if (!motg-phy.otg) { dev_err(pdev-dev, unable to allocate msm_otg\n); return -ENOMEM; @@ -1413,18 +1414,16 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy = motg-phy; phy-dev = pdev-dev; - motg-phy_reset_clk = clk_get(pdev-dev, usb_phy_clk); + motg-phy_reset_clk = devm_clk_get(pdev-dev, usb_phy_clk); if (IS_ERR(motg-phy_reset_clk)) { dev_err(pdev-dev, failed to get usb_phy_clk\n); - ret = PTR_ERR(motg-phy_reset_clk); - goto free_motg; + return PTR_ERR(motg-phy_reset_clk); } - motg-clk = clk_get(pdev-dev, usb_hs_clk); + motg-clk = devm_clk_get(pdev-dev, usb_hs_clk); if (IS_ERR(motg-clk)) { dev_err(pdev-dev, failed to get usb_hs_clk\n); - ret = PTR_ERR(motg-clk); - goto put_phy_reset_clk; + return PTR_ERR(motg-clk); } clk_set_rate(motg-clk, 6000); @@ -1436,21 +1435,19 @@ static int __init msm_otg_probe(struct platform_device *pdev) * on pclk source */ if (motg-pdata-pclk_src_name) { - motg-pclk_src = clk_get(pdev-dev, + motg-pclk_src = devm_clk_get(pdev-dev, motg-pdata-pclk_src_name); if (IS_ERR(motg-pclk_src)) - goto put_clk; + return PTR_ERR(motg-pclk_src); clk_set_rate(motg-pclk_src, INT_MAX); clk_prepare_enable(motg-pclk_src); } else motg-pclk_src = ERR_PTR(-ENOENT); - - motg-pclk = clk_get(pdev-dev, usb_hs_pclk); + motg-pclk = devm_clk_get(pdev-dev, usb_hs_pclk); if (IS_ERR(motg-pclk)) { dev_err(pdev-dev, failed to get usb_hs_pclk\n); - ret = PTR_ERR(motg-pclk); - goto put_pclk_src; + return PTR_ERR(motg-pclk); } /* @@ -1458,30 +1455,27 @@ static int __init msm_otg_probe(struct platform_device *pdev) * clock is introduced to remove the dependency on AXI * bus frequency. */ - motg-core_clk = clk_get(pdev-dev, usb_hs_core_clk); + motg-core_clk = devm_clk_get(pdev-dev, usb_hs_core_clk); if (IS_ERR(motg-core_clk)) motg-core_clk = NULL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(pdev-dev, failed to get platform resource mem\n); - ret = -ENODEV; - goto put_core_clk; + return -ENODEV; } - motg-regs = ioremap(res-start, resource_size(res)); + motg-regs = devm_ioremap_resource(pdev-dev, res); if (!motg-regs) { dev_err(pdev-dev, ioremap failed\n); - ret = -ENOMEM; - goto put_core_clk; + return PTR_ERR(motg-regs); } dev_info(pdev-dev, OTG regs = %p\n, motg-regs); motg-irq = platform_get_irq(pdev, 0); - if (!motg-irq) { + if (motg-irq 0) { dev_err(pdev-dev, platform_get_irq failed\n); - ret = -ENODEV; - goto free_regs; + return motg-irq; } clk_prepare_enable(motg-clk); @@ -1490,7 +1484,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) ret = msm_hsusb_init_vddcx(motg, 1); if (ret) { dev_err(pdev-dev, hsusb vddcx configuration failed\n); - goto free_regs; + return ret; } ret = msm_hsusb_ldo_init(motg, 1); @@ -1512,7 +1506,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) INIT_WORK(motg-sm_work, msm_otg_sm_work); INIT_DELAYED_WORK(motg-chg_work, msm_chg_detect_work); - ret = request_irq(motg-irq, msm_otg_irq,
[PATCH v2 7/7] usb: phy: msm: Lindent the code
From: Ivan T. Ivanov iiva...@mm-sol.com Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com --- drivers/usb/phy/phy-msm-usb.c | 99 ++--- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 6d05085..111f454 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -45,18 +45,18 @@ #define ULPI_IO_TIMEOUT_USEC (10 * 1000) -#define USB_PHY_3P3_VOL_MIN305 /* uV */ -#define USB_PHY_3P3_VOL_MAX330 /* uV */ +#define USB_PHY_3P3_VOL_MIN305 /* uV */ +#define USB_PHY_3P3_VOL_MAX330 /* uV */ #define USB_PHY_3P3_HPM_LOAD 5 /* uA */ #define USB_PHY_3P3_LPM_LOAD 4000/* uA */ -#define USB_PHY_1P8_VOL_MIN180 /* uV */ -#define USB_PHY_1P8_VOL_MAX180 /* uV */ +#define USB_PHY_1P8_VOL_MIN180 /* uV */ +#define USB_PHY_1P8_VOL_MAX180 /* uV */ #define USB_PHY_1P8_HPM_LOAD 5 /* uA */ #define USB_PHY_1P8_LPM_LOAD 4000/* uA */ -#define USB_PHY_VDD_DIG_VOL_MIN100 /* uV */ -#define USB_PHY_VDD_DIG_VOL_MAX132 /* uV */ +#define USB_PHY_VDD_DIG_VOL_MIN100 /* uV */ +#define USB_PHY_VDD_DIG_VOL_MAX132 /* uV */ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) { @@ -64,8 +64,8 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) if (init) { ret = regulator_set_voltage(motg-vddcx, - USB_PHY_VDD_DIG_VOL_MIN, - USB_PHY_VDD_DIG_VOL_MAX); + USB_PHY_VDD_DIG_VOL_MIN, + USB_PHY_VDD_DIG_VOL_MAX); if (ret) { dev_err(motg-phy.dev, Cannot set vddcx voltage\n); return ret; @@ -73,15 +73,17 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) ret = regulator_enable(motg-vddcx); if (ret) - dev_err(motg-phy.dev, unable to enable hsusb vddcx\n); + dev_err(motg-phy.dev, + unable to enable hsusb vddcx\n); } else { ret = regulator_set_voltage(motg-vddcx, 0, - USB_PHY_VDD_DIG_VOL_MAX); + USB_PHY_VDD_DIG_VOL_MAX); if (ret) dev_err(motg-phy.dev, Cannot set vddcx voltage\n); ret = regulator_disable(motg-vddcx); if (ret) - dev_err(motg-phy.dev, unable to disable hsusb vddcx\n); + dev_err(motg-phy.dev, + unable to disable hsusb vddcx\n); } return ret; @@ -93,26 +95,28 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) if (init) { rc = regulator_set_voltage(motg-v3p3, USB_PHY_3P3_VOL_MIN, - USB_PHY_3P3_VOL_MAX); + USB_PHY_3P3_VOL_MAX); if (rc) { dev_err(motg-phy.dev, Cannot set v3p3 voltage\n); return rc; } rc = regulator_enable(motg-v3p3); if (rc) { - dev_err(motg-phy.dev, unable to enable the hsusb 3p3\n); + dev_err(motg-phy.dev, + unable to enable the hsusb 3p3\n); return rc; } rc = regulator_set_voltage(motg-v1p8, USB_PHY_1P8_VOL_MIN, - USB_PHY_1P8_VOL_MAX); + USB_PHY_1P8_VOL_MAX); if (rc) { dev_err(motg-phy.dev, Cannot set v1p8 voltage\n); goto disable_3p3; } rc = regulator_enable(motg-v1p8); if (rc) { - dev_err(motg-phy.dev, unable to enable the hsusb 1p8\n); + dev_err(motg-phy.dev, + unable to enable the hsusb 1p8\n); goto disable_3p3; } @@ -156,26 +160,26 @@ static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, int on) if (on) { ret = regulator_set_optimum_mode(motg-v1p8, - USB_PHY_1P8_HPM_LOAD); +USB_PHY_1P8_HPM_LOAD); if (ret 0) { pr_err(Could not set HPM for v1p8\n); return ret; } ret = regulator_set_optimum_mode(motg-v3p3, - USB_PHY_3P3_HPM_LOAD); +USB_PHY_3P3_HPM_LOAD); if (ret 0) {
[PATCH v2 5/7] usb: phy: msm: Fix WARNING: quoted string split across lines
From: Ivan T. Ivanov iiva...@mm-sol.com This fixes checkpatch.pl warnings. Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com --- drivers/usb/phy/phy-msm-usb.c | 33 +++-- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 0e7d7ab..41938e6 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -67,8 +67,7 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) USB_PHY_VDD_DIG_VOL_MIN, USB_PHY_VDD_DIG_VOL_MAX); if (ret) { - dev_err(motg-phy.dev, unable to set the voltage - for hsusb vddcx\n); + dev_err(motg-phy.dev, Cannot set vddcx voltage\n); return ret; } @@ -79,8 +78,7 @@ static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) ret = regulator_set_voltage(motg-vddcx, 0, USB_PHY_VDD_DIG_VOL_MAX); if (ret) - dev_err(motg-phy.dev, unable to set the voltage - for hsusb vddcx\n); + dev_err(motg-phy.dev, Cannot set vddcx voltage\n); ret = regulator_disable(motg-vddcx); if (ret) dev_err(motg-phy.dev, unable to disable hsusb vddcx\n); @@ -97,8 +95,7 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) rc = regulator_set_voltage(motg-v3p3, USB_PHY_3P3_VOL_MIN, USB_PHY_3P3_VOL_MAX); if (rc) { - dev_err(motg-phy.dev, unable to set voltage level - for hsusb 3p3\n); + dev_err(motg-phy.dev, Cannot set v3p3 voltage\n); return rc; } rc = regulator_enable(motg-v3p3); @@ -110,8 +107,7 @@ static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) rc = regulator_set_voltage(motg-v1p8, USB_PHY_1P8_VOL_MIN, USB_PHY_1P8_VOL_MAX); if (rc) { - dev_err(motg-phy.dev, unable to set voltage level - for hsusb 1p8\n); + dev_err(motg-phy.dev, Cannot set v1p8 voltage\n); goto disable_3p3; } rc = regulator_enable(motg-v1p8); @@ -144,8 +140,7 @@ static int msm_hsusb_config_vddcx(struct msm_otg *motg, int high) ret = regulator_set_voltage(motg-vddcx, min_vol, max_vol); if (ret) { - pr_err(%s: unable to set the voltage for regulator - HSUSB_VDDCX\n, __func__); + dev_err(motg-phy.dev, Cannot set vddcx voltage\n); return ret; } @@ -163,15 +158,13 @@ static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, int on) ret = regulator_set_optimum_mode(motg-v1p8, USB_PHY_1P8_HPM_LOAD); if (ret 0) { - pr_err(%s: Unable to set HPM of the regulator - HSUSB_1p8\n, __func__); + pr_err(Could not set HPM for v1p8\n); return ret; } ret = regulator_set_optimum_mode(motg-v3p3, USB_PHY_3P3_HPM_LOAD); if (ret 0) { - pr_err(%s: Unable to set HPM of the regulator - HSUSB_3p3\n, __func__); + pr_err(Could not set HPM for v3p3\n); regulator_set_optimum_mode(motg-v1p8, USB_PHY_1P8_LPM_LOAD); return ret; @@ -180,13 +173,11 @@ static int msm_hsusb_ldo_set_mode(struct msm_otg *motg, int on) ret = regulator_set_optimum_mode(motg-v1p8, USB_PHY_1P8_LPM_LOAD); if (ret 0) - pr_err(%s: Unable to set LPM of the regulator - HSUSB_1p8\n, __func__); + pr_err(Could not set LPM for v1p8\n); ret = regulator_set_optimum_mode(motg-v3p3, USB_PHY_3P3_LPM_LOAD); if (ret 0) - pr_err(%s: Unable to set LPM of the regulator - HSUSB_3p3\n, __func__); + pr_err(Could not set LPM for v3p3\n); } pr_debug(reg (%s)\n, on ? HPM : LPM); @@ -519,8 +510,7 @@ static int msm_otg_resume(struct msm_otg *motg) * PHY. USB state can not be restored. Re-insertion * of USB cable is the only way to get USB working. */
[PATCH v2 6/7] usb: phy: msm: Fix WARNING: Prefer seq_puts to seq_printf
From: Ivan T. Ivanov iiva...@mm-sol.com This fixes checkpatch.pl warnings. Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com --- drivers/usb/phy/phy-msm-usb.c |6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 41938e6..6d05085 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1204,13 +1204,13 @@ static int msm_otg_mode_show(struct seq_file *s, void *unused) switch (otg-phy-state) { case OTG_STATE_A_HOST: - seq_printf(s, host\n); + seq_puts(s, host\n); break; case OTG_STATE_B_PERIPHERAL: - seq_printf(s, peripheral\n); + seq_puts(s, peripheral\n); break; default: - seq_printf(s, none\n); + seq_puts(s, none\n); break; } -- 1.7.9.5 -- 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/7] usb: phy: msm: Move mach depndend code to platform data
From: Ivan T. Ivanov iiva...@mm-sol.com This patch fix compilation error and is an intermediate step before the addition of DeviceTree support for newer targets. Fix suggested here: https://lkml.org/lkml/2013/6/19/381 Cc: David Brown dav...@codeaurora.org Cc: Daniel Walker dwal...@fifo99.com Cc: Bryan Huntsman bry...@codeaurora.org Cc: Stephen Boyd sb...@codeaurora.org Signed-off-by: Ivan T. Ivanov iiva...@mm-sol.com --- arch/arm/mach-msm/board-msm7x30.c | 35 +++ arch/arm/mach-msm/board-qsd8x50.c | 34 +++ drivers/usb/phy/phy-msm-usb.c | 55 ++--- include/linux/usb/msm_hsusb.h |2 ++ 4 files changed, 85 insertions(+), 41 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index db3d8c0..4df7daa 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -31,6 +31,7 @@ #include asm/setup.h #include mach/board.h +#include mach/clk.h #include mach/msm_iomap.h #include mach/dma.h @@ -61,10 +62,44 @@ static int hsusb_phy_init_seq[] = { -1 }; +static int hsusb_phy_link_clk_reset(struct clk *link_clk, bool assert) +{ + int ret; + + if (assert) { + ret = clk_reset(link_clk, CLK_RESET_ASSERT); + if (ret) + pr_err(usb hs_clk assert failed\n); + } else { + ret = clk_reset(link_clk, CLK_RESET_DEASSERT); + if (ret) + pr_err(usb hs_clk deassert failed\n); + } + return ret; +} + +static int hsusb_phy_clk_reset(struct clk *phy_clk) +{ + int ret; + + ret = clk_reset(phy_clk, CLK_RESET_ASSERT); + if (ret) { + pr_err(usb phy clk assert failed\n); + return ret; + } + usleep_range(1, 12000); + ret = clk_reset(phy_clk, CLK_RESET_DEASSERT); + if (ret) + pr_err(usb phy clk deassert failed\n); + return ret; +} + static struct msm_otg_platform_data msm_otg_pdata = { .phy_init_seq = hsusb_phy_init_seq, .mode = USB_PERIPHERAL, .otg_control= OTG_PHY_CONTROL, + .phy_link_clk_reset = hsusb_phy_link_clk_reset, + .phy_phy_clk_reset = hsusb_phy_clk_reset, }; struct msm_gpiomux_config msm_gpiomux_configs[GPIOMUX_NGPIOS] = { diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c index f14a73d..d3d92ab 100644 --- a/arch/arm/mach-msm/board-qsd8x50.c +++ b/arch/arm/mach-msm/board-qsd8x50.c @@ -82,10 +82,44 @@ static int hsusb_phy_init_seq[] = { -1 }; +static int hsusb_phy_link_clk_reset(struct clk *link_clk, bool assert) +{ + int ret; + + if (assert) { + ret = clk_reset(link_clk, CLK_RESET_ASSERT); + if (ret) + pr_err(usb hs_clk assert failed\n); + } else { + ret = clk_reset(link_clk, CLK_RESET_DEASSERT); + if (ret) + pr_err(usb hs_clk deassert failed\n); + } + return ret; +} + +static int hsusb_phy_clk_reset(struct clk *phy_clk) +{ + int ret; + + ret = clk_reset(phy_clk, CLK_RESET_ASSERT); + if (ret) { + pr_err(usb phy clk assert failed\n); + return ret; + } + usleep_range(1, 12000); + ret = clk_reset(phy_clk, CLK_RESET_DEASSERT); + if (ret) + pr_err(usb phy clk deassert failed\n); + return ret; +} + static struct msm_otg_platform_data msm_otg_pdata = { .phy_init_seq = hsusb_phy_init_seq, .mode = USB_PERIPHERAL, .otg_control= OTG_PHY_CONTROL, + .phy_link_clk_reset = hsusb_phy_link_clk_reset, + .phy_phy_clk_reset = hsusb_phy_clk_reset, }; static struct platform_device *devices[] __initdata = { diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index d08f334..ab1b880 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -40,8 +40,6 @@ #include linux/usb/msm_hsusb_hw.h #include linux/regulator/consumer.h -#include mach/clk.h - #define MSM_USB_BASE (motg-regs) #define DRIVER_NAMEmsm_otg @@ -306,51 +304,20 @@ static void ulpi_init(struct msm_otg *motg) } } -static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) -{ - int ret; - - if (assert) { - ret = clk_reset(motg-clk, CLK_RESET_ASSERT); - if (ret) - dev_err(motg-phy.dev, usb hs_clk assert failed\n); - } else { - ret = clk_reset(motg-clk, CLK_RESET_DEASSERT); - if (ret) - dev_err(motg-phy.dev, usb hs_clk deassert failed\n); - } - return ret; -} - -static int msm_otg_phy_clk_reset(struct msm_otg *motg) -{ - int ret; - - ret =