Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ks8695_wdt.c
Go to the documentation of this file.
1 /*
2  * Watchdog driver for Kendin/Micrel KS8695.
3  *
4  * (C) 2007 Andrew Victor
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12 
13 #include <linux/bitops.h>
14 #include <linux/errno.h>
15 #include <linux/fs.h>
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/miscdevice.h>
19 #include <linux/module.h>
20 #include <linux/moduleparam.h>
21 #include <linux/platform_device.h>
22 #include <linux/types.h>
23 #include <linux/watchdog.h>
24 #include <linux/io.h>
25 #include <linux/uaccess.h>
26 #include <mach/hardware.h>
27 
28 #define KS8695_TMR_OFFSET (0xF0000 + 0xE400)
29 #define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET)
30 
31 /*
32  * Timer registers
33  */
34 #define KS8695_TMCON (0x00) /* Timer Control Register */
35 #define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */
36 #define TMCON_T0EN (1 << 0) /* Timer 0 Enable */
37 
38 /* Timer0 Timeout Counter Register */
39 #define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */
40 
41 #define WDT_DEFAULT_TIME 5 /* seconds */
42 #define WDT_MAX_TIME 171 /* seconds */
43 
44 static int wdt_time = WDT_DEFAULT_TIME;
45 static bool nowayout = WATCHDOG_NOWAYOUT;
46 
47 module_param(wdt_time, int, 0);
48 MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
50 
51 #ifdef CONFIG_WATCHDOG_NOWAYOUT
52 module_param(nowayout, bool, 0);
53 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
55 #endif
56 
57 
58 static unsigned long ks8695wdt_busy;
59 static DEFINE_SPINLOCK(ks8695_lock);
60 
61 /* ......................................................................... */
62 
63 /*
64  * Disable the watchdog.
65  */
66 static inline void ks8695_wdt_stop(void)
67 {
68  unsigned long tmcon;
69 
70  spin_lock(&ks8695_lock);
71  /* disable timer0 */
74  spin_unlock(&ks8695_lock);
75 }
76 
77 /*
78  * Enable and reset the watchdog.
79  */
80 static inline void ks8695_wdt_start(void)
81 {
82  unsigned long tmcon;
83  unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
84 
85  spin_lock(&ks8695_lock);
86  /* disable timer0 */
89 
90  /* program timer0 */
92 
93  /* re-enable timer0 */
96  spin_unlock(&ks8695_lock);
97 }
98 
99 /*
100  * Reload the watchdog timer. (ie, pat the watchdog)
101  */
102 static inline void ks8695_wdt_reload(void)
103 {
104  unsigned long tmcon;
105 
106  spin_lock(&ks8695_lock);
107  /* disable, then re-enable timer0 */
111  spin_unlock(&ks8695_lock);
112 }
113 
114 /*
115  * Change the watchdog time interval.
116  */
117 static int ks8695_wdt_settimeout(int new_time)
118 {
119  /*
120  * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
121  *
122  * Since WDV is a 16-bit counter, the maximum period is
123  * 65536 / 0.256 = 256 seconds.
124  */
125  if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
126  return -EINVAL;
127 
128  /* Set new watchdog time. It will be used when
129  ks8695_wdt_start() is called. */
130  wdt_time = new_time;
131  return 0;
132 }
133 
134 /* ......................................................................... */
135 
136 /*
137  * Watchdog device is opened, and watchdog starts running.
138  */
139 static int ks8695_wdt_open(struct inode *inode, struct file *file)
140 {
141  if (test_and_set_bit(0, &ks8695wdt_busy))
142  return -EBUSY;
143 
144  ks8695_wdt_start();
145  return nonseekable_open(inode, file);
146 }
147 
148 /*
149  * Close the watchdog device.
150  * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
151  * disabled.
152  */
153 static int ks8695_wdt_close(struct inode *inode, struct file *file)
154 {
155  /* Disable the watchdog when file is closed */
156  if (!nowayout)
157  ks8695_wdt_stop();
158  clear_bit(0, &ks8695wdt_busy);
159  return 0;
160 }
161 
162 static const struct watchdog_info ks8695_wdt_info = {
163  .identity = "ks8695 watchdog",
165 };
166 
167 /*
168  * Handle commands from user-space.
169  */
170 static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd,
171  unsigned long arg)
172 {
173  void __user *argp = (void __user *)arg;
174  int __user *p = argp;
175  int new_value;
176 
177  switch (cmd) {
178  case WDIOC_GETSUPPORT:
179  return copy_to_user(argp, &ks8695_wdt_info,
180  sizeof(ks8695_wdt_info)) ? -EFAULT : 0;
181  case WDIOC_GETSTATUS:
182  case WDIOC_GETBOOTSTATUS:
183  return put_user(0, p);
184  case WDIOC_SETOPTIONS:
185  if (get_user(new_value, p))
186  return -EFAULT;
187  if (new_value & WDIOS_DISABLECARD)
188  ks8695_wdt_stop();
189  if (new_value & WDIOS_ENABLECARD)
190  ks8695_wdt_start();
191  return 0;
192  case WDIOC_KEEPALIVE:
193  ks8695_wdt_reload(); /* pat the watchdog */
194  return 0;
195  case WDIOC_SETTIMEOUT:
196  if (get_user(new_value, p))
197  return -EFAULT;
198  if (ks8695_wdt_settimeout(new_value))
199  return -EINVAL;
200  /* Enable new time value */
201  ks8695_wdt_start();
202  /* Return current value */
203  return put_user(wdt_time, p);
204  case WDIOC_GETTIMEOUT:
205  return put_user(wdt_time, p);
206  default:
207  return -ENOTTY;
208  }
209 }
210 
211 /*
212  * Pat the watchdog whenever device is written to.
213  */
214 static ssize_t ks8695_wdt_write(struct file *file, const char *data,
215  size_t len, loff_t *ppos)
216 {
217  ks8695_wdt_reload(); /* pat the watchdog */
218  return len;
219 }
220 
221 /* ......................................................................... */
222 
223 static const struct file_operations ks8695wdt_fops = {
224  .owner = THIS_MODULE,
225  .llseek = no_llseek,
226  .unlocked_ioctl = ks8695_wdt_ioctl,
227  .open = ks8695_wdt_open,
228  .release = ks8695_wdt_close,
229  .write = ks8695_wdt_write,
230 };
231 
232 static struct miscdevice ks8695wdt_miscdev = {
233  .minor = WATCHDOG_MINOR,
234  .name = "watchdog",
235  .fops = &ks8695wdt_fops,
236 };
237 
238 static int __devinit ks8695wdt_probe(struct platform_device *pdev)
239 {
240  int res;
241 
242  if (ks8695wdt_miscdev.parent)
243  return -EBUSY;
244  ks8695wdt_miscdev.parent = &pdev->dev;
245 
246  res = misc_register(&ks8695wdt_miscdev);
247  if (res)
248  return res;
249 
250  pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n",
251  wdt_time, nowayout ? ", nowayout" : "");
252  return 0;
253 }
254 
255 static int __devexit ks8695wdt_remove(struct platform_device *pdev)
256 {
257  int res;
258 
259  res = misc_deregister(&ks8695wdt_miscdev);
260  if (!res)
261  ks8695wdt_miscdev.parent = NULL;
262 
263  return res;
264 }
265 
266 static void ks8695wdt_shutdown(struct platform_device *pdev)
267 {
268  ks8695_wdt_stop();
269 }
270 
271 #ifdef CONFIG_PM
272 
273 static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message)
274 {
275  ks8695_wdt_stop();
276  return 0;
277 }
278 
279 static int ks8695wdt_resume(struct platform_device *pdev)
280 {
281  if (ks8695wdt_busy)
282  ks8695_wdt_start();
283  return 0;
284 }
285 
286 #else
287 #define ks8695wdt_suspend NULL
288 #define ks8695wdt_resume NULL
289 #endif
290 
291 static struct platform_driver ks8695wdt_driver = {
292  .probe = ks8695wdt_probe,
293  .remove = __devexit_p(ks8695wdt_remove),
294  .shutdown = ks8695wdt_shutdown,
295  .suspend = ks8695wdt_suspend,
296  .resume = ks8695wdt_resume,
297  .driver = {
298  .name = "ks8695_wdt",
299  .owner = THIS_MODULE,
300  },
301 };
302 
303 static int __init ks8695_wdt_init(void)
304 {
305  /* Check that the heartbeat value is within range;
306  if not reset to the default */
307  if (ks8695_wdt_settimeout(wdt_time)) {
308  ks8695_wdt_settimeout(WDT_DEFAULT_TIME);
309  pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i"
310  ", using %d\n", wdt_time, WDT_MAX_TIME);
311  }
312  return platform_driver_register(&ks8695wdt_driver);
313 }
314 
315 static void __exit ks8695_wdt_exit(void)
316 {
317  platform_driver_unregister(&ks8695wdt_driver);
318 }
319 
320 module_init(ks8695_wdt_init);
321 module_exit(ks8695_wdt_exit);
322 
323 MODULE_AUTHOR("Andrew Victor");
324 MODULE_DESCRIPTION("Watchdog driver for KS8695");
325 MODULE_LICENSE("GPL");
327 MODULE_ALIAS("platform:ks8695_wdt");