RE: Fintek F81865 chip (Wdog support)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Sorry.

Wrong driver...

Best regards,
Bruno Ferreira
________________________________________
From: BrunoFerreira [bruno.ferreira@xxxxxxxxxx]
Sent: Friday, August 02, 2013 3:17 PM
To: Wim Van Sebroeck
Cc: Giel van Schijndel; linux-watchdog@xxxxxxxxxxxxxxx
Subject: Re: Fintek F81865 chip (Wdog support)

Hi All.

Sorry about the delay.
Please find in attach the driver source code with support for this chip.

Kind regards,
Bruno Ferreira

On 05/26/2013 05:44 PM, Wim Van Sebroeck wrote:
> Hi All,
>
>> On Wed, 13 Mar 2013 15:04:38 +0000, BrunoFerreira wrote:
>>> I'm currently working with a new board iEi NOVA-PV-D5251 [1] that
>>> have the Fintek F81865 chip for Super I/O support and I will need to
>>> develop the watchdog driver and another to access to the IO that this
>>> board supports (gpio). I made a search and I see that already exists
>>> a
>>> driver for F71808E chip, I use this driver as an example an I made a
>>> new driver for F81865 chip. My driver is working pretty well, but
>>> I've
>>> a question that I can't find the answer on datasheet of this chip
>>> that
>>> may be Wim or Giel could know.
>>> On both chips, we need to configure a pin that can work as a normal
>>> GPIO (F81865: Set pin 70 the function of WDTRST#/GPIO15 is WDTRST# |
>>> F71808E: Set pin 21 to GPIO23/WDTRST#, then to WDTRST#), this is here
>>> where I get myself confused, this WDTRST will be mapped in any GPIO
>>> output on my board? I mean, if the watchdog is enable I will get any
>>> output pin set to 1 (i.e. the GPIO15, output 5?) and when the
>>> watchdog
>>> goes down this output goes to 0?
>> Sorry, I can't give you a definite answer on that. All Fintek
>> datasheets I've seen are very poorly written. At one point I actually
>> stopped trusting the datasheet enough to go through the hassle of
>> hooking up a scope to the pins of the chip.
>>
>> Hazarding a guess however, I'd say that when the watchdog is enabled
>> you cannot use the WDTRST pin as a GPIO pin.
>>
>>> The other question is to Wim, can you tell me if there is interest in
>>> add this chip support to kernel?
>> This would be a yes, unless there are good reasons not to include it in
>> mainline.
> The answer is indeed yes. Has a patch been created since this message?
>
> Kind regards,
> Wim.
>
>

/***************************************************************************
* Copyright (C) 2013 Bruno Ferreira <bruno.ferreira@xxxxxxxxxx>            *
*                                                                          *
* This program is free software; you can redistribute it and/or modify     *
* it under the terms of the GNU General Public License as published by     *
* the Free Software Foundation; either version 2 of the License, or        *
* (at your option) any later version.                                      *
*                                                                          *
* This program is distributed in the hope that it will be useful,          *
* but WITHOUT ANY WARRANTY; without even the implied warranty of           *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the             *
* GNU General Public License for more details.                             *
*                                                                          *
* You should have received a copy of the GNU General Public License        *
* along with this program; if not, write to the                            *
* Free Software Foundation, Inc.,                                          *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.                 *
***************************************************************************/

/**
   * [x86] Add support for Fintek hardware watchdogs (Closes: #601187)
     - resource: Add shared I/O region support
     - hwmon: f71882fg: Use a muxed resource lock for the Super I/O port
     - watchdog: Add f71808e_wdt driver

 http://lxr.free-electrons.com/source/drivers/watchdog/f71808e_wdt.c?v=3.3
 https://github.com/spotify/linux/blob/master/drivers/watchdog/f71808e_wdt.c
 */

#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <linux/ioport.h>

#define DRVNAME                 "f81865_wdt"

/* Global Control Registers */
#define SIO_F81865_LD_WDT	0x07	/* Watchdog Logic Number Register (LDN) */
#define SIO_F81865_LD_GPIO	0x06	/* GPIO Logic Number Register (LDN) */
#define SIO_UNLOCK_KEY		0x87	/* Key to enable Super-I/O */
#define SIO_LOCK_KEY		0xAA	/* Key to diasble Super-I/O */

#define SIO_REG_LDSEL		0x07	/* Logical device select */
#define SIO_REG_DEVID		0x20	/* Device ID (2 bytes) */
#define SIO_REG_DEVREV		0x22	/* Device revision */
#define SIO_REG_MANID		0x23	/* Fintek ID (2 bytes) */

/* WDT Device Configuration Registers (LDN CR07) */
#define SIO_REG_ENABLE		0x30	/* WDT Device enable register */
#define SIO_REG_ADDR		0x60	/* Base device address (2 bytes) */
#define F81865_REG_WDT_CONF     0xf5    /* WDT Control Register */
#define F81865_REG_WD_TIME      0xf6    /* WDT Time Register */
#define F81865_REG_WD_PME       0xfa    /* WDT PME Enable register */

/* Manufacture and Chip Information */
#define SIO_FINTEK_ID		0x1934	/* Manufacturers ID */
#define SIO_F81865_ID           0x0704  /* Chipset ID*/

/* Watchdog Timer Function and WDT Control Register Flags */
#define F81865_FLAG_WDTMOUT_STS	6       /* WD timeout event */
#define F81865_FLAG_WD_EN       5       /* WD time counting */
#define F81865_FLAG_WD_PULSE    4       /* WD pulse mode */
#define F81865_FLAG_WD_UNIT     3       /* WD time unit */
#define F81865_FLAG_WD_UACTIVE  2       /* WD RSTOUT polarity */

/* Default values */
#define WATCHDOG_TIMEOUT        60      /* 1 minute default timeout */
#define WATCHDOG_MAX_TIMEOUT    (60 * 255) /* WD_TIME is a byte long */
#define WATCHDOG_PULSE_WIDTH    5000     /* 125 ms, default pulse width for 
                                        watchdog signal */

/*  Module parameters */
static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, " Override the detected device ID");

