]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/scsi/ufs/ufshcd.c
[SCSI] ufs: add operation for the uic power mode change
[mirror_ubuntu-artful-kernel.git] / drivers / scsi / ufs / ufshcd.c
index b743bd6fce6bf820750d38cc94a6701abb84173e..1788d9cc571bd73ec1201fa1f4766980352bb3a1 100644 (file)
 #include <linux/async.h>
 
 #include "ufshcd.h"
+#include "unipro.h"
 
 #define UFSHCD_ENABLE_INTRS    (UTP_TRANSFER_REQ_COMPL |\
                                 UTP_TASK_REQ_COMPL |\
+                                UIC_POWER_MODE |\
                                 UFSHCD_ERROR_MASK)
 /* UIC command timeout, unit: ms */
 #define UIC_CMD_TIMEOUT        500
 
+/* NOP OUT retries waiting for NOP IN response */
+#define NOP_OUT_RETRIES    10
+/* Timeout after 30 msecs if NOP OUT hangs without response */
+#define NOP_OUT_TIMEOUT    30 /* msecs */
+
+/* Query request retries */
+#define QUERY_REQ_RETRIES 10
+/* Query request timeout */
+#define QUERY_REQ_TIMEOUT 30 /* msec */
+
+/* Expose the flag value from utp_upiu_query.value */
+#define MASK_QUERY_UPIU_FLAG_LOC 0xFF
+
+/* Interrupt aggregation default timeout, unit: 40us */
+#define INT_AGGR_DEF_TO        0x02
+
 enum {
        UFSHCD_MAX_CHANNEL      = 0,
        UFSHCD_MAX_ID           = 1,
@@ -65,11 +83,39 @@ enum {
        UFSHCD_INT_CLEAR,
 };
 
-/* Interrupt aggregation options */
-enum {
-       INT_AGGR_RESET,
-       INT_AGGR_CONFIG,
-};
+/*
+ * ufshcd_wait_for_register - wait for register value to change
+ * @hba - per-adapter interface
+ * @reg - mmio register offset
+ * @mask - mask to apply to read register value
+ * @val - wait condition
+ * @interval_us - polling interval in microsecs
+ * @timeout_ms - timeout in millisecs
+ *
+ * Returns -ETIMEDOUT on error, zero on success
+ */
+static int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
+               u32 val, unsigned long interval_us, unsigned long timeout_ms)
+{
+       int err = 0;
+       unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+
+       /* ignore bits that we don't intend to wait on */
+       val = val & mask;
+
+       while ((ufshcd_readl(hba, reg) & mask) != val) {
+               /* wakeup within 50us of expiry */
+               usleep_range(interval_us, interval_us + 50);
+
+               if (time_after(jiffies, timeout)) {
+                       if ((ufshcd_readl(hba, reg) & mask) != val)
+                               err = -ETIMEDOUT;
+                       break;
+               }
+       }
+
+       return err;
+}
 
 /**
  * ufshcd_get_intr_mask - Get the interrupt bit mask
@@ -191,18 +237,25 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
 }
 
 /**
- * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
- * @ucd_rsp_ptr: pointer to response UPIU
+ * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
+ * @hba: Pointer to adapter instance
  *
- * This function checks the response UPIU for valid transaction type in
- * response field
- * Returns 0 on success, non-zero on failure
+ * This function gets UIC command argument3
+ * Returns 0 on success, non zero value on error
+ */
+static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
+{
+       return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
+}
+
+/**
+ * ufshcd_get_req_rsp - returns the TR response transaction type
+ * @ucd_rsp_ptr: pointer to response UPIU
  */
 static inline int
-ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
+ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
 {
-       return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
-                UPIU_TRANSACTION_RESPONSE) ? 0 : DID_ERROR << 16;
+       return be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24;
 }
 
 /**
@@ -218,31 +271,60 @@ ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
        return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
 }
 
+/*
+ * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
+ *                             from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * Return the data segment length.
+ */
+static inline unsigned int
+ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+       return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
+               MASK_RSP_UPIU_DATA_SEG_LEN;
+}
+
 /**
- * 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
- *             aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
- *             INT_AGGR_TIMEOUT_VALUE are used.
+ * 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_reset_intr_aggr - Reset interrupt aggregation values.
  * @hba: per adapter instance
- * @option: Interrupt aggregation option
  */
 static inline void
-ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+ufshcd_reset_intr_aggr(struct ufs_hba *hba)
 {
-       switch (option) {
-       case INT_AGGR_RESET:
-               ufshcd_writel(hba, INT_AGGR_ENABLE |
-                             INT_AGGR_COUNTER_AND_TIMER_RESET,
-                             REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-               break;
-       case INT_AGGR_CONFIG:
-               ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
-                             INT_AGGR_COUNTER_THRESHOLD_VALUE |
-                             INT_AGGR_TIMEOUT_VALUE,
-                             REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
-               break;
-       }
+       ufshcd_writel(hba, INT_AGGR_ENABLE |
+                     INT_AGGR_COUNTER_AND_TIMER_RESET,
+                     REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
+ * ufshcd_config_intr_aggr - Configure interrupt aggregation values.
+ * @hba: per adapter instance
+ * @cnt: Interrupt aggregation counter threshold
+ * @tmout: Interrupt aggregation timeout value
+ */
+static inline void
+ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout)
+{
+       ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
+                     INT_AGGR_COUNTER_THLD_VAL(cnt) |
+                     INT_AGGR_TIMEOUT_VAL(tmout),
+                     REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
 }
 
 /**
@@ -298,14 +380,69 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
 static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
 {
        int len;
-       if (lrbp->sense_buffer) {
-               len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
+       if (lrbp->sense_buffer &&
+           ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
+               len = be16_to_cpu(lrbp->ucd_rsp_ptr->sr.sense_data_len);
                memcpy(lrbp->sense_buffer,
-                       lrbp->ucd_rsp_ptr->sense_data,
+                       lrbp->ucd_rsp_ptr->sr.sense_data,
                        min_t(int, len, SCSI_SENSE_BUFFERSIZE));
        }
 }
 
+/**
+ * ufshcd_query_to_cpu() - formats the buffer to native cpu endian
+ * @response: upiu query response to convert
+ */
+static inline void ufshcd_query_to_cpu(struct utp_upiu_query *response)
+{
+       response->length = be16_to_cpu(response->length);
+       response->value = be32_to_cpu(response->value);
+}
+
+/**
+ * ufshcd_query_to_be() - formats the buffer to big endian
+ * @request: upiu query request to convert
+ */
+static inline void ufshcd_query_to_be(struct utp_upiu_query *request)
+{
+       request->length = cpu_to_be16(request->length);
+       request->value = cpu_to_be32(request->value);
+}
+
+/**
+ * ufshcd_copy_query_response() - Copy the Query Response and the data
+ * descriptor
+ * @hba: per adapter instance
+ * @lrb - pointer to local reference block
+ */
+static
+void ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+       struct ufs_query_res *query_res = &hba->dev_cmd.query.response;
+
+       /* Get the UPIU response */
+       query_res->response = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr) >>
+                       UPIU_RSP_CODE_OFFSET;
+
+       memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE);
+       ufshcd_query_to_cpu(&query_res->upiu_res);
+
+
+       /* Get the descriptor */
+       if (lrbp->ucd_rsp_ptr->qr.opcode == UPIU_QUERY_OPCODE_READ_DESC) {
+               u8 *descp = (u8 *)&lrbp->ucd_rsp_ptr +
+                               GENERAL_UPIU_REQUEST_SIZE;
+               u16 len;
+
+               /* data segment length */
+               len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) &
+                                               MASK_QUERY_DATA_SEG_LEN;
+
+               memcpy(hba->dev_cmd.query.descriptor, descp,
+                               min_t(u16, len, QUERY_DESC_MAX_SIZE));
+       }
+}
+
 /**
  * ufshcd_hba_capabilities - Read controller capabilities
  * @hba: per adapter instance
@@ -334,6 +471,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
                return false;
 }
 
+/**
+ * ufshcd_get_upmcrs - Get the power mode change request status
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the UPMCRS field of HCS register
+ * Returns value of UPMCRS field
+ */
+static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
+{
+       return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
+}
+
 /**
  * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
  * @hba: per adapter instance
@@ -518,77 +667,171 @@ static void ufshcd_disable_intr(struct ufs_hba *hba, u32 intrs)
        ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE);
 }
 
+/**
+ * ufshcd_prepare_req_desc_hdr() - Fills the requests header
+ * descriptor according to request
+ * @lrbp: pointer to local reference block
+ * @upiu_flags: flags required in the header
+ * @cmd_dir: requests data direction
+ */
+static void ufshcd_prepare_req_desc_hdr(struct ufshcd_lrb *lrbp,
+               u32 *upiu_flags, enum dma_data_direction cmd_dir)
+{
+       struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
+       u32 data_direction;
+       u32 dword_0;
+
+       if (cmd_dir == DMA_FROM_DEVICE) {
+               data_direction = UTP_DEVICE_TO_HOST;
+               *upiu_flags = UPIU_CMD_FLAGS_READ;
+       } else if (cmd_dir == DMA_TO_DEVICE) {
+               data_direction = UTP_HOST_TO_DEVICE;
+               *upiu_flags = UPIU_CMD_FLAGS_WRITE;
+       } else {
+               data_direction = UTP_NO_DATA_TRANSFER;
+               *upiu_flags = UPIU_CMD_FLAGS_NONE;
+       }
+
+       dword_0 = data_direction | (lrbp->command_type
+                               << UPIU_COMMAND_TYPE_OFFSET);
+       if (lrbp->intr_cmd)
+               dword_0 |= UTP_REQ_DESC_INT_CMD;
+
+       /* Transfer request descriptor header fields */
+       req_desc->header.dword_0 = cpu_to_le32(dword_0);
+
+       /*
+        * assigning invalid value for command status. Controller
+        * updates OCS on command completion, with the command
+        * status
+        */
+       req_desc->header.dword_2 =
+               cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+}
+
+/**
+ * ufshcd_prepare_utp_scsi_cmd_upiu() - fills the utp_transfer_req_desc,
+ * for scsi commands
+ * @lrbp - local reference block pointer
+ * @upiu_flags - flags
+ */
+static
+void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u32 upiu_flags)
+{
+       struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+
+       /* command descriptor fields */
+       ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
+                               UPIU_TRANSACTION_COMMAND, upiu_flags,
+                               lrbp->lun, lrbp->task_tag);
+       ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
+                               UPIU_COMMAND_SET_TYPE_SCSI, 0, 0, 0);
+
+       /* Total EHS length and Data segment length will be zero */
+       ucd_req_ptr->header.dword_2 = 0;
+
+       ucd_req_ptr->sc.exp_data_transfer_len =
+               cpu_to_be32(lrbp->cmd->sdb.length);
+
+       memcpy(ucd_req_ptr->sc.cdb, lrbp->cmd->cmnd,
+               (min_t(unsigned short, lrbp->cmd->cmd_len, MAX_CDB_SIZE)));
+}
+
+/**
+ * ufshcd_prepare_utp_query_req_upiu() - fills the utp_transfer_req_desc,
+ * for query requsts
+ * @hba: UFS hba
+ * @lrbp: local reference block pointer
+ * @upiu_flags: flags
+ */
+static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
+                               struct ufshcd_lrb *lrbp, u32 upiu_flags)
+{
+       struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+       struct ufs_query *query = &hba->dev_cmd.query;
+       u16 len = query->request.upiu_req.length;
+       u8 *descp = (u8 *)lrbp->ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE;
+
+       /* Query request header */
+       ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
+                       UPIU_TRANSACTION_QUERY_REQ, upiu_flags,
+                       lrbp->lun, lrbp->task_tag);
+       ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
+                       0, query->request.query_func, 0, 0);
+
+       /* Data segment length */
+       ucd_req_ptr->header.dword_2 = UPIU_HEADER_DWORD(
+                       0, 0, len >> 8, (u8)len);
+
+       /* Copy the Query Request buffer as is */
+       memcpy(&ucd_req_ptr->qr, &query->request.upiu_req,
+                       QUERY_OSF_SIZE);
+       ufshcd_query_to_be(&ucd_req_ptr->qr);
+
+       /* Copy the Descriptor */
+       if ((len > 0) && (query->request.upiu_req.opcode ==
+                                       UPIU_QUERY_OPCODE_WRITE_DESC)) {
+               memcpy(descp, query->descriptor,
+                               min_t(u16, len, QUERY_DESC_MAX_SIZE));
+       }
+}
+
+static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp)
+{
+       struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
+
+       memset(ucd_req_ptr, 0, sizeof(struct utp_upiu_req));
+
+       /* command descriptor fields */
+       ucd_req_ptr->header.dword_0 =
+               UPIU_HEADER_DWORD(
+                       UPIU_TRANSACTION_NOP_OUT, 0, 0, lrbp->task_tag);
+}
+
 /**
  * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
+ * @hba - per adapter instance
  * @lrb - pointer to local reference block
  */
-static void ufshcd_compose_upiu(struct ufshcd_lrb *lrbp)
+static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 {
-       struct utp_transfer_req_desc *req_desc;
-       struct utp_upiu_cmd *ucd_cmd_ptr;
-       u32 data_direction;
        u32 upiu_flags;
-
-       ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
-       req_desc = lrbp->utr_descriptor_ptr;
+       int ret = 0;
 
        switch (lrbp->command_type) {
        case UTP_CMD_TYPE_SCSI:
-               if (lrbp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
-                       data_direction = UTP_DEVICE_TO_HOST;
-                       upiu_flags = UPIU_CMD_FLAGS_READ;
-               } else if (lrbp->cmd->sc_data_direction == DMA_TO_DEVICE) {
-                       data_direction = UTP_HOST_TO_DEVICE;
-                       upiu_flags = UPIU_CMD_FLAGS_WRITE;
+               if (likely(lrbp->cmd)) {
+                       ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags,
+                                       lrbp->cmd->sc_data_direction);
+                       ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
                } else {
-                       data_direction = UTP_NO_DATA_TRANSFER;
-                       upiu_flags = UPIU_CMD_FLAGS_NONE;
+                       ret = -EINVAL;
                }
-
-               /* Transfer request descriptor header fields */
-               req_desc->header.dword_0 =
-                       cpu_to_le32(data_direction | UTP_SCSI_COMMAND);
-
-               /*
-                * assigning invalid value for command status. Controller
-                * updates OCS on command completion, with the command
-                * status
-                */
-               req_desc->header.dword_2 =
-                       cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
-
-               /* command descriptor fields */
-               ucd_cmd_ptr->header.dword_0 =
-                       cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
-                                                     upiu_flags,
-                                                     lrbp->lun,
-                                                     lrbp->task_tag));
-               ucd_cmd_ptr->header.dword_1 =
-                       cpu_to_be32(
-                               UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
-                                                 0,
-                                                 0,
-                                                 0));
-
-               /* Total EHS length and Data segment length will be zero */
-               ucd_cmd_ptr->header.dword_2 = 0;
-
-               ucd_cmd_ptr->exp_data_transfer_len =
-                       cpu_to_be32(lrbp->cmd->sdb.length);
-
-               memcpy(ucd_cmd_ptr->cdb,
-                      lrbp->cmd->cmnd,
-                      (min_t(unsigned short,
-                             lrbp->cmd->cmd_len,
-                             MAX_CDB_SIZE)));
                break;
        case UTP_CMD_TYPE_DEV_MANAGE:
-               /* For query function implementation */
+               ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE);
+               if (hba->dev_cmd.type == DEV_CMD_TYPE_QUERY)
+                       ufshcd_prepare_utp_query_req_upiu(
+                                       hba, lrbp, upiu_flags);
+               else if (hba->dev_cmd.type == DEV_CMD_TYPE_NOP)
+                       ufshcd_prepare_utp_nop_upiu(lrbp);
+               else
+                       ret = -EINVAL;
                break;
        case UTP_CMD_TYPE_UFS:
                /* For UFS native command implementation */
+               ret = -ENOTSUPP;
+               dev_err(hba->dev, "%s: UFS native command are not supported\n",
+                       __func__);
+               break;
+       default:
+               ret = -ENOTSUPP;
+               dev_err(hba->dev, "%s: unknown command type: 0x%x\n",
+                               __func__, lrbp->command_type);
                break;
        } /* end of switch */
+
+       return ret;
 }
 
 /**
@@ -615,21 +858,37 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
                goto out;
        }
 
+       /* acquire the tag to make sure device cmds don't use it */
+       if (test_and_set_bit_lock(tag, &hba->lrb_in_use)) {
+               /*
+                * Dev manage command in progress, requeue the command.
+                * Requeuing the command helps in cases where the request *may*
+                * find different tag instead of waiting for dev manage command
+                * completion.
+                */
+               err = SCSI_MLQUEUE_HOST_BUSY;
+               goto out;
+       }
+
        lrbp = &hba->lrb[tag];
 
+       WARN_ON(lrbp->cmd);
        lrbp->cmd = cmd;
        lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
        lrbp->sense_buffer = cmd->sense_buffer;
        lrbp->task_tag = tag;
        lrbp->lun = cmd->device->lun;
-
+       lrbp->intr_cmd = false;
        lrbp->command_type = UTP_CMD_TYPE_SCSI;
 
        /* form UPIU before issuing the command */
-       ufshcd_compose_upiu(lrbp);
+       ufshcd_compose_upiu(hba, lrbp);
        err = ufshcd_map_sg(lrbp);
-       if (err)
+       if (err) {
+               lrbp->cmd = NULL;
+               clear_bit_unlock(tag, &hba->lrb_in_use);
                goto out;
+       }
 
        /* issue command to the controller */
        spin_lock_irqsave(hba->host->host_lock, flags);
@@ -639,6 +898,338 @@ out:
        return err;
 }
 
