All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] mmc: dw_mmc: Allow lower TMOUT value than maximum
@ 2021-11-03 15:23 Mårten Lindahl
  2021-11-06  0:14 ` Doug Anderson
  0 siblings, 1 reply; 3+ messages in thread
From: Mårten Lindahl @ 2021-11-03 15:23 UTC (permalink / raw)
  To: Jaehoon Chung, Ulf Hansson
  Cc: Doug Anderson, kernel, linux-mmc, Mårten Lindahl

The TMOUT register is always set with a full value for every transfer,
which (with a 200MHz clock) will give a full DRTO of ~84 milliseconds.
Since the software dto_timer acts as a backup in cases when this timeout
is not long enough, it is normally not a problem. But setting a full
value makes it impossible to test shorter timeouts, when for example
testing data read times on different SD cards.

Add a function to set any value smaller than the maximum of 0xFFFFFF.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
---
 drivers/mmc/host/dw_mmc.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 6578cc64ae9e..0d23b8ed9403 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -54,6 +54,7 @@
 
 #define DW_MCI_FREQ_MAX	200000000	/* unit: HZ */
 #define DW_MCI_FREQ_MIN	100000		/* unit: HZ */
+#define DW_MCI_DATA_TMOUT_NS_MAX	83886075
 
 #define IDMAC_INT_CLR		(SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
 				 SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
@@ -1283,6 +1284,32 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
 	mci_writel(host, CTYPE, (slot->ctype << slot->id));
 }
 
+static void dw_mci_set_data_timeout(struct dw_mci *host, u32 timeout_ns)
+{
+	u32 timeout, freq_mhz, tmp, tmout;
+
+	if (!timeout_ns || timeout_ns > DW_MCI_DATA_TMOUT_NS_MAX) {
+		/* Set maximum */
+		tmout = 0xFFFFFFFF;
+		goto tmout_done;
+	}
+
+	timeout = timeout_ns;
+	freq_mhz = DIV_ROUND_UP(host->bus_hz, NSEC_PER_MSEC);
+
+	/* TMOUT[7:0] (RESPONSE_TIMEOUT) */
+	tmout = 0xFF; /* Set maximum */
+
+	/* TMOUT[31:8] (DATA_TIMEOUT) */
+	tmp = DIV_ROUND_UP_ULL((u64)timeout * freq_mhz, MSEC_PER_SEC);
+	tmout |= (tmp & 0xFFFFFF) << 8;
+
+tmout_done:
+	mci_writel(host, TMOUT, tmout);
+	dev_dbg(host->dev, "timeout_ns: %u => TMOUT[31:8]: 0x%06x",
+		timeout_ns, tmout >> 8);
+}
+
 static void __dw_mci_start_request(struct dw_mci *host,
 				   struct dw_mci_slot *slot,
 				   struct mmc_command *cmd)
@@ -1303,7 +1330,7 @@ static void __dw_mci_start_request(struct dw_mci *host,
 
 	data = cmd->data;
 	if (data) {
-		mci_writel(host, TMOUT, 0xFFFFFFFF);
+		dw_mci_set_data_timeout(host, data->timeout_ns);
 		mci_writel(host, BYTCNT, data->blksz*data->blocks);
 		mci_writel(host, BLKSIZ, data->blksz);
 	}
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] mmc: dw_mmc: Allow lower TMOUT value than maximum
  2021-11-03 15:23 [PATCH] mmc: dw_mmc: Allow lower TMOUT value than maximum Mårten Lindahl
@ 2021-11-06  0:14 ` Doug Anderson
  2021-11-08 10:30   ` Marten Lindahl
  0 siblings, 1 reply; 3+ messages in thread
From: Doug Anderson @ 2021-11-06  0:14 UTC (permalink / raw)
  To: Mårten Lindahl; +Cc: Jaehoon Chung, Ulf Hansson, kernel, linux-mmc

Hi,

On Wed, Nov 3, 2021 at 8:24 AM Mårten Lindahl <marten.lindahl@axis.com> wrote:
>
> The TMOUT register is always set with a full value for every transfer,
> which (with a 200MHz clock) will give a full DRTO of ~84 milliseconds.
> Since the software dto_timer acts as a backup in cases when this timeout
> is not long enough, it is normally not a problem. But setting a full
> value makes it impossible to test shorter timeouts, when for example
> testing data read times on different SD cards.
>
> Add a function to set any value smaller than the maximum of 0xFFFFFF.
>
> Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> ---
>  drivers/mmc/host/dw_mmc.c | 29 ++++++++++++++++++++++++++++-
>  1 file changed, 28 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 6578cc64ae9e..0d23b8ed9403 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -54,6 +54,7 @@
>
>  #define DW_MCI_FREQ_MAX        200000000       /* unit: HZ */
>  #define DW_MCI_FREQ_MIN        100000          /* unit: HZ */
> +#define DW_MCI_DATA_TMOUT_NS_MAX       83886075
>
>  #define IDMAC_INT_CLR          (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
>                                  SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
> @@ -1283,6 +1284,32 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
>         mci_writel(host, CTYPE, (slot->ctype << slot->id));
>  }
>
> +static void dw_mci_set_data_timeout(struct dw_mci *host, u32 timeout_ns)

The type of "timeout_ns" should match `struct mmc_data`, which is
unsigned int, not u32.


> +{
> +       u32 timeout, freq_mhz, tmp, tmout;
> +
> +       if (!timeout_ns || timeout_ns > DW_MCI_DATA_TMOUT_NS_MAX) {
> +               /* Set maximum */
> +               tmout = 0xFFFFFFFF;
> +               goto tmout_done;
> +       }

I don't think that the above is right. If the card clock is 50 Hz
instead of 200 Hz then 0xffffffff is actually ~83 ms * 4 = ~332 ms. It
would be better to attempt to program it correctly.

Can you just do the math below and if the number is greater than can
be represented then you can just put in the max?

Interestingly enough, in `struct mmc_data` this is documented as a max
of 80 ms, though I don't think your code should care about that. Just
cap to the maximum value after your math.


> +       timeout = timeout_ns;
> +       freq_mhz = DIV_ROUND_UP(host->bus_hz, NSEC_PER_MSEC);
> +
> +       /* TMOUT[7:0] (RESPONSE_TIMEOUT) */
> +       tmout = 0xFF; /* Set maximum */
> +
> +       /* TMOUT[31:8] (DATA_TIMEOUT) */
> +       tmp = DIV_ROUND_UP_ULL((u64)timeout * freq_mhz, MSEC_PER_SEC);
> +       tmout |= (tmp & 0xFFFFFF) << 8;

Combining your two calculations, I guess you have:

tmp = timeout * (bus_hz / 1000000) / 1000

Why isn't this just:

tmp = (timeout * bus_hz) / 1000000000

Since you're doing 64-bit math anyway I don't think you need to worry
about that calculation overflowing. Multiplying two 32-bit numbers
can't exceed 64-bits, right?


Also: I think "bus_hz" is the wrong thing to be using here. You need
to take CLKDIV into account like dw_mci_set_drto() does.


-Doug

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] mmc: dw_mmc: Allow lower TMOUT value than maximum
  2021-11-06  0:14 ` Doug Anderson