static const int max_timeout = WATCHDOG_MAX_TIMEOUT;
static int timeout = 60;                /* default timeout in seconds */
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, 
                " Watchdog timeout in seconds. 1<= timeout <=" 
                __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) " (default=" 
                __MODULE_STRING(WATCHDOG_TIMEOUT) ")");

static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
module_param(pulse_width, uint, 0);
MODULE_PARM_DESC(pulse_width,
	" Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms"
			" (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");

static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0444);
MODULE_PARM_DESC(nowayout, " Disable watchdog shutdown on close");

static unsigned int start_withtimeout;
module_param(start_withtimeout, uint, 0);
MODULE_PARM_DESC(start_withtimeout, " Start watchdog timer on module load with"
	" given initial timeout. Zero (default) disables this feature.");

/* Chips variants */
enum chips { f81865 };

/* Chips Names */
static const char *f81865_names[] = { "F81865" };

/* Super-I/O Function prototypes */
static inline int superio_enter(int base);
static inline void superio_exit(int base);
static inline void superio_select(int base, int ldn);
static inline int superio_inb(int base, int reg);
static inline int superio_inw(int base, int reg);
static inline void superio_outb(int base, int reg, u8 val);
static inline void superio_set_bit(int base, int reg, int bit);
static inline void superio_clear_bit(int base, int reg, int bit);

/* Wdog internal data information */
struct watchdog_data {
    unsigned short      sioaddr;        /* default index port */
    enum chips          type;           /* chip type */
    unsigned long       opened;         /* driver open state */
    struct mutex        lock;           /* concurrency control */
    char                expect_close;   /* controlled close */
    struct watchdog_info ident;         /* wdog information*/

    unsigned short      timeout;        /* current wdog timeout */
    u8                  timer_val;	/* content for the WD_TIME register */
    u8                  pulse_val;	/* pulse width flag */
    char                pulse_mode;	/* enable pulse output mode? */
    char                caused_reboot;	/* last reboot was by the watchdog */
};

static struct watchdog_data watchdog = {
    .lock = __MUTEX_INITIALIZER(watchdog.lock),
};

/* Super I/O functions */
static inline int superio_enter(int base)
{
    /* don't step on other drivers' I/O space by accident */
    if (!request_muxed_region(base, 2, DRVNAME)) {
        printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n", 
              (int)base);
        return -EBUSY;
    }

    /* according to the datasheet the key must be send twice! */
    outb(SIO_UNLOCK_KEY, base);
    outb(SIO_UNLOCK_KEY, base);

    return 0;
}

