1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * drivers/char/watchdog/iop_wdt.c 4 * 5 * WDT driver for Intel I/O Processors 6 * Copyright (C) 2005, Intel Corporation. 7 * 8 * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. 9 * 10 * Curt E Bruns <curt.e.bruns@intel.com> 11 * Peter Milne <peter.milne@d-tacq.com> 12 * Dan Williams <dan.j.williams@intel.com> 13 */ 14 15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16 17#include <linux/module.h> 18#include <linux/kernel.h> 19#include <linux/fs.h> 20#include <linux/init.h> 21#include <linux/device.h> 22#include <linux/miscdevice.h> 23#include <linux/watchdog.h> 24#include <linux/uaccess.h> 25#include <mach/hardware.h> 26 27static bool nowayout = WATCHDOG_NOWAYOUT; 28static unsigned long wdt_status; 29static unsigned long boot_status; 30static DEFINE_SPINLOCK(wdt_lock); 31 32#define WDT_IN_USE 0 33#define WDT_OK_TO_CLOSE 1 34#define WDT_ENABLED 2 35 36static unsigned long iop_watchdog_timeout(void) 37{ 38 return (0xffffffffUL / get_iop_tick_rate()); 39} 40 41/** 42 * wdt_supports_disable - determine if we are accessing a iop13xx watchdog 43 * or iop3xx by whether it has a disable command 44 */ 45static int wdt_supports_disable(void) 46{ 47 int can_disable; 48 49 if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) 50 can_disable = 1; 51 else 52 can_disable = 0; 53 54 return can_disable; 55} 56 57static void wdt_enable(void) 58{ 59 /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF 60 * Takes approx. 10.7s to timeout 61 */ 62 spin_lock(&wdt_lock); 63 write_wdtcr(IOP_WDTCR_EN_ARM); 64 write_wdtcr(IOP_WDTCR_EN); 65 spin_unlock(&wdt_lock); 66} 67 68/* returns 0 if the timer was successfully disabled */ 69static int wdt_disable(void) 70{ 71 /* Stop Counting */ 72 if (wdt_supports_disable()) { 73 spin_lock(&wdt_lock); 74 write_wdtcr(IOP_WDTCR_DIS_ARM); 75 write_wdtcr(IOP_WDTCR_DIS); 76 clear_bit(WDT_ENABLED, &wdt_status); 77 spin_unlock(&wdt_lock); 78 pr_info("Disabled\n"); 79 return 0; 80 } else 81 return 1; 82} 83 84static int iop_wdt_open(struct inode *inode, struct file *file) 85{ 86 if (test_and_set_bit(WDT_IN_USE, &wdt_status)) 87 return -EBUSY; 88 89 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 90 wdt_enable(); 91 set_bit(WDT_ENABLED, &wdt_status); 92 return stream_open(inode, file); 93} 94 95static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len, 96 loff_t *ppos) 97{ 98 if (len) { 99 if (!nowayout) { 100 size_t i; 101 102 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 103 104 for (i = 0; i != len; i++) { 105 char c; 106 107 if (get_user(c, data + i)) 108 return -EFAULT; 109 if (c == 'V') 110 set_bit(WDT_OK_TO_CLOSE, &wdt_status); 111 } 112 } 113 wdt_enable(); 114 } 115 return len; 116} 117 118static const struct watchdog_info ident = { 119 .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 120 .identity = "iop watchdog", 121}; 122 123static long iop_wdt_ioctl(struct file *file, 124 unsigned int cmd, unsigned long arg) 125{ 126 int options; 127 int ret = -ENOTTY; 128 int __user *argp = (int __user *)arg; 129 130 switch (cmd) { 131 case WDIOC_GETSUPPORT: 132 if (copy_to_user(argp, &ident, sizeof(ident))) 133 ret = -EFAULT; 134 else 135 ret = 0; 136 break; 137 138 case WDIOC_GETSTATUS: 139 ret = put_user(0, argp); 140 break; 141 142 case WDIOC_GETBOOTSTATUS: 143 ret = put_user(boot_status, argp); 144 break; 145 146 case WDIOC_SETOPTIONS: 147 if (get_user(options, (int *)arg)) 148 return -EFAULT; 149 150 if (options & WDIOS_DISABLECARD) { 151 if (!nowayout) { 152 if (wdt_disable() == 0) { 153 set_bit(WDT_OK_TO_CLOSE, &wdt_status); 154 ret = 0; 155 } else 156 ret = -ENXIO; 157 } else 158 ret = 0; 159 } 160 if (options & WDIOS_ENABLECARD) { 161 wdt_enable(); 162 ret = 0; 163 } 164 break; 165 166 case WDIOC_KEEPALIVE: 167 wdt_enable(); 168 ret = 0; 169 break; 170 171 case WDIOC_GETTIMEOUT: 172 ret = put_user(iop_watchdog_timeout(), argp); 173 break; 174 } 175 return ret; 176} 177 178static int iop_wdt_release(struct inode *inode, struct file *file) 179{ 180 int state = 1; 181 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) 182 if (test_bit(WDT_ENABLED, &wdt_status)) 183 state = wdt_disable(); 184 185 /* if the timer is not disabled reload and notify that we are still 186 * going down 187 */ 188 if (state != 0) { 189 wdt_enable(); 190 pr_crit("Device closed unexpectedly - reset in %lu seconds\n", 191 iop_watchdog_timeout()); 192 } 193 194 clear_bit(WDT_IN_USE, &wdt_status); 195 clear_bit(WDT_OK_TO_CLOSE, &wdt_status); 196 197 return 0; 198} 199 200static const struct file_operations iop_wdt_fops = { 201 .owner = THIS_MODULE, 202 .llseek = no_llseek, 203 .write = iop_wdt_write, 204 .unlocked_ioctl = iop_wdt_ioctl, 205 .compat_ioctl = compat_ptr_ioctl, 206 .open = iop_wdt_open, 207 .release = iop_wdt_release, 208}; 209 210static struct miscdevice iop_wdt_miscdev = { 211 .minor = WATCHDOG_MINOR, 212 .name = "watchdog", 213 .fops = &iop_wdt_fops, 214}; 215 216static int __init iop_wdt_init(void) 217{ 218 int ret; 219 220 /* check if the reset was caused by the watchdog timer */ 221 boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; 222 223 /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset 224 * NOTE: An IB Reset will Reset both cores in the IOP342 225 */ 226 write_wdtsr(IOP13XX_WDTCR_IB_RESET); 227 228 /* Register after we have the device set up so we cannot race 229 with an open */ 230 ret = misc_register(&iop_wdt_miscdev); 231 if (ret == 0) 232 pr_info("timeout %lu sec\n", iop_watchdog_timeout()); 233 234 return ret; 235} 236 237static void __exit iop_wdt_exit(void) 238{ 239 misc_deregister(&iop_wdt_miscdev); 240} 241 242module_init(iop_wdt_init); 243module_exit(iop_wdt_exit); 244 245module_param(nowayout, bool, 0); 246MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 247 248MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>"); 249MODULE_DESCRIPTION("iop watchdog timer driver"); 250MODULE_LICENSE("GPL"); 251