Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ath79_wdt.c
Go to the documentation of this file.
1 /*
2  * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer.
3  *
4  * Copyright (C) 2008-2011 Gabor Juhos <[email protected]>
5  * Copyright (C) 2008 Imre Kaloz <[email protected]>
6  *
7  * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
8  * Author: Deepak Saxena <[email protected]>
9  * Copyright 2004 (c) MontaVista, Software, Inc.
10  *
11  * which again was based on sa1100 driver,
12  * Copyright (C) 2000 Oleg Drokin <[email protected]>
13  *
14  * This program is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU General Public License version 2 as published
16  * by the Free Software Foundation.
17  *
18  */
19 
20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21 
22 #include <linux/bitops.h>
23 #include <linux/errno.h>
24 #include <linux/fs.h>
25 #include <linux/init.h>
26 #include <linux/kernel.h>
27 #include <linux/miscdevice.h>
28 #include <linux/module.h>
29 #include <linux/moduleparam.h>
30 #include <linux/platform_device.h>
31 #include <linux/types.h>
32 #include <linux/watchdog.h>
33 #include <linux/clk.h>
34 #include <linux/err.h>
35 
36 #include <asm/mach-ath79/ath79.h>
38 
39 #define DRIVER_NAME "ath79-wdt"
40 
41 #define WDT_TIMEOUT 15 /* seconds */
42 
43 #define WDOG_CTRL_LAST_RESET BIT(31)
44 #define WDOG_CTRL_ACTION_MASK 3
45 #define WDOG_CTRL_ACTION_NONE 0 /* no action */
46 #define WDOG_CTRL_ACTION_GPI 1 /* general purpose interrupt */
47 #define WDOG_CTRL_ACTION_NMI 2 /* NMI */
48 #define WDOG_CTRL_ACTION_FCR 3 /* full chip reset */
49 
50 static bool nowayout = WATCHDOG_NOWAYOUT;
51 module_param(nowayout, bool, 0);
52 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
53  "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
54 
55 static int timeout = WDT_TIMEOUT;
56 module_param(timeout, int, 0);
57 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
58  "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)");
59 
60 static unsigned long wdt_flags;
61 
62 #define WDT_FLAGS_BUSY 0
63 #define WDT_FLAGS_EXPECT_CLOSE 1
64 
65 static struct clk *wdt_clk;
66 static unsigned long wdt_freq;
67 static int boot_status;
68 static int max_timeout;
69 
70 static inline void ath79_wdt_keepalive(void)
71 {
72  ath79_reset_wr(AR71XX_RESET_REG_WDOG, wdt_freq * timeout);
73  /* flush write */
74  ath79_reset_rr(AR71XX_RESET_REG_WDOG);
75 }
76 
77 static inline void ath79_wdt_enable(void)
78 {
79  ath79_wdt_keepalive();
81  /* flush write */
82  ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
83 }
84 
85 static inline void ath79_wdt_disable(void)
86 {
88  /* flush write */
89  ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
90 }
91 
92 static int ath79_wdt_set_timeout(int val)
93 {
94  if (val < 1 || val > max_timeout)
95  return -EINVAL;
96 
97  timeout = val;
98  ath79_wdt_keepalive();
99 
100  return 0;
101 }
102 
103 static int ath79_wdt_open(struct inode *inode, struct file *file)
104 {
105  if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags))
106  return -EBUSY;
107 
108  clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
109  ath79_wdt_enable();
110 
111  return nonseekable_open(inode, file);
112 }
113 
114 static int ath79_wdt_release(struct inode *inode, struct file *file)
115 {
116  if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
117  ath79_wdt_disable();
118  else {
119  pr_crit("device closed unexpectedly, watchdog timer will not stop!\n");
120  ath79_wdt_keepalive();
121  }
122 
123  clear_bit(WDT_FLAGS_BUSY, &wdt_flags);
124  clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
125 
126  return 0;
127 }
128 
129 static ssize_t ath79_wdt_write(struct file *file, const char *data,
130  size_t len, loff_t *ppos)
131 {
132  if (len) {
133  if (!nowayout) {
134  size_t i;
135 
136  clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
137 
138  for (i = 0; i != len; i++) {
139  char c;
140 
141  if (get_user(c, data + i))
142  return -EFAULT;
143 
144  if (c == 'V')
146  &wdt_flags);
147  }
148  }
149 
150  ath79_wdt_keepalive();
151  }
152 
153  return len;
154 }
155 
156 static const struct watchdog_info ath79_wdt_info = {
159  .firmware_version = 0,
160  .identity = "ATH79 watchdog",
161 };
162 
163 static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
164  unsigned long arg)
165 {
166  void __user *argp = (void __user *)arg;
167  int __user *p = argp;
168  int err;
169  int t;
170 
171  switch (cmd) {
172  case WDIOC_GETSUPPORT:
173  err = copy_to_user(argp, &ath79_wdt_info,
174  sizeof(ath79_wdt_info)) ? -EFAULT : 0;
175  break;
176 
177  case WDIOC_GETSTATUS:
178  err = put_user(0, p);
179  break;
180 
181  case WDIOC_GETBOOTSTATUS:
182  err = put_user(boot_status, p);
183  break;
184 
185  case WDIOC_KEEPALIVE:
186  ath79_wdt_keepalive();
187  err = 0;
188  break;
189 
190  case WDIOC_SETTIMEOUT:
191  err = get_user(t, p);
192  if (err)
193  break;
194 
195  err = ath79_wdt_set_timeout(t);
196  if (err)
197  break;
198 
199  /* fallthrough */
200  case WDIOC_GETTIMEOUT:
201  err = put_user(timeout, p);
202  break;
203 
204  default:
205  err = -ENOTTY;
206  break;
207  }
208 
209  return err;
210 }
211 
212 static const struct file_operations ath79_wdt_fops = {
213  .owner = THIS_MODULE,
214  .llseek = no_llseek,
215  .write = ath79_wdt_write,
216  .unlocked_ioctl = ath79_wdt_ioctl,
217  .open = ath79_wdt_open,
218  .release = ath79_wdt_release,
219 };
220 
221 static struct miscdevice ath79_wdt_miscdev = {
222  .minor = WATCHDOG_MINOR,
223  .name = "watchdog",
224  .fops = &ath79_wdt_fops,
225 };
226 
227 static int __devinit ath79_wdt_probe(struct platform_device *pdev)
228 {
229  u32 ctrl;
230  int err;
231 
232  wdt_clk = clk_get(&pdev->dev, "wdt");
233  if (IS_ERR(wdt_clk))
234  return PTR_ERR(wdt_clk);
235 
236  err = clk_enable(wdt_clk);
237  if (err)
238  goto err_clk_put;
239 
240  wdt_freq = clk_get_rate(wdt_clk);
241  if (!wdt_freq) {
242  err = -EINVAL;
243  goto err_clk_disable;
244  }
245 
246  max_timeout = (0xfffffffful / wdt_freq);
247  if (timeout < 1 || timeout > max_timeout) {
248  timeout = max_timeout;
249  dev_info(&pdev->dev,
250  "timeout value must be 0 < timeout < %d, using %d\n",
251  max_timeout, timeout);
252  }
253 
254  ctrl = ath79_reset_rr(AR71XX_RESET_REG_WDOG_CTRL);
255  boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
256 
257  err = misc_register(&ath79_wdt_miscdev);
258  if (err) {
259  dev_err(&pdev->dev,
260  "unable to register misc device, err=%d\n", err);
261  goto err_clk_disable;
262  }
263 
264  return 0;
265 
266 err_clk_disable:
267  clk_disable(wdt_clk);
268 err_clk_put:
269  clk_put(wdt_clk);
270  return err;
271 }
272 
273 static int __devexit ath79_wdt_remove(struct platform_device *pdev)
274 {
275  misc_deregister(&ath79_wdt_miscdev);
276  clk_disable(wdt_clk);
277  clk_put(wdt_clk);
278  return 0;
279 }
280 
281 static void ath97_wdt_shutdown(struct platform_device *pdev)
282 {
283  ath79_wdt_disable();
284 }
285 
286 static struct platform_driver ath79_wdt_driver = {
287  .remove = __devexit_p(ath79_wdt_remove),
288  .shutdown = ath97_wdt_shutdown,
289  .driver = {
290  .name = DRIVER_NAME,
291  .owner = THIS_MODULE,
292  },
293 };
294 
295 static int __init ath79_wdt_init(void)
296 {
297  return platform_driver_probe(&ath79_wdt_driver, ath79_wdt_probe);
298 }
299 module_init(ath79_wdt_init);
300 
301 static void __exit ath79_wdt_exit(void)
302 {
303  platform_driver_unregister(&ath79_wdt_driver);
304 }
305 module_exit(ath79_wdt_exit);
306 
307 MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
308 MODULE_AUTHOR("Gabor Juhos <[email protected]");
309 MODULE_AUTHOR("Imre Kaloz <[email protected]");
310 MODULE_LICENSE("GPL v2");
311 MODULE_ALIAS("platform:" DRIVER_NAME);