static inline void superio_exit(int base)
{
    outb(SIO_LOCK_KEY, base);
    release_region(base, 2);
}

static inline void superio_select(int base, int ldn)
{
    outb(SIO_REG_LDSEL, base);
    outb(ldn, base + 1);
}

static inline int superio_inb(int base, int reg)
{
    outb(reg, base);
    return inb(base + 1);
}

static int superio_inw(int base, int reg)
{
    int val;
    val  = superio_inb(base, reg) << 8;
    val |= superio_inb(base, reg + 1);
    return val;
}

static inline void superio_outb(int base, int reg, u8 val)
{
    outb(reg, base);
    outb(val, base + 1);
}

static inline void superio_set_bit(int base, int reg, int bit)
{
    unsigned long val = superio_inb(base, reg);
    __set_bit(bit, &val);
    superio_outb(base, reg, val);
}

static inline void superio_clear_bit(int base, int reg, int bit)
{
    unsigned long val = superio_inb(base, reg);
    __clear_bit(bit, &val);
    superio_outb(base, reg, val);
}

/* Internal Configuration functions */
static int watchdog_set_timeout(int timeout)
{
    if (timeout <= 0 || timeout >  max_timeout) {
        printk(KERN_ERR DRVNAME ": watchdog timeout out of range\n");
        return -EINVAL;
    }

    mutex_lock(&watchdog.lock);

    watchdog.timeout = timeout;
    if (timeout > 0xff) {
        watchdog.timer_val = DIV_ROUND_UP(timeout, 60);
    } else {
        watchdog.timer_val = timeout;
    }

    mutex_unlock(&watchdog.lock);
    printk(KERN_INFO DRVNAME ": watchdog_set_timeout(%d)...\n", timeout);
    return 0;
}

static int watchdog_set_pulse_width(unsigned int pw)
{
    int err = 0;
    mutex_lock(&watchdog.lock);

    if (pw <= 1) {
        watchdog.pulse_val = 0;
    } else if (pw <= 25) {
        watchdog.pulse_val = 1;
    } else if (pw <= 125) {
        watchdog.pulse_val = 2;
    } else if (pw <= 5000) {
        watchdog.pulse_val = 3;
    } else {
        printk(KERN_ERR DRVNAME ": pulse width out of range\n");
        err = -EINVAL;
        goto exit_unlock;
    }
    watchdog.pulse_mode = pw;

exit_unlock:
    mutex_unlock(&watchdog.lock);

    printk(KERN_INFO DRVNAME ": watchdog_set_pulse_width:'%d' - (%d)...\n", pw, 
           err);
    return err;
}

/* Driver useful functions */
static int watchdog_keepalive(void)
{
    int err = 0;
    mutex_lock(&watchdog.lock);

    err = superio_enter(watchdog.sioaddr);
    if (err) goto exit_unlock;

    superio_select(watchdog.sioaddr, SIO_F81865_LD_WDT);

    /* Set timer value */
    superio_outb(watchdog.sioaddr, F81865_REG_WD_TIME, watchdog.timer_val);

    superio_exit(watchdog.sioaddr);

exit_unlock:
    mutex_unlock(&watchdog.lock);

    return err;
}

