OMAP3: PM: Fixed VDD2 control to work from both sysfs and SRF API
[linux-ginger.git] / drivers / cbus / retu-wdt.c
blob689a5dc6466addd5927ac159a57b91aea4a7c310
1 /**
2 * drivers/cbus/retu-wdt.c
4 * Driver for Retu watchdog
6 * Copyright (C) 2004, 2005 Nokia Corporation
8 * Written by Amit Kucheria <amit.kucheria@nokia.com>
10 * This file is subject to the terms and conditions of the GNU General
11 * Public License. See the file "COPYING" in the main directory of this
12 * archive for more details.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/device.h>
27 #include <linux/init.h>
28 #include <linux/fs.h>
29 #include <linux/io.h>
30 #include <linux/platform_device.h>
32 #include <linux/completion.h>
33 #include <linux/errno.h>
34 #include <linux/moduleparam.h>
35 #include <linux/platform_device.h>
36 #include <linux/miscdevice.h>
37 #include <linux/watchdog.h>
39 #include <asm/uaccess.h>
41 #include <plat/prcm.h>
43 #include "cbus.h"
44 #include "retu.h"
46 /* Watchdog timeout in seconds */
47 #define RETU_WDT_MIN_TIMER 0
48 #define RETU_WDT_DEFAULT_TIMER 32
49 #define RETU_WDT_MAX_TIMER 63
51 static struct completion retu_wdt_completion;
52 static DEFINE_MUTEX(retu_wdt_mutex);
54 /* Current period of watchdog */
55 static unsigned int period_val = RETU_WDT_DEFAULT_TIMER;
56 static int counter_param = RETU_WDT_MAX_TIMER;
58 struct retu_wdt_dev {
59 struct device *dev;
60 int users;
61 struct miscdevice retu_wdt_miscdev;
62 struct timer_list ping_timer;
65 static struct retu_wdt_dev *retu_wdt;
67 static void retu_wdt_set_ping_timer(unsigned long enable);
69 static int _retu_modify_counter(unsigned int new)
71 retu_write_reg(RETU_REG_WATCHDOG, (u16)new);
73 return 0;
76 static int retu_modify_counter(unsigned int new)
78 if (new < RETU_WDT_MIN_TIMER || new > RETU_WDT_MAX_TIMER)
79 return -EINVAL;
81 mutex_lock(&retu_wdt_mutex);
82 period_val = new;
83 _retu_modify_counter(period_val);
84 mutex_unlock(&retu_wdt_mutex);
86 return 0;
89 static ssize_t retu_wdt_period_show(struct device *dev,
90 struct device_attribute *attr, char *buf)
92 /* Show current max counter */
93 return sprintf(buf, "%u\n", (u16)period_val);
97 * Note: This inteface is non-standard and likely to disappear!
98 * Use /dev/watchdog instead, that's the standard.
100 static ssize_t retu_wdt_period_store(struct device *dev,
101 struct device_attribute *attr,
102 const char *buf, size_t count)
104 unsigned int new_period;
105 int ret;
107 #ifdef CONFIG_WATCHDOG_NOWAYOUT
108 retu_wdt_set_ping_timer(0);
109 #endif
111 if (sscanf(buf, "%u", &new_period) != 1) {
112 printk(KERN_ALERT "retu_wdt_period_store: Invalid input\n");
113 return -EINVAL;
116 ret = retu_modify_counter(new_period);
117 if (ret < 0)
118 return ret;
120 return strnlen(buf, count);
123 static ssize_t retu_wdt_counter_show(struct device *dev,
124 struct device_attribute *attr, char *buf)
126 u16 counter;
128 /* Show current value in watchdog counter */
129 counter = retu_read_reg(RETU_REG_WATCHDOG);
131 /* Only the 5 LSB are important */
132 return snprintf(buf, PAGE_SIZE, "%u\n", (counter & 0x3F));
135 static DEVICE_ATTR(period, S_IRUGO | S_IWUSR, retu_wdt_period_show, \
136 retu_wdt_period_store);
137 static DEVICE_ATTR(counter, S_IRUGO, retu_wdt_counter_show, NULL);
139 /*----------------------------------------------------------------------------*/
142 * Since retu watchdog cannot be disabled in hardware, we must kick it
143 * with a timer until userspace watchdog software takes over. Do this
144 * unless /dev/watchdog is open or CONFIG_WATCHDOG_NOWAYOUT is set.
146 static void retu_wdt_set_ping_timer(unsigned long enable)
148 _retu_modify_counter(RETU_WDT_MAX_TIMER);
149 if (enable)
150 mod_timer(&retu_wdt->ping_timer,
151 jiffies + RETU_WDT_DEFAULT_TIMER * HZ);
152 else
153 del_timer_sync(&retu_wdt->ping_timer);
156 static int retu_wdt_open(struct inode *inode, struct file *file)
158 if (test_and_set_bit(1, (unsigned long *)&(retu_wdt->users)))
159 return -EBUSY;
161 file->private_data = (void *)retu_wdt;
162 retu_wdt_set_ping_timer(0);
164 return nonseekable_open(inode, file);
167 static int retu_wdt_release(struct inode *inode, struct file *file)
169 struct retu_wdt_dev *wdev = file->private_data;
171 #ifndef CONFIG_WATCHDOG_NOWAYOUT
172 retu_wdt_set_ping_timer(1);
173 #endif
174 wdev->users = 0;
176 return 0;
179 static ssize_t retu_wdt_write(struct file *file, const char __user *data,
180 size_t len, loff_t *ppos)
182 if (len)
183 retu_modify_counter(RETU_WDT_MAX_TIMER);
185 return len;
188 static int retu_wdt_ioctl(struct inode *inode, struct file *file,
189 unsigned int cmd, unsigned long arg)
191 int new_margin;
193 static struct watchdog_info ident = {
194 .identity = "Retu Watchdog",
195 .options = WDIOF_SETTIMEOUT,
196 .firmware_version = 0,
199 switch (cmd) {
200 default:
201 return -ENOTTY;
202 case WDIOC_GETSUPPORT:
203 return copy_to_user((struct watchdog_info __user *)arg, &ident,
204 sizeof(ident));
205 case WDIOC_GETSTATUS:
206 return put_user(0, (int __user *)arg);
207 case WDIOC_GETBOOTSTATUS:
208 if (cpu_is_omap16xx())
209 return put_user(omap_readw(ARM_SYSST),
210 (int __user *)arg);
211 if (cpu_is_omap24xx())
212 return put_user(omap_prcm_get_reset_sources(),
213 (int __user *)arg);
214 case WDIOC_KEEPALIVE:
215 retu_modify_counter(RETU_WDT_MAX_TIMER);
216 break;
217 case WDIOC_SETTIMEOUT:
218 if (get_user(new_margin, (int __user *)arg))
219 return -EFAULT;
220 retu_modify_counter(new_margin);
221 /* Fall through */
222 case WDIOC_GETTIMEOUT:
223 return put_user(period_val, (int __user *)arg);
226 return 0;
229 /* Start kicking retu watchdog until user space starts doing the kicking */
230 static int __init retu_wdt_ping(void)
233 #ifdef CONFIG_WATCHDOG_NOWAYOUT
234 retu_modify_counter(RETU_WDT_MAX_TIMER);
235 #else
236 retu_wdt_set_ping_timer(1);
237 #endif
239 return 0;
241 late_initcall(retu_wdt_ping);
243 static const struct file_operations retu_wdt_fops = {
244 .owner = THIS_MODULE,
245 .write = retu_wdt_write,
246 .ioctl = retu_wdt_ioctl,
247 .open = retu_wdt_open,
248 .release = retu_wdt_release,
251 /*----------------------------------------------------------------------------*/
253 static int __devinit retu_wdt_probe(struct device *dev)
255 struct retu_wdt_dev *wdev;
256 int ret;
258 wdev = kzalloc(sizeof(struct retu_wdt_dev), GFP_KERNEL);
259 if (!wdev)
260 return -ENOMEM;
262 wdev->users = 0;
264 ret = device_create_file(dev, &dev_attr_period);
265 if (ret) {
266 printk(KERN_ERR "retu_wdt_probe: Error creating "
267 "sys device file: period\n");
268 goto free1;
271 ret = device_create_file(dev, &dev_attr_counter);
272 if (ret) {
273 printk(KERN_ERR "retu_wdt_probe: Error creating "
274 "sys device file: counter\n");
275 goto free2;
278 dev_set_drvdata(dev, wdev);
279 retu_wdt = wdev;
280 wdev->retu_wdt_miscdev.parent = dev;
281 wdev->retu_wdt_miscdev.minor = WATCHDOG_MINOR;
282 wdev->retu_wdt_miscdev.name = "watchdog";
283 wdev->retu_wdt_miscdev.fops = &retu_wdt_fops;
285 ret = misc_register(&(wdev->retu_wdt_miscdev));
286 if (ret)
287 goto free3;
289 setup_timer(&wdev->ping_timer, retu_wdt_set_ping_timer, 1);
291 /* Kick the watchdog for kernel booting to finish */
292 retu_modify_counter(RETU_WDT_MAX_TIMER);
294 return 0;
296 free3:
297 device_remove_file(dev, &dev_attr_counter);
299 free2:
300 device_remove_file(dev, &dev_attr_period);
301 free1:
302 kfree(wdev);
304 return ret;
307 static int __devexit retu_wdt_remove(struct device *dev)
309 struct retu_wdt_dev *wdev;
311 wdev = dev_get_drvdata(dev);
312 misc_deregister(&(wdev->retu_wdt_miscdev));
313 device_remove_file(dev, &dev_attr_period);
314 device_remove_file(dev, &dev_attr_counter);
315 kfree(wdev);
317 return 0;
320 static void retu_wdt_device_release(struct device *dev)
322 complete(&retu_wdt_completion);
325 static struct platform_device retu_wdt_device = {
326 .name = "retu-watchdog",
327 .id = -1,
328 .dev = {
329 .release = retu_wdt_device_release,
333 static struct device_driver retu_wdt_driver = {
334 .name = "retu-watchdog",
335 .bus = &platform_bus_type,
336 .probe = retu_wdt_probe,
337 .remove = __devexit_p(retu_wdt_remove),
340 static int __init retu_wdt_init(void)
342 int ret;
344 init_completion(&retu_wdt_completion);
346 ret = driver_register(&retu_wdt_driver);
347 if (ret)
348 return ret;
350 ret = platform_device_register(&retu_wdt_device);
351 if (ret)
352 goto exit1;
354 /* passed as module parameter? */
355 ret = retu_modify_counter(counter_param);
356 if (ret == -EINVAL) {
357 ret = retu_modify_counter(RETU_WDT_DEFAULT_TIMER);
358 printk(KERN_INFO
359 "retu_wdt_init: Intializing to default value\n");
362 printk(KERN_INFO "Retu watchdog driver initialized\n");
363 return ret;
365 exit1:
366 driver_unregister(&retu_wdt_driver);
367 wait_for_completion(&retu_wdt_completion);
369 return ret;
372 static void __exit retu_wdt_exit(void)
374 platform_device_unregister(&retu_wdt_device);
375 driver_unregister(&retu_wdt_driver);
377 wait_for_completion(&retu_wdt_completion);
380 module_init(retu_wdt_init);
381 module_exit(retu_wdt_exit);
382 module_param(counter_param, int, 0);
384 MODULE_DESCRIPTION("Retu WatchDog");
385 MODULE_AUTHOR("Amit Kucheria");
386 MODULE_LICENSE("GPL");