The branch main has been updated by jaeyoon:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=31407551c3d4d29905f77abddac3fd9b649e166b

commit 31407551c3d4d29905f77abddac3fd9b649e166b
Author:     Jaeyoon Choi <[email protected]>
AuthorDate: 2025-12-01 04:39:49 +0000
Commit:     Jaeyoon Choi <[email protected]>
CommitDate: 2025-12-01 04:41:03 +0000

    ufshci: Add a check for WLUN during driver initialization
    
    This patch checks whether wlun is registered as a periph device.
    It also implements a function to issue an SSU.
    
    Reviewed by:            imp (mentor)
    Sponsored by:           Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D53923
---
 sys/dev/ufshci/ufshci_ctrlr.c   |  12 ++---
 sys/dev/ufshci/ufshci_dev.c     |  14 ++++-
 sys/dev/ufshci/ufshci_private.h |  14 +++++
 sys/dev/ufshci/ufshci_sim.c     | 114 +++++++++++++++++++++++++++++++++++++++-
 sys/dev/ufshci/ufshci_sysctl.c  |   4 ++
 5 files changed, 150 insertions(+), 8 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index 35663b480cfa..1ffa99255088 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -79,12 +79,6 @@ ufshci_ctrlr_start(struct ufshci_controller *ctrlr, bool 
resetting)
                return;
        }
 
-       /* Initialize UFS Power Mode */
-       if (ufshci_dev_init_ufs_power_mode(ctrlr) != 0) {
-               ufshci_ctrlr_fail(ctrlr);
-               return;
-       }
-
        /* Read Controller Descriptor (Device, Geometry) */
        if (ufshci_dev_get_descriptor(ctrlr) != 0) {
                ufshci_ctrlr_fail(ctrlr);
@@ -109,6 +103,12 @@ ufshci_ctrlr_start(struct ufshci_controller *ctrlr, bool 
resetting)
                return;
        }
 
+       /* Initialize UFS Power Mode */
+       if (ufshci_dev_init_ufs_power_mode(ctrlr) != 0) {
+               ufshci_ctrlr_fail(ctrlr);
+               return;
+       }
+
        TSEXIT();
 }
 
diff --git a/sys/dev/ufshci/ufshci_dev.c b/sys/dev/ufshci/ufshci_dev.c
index 975468e5156f..9696e8ea8091 100644
--- a/sys/dev/ufshci/ufshci_dev.c
+++ b/sys/dev/ufshci/ufshci_dev.c
@@ -455,8 +455,20 @@ ufshci_dev_init_uic_power_mode(struct ufshci_controller 
*ctrlr)
 int
 ufshci_dev_init_ufs_power_mode(struct ufshci_controller *ctrlr)
 {
-       /* TODO: Need to implement */
+       ctrlr->ufs_dev.power_mode_supported = false;
 
+       if (ctrlr->quirks & UFSHCI_QUIRK_SKIP_WELL_KNOWN_LUNS)
+               return (0);
+
+       ctrlr->ufs_device_wlun_periph = ufshci_sim_find_periph(ctrlr,
+           UFSHCI_WLUN_UFS_DEVICE);
+       if (ctrlr->ufs_device_wlun_periph == NULL) {
+               ufshci_printf(ctrlr,
+                   "Well-known LUN `UFS Device (0x50)` not found\n");
+               return (0);
+       }
+
+       ctrlr->ufs_dev.power_mode_supported = true;
        return (0);
 }
 
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index 3cee021880a8..22e77c9b9326 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -31,6 +31,8 @@
 
 #include <machine/bus.h>
 
+#include <cam/cam.h>
+
 #include "ufshci.h"
 
 MALLOC_DECLARE(M_UFSHCI);
@@ -247,6 +249,9 @@ struct ufshci_device {
        uint32_t wb_user_space_config_option;
        uint8_t wb_dedicated_lu;
        uint32_t write_booster_flush_threshold;
+
+       /* Power mode */
+       bool power_mode_supported;
 };
 
 /*
@@ -274,6 +279,9 @@ struct ufshci_controller {
        struct cam_sim *ufshci_sim;
        struct cam_path *ufshci_path;
 
+       struct cam_periph *ufs_device_wlun_periph;
+       struct mtx ufs_device_wlun_mtx;
+
        struct mtx sc_mtx;
        uint32_t sc_unit;
        uint8_t sc_name[16];
@@ -371,8 +379,14 @@ void ufshci_completion_poll_cb(void *arg, const struct 
ufshci_completion *cpl,
     bool error);
 
 /* SIM */
+uint8_t ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun);
+uint64_t ufshci_sim_translate_ufs_to_scsi_lun(uint8_t ufs_lun);
 int ufshci_sim_attach(struct ufshci_controller *ctrlr);
 void ufshci_sim_detach(struct ufshci_controller *ctrlr);
+struct cam_periph *ufshci_sim_find_periph(struct ufshci_controller *ctrlr,
+    uint8_t wlun);
+int ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start,
+    int pwr_cond, bool immed);
 
 /* Controller */
 int ufshci_ctrlr_construct(struct ufshci_controller *ctrlr, device_t dev);
diff --git a/sys/dev/ufshci/ufshci_sim.c b/sys/dev/ufshci/ufshci_sim.c
index db2e194b699b..0cd691de47e2 100644
--- a/sys/dev/ufshci/ufshci_sim.c
+++ b/sys/dev/ufshci/ufshci_sim.c
@@ -10,9 +10,11 @@
 #include <cam/cam.h>
 #include <cam/cam_ccb.h>
 #include <cam/cam_debug.h>
+#include <cam/cam_periph.h>
 #include <cam/cam_sim.h>
 #include <cam/cam_xpt_sim.h>
 #include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
 
 #include "ufshci_private.h"
 
@@ -76,7 +78,7 @@ ufshci_sim_illegal_request(union ccb *ccb)
  * The SCSI LUN format and the UFS UPIU LUN format are different.
  * This function converts the SCSI LUN format to the UFS UPIU LUN format.
  */
-static uint8_t
+uint8_t
 ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun)
 {
        const int address_format_offset = 8;
@@ -94,6 +96,19 @@ ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun)
        return (scsi_lun & UFSHCI_UPIU_UNIT_NUMBER_ID_MASK);
 }
 
+uint64_t
+ufshci_sim_translate_ufs_to_scsi_lun(uint8_t ufs_lun)
+{
+       /* Logical unit */
+       if (!(ufs_lun & UFSHCI_UPIU_WLUN_ID_MASK)) {
+               return ufs_lun;
+       }
+
+       /* Well known logical unit */
+       return (((uint64_t)ufs_lun & ~UFSHCI_UPIU_WLUN_ID_MASK) |
+           (RPL_LUNDATA_ATYP_EXTLUN | RPL_LUNDATA_EXT_EAM_WK) << 8);
+}
+
 static void
 ufshchi_sim_scsiio(struct cam_sim *sim, union ccb *ccb)
 {
@@ -396,3 +411,100 @@ ufshci_sim_detach(struct ufshci_controller *ctrlr)
                ctrlr->ufshci_sim = NULL;
        }
 }
+
+struct cam_periph *
+ufshci_sim_find_periph(struct ufshci_controller *ctrlr, uint8_t wlun)
+{
+       struct cam_path *path;
+       struct cam_periph *periph = NULL;
+       uint64_t scsi_lun;
+       uint64_t timeout;
+
+       scsi_lun = ufshci_sim_translate_ufs_to_scsi_lun(wlun);
+
+       if (xpt_create_path(&path, /*periph*/ NULL,
+               cam_sim_path(ctrlr->ufshci_sim), 0, scsi_lun) != CAM_REQ_CMP) {
+               return NULL;
+       }
+
+       /* Wait for the perip device to be found */
+       timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms);
+
+       while (1) {
+               xpt_path_lock(path);
+               periph = cam_periph_find(path, "pass");
+               xpt_path_unlock(path);
+
+               if (periph) {
+                       xpt_free_path(path);
+                       break;
+               }
+
+               if (timeout - ticks < 0) {
+                       ufshci_printf(ctrlr,
+                           "Failed to find the Well known LUN(0x%x)\n", wlun);
+                       break;
+               }
+
+               pause_sbt("ufshci_find_periph", ustosbt(100), 0, C_PREL(1));
+       }
+
+       return periph;
+}
+
+/* This function is called during suspend/resume. */
+int
+ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start,
+    int power_condition, bool immed)
+{
+       struct cam_periph *periph = ctrlr->ufs_device_wlun_periph;
+       union ccb *ccb;
+       int err;
+
+       /* Acquire periph reference */
+       if (periph && cam_periph_acquire(periph) != 0) {
+               periph = NULL;
+       }
+
+       if (periph == NULL) {
+               /* If the periph device does not exist, it will try to find it
+                * again */
+               periph = ufshci_sim_find_periph(ctrlr,
+                   (uint8_t)UFSHCI_WLUN_UFS_DEVICE);
+               if (periph)
+                       ctrlr->ufs_device_wlun_periph = periph;
+       }
+
+       if (periph == NULL) {
+               ufshci_printf(ctrlr,
+                   "Well-known LUN `UFS Device (0x50)` not found\n");
+               return ENODEV;
+       }
+       cam_periph_lock(periph);
+       ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+       if (!ccb) {
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
+               return ENOMEM;
+       }
+
+       scsi_start_stop(&ccb->csio,
+           /*retries*/ 4,
+           /*cbfcnp*/ NULL,
+           /*tag_action*/ MSG_SIMPLE_Q_TAG,
+           /*start*/ start ? 1 : 0,
+           /*load_eject*/ 0,
+           /*immediate*/ immed ? 1 : 0,
+           /*power_condition*/ power_condition, SSD_MIN_SIZE,
+           ctrlr->device_init_timeout_in_ms);
+
+       ccb->ccb_h.flags |= CAM_DIR_NONE | CAM_DEV_QFRZDIS;
+
+       err = cam_periph_runccb(ccb, NULL, 0, SF_RETRY_UA, NULL);
+
+       cam_periph_unlock(periph);
+       /* Release periph reference */
+       cam_periph_release(periph);
+
+       return (err == 0) ? 0 : EIO;
+}
diff --git a/sys/dev/ufshci/ufshci_sysctl.c b/sys/dev/ufshci/ufshci_sysctl.c
index 56bc06b13f3c..3ec4ea935464 100644
--- a/sys/dev/ufshci/ufshci_sysctl.c
+++ b/sys/dev/ufshci/ufshci_sysctl.c
@@ -197,6 +197,10 @@ ufshci_sysctl_initialize_ctrlr(struct ufshci_controller 
*ctrlr)
            &dev->wb_user_space_config_option, 0,
            "WriteBooster preserve user space mode");
 
+       SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO, "power_mode_supported",
+           CTLFLAG_RD, &dev->power_mode_supported, 0,
+           "Device power mode support");
+
        SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO, "timeout_period",
            CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, &ctrlr->timeout_period,
            0, ufshci_sysctl_timeout_period, "IU",

Reply via email to