new file mode 100644
@@ -0,0 +1,8 @@
+What: /sys/bus/pci/devices/<bus>:<vid>:<pid>.<n>/auto_hibern8
+Date: July 2017
+Contact: linux-scsi@vger.kernel.org
+Description:
+ This attribiute provides information on current setting of
+ Auto-Hibern8 UFS Host feature (read <unit: microseconds>)
+ and ability to set the feature on - by writing desired
+ timeout value <unit: microseconds>, or off (by writing '0').
@@ -931,6 +931,31 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
}
/**
+ * ufshcd_read_auto_hibern8_state - Reads hosts auto-hibern8 feature state
+ * @hba: per adapter instance
+ */
+u32 ufshcd_read_auto_hibern8_state(struct ufs_hba *hba)
+{
+ return ufshcd_readl(hba, REG_AUTO_HIBERN8_IDLE_TIMER);
+}
+
+/**
+ * ufshcd_setup_auto_hibern8 - Sets up hosts auto-hibern8 feature
+ * @hba: per adapter instance
+ * @scale: timer scale (1/10/100us/1/10/100ms)
+ * @timer_val: value to be multipled with scale (idle timeout)
+ */
+void ufshcd_setup_auto_hibern8(struct ufs_hba *hba, u8 scale, u16 timer_val)
+{
+ u32 val = (scale << UFSHCI_AHIBERN8_SCALE_OFFSET)
+ & UFSHCI_AHIBERN8_SCALE_MASK;
+
+ val |= timer_val & UFSHCI_AHIBERN8_TIMER_MASK;
+
+ ufshcd_writel(hba, val, REG_AUTO_HIBERN8_IDLE_TIMER);
+}
+
+/**
* ufshcd_is_devfreq_scaling_required - check if scaling is required or not
* @hba: per adapter instance
* @scale_up: True if scaling up and false if scaling down
@@ -6583,12 +6608,65 @@ static ssize_t spm_lvl_store(struct device *dev,
return ufshcd_pm_lvl_store(dev, attr, buf, count, false);
}
+#define UFS_AHIBERN8_SCALE_STEP_MAGNITUDE 10
+
+static ssize_t auto_hibern8_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 val;
+ unsigned long timer;
+ u8 scale;
+
+ if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
+ return -EOPNOTSUPP;
+
+ val = ufshcd_read_auto_hibern8_state(hba);
+ timer = val & UFSHCI_AHIBERN8_TIMER_MASK;
+ scale = (val & UFSHCI_AHIBERN8_SCALE_MASK)
+ >> UFSHCI_AHIBERN8_SCALE_OFFSET;
+
+ for (; scale > 0; --scale)
+ timer *= UFS_AHIBERN8_SCALE_STEP_MAGNITUDE;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n", timer);
+}
+
+static ssize_t auto_hibern8_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned long timer;
+ u8 scale = UFSHCI_AHIBERN8_SCALE_1US;
+
+ if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
+ return -EOPNOTSUPP;
+
+ if (kstrtoul(buf, 0, &timer))
+ return -EINVAL;
+
+ while (timer > UFSHCI_AHIBERN8_TIMER_MASK &&
+ scale < UFSHCI_AHIBERN8_SCALE_MAX) {
+ timer /= UFS_AHIBERN8_SCALE_STEP_MAGNITUDE;
+ ++scale;
+ }
+
+ if (scale >= UFSHCI_AHIBERN8_SCALE_MAX)
+ return -EINVAL;
+
+ ufshcd_setup_auto_hibern8(hba, scale, (u16) timer);
+
+ return count;
+}
+
static DEVICE_ATTR_RW(rpm_lvl);
static DEVICE_ATTR_RW(spm_lvl);
+static DEVICE_ATTR_RW(auto_hibern8);
static struct device_attribute *ufshcd_dev_attrs[] = {
&dev_attr_rpm_lvl,
&dev_attr_spm_lvl,
+ &dev_attr_auto_hibern8,
NULL,
};
@@ -48,7 +48,7 @@ enum {
REG_UFS_VERSION = 0x08,
REG_CONTROLLER_DEV_ID = 0x10,
REG_CONTROLLER_PROD_ID = 0x14,
- REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18,
+ REG_AUTO_HIBERN8_IDLE_TIMER = 0x18,
REG_INTERRUPT_STATUS = 0x20,
REG_INTERRUPT_ENABLE = 0x24,
REG_CONTROLLER_STATUS = 0x30,
@@ -86,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,
@@ -136,9 +137,9 @@ enum {
#define CONTROLLER_FATAL_ERROR UFS_BIT(16)
#define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17)
-#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\
- UIC_HIBERNATE_EXIT |\
- UIC_POWER_MODE)
+#define UFSHCD_UHS_MASK (UIC_HIBERNATE_EXIT | UIC_HIBERNATE_ENTER)
+
+#define UFSHCD_UIC_PWR_MASK (UFSHCD_UHS_MASK | UIC_POWER_MODE)
#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK)
@@ -428,4 +429,18 @@ struct utp_task_req_desc {
__le32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
};
+enum {
+ UFSHCI_AHIBERN8_SCALE_1US = 0,
+ UFSHCI_AHIBERN8_SCALE_10US = 1,
+ UFSHCI_AHIBERN8_SCALE_100US = 2,
+ UFSHCI_AHIBERN8_SCALE_1MS = 3,
+ UFSHCI_AHIBERN8_SCALE_10MS = 4,
+ UFSHCI_AHIBERN8_SCALE_100MS = 5,
+ UFSHCI_AHIBERN8_SCALE_MAX,
+};
+
+#define UFSHCI_AHIBERN8_TIMER_MASK 0x03ff
+#define UFSHCI_AHIBERN8_SCALE_MASK 0x1C00
+#define UFSHCI_AHIBERN8_SCALE_OFFSET 10
+
#endif /* End of Header */