RE: [PATCH v3 5/6] scsi: ufs: add operation for the uic power mode change

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

 



On Tue, August 27, 2013, Subhash Jadavani wrote:
> On 8/27/2013 4:58 PM, Seungwon Jeon wrote:
> > On Tue, August 27, 2013, Subhash Jadavani wrote:
> >> On 8/26/2013 8:10 PM, Seungwon Jeon wrote:
> >>> Setting PA_PWRMode using DME_SET triggers the power mode
> >>> change. And then the result will be given by the HCS.UPMCRS.
> >>> This operation should be done atomically.
> >>>
> >>> Signed-off-by: Seungwon Jeon <tgih.jun@xxxxxxxxxxx>
> >>> ---
> >>>    drivers/scsi/ufs/ufshcd.c |   84 ++++++++++++++++++++++++++++++++++++++++++--
> >>>    drivers/scsi/ufs/ufshcd.h |    3 ++
> >>>    drivers/scsi/ufs/ufshci.h |   12 ++++++
> >>>    3 files changed, 95 insertions(+), 4 deletions(-)
> >>>
> >>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> >>> index 00bfc1a..c67118d 100644
> >>> --- a/drivers/scsi/ufs/ufshcd.c
> >>> +++ b/drivers/scsi/ufs/ufshcd.c
> >>> @@ -36,9 +36,11 @@
> >>>    #include <linux/async.h>
> >>>
> >>>    #include "ufshcd.h"
> >>> +#include "unipro.h"
> >>>
> >>>    #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
> >>> @@ -520,6 +522,18 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
> >>>    }
> >>>
> >>>    /**
> >>> + * ufshcd_get_upmcrs - Get the power mode change request status
> >>> + * @hba: Pointer to adapter instance
> >>> + *
> >>> + * This function gets the UPMCRS field of HCS register
> >>> + * Returns value of UPMCRS field
> >>> + */
> >>> +static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
> >>> +{
> >>> +	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
> >>> +}
> >>> +
> >>> +/**
> >>>     * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
> >>>     * @hba: per adapter instance
> >>>     * @uic_cmd: UIC command
> >>> @@ -1526,6 +1540,64 @@ out:
> >>>    EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
> >>>
> >>>    /**
> >>> + * 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
> >>> + */
> >>> +int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
> >>> +{
> >>> +	struct uic_command uic_cmd = {0};
> >>> +	struct completion pwr_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);
> >>> +
> >>> +	spin_lock_irqsave(hba->host->host_lock, flags);
> >>> +	hba->pwr_done = &pwr_done;
> >>> +	spin_unlock_irqrestore(hba->host->host_lock, flags);
> >>> +	ret = __ufshcd_send_uic_cmd(hba, &uic_cmd);
> >>> +	if (ret) {
> >>> +		dev_err(hba->dev,
> >>> +			"pwr mode change with mode 0x%x uic error %d\n",
> >>> +			mode, ret);
> >>> +		goto out;
> >>> +	}
> >>> +
> >>> +	if (!wait_for_completion_timeout(hba->pwr_done,
> >>> +					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
> >>> +		dev_err(hba->dev,
> >>> +			"pwr mode change with mode 0x%x completion timeout\n",
> >>> +			mode);
> >>> +		ret = -ETIMEDOUT;
> >>> +		goto out;
> >>> +	}
> >>> +
> >>> +	status = ufshcd_get_upmcrs(hba);
> >> I have a doubt on the relation between UIC Power Mode Status (UPMS) bit
> >> & UIC Power Mode Change Request Status (UPMCRS) field.
> >>
> >> Once we send the DME_SET(PA_PWRMODE) command, we will get the UIC
> >> command completion interrupt (with IS.UCCS bit set) which just indicate
> >> that the attribute is written but power mode change procedure will
> >> complete only after some delay which is indicated via IS.UPMS status bit
> >> and then we would read the HCS.UPMCRS field to know the status of power
> >> mode change.
> >>
> >> This is my exact doubt: Currently UFSHCD driver first clears the IS.UPMS
> >> bit (ufshcd_intr() first clears the current interrupts in
> >> REG_INTERRUPT_STATUS and then calls the ufshcd_sl_intr() handler, and in
> >> your current patch we are anyway not reading the UPMCRS field from
> >> interrupt contex ) and then read the HCS.UPMCRS field. Will HCS.UPMCRS
> >> field retain its status value even after IS.UPMS bit is cleared? UFSHCI
> >> specification doesn't clearly mention this so either we have to get the
> >> clarity from JEDEC or from your ufs host controller vendor on the
> >> behaviour. I am checking with our ufs host controller vendor as well and
> >> will let you know what is the expected behaviour. BTW, i guess you might
> >> have already tested this patch to be working on your UFS host controller?
> > Hi Subhash,
> >
> > Yes, It's already tested and works fine.
> > IS.UPMS is a indication to inform the result of 'power mode change'(UPMCRS).
> > So clearing the interrupt would not affect this result.
> Thanks for clarifying.
> 
> > Generally it seems not common idea.
> 
> Infact on SDHCi controller, there is a case where "auto-cmd error
> status" register fields are only valid when the "auto-cmd error"
> interrupt bit is set in interrupt status register. Clearing status bit
> also clears the "auto-cmd error status" register fields automatically.
> So i was in doubt whether similar could be the case here or not.

It's interesting thing to me.

> 
> > Anyway, if your host has different behavior, please let me know.
> 
> Sure, checking on this. Would let you know soon. BTW, UFSHCI
> specification doesn't mention it clearly so it could be implementation
> dependent as well and if it is the case, we might have to change it
> later to accomodate the different implementation. So for now, you have
> my Reviewed-by: Subhash Jadavani <subhashj@xxxxxxxxxxxxxx>

Thank you for review.

Seungwon Jeon

> 
> >
> > Thanks,
> > Seungwon Jeon
> >
> >>> +	if (status != PWR_LOCAL) {
> >>> +		dev_err(hba->dev,
> >>> +			"pwr mode change failed, host umpcrs:0x%x\n",
> >>> +			status);
> >>> +		ret = (status != PWR_OK) ? status : -1;
> >>> +	}
> >>> +out:
> >>> +	spin_lock_irqsave(hba->host->host_lock, flags);
> >>> +	hba->pwr_done = NULL;
> >>> +	spin_unlock_irqrestore(hba->host->host_lock, flags);
> >>> +	mutex_unlock(&hba->uic_cmd_mutex);
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +/**
> >>>     * ufshcd_complete_dev_init() - checks device readiness
> >>>     * hba: per-adapter instance
> >>>     *
> >>> @@ -1992,16 +2064,20 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
> >>>    /**
> >>>     * ufshcd_uic_cmd_compl - handle completion of uic command
> >>>     * @hba: per adapter instance
> >>> + * @intr_status: interrupt status generated by the controller
> >>>     */
> >>> -static void ufshcd_uic_cmd_compl(struct ufs_hba *hba)
> >>> +static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
> >>>    {
> >>> -	if (hba->active_uic_cmd) {
> >>> +	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
> >>>    		hba->active_uic_cmd->argument2 |=
> >>>    			ufshcd_get_uic_cmd_result(hba);
> >>>    		hba->active_uic_cmd->argument3 =
> >>>    			ufshcd_get_dme_attr_val(hba);
> >>>    		complete(&hba->active_uic_cmd->done);
> >>>    	}
> >>> +
> >>> +	if ((intr_status & UIC_POWER_MODE) && hba->pwr_done)
> >>> +		complete(hba->pwr_done);
> >>>    }
> >>>
> >>>    /**
> >>> @@ -2451,8 +2527,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
> >>>    	if (hba->errors)
> >>>    		ufshcd_check_errors(hba);
> >>>
> >>> -	if (intr_status & UIC_COMMAND_COMPL)
> >>> -		ufshcd_uic_cmd_compl(hba);
> >>> +	if (intr_status & UFSHCD_UIC_MASK)
> >>> +		ufshcd_uic_cmd_compl(hba, intr_status);
> >>>
> >>>    	if (intr_status & UTP_TASK_REQ_COMPL)
> >>>    		ufshcd_tmc_handler(hba);
> >>> diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
> >>> index ab1518d..ecff83d 100644
> >>> --- a/drivers/scsi/ufs/ufshcd.h
> >>> +++ b/drivers/scsi/ufs/ufshcd.h
> >>> @@ -178,6 +178,7 @@ struct ufs_dev_cmd {
> >>>     * @tm_tag_wq: wait queue for free task management slots
> >>>     * @tm_condition: condition variable for task management
> >>>     * @tm_slots_in_use: bit map of task management request slots in use
> >>> + * @pwr_done: completion for power mode change
> >>>     * @ufshcd_state: UFSHCD states
> >>>     * @eh_flags: Error handling flags
> >>>     * @intr_mask: Interrupt Mask Bits
> >>> @@ -227,6 +228,8 @@ struct ufs_hba {
> >>>    	unsigned long tm_condition;
> >>>    	unsigned long tm_slots_in_use;
> >>>
> >>> +	struct completion *pwr_done;
> >>> +
> >>>    	u32 ufshcd_state;
> >>>    	u32 eh_flags;
> >>>    	u32 intr_mask;
> >>> diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
> >>> index 1e1fe26..0475c66 100644
> >>> --- a/drivers/scsi/ufs/ufshci.h
> >>> +++ b/drivers/scsi/ufs/ufshci.h
> >>> @@ -124,6 +124,9 @@ 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_ERROR_MASK	(UIC_ERROR |\
> >>>    				DEVICE_FATAL_ERROR |\
> >>>    				CONTROLLER_FATAL_ERROR |\
> >>> @@ -142,6 +145,15 @@ enum {
> >>>    #define DEVICE_ERROR_INDICATOR			UFS_BIT(5)
> >>>    #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK	UFS_MASK(0x7, 8)
> >>>
> >>> +enum {
> >>> +	PWR_OK		= 0x0,
> >>> +	PWR_LOCAL	= 0x01,
> >>> +	PWR_REMOTE	= 0x02,
> >>> +	PWR_BUSY	= 0x03,
> >>> +	PWR_ERROR_CAP	= 0x04,
> >>> +	PWR_FATAL_ERROR	= 0x05,
> >>> +};
> >>> +
> >>>    /* HCE - Host Controller Enable 34h */
> >>>    #define CONTROLLER_ENABLE	UFS_BIT(0)
> >>>    #define CONTROLLER_DISABLE	0x0
> >> --
> >> 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-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




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux