Skip to contentSkip to conversation

UFS: Power management support

PATCH Superseded Dolev Raviv 5 participants 36 messages Sep 10, 2014 – Sep 23, 2014

Subsystems (5)

v3 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v3 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 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

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 (6):
scsi: support well known logical units
scsi: balance out autopm get/put calls in scsi_sysfs_add_sdev()
scsi: ufs: refactor query descriptor API support
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      |   37 +
 drivers/scsi/scsi_scan.c                           |   14 +
 drivers/scsi/scsi_sysfs.c                          |    5 +-
 drivers/scsi/sd.c                                  |    2 +
 drivers/scsi/sr.c                                  |    2 +
 drivers/scsi/st.c                                  |    2 +
 drivers/scsi/ufs/Kconfig                           |    2 +
 drivers/scsi/ufs/ufs.h                             |  131 +-
 drivers/scsi/ufs/ufshcd-pci.c                      |   55 +-
 drivers/scsi/ufs/ufshcd-pltfrm.c                   |  282 ++-
 drivers/scsi/ufs/ufshcd.c                          | 2408 ++++++++++++++++++--
 drivers/scsi/ufs/ufshcd.h                          |  277 ++-
 drivers/scsi/ufs/ufshci.h                          |    9 +-
 drivers/scsi/ufs/unipro.h                          |   56 +
 include/scsi/scsi.h                                |    1 +
 15 files changed, 2962 insertions(+), 321 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
#1 lore raw

PATCH v3 1/16 scsi: support well known logical units

From: 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.

drivers/scsi/scsi_scan.c +14 -0
index 56675db..d7f0df8 100644
@@ -805,6 +805,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;
 	}
 
 	if (sdev->type == TYPE_RBC || sdev->type == TYPE_ROM) {
@@ -1400,6 +1408,12 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
 	memset(&scsi_cmd[1], 0, 5);
 
 	/*
+	 * 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.
 	 */
 	scsi_cmd[6] = (unsigned char) (length >> 24) & 0xff;
include/scsi/scsi.h +1 -0
index 261e708..d17178e 100644
@@ -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
#2 lore raw

Re: [PATCH V3 01/16] scsi: support well known logical units

Quoted 8 lines · Dolev Raviv

From: 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.

This is the defintion of the field in SPC (does the UFS spec duplicate all of SPC?), but still doesn't explain why you want it.

Also the SELECT REPORT field only appeared in SPC-3, so we should not set it for devices that report older standards compliance.

#3 lore raw

RE: [PATCH V3 01/16] scsi: support well known logical units

Quoted 1 line

This is the defintion of the field in SPC (does the UFS spec duplicate all

of SPC?),
Yes, UFS device specification (both 1.1 and 2.0) supports [SAM], INCITS T10 draft standard: SCSI Architecture Model - 5 (SAM-5), Revision 05, 19 May 2010
[SPC], INCITS T10 draft standard: SCSI Primary Commands - 4 (SPC-4), Revision 27, 11 October 2010
[SBC], INCITS T10 draft standard: SCSI Block Commands - 3 (SBC-3), Revision 24, 05 August 2010

In addition, this is what specification says explicitly " SCSI SBC and SPC commands are the baseline for UFS. UFS will not modify the SBC and SPC Compliant commands. The goal is to maximize re-use and leverage of the software codebase available on platforms (PC, netbook, MID) that are already supporting SCSI."

Quoted 1 line

but still doesn't explain why you want it.

UFS device has supports 4 different well known logical units: "REPORT_LUNS" (address: 01h), "UFS Device" (address: 50h), "RPMB" (address: 44h) and "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 (address: 50h) 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.

Quoted 1 line

Also the SELECT REPORT field only appeared in SPC-3, so we should not set

it for devices that report older standards compliance.

Thanks for pointing this out, was not aware of this. We will add appropriate check (if (sdev->scsi_level >= SCSI_SPC_3)) before setting the SELECT REPORT field.

Regards,
Subhash

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Christoph Hellwig
Sent: Wednesday, September 10, 2014 11:59 AM
To: Dolev Raviv
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Subhash Jadavani; Sujit Reddy Thumma; Martin K. Petersen
Subject: Re: [PATCH V3 01/16] scsi: support well known logical units

Quoted 8 lines · Dolev Raviv

From: 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.

This is the defintion of the field in SPC (does the UFS spec duplicate all of SPC?), but still doesn't explain why you want it.

Also the SELECT REPORT field only appeared in SPC-3, so we should not set it for devices that report older standards compliance.

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

#4 lore raw

Re: [PATCH V3 01/16] scsi: support well known logical units

Quoted 12 lines · Subhash Jadavani

UFS device has supports 4 different well known logical units: "REPORT_LUNS" (address: 01h), "UFS Device" (address: 50h), "RPMB" (address: 44h) and "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 (address: 50h) 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.

Quoted 2 lines

We also require the scsi_device instance for "RPMB" (Replay Protected Memory Block) LU so user space process can control this LU.

If those are the only LUs you specificly need I'd suggest you just manually call scsi_add_device from your driver for those instead of listing them in REPORT_LUNS and making them part of the normal LUN scan. One advantage of the well known LUNs is that you always know where in the LUN namespace they are :)

#5 lore raw

RE: [PATCH V3 01/16] scsi: support well known logical units

Quoted 1 line

If those are the only LUs you specificly need I'd suggest you just

manually call scsi_add_device from your driver for those instead of listing them in REPORT_LUNS and making them part of the normal LUN scan.

Yes, we can do that but not sure what would be advantage of making the LLDs to add these ad-hoc hooks instead of scsi_scan handle it on its own and leaving the LLDs from adding them explicitly? Do you foresee any issues (other than few extra scsi_device instances which may not be useful all scsi devices) with scsi_scan scanning all the LUs (including well known LUs)?

Regards,
Subhash

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of 'Christoph Hellwig'
Sent: Saturday, September 13, 2014 11:55 AM
To: Subhash Jadavani
Cc: 'Christoph Hellwig'; 'Dolev Raviv'; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; 'Sujit Reddy Thumma'; 'Martin K. Petersen'
Subject: Re: [PATCH V3 01/16] scsi: support well known logical units

Quoted 2 lines · Subhash Jadavani

UFS device has supports 4 different well known logical units:

"REPORT_LUNS"

Quoted 11 lines

(address: 01h), "UFS Device" (address: 50h), "RPMB" (address: 44h) and "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
(address: 50h) 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.

Quoted 3 lines

We also require the scsi_device instance for "RPMB" (Replay Protected Memory
Block) LU so user space process can control this LU.

If those are the only LUs you specificly need I'd suggest you just manually call scsi_add_device from your driver for those instead of listing them in REPORT_LUNS and making them part of the normal LUN scan. One advantage of the well known LUNs is that you always know where in the LUN namespace they are :)

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

#6 lore raw

RE: [PATCH V3 01/16] scsi: support well known logical units

Hi Chris,

While testing with another vendor's UFS devices, I realized that SELECT REPORT need not to be set to 0x02 for making the device report the W-LUs. Even when SELECT REPORT is set 0x00, this particular UFS device reports the W-LUs so I looked at the SCSI specification in details and it seems ideally device should report all the well-known logical units (as it follows the extended logical unit addressing format) when SELECT REPORT is set 0x00. Which means one vendor's UFS devices are following SCSI spec for W-LUs reporting while another vendor's UFS devices don't.

I have reported violation of spec to this other vendor and have also asked them to fix it. Until they fix their non-standard behavior, we can workaround their behavior using the device specific quirks in UFS driver itself.

For now in this patch, we will skip modifying SELECT REPORT field but we would still require the other part of this patch for fixing the scsi_device type to wlun (sdev->type = TYPE_WLUN). Hence we will change the commit text accordingly to reflect the exact purpose of the change.

Regards,
Subhash

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Subhash Jadavani
Sent: Thursday, September 18, 2014 10:18 AM
To: 'Christoph Hellwig'
Cc: 'Dolev Raviv'; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; 'Sujit Reddy Thumma'; 'Martin K. Petersen'
Subject: RE: [PATCH V3 01/16] scsi: support well known logical units

Quoted 1 line

If those are the only LUs you specificly need I'd suggest you just

manually call scsi_add_device from your driver for those instead of listing them in REPORT_LUNS and making them part of the normal LUN scan.

Yes, we can do that but not sure what would be advantage of making the LLDs to add these ad-hoc hooks instead of scsi_scan handle it on its own and leaving the LLDs from adding them explicitly? Do you foresee any issues (other than few extra scsi_device instances which may not be useful all scsi devices) with scsi_scan scanning all the LUs (including well known LUs)?

Regards,
Subhash

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of 'Christoph Hellwig'
Sent: Saturday, September 13, 2014 11:55 AM
To: Subhash Jadavani
Cc: 'Christoph Hellwig'; 'Dolev Raviv'; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; 'Sujit Reddy Thumma'; 'Martin K. Petersen'
Subject: Re: [PATCH V3 01/16] scsi: support well known logical units

Quoted 2 lines · Subhash Jadavani

UFS device has supports 4 different well known logical units:

"REPORT_LUNS"

Quoted 11 lines

(address: 01h), "UFS Device" (address: 50h), "RPMB" (address: 44h) and "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
(address: 50h) 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.

Quoted 3 lines

We also require the scsi_device instance for "RPMB" (Replay Protected Memory
Block) LU so user space process can control this LU.

If those are the only LUs you specificly need I'd suggest you just manually call scsi_add_device from your driver for those instead of listing them in REPORT_LUNS and making them part of the normal LUN scan. One advantage of the well known LUNs is that you always know where in the LUN namespace they are :)

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

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

#7 lore raw

Re: [PATCH V3 01/16] scsi: support well known logical units

Quoted 8 lines · Subhash Jadavani

Hi Chris,

While testing with another vendor's UFS devices, I realized that SELECT REPORT need not to be set to 0x02 for making the device report the W-LUs. Even when SELECT REPORT is set 0x00, this particular UFS device reports the W-LUs so I looked at the SCSI specification in details and it seems ideally device should report all the well-known logical units (as it follows the extended logical unit addressing format) when SELECT REPORT is set 0x00.

I don't really know devices exporting wluns by default. The wording in the latests SPC-4 draft I have says:

00h The list shall contain the logical units accessible to the I_T nexus
with the following addressing methods (see SAM-4):
a) Logical unit addressing method,
b) Peripheral device addressing method; and
c) Flat space addressing method.
If there are no logical units, the LUN LIST LENGTH field shall be zero.
01h The list shall contain only well known logical units, if any.
If there are no well known logical units, the LUN LIST LENGTH field shall be zero.
02h The list shall contain all logical units accessible to the I_T nexus.

SAM-4 defines WLUNs as their own addressing format in 4.6.11:

Well known logical units are addressed using the well known logical unit extended address format (see table 28).

Note that the reason I don't like reporting wluns by default is that so far we haven't come up with a use case for them in either the kernel or generic userspace. As long as usage of the wluns you want to support is confirmed to ufs, which is an unusually rightly coupled implementation of the initiator and target side I'd like to keep it isolated to ufs. If we ever come up with a more generic usage of other wluns we should also report them during the initial target scan.

Quoted 4 lines

For now in this patch, we will skip modifying SELECT REPORT field but we would still require the other part of this patch for fixing the scsi_device type to wlun (sdev->type = TYPE_WLUN). Hence we will change the commit text accordingly to reflect the exact purpose of the change.

Yes, please resend this change as a separate patch with an updated description. I'd love to merge the whole ufs update for this merge window, so a timeline resent would be appreciated.

#8 lore raw

RE: [PATCH V3 01/16] scsi: support well known logical units

Hi Chris,

Quoted 1 line

I don't really know devices exporting wluns by default. The wording in the

latests SPC-4 draft I have says:

That's true. In fact further finding showed me this: UFS device specification has explicitly mentioned the purpose of SELECT REPORT field when its set to 00h. When SELECT REPORT field is cleared, device should only report LUs which support peripheral device addressing method. Even UFS conformance test specification seems to confirm this explicitly. Hence if the UFS device adhere the UFS device specification then its expected for device to not report the W-LUs when SELECT REPORT is 00h.

But I have seen one UFS device vendor specifically reporting w-lus even when select report is cleared. I am going to follow up them to fix their behavior. But for now, I guess we are on same page that device shouldn't report W-LUS when SELECT REPORT field is cleared.

Quoted 4 lines
Quoted 4 lines

For now in this patch, we will skip modifying SELECT REPORT field but we would still require the other part of this patch for fixing the scsi_device type to wlun (sdev->type = TYPE_WLUN). Hence we will change the commit text accordingly to reflect the exact purpose of the

change.

Quoted 1 line

Yes, please resend this change as a separate patch with an updated

description. I'd love to merge the whole ufs update for this merge window, so a timeline resent would be appreciated.

Yes, rest of the part in this patch would still remain applicable with updated description. We will send it soon along with one more patch in UFS driver which would manually add scsi_device instance for the UFS W-LUs.

Regards,
Subhash

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of 'Christoph Hellwig'
Sent: Monday, September 22, 2014 7:29 AM
To: Subhash Jadavani
Cc: 'Dolev Raviv'; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; 'Sujit Reddy Thumma'; 'Martin K. Petersen'
Subject: Re: [PATCH V3 01/16] scsi: support well known logical units

Quoted 4 lines · Subhash Jadavani

Hi Chris,

While testing with another vendor's UFS devices, I realized that SELECT REPORT need not to be set to 0x02 for making the device report the

W-LUs.

Quoted 4 lines

Even when SELECT REPORT is set 0x00, this particular UFS device reports the W-LUs so I looked at the SCSI specification in details and it seems ideally device should report all the well-known logical units (as it follows the extended logical unit addressing format) when SELECT

REPORT is set 0x00.

I don't really know devices exporting wluns by default. The wording in the latests SPC-4 draft I have says:

00h The list shall contain the logical units accessible to the I_T nexus
with the following addressing methods (see SAM-4):
a) Logical unit addressing method,
b) Peripheral device addressing method; and
c) Flat space addressing method.
If there are no logical units, the LUN LIST LENGTH field shall be zero.
01h The list shall contain only well known logical units, if any.
If there are no well known logical units, the LUN LIST LENGTH field shall be zero.
02h The list shall contain all logical units accessible to the I_T nexus.

SAM-4 defines WLUNs as their own addressing format in 4.6.11:

Well known logical units are addressed using the well known logical unit extended address format (see table 28).

Note that the reason I don't like reporting wluns by default is that so far we haven't come up with a use case for them in either the kernel or generic userspace. As long as usage of the wluns you want to support is confirmed to ufs, which is an unusually rightly coupled implementation of the initiator and target side I'd like to keep it isolated to ufs. If we ever come up with a more generic usage of other wluns we should also report them during the initial target scan.

Quoted 4 lines

For now in this patch, we will skip modifying SELECT REPORT field but we would still require the other part of this patch for fixing the scsi_device type to wlun (sdev->type = TYPE_WLUN). Hence we will change the commit text accordingly to reflect the exact purpose of the

change.

Yes, please resend this change as a separate patch with an updated description. I'd love to merge the whole ufs update for this merge window, so a timeline resent would be appreciated.

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

#9 lore raw

PATCH v3 2/16 scsi: balance out autopm get/put calls in scsi_sysfs_add_sdev()

In reply to Dolev Raviv

From: Subhash Jadavani <[email protected]>

SCSI Well-known logical units generally don't have any scsi driver associated with it which means no one will call scsi_autopm_put_device() on these wlun scsi devices and this would result in keeping the corresponding scsi device always active (hence LLD can't be suspended as well). Same exact problem can be seen for other scsi device representing normal logical unit whose driver is yet to be loaded. This patch fixes the above problem with this approach:

- make the scsi_autopm_put_device call at the end of scsi_sysfs_add_sdev to make it balance out the get earlier in the function.
- let drivers do paired get/put calls in their probe methods.

drivers/scsi/scsi_sysfs.c +1 -4
index 8b4105a..3524b68 100644
@@ -1044,10 +1044,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
 	pm_runtime_enable(&sdev->sdev_gendev);
 	scsi_autopm_put_target(starget);
 
-	/* The following call will keep sdev active indefinitely, until
-	 * its driver does a corresponding scsi_autopm_pm_device().  Only
-	 * drivers supporting autosuspend will do this.
-	 */
 	scsi_autopm_get_device(sdev);
 
 	error = device_add(&sdev->sdev_gendev);
@@ -1085,6 +1081,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
 		}
 	}
 
+	scsi_autopm_put_device(sdev);
 	return error;
 }
 
drivers/scsi/sd.c +2 -0
index aa43496..0cb5c9f 100644
@@ -2965,6 +2965,7 @@ static int sd_probe(struct device *dev)
 	int index;
 	int error;
 
+	scsi_autopm_get_device(sdp);
 	error = -ENODEV;
 	if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC)
 		goto out;
@@ -3041,6 +3042,7 @@ static int sd_probe(struct device *dev)
  out_free:
 	kfree(sdkp);
  out:
+	scsi_autopm_put_device(sdp);
 	return error;
 }
 
drivers/scsi/sr.c +2 -0
index 7eeb936..2de44cc 100644
@@ -657,6 +657,7 @@ static int sr_probe(struct device *dev)
 	struct scsi_cd *cd;
 	int minor, error;
 
+	scsi_autopm_get_device(sdev);
 	error = -ENODEV;
 	if (sdev->type != TYPE_ROM && sdev->type != TYPE_WORM)
 		goto fail;
@@ -744,6 +745,7 @@ fail_put:
 fail_free:
 	kfree(cd);
 fail:
+	scsi_autopm_put_device(sdev);
 	return error;
 }
 
drivers/scsi/st.c +2 -0
index aff9689..d3fd6e8 100644
@@ -4105,6 +4105,7 @@ static int st_probe(struct device *dev)
 		return -ENODEV;
 	}
 
+	scsi_autopm_get_device(SDp);
 	i = queue_max_segments(SDp->request_queue);
 	if (st_max_sg_segs < i)
 		i = st_max_sg_segs;
@@ -4244,6 +4245,7 @@ out_put_disk:
 out_buffer_free:
 	kfree(buffer);
 out:
+	scsi_autopm_put_device(SDp);
 	return -ENODEV;
 };
 
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
#10 lore raw

Re: [PATCH V3 02/16] scsi: balance out autopm get/put calls in scsi_sysfs_add_sdev()

Quoted 10 lines · Dolev Raviv

From: Subhash Jadavani <[email protected]>

SCSI Well-known logical units generally don't have any scsi driver associated with it which means no one will call scsi_autopm_put_device() on these wlun scsi devices and this would result in keeping the corresponding scsi device always active (hence LLD can't be suspended as well). Same exact problem can be seen for other scsi device representing normal logical unit whose driver is yet to be loaded. This patch fixes the above problem with this approach:

Quoted 6 lines

- make the scsi_autopm_put_device call at the end of scsi_sysfs_add_sdev to make it balance out the get earlier in the function.
- let drivers do paired get/put calls in their probe methods.

