Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Lantiq: Add watchdog support

This patch adds the driver for the watchdog found inside the Lantiq SoC family.

Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Ralph Hempel <ralph.hempel@lantiq.com>
Cc: Wim Van Sebroeck <wim@iguana.be>
Cc: linux-mips@linux-mips.org
Cc: linux-watchdog@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/2327/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

John Crispin and committed by
Ralf Baechle
2f58b8d0 f1f0ceaa

+268
+6
drivers/watchdog/Kconfig
··· 990 990 To compile this driver as a loadable module, choose M here. 991 991 The module will be called bcm63xx_wdt. 992 992 993 + config LANTIQ_WDT 994 + tristate "Lantiq SoC watchdog" 995 + depends on LANTIQ 996 + help 997 + Hardware driver for the Lantiq SoC Watchdog Timer. 998 + 993 999 # PARISC Architecture 994 1000 995 1001 # POWERPC Architecture
+1
drivers/watchdog/Makefile
··· 123 123 obj-$(CONFIG_TXX9_WDT) += txx9wdt.o 124 124 obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o 125 125 octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o 126 + obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o 126 127 127 128 # PARISC Architecture 128 129
+261
drivers/watchdog/lantiq_wdt.c
··· 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, &ltq_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, &ltq_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 = &ltq_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(&ltq_wdt_miscdev); 218 + } 219 + 220 + static int __devexit 221 + ltq_wdt_remove(struct platform_device *pdev) 222 + { 223 + misc_deregister(&ltq_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(&ltq_wdt_driver, ltq_wdt_probe); 244 + } 245 + 246 + static void __exit 247 + exit_ltq_wdt(void) 248 + { 249 + return platform_driver_unregister(&ltq_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);