scsi: ufs: add auto hibern8 support
UFS HCI v2.0 specification adds support for auto hibern8. Add support to enable this in the ufs host controller driver. Change-Id: I57e5be8eae4b2cf70a2a9d9c81c9a54a24e03e71 Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
This commit is contained in:
parent
fc630843db
commit
f0a5174e94
3 changed files with 68 additions and 5 deletions
|
@ -1570,6 +1570,16 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
|
|||
cancel_delayed_work_sync(&hba->clk_gating.gate_work);
|
||||
}
|
||||
|
||||
static void ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, u32 delay)
|
||||
{
|
||||
ufshcd_rmwl(hba, AUTO_HIBERN8_TIMER_SCALE_MASK |
|
||||
AUTO_HIBERN8_IDLE_TIMER_MASK,
|
||||
AUTO_HIBERN8_TIMER_SCALE_1_MS | delay,
|
||||
REG_AUTO_HIBERN8_IDLE_TIMER);
|
||||
/* Make sure the timer gets applied before further operations */
|
||||
mb();
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_hibern8_hold - Make sure that link is not in hibern8.
|
||||
*
|
||||
|
@ -1799,6 +1809,13 @@ static ssize_t ufshcd_hibern8_on_idle_delay_store(struct device *dev,
|
|||
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||
hba->hibern8_on_idle.delay_ms = value;
|
||||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
|
||||
/* Update auto hibern8 timer value if supported */
|
||||
if (ufshcd_is_auto_hibern8_supported(hba) &&
|
||||
hba->hibern8_on_idle.is_enabled)
|
||||
ufshcd_set_auto_hibern8_timer(hba,
|
||||
hba->hibern8_on_idle.delay_ms);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1825,6 +1842,13 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev,
|
|||
if (value == hba->hibern8_on_idle.is_enabled)
|
||||
goto out;
|
||||
|
||||
/* Update auto hibern8 timer value if supported */
|
||||
if (ufshcd_is_auto_hibern8_supported(hba)) {
|
||||
ufshcd_set_auto_hibern8_timer(hba,
|
||||
value ? hba->hibern8_on_idle.delay_ms : value);
|
||||
goto update;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
/*
|
||||
* As clock gating work would wait for the hibern8 enter work
|
||||
|
@ -1838,6 +1862,7 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev,
|
|||
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||
}
|
||||
|
||||
update:
|
||||
hba->hibern8_on_idle.is_enabled = value;
|
||||
out:
|
||||
return count;
|
||||
|
@ -1848,12 +1873,23 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)
|
|||
/* initialize the state variable here */
|
||||
hba->hibern8_on_idle.state = HIBERN8_EXITED;
|
||||
|
||||
if (!ufshcd_is_hibern8_on_idle_allowed(hba))
|
||||
if (!ufshcd_is_hibern8_on_idle_allowed(hba) &&
|
||||
!ufshcd_is_auto_hibern8_supported(hba))
|
||||
return;
|
||||
|
||||
INIT_DELAYED_WORK(&hba->hibern8_on_idle.enter_work,
|
||||
ufshcd_hibern8_enter_work);
|
||||
INIT_WORK(&hba->hibern8_on_idle.exit_work, ufshcd_hibern8_exit_work);
|
||||
if (ufshcd_is_auto_hibern8_supported(hba)) {
|
||||
hba->hibern8_on_idle.state = AUTO_HIBERN8;
|
||||
/*
|
||||
* Disable SW hibern8 enter on idle in case
|
||||
* auto hibern8 is supported
|
||||
*/
|
||||
hba->caps &= ~UFSHCD_CAP_HIBERN8_ENTER_ON_IDLE;
|
||||
} else {
|
||||
INIT_DELAYED_WORK(&hba->hibern8_on_idle.enter_work,
|
||||
ufshcd_hibern8_enter_work);
|
||||
INIT_WORK(&hba->hibern8_on_idle.exit_work,
|
||||
ufshcd_hibern8_exit_work);
|
||||
}
|
||||
|
||||
hba->hibern8_on_idle.delay_ms = 10;
|
||||
hba->hibern8_on_idle.is_enabled = true;
|
||||
|
@ -1881,7 +1917,8 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)
|
|||
|
||||
static void ufshcd_exit_hibern8_on_idle(struct ufs_hba *hba)
|
||||
{
|
||||
if (!ufshcd_is_hibern8_on_idle_allowed(hba))
|
||||
if (!ufshcd_is_hibern8_on_idle_allowed(hba) &&
|
||||
!ufshcd_is_auto_hibern8_supported(hba))
|
||||
return;
|
||||
device_remove_file(hba->dev, &hba->hibern8_on_idle.delay_attr);
|
||||
device_remove_file(hba->dev, &hba->hibern8_on_idle.enable_attr);
|
||||
|
@ -6702,6 +6739,11 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Enable auto hibern8 if supported */
|
||||
if (ufshcd_is_auto_hibern8_supported(hba))
|
||||
ufshcd_set_auto_hibern8_timer(hba,
|
||||
hba->hibern8_on_idle.delay_ms);
|
||||
|
||||
/* Debug counters initialization */
|
||||
ufshcd_clear_dbg_ufs_stats(hba);
|
||||
/* set the default level for urgent bkops */
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#define UFSHCD_DRIVER_VERSION "0.3"
|
||||
|
||||
#define UFS_BIT(x) BIT(x)
|
||||
#define UFS_MASK(x, y) (x << ((y) % BITS_PER_LONG))
|
||||
|
||||
struct ufs_hba;
|
||||
|
||||
|
@ -429,6 +430,7 @@ enum ufshcd_hibern8_on_idle_state {
|
|||
HIBERN8_EXITED,
|
||||
REQ_HIBERN8_ENTER,
|
||||
REQ_HIBERN8_EXIT,
|
||||
AUTO_HIBERN8,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -929,6 +931,11 @@ static inline bool ufshcd_is_intr_aggr_allowed(struct ufs_hba *hba)
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline bool ufshcd_is_auto_hibern8_supported(struct ufs_hba *hba)
|
||||
{
|
||||
return !!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT);
|
||||
}
|
||||
|
||||
static inline bool ufshcd_is_crypto_supported(struct ufs_hba *hba)
|
||||
{
|
||||
return !!(hba->capabilities & MASK_CRYPTO_SUPPORT);
|
||||
|
|
|
@ -48,6 +48,7 @@ enum {
|
|||
REG_UFS_VERSION = 0x08,
|
||||
REG_CONTROLLER_DEV_ID = 0x10,
|
||||
REG_CONTROLLER_PROD_ID = 0x14,
|
||||
REG_AUTO_HIBERN8_IDLE_TIMER = 0x18,
|
||||
REG_INTERRUPT_STATUS = 0x20,
|
||||
REG_INTERRUPT_ENABLE = 0x24,
|
||||
REG_CONTROLLER_STATUS = 0x30,
|
||||
|
@ -85,6 +86,7 @@ enum {
|
|||
enum {
|
||||
MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F,
|
||||
MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
|
||||
MASK_AUTO_HIBERN8_SUPPORT = 0x00800000,
|
||||
MASK_64_ADDRESSING_SUPPORT = 0x01000000,
|
||||
MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
|
||||
MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
|
||||
|
@ -117,6 +119,18 @@ enum {
|
|||
#define MANUFACTURE_ID_MASK UFS_MASK(0xFFFF, 0)
|
||||
#define PRODUCT_ID_MASK UFS_MASK(0xFFFF, 16)
|
||||
|
||||
/*
|
||||
* AHIT - Auto-Hibernate Idle Timer 18h
|
||||
*/
|
||||
#define AUTO_HIBERN8_IDLE_TIMER_MASK UFS_MASK(0x3FF, 0)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_MASK UFS_MASK(0x7, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_1_US UFS_MASK(0x0, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_10_US UFS_MASK(0x1, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_100_US UFS_MASK(0x2, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_1_MS UFS_MASK(0x3, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_10_MS UFS_MASK(0x4, 10)
|
||||
#define AUTO_HIBERN8_TIMER_SCALE_100_MS UFS_MASK(0x5, 10)
|
||||
|
||||
/* IS - Interrupt status (20h) / IE - Interrupt enable (24h) */
|
||||
#define UTP_TRANSFER_REQ_COMPL UFS_BIT(0)
|
||||
#define UIC_DME_END_PT_RESET UFS_BIT(1)
|
||||
|
|
Loading…
Add table
Reference in a new issue