static int watchdog_start(void)
{
    int err = 0;

    mutex_lock(&watchdog.lock);
    err = superio_enter(watchdog.sioaddr);
    if (err) goto exit_unlock;

    /* @WARNING@ I'm sure that is the right LND... NEED TO CONFIRM...*/
    // SIO_F81865_LD_GPIO -> 0x06
    //superio_select(watchdog.sioaddr, SIO_F81865_LD_WDT);

    /* Watchdog output pin configuration */
    switch (watchdog.type) {
        case f81865:
            /* Set pin 70 the function of WDTRST#/GPIO15 is WDTRST# */
            //superio_clear_bit(watchdog.sioaddr, 0x2b, 5);
            printk(KERN_ERR DRVNAME ": Set pin 70 the function of WDTRST#/GPIO15 "
                   "is WDTRST#\n");
            break;
        default:
            printk(KERN_ERR DRVNAME ": Unable to configure WDTRST pin...\n");
            err = -ENODEV;
            goto exit_superio;
    }

    superio_select(watchdog.sioaddr, SIO_F81865_LD_WDT);
    superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0);

    /* Enable WD time out output via WDTRST# */
    superio_set_bit(watchdog.sioaddr, F81865_REG_WD_PME, 0);

    /* Set Pulse mode ...*/
    if (watchdog.pulse_mode) {
        /* Select "pulse" output mode with given duration */
        u8 wdt_conf = superio_inb(watchdog.sioaddr, F81865_REG_WDT_CONF);

        /* Set WD_PSWIDTH bits (1:0) */
        wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03);
        /* Set WD_PULSE to "pulse" mode */
        wdt_conf |= BIT(F81865_FLAG_WD_PULSE);

        superio_outb(watchdog.sioaddr, F81865_REG_WDT_CONF, wdt_conf);
    } else {
        /* Select "level" output mode */
        superio_clear_bit(watchdog.sioaddr, F81865_REG_WDT_CONF, 
                          F81865_FLAG_WD_PULSE);
    }

    /* Set timer value */
    superio_outb(watchdog.sioaddr, F81865_REG_WD_TIME, watchdog.timer_val);

    /* Enable WD */
    superio_set_bit(watchdog.sioaddr, F81865_REG_WDT_CONF, F81865_FLAG_WD_EN);

exit_superio:
    superio_exit(watchdog.sioaddr);
exit_unlock:
    mutex_unlock(&watchdog.lock);

    printk(KERN_INFO DRVNAME ": watchdog_start(%d)...\n", err);
    return err;
}

static int watchdog_stop(void)
{
    int err = 0;
    mutex_lock(&watchdog.lock);

    err = superio_enter(watchdog.sioaddr);
    if (err) goto exit_unlock;
    
    superio_select(watchdog.sioaddr, SIO_F81865_LD_WDT);
    superio_clear_bit(watchdog.sioaddr, F81865_REG_WDT_CONF, F81865_FLAG_WD_EN);
    superio_exit(watchdog.sioaddr);

exit_unlock:
    mutex_unlock(&watchdog.lock);

    return err;
}

static int watchdog_get_status(void)
{
    int status = 0;

    mutex_lock(&watchdog.lock);
    status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0;
    mutex_unlock(&watchdog.lock);

    printk(KERN_INFO DRVNAME ": watchdog_get_status(%d)...\n", status);
    return status;
}

static bool watchdog_is_running(void)
{
    bool is_running = true;
    mutex_lock(&watchdog.lock);

    if (superio_enter(watchdog.sioaddr)) goto exit_unlock;
    superio_select(watchdog.sioaddr, SIO_F81865_LD_WDT);

    is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0)) & 
        ((superio_inb(watchdog.sioaddr, F81865_REG_WDT_CONF) & 
         BIT(F81865_FLAG_WD_EN)) != 0) ? 1 : 0;

    superio_exit(watchdog.sioaddr);

exit_unlock:
    mutex_unlock(&watchdog.lock);

    printk(KERN_INFO DRVNAME ": watchdog_is_running(%s)...\n", 
          (is_running ? "Yes" : "No"));
    return is_running;
}

/* /dev/watchdog api */
static int watchdog_open(struct inode *inode, struct file *file)
{
    int err;

    /* If the watchdog is alive we don't need to start it again */
    if (test_and_set_bit(0, &watchdog.opened)) return -EBUSY;

    err = watchdog_start();
    if (err) {
        clear_bit(0, &watchdog.opened);
        printk(KERN_ERR DRVNAME ": fail to open watchdog...\n");
        return err;
    }

    if (nowayout) __module_get(THIS_MODULE);

    watchdog.expect_close = 0;
    printk(KERN_INFO DRVNAME ": watchdog open...\n");
    return nonseekable_open(inode, file);
}

static int watchdog_release(struct inode *inode, struct file *file)
{
    clear_bit(0, &watchdog.opened);

    if (!watchdog.expect_close) {
        watchdog_keepalive();
        printk(KERN_CRIT DRVNAME
                ": Unexpected close, not stopping watchdog!\n");
    } else if (!nowayout) {
        watchdog_stop();
    }

    printk(KERN_INFO DRVNAME ": watchdog release...\n");
    return 0;
}

