aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorBoris BREZILLON <b.brezillon@overkiz.com>2013-10-04 03:24:12 -0400
committerWim Van Sebroeck <wim@iguana.be>2014-01-28 15:28:37 -0500
commit5161b31dc39a6d6dadc95f298de48a725b73ada8 (patch)
tree9975955af8b4387f98d9e6e7bfed31e2a215e546 /drivers/watchdog
parente30722e49783ed5ea56b5737a2821d022c4e915e (diff)
watchdog: at91sam9_wdt: better watchdog support
The at91sam9 watchdog timer can only be configured once, and the current implementation tries to configure it in a static way: - 2 seconds timeout - wdt restart every 500ms If the timer has already been configured with different values, it returns an error and do not create any watchdog device. This is not critical if the watchdog is disabled, but if it has been enabled with different timeout values it will lead to a SoC reset. This patch series tries to address this issue by adapting the heartbeat value according the WDT timer config: - it first tries to configure the timer as requested. - if it fails it fallbacks to the current config, adapting its heartbeat timer to the needs This patch series also move to a dynamically allocated at91wdt device instead of the static instance. It adds a new at91 wdt type: software. This new type make use of the at91 wdt interrupt to trigger a software reboot. Finally it adds several properties to the device tree bindings. Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com> Reviewed-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/at91sam9_wdt.c309
1 files changed, 223 insertions, 86 deletions
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index be37dde4f864..9bd089ebb70f 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -19,11 +19,13 @@
19 19
20#include <linux/errno.h> 20#include <linux/errno.h>
21#include <linux/init.h> 21#include <linux/init.h>
22#include <linux/interrupt.h>
22#include <linux/io.h> 23#include <linux/io.h>
23#include <linux/kernel.h> 24#include <linux/kernel.h>
24#include <linux/module.h> 25#include <linux/module.h>
25#include <linux/moduleparam.h> 26#include <linux/moduleparam.h>
26#include <linux/platform_device.h> 27#include <linux/platform_device.h>
28#include <linux/reboot.h>
27#include <linux/types.h> 29#include <linux/types.h>
28#include <linux/watchdog.h> 30#include <linux/watchdog.h>
29#include <linux/jiffies.h> 31#include <linux/jiffies.h>
@@ -31,22 +33,33 @@
31#include <linux/bitops.h> 33#include <linux/bitops.h>
32#include <linux/uaccess.h> 34#include <linux/uaccess.h>
33#include <linux/of.h> 35#include <linux/of.h>
36#include <linux/of_irq.h>
34 37
35#include "at91sam9_wdt.h" 38#include "at91sam9_wdt.h"
36 39
37#define DRV_NAME "AT91SAM9 Watchdog" 40#define DRV_NAME "AT91SAM9 Watchdog"
38 41
39#define wdt_read(field) \ 42#define wdt_read(wdt, field) \
40 __raw_readl(at91wdt_private.base + field) 43 __raw_readl((wdt)->base + (field))
41#define wdt_write(field, val) \ 44#define wdt_write(wtd, field, val) \
42 __raw_writel((val), at91wdt_private.base + field) 45 __raw_writel((val), (wdt)->base + (field))
43 46
44/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, 47/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
45 * use this to convert a watchdog 48 * use this to convert a watchdog
46 * value from/to milliseconds. 49 * value from/to milliseconds.
47 */ 50 */
48#define ms_to_ticks(t) (((t << 8) / 1000) - 1) 51#define ticks_to_hz_rounddown(t) ((((t) + 1) * HZ) >> 8)
49#define ticks_to_ms(t) (((t + 1) * 1000) >> 8) 52#define ticks_to_hz_roundup(t) (((((t) + 1) * HZ) + 255) >> 8)
53#define ticks_to_secs(t) (((t) + 1) >> 8)
54#define secs_to_ticks(s) (((s) << 8) - 1)
55
56#define WDT_MR_RESET 0x3FFF2FFF
57
58/* Watchdog max counter value in ticks */
59#define WDT_COUNTER_MAX_TICKS 0xFFF
60
61/* Watchdog max delta/value in secs */
62#define WDT_COUNTER_MAX_SECS ticks_to_secs(WDT_COUNTER_MAX_TICKS)
50 63
51/* Hardware timeout in seconds */ 64/* Hardware timeout in seconds */
52#define WDT_HW_TIMEOUT 2 65#define WDT_HW_TIMEOUT 2
@@ -66,23 +79,40 @@ module_param(nowayout, bool, 0);
66MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 79MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
67 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 80 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
68 81
69static struct watchdog_device at91_wdt_dev; 82#define to_wdt(wdd) container_of(wdd, struct at91wdt, wdd)
70static void at91_ping(unsigned long data); 83struct at91wdt {
71 84 struct watchdog_device wdd;
72static struct {
73 void __iomem *base; 85 void __iomem *base;
74 unsigned long next_heartbeat; /* the next_heartbeat for the timer */ 86 unsigned long next_heartbeat; /* the next_heartbeat for the timer */
75 struct timer_list timer; /* The timer that pings the watchdog */ 87 struct timer_list timer; /* The timer that pings the watchdog */
76} at91wdt_private; 88 u32 mr;
89 u32 mr_mask;
90 unsigned long heartbeat; /* WDT heartbeat in jiffies */
91 bool nowayout;
92 unsigned int irq;
93};
77 94
78/* ......................................................................... */ 95/* ......................................................................... */
79 96
97static irqreturn_t wdt_interrupt(int irq, void *dev_id)
98{
99 struct at91wdt *wdt = (struct at91wdt *)dev_id;
100
101 if (wdt_read(wdt, AT91_WDT_SR)) {
102 pr_crit("at91sam9 WDT software reset\n");
103 emergency_restart();
104 pr_crit("Reboot didn't ?????\n");
105 }
106
107 return IRQ_HANDLED;
108}
109
80/* 110/*
81 * Reload the watchdog timer. (ie, pat the watchdog) 111 * Reload the watchdog timer. (ie, pat the watchdog)
82 */ 112 */
83static inline void at91_wdt_reset(void) 113static inline void at91_wdt_reset(struct at91wdt *wdt)
84{ 114{
85 wdt_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); 115 wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
86} 116}
87 117
88/* 118/*
@@ -90,26 +120,21 @@ static inline void at91_wdt_reset(void)
90 */ 120 */
91static void at91_ping(unsigned long data) 121static void at91_ping(unsigned long data)
92{ 122{
93 if (time_before(jiffies, at91wdt_private.next_heartbeat) || 123 struct at91wdt *wdt = (struct at91wdt *)data;
94 (!watchdog_active(&at91_wdt_dev))) { 124 if (time_before(jiffies, wdt->next_heartbeat) ||
95 at91_wdt_reset(); 125 !watchdog_active(&wdt->wdd)) {
96 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 126 at91_wdt_reset(wdt);
97 } else 127 mod_timer(&wdt->timer, jiffies + wdt->heartbeat);
128 } else {
98 pr_crit("I will reset your machine !\n"); 129 pr_crit("I will reset your machine !\n");
99} 130 }
100
101static int at91_wdt_ping(struct watchdog_device *wdd)
102{
103 /* calculate when the next userspace timeout will be */
104 at91wdt_private.next_heartbeat = jiffies + wdd->timeout * HZ;
105 return 0;
106} 131}
107 132
108static int at91_wdt_start(struct watchdog_device *wdd) 133static int at91_wdt_start(struct watchdog_device *wdd)
109{ 134{
110 /* calculate the next userspace timeout and modify the timer */ 135 struct at91wdt *wdt = to_wdt(wdd);
111 at91_wdt_ping(wdd); 136 /* calculate when the next userspace timeout will be */
112 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 137 wdt->next_heartbeat = jiffies + wdd->timeout * HZ;
113 return 0; 138 return 0;
114} 139}
115 140
@@ -122,39 +147,89 @@ static int at91_wdt_stop(struct watchdog_device *wdd)
122static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout) 147static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout)
123{ 148{
124 wdd->timeout = new_timeout; 149 wdd->timeout = new_timeout;
125 return 0; 150 return at91_wdt_start(wdd);
126} 151}
127 152
128/* 153static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt)
129 * Set the watchdog time interval in 1/256Hz (write-once)
130 * Counter is 12 bit.
131 */
132static int at91_wdt_settimeout(unsigned int timeout)
133{ 154{
134 unsigned int reg; 155 u32 tmp;
135 unsigned int mr; 156 u32 delta;
136 157 u32 value;
137 /* Check if disabled */ 158 int err;
138 mr = wdt_read(AT91_WDT_MR); 159 u32 mask = wdt->mr_mask;
139 if (mr & AT91_WDT_WDDIS) { 160 unsigned long min_heartbeat = 1;
140 pr_err("sorry, watchdog is disabled\n"); 161 struct device *dev = &pdev->dev;
141 return -EIO; 162
163 tmp = wdt_read(wdt, AT91_WDT_MR);
164 if ((tmp & mask) != (wdt->mr & mask)) {
165 if (tmp == WDT_MR_RESET) {
166 wdt_write(wdt, AT91_WDT_MR, wdt->mr);
167 tmp = wdt_read(wdt, AT91_WDT_MR);
168 }
169 }
170
171 if (tmp & AT91_WDT_WDDIS) {
172 if (wdt->mr & AT91_WDT_WDDIS)
173 return 0;
174 dev_err(dev, "watchdog is disabled\n");
175 return -EINVAL;
176 }
177
178 value = tmp & AT91_WDT_WDV;
179 delta = (tmp & AT91_WDT_WDD) >> 16;
180
181 if (delta < value)
182 min_heartbeat = ticks_to_hz_roundup(value - delta);
183
184 wdt->heartbeat = ticks_to_hz_rounddown(value);
185 if (!wdt->heartbeat) {
186 dev_err(dev,
187 "heartbeat is too small for the system to handle it correctly\n");
188 return -EINVAL;
189 }
190
191 if (wdt->heartbeat < min_heartbeat + 4) {
192 wdt->heartbeat = min_heartbeat;
193 dev_warn(dev,
194 "min heartbeat and max heartbeat might be too close for the system to handle it correctly\n");
195 if (wdt->heartbeat < 4)
196 dev_warn(dev,
197 "heartbeat might be too small for the system to handle it correctly\n");
198 } else {
199 wdt->heartbeat -= 4;
142 } 200 }
143 201
144 /* 202 if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) {
145 * All counting occurs at SLOW_CLOCK / 128 = 256 Hz 203 err = request_irq(wdt->irq, wdt_interrupt,
146 * 204 IRQF_SHARED | IRQF_IRQPOLL,
147 * Since WDV is a 12-bit counter, the maximum period is 205 pdev->name, wdt);
148 * 4096 / 256 = 16 seconds. 206 if (err)
149 */ 207 return err;
150 reg = AT91_WDT_WDRSTEN /* causes watchdog reset */ 208 }
151 /* | AT91_WDT_WDRPROC causes processor reset only */ 209
152 | AT91_WDT_WDDBGHLT /* disabled in debug mode */ 210 if ((tmp & wdt->mr_mask) != (wdt->mr & wdt->mr_mask))
153 | AT91_WDT_WDD /* restart at any time */ 211 dev_warn(dev,
154 | (timeout & AT91_WDT_WDV); /* timer value */ 212 "watchdog already configured differently (mr = %x expecting %x)\n",
155 wdt_write(AT91_WDT_MR, reg); 213 tmp & wdt->mr_mask, wdt->mr & wdt->mr_mask);
214
215 setup_timer(&wdt->timer, at91_ping, (unsigned long)wdt);
216 mod_timer(&wdt->timer, jiffies + wdt->heartbeat);
217
218 /* Try to set timeout from device tree first */
219 if (watchdog_init_timeout(&wdt->wdd, 0, dev))
220 watchdog_init_timeout(&wdt->wdd, heartbeat, dev);
221 watchdog_set_nowayout(&wdt->wdd, wdt->nowayout);
222 err = watchdog_register_device(&wdt->wdd);
223 if (err)
224 goto out_stop_timer;
225
226 wdt->next_heartbeat = jiffies + wdt->wdd.timeout * HZ;
156 227
157 return 0; 228 return 0;
229
230out_stop_timer:
231 del_timer(&wdt->timer);
232 return err;
158} 233}
159 234
160/* ......................................................................... */ 235/* ......................................................................... */
@@ -169,61 +244,123 @@ static const struct watchdog_ops at91_wdt_ops = {
169 .owner = THIS_MODULE, 244 .owner = THIS_MODULE,
170 .start = at91_wdt_start, 245 .start = at91_wdt_start,
171 .stop = at91_wdt_stop, 246 .stop = at91_wdt_stop,
172 .ping = at91_wdt_ping,
173 .set_timeout = at91_wdt_set_timeout, 247 .set_timeout = at91_wdt_set_timeout,
174}; 248};
175 249
176static struct watchdog_device at91_wdt_dev = { 250#if defined(CONFIG_OF)
177 .info = &at91_wdt_info, 251static int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt)
178 .ops = &at91_wdt_ops, 252{
179 .timeout = WDT_HEARTBEAT, 253 u32 min = 0;
180 .min_timeout = 1, 254 u32 max = WDT_COUNTER_MAX_SECS;
181 .max_timeout = 0xFFFF, 255 const char *tmp;
182}; 256
257 /* Get the interrupts property */
258 wdt->irq = irq_of_parse_and_map(np, 0);
259 if (!wdt->irq)
260 dev_warn(wdt->wdd.parent, "failed to get IRQ from DT\n");
261
262 if (!of_property_read_u32_index(np, "atmel,max-heartbeat-sec", 0,
263 &max)) {
264 if (!max || max > WDT_COUNTER_MAX_SECS)
265 max = WDT_COUNTER_MAX_SECS;
266
267 if (!of_property_read_u32_index(np, "atmel,min-heartbeat-sec",
268 0, &min)) {
269 if (min >= max)
270 min = max - 1;
271 }
272 }
273
274 min = secs_to_ticks(min);
275 max = secs_to_ticks(max);
276
277 wdt->mr_mask = 0x3FFFFFFF;
278 wdt->mr = 0;
279 if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
280 !strcmp(tmp, "software")) {
281 wdt->mr |= AT91_WDT_WDFIEN;
282 wdt->mr_mask &= ~AT91_WDT_WDRPROC;
283 } else {
284 wdt->mr |= AT91_WDT_WDRSTEN;
285 }
286
287 if (!of_property_read_string(np, "atmel,reset-type", &tmp) &&
288 !strcmp(tmp, "proc"))
289 wdt->mr |= AT91_WDT_WDRPROC;
290
291 if (of_property_read_bool(np, "atmel,disable")) {
292 wdt->mr |= AT91_WDT_WDDIS;
293 wdt->mr_mask &= AT91_WDT_WDDIS;
294 }
295
296 if (of_property_read_bool(np, "atmel,idle-halt"))
297 wdt->mr |= AT91_WDT_WDIDLEHLT;
298
299 if (of_property_read_bool(np, "atmel,dbg-halt"))
300 wdt->mr |= AT91_WDT_WDDBGHLT;
301
302 wdt->mr |= max | ((max - min) << 16);
303
304 return 0;
305}
306#else
307static inline int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt)
308{
309 return 0;
310}
311#endif
183 312
184static int __init at91wdt_probe(struct platform_device *pdev) 313static int __init at91wdt_probe(struct platform_device *pdev)
185{ 314{
186 struct resource *r; 315 struct resource *r;
187 int res; 316 int err;
317 struct at91wdt *wdt;
188 318
189 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 319 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
190 if (!r) 320 if (!wdt)
191 return -ENODEV;
192 at91wdt_private.base = ioremap(r->start, resource_size(r));
193 if (!at91wdt_private.base) {
194 dev_err(&pdev->dev, "failed to map registers, aborting.\n");
195 return -ENOMEM; 321 return -ENOMEM;
196 }
197 322
198 at91_wdt_dev.parent = &pdev->dev; 323 wdt->mr = (WDT_HW_TIMEOUT * 256) | AT91_WDT_WDRSTEN | AT91_WDT_WDD |
199 watchdog_init_timeout(&at91_wdt_dev, heartbeat, &pdev->dev); 324 AT91_WDT_WDDBGHLT | AT91_WDT_WDIDLEHLT;
200 watchdog_set_nowayout(&at91_wdt_dev, nowayout); 325 wdt->mr_mask = 0x3FFFFFFF;
326 wdt->nowayout = nowayout;
327 wdt->wdd.parent = &pdev->dev;
328 wdt->wdd.info = &at91_wdt_info;
329 wdt->wdd.ops = &at91_wdt_ops;
330 wdt->wdd.timeout = WDT_HEARTBEAT;
331 wdt->wdd.min_timeout = 1;
332 wdt->wdd.max_timeout = 0xFFFF;
201 333
202 /* Set watchdog */ 334 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
203 res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000)); 335 wdt->base = devm_ioremap_resource(&pdev->dev, r);
204 if (res) 336 if (IS_ERR(wdt->base))
205 return res; 337 return PTR_ERR(wdt->base);
338
339 if (pdev->dev.of_node) {
340 err = of_at91wdt_init(pdev->dev.of_node, wdt);
341 if (err)
342 return err;
343 }
206 344
207 res = watchdog_register_device(&at91_wdt_dev); 345 err = at91_wdt_init(pdev, wdt);
208 if (res) 346 if (err)
209 return res; 347 return err;
210 348
211 at91wdt_private.next_heartbeat = jiffies + at91_wdt_dev.timeout * HZ; 349 platform_set_drvdata(pdev, wdt);
212 setup_timer(&at91wdt_private.timer, at91_ping, 0);
213 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
214 350
215 pr_info("enabled (heartbeat=%d sec, nowayout=%d)\n", 351 pr_info("enabled (heartbeat=%d sec, nowayout=%d)\n",
216 at91_wdt_dev.timeout, nowayout); 352 wdt->wdd.timeout, wdt->nowayout);
217 353
218 return 0; 354 return 0;
219} 355}
220 356
221static int __exit at91wdt_remove(struct platform_device *pdev) 357static int __exit at91wdt_remove(struct platform_device *pdev)
222{ 358{
223 watchdog_unregister_device(&at91_wdt_dev); 359 struct at91wdt *wdt = platform_get_drvdata(pdev);
360 watchdog_unregister_device(&wdt->wdd);
224 361
225 pr_warn("I quit now, hardware will probably reboot!\n"); 362 pr_warn("I quit now, hardware will probably reboot!\n");
226 del_timer(&at91wdt_private.timer); 363 del_timer(&wdt->timer);
227 364
228 return 0; 365 return 0;
229} 366}