The branch main has been updated by jaeyoon:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=3a99f31fdb6c66434f77d83ca5166aae4ad2d22c

commit 3a99f31fdb6c66434f77d83ca5166aae4ad2d22c
Author:     Jaeyoon Choi <[email protected]>
AuthorDate: 2025-12-03 04:05:36 +0000
Commit:     Jaeyoon Choi <[email protected]>
CommitDate: 2025-12-03 04:05:36 +0000

    ufshci: Support suspend/resume
    
    Handle system power events and issue START STOP UNIT (SSU) to the
    UFS Device WLUN (0x50).
    
    Reviewed by:            imp (mentor)
    Sponsored by:           Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D54002
---
 sys/dev/ufshci/ufshci_ctrlr.c     | 50 +++++++++++++++++++++++++++++++++++++++
 sys/dev/ufshci/ufshci_dev.c       | 19 +++++++++++++++
 sys/dev/ufshci/ufshci_pci.c       | 23 ++++++++++++++++--
 sys/dev/ufshci/ufshci_private.h   | 35 ++++++++++++++++++++++++++-
 sys/dev/ufshci/ufshci_req_queue.c |  3 ++-
 sys/dev/ufshci/ufshci_sim.c       |  7 +++++-
 sys/dev/ufshci/ufshci_sysctl.c    |  3 +++
 7 files changed, 135 insertions(+), 5 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index 1ffa99255088..f0cb08b34823 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -610,3 +610,53 @@ ufshci_reg_dump(struct ufshci_controller *ctrlr)
 
        ufshci_printf(ctrlr, "========================================\n");
 }
+
+int
+ufshci_ctrlr_suspend(struct ufshci_controller *ctrlr, enum power_stype stype)
+{
+       int error;
+
+       if (!ctrlr->ufs_dev.power_mode_supported)
+               return (0);
+
+       /* TODO: Need to flush the request queue */
+
+       if (ctrlr->ufs_device_wlun_periph) {
+               ctrlr->ufs_dev.power_mode = power_map[stype].dev_pwr;
+               error = ufshci_sim_send_ssu(ctrlr, /*start*/ false,
+                   power_map[stype].ssu_pc, /*immed*/ false);
+               if (error) {
+                       ufshci_printf(ctrlr,
+                           "Failed to send SSU in suspend handler\n");
+                       return (error);
+               }
+       }
+
+       /* TODO: Change the link state to Hibernate if necessary. */
+
+       return (0);
+}
+
+int
+ufshci_ctrlr_resume(struct ufshci_controller *ctrlr, enum power_stype stype)
+{
+       int error;
+
+       if (!ctrlr->ufs_dev.power_mode_supported)
+               return (0);
+
+       /* TODO: Change the link state to Active if necessary. */
+
+       if (ctrlr->ufs_device_wlun_periph) {
+               ctrlr->ufs_dev.power_mode = power_map[stype].dev_pwr;
+               error = ufshci_sim_send_ssu(ctrlr, /*start*/ false,
+                   power_map[stype].ssu_pc, /*immed*/ false);
+               if (error) {
+                       ufshci_printf(ctrlr,
+                           "Failed to send SSU in resume handler\n");
+                       return (error);
+               }
+       }
+
+       return (0);
+}
diff --git a/sys/dev/ufshci/ufshci_dev.c b/sys/dev/ufshci/ufshci_dev.c
index 9696e8ea8091..136823523fd3 100644
--- a/sys/dev/ufshci/ufshci_dev.c
+++ b/sys/dev/ufshci/ufshci_dev.c
@@ -469,6 +469,8 @@ ufshci_dev_init_ufs_power_mode(struct ufshci_controller 
*ctrlr)
        }
 
        ctrlr->ufs_dev.power_mode_supported = true;
+       ctrlr->ufs_dev.power_mode = UFSHCI_DEV_PWR_ACTIVE;
+
        return (0);
 }
 
@@ -786,3 +788,20 @@ out:
        ufshci_dev_disable_write_booster(ctrlr);
        return (error);
 }
