Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ts72xx_wdt.c
Go to the documentation of this file.
1 /*
2  * Watchdog driver for Technologic Systems TS-72xx based SBCs
3  * (TS-7200, TS-7250 and TS-7260). These boards have external
4  * glue logic CPLD chip, which includes programmable watchdog
5  * timer.
6  *
7  * Copyright (c) 2009 Mika Westerberg <[email protected]>
8  *
9  * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
10  *
11  * This file is licensed under the terms of the GNU General Public
12  * License version 2. This program is licensed "as is" without any
13  * warranty of any kind, whether express or implied.
14  */
15 
16 #include <linux/fs.h>
17 #include <linux/io.h>
18 #include <linux/module.h>
19 #include <linux/moduleparam.h>
20 #include <linux/miscdevice.h>
21 #include <linux/mutex.h>
22 #include <linux/platform_device.h>
23 #include <linux/slab.h>
24 #include <linux/watchdog.h>
25 #include <linux/uaccess.h>
26 
27 #define TS72XX_WDT_FEED_VAL 0x05
28 #define TS72XX_WDT_DEFAULT_TIMEOUT 8
29 
31 module_param(timeout, int, 0);
32 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
33  "(1 <= timeout <= 8, default="
35  ")");
36 
37 static bool nowayout = WATCHDOG_NOWAYOUT;
38 module_param(nowayout, bool, 0);
39 MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
40 
50 struct ts72xx_wdt {
51  struct mutex lock;
52  int regval;
53 
54 #define TS72XX_WDT_BUSY_FLAG 1
55 #define TS72XX_WDT_EXPECT_CLOSE_FLAG 2
56  int flags;
57 
60 
62 };
63 
65 
66 /*
67  * TS-72xx Watchdog supports following timeouts (value written
68  * to control register):
69  * value description
70  * -------------------------
71  * 0x00 watchdog disabled
72  * 0x01 250ms
73  * 0x02 500ms
74  * 0x03 1s
75  * 0x04 reserved
76  * 0x05 2s
77  * 0x06 4s
78  * 0x07 8s
79  *
80  * Timeouts below 1s are not very usable so we don't
81  * allow them at all.
82  *
83  * We provide two functions that convert between these:
84  * timeout_to_regval() and regval_to_timeout().
85  */
86 static const struct {
87  int timeout;
88  int regval;
89 } ts72xx_wdt_map[] = {
90  { 1, 3 },
91  { 2, 5 },
92  { 4, 6 },
93  { 8, 7 },
94 };
95 
104 static int timeout_to_regval(int new_timeout)
105 {
106  int i;
107 
108  /* first limit it to 1 - 8 seconds */
109  new_timeout = clamp_val(new_timeout, 1, 8);
110 
111  for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
112  if (ts72xx_wdt_map[i].timeout >= new_timeout)
113  return ts72xx_wdt_map[i].regval;
114  }
115 
116  return -EINVAL;
117 }
118 
126 static int regval_to_timeout(int regval)
127 {
128  int i;
129 
130  for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
131  if (ts72xx_wdt_map[i].regval == regval)
132  return ts72xx_wdt_map[i].timeout;
133  }
134 
135  return -EINVAL;
136 }
137 
144 static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt)
145 {
147 }
148 
158 static void ts72xx_wdt_start(struct ts72xx_wdt *wdt)
159 {
160  /*
161  * To program the wdt, it first must be "fed" and
162  * only after that (within 30 usecs) the configuration
163  * can be changed.
164  */
165  ts72xx_wdt_kick(wdt);
166  __raw_writeb((u8)wdt->regval, wdt->control_reg);
167 }
168 
175 static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt)
176 {
177  ts72xx_wdt_kick(wdt);
178  __raw_writeb(0, wdt->control_reg);
179 }
180 
181 static int ts72xx_wdt_open(struct inode *inode, struct file *file)
182 {
183  struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev);
184  int regval;
185 
186  /*
187  * Try to convert default timeout to valid register
188  * value first.
189  */
190  regval = timeout_to_regval(timeout);
191  if (regval < 0) {
192  dev_err(&wdt->pdev->dev,
193  "failed to convert timeout (%d) to register value\n",
194  timeout);
195  return -EINVAL;
196  }
197 
198  if (mutex_lock_interruptible(&wdt->lock))
199  return -ERESTARTSYS;
200 
201  if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) {
202  mutex_unlock(&wdt->lock);
203  return -EBUSY;
204  }
205 
207  wdt->regval = regval;
208  file->private_data = wdt;
209 
210  ts72xx_wdt_start(wdt);
211 
212  mutex_unlock(&wdt->lock);
213  return nonseekable_open(inode, file);
214 }
215 
216 static int ts72xx_wdt_release(struct inode *inode, struct file *file)
217 {
218  struct ts72xx_wdt *wdt = file->private_data;
219 
220  if (mutex_lock_interruptible(&wdt->lock))
221  return -ERESTARTSYS;
222 
223  if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) {
224  ts72xx_wdt_stop(wdt);
225  } else {
226  dev_warn(&wdt->pdev->dev,
227  "TS-72XX WDT device closed unexpectly. "
228  "Watchdog timer will not stop!\n");
229  /*
230  * Kick it one more time, to give userland some time
231  * to recover (for example, respawning the kicker
232  * daemon).
233  */
234  ts72xx_wdt_kick(wdt);
235  }
236 
237  wdt->flags = 0;
238 
239  mutex_unlock(&wdt->lock);
240  return 0;
241 }
242 
243 static ssize_t ts72xx_wdt_write(struct file *file,
244  const char __user *data,
245  size_t len,
246  loff_t *ppos)
247 {
248  struct ts72xx_wdt *wdt = file->private_data;
249 
250  if (!len)
251  return 0;
252 
253  if (mutex_lock_interruptible(&wdt->lock))
254  return -ERESTARTSYS;
255 
256  ts72xx_wdt_kick(wdt);
257 
258  /*
259  * Support for magic character closing. User process
260  * writes 'V' into the device, just before it is closed.
261  * This means that we know that the wdt timer can be
262  * stopped after user closes the device.
263  */
264  if (!nowayout) {
265  int i;
266 
267  for (i = 0; i < len; i++) {
268  char c;
269 
270  /* In case it was set long ago */
272 
273  if (get_user(c, data + i)) {
274  mutex_unlock(&wdt->lock);
275  return -EFAULT;
276  }
277  if (c == 'V') {
279  break;
280  }
281  }
282  }
283 
284  mutex_unlock(&wdt->lock);
285  return len;
286 }
287 
288 static const struct watchdog_info winfo = {
291  .firmware_version = 1,
292  .identity = "TS-72XX WDT",
293 };
294 
295 static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
296  unsigned long arg)
297 {
298  struct ts72xx_wdt *wdt = file->private_data;
299  void __user *argp = (void __user *)arg;
300  int __user *p = (int __user *)argp;
301  int error = 0;
302 
303  if (mutex_lock_interruptible(&wdt->lock))
304  return -ERESTARTSYS;
305 
306  switch (cmd) {
307  case WDIOC_GETSUPPORT:
308  error = copy_to_user(argp, &winfo, sizeof(winfo));
309  break;
310 
311  case WDIOC_GETSTATUS:
312  case WDIOC_GETBOOTSTATUS:
313  return put_user(0, p);
314 
315  case WDIOC_KEEPALIVE:
316  ts72xx_wdt_kick(wdt);
317  break;
318 
319  case WDIOC_SETOPTIONS: {
320  int options;
321 
322  if (get_user(options, p)) {
323  error = -EFAULT;
324  break;
325  }
326 
327  error = -EINVAL;
328 
329  if ((options & WDIOS_DISABLECARD) != 0) {
330  ts72xx_wdt_stop(wdt);
331  error = 0;
332  }
333  if ((options & WDIOS_ENABLECARD) != 0) {
334  ts72xx_wdt_start(wdt);
335  error = 0;
336  }
337 
338  break;
339  }
340 
341  case WDIOC_SETTIMEOUT: {
342  int new_timeout;
343 
344  if (get_user(new_timeout, p)) {
345  error = -EFAULT;
346  } else {
347  int regval;
348 
349  regval = timeout_to_regval(new_timeout);
350  if (regval < 0) {
351  error = -EINVAL;
352  } else {
353  ts72xx_wdt_stop(wdt);
354  wdt->regval = regval;
355  ts72xx_wdt_start(wdt);
356  }
357  }
358  if (error)
359  break;
360 
361  /*FALLTHROUGH*/
362  }
363 
364  case WDIOC_GETTIMEOUT:
365  if (put_user(regval_to_timeout(wdt->regval), p))
366  error = -EFAULT;
367  break;
368 
369  default:
370  error = -ENOTTY;
371  break;
372  }
373 
374  mutex_unlock(&wdt->lock);
375  return error;
376 }
377 
378 static const struct file_operations ts72xx_wdt_fops = {
379  .owner = THIS_MODULE,
380  .llseek = no_llseek,
381  .open = ts72xx_wdt_open,
382  .release = ts72xx_wdt_release,
383  .write = ts72xx_wdt_write,
384  .unlocked_ioctl = ts72xx_wdt_ioctl,
385 };
386 
387 static struct miscdevice ts72xx_wdt_miscdev = {
388  .minor = WATCHDOG_MINOR,
389  .name = "watchdog",
390  .fops = &ts72xx_wdt_fops,
391 };
392 
393 static __devinit int ts72xx_wdt_probe(struct platform_device *pdev)
394 {
395  struct ts72xx_wdt *wdt;
396  struct resource *r1, *r2;
397  int error = 0;
398 
399  wdt = kzalloc(sizeof(struct ts72xx_wdt), GFP_KERNEL);
400  if (!wdt) {
401  dev_err(&pdev->dev, "failed to allocate memory\n");
402  return -ENOMEM;
403  }
404 
405  r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
406  if (!r1) {
407  dev_err(&pdev->dev, "failed to get memory resource\n");
408  error = -ENODEV;
409  goto fail;
410  }
411 
412  r1 = request_mem_region(r1->start, resource_size(r1), pdev->name);
413  if (!r1) {
414  dev_err(&pdev->dev, "cannot request memory region\n");
415  error = -EBUSY;
416  goto fail;
417  }
418 
419  wdt->control_reg = ioremap(r1->start, resource_size(r1));
420  if (!wdt->control_reg) {
421  dev_err(&pdev->dev, "failed to map memory\n");
422  error = -ENODEV;
423  goto fail_free_control;
424  }
425 
426  r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
427  if (!r2) {
428  dev_err(&pdev->dev, "failed to get memory resource\n");
429  error = -ENODEV;
430  goto fail_unmap_control;
431  }
432 
433  r2 = request_mem_region(r2->start, resource_size(r2), pdev->name);
434  if (!r2) {
435  dev_err(&pdev->dev, "cannot request memory region\n");
436  error = -EBUSY;
437  goto fail_unmap_control;
438  }
439 
440  wdt->feed_reg = ioremap(r2->start, resource_size(r2));
441  if (!wdt->feed_reg) {
442  dev_err(&pdev->dev, "failed to map memory\n");
443  error = -ENODEV;
444  goto fail_free_feed;
445  }
446 
447  platform_set_drvdata(pdev, wdt);
448  ts72xx_wdt_pdev = pdev;
449  wdt->pdev = pdev;
450  mutex_init(&wdt->lock);
451 
452  /* make sure that the watchdog is disabled */
453  ts72xx_wdt_stop(wdt);
454 
455  error = misc_register(&ts72xx_wdt_miscdev);
456  if (error) {
457  dev_err(&pdev->dev, "failed to register miscdev\n");
458  goto fail_unmap_feed;
459  }
460 
461  dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
462 
463  return 0;
464 
465 fail_unmap_feed:
466  platform_set_drvdata(pdev, NULL);
467  iounmap(wdt->feed_reg);
468 fail_free_feed:
469  release_mem_region(r2->start, resource_size(r2));
470 fail_unmap_control:
471  iounmap(wdt->control_reg);
472 fail_free_control:
473  release_mem_region(r1->start, resource_size(r1));
474 fail:
475  kfree(wdt);
476  return error;
477 }
478 
479 static __devexit int ts72xx_wdt_remove(struct platform_device *pdev)
480 {
481  struct ts72xx_wdt *wdt = platform_get_drvdata(pdev);
482  struct resource *res;
483  int error;
484 
485  error = misc_deregister(&ts72xx_wdt_miscdev);
486  platform_set_drvdata(pdev, NULL);
487 
488  iounmap(wdt->feed_reg);
489  res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
490  release_mem_region(res->start, resource_size(res));
491 
492  iounmap(wdt->control_reg);
493  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
494  release_mem_region(res->start, resource_size(res));
495 
496  kfree(wdt);
497  return error;
498 }
499 
500 static struct platform_driver ts72xx_wdt_driver = {
501  .probe = ts72xx_wdt_probe,
502  .remove = __devexit_p(ts72xx_wdt_remove),
503  .driver = {
504  .name = "ts72xx-wdt",
505  .owner = THIS_MODULE,
506  },
507 };
508 
509 module_platform_driver(ts72xx_wdt_driver);
510 
511 MODULE_AUTHOR("Mika Westerberg <[email protected]>");
512 MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
513 MODULE_LICENSE("GPL");
514 MODULE_ALIAS("platform:ts72xx-wdt");