diff --git a/sys/dev/ufshci/ufshci.c b/sys/dev/ufshci/ufshci.c --- a/sys/dev/ufshci/ufshci.c +++ b/sys/dev/ufshci/ufshci.c @@ -114,6 +114,8 @@ return; } } + + ufshci_dev_enable_auto_hibernate(ctrlr); } int 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 @@ -92,6 +92,8 @@ return; } + ufshci_dev_init_auto_hibernate(ctrlr); + /* TODO: Configure Write Protect */ /* TODO: Configure Background Operations */ 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 @@ -449,6 +449,33 @@ 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 --- a/sys/dev/ufshci/ufshci_pci.c +++ b/sys/dev/ufshci/ufshci_pci.c @@ -56,7 +56,8 @@ 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 --- a/sys/dev/ufshci/ufshci_private.h +++ b/sys/dev/ufshci/ufshci_private.h @@ -293,6 +293,10 @@ 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 @@ 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; @@ -456,6 +463,8 @@ 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 --- a/sys/dev/ufshci/ufshci_sysctl.c +++ b/sys/dev/ufshci/ufshci_sysctl.c @@ -11,6 +11,7 @@ #include #include "ufshci_private.h" +#include "ufshci_reg.h" static int ufshci_sysctl_timeout_period(SYSCTL_HANDLER_ARGS) @@ -106,6 +107,22 @@ 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 @@ 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 --- a/sys/dev/ufshci/ufshci_uic_cmd.c +++ b/sys/dev/ufshci/ufshci_uic_cmd.c @@ -196,8 +196,9 @@ 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)