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