linux/drivers/watchdog/pnx833x_wdt.c
<<
>>
Prefs
   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");
 281MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 282