]> Git Repo - linux.git/commitdiff
Merge patch series "UFS Advanced RPMB"
authorMartin K. Petersen <[email protected]>
Fri, 30 Dec 2022 21:01:16 +0000 (21:01 +0000)
committerMartin K. Petersen <[email protected]>
Fri, 30 Dec 2022 21:01:16 +0000 (21:01 +0000)
Bean Huo <[email protected]> says:

This series of changes is to add support for UFS advanced RPMB in
ufs_bsg. The advanced RPMB application of user space is ufs_utils, the
reference code is at:

  https://github.com/beanhuo/ufs-utils-Micron/blob/ufs_arpmb/ufs_arpmb.c.

Changes to ufs_utils will be pushed to:

  https://github.com/westerndigitalcorporation/ufs-utils

Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Martin K. Petersen <[email protected]>
1  2 
drivers/ufs/core/ufshcd.c

index e18c9f4463ec592a70b95fa7e788e33286a86caf,1ecee6507e8858a7ce3b46c7ca1b81d45eab0207..5387603d311c24c81da677465ea7c28d431cfcf9
@@@ -56,6 -56,9 +56,9 @@@
  /* Query request timeout */
  #define QUERY_REQ_TIMEOUT 1500 /* 1.5 seconds */
  
+ /* Advanced RPMB request timeout */
+ #define ADVANCED_RPMB_REQ_TIMEOUT  3000 /* 3 seconds */
  /* Task management command timeout */
  #define TM_CMD_TIMEOUT        100 /* msecs */
  
@@@ -775,7 -778,7 +778,7 @@@ static inline void ufshcd_utrl_clear(st
  }
  
  /**
 - * ufshcd_utmrl_clear - Clear a bit in UTRMLCLR register
 + * ufshcd_utmrl_clear - Clear a bit in UTMRLCLR register
   * @hba: per adapter instance
   * @pos: position of the bit to be cleared
   */
@@@ -2399,38 -2402,30 +2402,30 @@@ int ufshcd_send_uic_cmd(struct ufs_hba 
  }
  
  /**
-  * ufshcd_map_sg - Map scatter-gather list to prdt
-  * @hba: per adapter instance
-  * @lrbp: pointer to local reference block
-  *
-  * Returns 0 in case of success, non-zero value in case of failure
+  * ufshcd_sgl_to_prdt - SG list to PRTD (Physical Region Description Table, 4DW format)
+  * @hba:      per-adapter instance
+  * @lrbp:     pointer to local reference block
+  * @sg_entries:       The number of sg lists actually used
+  * @sg_list:  Pointer to SG list
   */
