mm: thp: set the accessed flag for old pages on access fault
[pandora-kernel.git] / drivers / watchdog / stmp3xxx_wdt.c
1 /*
2  * Watchdog driver for Freescale STMP37XX/STMP378X
3  *
4  * Author: Vitaly Wool <vital@embeddedalley.com>
5  *
6  * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
7  * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
8  */
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/fs.h>
12 #include <linux/miscdevice.h>
13 #include <linux/watchdog.h>
14 #include <linux/platform_device.h>
15 #include <linux/spinlock.h>
16 #include <linux/uaccess.h>
17 #include <linux/module.h>
18
19 #include <mach/platform.h>
20 #include <mach/regs-rtc.h>
21
22 #define DEFAULT_HEARTBEAT       19
23 #define MAX_HEARTBEAT           (0x10000000 >> 6)
24
25 /* missing bitmask in headers */
26 #define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER     0x80000000
27
28 #define WDT_IN_USE              0
29 #define WDT_OK_TO_CLOSE         1
30
31 #define WDOG_COUNTER_RATE       1000 /* 1 kHz clock */
32
33 static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock);
34 static unsigned long wdt_status;
35 static const int nowayout = WATCHDOG_NOWAYOUT;
36 static int heartbeat = DEFAULT_HEARTBEAT;
37 static unsigned long boot_status;
38
39 static void wdt_enable(u32 value)
40 {
41         spin_lock(&stmp3xxx_wdt_io_lock);
42         __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG);
43         stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
44         stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
45                         REGS_RTC_BASE + HW_RTC_PERSISTENT1);
46         spin_unlock(&stmp3xxx_wdt_io_lock);
47 }
48
49 static void wdt_disable(void)
50 {
51         spin_lock(&stmp3xxx_wdt_io_lock);
52         stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
53                         REGS_RTC_BASE + HW_RTC_PERSISTENT1);
54         stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
55         spin_unlock(&stmp3xxx_wdt_io_lock);
56 }
57
58 static void wdt_ping(void)
59 {
60         wdt_enable(heartbeat * WDOG_COUNTER_RATE);
61 }
62
63 static int stmp3xxx_wdt_open(struct inode *inode, struct file *file)
64 {
65         if (test_and_set_bit(WDT_IN_USE, &wdt_status))
66                 return -EBUSY;
67
68         clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
69         wdt_ping();
70
71         return nonseekable_open(inode, file);
72 }
73
74 static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data,
75         size_t len, loff_t *ppos)
76 {
77         if (len) {
78                 if (!nowayout) {
79                         size_t i;
80
81                         clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
82
83                         for (i = 0; i != len; i++) {
84                                 char c;
85
86                                 if (get_user(c, data + i))
87                                         return -EFAULT;
88                                 if (c == 'V')
89                                         set_bit(WDT_OK_TO_CLOSE, &wdt_status);
90                         }
91                 }
92                 wdt_ping();
93         }
94
95         return len;
96 }
97
98 static const struct watchdog_info ident = {
99         .options        = WDIOF_CARDRESET |
100                           WDIOF_MAGICCLOSE |
101                           WDIOF_SETTIMEOUT |
102                           WDIOF_KEEPALIVEPING,
103         .identity       = "STMP3XXX Watchdog",
104 };
105
106 static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd,
107         unsigned long arg)
108 {
109         void __user *argp = (void __user *)arg;
110         int __user *p = argp;
111         int new_heartbeat, opts;
112         int ret = -ENOTTY;
113
114         switch (cmd) {
115         case WDIOC_GETSUPPORT:
116                 ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
117                 break;
118
119         case WDIOC_GETSTATUS:
120                 ret = put_user(0, p);
121                 break;
122
123         case WDIOC_GETBOOTSTATUS:
124                 ret = put_user(boot_status, p);
125                 break;
126
127         case WDIOC_SETOPTIONS:
128                 if (get_user(opts, p)) {
129                         ret = -EFAULT;
130                         break;
131                 }
132                 if (opts & WDIOS_DISABLECARD)
133                         wdt_disable();
134                 else if (opts & WDIOS_ENABLECARD)
135                         wdt_ping();
136                 else {
137                         pr_debug("%s: unknown option 0x%x\n", __func__, opts);
138                         ret = -EINVAL;
139                         break;
140                 }
141                 ret = 0;
142                 break;
143
144         case WDIOC_KEEPALIVE:
145                 wdt_ping();
146                 ret = 0;
147                 break;
148
149         case WDIOC_SETTIMEOUT:
150                 if (get_user(new_heartbeat, p)) {
151                         ret = -EFAULT;
152                         break;
153                 }
154                 if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) {
155                         ret = -EINVAL;
156                         break;
157                 }
158
159                 heartbeat = new_heartbeat;
160                 wdt_ping();
161                 /* Fall through */
162
163         case WDIOC_GETTIMEOUT:
164                 ret = put_user(heartbeat, p);
165                 break;
166         }
167         return ret;
168 }
169
170 static int stmp3xxx_wdt_release(struct inode *inode, struct file *file)
171 {
172         int ret = 0;
173
174         if (!nowayout) {
175                 if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
176                         wdt_ping();
177                         pr_debug("%s: Device closed unexpectdly\n", __func__);
178                         ret = -EINVAL;
179                 } else {
180                         wdt_disable();
181                         clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
182                 }
183         }
184         clear_bit(WDT_IN_USE, &wdt_status);
185
186         return ret;
187 }
188
189 static const struct file_operations stmp3xxx_wdt_fops = {
190         .owner = THIS_MODULE,
191         .llseek = no_llseek,
192         .write = stmp3xxx_wdt_write,
193         .unlocked_ioctl = stmp3xxx_wdt_ioctl,
194         .open = stmp3xxx_wdt_open,
195         .release = stmp3xxx_wdt_release,
196 };
197
198 static struct miscdevice stmp3xxx_wdt_miscdev = {
199         .minor = WATCHDOG_MINOR,
200         .name = "watchdog",
201         .fops = &stmp3xxx_wdt_fops,
202 };
203
204 static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev)
205 {
206         int ret = 0;
207
208         if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
209                 heartbeat = DEFAULT_HEARTBEAT;
210
211         boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) &
212                         BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER;
213         boot_status = !!boot_status;
214         stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
215                         REGS_RTC_BASE + HW_RTC_PERSISTENT1);
216         wdt_disable();          /* disable for now */
217
218         ret = misc_register(&stmp3xxx_wdt_miscdev);
219         if (ret < 0) {
220                 dev_err(&pdev->dev, "cannot register misc device\n");
221                 return ret;
222         }
223
224         printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n",
225                 heartbeat);
226
227         return ret;
228 }
229
230 static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev)
231 {
232         misc_deregister(&stmp3xxx_wdt_miscdev);
233         return 0;
234 }
235
236 #ifdef CONFIG_PM
237 static int wdt_suspended;
238 static u32 wdt_saved_time;
239
240 static int stmp3xxx_wdt_suspend(struct platform_device *pdev,
241                                 pm_message_t state)
242 {
243         if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) &
244                 BM_RTC_CTRL_WATCHDOGEN) {
245                 wdt_suspended = 1;
246                 wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG);
247                 wdt_disable();
248         }
249         return 0;
250 }
251
252 static int stmp3xxx_wdt_resume(struct platform_device *pdev)
253 {
254         if (wdt_suspended) {
255                 wdt_enable(wdt_saved_time);
256                 wdt_suspended = 0;
257         }
258         return 0;
259 }
260 #else
261 #define stmp3xxx_wdt_suspend    NULL
262 #define stmp3xxx_wdt_resume     NULL
263 #endif
264
265 static struct platform_driver platform_wdt_driver = {
266         .driver = {
267                 .name = "stmp3xxx_wdt",
268         },
269         .probe = stmp3xxx_wdt_probe,
270         .remove = __devexit_p(stmp3xxx_wdt_remove),
271         .suspend = stmp3xxx_wdt_suspend,
272         .resume = stmp3xxx_wdt_resume,
273 };
274
275 static int __init stmp3xxx_wdt_init(void)
276 {
277         return platform_driver_register(&platform_wdt_driver);
278 }
279
280 static void __exit stmp3xxx_wdt_exit(void)
281 {
282         return platform_driver_unregister(&platform_wdt_driver);
283 }
284
285 module_init(stmp3xxx_wdt_init);
286 module_exit(stmp3xxx_wdt_exit);
287
288 MODULE_DESCRIPTION("STMP3XXX Watchdog Driver");
289 MODULE_LICENSE("GPL");
290
291 module_param(heartbeat, int, 0);
292 MODULE_PARM_DESC(heartbeat,
293                  "Watchdog heartbeat period in seconds from 1 to "
294                  __MODULE_STRING(MAX_HEARTBEAT) ", default "
295                  __MODULE_STRING(DEFAULT_HEARTBEAT));
296
297 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);