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