- static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+ static void ufshcd_sgl_to_prdt(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, int sg_entries,
+                              struct scatterlist *sg_list)
  {
        struct ufshcd_sg_entry *prd_table;
        struct scatterlist *sg;
-       struct scsi_cmnd *cmd;
-       int sg_segments;
        int i;
  
-       cmd = lrbp->cmd;
-       sg_segments = scsi_dma_map(cmd);
-       if (sg_segments < 0)
-               return sg_segments;
-       if (sg_segments) {
+       if (sg_entries) {
  
                if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN)
                        lrbp->utr_descriptor_ptr->prd_table_length =
-                               cpu_to_le16((sg_segments *
-                                       sizeof(struct ufshcd_sg_entry)));
+                               cpu_to_le16((sg_entries * sizeof(struct ufshcd_sg_entry)));
                else
-                       lrbp->utr_descriptor_ptr->prd_table_length =
-                               cpu_to_le16(sg_segments);
+                       lrbp->utr_descriptor_ptr->prd_table_length = cpu_to_le16(sg_entries);
  
                prd_table = lrbp->ucd_prdt_ptr;
  
-               scsi_for_each_sg(cmd, sg, sg_segments, i) {
+               for_each_sg(sg_list, sg, sg_entries, i) {
                        const unsigned int len = sg_dma_len(sg);
  
                        /*
        } else {
                lrbp->utr_descriptor_ptr->prd_table_length = 0;
        }
+ }
+ /**
+  * ufshcd_map_sg - Map scatter-gather list to prdt
+  * @hba: per adapter instance
+  * @lrbp: pointer to local reference block
+  *
+  * Returns 0 in case of success, non-zero value in case of failure
+  */
+ static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+ {
+       struct scsi_cmnd *cmd = lrbp->cmd;
+       int sg_segments = scsi_dma_map(cmd);
+       if (sg_segments < 0)
+               return sg_segments;
+       ufshcd_sgl_to_prdt(hba, lrbp, sg_segments, scsi_sglist(cmd));
  
        return 0;
  }
@@@ -2496,14 -2509,15 +2509,15 @@@ static void ufshcd_disable_intr(struct 
  }
  
  /**
-  * ufshcd_prepare_req_desc_hdr() - Fills the requests header
+  * ufshcd_prepare_req_desc_hdr - Fill UTP Transfer request descriptor header according to request
   * descriptor according to request
   * @lrbp: pointer to local reference block
   * @upiu_flags: flags required in the header
   * @cmd_dir: requests data direction
+  * @ehs_length: Total EHS Length (in 32‐bytes units of all Extra Header Segments)
   */
- static void ufshcd_prepare_req_desc_hdr(struct ufshcd_lrb *lrbp,
-                       u8 *upiu_flags, enum dma_data_direction cmd_dir)
+ static void ufshcd_prepare_req_desc_hdr(struct ufshcd_lrb *lrbp, u8 *upiu_flags,
+                                       enum dma_data_direction cmd_dir, int ehs_length)
  {
        struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
        u32 data_direction;
                *upiu_flags = UPIU_CMD_FLAGS_NONE;
        }
  
-       dword_0 = data_direction | (lrbp->command_type
-                               << UPIU_COMMAND_TYPE_OFFSET);
+       dword_0 = data_direction | (lrbp->command_type << UPIU_COMMAND_TYPE_OFFSET) |
+               ehs_length << 8;
        if (lrbp->intr_cmd)
                dword_0 |= UTP_REQ_DESC_INT_CMD;
  
@@@ -2578,8 -2592,7 +2592,7 @@@ void ufshcd_prepare_utp_scsi_cmd_upiu(s
  }
  
  /**
-  * ufshcd_prepare_utp_query_req_upiu() - fills the utp_transfer_req_desc,
-  * for query requsts
+  * ufshcd_prepare_utp_query_req_upiu() - fill the utp_transfer_req_desc for query request
   * @hba: UFS hba
   * @lrbp: local reference block pointer
   * @upiu_flags: flags
@@@ -2650,7 -2663,7 +2663,7 @@@ static int ufshcd_compose_devman_upiu(s
        else
                lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;
  
-       ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE);
+       ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE, 0);
        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)
@@@ -2678,8 -2691,7 +2691,7 @@@ static int ufshcd_comp_scsi_upiu(struc
                lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;
  
        if (likely(lrbp->cmd)) {
-               ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags,
-                                               lrbp->cmd->sc_data_direction);
+               ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, lrbp->cmd->sc_data_direction, 0);
                ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
        } else {
                ret = -EINVAL;
@@@ -2945,6 -2957,12 +2957,12 @@@ ufshcd_dev_cmd_completion(struct ufs_hb
                dev_err(hba->dev, "%s: Reject UPIU not fully implemented\n",
                                __func__);
                break;
+       case UPIU_TRANSACTION_RESPONSE:
+               if (hba->dev_cmd.type != DEV_CMD_TYPE_RPMB) {
+                       err = -EINVAL;
+                       dev_err(hba->dev, "%s: unexpected response %x\n", __func__, resp);
+               }
+               break;
        default:
                err = -EINVAL;
                dev_err(hba->dev, "%s: Invalid device management cmd response: %x\n",
@@@ -3100,7 -3118,7 +3118,7 @@@ static int ufshcd_query_flag_retry(stru
  
        if (ret)
                dev_err(hba->dev,
 -                      "%s: query attribute, opcode %d, idn %d, failed with error %d after %d retries\n",
 +                      "%s: query flag, opcode %d, idn %d, failed with error %d after %d retries\n",
                        __func__, opcode, idn, ret, retries);
        return ret;
  }
@@@ -4944,6 -4962,12 +4962,12 @@@ static void ufshcd_lu_init(struct ufs_h
            desc_buf[UNIT_DESC_PARAM_LU_WR_PROTECT] == UFS_LU_POWER_ON_WP)
                hba->dev_info.is_lu_power_on_wp = true;
  
+       /* In case of RPMB LU, check if advanced RPMB mode is enabled */
+       if (desc_buf[UNIT_DESC_PARAM_UNIT_INDEX] == UFS_UPIU_RPMB_WLUN &&
+           desc_buf[RPMB_UNIT_DESC_PARAM_REGION_EN] & BIT(4))
+               hba->dev_info.b_advanced_rpmb_en = true;
        kfree(desc_buf);
  set_qdepth:
        /*
@@@ -6868,7 -6892,7 +6892,7 @@@ static int ufshcd_issue_devman_upiu_cmd
        /* update the task tag in the request upiu */
        req_upiu->header.dword_0 |= cpu_to_be32(tag);
  
-       ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE);
+       ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE, 0);
  
        /* just copy the upiu request as it is */
        memcpy(lrbp->ucd_req_ptr, req_upiu, sizeof(*lrbp->ucd_req_ptr));
@@@ -6991,6 -7015,100 +7015,100 @@@ int ufshcd_exec_raw_upiu_cmd(struct ufs
        return err;
  }
  
+ /**
+  * ufshcd_advanced_rpmb_req_handler - handle advanced RPMB request
+  * @hba:      per adapter instance
+  * @req_upiu: upiu request
+  * @rsp_upiu: upiu reply
+  * @req_ehs:  EHS field which contains Advanced RPMB Request Message
+  * @rsp_ehs:  EHS field which returns Advanced RPMB Response Message
+  * @sg_cnt:   The number of sg lists actually used
+  * @sg_list:  Pointer to SG list when DATA IN/OUT UPIU is required in ARPMB operation
+  * @dir:      DMA direction
+  *
+  * Returns zero on success, non-zero on failure
+  */
+ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *req_upiu,
+                        struct utp_upiu_req *rsp_upiu, struct ufs_ehs *req_ehs,
+                        struct ufs_ehs *rsp_ehs, int sg_cnt, struct scatterlist *sg_list,
+                        enum dma_data_direction dir)
+ {
+       DECLARE_COMPLETION_ONSTACK(wait);
+       const u32 tag = hba->reserved_slot;
+       struct ufshcd_lrb *lrbp;
+       int err = 0;
+       int result;
+       u8 upiu_flags;
+       u8 *ehs_data;
+       u16 ehs_len;
+       /* Protects use of hba->reserved_slot. */
+       ufshcd_hold(hba, false);
+       mutex_lock(&hba->dev_cmd.lock);
+       down_read(&hba->clk_scaling_lock);
+       lrbp = &hba->lrb[tag];
+       WARN_ON(lrbp->cmd);
+       lrbp->cmd = NULL;
+       lrbp->task_tag = tag;
+       lrbp->lun = UFS_UPIU_RPMB_WLUN;
+       lrbp->intr_cmd = true;
+       ufshcd_prepare_lrbp_crypto(NULL, lrbp);
+       hba->dev_cmd.type = DEV_CMD_TYPE_RPMB;
+       /* Advanced RPMB starts from UFS 4.0, so its command type is UTP_CMD_TYPE_UFS_STORAGE */
+       lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;
+       ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, dir, 2);
+       /* update the task tag and LUN in the request upiu */
+       req_upiu->header.dword_0 |= cpu_to_be32(upiu_flags << 16 | UFS_UPIU_RPMB_WLUN << 8 | tag);
+       /* copy the UPIU(contains CDB) request as it is */
+       memcpy(lrbp->ucd_req_ptr, req_upiu, sizeof(*lrbp->ucd_req_ptr));
+       /* Copy EHS, starting with byte32, immediately after the CDB package */
+       memcpy(lrbp->ucd_req_ptr + 1, req_ehs, sizeof(*req_ehs));
+       if (dir != DMA_NONE && sg_list)
+               ufshcd_sgl_to_prdt(hba, lrbp, sg_cnt, sg_list);
+       memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
+       hba->dev_cmd.complete = &wait;
+       ufshcd_send_command(hba, tag);
+       err = ufshcd_wait_for_dev_cmd(hba, lrbp, ADVANCED_RPMB_REQ_TIMEOUT);
+       if (!err) {
+               /* Just copy the upiu response as it is */
+               memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu));
+               /* Get the response UPIU result */
+               result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+               ehs_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) >> 24;
+               /*
+                * Since the bLength in EHS indicates the total size of the EHS Header and EHS Data
+                * in 32 Byte units, the value of the bLength Request/Response for Advanced RPMB
+                * Message is 02h
+                */
+               if (ehs_len == 2 && rsp_ehs) {
+                       /*
+                        * ucd_rsp_ptr points to a buffer with a length of 512 bytes
+                        * (ALIGNED_UPIU_SIZE = 512), and the EHS data just starts from byte32
+                        */
+                       ehs_data = (u8 *)lrbp->ucd_rsp_ptr + EHS_OFFSET_IN_RESPONSE;
+                       memcpy(rsp_ehs, ehs_data, ehs_len * 32);
+               }
+       }
+       up_read(&hba->clk_scaling_lock);
+       mutex_unlock(&hba->dev_cmd.lock);
+       ufshcd_release(hba);
+       return err ? : result;
+ }
  /**
   * ufshcd_eh_device_reset_handler() - Reset a single logical unit.
   * @cmd: SCSI command pointer
@@@ -9580,7 -9698,6 +9698,7 @@@ void ufshcd_remove(struct ufs_hba *hba
        ufshpb_remove(hba);
        ufs_sysfs_remove_nodes(hba->dev);
        blk_mq_destroy_queue(hba->tmf_queue);
 +      blk_put_queue(hba->tmf_queue);
        blk_mq_free_tag_set(&hba->tmf_tag_set);
        scsi_remove_host(hba->host);
        /* disable interrupts */
@@@ -9877,7 -9994,6 +9995,7 @@@ int ufshcd_init(struct ufs_hba *hba, vo
  
  free_tmf_queue:
        blk_mq_destroy_queue(hba->tmf_queue);
 +      blk_put_queue(hba->tmf_queue);
  free_tmf_tag_set:
        blk_mq_free_tag_set(&hba->tmf_tag_set);
  out_remove_scsi_host:
This page took 0.104726 seconds and 4 git commands to generate.