The branch main has been updated by jaeyoon:

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

commit db8b06468baefb9d9b5db5f91084d85071bfb5cb
Author:     Jaeyoon Choi <[email protected]>
AuthorDate: 2025-12-03 04:06:01 +0000
Commit:     Jaeyoon Choi <[email protected]>
CommitDate: 2025-12-03 04:06:01 +0000

    ufshci: Support UIC Auto Hibernation
    
    Automatically transition the UniPro link to Hibernate when it is idle
    for the duration configured by the Auto-Hibernate Idle Timer (AHIT).
    This reduces link power while the device is inactive.
    
    Reviewed by:            imp (mentor)
    Sponsored by:           Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D54004
---
 sys/dev/ufshci/ufshci_ctrlr.c   |  4 ++++
 sys/dev/ufshci/ufshci_dev.c     | 27 +++++++++++++++++++++++++++
 sys/dev/ufshci/ufshci_pci.c     |  3 ++-
 sys/dev/ufshci/ufshci_private.h |  9 +++++++++
 sys/dev/ufshci/ufshci_sysctl.c  | 28 ++++++++++++++++++++++++++++
 sys/dev/ufshci/ufshci_uic_cmd.c |  5 +++--
 6 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index 494313df95de..ce0da4cab907 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -92,6 +92,8 @@ ufshci_ctrlr_start(struct ufshci_controller *ctrlr, bool 
resetting)
                return;
        }
 
+       ufshci_dev_init_auto_hibernate(ctrlr);
+
        /* TODO: Configure Write Protect */
 
        /* TODO: Configure Background Operations */
@@ -674,5 +676,7 @@ ufshci_ctrlr_resume(struct ufshci_controller *ctrlr, enum 
power_stype stype)
                }
        }
 
+       ufshci_dev_enable_auto_hibernate(ctrlr);
+
        return (0);
 }
diff --git a/sys/dev/ufshci/ufshci_dev.c b/sys/dev/ufshci/ufshci_dev.c
index c4a5bda9c79a..38c6de9731a4 100644
--- a/sys/dev/ufshci/ufshci_dev.c
+++ b/sys/dev/ufshci/ufshci_dev.c
@@ -449,6 +449,33 @@ ufshci_dev_init_uic_power_mode(struct ufshci_controller 
*ctrlr)
        return (0);
 }
 
+void
+ufshci_dev_enable_auto_hibernate(struct ufshci_controller *ctrlr)
+{
+       if (!ctrlr->ufs_dev.auto_hibernation_supported)
+               return;
+
+       ufshci_mmio_write_4(ctrlr, ahit, ctrlr->ufs_dev.ahit);
+}
+
+void
+ufshci_dev_init_auto_hibernate(struct ufshci_controller *ctrlr)
+{
+       ctrlr->ufs_dev.auto_hibernation_supported =
+           UFSHCIV(UFSHCI_CAP_REG_AUTOH8, ctrlr->cap) &&
+           !(ctrlr->quirks & UFSHCI_QUIRK_BROKEN_AUTO_HIBERNATE);
+
+       if (!ctrlr->ufs_dev.auto_hibernation_supported)
+               return;
+
+       /* The default value for auto hibernation is 150 ms */
+       ctrlr->ufs_dev.ahit = 0;
+       ctrlr->ufs_dev.ahit |= UFSHCIF(UFSHCI_AHIT_REG_AH8ITV, 150);
+       ctrlr->ufs_dev.ahit |= UFSHCIF(UFSHCI_AHIT_REG_TS, 3);
+
+       ufshci_dev_enable_auto_hibernate(ctrlr);
+}
+
 void
 ufshci_dev_init_uic_link_state(struct ufshci_controller *ctrlr)
 {
diff --git a/sys/dev/ufshci/ufshci_pci.c b/sys/dev/ufshci/ufshci_pci.c
index 5fce14997784..b2a958f1cd1a 100644
--- a/sys/dev/ufshci/ufshci_pci.c
+++ b/sys/dev/ufshci/ufshci_pci.c
@@ -58,7 +58,8 @@ static struct _pcsid {
            UFSHCI_REF_CLK_19_2MHz,
            UFSHCI_QUIRK_LONG_PEER_PA_TACTIVATE |
                UFSHCI_QUIRK_WAIT_AFTER_POWER_MODE_CHANGE |
-               UFSHCI_QUIRK_CHANGE_LANE_AND_GEAR_SEPARATELY },
+               UFSHCI_QUIRK_CHANGE_LANE_AND_GEAR_SEPARATELY |
+               UFSHCI_QUIRK_BROKEN_AUTO_HIBERNATE },
        { 0x54ff8086, "Intel UFS Host Controller", UFSHCI_REF_CLK_19_2MHz },
        { 0x00000000, NULL } };
 
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index 8a49c2a9bc2b..bcb2bcef0230 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -293,6 +293,10 @@ struct ufshci_device {
        bool power_mode_supported;
        enum ufshci_dev_pwr power_mode;
        enum ufshci_uic_link_state link_state;
+
+       /* Auto Hibernation */
+       bool auto_hibernation_supported;
+       uint32_t ahit;
 };
 
 /*
@@ -314,6 +318,9 @@ struct ufshci_controller {
        16 /* QEMU does not support Task Management Request */
 #define UFSHCI_QUIRK_SKIP_WELL_KNOWN_LUNS \
        32 /* QEMU does not support Well known logical units*/
+#define UFSHCI_QUIRK_BROKEN_AUTO_HIBERNATE                                    \
+       64 /* Some controllers have the Auto hibernate feature enabled but it \
+             does not work. */
 
        uint32_t ref_clk;
 
@@ -457,6 +464,8 @@ int ufshci_dev_init(struct ufshci_controller *ctrlr);
 int ufshci_dev_reset(struct ufshci_controller *ctrlr);
 int ufshci_dev_init_reference_clock(struct ufshci_controller *ctrlr);
 int ufshci_dev_init_unipro(struct ufshci_controller *ctrlr);
+void ufshci_dev_enable_auto_hibernate(struct ufshci_controller *ctrlr);
+void ufshci_dev_init_auto_hibernate(struct ufshci_controller *ctrlr);
 int ufshci_dev_init_uic_power_mode(struct ufshci_controller *ctrlr);
 void ufshci_dev_init_uic_link_state(struct ufshci_controller *ctrlr);
 int ufshci_dev_init_ufs_power_mode(struct ufshci_controller *ctrlr);
diff --git a/sys/dev/ufshci/ufshci_sysctl.c b/sys/dev/ufshci/ufshci_sysctl.c
index 30b0ccaeed13..495f087f3c50 100644
--- a/sys/dev/ufshci/ufshci_sysctl.c
+++ b/sys/dev/ufshci/ufshci_sysctl.c
@@ -11,6 +11,7 @@
 #include <sys/sysctl.h>
 
 #include "ufshci_private.h"
+#include "ufshci_reg.h"
 
 static int
 ufshci_sysctl_timeout_period(SYSCTL_HANDLER_ARGS)
@@ -106,6 +107,22 @@ ufshci_sysctl_num_failures(SYSCTL_HANDLER_ARGS)
        return (sysctl_handle_64(oidp, &num_failures, 0, req));
 }
 
+static int
+ufshci_sysctl_ahit(SYSCTL_HANDLER_ARGS)
+{
+       struct ufshci_controller *ctrlr = arg1;
+       int64_t scale, timer;
+       const int64_t scale_factor = 10;
+
+       scale = UFSHCIV(UFSHCI_AHIT_REG_TS, ctrlr->ufs_dev.ahit);
+       timer = UFSHCIV(UFSHCI_AHIT_REG_AH8ITV, ctrlr->ufs_dev.ahit);
+
+       while (scale--)
+               timer *= scale_factor;
+
+       return (sysctl_handle_64(oidp, &timer, 0, req));
+}
+
 static void
 ufshci_sysctl_initialize_queue(struct ufshci_hw_queue *hwq,
     struct sysctl_ctx_list *ctrlr_ctx, struct sysctl_oid *que_tree)
@@ -201,6 +218,17 @@ ufshci_sysctl_initialize_ctrlr(struct ufshci_controller 
*ctrlr)
            CTLFLAG_RD, &dev->power_mode_supported, 0,
            "Device power mode support");
 
+       SYSCTL_ADD_BOOL(ctrlr_ctx, ctrlr_list, OID_AUTO,
+           "auto_hibernation_supported", CTLFLAG_RD,
+           &dev->auto_hibernation_supported, 0,
+           "Device auto hibernation support");
+
+       SYSCTL_ADD_PROC(ctrlr_ctx, ctrlr_list, OID_AUTO,
+           "auto_hibernate_idle_timer_value",
+           CTLTYPE_S64 | CTLFLAG_RD | CTLFLAG_MPSAFE, ctrlr, 0,
+           ufshci_sysctl_ahit, "IU",
+           "Auto-Hibernate Idle Timer Value (in microseconds)");
+
        SYSCTL_ADD_UINT(ctrlr_ctx, ctrlr_list, OID_AUTO, "power_mode",
            CTLFLAG_RD, &dev->power_mode, 0, "Current device power mode");
 
diff --git a/sys/dev/ufshci/ufshci_uic_cmd.c b/sys/dev/ufshci/ufshci_uic_cmd.c
index 29c143cec52c..c6e6afe3f688 100644
--- a/sys/dev/ufshci/ufshci_uic_cmd.c
+++ b/sys/dev/ufshci/ufshci_uic_cmd.c
@@ -196,8 +196,9 @@ ufshci_uic_send_cmd(struct ufshci_controller *ctrlr,
        config_result_code = ufshci_mmio_read_4(ctrlr, ucmdarg2);
        if (config_result_code) {
                ufshci_printf(ctrlr,
-                   "Failed to send UIC command. (config result code = 0x%x)\n",
-                   config_result_code);
+                   "Failed to send UIC command (Opcode: 0x%x"
+                   ", config result code = 0x%x)\n",
+                   uic_cmd->opcode, config_result_code);
        }
 
        if (return_value != NULL)

Reply via email to