UFS: Power management support
Subsystems (4)
v6 cover UFS: Power management support
This patch seies introduces support for power management in the driver as well as vendor specific initialization - registers, clocks, voltage regulators etc.
It includes also a rework for the init sequence and other PM pre-requisite such as write protection support, handling well-known LUN, error handling (retries), bkops, START_STOP unit command, and ICC levels settings.
--
Changes from V5:
- Add a print to "[01/18] scsi: fixing the "type" for well known LUs" indicating on the type change.
- Moved "sdev->is_visible" test to scsi_sysfs_add_devices
- Reverted the squashed between
"[10/17] scsi: ufs: introduce well known logical unit in ufs" and "[09/17] scsi: ufs: manually add well known logical"
and addressed othe comments by Chris.
- Fixed a bug and comments reported by Akinobu Mita <[email protected]>
Changes from V4:
- Restored "[01/16] scsi: support well known logical units" to fix type issue" and renamed "[01/17] scsi: fixing the "type" for well known LUs"
- Add "[02/17] scsi: sysfs: don't add scsi_device if its already added" to help addressing Chris comments.
- Squashed "[10/17] scsi: ufs: introduce well known logical unit in ufs" and "[09/17] scsi: ufs: manually add well known logical" and addressed most of Chris comments here.
- Cleanup in "[11/16] scsi: ufs: add UFS power management support" to align it with comments made on previous patches.
Changes from V3:
- Replaced "[01/16] scsi: support well known logical units" with "[09/17] scsi: ufs: manually add well known logical
- add patch "[PATCH V4 05/17] scsi: ufs: add voting support for host"
- Fix couple of compilation issues introduced in V3
- Add a NULL pointer test to hba->vops before accessing it
- Changed disable/enable irq with request/free irq
Changes from V2:
- Reordered scsi core patches
- add patch "[PATCH V3 02/16] scsi: balance out autopm get/put calls in"
- Minor changes/fixes to the patches:
* "[PATCH V3 10/16] scsi: ufs: add UFS power management support"
* "[PATCH V3 12/16] scsi: ufs: Add support for clock gating"
* "[PATCH V3 16/16] scsi: ufs: definitions for phy interface"
In order to address community concerns, and as a result of further development and testing.
Changes from V1:
- 6 new patches apended at the end
- Allow overriding power configuration with controller support and preferences/capabilities <Dolev Raviv>
- Allow overriding power choice with controller capabilities <Dolev Raviv>
- Add support for clock gating and clock scaling <Sahitya Tummala>
- Add capability to control the auto bkops during suspend <Subhash Jadavani>
- Add misc changes for phy/unipro driver usage <Dolev Raviv>
Dolev Raviv (2):
scsi: ufs: refactor configuring power mode
scsi: ufs: definitions for phy interface
Raviv Shvili (1):
scsi: ufs: add voting support for host controller power
Sahitya Tummala (3):
scsi: ufs: Add support for clock gating
scsi: ufs: Add freq-table-hz property for UFS device
scsi: ufs: Add support for clock scaling using devfreq framework
Subhash Jadavani (7):
scsi: fixing the "type" for well known LUs
scsi: sysfs: don't add scsi_device if its already added
scsi: ufs: refactor query descriptor API support
scsi: ufs: manually add well known logical units
scsi: ufs: introduce well known logical unit in ufs
scsi: ufs: add UFS power management support
scsi: ufs: tune bkops while power managment events
Sujit Reddy Thumma (4):
scsi: ufs: Allow vendor specific initialization
scsi: ufs: Add regulator enable support
scsi: ufs: Add clock initialization support
scsi: ufs: improve init sequence
Yaniv Gardi (1):
scsi: ufs: Active Power Mode - configuring bActiveICCLevel
.../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 41 + drivers/scsi/scsi_scan.c | 16 + drivers/scsi/ufs/Kconfig | 2 + drivers/scsi/ufs/ufs.h | 132 +- drivers/scsi/ufs/ufshcd-pci.c | 55 +- drivers/scsi/ufs/ufshcd-pltfrm.c | 291 ++- drivers/scsi/ufs/ufshcd.c | 2592 ++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 280 ++- drivers/scsi/ufs/ufshci.h | 9 +- drivers/scsi/ufs/unipro.h | 56 + include/scsi/scsi.h | 1 + 11 files changed, 3150 insertions(+), 325 deletions(-)
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 1/18 scsi: fixing the "type" for well known LUs
From: Subhash Jadavani <[email protected]>
Some devices may respond with wrong type for well-known logical units. This patch forces well-known type for devices which doesn't report it correct.
drivers/scsi/scsi_scan.c +13 -0
@@ -805,6 +805,19 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, }else { sdev->type = (inq_result[0 ] &0x1f ); sdev->removable = (inq_result[1 ] &0x80 ) >>7 ; + +/* +* some devices may respond with wrong type for +* well-known logical units. Force well-known type +* to enumerate them correctly. +*/ +if (scsi_is_wlun (sdev->lun) && sdev->type !=TYPE_WLUN ) { +sdev_printk (KERN_WARNING , sdev, +"%s: correcting incorrect peripheral device type 0x%x for W-LUN 0x%16phN\n" , + __func__, sdev->type, sdev->lun); + sdev->type =TYPE_WLUN ; + } + }if (sdev->type ==TYPE_RBC || sdev->type ==TYPE_ROM ) {
include/scsi/scsi.h +1 -0
@@ -333,6 +333,7 @@ static inline int scsi_status_is_good(int status)#define TYPE_RBC 0x0e#define TYPE_OSD 0x11#define TYPE_ZBC 0x14 +#define TYPE_WLUN 0x1e/* well-known logical unit */ #define TYPE_NO_LUN 0x7f/* SCSI protocols; these are taken from SPC-3 section 7.5 */
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH V6 01/18] scsi: fixing the "type" for well known LUs
Robert, I guess this version is okay with you?
Quoted 56 lines · Dolev Raviv
From: Subhash Jadavani <[email protected]>
Some devices may respond with wrong type for well-known logical units. This patch forces well-known type for devices which doesn't report it correct.
Signed-off-by Subhash Jadavani <[email protected]>Signed-off-by Sujit Reddy Thumma <[email protected]>Signed-off-by Dolev Raviv <[email protected]>drivers/scsi/scsi_scan.c +13
-0@@ -805,6 +805,19 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, }else { sdev->type = (inq_result[0 ] &0x1f ); sdev->removable = (inq_result[1 ] &0x80 ) >>7 ; + +/* +* some devices may respond with wrong type for +* well-known logical units. Force well-known type +* to enumerate them correctly. +*/ +if (scsi_is_wlun (sdev->lun) && sdev->type !=TYPE_WLUN ) { +sdev_printk (KERN_WARNING , sdev, +"%s: correcting incorrect peripheral device type 0x%x for W-LUN 0x%16phN\n" , + __func__, sdev->type, sdev->lun); + sdev->type =TYPE_WLUN ; + } + }if (sdev->type ==TYPE_RBC || sdev->type ==TYPE_ROM ) {include/scsi/scsi.h +1
-0@@ -333,6 +333,7 @@ static inline int scsi_status_is_good(int status)#define TYPE_RBC 0x0e#define TYPE_OSD 0x11#define TYPE_ZBC 0x14 +#define TYPE_WLUN 0x1e/* well-known logical unit */ #define TYPE_NO_LUN 0x7f/* SCSI protocols; these are taken from SPC-3 section 7.5 */ Signature · 9 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
---end quoted text---
Elliott, Robert (Server Storage)
Reviewed-byRE: [PATCH V6 01/18] scsi: fixing the "type" for well known LUs
Quoted 44 lines
-----Original Message-----
From: Christoph Hellwig [mailto:[email protected]]
Sent: Friday, 26 September, 2014 3:14 AM
To: Dolev Raviv
Cc: [email protected]; [email protected]; linux- [email protected]; [email protected]; linux-arm- [email protected]; [email protected]; Subhash Jadavani; Sujit Reddy Thumma; Elliott, Robert (Server Storage); Martin K. Petersen
Subject: Re: [PATCH V6 01/18] scsi: fixing the "type" for well known LUsRobert, I guess this version is okay with you?
drivers/scsi/scsi_scan.c static int scsi_add_lun(struct scsi_device *sdev,
Quoted 15 lines · Dolev Raviv
From: Subhash Jadavani <[email protected]>
Some devices may respond with wrong type for well-known logical units. This patch forces well-known type for devices which doesn't report it correct.
Signed-off-by Subhash Jadavani <[email protected]>Signed-off-by Sujit Reddy Thumma <[email protected]>Signed-off-by Dolev Raviv <[email protected]>drivers/scsi/scsi_scan.c +0
-0@@ -805,6 +805,19 @@ static int scsi_add_lun(struct scsi_device *sdev,unsigned char
*inq_result,drivers/scsi/scsi_scan.c static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
Quoted 12 lines
drivers/scsi/scsi_scan.c +9
-0}else { sdev->type = (inq_result[0 ] &0x1f ); sdev->removable = (inq_result[1 ] &0x80 ) >>7 ; + +/* +* some devices may respond with wrong type for +* well-known logical units. Force well-known type +* to enumerate them correctly. +*/ +if (scsi_is_wlun (sdev->lun) && sdev->type !=TYPE_WLUN ) { + sdev_printk(KERN_WARNING, sdev, + "%s: correcting incorrect peripheral device type0x%x for W-LUN 0x%16phN\n",
Quoted 4 lines
drivers/scsi/scsi_scan.c static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, · line 820
Quoted 4 lines
drivers/scsi/scsi_scan.c +4
-0+ __func__, sdev->type, sdev->lun); + sdev->type =TYPE_WLUN ; + } +
Yes, that looks good.
PATCH v6 2/18 scsi: sysfs: don't add scsi_device if its already added
In reply to Dolev RavivFrom: Subhash Jadavani <[email protected]>
If LLD has added scsi device (by calling scsi_add_device) before scheduling async scsi_scan_host then scsi_finish_async_scan() will end up calling scsi_sysfs_add_sdev for scsi device which was already added by LLD. This patch fixes this issue by skipping the call to scsi_sysfs_add_sdev() if it's already visible to rest of the kernel.
drivers/scsi/scsi_scan.c +3 -0
@@ -1746,6 +1746,9 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost)/* target removed before the device could be added */ if (sdev->sdev_state ==SDEV_DEL )continue ; +/* If device is already visible, skip adding it to sysfs */ +if (sdev->is_visible) +continue ;if (!scsi_host_scan_allowed (shost) ||scsi_sysfs_add_sdev (sdev) !=0 )__scsi_remove_device (sdev);
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH V6 02/18] scsi: sysfs: don't add scsi_device if its already added
Looks fine to me, just want a cross check from Martin or Hannes.
Quoted 35 lines · Dolev Raviv
From: Subhash Jadavani <[email protected]>
If LLD has added scsi device (by calling
scsi_add_device) before scheduling asyncscsi_scan_hostthenscsi_finish_async_scan()will end up callingscsi_sysfs_add_sdevfor scsi device which was already added by LLD. This patch fixes this issue by skipping the call toscsi_sysfs_add_sdev()if it's already visible to rest of the kernel.Signed-off-by Subhash Jadavani <[email protected]>Signed-off-by Dolev Raviv <[email protected]>drivers/scsi/scsi_scan.c +3
-0@@ -1746,6 +1746,9 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost)/* target removed before the device could be added */ if (sdev->sdev_state ==SDEV_DEL )continue ; +/* If device is already visible, skip adding it to sysfs */ +if (sdev->is_visible) +continue ;if (!scsi_host_scan_allowed (shost) ||scsi_sysfs_add_sdev (sdev) !=0 )__scsi_remove_device (sdev);Signature · 9 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
---end quoted text---
Hannes Reinecke
Reviewed-byRe: [PATCH V6 02/18] scsi: sysfs: don't add scsi_device if its already added
Quoted 34 lines · Christoph Hellwig
Looks fine to me, just want a cross check from Martin or Hannes.
Quoted 31 lines · Dolev Raviv
From: Subhash Jadavani <[email protected]>
If LLD has added scsi device (by calling
scsi_add_device) before scheduling asyncscsi_scan_hostthenscsi_finish_async_scan()will end up callingscsi_sysfs_add_sdevfor scsi device which was already added by LLD. This patch fixes this issue by skipping the call toscsi_sysfs_add_sdev()if it's already visible to rest of the kernel.Signed-off-by Subhash Jadavani <[email protected]>Signed-off-by Dolev Raviv <[email protected]>drivers/scsi/scsi_scan.c +3
-0@@ -1746,6 +1746,9 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost)/* target removed before the device could be added */ if (sdev->sdev_state ==SDEV_DEL )continue ; +/* If device is already visible, skip adding it to sysfs */ +if (sdev->is_visible) +continue ;if (!scsi_host_scan_allowed (shost) ||scsi_sysfs_add_sdev (sdev) !=0 )__scsi_remove_device (sdev);Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Quoted 6 lines
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html---end quoted text---
--
Looks okay.
Cheers,
Hannes
Signature · 4 lines
Dr. Hannes Reinecke zSeries & Storage [email protected] +49 911 74053 688 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg GF: J. Hawn, J. Guild, F. Imendörffer, HRB 16746 (AG Nürnberg)
Martin K. Petersen
Reviewed-byRe: [PATCH V6 02/18] scsi: sysfs: don't add scsi_device if its already added
In reply to Christoph HellwigQuoted 7 lines
Quoted 7 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
"Christoph" == Christoph Hellwig <[email protected]> writes:
If LLD has added scsi device (by calling
scsi_add_device) before scheduling asyncscsi_scan_hostthenscsi_finish_async_scan()will end up callingscsi_sysfs_add_sdevfor scsi device which was already added by LLD. This patch fixes this issue by skipping the call toscsi_sysfs_add_sdev()if it's already visible to rest of the kernel.
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering
PATCH v6 3/18 scsi: ufs: Allow vendor specific initialization
In reply to Dolev RavivFrom: Sujit Reddy Thumma <[email protected]>
Some vendor specific controller versions might need to configure vendor specific - registers, clocks, voltage regulators etc. to initialize the host controller UTP layer and Uni-Pro stack. Provide some common initialization operations that can be used to configure vendor specifics. The methods can be extended in future, for example, for power mode transitions.
The operations are vendor/board specific and hence determined with the help of compatible property in device tree.
drivers/scsi/ufs/ufshcd-pci.c +7 -1
@@ -164,7 +164,13 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mmio_base =pcim_iomap_table (pdev)[0 ]; - err =ufshcd_init (&pdev->dev, &hba, mmio_base, pdev->irq); + err =ufshcd_alloc_host (&pdev->dev, &hba); +if (err) { +dev_err (&pdev->dev,"Allocation failed\n" ); +return err; + } + + err =ufshcd_init (hba, mmio_base, pdev->irq);if (err) {dev_err (&pdev->dev,"Initialization failed\n" );return err;
drivers/scsi/ufs/ufshcd-pltfrm.c +26 -3
@@ -35,9 +35,24 @@#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/of.h> #include "ufshcd.h" +static const struct of_device_id ufs_of_match[]; +static struct ufs_hba_variant_ops *get_variant_ops (struct device *dev) +{ +if (dev->of_node) { +const struct of_device_id *match; + + match =of_match_node (ufs_of_match, dev->of_node); +if (match) +return (struct ufs_hba_variant_ops *)match->data; + } + +return NULL ; +} +#ifdef CONFIG_PM /** * ufshcd_pltfrm_suspend - suspend power management function
@@ -138,8 +153,8 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) mem_res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); mmio_base =devm_ioremap_resource (dev, mem_res); -if (IS_ERR (mmio_base)) { - err =PTR_ERR (mmio_base); +if (IS_ERR (*(void **)&mmio_base)) { + err =PTR_ERR (*(void **)&mmio_base); goto out; }
@@ -150,10 +165,18 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) goto out; } + err =ufshcd_alloc_host (dev, &hba); +if (err) { +dev_err (&pdev->dev,"Allocation failed\n" ); + goto out; + } + + hba->vops =get_variant_ops (&pdev->dev); +pm_runtime_set_active (&pdev->dev);pm_runtime_enable (&pdev->dev); - err =ufshcd_init (dev, &hba, mmio_base, irq); + err =ufshcd_init (hba, mmio_base, irq);if (err) {dev_err (dev,"Intialization failed\n" ); goto out_disable_rpm;
drivers/scsi/ufs/ufshcd.c +129 -32
276 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +32 -2
@@ -68,6 +68,8 @@#define UFSHCD "ufshcd"#define UFSHCD_DRIVER_VERSION "0.2" +struct ufs_hba ; +enum dev_cmd_type {DEV_CMD_TYPE_NOP =0x0 ,DEV_CMD_TYPE_QUERY =0x1 ,
@@ -152,6 +154,30 @@ struct ufs_dev_cmd {struct ufs_query query; }; +#define PRE_CHANGE 0 +#define POST_CHANGE 1 +/** +* struct ufs_hba_variant_ops - variant specific callbacks +* @name: variant name +* @init: called when the driver is initialized +* @exit: called to cleanup everything done in init +* @setup_clocks: called before touching any of the controller registers +* @setup_regulators: called before accessing the host controller +* @hce_enable_notify: called before and after HCE enable bit is set to allow +* variant specific Uni-Pro initialization. +* @link_startup_notify: called before and after Link startup is carried out +* to allow variant specific Uni-Pro initialization. +*/ +struct ufs_hba_variant_ops { +const char *name; +int (*init)(struct ufs_hba *); +void (*exit)(struct ufs_hba *); +int (*setup_clocks)(struct ufs_hba *,bool ); +int (*setup_regulators)(struct ufs_hba *,bool ); +int (*hce_enable_notify)(struct ufs_hba *,bool ); +int (*link_startup_notify)(struct ufs_hba *,bool ); +}; +/** * struct ufs_hba - per adapter private structure * @mmio_base: UFSHCI base register address
@@ -171,6 +197,8 @@ struct ufs_dev_cmd {* @nutrs: Transfer Request Queue depth supported by controller * @nutmrs: Task Management Queue depth supported by controller * @ufs_version: UFS Version to which controller complies +* @vops: pointer to variant specific operations +* @priv: pointer to variant specific private data * @irq: Irq number of the controller * @active_uic_cmd: handle of active UIC command * @uic_cmd_mutex: mutex for uic command
@@ -218,6 +246,8 @@ struct ufs_hba {int nutrs;int nutmrs;u32 ufs_version; +struct ufs_hba_variant_ops *vops; +void *priv;unsigned int irq;struct uic_command *active_uic_cmd;
@@ -256,8 +286,8 @@ struct ufs_hba {#define ufshcd_readl (hba, reg) \ readl((hba)->mmio_base + (reg)) -int ufshcd_init (struct device *,struct ufs_hba ** ,void __iomem * , -unsigned int ); +int ufshcd_alloc_host (struct device *,struct ufs_hba **); +int ufshcd_init (struct ufs_hba * ,void __iomem * ,unsigned int );void ufshcd_remove (struct ufs_hba *);/**
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 4/18 scsi: ufs: Add regulator enable support
In reply to Dolev RavivFrom: Sujit Reddy Thumma <[email protected]>
UFS devices are powered by at most three external power supplies -
- VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V
- VCCQ - The controller and I/O power supply, 1.1V to 1.3V
- VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V
For some devices VCCQ or VCCQ2 are optional as they can be generated using internal LDO inside the UFS device.
Add DT bindings for voltage regulators that can be controlled from host driver.
Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +24 -0
@@ -8,9 +8,33 @@ Required properties: - interrupts : <interrupt mapping for UFS host controller IRQ> - reg : <registers mapping> +Optional properties: +- vcc-supply : phandle to VCC supply regulator node +- vccq-supply : phandle to VCCQ supply regulator node +- vccq2-supply : phandle to VCCQ2 supply regulator node +- vcc-supply-1p8 : For embedded UFS devices, valid VCC range is 1.7-1.95V + or 2.7-3.6V. This boolean property when set, specifies + to use low voltage range of 1.7-1.95V. Note for external + UFS cards this property is invalid and valid VCC range is + always 2.7-3.6V. +- vcc-max-microamp : specifies max. load that can be drawn from vcc supply +- vccq-max-microamp : specifies max. load that can be drawn from vccq supply +- vccq2-max-microamp : specifies max. load that can be drawn from vccq2 supply + +Note: If above properties are not defined it can be assumed that the supply +regulators are always on. + Example: ufshc@0xfc598000 { compatible = "jedec,ufs-1.1"; reg = <0xfc598000 0x800>; interrupts = <0 28 0>; + + vcc-supply = <&xxx_reg1>; + vcc-supply-1p8; + vccq-supply = <&xxx_reg2>; + vccq2-supply = <&xxx_reg3>; + vcc-max-microamp = 500000; + vccq-max-microamp = 200000; + vccq2-max-microamp = 200000; };
drivers/scsi/ufs/ufs.h +25 -0
@@ -362,4 +362,29 @@ struct ufs_query_res { struct utp_upiu_query upiu_res; }; +#define UFS_VREG_VCC_MIN_UV 2700000/* uV */ +#define UFS_VREG_VCC_MAX_UV 3600000/* uV */ +#define UFS_VREG_VCC_1P8_MIN_UV 1700000/* uV */ +#define UFS_VREG_VCC_1P8_MAX_UV 1950000/* uV */ +#define UFS_VREG_VCCQ_MIN_UV 1100000/* uV */ +#define UFS_VREG_VCCQ_MAX_UV 1300000/* uV */ +#define UFS_VREG_VCCQ2_MIN_UV 1650000/* uV */ +#define UFS_VREG_VCCQ2_MAX_UV 1950000/* uV */ + +struct ufs_vreg { +struct regulator *reg; +const char *name; +bool enabled; +int min_uV; +int max_uV; +int min_uA; +int max_uA; +}; + +struct ufs_vreg_info { +struct ufs_vreg *vcc; +struct ufs_vreg *vccq; +struct ufs_vreg *vccq2; +}; +#endif /* End of Header */
drivers/scsi/ufs/ufshcd-pltfrm.c +100 -0
112 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.c +190 -3
229 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +3 -0
@@ -52,6 +52,7 @@#include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/completion.h> +#include <linux/regulator/consumer.h> #include <asm/irq.h> #include <asm/byteorder.h>
@@ -219,6 +220,7 @@ struct ufs_hba_variant_ops {* @saved_uic_err: sticky UIC error mask * @dev_cmd: ufs device management command information * @auto_bkops_enabled: to track whether bkops is enabled in device +* @vreg_info: UFS device voltage regulator information */ struct ufs_hba {void __iomem *mmio_base;
@@ -279,6 +281,7 @@ struct ufs_hba {struct ufs_dev_cmd dev_cmd;bool auto_bkops_enabled; +struct ufs_vreg_info vreg_info; };#define ufshcd_writel (hba, val, reg) \
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 5/18 scsi: ufs: Add clock initialization support
In reply to Dolev RavivFrom: Sujit Reddy Thumma <[email protected]>
Add generic clock initialization support for UFSHCD platform driver. The clock info is read from device tree using standard clock bindings. A generic max-clock-frequency-hz property is defined to save information on maximum operating clock frequency the h/w supports.
Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +14 -1
@@ -21,8 +21,17 @@ Optional properties: - vccq-max-microamp : specifies max. load that can be drawn from vccq supply - vccq2-max-microamp : specifies max. load that can be drawn from vccq2 supply +- clocks : List of phandle and clock specifier pairs +- clock-names : List of clock input name strings sorted in the same + order as the clocks property. +- max-clock-frequency-hz : List of maximum operating frequency stored in the same + order as the clocks property. If this property is not + defined or a value in the array is "0" then it is assumed + that the frequency is set by the parent clock or a + fixed rate clock source. + Note: If above properties are not defined it can be assumed that the supply -regulators are always on. +regulators or clocks are always on. Example: ufshc@0xfc598000 {
@@ -37,4 +46,8 @@ Example: vcc-max-microamp = 500000; vccq-max-microamp = 200000; vccq2-max-microamp = 200000; + + clocks = <&core 0>, <&ref 0>, <&iface 0>; + clock-names = "core_clk", "ref_clk", "iface_clk"; + max-clock-frequency-hz = <100000000 19200000 0>; };
drivers/scsi/ufs/ufshcd-pci.c +2 -0
@@ -170,6 +170,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)return err; } +INIT_LIST_HEAD (&hba->clk_list_head); + err =ufshcd_init (hba, mmio_base, pdev->irq);if (err) {dev_err (&pdev->dev,"Initialization failed\n" );
drivers/scsi/ufs/ufshcd-pltfrm.c +71 -0
83 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.c +87 -2
119 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +18 -0
@@ -155,6 +155,22 @@ struct ufs_dev_cmd {struct ufs_query query; }; +/** +* struct ufs_clk_info - UFS clock related info +* @list: list headed by hba->clk_list_head +* @clk: clock node +* @name: clock name +* @max_freq: maximum frequency supported by the clock +* @enabled: variable to check against multiple enable/disable +*/ +struct ufs_clk_info { +struct list_head list; +struct clk *clk; +const char *name; +u32 max_freq; +bool enabled; +}; +#define PRE_CHANGE 0#define POST_CHANGE 1/**
@@ -221,6 +237,7 @@ struct ufs_hba_variant_ops {* @dev_cmd: ufs device management command information * @auto_bkops_enabled: to track whether bkops is enabled in device * @vreg_info: UFS device voltage regulator information +* @clk_list_head: UFS host controller clocks list node head */ struct ufs_hba {void __iomem *mmio_base;
@@ -282,6 +299,7 @@ struct ufs_hba {bool auto_bkops_enabled;struct ufs_vreg_info vreg_info; +struct list_head clk_list_head; };#define ufshcd_writel (hba, val, reg) \
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 6/18 scsi: ufs: add voting support for host controller power
In reply to Dolev RavivFrom: Raviv Shvili <[email protected]>
Add the support for voting of the regulator powering the host controller logic.
Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +4 -0
@@ -9,6 +9,7 @@ Required properties: - reg : <registers mapping> Optional properties: +- vdd-hba-supply : phandle to UFS host controller supply regulator node - vcc-supply : phandle to VCC supply regulator node - vccq-supply : phandle to VCCQ supply regulator node - vccq2-supply : phandle to VCCQ2 supply regulator node
@@ -20,6 +21,7 @@ Optional properties: - vcc-max-microamp : specifies max. load that can be drawn from vcc supply - vccq-max-microamp : specifies max. load that can be drawn from vccq supply - vccq2-max-microamp : specifies max. load that can be drawn from vccq2 supply +- <name>-fixed-regulator : boolean property specifying that <name>-supply is a fixed regulator - clocks : List of phandle and clock specifier pairs - clock-names : List of clock input name strings sorted in the same
@@ -39,6 +41,8 @@ Example: reg = <0xfc598000 0x800>; interrupts = <0 28 0>; + vdd-hba-supply = <&xxx_reg0>; + vdd-hba-fixed-regulator; vcc-supply = <&xxx_reg1>; vcc-supply-1p8; vccq-supply = <&xxx_reg2>;
drivers/scsi/ufs/ufs.h +1 -0
@@ -385,6 +385,7 @@ struct ufs_vreg_info {struct ufs_vreg *vcc;struct ufs_vreg *vccq;struct ufs_vreg *vccq2; + struct ufs_vreg *vdd_hba; };#endif /* End of Header */
drivers/scsi/ufs/ufshcd-pltfrm.c +9 -0
@@ -147,6 +147,11 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name, vreg->name =kstrdup (name,GFP_KERNEL ); +/* if fixed regulator no need further initialization */ +snprintf (prop_name,MAX_PROP_SIZE ,"%s-fixed-regulator" , name); +if (of_property_read_bool (np, prop_name)) + goto out; +snprintf (prop_name,MAX_PROP_SIZE ,"%s-max-microamp" , name); ret =of_property_read_u32 (np, prop_name, &vreg->max_uA);if (ret) {
@@ -198,6 +203,10 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba)struct device *dev = hba->dev;struct ufs_vreg_info *info = &hba->vreg_info; + err =ufshcd_populate_vreg (dev,"vdd-hba" , &info->vdd_hba); +if (err) + goto out; + err =ufshcd_populate_vreg (dev,"vcc" , &info->vcc);if (err) goto out;
drivers/scsi/ufs/ufshcd.c +40 -2
78 lines not on this page · raw message
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 7/18 scsi: ufs: refactor query descriptor API support
In reply to Dolev RavivFrom: Subhash Jadavani <[email protected]>
Currently reading query descriptor is more tightened to each descriptor type. This patch generalize the approach and allows reading any parameter from any query descriptor.
drivers/scsi/ufs/ufs.h +23 -2
@@ -50,6 +50,8 @@cpu_to_be32 ((byte3 <<24 ) | (byte2 <<16 ) |\ (byte1 << 8) | (byte0)) +#define UFS_UPIU_MAX_GENERAL_LUN 8 +/* * UFS Protocol Information Unit related definitions */
@@ -129,10 +131,29 @@ enum desc_idn {QUERY_DESC_IDN_RFU_1 =0x6 ,QUERY_DESC_IDN_GEOMETRY =0x7 ,QUERY_DESC_IDN_POWER =0x8 , - QUERY_DESC_IDN_RFU_2 = 0x9, + QUERY_DESC_IDN_MAX, +}; + +enum desc_header_offset { +QUERY_DESC_LENGTH_OFFSET =0x00 , +QUERY_DESC_DESC_TYPE_OFFSET =0x01 , +}; + +enum ufs_desc_max_size { +QUERY_DESC_DEVICE_MAX_SIZE =0x1F , +QUERY_DESC_CONFIGURAION_MAX_SIZE =0x90 , +QUERY_DESC_UNIT_MAX_SIZE =0x23 , +QUERY_DESC_INTERCONNECT_MAX_SIZE =0x06 , +/* +* Max. 126 UNICODE characters (2 bytes per character) plus 2 bytes +* of descriptor header. +*/ +QUERY_DESC_STRING_MAX_SIZE =0xFE , +QUERY_DESC_GEOMETRY_MAZ_SIZE =0x44 , +QUERY_DESC_POWER_MAX_SIZE =0x62 , +QUERY_DESC_RFU_MAX_SIZE =0x00 , }; -#define UNIT_DESC_MAX_SIZE 0x22/* Unit descriptor parameters offsets in bytes*/ enum unit_desc_param { UNIT_DESC_PARAM_LEN = 0x0,
drivers/scsi/ufs/ufshcd.c +130 -37
203 lines not on this page · raw message
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 8/18 scsi: ufs: improve init sequence
In reply to Dolev RavivFrom: Sujit Reddy Thumma <[email protected]>
In ->hce_enable_notify() callback the vendor specific initialization may carry out additional DME configuration using UIC commands and hence the UIC command completion interrupt enable bit should be set before the post reset notification.
Add retries if the link-startup fails. This is required since due to hardware timing issues, the Uni-Pro link-startup might fail. The UFS HCI recovery procedure contradicts the Uni-Pro sequence. The UFS HCI specifies to resend DME_LINKSTARTUP command after IS.ULLS (link-lost interrupt) is received. The Uni-Pro specifies that if link-startup fails the link is in "down" state. The link-lost is indicated to the DME user only when the link is up. Hence, the UFS HCI recovery procedure of waiting for IS.ULLS and retrying link-startup may not work properly.
At the end, if detection fails, power off (disable clocks, regulators, phy) if the UFS device detection fails. This saves power while UFS device is not embedded into the system.
drivers/scsi/ufs/ufshcd.c +73 -28
@@ -62,6 +62,12 @@/* Task management command timeout */ #define TM_CMD_TIMEOUT 100/* msecs */ +/* maximum number of link-startup retries */ +#define DME_LINKSTARTUP_RETRIES 3 + +/* maximum number of reset retries before giving up */ +#define MAX_HOST_RESET_RETRIES 5 +/* Expose the flag value from utp_upiu_query.value */ #define MASK_QUERY_UPIU_FLAG_LOC 0xFF
@@ -137,6 +143,8 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba);static void ufshcd_async_scan (void *data,async_cookie_t cookie);static int ufshcd_reset_and_restore (struct ufs_hba *hba);static int ufshcd_clear_tm_cmd (struct ufs_hba *hba,int tag); +static void ufshcd_hba_exit (struct ufs_hba *hba); +static int ufshcd_probe_hba (struct ufs_hba *hba);/* * ufshcd_wait_for_register - wait for register value to change
@@ -2043,6 +2051,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)msleep (5 ); } +/* enable UIC related interrupts */ +ufshcd_enable_intr (hba,UIC_COMMAND_COMPL ); +if (hba->vops && hba->vops->hce_enable_notify) hba->vops->hce_enable_notify (hba,POST_CHANGE );
@@ -2058,23 +2069,33 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)static int ufshcd_link_startup (struct ufs_hba *hba) {int ret; +int retries =DME_LINKSTARTUP_RETRIES ; -/* enable UIC related interrupts */ -ufshcd_enable_intr (hba,UIC_COMMAND_COMPL ); +do { +if (hba->vops && hba->vops->link_startup_notify) + hba->vops->link_startup_notify (hba,PRE_CHANGE ); -if (hba->vops && hba->vops->link_startup_notify) - hba->vops->link_startup_notify (hba,PRE_CHANGE ); + ret =ufshcd_dme_link_startup (hba); - ret =ufshcd_dme_link_startup (hba); -if (ret) - goto out; +/* check if device is detected by inter-connect layer */ +if (!ret && !ufshcd_is_device_present (hba)) { +dev_err (hba->dev,"%s: Device not present\n" , __func__); + ret = -ENXIO ; + goto out; + } -/* check if device is detected by inter-connect layer */ -if (!ufshcd_is_device_present (hba)) { -dev_err (hba->dev,"%s: Device not present\n" , __func__); - ret = -ENXIO ; +/* +* DME link lost indication is only received when link is up, +* but we can't be sure if the link is up until link startup +* succeeds. So reset the local Uni-Pro and try again. +*/ +if (ret &&ufshcd_hba_enable (hba)) + goto out; + }while (ret && retries--); + +if (ret) +/* failed to get the link up... retire */ goto out; - }/* Include any host controller configuration via UIC commands */ if (hba->vops && hba->vops->link_startup_notify) {
@@ -3139,7 +3160,6 @@ out:static int ufshcd_host_reset_and_restore (struct ufs_hba *hba) {int err; -async_cookie_t cookie;unsigned long flags;/* Reset the host controller */
@@ -3152,10 +3172,9 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) goto out;/* Establish the link again and restore the device */ - cookie =async_schedule (ufshcd_async_scan, hba); -/* wait for async scan to be completed */ -async_synchronize_cookie (++cookie); -if (hba->ufshcd_state !=UFSHCD_STATE_OPERATIONAL ) + err =ufshcd_probe_hba (hba); + +if (!err && (hba->ufshcd_state !=UFSHCD_STATE_OPERATIONAL )) err = -EIO ; out:if (err)
@@ -3177,8 +3196,11 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba) {int err =0 ;unsigned long flags; +int retries =MAX_HOST_RESET_RETRIES ; - err =ufshcd_host_reset_and_restore (hba); +do { + err =ufshcd_host_reset_and_restore (hba); + }while (err && --retries);/* * After reset the door-bell might be cleared, complete
@@ -3243,13 +3265,13 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd) }/** -* ufshcd_async_scan - asynchronous execution for link startup -* @data: data pointer to pass to this function -* @cookie: cookie data +* ufshcd_probe_hba - probe hba to detect device and initialize +* @hba: per-adapter instance +* +* Execute link-startup and verify device initialization */ -static void ufshcd_async_scan (void *data,async_cookie_t cookie) +static int ufshcd_probe_hba (struct ufs_hba *hba) { -struct ufs_hba *hba = (struct ufs_hba *)data;int ret; ret =ufshcd_link_startup (hba);
@@ -3275,7 +3297,26 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)pm_runtime_put_sync (hba->dev); } out: -return ; +/* +* If we failed to initialize the device or the device is not +* present, turn off the power/clocks etc. +*/ +if (ret && !ufshcd_eh_in_progress (hba)) +ufshcd_hba_exit (hba); + +return ret; +} + +/** +* ufshcd_async_scan - asynchronous execution for probing hba +* @data: data pointer to pass to this function +* @cookie: cookie data +*/ +static void ufshcd_async_scan (void *data,async_cookie_t cookie) +{ +struct ufs_hba *hba = (struct ufs_hba *)data; + +ufshcd_probe_hba (hba); }static struct scsi_host_template ufshcd_driver_template = {
@@ -3631,6 +3672,7 @@ static int ufshcd_hba_init(struct ufs_hba *hba)if (err) goto out_disable_vreg; + hba->is_powered = true; goto out; out_disable_vreg:
@@ -3645,10 +3687,13 @@ out:static void ufshcd_hba_exit (struct ufs_hba *hba) { -ufshcd_variant_hba_exit (hba); -ufshcd_setup_vreg (hba, false); -ufshcd_setup_clocks (hba, false); -ufshcd_setup_hba_vreg (hba, false); +if (hba->is_powered) { +ufshcd_variant_hba_exit (hba); +ufshcd_setup_vreg (hba, false); +ufshcd_setup_clocks (hba, false); +ufshcd_setup_hba_vreg (hba, false); + hba->is_powered = false; + } }/**
drivers/scsi/ufs/ufshcd.h +2 -0
@@ -228,6 +228,7 @@ struct ufs_hba_variant_ops {* @eh_flags: Error handling flags * @intr_mask: Interrupt Mask Bits * @ee_ctrl_mask: Exception event control mask +* @is_powered: flag to check if HBA is powered * @eh_work: Worker to handle UFS errors that require s/w attention * @eeh_work: Worker to handle exception events * @errors: HBA errors
@@ -283,6 +284,7 @@ struct ufs_hba {u32 eh_flags;u32 intr_mask;u16 ee_ctrl_mask; +bool is_powered;/* Work Queues */ struct work_struct eh_work;
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 9/18 scsi: ufs: Active Power Mode - configuring bActiveICCLevel
In reply to Dolev RavivFrom: Yaniv Gardi <[email protected]>
The maximum power consumption in active is determined by bActiveICCLevel. The configuration is done by reading max current supported by the regulators connected to VCC, VCCQ and VCCQ2 rails on the boards, and reading the current consumption levels from the device for each rails (vcc/vccq/vccq2) using power descriptor.
We configure the bActiveICCLevel attribute, with the max value that correspond to the minimum-of(VCC-current-level,VCCQ-current-level, VCCQ2-current-level).
In order to minimize resume latency, pre-fetch icc levels and reference clock during initialization and avoid reading them each link startup during resume.
drivers/scsi/ufs/ufs.h +26 -0
@@ -115,6 +115,7 @@ enum flag_idn {/* Attribute idn for Query requests */ enum attr_idn { +QUERY_ATTR_IDN_ACTIVE_ICC_LVL =0x03 ,QUERY_ATTR_IDN_BKOPS_STATUS =0x05 ,QUERY_ATTR_IDN_EE_CONTROL =0x0D ,QUERY_ATTR_IDN_EE_STATUS =0x0E ,
@@ -174,6 +175,31 @@ enum unit_desc_param { UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, }; +/* bActiveICCLevel parameter current units */ +enum { +UFSHCD_NANO_AMP =0 , +UFSHCD_MICRO_AMP =1 , +UFSHCD_MILI_AMP =2 , +UFSHCD_AMP =3 , +}; + +#define POWER_DESC_MAX_SIZE 0x62 +#define POWER_DESC_MAX_ACTV_ICC_LVLS 16 + +/* Attribute bActiveICCLevel parameter bit masks definitions */ +#define ATTR_ICC_LVL_UNIT_OFFSET 14 +#define ATTR_ICC_LVL_UNIT_MASK (0x3 << ATTR_ICC_LVL_UNIT_OFFSET) +#define ATTR_ICC_LVL_VALUE_MASK 0x3FF + +/* Power descriptor parameters offsets in bytes */ +enum power_desc_param_offset { +PWR_DESC_LEN =0x0 , +PWR_DESC_TYPE =0x1 , +PWR_DESC_ACTIVE_LVLS_VCC_0 =0x2 , +PWR_DESC_ACTIVE_LVLS_VCCQ_0 =0x22 , +PWR_DESC_ACTIVE_LVLS_VCCQ2_0 =0x42 , +}; +/* Exception event mask values */ enum {MASK_EE_STATUS =0xFFFF ,
drivers/scsi/ufs/ufshcd.c +126 -0
141 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +13 -0
@@ -196,6 +196,15 @@ struct ufs_hba_variant_ops { };/** +* struct ufs_init_prefetch - contains data that is pre-fetched once during +* initialization +* @icc_level: icc level which was read during initialization +*/ +struct ufs_init_prefetch { +u32 icc_level; +}; + +/** * struct ufs_hba - per adapter private structure * @mmio_base: UFSHCI base register address * @ucdl_base_addr: UFS Command Descriptor base address
@@ -229,6 +238,8 @@ struct ufs_hba_variant_ops {* @intr_mask: Interrupt Mask Bits * @ee_ctrl_mask: Exception event control mask * @is_powered: flag to check if HBA is powered +* @is_init_prefetch: flag to check if data was pre-fetched in initialization +* @init_prefetch_data: data pre-fetched during initialization * @eh_work: Worker to handle UFS errors that require s/w attention * @eeh_work: Worker to handle exception events * @errors: HBA errors
@@ -285,6 +296,8 @@ struct ufs_hba {u32 intr_mask;u16 ee_ctrl_mask;bool is_powered; +bool is_init_prefetch; +struct ufs_init_prefetch init_prefetch_data;/* Work Queues */ struct work_struct eh_work;
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 10/18 scsi: ufs: manually add well known logical units
In reply to Dolev RavivFrom: Subhash Jadavani <[email protected]>
UFS device specification requires the UFS devices to support 4 well known logical units:
"REPORT_LUNS" (address: 01h) "UFS Device" (address: 50h) "RPMB" (address: 44h) "BOOT" (address: 30h)
UFS device's power management needs to be controlled by "POWER CONDITION" field of SSU (START STOP UNIT) command. But this "power condition" field will take effect only when its sent to "UFS device" well known logical unit hence we require the scsi_device instance to represent this logical unit in order for the UFS host driver to send the SSU command for power management.
We also require the scsi_device instance for "RPMB" (Replay Protected Memory Block) LU so user space process can control this LU. User space may also want to have access to BOOT LU.
This patch adds the scsi device instances for each of all well known LUs (except "REPORT LUNS" LU).
drivers/scsi/ufs/ufshcd.c +103 -0
@@ -902,6 +902,17 @@ static int ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) }/** +* ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID +* @scsi_lun: UPIU W-LUN id +* +* Returns SCSI W-LUN id +*/ +static inline u16 ufshcd_upiu_wlun_to_scsi_wlun (u8 upiu_wlun_id) +{ +return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID ) |SCSI_W_LUN_BASE ; +} + +/** * ufshcd_queuecommand - main entry point for SCSI requests * @cmd: command from SCSI Midlayer * @done: call back function
@@ -3384,6 +3395,93 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) }/** +* ufshcd_scsi_add_wlus - Adds required W-LUs +* @hba: per-adapter instance +* +* UFS device specification requires the UFS devices to support 4 well known +* logical units: +* "REPORT_LUNS" (address: 01h) +* "UFS Device" (address: 50h) +* "RPMB" (address: 44h) +* "BOOT" (address: 30h) +* UFS device's power management needs to be controlled by "POWER CONDITION" +* field of SSU (START STOP UNIT) command. But this "power condition" field +* will take effect only when its sent to "UFS device" well known logical unit +* hence we require the scsi_device instance to represent this logical unit in +* order for the UFS host driver to send the SSU command for power management. + +* We also require the scsi_device instance for "RPMB" (Replay Protected Memory +* Block) LU so user space process can control this LU. User space may also +* want to have access to BOOT LU. + +* This function adds scsi device instances for each of all well known LUs +* (except "REPORT LUNS" LU). +* +* Returns zero on success (all required W-LUs are added successfully), +* non-zero error value on failure (if failed to add any of the required W-LU). +*/ +static int ufshcd_scsi_add_wlus (struct ufs_hba *hba) +{ +int ret =0 ; + + hba->sdev_ufs_device =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN ),NULL ); +if (IS_ERR (hba->sdev_ufs_device)) { + ret =PTR_ERR (hba->sdev_ufs_device); + hba->sdev_ufs_device =NULL ; + goto out; + } + + hba->sdev_boot =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_BOOT_WLUN ),NULL ); +if (IS_ERR (hba->sdev_boot)) { + ret =PTR_ERR (hba->sdev_boot); + hba->sdev_boot =NULL ; + goto remove_sdev_ufs_device; + } + + hba->sdev_rpmb =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN ),NULL ); +if (IS_ERR (hba->sdev_rpmb)) { + ret =PTR_ERR (hba->sdev_rpmb); + hba->sdev_rpmb =NULL ; + goto remove_sdev_boot; + } + goto out; + +remove_sdev_boot: +scsi_remove_device (hba->sdev_boot); +remove_sdev_ufs_device: +scsi_remove_device (hba->sdev_ufs_device); +out: +return ret; +} + +/** +* ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by +* ufshcd_scsi_add_wlus() +* @hba: per-adapter instance +* +*/ +static void ufshcd_scsi_remove_wlus (struct ufs_hba *hba) +{ +if (hba->sdev_ufs_device) { +scsi_remove_device (hba->sdev_ufs_device); + hba->sdev_ufs_device =NULL ; + } + +if (hba->sdev_boot) { +scsi_remove_device (hba->sdev_boot); + hba->sdev_boot =NULL ; + } + +if (hba->sdev_rpmb) { +scsi_remove_device (hba->sdev_rpmb); + hba->sdev_rpmb =NULL ; + } +} + +/** * ufshcd_probe_hba - probe hba to detect device and initialize * @hba: per-adapter instance *
@@ -3415,6 +3513,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)if (!hba->is_init_prefetch)ufshcd_init_icc_levels (hba); +/* Add required well known logical units to scsi mid layer */ +if (ufshcd_scsi_add_wlus (hba)) + goto out; +scsi_scan_host (hba->host);pm_runtime_put_sync (hba->dev); }
@@ -3901,6 +4003,7 @@ EXPORT_SYMBOL(ufshcd_runtime_idle);void ufshcd_remove (struct ufs_hba *hba) {scsi_remove_host (hba->host); +ufshcd_scsi_remove_wlus (hba);/* disable interrupts */ ufshcd_disable_intr (hba, hba->intr_mask);ufshcd_hba_stop (hba);
drivers/scsi/ufs/ufshcd.h +7 -0
@@ -266,6 +266,13 @@ struct ufs_hba {struct Scsi_Host *host;struct device *dev; +/* +* This field is to keep a reference to "scsi_device" corresponding to +* "UFS device" W-LU. +*/ +struct scsi_device *sdev_ufs_device; +struct scsi_device *sdev_rpmb; +struct scsi_device *sdev_boot;struct ufshcd_lrb *lrb;unsigned long lrb_in_use;
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH V6 10/18] scsi: ufs: manually add well known logical units
2014-09-25 21:32 GMT+09:00 Dolev Raviv <[email protected]>:
Quoted 12 lines
drivers/scsi/ufs/ufshcd.c +12
-0+static int ufshcd_scsi_add_wlus (struct ufs_hba *hba) +{ +int ret =0 ; + + hba->sdev_ufs_device =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN ),NULL ); +if (IS_ERR (hba->sdev_ufs_device)) { + ret =PTR_ERR (hba->sdev_ufs_device); + hba->sdev_ufs_device =NULL ; + goto out; + } +
drivers/scsi/ufs/ufshcd.c static void ufshcd_init_icc_levels(struct ufs_hba *hba) · line 3444
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ hba->sdev_boot =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_BOOT_WLUN ),NULL ); +if (IS_ERR (hba->sdev_boot)) { + ret =PTR_ERR (hba->sdev_boot); + hba->sdev_boot =NULL ; + goto remove_sdev_ufs_device; + } + + hba->sdev_rpmb =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN ),NULL );
These __scsi_add_device() calls for W-LUs increase the module reference count of ufshcd.ko by three. But no one calls scsi_device_put() for these W-LUs, so it is impossible to unload ufshcd.ko.
There are scsi_remove_device() calls for W-LUs in ufshcd_scsi_remove_wlus(), But these calls do nothing (please see a comment below).
I could fix this issue by putting scsi_device_put() just after each __scsi_add_device() call. I'll prepare a patch if I haven't missed something.
Quoted 28 lines
drivers/scsi/ufs/ufshcd.c +28
-0+if (IS_ERR (hba->sdev_rpmb)) { + ret =PTR_ERR (hba->sdev_rpmb); + hba->sdev_rpmb =NULL ; + goto remove_sdev_boot; + } + goto out; + +remove_sdev_boot: +scsi_remove_device (hba->sdev_boot); +remove_sdev_ufs_device: +scsi_remove_device (hba->sdev_ufs_device); +out: +return ret; +} + +/** +* ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by +* ufshcd_scsi_add_wlus() +* @hba: per-adapter instance +* +*/ +static void ufshcd_scsi_remove_wlus (struct ufs_hba *hba) +{ +if (hba->sdev_ufs_device) { +scsi_remove_device (hba->sdev_ufs_device); + hba->sdev_ufs_device =NULL ; + } +
drivers/scsi/ufs/ufshcd.c static void ufshcd_init_icc_levels(struct ufs_hba *hba) · line 3482
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+if (hba->sdev_boot) { +scsi_remove_device (hba->sdev_boot); + hba->sdev_boot =NULL ; + } + +if (hba->sdev_rpmb) { +scsi_remove_device (hba->sdev_rpmb); + hba->sdev_rpmb =NULL ; + } +}
When ufshcd_scsi_remove_wlus() is called in ufshcd_remove(), these three W-LU devices have already been deleted by scsi_remove_host() which is called just before ufshcd_scsi_remove_wlus(). The scsi device state of these W-LU devices is SDEV_DEL at this time, and scsi_remove_device() does nothing.
RE: [PATCH V6 10/18] scsi: ufs: manually add well known logical units
Quoted 1 line
I could fix this issue by putting
scsi_device_put()just after each__scsi_add_device()call. I'll prepare a patch if I haven't missed something.
Thanks Akinobu for fixing it. We were always compiling the driver statically in kernel so didn't notice this. Yes, I would be happy to ACK your change. BTW, these patches are already merged into I could fix this issue by putting git://git.infradead.org/users/hch/scsi-queue.git -b drivers-for-3.18, so you may to base your fix on it.
-----Original Message-----
From: Akinobu Mita [mailto:[email protected]]
Sent: Friday, October 03, 2014 9:16 AM
To: Dolev Raviv
Cc: Jej B; Christoph Hellwig; [email protected]; [email protected]; [email protected]; Santosh Y; Subhash Jadavani
Subject: Re: [PATCH V6 10/18] scsi: ufs: manually add well known logical units
2014-09-25 21:32 GMT+09:00 Dolev Raviv <[email protected]>:
Quoted 12 lines
drivers/scsi/ufs/ufshcd.c +12
-0+static int ufshcd_scsi_add_wlus (struct ufs_hba *hba) { +int ret =0 ; + + hba->sdev_ufs_device =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_UFS_DEVICE_WLUN ),NULL ); +if (IS_ERR (hba->sdev_ufs_device)) { + ret =PTR_ERR (hba->sdev_ufs_device); + hba->sdev_ufs_device =NULL ; + goto out; + } + + hba->sdev_boot = __scsi_add_device(hba->host, 0, 0,
drivers/scsi/ufs/ufshcd.c static void ufshcd_init_icc_levels(struct ufs_hba *hba)
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+ ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL); +if (IS_ERR (hba->sdev_boot)) { + ret =PTR_ERR (hba->sdev_boot); + hba->sdev_boot =NULL ; + goto remove_sdev_ufs_device; + } + + hba->sdev_rpmb =__scsi_add_device (hba->host,0 ,0 , +ufshcd_upiu_wlun_to_scsi_wlun (UFS_UPIU_RPMB_WLUN ), +NULL );
These __scsi_add_device() calls for W-LUs increase the module reference count of ufshcd.ko by three. But no one calls scsi_device_put() for these W-LUs, so it is impossible to unload ufshcd.ko.
There are scsi_remove_device() calls for W-LUs in ufshcd_scsi_remove_wlus(), But these calls do nothing (please see a comment below).
I could fix this issue by putting scsi_device_put() just after each __scsi_add_device() call. I'll prepare a patch if I haven't missed something.
Quoted 27 lines
drivers/scsi/ufs/ufshcd.c +27
-0+if (IS_ERR (hba->sdev_rpmb)) { + ret =PTR_ERR (hba->sdev_rpmb); + hba->sdev_rpmb =NULL ; + goto remove_sdev_boot; + } + goto out; + +remove_sdev_boot: +scsi_remove_device (hba->sdev_boot); +remove_sdev_ufs_device: +scsi_remove_device (hba->sdev_ufs_device); +out: +return ret; +} + +/** +* ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by +* ufshcd_scsi_add_wlus() +* @hba: per-adapter instance +* +*/ +static void ufshcd_scsi_remove_wlus (struct ufs_hba *hba) { +if (hba->sdev_ufs_device) { +scsi_remove_device (hba->sdev_ufs_device); + hba->sdev_ufs_device =NULL ; + } +
drivers/scsi/ufs/ufshcd.c static void ufshcd_init_icc_levels(struct ufs_hba *hba) · line 3482
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10
-0+if (hba->sdev_boot) { +scsi_remove_device (hba->sdev_boot); + hba->sdev_boot =NULL ; + } + +if (hba->sdev_rpmb) { +scsi_remove_device (hba->sdev_rpmb); + hba->sdev_rpmb =NULL ; + } +}
When ufshcd_scsi_remove_wlus() is called in ufshcd_remove(), these three W-LU devices have already been deleted by scsi_remove_host() which is called just before ufshcd_scsi_remove_wlus(). The scsi device state of these W-LU devices is SDEV_DEL at this time, and scsi_remove_device() does nothing.
Re: [PATCH V6 10/18] scsi: ufs: manually add well known logical units
In reply to Akinobu MitaQuoted 12 lines · Akinobu Mita
These
__scsi_add_device()calls for W-LUs increase the module reference count of ufshcd.ko by three. But no one callsscsi_device_put()for these W-LUs, so it is impossible to unload ufshcd.ko.There are
scsi_remove_device()calls for W-LUs inufshcd_scsi_remove_wlus(), But these calls do nothing (please see a comment below).I could fix this issue by putting
scsi_device_put()just after each__scsi_add_device()call. I'll prepare a patch if I haven't missed something.
Quoted 5 lines
When
ufshcd_scsi_remove_wlus()is called inufshcd_remove(), these three W-LU devices have already been deleted byscsi_remove_host()which is called just beforeufshcd_scsi_remove_wlus(). The scsi device state of these W-LU devices isSDEV_DELat this time, andscsi_remove_device()does nothing.
No, we need to keep a reference to avoid them going away due to a manual delete. We need a scsi_device_put where we currently call scsi_remove_device I guess.
Re: [PATCH V6 10/18] scsi: ufs: manually add well known logical units
2014-10-04 1:35 GMT+09:00 Christoph Hellwig <[email protected]>:
Quoted 20 lines · Akinobu Mita
Quoted 12 lines
These
__scsi_add_device()calls for W-LUs increase the module reference count of ufshcd.ko by three. But no one callsscsi_device_put()for these W-LUs, so it is impossible to unload ufshcd.ko.There are
scsi_remove_device()calls for W-LUs inufshcd_scsi_remove_wlus(), But these calls do nothing (please see a comment below).I could fix this issue by putting
scsi_device_put()just after each__scsi_add_device()call. I'll prepare a patch if I haven't missed something.Quoted 5 lines
When
ufshcd_scsi_remove_wlus()is called inufshcd_remove(), these three W-LU devices have already been deleted byscsi_remove_host()which is called just beforeufshcd_scsi_remove_wlus(). The scsi device state of these W-LU devices isSDEV_DELat this time, andscsi_remove_device()does nothing.
Quoted 3 lines
No, we need to keep a reference to avoid them going away due to a manual delete. We need a
scsi_device_putwhere we currently callscsi_remove_deviceI guess.
This also works for now. But there is a concern.
In the previous email, I said that __scsi_add_device() calls for W-LUs increase the module reference count of ufshcd.ko by three. But the module reference count of ufshcd-pci.ko (or ufshcd-pltfrm.ko) should be increased instead of ufshcd.ko. Because ufshcd.ko is core driver and it just provides library function for the actual LLD (ufshcd-pci.ko and ufshcd-pltfrm.ko).
Once this problem is fixed (by setting correct ufshcd_driver_template.module or else way), it is impossible to unload ufshcd-pci.ko (or ufshcd-pltfrm.ko).
So the fix I proposed in the previous email plus additional work to avoid breakage with manual delete of W-LUs (specifically, correct mutal exclusion for hba->sdev_ufs_device accesses from ufshcd_slave_destroy() and from ufshcd_set_dev_pwr_mode()) is acceptable?
PATCH v6 11/18 scsi: ufs: introduce well known logical unit in ufs
In reply to Dolev RavivFrom: Subhash Jadavani <[email protected]>
UFS device may have standard LUs and LUN id could be from 0x00 to 0x7F. UFS device specification use "Peripheral Device Addressing Format" (SCSI SAM-5) for standard LUs.
UFS device may also have the Well Known LUs (also referred as W-LU) which again could be from 0x00 to 0x7F. For W-LUs, UFS device specification only allows the "Extended Addressing Format" (SCSI SAM-5) which means the W-LUNs would start from 0xC100 onwards.
This means max. LUN number reported from UFS device could be 0xC17F hence this patch advertise the "max_lun" as 0xC17F which will allow SCSI mid layer to detect the W-LUs as well.
But once the W-LUs are detected, UFSHCD driver may get the commands with SCSI LUN id upto 0xC17F but UPIU LUN id field is only 8-bit wide so it requires the mapping of SCSI LUN id to UPIU LUN id. This patch also add support for this mapping.
drivers/scsi/ufs/ufs.h +20 -1
@@ -49,9 +49,28 @@#define UPIU_HEADER_DWORD (byte3, byte2, byte1, byte0)\ cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ (byte1 << 8) | (byte0)) - +/* +* UFS device may have standard LUs and LUN id could be from 0x00 to +* 0x7F. Standard LUs use "Peripheral Device Addressing Format". +* UFS device may also have the Well Known LUs (also referred as W-LU) +* which again could be from 0x00 to 0x7F. For W-LUs, device only use +* the "Extended Addressing Format" which means the W-LUNs would be +* from 0xc100 (SCSI_W_LUN_BASE) onwards. +* This means max. LUN number reported from UFS device could be 0xC17F. +*/ +#define UFS_UPIU_MAX_UNIT_NUM_ID 0x7F +#define UFS_MAX_LUNS (SCSI_W_LUN_BASE + UFS_UPIU_MAX_UNIT_NUM_ID) +#define UFS_UPIU_WLUN_ID (1 << 7)#define UFS_UPIU_MAX_GENERAL_LUN 8 +/* Well known logical unit id in LUN field of UPIU */ +enum { +UFS_UPIU_REPORT_LUNS_WLUN =0x81 , +UFS_UPIU_UFS_DEVICE_WLUN =0xD0 , +UFS_UPIU_BOOT_WLUN =0xB0 , +UFS_UPIU_RPMB_WLUN =0xC4 , +}; +/* * UFS Protocol Information Unit related definitions */
drivers/scsi/ufs/ufshcd.c +64 -20
145 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +1 -1
@@ -124,7 +124,7 @@ struct ufshcd_lrb {int command_type;int task_tag; -unsigned int lun; +u8 lun;/* UPIU LUN id field is only 8-bit wide */ bool intr_cmd; };
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 12/18 scsi: ufs: add UFS power management support
In reply to Dolev RavivFrom: Subhash Jadavani <[email protected]>
This patch adds support for UFS device and UniPro link power management during runtime/system PM.
Main idea is to define multiple UFS low power levels based on UFS device and UFS link power states. This would allow any specific platform or pci driver to choose the best suited low power level during runtime and system suspend based on their power goals.
bkops handlig:
To put the UFS device in sleep state when bkops is disabled, first query the bkops status from the device and enable bkops on device only if device needs time to perform the bkops.
START_STOP handling:
Before sending START_STOP_UNIT to the device well-known logical unit (w-lun) to make sure that the device w-lun unit attention condition is cleared.
Write protection:
UFS device specification allows LUs to be write protected, either permanently or power on write protected. If any LU is power on write protected and if the card is power cycled (by powering off VCCQ and/or VCC rails), LU's write protect status would be lost. So this means those LUs can be written now. To ensures that UFS device is power cycled only if the power on protect is not set for any of the LUs, check if power on write protect is set and if device is in sleep/power-off state & link in inactive state (Hibern8 or OFF state).
If none of the Logical Units on UFS device is power on write protected then all UFS device power rails (VCC, VCCQ & VCCQ2) can be turned off if UFS device is in power-off state and UFS link is in OFF state. But current implementation would disable all device power rails even if UFS link is not in OFF state.
Low power mode:
If UFS link is in OFF state then UFS host controller can be power collapsed to avoid leakage current from it. Note that if UFS host controller is power collapsed, full UFS reinitialization will be required on resume to re-establish the link between host and device.
drivers/scsi/ufs/ufs.h +35 -1
@@ -129,6 +129,7 @@ enum {/* Flag idn for Query Requests*/ enum flag_idn {QUERY_FLAG_IDN_FDEVICEINIT =0x01 , +QUERY_FLAG_IDN_PWR_ON_WPE =0x03 ,QUERY_FLAG_IDN_BKOPS_EN =0x04 , };
@@ -194,6 +195,18 @@ enum unit_desc_param { UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, }; +/* +* Logical Unit Write Protect +* 00h: LU not write protected +* 01h: LU write protected when fPowerOnWPEn =1 +* 02h: LU permanently write protected when fPermanentWPEn =1 +*/ +enum ufs_lu_wp_type { +UFS_LU_NO_WP =0x00 , +UFS_LU_POWER_ON_WP =0x01 , +UFS_LU_PERM_WP =0x02 , +}; +/* bActiveICCLevel parameter current units */ enum {UFSHCD_NANO_AMP =0 ,
@@ -226,11 +239,12 @@ enum { };/* Background operation status */ -enum { +enum bkops_status {BKOPS_STATUS_NO_OP =0x0 ,BKOPS_STATUS_NON_CRITICAL =0x1 ,BKOPS_STATUS_PERF_IMPACT =0x2 ,BKOPS_STATUS_CRITICAL =0x3 , +BKOPS_STATUS_MAX =BKOPS_STATUS_CRITICAL , };/* UTP QUERY Transaction Specific Fields OpCode */
@@ -291,6 +305,14 @@ enum {UPIU_TASK_MANAGEMENT_FUNC_FAILED =0x05 , UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09, }; + +/* UFS device power modes */ +enum ufs_dev_pwr_mode { +UFS_ACTIVE_PWR_MODE =1 , +UFS_SLEEP_PWR_MODE =2 , +UFS_POWERDOWN_PWR_MODE =3 , +}; +/** * struct utp_upiu_header - UPIU header structure * @dword_0: UPIU header DW-0
@@ -437,6 +459,12 @@ struct ufs_query_res {#define UFS_VREG_VCCQ2_MIN_UV 1650000/* uV */ #define UFS_VREG_VCCQ2_MAX_UV 1950000/* uV */ +/* +* VCCQ & VCCQ2 current requirement when UFS device is in sleep state +* and link is in Hibern8 state. +*/ +#define UFS_VREG_LPM_LOAD_UA 1000/* uA */ +struct ufs_vreg {struct regulator *reg;const char *name;
@@ -454,4 +482,10 @@ struct ufs_vreg_info { struct ufs_vreg *vdd_hba; }; +struct ufs_dev_info { +bool f_power_on_wp_en; +/* Keeps information if any of the LU is power on write protected */ +bool is_lu_power_on_wp; +}; +#endif /* End of Header */
drivers/scsi/ufs/ufshcd-pci.c +10 -35
81 lines not on this page · raw message
drivers/scsi/ufs/ufshcd-pltfrm.c +15 -45
102 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.c +852 -93
1,205 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +71 -5
131 lines not on this page · raw message
drivers/scsi/ufs/ufshci.h +6 -3
@@ -124,8 +124,11 @@ enum {#define CONTROLLER_FATAL_ERROR UFS_BIT(16)#define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17) -#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL |\ - UIC_POWER_MODE) +#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\ + UIC_HIBERNATE_EXIT |\ + UIC_POWER_MODE) + +#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK)#define UFSHCD_ERROR_MASK (UIC_ERROR |\ DEVICE_FATAL_ERROR |\
@@ -210,7 +213,7 @@ enum {#define UIC_GET_ATTR_ID (v) (((v) >> 16) & 0xFFFF)/* UIC Commands */ -enum { +enum uic_cmd_dme {UIC_CMD_DME_GET =0x01 ,UIC_CMD_DME_SET =0x02 ,UIC_CMD_DME_PEER_GET =0x03 ,
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 13/18 scsi: ufs: refactor configuring power mode
In reply to Dolev RavivSometimes, the device shall report its maximum power and speed capabilities, but we might not wish to configure it to use those maximum capabilities.
This change adds support for the vendor specific host driver to implement power change notify callback.
To enable configuring different power modes (number of lanes, gear number and fast/slow modes) it is necessary to split the configuration stage from the stage that reads the device max power mode. In addition, it is not required to read the configuration more than once, thus the configuration is stored after reading it once.
drivers/scsi/ufs/ufshcd.c +133 -33
@@ -179,6 +179,8 @@ static void ufshcd_hba_exit(struct ufs_hba *hba);static int ufshcd_probe_hba (struct ufs_hba *hba);static int ufshcd_host_reset_and_restore (struct ufs_hba *hba);static irqreturn_t ufshcd_intr (int irq,void *__hba); +static int ufshcd_config_pwr_mode (struct ufs_hba *hba, +struct ufs_pa_layer_attr *desired_pwr_mode);static inline int ufshcd_enable_irq (struct ufs_hba *hba) {
@@ -1958,40 +1960,83 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) }/** -* ufshcd_config_max_pwr_mode - Set & Change power mode with -* maximum capability attribute information. -* @hba: per adapter instance -* -* Returns 0 on success, non-zero value on failure +* ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device +* @hba: per-adapter instance */ -static int ufshcd_config_max_pwr_mode (struct ufs_hba *hba) +static int ufshcd_get_max_pwr_mode (struct ufs_hba *hba) { -enum {RX =0 ,TX =1 }; -u32 lanes[] = {1 ,1 }; -u32 gear[] = {1 ,1 }; -u8 pwr[] = {FASTAUTO_MODE ,FASTAUTO_MODE }; -int ret; +struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; + +if (hba->max_pwr_info.is_valid) +return 0 ; + + pwr_info->pwr_tx =FASTAUTO_MODE ; + pwr_info->pwr_rx =FASTAUTO_MODE ; + pwr_info->hs_rate =PA_HS_MODE_B ;/* Get the connected lane count */ -ufshcd_dme_get (hba,UIC_ARG_MIB (PA_CONNECTEDRXDATALANES ), &lanes[RX ]); -ufshcd_dme_get (hba,UIC_ARG_MIB (PA_CONNECTEDTXDATALANES ), &lanes[TX ]); +ufshcd_dme_get (hba,UIC_ARG_MIB (PA_CONNECTEDRXDATALANES ), + &pwr_info->lane_rx); +ufshcd_dme_get (hba,UIC_ARG_MIB (PA_CONNECTEDTXDATALANES ), + &pwr_info->lane_tx); + +if (!pwr_info->lane_rx || !pwr_info->lane_tx) { +dev_err (hba->dev,"%s: invalid connected lanes value. rx=%d, tx=%d\n" , + __func__, + pwr_info->lane_rx, + pwr_info->lane_tx); +return -EINVAL ; + }/* * First, get the maximum gears of HS speed. * If a zero value, it means there is no HSGEAR capability. * Then, get the maximum gears of PWM speed. */ -ufshcd_dme_get (hba,UIC_ARG_MIB (PA_MAXRXHSGEAR ), &gear[RX ]); -if (!gear[RX ]) { -ufshcd_dme_get (hba,UIC_ARG_MIB (PA_MAXRXPWMGEAR ), &gear[RX ]); - pwr[RX ] =SLOWAUTO_MODE ; +ufshcd_dme_get (hba,UIC_ARG_MIB (PA_MAXRXHSGEAR ), &pwr_info->gear_rx); +if (!pwr_info->gear_rx) { +ufshcd_dme_get (hba,UIC_ARG_MIB (PA_MAXRXPWMGEAR ), + &pwr_info->gear_rx); +if (!pwr_info->gear_rx) { +dev_err (hba->dev,"%s: invalid max pwm rx gear read = %d\n" , + __func__, pwr_info->gear_rx); +return -EINVAL ; + } + pwr_info->pwr_rx =SLOWAUTO_MODE ; } -ufshcd_dme_peer_get (hba,UIC_ARG_MIB (PA_MAXRXHSGEAR ), &gear[TX ]); -if (!gear[TX ]) { +ufshcd_dme_peer_get (hba,UIC_ARG_MIB (PA_MAXRXHSGEAR ), + &pwr_info->gear_tx); +if (!pwr_info->gear_tx) {ufshcd_dme_peer_get (hba,UIC_ARG_MIB (PA_MAXRXPWMGEAR ), - &gear[TX ]); - pwr[TX ] =SLOWAUTO_MODE ; + &pwr_info->gear_tx); +if (!pwr_info->gear_tx) { +dev_err (hba->dev,"%s: invalid max pwm tx gear read = %d\n" , + __func__, pwr_info->gear_tx); +return -EINVAL ; + } + pwr_info->pwr_tx =SLOWAUTO_MODE ; + } + + hba->max_pwr_info.is_valid = true; +return 0 ; +} + +int ufshcd_change_power_mode (struct ufs_hba *hba, +struct ufs_pa_layer_attr *pwr_mode) +{ +int ret; + +/* if already configured to the requested pwr_mode */ +if (pwr_mode->gear_rx == hba->pwr_info.gear_rx && + pwr_mode->gear_tx == hba->pwr_info.gear_tx && + pwr_mode->lane_rx == hba->pwr_info.lane_rx && + pwr_mode->lane_tx == hba->pwr_info.lane_tx && + pwr_mode->pwr_rx == hba->pwr_info.pwr_rx && + pwr_mode->pwr_tx == hba->pwr_info.pwr_tx && + pwr_mode->hs_rate == hba->pwr_info.hs_rate) { +dev_dbg (hba->dev,"%s: power already configured\n" , __func__); +return 0 ; }/*
@@ -2000,23 +2045,67 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)* - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION, * - PA_HSSERIES */ -ufshcd_dme_set (hba,UIC_ARG_MIB (PA_RXGEAR ), gear[RX ]); -ufshcd_dme_set (hba,UIC_ARG_MIB (PA_ACTIVERXDATALANES ), lanes[RX ]); -if (pwr[RX ] ==FASTAUTO_MODE ) +ufshcd_dme_set (hba,UIC_ARG_MIB (PA_RXGEAR ), pwr_mode->gear_rx); +ufshcd_dme_set (hba,UIC_ARG_MIB (PA_ACTIVERXDATALANES ), + pwr_mode->lane_rx); +if (pwr_mode->pwr_rx ==FASTAUTO_MODE || + pwr_mode->pwr_rx ==FAST_MODE )ufshcd_dme_set (hba,UIC_ARG_MIB (PA_RXTERMINATION ), TRUE); +else +ufshcd_dme_set (hba,UIC_ARG_MIB (PA_RXTERMINATION ), FALSE); -ufshcd_dme_set (hba,UIC_ARG_MIB (PA_TXGEAR ), gear[TX ]); -ufshcd_dme_set (hba,UIC_ARG_MIB (PA_ACTIVETXDATALANES ), lanes[TX ]); -if (pwr[TX ] ==FASTAUTO_MODE ) +ufshcd_dme_set (hba,UIC_ARG_MIB (PA_TXGEAR ), pwr_mode->gear_tx); +ufshcd_dme_set (hba,UIC_ARG_MIB (PA_ACTIVETXDATALANES ), + pwr_mode->lane_tx); +if (pwr_mode->pwr_tx ==FASTAUTO_MODE || + pwr_mode->pwr_tx ==FAST_MODE )ufshcd_dme_set (hba,UIC_ARG_MIB (PA_TXTERMINATION ), TRUE); +else +ufshcd_dme_set (hba,UIC_ARG_MIB (PA_TXTERMINATION ), FALSE); -if (pwr[RX ] ==FASTAUTO_MODE || pwr[TX ] ==FASTAUTO_MODE ) -ufshcd_dme_set (hba,UIC_ARG_MIB (PA_HSSERIES ),PA_HS_MODE_B ); +if (pwr_mode->pwr_rx ==FASTAUTO_MODE || + pwr_mode->pwr_tx ==FASTAUTO_MODE || + pwr_mode->pwr_rx ==FAST_MODE || + pwr_mode->pwr_tx ==FAST_MODE ) +ufshcd_dme_set (hba,UIC_ARG_MIB (PA_HSSERIES ), + pwr_mode->hs_rate); - ret =ufshcd_uic_change_pwr_mode (hba, pwr[RX ] <<4 | pwr[TX ]); -if (ret) + ret =ufshcd_uic_change_pwr_mode (hba, pwr_mode->pwr_rx <<4 + | pwr_mode->pwr_tx); + +if (ret) {dev_err (hba->dev, -"pwr_mode: power mode change failed %d\n" , ret); +"%s: power mode change failed %d\n" , __func__, ret); + }else { +if (hba->vops && hba->vops->pwr_change_notify) + hba->vops->pwr_change_notify (hba, +POST_CHANGE ,NULL , pwr_mode); + +memcpy (&hba->pwr_info, pwr_mode, +sizeof (struct ufs_pa_layer_attr )); + } + +return ret; +} + +/** +* ufshcd_config_pwr_mode - configure a new power mode +* @hba: per-adapter instance +* @desired_pwr_mode: desired power configuration +*/ +static int ufshcd_config_pwr_mode (struct ufs_hba *hba, +struct ufs_pa_layer_attr *desired_pwr_mode) +{ +struct ufs_pa_layer_attr final_params = {0 }; +int ret; + +if (hba->vops && hba->vops->pwr_change_notify) + hba->vops->pwr_change_notify (hba, +PRE_CHANGE , desired_pwr_mode, &final_params); +else +memcpy (&final_params, desired_pwr_mode,sizeof (final_params)); + + ret =ufshcd_change_power_mode (hba, &final_params);return ret; }
@@ -3757,7 +3846,16 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) hba->ufshcd_state =UFSHCD_STATE_OPERATIONAL ; hba->wlun_dev_clr_ua = true; -ufshcd_config_max_pwr_mode (hba); +if (ufshcd_get_max_pwr_mode (hba)) { +dev_err (hba->dev, +"%s: Failed getting max supported power mode\n" , + __func__); + }else { + ret =ufshcd_config_pwr_mode (hba, &hba->max_pwr_info.info); +if (ret) +dev_err (hba->dev,"%s: Failed setting power mode, err = %d\n" , + __func__, ret); + }/* * If we are in error handling context or in power management callbacks
@@ -4920,6 +5018,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) host->unique_id = host->host_no; host->max_cmd_len =MAX_CDB_SIZE ; + hba->max_pwr_info.is_valid = false; +/* Initailize wait queue for task management */ init_waitqueue_head (&hba->tm_wq);init_waitqueue_head (&hba->tm_tag_wq);
drivers/scsi/ufs/ufshcd.h +27 -0
@@ -221,6 +221,22 @@ struct ufs_clk_info {#define PRE_CHANGE 0#define POST_CHANGE 1 + +struct ufs_pa_layer_attr { +u32 gear_rx; +u32 gear_tx; +u32 lane_rx; +u32 lane_tx; +u32 pwr_rx; +u32 pwr_tx; +u32 hs_rate; +}; + +struct ufs_pwr_mode_info { +bool is_valid; +struct ufs_pa_layer_attr info; +}; +/** * struct ufs_hba_variant_ops - variant specific callbacks * @name: variant name
@@ -232,6 +248,9 @@ struct ufs_clk_info {* variant specific Uni-Pro initialization. * @link_startup_notify: called before and after Link startup is carried out * to allow variant specific Uni-Pro initialization. +* @pwr_change_notify: called before and after a power mode change +* is carried out to allow vendor spesific capabilities +* to be set. * @suspend: called during host controller PM callback * @resume: called during host controller PM callback */
@@ -243,6 +262,9 @@ struct ufs_hba_variant_ops {int (*setup_regulators)(struct ufs_hba *,bool );int (*hce_enable_notify)(struct ufs_hba *,bool );int (*link_startup_notify)(struct ufs_hba *,bool ); +int (*pwr_change_notify)(struct ufs_hba *, +bool ,struct ufs_pa_layer_attr *, +struct ufs_pa_layer_attr *);int (*suspend)(struct ufs_hba *,enum ufs_pm_op );int (*resume)(struct ufs_hba *,enum ufs_pm_op ); };
@@ -302,6 +324,8 @@ struct ufs_init_prefetch {* @auto_bkops_enabled: to track whether bkops is enabled in device * @vreg_info: UFS device voltage regulator information * @clk_list_head: UFS host controller clocks list node head +* @pwr_info: holds current power mode +* @max_pwr_info: keeps the device max valid pwm */ struct ufs_hba {void __iomem *mmio_base;
@@ -387,6 +411,9 @@ struct ufs_hba {struct list_head clk_list_head;bool wlun_dev_clr_ua; + +struct ufs_pa_layer_attr pwr_info; +struct ufs_pwr_mode_info max_pwr_info; };#define ufshcd_writel (hba, val, reg) \
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH/RESEND V6 13/18] scsi: ufs: refactor configuring power mode
2014-09-25 21:32 GMT+09:00 Dolev Raviv <[email protected]>:
Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +6
-0+int ufshcd_change_power_mode (struct ufs_hba *hba, +struct ufs_pa_layer_attr *pwr_mode) +{ +int ret; + +/* if already configured to the requested pwr_mode */
drivers/scsi/ufs/ufshcd.c static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) · line 2040
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +9
-0+if (pwr_mode->gear_rx == hba->pwr_info.gear_rx && + pwr_mode->gear_tx == hba->pwr_info.gear_tx && + pwr_mode->lane_rx == hba->pwr_info.lane_rx && + pwr_mode->lane_tx == hba->pwr_info.lane_tx && + pwr_mode->pwr_rx == hba->pwr_info.pwr_rx && + pwr_mode->pwr_tx == hba->pwr_info.pwr_tx && + pwr_mode->hs_rate == hba->pwr_info.hs_rate) { +dev_dbg (hba->dev,"%s: power already configured\n" , __func__); +return 0 ; }
If the UFS Power management level 5 is chosen, the UIC link is down during suspend. And the power mode should be configured during resume again. Unfortunately, it will not be configured because the above condition hits unintentionally.
I would like to propose a fix which changes the type of hba->pwr_info to struct ufs_pwr_mode_info, and turn off hba->pwr_info.is_valid when UIC link is down and add check for hba->pwr_info.is_valid in above condition.
RE: [PATCH/RESEND V6 13/18] scsi: ufs: refactor configuring power mode
Quoted 1 line
If the UFS Power management level 5 is chosen, the UIC link is down during suspend. And the power mode should be configured during resume again. Unfortunately, it will not be configured because the above condition hits unintentionally.
Yes, it seems a bug.
Quoted 1 line
I would like to propose a fix which changes the type of
hba->pwr_infoto structufs_pwr_mode_info, and turn offhba->pwr_info.is_validwhen UIC link is down and add check forhba->pwr_info.is_validin above condition.
Agreed, Please send your fix on git://git.infradead.org/users/hch/scsi-queue.git -b drivers-for-3.18.
-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Akinobu Mita
Sent: Friday, October 03, 2014 9:20 AM
To: Dolev Raviv
Cc: Jej B; Christoph Hellwig; [email protected]; [email protected]; [email protected]; Santosh Y; Yaniv Gardi
Subject: Re: [PATCH/RESEND V6 13/18] scsi: ufs: refactor configuring power mode
2014-09-25 21:32 GMT+09:00 Dolev Raviv <[email protected]>:
Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +5
-0+int ufshcd_change_power_mode (struct ufs_hba *hba, +struct ufs_pa_layer_attr *pwr_mode) { +int ret; + +/* if already configured to the requested pwr_mode */
drivers/scsi/ufs/ufshcd.c static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) · line 2040
Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +9
-0+if (pwr_mode->gear_rx == hba->pwr_info.gear_rx && + pwr_mode->gear_tx == hba->pwr_info.gear_tx && + pwr_mode->lane_rx == hba->pwr_info.lane_rx && + pwr_mode->lane_tx == hba->pwr_info.lane_tx && + pwr_mode->pwr_rx == hba->pwr_info.pwr_rx && + pwr_mode->pwr_tx == hba->pwr_info.pwr_tx && + pwr_mode->hs_rate == hba->pwr_info.hs_rate) { +dev_dbg (hba->dev,"%s: power already configured\n" , __func__); +return 0 ; }
If the UFS Power management level 5 is chosen, the UIC link is down during suspend. And the power mode should be configured during resume again. Unfortunately, it will not be configured because the above condition hits unintentionally.
I would like to propose a fix which changes the type of hba->pwr_info to struct ufs_pwr_mode_info, and turn off hba->pwr_info.is_valid when UIC link is down and add check for hba->pwr_info.is_valid in above condition. --
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v6 14/18 scsi: ufs: Add support for clock gating
In reply to Dolev RavivFrom: Sahitya Tummala <[email protected]>
The UFS controller clocks can be gated after certain period of inactivity, which is typically less than runtime suspend timeout. In addition to clocks the link will also be put into Hibern8 mode to save more power.
The clock gating can be turned on by enabling the capability UFSHCD_CAP_CLK_GATING. To enable entering into Hibern8 mode as part of clock gating, set the capability UFSHCD_CAP_HIBERN8_WITH_CLK_GATING.
The tracing events for clock gating can be enabled through debugfs as:
echo 1 > /sys/kernel/debug/tracing/events/ufs/ufshcd_clk_gating/enable cat /sys/kernel/debug/tracing/trace_pipe
drivers/scsi/ufs/ufshcd.c +298 -25
@@ -177,6 +177,11 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba);static int ufshcd_clear_tm_cmd (struct ufs_hba *hba,int tag);static void ufshcd_hba_exit (struct ufs_hba *hba);static int ufshcd_probe_hba (struct ufs_hba *hba); +static int __ufshcd_setup_clocks (struct ufs_hba *hba,bool on, +bool skip_ref_clk); +static int ufshcd_setup_clocks (struct ufs_hba *hba,bool on); +static int ufshcd_uic_hibern8_exit (struct ufs_hba *hba); +static int ufshcd_uic_hibern8_enter (struct ufs_hba *hba);static int ufshcd_host_reset_and_restore (struct ufs_hba *hba);static irqreturn_t ufshcd_intr (int irq,void *__hba); static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
@@ -507,6 +512,231 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)return (ufshcd_readl (hba,REG_CONTROLLER_ENABLE ) &0x1 ) ?0 :1 ; } +static void ufshcd_ungate_work (struct work_struct *work) +{ +int ret; +unsigned long flags; + struct ufs_hba *hba = container_of(work, struct ufs_hba, + clk_gating.ungate_work); + +cancel_delayed_work_sync (&hba->clk_gating.gate_work); + +spin_lock_irqsave (hba->host->host_lock, flags); +if (hba->clk_gating.state ==CLKS_ON ) { +spin_unlock_irqrestore (hba->host->host_lock, flags); + goto unblock_reqs; + } + +spin_unlock_irqrestore (hba->host->host_lock, flags); +ufshcd_setup_clocks (hba, true); + +/* Exit from hibern8 */ +if (ufshcd_can_hibern8_during_gating (hba)) { +/* Prevent gating in this path */ + hba->clk_gating.is_suspended = true; +if (ufshcd_is_link_hibern8 (hba)) { + ret =ufshcd_uic_hibern8_exit (hba); +if (ret) +dev_err (hba->dev,"%s: hibern8 exit failed %d\n" , + __func__, ret); +else +ufshcd_set_link_active (hba); + } + hba->clk_gating.is_suspended = false; + } +unblock_reqs: +scsi_unblock_requests (hba->host); +} + +/** +* ufshcd_hold - Enable clocks that were gated earlier due to ufshcd_release. +* Also, exit from hibern8 mode and set the link as active. +* @hba: per adapter instance +* @async: This indicates whether caller should ungate clocks asynchronously. +*/ +int ufshcd_hold (struct ufs_hba *hba,bool async) +{ +int rc =0 ; +unsigned long flags; + +if (!ufshcd_is_clkgating_allowed (hba)) + goto out; +start: +spin_lock_irqsave (hba->host->host_lock, flags); + hba->clk_gating.active_reqs++; + +switch (hba->clk_gating.state) { +case CLKS_ON : +break ; +case REQ_CLKS_OFF : +if (cancel_delayed_work (&hba->clk_gating.gate_work)) { + hba->clk_gating.state =CLKS_ON ; +break ; + } +/* +* If we here, it means gating work is either done or +* currently running. Hence, fall through to cancel gating +* work and to enable clocks. +*/ +case CLKS_OFF : +scsi_block_requests (hba->host); + hba->clk_gating.state =REQ_CLKS_ON ; +schedule_work (&hba->clk_gating.ungate_work); +/* +* fall through to check if we should wait for this +* work to be done or not. +*/ +case REQ_CLKS_ON : +if (async) { + rc = -EAGAIN ; + hba->clk_gating.active_reqs--; +break ; + } + +spin_unlock_irqrestore (hba->host->host_lock, flags); +flush_work (&hba->clk_gating.ungate_work); +/* Make sure state is CLKS_ON before returning */ + goto start; +default : +dev_err (hba->dev,"%s: clk gating is in invalid state %d\n" , + __func__, hba->clk_gating.state); +break ; + } +spin_unlock_irqrestore (hba->host->host_lock, flags); +out: +return rc; +} + +static void ufshcd_gate_work (struct work_struct *work) +{ + struct ufs_hba *hba = container_of(work, struct ufs_hba, + clk_gating.gate_work.work); +unsigned long flags; + +spin_lock_irqsave (hba->host->host_lock, flags); +if (hba->clk_gating.is_suspended) { + hba->clk_gating.state =CLKS_ON ; + goto rel_lock; + } + +if (hba->clk_gating.active_reqs + || hba->ufshcd_state !=UFSHCD_STATE_OPERATIONAL + || hba->lrb_in_use || hba->outstanding_tasks + || hba->active_uic_cmd || hba->uic_async_done) + goto rel_lock; + +spin_unlock_irqrestore (hba->host->host_lock, flags); + +/* put the link into hibern8 mode before turning off clocks */ +if (ufshcd_can_hibern8_during_gating (hba)) { +if (ufshcd_uic_hibern8_enter (hba)) { + hba->clk_gating.state =CLKS_ON ; + goto out; + } +ufshcd_set_link_hibern8 (hba); + } + +if (!ufshcd_is_link_active (hba)) +ufshcd_setup_clocks (hba, false); +else +/* If link is active, device ref_clk can't be switched off */ +__ufshcd_setup_clocks (hba, false, true); + +/* +* In case you are here to cancel this work the gating state +* would be marked as REQ_CLKS_ON. In this case keep the state +* as REQ_CLKS_ON which would anyway imply that clocks are off +* and a request to turn them on is pending. By doing this way, +* we keep the state machine in tact and this would ultimately +* prevent from doing cancel work multiple times when there are +* new requests arriving before the current cancel work is done. +*/ +spin_lock_irqsave (hba->host->host_lock, flags); +if (hba->clk_gating.state ==REQ_CLKS_OFF ) + hba->clk_gating.state =CLKS_OFF ; + +rel_lock: +spin_unlock_irqrestore (hba->host->host_lock, flags); +out: +return ; +} + +/* host lock must be held before calling this variant */ +static void __ufshcd_release (struct ufs_hba *hba) +{ +if (!ufshcd_is_clkgating_allowed (hba)) +return ; + + hba->clk_gating.active_reqs--; + +if (hba->clk_gating.active_reqs || hba->clk_gating.is_suspended + || hba->ufshcd_state !=UFSHCD_STATE_OPERATIONAL + || hba->lrb_in_use || hba->outstanding_tasks + || hba->active_uic_cmd || hba->uic_async_done) +return ; + + hba->clk_gating.state =REQ_CLKS_OFF ; +schedule_delayed_work (&hba->clk_gating.gate_work, +msecs_to_jiffies (hba->clk_gating.delay_ms)); +} + +void ufshcd_release (struct ufs_hba *hba) +{ +unsigned long flags; + +spin_lock_irqsave (hba->host->host_lock, flags); +__ufshcd_release (hba); +spin_unlock_irqrestore (hba->host->host_lock, flags); +} + +static ssize_t ufshcd_clkgate_delay_show (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct ufs_hba *hba =dev_get_drvdata (dev); + +return snprintf (buf,PAGE_SIZE ,"%lu\n" , hba->clk_gating.delay_ms); +} + +static ssize_t ufshcd_clkgate_delay_store (struct device *dev, +struct device_attribute *attr,const char *buf,size_t count) +{ +struct ufs_hba *hba =dev_get_drvdata (dev); +unsigned long flags, value; + +if (kstrtoul (buf,0 , &value)) +return -EINVAL ; + +spin_lock_irqsave (hba->host->host_lock, flags); + hba->clk_gating.delay_ms = value; +spin_unlock_irqrestore (hba->host->host_lock, flags); +return count; +} + +static void ufshcd_init_clk_gating (struct ufs_hba *hba) +{ +if (!ufshcd_is_clkgating_allowed (hba)) +return ; + + hba->clk_gating.delay_ms =150 ; +INIT_DELAYED_WORK (&hba->clk_gating.gate_work, ufshcd_gate_work); +INIT_WORK (&hba->clk_gating.ungate_work, ufshcd_ungate_work); + + hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show; + hba->clk_gating.delay_attr.store = ufshcd_clkgate_delay_store; +sysfs_attr_init (&hba->clk_gating.delay_attr.attr); + hba->clk_gating.delay_attr.attr.name ="clkgate_delay_ms" ; + hba->clk_gating.delay_attr.attr.mode =S_IRUGO |S_IWUSR ; +if (device_create_file (hba->dev, &hba->clk_gating.delay_attr)) +dev_err (hba->dev,"Failed to create sysfs for clkgate_delay\n" ); +} + +static void ufshcd_exit_clk_gating (struct ufs_hba *hba) +{ +if (!ufshcd_is_clkgating_allowed (hba)) +return ; +device_remove_file (hba->dev, &hba->clk_gating.delay_attr); +} +/** * ufshcd_send_command - Send SCSI or device management commands * @hba: per adapter instance
@@ -702,6 +932,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)int ret;unsigned long flags; +ufshcd_hold (hba, false);mutex_lock (&hba->uic_cmd_mutex);spin_lock_irqsave (hba->host->host_lock, flags); ret =__ufshcd_send_uic_cmd (hba, uic_cmd);
@@ -711,6 +942,7 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)mutex_unlock (&hba->uic_cmd_mutex); +ufshcd_release (hba);return ret; }
@@ -1037,6 +1269,14 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) goto out; } + err =ufshcd_hold (hba, true); +if (err) { + err =SCSI_MLQUEUE_HOST_BUSY ; +clear_bit_unlock (tag, &hba->lrb_in_use); + goto out; + } +WARN_ON (hba->clk_gating.state !=CLKS_ON ); + lrbp = &hba->lrb[tag];WARN_ON (lrbp->cmd);
@@ -1312,6 +1552,7 @@ static int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,BUG_ON (!hba); +ufshcd_hold (hba, false);mutex_lock (&hba->dev_cmd.lock);ufshcd_init_query (hba, &request, &response, opcode, idn, index, selector);
@@ -1355,6 +1596,7 @@ static int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, out_unlock:mutex_unlock (&hba->dev_cmd.lock); +ufshcd_release (hba);return err; }
@@ -1378,6 +1620,7 @@ static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,BUG_ON (!hba); +ufshcd_hold (hba, false);if (!attr_val) {dev_err (hba->dev,"%s: attribute value required for opcode 0x%x\n" , __func__, opcode);
@@ -1417,6 +1660,7 @@ static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, out_unlock:mutex_unlock (&hba->dev_cmd.lock); out: +ufshcd_release (hba);return err; }
@@ -1444,6 +1688,7 @@ static int ufshcd_query_descriptor(struct ufs_hba *hba,BUG_ON (!hba); +ufshcd_hold (hba, false);if (!desc_buf) {dev_err (hba->dev,"%s: descriptor buffer required for opcode 0x%x\n" , __func__, opcode);
@@ -1493,6 +1738,7 @@ static int ufshcd_query_descriptor(struct ufs_hba *hba, out_unlock:mutex_unlock (&hba->dev_cmd.lock); out: +ufshcd_release (hba);return err; }
@@ -1913,6 +2159,7 @@ out: hba->uic_async_done =NULL ;spin_unlock_irqrestore (hba->host->host_lock, flags);mutex_unlock (&hba->uic_cmd_mutex); +return ret; }
@@ -1927,12 +2174,16 @@ out:static int ufshcd_uic_change_pwr_mode (struct ufs_hba *hba,u8 mode) {struct uic_command uic_cmd = {0 }; +int ret; uic_cmd.command =UIC_CMD_DME_SET ; uic_cmd.argument1 =UIC_ARG_MIB (PA_PWRMODE ); uic_cmd.argument3 = mode; +ufshcd_hold (hba, false); + ret =ufshcd_uic_pwr_ctrl (hba, &uic_cmd); +ufshcd_release (hba); -return ufshcd_uic_pwr_ctrl (hba, &uic_cmd); +return ret; }static int ufshcd_uic_hibern8_enter (struct ufs_hba *hba)
@@ -2354,6 +2605,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)int err =0 ;int retries; +ufshcd_hold (hba, false);mutex_lock (&hba->dev_cmd.lock);for (retries =NOP_OUT_RETRIES ; retries >0 ; retries--) { err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP,
@@ -2365,6 +2617,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)dev_dbg (hba->dev,"%s: error %d retrying\n" , __func__, err); }mutex_unlock (&hba->dev_cmd.lock); +ufshcd_release (hba);if (err)dev_err (hba->dev,"%s: NOP OUT failed %d\n" , __func__, err);
@@ -2764,6 +3017,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)clear_bit_unlock (index, &hba->lrb_in_use);/* Do not touch lrbp after scsi done */ cmd->scsi_done (cmd); +__ufshcd_release (hba); }else if (lrbp->command_type ==UTP_CMD_TYPE_DEV_MANAGE ) {if (hba->dev_cmd.complete)complete (hba->dev_cmd.complete);
@@ -3048,6 +3302,7 @@ static void ufshcd_err_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eh_work);pm_runtime_get_sync (hba->dev); +ufshcd_hold (hba, false);spin_lock_irqsave (hba->host->host_lock, flags);if (hba->ufshcd_state ==UFSHCD_STATE_RESET ) {
@@ -3101,6 +3356,7 @@ static void ufshcd_err_handler(struct work_struct *work) out:scsi_unblock_requests (hba->host); +ufshcd_release (hba);pm_runtime_put_sync (hba->dev); }
@@ -3284,6 +3540,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,* the maximum wait time is bounded by %TM_CMD_TIMEOUT. */ wait_event (hba->tm_tag_wq,ufshcd_get_tm_free_slot (hba, &free_slot)); +ufshcd_hold (hba, false);spin_lock_irqsave (host->host_lock, flags); task_req_descp = hba->utmrdl_base_addr;
@@ -3335,6 +3592,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,ufshcd_put_tm_slot (hba, free_slot);wake_up (&hba->tm_tag_wq); +ufshcd_release (hba);return err; }
@@ -3417,6 +3675,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) hba =shost_priv (host); tag = cmd->request->tag; +ufshcd_hold (hba, false);/* If command is already aborted/completed, return SUCCESS */ if (!(test_bit (tag, &hba->outstanding_reqs))) goto out;
@@ -3481,6 +3740,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)clear_bit_unlock (tag, &hba->lrb_in_use);wake_up (&hba->dev_cmd.tag_wq); + out:if (!err) { err =SUCCESS ;
@@ -3489,6 +3749,11 @@ out: err =FAILED ; } +/* +* This ufshcd_release() corresponds to the original scsi cmd that got +* aborted here (as we won't get any IRQ for it). +*/ +ufshcd_release (hba);return err; }
@@ -3573,6 +3838,7 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd) hba =shost_priv (cmd->device->host); +ufshcd_hold (hba, false);/* * Check if there is any race with fatal error handling. * If so, wait for it to complete. Even though fatal error
@@ -3606,6 +3872,7 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd)ufshcd_clear_eh_in_progress (hba);spin_unlock_irqrestore (hba->host->host_lock, flags); +ufshcd_release (hba);return err; }
@@ -3925,6 +4192,7 @@ static struct scsi_host_template ufshcd_driver_template = { .sg_tablesize = SG_ALL, .cmd_per_lun = UFSHCD_CMD_PER_LUN, .can_queue = UFSHCD_CAN_QUEUE, + .max_host_blocked = 1, }; static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
@@ -4127,6 +4395,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,int ret =0 ;struct ufs_clk_info *clki;struct list_head *head = &hba->clk_list_head; +unsigned long flags;if (!head ||list_empty (head)) goto out;
@@ -4151,12 +4420,19 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, clki->name, on ? "en" : "dis"); } } + +if (hba->vops && hba->vops->setup_clocks) + ret = hba->vops->setup_clocks (hba, on); out:if (ret) {list_for_each_entry (clki, head, list) {if (!IS_ERR_OR_NULL (clki->clk) && clki->enabled)clk_disable_unprepare (clki->clk); } + }else if (!ret && on) { +spin_lock_irqsave (hba->host->host_lock, flags); + hba->clk_gating.state =CLKS_ON ; +spin_unlock_irqrestore (hba->host->host_lock, flags); }return ret; }
@@ -4217,23 +4493,14 @@ static int ufshcd_variant_hba_init(struct ufs_hba *hba) goto out; } -if (hba->vops->setup_clocks) { - err = hba->vops->setup_clocks (hba, true); -if (err) - goto out_exit; - } -if (hba->vops->setup_regulators) { err = hba->vops->setup_regulators (hba, true);if (err) - goto out_clks; + goto out_exit; } goto out; -out_clks: -if (hba->vops->setup_clocks) - hba->vops->setup_clocks (hba, false); out_exit:if (hba->vops->exit) hba->vops->exit (hba);
@@ -4555,6 +4822,9 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)* If we can't transition into any of the low power modes * just gate the clocks. */ +ufshcd_hold (hba, false); + hba->clk_gating.is_suspended = true; +if (req_dev_pwr_mode ==UFS_ACTIVE_PWR_MODE && req_link_state ==UIC_LINK_ACTIVE_STATE ) { goto disable_clks;
@@ -4577,7 +4847,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)*/ ret =ufshcd_bkops_ctrl (hba,BKOPS_STATUS_NON_CRITICAL );if (ret) - goto out; + goto enable_gating; } if ((req_dev_pwr_mode != hba->curr_dev_pwr_mode) &&
@@ -4587,7 +4857,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)ufshcd_disable_auto_bkops (hba); ret =ufshcd_set_dev_pwr_mode (hba, req_dev_pwr_mode);if (ret) - goto out; + goto enable_gating; } ret =ufshcd_link_state_transition (hba, req_link_state,1 );
@@ -4620,6 +4890,7 @@ disable_clks:/* If link is active, device ref_clk can't be switched off */ __ufshcd_setup_clocks (hba, false, true); + hba->clk_gating.state =CLKS_OFF ;/* * Disable the host irq as host controller as there won't be any * host controller trasanction expected till resume.
@@ -4641,6 +4912,9 @@ set_link_active: set_dev_active:if (!ufshcd_set_dev_pwr_mode (hba,UFS_ACTIVE_PWR_MODE ))ufshcd_disable_auto_bkops (hba); +enable_gating: + hba->clk_gating.is_suspended = false; +ufshcd_release (hba); out: hba->pm_op_in_progress =0 ;return ret;
@@ -4670,12 +4944,6 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)if (ret) goto out; -if (hba->vops && hba->vops->setup_clocks) { - ret = hba->vops->setup_clocks (hba, true); -if (ret) - goto disable_clks; - } -/* enable the host irq as host controller would be active soon */ ret =ufshcd_enable_irq (hba);if (ret)
@@ -4719,6 +4987,10 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) }ufshcd_disable_auto_bkops (hba); + hba->clk_gating.is_suspended = false; + +/* Schedule clock gating in case of no access to UFS device yet */ +ufshcd_release (hba); goto out; set_old_link_state:
@@ -4730,9 +5002,6 @@ disable_vreg:ufshcd_vreg_set_lpm (hba); disable_irq_and_vops_clks:ufshcd_disable_irq (hba); -if (hba->vops && hba->vops->setup_clocks) - ret = hba->vops->setup_clocks (hba, false); -disable_clks:ufshcd_setup_clocks (hba, false); out: hba->pm_op_in_progress =0 ;
@@ -4902,6 +5171,7 @@ void ufshcd_remove(struct ufs_hba *hba)scsi_host_put (hba->host); +ufshcd_exit_clk_gating (hba);ufshcd_hba_exit (hba); }EXPORT_SYMBOL_GPL (ufshcd_remove);
@@ -5037,11 +5307,12 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)/* Initialize device management tag acquire wait queue */ init_waitqueue_head (&hba->dev_cmd.tag_wq); +ufshcd_init_clk_gating (hba);/* IRQ registration */ err =devm_request_irq (dev, irq, ufshcd_intr,IRQF_SHARED ,UFSHCD , hba);if (err) {dev_err (hba->dev,"request irq failed\n" ); - goto out_disable; + goto exit_gating; }else { hba->is_irq_enabled = true; }
@@ -5050,13 +5321,13 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) err =scsi_init_shared_tag_map (host, host->can_queue);if (err) {dev_err (hba->dev,"init shared queue failed\n" ); - goto out_disable; + goto exit_gating; } err =scsi_add_host (host, hba->dev);if (err) {dev_err (hba->dev,"scsi_add_host failed\n" ); - goto out_disable; + goto exit_gating; }/* Host controller enable */
@@ -5081,6 +5352,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) out_remove_scsi_host:scsi_remove_host (hba->host); +exit_gating: +ufshcd_exit_clk_gating (hba); out_disable: hba->is_irq_enabled = false;scsi_host_put (host);
drivers/scsi/ufs/ufshcd.h +51 -0
69 lines not on this page · raw message
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 15/18 scsi: ufs: Add freq-table-hz property for UFS device
In reply to Dolev RavivFrom: Sahitya Tummala <[email protected]>
Add freq-table-hz propery for UFS device to keep track of <min max> frequencies supported by UFS clocks.
Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +6 -6
@@ -26,11 +26,11 @@ Optional properties: - clocks : List of phandle and clock specifier pairs - clock-names : List of clock input name strings sorted in the same order as the clocks property. -- max-clock-frequency-hz : List of maximum operating frequency stored in the same - order as the clocks property. If this property is not - defined or a value in the array is "0" then it is assumed - that the frequency is set by the parent clock or a - fixed rate clock source. +- freq-table-hz : Array of <min max> operating frequencies stored in the same + order as the clocks property. If this property is not + defined or a value in the array is "0" then it is assumed + that the frequency is set by the parent clock or a + fixed rate clock source. Note: If above properties are not defined it can be assumed that the supply regulators or clocks are always on.
@@ -53,5 +53,5 @@ Example: clocks = <&core 0>, <&ref 0>, <&iface 0>; clock-names = "core_clk", "ref_clk", "iface_clk"; - max-clock-frequency-hz = <100000000 19200000 0>; + freq-table-hz = <100000000 200000000>, <0 0>, <0 0>; };
drivers/scsi/ufs/ufshcd-pltfrm.c +35 -13
80 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +2 -0
@@ -209,6 +209,7 @@ struct ufs_dev_cmd {* @clk: clock node * @name: clock name * @max_freq: maximum frequency supported by the clock +* @min_freq: min frequency that can be used for clock scaling * @enabled: variable to check against multiple enable/disable */ struct ufs_clk_info {
@@ -216,6 +217,7 @@ struct ufs_clk_info {struct clk *clk;const char *name;u32 max_freq; +u32 min_freq;bool enabled; };
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 16/18 scsi: ufs: Add support for clock scaling using devfreq framework
In reply to Dolev RavivFrom: Sahitya Tummala <[email protected]>
The clocks for UFS device will be managed by generic DVFS (Dynamic Voltage and Frequency Scaling) framework within kernel. This devfreq framework works with different governors to scale the clocks. By default, UFS devices uses simple_ondemand governor which scales the clocks up if the load is more than upthreshold and scales down if the load is less than downthreshold.
drivers/scsi/ufs/Kconfig +2 -0
@@ -35,6 +35,8 @@ config SCSI_UFSHCD tristate "Universal Flash Storage Controller Driver Core" depends on SCSI && SCSI_DMA + select PM_DEVFREQ + select DEVFREQ_GOV_SIMPLE_ONDEMAND ---help--- This selects the support for UFS devices in Linux, say Y and make sure that you know the name of your UFS host adapter (the card
drivers/scsi/ufs/ufshcd.c +177 -1
271 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +20 -0
@@ -210,6 +210,7 @@ struct ufs_dev_cmd {* @name: clock name * @max_freq: maximum frequency supported by the clock * @min_freq: min frequency that can be used for clock scaling +* @curr_freq: indicates the current frequency that it is set to * @enabled: variable to check against multiple enable/disable */ struct ufs_clk_info {
@@ -218,6 +219,7 @@ struct ufs_clk_info {const char *name;u32 max_freq;u32 min_freq; +u32 curr_freq;bool enabled; };
@@ -244,6 +246,7 @@ struct ufs_pwr_mode_info {* @name: variant name * @init: called when the driver is initialized * @exit: called to cleanup everything done in init +* @clk_scale_notify: notifies that clks are scaled up/down * @setup_clocks: called before touching any of the controller registers * @setup_regulators: called before accessing the host controller * @hce_enable_notify: called before and after HCE enable bit is set to allow
@@ -260,6 +263,7 @@ struct ufs_hba_variant_ops {const char *name;int (*init)(struct ufs_hba *);void (*exit)(struct ufs_hba *); +void (*clk_scale_notify)(struct ufs_hba *);int (*setup_clocks)(struct ufs_hba *,bool );int (*setup_regulators)(struct ufs_hba *,bool );int (*hce_enable_notify)(struct ufs_hba *,bool );
@@ -303,6 +307,13 @@ struct ufs_clk_gating {int active_reqs; }; +struct ufs_clk_scaling { +ktime_t busy_start_t; +bool is_busy_started; +unsigned long tot_busy_t; +unsigned long window_start_t; +}; +/** * struct ufs_init_prefetch - contains data that is pre-fetched once during * initialization
@@ -456,6 +467,11 @@ struct ufs_hba {#define UFSHCD_CAP_CLK_GATING (1 << 0)/* Allow hiberb8 with clk gating */ #define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1) +/* Allow dynamic clk scaling */ +#define UFSHCD_CAP_CLK_SCALING (1 << 2) + +struct devfreq *devfreq; +struct ufs_clk_scaling clk_scaling; };/* Returns true if clocks can be gated. Otherwise false */
@@ -467,6 +483,10 @@ static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba) {return hba->caps &UFSHCD_CAP_HIBERN8_WITH_CLK_GATING ; } +static inline int ufshcd_is_clkscaling_enabled (struct ufs_hba *hba) +{ +return hba->caps &UFSHCD_CAP_CLK_SCALING ; +}#define ufshcd_writel (hba, val, reg) \ writel((val), (hba)->mmio_base + (reg))#define ufshcd_readl (hba, reg) \
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 17/18 scsi: ufs: tune bkops while power managment events
In reply to Dolev RavivFrom: Subhash Jadavani <[email protected]>
Add capability to control the auto bkops during suspend.
If host explicitly enables the auto bkops (background operation) on device then only device would perform the bkops on its own. If auto bkops is not enabled explicitly and if the device reaches to state where it must do background operation, device would raise the urgent bkops exception event to host and then host will enable the auto bkops on device. This patch adds the option to choose whether auto bkops should be enabled during runtime suspend or not. Since we don't want to keep the device active to perform the non critical bkops, host will enable urgent bkops only.
Keep auto-bkops enabled after resume if urgent bkops needed. If device bkops status shows that its in critical need of executing background operations, host should allow the device to continue doing background operations.
drivers/scsi/ufs/ufshcd.c +18 -8
@@ -4884,13 +4884,19 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) }if (ufshcd_is_runtime_pm (pm_op)) { -/* -* The device is idle with no requests in the queue, -* allow background operations if needed. -*/ - ret =ufshcd_bkops_ctrl (hba,BKOPS_STATUS_NON_CRITICAL ); -if (ret) - goto enable_gating; +if (ufshcd_can_autobkops_during_suspend (hba)) { +/* +* The device is idle with no requests in the queue, +* allow background operations if bkops status shows +* that performance might be impacted. +*/ + ret =ufshcd_urgent_bkops (hba); +if (ret) + goto enable_gating; + }else { +/* make sure that auto bkops is disabled */ +ufshcd_disable_auto_bkops (hba); + } } if ((req_dev_pwr_mode != hba->curr_dev_pwr_mode) &&
@@ -5038,7 +5044,11 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) goto set_old_link_state; } -ufshcd_disable_auto_bkops (hba); +/* +* If BKOPs operations are urgently needed at this moment then +* keep auto-bkops enabled or else disable it. +*/ +ufshcd_urgent_bkops (hba); hba->clk_gating.is_suspended = false;if (ufshcd_is_clkscaling_enabled (hba))
drivers/scsi/ufs/ufshcd.h +7 -0
@@ -469,6 +469,8 @@ struct ufs_hba {#define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1)/* Allow dynamic clk scaling */ #define UFSHCD_CAP_CLK_SCALING (1 << 2) +/* Allow auto bkops to enabled during runtime suspend */ +#define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)struct devfreq *devfreq;struct ufs_clk_scaling clk_scaling;
@@ -487,6 +489,11 @@ static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba) {return hba->caps &UFSHCD_CAP_CLK_SCALING ; } +static inline bool ufshcd_can_autobkops_during_suspend (struct ufs_hba *hba) +{ +return hba->caps &UFSHCD_CAP_AUTO_BKOPS_SUSPEND ; +} +#define ufshcd_writel (hba, val, reg) \ writel((val), (hba)->mmio_base + (reg))#define ufshcd_readl (hba, reg) \
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v6 18/18 scsi: ufs: definitions for phy interface
In reply to Dolev Raviv- Adding some of the definitions missing in unipro.h, including power enumeration.
- Read Modify Write Line helper function
- Indication for the type of suspend
drivers/scsi/ufs/ufshcd.c +2 -0
@@ -5114,6 +5114,8 @@ int ufshcd_system_suspend(struct ufs_hba *hba) ret =ufshcd_suspend (hba,UFS_SYSTEM_PM ); out: +if (!ret) + hba->is_sys_suspended = true;return ret; }EXPORT_SYMBOL (ufshcd_system_suspend);
drivers/scsi/ufs/ufshcd.h +18 -0
@@ -474,6 +474,7 @@ struct ufs_hba {struct devfreq *devfreq;struct ufs_clk_scaling clk_scaling; +bool is_sys_suspended; };/* Returns true if clocks can be gated. Otherwise false */
@@ -499,6 +500,23 @@ static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba *hba)#define ufshcd_readl (hba, reg) \ readl((hba)->mmio_base + (reg)) +/** +* ufshcd_rmwl - read modify write into a register +* @hba - per adapter instance +* @mask - mask to apply on read value +* @val - actual value to write +* @reg - register address +*/ +static inline void ufshcd_rmwl (struct ufs_hba *hba,u32 mask,u32 val,u32 reg) +{ +u32 tmp; + + tmp =ufshcd_readl (hba, reg); + tmp &= ~mask; + tmp |= (val & mask); +ufshcd_writel (hba, tmp, reg); +} +int ufshcd_alloc_host (struct device *,struct ufs_hba **);int ufshcd_init (struct ufs_hba * ,void __iomem * ,unsigned int );void ufshcd_remove (struct ufs_hba *);
drivers/scsi/ufs/unipro.h +56 -0
68 lines not on this page · raw message
Signature · 4 lines
1.8.5.2 -- QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Please stop putting listname-owner (in this case linux-scsi-owner) in the CC: list, that goes to me not the list.
I've had the series in a ufs-for-3.18 branch for a while, and the two issues pointed out by the buildbot were quickly fixed. Unless I get a loud complaint I will merge the entire series including the two core patches into drivers-for-3.18 tomorrow.