+static int ufshcd_compose_dev_cmd(struct ufs_hba *hba,
+               struct ufshcd_lrb *lrbp, enum dev_cmd_type cmd_type, int tag)
+{
+       lrbp->cmd = NULL;
+       lrbp->sense_bufflen = 0;
+       lrbp->sense_buffer = NULL;
+       lrbp->task_tag = tag;
+       lrbp->lun = 0; /* device management cmd is not specific to any LUN */
+       lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
+       lrbp->intr_cmd = true; /* No interrupt aggregation */
+       hba->dev_cmd.type = cmd_type;
+
+       return ufshcd_compose_upiu(hba, lrbp);
+}
+
+static int
+ufshcd_clear_cmd(struct ufs_hba *hba, int tag)
+{
+       int err = 0;
+       unsigned long flags;
+       u32 mask = 1 << tag;
+
+       /* clear outstanding transaction before retry */
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       ufshcd_utrl_clear(hba, tag);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       /*
+        * wait for for h/w to clear corresponding bit in door-bell.
+        * max. wait is 1 sec.
+        */
+       err = ufshcd_wait_for_register(hba,
+                       REG_UTP_TRANSFER_REQ_DOOR_BELL,
+                       mask, ~mask, 1000, 1000);
+
+       return err;
+}
+
+/**
+ * ufshcd_dev_cmd_completion() - handles device management command responses
+ * @hba: per adapter instance
+ * @lrbp: pointer to local reference block
+ */
+static int
+ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+       int resp;
+       int err = 0;
+
+       resp = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
+
+       switch (resp) {
+       case UPIU_TRANSACTION_NOP_IN:
+               if (hba->dev_cmd.type != DEV_CMD_TYPE_NOP) {
+                       err = -EINVAL;
+                       dev_err(hba->dev, "%s: unexpected response %x\n",
+                                       __func__, resp);
+               }
+               break;
+       case UPIU_TRANSACTION_QUERY_RSP:
+               ufshcd_copy_query_response(hba, lrbp);
+               break;
+       case UPIU_TRANSACTION_REJECT_UPIU:
+               /* TODO: handle Reject UPIU Response */
+               err = -EPERM;
+               dev_err(hba->dev, "%s: Reject UPIU not fully implemented\n",
+                               __func__);
+               break;
+       default:
+               err = -EINVAL;
+               dev_err(hba->dev, "%s: Invalid device management cmd response: %x\n",
+                               __func__, resp);
+               break;
+       }
+
+       return err;
+}
+
+static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba,
+               struct ufshcd_lrb *lrbp, int max_timeout)
+{
+       int err = 0;
+       unsigned long time_left;
+       unsigned long flags;
+
+       time_left = wait_for_completion_timeout(hba->dev_cmd.complete,
+                       msecs_to_jiffies(max_timeout));
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       hba->dev_cmd.complete = NULL;
+       if (likely(time_left)) {
+               err = ufshcd_get_tr_ocs(lrbp);
+               if (!err)
+                       err = ufshcd_dev_cmd_completion(hba, lrbp);
+       }
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       if (!time_left) {
+               err = -ETIMEDOUT;
+               if (!ufshcd_clear_cmd(hba, lrbp->task_tag))
+                       /* sucessfully cleared the command, retry if needed */
+                       err = -EAGAIN;
+       }
+
+       return err;
+}
+
+/**
+ * ufshcd_get_dev_cmd_tag - Get device management command tag
+ * @hba: per-adapter instance
+ * @tag: pointer to variable with available slot value
+ *
+ * Get a free slot and lock it until device management command
+ * completes.
+ *
+ * Returns false if free slot is unavailable for locking, else
+ * return true with tag value in @tag.
+ */
+static bool ufshcd_get_dev_cmd_tag(struct ufs_hba *hba, int *tag_out)
+{
+       int tag;
+       bool ret = false;
+       unsigned long tmp;
+
+       if (!tag_out)
+               goto out;
+
+       do {
+               tmp = ~hba->lrb_in_use;
+               tag = find_last_bit(&tmp, hba->nutrs);
+               if (tag >= hba->nutrs)
+                       goto out;
+       } while (test_and_set_bit_lock(tag, &hba->lrb_in_use));
+
+       *tag_out = tag;
+       ret = true;
+out:
+       return ret;
+}
+
+static inline void ufshcd_put_dev_cmd_tag(struct ufs_hba *hba, int tag)
+{
+       clear_bit_unlock(tag, &hba->lrb_in_use);
+}
+
+/**
+ * ufshcd_exec_dev_cmd - API for sending device management requests
+ * @hba - UFS hba
+ * @cmd_type - specifies the type (NOP, Query...)
+ * @timeout - time in seconds
+ *
+ * NOTE: Since there is only one available tag for device management commands,
+ * it is expected you hold the hba->dev_cmd.lock mutex.
+ */
+static int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
+               enum dev_cmd_type cmd_type, int timeout)
+{
+       struct ufshcd_lrb *lrbp;
+       int err;
+       int tag;
+       struct completion wait;
+       unsigned long flags;
+
+       /*
+        * Get free slot, sleep if slots are unavailable.
+        * Even though we use wait_event() which sleeps indefinitely,
+        * the maximum wait time is bounded by SCSI request timeout.
+        */
+       wait_event(hba->dev_cmd.tag_wq, ufshcd_get_dev_cmd_tag(hba, &tag));
+
+       init_completion(&wait);
+       lrbp = &hba->lrb[tag];
+       WARN_ON(lrbp->cmd);
+       err = ufshcd_compose_dev_cmd(hba, lrbp, cmd_type, tag);
+       if (unlikely(err))
+               goto out_put_tag;
+
+       hba->dev_cmd.complete = &wait;
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       ufshcd_send_command(hba, tag);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       err = ufshcd_wait_for_dev_cmd(hba, lrbp, timeout);
+
+out_put_tag:
+       ufshcd_put_dev_cmd_tag(hba, tag);
+       wake_up(&hba->dev_cmd.tag_wq);
+       return err;
+}
+
+/**
+ * ufshcd_query_flag() - API function for sending flag query requests
+ * hba: per-adapter instance
+ * query_opcode: flag query to perform
+ * idn: flag idn to access
+ * flag_res: the flag value after the query request completes
+ *
+ * Returns 0 for success, non-zero in case of failure
+ */
+static int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
+                       enum flag_idn idn, bool *flag_res)
+{
+       struct ufs_query_req *request;
+       struct ufs_query_res *response;
+       int err;
+
+       BUG_ON(!hba);
+
+       mutex_lock(&hba->dev_cmd.lock);
+       request = &hba->dev_cmd.query.request;
+       response = &hba->dev_cmd.query.response;
+       memset(request, 0, sizeof(struct ufs_query_req));
+       memset(response, 0, sizeof(struct ufs_query_res));
+
+       switch (opcode) {
+       case UPIU_QUERY_OPCODE_SET_FLAG:
+       case UPIU_QUERY_OPCODE_CLEAR_FLAG:
+       case UPIU_QUERY_OPCODE_TOGGLE_FLAG:
+               request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
+               break;
+       case UPIU_QUERY_OPCODE_READ_FLAG:
+               request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
+               if (!flag_res) {
+                       /* No dummy reads */
+                       dev_err(hba->dev, "%s: Invalid argument for read request\n",
+                                       __func__);
+                       err = -EINVAL;
+                       goto out_unlock;
+               }
+               break;
+       default:
+               dev_err(hba->dev,
+                       "%s: Expected query flag opcode but got = %d\n",
+                       __func__, opcode);
+               err = -EINVAL;
+               goto out_unlock;
+       }
+       request->upiu_req.opcode = opcode;
+       request->upiu_req.idn = idn;
+
+       /* Send query request */
+       err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY,
+                       QUERY_REQ_TIMEOUT);
+
+       if (err) {
+               dev_err(hba->dev,
+                       "%s: Sending flag query for idn %d failed, err = %d\n",
+                       __func__, idn, err);
+               goto out_unlock;
+       }
+
+       if (flag_res)
+               *flag_res = (response->upiu_res.value &
+                               MASK_QUERY_UPIU_FLAG_LOC) & 0x1;
+
+out_unlock:
+       mutex_unlock(&hba->dev_cmd.lock);
+       return err;
+}
+
+/**
+ * ufshcd_query_attr - API function for sending attribute requests
+ * hba: per-adapter instance
+ * opcode: attribute opcode
+ * idn: attribute idn to access
+ * index: index field
+ * selector: selector field
+ * attr_val: the attribute value after the query request completes
+ *
+ * Returns 0 for success, non-zero in case of failure
+*/
+int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+                       enum attr_idn idn, u8 index, u8 selector, u32 *attr_val)
+{
+       struct ufs_query_req *request;
+       struct ufs_query_res *response;
+       int err;
+
+       BUG_ON(!hba);
+
+       if (!attr_val) {
+               dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n",
+                               __func__, opcode);
+               err = -EINVAL;
+               goto out;
+       }
+
+       mutex_lock(&hba->dev_cmd.lock);
+       request = &hba->dev_cmd.query.request;
+       response = &hba->dev_cmd.query.response;
+       memset(request, 0, sizeof(struct ufs_query_req));
+       memset(response, 0, sizeof(struct ufs_query_res));
+
+       switch (opcode) {
+       case UPIU_QUERY_OPCODE_WRITE_ATTR:
+               request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
+               request->upiu_req.value = *attr_val;
+               break;
+       case UPIU_QUERY_OPCODE_READ_ATTR:
+               request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
+               break;
+       default:
+               dev_err(hba->dev, "%s: Expected query attr opcode but got = 0x%.2x\n",
+                               __func__, opcode);
+               err = -EINVAL;
+               goto out_unlock;
+       }
+
+       request->upiu_req.opcode = opcode;
+       request->upiu_req.idn = idn;
+       request->upiu_req.index = index;
+       request->upiu_req.selector = selector;
+
+       /* Send query request */
+       err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY,
+                                               QUERY_REQ_TIMEOUT);
+
+       if (err) {
+               dev_err(hba->dev, "%s: opcode 0x%.2x for idn %d failed, err = %d\n",
+                               __func__, opcode, idn, err);
+               goto out_unlock;
+       }
+
+       *attr_val = response->upiu_res.value;
+
+out_unlock:
+       mutex_unlock(&hba->dev_cmd.lock);
+out:
+       return err;
+}
+
 /**
  * ufshcd_memory_alloc - allocate memory for host memory space data structures
  * @hba: per adapter instance
@@ -774,8 +1365,8 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
                                cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
 
                hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
-               hba->lrb[i].ucd_cmd_ptr =
-                       (struct utp_upiu_cmd *)(cmd_descp + i);
+               hba->lrb[i].ucd_req_ptr =
+                       (struct utp_upiu_req *)(cmd_descp + i);
                hba->lrb[i].ucd_rsp_ptr =
                        (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
                hba->lrb[i].ucd_prdt_ptr =
@@ -784,30 +1375,213 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
 }
 
 /**
- * ufshcd_dme_link_startup - Notify Unipro to perform link startup
+ * ufshcd_dme_link_startup - Notify Unipro to perform link startup
+ * @hba: per adapter instance
+ *
+ * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
+ * in order to initialize the Unipro link startup procedure.
+ * Once the Unipro links are up, the device connected to the controller
+ * is detected.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_dme_link_startup(struct ufs_hba *hba)
+{
+       struct uic_command uic_cmd = {0};
+       int ret;
+
+       uic_cmd.command = UIC_CMD_DME_LINK_STARTUP;
+
+       ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+       if (ret)
+               dev_err(hba->dev,
+                       "dme-link-startup: error code %d\n", ret);
+       return ret;
+}
+
+/**
+ * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @attr_set: attribute set type as uic command argument2
+ * @mib_val: setting value as uic command argument3
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
+                       u8 attr_set, u32 mib_val, u8 peer)
+{
+       struct uic_command uic_cmd = {0};
+       static const char *const action[] = {
+               "dme-set",
+               "dme-peer-set"
+       };
+       const char *set = action[!!peer];
+       int ret;
+
+       uic_cmd.command = peer ?
+               UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
+       uic_cmd.argument1 = attr_sel;
+       uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
+       uic_cmd.argument3 = mib_val;
+
+       ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+       if (ret)
+               dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x error code %d\n",
+                       set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
+
+/**
+ * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
+ * @hba: per adapter instance
+ * @attr_sel: uic command argument1
+ * @mib_val: the value of the attribute as returned by the UIC command
+ * @peer: indicate whether peer or local
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
+                       u32 *mib_val, u8 peer)
+{
+       struct uic_command uic_cmd = {0};
+       static const char *const action[] = {
+               "dme-get",
+               "dme-peer-get"
+       };
+       const char *get = action[!!peer];
+       int ret;
+
+       uic_cmd.command = peer ?
+               UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
+       uic_cmd.argument1 = attr_sel;
+
+       ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
+       if (ret) {
+               dev_err(hba->dev, "%s: attr-id 0x%x error code %d\n",
+                       get, UIC_GET_ATTR_ID(attr_sel), ret);
+               goto out;
+       }
+
+       if (mib_val)
+               *mib_val = uic_cmd.argument3;
+out:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+
+/**
+ * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
+ *                             using DME_SET primitives.
  * @hba: per adapter instance
- *
- * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
- * in order to initialize the Unipro link startup procedure.
- * Once the Unipro links are up, the device connected to the controller
- * is detected.
+ * @mode: powr mode value
  *
  * Returns 0 on success, non-zero value on failure
  */
-static int ufshcd_dme_link_startup(struct ufs_hba *hba)
+int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
 {
        struct uic_command uic_cmd = {0};
+       struct completion pwr_done;
+       unsigned long flags;
+       u8 status;
        int ret;
 
-       uic_cmd.command = UIC_CMD_DME_LINK_STARTUP;
+       uic_cmd.command = UIC_CMD_DME_SET;
+       uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
+       uic_cmd.argument3 = mode;
+       init_completion(&pwr_done);
 
-       ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
-       if (ret)
+       mutex_lock(&hba->uic_cmd_mutex);
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       hba->pwr_done = &pwr_done;
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+       ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
+       if (ret) {
                dev_err(hba->dev,
-                       "dme-link-startup: error code %d\n", ret);
+                       "pwr mode change with mode 0x%x uic error %d\n",
+                       mode, ret);
+               goto out;
+       }
+
+       if (!wait_for_completion_timeout(hba->pwr_done,
+                                        msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
+               dev_err(hba->dev,
+                       "pwr mode change with mode 0x%x completion timeout\n",
+                       mode);
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       status = ufshcd_get_upmcrs(hba);
+       if (status != PWR_LOCAL) {
+               dev_err(hba->dev,
+                       "pwr mode change failed, host umpcrs:0x%x\n",
+                       status);
+               ret = (status != PWR_OK) ? status : -1;
+       }
+out:
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       hba->pwr_done = NULL;
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+       mutex_unlock(&hba->uic_cmd_mutex);
        return ret;
 }
 
+/**
+ * ufshcd_complete_dev_init() - checks device readiness
+ * hba: per-adapter instance
+ *
+ * Set fDeviceInit flag and poll until device toggles it.
+ */
+static int ufshcd_complete_dev_init(struct ufs_hba *hba)
+{
+       int i, retries, err = 0;
+       bool flag_res = 1;
+
+       for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+               /* Set the fDeviceInit flag */
+               err = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_SET_FLAG,
+                                       QUERY_FLAG_IDN_FDEVICEINIT, NULL);
+               if (!err || err == -ETIMEDOUT)
+                       break;
+               dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err);
+       }
+       if (err) {
+               dev_err(hba->dev,
+                       "%s setting fDeviceInit flag failed with error %d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       /* poll for max. 100 iterations for fDeviceInit flag to clear */
+       for (i = 0; i < 100 && !err && flag_res; i++) {
+               for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
+                       err = ufshcd_query_flag(hba,
+                                       UPIU_QUERY_OPCODE_READ_FLAG,
+                                       QUERY_FLAG_IDN_FDEVICEINIT, &flag_res);
+                       if (!err || err == -ETIMEDOUT)
+                               break;
+                       dev_dbg(hba->dev, "%s: error %d retrying\n", __func__,
+                                       err);
+               }
+       }
+       if (err)
+               dev_err(hba->dev,
+                       "%s reading fDeviceInit flag failed with error %d\n",
+                       __func__, err);
+       else if (flag_res)
+               dev_err(hba->dev,
+                       "%s fDeviceInit was not cleared by the device\n",
+                       __func__);
+
+out:
+       return err;
+}
+
 /**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
@@ -838,7 +1612,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
        ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
        /* Configure interrupt aggregation */
