Re: [PATCH 1/1] scsi: ufs: Add support for Auto-Hibernate Idle Timer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 18/02/18 11:45, Avri Altman wrote:
> 
> 
>> -----Original Message-----
>> From: linux-scsi-owner@xxxxxxxxxxxxxxx [mailto:linux-scsi-
>> owner@xxxxxxxxxxxxxxx] On Behalf Of Adrian Hunter
>> Sent: Friday, February 16, 2018 2:01 PM
>> To: Vinayak Holikatti <vinholikatti@xxxxxxxxx>; Martin K. Petersen
>> <martin.petersen@xxxxxxxxxx>; James E.J. Bottomley
>> <jejb@xxxxxxxxxxxxxxxxxx>
>> Cc: Stanislav Nijnikov <Stanislav.Nijnikov@xxxxxxx>; Jaegeuk Kim
>> <jaegeuk@xxxxxxxxxx>; Bart Van Assche <Bart.VanAssche@xxxxxxx>; linux-
>> scsi@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; Michal Potomski
>> <michalx.potomski@xxxxxxxxx>; Szymon Mielczarek
>> <szymonx.mielczarek@xxxxxxxxx>
>> Subject: [PATCH 1/1] scsi: ufs: Add support for Auto-Hibernate Idle Timer
>>
>> UFS host controllers may support an autonomous power management
>> feature called the Auto-Hibernate Idle Timer. The timer is set to the number
>> of microseconds of idle time before the UFS host controller will
>> autonomously put the link into Hibernate state. That will save power at the
>> expense of increased latency. Any access to the host controller interface
>> registers will automatically put the link out of Hibernate state. So once
>> configured, the feature is transparent to the driver.
>>
>> Expose the Auto-Hibernate Idle Timer value via SysFS to allow users to
>> choose between power efficiency or lower latency. Set a default value of
>> 150 ms.
>>
>> Signed-off-by: Adrian Hunter <adrian.hunter@xxxxxxxxx>
>> ---
>>  Documentation/ABI/testing/sysfs-driver-ufs | 15 ++++++
>>  drivers/scsi/ufs/ufs-sysfs.c               | 77 ++++++++++++++++++++++++++++++
>>  drivers/scsi/ufs/ufshcd.c                  | 26 ++++++++++
>>  drivers/scsi/ufs/ufshcd.h                  |  3 ++
>>  drivers/scsi/ufs/ufshci.h                  |  7 +++
>>  5 files changed, 128 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-driver-ufs
>> b/Documentation/ABI/testing/sysfs-driver-ufs
>> index 07f1c2f8dbfc..c7f9441079eb 100644
>> --- a/Documentation/ABI/testing/sysfs-driver-ufs
>> +++ b/Documentation/ABI/testing/sysfs-driver-ufs
>> @@ -1,3 +1,18 @@
>> +What:		/sys/bus/*/drivers/ufshcd/*/auto_hibern8
>> +Date:		February 2018
>> +Contact:	linux-scsi@xxxxxxxxxxxxxxx
>> +Description:
>> +		This file contains the auto-hibernate idle timer setting of a
>> +		UFS host controller. A value of '-1' means auto-hibernate is
>> not
>> +		supported. A value of '0' means auto-hibernate is not
>> enabled.
>> +		Otherwise the value is the number of microseconds of idle
>> time
>> +		before the UFS host controller will autonomously put the link
>> +		into hibernate state. That will save power at the expense of
>> +		increased latency. Note that the hardware supports 10-bit
>> values
>> +		with a power-of-ten multiplier which allows a maximum
>> value of
>> +		102300000. Refer to the UFS Host Controller Interface
>> +		specification for more details.
>> +
>>  What:
>> 	/sys/bus/platform/drivers/ufshcd/*/device_descriptor/device_type
>>  Date:		February 2018
>>  Contact:	Stanislav Nijnikov <stanislav.nijnikov@xxxxxxx>
>> diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index
>> cd7174d2d225..a0e38776dc92 100644
>> --- a/drivers/scsi/ufs/ufs-sysfs.c
>> +++ b/drivers/scsi/ufs/ufs-sysfs.c
>> @@ -3,6 +3,7 @@
>>
>>  #include <linux/err.h>
>>  #include <linux/string.h>
>> +#include <linux/bitfield.h>
>>  #include <asm/unaligned.h>
>>
>>  #include "ufs.h"
>> @@ -123,12 +124,88 @@ static ssize_t spm_lvl_store(struct device *dev,
>>  	return ufs_sysfs_pm_lvl_store(dev, attr, buf, count, false);  }
>>
>> +static void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit) {
>> +	unsigned long flags;
>> +
>> +	if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
>> +		return;
>> +
>> +	spin_lock_irqsave(hba->host->host_lock, flags);
>> +	if (hba->ahit == ahit)
>> +		goto out_unlock;
>> +	hba->ahit = ahit;
>> +	if (!pm_runtime_suspended(hba->dev))
>> +		ufshcd_writel(hba, hba->ahit,
>> REG_AUTO_HIBERNATE_IDLE_TIMER);
>> +out_unlock:
>> +	spin_unlock_irqrestore(hba->host->host_lock, flags); }
>> +
>> +/* Convert Auto-Hibernate Idle Timer register value to microseconds */
>> +static int ufshcd_ahit_to_us(u32 ahit) {
>> +	int timer = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK, ahit);
>> +	int scale = FIELD_GET(UFSHCI_AHIBERN8_SCALE_MASK, ahit);
>> +
>> +	for (; scale > 0; --scale)
>> +		timer *= UFSHCI_AHIBERN8_SCALE_FACTOR;
>> +
>> +	return timer;
>> +}
>> +
>> +/* Convert microseconds to Auto-Hibernate Idle Timer register value */
>> +static u32 ufshcd_us_to_ahit(unsigned int timer) {
>> +	unsigned int scale;
>> +
>> +	for (scale = 0; timer > UFSHCI_AHIBERN8_TIMER_MASK; ++scale)
>> +		timer /= UFSHCI_AHIBERN8_SCALE_FACTOR;
>> +
>> +	return FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, timer) |
>> +	       FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, scale); }
>> +
>> +static ssize_t auto_hibern8_show(struct device *dev,
>> +				 struct device_attribute *attr, char *buf) {
>> +	struct ufs_hba *hba = dev_get_drvdata(dev);
>> +	int timer = -1;
>> +
>> +	if (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT)
>> +		timer = ufshcd_ahit_to_us(hba->ahit);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d\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 int timer;
>> +
>> +	if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
>> +		return -EOPNOTSUPP;
>> +
>> +	if (kstrtouint(buf, 0, &timer))
>> +		return -EINVAL;
>> +
>> +	if (timer > UFSHCI_AHIBERN8_MAX)
>> +		return -EINVAL;
>> +
>> +	ufshcd_auto_hibern8_update(hba, ufshcd_us_to_ahit(timer));
>> +
>> +	return count;
>> +}
>> +
>>  static DEVICE_ATTR_RW(rpm_lvl);
>>  static DEVICE_ATTR_RW(spm_lvl);
>> +static DEVICE_ATTR_RW(auto_hibern8);
>>
>>  static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
>>  	&dev_attr_rpm_lvl.attr,
>>  	&dev_attr_spm_lvl.attr,
>> +	&dev_attr_auto_hibern8.attr,
>>  	NULL
>>  };
>>
>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index
>> 5d874e303d4e..d5fc2fa65495 100644
>> --- a/drivers/scsi/ufs/ufshcd.c
>> +++ b/drivers/scsi/ufs/ufshcd.c
>> @@ -41,6 +41,7 @@
>>  #include <linux/devfreq.h>
>>  #include <linux/nls.h>
>>  #include <linux/of.h>
>> +#include <linux/bitfield.h>
>>  #include "ufshcd.h"
>>  #include "ufs_quirks.h"
>>  #include "unipro.h"
>> @@ -3709,6 +3710,18 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba
>> *hba)
>>  	return ret;
>>  }
>>
>> +static void ufshcd_auto_hibern8_enable(struct ufs_hba *hba) {
>> +	unsigned long flags;
>> +
>> +	if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) || !hba-
>>> ahit)
>> +		return;
>> +
>> +	spin_lock_irqsave(hba->host->host_lock, flags);
>> +	ufshcd_writel(hba, hba->ahit, REG_AUTO_HIBERNATE_IDLE_TIMER);
>> +	spin_unlock_irqrestore(hba->host->host_lock, flags); }
>> +
>>   /**
>>   * ufshcd_init_pwr_info - setting the POR (power on reset)
>>   * values in hba power info
>> @@ -6315,6 +6328,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
>>  	/* UniPro link is active now */
>>  	ufshcd_set_link_active(hba);
>>
>> +	/* Enable Auto-Hibernate if configured */
>> +	ufshcd_auto_hibern8_enable(hba);
>> +
>>  	ret = ufshcd_verify_dev_init(hba);
>>  	if (ret)
>>  		goto out;
>> @@ -7399,6 +7415,10 @@ static int ufshcd_resume(struct ufs_hba *hba,
>> enum ufs_pm_op pm_op)
>>
>>  	/* Schedule clock gating in case of no access to UFS device yet */
>>  	ufshcd_release(hba);
>> +
>> +	/* Enable Auto-Hibernate if configured */
>> +	ufshcd_auto_hibern8_enable(hba);
>> +
>>  	goto out;
>>
>>  set_old_link_state:
>> @@ -7843,6 +7863,12 @@ int ufshcd_init(struct ufs_hba *hba, void
>> __iomem *mmio_base, unsigned int irq)
>>  						UFS_SLEEP_PWR_MODE,
>>  						UIC_LINK_HIBERN8_STATE);
>>
>> +	/* Set the default auto-hiberate idle timer value to 150 ms */
> Your commit said you are setting an idle timer in microseconds,  Better use usec to avoid confusion? 

As the SysFS documentation says "Note that the hardware supports 10-bit
values with a power-of-ten multiplier ... Refer to the UFS Host Controller
Interface specification for more details.", so 150,000 us is still a value
of 150 with a power-of-ten multiplier of 3.

> 
>  
>> +	if (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) {
>> +		hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK,
>> 150) |
>> +			    FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
>> +	}
>> +
>>  	/* Hold auto suspend until async scan completes */
>>  	pm_runtime_get_sync(dev);
>>
>> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index
>> a57d9bdebfed..763a8e6c98ee 100644
>> --- a/drivers/scsi/ufs/ufshcd.h
>> +++ b/drivers/scsi/ufs/ufshcd.h
>> @@ -531,6 +531,9 @@ struct ufs_hba {
>>  	struct device_attribute spm_lvl_attr;
>>  	int pm_op_in_progress;
>>
>> +	/* Auto-Hibernate Idle Timer register value */
>> +	u32 ahit;
>> +
>>  	struct ufshcd_lrb *lrb;
>>  	unsigned long lrb_in_use;
>>
>> diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index
>> 1a1b5d9fe514..bb5d9c7f3353 100644
>> --- a/drivers/scsi/ufs/ufshci.h
>> +++ b/drivers/scsi/ufs/ufshci.h
>> @@ -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,
>> @@ -119,6 +120,12 @@ enum {
>>  #define MANUFACTURE_ID_MASK	UFS_MASK(0xFFFF, 0)
>>  #define PRODUCT_ID_MASK		UFS_MASK(0xFFFF, 16)
>>
>> +/* AHIT - Auto-Hibernate Idle Timer */
>> +#define UFSHCI_AHIBERN8_TIMER_MASK		GENMASK(9, 0)
>> +#define UFSHCI_AHIBERN8_SCALE_MASK		GENMASK(12, 10)
>> +#define UFSHCI_AHIBERN8_SCALE_FACTOR		10
>> +#define UFSHCI_AHIBERN8_MAX			(1023 * 100000)
>> +
>>  /*
>>   * IS - Interrupt Status - 20h
>>   */
>> --
>> 1.9.1
> 
> 




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux