git: db8b06468bae - main - ufshci: Support UIC Auto Hibernation
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 03 Dec 2025 04:06:43 UTC
The branch main has been updated by jaeyoon:
URL: https://cgit.FreeBSD.org/src/commit/?id=db8b06468baefb9d9b5db5f91084d85071bfb5cb
commit db8b06468baefb9d9b5db5f91084d85071bfb5cb
Author: Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2025-12-03 04:06:01 +0000
Commit: Jaeyoon Choi <jaeyoon@FreeBSD.org>
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)