RE: [PATCH/RFC V2 10/16] scsi: ufs: add UFS power management support

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

 




> -----Original Message-----
> From: linux-scsi-owner@xxxxxxxxxxxxxxx
> [mailto:linux-scsi-owner@xxxxxxxxxxxxxxx] On Behalf Of Dong, Chuanxiao
> Sent: Wednesday, August 20, 2014 3:21 PM
> To: Dolev Raviv; James.Bottomley@xxxxxxxxxxxxxxxxxxxxx;
> hch@xxxxxxxxxxxxx
> Cc: linux-scsi@xxxxxxxxxxxxxxx; linux-scsi-owner@xxxxxxxxxxxxxxx;
> linux-arm-msm@xxxxxxxxxxxxxxx; santoshsy@xxxxxxxxx; Subhash Jadavani;
> Sujit Reddy Thumma
> Subject: RE: [PATCH/RFC V2 10/16] scsi: ufs: add UFS power management
> support
> 
> 
> 
> > -----Original Message-----
> > From: linux-scsi-owner@xxxxxxxxxxxxxxx
> > [mailto:linux-scsi-owner@xxxxxxxxxxxxxxx] On Behalf Of Dolev Raviv
> > Sent: Thursday, August 14, 2014 9:31 PM
> > To: James.Bottomley@xxxxxxxxxxxxxxxxxxxxx; hch@xxxxxxxxxxxxx
> > Cc: linux-scsi@xxxxxxxxxxxxxxx; linux-scsi-owner@xxxxxxxxxxxxxxx;
> > linux-arm-msm@xxxxxxxxxxxxxxx; santoshsy@xxxxxxxxx; Subhash Jadavani;
> > Dolev Raviv; Sujit Reddy Thumma
> > Subject: [PATCH/RFC V2 10/16] scsi: ufs: add UFS power management support
> >
> > From: Subhash Jadavani <subhashj@xxxxxxxxxxxxxx>
> >
> > 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.
> >
> > Signed-off-by: Subhash Jadavani <subhashj@xxxxxxxxxxxxxx>
> > Signed-off-by: Dolev Raviv <draviv@xxxxxxxxxxxxxx>
> > Signed-off-by: Sujit Reddy Thumma <sthumma@xxxxxxxxxxxxxx>
> >
> > diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index
> > bcc3a7f..2a82959 100644
> > --- a/drivers/scsi/ufs/ufs.h
> > +++ b/drivers/scsi/ufs/ufs.h
> > @@ -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 */
> > diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index
> > 1aac2ef..b8f7774 100644
> > --- a/drivers/scsi/ufs/ufshcd-pci.c
> > +++ b/drivers/scsi/ufs/ufshcd-pci.c
> > @@ -43,34 +43,24 @@
> >   * @pdev: pointer to PCI device handle
> >   * @state: power state
> >   *
> > - * Returns -ENOSYS
> > + * Returns 0 if successful
> > + * Returns non-zero otherwise
> >   */
> >  static int ufshcd_pci_suspend(struct device *dev)  {
> > -	/*
> > -	 * TODO:
> > -	 * 1. Call ufshcd_suspend
> > -	 * 2. Do bus specific power management
> > -	 */
> > -
> > -	return -ENOSYS;
> > +	return ufshcd_system_suspend(dev_get_drvdata(dev));
> >  }
> >
> >  /**
> >   * ufshcd_pci_resume - resume power management function
> >   * @pdev: pointer to PCI device handle
> >   *
> > - * Returns -ENOSYS
> > + * Returns 0 if successful
> > + * Returns non-zero otherwise
> >   */
> >  static int ufshcd_pci_resume(struct device *dev)  {
> > -	/*
> > -	 * TODO:
> > -	 * 1. Call ufshcd_resume.
> > -	 * 2. Do bus specific wake up
> > -	 */
> > -
> > -	return -ENOSYS;
> > +	return ufshcd_system_resume(dev_get_drvdata(dev));
> >  }
> >  #else
> >  #define ufshcd_pci_suspend	NULL
> > @@ -80,30 +70,15 @@ static int ufshcd_pci_resume(struct device *dev)
> > #ifdef CONFIG_PM_RUNTIME  static int
> ufshcd_pci_runtime_suspend(struct
> > device *dev)  {
> > -	struct ufs_hba *hba = dev_get_drvdata(dev);
> > -
> > -	if (!hba)
> > -		return 0;
> > -
> > -	return ufshcd_runtime_suspend(hba);
> > +	return ufshcd_runtime_suspend(dev_get_drvdata(dev));
> >  }
> >  static int ufshcd_pci_runtime_resume(struct device *dev)  {
> > -	struct ufs_hba *hba = dev_get_drvdata(dev);
> > -
> > -	if (!hba)
> > -		return 0;
> > -
> > -	return ufshcd_runtime_resume(hba);
> > +	return ufshcd_runtime_resume(dev_get_drvdata(dev));
> >  }
> >  static int ufshcd_pci_runtime_idle(struct device *dev)  {
> > -	struct ufs_hba *hba = dev_get_drvdata(dev);
> > -
> > -	if (!hba)
> > -		return 0;
> > -
> > -	return ufshcd_runtime_idle(hba);
> > +	return ufshcd_runtime_idle(dev_get_drvdata(dev));
> >  }
> >  #else /* !CONFIG_PM_RUNTIME */
> >  #define ufshcd_pci_runtime_suspend	NULL
> > @@ -117,7 +92,7 @@ static int ufshcd_pci_runtime_idle(struct device *dev)
> >   */
> >  static void ufshcd_pci_shutdown(struct pci_dev *pdev)  {
> > -	ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
> > +	ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev));
> >  }
> >
> >  /**
> > diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
> > index 642d80f..edaccd0 100644
> > --- a/drivers/scsi/ufs/ufshcd-pltfrm.c
> > +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
> > @@ -216,45 +216,24 @@ out:
> >   * ufshcd_pltfrm_suspend - suspend power management function
> >   * @dev: pointer to device handle
> >   *
> > - *
> > - * Returns 0
> > + * Returns 0 if successful
> > + * Returns non-zero otherwise
> >   */
> >  static int ufshcd_pltfrm_suspend(struct device *dev)  {
> > -	struct platform_device *pdev = to_platform_device(dev);
> > -	struct ufs_hba *hba =  platform_get_drvdata(pdev);
> > -
> > -	/*
> > -	 * TODO:
> > -	 * 1. Call ufshcd_suspend
> > -	 * 2. Do bus specific power management
> > -	 */
> > -
> > -	disable_irq(hba->irq);
> > -
> > -	return 0;
> > +	return ufshcd_system_suspend(dev_get_drvdata(dev));
> >  }
> >
> >  /**
> >   * ufshcd_pltfrm_resume - resume power management function
> >   * @dev: pointer to device handle
> >   *
> > - * Returns 0
> > + * Returns 0 if successful
> > + * Returns non-zero otherwise
> >   */
> >  static int ufshcd_pltfrm_resume(struct device *dev)  {
> > -	struct platform_device *pdev = to_platform_device(dev);
> > -	struct ufs_hba *hba =  platform_get_drvdata(pdev);
> > -
> > -	/*
> > -	 * TODO:
> > -	 * 1. Call ufshcd_resume.
> > -	 * 2. Do bus specific wake up
> > -	 */
> > -
> > -	enable_irq(hba->irq);
> > -
> > -	return 0;
> > +	return ufshcd_system_resume(dev_get_drvdata(dev));
> >  }
> >  #else
> >  #define ufshcd_pltfrm_suspend	NULL
> > @@ -264,30 +243,15 @@ static int ufshcd_pltfrm_resume(struct device
> *dev)
> > #ifdef CONFIG_PM_RUNTIME  static int
> > ufshcd_pltfrm_runtime_suspend(struct device *dev)  {
> > -	struct ufs_hba *hba =  dev_get_drvdata(dev);
> > -
> > -	if (!hba)
> > -		return 0;
> > -
> > -	return ufshcd_runtime_suspend(hba);
> > +	return ufshcd_runtime_suspend(dev_get_drvdata(dev));
> >  }
> >  static int ufshcd_pltfrm_runtime_resume(struct device *dev)  {
> > -	struct ufs_hba *hba =  dev_get_drvdata(dev);
> > -
> > -	if (!hba)
> > -		return 0;
> > -
> > -	return ufshcd_runtime_resume(hba);
> > +	return ufshcd_runtime_resume(dev_get_drvdata(dev));
> >  }
> >  static int ufshcd_pltfrm_runtime_idle(struct device *dev)  {
> > -	struct ufs_hba *hba =  dev_get_drvdata(dev);
> > -
> > -	if (!hba)
> > -		return 0;
> > -
> > -	return ufshcd_runtime_idle(hba);
> > +	return ufshcd_runtime_idle(dev_get_drvdata(dev));
> >  }
> >  #else /* !CONFIG_PM_RUNTIME */
> >  #define ufshcd_pltfrm_runtime_suspend	NULL
> > @@ -295,6 +259,11 @@ static int ufshcd_pltfrm_runtime_idle(struct device
> > *dev)
> >  #define ufshcd_pltfrm_runtime_idle	NULL
> >  #endif /* CONFIG_PM_RUNTIME */
> >
> > +static void ufshcd_pltfrm_shutdown(struct platform_device *pdev) {
> > +	ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); }
> > +
> >  /**
> >   * ufshcd_pltfrm_probe - probe routine of the driver
> >   * @pdev: pointer to Platform device handle @@ -395,6 +364,7 @@ static
> > const struct dev_pm_ops ufshcd_dev_pm_ops = {  static struct
> > platform_driver ufshcd_pltfrm_driver = {
> >  	.probe	= ufshcd_pltfrm_probe,
> >  	.remove	= ufshcd_pltfrm_remove,
> > +	.shutdown = ufshcd_pltfrm_shutdown,
> >  	.driver	= {
> >  		.name	= "ufshcd",
> >  		.owner	= THIS_MODULE,
> > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index
> > 876d210..fac71c9 100644
> > --- a/drivers/scsi/ufs/ufshcd.c
> > +++ b/drivers/scsi/ufs/ufshcd.c
> > @@ -44,7 +44,6 @@
> >
> >  #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
> >  				 UTP_TASK_REQ_COMPL |\
> > -				 UIC_POWER_MODE |\
> >  				 UFSHCD_ERROR_MASK)
> >  /* UIC command timeout, unit: ms */
> >  #define UIC_CMD_TIMEOUT	500
> > @@ -138,12 +137,63 @@ enum {
> >  #define ufshcd_clear_eh_in_progress(h) \
> >  	(h->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
> >
> > +#define ufshcd_set_ufs_dev_active(h) \
> > +	((h)->curr_dev_pwr_mode = UFS_ACTIVE_PWR_MODE) #define
> > +ufshcd_set_ufs_dev_sleep(h) \
> > +	((h)->curr_dev_pwr_mode = UFS_SLEEP_PWR_MODE) #define
> > +ufshcd_set_ufs_dev_poweroff(h) \
> > +	((h)->curr_dev_pwr_mode = UFS_POWERDOWN_PWR_MODE) #define
> > +ufshcd_is_ufs_dev_active(h) \
> > +	((h)->curr_dev_pwr_mode == UFS_ACTIVE_PWR_MODE) #define
> > +ufshcd_is_ufs_dev_sleep(h) \
> > +	((h)->curr_dev_pwr_mode == UFS_SLEEP_PWR_MODE) #define
> > +ufshcd_is_ufs_dev_poweroff(h) \
> > +	((h)->curr_dev_pwr_mode == UFS_POWERDOWN_PWR_MODE)
> > +
> > +static struct ufs_pm_lvl_states ufs_pm_lvl_states[] = {
> > +	{UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE},
> > +	{UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE},
> > +	{UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE},
> > +	{UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE},
> > +	{UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE},
> > +	{UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE}, };
> > +
> > +static inline enum ufs_dev_pwr_mode
> > +ufs_get_pm_lvl_to_dev_pwr_mode(enum ufs_pm_level lvl) {
> > +	return ufs_pm_lvl_states[lvl].dev_state; }
> > +
> > +static inline enum uic_link_state
> > +ufs_get_pm_lvl_to_link_pwr_state(enum ufs_pm_level lvl) {
> > +	return ufs_pm_lvl_states[lvl].link_state;
> > +}
> > +
> >  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);
> > +static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
> > +
> > +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);
> > +		hba->is_irq_enabled = false;
> > +	}
> > +}
> >
> >  /*
> >   * ufshcd_wait_for_register - wait for register value to change @@
> -1778,44
> > +1828,48 @@ out:
> >  EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
> >
> >  /**
> > - * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
> > - *				using DME_SET primitives.
> > + * ufshcd_uic_pwr_ctrl - executes UIC commands (which affects the link
> > + power
> > + * state) and waits for it to take effect.
> > + *
> >   * @hba: per adapter instance
> > - * @mode: powr mode value
> > + * @cmd: UIC command to execute
> > + *
> > + * DME operations like DME_SET(PA_PWRMODE), DME_HIBERNATE_ENTER
> > &
> > + * DME_HIBERNATE_EXIT commands take some time to take its effect on
> > + both host
> > + * and device UniPro link and hence it's final completion would be
> > + indicated by
> > + * dedicated status bits in Interrupt Status register (UPMS, UHES,
> > + UHXS) in
> > + * addition to normal UIC command completion Status (UCCS). This
> > + function only
> > + * returns after the relevant status bits indicate the completion.
> >   *
> >   * Returns 0 on success, non-zero value on failure
> >   */
> > -static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
> > +int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
> >  {
> > -	struct uic_command uic_cmd = {0};
> > -	struct completion pwr_done;
> > +	struct completion uic_async_done;
> >  	unsigned long flags;
> >  	u8 status;
> >  	int ret;
> >
> > -	uic_cmd.command = UIC_CMD_DME_SET;
> > -	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
> > -	uic_cmd.argument3 = mode;
> > -	init_completion(&pwr_done);
> > -
> >  	mutex_lock(&hba->uic_cmd_mutex);
> > +	init_completion(&uic_async_done);
> >
> >  	spin_lock_irqsave(hba->host->host_lock, flags);
> > -	hba->pwr_done = &pwr_done;
> > +	hba->uic_async_done = &uic_async_done;
> >  	spin_unlock_irqrestore(hba->host->host_lock, flags);
> > -	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
> > +
> > +	ret = __ufshcd_send_uic_cmd(hba, cmd);
> >  	if (ret) {
> >  		dev_err(hba->dev,
> > -			"pwr mode change with mode 0x%x uic error %d\n",
> > -			mode, ret);
> > +			"pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n",
> > +			cmd->command, cmd->argument3, ret);
> >  		goto out;
> >  	}
> >
> > -	if (!wait_for_completion_timeout(hba->pwr_done,
> > +	if (!wait_for_completion_timeout(hba->uic_async_done,
> >  					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
> >  		dev_err(hba->dev,
> > -			"pwr mode change with mode 0x%x completion timeout\n",
> > -			mode);
> > +			"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n",
> > +			cmd->command, cmd->argument3);
> >  		ret = -ETIMEDOUT;
> >  		goto out;
> >  	}
> > @@ -1823,19 +1877,62 @@ static int ufshcd_uic_change_pwr_mode(struct
> > ufs_hba *hba, u8 mode)
> >  	status = ufshcd_get_upmcrs(hba);
> >  	if (status != PWR_LOCAL) {
> >  		dev_err(hba->dev,
> > -			"pwr mode change failed, host umpcrs:0x%x\n",
> > -			status);
> > +			"pwr ctrl cmd 0x%0x failed, host umpcrs:0x%x\n",
> > +			cmd->command, status);
> >  		ret = (status != PWR_OK) ? status : -1;
> >  	}
> >  out:
> >  	spin_lock_irqsave(hba->host->host_lock, flags);
> > -	hba->pwr_done = NULL;
> > +	hba->uic_async_done = NULL;
> >  	spin_unlock_irqrestore(hba->host->host_lock, flags);
> >  	mutex_unlock(&hba->uic_cmd_mutex);
> >  	return ret;
> >  }
> >
> >  /**
> > + * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
> > + *				using DME_SET primitives.
> > + * @hba: per adapter instance
> > + * @mode: powr mode value
> > + *
> > + * Returns 0 on success, non-zero value on failure  */ static int
> > +ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) {
> > +	struct uic_command uic_cmd = {0};
> > +
> > +	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); }
> > +
> > +static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba) {
> > +	struct uic_command uic_cmd = {0};
> > +
> > +	uic_cmd.command = UIC_CMD_DME_HIBER_ENTER;
> > +
> > +	return ufshcd_uic_pwr_ctrl(hba, &uic_cmd); }
> > +
> > +static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba) {
> > +	struct uic_command uic_cmd = {0};
> > +	int ret;
> > +
> > +	uic_cmd.command = UIC_CMD_DME_HIBER_EXIT;
> > +	ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
> > +	if (ret) {
> > +		ufshcd_set_link_off(hba);
> > +		ret = ufshcd_host_reset_and_restore(hba);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> >   * ufshcd_config_max_pwr_mode - Set & Change power mode with
> >   *	maximum capability attribute information.
> >   * @hba: per adapter instance
> > @@ -2034,6 +2131,9 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
> >  		msleep(5);
> >  	}
> >
> > +	/* UniPro link is disabled at this point */
> > +	ufshcd_set_link_off(hba);
> > +
> >  	if (hba->vops && hba->vops->hce_enable_notify)
> >  		hba->vops->hce_enable_notify(hba, PRE_CHANGE);
> >
> > @@ -2066,7 +2166,7 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
> >  	}
> >
> >  	/* enable UIC related interrupts */
> > -	ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
> > +	ufshcd_enable_intr(hba, UFSHCD_UIC_MASK);
> >
> >  	if (hba->vops && hba->vops->hce_enable_notify)
> >  		hba->vops->hce_enable_notify(hba, POST_CHANGE); @@ -2195,6
> > +2295,62 @@ static void ufshcd_set_queue_depth(struct scsi_device *sdev)
> >  	scsi_activate_tcq(sdev, lun_qdepth);
> >  }
> >
> > +/*
> > + * ufshcd_get_lu_wp - returns the "b_lu_write_protect" from UNIT
> > +DESCRIPTOR
> > + * @hba: per-adapter instance
> > + * @lun: UFS device lun id
> > + * @b_lu_write_protect: pointer to buffer to hold the LU's write
> > +protect info
> > + *
> > + * Returns 0 in case of success and b_lu_write_protect status would be
> > +returned
> > + * @b_lu_write_protect parameter.
> > + * Returns -ENOTSUPP if reading b_lu_write_protect is not supported.
> > + * Returns -EINVAL in case of invalid parameters passed to this function.
> > + */
> > +static int ufshcd_get_lu_wp(struct ufs_hba *hba,
> > +			    u8 lun,
> > +			    u8 *b_lu_write_protect)
> > +{
> > +	int ret;
> > +
> > +	if (!b_lu_write_protect)
> > +		ret = -EINVAL;
> > +	/*
> > +	 * According to UFS device spec, RPMB LU can't be write
> > +	 * protected so skip reading bLUWriteProtect parameter for
> > +	 * it. For other W-LUs, UNIT DESCRIPTOR is not available.
> > +	 */
> > +	else if (lun >= UFS_UPIU_MAX_GENERAL_LUN)
> > +		ret = -ENOTSUPP;
> > +	else
> > +		ret = ufshcd_read_unit_desc_param(hba,
> > +					  lun,
> > +					  UNIT_DESC_PARAM_LU_WR_PROTECT,
> > +					  b_lu_write_protect,
> > +					  sizeof(*b_lu_write_protect));
> > +	return ret;
> > +}
> > +
> > +/**
> > + * ufshcd_get_lu_power_on_wp_status - get LU's power on write protect
> > + * status
> > + * @hba: per-adapter instance
> > + * @sdev: pointer to SCSI device
> > + *
> > + */
> > +static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba,
> > +						    struct scsi_device *sdev)
> > +{
> > +	if (hba->dev_info.f_power_on_wp_en &&
> > +	    !hba->dev_info.is_lu_power_on_wp) {
> > +		u8 b_lu_write_protect;
> > +
> > +		if (!ufshcd_get_lu_wp(hba, ufshcd_scsi_to_upiu_lun(sdev->lun),
> > +				      &b_lu_write_protect) &&
> > +		    (b_lu_write_protect == UFS_LU_POWER_ON_WP))
> > +			hba->dev_info.is_lu_power_on_wp = true;
> > +	}
> > +}
> > +
> >  /**
> >   * ufshcd_slave_alloc - handle initial SCSI device configurations
> >   * @sdev: pointer to SCSI device
> > @@ -2221,6 +2377,21 @@ static int ufshcd_slave_alloc(struct scsi_device
> > *sdev)
> >
> >  	ufshcd_set_queue_depth(sdev);
> >
> > +	ufshcd_get_lu_power_on_wp_status(hba, sdev);
> > +
> > +	/*
> > +	 * For selecting the UFS device power mode (Active / UFS_Sleep /
> > +	 * UFS_PowerDown), SCSI power management command (START STOP
> > UNIT)
> > +	 * needs to be sent to a "UFS device" Well known Logical Unit (W-LU).
> > +	 * As this command would be sent during the UFS host controller
> > +	 * runtime/system PM callbacks, we need a reference to "scsi_device"
> > +	 * associated to "UFS device" W-LU. This change saves the "scsi_device"
> > +	 * reference for "UFS device" W-LU during slave_configure() callback
> > +	 * from SCSI mid layer.
> > +	 */
> > +	if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN)
> > +		hba->sdev_ufs_device = sdev;
> > +
> >  	return 0;
> >  }
> >
> > @@ -2451,8 +2622,8 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba
> > *hba, u32 intr_status)
> >  		complete(&hba->active_uic_cmd->done);
> >  	}
> >
> > -	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
> > -		complete(hba->pwr_done);
> > +	if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done)
> > +		complete(hba->uic_async_done);
> >  }
> >
> >  /**
> > @@ -2664,33 +2835,62 @@ static inline int ufshcd_get_bkops_status(struct
> > ufs_hba *hba, u32 *status)  }
> >
> >  /**
> > - * ufshcd_urgent_bkops - handle urgent bkops exception event
> > + * ufshcd_bkops_ctrl - control the auto bkops based on current bkops
> > + status
> >   * @hba: per-adapter instance
> > + * @status: bkops_status value
> >   *
> > - * Enable fBackgroundOpsEn flag in the device to permit background
> > - * operations.
> > + * Read the bkops_status from the UFS device and Enable
> > + fBackgroundOpsEn
> > + * flag in the device to permit background operations if the device
> > + * bkops_status is greater than or equal to "status" argument passed to
> > + * this function, disable otherwise.
> > + *
> > + * Returns 0 for success, non-zero in case of failure.
> > + *
> > + * NOTE: Caller of this function can check the
> > + "hba->auto_bkops_enabled" flag
> > + * to know whether auto bkops is enabled or disabled after this
> > + function
> > + * returns control to it.
> >   */
> > -static int ufshcd_urgent_bkops(struct ufs_hba *hba)
> > +static int ufshcd_bkops_ctrl(struct ufs_hba *hba,
> > +			     enum bkops_status status)
> >  {
> >  	int err;
> > -	u32 status = 0;
> > +	u32 curr_status = 0;
> >
> > -	err = ufshcd_get_bkops_status(hba, &status);
> > +	err = ufshcd_get_bkops_status(hba, &curr_status);
> >  	if (err) {
> >  		dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
> >  				__func__, err);
> >  		goto out;
> > +	} else if (curr_status > BKOPS_STATUS_MAX) {
> > +		dev_err(hba->dev, "%s: invalid BKOPS status %d\n",
> > +				__func__, curr_status);
> > +		err = -EINVAL;
> > +		goto out;
> >  	}
> >
> > -	status = status & 0xF;
> > -
> > -	/* handle only if status indicates performance impact or critical */
> > -	if (status >= BKOPS_STATUS_PERF_IMPACT)
> > +	if (curr_status >= status)
> >  		err = ufshcd_enable_auto_bkops(hba);
> > +	else
> > +		err = ufshcd_disable_auto_bkops(hba);
> >  out:
> >  	return err;
> >  }
> >
> > +/**
> > + * ufshcd_urgent_bkops - handle urgent bkops exception event
> > + * @hba: per-adapter instance
> > + *
> > + * Enable fBackgroundOpsEn flag in the device to permit background
> > + * operations.
> > + *
> > + * If BKOPs is enabled, this function returns 0, 1 if the bkops in not
> > +enabled
> > + * and negative error value for any other failure.
> > + */
> > +static int ufshcd_urgent_bkops(struct ufs_hba *hba) {
> > +	return ufshcd_bkops_ctrl(hba, BKOPS_STATUS_PERF_IMPACT); }
> > +
> >  static inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)  {
> >  	return ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, @@
> > -2722,7 +2922,7 @@ static void ufshcd_exception_event_handler(struct
> > work_struct *work)
> >  	status &= hba->ee_ctrl_mask;
> >  	if (status & MASK_EE_URGENT_BKOPS) {
> >  		err = ufshcd_urgent_bkops(hba);
> > -		if (err)
> > +		if (err < 0)
> >  			dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
> >  					__func__, err);
> >  	}
> > @@ -3441,7 +3641,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
> >  	if (ret)
> >  		goto out;
> >
> > -	ufshcd_config_max_pwr_mode(hba);
> > +	/* UniPro link is active now */
> > +	ufshcd_set_link_active(hba);
> >
> >  	ret = ufshcd_verify_dev_init(hba);
> >  	if (ret)
> > @@ -3451,14 +3652,29 @@ static int ufshcd_probe_hba(struct ufs_hba
> *hba)
> >  	if (ret)
> >  		goto out;
> >
> > +	/* UFS device is also active now */
> > +	ufshcd_set_ufs_dev_active(hba);
> >  	ufshcd_force_reset_auto_bkops(hba);
> >  	hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
> > +	hba->wlun_dev_clr_ua = true;
> > +
> > +	ufshcd_config_max_pwr_mode(hba);
> > +
> > +	/*
> > +	 * If we are in error handling context or in power management callbacks
> > +	 * context, no need to scan the host
> > +	 */
> > +	if (!ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) {
> > +		bool flag;
> > +
> > +		/* clear any previous UFS device information */
> > +		memset(&hba->dev_info, 0, sizeof(hba->dev_info));
> > +		if (!ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG,
> > +				       QUERY_FLAG_IDN_PWR_ON_WPE, &flag))
> > +			hba->dev_info.f_power_on_wp_en = flag;
> >
> > -	/* If we are in error handling context no need to scan the host */
> > -	if (!ufshcd_eh_in_progress(hba)) {
> >  		if (!hba->is_init_prefetch)
> >  			ufshcd_init_icc_levels(hba);
> > -
> >  		scsi_scan_host(hba->host);
> >  		pm_runtime_put_sync(hba->dev);
> >  	}
> > @@ -3471,8 +3687,10 @@ out:
> >  	 * 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))
> > +	if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) {
> > +		pm_runtime_put_sync(hba->dev);
> >  		ufshcd_hba_exit(hba);
> > +	}
> >
> >  	return ret;
> >  }
> > @@ -3507,6 +3725,42 @@ static struct scsi_host_template
> > ufshcd_driver_template = {
> >  	.can_queue		= UFSHCD_CAN_QUEUE,
> >  };
> >
> > +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);
> 
> For the UFS host controller driver, which doesn't have the ufs_vreg structure for
> vcc and vccq, this function can cause kernel panic due to this BUG_ON(). Any
> reason to put a BUG_ON here instead of a NULL pointer checking?
> 
> Thanks
> Chuanxiao
> 
> > +
> > +	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);
> > +	}
> > +
> > +	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);
> > +}
> > +
> > +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); }
> > +
> >  static int ufshcd_config_vreg(struct device *dev,
> >  		struct ufs_vreg *vreg, bool on)
> >  {
> > @@ -3527,18 +3781,9 @@ static int ufshcd_config_vreg(struct device *dev,
> >  		}
> >
> >  		uA_load = on ? vreg->max_uA : 0;
> > -		ret = regulator_set_optimum_mode(reg, uA_load);
> > -		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_load=%d) failed,
> > err=%d\n",
> > -					__func__, name, uA_load, ret);
> > +		ret = ufshcd_config_vreg_load(dev, vreg, uA_load);
> > +		if (ret)
> >  			goto out;
> > -		}
> >  	}
> >  out:
> >  	return ret;
> > @@ -3654,7 +3899,8 @@ out:
> >  	return ret;
> >  }
> >
> > -static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on)
> > +static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
> > +				 bool skip_ref_clk)
> >  {
> >  	int ret = 0;
> >  	struct ufs_clk_info *clki;
> > @@ -3665,6 +3911,9 @@ static int ufshcd_setup_clocks(struct ufs_hba
> *hba,
> > bool on)
> >
> >  	list_for_each_entry(clki, head, list) {
> >  		if (!IS_ERR_OR_NULL(clki->clk)) {
> > +			if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
> > +				continue;
> > +
> >  			if (on && !clki->enabled) {
> >  				ret = clk_prepare_enable(clki->clk);
> >  				if (ret) {
> > @@ -3690,6 +3939,11 @@ out:
> >  	return ret;
> >  }
> >
> > +static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on) {
> > +	return  __ufshcd_setup_clocks(hba, on, false); }
> > +
> >  static int ufshcd_init_clocks(struct ufs_hba *hba)  {
> >  	int ret = 0;
> > @@ -3828,68 +4082,514 @@ static void ufshcd_hba_exit(struct ufs_hba
> *hba)
> >  	}
> >  }
> >
> > +static int
> > +ufshcd_send_request_sense(struct ufs_hba *hba, struct scsi_device *sdp)
> > +{
> > +	unsigned char cmd[6] = {REQUEST_SENSE,
> > +				0,
> > +				0,
> > +				0,
> > +				SCSI_SENSE_BUFFERSIZE,
> > +				0};
> > +	char *buffer;
> > +	int ret;
> > +
> > +	buffer = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
> > +	if (!buffer) {
> > +		ret = -ENOMEM;
> > +		goto out;
> > +	}
> > +
> > +	ret = scsi_execute_req_flags(sdp, cmd, DMA_FROM_DEVICE, buffer,
> > +				SCSI_SENSE_BUFFERSIZE, NULL,
> > +				msecs_to_jiffies(1000), 3, NULL, REQ_PM);
> > +	if (ret)
> > +		pr_err("%s: failed with err %d\n", __func__, ret);
> > +
> > +	kfree(buffer);
> > +out:
> > +	return ret;
> > +}
> > +
> >  /**
> > - * ufshcd_suspend - suspend power management function
> > + * ufshcd_set_dev_pwr_mode - sends START STOP UNIT command to set
> > device
> > + *			     power mode
> >   * @hba: per adapter instance
> > - * @state: power state
> > + * @pwr_mode: device power mode to set
> >   *
> > - * Returns -ENOSYS
> > + * Returns 0 if requested power mode is set successfully
> > + * Returns non-zero if failed to set the requested power mode
> >   */
> > -int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state)
> > +static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
> > +				     enum ufs_dev_pwr_mode pwr_mode)
> >  {
> > +	unsigned char cmd[6] = { START_STOP };
> > +	struct scsi_sense_hdr sshdr;
> > +	struct scsi_device *sdp = hba->sdev_ufs_device;
> > +	int ret;
> > +
> > +	if (!sdp || !scsi_device_online(sdp))
> > +		return -ENODEV;
> > +
> > +	/*
> > +	 * If scsi commands fail, the scsi mid-layer schedules scsi error-
> > +	 * handling, which would wait for host to be resumed. Since we know
> > +	 * we are functional while we are here, skip host resume in error
> > +	 * handling context.
> > +	 */
> > +	hba->host->eh_noresume = 1;
> > +	if (hba->wlun_dev_clr_ua) {
> > +		ret = ufshcd_send_request_sense(hba, sdp);
> > +		if (ret)
> > +			goto out;
> > +		/* Unit attention condition is cleared now */
> > +		hba->wlun_dev_clr_ua = false;
> > +	}
> > +
> > +	cmd[4] = pwr_mode << 4;
> > +
> >  	/*
> > -	 * TODO:
> > -	 * 1. Block SCSI requests from SCSI midlayer
> > -	 * 2. Change the internal driver state to non operational
> > -	 * 3. Set UTRLRSR and UTMRLRSR bits to zero
> > -	 * 4. Wait until outstanding commands are completed
> > -	 * 5. Set HCE to zero to send the UFS host controller to reset state
> > +	 * Current function would be generally called from the power
> > management
> > +	 * callbacks hence set the REQ_PM flag so that it doesn't resume the
> > +	 * already suspended childs.
> >  	 */
> > +	ret = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
> > +				     START_STOP_TIMEOUT, 0, NULL, REQ_PM);
> > +	if (ret) {
> > +		sdev_printk(KERN_WARNING, sdp,
> > +			  "START_STOP failed for power mode: %d\n", pwr_mode);
> > +		scsi_show_result(ret);
> > +		if (driver_byte(ret) & DRIVER_SENSE) {
> > +			scsi_show_sense_hdr(&sshdr);
> > +			scsi_show_extd_sense(sshdr.asc, sshdr.ascq);
> > +		}
> > +	}
> >
> > -	return -ENOSYS;
> > +	if (!ret)
> > +		hba->curr_dev_pwr_mode = pwr_mode;
> > +out:
> > +	hba->host->eh_noresume = 0;
> > +	return ret;
> > +}
> > +
> > +static int ufshcd_link_state_transition(struct ufs_hba *hba,
> > +					enum uic_link_state req_link_state,
> > +					int check_for_bkops)
> > +{
> > +	int ret = 0;
> > +
> > +	if (req_link_state == hba->uic_link_state)
> > +		return 0;
> > +
> > +	if (req_link_state == UIC_LINK_HIBERN8_STATE) {
> > +		ret = ufshcd_uic_hibern8_enter(hba);
> > +		if (!ret)
> > +			ufshcd_set_link_hibern8(hba);
> > +		else
> > +			goto out;
> > +	}
> > +	/*
> > +	 * If autobkops is enabled, link can't be turned off because
> > +	 * turning off the link would also turn off the device.
> > +	 */
> > +	else if ((req_link_state == UIC_LINK_OFF_STATE) &&
> > +		   (!check_for_bkops || (check_for_bkops &&
> > +		    !hba->auto_bkops_enabled))) {
> > +		/*
> > +		 * Change controller state to "reset state" which
> > +		 * should also put the link in off/reset state
> > +		 */
> > +		ufshcd_hba_stop(hba);
> > +		/*
> > +		 * TODO: Check if we need any delay to make sure that
> > +		 * controller is reset
> > +		 */
> > +		ufshcd_set_link_off(hba);
> > +	}
> > +
> > +out:
> > +	return ret;
> > +}
> > +
> > +static void ufshcd_vreg_set_lpm(struct ufs_hba *hba) {
> > +	/*
> > +	 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
> > +	 * power.
> > +	 *
> > +	 * If UFS device and link is in OFF state, all power supplies (VCC,
> > +	 * VCCQ, VCCQ2) can be turned off if power on write protect is not
> > +	 * required. If UFS link is inactive (Hibern8 or OFF state) and device
> > +	 * is in sleep state, put VCCQ & VCCQ2 rails in LPM mode.
> > +	 *
> > +	 * Ignore the error returned by ufshcd_toggle_vreg() as device is anyway
> > +	 * in low power state which would save some power.
> > +	 */
> > +	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
> > +	    !hba->dev_info.is_lu_power_on_wp) {
> > +		ufshcd_setup_vreg(hba, false);
> > +	} else if (!ufshcd_is_ufs_dev_active(hba)) {
> > +		ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
> > +		if (!ufshcd_is_link_active(hba)) {
> > +			ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
> > +			ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2);
> > +		}
> > +	}
> > +}
> > +
> > +static int ufshcd_vreg_set_hpm(struct ufs_hba *hba) {
> > +	int ret = 0;
> > +
> > +	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
> > +	    !hba->dev_info.is_lu_power_on_wp) {
> > +		ret = ufshcd_setup_vreg(hba, true);
> > +	} else if (!ufshcd_is_ufs_dev_active(hba)) {
> > +		ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
> > +		if (!ret && !ufshcd_is_link_active(hba)) {
> > +			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
> > +			if (ret)
> > +				goto vcc_disable;
> > +			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
> > +			if (ret)
> > +				goto vccq_lpm;
> > +		}
> > +	}
> > +	goto out;
> > +
> > +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;
> >  }
> > -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
> >   *
> > - * Returns -ENOSYS
> > + * 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).
> > + *
> > + * If this function is called during shutdown, it will make sure that
> > + * both UFS device and UFS link is powered off.
> > + *
> > + * NOTE: UFS device & link must be active before we enter in this function.
> > + *
> > + * Returns 0 for success and non-zero for failure
> >   */
> > -int ufshcd_resume(struct ufs_hba *hba)
> > +static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
> >  {
> > +	int ret = 0;
> > +	enum ufs_pm_level pm_lvl;
> > +	enum ufs_dev_pwr_mode req_dev_pwr_mode;
> > +	enum uic_link_state req_link_state;
> > +
> > +	hba->pm_op_in_progress = 1;
> > +	if (!ufshcd_is_shutdown_pm(pm_op)) {
> > +		pm_lvl = ufshcd_is_runtime_pm(pm_op) ?
> > +			 hba->rpm_lvl : hba->spm_lvl;
> > +		req_dev_pwr_mode = ufs_get_pm_lvl_to_dev_pwr_mode(pm_lvl);
> > +		req_link_state = ufs_get_pm_lvl_to_link_pwr_state(pm_lvl);
> > +	} else {
> > +		req_dev_pwr_mode = UFS_POWERDOWN_PWR_MODE;
> > +		req_link_state = UIC_LINK_OFF_STATE;
> > +	}
> > +
> > +	/*
> > +	 * If we can't transition into any of the low power modes
> > +	 * just gate the clocks.
> > +	 */
> > +	if (req_dev_pwr_mode == UFS_ACTIVE_PWR_MODE &&
> > +			req_link_state == UIC_LINK_ACTIVE_STATE) {
> > +		goto disable_clks;
> > +	}
> > +
> > +	if ((req_dev_pwr_mode == hba->curr_dev_pwr_mode) &&
> > +	    (req_link_state == hba->uic_link_state))
> > +		goto out;
> > +
> > +	/* UFS device & link must be active before we enter in this function */
> > +	if (!ufshcd_is_ufs_dev_active(hba) || !ufshcd_is_link_active(hba)) {
> > +		ret = -EINVAL;
> > +		goto out;
> > +	}
> > +
> > +	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 out;
> > +	}
> > +
> > +	if ((req_dev_pwr_mode != hba->curr_dev_pwr_mode) &&
> > +	     ((ufshcd_is_runtime_pm(pm_op) && !hba->auto_bkops_enabled) ||
> > +	       !ufshcd_is_runtime_pm(pm_op))) {
> > +		/* ensure that bkops is disabled */
> > +		ufshcd_disable_auto_bkops(hba);
> > +		ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode);
> > +		if (ret)
> > +			goto out;
> > +	}
> > +
> > +	ret = ufshcd_link_state_transition(hba, req_link_state, 1);
> > +	if (ret)
> > +		goto set_dev_active;
> > +
> > +	ufshcd_vreg_set_lpm(hba);
> > +
> > +disable_clks:
> > +	/*
> > +	 * Call vendor specific suspend callback. As these callbacks may access
> > +	 * vendor specific host controller register space call them before the
> > +	 * host clocks are ON.
> > +	 */
> > +	if (hba->vops && hba->vops->suspend) {
> > +		ret = hba->vops->suspend(hba, pm_op);
> > +		if (ret)
> > +			goto set_link_active;
> > +	}
> > +
> > +	if (hba->vops && hba->vops->setup_clocks) {
> > +		ret = hba->vops->setup_clocks(hba, false);
> > +		if (ret)
> > +			goto vops_resume;
> > +	}
> > +
> > +	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);
> > +
> >  	/*
> > -	 * TODO:
> > -	 * 1. Set HCE to 1, to start the UFS host controller
> > -	 * initialization process
> > -	 * 2. Set UTRLRSR and UTMRLRSR bits to 1
> > -	 * 3. Change the internal driver state to operational
> > -	 * 4. Unblock SCSI requests from SCSI midlayer
> > +	 * Disable the host irq as host controller as there won't be any
> > +	 * host controller trasanction expected till resume.
> >  	 */
> > +	ufshcd_disable_irq(hba);
> > +	goto out;
> >
> > -	return -ENOSYS;
> > +vops_resume:
> > +	if (hba->vops && hba->vops->resume)
> > +		hba->vops->resume(hba, pm_op);
> > +set_link_active:
> > +	ufshcd_vreg_set_hpm(hba);
> > +	if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba))
> > +		ufshcd_set_link_active(hba);
> > +	else if (ufshcd_is_link_off(hba))
> > +		ufshcd_host_reset_and_restore(hba);
> > +set_dev_active:
> > +	if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE))
> > +		ufshcd_disable_auto_bkops(hba);
> > +out:
> > +	hba->pm_op_in_progress = 0;
> > +	return ret;
> >  }
> > -EXPORT_SYMBOL_GPL(ufshcd_resume);
> >
> > -int ufshcd_runtime_suspend(struct ufs_hba *hba)
> > +/**
> > + * ufshcd_resume - helper function for resume operations
> > + * @hba: per adapter instance
> > + * @pm_op: runtime PM or system PM
> > + *
> > + * This function basically brings the UFS device, UniPro link and
> > +controller
> > + * to active state.
> > + *
> > + * Returns 0 for success and non-zero for failure  */ static int
> > +ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
> >  {
> > -	if (!hba)
> > -		return 0;
> > +	int ret;
> > +	enum uic_link_state old_link_state;
> > +
> > +	hba->pm_op_in_progress = 1;
> > +	old_link_state = hba->uic_link_state;
> > +	/* Make sure clocks are enabled before accessing controller */
> > +	ret = ufshcd_setup_clocks(hba, true);
> > +	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);
> > +
> > +	ret = ufshcd_vreg_set_hpm(hba);
> > +	if (ret)
> > +		goto disable_irq_and_vops_clks;
> >
> >  	/*
> > -	 * The device is idle with no requests in the queue,
> > -	 * allow background operations.
> > +	 * Call vendor specific resume callback. As these callbacks may access
> > +	 * vendor specific host controller register space call them when the
> > +	 * host clocks are ON.
> >  	 */
> > -	return ufshcd_enable_auto_bkops(hba);
> > +	if (hba->vops && hba->vops->resume) {
> > +		ret = hba->vops->resume(hba, pm_op);
> > +		if (ret)
> > +			goto disable_vreg;
> > +	}
> > +
> > +	if (ufshcd_is_link_hibern8(hba)) {
> > +		ret = ufshcd_uic_hibern8_exit(hba);
> > +		if (!ret)
> > +			ufshcd_set_link_active(hba);
> > +		else
> > +			goto vendor_suspend;
> > +	} else if (ufshcd_is_link_off(hba)) {
> > +		ret = ufshcd_host_reset_and_restore(hba);
> > +		/*
> > +		 * ufshcd_host_reset_and_restore() should have already
> > +		 * set the link state as active
> > +		 */
> > +		if (ret || !ufshcd_is_link_active(hba))
> > +			goto vendor_suspend;
> > +	}
> > +
> > +	if (!ufshcd_is_ufs_dev_active(hba)) {
> > +		ret = ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE);
> > +		if (ret)
> > +			goto set_old_link_state;
> > +	}
> > +
> > +	ufshcd_disable_auto_bkops(hba);

Should be auto background operation enabled again in ufshcd_resume()? It is disabled during ufshcd_suspend().

Thanks
Chuanxiao

> > +	goto out;
> > +
> > +set_old_link_state:
> > +	ufshcd_link_state_transition(hba, old_link_state, 0);
> > +vendor_suspend:
> > +	if (hba->vops && hba->vops->suspend)
> > +		hba->vops->suspend(hba, pm_op);
> > +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;
> > +	return ret;
> > +}
> > +
> > +/**
> > + * ufshcd_system_suspend - system suspend routine
> > + * @hba: per adapter instance
> > + * @pm_op: runtime PM or system PM
> > + *
> > + * Check the description of ufshcd_suspend() function for more details.
> > + *
> > + * Returns 0 for success and non-zero for failure  */ int
> > +ufshcd_system_suspend(struct ufs_hba *hba) {
> > +	int ret = 0;
> > +
> > +	if (!hba || !hba->is_powered)
> > +		goto out;
> > +
> > +	if (pm_runtime_suspended(hba->dev)) {
> > +		if (hba->rpm_lvl == hba->spm_lvl)
> > +			/*
> > +			 * There is possibility that device may still be in
> > +			 * active state during the runtime suspend.
> > +			 */
> > +			if ((ufs_get_pm_lvl_to_dev_pwr_mode(hba->spm_lvl) ==
> > +			    hba->curr_dev_pwr_mode)
> > && !hba->auto_bkops_enabled)
> > +				goto out;
> > +
> > +		/*
> > +		 * UFS device and/or UFS link low power states during runtime
> > +		 * suspend seems to be different than what is expected during
> > +		 * system suspend. Hence runtime resume the devic & link and
> > +		 * let the system suspend low power states to take effect.
> > +		 * TODO: If resume takes longer time, we might have optimize
> > +		 * it in future by not resuming everything if possible.
> > +		 */
> > +		ret = ufshcd_runtime_resume(hba);
> > +		if (ret)
> > +			goto out;
> > +	}
> > +
> > +	ret = ufshcd_suspend(hba, UFS_SYSTEM_PM);
> > +out:
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(ufshcd_system_suspend);
> > +
> > +/**
> > + * ufshcd_system_resume - system resume routine
> > + * @hba: per adapter instance
> > + *
> > + * Returns 0 for success and non-zero for failure  */
> > +
> > +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
> > +		return ufshcd_resume(hba, UFS_SYSTEM_PM); }
> > +EXPORT_SYMBOL(ufshcd_system_resume);
> > +
> > +/**
> > + * ufshcd_runtime_suspend - runtime suspend routine
> > + * @hba: per adapter instance
> > + *
> > + * 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
> > +		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:
> > + *
> > + * 1. Turn on all the controller related clocks
> > + * 2. Bring the UniPro link out of Hibernate state
> > + * 3. If UFS device is in sleep state, turn ON VCC rail and bring the UFS device
> > + *    to active state.
> > + * 4. If auto-bkops is enabled on the device, disable it.
> > + *
> > + * So following would be the possible power state after this function
> > +return
> > + * successfully:
> > + *	S1: UFS device in Active state with VCC rail ON
> > + *	    UniPro link in Active state
> > + *	    All the UFS/UniPro controller clocks are ON
> > + *
> > + * Returns 0 for success and non-zero for failure  */
> >  int ufshcd_runtime_resume(struct ufs_hba *hba)  {
> > -	if (!hba)
> > +	if (!hba || !hba->is_powered)
> >  		return 0;
> > -
> > -	return ufshcd_disable_auto_bkops(hba);
> > +	else
> > +		return ufshcd_resume(hba, UFS_RUNTIME_PM);
> >  }
> >  EXPORT_SYMBOL(ufshcd_runtime_resume);
> >
> > @@ -3900,6 +4600,36 @@ int ufshcd_runtime_idle(struct ufs_hba *hba)
> > EXPORT_SYMBOL(ufshcd_runtime_idle);
> >
> >  /**
> > + * ufshcd_shutdown - shutdown routine
> > + * @hba: per adapter instance
> > + *
> > + * This function would power off both UFS device and UFS link.
> > + *
> > + * Returns 0 always to allow force shutdown even in case of errors.
> > + */
> > +int ufshcd_shutdown(struct ufs_hba *hba) {
> > +	int ret = 0;
> > +
> > +	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba))
> > +		goto out;
> > +
> > +	if (pm_runtime_suspended(hba->dev)) {
> > +		ret = ufshcd_runtime_resume(hba);
> > +		if (ret)
> > +			goto out;
> > +	}
> > +
> > +	ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM);
> > +out:
> > +	if (ret)
> > +		dev_err(hba->dev, "%s failed, err %d\n", __func__, ret);
> > +	/* allow force shutdown even in case of errors */
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(ufshcd_shutdown);
> > +
> > +/**
> >   * ufshcd_remove - de-allocate SCSI host and host memory space
> >   *		data structure memory
> >   * @hba - per adapter instance
> > @@ -4052,6 +4782,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem
> > *mmio_base, unsigned int irq)
> >  	if (err) {
> >  		dev_err(hba->dev, "request irq failed\n");
> >  		goto out_disable;
> > +	} else {
> > +		hba->is_irq_enabled = true;
> >  	}
> >
> >  	/* Enable SCSI tag mapping */
> > @@ -4077,6 +4809,12 @@ int ufshcd_init(struct ufs_hba *hba, void
> __iomem
> > *mmio_base, unsigned int irq)
> >  	/* Hold auto suspend until async scan completes */
> >  	pm_runtime_get_sync(dev);
> >
> > +	/*
> > +	 * The device-initialize-sequence hasn't been invoked yet.
> > +	 * Set the device to power-off state
> > +	 */
> > +	ufshcd_set_ufs_dev_poweroff(hba);
> > +
> >  	async_schedule(ufshcd_async_scan, hba);
> >
> >  	return 0;
> > @@ -4084,6 +4822,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem
> > *mmio_base, unsigned int irq)
> >  out_remove_scsi_host:
> >  	scsi_remove_host(hba->host);
> >  out_disable:
> > +	hba->is_irq_enabled = false;
> >  	scsi_host_put(host);
> >  	ufshcd_hba_exit(hba);
> >  out_error:
> > diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index
> > 9b5f77f..1ee429d 100644
> > --- a/drivers/scsi/ufs/ufshcd.h
> > +++ b/drivers/scsi/ufs/ufshcd.h
> > @@ -96,6 +96,54 @@ struct uic_command {
> >  	struct completion done;
> >  };
> >
> > +/* Used to differentiate the power management options */ enum
> ufs_pm_op
> > +{
> > +	UFS_RUNTIME_PM,
> > +	UFS_SYSTEM_PM,
> > +	UFS_SHUTDOWN_PM,
> > +};
> > +
> > +#define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM) #define
> > +ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM) #define
> > +ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM)
> > +
> > +/* Host <-> Device UniPro Link state */ enum uic_link_state {
> > +	UIC_LINK_OFF_STATE	= 0, /* Link powered down or disabled */
> > +	UIC_LINK_ACTIVE_STATE	= 1, /* Link is in Fast/Slow/Sleep state */
> > +	UIC_LINK_HIBERN8_STATE	= 2, /* Link is in Hibernate state */
> > +};
> > +
> > +#define ufshcd_is_link_off(hba) ((hba)->uic_link_state ==
> > +UIC_LINK_OFF_STATE) #define ufshcd_is_link_active(hba)
> > ((hba)->uic_link_state == \
> > +				    UIC_LINK_ACTIVE_STATE)
> > +#define ufshcd_is_link_hibern8(hba) ((hba)->uic_link_state == \
> > +				    UIC_LINK_HIBERN8_STATE)
> > +#define ufshcd_set_link_off(hba) ((hba)->uic_link_state =
> > +UIC_LINK_OFF_STATE) #define ufshcd_set_link_active(hba)
> > ((hba)->uic_link_state = \
> > +				    UIC_LINK_ACTIVE_STATE)
> > +#define ufshcd_set_link_hibern8(hba) ((hba)->uic_link_state = \
> > +				    UIC_LINK_HIBERN8_STATE)
> > +
> > +/*
> > + * UFS Power management levels.
> > + * Each level is in increasing order of power savings.
> > + */
> > +enum ufs_pm_level {
> > +	UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE
> > */
> > +	UFS_PM_LVL_1, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE
> > */
> > +	UFS_PM_LVL_2, /* UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE */
> > +	UFS_PM_LVL_3, /* UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE
> > */
> > +	UFS_PM_LVL_4, /* UFS_POWERDOWN_PWR_MODE,
> > UIC_LINK_HIBERN8_STATE */
> > +	UFS_PM_LVL_5, /* UFS_POWERDOWN_PWR_MODE,
> > UIC_LINK_OFF_STATE */
> > +	UFS_PM_LVL_MAX
> > +};
> > +
> > +struct ufs_pm_lvl_states {
> > +	enum ufs_dev_pwr_mode dev_state;
> > +	enum uic_link_state link_state;
> > +};
> > +
> >  /**
> >   * struct ufshcd_lrb - local reference block
> >   * @utr_descriptor_ptr: UTRD address of the command @@ -184,6
> +232,8
> > @@ 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.
> > + * @suspend: called during host controller PM callback
> > + * @resume: called during host controller PM callback
> >   */
> >  struct ufs_hba_variant_ops {
> >  	const char *name;
> > @@ -193,6 +243,8 @@ 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     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
> > +	int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
> >  };
> >
> >  /**
> > @@ -271,6 +323,13 @@ struct ufs_hba {
> >  	 * "UFS device" W-LU.
> >  	 */
> >  	struct scsi_device *sdev_ufs_device;
> > +	enum ufs_dev_pwr_mode curr_dev_pwr_mode;
> > +	enum uic_link_state uic_link_state;
> > +	/* Desired UFS power management level during runtime PM */
> > +	enum ufs_pm_level rpm_lvl;
> > +	/* Desired UFS power management level during system PM */
> > +	enum ufs_pm_level spm_lvl;
> > +	int pm_op_in_progress;
> >
> >  	struct ufshcd_lrb *lrb;
> >  	unsigned long lrb_in_use;
> > @@ -285,16 +344,17 @@ struct ufs_hba {
> >  	struct ufs_hba_variant_ops *vops;
> >  	void *priv;
> >  	unsigned int irq;
> > +	bool is_irq_enabled;
> >
> > -	struct uic_command *active_uic_cmd;
> > -	struct mutex uic_cmd_mutex;
> >
> >  	wait_queue_head_t tm_wq;
> >  	wait_queue_head_t tm_tag_wq;
> >  	unsigned long tm_condition;
> >  	unsigned long tm_slots_in_use;
> >
> > -	struct completion *pwr_done;
> > +	struct uic_command *active_uic_cmd;
> > +	struct mutex uic_cmd_mutex;
> > +	struct completion *uic_async_done;
> >
> >  	u32 ufshcd_state;
> >  	u32 eh_flags;
> > @@ -317,9 +377,13 @@ struct ufs_hba {
> >  	/* Device management request data */
> >  	struct ufs_dev_cmd dev_cmd;
> >
> > +	/* Keeps information of the UFS device connected to this host */
> > +	struct ufs_dev_info dev_info;
> >  	bool auto_bkops_enabled;
> >  	struct ufs_vreg_info vreg_info;
> >  	struct list_head clk_list_head;
> > +
> > +	bool wlun_dev_clr_ua;
> >  };
> >
> >  #define ufshcd_writel(hba, val, reg)	\
> > @@ -346,11 +410,12 @@ static inline void check_upiu_size(void)
> >  		GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE);  }
> >
> > -extern int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state);
> > -extern int ufshcd_resume(struct ufs_hba *hba);  extern int
> > ufshcd_runtime_suspend(struct ufs_hba *hba);  extern int
> > ufshcd_runtime_resume(struct ufs_hba *hba);  extern int
> > ufshcd_runtime_idle(struct ufs_hba *hba);
> > +extern int ufshcd_system_suspend(struct ufs_hba *hba); extern int
> > +ufshcd_system_resume(struct ufs_hba *hba); extern int
> > +ufshcd_shutdown(struct ufs_hba *hba);
> >  extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
> >  			       u8 attr_set, u32 mib_val, u8 peer);  extern int
> > ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, diff --git
> > a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index
> e1b844b..d572119
> > 100644
> > --- a/drivers/scsi/ufs/ufshci.h
> > +++ b/drivers/scsi/ufs/ufshci.h
> > @@ -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,
> > --
> > 1.8.5.2
> > --
> > QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a
> > member of Code Aurora Forum, hosted by The Linux Foundation
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body
> > of a message to majordomo@xxxxxxxxxxxxxxx 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 majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux