1 /* 2 * This program is free software; you can redistribute it and/or modify it 3 * under the terms of the GNU General Public License version 2 as published 4 * by the Free Software Foundation. 5 * 6 * Copyright (C) 2010 John Crispin <blogic@openwrt.org> 7 * Based on EP93xx wdt driver 8 */ 9 10 #include <linux/module.h> 11 #include <linux/fs.h> 12 #include <linux/miscdevice.h> 13 #include <linux/watchdog.h> 14 #include <linux/platform_device.h> 15 #include <linux/uaccess.h> 16 #include <linux/clk.h> 17 #include <linux/io.h> 18 19 #include <lantiq.h> 20 21 /* Section 3.4 of the datasheet 22 * The password sequence protects the WDT control register from unintended 23 * write actions, which might cause malfunction of the WDT. 24 * 25 * essentially the following two magic passwords need to be written to allow 26 * IO access to the WDT core 27 */ 28 #define LTQ_WDT_PW1 0x00BE0000 29 #define LTQ_WDT_PW2 0x00DC0000 30 31 #define LTQ_WDT_CR 0x0 /* watchdog control register */ 32 #define LTQ_WDT_SR 0x8 /* watchdog status register */ 33 34 #define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */ 35 #define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */ 36 #define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */ 37 /* divider to 0x40000 */ 38 #define LTQ_WDT_DIVIDER 0x40000 39 #define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */ 40 41 static int nowayout = WATCHDOG_NOWAYOUT; 42 43 static void __iomem *ltq_wdt_membase; 44 static unsigned long ltq_io_region_clk_rate; 45 46 static unsigned long ltq_wdt_bootstatus; 47 static unsigned long ltq_wdt_in_use; 48 static int ltq_wdt_timeout = 30; 49 static int ltq_wdt_ok_to_close; 50 51 static void 52 ltq_wdt_enable(void) 53 { 54 ltq_wdt_timeout = ltq_wdt_timeout * 55 (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000; 56 if (ltq_wdt_timeout > LTQ_MAX_TIMEOUT) 57 ltq_wdt_timeout = LTQ_MAX_TIMEOUT; 58 59 /* write the first password magic */ 60 ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); 61 /* write the second magic plus the configuration and new timeout */ 62 ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV | 63 LTQ_WDT_PW2 | ltq_wdt_timeout, ltq_wdt_membase + LTQ_WDT_CR); 64 } 65 66 static void 67 ltq_wdt_disable(void) 68 { 69 /* write the first password magic */ 70 ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); 71 /* write the second password magic with no config 72 * this turns the watchdog off 73 */ 74 ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR); 75 } 76 77 static ssize_t 78 ltq_wdt_write(struct file *file, const char __user *data, 79 size_t len, loff_t *ppos) 80 { 81 if (len) { 82 if (!nowayout) { 83 size_t i; 84 85 ltq_wdt_ok_to_close = 0; 86 for (i = 0; i != len; i++) { 87 char c; 88 89 if (get_user(c, data + i)) 90 return -EFAULT; 91 if (c == 'V') 92 ltq_wdt_ok_to_close = 1; 93 else 94 ltq_wdt_ok_to_close = 0; 95 } 96 } 97 ltq_wdt_enable(); 98 } 99 100 return len; 101 } 102 103 static struct watchdog_info ident = { 104 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 105 WDIOF_CARDRESET, 106 .identity = "ltq_wdt", 107 }; 108 109 static long 110 ltq_wdt_ioctl(struct file *file, 111 unsigned int cmd, unsigned long arg) 112 { 113 int ret = -ENOTTY; 114 115 switch (cmd) { 116 case WDIOC_GETSUPPORT: 117 ret = copy_to_user((struct watchdog_info __user *)arg, &ident, 118 sizeof(ident)) ? -EFAULT : 0; 119 break; 120 121 case WDIOC_GETBOOTSTATUS: 122 ret = put_user(ltq_wdt_bootstatus, (int __user *)arg); 123 break; 124 125 case WDIOC_GETSTATUS: 126 ret = put_user(0, (int __user *)arg); 127 break; 128 129 case WDIOC_SETTIMEOUT: 130 ret = get_user(ltq_wdt_timeout, (int __user *)arg); 131 if (!ret) 132 ltq_wdt_enable(); 133 /* intentional drop through */ 134 case WDIOC_GETTIMEOUT: 135 ret = put_user(ltq_wdt_timeout, (int __user *)arg); 136 break; 137 138 case WDIOC_KEEPALIVE: 139 ltq_wdt_enable(); 140 ret = 0; 141 break; 142 } 143 return ret; 144 } 145 146 static int 147 ltq_wdt_open(struct inode *inode, struct file *file) 148 { 149 if (test_and_set_bit(0, <q_wdt_in_use)) 150 return -EBUSY; 151 ltq_wdt_in_use = 1; 152 ltq_wdt_enable(); 153 154 return nonseekable_open(inode, file); 155 } 156 157 static int 158 ltq_wdt_release(struct inode *inode, struct file *file) 159 { 160 if (ltq_wdt_ok_to_close) 161 ltq_wdt_disable(); 162 else 163 pr_err("ltq_wdt: watchdog closed without warning\n"); 164 ltq_wdt_ok_to_close = 0; 165 clear_bit(0, <q_wdt_in_use); 166 167 return 0; 168 } 169 170 static const struct file_operations ltq_wdt_fops = { 171 .owner = THIS_MODULE, 172 .write = ltq_wdt_write, 173 .unlocked_ioctl = ltq_wdt_ioctl, 174 .open = ltq_wdt_open, 175 .release = ltq_wdt_release, 176 .llseek = no_llseek, 177 }; 178 179 static struct miscdevice ltq_wdt_miscdev = { 180 .minor = WATCHDOG_MINOR, 181 .name = "watchdog", 182 .fops = <q_wdt_fops, 183 }; 184 185 static int __init 186 ltq_wdt_probe(struct platform_device *pdev) 187 { 188 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 189 struct clk *clk; 190 191 if (!res) { 192 dev_err(&pdev->dev, "cannot obtain I/O memory region"); 193 return -ENOENT; 194 } 195 res = devm_request_mem_region(&pdev->dev, res->start, 196 resource_size(res), dev_name(&pdev->dev)); 197 if (!res) { 198 dev_err(&pdev->dev, "cannot request I/O memory region"); 199 return -EBUSY; 200 } 201 ltq_wdt_membase = devm_ioremap_nocache(&pdev->dev, res->start, 202 resource_size(res)); 203 if (!ltq_wdt_membase) { 204 dev_err(&pdev->dev, "cannot remap I/O memory region\n"); 205 return -ENOMEM; 206 } 207 208 /* we do not need to enable the clock as it is always running */ 209 clk = clk_get(&pdev->dev, "io"); 210 WARN_ON(!clk); 211 ltq_io_region_clk_rate = clk_get_rate(clk); 212 clk_put(clk); 213 214 if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST) 215 ltq_wdt_bootstatus = WDIOF_CARDRESET; 216 217 return misc_register(<q_wdt_miscdev); 218 } 219 220 static int __devexit 221 ltq_wdt_remove(struct platform_device *pdev) 222 { 223 misc_deregister(<q_wdt_miscdev); 224 225 if (ltq_wdt_membase) 226 iounmap(ltq_wdt_membase); 227 228 return 0; 229 } 230 231 232 static struct platform_driver ltq_wdt_driver = { 233 .remove = __devexit_p(ltq_wdt_remove), 234 .driver = { 235 .name = "ltq_wdt", 236 .owner = THIS_MODULE, 237 }, 238 }; 239 240 static int __init 241 init_ltq_wdt(void) 242 { 243 return platform_driver_probe(<q_wdt_driver, ltq_wdt_probe); 244 } 245 246 static void __exit 247 exit_ltq_wdt(void) 248 { 249 return platform_driver_unregister(<q_wdt_driver); 250 } 251 252 module_init(init_ltq_wdt); 253 module_exit(exit_ltq_wdt); 254 255 module_param(nowayout, int, 0); 256 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 257 258 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); 259 MODULE_DESCRIPTION("Lantiq SoC Watchdog"); 260 MODULE_LICENSE("GPL"); 261 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 262