Message ID | 20200316085303.20350-4-stanley.chu@mediatek.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | scsi: ufs: some cleanups and make the delay for host enabling customizable | expand |
On 2020-03-16 16:52, Stanley Chu wrote: > Introduce common delay function to collect all delay requirements > to simplify driver and take choices of udelay and usleep_range into > consideration. > > Signed-off-by: Stanley Chu <stanley.chu@mediatek.com> > Reviewed-by: Avri Altman <avri.altman@wdc.com> Reviewed-by: Can Guo <cang@codeaurora.org> > --- > drivers/scsi/ufs/ufshcd.c | 27 ++++++++++++++++++--------- > drivers/scsi/ufs/ufshcd.h | 1 + > 2 files changed, 19 insertions(+), 9 deletions(-) > > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c > index 314e808b0d4e..9fea346f7d22 100644 > --- a/drivers/scsi/ufs/ufshcd.c > +++ b/drivers/scsi/ufs/ufshcd.c > @@ -597,6 +597,18 @@ static void ufshcd_print_pwr_info(struct ufs_hba > *hba) > hba->pwr_info.hs_rate); > } > > +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool > can_sleep) > +{ > + if (!us) > + return; > + > + if (us < 10 || !can_sleep) > + udelay(us); > + else > + usleep_range(us, us + tolerance); > +} > +EXPORT_SYMBOL_GPL(ufshcd_wait_us); > + > /* > * ufshcd_wait_for_register - wait for register value to change > * @hba - per-adapter interface > @@ -620,10 +632,7 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, > u32 reg, u32 mask, > val = val & mask; > > while ((ufshcd_readl(hba, reg) & mask) != val) { > - if (can_sleep) > - usleep_range(interval_us, interval_us + 50); > - else > - udelay(interval_us); > + ufshcd_wait_us(interval_us, 50, can_sleep); > if (time_after(jiffies, timeout)) { > if ((ufshcd_readl(hba, reg) & mask) != val) > err = -ETIMEDOUT; > @@ -3565,7 +3574,7 @@ static inline void > ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba) > } > > /* allow sleep for extra 50us if needed */ > - usleep_range(min_sleep_time_us, min_sleep_time_us + 50); > + ufshcd_wait_us(min_sleep_time_us, 50, true); > } > > /** > @@ -4289,7 +4298,7 @@ int ufshcd_hba_enable(struct ufs_hba *hba) > * instruction might be read back. > * This delay can be changed based on the controller. > */ > - usleep_range(1000, 1100); > + ufshcd_wait_us(1000, 100, true); > > /* wait for the host controller to complete initialization */ > retry = 10; > @@ -4301,7 +4310,7 @@ int ufshcd_hba_enable(struct ufs_hba *hba) > "Controller enable failed\n"); > return -EIO; > } > - usleep_range(5000, 5100); > + ufshcd_wait_us(5000, 100, true); > } > > /* enable UIC related interrupts */ > @@ -6224,7 +6233,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) > reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); > if (reg & (1 << tag)) { > /* sleep for max. 200us to stabilize */ > - usleep_range(100, 200); > + ufshcd_wait_us(100, 100, true); > continue; > } > /* command completed already */ > @@ -7786,7 +7795,7 @@ static void ufshcd_vreg_set_lpm(struct ufs_hba > *hba) > */ > if (!ufshcd_is_link_active(hba) && > hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM) > - usleep_range(2000, 2100); > + ufshcd_wait_us(2000, 100, true); > > /* > * If UFS device is either in UFS_Sleep turn off VCC rail to save > some > diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h > index 52425371082a..842f0223f5e5 100644 > --- a/drivers/scsi/ufs/ufshcd.h > +++ b/drivers/scsi/ufs/ufshcd.h > @@ -792,6 +792,7 @@ int ufshcd_init(struct ufs_hba * , void __iomem * > , unsigned int); > int ufshcd_make_hba_operational(struct ufs_hba *hba); > void ufshcd_remove(struct ufs_hba *); > int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); > +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool > can_sleep); > int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, > u32 val, unsigned long interval_us, > unsigned long timeout_ms, bool can_sleep);
On 3/16/20 1:52 AM, Stanley Chu wrote: > +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool can_sleep) > +{ > + if (!us) > + return; > + > + if (us < 10 || !can_sleep) > + udelay(us); > + else > + usleep_range(us, us + tolerance); > +} > +EXPORT_SYMBOL_GPL(ufshcd_wait_us); I don't like this function because I think it makes the UFS code harder to read instead of easier. The 'can_sleep' argument is only set by one caller which I think is a strong argument to remove that argument again and to move the code that depends on that argument from the above function into the caller. Additionally, it is not possible to comprehend what a ufshcd_wait_us() call does without looking up the function definition to see what the meaning of the third argument is. Please drop this patch. Thanks, Bart.
Hi Bart, On Mon, 2020-03-16 at 09:23 -0700, Bart Van Assche wrote: > On 3/16/20 1:52 AM, Stanley Chu wrote: > > +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool can_sleep) > > +{ > > + if (!us) > > + return; > > + > > + if (us < 10 || !can_sleep) > > + udelay(us); > > + else > > + usleep_range(us, us + tolerance); > > +} > > +EXPORT_SYMBOL_GPL(ufshcd_wait_us); > > I don't like this function because I think it makes the UFS code harder > to read instead of easier. The 'can_sleep' argument is only set by one > caller which I think is a strong argument to remove that argument again > and to move the code that depends on that argument from the above > function into the caller. Additionally, it is not possible to comprehend > what a ufshcd_wait_us() call does without looking up the function > definition to see what the meaning of the third argument is. > > Please drop this patch. Thanks for your review and comments. If the problem is the third argument 'can_sleep' which makes the code not be easily comprehensible, how about just removing 'can_sleep' from this function and keeping left parts because this function provides good flexibility to users to choose udelay or usleep_range according to the 'us' argument? Thanks, Stanley Chu > > Thanks, > > Bart. >
On 2020-03-16 17:13, Stanley Chu wrote: > On Mon, 2020-03-16 at 09:23 -0700, Bart Van Assche wrote: >> On 3/16/20 1:52 AM, Stanley Chu wrote: >>> +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool can_sleep) >>> +{ >>> + if (!us) >>> + return; >>> + >>> + if (us < 10 || !can_sleep) >>> + udelay(us); >>> + else >>> + usleep_range(us, us + tolerance); >>> +} >>> +EXPORT_SYMBOL_GPL(ufshcd_wait_us); >> >> I don't like this function because I think it makes the UFS code harder >> to read instead of easier. The 'can_sleep' argument is only set by one >> caller which I think is a strong argument to remove that argument again >> and to move the code that depends on that argument from the above >> function into the caller. Additionally, it is not possible to comprehend >> what a ufshcd_wait_us() call does without looking up the function >> definition to see what the meaning of the third argument is. >> >> Please drop this patch. > > Thanks for your review and comments. > > If the problem is the third argument 'can_sleep' which makes the code > not be easily comprehensible, how about just removing 'can_sleep' from > this function and keeping left parts because this function provides good > flexibility to users to choose udelay or usleep_range according to the > 'us' argument? Hi Stanley, I think that we need to get rid of 'can_sleep' across the entire UFS driver. As far as I can see the only context from which 'can_sleep' is set to true is ufshcd_host_reset_and_restore() and 'can_sleep' is set to true because ufshcd_hba_stop() is called with a spinlock held. Do you agree that it is wrong to call udelay() while holding a spinlock() and that doing so has a bad impact on the energy consumption of the UFS driver? Has it already been considered to use another mechanism to serialize REG_CONTROLLER_ENABLE changes, e.g. a mutex? Thanks, Bart.
Hi Bart, On Mon, 2020-03-16 at 20:59 -0700, Bart Van Assche wrote: > On 2020-03-16 17:13, Stanley Chu wrote: > > On Mon, 2020-03-16 at 09:23 -0700, Bart Van Assche wrote: > >> On 3/16/20 1:52 AM, Stanley Chu wrote: > >>> +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool can_sleep) > >>> +{ > >>> + if (!us) > >>> + return; > >>> + > >>> + if (us < 10 || !can_sleep) > >>> + udelay(us); > >>> + else > >>> + usleep_range(us, us + tolerance); > >>> +} > >>> +EXPORT_SYMBOL_GPL(ufshcd_wait_us); > >> > >> I don't like this function because I think it makes the UFS code harder > >> to read instead of easier. The 'can_sleep' argument is only set by one > >> caller which I think is a strong argument to remove that argument again > >> and to move the code that depends on that argument from the above > >> function into the caller. Additionally, it is not possible to comprehend > >> what a ufshcd_wait_us() call does without looking up the function > >> definition to see what the meaning of the third argument is. > >> > >> Please drop this patch. > > > > Thanks for your review and comments. > > > > If the problem is the third argument 'can_sleep' which makes the code > > not be easily comprehensible, how about just removing 'can_sleep' from > > this function and keeping left parts because this function provides good > > flexibility to users to choose udelay or usleep_range according to the > > 'us' argument? > > Hi Stanley, > > I think that we need to get rid of 'can_sleep' across the entire UFS > driver. As far as I can see the only context from which 'can_sleep' is > set to true is ufshcd_host_reset_and_restore() and 'can_sleep' is set to > true because ufshcd_hba_stop() is called with a spinlock held. Do you > agree that it is wrong to call udelay() while holding a spinlock() and > that doing so has a bad impact on the energy consumption of the UFS > driver? Thanks for your positive suggestion. Indeed using udelay() with spinlock held may have performance or power consumption concerns. However the concern in ufshcd_hba_stop() could be ignored in most cases since the execution period of changing bit 0 in REG_CONTROLLER_ENABLE from 1 to 0 shall be very fast. In my local environment, it could have only several 'ns' latency thus udelay() was never executed during the stress test. The delay here may be required for rare cases that host is under an abnormal state. > Has it already been considered to use another mechanism to > serialize REG_CONTROLLER_ENABLE changes, e.g. a mutex? I think mutex is not suitable for REG_CONTROLLER_ENABLE case because stopping host (by what ufshcd_hba_stop does) will reset doorbell bits in the same time by host, and those doorbell bits are looked up by UFS interrupt routine for request completion flow as well. I agree that "can_sleep" can be improved and may have other optimized way but this is beyond this patch set. I would like to remove the "can_sleep" related modification from this patch set first. Thanks, Stanley Chu
(Sorry to resend this mail with "[SPAM]" prefix removed in title) Hi Bart, On Mon, 2020-03-16 at 20:59 -0700, Bart Van Assche wrote: > On 2020-03-16 17:13, Stanley Chu wrote: > > On Mon, 2020-03-16 at 09:23 -0700, Bart Van Assche wrote: > >> On 3/16/20 1:52 AM, Stanley Chu wrote: > >>> +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool can_sleep) > >>> +{ > >>> + if (!us) > >>> + return; > >>> + > >>> + if (us < 10 || !can_sleep) > >>> + udelay(us); > >>> + else > >>> + usleep_range(us, us + tolerance); > >>> +} > >>> +EXPORT_SYMBOL_GPL(ufshcd_wait_us); > >> > >> I don't like this function because I think it makes the UFS code harder > >> to read instead of easier. The 'can_sleep' argument is only set by one > >> caller which I think is a strong argument to remove that argument again > >> and to move the code that depends on that argument from the above > >> function into the caller. Additionally, it is not possible to comprehend > >> what a ufshcd_wait_us() call does without looking up the function > >> definition to see what the meaning of the third argument is. > >> > >> Please drop this patch. > > > > Thanks for your review and comments. > > > > If the problem is the third argument 'can_sleep' which makes the code > > not be easily comprehensible, how about just removing 'can_sleep' from > > this function and keeping left parts because this function provides good > > flexibility to users to choose udelay or usleep_range according to the > > 'us' argument? > > Hi Stanley, > > I think that we need to get rid of 'can_sleep' across the entire UFS > driver. As far as I can see the only context from which 'can_sleep' is > set to true is ufshcd_host_reset_and_restore() and 'can_sleep' is set to > true because ufshcd_hba_stop() is called with a spinlock held. Do you > agree that it is wrong to call udelay() while holding a spinlock() and > that doing so has a bad impact on the energy consumption of the UFS > driver? Thanks for your positive suggestion. Indeed using udelay() with spinlock held may have performance or power consumption concerns. However the concern in ufshcd_hba_stop() could be ignored in most cases since the execution period of changing bit 0 in REG_CONTROLLER_ENABLE from 1 to 0 shall be very fast. In my local environment, it could have only several 'ns' latency thus udelay() was never executed during the stress test. The delay here may be required for rare cases that host is under an abnormal state. > Has it already been considered to use another mechanism to > serialize REG_CONTROLLER_ENABLE changes, e.g. a mutex? I think mutex is not suitable for REG_CONTROLLER_ENABLE case because stopping host (by what ufshcd_hba_stop does) will reset doorbell bits in the same time by host, and those doorbell bits are looked up by UFS interrupt routine for request completion flow as well. I agree that "can_sleep" can be improved and may have other optimized way but this is beyond this patch set. I would like to remove the "can_sleep" related modification from this patch set first. Thanks, Stanley Chu
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 314e808b0d4e..9fea346f7d22 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -597,6 +597,18 @@ static void ufshcd_print_pwr_info(struct ufs_hba *hba) hba->pwr_info.hs_rate); } +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool can_sleep) +{ + if (!us) + return; + + if (us < 10 || !can_sleep) + udelay(us); + else + usleep_range(us, us + tolerance); +} +EXPORT_SYMBOL_GPL(ufshcd_wait_us); + /* * ufshcd_wait_for_register - wait for register value to change * @hba - per-adapter interface @@ -620,10 +632,7 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, val = val & mask; while ((ufshcd_readl(hba, reg) & mask) != val) { - if (can_sleep) - usleep_range(interval_us, interval_us + 50); - else - udelay(interval_us); + ufshcd_wait_us(interval_us, 50, can_sleep); if (time_after(jiffies, timeout)) { if ((ufshcd_readl(hba, reg) & mask) != val) err = -ETIMEDOUT; @@ -3565,7 +3574,7 @@ static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba) } /* allow sleep for extra 50us if needed */ - usleep_range(min_sleep_time_us, min_sleep_time_us + 50); + ufshcd_wait_us(min_sleep_time_us, 50, true); } /** @@ -4289,7 +4298,7 @@ int ufshcd_hba_enable(struct ufs_hba *hba) * instruction might be read back. * This delay can be changed based on the controller. */ - usleep_range(1000, 1100); + ufshcd_wait_us(1000, 100, true); /* wait for the host controller to complete initialization */ retry = 10; @@ -4301,7 +4310,7 @@ int ufshcd_hba_enable(struct ufs_hba *hba) "Controller enable failed\n"); return -EIO; } - usleep_range(5000, 5100); + ufshcd_wait_us(5000, 100, true); } /* enable UIC related interrupts */ @@ -6224,7 +6233,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); if (reg & (1 << tag)) { /* sleep for max. 200us to stabilize */ - usleep_range(100, 200); + ufshcd_wait_us(100, 100, true); continue; } /* command completed already */ @@ -7786,7 +7795,7 @@ static void ufshcd_vreg_set_lpm(struct ufs_hba *hba) */ if (!ufshcd_is_link_active(hba) && hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM) - usleep_range(2000, 2100); + ufshcd_wait_us(2000, 100, true); /* * If UFS device is either in UFS_Sleep turn off VCC rail to save some diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 52425371082a..842f0223f5e5 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -792,6 +792,7 @@ int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int); int ufshcd_make_hba_operational(struct ufs_hba *hba); void ufshcd_remove(struct ufs_hba *); int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); +void ufshcd_wait_us(unsigned long us, unsigned long tolerance, bool can_sleep); int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, u32 val, unsigned long interval_us, unsigned long timeout_ms, bool can_sleep);