2  * drivers/watchdog/ar7_wdt.c
 
   4  * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
 
   5  * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
 
   7  * Some code taken from:
 
   8  * National Semiconductor SCx200 Watchdog support
 
   9  * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
 
  11  * This program is free software; you can redistribute it and/or modify
 
  12  * it under the terms of the GNU General Public License as published by
 
  13  * the Free Software Foundation; either version 2 of the License, or
 
  14  * (at your option) any later version.
 
  16  * This program is distributed in the hope that it will be useful,
 
  17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  19  * GNU General Public License for more details.
 
  21  * You should have received a copy of the GNU General Public License
 
  22  * along with this program; if not, write to the Free Software
 
  23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
  26 #include <linux/module.h>
 
  27 #include <linux/moduleparam.h>
 
  28 #include <linux/errno.h>
 
  29 #include <linux/init.h>
 
  30 #include <linux/miscdevice.h>
 
  31 #include <linux/watchdog.h>
 
  32 #include <linux/notifier.h>
 
  33 #include <linux/reboot.h>
 
  35 #include <linux/ioport.h>
 
  37 #include <linux/uaccess.h>
 
  39 #include <asm/addrspace.h>
 
  40 #include <asm/ar7/ar7.h>
 
  42 #define DRVNAME "ar7_wdt"
 
  43 #define LONGNAME "TI AR7 Watchdog Timer"
 
  45 MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
 
  46 MODULE_DESCRIPTION(LONGNAME);
 
  47 MODULE_LICENSE("GPL");
 
  48 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 
  50 static int margin = 60;
 
  51 module_param(margin, int, 0);
 
  52 MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
 
  54 static int nowayout = WATCHDOG_NOWAYOUT;
 
  55 module_param(nowayout, int, 0);
 
  56 MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
 
  58 #define READ_REG(x) readl((void __iomem *)&(x))
 
  59 #define WRITE_REG(x, v) writel((v), (void __iomem *)&(x))
 
  72 static struct semaphore open_semaphore;
 
  73 static unsigned expect_close;
 
  75 /* XXX currently fixed, allows max margin ~68.72 secs */
 
  76 #define prescale_value 0xffff
 
  78 /* Offset of the WDT registers */
 
  79 static unsigned long ar7_regs_wdt;
 
  80 /* Pointer to the remapped WDT IO space */
 
  81 static struct ar7_wdt *ar7_wdt;
 
  82 static void ar7_wdt_get_regs(void)
 
  84         u16 chip_id = ar7_chip_id();
 
  88                 ar7_regs_wdt = AR7_REGS_WDT;
 
  91                 ar7_regs_wdt = UR8_REGS_WDT;
 
  97 static void ar7_wdt_kick(u32 value)
 
  99         WRITE_REG(ar7_wdt->kick_lock, 0x5555);
 
 100         if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) {
 
 101                 WRITE_REG(ar7_wdt->kick_lock, 0xaaaa);
 
 102                 if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) {
 
 103                         WRITE_REG(ar7_wdt->kick, value);
 
 107         printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n");
 
 110 static void ar7_wdt_prescale(u32 value)
 
 112         WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a);
 
 113         if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) {
 
 114                 WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5);
 
 115                 if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) {
 
 116                         WRITE_REG(ar7_wdt->prescale, value);
 
 120         printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n");
 
 123 static void ar7_wdt_change(u32 value)
 
 125         WRITE_REG(ar7_wdt->change_lock, 0x6666);
 
 126         if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) {
 
 127                 WRITE_REG(ar7_wdt->change_lock, 0xbbbb);
 
 128                 if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) {
 
 129                         WRITE_REG(ar7_wdt->change, value);
 
 133         printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n");
 
 136 static void ar7_wdt_disable(u32 value)
 
 138         WRITE_REG(ar7_wdt->disable_lock, 0x7777);
 
 139         if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) {
 
 140                 WRITE_REG(ar7_wdt->disable_lock, 0xcccc);
 
 141                 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) {
 
 142                         WRITE_REG(ar7_wdt->disable_lock, 0xdddd);
 
 143                         if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) {
 
 144                                 WRITE_REG(ar7_wdt->disable, value);
 
 149         printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n");
 
 152 static void ar7_wdt_update_margin(int new_margin)
 
 156         change = new_margin * (ar7_vbus_freq() / prescale_value);
 
 157         if (change < 1) change = 1;
 
 158         if (change > 0xffff) change = 0xffff;
 
 159         ar7_wdt_change(change);
 
 160         margin = change * prescale_value / ar7_vbus_freq();
 
 161         printk(KERN_INFO DRVNAME
 
 162                ": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
 
 163                margin, prescale_value, change, ar7_vbus_freq());
 
 166 static void ar7_wdt_enable_wdt(void)
 
 168         printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n");
 
 173 static void ar7_wdt_disable_wdt(void)
 
 175         printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n");
 
 179 static int ar7_wdt_open(struct inode *inode, struct file *file)
 
 181         /* only allow one at a time */
 
 182         if (down_trylock(&open_semaphore))
 
 184         ar7_wdt_enable_wdt();
 
 187         return nonseekable_open(inode, file);
 
 190 static int ar7_wdt_release(struct inode *inode, struct file *file)
 
 193                 printk(KERN_WARNING DRVNAME
 
 194                 ": watchdog device closed unexpectedly,"
 
 195                 "will not disable the watchdog timer\n");
 
 197                 ar7_wdt_disable_wdt();
 
 204 static int ar7_wdt_notify_sys(struct notifier_block *this,
 
 205                               unsigned long code, void *unused)
 
 207         if (code == SYS_HALT || code == SYS_POWER_OFF)
 
 209                         ar7_wdt_disable_wdt();
 
 214 static struct notifier_block ar7_wdt_notifier = {
 
 215         .notifier_call = ar7_wdt_notify_sys
 
 218 static ssize_t ar7_wdt_write(struct file *file, const char *data,
 
 219                              size_t len, loff_t *ppos)
 
 221         /* check for a magic close character */
 
 228                 for (i = 0; i < len; ++i) {
 
 230                         if (get_user(c, data+i))
 
 240 static int ar7_wdt_ioctl(struct inode *inode, struct file *file,
 
 241                          unsigned int cmd, unsigned long arg)
 
 243         static struct watchdog_info ident = {
 
 244                 .identity = LONGNAME,
 
 245                 .firmware_version = 1,
 
 246                 .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
 
 253         case WDIOC_GETSUPPORT:
 
 254                 if (copy_to_user((struct watchdog_info *)arg, &ident,
 
 258         case WDIOC_GETSTATUS:
 
 259         case WDIOC_GETBOOTSTATUS:
 
 260                 if (put_user(0, (int *)arg))
 
 263         case WDIOC_KEEPALIVE:
 
 266         case WDIOC_SETTIMEOUT:
 
 267                 if (get_user(new_margin, (int *)arg))
 
 272                 ar7_wdt_update_margin(new_margin);
 
 275         case WDIOC_GETTIMEOUT:
 
 276                 if (put_user(margin, (int *)arg))
 
 282 static const struct file_operations ar7_wdt_fops = {
 
 283         .owner          = THIS_MODULE,
 
 284         .write          = ar7_wdt_write,
 
 285         .ioctl          = ar7_wdt_ioctl,
 
 286         .open           = ar7_wdt_open,
 
 287         .release        = ar7_wdt_release,
 
 290 static struct miscdevice ar7_wdt_miscdev = {
 
 291         .minor          = WATCHDOG_MINOR,
 
 293         .fops           = &ar7_wdt_fops,
 
 296 static int __init ar7_wdt_init(void)
 
 302         if (!request_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt),
 
 304                 printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
 
 308         ar7_wdt = (struct ar7_wdt *)
 
 309                         ioremap(ar7_regs_wdt, sizeof(struct ar7_wdt));
 
 311         ar7_wdt_disable_wdt();
 
 312         ar7_wdt_prescale(prescale_value);
 
 313         ar7_wdt_update_margin(margin);
 
 315         sema_init(&open_semaphore, 1);
 
 317         rc = register_reboot_notifier(&ar7_wdt_notifier);
 
 319                 printk(KERN_ERR DRVNAME
 
 320                         ": unable to register reboot notifier\n");
 
 324         rc = misc_register(&ar7_wdt_miscdev);
 
 326                 printk(KERN_ERR DRVNAME ": unable to register misc device\n");
 
 332         unregister_reboot_notifier(&ar7_wdt_notifier);
 
 335         release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
 
 340 static void __exit ar7_wdt_cleanup(void)
 
 342         misc_deregister(&ar7_wdt_miscdev);
 
 343         unregister_reboot_notifier(&ar7_wdt_notifier);
 
 345         release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
 
 348 module_init(ar7_wdt_init);
 
 349 module_exit(ar7_wdt_cleanup);