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:
Venkat Gopalakrishnan 2016-04-15 16:26:59 -07:00 committed by Kyle Yan
parent fc630843db
commit f0a5174e94
3 changed files with 68 additions and 5 deletions

View file

@ -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 */

View file

@ -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);

View file

@ -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)