1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * IDT Interprise 79RC32434 watchdog driver 4 * 5 * Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org> 6 * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org> 7 * 8 * based on 9 * SoftDog 0.05: A Software Watchdog Device 10 * 11 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, 12 * All Rights Reserved. 13 */ 14 15 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16 17 #include <linux/module.h> /* For module specific items */ 18 #include <linux/moduleparam.h> /* For new moduleparam's */ 19 #include <linux/types.h> /* For standard types (like size_t) */ 20 #include <linux/errno.h> /* For the -ENODEV/... values */ 21 #include <linux/kernel.h> /* For printk/panic/... */ 22 #include <linux/fs.h> /* For file operations */ 23 #include <linux/miscdevice.h> /* For struct miscdevice */ 24 #include <linux/watchdog.h> /* For the watchdog specific items */ 25 #include <linux/init.h> /* For __init/__exit/... */ 26 #include <linux/platform_device.h> /* For platform_driver framework */ 27 #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ 28 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ 29 #include <linux/io.h> /* For devm_ioremap_nocache */ 30 31 #include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */ 32 33 #define VERSION "1.0" 34 35 static struct { 36 unsigned long inuse; 37 spinlock_t io_lock; 38 } rc32434_wdt_device; 39 40 static struct integ __iomem *wdt_reg; 41 42 static int expect_close; 43 44 /* Board internal clock speed in Hz, 45 * the watchdog timer ticks at. */ 46 extern unsigned int idt_cpu_freq; 47 48 /* translate wtcompare value to seconds and vice versa */ 49 #define WTCOMP2SEC(x) (x / idt_cpu_freq) 50 #define SEC2WTCOMP(x) (x * idt_cpu_freq) 51 52 /* Use a default timeout of 20s. This should be 53 * safe for CPU clock speeds up to 400MHz, as 54 * ((2 ^ 32) - 1) / (400MHz / 2) = 21s. */ 55 #define WATCHDOG_TIMEOUT 20 56 57 static int timeout = WATCHDOG_TIMEOUT; 58 module_param(timeout, int, 0); 59 MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default=" 60 __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 61 62 static bool nowayout = WATCHDOG_NOWAYOUT; 63 module_param(nowayout, bool, 0); 64 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 65 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 66 67 /* apply or and nand masks to data read from addr and write back */ 68 #define SET_BITS(addr, or, nand) \ 69 writel((readl(&addr) | or) & ~nand, &addr) 70 71 static int rc32434_wdt_set(int new_timeout) 72 { 73 int max_to = WTCOMP2SEC((u32)-1); 74 75 if (new_timeout < 0 || new_timeout > max_to) { 76 pr_err("timeout value must be between 0 and %d\n", max_to); 77 return -EINVAL; 78 } 79 timeout = new_timeout; 80 spin_lock(&rc32434_wdt_device.io_lock); 81 writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare); 82 spin_unlock(&rc32434_wdt_device.io_lock); 83 84 return 0; 85 } 86 87 static void rc32434_wdt_start(void) 88 { 89 u32 or, nand; 90 91 spin_lock(&rc32434_wdt_device.io_lock); 92 93 /* zero the counter before enabling */ 94 writel(0, &wdt_reg->wtcount); 95 96 /* don't generate a non-maskable interrupt, 97 * do a warm reset instead */ 98 nand = 1 << RC32434_ERR_WNE; 99 or = 1 << RC32434_ERR_WRE; 100 101 /* reset the ERRCS timeout bit in case it's set */ 102 nand |= 1 << RC32434_ERR_WTO; 103 104 SET_BITS(wdt_reg->errcs, or, nand); 105 106 /* set the timeout (either default or based on module param) */ 107 rc32434_wdt_set(timeout); 108 109 /* reset WTC timeout bit and enable WDT */ 110 nand = 1 << RC32434_WTC_TO; 111 or = 1 << RC32434_WTC_EN; 112 113 SET_BITS(wdt_reg->wtc, or, nand); 114 115 spin_unlock(&rc32434_wdt_device.io_lock); 116 pr_info("Started watchdog timer\n"); 117 } 118 119 static void rc32434_wdt_stop(void) 120 { 121 spin_lock(&rc32434_wdt_device.io_lock); 122 123 /* Disable WDT */ 124 SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN); 125 126 spin_unlock(&rc32434_wdt_device.io_lock); 127 pr_info("Stopped watchdog timer\n"); 128 } 129 130 static void rc32434_wdt_ping(void) 131 { 132 spin_lock(&rc32434_wdt_device.io_lock); 133 writel(0, &wdt_reg->wtcount); 134 spin_unlock(&rc32434_wdt_device.io_lock); 135 } 136 137 static int rc32434_wdt_open(struct inode *inode, struct file *file) 138 { 139 if (test_and_set_bit(0, &rc32434_wdt_device.inuse)) 140 return -EBUSY; 141 142 if (nowayout) 143 __module_get(THIS_MODULE); 144 145 rc32434_wdt_start(); 146 rc32434_wdt_ping(); 147 148 return stream_open(inode, file); 149 } 150 151 static int rc32434_wdt_release(struct inode *inode, struct file *file) 152 { 153 if (expect_close == 42) { 154 rc32434_wdt_stop(); 155 module_put(THIS_MODULE); 156 } else { 157 pr_crit("device closed unexpectedly. WDT will not stop!\n"); 158 rc32434_wdt_ping(); 159 } 160 clear_bit(0, &rc32434_wdt_device.inuse); 161 return 0; 162 } 163 164 static ssize_t rc32434_wdt_write(struct file *file, const char *data, 165 size_t len, loff_t *ppos) 166 { 167 if (len) { 168 if (!nowayout) { 169 size_t i; 170 171 /* In case it was set long ago */ 172 expect_close = 0; 173 174 for (i = 0; i != len; i++) { 175 char c; 176 if (get_user(c, data + i)) 177 return -EFAULT; 178 if (c == 'V') 179 expect_close = 42; 180 } 181 } 182 rc32434_wdt_ping(); 183 return len; 184 } 185 return 0; 186 } 187 188 static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, 189 unsigned long arg) 190 { 191 void __user *argp = (void __user *)arg; 192 int new_timeout; 193 unsigned int value; 194 static const struct watchdog_info ident = { 195 .options = WDIOF_SETTIMEOUT | 196 WDIOF_KEEPALIVEPING | 197 WDIOF_MAGICCLOSE, 198 .identity = "RC32434_WDT Watchdog", 199 }; 200 switch (cmd) { 201 case WDIOC_GETSUPPORT: 202 if (copy_to_user(argp, &ident, sizeof(ident))) 203 return -EFAULT; 204 break; 205 case WDIOC_GETSTATUS: 206 case WDIOC_GETBOOTSTATUS: 207 value = 0; 208 if (copy_to_user(argp, &value, sizeof(int))) 209 return -EFAULT; 210 break; 211 case WDIOC_SETOPTIONS: 212 if (copy_from_user(&value, argp, sizeof(int))) 213 return -EFAULT; 214 switch (value) { 215 case WDIOS_ENABLECARD: 216 rc32434_wdt_start(); 217 break; 218 case WDIOS_DISABLECARD: 219 rc32434_wdt_stop(); 220 break; 221 default: 222 return -EINVAL; 223 } 224 break; 225 case WDIOC_KEEPALIVE: 226 rc32434_wdt_ping(); 227 break; 228 case WDIOC_SETTIMEOUT: 229 if (copy_from_user(&new_timeout, argp, sizeof(int))) 230 return -EFAULT; 231 if (rc32434_wdt_set(new_timeout)) 232 return -EINVAL; 233 /* Fall through */ 234 case WDIOC_GETTIMEOUT: 235 return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0; 236 default: 237 return -ENOTTY; 238 } 239 240 return 0; 241 } 242 243 static const struct file_operations rc32434_wdt_fops = { 244 .owner = THIS_MODULE, 245 .llseek = no_llseek, 246 .write = rc32434_wdt_write, 247 .unlocked_ioctl = rc32434_wdt_ioctl, 248 .open = rc32434_wdt_open, 249 .release = rc32434_wdt_release, 250 }; 251 252 static struct miscdevice rc32434_wdt_miscdev = { 253 .minor = WATCHDOG_MINOR, 254 .name = "watchdog", 255 .fops = &rc32434_wdt_fops, 256 }; 257 258 static int rc32434_wdt_probe(struct platform_device *pdev) 259 { 260 int ret; 261 struct resource *r; 262 263 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res"); 264 if (!r) { 265 pr_err("failed to retrieve resources\n"); 266 return -ENODEV; 267 } 268 269 wdt_reg = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r)); 270 if (!wdt_reg) { 271 pr_err("failed to remap I/O resources\n"); 272 return -ENXIO; 273 } 274 275 spin_lock_init(&rc32434_wdt_device.io_lock); 276 277 /* Make sure the watchdog is not running */ 278 rc32434_wdt_stop(); 279 280 /* Check that the heartbeat value is within it's range; 281 * if not reset to the default */ 282 if (rc32434_wdt_set(timeout)) { 283 rc32434_wdt_set(WATCHDOG_TIMEOUT); 284 pr_info("timeout value must be between 0 and %d\n", 285 WTCOMP2SEC((u32)-1)); 286 } 287 288 ret = misc_register(&rc32434_wdt_miscdev); 289 if (ret < 0) { 290 pr_err("failed to register watchdog device\n"); 291 return ret; 292 } 293 294 pr_info("Watchdog Timer version " VERSION ", timer margin: %d sec\n", 295 timeout); 296 297 return 0; 298 } 299 300 static int rc32434_wdt_remove(struct platform_device *pdev) 301 { 302 misc_deregister(&rc32434_wdt_miscdev); 303 return 0; 304 } 305 306 static void rc32434_wdt_shutdown(struct platform_device *pdev) 307 { 308 rc32434_wdt_stop(); 309 } 310 311 static struct platform_driver rc32434_wdt_driver = { 312 .probe = rc32434_wdt_probe, 313 .remove = rc32434_wdt_remove, 314 .shutdown = rc32434_wdt_shutdown, 315 .driver = { 316 .name = "rc32434_wdt", 317 } 318 }; 319 320 module_platform_driver(rc32434_wdt_driver); 321 322 MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>," 323 "Florian Fainelli <florian@openwrt.org>"); 324 MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog"); 325 MODULE_LICENSE("GPL"); 326