Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 03 Dec 2025 04:06:42 +0000
From:      Jaeyoon Choi <jaeyoon@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: bec77e3a160d - main - ufshci: Support UIC hibernation enter/exit
Message-ID:  <692fb752.382d5.311e8b5@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by jaeyoon:

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

commit bec77e3a160dba10cbc30416f53a506ca2b9301f
Author:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
AuthorDate: 2025-12-03 04:05:51 +0000
Commit:     Jaeyoon Choi <jaeyoon@FreeBSD.org>
CommitDate: 2025-12-03 04:05:51 +0000

    ufshci: Support UIC hibernation enter/exit
    
    Add helpers to issue DME_HIBERNATE_ENTER/DME_HIBERNATE_EXIT and
    call them during suspend/resume.
    
    Reviewed by:            imp (mentor)
    Sponsored by:           Samsung Electronics
    Differential Revision:  https://reviews.freebsd.org/D54003
---
 sys/dev/ufshci/ufshci_ctrlr.c   | 22 ++++++++++--
 sys/dev/ufshci/ufshci_dev.c     | 77 +++++++++++++++++++++++++++++++++++++++--
 sys/dev/ufshci/ufshci_private.h | 33 ++++++++++++++----
 sys/dev/ufshci/ufshci_uic_cmd.c | 77 ++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 196 insertions(+), 13 deletions(-)

diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
index f0cb08b34823..494313df95de 100644
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -79,6 +79,8 @@ ufshci_ctrlr_start(struct ufshci_controller *ctrlr, bool resetting)
 		return;
 	}
 
+	ufshci_dev_init_uic_link_state(ctrlr);
+
 	/* Read Controller Descriptor (Device, Geometry) */
 	if (ufshci_dev_get_descriptor(ctrlr) != 0) {
 		ufshci_ctrlr_fail(ctrlr);
@@ -187,7 +189,7 @@ ufshci_ctrlr_enable_host_ctrlr(struct ufshci_controller *ctrlr)
 	return (0);
 }
 
-static int
+int
 ufshci_ctrlr_disable(struct ufshci_controller *ctrlr)
 {
 	int error;
@@ -632,7 +634,14 @@ ufshci_ctrlr_suspend(struct ufshci_controller *ctrlr, enum power_stype stype)
 		}
 	}
 
-	/* TODO: Change the link state to Hibernate if necessary. */
+	/* Change the link state */
+	error = ufshci_dev_link_state_transition(ctrlr,
+	    power_map[stype].link_state);
+	if (error) {
+		ufshci_printf(ctrlr,
+		    "Failed to transition link state in suspend handler\n");
+		return (error);
+	}
 
 	return (0);
 }
@@ -645,7 +654,14 @@ ufshci_ctrlr_resume(struct ufshci_controller *ctrlr, enum power_stype stype)
 	if (!ctrlr->ufs_dev.power_mode_supported)
 		return (0);
 
-	/* TODO: Change the link state to Active if necessary. */
+	/* Change the link state */
+	error = ufshci_dev_link_state_transition(ctrlr,
+	    power_map[stype].link_state);
+	if (error) {
+		ufshci_printf(ctrlr,
+		    "Failed to transition link state in resume handler\n");
+		return (error);
+	}
 
 	if (ctrlr->ufs_device_wlun_periph) {
 		ctrlr->ufs_dev.power_mode = power_map[stype].dev_pwr;
diff --git a/sys/dev/ufshci/ufshci_dev.c b/sys/dev/ufshci/ufshci_dev.c
index 136823523fd3..c4a5bda9c79a 100644
--- a/sys/dev/ufshci/ufshci_dev.c
+++ b/sys/dev/ufshci/ufshci_dev.c
@@ -433,9 +433,6 @@ ufshci_dev_init_uic_power_mode(struct ufshci_controller *ctrlr)
 		return (ENXIO);
 	}
 