+
+int
+ufshci_dev_get_current_power_mode(struct ufshci_controller *ctrlr,
+    uint8_t *power_mode)
+{
+       uint64_t value;
+       int err;
+
+       err = ufshci_dev_read_attribute(ctrlr, UFSHCI_ATTR_B_CURRENT_POWER_MODE,
+           /*index*/ 0, /*selector*/ 0, &value);
+       if (err)
+               return (err);
+
+       *power_mode = (uint8_t)value;
+
+       return (0);
+}
diff --git a/sys/dev/ufshci/ufshci_pci.c b/sys/dev/ufshci/ufshci_pci.c
index 7f78e462db72..5fce14997784 100644
--- a/sys/dev/ufshci/ufshci_pci.c
+++ b/sys/dev/ufshci/ufshci_pci.c
@@ -23,6 +23,8 @@
 static int ufshci_pci_probe(device_t);
 static int ufshci_pci_attach(device_t);
 static int ufshci_pci_detach(device_t);
+static int ufshci_pci_suspend(device_t);
+static int ufshci_pci_resume(device_t);
 
 static int ufshci_pci_setup_interrupts(struct ufshci_controller *ctrlr);
 
@@ -31,8 +33,8 @@ static device_method_t ufshci_pci_methods[] = {
        DEVMETHOD(device_probe, ufshci_pci_probe),
        DEVMETHOD(device_attach, ufshci_pci_attach),
        DEVMETHOD(device_detach, ufshci_pci_detach),
-       /* TODO: Implement Suspend, Resume */
-       { 0, 0 }
+       DEVMETHOD(device_suspend, ufshci_pci_suspend),
+       DEVMETHOD(device_resume, ufshci_pci_resume), { 0, 0 }
 };
 
 static driver_t ufshci_pci_driver = {
@@ -261,3 +263,20 @@ msi:
 intx:
        return (ufshci_pci_setup_shared(ctrlr, ctrlr->msi_count > 0 ? 1 : 0));
 }
+
+static int
+ufshci_pci_suspend(device_t dev)
+{
+       struct ufshci_controller *ctrlr = device_get_softc(dev);
+
+       /* Currently, PCI-based ufshci only supports POWER_STYPE_STANDBY */
+       return (ufshci_ctrlr_suspend(ctrlr, POWER_STYPE_STANDBY));
+}
+
+static int
+ufshci_pci_resume(device_t dev)
+{
+       struct ufshci_controller *ctrlr = device_get_softc(dev);
+
+       return (ufshci_ctrlr_resume(ctrlr, POWER_STYPE_AWAKE));
+}
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index 22e77c9b9326..fa5caf87196c 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -26,12 +26,14 @@
 #include <sys/memdesc.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
+#include <sys/power.h>
 #include <sys/rman.h>
 #include <sys/taskqueue.h>
 
 #include <machine/bus.h>
 
 #include <cam/cam.h>
+#include <cam/scsi/scsi_all.h>
 
 #include "ufshci.h"
 
@@ -233,6 +235,30 @@ struct ufshci_req_queue {
        bus_dmamap_t ucdmem_map;
 };
 
