1/* 2 * PNX833x Hardware Watchdog Driver 3 * Copyright 2008 NXP Semiconductors 4 * Daniel Laird <daniel.j.laird@nxp.com> 5 * Andre McCurdy <andre.mccurdy@nxp.com> 6 * 7 * Heavily based upon - IndyDog 0.3 8 * A Hardware Watchdog Device for SGI IP22 9 * 10 * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved. 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 15 * 2 of the License, or (at your option) any later version. 16 * 17 * based on softdog.c by Alan Cox <alan@redhat.com> 18 */ 19 20#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 21 22#include <linux/module.h> 23#include <linux/moduleparam.h> 24#include <linux/types.h> 25#include <linux/kernel.h> 26#include <linux/fs.h> 27#include <linux/mm.h> 28#include <linux/miscdevice.h> 29#include <linux/watchdog.h> 30#include <linux/notifier.h> 31#include <linux/reboot.h> 32#include <linux/init.h> 33#include <asm/mach-pnx833x/pnx833x.h> 34 35#define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */ 36#define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */ 37#define PNX_WATCHDOG_TIMEOUT (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY) 38#define PNX_TIMEOUT_VALUE 2040000000U 39 40/** CONFIG block */ 41#define PNX833X_CONFIG (0x07000U) 42#define PNX833X_CONFIG_CPU_WATCHDOG (0x54) 43#define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58) 44#define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c) 45 46/** RESET block */ 47#define PNX833X_RESET (0x08000U) 48#define PNX833X_RESET_CONFIG (0x08) 49 50static int pnx833x_wdt_alive; 51 52/* Set default timeout in MHZ.*/ 53static int pnx833x_wdt_timeout = PNX_WATCHDOG_TIMEOUT; 54module_param(pnx833x_wdt_timeout, int, 0); 55MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default=" 56 __MODULE_STRING(PNX_TIMEOUT_VALUE) "(30 seconds)."); 57 58static bool nowayout = WATCHDOG_NOWAYOUT; 59module_param(nowayout, bool, 0); 60MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 61 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 62 63#define START_DEFAULT 1 64static int start_enabled = START_DEFAULT; 65module_param(start_enabled, int, 0); 66MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion " 67 "(default=" __MODULE_STRING(START_DEFAULT) ")"); 68 69static void pnx833x_wdt_start(void) 70{ 71 /* Enable watchdog causing reset. */ 72 PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1; 73 /* Set timeout.*/ 74 PNX833X_REG(PNX833X_CONFIG + 75 PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; 76 /* Enable watchdog. */ 77 PNX833X_REG(PNX833X_CONFIG + 78 PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1; 79 80 pr_info("Started watchdog timer\n"); 81} 82 83static void pnx833x_wdt_stop(void) 84{ 85 /* Disable watchdog causing reset. */ 86 PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE; 87 /* Disable watchdog.*/ 88 PNX833X_REG(PNX833X_CONFIG + 89 PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE; 90 91 pr_info("Stopped watchdog timer\n"); 92} 93 94static void pnx833x_wdt_ping(void) 95{ 96 PNX833X_REG(PNX833X_CONFIG + 97 PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; 98} 99 100/* 101 * Allow only one person to hold it open 102 */ 103static int pnx833x_wdt_open(struct inode *inode, struct file *file) 104{ 105 if (test_and_set_bit(0, &pnx833x_wdt_alive)) 106 return -EBUSY; 107 108 if (nowayout) 109 __module_get(THIS_MODULE); 110 111 /* Activate timer */ 112 if (!start_enabled) 113 pnx833x_wdt_start(); 114 115 pnx833x_wdt_ping(); 116 117 pr_info("Started watchdog timer\n"); 118 119 return nonseekable_open(inode, file); 120} 121 122static int pnx833x_wdt_release(struct inode *inode, struct file *file) 123{ 124 /* Shut off the timer. 125 * Lock it in if it's a module and we defined ...NOWAYOUT */ 126 if (!nowayout) 127 pnx833x_wdt_stop(); /* Turn the WDT off */ 128 129 clear_bit(0, &pnx833x_wdt_alive); 130 return 0; 131} 132 133static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) 134{ 135 /* Refresh the timer. */ 136 if (len) 137 pnx833x_wdt_ping(); 138 139 return len; 140} 141 142static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd, 143 unsigned long arg) 144{ 145 int options, new_timeout = 0; 146 uint32_t timeout, timeout_left = 0; 147 148 static const struct watchdog_info ident = { 149 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 150 .firmware_version = 0, 151 .identity = "Hardware Watchdog for PNX833x", 152 }; 153 154 switch (cmd) { 155 default: 156 return -ENOTTY; 157 158 case WDIOC_GETSUPPORT: 159 if (copy_to_user((struct watchdog_info *)arg, 160 &ident, sizeof(ident))) 161 return -EFAULT; 162 return 0; 163 164 case WDIOC_GETSTATUS: 165 case WDIOC_GETBOOTSTATUS: 166 return put_user(0, (int *)arg); 167 168 case WDIOC_SETOPTIONS: 169 if (get_user(options, (int *)arg)) 170 return -EFAULT; 171 172 if (options & WDIOS_DISABLECARD) 173 pnx833x_wdt_stop(); 174 175 if (options & WDIOS_ENABLECARD) 176 pnx833x_wdt_start(); 177 178 return 0; 179 180 case WDIOC_KEEPALIVE: 181 pnx833x_wdt_ping(); 182 return 0; 183 184 case WDIOC_SETTIMEOUT: 185 { 186 if (get_user(new_timeout, (int *)arg)) 187 return -EFAULT; 188 189 pnx833x_wdt_timeout = new_timeout; 190 PNX833X_REG(PNX833X_CONFIG + 191 PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout; 192 return put_user(new_timeout, (int *)arg); 193 } 194 195 case WDIOC_GETTIMEOUT: 196 timeout = PNX833X_REG(PNX833X_CONFIG + 197 PNX833X_CONFIG_CPU_WATCHDOG_COMPARE); 198 return put_user(timeout, (int *)arg); 199 200 case WDIOC_GETTIMELEFT: 201 timeout_left = PNX833X_REG(PNX833X_CONFIG + 202 PNX833X_CONFIG_CPU_WATCHDOG); 203 return put_user(timeout_left, (int *)arg); 204 205 } 206} 207 208static int pnx833x_wdt_notify_sys(struct notifier_block *this, 209 unsigned long code, void *unused) 210{ 211 if (code == SYS_DOWN || code == SYS_HALT) 212 pnx833x_wdt_stop(); /* Turn the WDT off */ 213 214 return NOTIFY_DONE; 215} 216 217static const struct file_operations pnx833x_wdt_fops = { 218 .owner = THIS_MODULE, 219 .llseek = no_llseek, 220 .write = pnx833x_wdt_write, 221 .unlocked_ioctl = pnx833x_wdt_ioctl, 222 .open = pnx833x_wdt_open, 223 .release = pnx833x_wdt_release, 224}; 225 226static struct miscdevice pnx833x_wdt_miscdev = { 227 .minor = WATCHDOG_MINOR, 228 .name = "watchdog", 229 .fops = &pnx833x_wdt_fops, 230}; 231 232static struct notifier_block pnx833x_wdt_notifier = { 233 .notifier_call = pnx833x_wdt_notify_sys, 234}; 235 236static int __init watchdog_init(void) 237{ 238 int ret, cause; 239 240 /* Lets check the reason for the reset.*/ 241 cause = PNX833X_REG(PNX833X_RESET); 242 /*If bit 31 is set then watchdog was cause of reset.*/ 243 if (cause & 0x80000000) { 244 pr_info("The system was previously reset due to the watchdog firing - please investigate...\n"); 245 } 246 247 ret = register_reboot_notifier(&pnx833x_wdt_notifier); 248 if (ret) { 249 pr_err("cannot register reboot notifier (err=%d)\n", ret); 250 return ret; 251 } 252 253 ret = misc_register(&pnx833x_wdt_miscdev); 254 if (ret) { 255 pr_err("cannot register miscdev on minor=%d (err=%d)\n", 256 WATCHDOG_MINOR, ret); 257 unregister_reboot_notifier(&pnx833x_wdt_notifier); 258 return ret; 259 } 260 261 pr_info("Hardware Watchdog Timer for PNX833x: Version 0.1\n"); 262 263 if (start_enabled) 264 pnx833x_wdt_start(); 265 266 return 0; 267} 268 269static void __exit watchdog_exit(void) 270{ 271 misc_deregister(&pnx833x_wdt_miscdev); 272 unregister_reboot_notifier(&pnx833x_wdt_notifier); 273} 274 275module_init(watchdog_init); 276module_exit(watchdog_exit); 277 278MODULE_AUTHOR("Daniel Laird/Andre McCurdy"); 279MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x"); 280MODULE_LICENSE("GPL"); 281