1// SPDX-License-Identifier: GPL-2.0 2/* 3 * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface 4 * 5 * Author: James Chapman <jchapman@katalix.com> 6 * 7 * Platform-specific setup code should configure the dog to generate 8 * interrupt or reset as required. This code only enables/disables 9 * and services the watchdog. 10 * 11 * Derived from mpc8xx_wdt.c, with the following copyright. 12 * 13 * 2002 (c) Florian Schirmer <jolt@tuxbox.org> 14 */ 15 16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 17 18#include <linux/fs.h> 19#include <linux/init.h> 20#include <linux/kernel.h> 21#include <linux/miscdevice.h> 22#include <linux/module.h> 23#include <linux/watchdog.h> 24#include <linux/platform_device.h> 25#include <linux/mv643xx.h> 26#include <linux/uaccess.h> 27#include <linux/io.h> 28 29#define MV64x60_WDT_WDC_OFFSET 0 30 31/* 32 * The watchdog configuration register contains a pair of 2-bit fields, 33 * 1. a reload field, bits 27-26, which triggers a reload of 34 * the countdown register, and 35 * 2. an enable field, bits 25-24, which toggles between 36 * enabling and disabling the watchdog timer. 37 * Bit 31 is a read-only field which indicates whether the 38 * watchdog timer is currently enabled. 39 * 40 * The low 24 bits contain the timer reload value. 41 */ 42#define MV64x60_WDC_ENABLE_SHIFT 24 43#define MV64x60_WDC_SERVICE_SHIFT 26 44#define MV64x60_WDC_ENABLED_SHIFT 31 45 46#define MV64x60_WDC_ENABLED_TRUE 1 47#define MV64x60_WDC_ENABLED_FALSE 0 48 49/* Flags bits */ 50#define MV64x60_WDOG_FLAG_OPENED 0 51 52static unsigned long wdt_flags; 53static int wdt_status; 54static void __iomem *mv64x60_wdt_regs; 55static int mv64x60_wdt_timeout; 56static int mv64x60_wdt_count; 57static unsigned int bus_clk; 58static char expect_close; 59static DEFINE_SPINLOCK(mv64x60_wdt_spinlock); 60 61static bool nowayout = WATCHDOG_NOWAYOUT; 62module_param(nowayout, bool, 0); 63MODULE_PARM_DESC(nowayout, 64 "Watchdog cannot be stopped once started (default=" 65 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 66 67static int mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift) 68{ 69 u32 data; 70 u32 enabled; 71 int ret = 0; 72 73 spin_lock(&mv64x60_wdt_spinlock); 74 data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); 75 enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1; 76 77 /* only toggle the requested field if enabled state matches predicate */ 78 if ((enabled ^ enabled_predicate) == 0) { 79 /* We write a 1, then a 2 -- to the appropriate field */ 80 data = (1 << field_shift) | mv64x60_wdt_count; 81 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); 82 83 data = (2 << field_shift) | mv64x60_wdt_count; 84 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); 85 ret = 1; 86 } 87 spin_unlock(&mv64x60_wdt_spinlock); 88 89 return ret; 90} 91 92static void mv64x60_wdt_service(void) 93{ 94 mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE, 95 MV64x60_WDC_SERVICE_SHIFT); 96} 97 98static void mv64x60_wdt_handler_enable(void) 99{ 100 if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE, 101 MV64x60_WDC_ENABLE_SHIFT)) { 102 mv64x60_wdt_service(); 103 pr_notice("watchdog activated\n"); 104 } 105} 106 107static void mv64x60_wdt_handler_disable(void) 108{ 109 if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE, 110 MV64x60_WDC_ENABLE_SHIFT)) 111 pr_notice("watchdog deactivated\n"); 112} 113 114static void mv64x60_wdt_set_timeout(unsigned int timeout) 115{ 116 /* maximum bus cycle count is 0xFFFFFFFF */ 117 if (timeout > 0xFFFFFFFF / bus_clk) 118 timeout = 0xFFFFFFFF / bus_clk; 119 120 mv64x60_wdt_count = timeout * bus_clk >> 8; 121 mv64x60_wdt_timeout = timeout; 122} 123 124static int mv64x60_wdt_open(struct inode *inode, struct file *file) 125{ 126 if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags)) 127 return -EBUSY; 128 129 if (nowayout) 130 __module_get(THIS_MODULE); 131 132 mv64x60_wdt_handler_enable(); 133 134 return stream_open(inode, file); 135} 136 137static int mv64x60_wdt_release(struct inode *inode, struct file *file) 138{ 139 if (expect_close == 42) 140 mv64x60_wdt_handler_disable(); 141 else { 142 pr_crit("unexpected close, not stopping timer!\n"); 143 mv64x60_wdt_service(); 144 } 145 expect_close = 0; 146 147 clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags); 148 149 return 0; 150} 151 152static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data, 153 size_t len, loff_t *ppos) 154{ 155 if (len) { 156 if (!nowayout) { 157 size_t i; 158 159 expect_close = 0; 160 161 for (i = 0; i != len; i++) { 162 char c; 163 if (get_user(c, data + i)) 164 return -EFAULT; 165 if (c == 'V') 166 expect_close = 42; 167 } 168 } 169 mv64x60_wdt_service(); 170 } 171 172 return len; 173} 174 175static long mv64x60_wdt_ioctl(struct file *file, 176 unsigned int cmd, unsigned long arg) 177{ 178 int timeout; 179 int options; 180 void __user *argp = (void __user *)arg; 181 static const struct watchdog_info info = { 182 .options = WDIOF_SETTIMEOUT | 183 WDIOF_MAGICCLOSE | 184 WDIOF_KEEPALIVEPING, 185 .firmware_version = 0, 186 .identity = "MV64x60 watchdog", 187 }; 188 189 switch (cmd) { 190 case WDIOC_GETSUPPORT: 191 if (copy_to_user(argp, &info, sizeof(info))) 192 return -EFAULT; 193 break; 194 195 case WDIOC_GETSTATUS: 196 case WDIOC_GETBOOTSTATUS: 197 if (put_user(wdt_status, (int __user *)argp)) 198 return -EFAULT; 199 wdt_status &= ~WDIOF_KEEPALIVEPING; 200 break; 201 202 case WDIOC_GETTEMP: 203 return -EOPNOTSUPP; 204 205 case WDIOC_SETOPTIONS: 206 if (get_user(options, (int __user *)argp)) 207 return -EFAULT; 208 209 if (options & WDIOS_DISABLECARD) 210 mv64x60_wdt_handler_disable(); 211 212 if (options & WDIOS_ENABLECARD) 213 mv64x60_wdt_handler_enable(); 214 break; 215 216 case WDIOC_KEEPALIVE: 217 mv64x60_wdt_service(); 218 wdt_status |= WDIOF_KEEPALIVEPING; 219 break; 220 221 case WDIOC_SETTIMEOUT: 222 if (get_user(timeout, (int __user *)argp)) 223 return -EFAULT; 224 mv64x60_wdt_set_timeout(timeout); 225 fallthrough; 226 227 case WDIOC_GETTIMEOUT: 228 if (put_user(mv64x60_wdt_timeout, (int __user *)argp)) 229 return -EFAULT; 230 break; 231 232 default: 233 return -ENOTTY; 234 } 235 236 return 0; 237} 238 239static const struct file_operations mv64x60_wdt_fops = { 240 .owner = THIS_MODULE, 241 .llseek = no_llseek, 242 .write = mv64x60_wdt_write, 243 .unlocked_ioctl = mv64x60_wdt_ioctl, 244 .compat_ioctl = compat_ptr_ioctl, 245 .open = mv64x60_wdt_open, 246 .release = mv64x60_wdt_release, 247}; 248 249static struct miscdevice mv64x60_wdt_miscdev = { 250 .minor = WATCHDOG_MINOR, 251 .name = "watchdog", 252 .fops = &mv64x60_wdt_fops, 253}; 254 255static int mv64x60_wdt_probe(struct platform_device *dev) 256{ 257 struct mv64x60_wdt_pdata *pdata = dev_get_platdata(&dev->dev); 258 struct resource *r; 259 int timeout = 10; 260 261 bus_clk = 133; /* in MHz */ 262 if (pdata) { 263 timeout = pdata->timeout; 264 bus_clk = pdata->bus_clk; 265 } 266 267 /* Since bus_clk is truncated MHz, actual frequency could be 268 * up to 1MHz higher. Round up, since it's better to time out 269 * too late than too soon. 270 */ 271 bus_clk++; 272 bus_clk *= 1000000; /* convert to Hz */ 273 274 r = platform_get_resource(dev, IORESOURCE_MEM, 0); 275 if (!r) 276 return -ENODEV; 277 278 mv64x60_wdt_regs = devm_ioremap(&dev->dev, r->start, resource_size(r)); 279 if (mv64x60_wdt_regs == NULL) 280 return -ENOMEM; 281 282 mv64x60_wdt_set_timeout(timeout); 283 284 mv64x60_wdt_handler_disable(); /* in case timer was already running */ 285 286 return misc_register(&mv64x60_wdt_miscdev); 287} 288 289static int mv64x60_wdt_remove(struct platform_device *dev) 290{ 291 misc_deregister(&mv64x60_wdt_miscdev); 292 293 mv64x60_wdt_handler_disable(); 294 295 return 0; 296} 297 298static struct platform_driver mv64x60_wdt_driver = { 299 .probe = mv64x60_wdt_probe, 300 .remove = mv64x60_wdt_remove, 301 .driver = { 302 .name = MV64x60_WDT_NAME, 303 }, 304}; 305 306static int __init mv64x60_wdt_init(void) 307{ 308 pr_info("MV64x60 watchdog driver\n"); 309 310 return platform_driver_register(&mv64x60_wdt_driver); 311} 312 313static void __exit mv64x60_wdt_exit(void) 314{ 315 platform_driver_unregister(&mv64x60_wdt_driver); 316} 317 318module_init(mv64x60_wdt_init); 319module_exit(mv64x60_wdt_exit); 320 321MODULE_AUTHOR("James Chapman <jchapman@katalix.com>"); 322MODULE_DESCRIPTION("MV64x60 watchdog driver"); 323MODULE_LICENSE("GPL"); 324MODULE_ALIAS("platform:" MV64x60_WDT_NAME); 325