treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500
[linux-2.6-microblaze.git] / drivers / watchdog / ks8695_wdt.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Watchdog driver for Kendin/Micrel KS8695.
4  *
5  * (C) 2007 Andrew Victor
6  */
7
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10 #include <linux/bitops.h>
11 #include <linux/errno.h>
12 #include <linux/fs.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/miscdevice.h>
16 #include <linux/module.h>
17 #include <linux/moduleparam.h>
18 #include <linux/platform_device.h>
19 #include <linux/types.h>
20 #include <linux/watchdog.h>
21 #include <linux/io.h>
22 #include <linux/uaccess.h>
23 #include <mach/hardware.h>
24
25 #define KS8695_TMR_OFFSET       (0xF0000 + 0xE400)
26 #define KS8695_TMR_VA           (KS8695_IO_VA + KS8695_TMR_OFFSET)
27
28 /*
29  * Timer registers
30  */
31 #define KS8695_TMCON            (0x00)          /* Timer Control Register */
32 #define KS8695_T0TC             (0x08)          /* Timer 0 Timeout Count Register */
33 #define TMCON_T0EN              (1 << 0)        /* Timer 0 Enable */
34
35 /* Timer0 Timeout Counter Register */
36 #define T0TC_WATCHDOG           (0xff)          /* Enable watchdog mode */
37
38 #define WDT_DEFAULT_TIME        5       /* seconds */
39 #define WDT_MAX_TIME            171     /* seconds */
40
41 static int wdt_time = WDT_DEFAULT_TIME;
42 static bool nowayout = WATCHDOG_NOWAYOUT;
43
44 module_param(wdt_time, int, 0);
45 MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
46                                         __MODULE_STRING(WDT_DEFAULT_TIME) ")");
47
48 #ifdef CONFIG_WATCHDOG_NOWAYOUT
49 module_param(nowayout, bool, 0);
50 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
51                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
52 #endif
53
54
55 static unsigned long ks8695wdt_busy;
56 static DEFINE_SPINLOCK(ks8695_lock);
57
58 /* ......................................................................... */
59
60 /*
61  * Disable the watchdog.
62  */
63 static inline void ks8695_wdt_stop(void)
64 {
65         unsigned long tmcon;
66
67         spin_lock(&ks8695_lock);
68         /* disable timer0 */
69         tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
70         __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
71         spin_unlock(&ks8695_lock);
72 }
73
74 /*
75  * Enable and reset the watchdog.
76  */
77 static inline void ks8695_wdt_start(void)
78 {
79         unsigned long tmcon;
80         unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
81
82         spin_lock(&ks8695_lock);
83         /* disable timer0 */
84         tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
85         __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
86
87         /* program timer0 */
88         __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC);
89
90         /* re-enable timer0 */
91         tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
92         __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
93         spin_unlock(&ks8695_lock);
94 }
95
96 /*
97  * Reload the watchdog timer.  (ie, pat the watchdog)
98  */
99 static inline void ks8695_wdt_reload(void)
100 {
101         unsigned long tmcon;
102
103         spin_lock(&ks8695_lock);
104         /* disable, then re-enable timer0 */
105         tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
106         __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
107         __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
108         spin_unlock(&ks8695_lock);
109 }
110
111 /*
112  * Change the watchdog time interval.
113  */
114 static int ks8695_wdt_settimeout(int new_time)
115 {
116         /*
117          * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
118          *
119          * Since WDV is a 16-bit counter, the maximum period is
120          * 65536 / 0.256 = 256 seconds.
121          */
122         if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
123                 return -EINVAL;
124
125         /* Set new watchdog time. It will be used when
126            ks8695_wdt_start() is called. */
127         wdt_time = new_time;
128         return 0;
129 }
130
131 /* ......................................................................... */
132
133 /*
134  * Watchdog device is opened, and watchdog starts running.
135  */
136 static int ks8695_wdt_open(struct inode *inode, struct file *file)
137 {
138         if (test_and_set_bit(0, &ks8695wdt_busy))
139                 return -EBUSY;
140
141         ks8695_wdt_start();
142         return stream_open(inode, file);
143 }
144
145 /*
146  * Close the watchdog device.
147  * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
148  *  disabled.
149  */
150 static int ks8695_wdt_close(struct inode *inode, struct file *file)
151 {
152         /* Disable the watchdog when file is closed */
153         if (!nowayout)
154                 ks8695_wdt_stop();
155         clear_bit(0, &ks8695wdt_busy);
156         return 0;
157 }
158
159 static const struct watchdog_info ks8695_wdt_info = {
160         .identity       = "ks8695 watchdog",
161         .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
162 };
163
164 /*
165  * Handle commands from user-space.
166  */
167 static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd,
168                                                         unsigned long arg)
169 {
170         void __user *argp = (void __user *)arg;
171         int __user *p = argp;
172         int new_value;
173
174         switch (cmd) {
175         case WDIOC_GETSUPPORT:
176                 return copy_to_user(argp, &ks8695_wdt_info,
177                                         sizeof(ks8695_wdt_info)) ? -EFAULT : 0;
178         case WDIOC_GETSTATUS:
179         case WDIOC_GETBOOTSTATUS:
180                 return put_user(0, p);
181         case WDIOC_SETOPTIONS:
182                 if (get_user(new_value, p))
183                         return -EFAULT;
184                 if (new_value & WDIOS_DISABLECARD)
185                         ks8695_wdt_stop();
186                 if (new_value & WDIOS_ENABLECARD)
187                         ks8695_wdt_start();
188                 return 0;
189         case WDIOC_KEEPALIVE:
190                 ks8695_wdt_reload();    /* pat the watchdog */
191                 return 0;
192         case WDIOC_SETTIMEOUT:
193                 if (get_user(new_value, p))
194                         return -EFAULT;
195                 if (ks8695_wdt_settimeout(new_value))
196                         return -EINVAL;
197                 /* Enable new time value */
198                 ks8695_wdt_start();
199                 /* Return current value */
200                 return put_user(wdt_time, p);
201         case WDIOC_GETTIMEOUT:
202                 return put_user(wdt_time, p);
203         default:
204                 return -ENOTTY;
205         }
206 }
207
208 /*
209  * Pat the watchdog whenever device is written to.
210  */
211 static ssize_t ks8695_wdt_write(struct file *file, const char *data,
212                                                 size_t len, loff_t *ppos)
213 {
214         ks8695_wdt_reload();            /* pat the watchdog */
215         return len;
216 }
217
218 /* ......................................................................... */
219
220 static const struct file_operations ks8695wdt_fops = {
221         .owner          = THIS_MODULE,
222         .llseek         = no_llseek,
223         .unlocked_ioctl = ks8695_wdt_ioctl,
224         .open           = ks8695_wdt_open,
225         .release        = ks8695_wdt_close,
226         .write          = ks8695_wdt_write,
227 };
228
229 static struct miscdevice ks8695wdt_miscdev = {
230         .minor          = WATCHDOG_MINOR,
231         .name           = "watchdog",
232         .fops           = &ks8695wdt_fops,
233 };
234
235 static int ks8695wdt_probe(struct platform_device *pdev)
236 {
237         int res;
238
239         if (ks8695wdt_miscdev.parent)
240                 return -EBUSY;
241         ks8695wdt_miscdev.parent = &pdev->dev;
242
243         res = misc_register(&ks8695wdt_miscdev);
244         if (res)
245                 return res;
246
247         pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n",
248                 wdt_time, nowayout ? ", nowayout" : "");
249         return 0;
250 }
251
252 static int ks8695wdt_remove(struct platform_device *pdev)
253 {
254         misc_deregister(&ks8695wdt_miscdev);
255         ks8695wdt_miscdev.parent = NULL;
256
257         return 0;
258 }
259
260 static void ks8695wdt_shutdown(struct platform_device *pdev)
261 {
262         ks8695_wdt_stop();
263 }
264
265 #ifdef CONFIG_PM
266
267 static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message)
268 {
269         ks8695_wdt_stop();
270         return 0;
271 }
272
273 static int ks8695wdt_resume(struct platform_device *pdev)
274 {
275         if (ks8695wdt_busy)
276                 ks8695_wdt_start();
277         return 0;
278 }
279
280 #else
281 #define ks8695wdt_suspend NULL
282 #define ks8695wdt_resume        NULL
283 #endif
284
285 static struct platform_driver ks8695wdt_driver = {
286         .probe          = ks8695wdt_probe,
287         .remove         = ks8695wdt_remove,
288         .shutdown       = ks8695wdt_shutdown,
289         .suspend        = ks8695wdt_suspend,
290         .resume         = ks8695wdt_resume,
291         .driver         = {
292                 .name   = "ks8695_wdt",
293         },
294 };
295
296 static int __init ks8695_wdt_init(void)
297 {
298         /* Check that the heartbeat value is within range;
299            if not reset to the default */
300         if (ks8695_wdt_settimeout(wdt_time)) {
301                 ks8695_wdt_settimeout(WDT_DEFAULT_TIME);
302                 pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i"
303                                         ", using %d\n", wdt_time, WDT_MAX_TIME);
304         }
305         return platform_driver_register(&ks8695wdt_driver);
306 }
307
308 static void __exit ks8695_wdt_exit(void)
309 {
310         platform_driver_unregister(&ks8695wdt_driver);
311 }
312
313 module_init(ks8695_wdt_init);
314 module_exit(ks8695_wdt_exit);
315
316 MODULE_AUTHOR("Andrew Victor");
317 MODULE_DESCRIPTION("Watchdog driver for KS8695");
318 MODULE_LICENSE("GPL");
319 MODULE_ALIAS("platform:ks8695_wdt");