ufs: introduce well known logical unit in ufs
authorSubhash Jadavani <subhashj@codeaurora.org>
Thu, 25 Sep 2014 12:32:29 +0000 (15:32 +0300)
committerChristoph Hellwig <hch@lst.de>
Wed, 1 Oct 2014 11:11:22 +0000 (13:11 +0200)
UFS device may have standard LUs and LUN id could be from 0x00 to 0x7F.
UFS device specification use "Peripheral Device Addressing Format"
(SCSI SAM-5) for standard LUs.

UFS device may also have the Well Known LUs (also referred as W-LU) which
again could be from 0x00 to 0x7F. For W-LUs, UFS device specification only
allows the "Extended Addressing Format" (SCSI SAM-5) which means the W-LUNs
would start from 0xC100 onwards.

This means max. LUN number reported from UFS device could be 0xC17F hence
this patch advertise the "max_lun" as 0xC17F which will allow SCSI mid
layer to detect the W-LUs as well.

But once the W-LUs are detected, UFSHCD driver may get the commands with
SCSI LUN id upto 0xC17F but UPIU LUN id field is only 8-bit wide so it
requires the mapping of SCSI LUN id to UPIU LUN id. This patch also add
support for this mapping.

Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: Dolev Raviv <draviv@codeaurora.org>
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/scsi/ufs/ufs.h
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h

index 4ca99ed..37d64c1 100644 (file)
 #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
                        cpu_to_be32((byte3 << 24) | (byte2 << 16) |\
                         (byte1 << 8) | (byte0))
-
+/*
+ * UFS device may have standard LUs and LUN id could be from 0x00 to
+ * 0x7F. Standard LUs use "Peripheral Device Addressing Format".
+ * UFS device may also have the Well Known LUs (also referred as W-LU)
+ * which again could be from 0x00 to 0x7F. For W-LUs, device only use
+ * the "Extended Addressing Format" which means the W-LUNs would be
+ * from 0xc100 (SCSI_W_LUN_BASE) onwards.
+ * This means max. LUN number reported from UFS device could be 0xC17F.
+ */
+#define UFS_UPIU_MAX_UNIT_NUM_ID       0x7F
+#define UFS_MAX_LUNS           (SCSI_W_LUN_BASE + UFS_UPIU_MAX_UNIT_NUM_ID)
+#define UFS_UPIU_WLUN_ID       (1 << 7)
 #define UFS_UPIU_MAX_GENERAL_LUN       8
 
+/* Well known logical unit id in LUN field of UPIU */
+enum {
+       UFS_UPIU_REPORT_LUNS_WLUN       = 0x81,
+       UFS_UPIU_UFS_DEVICE_WLUN        = 0xD0,
+       UFS_UPIU_BOOT_WLUN              = 0xB0,
+       UFS_UPIU_RPMB_WLUN              = 0xC4,
+};
+
 /*
  * UFS Protocol Information Unit related definitions
  */
index 692fd7a..a1eae49 100644 (file)
@@ -100,7 +100,6 @@ static u32 ufs_query_desc_max_size[] = {
 enum {
        UFSHCD_MAX_CHANNEL      = 0,
        UFSHCD_MAX_ID           = 1,
-       UFSHCD_MAX_LUNS         = 8,
        UFSHCD_CMD_PER_LUN      = 32,
        UFSHCD_CAN_QUEUE        = 32,
 };
@@ -901,6 +900,21 @@ static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
        return ret;
 }
 
+/*
+ * ufshcd_scsi_to_upiu_lun - maps scsi LUN to UPIU LUN
+ * @scsi_lun: scsi LUN id
+ *
+ * Returns UPIU LUN id
+ */
+static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
+{
+       if (scsi_is_wlun(scsi_lun))
+               return (scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID)
+                       | UFS_UPIU_WLUN_ID;
+       else
+               return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID;
+}
+
 /**
  * ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID
  * @scsi_lun: UPIU W-LUN id
@@ -970,7 +984,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
        lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
        lrbp->sense_buffer = cmd->sense_buffer;
        lrbp->task_tag = tag;
-       lrbp->lun = cmd->device->lun;
+       lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
        lrbp->intr_cmd = false;
        lrbp->command_type = UTP_CMD_TYPE_SCSI;
 
@@ -1524,7 +1538,7 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba,
         * Unit descriptors are only available for general purpose LUs (LUN id
         * from 0 to 7) and RPMB Well known LU.
         */
-       if (lun >= UFS_UPIU_MAX_GENERAL_LUN)
+       if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
                return -EOPNOTSUPP;
 
        return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
