1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Watchdog driver for the SA11x0/PXA2xx 4 * 5 * (c) Copyright 2000 Oleg Drokin <green@crimea.edu> 6 * Based on SoftDog driver by Alan Cox <alan@lxorguk.ukuu.org.uk> 7 * 8 * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide 9 * warranty for any of this software. This material is provided 10 * "AS-IS" and at no charge. 11 * 12 * (c) Copyright 2000 Oleg Drokin <green@crimea.edu> 13 * 14 * 27/11/2000 Initial release 15 */ 16 17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18 19#include <linux/module.h> 20#include <linux/moduleparam.h> 21#include <linux/clk.h> 22#include <linux/types.h> 23#include <linux/kernel.h> 24#include <linux/fs.h> 25#include <linux/miscdevice.h> 26#include <linux/watchdog.h> 27#include <linux/init.h> 28#include <linux/io.h> 29#include <linux/bitops.h> 30#include <linux/uaccess.h> 31#include <linux/timex.h> 32 33#ifdef CONFIG_ARCH_PXA 34#include <mach/regs-ost.h> 35#endif 36 37#include <mach/reset.h> 38#include <mach/hardware.h> 39 40static unsigned long oscr_freq; 41static unsigned long sa1100wdt_users; 42static unsigned int pre_margin; 43static int boot_status; 44 45/* 46 * Allow only one person to hold it open 47 */ 48static int sa1100dog_open(struct inode *inode, struct file *file) 49{ 50 if (test_and_set_bit(1, &sa1100wdt_users)) 51 return -EBUSY; 52 53 /* Activate SA1100 Watchdog timer */ 54 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 55 writel_relaxed(OSSR_M3, OSSR); 56 writel_relaxed(OWER_WME, OWER); 57 writel_relaxed(readl_relaxed(OIER) | OIER_E3, OIER); 58 return stream_open(inode, file); 59} 60 61/* 62 * The watchdog cannot be disabled. 63 * 64 * Previous comments suggested that turning off the interrupt by 65 * clearing OIER[E3] would prevent the watchdog timing out but this 66 * does not appear to be true (at least on the PXA255). 67 */ 68static int sa1100dog_release(struct inode *inode, struct file *file) 69{ 70 pr_crit("Device closed - timer will not stop\n"); 71 clear_bit(1, &sa1100wdt_users); 72 return 0; 73} 74 75static ssize_t sa1100dog_write(struct file *file, const char __user *data, 76 size_t len, loff_t *ppos) 77{ 78 if (len) 79 /* Refresh OSMR3 timer. */ 80 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 81 return len; 82} 83 84static const struct watchdog_info ident = { 85 .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT 86 | WDIOF_KEEPALIVEPING, 87 .identity = "SA1100/PXA255 Watchdog", 88 .firmware_version = 1, 89}; 90 91static long sa1100dog_ioctl(struct file *file, unsigned int cmd, 92 unsigned long arg) 93{ 94 int ret = -ENOTTY; 95 int time; 96 void __user *argp = (void __user *)arg; 97 int __user *p = argp; 98 99 switch (cmd) { 100 case WDIOC_GETSUPPORT: 101 ret = copy_to_user(argp, &ident, 102 sizeof(ident)) ? -EFAULT : 0; 103 break; 104 105 case WDIOC_GETSTATUS: 106 ret = put_user(0, p); 107 break; 108 109 case WDIOC_GETBOOTSTATUS: 110 ret = put_user(boot_status, p); 111 break; 112 113 case WDIOC_KEEPALIVE: 114 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 115 ret = 0; 116 break; 117 118 case WDIOC_SETTIMEOUT: 119 ret = get_user(time, p); 120 if (ret) 121 break; 122 123 if (time <= 0 || (oscr_freq * (long long)time >= 0xffffffff)) { 124 ret = -EINVAL; 125 break; 126 } 127 128 pre_margin = oscr_freq * time; 129 writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); 130 fallthrough; 131 132 case WDIOC_GETTIMEOUT: 133 ret = put_user(pre_margin / oscr_freq, p); 134 break; 135 } 136 return ret; 137} 138 139static const struct file_operations sa1100dog_fops = { 140 .owner = THIS_MODULE, 141 .llseek = no_llseek, 142 .write = sa1100dog_write, 143 .unlocked_ioctl = sa1100dog_ioctl, 144 .compat_ioctl = compat_ptr_ioctl, 145 .open = sa1100dog_open, 146 .release = sa1100dog_release, 147}; 148 149static struct miscdevice sa1100dog_miscdev = { 150 .minor = WATCHDOG_MINOR, 151 .name = "watchdog", 152 .fops = &sa1100dog_fops, 153}; 154 155static int margin __initdata = 60; /* (secs) Default is 1 minute */ 156static struct clk *clk; 157 158static int __init sa1100dog_init(void) 159{ 160 int ret; 161 162 clk = clk_get(NULL, "OSTIMER0"); 163 if (IS_ERR(clk)) { 164 pr_err("SA1100/PXA2xx Watchdog Timer: clock not found: %d\n", 165 (int) PTR_ERR(clk)); 166 return PTR_ERR(clk); 167 } 168 169 ret = clk_prepare_enable(clk); 170 if (ret) { 171 pr_err("SA1100/PXA2xx Watchdog Timer: clock failed to prepare+enable: %d\n", 172 ret); 173 goto err; 174 } 175 176 oscr_freq = clk_get_rate(clk); 177 178 /* 179 * Read the reset status, and save it for later. If 180 * we suspend, RCSR will be cleared, and the watchdog 181 * reset reason will be lost. 182 */ 183 boot_status = (reset_status & RESET_STATUS_WATCHDOG) ? 184 WDIOF_CARDRESET : 0; 185 pre_margin = oscr_freq * margin; 186 187 ret = misc_register(&sa1100dog_miscdev); 188 if (ret == 0) { 189 pr_info("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", 190 margin); 191 return 0; 192 } 193 194 clk_disable_unprepare(clk); 195err: 196 clk_put(clk); 197 return ret; 198} 199 200static void __exit sa1100dog_exit(void) 201{ 202 misc_deregister(&sa1100dog_miscdev); 203 clk_disable_unprepare(clk); 204 clk_put(clk); 205} 206 207module_init(sa1100dog_init); 208module_exit(sa1100dog_exit); 209 210MODULE_AUTHOR("Oleg Drokin <green@crimea.edu>"); 211MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog"); 212 213module_param(margin, int, 0); 214MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); 215 216MODULE_LICENSE("GPL"); 217