Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pnx833x_wdt.c
Go to the documentation of this file.
1 /*
2  * PNX833x Hardware Watchdog Driver
3  * Copyright 2008 NXP Semiconductors
4  * Daniel Laird <[email protected]>
5  * Andre McCurdy <[email protected]>
6  *
7  * Heavily based upon - IndyDog 0.3
8  * A Hardware Watchdog Device for SGI IP22
9  *
10  * (c) Copyright 2002 Guido Guenther <[email protected]>, All Rights Reserved.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version
15  * 2 of the License, or (at your option) any later version.
16  *
17  * based on softdog.c by Alan Cox <[email protected]>
18  */
19 
20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21 
22 #include <linux/module.h>
23 #include <linux/moduleparam.h>
24 #include <linux/types.h>
25 #include <linux/kernel.h>
26 #include <linux/fs.h>
27 #include <linux/mm.h>
28 #include <linux/miscdevice.h>
29 #include <linux/watchdog.h>
30 #include <linux/notifier.h>
31 #include <linux/reboot.h>
32 #include <linux/init.h>
34 
35 #define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */
36 #define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */
37 #define PNX_WATCHDOG_TIMEOUT (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY)
38 #define PNX_TIMEOUT_VALUE 2040000000U
39 
41 #define PNX833X_CONFIG (0x07000U)
42 #define PNX833X_CONFIG_CPU_WATCHDOG (0x54)
43 #define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58)
44 #define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c)
45 
47 #define PNX833X_RESET (0x08000U)
48 #define PNX833X_RESET_CONFIG (0x08)
49 
50 static int pnx833x_wdt_alive;
51 
52 /* Set default timeout in MHZ.*/
53 static int pnx833x_wdt_timeout = PNX_WATCHDOG_TIMEOUT;
54 module_param(pnx833x_wdt_timeout, int, 0);
55 MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default="
56  __MODULE_STRING(PNX_TIMEOUT_VALUE) "(30 seconds).");
57 
58 static bool nowayout = WATCHDOG_NOWAYOUT;
59 module_param(nowayout, bool, 0);
60 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
62 
63 #define START_DEFAULT 1
64 static int start_enabled = START_DEFAULT;
65 module_param(start_enabled, int, 0);
66 MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion "
67  "(default=" __MODULE_STRING(START_DEFAULT) ")");
68 
69 static void pnx833x_wdt_start(void)
70 {
71  /* Enable watchdog causing reset. */
73  /* Set timeout.*/
75  PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
76  /* Enable watchdog. */
79 
80  pr_info("Started watchdog timer\n");
81 }
82 
83 static void pnx833x_wdt_stop(void)
84 {
85  /* Disable watchdog causing reset. */
86  PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE;
87  /* Disable watchdog.*/
90 
91  pr_info("Stopped watchdog timer\n");
92 }
93 
94 static void pnx833x_wdt_ping(void)
95 {
97  PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
98 }
99 
100 /*
101  * Allow only one person to hold it open
102  */
103 static int pnx833x_wdt_open(struct inode *inode, struct file *file)
104 {
105  if (test_and_set_bit(0, &pnx833x_wdt_alive))
106  return -EBUSY;
107 
108  if (nowayout)
109  __module_get(THIS_MODULE);
110 
111  /* Activate timer */
112  if (!start_enabled)
113  pnx833x_wdt_start();
114 
115  pnx833x_wdt_ping();
116 
117  pr_info("Started watchdog timer\n");
118 
119  return nonseekable_open(inode, file);
120 }
121 
122 static int pnx833x_wdt_release(struct inode *inode, struct file *file)
123 {
124  /* Shut off the timer.
125  * Lock it in if it's a module and we defined ...NOWAYOUT */
126  if (!nowayout)
127  pnx833x_wdt_stop(); /* Turn the WDT off */
128 
129  clear_bit(0, &pnx833x_wdt_alive);
130  return 0;
131 }
132 
133 static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
134 {
135  /* Refresh the timer. */
136  if (len)
137  pnx833x_wdt_ping();
138 
139  return len;
140 }
141 
142 static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd,
143  unsigned long arg)
144 {
145  int options, new_timeout = 0;
146  uint32_t timeout, timeout_left = 0;
147 
148  static const struct watchdog_info ident = {
150  .firmware_version = 0,
151  .identity = "Hardware Watchdog for PNX833x",
152  };
153 
154  switch (cmd) {
155  default:
156  return -ENOTTY;
157 
158  case WDIOC_GETSUPPORT:
159  if (copy_to_user((struct watchdog_info *)arg,
160  &ident, sizeof(ident)))
161  return -EFAULT;
162  return 0;
163 
164  case WDIOC_GETSTATUS:
165  case WDIOC_GETBOOTSTATUS:
166  return put_user(0, (int *)arg);
167 
168  case WDIOC_SETOPTIONS:
169  if (get_user(options, (int *)arg))
170  return -EFAULT;
171 
172  if (options & WDIOS_DISABLECARD)
173  pnx833x_wdt_stop();
174 
175  if (options & WDIOS_ENABLECARD)
176  pnx833x_wdt_start();
177 
178  return 0;
179 
180  case WDIOC_KEEPALIVE:
181  pnx833x_wdt_ping();
182  return 0;
183 
184  case WDIOC_SETTIMEOUT:
185  {
186  if (get_user(new_timeout, (int *)arg))
187  return -EFAULT;
188 
189  pnx833x_wdt_timeout = new_timeout;
192  return put_user(new_timeout, (int *)arg);
193  }
194 
195  case WDIOC_GETTIMEOUT:
196  timeout = PNX833X_REG(PNX833X_CONFIG +
198  return put_user(timeout, (int *)arg);
199 
200  case WDIOC_GETTIMELEFT:
201  timeout_left = PNX833X_REG(PNX833X_CONFIG +
203  return put_user(timeout_left, (int *)arg);
204 
205  }
206 }
207 
208 static int pnx833x_wdt_notify_sys(struct notifier_block *this,
209  unsigned long code, void *unused)
210 {
211  if (code == SYS_DOWN || code == SYS_HALT)
212  pnx833x_wdt_stop(); /* Turn the WDT off */
213 
214  return NOTIFY_DONE;
215 }
216 
217 static const struct file_operations pnx833x_wdt_fops = {
218  .owner = THIS_MODULE,
219  .llseek = no_llseek,
220  .write = pnx833x_wdt_write,
221  .unlocked_ioctl = pnx833x_wdt_ioctl,
222  .open = pnx833x_wdt_open,
223  .release = pnx833x_wdt_release,
224 };
225 
226 static struct miscdevice pnx833x_wdt_miscdev = {
227  .minor = WATCHDOG_MINOR,
228  .name = "watchdog",
229  .fops = &pnx833x_wdt_fops,
230 };
231 
232 static struct notifier_block pnx833x_wdt_notifier = {
233  .notifier_call = pnx833x_wdt_notify_sys,
234 };
235 
236 static int __init watchdog_init(void)
237 {
238  int ret, cause;
239 
240  /* Lets check the reason for the reset.*/
241  cause = PNX833X_REG(PNX833X_RESET);
242  /*If bit 31 is set then watchdog was cause of reset.*/
243  if (cause & 0x80000000) {
244  pr_info("The system was previously reset due to the watchdog firing - please investigate...\n");
245  }
246 
247  ret = register_reboot_notifier(&pnx833x_wdt_notifier);
248  if (ret) {
249  pr_err("cannot register reboot notifier (err=%d)\n", ret);
250  return ret;
251  }
252 
253  ret = misc_register(&pnx833x_wdt_miscdev);
254  if (ret) {
255  pr_err("cannot register miscdev on minor=%d (err=%d)\n",
256  WATCHDOG_MINOR, ret);
257  unregister_reboot_notifier(&pnx833x_wdt_notifier);
258  return ret;
259  }
260 
261  pr_info("Hardware Watchdog Timer for PNX833x: Version 0.1\n");
262 
263  if (start_enabled)
264  pnx833x_wdt_start();
265 
266  return 0;
267 }
268 
269 static void __exit watchdog_exit(void)
270 {
271  misc_deregister(&pnx833x_wdt_miscdev);
272  unregister_reboot_notifier(&pnx833x_wdt_notifier);
273 }
274 
275 module_init(watchdog_init);
276 module_exit(watchdog_exit);
277 
278 MODULE_AUTHOR("Daniel Laird/Andre McCurdy");
279 MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x");
280 MODULE_LICENSE("GPL");