From nobody Wed Dec 3 04:06:42 2025 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4dLkbk5z86z6JmTj for ; Wed, 03 Dec 2025 04:06:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4dLkbk1c6Cz44Dq for ; Wed, 03 Dec 2025 04:06:42 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1764734802; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Z5neDB6fTPEvGtgXOiuiIomXfPFvbLZ2NCfrbrHqemk=; b=BBufQpWzdpWPrnTN7H+oEqId3q73zr7ABYNYCfAURXJuvKCsQlCkjRqQNJ8A8PE3ScBb/S uwVQVtQ6ssDayQEDisKjISDMo7TGRTqJOb597as0Fv8mi2Kb+clDvw2d0fJLf17yomT+XE Dna91f9l65Xpr3YJ4m70ejaXPUH9LWIvjH4BksDbncGuEmsGRlgGHQbA+6xuIdsz+tf7Du wyHAfi+G96hvqSQeg4+7pb/wYVZ6ww8MKPXNGa0EBlAHWg4u0nd+HDz3td13VM7SHiFvW2 5VN6HExbgIpBXAVbmyfQdFrFuem+DUCjFfp2LZsilSYnE+v4OBXtZOXyznvNlA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1764734802; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Z5neDB6fTPEvGtgXOiuiIomXfPFvbLZ2NCfrbrHqemk=; b=cSGhBOFzXfXNH9rx6OVbOaqNmkQjCr3b3vfOxqVKoYa6UBZSpd25zU8XvUgDb2DbM7IQDj c/R0A9zR/NLLaPinKo/8WQqZ94Xb3S68igUUBcp1ZmpppalMgKw7pijRc2b1SfChWbp0zQ PdWRYMSD74GSBk+dTEuCpX4Q6eBNg3+hYE8ChEK37bvy73rx/3Jhfo4I4Qq+bXGS127JT/ dJgwsPFH1SuchUNy6jq8TxFL0qH7qxi1DfH/F2rCOibDWq+MwEfEXPpVju9plRXM1GFc2o HNLpTAg06Qsql0s4m/M8x+k7EBZpd9K0mphHrrbDgQLJCnK4fB3tHoX6+OF59Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1764734802; a=rsa-sha256; cv=none; b=vpl5kvjedWTFUBGyiCv5wQwkjBgd6S8wzZQC2lNTEfJ769BHD4++bjE9j8cwIt7pdqvGPA l5euD85ZibM+XMXcZXzt3pLSbZFW99dJQy8HZZW0sMQfcVdmPRAtVEBKHF3EgfVTIlDyYZ XHzm3iYH6SPmYhhX1oxOZE8XTZyiVrIXryS9st2Vb5tpQW1VrtNWGs+bcPt+URs/V/Rlf4 /FXRbNgGX7TdJYkaWXx1zQpczLxyPNenKhZRRW07Vk9ydfVRCyIMB8ya9AkO0RK0I3rdZN T8mQCjlTTDT9xeGFl2C1FhbCrx9OdCSfPJP5Gk29rtZKwK5f5kXyk2AT+Uft5w== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4dLkbk0xZVz5cx for ; Wed, 03 Dec 2025 04:06:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 382d5 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 03 Dec 2025 04:06:42 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Jaeyoon Choi Subject: git: bec77e3a160d - main - ufshci: Support UIC hibernation enter/exit List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jaeyoon X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: bec77e3a160dba10cbc30416f53a506ca2b9301f Auto-Submitted: auto-generated Date: Wed, 03 Dec 2025 04:06:42 +0000 Message-Id: <692fb752.382d5.311e8b5@gitrepo.freebsd.org> The branch main has been updated by jaeyoon: URL: https://cgit.FreeBSD.org/src/commit/?id=bec77e3a160dba10cbc30416f53a506ca2b9301f commit bec77e3a160dba10cbc30416f53a506ca2b9301f Author: Jaeyoon Choi AuthorDate: 2025-12-03 04:05:51 +0000 Commit: Jaeyoon Choi 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)); +}