linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] watchdog: sama5d4: fix issues
@ 2017-03-02 17:31 Alexandre Belloni
  2017-03-02 17:31 ` [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling Alexandre Belloni
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Alexandre Belloni @ 2017-03-02 17:31 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel, Alexandre Belloni

Hi,

This is a rework of how the watchdog is getting programmed. Currently,
there are multiple issue that have the same symptoms: the watchdog is
unexpectidly resetting the SoCs when configuring it.

The first issue was how WDDIS was handled. To sum it up, the watchdog
has to be configured when removing WDDIS instead of first removing it
then configuring it. It is solved by only configuring the IP when
enabling the watchdog.
Note that there were no issue updating the timeout with the watchdog
running.

The second issue is how the write are synchronized inside the IP. The
datasheet state that iit is necessary to wait 3 slow clock periods
between a write to CR and subsequents writes to CR and MR. This was not
done in the driver. Also, it apears it is necessary to wait the same
amount of time between a write to MR and a write to CR or MR.

Finally, a simplification of the probe is done and a comment is added in
the resume function to explain why the reset may be delayed after a
suspend/resume cycle (but it will still happen).

Before the series, the watchdog would reset the SoC after a few
configurations. Now, I've lett a test progam run that managed to do more
than 1 000 000 configurations with the watchdog enabled and the same
while disabled.

Alexandre Belloni (4):
  watchdog: sama5d4: fix WDDIS handling
  watchdog: sama5d4: fix race condition
  watchodg: sama5d4: simplify probe
  watchdog: sama5d4: Add comment explaining what happens on resume

 drivers/watchdog/sama5d4_wdt.c | 96 ++++++++++++++++++++++++++++++------------
 1 file changed, 68 insertions(+), 28 deletions(-)

-- 
2.11.0

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

* [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling
  2017-03-02 17:31 [PATCH 0/4] watchdog: sama5d4: fix issues Alexandre Belloni
@ 2017-03-02 17:31 ` Alexandre Belloni
  2017-03-04 15:04   ` Guenter Roeck
  2017-03-07  2:05   ` Wenyou.Yang
  2017-03-02 17:31 ` [PATCH 2/4] watchdog: sama5d4: fix race condition Alexandre Belloni
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 17+ messages in thread
From: Alexandre Belloni @ 2017-03-02 17:31 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel, Alexandre Belloni

The datasheet states: "When setting the WDDIS bit, and while it is set, the
fields WDV and WDD must not be modified."

Because the whole configuration is already cached inside .mr, wait for the
user to enable the watchdog to configure it so it is enabled and configured
at the same time (what the IP is actually expecting).

When the watchdog is already enabled, it is not an issue to reconfigure it.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/watchdog/sama5d4_wdt.c | 48 ++++++++++++++++++++++++++----------------
 1 file changed, 30 insertions(+), 18 deletions(-)

diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index f709962018ac..5cee20caca78 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -44,6 +44,8 @@ MODULE_PARM_DESC(nowayout,
 	"Watchdog cannot be stopped once started (default="
 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
+#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
+
 #define wdt_read(wdt, field) \
 	readl_relaxed((wdt)->reg_base + (field))
 
@@ -89,7 +91,16 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
 	wdt->mr &= ~AT91_WDT_WDD;
 	wdt->mr |= AT91_WDT_SET_WDV(value);
 	wdt->mr |= AT91_WDT_SET_WDD(value);
-	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
+
+	/*
+	 * WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When
+	 * setting the WDDIS bit, and while it is set, the fields WDV and WDD
+	 * must not be modified.
+	 * If the watchdog is enabled, then the timeout can be updated. Else,
+	 * wait that the user enables it.
+	 */
+	if (wdt_enabled)
+		wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
 
 	wdd->timeout = timeout;
 
@@ -145,23 +156,20 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
 
 static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
 {
-	struct watchdog_device *wdd = &wdt->wdd;
-	u32 value = WDT_SEC2TICKS(wdd->timeout);
 	u32 reg;
-
 	/*
-	 * Because the fields WDV and WDD must not be modified when the WDDIS
-	 * bit is set, so clear the WDDIS bit before writing the WDT_MR.
+	 * When booting and resuming, the bootloader may have changed the
+	 * watchdog configuration.
+	 * If the watchdog is already running, we can safely update it.
+	 * Else, we have to disable it properly.
 	 */
-	reg = wdt_read(wdt, AT91_WDT_MR);
-	reg &= ~AT91_WDT_WDDIS;
-	wdt_write(wdt, AT91_WDT_MR, reg);
-
-	wdt->mr |= AT91_WDT_SET_WDD(value);
-	wdt->mr |= AT91_WDT_SET_WDV(value);
-
-	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
-
+	if (wdt_enabled) {
+		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
+	} else {
+		reg = wdt_read(wdt, AT91_WDT_MR);
+		if (!(reg & AT91_WDT_WDDIS))
+			wdt_write(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS);
+	}
 	return 0;
 }
 
@@ -172,6 +180,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 	struct resource *res;
 	void __iomem *regs;
 	u32 irq = 0;
+	u32 timeout;
 	int ret;
 
 	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
@@ -221,6 +230,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	timeout = WDT_SEC2TICKS(wdd->timeout);
+
+	wdt->mr |= AT91_WDT_SET_WDD(timeout);
+	wdt->mr |= AT91_WDT_SET_WDV(timeout);
+
 	ret = sama5d4_wdt_init(wdt);
 	if (ret)
 		return ret;
@@ -263,9 +277,7 @@ static int sama5d4_wdt_resume(struct device *dev)
 {
 	struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
 
-	wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
-	if (wdt->mr & AT91_WDT_WDDIS)
-		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
+	sama5d4_wdt_init(wdt);
 
 	return 0;
 }
-- 
2.11.0

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

* [PATCH 2/4] watchdog: sama5d4: fix race condition
  2017-03-02 17:31 [PATCH 0/4] watchdog: sama5d4: fix issues Alexandre Belloni
  2017-03-02 17:31 ` [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling Alexandre Belloni
@ 2017-03-02 17:31 ` Alexandre Belloni
  2017-03-02 17:42   ` Guenter Roeck
                     ` (2 more replies)
  2017-03-02 17:31 ` [PATCH 3/4] watchodg: sama5d4: simplify probe Alexandre Belloni
  2017-03-02 17:31 ` [PATCH 4/4] watchdog: sama5d4: Add comment explaining what happens on resume Alexandre Belloni
  3 siblings, 3 replies; 17+ messages in thread
From: Alexandre Belloni @ 2017-03-02 17:31 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel, Alexandre Belloni

WDT_MR and WDT_CR must not updated within three slow clock periods after
the last ping (write to WDT_CR or WDT_MR). Ensure enough time has elapsed
before writing those registers.
wdt_write() waits for 4 periods to ensure at least 3 edges are seen by the
IP.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/watchdog/sama5d4_wdt.c | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index 5cee20caca78..362fd229786d 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -6,6 +6,7 @@
  * Licensed under GPLv2.
  */
 
+#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -29,6 +30,7 @@ struct sama5d4_wdt {
 	struct watchdog_device	wdd;
 	void __iomem		*reg_base;
 	u32			mr;
+	unsigned long		last_ping;
 };
 
 static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
@@ -49,8 +51,29 @@ MODULE_PARM_DESC(nowayout,
 #define wdt_read(wdt, field) \
 	readl_relaxed((wdt)->reg_base + (field))
 
-#define wdt_write(wtd, field, val) \
-	writel_relaxed((val), (wdt)->reg_base + (field))
+/* 4 slow clock periods is 4/32768 = 122.07µs*/
+#define WDT_DELAY	usecs_to_jiffies(123)
+
+static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
+{
+	/*
+	 * WDT_CR and WDT_MR must not be modified within three slow clock
+	 * periods following a restart of the watchdog performed by a write
+	 * access in WDT_CR.
+	 */
+	while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
+		usleep_range(30, 125);
+	writel_relaxed(val, wdt->reg_base + field);
+	wdt->last_ping = jiffies;
+}
+
+static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
+{
+	if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
+		udelay(123);
+	writel_relaxed(val, wdt->reg_base + field);
+	wdt->last_ping = jiffies;
+}
 
 static int sama5d4_wdt_start(struct watchdog_device *wdd)
 {
@@ -164,11 +187,12 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
 	 * Else, we have to disable it properly.
 	 */
 	if (wdt_enabled) {
-		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
+		wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
 	} else {
 		reg = wdt_read(wdt, AT91_WDT_MR);
 		if (!(reg & AT91_WDT_WDDIS))
-			wdt_write(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS);
+			wdt_write_nosleep(wdt, AT91_WDT_MR,
+					  reg | AT91_WDT_WDDIS);
 	}
 	return 0;
 }
@@ -193,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &sama5d4_wdt_ops;
 	wdd->min_timeout = MIN_WDT_TIMEOUT;
 	wdd->max_timeout = MAX_WDT_TIMEOUT;
+	wdt->last_ping = jiffies;
 
 	watchdog_set_drvdata(wdd, wdt);
 
-- 
2.11.0

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

* [PATCH 3/4] watchodg: sama5d4: simplify probe
  2017-03-02 17:31 [PATCH 0/4] watchdog: sama5d4: fix issues Alexandre Belloni
  2017-03-02 17:31 ` [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling Alexandre Belloni
  2017-03-02 17:31 ` [PATCH 2/4] watchdog: sama5d4: fix race condition Alexandre Belloni
@ 2017-03-02 17:31 ` Alexandre Belloni
  2017-03-02 19:29   ` Alexander Dahl
                     ` (2 more replies)
  2017-03-02 17:31 ` [PATCH 4/4] watchdog: sama5d4: Add comment explaining what happens on resume Alexandre Belloni
  3 siblings, 3 replies; 17+ messages in thread
From: Alexandre Belloni @ 2017-03-02 17:31 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel, Alexandre Belloni

Because the only way to use the driver is to have a device tree enabling
it, pdev->dev.of_node will never be NULL. Remove the unnecessary check.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/watchdog/sama5d4_wdt.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index 362fd229786d..d710014f3b7d 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -228,15 +228,13 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 
 	wdt->reg_base = regs;
 
-	if (pdev->dev.of_node) {
-		irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
-		if (!irq)
-			dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
+	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+	if (!irq)
+		dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
 
-		ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
-		if (ret)
-			return ret;
-	}
+	ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
+	if (ret)
+		return ret;
 
 	if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
 		ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
-- 
2.11.0

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

* [PATCH 4/4] watchdog: sama5d4: Add comment explaining what happens on resume
  2017-03-02 17:31 [PATCH 0/4] watchdog: sama5d4: fix issues Alexandre Belloni
                   ` (2 preceding siblings ...)
  2017-03-02 17:31 ` [PATCH 3/4] watchodg: sama5d4: simplify probe Alexandre Belloni
@ 2017-03-02 17:31 ` Alexandre Belloni
  2017-03-04 15:07   ` Guenter Roeck
  3 siblings, 1 reply; 17+ messages in thread
From: Alexandre Belloni @ 2017-03-02 17:31 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel, Alexandre Belloni

Because suspending to RAM may lose the register values, they are restored
on resume. This is currently done unconditionally because there is
currently no way to know (from the driver) whether they have really been
lost or are still valid. Writing MR also pings the watchdog and this may
not be what is expected so add a comment explaining why it happens.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/watchdog/sama5d4_wdt.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index d710014f3b7d..0ae947c3d7bc 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -300,6 +300,11 @@ static int sama5d4_wdt_resume(struct device *dev)
 {
 	struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
 
+	/*
+	 * FIXME: writing MR also pings the watchdog which may not be desired.
+	 * This should only be done when the registers are lost on suspend but
+	 * there is no way to get this information right now.
+	 */
 	sama5d4_wdt_init(wdt);
 
 	return 0;
-- 
2.11.0

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

* Re: [PATCH 2/4] watchdog: sama5d4: fix race condition
  2017-03-02 17:31 ` [PATCH 2/4] watchdog: sama5d4: fix race condition Alexandre Belloni
@ 2017-03-02 17:42   ` Guenter Roeck
  2017-03-02 17:54     ` Alexandre Belloni
  2017-03-04 15:05   ` Guenter Roeck
  2017-03-07  2:06   ` Wenyou.Yang
  2 siblings, 1 reply; 17+ messages in thread
From: Guenter Roeck @ 2017-03-02 17:42 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel

On Thu, Mar 02, 2017 at 06:31:12PM +0100, Alexandre Belloni wrote:
> WDT_MR and WDT_CR must not updated within three slow clock periods after
> the last ping (write to WDT_CR or WDT_MR). Ensure enough time has elapsed
> before writing those registers.
> wdt_write() waits for 4 periods to ensure at least 3 edges are seen by the
> IP.
> 

Would it be possible to use min_hw_heartbeat_ms for this purpose ?

Thanks,
Guenter

> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> ---
>  drivers/watchdog/sama5d4_wdt.c | 33 +++++++++++++++++++++++++++++----
>  1 file changed, 29 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> index 5cee20caca78..362fd229786d 100644
> --- a/drivers/watchdog/sama5d4_wdt.c
> +++ b/drivers/watchdog/sama5d4_wdt.c
> @@ -6,6 +6,7 @@
>   * Licensed under GPLv2.
>   */
>  
> +#include <linux/delay.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/kernel.h>
> @@ -29,6 +30,7 @@ struct sama5d4_wdt {
>  	struct watchdog_device	wdd;
>  	void __iomem		*reg_base;
>  	u32			mr;
> +	unsigned long		last_ping;
>  };
>  
>  static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
> @@ -49,8 +51,29 @@ MODULE_PARM_DESC(nowayout,
>  #define wdt_read(wdt, field) \
>  	readl_relaxed((wdt)->reg_base + (field))
>  
> -#define wdt_write(wtd, field, val) \
> -	writel_relaxed((val), (wdt)->reg_base + (field))
> +/* 4 slow clock periods is 4/32768 = 122.07µs*/
> +#define WDT_DELAY	usecs_to_jiffies(123)
> +
> +static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
> +{
> +	/*
> +	 * WDT_CR and WDT_MR must not be modified within three slow clock
> +	 * periods following a restart of the watchdog performed by a write
> +	 * access in WDT_CR.
> +	 */
> +	while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> +		usleep_range(30, 125);
> +	writel_relaxed(val, wdt->reg_base + field);
> +	wdt->last_ping = jiffies;
> +}
> +
> +static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
> +{
> +	if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> +		udelay(123);
> +	writel_relaxed(val, wdt->reg_base + field);
> +	wdt->last_ping = jiffies;
> +}
>  
>  static int sama5d4_wdt_start(struct watchdog_device *wdd)
>  {
> @@ -164,11 +187,12 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
>  	 * Else, we have to disable it properly.
>  	 */
>  	if (wdt_enabled) {
> -		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +		wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
>  	} else {
>  		reg = wdt_read(wdt, AT91_WDT_MR);
>  		if (!(reg & AT91_WDT_WDDIS))
> -			wdt_write(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS);
> +			wdt_write_nosleep(wdt, AT91_WDT_MR,
> +					  reg | AT91_WDT_WDDIS);
>  	}
>  	return 0;
>  }
> @@ -193,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
>  	wdd->ops = &sama5d4_wdt_ops;
>  	wdd->min_timeout = MIN_WDT_TIMEOUT;
>  	wdd->max_timeout = MAX_WDT_TIMEOUT;
> +	wdt->last_ping = jiffies;
>  
>  	watchdog_set_drvdata(wdd, wdt);
>  
> -- 
> 2.11.0
> 

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

* Re: [PATCH 2/4] watchdog: sama5d4: fix race condition
  2017-03-02 17:42   ` Guenter Roeck
@ 2017-03-02 17:54     ` Alexandre Belloni
  0 siblings, 0 replies; 17+ messages in thread
From: Alexandre Belloni @ 2017-03-02 17:54 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel

On 02/03/2017 at 09:42:24 -0800, Guenter Roeck wrote:
> On Thu, Mar 02, 2017 at 06:31:12PM +0100, Alexandre Belloni wrote:
> > WDT_MR and WDT_CR must not updated within three slow clock periods after
> > the last ping (write to WDT_CR or WDT_MR). Ensure enough time has elapsed
> > before writing those registers.
> > wdt_write() waits for 4 periods to ensure at least 3 edges are seen by the
> > IP.
> > 
> 
> Would it be possible to use min_hw_heartbeat_ms for this purpose ?
> 

I don't think so unless you want the core to also use that delay between
__watchdog_ping, watchdog_start, watchdog_set_timeout and watchdog_stop.
Currently, it is only between two calls of __watchdog_ping,

> Thanks,
> Guenter
> 
> > Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> > ---
> >  drivers/watchdog/sama5d4_wdt.c | 33 +++++++++++++++++++++++++++++----
> >  1 file changed, 29 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> > index 5cee20caca78..362fd229786d 100644
> > --- a/drivers/watchdog/sama5d4_wdt.c
> > +++ b/drivers/watchdog/sama5d4_wdt.c
> > @@ -6,6 +6,7 @@
> >   * Licensed under GPLv2.
> >   */
> >  
> > +#include <linux/delay.h>
> >  #include <linux/interrupt.h>
> >  #include <linux/io.h>
> >  #include <linux/kernel.h>
> > @@ -29,6 +30,7 @@ struct sama5d4_wdt {
> >  	struct watchdog_device	wdd;
> >  	void __iomem		*reg_base;
> >  	u32			mr;
> > +	unsigned long		last_ping;
> >  };
> >  
> >  static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
> > @@ -49,8 +51,29 @@ MODULE_PARM_DESC(nowayout,
> >  #define wdt_read(wdt, field) \
> >  	readl_relaxed((wdt)->reg_base + (field))
> >  
> > -#define wdt_write(wtd, field, val) \
> > -	writel_relaxed((val), (wdt)->reg_base + (field))
> > +/* 4 slow clock periods is 4/32768 = 122.07µs*/
> > +#define WDT_DELAY	usecs_to_jiffies(123)
> > +
> > +static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
> > +{
> > +	/*
> > +	 * WDT_CR and WDT_MR must not be modified within three slow clock
> > +	 * periods following a restart of the watchdog performed by a write
> > +	 * access in WDT_CR.
> > +	 */
> > +	while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> > +		usleep_range(30, 125);
> > +	writel_relaxed(val, wdt->reg_base + field);
> > +	wdt->last_ping = jiffies;
> > +}
> > +
> > +static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
> > +{
> > +	if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> > +		udelay(123);
> > +	writel_relaxed(val, wdt->reg_base + field);
> > +	wdt->last_ping = jiffies;
> > +}
> >  
> >  static int sama5d4_wdt_start(struct watchdog_device *wdd)
> >  {
> > @@ -164,11 +187,12 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
> >  	 * Else, we have to disable it properly.
> >  	 */
> >  	if (wdt_enabled) {
> > -		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> > +		wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
> >  	} else {
> >  		reg = wdt_read(wdt, AT91_WDT_MR);
> >  		if (!(reg & AT91_WDT_WDDIS))
> > -			wdt_write(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS);
> > +			wdt_write_nosleep(wdt, AT91_WDT_MR,
> > +					  reg | AT91_WDT_WDDIS);
> >  	}
> >  	return 0;
> >  }
> > @@ -193,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
> >  	wdd->ops = &sama5d4_wdt_ops;
> >  	wdd->min_timeout = MIN_WDT_TIMEOUT;
> >  	wdd->max_timeout = MAX_WDT_TIMEOUT;
> > +	wdt->last_ping = jiffies;
> >  
> >  	watchdog_set_drvdata(wdd, wdt);
> >  
> > -- 
> > 2.11.0
> > 

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

* Re: [PATCH 3/4] watchodg: sama5d4: simplify probe
  2017-03-02 17:31 ` [PATCH 3/4] watchodg: sama5d4: simplify probe Alexandre Belloni
@ 2017-03-02 19:29   ` Alexander Dahl
  2017-03-03  8:03   ` Thomas Petazzoni
  2017-03-04 15:06   ` Guenter Roeck
  2 siblings, 0 replies; 17+ messages in thread
From: Alexander Dahl @ 2017-03-02 19:29 UTC (permalink / raw)
  To: Alexandre Belloni; +Cc: linux-watchdog, linux-arm-kernel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 395 bytes --]

Hei hei,

there's a typo in the subject you may want to fix in a v2:

watchodg → watchdog

Greets
Alex

-- 
»With the first link, the chain is forged. The first speech censured, 
the first thought forbidden, the first freedom denied, chains us all 
irrevocably.« (Jean-Luc Picard, quoting Judge Aaron Satie)
*** GnuPG-FP: C28E E6B9 0263 95CF 8FAF  08FA 34AD CD00 7221 5CC6 ***

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH 3/4] watchodg: sama5d4: simplify probe
  2017-03-02 17:31 ` [PATCH 3/4] watchodg: sama5d4: simplify probe Alexandre Belloni
  2017-03-02 19:29   ` Alexander Dahl
@ 2017-03-03  8:03   ` Thomas Petazzoni
  2017-03-03  9:29     ` Alexandre Belloni
  2017-03-04 15:06   ` Guenter Roeck
  2 siblings, 1 reply; 17+ messages in thread
From: Thomas Petazzoni @ 2017-03-03  8:03 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Guenter Roeck, linux-watchdog, Wenyou.Yang, linux-kernel,
	Nicolas Ferre, Wim Van Sebroeck, linux-arm-kernel

Hello,

On Thu,  2 Mar 2017 18:31:13 +0100, Alexandre Belloni wrote:

> +	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);

Any reason to use irq_of_parse_and_map() over the more conventional
platform_get_irq() ?

Best regards,

Thomas
-- 
Thomas Petazzoni, CTO, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

* Re: [PATCH 3/4] watchodg: sama5d4: simplify probe
  2017-03-03  8:03   ` Thomas Petazzoni
@ 2017-03-03  9:29     ` Alexandre Belloni
  2017-03-03 14:14       ` Guenter Roeck
  0 siblings, 1 reply; 17+ messages in thread
From: Alexandre Belloni @ 2017-03-03  9:29 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Guenter Roeck, linux-watchdog, Wenyou.Yang, linux-kernel,
	Nicolas Ferre, Wim Van Sebroeck, linux-arm-kernel

On 03/03/2017 at 09:03:25 +0100, Thomas Petazzoni wrote:
> On Thu,  2 Mar 2017 18:31:13 +0100, Alexandre Belloni wrote:
> 
> > +	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
> 
> Any reason to use irq_of_parse_and_map() over the more conventional
> platform_get_irq() ?
> 

No particular reason but I'm just removing the if (pdev->dev.of_node)

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

* Re: [PATCH 3/4] watchodg: sama5d4: simplify probe
  2017-03-03  9:29     ` Alexandre Belloni
@ 2017-03-03 14:14       ` Guenter Roeck
  0 siblings, 0 replies; 17+ messages in thread
From: Guenter Roeck @ 2017-03-03 14:14 UTC (permalink / raw)
  To: Alexandre Belloni, Thomas Petazzoni
  Cc: linux-watchdog, Wenyou.Yang, linux-kernel, Nicolas Ferre,
	Wim Van Sebroeck, linux-arm-kernel

On 03/03/2017 01:29 AM, Alexandre Belloni wrote:
> On 03/03/2017 at 09:03:25 +0100, Thomas Petazzoni wrote:
>> On Thu,  2 Mar 2017 18:31:13 +0100, Alexandre Belloni wrote:
>>
>>> +	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
>>
>> Any reason to use irq_of_parse_and_map() over the more conventional
>> platform_get_irq() ?
>>
>
> No particular reason but I'm just removing the if (pdev->dev.of_node)
>
A function call change would (should) be a separate patch.

Guenter

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

* Re: [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling
  2017-03-02 17:31 ` [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling Alexandre Belloni
@ 2017-03-04 15:04   ` Guenter Roeck
  2017-03-07  2:05   ` Wenyou.Yang
  1 sibling, 0 replies; 17+ messages in thread
From: Guenter Roeck @ 2017-03-04 15:04 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel

On 03/02/2017 09:31 AM, Alexandre Belloni wrote:
> The datasheet states: "When setting the WDDIS bit, and while it is set, the
> fields WDV and WDD must not be modified."
>
> Because the whole configuration is already cached inside .mr, wait for the
> user to enable the watchdog to configure it so it is enabled and configured
> at the same time (what the IP is actually expecting).
>
> When the watchdog is already enabled, it is not an issue to reconfigure it.
>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
>  drivers/watchdog/sama5d4_wdt.c | 48 ++++++++++++++++++++++++++----------------
>  1 file changed, 30 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> index f709962018ac..5cee20caca78 100644
> --- a/drivers/watchdog/sama5d4_wdt.c
> +++ b/drivers/watchdog/sama5d4_wdt.c
> @@ -44,6 +44,8 @@ MODULE_PARM_DESC(nowayout,
>  	"Watchdog cannot be stopped once started (default="
>  	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
>
> +#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
> +
>  #define wdt_read(wdt, field) \
>  	readl_relaxed((wdt)->reg_base + (field))
>
> @@ -89,7 +91,16 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
>  	wdt->mr &= ~AT91_WDT_WDD;
>  	wdt->mr |= AT91_WDT_SET_WDV(value);
>  	wdt->mr |= AT91_WDT_SET_WDD(value);
> -	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +
> +	/*
> +	 * WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When
> +	 * setting the WDDIS bit, and while it is set, the fields WDV and WDD
> +	 * must not be modified.
> +	 * If the watchdog is enabled, then the timeout can be updated. Else,
> +	 * wait that the user enables it.
> +	 */
> +	if (wdt_enabled)
> +		wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
>
>  	wdd->timeout = timeout;
>
> @@ -145,23 +156,20 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
>
>  static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
>  {
> -	struct watchdog_device *wdd = &wdt->wdd;
> -	u32 value = WDT_SEC2TICKS(wdd->timeout);
>  	u32 reg;
> -
>  	/*
> -	 * Because the fields WDV and WDD must not be modified when the WDDIS
> -	 * bit is set, so clear the WDDIS bit before writing the WDT_MR.
> +	 * When booting and resuming, the bootloader may have changed the
> +	 * watchdog configuration.
> +	 * If the watchdog is already running, we can safely update it.
> +	 * Else, we have to disable it properly.
>  	 */
> -	reg = wdt_read(wdt, AT91_WDT_MR);
> -	reg &= ~AT91_WDT_WDDIS;
> -	wdt_write(wdt, AT91_WDT_MR, reg);
> -
> -	wdt->mr |= AT91_WDT_SET_WDD(value);
> -	wdt->mr |= AT91_WDT_SET_WDV(value);
> -
> -	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> -
> +	if (wdt_enabled) {
> +		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +	} else {
> +		reg = wdt_read(wdt, AT91_WDT_MR);
> +		if (!(reg & AT91_WDT_WDDIS))
> +			wdt_write(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS);
> +	}
>  	return 0;
>  }
>
> @@ -172,6 +180,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
>  	struct resource *res;
>  	void __iomem *regs;
>  	u32 irq = 0;
> +	u32 timeout;
>  	int ret;
>
>  	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
> @@ -221,6 +230,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>
> +	timeout = WDT_SEC2TICKS(wdd->timeout);
> +
> +	wdt->mr |= AT91_WDT_SET_WDD(timeout);
> +	wdt->mr |= AT91_WDT_SET_WDV(timeout);
> +
>  	ret = sama5d4_wdt_init(wdt);
>  	if (ret)
>  		return ret;
> @@ -263,9 +277,7 @@ static int sama5d4_wdt_resume(struct device *dev)
>  {
>  	struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
>
> -	wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
> -	if (wdt->mr & AT91_WDT_WDDIS)
> -		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +	sama5d4_wdt_init(wdt);
>
>  	return 0;
>  }
>

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

* Re: [PATCH 2/4] watchdog: sama5d4: fix race condition
  2017-03-02 17:31 ` [PATCH 2/4] watchdog: sama5d4: fix race condition Alexandre Belloni
  2017-03-02 17:42   ` Guenter Roeck
@ 2017-03-04 15:05   ` Guenter Roeck
  2017-03-07  2:06   ` Wenyou.Yang
  2 siblings, 0 replies; 17+ messages in thread
From: Guenter Roeck @ 2017-03-04 15:05 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel

On 03/02/2017 09:31 AM, Alexandre Belloni wrote:
> WDT_MR and WDT_CR must not updated within three slow clock periods after
> the last ping (write to WDT_CR or WDT_MR). Ensure enough time has elapsed
> before writing those registers.
> wdt_write() waits for 4 periods to ensure at least 3 edges are seen by the
> IP.
>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
>  drivers/watchdog/sama5d4_wdt.c | 33 +++++++++++++++++++++++++++++----
>  1 file changed, 29 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> index 5cee20caca78..362fd229786d 100644
> --- a/drivers/watchdog/sama5d4_wdt.c
> +++ b/drivers/watchdog/sama5d4_wdt.c
> @@ -6,6 +6,7 @@
>   * Licensed under GPLv2.
>   */
>
> +#include <linux/delay.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/kernel.h>
> @@ -29,6 +30,7 @@ struct sama5d4_wdt {
>  	struct watchdog_device	wdd;
>  	void __iomem		*reg_base;
>  	u32			mr;
> +	unsigned long		last_ping;
>  };
>
>  static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
> @@ -49,8 +51,29 @@ MODULE_PARM_DESC(nowayout,
>  #define wdt_read(wdt, field) \
>  	readl_relaxed((wdt)->reg_base + (field))
>
> -#define wdt_write(wtd, field, val) \
> -	writel_relaxed((val), (wdt)->reg_base + (field))
> +/* 4 slow clock periods is 4/32768 = 122.07µs*/
> +#define WDT_DELAY	usecs_to_jiffies(123)
> +
> +static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
> +{
> +	/*
> +	 * WDT_CR and WDT_MR must not be modified within three slow clock
> +	 * periods following a restart of the watchdog performed by a write
> +	 * access in WDT_CR.
> +	 */
> +	while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> +		usleep_range(30, 125);
> +	writel_relaxed(val, wdt->reg_base + field);
> +	wdt->last_ping = jiffies;
> +}
> +
> +static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
> +{
> +	if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> +		udelay(123);
> +	writel_relaxed(val, wdt->reg_base + field);
> +	wdt->last_ping = jiffies;
> +}
>
>  static int sama5d4_wdt_start(struct watchdog_device *wdd)
>  {
> @@ -164,11 +187,12 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
>  	 * Else, we have to disable it properly.
>  	 */
>  	if (wdt_enabled) {
> -		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +		wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
>  	} else {
>  		reg = wdt_read(wdt, AT91_WDT_MR);
>  		if (!(reg & AT91_WDT_WDDIS))
> -			wdt_write(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS);
> +			wdt_write_nosleep(wdt, AT91_WDT_MR,
> +					  reg | AT91_WDT_WDDIS);
>  	}
>  	return 0;
>  }
> @@ -193,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
>  	wdd->ops = &sama5d4_wdt_ops;
>  	wdd->min_timeout = MIN_WDT_TIMEOUT;
>  	wdd->max_timeout = MAX_WDT_TIMEOUT;
> +	wdt->last_ping = jiffies;
>
>  	watchdog_set_drvdata(wdd, wdt);
>
>

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

* Re: [PATCH 3/4] watchodg: sama5d4: simplify probe
  2017-03-02 17:31 ` [PATCH 3/4] watchodg: sama5d4: simplify probe Alexandre Belloni
  2017-03-02 19:29   ` Alexander Dahl
  2017-03-03  8:03   ` Thomas Petazzoni
@ 2017-03-04 15:06   ` Guenter Roeck
  2 siblings, 0 replies; 17+ messages in thread
From: Guenter Roeck @ 2017-03-04 15:06 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel

On 03/02/2017 09:31 AM, Alexandre Belloni wrote:
> Because the only way to use the driver is to have a device tree enabling
> it, pdev->dev.of_node will never be NULL. Remove the unnecessary check.
>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
>  drivers/watchdog/sama5d4_wdt.c | 14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> index 362fd229786d..d710014f3b7d 100644
> --- a/drivers/watchdog/sama5d4_wdt.c
> +++ b/drivers/watchdog/sama5d4_wdt.c
> @@ -228,15 +228,13 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
>
>  	wdt->reg_base = regs;
>
> -	if (pdev->dev.of_node) {
> -		irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
> -		if (!irq)
> -			dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
> +	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
> +	if (!irq)
> +		dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
>
> -		ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
> -		if (ret)
> -			return ret;
> -	}
> +	ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
> +	if (ret)
> +		return ret;
>
>  	if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
>  		ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
>

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

* Re: [PATCH 4/4] watchdog: sama5d4: Add comment explaining what happens on resume
  2017-03-02 17:31 ` [PATCH 4/4] watchdog: sama5d4: Add comment explaining what happens on resume Alexandre Belloni
@ 2017-03-04 15:07   ` Guenter Roeck
  0 siblings, 0 replies; 17+ messages in thread
From: Guenter Roeck @ 2017-03-04 15:07 UTC (permalink / raw)
  To: Alexandre Belloni
  Cc: Wim Van Sebroeck, Nicolas Ferre, Wenyou.Yang, linux-watchdog,
	linux-arm-kernel, linux-kernel

On 03/02/2017 09:31 AM, Alexandre Belloni wrote:
> Because suspending to RAM may lose the register values, they are restored
> on resume. This is currently done unconditionally because there is
> currently no way to know (from the driver) whether they have really been
> lost or are still valid. Writing MR also pings the watchdog and this may
> not be what is expected so add a comment explaining why it happens.
>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
>  drivers/watchdog/sama5d4_wdt.c | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> index d710014f3b7d..0ae947c3d7bc 100644
> --- a/drivers/watchdog/sama5d4_wdt.c
> +++ b/drivers/watchdog/sama5d4_wdt.c
> @@ -300,6 +300,11 @@ static int sama5d4_wdt_resume(struct device *dev)
>  {
>  	struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
>
> +	/*
> +	 * FIXME: writing MR also pings the watchdog which may not be desired.
> +	 * This should only be done when the registers are lost on suspend but
> +	 * there is no way to get this information right now.
> +	 */
>  	sama5d4_wdt_init(wdt);
>
>  	return 0;
>

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

* RE: [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling
  2017-03-02 17:31 ` [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling Alexandre Belloni
  2017-03-04 15:04   ` Guenter Roeck
@ 2017-03-07  2:05   ` Wenyou.Yang
  1 sibling, 0 replies; 17+ messages in thread
From: Wenyou.Yang @ 2017-03-07  2:05 UTC (permalink / raw)
  To: alexandre.belloni, linux
  Cc: wim, Nicolas.Ferre, linux-watchdog, linux-arm-kernel, linux-kernel



> -----Original Message-----
> From: Alexandre Belloni [mailto:alexandre.belloni@free-electrons.com]
> Sent: 2017年3月3日 1:31
> To: Guenter Roeck <linux@roeck-us.net>
> Cc: Wim Van Sebroeck <wim@iguana.be>; Nicolas Ferre - M43238
> <Nicolas.Ferre@microchip.com>; Wenyou Yang - A41535
> <Wenyou.Yang@microchip.com>; linux-watchdog@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Alexandre Belloni
> <alexandre.belloni@free-electrons.com>
> Subject: [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling
> 
> The datasheet states: "When setting the WDDIS bit, and while it is set, the fields
> WDV and WDD must not be modified."
> 
> Because the whole configuration is already cached inside .mr, wait for the user to
> enable the watchdog to configure it so it is enabled and configured at the same
> time (what the IP is actually expecting).
> 
> When the watchdog is already enabled, it is not an issue to reconfigure it.

Indeed, thanks.
Acked-by: Wenyou.Yang <wenyou.yang@microchip.com>

> 
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> ---
>  drivers/watchdog/sama5d4_wdt.c | 48 ++++++++++++++++++++++++++----------
> ------
>  1 file changed, 30 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> index f709962018ac..5cee20caca78 100644
> --- a/drivers/watchdog/sama5d4_wdt.c
> +++ b/drivers/watchdog/sama5d4_wdt.c
> @@ -44,6 +44,8 @@ MODULE_PARM_DESC(nowayout,
>  	"Watchdog cannot be stopped once started (default="
>  	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> 
> +#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
> +
>  #define wdt_read(wdt, field) \
>  	readl_relaxed((wdt)->reg_base + (field))
> 
> @@ -89,7 +91,16 @@ static int sama5d4_wdt_set_timeout(struct
> watchdog_device *wdd,
>  	wdt->mr &= ~AT91_WDT_WDD;
>  	wdt->mr |= AT91_WDT_SET_WDV(value);
>  	wdt->mr |= AT91_WDT_SET_WDD(value);
> -	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +
> +	/*
> +	 * WDDIS has to be 0 when updating WDD/WDV. The datasheet states:
> When
> +	 * setting the WDDIS bit, and while it is set, the fields WDV and WDD
> +	 * must not be modified.
> +	 * If the watchdog is enabled, then the timeout can be updated. Else,
> +	 * wait that the user enables it.
> +	 */
> +	if (wdt_enabled)
> +		wdt_write(wdt, AT91_WDT_MR, wdt->mr &
> ~AT91_WDT_WDDIS);
> 
>  	wdd->timeout = timeout;
> 
> @@ -145,23 +156,20 @@ static int of_sama5d4_wdt_init(struct device_node *np,
> struct sama5d4_wdt *wdt)
> 
>  static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)  {
> -	struct watchdog_device *wdd = &wdt->wdd;
> -	u32 value = WDT_SEC2TICKS(wdd->timeout);
>  	u32 reg;
> -
>  	/*
> -	 * Because the fields WDV and WDD must not be modified when the
> WDDIS
> -	 * bit is set, so clear the WDDIS bit before writing the WDT_MR.
> +	 * When booting and resuming, the bootloader may have changed the
> +	 * watchdog configuration.
> +	 * If the watchdog is already running, we can safely update it.
> +	 * Else, we have to disable it properly.
>  	 */
> -	reg = wdt_read(wdt, AT91_WDT_MR);
> -	reg &= ~AT91_WDT_WDDIS;
> -	wdt_write(wdt, AT91_WDT_MR, reg);
> -
> -	wdt->mr |= AT91_WDT_SET_WDD(value);
> -	wdt->mr |= AT91_WDT_SET_WDV(value);
> -
> -	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> -
> +	if (wdt_enabled) {
> +		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +	} else {
> +		reg = wdt_read(wdt, AT91_WDT_MR);
> +		if (!(reg & AT91_WDT_WDDIS))
> +			wdt_write(wdt, AT91_WDT_MR, reg |
> AT91_WDT_WDDIS);
> +	}
>  	return 0;
>  }
> 
> @@ -172,6 +180,7 @@ static int sama5d4_wdt_probe(struct platform_device
> *pdev)
>  	struct resource *res;
>  	void __iomem *regs;
>  	u32 irq = 0;
> +	u32 timeout;
>  	int ret;
> 
>  	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); @@ -
> 221,6 +230,11 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
>  		return ret;
>  	}
> 
> +	timeout = WDT_SEC2TICKS(wdd->timeout);
> +
> +	wdt->mr |= AT91_WDT_SET_WDD(timeout);
> +	wdt->mr |= AT91_WDT_SET_WDV(timeout);
> +
>  	ret = sama5d4_wdt_init(wdt);
>  	if (ret)
>  		return ret;
> @@ -263,9 +277,7 @@ static int sama5d4_wdt_resume(struct device *dev)  {
>  	struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
> 
> -	wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
> -	if (wdt->mr & AT91_WDT_WDDIS)
> -		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +	sama5d4_wdt_init(wdt);
> 
>  	return 0;
>  }
> --
> 2.11.0

Best Regards,
Wenyou Yang

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

* RE: [PATCH 2/4] watchdog: sama5d4: fix race condition
  2017-03-02 17:31 ` [PATCH 2/4] watchdog: sama5d4: fix race condition Alexandre Belloni
  2017-03-02 17:42   ` Guenter Roeck
  2017-03-04 15:05   ` Guenter Roeck
@ 2017-03-07  2:06   ` Wenyou.Yang
  2 siblings, 0 replies; 17+ messages in thread
From: Wenyou.Yang @ 2017-03-07  2:06 UTC (permalink / raw)
  To: alexandre.belloni, linux
  Cc: wim, Nicolas.Ferre, linux-watchdog, linux-arm-kernel, linux-kernel



> -----Original Message-----
> From: Alexandre Belloni [mailto:alexandre.belloni@free-electrons.com]
> Sent: 2017年3月3日 1:31
> To: Guenter Roeck <linux@roeck-us.net>
> Cc: Wim Van Sebroeck <wim@iguana.be>; Nicolas Ferre - M43238
> <Nicolas.Ferre@microchip.com>; Wenyou Yang - A41535
> <Wenyou.Yang@microchip.com>; linux-watchdog@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Alexandre Belloni
> <alexandre.belloni@free-electrons.com>
> Subject: [PATCH 2/4] watchdog: sama5d4: fix race condition
> 
> WDT_MR and WDT_CR must not updated within three slow clock periods after
> the last ping (write to WDT_CR or WDT_MR). Ensure enough time has elapsed
> before writing those registers.
> wdt_write() waits for 4 periods to ensure at least 3 edges are seen by the IP.

Indeed.
Acked-by: Wenyou.Yang <wenyou.yang@microchip.com>

Thanks

> 
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> ---
>  drivers/watchdog/sama5d4_wdt.c | 33 +++++++++++++++++++++++++++++----
>  1 file changed, 29 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
> index 5cee20caca78..362fd229786d 100644
> --- a/drivers/watchdog/sama5d4_wdt.c
> +++ b/drivers/watchdog/sama5d4_wdt.c
> @@ -6,6 +6,7 @@
>   * Licensed under GPLv2.
>   */
> 
> +#include <linux/delay.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
>  #include <linux/kernel.h>
> @@ -29,6 +30,7 @@ struct sama5d4_wdt {
>  	struct watchdog_device	wdd;
>  	void __iomem		*reg_base;
>  	u32			mr;
> +	unsigned long		last_ping;
>  };
> 
>  static int wdt_timeout = WDT_DEFAULT_TIMEOUT; @@ -49,8 +51,29 @@
> MODULE_PARM_DESC(nowayout,  #define wdt_read(wdt, field) \
>  	readl_relaxed((wdt)->reg_base + (field))
> 
> -#define wdt_write(wtd, field, val) \
> -	writel_relaxed((val), (wdt)->reg_base + (field))
> +/* 4 slow clock periods is 4/32768 = 122.07µs*/
> +#define WDT_DELAY	usecs_to_jiffies(123)
> +
> +static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val) {
> +	/*
> +	 * WDT_CR and WDT_MR must not be modified within three slow clock
> +	 * periods following a restart of the watchdog performed by a write
> +	 * access in WDT_CR.
> +	 */
> +	while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> +		usleep_range(30, 125);
> +	writel_relaxed(val, wdt->reg_base + field);
> +	wdt->last_ping = jiffies;
> +}
> +
> +static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32
> +val) {
> +	if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
> +		udelay(123);
> +	writel_relaxed(val, wdt->reg_base + field);
> +	wdt->last_ping = jiffies;
> +}
> 
>  static int sama5d4_wdt_start(struct watchdog_device *wdd)  { @@ -164,11
> +187,12 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
>  	 * Else, we have to disable it properly.
>  	 */
>  	if (wdt_enabled) {
> -		wdt_write(wdt, AT91_WDT_MR, wdt->mr);
> +		wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
>  	} else {
>  		reg = wdt_read(wdt, AT91_WDT_MR);
>  		if (!(reg & AT91_WDT_WDDIS))
> -			wdt_write(wdt, AT91_WDT_MR, reg |
> AT91_WDT_WDDIS);
> +			wdt_write_nosleep(wdt, AT91_WDT_MR,
> +					  reg | AT91_WDT_WDDIS);
>  	}
>  	return 0;
>  }
> @@ -193,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device
> *pdev)
>  	wdd->ops = &sama5d4_wdt_ops;
>  	wdd->min_timeout = MIN_WDT_TIMEOUT;
>  	wdd->max_timeout = MAX_WDT_TIMEOUT;
> +	wdt->last_ping = jiffies;
> 
>  	watchdog_set_drvdata(wdd, wdt);
> 
> --
> 2.11.0


Best Regards,
Wenyou Yang

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

end of thread, other threads:[~2017-03-07  2:15 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-02 17:31 [PATCH 0/4] watchdog: sama5d4: fix issues Alexandre Belloni
2017-03-02 17:31 ` [PATCH 1/4] watchdog: sama5d4: fix WDDIS handling Alexandre Belloni
2017-03-04 15:04   ` Guenter Roeck
2017-03-07  2:05   ` Wenyou.Yang
2017-03-02 17:31 ` [PATCH 2/4] watchdog: sama5d4: fix race condition Alexandre Belloni
2017-03-02 17:42   ` Guenter Roeck
2017-03-02 17:54     ` Alexandre Belloni
2017-03-04 15:05   ` Guenter Roeck
2017-03-07  2:06   ` Wenyou.Yang
2017-03-02 17:31 ` [PATCH 3/4] watchodg: sama5d4: simplify probe Alexandre Belloni
2017-03-02 19:29   ` Alexander Dahl
2017-03-03  8:03   ` Thomas Petazzoni
2017-03-03  9:29     ` Alexandre Belloni
2017-03-03 14:14       ` Guenter Roeck
2017-03-04 15:06   ` Guenter Roeck
2017-03-02 17:31 ` [PATCH 4/4] watchdog: sama5d4: Add comment explaining what happens on resume Alexandre Belloni
2017-03-04 15:07   ` Guenter Roeck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).