Looks good to me, I will pick this up for the next core-for-3.18 update.

#11 lore raw

PATCH v3 3/16 scsi: ufs: Allow vendor specific initialization

In reply to Dolev Raviv

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
index afaabe2..7a6edbc 100644
@@ -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
index 5e46232..d727b1a 100644
@@ -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
index acf318e..8c6bec0 100644
@@ -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
#12 lore raw

PATCH v3 4/16 scsi: ufs: Add regulator enable support

In reply to Dolev Raviv

From: 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
index 20468b2..65e3117 100644
@@ -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
index fafcf5e..729ce7d 100644
@@ -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
index 8c6bec0..c0232f9 100644
@@ -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
#13 lore raw

PATCH v3 5/16 scsi: ufs: Add clock initialization support

In reply to Dolev Raviv

From: 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
index 65e3117..b0f791a 100644
@@ -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
index 7a6edbc..2a26faa 100644
@@ -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
index c0232f9..bc0f7ed 100644
@@ -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
#14 lore raw

PATCH v3 6/16 scsi: ufs: refactor query descriptor API support

In reply to Dolev Raviv

From: 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
index 729ce7d..7ea8e71 100644
@@ -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
#15 lore raw

PATCH v3 7/16 scsi: ufs: improve init sequence

In reply to Dolev Raviv

From: 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
index 57a8dbb..1fabff4 100644
@@ -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
index bc0f7ed..eddb3f3 100644
@@ -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
#16 lore raw

PATCH v3 8/16 scsi: ufs: Active Power Mode - configuring bActiveICCLevel

In reply to Dolev Raviv

From: 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
index 7ea8e71..b0e1f62 100644
@@ -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
index eddb3f3..8365ad4 100644
@@ -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
#17 lore raw

PATCH v3 9/16 scsi: ufs: introduce well known logical unit in ufs

In reply to Dolev Raviv

From: 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
index b0e1f62..bcc3a7f 100644
@@ -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 +6 -1
index 8365ad4..9b5f77f 100644
@@ -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
#18 lore raw

Re: [PATCH V3 09/16] scsi: ufs: introduce well known logical unit in ufs

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

