virtio-mem: simplify high-level unplug handling in Sub Block Mode
[linux-2.6-microblaze.git] / drivers / watchdog / ath79_wdt.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer.
4  *
5  * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
6  * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
7  *
8  * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
9  *      Author: Deepak Saxena <dsaxena@plexity.net>
10  *      Copyright 2004 (c) MontaVista, Software, Inc.
11  *
12  * which again was based on sa1100 driver,
13  *      Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
14  */
15
16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
18 #include <linux/bitops.h>
19 #include <linux/delay.h>
20 #include <linux/errno.h>
21 #include <linux/fs.h>
22 #include <linux/io.h>
23 #include <linux/kernel.h>
24 #include <linux/miscdevice.h>
25 #include <linux/module.h>
26 #include <linux/moduleparam.h>
27 #include <linux/platform_device.h>
28 #include <linux/types.h>
29 #include <linux/watchdog.h>
30 #include <linux/clk.h>
31 #include <linux/err.h>
32 #include <linux/of.h>
33 #include <linux/of_platform.h>
34 #include <linux/uaccess.h>
35
36 #define DRIVER_NAME     "ath79-wdt"
37
38 #define WDT_TIMEOUT     15      /* seconds */
39
40 #define WDOG_REG_CTRL           0x00
41 #define WDOG_REG_TIMER          0x04
42
43 #define WDOG_CTRL_LAST_RESET    BIT(31)
44 #define WDOG_CTRL_ACTION_MASK   3
45 #define WDOG_CTRL_ACTION_NONE   0       /* no action */
46 #define WDOG_CTRL_ACTION_GPI    1       /* general purpose interrupt */
47 #define WDOG_CTRL_ACTION_NMI    2       /* NMI */
48 #define WDOG_CTRL_ACTION_FCR    3       /* full chip reset */
49
50 static bool nowayout = WATCHDOG_NOWAYOUT;
51 module_param(nowayout, bool, 0);
52 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
53                            "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
54
55 static int timeout = WDT_TIMEOUT;
56 module_param(timeout, int, 0);
57 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
58                           "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)");
59
60 static unsigned long wdt_flags;
61
62 #define WDT_FLAGS_BUSY          0
63 #define WDT_FLAGS_EXPECT_CLOSE  1
64
65 static struct clk *wdt_clk;
66 static unsigned long wdt_freq;
67 static int boot_status;
68 static int max_timeout;
69 static void __iomem *wdt_base;
70
71 static inline void ath79_wdt_wr(unsigned reg, u32 val)
72 {
73         iowrite32(val, wdt_base + reg);
74 }
75
76 static inline u32 ath79_wdt_rr(unsigned reg)
77 {
78         return ioread32(wdt_base + reg);
79 }
80
81 static inline void ath79_wdt_keepalive(void)
82 {
83         ath79_wdt_wr(WDOG_REG_TIMER, wdt_freq * timeout);
84         /* flush write */
85         ath79_wdt_rr(WDOG_REG_TIMER);
86 }
87
88 static inline void ath79_wdt_enable(void)
89 {
90         ath79_wdt_keepalive();
91
92         /*
93          * Updating the TIMER register requires a few microseconds
94          * on the AR934x SoCs at least. Use a small delay to ensure
95          * that the TIMER register is updated within the hardware
96          * before enabling the watchdog.
97          */
98         udelay(2);
99
100         ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR);
101         /* flush write */
102         ath79_wdt_rr(WDOG_REG_CTRL);
103 }
104
105 static inline void ath79_wdt_disable(void)
106 {
107         ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_NONE);
108         /* flush write */
109         ath79_wdt_rr(WDOG_REG_CTRL);
110 }
111
112 static int ath79_wdt_set_timeout(int val)
113 {
114         if (val < 1 || val > max_timeout)
115                 return -EINVAL;
116
117         timeout = val;
118         ath79_wdt_keepalive();
119
120         return 0;
121 }
122
123 static int ath79_wdt_open(struct inode *inode, struct file *file)
124 {
125         if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags))
126                 return -EBUSY;
127
128         clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
129         ath79_wdt_enable();
130
131         return stream_open(inode, file);
132 }
133
134 static int ath79_wdt_release(struct inode *inode, struct file *file)
135 {
136         if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
137                 ath79_wdt_disable();
138         else {
139                 pr_crit("device closed unexpectedly, watchdog timer will not stop!\n");
140                 ath79_wdt_keepalive();
141         }
142
143         clear_bit(WDT_FLAGS_BUSY, &wdt_flags);
144         clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
145
146         return 0;
147 }
148
149 static ssize_t ath79_wdt_write(struct file *file, const char *data,
150                                 size_t len, loff_t *ppos)
151 {
152         if (len) {
153                 if (!nowayout) {
154                         size_t i;
155
156                         clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
157
158                         for (i = 0; i != len; i++) {
159                                 char c;
160
161                                 if (get_user(c, data + i))
162                                         return -EFAULT;
163
164                                 if (c == 'V')
165                                         set_bit(WDT_FLAGS_EXPECT_CLOSE,
166                                                 &wdt_flags);
167                         }
168                 }
169
170                 ath79_wdt_keepalive();
171         }
172
173         return len;
174 }
175
176 static const struct watchdog_info ath79_wdt_info = {
177         .options                = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
178                                   WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
179         .firmware_version       = 0,
180         .identity               = "ATH79 watchdog",
181 };
182
183 static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
184                             unsigned long arg)
185 {
186         void __user *argp = (void __user *)arg;
187         int __user *p = argp;
188         int err;
189         int t;
190
191         switch (cmd) {
192         case WDIOC_GETSUPPORT:
193                 err = copy_to_user(argp, &ath79_wdt_info,
194                                    sizeof(ath79_wdt_info)) ? -EFAULT : 0;
195                 break;
196
197         case WDIOC_GETSTATUS:
198                 err = put_user(0, p);
199                 break;
200
201         case WDIOC_GETBOOTSTATUS:
202                 err = put_user(boot_status, p);
203                 break;
204
205         case WDIOC_KEEPALIVE:
206                 ath79_wdt_keepalive();
207                 err = 0;
208                 break;
209
210         case WDIOC_SETTIMEOUT:
211                 err = get_user(t, p);
212                 if (err)
213                         break;
214
215                 err = ath79_wdt_set_timeout(t);
216                 if (err)
217                         break;
218                 fallthrough;
219
220         case WDIOC_GETTIMEOUT:
221                 err = put_user(timeout, p);
222                 break;
223
224         default:
225                 err = -ENOTTY;
226                 break;
227         }
228
229         return err;
230 }
231
232 static const struct file_operations ath79_wdt_fops = {
233         .owner          = THIS_MODULE,
234         .llseek         = no_llseek,
235         .write          = ath79_wdt_write,
236         .unlocked_ioctl = ath79_wdt_ioctl,
237         .compat_ioctl   = compat_ptr_ioctl,
238         .open           = ath79_wdt_open,
239         .release        = ath79_wdt_release,
240 };
241
242 static struct miscdevice ath79_wdt_miscdev = {
243         .minor = WATCHDOG_MINOR,
244         .name = "watchdog",
245         .fops = &ath79_wdt_fops,
246 };
247
248 static int ath79_wdt_probe(struct platform_device *pdev)
249 {
250         u32 ctrl;
251         int err;
252
253         if (wdt_base)
254                 return -EBUSY;
255
256         wdt_base = devm_platform_ioremap_resource(pdev, 0);
257         if (IS_ERR(wdt_base))
258                 return PTR_ERR(wdt_base);
259
260         wdt_clk = devm_clk_get(&pdev->dev, "wdt");
261         if (IS_ERR(wdt_clk))
262                 return PTR_ERR(wdt_clk);
263
264         err = clk_prepare_enable(wdt_clk);
265         if (err)
266                 return err;
267
268         wdt_freq = clk_get_rate(wdt_clk);
269         if (!wdt_freq) {
270                 err = -EINVAL;
271                 goto err_clk_disable;
272         }
273
274         max_timeout = (0xfffffffful / wdt_freq);
275         if (timeout < 1 || timeout > max_timeout) {
276                 timeout = max_timeout;
277                 dev_info(&pdev->dev,
278                         "timeout value must be 0 < timeout < %d, using %d\n",
279                         max_timeout, timeout);
280         }
281
282         ctrl = ath79_wdt_rr(WDOG_REG_CTRL);
283         boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
284
285         err = misc_register(&ath79_wdt_miscdev);
286         if (err) {
287                 dev_err(&pdev->dev,
288                         "unable to register misc device, err=%d\n", err);
289                 goto err_clk_disable;
290         }
291
292         return 0;
293
294 err_clk_disable:
295         clk_disable_unprepare(wdt_clk);
296         return err;
297 }
298
299 static int ath79_wdt_remove(struct platform_device *pdev)
300 {
301         misc_deregister(&ath79_wdt_miscdev);
302         clk_disable_unprepare(wdt_clk);
303         return 0;
304 }
305
306 static void ath79_wdt_shutdown(struct platform_device *pdev)
307 {
308         ath79_wdt_disable();
309 }
310
311 #ifdef CONFIG_OF
312 static const struct of_device_id ath79_wdt_match[] = {
313         { .compatible = "qca,ar7130-wdt" },
314         {},
315 };
316 MODULE_DEVICE_TABLE(of, ath79_wdt_match);
317 #endif
318
319 static struct platform_driver ath79_wdt_driver = {
320         .probe          = ath79_wdt_probe,
321         .remove         = ath79_wdt_remove,
322         .shutdown       = ath79_wdt_shutdown,
323         .driver         = {
324                 .name   = DRIVER_NAME,
325                 .of_match_table = of_match_ptr(ath79_wdt_match),
326         },
327 };
328
329 module_platform_driver(ath79_wdt_driver);
330
331 MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
332 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
333 MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
334 MODULE_LICENSE("GPL v2");
335 MODULE_ALIAS("platform:" DRIVER_NAME);