aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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}