Quoted 13 lines
drivers/scsi/ufs/ufshcd.c +13 -0
+static void ufshcd_set_queue_depth(struct scsi_device *sdev)
+{
+       int ret = 0;
+       u8 lun_qdepth;
+       struct ufs_hba *hba;
+
+       hba = shost_priv(sdev->host);
+
+       lun_qdepth = hba->nutrs;
+       ret = ufshcd_read_unit_desc_param(hba,
+                                         ufshcd_scsi_to_upiu_lun(sdev->lun),
+                                         UNIT_DESC_PARAM_LU_Q_DEPTH,
+                                         &lun_qdepth,

drivers/scsi/ufs/ufshcd.c static int ufshcd_verify_dev_init(struct ufs_hba *hba) · line 2191

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+                                         sizeof(lun_qdepth));
+
+       /* Some WLUN doesn't support unit descriptor */
+       if (ret == -EOPNOTSUPP)
+               lun_qdepth = 1;
+       else if (!lun_qdepth)
+               /* eventually, we can figure out the real queue depth */
+               lun_qdepth = hba->nutrs;
+       else
+               lun_qdepth = min_t(int, lun_qdepth, hba->nutrs);

If ufshcd_read_unit_desc_param() failed and its error code was not -EOPNOTSUPP, lun_qdepth is undefined. In such cases lun_qdepth should be 1?

#19 lore raw

Re: [PATCH V3 09/16] scsi: ufs: introduce well known logical unit in ufs

Quoted 22 lines

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

Quoted 20 lines
drivers/scsi/ufs/ufshcd.c +11 -0
+static void ufshcd_set_queue_depth(struct scsi_device *sdev)
+{
+       int ret = 0;
+       u8 lun_qdepth;
+       struct ufs_hba *hba;
+
+       hba = shost_priv(sdev->host);
+
+       lun_qdepth = hba->nutrs;
+       ret = ufshcd_read_unit_desc_param(hba,
+

ufshcd_scsi_to_upiu_lun(sdev->lun),

drivers/scsi/ufs/ufshcd.c +8 -0
+                                         UNIT_DESC_PARAM_LU_Q_DEPTH,
+                                         &lun_qdepth,
+                                         sizeof(lun_qdepth));
+
+       /* Some WLUN doesn't support unit descriptor */
+       if (ret == -EOPNOTSUPP)
+               lun_qdepth = 1;
+       else if (!lun_qdepth)
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c static int ufshcd_verify_dev_init(struct ufs_hba *hba) · line 2191

Quoted 4 lines
drivers/scsi/ufs/ufshcd.c +4 -0
+               /* eventually, we can figure out the real queue depth */
+               lun_qdepth = hba->nutrs;
+       else
+               lun_qdepth = min_t(int, lun_qdepth, hba->nutrs);

If ufshcd_read_unit_desc_param() failed and its error code was not -EOPNOTSUPP, lun_qdepth is undefined. In such cases lun_qdepth should be 1?

I'm not sure I follow your concern.
If this lun does not support command queuing (ret == -EOPNOTSUPP) obviously, lun_qdepth should be 1.
If there was an error and lun_qdepth, was returned as 0, hba->nutrs will be our default choice.
last, even if the query succeed we want to make sure we don't exceed the hba->nutrs.

Thanks,
Dolev

Quoted 5 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

Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
#20 lore raw

Re: [PATCH V3 09/16] scsi: ufs: introduce well known logical unit in ufs

2014-09-15 19:39 GMT+09:00 Dolev Raviv <[email protected]>:

Quoted 31 lines
Quoted 22 lines

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

Quoted 20 lines
drivers/scsi/ufs/ufshcd.c +11 -0
+static void ufshcd_set_queue_depth(struct scsi_device *sdev)
+{
+       int ret = 0;
+       u8 lun_qdepth;
+       struct ufs_hba *hba;
+
+       hba = shost_priv(sdev->host);
+
+       lun_qdepth = hba->nutrs;
+       ret = ufshcd_read_unit_desc_param(hba,
+

ufshcd_scsi_to_upiu_lun(sdev->lun),

drivers/scsi/ufs/ufshcd.c +8 -0
+                                         UNIT_DESC_PARAM_LU_Q_DEPTH,
+                                         &lun_qdepth,
+                                         sizeof(lun_qdepth));
+
+       /* Some WLUN doesn't support unit descriptor */
+       if (ret == -EOPNOTSUPP)
+               lun_qdepth = 1;
+       else if (!lun_qdepth)
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c static int ufshcd_verify_dev_init(struct ufs_hba *hba) · line 2191

Quoted 4 lines
drivers/scsi/ufs/ufshcd.c +4 -0
+               /* eventually, we can figure out the real queue depth */
+               lun_qdepth = hba->nutrs;
+       else
+               lun_qdepth = min_t(int, lun_qdepth, hba->nutrs);

If ufshcd_read_unit_desc_param() failed and its error code was not -EOPNOTSUPP, lun_qdepth is undefined. In such cases lun_qdepth should be 1?

Quoted 7 lines

I'm not sure I follow your concern.
If this lun does not support command queuing (ret == -EOPNOTSUPP) obviously, lun_qdepth should be 1.
If there was an error and lun_qdepth, was returned as 0, hba->nutrs will be our default choice.
last, even if the query succeed we want to make sure we don't exceed the hba->nutrs.

I missed that the local variable lun_qdepth is initialized to hba->nutrs just before calling ufshcd_read_unit_desc_param(). So what I said undefined in the last message was just wrong. Thanks for the clarification.

#21 lore raw

PATCH v3 10/16 scsi: ufs: add UFS power management support

In reply to Dolev Raviv

From: 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
index bcc3a7f..2a82959 100644
@@ -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 +840 -85

1,159 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
index e1b844b..d572119 100644
@@ -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
#22 lore raw

Re: [PATCH V3 10/16] scsi: ufs: add UFS power management support

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +5 -0
+static inline void ufshcd_enable_irq(struct ufs_hba *hba)
+{
+       if (!hba->is_irq_enabled) {
+               enable_irq(hba->irq);
+               hba->is_irq_enabled = true;

drivers/scsi/ufs/ufshcd.c enum { · line 196

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+       }
+}
+
+static inline void ufshcd_disable_irq(struct ufs_hba *hba)
+{
+       if (hba->is_irq_enabled) {
+               disable_irq(hba->irq);
+               hba->is_irq_enabled = false;
+       }
+}

This IRQ could be shared among several devices because it is requested with IRQF_SHARED. So enable_irq()/disable_irq() should be replaced with request_irq()/free_irq()? Otherwise other devices which share the same IRQ will be malfunction while disabling IRQ.

#23 lore raw

Re: [PATCH V3 10/16] scsi: ufs: add UFS power management support

Quoted 14 lines

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

Quoted 12 lines
drivers/scsi/ufs/ufshcd.c +12 -0
+static inline void ufshcd_enable_irq(struct ufs_hba *hba)
+{
+       if (!hba->is_irq_enabled) {
+               enable_irq(hba->irq);
+               hba->is_irq_enabled = true;
+       }
+}
+
+static inline void ufshcd_disable_irq(struct ufs_hba *hba)
+{
+       if (hba->is_irq_enabled) {
+               disable_irq(hba->irq);
Quoted 8 lines

drivers/scsi/ufs/ufshcd.c enum { · line 196

Quoted 3 lines
drivers/scsi/ufs/ufshcd.c +3 -0
+               hba->is_irq_enabled = false;
+       }
+}

This IRQ could be shared among several devices because it is requested with IRQF_SHARED. So enable_irq()/disable_irq() should be replaced with request_irq()/free_irq()? Otherwise other devices which share the same IRQ will be malfunction while disabling IRQ.

Thanks, I will test your suggestion.

Quoted 5 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

Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
#24 lore raw

Re: [PATCH V3 10/16] scsi: ufs: add UFS power management support

In reply to Dolev Raviv

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

Quoted 19 lines
drivers/scsi/ufs/ufshcd.c +19 -0
+static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
+                                  int ua)
+{
+       int ret = 0;
+       struct regulator *reg = vreg->reg;
+       const char *name = vreg->name;
+
+       BUG_ON(!vreg);
+
+       ret = regulator_set_optimum_mode(reg, ua);
+       if (ret >= 0) {
+               /*
+                * regulator_set_optimum_mode() returns new regulator
+                * mode upon success.
+                */
+               ret = 0;
+       } else {
+               dev_err(dev, "%s: %s set optimum mode(ua=%d) failed, err=%d\n",
+                               __func__, name, ua, ret);

drivers/scsi/ufs/ufshcd.c static struct scsi_host_template ufshcd_driver_template = { · line 3756

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+       }
+
+       return ret;
+}
+
+static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
+                                        struct ufs_vreg *vreg)
+{
+       return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+}

If hba->vreg_info.vcc* is NULL as no applicable regulator driver exists, this function can be called with vreg == NULL through ufshcd_suspend() -> ufshcd_vreg_set_lpm() -> ufshcd_config_vreg_lpm(). Then this causes null pointer dereference or hits BUG_ON in ufshcd_config_vreg_load().

drivers/scsi/ufs/ufshcd.c static struct scsi_host_template ufshcd_driver_template = { · line 3762

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +5 -0
+static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
+                                        struct ufs_vreg *vreg)
+{
+       return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+}

Similar issue as above will happen through ufshcd_resume() -> ufshcd_vreg_set_hpm() -> ufshcd_config_vreg_hpm(). Then this causes null pointer dereference of vreg->max_uA.

So should these functions be noop when vreg == NULL is passed?

#25 lore raw

Re: [PATCH V3 10/16] scsi: ufs: add UFS power management support

In reply to Dolev Raviv

Hi Dolev,

Quoted 5 lines · Dolev Raviv

From: Subhash Jadavani <[email protected]>

This patch adds support for UFS device and UniPro link power management during runtime/system PM.

<snip>

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4271

Quoted 12 lines
drivers/scsi/ufs/ufshcd.c +12 -0
+vccq_lpm:
+	ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
+vcc_disable:
+	ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
+out:
+	return ret;
+}
+
+static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba)
+{
+	if (ufshcd_is_link_off(hba))
+		ufshcd_setup_hba_vreg(hba, false);

I didn't find any declaration and definition of above API in your patch set. During compilation of this driver I got a below error, am I missing any thing?

drivers/scsi/ufs/ufshcd.c: In function ‘ufshcd_hba_vreg_set_lpm’:
drivers/scsi/ufs/ufshcd.c:4656:3: error: implicit declaration of function ‘ufshcd_setup_hba_vreg’ [-Werror=implicit-function-declaration]
ufshcd_setup_hba_vreg(hba, false);
^

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4277

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +6 -0
+}
+
+static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba)
+{
+	if (ufshcd_is_link_off(hba))
+		ufshcd_setup_hba_vreg(hba, true);

Ditto.

Quoted 13 lines

}
-EXPORT_SYMBOL_GPL(ufshcd_suspend);

/**
- * ufshcd_resume - resume power management function
+ * ufshcd_suspend - helper function for suspend operations
* @hba: per adapter instance
+ * @pm_op: desired low power operation type
+ *
+ * This function will try to put the UFS device and link into low power
+ * mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl"
+ * (System PM level).
*

<snip>

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4560

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +9 -0
+int ufshcd_system_resume(struct ufs_hba *hba)
+{
+	if (!hba || !hba->is_powered || pm_runtime_suspended(hba->dev))
+		/*
+		 * Let the runtime resume take care of resuming
+		 * if runtime suspended.
+		 */
+		return 0;
+	else

"else" is not generally useful after a break or return.

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +7 -0
+		return ufshcd_resume(hba, UFS_SYSTEM_PM);
+}
+EXPORT_SYMBOL(ufshcd_system_resume);
+
+/**
+ * ufshcd_runtime_suspend - runtime suspend routine
+ * @hba: per adapter instance

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4577

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+ *
+ * Check the description of ufshcd_suspend() function for more details.
+ *
+ * Returns 0 for success and non-zero for failure
+ */
+int ufshcd_runtime_suspend(struct ufs_hba *hba)
+{
+	if (!hba || !hba->is_powered)
+		return 0;
+	else

"else" is not generally useful after a break or return.

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4588

Quoted 11 lines
drivers/scsi/ufs/ufshcd.c +8 -0
+		return ufshcd_suspend(hba, UFS_RUNTIME_PM);
 }
 EXPORT_SYMBOL(ufshcd_runtime_suspend);
 
+/**
+ * ufshcd_runtime_resume - runtime resume routine
+ * @hba: per adapter instance
+ *
+ * This function basically brings the UFS device, UniPro link and controller
+ * to active state. Following operations are done in this function:
+ *

Thanks,
--Kiran

#26 lore raw

Re: [PATCH V3 10/16] scsi: ufs: add UFS power management support

Hi Kiran,
Thanks for bringing it to my attention. Fortunately I already noticed it and fixed it. It will be sent soon when all comments on this series are addressed.

Thanks,
Dolev

Quoted 109 lines

Hi Dolev,

Quoted 5 lines · Dolev Raviv

From: Subhash Jadavani <[email protected]>

This patch adds support for UFS device and UniPro link power management during runtime/system PM.

<snip>

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4271

Quoted 12 lines
drivers/scsi/ufs/ufshcd.c +12 -0
+vccq_lpm:
+	ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
+vcc_disable:
+	ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
+out:
+	return ret;
+}
+
+static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba)
+{
+	if (ufshcd_is_link_off(hba))
+		ufshcd_setup_hba_vreg(hba, false);

I didn't find any declaration and definition of above API in your patch set.
During compilation of this driver I got a below error, am I missing any thing?

drivers/scsi/ufs/ufshcd.c: In function ‘ufshcd_hba_vreg_set_lpm’:
drivers/scsi/ufs/ufshcd.c:4656:3: error: implicit declaration of function ‘ufshcd_setup_hba_vreg’ [-Werror=implicit-function-declaration]

   ufshcd_setup_hba_vreg(hba, false);
   ^

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4277

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +6 -0
+}
+
+static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba)
+{
+	if (ufshcd_is_link_off(hba))
+		ufshcd_setup_hba_vreg(hba, true);

Ditto.

Quoted 13 lines

}
-EXPORT_SYMBOL_GPL(ufshcd_suspend);

/**
- * ufshcd_resume - resume power management function
+ * ufshcd_suspend - helper function for suspend operations
* @hba: per adapter instance
+ * @pm_op: desired low power operation type
+ *
+ * This function will try to put the UFS device and link into low power
+ * mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl"
+ * (System PM level).
*

<snip>

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4560

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +9 -0
+int ufshcd_system_resume(struct ufs_hba *hba)
+{
+	if (!hba || !hba->is_powered || pm_runtime_suspended(hba->dev))
+		/*
+		 * Let the runtime resume take care of resuming
+		 * if runtime suspended.
+		 */
+		return 0;
+	else

"else" is not generally useful after a break or return.

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +7 -0
+		return ufshcd_resume(hba, UFS_SYSTEM_PM);
+}
+EXPORT_SYMBOL(ufshcd_system_resume);
+
+/**
+ * ufshcd_runtime_suspend - runtime suspend routine
+ * @hba: per adapter instance

drivers/scsi/ufs/ufshcd.c static void ufshcd_hba_exit(struct ufs_hba *hba) · line 4577

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +10 -0
+ *
+ * Check the description of ufshcd_suspend() function for more details.
+ *
+ * Returns 0 for success and non-zero for failure
+ */
+int ufshcd_runtime_suspend(struct ufs_hba *hba)
+{
+	if (!hba || !hba->is_powered)
+		return 0;
+	else

"else" is not generally useful after a break or return.

Quoted 9 lines
drivers/scsi/ufs/ufshcd.c +6 -0
+		return ufshcd_suspend(hba, UFS_RUNTIME_PM);
 }
 EXPORT_SYMBOL(ufshcd_runtime_suspend);
 
+/**
+ * ufshcd_runtime_resume - runtime resume routine
+ * @hba: per adapter instance
+ *
+ * This function basically brings the UFS device, UniPro link and
Quoted 3 lines

controller
+ * to active state. Following operations are done in this function:
+ *

Thanks,
--Kiran

Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
#27 lore raw

PATCH v3 11/16 scsi: ufs: refactor configuring power mode

In reply to Dolev Raviv

Sometimes, 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 +111 -34
index 79ef312..bfa9a75 100644
@@ -178,6 +178,8 @@ 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_host_reset_and_restore(struct ufs_hba *hba);
+static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
+		struct ufs_pa_layer_attr *desired_pwr_mode);
 
 static inline void ufshcd_enable_irq(struct ufs_hba *hba)
 {
@@ -1933,65 +1935,129 @@ 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;
+}
+
+/**
+ * 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->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));
+
 	/*
 	 * Configure attributes for power mode change with below.
 	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
 	 * - 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), final_params.gear_rx);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES),
+			final_params.lane_rx);
+	if (final_params.pwr_rx == FASTAUTO_MODE ||
+			final_params.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), final_params.gear_tx);
+	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES),
+			final_params.lane_tx);
+	if (final_params.pwr_tx == FASTAUTO_MODE ||
+			final_params.pwr_tx == FAST_MODE)
 		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
-
-	if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
-		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
-
-	ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
-	if (ret)
+	else
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), FALSE);
+
+	if ((final_params.pwr_rx == FASTAUTO_MODE ||
+			final_params.pwr_tx == FASTAUTO_MODE ||
+			final_params.pwr_rx == FAST_MODE ||
+			final_params.pwr_tx == FAST_MODE) &&
+			final_params.hs_rate == PA_HS_MODE_B)
+		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES),
+				final_params.hs_rate);
+
+	ret = ufshcd_uic_change_pwr_mode(hba, final_params.pwr_rx << 4
+			| final_params.pwr_tx);
+	if (ret) {
 		dev_err(hba->dev,
 			"pwr_mode: power mode change failed %d\n", ret);
+	} else {
+		if (hba->vops->pwr_change_notify)
+			hba->vops->pwr_change_notify(hba,
+				POST_CHANGE, NULL, &final_params);
+
+		memcpy(&hba->pwr_info, &final_params, sizeof(final_params));
+	}
 
 	return ret;
 }
@@ -3658,7 +3724,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
@@ -4775,6 +4850,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
index 1ee429d..caebdcb 100644
@@ -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;
@@ -384,6 +408,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
#28 lore raw

Re: [PATCH V3 11/16] scsi: ufs: refactor configuring power mode

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

drivers/scsi/ufs/ufshcd.c static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) · line 2011

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +7 -0
+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->pwr_change_notify)

If hba->vops is null as no vendor specific callbacks, this causes a null pointer dereference. So null check for hba->vops is also needed just like other callbacks.

Quoted 46 lines
drivers/scsi/ufs/ufshcd.c +25 -12
+               hba->vops->pwr_change_notify(hba,
+                    PRE_CHANGE, desired_pwr_mode, &final_params);
+       else
+               memcpy(&final_params, desired_pwr_mode, sizeof(final_params));
+
        /*
         * Configure attributes for power mode change with below.
         * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
         * - 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), final_params.gear_rx);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES),
+                       final_params.lane_rx);
+       if (final_params.pwr_rx == FASTAUTO_MODE ||
+                       final_params.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), final_params.gear_tx);
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES),
+                       final_params.lane_tx);
+       if (final_params.pwr_tx == FASTAUTO_MODE ||
+                       final_params.pwr_tx == FAST_MODE)
                ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
-
-       if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
-               ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), PA_HS_MODE_B);
-
-       ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
-       if (ret)
+       else
+               ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), FALSE);
+
+       if ((final_params.pwr_rx == FASTAUTO_MODE ||
+                       final_params.pwr_tx == FASTAUTO_MODE ||
+                       final_params.pwr_rx == FAST_MODE ||
+                       final_params.pwr_tx == FAST_MODE) &&
+                       final_params.hs_rate == PA_HS_MODE_B)

drivers/scsi/ufs/ufshcd.c static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) · line 2055

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +8 -0
+               ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES),
+                               final_params.hs_rate);
+
+       ret = ufshcd_uic_change_pwr_mode(hba, final_params.pwr_rx << 4
+                       | final_params.pwr_tx);
+       if (ret) {
                dev_err(hba->dev,
                        "pwr_mode: power mode change failed %d\n", ret);
+       } else {
+               if (hba->vops->pwr_change_notify)

Ditto.

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +5 -0
+                       hba->vops->pwr_change_notify(hba,
+                               POST_CHANGE, NULL, &final_params);
+
+               memcpy(&hba->pwr_info, &final_params, sizeof(final_params));
+       }
#29 lore raw

Re: [PATCH V3 11/16] scsi: ufs: refactor configuring power mode

Quoted 13 lines

2014-09-10 20:54 GMT+09:00 Dolev Raviv <[email protected]>:

drivers/scsi/ufs/ufshcd.c static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) · line 2011

Quoted 7 lines
drivers/scsi/ufs/ufshcd.c +7 -0
+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->pwr_change_notify)

If hba->vops is null as no vendor specific callbacks, this causes a null pointer dereference. So null check for hba->vops is also needed just like other callbacks.

Sure, will fix both.

Quoted 81 lines
Quoted 57 lines
drivers/scsi/ufs/ufshcd.c +4 -0
+               hba->vops->pwr_change_notify(hba,
+                    PRE_CHANGE, desired_pwr_mode, &final_params);
+       else
+               memcpy(&final_params, desired_pwr_mode,

sizeof(final_params));

drivers/scsi/ufs/ufshcd.c +1 -2
+
        /*
         * Configure attributes for power mode change with below.
         * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
         * - 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]);

(quoted diff) +1 -1
-       if (pwr[RX] == FASTAUTO_MODE)
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR),

final_params.gear_rx);

drivers/scsi/ufs/ufshcd.c +4 -0
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES),
+                       final_params.lane_rx);
+       if (final_params.pwr_rx == FASTAUTO_MODE ||
+                       final_params.pwr_rx == FAST_MODE)
                ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION),

TRUE);

(quoted diff) +2 -0
+       else
+               ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION),

FALSE);

(quoted diff) +0 -2
-       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), gear[TX]);
-       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES),

lanes[TX]);

(quoted diff) +1 -1
-       if (pwr[TX] == FASTAUTO_MODE)
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR),

final_params.gear_tx);

drivers/scsi/ufs/ufshcd.c +4 -0
+       ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES),
+                       final_params.lane_tx);
+       if (final_params.pwr_tx == FASTAUTO_MODE ||
+                       final_params.pwr_tx == FAST_MODE)
                ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION),

TRUE);

drivers/scsi/ufs/ufshcd.c +0 -3
-
-       if (pwr[RX] == FASTAUTO_MODE || pwr[TX] == FASTAUTO_MODE)
-               ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES),

PA_HS_MODE_B);

drivers/scsi/ufs/ufshcd.c +2 -3
-
-       ret = ufshcd_uic_change_pwr_mode(hba, pwr[RX] << 4 | pwr[TX]);
-       if (ret)
+       else
+               ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION),

FALSE);

drivers/scsi/ufs/ufshcd.c +6 -0
+
+       if ((final_params.pwr_rx == FASTAUTO_MODE ||
+                       final_params.pwr_tx == FASTAUTO_MODE ||
+                       final_params.pwr_rx == FAST_MODE ||
+                       final_params.pwr_tx == FAST_MODE) &&
+                       final_params.hs_rate == PA_HS_MODE_B)

drivers/scsi/ufs/ufshcd.c static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) · line 2055

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +8 -0
+               ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES),
+                               final_params.hs_rate);
+
+       ret = ufshcd_uic_change_pwr_mode(hba, final_params.pwr_rx << 4
+                       | final_params.pwr_tx);
+       if (ret) {
                dev_err(hba->dev,
                        "pwr_mode: power mode change failed %d\n", ret);
+       } else {
+               if (hba->vops->pwr_change_notify)

Ditto.

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +4 -0
+                       hba->vops->pwr_change_notify(hba,
+                               POST_CHANGE, NULL, &final_params);
+
+               memcpy(&hba->pwr_info, &final_params,

sizeof(final_params));
+ }

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

Signature · 2 lines
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
#30 lore raw

PATCH v3 12/16 scsi: ufs: Add support for clock gating

In reply to Dolev Raviv

From: 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 +299 -26
index bfa9a75..c762a6a 100644
@@ -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 int ufshcd_config_pwr_mode(struct ufs_hba *hba,
 		struct ufs_pa_layer_attr *desired_pwr_mode);
@@ -498,6 +503,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;
+		} else {
+			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
@@ -699,10 +929,12 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
 {
 	int ret;
 
+	ufshcd_hold(hba, false);
 	mutex_lock(&hba->uic_cmd_mutex);
 	ret = __ufshcd_send_uic_cmd(hba, uic_cmd);
 	mutex_unlock(&hba->uic_cmd_mutex);
 
+	ufshcd_release(hba);
 	return ret;
 }
 
@@ -1018,6 +1250,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);
@@ -1293,6 +1533,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);
@@ -1336,6 +1577,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;
 }
 
@@ -1359,6 +1601,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);
@@ -1398,6 +1641,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;
 }
 
@@ -1425,6 +1669,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);
@@ -1474,6 +1719,7 @@ static int ufshcd_query_descriptor(struct ufs_hba *hba,
 out_unlock:
 	mutex_unlock(&hba->dev_cmd.lock);
 out:
+	ufshcd_release(hba);
 	return err;
 }
 
@@ -1888,6 +2134,7 @@ out:
 	hba->uic_async_done = NULL;
 	spin_unlock_irqrestore(hba->host->host_lock, flags);
 	mutex_unlock(&hba->uic_cmd_mutex);
+
 	return ret;
 }
 
@@ -1902,12 +2149,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;
-
-	return ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+	ufshcd_hold(hba, false);
+	ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+	ufshcd_release(hba);
+out:
+	return ret;
 }
 
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba)
@@ -2306,6 +2557,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,
@@ -2317,6 +2569,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);
@@ -2729,6 +2982,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);
@@ -3013,6 +3267,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) {
@@ -3066,6 +3321,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);
 }
 
@@ -3249,6 +3505,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;
@@ -3300,6 +3557,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;
 }
 
@@ -3382,6 +3640,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;
@@ -3446,6 +3705,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;
@@ -3454,6 +3714,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;
 }
 
@@ -3538,6 +3803,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
@@ -3571,6 +3837,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;
 }
 
@@ -3798,6 +4065,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,
@@ -3980,6 +4248,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;
@@ -4004,12 +4273,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;
 }
@@ -4070,23 +4346,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);
@@ -4390,6 +4657,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;
@@ -4412,7 +4682,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) &&
@@ -4422,7 +4692,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);
@@ -4455,6 +4725,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.
@@ -4476,6 +4747,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;
@@ -4505,12 +4779,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 */
 	ufshcd_enable_irq(hba);
 
@@ -4552,6 +4820,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:
@@ -4563,9 +4835,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;
@@ -4734,6 +5003,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);
@@ -4869,11 +5139,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;
 	}
@@ -4882,13 +5153,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 */
@@ -4913,6 +5184,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
#31 lore raw

Re: [PATCH V3 12/16] scsi: ufs: Add support for clock gating

Quoted 7 lines · Dolev Raviv

From: 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.

<snip>

Quoted 6 lines
drivers/scsi/ufs/ufshcd.c +1 -0
@@ -1902,12 +2149,16 @@ out:
 static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
 {
 	struct uic_command uic_cmd = {0};
+	int ret;

drivers/scsi/ufs/ufshcd.c out: · line 2160

Quoted 10 lines
drivers/scsi/ufs/ufshcd.c +4 -2
 
 	uic_cmd.command = UIC_CMD_DME_SET;
 	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
 	uic_cmd.argument3 = mode;
-
-	return ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+	ufshcd_hold(hba, false);
+	ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
+	ufshcd_release(hba);
+out:

May be you can remove this label because it is not used anywhere.

Quoted 13 lines
drivers/scsi/ufs/ufshcd.c +2 -0
+	return ret;
 }
 
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba)
@@ -2306,6 +2557,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,

drivers/scsi/ufs/ufshcd.c static int ufshcd_verify_dev_init(struct ufs_hba *hba) · line 2573

Quoted 5 lines
drivers/scsi/ufs/ufshcd.c +1 -0
@@ -2317,6 +2569,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);
 

Thanks,
--Kiran

#32 lore raw

PATCH v3 13/16 scsi: ufs: Add freq-table-hz property for UFS device

In reply to Dolev Raviv

From: 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
index b0f791a..e73a619 100644
@@ -24,11 +24,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.
@@ -49,5 +49,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
index 93c7c82..2caa27e 100644
@@ -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
#33 lore raw

PATCH v3 14/16 scsi: ufs: Add support for clock scaling using devfreq framework

In reply to Dolev Raviv

From: 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
index f07f901..6e07b2a 100644
@@ -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
index 2caa27e..d5699d0 100644
@@ -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
@@ -453,6 +464,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 */
@@ -464,6 +480,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
#34 lore raw

PATCH v3 15/16 scsi: ufs: tune bkops while power managment events

In reply to Dolev Raviv

From: 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
index 0bbb933..33984cc 100644
@@ -4719,13 +4719,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) &&
@@ -4871,7 +4877,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
index d5699d0..b94b835 100644
@@ -466,6 +466,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;
@@ -484,6 +486,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
#35 lore raw

PATCH v3 16/16 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
index 33984cc..8b7c332 100644
@@ -4947,6 +4947,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
index b94b835..d502d70 100644
@@ -471,6 +471,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 */
@@ -496,6 +497,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