summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorAlexandre Belloni <alexandre.belloni@free-electrons.com>2017-03-02 12:31:12 -0500
committerWim Van Sebroeck <wim@iguana.be>2017-05-18 12:51:24 -0400
commitddd6d240b26dcb8b8dc98bd493eba944dd97ebc8 (patch)
tree3841ccf38b20b4e0b3adbb3aa12c801cff69450e /drivers/watchdog
parent015b528644a84b0018d3286ecd6ea5f82dce0180 (diff)
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. Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> Acked-by: Wenyou.Yang <wenyou.yang@microchip.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/sama5d4_wdt.c33
1 files 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 @@
6 * Licensed under GPLv2. 6 * Licensed under GPLv2.
7 */ 7 */
8 8
9#include <linux/delay.h>
9#include <linux/interrupt.h> 10#include <linux/interrupt.h>
10#include <linux/io.h> 11#include <linux/io.h>
11#include <linux/kernel.h> 12#include <linux/kernel.h>
@@ -29,6 +30,7 @@ struct sama5d4_wdt {
29 struct watchdog_device wdd; 30 struct watchdog_device wdd;
30 void __iomem *reg_base; 31 void __iomem *reg_base;
31 u32 mr; 32 u32 mr;
33 unsigned long last_ping;
32}; 34};
33 35
34static int wdt_timeout = WDT_DEFAULT_TIMEOUT; 36static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
@@ -49,8 +51,29 @@ MODULE_PARM_DESC(nowayout,
49#define wdt_read(wdt, field) \ 51#define wdt_read(wdt, field) \
50 readl_relaxed((wdt)->reg_base + (field)) 52 readl_relaxed((wdt)->reg_base + (field))
51 53
52#define wdt_write(wtd, field, val) \ 54/* 4 slow clock periods is 4/32768 = 122.07µs*/
53 writel_relaxed((val), (wdt)->reg_base + (field)) 55#define WDT_DELAY usecs_to_jiffies(123)
56
57static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
58{
59 /*
60 * WDT_CR and WDT_MR must not be modified within three slow clock
61 * periods following a restart of the watchdog performed by a write
62 * access in WDT_CR.
63 */
64 while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
65 usleep_range(30, 125);
66 writel_relaxed(val, wdt->reg_base + field);
67 wdt->last_ping = jiffies;
68}
69
70static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
71{
72 if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
73 udelay(123);
74 writel_relaxed(val, wdt->reg_base + field);
75 wdt->last_ping = jiffies;
76}
54 77
55static int sama5d4_wdt_start(struct watchdog_device *wdd) 78static int sama5d4_wdt_start(struct watchdog_device *wdd)
56{ 79{
@@ -164,11 +187,12 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
164 * Else, we have to disable it properly. 187 * Else, we have to disable it properly.
165 */ 188 */
166 if (wdt_enabled) { 189 if (wdt_enabled) {
167 wdt_write(wdt, AT91_WDT_MR, wdt->mr); 190 wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
168 } else { 191 } else {
169 reg = wdt_read(wdt, AT91_WDT_MR); 192 reg = wdt_read(wdt, AT91_WDT_MR);
170 if (!(reg & AT91_WDT_WDDIS)) 193 if (!(reg & AT91_WDT_WDDIS))
171 wdt_write(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS); 194 wdt_write_nosleep(wdt, AT91_WDT_MR,
195 reg | AT91_WDT_WDDIS);
172 } 196 }
173 return 0; 197 return 0;
174} 198}
@@ -193,6 +217,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
193 wdd->ops = &sama5d4_wdt_ops; 217 wdd->ops = &sama5d4_wdt_ops;
194 wdd->min_timeout = MIN_WDT_TIMEOUT; 218 wdd->min_timeout = MIN_WDT_TIMEOUT;
195 wdd->max_timeout = MAX_WDT_TIMEOUT; 219 wdd->max_timeout = MAX_WDT_TIMEOUT;
220 wdt->last_ping = jiffies;
196 221
197 watchdog_set_drvdata(wdd, wdt); 222 watchdog_set_drvdata(wdd, wdt);
198 223