Skip to content

Commit 57d104c

Browse files
Subhash JadavaniChristoph Hellwig
authored andcommitted
ufs: add UFS power management support
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. Signed-off-by: Subhash Jadavani <[email protected]> Signed-off-by: Dolev Raviv <[email protected]> Signed-off-by: Sujit Reddy Thumma <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]>
1 parent 0ce147d commit 57d104c

File tree

6 files changed

+989
-182
lines changed

6 files changed

+989
-182
lines changed

drivers/scsi/ufs/ufs.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ enum {
129129
/* Flag idn for Query Requests*/
130130
enum flag_idn {
131131
QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
132+
QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
132133
QUERY_FLAG_IDN_BKOPS_EN = 0x04,
133134
};
134135

@@ -194,6 +195,18 @@ enum unit_desc_param {
194195
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
195196
};
196197

198+
/*
199+
* Logical Unit Write Protect
200+
* 00h: LU not write protected
201+
* 01h: LU write protected when fPowerOnWPEn =1
202+
* 02h: LU permanently write protected when fPermanentWPEn =1
203+
*/
204+
enum ufs_lu_wp_type {
205+
UFS_LU_NO_WP = 0x00,
206+
UFS_LU_POWER_ON_WP = 0x01,
207+
UFS_LU_PERM_WP = 0x02,
208+
};
209+
197210
/* bActiveICCLevel parameter current units */
198211
enum {
199212
UFSHCD_NANO_AMP = 0,
@@ -226,11 +239,12 @@ enum {
226239
};
227240

228241
/* Background operation status */
229-
enum {
242+
enum bkops_status {
230243
BKOPS_STATUS_NO_OP = 0x0,
231244
BKOPS_STATUS_NON_CRITICAL = 0x1,
232245
BKOPS_STATUS_PERF_IMPACT = 0x2,
233246
BKOPS_STATUS_CRITICAL = 0x3,
247+
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
234248
};
235249

236250
/* UTP QUERY Transaction Specific Fields OpCode */
@@ -291,6 +305,14 @@ enum {
291305
UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
292306
UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
293307
};
308+
309+
/* UFS device power modes */
310+
enum ufs_dev_pwr_mode {
311+
UFS_ACTIVE_PWR_MODE = 1,
312+
UFS_SLEEP_PWR_MODE = 2,
313+
UFS_POWERDOWN_PWR_MODE = 3,
314+
};
315+
294316
/**
295317
* struct utp_upiu_header - UPIU header structure
296318
* @dword_0: UPIU header DW-0
@@ -437,6 +459,12 @@ struct ufs_query_res {
437459
#define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */
438460
#define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */
439461

462+
/*
463+
* VCCQ & VCCQ2 current requirement when UFS device is in sleep state
464+
* and link is in Hibern8 state.
465+
*/
466+
#define UFS_VREG_LPM_LOAD_UA 1000 /* uA */
467+
440468
struct ufs_vreg {
441469
struct regulator *reg;
442470
const char *name;
@@ -454,4 +482,10 @@ struct ufs_vreg_info {
454482
struct ufs_vreg *vdd_hba;
455483
};
456484

485+
struct ufs_dev_info {
486+
bool f_power_on_wp_en;
487+
/* Keeps information if any of the LU is power on write protected */
488+
bool is_lu_power_on_wp;
489+
};
490+
457491
#endif /* End of Header */

drivers/scsi/ufs/ufshcd-pci.c

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -43,34 +43,24 @@
4343
* @pdev: pointer to PCI device handle
4444
* @state: power state
4545
*
46-
* Returns -ENOSYS
46+
* Returns 0 if successful
47+
* Returns non-zero otherwise
4748
*/
4849
static int ufshcd_pci_suspend(struct device *dev)
4950
{
50-
/*
51-
* TODO:
52-
* 1. Call ufshcd_suspend
53-
* 2. Do bus specific power management
54-
*/
55-
56-
return -ENOSYS;
51+
return ufshcd_system_suspend(dev_get_drvdata(dev));
5752
}
5853

5954
/**
6055
* ufshcd_pci_resume - resume power management function
6156
* @pdev: pointer to PCI device handle
6257
*
63-
* Returns -ENOSYS
58+
* Returns 0 if successful
59+
* Returns non-zero otherwise
6460
*/
6561
static int ufshcd_pci_resume(struct device *dev)
6662
{
67-
/*
68-
* TODO:
69-
* 1. Call ufshcd_resume.
70-
* 2. Do bus specific wake up
71-
*/
72-
73-
return -ENOSYS;
63+
return ufshcd_system_resume(dev_get_drvdata(dev));
7464
}
7565
#else
7666
#define ufshcd_pci_suspend NULL
@@ -80,30 +70,15 @@ static int ufshcd_pci_resume(struct device *dev)
8070
#ifdef CONFIG_PM_RUNTIME
8171
static int ufshcd_pci_runtime_suspend(struct device *dev)
8272
{
83-
struct ufs_hba *hba = dev_get_drvdata(dev);
84-
85-
if (!hba)
86-
return 0;
87-
88-
return ufshcd_runtime_suspend(hba);
73+
return ufshcd_runtime_suspend(dev_get_drvdata(dev));
8974
}
9075
static int ufshcd_pci_runtime_resume(struct device *dev)
9176
{
92-
struct ufs_hba *hba = dev_get_drvdata(dev);
93-
94-
if (!hba)
95-
return 0;
96-
97-
return ufshcd_runtime_resume(hba);
77+
return ufshcd_runtime_resume(dev_get_drvdata(dev));
9878
}
9979
static int ufshcd_pci_runtime_idle(struct device *dev)
10080
{
101-
struct ufs_hba *hba = dev_get_drvdata(dev);
102-
103-
if (!hba)
104-
return 0;
105-
106-
return ufshcd_runtime_idle(hba);
81+
return ufshcd_runtime_idle(dev_get_drvdata(dev));
10782
}
10883
#else /* !CONFIG_PM_RUNTIME */
10984
#define ufshcd_pci_runtime_suspend NULL
@@ -117,7 +92,7 @@ static int ufshcd_pci_runtime_idle(struct device *dev)
11792
*/
11893
static void ufshcd_pci_shutdown(struct pci_dev *pdev)
11994
{
120-
ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
95+
ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev));
12196
}
12297

12398
/**

drivers/scsi/ufs/ufshcd-pltfrm.c

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -225,45 +225,24 @@ static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
225225
* ufshcd_pltfrm_suspend - suspend power management function
226226
* @dev: pointer to device handle
227227
*
228-
*
229-
* Returns 0
228+
* Returns 0 if successful
229+
* Returns non-zero otherwise
230230
*/
231231
static int ufshcd_pltfrm_suspend(struct device *dev)
232232
{
233-
struct platform_device *pdev = to_platform_device(dev);
234-
struct ufs_hba *hba = platform_get_drvdata(pdev);
235-
236-
/*
237-
* TODO:
238-
* 1. Call ufshcd_suspend
239-
* 2. Do bus specific power management
240-
*/
241-
242-
disable_irq(hba->irq);
243-
244-
return 0;
233+
return ufshcd_system_suspend(dev_get_drvdata(dev));
245234
}
246235

247236
/**
248237
* ufshcd_pltfrm_resume - resume power management function
249238
* @dev: pointer to device handle
250239
*
251-
* Returns 0
240+
* Returns 0 if successful
241+
* Returns non-zero otherwise
252242
*/
253243
static int ufshcd_pltfrm_resume(struct device *dev)
254244
{
255-
struct platform_device *pdev = to_platform_device(dev);
256-
struct ufs_hba *hba = platform_get_drvdata(pdev);
257-
258-
/*
259-
* TODO:
260-
* 1. Call ufshcd_resume.
261-
* 2. Do bus specific wake up
262-
*/
263-
264-
enable_irq(hba->irq);
265-
266-
return 0;
245+
return ufshcd_system_resume(dev_get_drvdata(dev));
267246
}
268247
#else
269248
#define ufshcd_pltfrm_suspend NULL
@@ -273,37 +252,27 @@ static int ufshcd_pltfrm_resume(struct device *dev)
273252
#ifdef CONFIG_PM_RUNTIME
274253
static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
275254
{
276-
struct ufs_hba *hba = dev_get_drvdata(dev);
277-
278-
if (!hba)
279-
return 0;
280-
281-
return ufshcd_runtime_suspend(hba);
255+
return ufshcd_runtime_suspend(dev_get_drvdata(dev));
282256
}
283257
static int ufshcd_pltfrm_runtime_resume(struct device *dev)
284258
{
285-
struct ufs_hba *hba = dev_get_drvdata(dev);
286-
287-
if (!hba)
288-
return 0;
289-
290-
return ufshcd_runtime_resume(hba);
259+
return ufshcd_runtime_resume(dev_get_drvdata(dev));
291260
}
292261
static int ufshcd_pltfrm_runtime_idle(struct device *dev)
293262
{
294-
struct ufs_hba *hba = dev_get_drvdata(dev);
295-
296-
if (!hba)
297-
return 0;
298-
299-
return ufshcd_runtime_idle(hba);
263+
return ufshcd_runtime_idle(dev_get_drvdata(dev));
300264
}
301265
#else /* !CONFIG_PM_RUNTIME */
302266
#define ufshcd_pltfrm_runtime_suspend NULL
303267
#define ufshcd_pltfrm_runtime_resume NULL
304268
#define ufshcd_pltfrm_runtime_idle NULL
305269
#endif /* CONFIG_PM_RUNTIME */
306270

271+
static void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
272+
{
273+
ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
274+
}
275+
307276
/**
308277
* ufshcd_pltfrm_probe - probe routine of the driver
309278
* @pdev: pointer to Platform device handle
@@ -404,6 +373,7 @@ static const struct dev_pm_ops ufshcd_dev_pm_ops = {
404373
static struct platform_driver ufshcd_pltfrm_driver = {
405374
.probe = ufshcd_pltfrm_probe,
406375
.remove = ufshcd_pltfrm_remove,
376+
.shutdown = ufshcd_pltfrm_shutdown,
407377
.driver = {
408378
.name = "ufshcd",
409379
.owner = THIS_MODULE,

0 commit comments

Comments
 (0)