GNU Linux-libre 4.19.314-gnu1
[releases.git] / drivers / watchdog / pnx833x_wdt.c
1 /*
2  *  PNX833x Hardware Watchdog Driver
3  *  Copyright 2008 NXP Semiconductors
4  *  Daniel Laird <daniel.j.laird@nxp.com>
5  *  Andre McCurdy <andre.mccurdy@nxp.com>
6  *
7  *  Heavily based upon - IndyDog        0.3
8  *  A Hardware Watchdog Device for SGI IP22
9  *
10  * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, 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 <alan@redhat.com>
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>
33 #include <asm/mach-pnx833x/pnx833x.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
40 /** CONFIG block */
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
46 /** RESET block */
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="
61                                         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
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. */
72         PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1;
73         /* Set timeout.*/
74         PNX833X_REG(PNX833X_CONFIG +
75                 PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
76         /* Enable watchdog. */
77         PNX833X_REG(PNX833X_CONFIG +
78                                 PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1;
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.*/
88         PNX833X_REG(PNX833X_CONFIG +
89                         PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE;
90
91         pr_info("Stopped watchdog timer\n");
92 }
93
94 static void pnx833x_wdt_ping(void)
95 {
96         PNX833X_REG(PNX833X_CONFIG +
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 = {
149                 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
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;
190                 PNX833X_REG(PNX833X_CONFIG +
191                         PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout;
192                 return put_user(new_timeout, (int *)arg);
193         }
194
195         case WDIOC_GETTIMEOUT:
196                 timeout = PNX833X_REG(PNX833X_CONFIG +
197                                         PNX833X_CONFIG_CPU_WATCHDOG_COMPARE);
198                 return put_user(timeout, (int *)arg);
199
200         case WDIOC_GETTIMELEFT:
201                 timeout_left = PNX833X_REG(PNX833X_CONFIG +
202                                                 PNX833X_CONFIG_CPU_WATCHDOG);
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");