+enum ufshci_dev_pwr {
+       UFSHCI_DEV_PWR_ACTIVE = 0,
+       UFSHCI_DEV_PWR_SLEEP,
+       UFSHCI_DEV_PWR_POWERDOWN,
+       UFSHCI_DEV_PWR_DEEPSLEEP,
+       UFSHCI_DEV_PWR_COUNT,
+};
+
+struct ufshci_power_entry {
+       enum ufshci_dev_pwr dev_pwr;
+       uint8_t ssu_pc; /* SSU Power Condition */
+};
+
+/* SSU Power Condition 0x40 is defined in the UFS specification */
+static const struct ufshci_power_entry power_map[POWER_STYPE_COUNT] = {
+       [POWER_STYPE_AWAKE] = { UFSHCI_DEV_PWR_ACTIVE, SSS_PC_ACTIVE },
+       [POWER_STYPE_STANDBY] = { UFSHCI_DEV_PWR_SLEEP, SSS_PC_IDLE },
+       [POWER_STYPE_SUSPEND_TO_MEM] = { UFSHCI_DEV_PWR_POWERDOWN,
+           SSS_PC_STANDBY },
+       [POWER_STYPE_SUSPEND_TO_IDLE] = { UFSHCI_DEV_PWR_SLEEP, SSS_PC_IDLE },
+       [POWER_STYPE_HIBERNATE] = { UFSHCI_DEV_PWR_DEEPSLEEP, 0x40 },
+       [POWER_STYPE_POWEROFF] = { UFSHCI_DEV_PWR_POWERDOWN, SSS_PC_STANDBY },
+};
+
 struct ufshci_device {
        uint32_t max_lun_count;
 
@@ -252,6 +278,7 @@ struct ufshci_device {
 
        /* Power mode */
        bool power_mode_supported;
+       enum ufshci_dev_pwr power_mode;
 };
 
 /*
@@ -386,12 +413,16 @@ 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);
+    uint8_t pwr_cond, bool immed);
 
 /* Controller */
 int ufshci_ctrlr_construct(struct ufshci_controller *ctrlr, device_t dev);
 void ufshci_ctrlr_destruct(struct ufshci_controller *ctrlr, device_t dev);
 void ufshci_ctrlr_reset(struct ufshci_controller *ctrlr);
+int ufshci_ctrlr_suspend(struct ufshci_controller *ctrlr,
+    enum power_stype stype);
+int ufshci_ctrlr_resume(struct ufshci_controller *ctrlr,
+    enum power_stype stype);
 /* ctrlr defined as void * to allow use with config_intrhook. */
 void ufshci_ctrlr_start_config_hook(void *arg);
 void ufshci_ctrlr_poll(struct ufshci_controller *ctrlr);
@@ -415,6 +446,8 @@ int ufshci_dev_init_uic_power_mode(struct ufshci_controller 
*ctrlr);
 int ufshci_dev_init_ufs_power_mode(struct ufshci_controller *ctrlr);
 int ufshci_dev_get_descriptor(struct ufshci_controller *ctrlr);
 int ufshci_dev_config_write_booster(struct ufshci_controller *ctrlr);
+int ufshci_dev_get_current_power_mode(struct ufshci_controller *ctrlr,
+    uint8_t *power_mode);
 
 /* Controller Command */
 void ufshci_ctrlr_cmd_send_task_mgmt_request(struct ufshci_controller *ctrlr,
diff --git a/sys/dev/ufshci/ufshci_req_queue.c 
b/sys/dev/ufshci/ufshci_req_queue.c
index 7aa164d00bec..df7e4b159278 100644
--- a/sys/dev/ufshci/ufshci_req_queue.c
+++ b/sys/dev/ufshci/ufshci_req_queue.c
@@ -147,7 +147,8 @@ ufshci_req_queue_response_is_error(struct ufshci_req_queue 
*req_queue,
        /* Check response UPIU header */
        if (response->header.response != UFSHCI_RESPONSE_CODE_TARGET_SUCCESS) {
                ufshci_printf(req_queue->ctrlr,
-                   "Invalid response code = 0x%x\n",
+                   "Function(0x%x) Invalid response code = 0x%x\n",
+                   response->header.ext_iid_or_function,
                    response->header.response);
                is_error = true;
        }
diff --git a/sys/dev/ufshci/ufshci_sim.c b/sys/dev/ufshci/ufshci_sim.c
index 0cd691de47e2..1589e7475ad4 100644
--- a/sys/dev/ufshci/ufshci_sim.c
+++ b/sys/dev/ufshci/ufshci_sim.c
@@ -46,6 +46,11 @@ ufshci_sim_scsiio_done(void *ccb_arg, const struct 
ufshci_completion *cpl,
 
        ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
        if (error) {
+               printf("ufshci: SCSI command completion error, Status(0x%x)"
+                      " Key(0x%x), ASC(0x%x), ASCQ(0x%x)\n",
+                   cpl->response_upiu.cmd_response_upiu.header
+                       .ext_iid_or_status,
+                   sense_data[2], sense_data[12], sense_data[13]);
                ccb->ccb_h.status = CAM_REQ_CMP_ERR;
                xpt_done(ccb);
        } else {
@@ -455,7 +460,7 @@ ufshci_sim_find_periph(struct ufshci_controller *ctrlr, 
uint8_t wlun)
 /* This function is called during suspend/resume. */
 int
 ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start,
-    int power_condition, bool immed)
+    uint8_t power_condition, bool immed)
 {
        struct cam_periph *periph = ctrlr->ufs_device_wlun_periph;
        union ccb *ccb;
diff --git a/sys/dev/ufshci/ufshci_sysctl.c b/sys/dev/ufshci/ufshci_sysctl.c
index 3ec4ea935464..30b0ccaeed13 100644
--- a/sys/dev/ufshci/ufshci_sysctl.c
+++ b/sys/dev/ufshci/ufshci_sysctl.c
@@ -201,6 +201,9 @@ ufshci_sysctl_initialize_ctrlr(struct ufshci_controller 
*ctrlr)
            CTLFLAG_RD, &dev->power_mode_supported, 0,
            "Device power mode support");
 
+       SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "power_mode",
+           CTLFLAG_RD, &dev->power_mode, 0, "Current device power mode");
+
        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