linux/drivers/watchdog/pika_wdt.c
<<
>>
Prefs
   1/*
   2 * PIKA FPGA based Watchdog Timer
   3 *
   4 * Copyright (c) 2008 PIKA Technologies
   5 *   Sean MacLennan <smaclennan@pikatech.com>
   6 */
   7
   8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9
  10#include <linux/init.h>
  11#include <linux/errno.h>
  12#include <linux/module.h>
  13#include <linux/moduleparam.h>
  14#include <linux/types.h>
  15#include <linux/kernel.h>
  16#include <linux/fs.h>
  17#include <linux/miscdevice.h>
  18#include <linux/watchdog.h>
  19#include <linux/reboot.h>
  20#include <linux/jiffies.h>
  21#include <linux/timer.h>
  22#include <linux/bitops.h>
  23#include <linux/uaccess.h>
  24#include <linux/io.h>
  25#include <linux/of_platform.h>
  26
  27#define DRV_NAME "PIKA-WDT"
  28
  29/* Hardware timeout in seconds */
  30#define WDT_HW_TIMEOUT 2
  31
  32/* Timer heartbeat (500ms) */
  33#define WDT_TIMEOUT     (HZ/2)
  34
  35/* User land timeout */
  36#define WDT_HEARTBEAT 15
  37static int heartbeat = WDT_HEARTBEAT;
  38module_param(heartbeat, int, 0);
  39MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
  40        "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
  41
  42static bool nowayout = WATCHDOG_NOWAYOUT;
  43module_param(nowayout, bool, 0);
  44MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
  45        "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  46
  47static struct {
  48        void __iomem *fpga;
  49        unsigned long next_heartbeat;   /* the next_heartbeat for the timer */
  50        unsigned long open;
  51        char expect_close;
  52        int bootstatus;
  53        struct timer_list timer;        /* The timer that pings the watchdog */
  54} pikawdt_private;
  55
  56static struct watchdog_info ident = {
  57        .identity       = DRV_NAME,
  58        .options        = WDIOF_CARDRESET |
  59                          WDIOF_SETTIMEOUT |
  60                          WDIOF_KEEPALIVEPING |
  61                          WDIOF_MAGICCLOSE,
  62};
  63
  64/*
  65 * Reload the watchdog timer.  (ie, pat the watchdog)
  66 */
  67static inline void pikawdt_reset(void)
  68{
  69        /* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) --
  70         * Bit 7,    WTCHDG_EN: When set to 1, the watchdog timer is enabled.
  71         *           Once enabled, it cannot be disabled. The watchdog can be
  72         *           kicked by performing any write access to the reset
  73         *           control register (this register).
  74         * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in
  75         *           seconds. Valid ranges are 1 to 15 seconds. The value can
  76         *           be modified dynamically.
  77         */
  78        unsigned reset = in_be32(pikawdt_private.fpga + 0x14);
  79        /* enable with max timeout - 15 seconds */
  80        reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8);
  81        out_be32(pikawdt_private.fpga + 0x14, reset);
  82}
  83
  84/*
  85 * Timer tick
  86 */
  87static void pikawdt_ping(unsigned long data)
  88{
  89        if (time_before(jiffies, pikawdt_private.next_heartbeat) ||
  90                        (!nowayout && !pikawdt_private.open)) {
  91                pikawdt_reset();
  92                mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
  93        } else
  94                pr_crit("I will reset your machine !\n");
  95}
  96
  97
  98static void pikawdt_keepalive(void)
  99{
 100        pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ;
 101}
 102
 103static void pikawdt_start(void)
 104{
 105        pikawdt_keepalive();
 106        mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT);
 107}
 108
 109/*
 110 * Watchdog device is opened, and watchdog starts running.
 111 */
 112static int pikawdt_open(struct inode *inode, struct file *file)
 113{
 114        /* /dev/watchdog can only be opened once */
 115        if (test_and_set_bit(0, &pikawdt_private.open))
 116                return -EBUSY;
 117
 118        pikawdt_start();
 119
 120        return nonseekable_open(inode, file);
 121}
 122
 123/*
 124 * Close the watchdog device.
 125 */
 126static int pikawdt_release(struct inode *inode, struct file *file)
 127{
 128        /* stop internal ping */
 129        if (!pikawdt_private.expect_close)
 130                del_timer(&pikawdt_private.timer);
 131
 132        clear_bit(0, &pikawdt_private.open);
 133        pikawdt_private.expect_close = 0;
 134        return 0;
 135}
 136
 137/*
 138 * Pat the watchdog whenever device is written to.
 139 */
 140static ssize_t pikawdt_write(struct file *file, const char __user *data,
 141                             size_t len, loff_t *ppos)
 142{
 143        if (!len)
 144                return 0;
 145
 146        /* Scan for magic character */
 147        if (!nowayout) {
 148                size_t i;
 149
 150                pikawdt_private.expect_close = 0;
 151
 152                for (i = 0; i < len; i++) {
 153                        char c;
 154                        if (get_user(c, data + i))
 155                                return -EFAULT;
 156                        if (c == 'V') {
 157                                pikawdt_private.expect_close = 42;
 158                                break;
 159                        }
 160                }
 161        }
 162
 163        pikawdt_keepalive();
 164
 165        return len;
 166}
 167
 168/*
 169 * Handle commands from user-space.
 170 */
 171static long pikawdt_ioctl(struct file *file,
 172                unsigned int cmd, unsigned long arg)
 173{
 174        void __user *argp = (void __user *)arg;
 175        int __user *p = argp;
 176        int new_value;
 177
 178        switch (cmd) {
 179        case WDIOC_GETSUPPORT:
 180                return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
 181
 182        case WDIOC_GETSTATUS:
 183                return put_user(0, p);
 184
 185        case WDIOC_GETBOOTSTATUS:
 186                return put_user(pikawdt_private.bootstatus, p);
 187
 188        case WDIOC_KEEPALIVE:
 189                pikawdt_keepalive();
 190                return 0;
 191
 192        case WDIOC_SETTIMEOUT:
 193                if (get_user(new_value, p))
 194                        return -EFAULT;
 195
 196                heartbeat = new_value;
 197                pikawdt_keepalive();
 198
 199                return put_user(new_value, p);  /* return current value */
 200
 201        case WDIOC_GETTIMEOUT:
 202                return put_user(heartbeat, p);
 203        }
 204        return -ENOTTY;
 205}
 206
 207
 208static const struct file_operations pikawdt_fops = {
 209        .owner          = THIS_MODULE,
 210        .llseek         = no_llseek,
 211        .open           = pikawdt_open,
 212        .release        = pikawdt_release,
 213        .write          = pikawdt_write,
 214        .unlocked_ioctl = pikawdt_ioctl,
 215};
 216
 217static struct miscdevice pikawdt_miscdev = {
 218        .minor  = WATCHDOG_MINOR,
 219        .name   = "watchdog",
 220        .fops   = &pikawdt_fops,
 221};
 222
 223static int __init pikawdt_init(void)
 224{
 225        struct device_node *np;
 226        void __iomem *fpga;
 227        static u32 post1;
 228        int ret;
 229
 230        np = of_find_compatible_node(NULL, NULL, "pika,fpga");
 231        if (np == NULL) {
 232                pr_err("Unable to find fpga\n");
 233                return -ENOENT;
 234        }
 235
 236        pikawdt_private.fpga = of_iomap(np, 0);
 237        of_node_put(np);
 238        if (pikawdt_private.fpga == NULL) {
 239                pr_err("Unable to map fpga\n");
 240                return -ENOMEM;
 241        }
 242
 243        ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff;
 244
 245        /* POST information is in the sd area. */
 246        np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd");
 247        if (np == NULL) {
 248                pr_err("Unable to find fpga-sd\n");
 249                ret = -ENOENT;
 250                goto out;
 251        }
 252
 253        fpga = of_iomap(np, 0);
 254        of_node_put(np);
 255        if (fpga == NULL) {
 256                pr_err("Unable to map fpga-sd\n");
 257                ret = -ENOMEM;
 258                goto out;
 259        }
 260
 261        /* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) --
 262         * Bit 31,   WDOG: Set to 1 when the last reset was caused by a watchdog
 263         *           timeout.
 264         */
 265        post1 = in_be32(fpga + 0x40);
 266        if (post1 & 0x80000000)
 267                pikawdt_private.bootstatus = WDIOF_CARDRESET;
 268
 269        iounmap(fpga);
 270
 271        setup_timer(&pikawdt_private.timer, pikawdt_ping, 0);
 272
 273        ret = misc_register(&pikawdt_miscdev);
 274        if (ret) {
 275                pr_err("Unable to register miscdev\n");
 276                goto out;
 277        }
 278
 279        pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
 280                heartbeat, nowayout);
 281        return 0;
 282
 283out:
 284        iounmap(pikawdt_private.fpga);
 285        return ret;
 286}
 287
 288static void __exit pikawdt_exit(void)
 289{
 290        misc_deregister(&pikawdt_miscdev);
 291
 292        iounmap(pikawdt_private.fpga);
 293}
 294
 295module_init(pikawdt_init);
 296module_exit(pikawdt_exit);
 297
 298MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>");
 299MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer");
 300MODULE_LICENSE("GPL");
 301MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 302
 303
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.