static ssize_t watchdog_write(struct file *file, const char __user *buf, 
       size_t count, loff_t *ppos)
{
    if (count) {
        if(!nowayout) {
            size_t i;
            /* In case it was set long ago */
            bool expect_close = false;
            for (i = 0; i != count; i++) {
                char c;
                if (get_user(c, buf + i)) return -EFAULT;
                expect_close = (c == 'V');
            }

            /* Properly order writes across fork()ed processes */
            mutex_lock(&watchdog.lock);
            watchdog.expect_close = expect_close;
            mutex_unlock(&watchdog.lock);
        }

        /* someone wrote to us, we should restart timer */
        watchdog_keepalive();
    }
    return count;
}

static long watchdog_ioctl(struct file *file, unsigned int cmd, 
       unsigned long arg)
{
    int status;
    int new_options;
    int new_timeout;

    union {
        struct watchdog_info __user *ident;
        int __user *i;
    } uarg;

    uarg.i = (int __user *) arg;
    switch (cmd) {
        case WDIOC_GETSUPPORT:
            return copy_to_user(uarg.ident, &watchdog.ident, 
                   sizeof(watchdog.ident)) ? -EFAULT : 0;

        case WDIOC_GETSTATUS:
                status = watchdog_get_status();
                if (status < 0) return status;
            return put_user(status, uarg.i);

        case WDIOC_GETBOOTSTATUS:
            return put_user(0, uarg.i);

        case WDIOC_SETOPTIONS:
            if (get_user(new_options, uarg.i)) return -EFAULT;
            if (new_options & WDIOS_DISABLECARD) watchdog_stop();
            if (new_options & WDIOS_ENABLECARD) return watchdog_start();

        case WDIOC_KEEPALIVE:
            watchdog_is_running();
            watchdog_keepalive(); return 0;

        case WDIOC_SETTIMEOUT:
            if (get_user(new_timeout, uarg.i)) return -EFAULT;
            if (watchdog_set_timeout(new_timeout)) return -EINVAL;
            watchdog_keepalive();
            /* Return the WDIOC_GETTIMEOUT */

        case WDIOC_GETTIMEOUT:
            return put_user(watchdog.timeout, uarg.i);
        default:
            return -ENOTTY;
    }
}

static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, 
       void *unused)
{
    if (code == SYS_DOWN || code == SYS_HALT) watchdog_stop();
    return NOTIFY_DONE;
}

/* /dev/watchdog api available options */
static const struct file_operations watchdog_fops = {
    .owner              = THIS_MODULE,
    .llseek             = no_llseek,
    .open               = watchdog_open,
    .release            = watchdog_release,
    .write              = watchdog_write,
    .unlocked_ioctl     = watchdog_ioctl,
};

static struct miscdevice watchdog_miscdev = {
    .minor	= WATCHDOG_MINOR,
    .name	= "watchdog",
    .fops	= &watchdog_fops,
};

static struct notifier_block watchdog_notifier = {
    .notifier_call = watchdog_notify_sys,
};

/* /dev/watchdog Main functions */
static int __init watchdog_init(int sio_addr)
{
    int wdt_conf, err = 0;

    watchdog.sioaddr = sio_addr;
    watchdog.ident.options = WDIOC_SETTIMEOUT | WDIOF_MAGICCLOSE | 
                             WDIOF_KEEPALIVEPING;

    snprintf(watchdog.ident.identity, sizeof(watchdog.ident.identity), 
    "%s watchdog", f81865_names[watchdog.type]);

    /* start wdog configuration */
    err = superio_enter(watchdog.sioaddr);
    if (err) return err;

    superio_select(watchdog.sioaddr, SIO_F81865_LD_WDT);
    wdt_conf = superio_inb(watchdog.sioaddr, F81865_REG_WDT_CONF);
    watchdog.caused_reboot = wdt_conf & F81865_FLAG_WDTMOUT_STS;
    superio_exit(watchdog.sioaddr);

    err = watchdog_set_timeout(timeout);
    if (err) return err;
    
    err = watchdog_set_pulse_width(pulse_width);
    if (err) return err;

    err = register_reboot_notifier(&watchdog_notifier);
    if (err) return err;

    err = misc_register(&watchdog_miscdev);
    if (err) {
        printk(KERN_ERR DRVNAME ": cannot register miscdev on minor=%d\n",
                watchdog_miscdev.minor);
        goto exit_reboot;
    }

    if (start_withtimeout) {
        if (start_withtimeout <= 0 || start_withtimeout > max_timeout) {
            printk(KERN_ERR DRVNAME ": starting timeout out of range\n");
            err = -EINVAL;
            goto exit_miscdev;
        }

        err = watchdog_start();
        if (err) {
            printk(KERN_ERR DRVNAME ": cannot start watchdog timer\n");
            goto exit_miscdev;
        }

        mutex_lock(&watchdog.lock);
        err = superio_enter(watchdog.sioaddr);
        if (err) goto exit_unlock;

        superio_select(watchdog.sioaddr, SIO_F81865_LD_WDT);

        if (start_withtimeout > 0xff) {
            /* select minutes for timer units */
            superio_set_bit(watchdog.sioaddr, F81865_REG_WDT_CONF, 
                            F81865_FLAG_WD_UNIT);
            superio_outb(watchdog.sioaddr, F81865_REG_WD_TIME, 
                         DIV_ROUND_UP(start_withtimeout, 60));
        } else {
            /* select seconds for timer units */
            superio_clear_bit(watchdog.sioaddr, F81865_REG_WDT_CONF, 
                              F81865_FLAG_WD_UNIT);
            superio_outb(watchdog.sioaddr, F81865_REG_WD_TIME, 
                         start_withtimeout);
        }

        /* set RSOUTPUT polarity to active low */
        superio_clear_bit(watchdog.sioaddr, F81865_REG_WDT_CONF,
                          F81865_FLAG_WD_UACTIVE);

        superio_exit(watchdog.sioaddr);
        mutex_unlock(&watchdog.lock);

        if (nowayout) __module_get(THIS_MODULE);

        printk(KERN_INFO DRVNAME
                ": watchdog started with initial timeout of %u sec\n",
                start_withtimeout);
    }

    printk(KERN_INFO DRVNAME
            ": watchdog started with  timeout of %u sec\n",
            watchdog.timeout);
    return 0;
    
exit_unlock:
	mutex_unlock(&watchdog.lock);
exit_miscdev:
	misc_deregister(&watchdog_miscdev);
exit_reboot:
	unregister_reboot_notifier(&watchdog_notifier);

    return err;
}

static int __init f81865_find(int sio_addr)
{
    u16 devid;
    int err = superio_enter(sio_addr);
    if (err) return err;

    devid = superio_inw(sio_addr, SIO_REG_MANID);
    if (devid != SIO_FINTEK_ID) {
        pr_debug(DRVNAME ": Not a Fintek device\n");
        err = -ENODEV;
        goto exit;
    }

    devid = force_id ? force_id : superio_inw(sio_addr, SIO_REG_DEVID);
    switch (devid) {
        case SIO_F81865_ID:
            watchdog.type = f81865;
            break;
        default:
            printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n", 
                  (unsigned int) devid);
            err = -ENODEV;
            goto exit;
    }

    printk(KERN_INFO DRVNAME ": Found %s watchdog chip, revision %d\n", 
    f81865_names[watchdog.type], (int) superio_inb(sio_addr, SIO_REG_DEVREV));

exit:
    superio_exit(sio_addr);

    return err;
}

static int __init f81865_init(void)
{
    static const unsigned short addrs[] = { 0x2e, 0x4e };
    int err = -ENODEV, i;

    for (i=0; i < ARRAY_SIZE(addrs); i++) {
        err = f81865_find(addrs[i]);
        if (err == 0) break;
    }
    if (i == ARRAY_SIZE(addrs)) return err;

    return watchdog_init(addrs[i]);
}

static void __exit f81865_exit(void)
{
    if (watchdog_is_running()) {
        printk(KERN_WARNING DRVNAME
                ": Watchdog timer still running, stopping it\n");
        watchdog_stop();
    }

    misc_deregister(&watchdog_miscdev);
    unregister_reboot_notifier(&watchdog_notifier);
}

MODULE_AUTHOR("Bruno Ferreira");
MODULE_DESCRIPTION("Hardware Watchdog Device Driver for F81865 chip I/O");
MODULE_LICENSE("GPL");

module_init(f81865_init);
module_exit(f81865_exit);

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux