UFS: Power managment support
Subsystems (4)
cover UFS: Power managment support
This patch seies introduces support for power managment in the driver as well as vendor specific initialization - registers, clocks, voltage regulators etc.
It includes also a rework for the init sequnce and other PM pre- requisit such as write protection support, well-known LUN, error handling (retries), bkops, START_STOP, and ICC levels.
Subhash Jadavani (4):
scsi: ufs: refactor query descriptor API support
scsi: support well known logical units
scsi: ufs: introduce well known logical unit in ufs
scsi: ufs: add UFS power management support
Sujit Reddy Thumma (5):
scsi: ufs: Allow vendor specific initialization
scsi: ufs: Add regulator enable support
scsi: ufs: Add clock initialization support
scsi: ufs: improve init sequence
scsi: sd: Avoid sending medium write commands if device is write
protected
Yaniv Gardi (1):
scsi: ufs: Active Power Mode - configuring bActiveICCLevel
.../devicetree/bindings/ufs/ufshcd-pltfrm.txt | 37 + drivers/scsi/scsi_scan.c | 16 + drivers/scsi/scsi_sysfs.c | 7 + drivers/scsi/sd.c | 6 +- drivers/scsi/ufs/ufs.h | 131 +- drivers/scsi/ufs/ufshcd-pci.c | 55 +- drivers/scsi/ufs/ufshcd-pltfrm.c | 260 ++- drivers/scsi/ufs/ufshcd.c | 1733 ++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 152 +- drivers/scsi/ufs/ufshci.h | 9 +- include/scsi/scsi.h | 1 + include/scsi/scsi_host.h | 5 + 12 files changed, 2155 insertions(+), 257 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
RFC 1/10 scsi: ufs: Allow vendor specific initialization
From: 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
RFC 2/10 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
RFC 3/10 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
RFC 4/10 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
RFC 5/10 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 +72 -27
@@ -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; + } + +/* +* 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--); -/* 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 ; +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 = {
@@ -3596,6 +3637,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:
@@ -3608,9 +3650,12 @@ 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); +if (hba->is_powered) { +ufshcd_variant_hba_exit (hba); +ufshcd_setup_vreg (hba, false); +ufshcd_setup_clocks (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
RFC 6/10 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
RFC 7/10 scsi: support well known logical units
In reply to Dolev RavivFrom: Subhash Jadavani <[email protected]>
REPORT LUNS command has "SELECT REPORT" field which controls what type of logical units to be reported by device server. According to UFS device standard, if this field is set to 0, REPORT LUNS would report only report standard logical units. If it's set to 1 then it would report only well known logical unit and if it's set to 2 then device would report both standard and well known logical units.
Some well-known logical units might not have scsi upper-layer drivers. In such cases, the runtime PM reference count increased during device enumeration will not be decreased, causing the parent device (host) to always be on even during idle.
This change allows the SCSI LLD (Low Level Driver) to choose which type of logical units should be detected.
drivers/scsi/scsi_scan.c +16 -0
@@ -802,6 +802,14 @@ 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->type =TYPE_WLUN ; }switch (sdev->type) {
@@ -817,6 +825,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,case TYPE_COMM :case TYPE_RAID :case TYPE_OSD : +case TYPE_WLUN : sdev->writeable =1 ;break ;case TYPE_ROM :
@@ -1412,6 +1421,13 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,*/ memset (&scsi_cmd[1 ],0 ,5 ); +if (shost->report_wlus) +/* +* Set "SELECT REPORT" field to 0x2 which will make device to +* report well known logical units along with standard LUs. +*/ + scsi_cmd[2 ] =0x2 ; +/* * bytes 6 - 9: length of the command. */
drivers/scsi/scsi_sysfs.c +7 -0
@@ -1060,6 +1060,13 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) } } +/* +* put runtime pm reference for well-known logical units, +* drivers are expected to _get_* again during probe. +*/ +if (scsi_is_wlun (sdev->lun)) +scsi_autopm_put_device (sdev); +return error; }
include/scsi/scsi.h +1 -0
@@ -332,6 +332,7 @@ static inline int scsi_status_is_good(int status)#define TYPE_ENCLOSURE 0x0d/* Enclosure Services Device */ #define TYPE_RBC 0x0e#define TYPE_OSD 0x11 +#define TYPE_WLUN 0x1e/* well-known logical unit */ #define TYPE_NO_LUN 0x7f/* SCSI protocols; these are taken from SPC-3 section 7.5 */
include/scsi/scsi_host.h +5 -0
@@ -675,6 +675,11 @@ struct Scsi_Host { unsigned no_write_same:1;/* +* Set "SELECT REPORT" field to allow detection of well known logical +* units along with standard LUs. +*/ + unsigned report_wlus:1; +/* * Optional work queue to be utilized by the transport */ char work_q_name[20 ];
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
RFC 8/10 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 +65 -20
149 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +6 -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; };
@@ -266,6 +266,11 @@ 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 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
RFC 9/10 scsi: sd: Avoid sending medium write commands if device is write protected
In reply to Dolev RavivFrom: Sujit Reddy Thumma <[email protected]>
The SYNCHRONIZE_CACHE command is a medium write command and hence can fail when the device is write protected. Avoid sending such commands by making sure that write-cache-enable is disabled even though the device claim to support it.
drivers/scsi/sd.c +5 -1
@@ -185,7 +185,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr,if (ct <0 )return -EINVAL ; rcd = ct &0x01 ?1 :0 ; - wce = ct &0x02 ?1 :0 ; + wce = (ct &0x02 ) && !sdkp->write_prot ?1 :0 ;if (sdkp->cache_override) { sdkp->WCE = wce;
@@ -2493,6 +2493,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) sdkp->DPOFUA =0 ; } +/* No cache flush allowed for write protected devices */ +if (sdkp->WCE && sdkp->write_prot) + sdkp->WCE =0 ; +if (sdkp->first_scan || old_wce != sdkp->WCE || old_rcd != sdkp->RCD || old_dpofua != sdkp->DPOFUA) sd_printk(KERN_NOTICE, sdkp,
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: [RFC 09/10] scsi: sd: Avoid sending medium write commands if device is write protected
This looks reasonable to me. Martin, and objections?
Quoted 43 lines · Dolev Raviv
From: Sujit Reddy Thumma <[email protected]>
The
SYNCHRONIZE_CACHEcommand is a medium write command and hence can fail when the device is write protected. Avoid sending such commands by making sure that write-cache-enable is disabled even though the device claim to support it.Signed-off-by Sujit Reddy Thumma <[email protected]>Signed-off-by Dolev Raviv <[email protected]>drivers/scsi/sd.c +5
-1@@ -185,7 +185,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr,if (ct <0 )return -EINVAL ; rcd = ct &0x01 ?1 :0 ; - wce = ct &0x02 ?1 :0 ; + wce = (ct &0x02 ) && !sdkp->write_prot ?1 :0 ;if (sdkp->cache_override) { sdkp->WCE = wce;@@ -2493,6 +2493,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) sdkp->DPOFUA =0 ; } +/* No cache flush allowed for write protected devices */ +if (sdkp->WCE && sdkp->write_prot) + sdkp->WCE =0 ; +if (sdkp->first_scan || old_wce != sdkp->WCE || old_rcd != sdkp->RCD || old_dpofua != sdkp->DPOFUA) sd_printk(KERN_NOTICE, sdkp,Signature · 8 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---
Martin K. Petersen
Reviewed-byRe: [RFC 09/10] scsi: sd: Avoid sending medium write commands if device is write protected
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
"Christoph" == Christoph Hellwig <[email protected]> writes:
Christoph> This looks reasonable to me.
I agree.
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering
Re: [RFC 09/10] scsi: sd: Avoid sending medium write commands if device is write protected
In reply to Dolev RavivQuoted 9 lines · Dolev Raviv
From: Sujit Reddy Thumma <[email protected]>
The
SYNCHRONIZE_CACHEcommand is a medium write command and hence can fail when the device is write protected. Avoid sending such commands by making sure that write-cache-enable is disabled even though the device claim to support it.Signed-off-by Sujit Reddy Thumma <[email protected]>Signed-off-by Dolev Raviv <[email protected]>
Looks good. Setting SWP is defined to flush caches; there is no other reason I could imagine WP media would need a SYNCHRONIZE CACHE.
-- vs;
Christoph Hellwig
AppliedRe: [RFC 09/10] sd: Avoid sending medium write commands if device is write protected
In reply to Dolev RavivThanks,
applied.
RFC 10/10 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.
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;
@@ -453,4 +481,10 @@ struct ufs_vreg_info { struct ufs_vreg *vccq2; }; +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 +824 -85
1,143 lines not on this page · raw message
drivers/scsi/ufs/ufshcd.h +70 -5
130 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