Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
iop_wdt.c
Go to the documentation of this file.
1 /*
2  * drivers/char/watchdog/iop_wdt.c
3  *
4  * WDT driver for Intel I/O Processors
5  * Copyright (C) 2005, Intel Corporation.
6  *
7  * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc.
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms and conditions of the GNU General Public License,
11  * version 2, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20  * Place - Suite 330, Boston, MA 02111-1307 USA.
21  *
22  * Curt E Bruns <[email protected]>
23  * Peter Milne <[email protected]>
24  * Dan Williams <[email protected]>
25  */
26 
27 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28 
29 #include <linux/module.h>
30 #include <linux/kernel.h>
31 #include <linux/fs.h>
32 #include <linux/init.h>
33 #include <linux/device.h>
34 #include <linux/miscdevice.h>
35 #include <linux/watchdog.h>
36 #include <linux/uaccess.h>
37 #include <mach/hardware.h>
38 
39 static bool nowayout = WATCHDOG_NOWAYOUT;
40 static unsigned long wdt_status;
41 static unsigned long boot_status;
42 static DEFINE_SPINLOCK(wdt_lock);
43 
44 #define WDT_IN_USE 0
45 #define WDT_OK_TO_CLOSE 1
46 #define WDT_ENABLED 2
47 
48 static unsigned long iop_watchdog_timeout(void)
49 {
50  return (0xffffffffUL / get_iop_tick_rate());
51 }
52 
57 static int wdt_supports_disable(void)
58 {
59  int can_disable;
60 
62  can_disable = 1;
63  else
64  can_disable = 0;
65 
66  return can_disable;
67 }
68 
69 static void wdt_enable(void)
70 {
71  /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF
72  * Takes approx. 10.7s to timeout
73  */
74  spin_lock(&wdt_lock);
75  write_wdtcr(IOP_WDTCR_EN_ARM);
76  write_wdtcr(IOP_WDTCR_EN);
77  spin_unlock(&wdt_lock);
78 }
79 
80 /* returns 0 if the timer was successfully disabled */
81 static int wdt_disable(void)
82 {
83  /* Stop Counting */
84  if (wdt_supports_disable()) {
85  spin_lock(&wdt_lock);
86  write_wdtcr(IOP_WDTCR_DIS_ARM);
87  write_wdtcr(IOP_WDTCR_DIS);
88  clear_bit(WDT_ENABLED, &wdt_status);
89  spin_unlock(&wdt_lock);
90  pr_info("Disabled\n");
91  return 0;
92  } else
93  return 1;
94 }
95 
96 static int iop_wdt_open(struct inode *inode, struct file *file)
97 {
98  if (test_and_set_bit(WDT_IN_USE, &wdt_status))
99  return -EBUSY;
100 
101  clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
102  wdt_enable();
103  set_bit(WDT_ENABLED, &wdt_status);
104  return nonseekable_open(inode, file);
105 }
106 
107 static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len,
108  loff_t *ppos)
109 {
110  if (len) {
111  if (!nowayout) {
112  size_t i;
113 
114  clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
115 
116  for (i = 0; i != len; i++) {
117  char c;
118 
119  if (get_user(c, data + i))
120  return -EFAULT;
121  if (c == 'V')
122  set_bit(WDT_OK_TO_CLOSE, &wdt_status);
123  }
124  }
125  wdt_enable();
126  }
127  return len;
128 }
129 
130 static const struct watchdog_info ident = {
132  .identity = "iop watchdog",
133 };
134 
135 static long iop_wdt_ioctl(struct file *file,
136  unsigned int cmd, unsigned long arg)
137 {
138  int options;
139  int ret = -ENOTTY;
140  int __user *argp = (int __user *)arg;
141 
142  switch (cmd) {
143  case WDIOC_GETSUPPORT:
144  if (copy_to_user(argp, &ident, sizeof(ident)))
145  ret = -EFAULT;
146  else
147  ret = 0;
148  break;
149 
150  case WDIOC_GETSTATUS:
151  ret = put_user(0, argp);
152  break;
153 
154  case WDIOC_GETBOOTSTATUS:
155  ret = put_user(boot_status, argp);
156  break;
157 
158  case WDIOC_SETOPTIONS:
159  if (get_user(options, (int *)arg))
160  return -EFAULT;
161 
162  if (options & WDIOS_DISABLECARD) {
163  if (!nowayout) {
164  if (wdt_disable() == 0) {
165  set_bit(WDT_OK_TO_CLOSE, &wdt_status);
166  ret = 0;
167  } else
168  ret = -ENXIO;
169  } else
170  ret = 0;
171  }
172  if (options & WDIOS_ENABLECARD) {
173  wdt_enable();
174  ret = 0;
175  }
176  break;
177 
178  case WDIOC_KEEPALIVE:
179  wdt_enable();
180  ret = 0;
181  break;
182 
183  case WDIOC_GETTIMEOUT:
184  ret = put_user(iop_watchdog_timeout(), argp);
185  break;
186  }
187  return ret;
188 }
189 
190 static int iop_wdt_release(struct inode *inode, struct file *file)
191 {
192  int state = 1;
193  if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
194  if (test_bit(WDT_ENABLED, &wdt_status))
195  state = wdt_disable();
196 
197  /* if the timer is not disabled reload and notify that we are still
198  * going down
199  */
200  if (state != 0) {
201  wdt_enable();
202  pr_crit("Device closed unexpectedly - reset in %lu seconds\n",
203  iop_watchdog_timeout());
204  }
205 
206  clear_bit(WDT_IN_USE, &wdt_status);
207  clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
208 
209  return 0;
210 }
211 
212 static const struct file_operations iop_wdt_fops = {
213  .owner = THIS_MODULE,
214  .llseek = no_llseek,
215  .write = iop_wdt_write,
216  .unlocked_ioctl = iop_wdt_ioctl,
217  .open = iop_wdt_open,
218  .release = iop_wdt_release,
219 };
220 
221 static struct miscdevice iop_wdt_miscdev = {
222  .minor = WATCHDOG_MINOR,
223  .name = "watchdog",
224  .fops = &iop_wdt_fops,
225 };
226 
227 static int __init iop_wdt_init(void)
228 {
229  int ret;
230 
231  /* check if the reset was caused by the watchdog timer */
232  boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0;
233 
234  /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset
235  * NOTE: An IB Reset will Reset both cores in the IOP342
236  */
237  write_wdtsr(IOP13XX_WDTCR_IB_RESET);
238 
239  /* Register after we have the device set up so we cannot race
240  with an open */
241  ret = misc_register(&iop_wdt_miscdev);
242  if (ret == 0)
243  pr_info("timeout %lu sec\n", iop_watchdog_timeout());
244 
245  return ret;
246 }
247 
248 static void __exit iop_wdt_exit(void)
249 {
250  misc_deregister(&iop_wdt_miscdev);
251 }
252 
253 module_init(iop_wdt_init);
254 module_exit(iop_wdt_exit);
255 
256 module_param(nowayout, bool, 0);
257 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
258 
259 MODULE_AUTHOR("Curt E Bruns <[email protected]>");
260 MODULE_DESCRIPTION("iop watchdog timer driver");
261 MODULE_LICENSE("GPL");