-       ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+       ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
 
        /* Configure UTRL and UTMRL base address registers */
        ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -960,6 +1734,38 @@ out:
        return ret;
 }
 
+/**
+ * ufshcd_verify_dev_init() - Verify device initialization
+ * @hba: per-adapter instance
+ *
+ * Send NOP OUT UPIU and wait for NOP IN response to check whether the
+ * device Transport Protocol (UTP) layer is ready after a reset.
+ * If the UTP layer at the device side is not initialized, it may
+ * not respond with NOP IN UPIU within timeout of %NOP_OUT_TIMEOUT
+ * and we retry sending NOP OUT for %NOP_OUT_RETRIES iterations.
+ */
+static int ufshcd_verify_dev_init(struct ufs_hba *hba)
+{
+       int err = 0;
+       int retries;
+
+       mutex_lock(&hba->dev_cmd.lock);
+       for (retries = NOP_OUT_RETRIES; retries > 0; retries--) {
+               err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP,
+                                              NOP_OUT_TIMEOUT);
+
+               if (!err || err == -ETIMEDOUT)
+                       break;
+
+               dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err);
+       }
+       mutex_unlock(&hba->dev_cmd.lock);
+
+       if (err)
+               dev_err(hba->dev, "%s: NOP OUT failed %d\n", __func__, err);
+       return err;
+}
+
 /**
  * ufshcd_do_reset - reset the host controller
  * @hba: per adapter instance
@@ -986,13 +1792,20 @@ static int ufshcd_do_reset(struct ufs_hba *hba)
        for (tag = 0; tag < hba->nutrs; tag++) {
                if (test_bit(tag, &hba->outstanding_reqs)) {
                        lrbp = &hba->lrb[tag];
-                       scsi_dma_unmap(lrbp->cmd);
-                       lrbp->cmd->result = DID_RESET << 16;
-                       lrbp->cmd->scsi_done(lrbp->cmd);
-                       lrbp->cmd = NULL;
+                       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;
@@ -1145,32 +1958,24 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
        int result = 0;
 
        switch (scsi_status) {
-       case SAM_STAT_GOOD:
-               result |= DID_OK << 16 |
-                         COMMAND_COMPLETE << 8 |
-                         SAM_STAT_GOOD;
-               break;
        case SAM_STAT_CHECK_CONDITION:
+               ufshcd_copy_sense_data(lrbp);
+       case SAM_STAT_GOOD:
                result |= DID_OK << 16 |
                          COMMAND_COMPLETE << 8 |
-                         SAM_STAT_CHECK_CONDITION;
-               ufshcd_copy_sense_data(lrbp);
-               break;
-       case SAM_STAT_BUSY:
-               result |= SAM_STAT_BUSY;
+                         scsi_status;
                break;
        case SAM_STAT_TASK_SET_FULL:
-
                /*
                 * If a LUN reports SAM_STAT_TASK_SET_FULL, then the LUN queue
                 * depth needs to be adjusted to the exact number of
                 * outstanding commands the LUN can handle at any given time.
                 */
                ufshcd_adjust_lun_qdepth(lrbp->cmd);
-               result |= SAM_STAT_TASK_SET_FULL;
-               break;
+       case SAM_STAT_BUSY:
        case SAM_STAT_TASK_ABORTED:
-               result |= SAM_STAT_TASK_ABORTED;
+               ufshcd_copy_sense_data(lrbp);
+               result |= scsi_status;
                break;
        default:
                result |= DID_ERROR << 16;