@ 2021-11-08 10:30   ` Marten Lindahl
  0 siblings, 0 replies; 3+ messages in thread
From: Marten Lindahl @ 2021-11-08 10:30 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Mårten Lindahl, Jaehoon Chung, Ulf Hansson, kernel,
	linux-mmc@vger.kernel.org

On Sat, Nov 06, 2021 at 01:14:48AM +0100, Doug Anderson wrote:
> Hi,

Hi Doug!

> 
> On Wed, Nov 3, 2021 at 8:24 AM Mårten Lindahl <marten.lindahl@axis.com> wrote:
> >
> > The TMOUT register is always set with a full value for every transfer,
> > which (with a 200MHz clock) will give a full DRTO of ~84 milliseconds.
> > Since the software dto_timer acts as a backup in cases when this timeout
> > is not long enough, it is normally not a problem. But setting a full
> > value makes it impossible to test shorter timeouts, when for example
> > testing data read times on different SD cards.
> >
> > Add a function to set any value smaller than the maximum of 0xFFFFFF.
> >
> > Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> > ---
> >  drivers/mmc/host/dw_mmc.c | 29 ++++++++++++++++++++++++++++-
> >  1 file changed, 28 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> > index 6578cc64ae9e..0d23b8ed9403 100644
> > --- a/drivers/mmc/host/dw_mmc.c
> > +++ b/drivers/mmc/host/dw_mmc.c
> > @@ -54,6 +54,7 @@
> >
> >  #define DW_MCI_FREQ_MAX        200000000       /* unit: HZ */
> >  #define DW_MCI_FREQ_MIN        100000          /* unit: HZ */
> > +#define DW_MCI_DATA_TMOUT_NS_MAX       83886075
> >
> >  #define IDMAC_INT_CLR          (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
> >                                  SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
> > @@ -1283,6 +1284,32 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
> >         mci_writel(host, CTYPE, (slot->ctype << slot->id));
> >  }
> >
> > +static void dw_mci_set_data_timeout(struct dw_mci *host, u32 timeout_ns)
> 
> The type of "timeout_ns" should match `struct mmc_data`, which is
> unsigned int, not u32.

Will fix.

> 
> 
> > +{
> > +       u32 timeout, freq_mhz, tmp, tmout;
> > +
> > +       if (!timeout_ns || timeout_ns > DW_MCI_DATA_TMOUT_NS_MAX) {
> > +               /* Set maximum */
> > +               tmout = 0xFFFFFFFF;
> > +               goto tmout_done;
> > +       }
> 
> I don't think that the above is right. If the card clock is 50 Hz
> instead of 200 Hz then 0xffffffff is actually ~83 ms * 4 = ~332 ms. It
> would be better to attempt to program it correctly.
> 
> Can you just do the math below and if the number is greater than can
> be represented then you can just put in the max?
> 

Yes. Good point. Much better way to do it.

> Interestingly enough, in `struct mmc_data` this is documented as a max
> of 80 ms, though I don't think your code should care about that. Just
> cap to the maximum value after your math.
> 
> 
> > +       timeout = timeout_ns;
> > +       freq_mhz = DIV_ROUND_UP(host->bus_hz, NSEC_PER_MSEC);
> > +
> > +       /* TMOUT[7:0] (RESPONSE_TIMEOUT) */
> > +       tmout = 0xFF; /* Set maximum */
> > +
> > +       /* TMOUT[31:8] (DATA_TIMEOUT) */
> > +       tmp = DIV_ROUND_UP_ULL((u64)timeout * freq_mhz, MSEC_PER_SEC);
> > +       tmout |= (tmp & 0xFFFFFF) << 8;
> 
> Combining your two calculations, I guess you have:
> 
> tmp = timeout * (bus_hz / 1000000) / 1000
> 
> Why isn't this just:
> 
> tmp = (timeout * bus_hz) / 1000000000
> 
> Since you're doing 64-bit math anyway I don't think you need to worry
> about that calculation overflowing. Multiplying two 32-bit numbers
> can't exceed 64-bits, right?

Will do so.
> 
> 
> Also: I think "bus_hz" is the wrong thing to be using here. You need
> to take CLKDIV into account like dw_mci_set_drto() does.
> 

Will do so.
Good comments. Thanks!

Kind regards
Mårten
> 
> -Doug

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-11-08 10:30 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-11-03 15:23 [PATCH] mmc: dw_mmc: Allow lower TMOUT value than maximum Mårten Lindahl
2021-11-06  0:14 ` Doug Anderson
2021-11-08 10:30   ` Marten Lindahl

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.