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