-	/* Clear 'Power Mode completion status' */
-	ufshci_mmio_write_4(ctrlr, is, UFSHCIM(UFSHCI_IS_REG_UPMS));
-
 	if (ctrlr->quirks & UFSHCI_QUIRK_WAIT_AFTER_POWER_MODE_CHANGE) {
 		/*
 		 * Intel Lake-field UFSHCI has a quirk.
@@ -452,6 +449,12 @@ ufshci_dev_init_uic_power_mode(struct ufshci_controller *ctrlr)
 	return (0);
 }
 
+void
+ufshci_dev_init_uic_link_state(struct ufshci_controller *ctrlr)
+{
+	ctrlr->ufs_dev.link_state = UFSHCI_UIC_LINK_STATE_ACTIVE;
+}
+
 int
 ufshci_dev_init_ufs_power_mode(struct ufshci_controller *ctrlr)
 {
@@ -805,3 +808,71 @@ ufshci_dev_get_current_power_mode(struct ufshci_controller *ctrlr,
 
 	return (0);
 }
+
+static int
+ufshci_dev_hibernate_enter(struct ufshci_controller *ctrlr)
+{
+	int error;
+
+	error = ufshci_uic_send_dme_hibernate_enter(ctrlr);
+	if (error)
+		return (error);
+
+	return (ufshci_uic_hibernation_ready(ctrlr));
+}
+
+static int
+ufshci_dev_hibernate_exit(struct ufshci_controller *ctrlr)
+{
+	int error;
+
+	error = ufshci_uic_send_dme_hibernate_exit(ctrlr);
+	if (error)
+		return (error);
+
+	return (ufshci_uic_hibernation_ready(ctrlr));
+}
+
+int
+ufshci_dev_link_state_transition(struct ufshci_controller *ctrlr,
+    enum ufshci_uic_link_state target_state)
+{
+	struct ufshci_device *dev = &ctrlr->ufs_dev;
+	int error = 0;
+
+	if (dev->link_state == target_state)
+		return (0);
+
+	switch (target_state) {
+	case UFSHCI_UIC_LINK_STATE_OFF:
+		error = ufshci_dev_hibernate_enter(ctrlr);
+		if (error)
+			break;
+		error = ufshci_ctrlr_disable(ctrlr);
+		break;
+	case UFSHCI_UIC_LINK_STATE_ACTIVE:
+		if (dev->link_state == UFSHCI_UIC_LINK_STATE_HIBERNATE)
+			error = ufshci_dev_hibernate_exit(ctrlr);
+		else
+			error = EINVAL;
+		break;
+	case UFSHCI_UIC_LINK_STATE_HIBERNATE:
+		if (dev->link_state == UFSHCI_UIC_LINK_STATE_ACTIVE)
+			error = ufshci_dev_hibernate_enter(ctrlr);
+		else
+			error = EINVAL;
+		break;
+	case UFSHCI_UIC_LINK_STATE_BROKEN:
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+
+	if (error)
+		return (error);
+
+	dev->link_state = target_state;
+
+	return (0);
+}
diff --git a/sys/dev/ufshci/ufshci_private.h b/sys/dev/ufshci/ufshci_private.h
index fa5caf87196c..8a49c2a9bc2b 100644
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -243,20 +243,33 @@ enum ufshci_dev_pwr {
 	UFSHCI_DEV_PWR_COUNT,
 };
 
+enum ufshci_uic_link_state {
+	UFSHCI_UIC_LINK_STATE_OFF = 0,
+	UFSHCI_UIC_LINK_STATE_ACTIVE,
+	UFSHCI_UIC_LINK_STATE_HIBERNATE,
+	UFSHCI_UIC_LINK_STATE_BROKEN,
+};
+
 struct ufshci_power_entry {
 	enum ufshci_dev_pwr dev_pwr;
 	uint8_t ssu_pc; /* SSU Power Condition */
+	enum ufshci_uic_link_state link_state;
 };
 
 /* 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_AWAKE] = { UFSHCI_DEV_PWR_ACTIVE, SSS_PC_ACTIVE,
+	    UFSHCI_UIC_LINK_STATE_ACTIVE },
+	[POWER_STYPE_STANDBY] = { UFSHCI_DEV_PWR_SLEEP, SSS_PC_IDLE,
+	    UFSHCI_UIC_LINK_STATE_HIBERNATE },
 	[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 },
+	    SSS_PC_STANDBY, UFSHCI_UIC_LINK_STATE_HIBERNATE },
+	[POWER_STYPE_SUSPEND_TO_IDLE] = { UFSHCI_DEV_PWR_SLEEP, SSS_PC_IDLE,
+	    UFSHCI_UIC_LINK_STATE_HIBERNATE },
+	[POWER_STYPE_HIBERNATE] = { UFSHCI_DEV_PWR_DEEPSLEEP, 0x40,
+	    UFSHCI_UIC_LINK_STATE_OFF },
+	[POWER_STYPE_POWEROFF] = { UFSHCI_DEV_PWR_POWERDOWN, SSS_PC_STANDBY,
+	    UFSHCI_UIC_LINK_STATE_OFF },
 };
 
 struct ufshci_device {
@@ -279,6 +292,7 @@ struct ufshci_device {
 	/* Power mode */
 	bool power_mode_supported;
 	enum ufshci_dev_pwr power_mode;
+	enum ufshci_uic_link_state link_state;
 };
 
 /*
@@ -423,6 +437,7 @@ int ufshci_ctrlr_suspend(struct ufshci_controller *ctrlr,
     enum power_stype stype);
 int ufshci_ctrlr_resume(struct ufshci_controller *ctrlr,
     enum power_stype stype);
+int ufshci_ctrlr_disable(struct ufshci_controller *ctrlr);
 /* 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);
@@ -443,11 +458,14 @@ 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);
 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);
 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);
+int ufshci_dev_link_state_transition(struct ufshci_controller *ctrlr,
+    enum ufshci_uic_link_state target_state);
 
 /* Controller Command */
 void ufshci_ctrlr_cmd_send_task_mgmt_request(struct ufshci_controller *ctrlr,
@@ -508,6 +526,7 @@ int ufshci_req_sdb_get_inflight_io(struct ufshci_controller *ctrlr);
 
 /* UIC Command */
 int ufshci_uic_power_mode_ready(struct ufshci_controller *ctrlr);
+int ufshci_uic_hibernation_ready(struct ufshci_controller *ctrlr);
 int ufshci_uic_cmd_ready(struct ufshci_controller *ctrlr);
 int ufshci_uic_send_dme_link_startup(struct ufshci_controller *ctrlr);
 int ufshci_uic_send_dme_get(struct ufshci_controller *ctrlr, uint16_t attribute,
@@ -519,6 +538,8 @@ int ufshci_uic_send_dme_peer_get(struct ufshci_controller *ctrlr,
 int ufshci_uic_send_dme_peer_set(struct ufshci_controller *ctrlr,
     uint16_t attribute, uint32_t value);
 int ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller *ctrlr);
+int ufshci_uic_send_dme_hibernate_enter(struct ufshci_controller *ctrlr);
+int ufshci_uic_send_dme_hibernate_exit(struct ufshci_controller *ctrlr);
 
 /* SYSCTL */
 void ufshci_sysctl_initialize_ctrlr(struct ufshci_controller *ctrlr);
diff --git a/sys/dev/ufshci/ufshci_uic_cmd.c b/sys/dev/ufshci/ufshci_uic_cmd.c
index b9c867ff7065..29c143cec52c 100644
--- a/sys/dev/ufshci/ufshci_uic_cmd.c
+++ b/sys/dev/ufshci/ufshci_uic_cmd.c
@@ -23,6 +23,7 @@ ufshci_uic_power_mode_ready(struct ufshci_controller *ctrlr)
 	while (1) {
 		is = ufshci_mmio_read_4(ctrlr, is);
 		if (UFSHCIV(UFSHCI_IS_REG_UPMS, is)) {
+			/* Clear 'Power Mode completion status' */
 			ufshci_mmio_write_4(ctrlr, is,
 			    UFSHCIM(UFSHCI_IS_REG_UPMS));
 			break;
@@ -32,7 +33,7 @@ ufshci_uic_power_mode_ready(struct ufshci_controller *ctrlr)
 			ufshci_printf(ctrlr,
 			    "Power mode is not changed "
 			    "within %d ms\n",
-			    ctrlr->uic_cmd_timeout_in_ms);
+			    ctrlr->device_init_timeout_in_ms);
 			return (ENXIO);
 		}
 
@@ -52,6 +53,54 @@ ufshci_uic_power_mode_ready(struct ufshci_controller *ctrlr)
 	return (0);
 }
 
+int
+ufshci_uic_hibernation_ready(struct ufshci_controller *ctrlr)
+{
+	uint32_t is, hcs;
+	int timeout;
+
+	/* Wait for the IS flag to change */
+	timeout = ticks + MSEC_2_TICKS(ctrlr->uic_cmd_timeout_in_ms);
+
+	while (1) {
+		is = ufshci_mmio_read_4(ctrlr, is);
+		if (UFSHCIV(UFSHCI_IS_REG_UHES, is)) {
+			/* Clear 'UIC Hibernate Enter Status' */
+			ufshci_mmio_write_4(ctrlr, is,
+			    UFSHCIM(UFSHCI_IS_REG_UHES));
+			break;
+		}
+		if (UFSHCIV(UFSHCI_IS_REG_UHXS, is)) {
+			/* Clear 'UIC Hibernate Exit Status' */
+			ufshci_mmio_write_4(ctrlr, is,
+			    UFSHCIM(UFSHCI_IS_REG_UHXS));
+			break;
+		}
+
+		if (timeout - ticks < 0) {
+			ufshci_printf(ctrlr,
+			    "Hibernation enter/exit are not completed "
+			    "within %d ms\n",
+			    ctrlr->uic_cmd_timeout_in_ms);
+			return (ENXIO);
+		}
+
+		/* TODO: Replace busy-wait with interrupt-based pause. */
+		DELAY(10);
+	}
+
+	/* Check HCS power mode change request status */
+	hcs = ufshci_mmio_read_4(ctrlr, hcs);
+	if (UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs) != 0x01) {
+		ufshci_printf(ctrlr,
+		    "Hibernation enter/exit request status error: 0x%x\n",
+		    UFSHCIV(UFSHCI_HCS_REG_UPMCRS, hcs));
+		return (ENXIO);
+	}
+
+	return (0);
+}
+
 int
 ufshci_uic_cmd_ready(struct ufshci_controller *ctrlr)
 {
@@ -239,3 +288,29 @@ ufshci_uic_send_dme_endpoint_reset(struct ufshci_controller *ctrlr)
 
 	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
 }
+
+int
+ufshci_uic_send_dme_hibernate_enter(struct ufshci_controller *ctrlr)
+{
+	struct ufshci_uic_cmd uic_cmd;
+
+	uic_cmd.opcode = UFSHCI_DME_HIBERNATE_ENTER;
+	uic_cmd.argument1 = 0;
+	uic_cmd.argument2 = 0;
+	uic_cmd.argument3 = 0;
+
+	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
+}
+
+int
+ufshci_uic_send_dme_hibernate_exit(struct ufshci_controller *ctrlr)
+{
+	struct ufshci_uic_cmd uic_cmd;
+
+	uic_cmd.opcode = UFSHCI_DME_HIBERNATE_EXIT;
+	uic_cmd.argument1 = 0;
+	uic_cmd.argument2 = 0;
+	uic_cmd.argument3 = 0;
+
+	return (ufshci_uic_send_cmd(ctrlr, &uic_cmd, NULL));
+}


help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?692fb752.382d5.311e8b5>