Linux-Watchdog Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations
@ 2020-07-28  6:42 Anson Huang
  2020-07-28  6:42 ` [PATCH 2/2] watchdog: imx7ulp: Watchdog should continue running for wait/stop mode Anson Huang
  2020-07-28 14:34 ` [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations Guenter Roeck
  0 siblings, 2 replies; 5+ messages in thread
From: Anson Huang @ 2020-07-28  6:42 UTC (permalink / raw)
  To: wim, linux, shawnguo, s.hauer, kernel, festevam, linux-watchdog,
	linux-arm-kernel, linux-kernel
  Cc: Linux-imx

According to reference manual, the i.MX7ULP WDOG's operations should
follow below sequence:

1. disable global interrupts;
2. unlock the wdog and wait unlock bit set;
3. reconfigure the wdog and wait for reconfiguration bit set;
4. enabel global interrupts.

Strictly follow the recommended sequence can make it more robust.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
 drivers/watchdog/imx7ulp_wdt.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c
index 7993c8c..b414ecf 100644
--- a/drivers/watchdog/imx7ulp_wdt.c
+++ b/drivers/watchdog/imx7ulp_wdt.c
@@ -4,6 +4,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -48,17 +49,32 @@ struct imx7ulp_wdt_device {
 	struct clk *clk;
 };
 
+static inline void imx7ulp_wdt_wait(void __iomem *base, u32 mask)
+{
+	int retries = 100;
+
+	do {
+		if (readl_relaxed(base + WDOG_CS) & mask)
+			return;
+		usleep_range(200, 1000);
+	} while (retries--);
+}
+
 static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
 {
 	struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
 
 	u32 val = readl(wdt->base + WDOG_CS);
 
+	local_irq_disable();
 	writel(UNLOCK, wdt->base + WDOG_CNT);
+	imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
 	if (enable)
 		writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
 	else
 		writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
+	imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
+	local_irq_enable();
 }
 
 static bool imx7ulp_wdt_is_enabled(void __iomem *base)
@@ -72,7 +88,12 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
 {
 	struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
 
+	local_irq_disable();
+	writel(UNLOCK, wdt->base + WDOG_CNT);
+	imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
 	writel(REFRESH, wdt->base + WDOG_CNT);
+	imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
+	local_irq_enable();
 
 	return 0;
 }
@@ -98,8 +119,12 @@ static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
 	struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
 	u32 val = WDOG_CLOCK_RATE * timeout;
 
+	local_irq_disable();
 	writel(UNLOCK, wdt->base + WDOG_CNT);
+	imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
 	writel(val, wdt->base + WDOG_TOVAL);
+	imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
+	local_irq_enable();
 
 	wdog->timeout = timeout;
 
@@ -140,15 +165,19 @@ static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
 {
 	u32 val;
 
+	local_irq_disable();
 	/* unlock the wdog for reconfiguration */
 	writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
 	writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
+	imx7ulp_wdt_wait(base, WDOG_CS_ULK);
 
 	/* set an initial timeout value in TOVAL */
 	writel(timeout, base + WDOG_TOVAL);
 	/* enable 32bit command sequence and reconfigure */
 	val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE;
 	writel(val, base + WDOG_CS);
+	imx7ulp_wdt_wait(base, WDOG_CS_RCS);
+	local_irq_enable();
 }
 
 static void imx7ulp_wdt_action(void *data)
-- 
2.7.4


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

* [PATCH 2/2] watchdog: imx7ulp: Watchdog should continue running for wait/stop mode
  2020-07-28  6:42 [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations Anson Huang
@ 2020-07-28  6:42 ` Anson Huang
  2020-07-28 14:34 ` [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations Guenter Roeck
  1 sibling, 0 replies; 5+ messages in thread
From: Anson Huang @ 2020-07-28  6:42 UTC (permalink / raw)
  To: wim, linux, shawnguo, s.hauer, kernel, festevam, linux-watchdog,
	linux-arm-kernel, linux-kernel
  Cc: Linux-imx

When kernel idle, system will enter wait/stop mode, wdog should continue
running in this scenario, and the refresh thread can wake up system from
wait/stop mode.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
 drivers/watchdog/imx7ulp_wdt.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c
index b414ecf..271a2e4 100644
--- a/drivers/watchdog/imx7ulp_wdt.c
+++ b/drivers/watchdog/imx7ulp_wdt.c
@@ -22,6 +22,8 @@
 #define WDOG_CS_CLK		(LPO_CLK << LPO_CLK_SHIFT)
 #define WDOG_CS_EN		BIT(7)
 #define WDOG_CS_UPDATE		BIT(5)
+#define WDOG_CS_WAIT		BIT(1)
+#define WDOG_CS_STOP		BIT(0)
 
 #define WDOG_CNT	0x4
 #define WDOG_TOVAL	0x8
@@ -174,7 +176,8 @@ static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
 	/* set an initial timeout value in TOVAL */
 	writel(timeout, base + WDOG_TOVAL);
 	/* enable 32bit command sequence and reconfigure */
-	val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE;
+	val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
+	      WDOG_CS_WAIT | WDOG_CS_STOP;
 	writel(val, base + WDOG_CS);
 	imx7ulp_wdt_wait(base, WDOG_CS_RCS);
 	local_irq_enable();
-- 
2.7.4


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

* Re: [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations
  2020-07-28  6:42 [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations Anson Huang
  2020-07-28  6:42 ` [PATCH 2/2] watchdog: imx7ulp: Watchdog should continue running for wait/stop mode Anson Huang
@ 2020-07-28 14:34 ` Guenter Roeck
  2020-07-28 23:36   ` Anson Huang
  1 sibling, 1 reply; 5+ messages in thread
From: Guenter Roeck @ 2020-07-28 14:34 UTC (permalink / raw)
  To: Anson Huang, wim, shawnguo, s.hauer, kernel, festevam,
	linux-watchdog, linux-arm-kernel, linux-kernel
  Cc: Linux-imx

On 7/27/20 11:42 PM, Anson Huang wrote:
> According to reference manual, the i.MX7ULP WDOG's operations should
> follow below sequence:
> 
> 1. disable global interrupts;
> 2. unlock the wdog and wait unlock bit set;
> 3. reconfigure the wdog and wait for reconfiguration bit set;
> 4. enabel global interrupts.
> 
> Strictly follow the recommended sequence can make it more robust.
> 
> Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> ---
>  drivers/watchdog/imx7ulp_wdt.c | 29 +++++++++++++++++++++++++++++
>  1 file changed, 29 insertions(+)
> 
> diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c
> index 7993c8c..b414ecf 100644
> --- a/drivers/watchdog/imx7ulp_wdt.c
> +++ b/drivers/watchdog/imx7ulp_wdt.c
> @@ -4,6 +4,7 @@
>   */
>  
>  #include <linux/clk.h>
> +#include <linux/delay.h>
>  #include <linux/io.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
> @@ -48,17 +49,32 @@ struct imx7ulp_wdt_device {
>  	struct clk *clk;
>  };
>  
> +static inline void imx7ulp_wdt_wait(void __iomem *base, u32 mask)
> +{
> +	int retries = 100;
> +
> +	do {
> +		if (readl_relaxed(base + WDOG_CS) & mask)
> +			return;
> +		usleep_range(200, 1000);
> +	} while (retries--);

Sleep with interrupts disabled ? I can not imagine that this works well
in a single CPU system. On top of that, it seems quite pointless.
Either you don't want to be interrupted or you do, but sleeping
with interrupts disabled really doesn't make sense. And does it really
take 200-1000 uS for the watchdog subsystem to react, and sometimes up
to 200 * 100 = 20 mS ? That seems highly unlikely. If such a delay loop
is indeed needed, it should be limited by a time, not by number of
repetitions.

Unless there is evidence that there is a problem that needs to be solved,
I am not going to accept this code.

Thanks,
Guenter

> +}
> +
>  static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
>  {
>  	struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
>  
>  	u32 val = readl(wdt->base + WDOG_CS);
>  
> +	local_irq_disable();
>  	writel(UNLOCK, wdt->base + WDOG_CNT);
> +	imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
>  	if (enable)
>  		writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
>  	else
>  		writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
> +	imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
> +	local_irq_enable();
>  }
>  
>  static bool imx7ulp_wdt_is_enabled(void __iomem *base)
> @@ -72,7 +88,12 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
>  {
>  	struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
>  
> +	local_irq_disable();
> +	writel(UNLOCK, wdt->base + WDOG_CNT);
> +	imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
>  	writel(REFRESH, wdt->base + WDOG_CNT);
> +	imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
> +	local_irq_enable();
>  
>  	return 0;
>  }
> @@ -98,8 +119,12 @@ static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
>  	struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
>  	u32 val = WDOG_CLOCK_RATE * timeout;
>  
> +	local_irq_disable();
>  	writel(UNLOCK, wdt->base + WDOG_CNT);
> +	imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
>  	writel(val, wdt->base + WDOG_TOVAL);
> +	imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
> +	local_irq_enable();
>  
>  	wdog->timeout = timeout;
>  
> @@ -140,15 +165,19 @@ static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
>  {
>  	u32 val;
>  
> +	local_irq_disable();
>  	/* unlock the wdog for reconfiguration */
>  	writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
>  	writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
> +	imx7ulp_wdt_wait(base, WDOG_CS_ULK);
>  
>  	/* set an initial timeout value in TOVAL */
>  	writel(timeout, base + WDOG_TOVAL);
>  	/* enable 32bit command sequence and reconfigure */
>  	val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE;
>  	writel(val, base + WDOG_CS);
> +	imx7ulp_wdt_wait(base, WDOG_CS_RCS);
> +	local_irq_enable();
>  }
>  
>  static void imx7ulp_wdt_action(void *data)
> 


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

* RE: [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations
  2020-07-28 14:34 ` [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations Guenter Roeck
@ 2020-07-28 23:36   ` Anson Huang
  2020-07-29  2:26     ` Anson Huang
  0 siblings, 1 reply; 5+ messages in thread
From: Anson Huang @ 2020-07-28 23:36 UTC (permalink / raw)
  To: Guenter Roeck, wim, shawnguo, s.hauer, kernel, festevam,
	linux-watchdog, linux-arm-kernel, linux-kernel
  Cc: dl-linux-imx

Hi, Guenter


> Subject: Re: [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for
> wdog operations
> 
> On 7/27/20 11:42 PM, Anson Huang wrote:
> > According to reference manual, the i.MX7ULP WDOG's operations should
> > follow below sequence:
> >
> > 1. disable global interrupts;
> > 2. unlock the wdog and wait unlock bit set; 3. reconfigure the wdog
> > and wait for reconfiguration bit set; 4. enabel global interrupts.
> >
> > Strictly follow the recommended sequence can make it more robust.
> >
> > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > ---
> >  drivers/watchdog/imx7ulp_wdt.c | 29 +++++++++++++++++++++++++++++
> >  1 file changed, 29 insertions(+)
> >
> > diff --git a/drivers/watchdog/imx7ulp_wdt.c
> > b/drivers/watchdog/imx7ulp_wdt.c index 7993c8c..b414ecf 100644
> > --- a/drivers/watchdog/imx7ulp_wdt.c
> > +++ b/drivers/watchdog/imx7ulp_wdt.c
> > @@ -4,6 +4,7 @@
> >   */
> >
> >  #include <linux/clk.h>
> > +#include <linux/delay.h>
> >  #include <linux/io.h>
> >  #include <linux/kernel.h>
> >  #include <linux/module.h>
> > @@ -48,17 +49,32 @@ struct imx7ulp_wdt_device {
> >  	struct clk *clk;
> >  };
> >
> > +static inline void imx7ulp_wdt_wait(void __iomem *base, u32 mask) {
> > +	int retries = 100;
> > +
> > +	do {
> > +		if (readl_relaxed(base + WDOG_CS) & mask)
> > +			return;
> > +		usleep_range(200, 1000);
> > +	} while (retries--);
> 
> Sleep with interrupts disabled ? I can not imagine that this works well in a
> single CPU system. On top of that, it seems quite pointless.
> Either you don't want to be interrupted or you do, but sleeping with interrupts
> disabled really doesn't make sense. And does it really take 200-1000 uS for the
> watchdog subsystem to react, and sometimes up to 200 * 100 = 20 mS ? That
> seems highly unlikely. If such a delay loop is indeed needed, it should be
> limited by a time, not by number of repetitions.
> 
> Unless there is evidence that there is a problem that needs to be solved, I am
> not going to accept this code.
> 

Oops, this is a mistake of using sleep with interrupt disabled, sorry for that.
The best option is to use readl_relaxed_poll_timeout_atomic() to poll the status
bit, however, the i.MX7ULP watchdog is very special that the unlock window ONLY
open for several cycles, that means the unlock status bit will be set and then clear
automatically after those cycles, using readl_relaxed_poll_timeout_atomic() will
fail since there are many timeout handle code in it and the unlock window is open
and close during this timeout handle interval, so it fail to catch the unlock bit.

The ideal option is using atomic polling without any other timeout check to make
sure the unlock window is NOT missed, but I think Linux kernel will NOT accept
a while loop without timeout, and that is why I tried to use usleep_ranges(), but
obviously I made a mistake of using it with IRQ disabled.

Do you have any suggestion of how to handle such case? If the hardware ONLY
unlock the register for a small window, how to poll the status bit with timeout
handle and also make sure the timeout handle code as quick as possible to NOT
miss the window?

Thanks,
Anson

 




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

* RE: [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations
  2020-07-28 23:36   ` Anson Huang
@ 2020-07-29  2:26     ` Anson Huang
  0 siblings, 0 replies; 5+ messages in thread
From: Anson Huang @ 2020-07-29  2:26 UTC (permalink / raw)
  To: Guenter Roeck, wim, shawnguo, s.hauer, kernel, festevam,
	linux-watchdog, linux-arm-kernel, linux-kernel
  Cc: dl-linux-imx

Hi, Guenter


> Subject: RE: [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for
> wdog operations
> 
> Hi, Guenter
> 
> 
> > Subject: Re: [PATCH 1/2] watchdog: imx7ulp: Strictly follow the
> > sequence for wdog operations
> >
> > On 7/27/20 11:42 PM, Anson Huang wrote:
> > > According to reference manual, the i.MX7ULP WDOG's operations should
> > > follow below sequence:
> > >
> > > 1. disable global interrupts;
> > > 2. unlock the wdog and wait unlock bit set; 3. reconfigure the wdog
> > > and wait for reconfiguration bit set; 4. enabel global interrupts.
> > >
> > > Strictly follow the recommended sequence can make it more robust.
> > >
> > > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > > ---
> > >  drivers/watchdog/imx7ulp_wdt.c | 29 +++++++++++++++++++++++++++++
> > >  1 file changed, 29 insertions(+)
> > >
> > > diff --git a/drivers/watchdog/imx7ulp_wdt.c
> > > b/drivers/watchdog/imx7ulp_wdt.c index 7993c8c..b414ecf 100644
> > > --- a/drivers/watchdog/imx7ulp_wdt.c
> > > +++ b/drivers/watchdog/imx7ulp_wdt.c
> > > @@ -4,6 +4,7 @@
> > >   */
> > >
> > >  #include <linux/clk.h>
> > > +#include <linux/delay.h>
> > >  #include <linux/io.h>
> > >  #include <linux/kernel.h>
> > >  #include <linux/module.h>
> > > @@ -48,17 +49,32 @@ struct imx7ulp_wdt_device {
> > >  	struct clk *clk;
> > >  };
> > >
> > > +static inline void imx7ulp_wdt_wait(void __iomem *base, u32 mask) {
> > > +	int retries = 100;
> > > +
> > > +	do {
> > > +		if (readl_relaxed(base + WDOG_CS) & mask)
> > > +			return;
> > > +		usleep_range(200, 1000);
> > > +	} while (retries--);
> >
> > Sleep with interrupts disabled ? I can not imagine that this works
> > well in a single CPU system. On top of that, it seems quite pointless.
> > Either you don't want to be interrupted or you do, but sleeping with
> > interrupts disabled really doesn't make sense. And does it really take
> > 200-1000 uS for the watchdog subsystem to react, and sometimes up to
> > 200 * 100 = 20 mS ? That seems highly unlikely. If such a delay loop
> > is indeed needed, it should be limited by a time, not by number of
> repetitions.
> >
> > Unless there is evidence that there is a problem that needs to be
> > solved, I am not going to accept this code.
> >
> 
> Oops, this is a mistake of using sleep with interrupt disabled, sorry for that.
> The best option is to use readl_relaxed_poll_timeout_atomic() to poll the
> status bit, however, the i.MX7ULP watchdog is very special that the unlock
> window ONLY open for several cycles, that means the unlock status bit will be
> set and then clear automatically after those cycles, using
> readl_relaxed_poll_timeout_atomic() will fail since there are many timeout
> handle code in it and the unlock window is open and close during this timeout
> handle interval, so it fail to catch the unlock bit.
> 
> The ideal option is using atomic polling without any other timeout check to
> make sure the unlock window is NOT missed, but I think Linux kernel will NOT
> accept a while loop without timeout, and that is why I tried to use
> usleep_ranges(), but obviously I made a mistake of using it with IRQ disabled.
> 
> Do you have any suggestion of how to handle such case? If the hardware ONLY
> unlock the register for a small window, how to poll the status bit with timeout
> handle and also make sure the timeout handle code as quick as possible to
> NOT miss the window?
> 

I did more experiment and found that below readl_poll_timeout_atomic() is actually
working, so I sent a V2 with it, please help review, thank you.


+       u32 val = readl(base + WDOG_CS);
+
+       if (!(val & mask))
+               WARN_ON(readl_poll_timeout_atomic(base + WDOG_CS, val,
+                                                 val & mask, 0,
+                                                 WDOG_WAIT_TIMEOUT));

Thanks,
Anson
 


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

end of thread, back to index

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-28  6:42 [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations Anson Huang
2020-07-28  6:42 ` [PATCH 2/2] watchdog: imx7ulp: Watchdog should continue running for wait/stop mode Anson Huang
2020-07-28 14:34 ` [PATCH 1/2] watchdog: imx7ulp: Strictly follow the sequence for wdog operations Guenter Roeck
2020-07-28 23:36   ` Anson Huang
2020-07-29  2:26     ` Anson Huang

Linux-Watchdog Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-watchdog/0 linux-watchdog/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-watchdog linux-watchdog/ https://lore.kernel.org/linux-watchdog \
		linux-watchdog@vger.kernel.org
	public-inbox-index linux-watchdog

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-watchdog


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git