Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F149260085
D54003.1789779821.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D54003.1789779821.diff
View Options
diff --git a/sys/dev/ufshci/ufshci_ctrlr.c b/sys/dev/ufshci/ufshci_ctrlr.c
--- a/sys/dev/ufshci/ufshci_ctrlr.c
+++ b/sys/dev/ufshci/ufshci_ctrlr.c
@@ -79,6 +79,8 @@
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 @@
return (0);
}
-static int
+int
ufshci_ctrlr_disable(struct ufshci_controller *ctrlr)
{
int error;
@@ -632,7 +634,14 @@
}
}
- /* 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 @@
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
--- a/sys/dev/ufshci/ufshci_dev.c
+++ b/sys/dev/ufshci/ufshci_dev.c
@@ -433,9 +433,6 @@
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 @@
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 @@
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
--- a/sys/dev/ufshci/ufshci_private.h
+++ b/sys/dev/ufshci/ufshci_private.h
@@ -243,20 +243,33 @@
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 @@
/* Power mode */
bool power_mode_supported;
enum ufshci_dev_pwr power_mode;
+ enum ufshci_uic_link_state link_state;
};
/*
@@ -423,6 +437,7 @@
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_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 @@
/* 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_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
--- a/sys/dev/ufshci/ufshci_uic_cmd.c
+++ b/sys/dev/ufshci/ufshci_uic_cmd.c
@@ -23,6 +23,7 @@
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_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 @@
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 @@
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));
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Sep 19, 1:03 AM (6 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29766616
Default Alt Text
D54003.1789779821.diff (9 KB)
Attached To
Mode
D54003: ufshci: Support UIC hibernation enter/exit
Attached
Detach File
Event Timeline
Log In to Comment