@@ -2155,6 +2169,44 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)
 }
 
 /**
+ * ufshcd_set_queue_depth - set lun queue depth
+ * @sdev: pointer to SCSI device
+ *
+ * Read bLUQueueDepth value and activate scsi tagged command
+ * queueing. For WLUN, queue depth is set to 1. For best-effort
+ * cases (bLUQueueDepth = 0) the queue depth is set to a maximum
+ * value that host can queue.
+ */
+static void ufshcd_set_queue_depth(struct scsi_device *sdev)
+{
+       int ret = 0;
+       u8 lun_qdepth;
+       struct ufs_hba *hba;
+
+       hba = shost_priv(sdev->host);
+
+       lun_qdepth = hba->nutrs;
+       ret = ufshcd_read_unit_desc_param(hba,
+                                         ufshcd_scsi_to_upiu_lun(sdev->lun),
+                                         UNIT_DESC_PARAM_LU_Q_DEPTH,
+                                         &lun_qdepth,
+                                         sizeof(lun_qdepth));
+
+       /* Some WLUN doesn't support unit descriptor */
+       if (ret == -EOPNOTSUPP)
+               lun_qdepth = 1;
+       else if (!lun_qdepth)
+               /* eventually, we can figure out the real queue depth */
+               lun_qdepth = hba->nutrs;
+       else
+               lun_qdepth = min_t(int, lun_qdepth, hba->nutrs);
+
+       dev_dbg(hba->dev, "%s: activate tcq with queue depth %d\n",
+                       __func__, lun_qdepth);
+       scsi_activate_tcq(sdev, lun_qdepth);
+}
+
+/**
  * ufshcd_slave_alloc - handle initial SCSI device configurations
  * @sdev: pointer to SCSI device
  *
@@ -2163,8 +2215,6 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)
 static int ufshcd_slave_alloc(struct scsi_device *sdev)
 {
        struct ufs_hba *hba;
-       u8 lun_qdepth;
-       int ret;
 
        hba = shost_priv(sdev->host);
        sdev->tagged_supported = 1;
@@ -2179,20 +2229,8 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev)
        /* REPORT SUPPORTED OPERATION CODES is not supported */
        sdev->no_report_opcodes = 1;
 
-       ret = ufshcd_read_unit_desc_param(hba,
-                                         sdev->lun,
-                                         UNIT_DESC_PARAM_LU_Q_DEPTH,
-                                         &lun_qdepth,
-                                         sizeof(lun_qdepth));
-       if (!ret || !lun_qdepth)
-               /* eventually, we can figure out the real queue depth */
-               lun_qdepth = hba->nutrs;
-       else
-               lun_qdepth = min_t(int, lun_qdepth, hba->nutrs);
 
-       dev_dbg(hba->dev, "%s: activate tcq with queue depth %d\n",
-                       __func__, lun_qdepth);
-       scsi_activate_tcq(sdev, lun_qdepth);
+       ufshcd_set_queue_depth(sdev);
 
        return 0;
 }
@@ -2255,6 +2293,9 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev)
 
        hba = shost_priv(sdev->host);
        scsi_deactivate_tcq(sdev, hba->nutrs);
+       /* Drop the reference as it won't be needed anymore */
+       if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN)
+               hba->sdev_ufs_device = NULL;
 }
 
 /**
@@ -2972,7 +3013,10 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
                                              lun_id, task_tag);
        task_req_upiup->header.dword_1 =
                UPIU_HEADER_DWORD(0, tm_function, 0, 0);
-
+       /*
+        * The host shall provide the same value for LUN field in the basic
+        * header and for Input Parameter.
+        */
        task_req_upiup->input_param1 = cpu_to_be32(lun_id);
        task_req_upiup->input_param2 = cpu_to_be32(task_id);
 
@@ -4121,7 +4165,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
        host->can_queue = hba->nutrs;
        host->cmd_per_lun = hba->nutrs;
        host->max_id = UFSHCD_MAX_ID;
-       host->max_lun = UFSHCD_MAX_LUNS;
+       host->max_lun = UFS_MAX_LUNS;
        host->max_channel = UFSHCD_MAX_CHANNEL;
        host->unique_id = host->host_no;
        host->max_cmd_len = MAX_CDB_SIZE;
index 25065ea..5c25337 100644 (file)
@@ -124,7 +124,7 @@ struct ufshcd_lrb {
 
        int command_type;
        int task_tag;
-       unsigned int lun;
+       u8 lun; /* UPIU LUN id field is only 8-bit wide */
        bool intr_cmd;
 };