lkml.org 
[lkml]   [2011]   [May]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    SubjectRE: [PATCH 2/2] watchdog: add support for MOXA V2100 watchdog driver
    Date
    From
    From: Jimmy Chen <jimmy.chen@moxa.com>

    Does anyone have comment on this driver?

    Signed-off-by: Jimmy Chen <jimmy.chen@moxa.com>
    ---
    diff --git a/drivers/watchdog/moxa_wdt.c b/drivers/watchdog/moxa_wdt.c
    new file mode 100644
    index 0000000..fdecc9e
    --- /dev/null
    +++ b/drivers/watchdog/moxa_wdt.c
    @@ -0,0 +1,409 @@
    +/*
    + * serial driver for the MOXA V2100 platform.
    + *
    + * Copyright (c) MOXA Inc. All rights reserved.
    + * Jimmy Chen <jimmy.chen@moxa.com>
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License version 2 as
    + * published by the Free Software Foundation.
    + *
    + */
    +
    +#define pr_fmt(fmt) KBUILD_MODNAME":"fmt
    +
    +#include <linux/interrupt.h>
    +#include <linux/module.h>
    +#include <linux/moduleparam.h>
    +#include <linux/types.h>
    +#include <linux/miscdevice.h>
    +#include <linux/watchdog.h>
    +#include <linux/fs.h>
    +#include <linux/ioport.h>
    +#include <linux/notifier.h>
    +#include <linux/reboot.h>
    +#include <linux/init.h>
    +#include <linux/io.h>
    +#include <linux/uaccess.h>
    +
    +#include <asm/system.h>
    +
    +#define HW_VENDOR_ID_H ((u8)0x87)
    +#define HW_VENDOR_ID_L ((u8)0x83)
    +
    +#define SUPERIO_PORT ((u8)0x2e)
    +#define CHIP_ID_BYTE1 ((u8)0x20)
    +#define CHIP_ID_BYTE2 ((u8)0x21)
    +#define LOGIC_DEVICE_NUMBER ((u8)0x07)
    +#define GPIO_CONFIG_REG ((u8)0x07)
    +#define WATCHDOG_TIMER1_CTRL ((u8)0x71)
    +#define WATCHDOG_TIMER1_CONFIG_REG ((u8)0x72)
    +#define WATCHDOG_TIMER1_TIMEOUT_VAL ((u8)0x73)
    +#define WATCHDOG_TIMER2_CTRL ((u8)0x81)
    +#define WATCHDOG_TIMER2_CONFIG_REG ((u8)0x82)
    +#define WATCHDOG_TIMER2_TIMEOUT_VAL ((u8)0x83)
    +
    +#define DEFAULT_WATCHDOG_TIMEOUT (30UL*1000UL) /* 30 seconds */
    +#define WATCHDOG_MIN_TIMEOUT (1UL*1000UL) /* 2 seconds */
    +#define WATCHDOG_MAX_TIMEOUT (255UL*1000UL) /* 255 seconds */
    +
    +
    +static unsigned long wdt_is_open;
    +static char expect_close;
    +
    +static int timeout = DEFAULT_WATCHDOG_TIMEOUT;
    +module_param(timeout, int, 0);
    +MODULE_PARM_DESC(timeout,
    + "Watchdog timeout in seconds. 1<= timeout <=63, default="
    + __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
    +
    +static int nowayout = WATCHDOG_NOWAYOUT;
    +module_param(nowayout, int, 0);
    +MODULE_PARM_DESC(nowayout, "Watchdog cannot be "
    + "stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
    +
    +static spinlock_t wdt_lock = SPIN_LOCK_UNLOCKED;
    +
    +static inline unsigned char superio_get_reg(u8 val)
    +{
    + outb_p(val, SUPERIO_PORT);
    + val = inb_p(SUPERIO_PORT+1);
    + return val;
    +}
    +
    +static inline void superio_set_reg(u8 val, u8 index)
    +{
    + outb_p(index, SUPERIO_PORT);
    + outb_p(val, (SUPERIO_PORT+1));
    +}
    +
    +static inline void superio_select_dev(u8 val)
    +{
    + superio_set_reg(val, LOGIC_DEVICE_NUMBER);
    +}
    +
    +static inline void superio_init(void)
    +{
    + outb(0x87, SUPERIO_PORT);
    + outb(0x01, SUPERIO_PORT);
    + outb(0x55, SUPERIO_PORT);
    + outb(0x55, SUPERIO_PORT);
    +}
    +
    +static inline void superio_release(void)
    +{
    + outb_p(0x02, SUPERIO_PORT);
    + outb_p(0x02, SUPERIO_PORT+1);
    +}
    +
    +/**
    + * wdt_start:
    + *
    + * Start the watchdog driver.
    + */
    +
    +static int moxawdt_start(void)
    +{
    + unsigned long flags;
    + unsigned char val;
    +
    + pr_debug("wdt_start: timeout=%d\n", timeout);
    +
    + spin_lock_irqsave(&wdt_lock, flags);
    + superio_init();
    + superio_select_dev(GPIO_CONFIG_REG);
    + val = superio_get_reg(WATCHDOG_TIMER1_CONFIG_REG) | 0x10;
    + superio_set_reg(val, WATCHDOG_TIMER1_CONFIG_REG);
    + superio_set_reg((timeout / 1000), WATCHDOG_TIMER1_TIMEOUT_VAL);
    + spin_unlock_irqrestore(&wdt_lock, flags);
    + return 0;
    +}
    +
    +/**
    + * wdt_stop:
    + *
    + * Stop the watchdog driver.
    + */
    +
    +static int wdt_stop(void)
    +{
    + unsigned long flags;
    +
    + spin_lock_irqsave(&wdt_lock, flags);
    +
    + pr_debug("wdt_disable\n");
    + superio_init();
    + superio_select_dev(GPIO_CONFIG_REG);
    + superio_set_reg(0, WATCHDOG_TIMER1_TIMEOUT_VAL);
    + spin_unlock_irqrestore(&wdt_lock, flags);
    + return 0;
    +}
    +
    +/**
    + * wdt_ping:
    + *
    + * Reload counter one with the watchdog heartbeat. We don't bother
    + * reloading the cascade counter.
    + */
    +
    +static void wdt_ping(void)
    +{
    + unsigned long flags;
    +
    + spin_lock_irqsave(&wdt_lock, flags);
    +
    + pr_debug("wdt_ping: timeout=%d\n", timeout);
    + superio_init();
    + superio_select_dev(GPIO_CONFIG_REG);
    + superio_set_reg((timeout / 1000), WATCHDOG_TIMER1_TIMEOUT_VAL);
    + spin_unlock_irqrestore(&wdt_lock, flags);
    +}
    +
    +/**
    + * wdt_verify_vendor:
    + * return true if vendor ID match
    + */
    +
    +static int wdt_verify_vendor(void)
    +{
    + unsigned char chip_id_h;
    + unsigned char chip_id_l;
    +
    + superio_init();
    + superio_select_dev(GPIO_CONFIG_REG);
    + chip_id_h = superio_get_reg(CHIP_ID_BYTE1);
    + chip_id_l = superio_get_reg(CHIP_ID_BYTE2);
    + if ((chip_id_h == HW_VENDOR_ID_H) && (chip_id_l == HW_VENDOR_ID_L))
    + return 0;
    +
    + return 1;
    +}
    +
    +/**
    + * wdt_set_timeout:
    + * @t: the new heartbeat value that needs to be set.
    + *
    + * Set a new heartbeat value for the watchdog device. If the heartbeat
    + * value is incorrect we keep the old value and return -EINVAL. If
    + * successful we return 0.
    + */
    +
    +static int wdt_set_timeout(int *t)
    +{
    + if (*t < WATCHDOG_MIN_TIMEOUT || *t > WATCHDOG_MAX_TIMEOUT) {
    + *t = DEFAULT_WATCHDOG_TIMEOUT;
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +static int moxawdt_open(struct inode *inode, struct file *file)
    +{
    +
    + if (test_and_set_bit(0, &wdt_is_open))
    + return -EBUSY;
    +
    + pr_debug("moxawdt_open entry\n");
    + moxawdt_start();
    + return nonseekable_open(inode, file);
    +
    + return 0;
    +}
    +
    +static int moxawdt_release(struct inode *inode, struct file *file)
    +{
    + pr_debug("moxawdt_release entry\n");
    +
    + if (expect_close == 42) {
    + wdt_stop();
    + clear_bit(0, &wdt_is_open);
    + } else {
    + pr_crit("wdt: WDT device closed unexpectedly. WDT will not stop!\n");
    + wdt_ping();
    + }
    + expect_close = 0;
    +
    + return 0;
    +}
    +
    +static long moxawdt_ioctl(struct file *file,
    + unsigned int cmd, unsigned long arg)
    +{
    + void __user *argp = (void __user *)arg;
    + int __user *p = argp;
    + int new_timeout;
    + int status;
    +
    + static struct watchdog_info ident = {
    + .options = WDIOF_SETTIMEOUT|
    + WDIOF_MAGICCLOSE|
    + WDIOF_KEEPALIVEPING,
    + .firmware_version = 1,
    + .identity = "MOXA2100WDT ",
    + };
    +
    + switch (cmd) {
    + case WDIOC_GETSUPPORT:
    + return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
    + case WDIOC_GETSTATUS:
    + status = 1;
    + return put_user(status, p);
    + case WDIOC_GETBOOTSTATUS:
    + return put_user(0, p);
    + case WDIOC_KEEPALIVE:
    + wdt_ping();
    + return 0;
    + case WDIOC_SETTIMEOUT:
    + if (get_user(new_timeout, p))
    + return -EFAULT;
    + if (wdt_set_timeout(&new_timeout))
    + return -EINVAL;
    + wdt_ping();
    + /* Fall */
    + case WDIOC_GETTIMEOUT:
    + return put_user(timeout, p);
    + default:
    + return -ENOTTY;
    + }
    + return 0;
    +}
    +
    +/*
    + * moxawdt_write:
    + * @file: file handle to the watchdog
    + * @buf: buffer to write (unused as data does not matter here
    + * @count: count of bytes
    + * @ppos: pointer to the position to write. No seeks allowed
    + *
    + * A write to a watchdog device is defined as a keepalive signal. Any
    + * write of data will do, as we we don't define content meaning.
    + */
    +
    +static ssize_t moxawdt_write(struct file *file, const char *buf, \
    + size_t count, loff_t *ppos)
    +{
    + if (count) {
    + if (!nowayout) {
    + size_t i;
    +
    + /* In case it was set long ago */
    + for (i = 0; i != count; i++) {
    + char c;
    + if (get_user(c, buf + i))
    + return -EFAULT;
    + if (c == 'V')
    + expect_close = 42;
    + }
    + }
    +
    + }
    + return count;
    +}
    +
    +/**
    + * notify_sys:
    + * @this: our notifier block
    + * @code: the event being reported
    + * @unused: unused
    + *
    + * Our notifier is called on system shutdowns. We want to turn the card
    + * off at reboot otherwise the machine will reboot again during memory
    + * test or worse yet during the following fsck. This would suck, in fact
    + * trust me - if it happens it does suck.
    + */
    +
    +static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
    + void *unused)
    +{
    + if (code == SYS_DOWN || code == SYS_HALT)
    + wdt_stop();
    + return NOTIFY_DONE;
    +}
    +
    +/*
    + * The WDT card needs to learn about soft shutdowns in order to
    + * turn the timebomb registers off.
    + */
    +
    +static struct notifier_block wdt_notifier = {
    + .notifier_call = wdt_notify_sys,
    +};
    +
    +static const struct file_operations moxawdt_fops = {
    + .owner = THIS_MODULE,
    + .llseek = no_llseek,
    + .open = moxawdt_open,
    + .write = moxawdt_write,
    + .unlocked_ioctl = moxawdt_ioctl,
    + .release = moxawdt_release,
    +};
    +
    +static struct miscdevice wdt_miscdev = {
    + .minor = WATCHDOG_MINOR,
    + .name = "watchdog",
    + .fops = &moxawdt_fops,
    +};
    +
    +static void __exit moxawdt_exit(void)
    +{
    + misc_deregister(&wdt_miscdev);
    + unregister_reboot_notifier(&wdt_notifier);
    + release_region(SUPERIO_PORT, 2);
    + superio_release();
    +}
    +
    +static int __init moxawdt_init(void)
    +{
    + int ret;
    +
    + if (wdt_set_timeout(&timeout)) {
    + pr_err("timeout value must be %lu < timeout < %lu, using %d\n",
    + WATCHDOG_MIN_TIMEOUT, WATCHDOG_MAX_TIMEOUT,
    + timeout);
    + }
    +
    + if (!request_region(SUPERIO_PORT, 2, "moxawdt")) {
    + pr_err("moxawdt_init: can't get I/O address 0x%x\n", SUPERIO_PORT);
    + ret = -EBUSY;
    + goto reqreg_err;
    + }
    +
    + if (wdt_verify_vendor()) {
    + pr_err("hw device id not match!!\n");
    + ret = -ENODEV;
    + goto reqreg_err;
    + }
    +
    + ret = register_reboot_notifier(&wdt_notifier);
    + if (ret) {
    + pr_err("can't register reboot notifier\n");
    + goto regreb_err;
    + }
    +
    + ret = misc_register(&wdt_miscdev);
    + if (ret) {
    + pr_err("Moxa V2100-LX WatchDog: Register misc fail !\n");
    + goto regmisc_err;
    + }
    +
    + pr_info("Moxa V2100 Watchdog Driver. nowayout=%d, timeout=%d\n", nowayout, timeout);
    +
    + return 0;
    +
    +regmisc_err:
    + unregister_reboot_notifier(&wdt_notifier);
    +regreb_err:
    + release_region(SUPERIO_PORT, 2);
    +reqreg_err:
    + return ret;
    +}
    +
    +module_init(moxawdt_init);
    +module_exit(moxawdt_exit);
    +
    +MODULE_LICENSE("GPL v2");
    +MODULE_AUTHOR("Jimmy.Chen@moxa.com");
    +MODULE_DESCRIPTION("Moxa V2100-LX WDT driver");
    +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2011-05-06 07:55    [W:0.053 / U:32.824 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site