aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig1
-rw-r--r--drivers/watchdog/coh901327_wdt.c192
2 files changed, 56 insertions, 137 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8895396ecd..e25a4646672a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -287,6 +287,7 @@ config COH901327_WATCHDOG
287 bool "ST-Ericsson COH 901 327 watchdog" 287 bool "ST-Ericsson COH 901 327 watchdog"
288 depends on ARCH_U300 288 depends on ARCH_U300
289 default y if MACH_U300 289 default y if MACH_U300
290 select WATCHDOG_CORE
290 help 291 help
291 Say Y here to include Watchdog timer support for the 292 Say Y here to include Watchdog timer support for the
292 watchdog embedded into the ST-Ericsson U300 series platforms. 293 watchdog embedded into the ST-Ericsson U300 series platforms.
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index 5b89f7d6cd0f..7f0cbeb58770 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -8,17 +8,15 @@
8 */ 8 */
9#include <linux/module.h> 9#include <linux/module.h>
10#include <linux/types.h> 10#include <linux/types.h>
11#include <linux/fs.h>
12#include <linux/miscdevice.h>
13#include <linux/watchdog.h> 11#include <linux/watchdog.h>
14#include <linux/interrupt.h> 12#include <linux/interrupt.h>
15#include <linux/pm.h> 13#include <linux/pm.h>
16#include <linux/platform_device.h> 14#include <linux/platform_device.h>
17#include <linux/io.h> 15#include <linux/io.h>
18#include <linux/bitops.h> 16#include <linux/bitops.h>
19#include <linux/uaccess.h>
20#include <linux/clk.h> 17#include <linux/clk.h>
21#include <linux/delay.h> 18#include <linux/delay.h>
19#include <linux/err.h>
22 20
23#define DRV_NAME "WDOG COH 901 327" 21#define DRV_NAME "WDOG COH 901 327"
24 22
@@ -74,8 +72,6 @@ static resource_size_t phybase;
74static resource_size_t physize; 72static resource_size_t physize;
75static int irq; 73static int irq;
76static void __iomem *virtbase; 74static void __iomem *virtbase;
77static unsigned long coh901327_users;
78static unsigned long boot_status;
79static struct device *parent; 75static struct device *parent;
80 76
81/* 77/*
@@ -155,30 +151,31 @@ static void coh901327_disable(void)
155 __func__, val); 151 __func__, val);
156} 152}
157 153
158static void coh901327_start(void) 154static int coh901327_start(struct watchdog_device *wdt_dev)
159{ 155{
160 coh901327_enable(margin * 100); 156 coh901327_enable(margin * 100);
157 return 0;
158}
159
160static int coh901327_stop(struct watchdog_device *wdt_dev)
161{
162 coh901327_disable();
163 return 0;
161} 164}
162 165
163static void coh901327_keepalive(void) 166static int coh901327_ping(struct watchdog_device *wdd)
164{ 167{
165 clk_enable(clk); 168 clk_enable(clk);
166 /* Feed the watchdog */ 169 /* Feed the watchdog */
167 writew(U300_WDOG_FR_FEED_RESTART_TIMER, 170 writew(U300_WDOG_FR_FEED_RESTART_TIMER,
168 virtbase + U300_WDOG_FR); 171 virtbase + U300_WDOG_FR);
169 clk_disable(clk); 172 clk_disable(clk);
173 return 0;
170} 174}
171 175
172static int coh901327_settimeout(int time) 176static int coh901327_settimeout(struct watchdog_device *wdt_dev,
177 unsigned int time)
173{ 178{
174 /*
175 * Max margin is 327 since the 10ms
176 * timeout register is max
177 * 0x7FFF = 327670ms ~= 327s.
178 */
179 if (time <= 0 || time > 327)
180 return -EINVAL;
181
182 margin = time; 179 margin = time;
183 clk_enable(clk); 180 clk_enable(clk);
184 /* Set new timeout value */ 181 /* Set new timeout value */
@@ -190,6 +187,23 @@ static int coh901327_settimeout(int time)
190 return 0; 187 return 0;
191} 188}
192 189
190static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev)
191{
192 u16 val;
193
194 clk_enable(clk);
195 /* Read repeatedly until the value is stable! */
196 val = readw(virtbase + U300_WDOG_CR);
197 while (val & U300_WDOG_CR_VALID_IND)
198 val = readw(virtbase + U300_WDOG_CR);
199 val &= U300_WDOG_CR_COUNT_VALUE_MASK;
200 clk_disable(clk);
201 if (val != 0)
202 val /= 100;
203
204 return val;
205}
206
193/* 207/*
194 * This interrupt occurs 10 ms before the watchdog WILL bark. 208 * This interrupt occurs 10 ms before the watchdog WILL bark.
195 */ 209 */
@@ -218,130 +232,35 @@ static irqreturn_t coh901327_interrupt(int irq, void *data)
218 return IRQ_HANDLED; 232 return IRQ_HANDLED;
219} 233}
220 234
221/* 235static const struct watchdog_info coh901327_ident = {
222 * Allow only one user (daemon) to open the watchdog 236 .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
223 */ 237 .identity = DRV_NAME,
224static int coh901327_open(struct inode *inode, struct file *file) 238};
225{
226 if (test_and_set_bit(1, &coh901327_users))
227 return -EBUSY;
228 coh901327_start();
229 return nonseekable_open(inode, file);
230}
231
232static int coh901327_release(struct inode *inode, struct file *file)
233{
234 clear_bit(1, &coh901327_users);
235 coh901327_disable();
236 return 0;
237}
238
239static ssize_t coh901327_write(struct file *file, const char __user *data,
240 size_t len, loff_t *ppos)
241{
242 if (len)
243 coh901327_keepalive();
244 return len;
245}
246
247static long coh901327_ioctl(struct file *file, unsigned int cmd,
248 unsigned long arg)
249{
250 int ret = -ENOTTY;
251 u16 val;
252 int time;
253 int new_options;
254 union {
255 struct watchdog_info __user *ident;
256 int __user *i;
257 } uarg;
258 static const struct watchdog_info ident = {
259 .options = WDIOF_CARDRESET |
260 WDIOF_SETTIMEOUT |
261 WDIOF_KEEPALIVEPING,
262 .identity = "COH 901 327 Watchdog",
263 .firmware_version = 1,
264 };
265 uarg.i = (int __user *)arg;
266
267 switch (cmd) {
268 case WDIOC_GETSUPPORT:
269 ret = copy_to_user(uarg.ident, &ident,
270 sizeof(ident)) ? -EFAULT : 0;
271 break;
272
273 case WDIOC_GETSTATUS:
274 ret = put_user(0, uarg.i);
275 break;
276
277 case WDIOC_GETBOOTSTATUS:
278 ret = put_user(boot_status, uarg.i);
279 break;
280
281 case WDIOC_SETOPTIONS:
282 ret = get_user(new_options, uarg.i);
283 if (ret)
284 break;
285 if (new_options & WDIOS_DISABLECARD)
286 coh901327_disable();
287 if (new_options & WDIOS_ENABLECARD)
288 coh901327_start();
289 ret = 0;
290 break;
291
292 case WDIOC_KEEPALIVE:
293 coh901327_keepalive();
294 ret = 0;
295 break;
296
297 case WDIOC_SETTIMEOUT:
298 ret = get_user(time, uarg.i);
299 if (ret)
300 break;
301
302 ret = coh901327_settimeout(time);
303 if (ret)
304 break;
305 /* Then fall through to return set value */
306
307 case WDIOC_GETTIMEOUT:
308 ret = put_user(margin, uarg.i);
309 break;
310
311 case WDIOC_GETTIMELEFT:
312 clk_enable(clk);
313 /* Read repeatedly until the value is stable! */
314 val = readw(virtbase + U300_WDOG_CR);
315 while (val & U300_WDOG_CR_VALID_IND)
316 val = readw(virtbase + U300_WDOG_CR);
317 val &= U300_WDOG_CR_COUNT_VALUE_MASK;
318 clk_disable(clk);
319 if (val != 0)
320 val /= 100;
321 ret = put_user(val, uarg.i);
322 break;
323 }
324 return ret;
325}
326 239
327static const struct file_operations coh901327_fops = { 240static struct watchdog_ops coh901327_ops = {
328 .owner = THIS_MODULE, 241 .owner = THIS_MODULE,
329 .llseek = no_llseek, 242 .start = coh901327_start,
330 .write = coh901327_write, 243 .stop = coh901327_stop,
331 .unlocked_ioctl = coh901327_ioctl, 244 .ping = coh901327_ping,
332 .open = coh901327_open, 245 .set_timeout = coh901327_settimeout,
333 .release = coh901327_release, 246 .get_timeleft = coh901327_gettimeleft,
334}; 247};
335 248
336static struct miscdevice coh901327_miscdev = { 249static struct watchdog_device coh901327_wdt = {
337 .minor = WATCHDOG_MINOR, 250 .info = &coh901327_ident,
338 .name = "watchdog", 251 .ops = &coh901327_ops,
339 .fops = &coh901327_fops, 252 /*
253 * Max margin is 327 since the 10ms
254 * timeout register is max
255 * 0x7FFF = 327670ms ~= 327s.
256 */
257 .min_timeout = 0,
258 .max_timeout = 327,
340}; 259};
341 260
342static int __exit coh901327_remove(struct platform_device *pdev) 261static int __exit coh901327_remove(struct platform_device *pdev)
343{ 262{
344 misc_deregister(&coh901327_miscdev); 263 watchdog_unregister_device(&coh901327_wdt);
345 coh901327_disable(); 264 coh901327_disable();
346 free_irq(irq, pdev); 265 free_irq(irq, pdev);
347 clk_put(clk); 266 clk_put(clk);
@@ -350,7 +269,6 @@ static int __exit coh901327_remove(struct platform_device *pdev)
350 return 0; 269 return 0;
351} 270}
352 271
353
354static int __init coh901327_probe(struct platform_device *pdev) 272static int __init coh901327_probe(struct platform_device *pdev)
355{ 273{
356 int ret; 274 int ret;
@@ -393,7 +311,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
393 case U300_WDOG_SR_STATUS_TIMED_OUT: 311 case U300_WDOG_SR_STATUS_TIMED_OUT:
394 dev_info(&pdev->dev, 312 dev_info(&pdev->dev,
395 "watchdog timed out since last chip reset!\n"); 313 "watchdog timed out since last chip reset!\n");
396 boot_status = WDIOF_CARDRESET; 314 coh901327_wdt.bootstatus |= WDIOF_CARDRESET;
397 /* Status will be cleared below */ 315 /* Status will be cleared below */
398 break; 316 break;
399 case U300_WDOG_SR_STATUS_NORMAL: 317 case U300_WDOG_SR_STATUS_NORMAL:
@@ -435,7 +353,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
435 353
436 clk_disable(clk); 354 clk_disable(clk);
437 355
438 ret = misc_register(&coh901327_miscdev); 356 ret = watchdog_register_device(&coh901327_wdt);
439 if (ret == 0) 357 if (ret == 0)
440 dev_info(&pdev->dev, 358 dev_info(&pdev->dev,
441 "initialized. timer margin=%d sec\n", margin); 359 "initialized. timer margin=%d sec\n", margin);
@@ -547,4 +465,4 @@ module_param(margin, int, 0);
547MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); 465MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
548 466
549MODULE_LICENSE("GPL"); 467MODULE_LICENSE("GPL");
550MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 468MODULE_ALIAS("platform:coh901327-watchdog");