Version:  2.0.40 2.2.26 2.4.37 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16

Linux/drivers/watchdog/dw_wdt.c

  1 /*
  2  * Copyright 2010-2011 Picochip Ltd., Jamie Iles
  3  * http://www.picochip.com
  4  *
  5  * This program is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU General Public License
  7  * as published by the Free Software Foundation; either version
  8  * 2 of the License, or (at your option) any later version.
  9  *
 10  * This file implements a driver for the Synopsys DesignWare watchdog device
 11  * in the many subsystems. The watchdog has 16 different timeout periods
 12  * and these are a function of the input clock frequency.
 13  *
 14  * The DesignWare watchdog cannot be stopped once it has been started so we
 15  * use a software timer to implement a ping that will keep the watchdog alive.
 16  * If we receive an expected close for the watchdog then we keep the timer
 17  * running, otherwise the timer is stopped and the watchdog will expire.
 18  */
 19 
 20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 21 
 22 #include <linux/bitops.h>
 23 #include <linux/clk.h>
 24 #include <linux/device.h>
 25 #include <linux/err.h>
 26 #include <linux/fs.h>
 27 #include <linux/io.h>
 28 #include <linux/kernel.h>
 29 #include <linux/miscdevice.h>
 30 #include <linux/module.h>
 31 #include <linux/moduleparam.h>
 32 #include <linux/of.h>
 33 #include <linux/pm.h>
 34 #include <linux/platform_device.h>
 35 #include <linux/spinlock.h>
 36 #include <linux/timer.h>
 37 #include <linux/uaccess.h>
 38 #include <linux/watchdog.h>
 39 
 40 #define WDOG_CONTROL_REG_OFFSET             0x00
 41 #define WDOG_CONTROL_REG_WDT_EN_MASK        0x01
 42 #define WDOG_TIMEOUT_RANGE_REG_OFFSET       0x04
 43 #define WDOG_CURRENT_COUNT_REG_OFFSET       0x08
 44 #define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c
 45 #define WDOG_COUNTER_RESTART_KICK_VALUE     0x76
 46 
 47 /* The maximum TOP (timeout period) value that can be set in the watchdog. */
 48 #define DW_WDT_MAX_TOP          15
 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 #define WDT_TIMEOUT             (HZ / 2)
 56 
 57 static struct {
 58         spinlock_t              lock;
 59         void __iomem            *regs;
 60         struct clk              *clk;
 61         unsigned long           in_use;
 62         unsigned long           next_heartbeat;
 63         struct timer_list       timer;
 64         int                     expect_close;
 65 } dw_wdt;
 66 
 67 static inline int dw_wdt_is_enabled(void)
 68 {
 69         return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) &
 70                 WDOG_CONTROL_REG_WDT_EN_MASK;
 71 }
 72 
 73 static inline int dw_wdt_top_in_seconds(unsigned top)
 74 {
 75         /*
 76          * There are 16 possible timeout values in 0..15 where the number of
 77          * cycles is 2 ^ (16 + i) and the watchdog counts down.
 78          */
 79         return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk);
 80 }
 81 
 82 static int dw_wdt_get_top(void)
 83 {
 84         int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
 85 
 86         return dw_wdt_top_in_seconds(top);
 87 }
 88 
 89 static inline void dw_wdt_set_next_heartbeat(void)
 90 {
 91         dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ;
 92 }
 93 
 94 static int dw_wdt_set_top(unsigned top_s)
 95 {
 96         int i, top_val = DW_WDT_MAX_TOP;
 97 
 98         /*
 99          * Iterate over the timeout values until we find the closest match. We
100          * always look for >=.
101          */
102         for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
103                 if (dw_wdt_top_in_seconds(i) >= top_s) {
104                         top_val = i;
105                         break;
106                 }
107 
108         /* Set the new value in the watchdog. */
109         writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
110 
111         dw_wdt_set_next_heartbeat();
112 
113         return dw_wdt_top_in_seconds(top_val);
114 }
115 
116 static void dw_wdt_keepalive(void)
117 {
118         writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
119                WDOG_COUNTER_RESTART_REG_OFFSET);
120 }
121 
122 static void dw_wdt_ping(unsigned long data)
123 {
124         if (time_before(jiffies, dw_wdt.next_heartbeat) ||
125             (!nowayout && !dw_wdt.in_use)) {
126                 dw_wdt_keepalive();
127                 mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
128         } else
129                 pr_crit("keepalive missed, machine will reset\n");
130 }
131 
132 static int dw_wdt_open(struct inode *inode, struct file *filp)
133 {
134         if (test_and_set_bit(0, &dw_wdt.in_use))
135                 return -EBUSY;
136 
137         /* Make sure we don't get unloaded. */
138         __module_get(THIS_MODULE);
139 
140         spin_lock(&dw_wdt.lock);
141         if (!dw_wdt_is_enabled()) {
142                 /*
143                  * The watchdog is not currently enabled. Set the timeout to
144                  * the maximum and then start it.
145                  */
146                 dw_wdt_set_top(DW_WDT_MAX_TOP);
147                 writel(WDOG_CONTROL_REG_WDT_EN_MASK,
148                        dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
149         }
150 
151         dw_wdt_set_next_heartbeat();
152 
153         spin_unlock(&dw_wdt.lock);
154 
155         return nonseekable_open(inode, filp);
156 }
157 
158 static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
159                             size_t len, loff_t *offset)
160 {
161         if (!len)
162                 return 0;
163 
164         if (!nowayout) {
165                 size_t i;
166 
167                 dw_wdt.expect_close = 0;
168 
169                 for (i = 0; i < len; ++i) {
170                         char c;
171 
172                         if (get_user(c, buf + i))
173                                 return -EFAULT;
174 
175                         if (c == 'V') {
176                                 dw_wdt.expect_close = 1;
177                                 break;
178                         }
179                 }
180         }
181 
182         dw_wdt_set_next_heartbeat();
183         mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
184 
185         return len;
186 }
187 
188 static u32 dw_wdt_time_left(void)
189 {
190         return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
191                 clk_get_rate(dw_wdt.clk);
192 }
193 
194 static const struct watchdog_info dw_wdt_ident = {
195         .options        = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
196                           WDIOF_MAGICCLOSE,
197         .identity       = "Synopsys DesignWare Watchdog",
198 };
199 
200 static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
201 {
202         unsigned long val;
203         int timeout;
204 
205         switch (cmd) {
206         case WDIOC_GETSUPPORT:
207                 return copy_to_user((void __user *)arg, &dw_wdt_ident,
208                                     sizeof(dw_wdt_ident)) ? -EFAULT : 0;
209 
210         case WDIOC_GETSTATUS:
211         case WDIOC_GETBOOTSTATUS:
212                 return put_user(0, (int __user *)arg);
213 
214         case WDIOC_KEEPALIVE:
215                 dw_wdt_set_next_heartbeat();
216                 return 0;
217 
218         case WDIOC_SETTIMEOUT:
219                 if (get_user(val, (int __user *)arg))
220                         return -EFAULT;
221                 timeout = dw_wdt_set_top(val);
222                 return put_user(timeout , (int __user *)arg);
223 
224         case WDIOC_GETTIMEOUT:
225                 return put_user(dw_wdt_get_top(), (int __user *)arg);
226 
227         case WDIOC_GETTIMELEFT:
228                 /* Get the time left until expiry. */
229                 if (get_user(val, (int __user *)arg))
230                         return -EFAULT;
231                 return put_user(dw_wdt_time_left(), (int __user *)arg);
232 
233         default:
234                 return -ENOTTY;
235         }
236 }
237 
238 static int dw_wdt_release(struct inode *inode, struct file *filp)
239 {
240         clear_bit(0, &dw_wdt.in_use);
241 
242         if (!dw_wdt.expect_close) {
243                 del_timer(&dw_wdt.timer);
244 
245                 if (!nowayout)
246                         pr_crit("unexpected close, system will reboot soon\n");
247                 else
248                         pr_crit("watchdog cannot be disabled, system will reboot soon\n");
249         }
250 
251         dw_wdt.expect_close = 0;
252 
253         return 0;
254 }
255 
256 #ifdef CONFIG_PM_SLEEP
257 static int dw_wdt_suspend(struct device *dev)
258 {
259         clk_disable_unprepare(dw_wdt.clk);
260 
261         return 0;
262 }
263 
264 static int dw_wdt_resume(struct device *dev)
265 {
266         int err = clk_prepare_enable(dw_wdt.clk);
267 
268         if (err)
269                 return err;
270 
271         dw_wdt_keepalive();
272 
273         return 0;
274 }
275 #endif /* CONFIG_PM_SLEEP */
276 
277 static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
278 
279 static const struct file_operations wdt_fops = {
280         .owner          = THIS_MODULE,
281         .llseek         = no_llseek,
282         .open           = dw_wdt_open,
283         .write          = dw_wdt_write,
284         .unlocked_ioctl = dw_wdt_ioctl,
285         .release        = dw_wdt_release
286 };
287 
288 static struct miscdevice dw_wdt_miscdev = {
289         .fops           = &wdt_fops,
290         .name           = "watchdog",
291         .minor          = WATCHDOG_MINOR,
292 };
293 
294 static int dw_wdt_drv_probe(struct platform_device *pdev)
295 {
296         int ret;
297         struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
298 
299         if (!mem)
300                 return -EINVAL;
301 
302         dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem);
303         if (IS_ERR(dw_wdt.regs))
304                 return PTR_ERR(dw_wdt.regs);
305 
306         dw_wdt.clk = devm_clk_get(&pdev->dev, NULL);
307         if (IS_ERR(dw_wdt.clk))
308                 return PTR_ERR(dw_wdt.clk);
309 
310         ret = clk_prepare_enable(dw_wdt.clk);
311         if (ret)
312                 return ret;
313 
314         spin_lock_init(&dw_wdt.lock);
315 
316         ret = misc_register(&dw_wdt_miscdev);
317         if (ret)
318                 goto out_disable_clk;
319 
320         dw_wdt_set_next_heartbeat();
321         setup_timer(&dw_wdt.timer, dw_wdt_ping, 0);
322         mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
323 
324         return 0;
325 
326 out_disable_clk:
327         clk_disable_unprepare(dw_wdt.clk);
328 
329         return ret;
330 }
331 
332 static int dw_wdt_drv_remove(struct platform_device *pdev)
333 {
334         misc_deregister(&dw_wdt_miscdev);
335 
336         clk_disable_unprepare(dw_wdt.clk);
337 
338         return 0;
339 }
340 
341 #ifdef CONFIG_OF
342 static const struct of_device_id dw_wdt_of_match[] = {
343         { .compatible = "snps,dw-wdt", },
344         { /* sentinel */ }
345 };
346 MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
347 #endif
348 
349 static struct platform_driver dw_wdt_driver = {
350         .probe          = dw_wdt_drv_probe,
351         .remove         = dw_wdt_drv_remove,
352         .driver         = {
353                 .name   = "dw_wdt",
354                 .owner  = THIS_MODULE,
355                 .of_match_table = of_match_ptr(dw_wdt_of_match),
356                 .pm     = &dw_wdt_pm_ops,
357         },
358 };
359 
360 module_platform_driver(dw_wdt_driver);
361 
362 MODULE_AUTHOR("Jamie Iles");
363 MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
364 MODULE_LICENSE("GPL");
365 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us