@@ -1199,27 +2004,39 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 
        switch (ocs) {
        case OCS_SUCCESS:
+               result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
+
+               switch (result) {
+               case UPIU_TRANSACTION_RESPONSE:
+                       /*
+                        * get the response UPIU result to extract
+                        * the SCSI command status
+                        */
+                       result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+
+                       /*
+                        * get the result based on SCSI status response
+                        * to notify the SCSI midlayer of the command status
+                        */
+                       scsi_status = result & MASK_SCSI_STATUS;
+                       result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
 
-               /* check if the returned transfer response is valid */
-               result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
-               if (result) {
+                       if (ufshcd_is_exception_event(lrbp->ucd_rsp_ptr))
+                               schedule_work(&hba->eeh_work);
+                       break;
+               case UPIU_TRANSACTION_REJECT_UPIU:
+                       /* TODO: handle Reject UPIU Response */
+                       result = DID_ERROR << 16;
+                       dev_err(hba->dev,
+                               "Reject UPIU not fully implemented\n");
+                       break;
+               default:
+                       result = DID_ERROR << 16;
                        dev_err(hba->dev,
-                               "Invalid response = %x\n", result);
+                               "Unexpected request response code = %x\n",
+                               result);
                        break;
                }
-
-               /*
-                * get the response UPIU result to extract
-                * the SCSI command status
-                */
-               result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
-
-               /*
-                * get the result based on SCSI status response
-                * to notify the SCSI midlayer of the command status
-                */
-               scsi_status = result & MASK_SCSI_STATUS;
-               result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
                break;
        case OCS_ABORTED:
                result |= DID_ABORT << 16;
@@ -1243,14 +2060,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
 /**
  * ufshcd_uic_cmd_compl - handle completion of uic command
  * @hba: per adapter instance
+ * @intr_status: interrupt status generated by the controller
  */
-static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
+static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
 {
-       if (hba->active_uic_cmd) {
+       if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
                hba->active_uic_cmd->argument2 |=
                        ufshcd_get_uic_cmd_result(hba);
+               hba->active_uic_cmd->argument3 =
+                       ufshcd_get_dme_attr_val(hba);
                complete(&hba->active_uic_cmd->done);
        }
+
+       if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
+               complete(hba->pwr_done);
 }
 
 /**
@@ -1259,28 +2082,40 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
  */
 static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
 {
-       struct ufshcd_lrb *lrb;
+       struct ufshcd_lrb *lrbp;
+       struct scsi_cmnd *cmd;
        unsigned long completed_reqs;
        u32 tr_doorbell;
        int result;
        int index;
+       bool int_aggr_reset = false;
 
-       lrb = hba->lrb;
        tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
        completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
 
        for (index = 0; index < hba->nutrs; index++) {
                if (test_bit(index, &completed_reqs)) {
+                       lrbp = &hba->lrb[index];
+                       cmd = lrbp->cmd;
+                       /*
+                        * Don't skip resetting interrupt aggregation counters
+                        * if a regular command is present.
+                        */
+                       int_aggr_reset |= !lrbp->intr_cmd;
 
-                       result = ufshcd_transfer_rsp_status(hba, &lrb[index]);
-
-                       if (lrb[index].cmd) {
-                               scsi_dma_unmap(lrb[index].cmd);
-                               lrb[index].cmd->result = result;
-                               lrb[index].cmd->scsi_done(lrb[index].cmd);
-
+                       if (cmd) {
+                               result = ufshcd_transfer_rsp_status(hba, lrbp);
+                               scsi_dma_unmap(cmd);
+                               cmd->result = result;
                                /* Mark completed command as NULL in LRB */
-                               lrb[index].cmd = NULL;
+                               lrbp->cmd = NULL;
+                               clear_bit_unlock(index, &hba->lrb_in_use);
+                               /* Do not touch lrbp after scsi done */
+                               cmd->scsi_done(cmd);
+                       } else if (lrbp->command_type ==
+                                       UTP_CMD_TYPE_DEV_MANAGE) {
+                               if (hba->dev_cmd.complete)
+                                       complete(hba->dev_cmd.complete);
                        }
                } /* end of if */
        } /* end of for */
@@ -1288,8 +2123,238 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
        /* clear corresponding bits of completed commands */
        hba->outstanding_reqs ^= completed_reqs;
 
+       /* we might have free'd some tags above */
+       wake_up(&hba->dev_cmd.tag_wq);
+
        /* Reset interrupt aggregation counters */
-       ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+       if (int_aggr_reset)
+               ufshcd_reset_intr_aggr(hba);
+}
+
+/**
+ * ufshcd_disable_ee - disable exception event
+ * @hba: per-adapter instance
+ * @mask: exception event to disable
+ *
+ * Disables exception event in the device so that the EVENT_ALERT
+ * bit is not set.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_disable_ee(struct ufs_hba *hba, u16 mask)
+{
+       int err = 0;
+       u32 val;
+
+       if (!(hba->ee_ctrl_mask & mask))
+               goto out;
+
+       val = hba->ee_ctrl_mask & ~mask;
+       val &= 0xFFFF; /* 2 bytes */
+       err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+                       QUERY_ATTR_IDN_EE_CONTROL, 0, 0, &val);
+       if (!err)
+               hba->ee_ctrl_mask &= ~mask;
+out:
+       return err;
+}
+
+/**
+ * ufshcd_enable_ee - enable exception event
+ * @hba: per-adapter instance
+ * @mask: exception event to enable
+ *
+ * Enable corresponding exception event in the device to allow
+ * device to alert host in critical scenarios.
+ *
+ * Returns zero on success, non-zero error value on failure.
+ */
+static int ufshcd_enable_ee(struct ufs_hba *hba, u16 mask)
+{
+       int err = 0;
+       u32 val;
+
+       if (hba->ee_ctrl_mask & mask)
+               goto out;
+
+       val = hba->ee_ctrl_mask | mask;
+       val &= 0xFFFF; /* 2 bytes */
+       err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+                       QUERY_ATTR_IDN_EE_CONTROL, 0, 0, &val);
+       if (!err)
+               hba->ee_ctrl_mask |= mask;
+out:
+       return err;
+}
+
+/**
+ * ufshcd_enable_auto_bkops - Allow device managed BKOPS
+ * @hba: per-adapter instance
+ *
+ * Allow device to manage background operations on its own. Enabling
+ * this might lead to inconsistent latencies during normal data transfers
+ * as the device is allowed to manage its own way of handling background
+ * operations.
+ *
+ * Returns zero on success, non-zero on failure.
+ */
+static int ufshcd_enable_auto_bkops(struct ufs_hba *hba)
+{
+       int err = 0;
+
+       if (hba->auto_bkops_enabled)
+               goto out;
+
+       err = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_SET_FLAG,
+                       QUERY_FLAG_IDN_BKOPS_EN, NULL);
+       if (err) {
+               dev_err(hba->dev, "%s: failed to enable bkops %d\n",
+                               __func__, err);
+               goto out;
+       }
+
+       hba->auto_bkops_enabled = true;
+
+       /* No need of URGENT_BKOPS exception from the device */
+       err = ufshcd_disable_ee(hba, MASK_EE_URGENT_BKOPS);
+       if (err)
+               dev_err(hba->dev, "%s: failed to disable exception event %d\n",
+                               __func__, err);
+out:
+       return err;
+}
+
+/**
+ * ufshcd_disable_auto_bkops - block device in doing background operations
+ * @hba: per-adapter instance
+ *
+ * Disabling background operations improves command response latency but
+ * has drawback of device moving into critical state where the device is
+ * not-operable. Make sure to call ufshcd_enable_auto_bkops() whenever the
+ * host is idle so that BKOPS are managed effectively without any negative
+ * impacts.
+ *
+ * Returns zero on success, non-zero on failure.
+ */
+static int ufshcd_disable_auto_bkops(struct ufs_hba *hba)
+{
+       int err = 0;
+
+       if (!hba->auto_bkops_enabled)
+               goto out;
+
+       /*
+        * If host assisted BKOPs is to be enabled, make sure
+        * urgent bkops exception is allowed.
+        */
+       err = ufshcd_enable_ee(hba, MASK_EE_URGENT_BKOPS);
+       if (err) {
+               dev_err(hba->dev, "%s: failed to enable exception event %d\n",
+                               __func__, err);
+               goto out;
+       }
+
+       err = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_CLEAR_FLAG,
+                       QUERY_FLAG_IDN_BKOPS_EN, NULL);
+       if (err) {
+               dev_err(hba->dev, "%s: failed to disable bkops %d\n",
+                               __func__, err);
+               ufshcd_disable_ee(hba, MASK_EE_URGENT_BKOPS);
+               goto out;
+       }
+
+       hba->auto_bkops_enabled = false;
+out:
+       return err;
+}
+
+/**
+ * ufshcd_force_reset_auto_bkops - force enable of auto bkops
+ * @hba: per adapter instance
+ *
+ * After a device reset the device may toggle the BKOPS_EN flag
+ * to default value. The s/w tracking variables should be updated
+ * as well. Do this by forcing enable of auto bkops.
+ */
+static void  ufshcd_force_reset_auto_bkops(struct ufs_hba *hba)
+{
+       hba->auto_bkops_enabled = false;
+       hba->ee_ctrl_mask |= MASK_EE_URGENT_BKOPS;
+       ufshcd_enable_auto_bkops(hba);
+}
+
+static inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status)
+{
+       return ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+                       QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, status);
+}
+
+/**
+ * ufshcd_urgent_bkops - handle urgent bkops exception event
+ * @hba: per-adapter instance
+ *
+ * Enable fBackgroundOpsEn flag in the device to permit background
+ * operations.
+ */
+static int ufshcd_urgent_bkops(struct ufs_hba *hba)
+{
+       int err;
+       u32 status = 0;
+
+       err = ufshcd_get_bkops_status(hba, &status);
+       if (err) {
+               dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
+                               __func__, err);
+               goto out;
+       }
+
+       status = status & 0xF;
+
+       /* handle only if status indicates performance impact or critical */
+       if (status >= BKOPS_STATUS_PERF_IMPACT)
+               err = ufshcd_enable_auto_bkops(hba);
+out:
+       return err;
+}
+
+static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
+{
+       return ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+                       QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
+}
+
+/**
+ * ufshcd_exception_event_handler - handle exceptions raised by device
+ * @work: pointer to work data
+ *
+ * Read bExceptionEventStatus attribute from the device and handle the
+ * exception event accordingly.
+ */
+static void ufshcd_exception_event_handler(struct work_struct *work)
+{
+       struct ufs_hba *hba;
+       int err;
+       u32 status = 0;
+       hba = container_of(work, struct ufs_hba, eeh_work);
+
+       pm_runtime_get_sync(hba->dev);
+       err = ufshcd_get_ee_status(hba, &status);
+       if (err) {
+               dev_err(hba->dev, "%s: failed to get exception status %d\n",
+                               __func__, err);
+               goto out;
+       }
+
+       status &= hba->ee_ctrl_mask;
+       if (status & MASK_EE_URGENT_BKOPS) {
+               err = ufshcd_urgent_bkops(hba);
+               if (err)
+                       dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
+                                       __func__, err);
+       }
+out:
+       pm_runtime_put_sync(hba->dev);
+       return;
 }
 
 /**
@@ -1301,9 +2366,11 @@ static void ufshcd_fatal_err_handler(struct work_struct *work)
        struct ufs_hba *hba;
        hba = container_of(work, struct ufs_hba, feh_workq);
 
+       pm_runtime_get_sync(hba->dev);
        /* check if reset is already in progress */
        if (hba->ufshcd_state != UFSHCD_STATE_RESET)
                ufshcd_do_reset(hba);
+       pm_runtime_put_sync(hba->dev);
 }
 
 /**
@@ -1352,8 +2419,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
        if (hba->errors)
                ufshcd_err_handler(hba);
 
-       if (intr_status & UIC_COMMAND_COMPL)
-               ufshcd_uic_cmd_compl(hba);
+       if (intr_status & UFSHCD_UIC_MASK)
+               ufshcd_uic_cmd_compl(hba, intr_status);
 
        if (intr_status & UTP_TASK_REQ_COMPL)
                ufshcd_tmc_handler(hba);
@@ -1432,10 +2499,10 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba,
        task_req_upiup =
                (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
        task_req_upiup->header.dword_0 =
-               cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
-                                             lrbp->lun, lrbp->task_tag));
+               UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
+                                             lrbp->lun, lrbp->task_tag);
        task_req_upiup->header.dword_1 =
-       cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
+               UPIU_HEADER_DWORD(0, tm_function, 0, 0);
 
        task_req_upiup->input_param1 = lrbp->lun;
        task_req_upiup->input_param1 =
@@ -1502,9 +2569,11 @@ static int ufshcd_device_reset(struct scsi_cmnd *cmd)
                        if (hba->lrb[pos].cmd) {
                                scsi_dma_unmap(hba->lrb[pos].cmd);
                                hba->lrb[pos].cmd->result =
-                                               DID_ABORT << 16;
+                                       DID_ABORT << 16;
                                hba->lrb[pos].cmd->scsi_done(cmd);
                                hba->lrb[pos].cmd = NULL;
+                               clear_bit_unlock(pos, &hba->lrb_in_use);
+                               wake_up(&hba->dev_cmd.tag_wq);
                        }
                }
        } /* end of for */
@@ -1572,6 +2641,9 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
        __clear_bit(tag, &hba->outstanding_reqs);
        hba->lrb[tag].cmd = NULL;
        spin_unlock_irqrestore(host->host_lock, flags);
+
+       clear_bit_unlock(tag, &hba->lrb_in_use);
+       wake_up(&hba->dev_cmd.tag_wq);
 out:
        return err;
 }
@@ -1587,8 +2659,22 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
        int ret;
 
        ret = ufshcd_link_startup(hba);
-       if (!ret)
-               scsi_scan_host(hba->host);
+       if (ret)
+               goto out;
+
+       ret = ufshcd_verify_dev_init(hba);
+       if (ret)
+               goto out;
+
+       ret = ufshcd_complete_dev_init(hba);
+       if (ret)
+               goto out;
+
+       ufshcd_force_reset_auto_bkops(hba);
+       scsi_scan_host(hba->host);
+       pm_runtime_put_sync(hba->dev);
+out:
+       return;
 }
 
 static struct scsi_host_template ufshcd_driver_template = {
@@ -1650,6 +2736,34 @@ int ufshcd_resume(struct ufs_hba *hba)
 }
 EXPORT_SYMBOL_GPL(ufshcd_resume);
 
+int ufshcd_runtime_suspend(struct ufs_hba *hba)
+{
+       if (!hba)
+               return 0;
+
+       /*
+        * The device is idle with no requests in the queue,
+        * allow background operations.
+        */
+       return ufshcd_enable_auto_bkops(hba);
+}
+EXPORT_SYMBOL(ufshcd_runtime_suspend);
+
+int ufshcd_runtime_resume(struct ufs_hba *hba)
+{
+       if (!hba)
+               return 0;
+
+       return ufshcd_disable_auto_bkops(hba);
+}
+EXPORT_SYMBOL(ufshcd_runtime_resume);
+
+int ufshcd_runtime_idle(struct ufs_hba *hba)
+{
+       return 0;
+}
+EXPORT_SYMBOL(ufshcd_runtime_idle);
+
 /**
  * ufshcd_remove - de-allocate SCSI host and host memory space
  *             data structure memory
@@ -1657,11 +2771,11 @@ EXPORT_SYMBOL_GPL(ufshcd_resume);
  */
 void ufshcd_remove(struct ufs_hba *hba)
 {
+       scsi_remove_host(hba->host);
        /* disable interrupts */
        ufshcd_disable_intr(hba, hba->intr_mask);
        ufshcd_hba_stop(hba);
 
-       scsi_remove_host(hba->host);
        scsi_host_put(hba->host);
 }
 EXPORT_SYMBOL_GPL(ufshcd_remove);
@@ -1740,10 +2854,17 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
 
        /* Initialize work queues */
        INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
+       INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
 
        /* Initialize UIC command mutex */
        mutex_init(&hba->uic_cmd_mutex);
 
+       /* Initialize mutex for device management commands */
+       mutex_init(&hba->dev_cmd.lock);
+
+       /* Initialize device management tag acquire wait queue */
+       init_waitqueue_head(&hba->dev_cmd.tag_wq);
+
        /* IRQ registration */
        err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
        if (err) {
@@ -1773,6 +2894,9 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
 
        *hba_handle = hba;
 
+       /* Hold auto suspend until async scan completes */
+       pm_runtime_get_sync(dev);
+
        async_schedule(ufshcd_async_scan, hba);
 
        return 0;