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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 } };
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user