All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: Fintek F81865 chip (Wdog support)
       [not found] <51409586.4030204@efacec.com>
@ 2013-03-26 17:26 ` Giel van Schijndel
  2013-05-26 16:44   ` Wim Van Sebroeck
  0 siblings, 1 reply; 5+ messages in thread
From: Giel van Schijndel @ 2013-03-26 17:26 UTC (permalink / raw)
  To: BrunoFerreira; +Cc: Wim Van Sebroeck, linux-watchdog

 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.

-- 
 Giel

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Fintek F81865 chip (Wdog support)
  2013-03-26 17:26 ` Fintek F81865 chip (Wdog support) Giel van Schijndel
@ 2013-05-26 16:44   ` Wim Van Sebroeck
  2013-08-02 14:17     ` BrunoFerreira
  0 siblings, 1 reply; 5+ messages in thread
From: Wim Van Sebroeck @ 2013-05-26 16:44 UTC (permalink / raw)
  To: Giel van Schijndel; +Cc: BrunoFerreira, linux-watchdog

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.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Fintek F81865 chip (Wdog support)
  2013-05-26 16:44   ` Wim Van Sebroeck
@ 2013-08-02 14:17     ` BrunoFerreira
  2013-08-02 14:48       ` Bruno André Ferreira
  0 siblings, 1 reply; 5+ messages in thread
From: BrunoFerreira @ 2013-08-02 14:17 UTC (permalink / raw)
  To: Wim Van Sebroeck; +Cc: Giel van Schijndel, linux-watchdog

[-- Attachment #1: Type: text/plain, Size: 1980 bytes --]

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.
>
>


[-- Attachment #2: f81865_gpio.c --]
[-- Type: text/x-csrc, Size: 7951 bytes --]

/***************************************************************************
* Copyright (C) 2013 Bruno Ferreira <bruno.ferreira@efacec.com>            *
*                                                                          *
* 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.                 *
***************************************************************************/

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/ioport.h>

#include <linux/gpio.h>

#define DRVNAME                 "f81865_gpio"

/* 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_DEVID		0x20	/* Device ID (2 bytes) */
#define SIO_REG_DEVREV		0x22	/* Device revision */
#define SIO_REG_MANID		0x23	/* Fintek ID (2 bytes) */

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

static DEFINE_SPINLOCK(gpio_lock);

/* GPIO internal data information */
struct gpio_data {
    unsigned short      sioaddr;        /* default index port */
    unsigned long       opened;         /* driver open state */
};

static struct gpio_data gpio = {};

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

static inline void superio_set_bit(int base, int reg, int bit);
static inline void superio_clear_bit(int base, int reg, int bit);


/* 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 int superio_inb(int base, int reg)
{
    outb(reg, base);
    return inb(base + 1);
}

static inline void superio_outb(int base, int reg, u8 val)
{
    outb(reg, base);
    outb(val, 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_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);
}

/* GPIO api */
static int f81865_gpio_direction_in(struct gpio_chip *gc, unsigned _gpio_num)
{
    return 0;
}

static int f81865_gpio_direction_out(struct gpio_chip *gc, unsigned _gpio_num, int val)
{
    return 0;
}

static int f81865_gpio_get(struct gpio_chip *gc, unsigned _gpio_num)
{
    return 0;
}

static void f81865_gpio_set(struct gpio_chip *gc, unsigned _gpio_num, int val)
{

}

static struct gpio_chip f81865_gpio_chip =
{
    .label              = DRVNAME,
    .owner              = THIS_MODULE,
    .get                = f81865_gpio_get,
    .direction_input    = f81865_gpio_direction_in,
    .set                = f81865_gpio_set,
    .direction_output   = f81865_gpio_direction_out,
};

/* Driver useful functions */
static int __init f81865_configure(void)
{
    int err = 0;
    /* temporary */
    //u8 io_reg, curr_vals;
    
    /* Enable all pins with GPIO capability */
    /* By default we enable all possible GPIOs on the chip */
    spin_lock(&gpio_lock);
    err = superio_enter(gpio.sioaddr);
    if (err) goto exit_unlock;
    
    /* Enable GPIO... */

    //io_reg = 0x27; /* Enable Rom Address Register */
    //curr_vals = superio_inb(gpio.sioaddr, io_reg);
    //printk(KERN_INFO DRVNAME ": Rom Address Register: (%d) \n", curr_vals);
    //superio_set_bit(gpio.sioaddr, io_reg, 4); /* PORT_4E_EN to 1*/

    /* Enable GPIO[10-17], GPIO1 | WARNING the GPIO15 could be the WDTRST# */
    /* GPIO1 Enable Register - Index 2Bh */
    superio_outb(gpio.sioaddr, 0x2b, 0xff);

    /* Enable GPIO[20-27], GPIO1 */
    /* GPIO2 Enable Register - Index 2Ch */
    superio_outb(gpio.sioaddr, 0x2c, 0xff);

    /* Enable GPIO[30-37], GPIO1 */
    /* GPIO3 Enable Register - Index 29h */
    superio_outb(gpio.sioaddr, 0x29, 0xff);

    /* Enable GPIO[40-47], GPIO1 */
    /* GPIO4 Enable Register - Index 28h */
    superio_outb(gpio.sioaddr, 0x28, 0xff);

    superio_exit(gpio.sioaddr);
    
exit_unlock:
    spin_unlock(&gpio_lock);

    printk(KERN_INFO DRVNAME ": configured(%d)...\n", err);
    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 = superio_inw(sio_addr, SIO_REG_DEVID);
    if (devid != SIO_F81865_ID) {
        printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n", (unsigned int) devid);
        err = -ENODEV;
        goto exit;
    }

    printk(KERN_INFO DRVNAME ": Found F81865 Super I/O chip, revision %d\n", (int) superio_inb(sio_addr, SIO_REG_DEVREV));

exit:
    superio_exit(sio_addr);

    return err;
}

static int __init f81865_gpio_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;

    /* set GPIO base address */
    gpio.sioaddr = addrs[i];

    /* configure GPIO pins capability */
    err = f81865_configure();
    if (err)
    {
        printk(KERN_ERR DRVNAME ": configuring the GPIO...\n");
        return EAGAIN;
    }

    f81865_gpio_chip.base = -1;
    f81865_gpio_chip.ngpio = 26; /* !!! FIX THIS !!! */

    err = gpiochip_add(&f81865_gpio_chip);
    
    return (err < 0) ? err : 0;
}

static void __exit f81865_gpio_exit(void)
{
    gpiochip_remove(&f81865_gpio_chip);
    printk(KERN_INFO DRVNAME ": unloaded...\n");   
}

MODULE_AUTHOR("Bruno Ferreira");
MODULE_DESCRIPTION("GPIO interface for F81865 Super I/O chip");
MODULE_LICENSE("GPL");

module_init(f81865_gpio_init);
module_exit(f81865_gpio_exit);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: Fintek F81865 chip (Wdog support)
  2013-08-02 14:17     ` BrunoFerreira
@ 2013-08-02 14:48       ` Bruno André Ferreira
  2013-08-02 21:01         ` Guenter Roeck
  0 siblings, 1 reply; 5+ messages in thread
From: Bruno André Ferreira @ 2013-08-02 14:48 UTC (permalink / raw)
  To: Wim Van Sebroeck; +Cc: Giel van Schijndel, linux-watchdog

[-- Attachment #1: Type: text/plain, Size: 2348 bytes --]

Sorry.

Wrong driver...

Best regards,
Bruno Ferreira
________________________________________
From: BrunoFerreira [bruno.ferreira@efacec.com]
Sent: Friday, August 02, 2013 3:17 PM
To: Wim Van Sebroeck
Cc: Giel van Schijndel; linux-watchdog@vger.kernel.org
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.
>
>


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: f81865_wdt.c --]
[-- Type: text/x-csrc; name="f81865_wdt.c", Size: 21773 bytes --]

/***************************************************************************
* Copyright (C) 2013 Bruno Ferreira <bruno.ferreira@efacec.com>            *
*                                                                          *
* 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);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Fintek F81865 chip (Wdog support)
  2013-08-02 14:48       ` Bruno André Ferreira
@ 2013-08-02 21:01         ` Guenter Roeck
  0 siblings, 0 replies; 5+ messages in thread
From: Guenter Roeck @ 2013-08-02 21:01 UTC (permalink / raw)
  To: Bruno André Ferreira
  Cc: Wim Van Sebroeck, Giel van Schijndel, linux-watchdog

On 08/02/2013 07:48 AM, Bruno André Ferreira wrote:
> Sorry.
>
> Wrong driver...
>
> Best regards,
> Bruno Ferreira
> ________________________________________
> From: BrunoFerreira [bruno.ferreira@efacec.com]
> Sent: Friday, August 02, 2013 3:17 PM
> To: Wim Van Sebroeck
> Cc: Giel van Schijndel; linux-watchdog@vger.kernel.org
> 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.
>
Hi Bruno,

Just wondering ... why didn't you just add the new chip type to the existing f71808e driver ?

I don't see a significant enough difference to warrant a new driver. Your driver doesn't
support minutes mode, but I am quite sure that is not a chip limitation. Other than that,
everything seems to be identical.

Thanks,
Guenter


--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-08-02 21:01 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <51409586.4030204@efacec.com>
2013-03-26 17:26 ` Fintek F81865 chip (Wdog support) Giel van Schijndel
2013-05-26 16:44   ` Wim Van Sebroeck
2013-08-02 14:17     ` BrunoFerreira
2013-08-02 14:48       ` Bruno André Ferreira
2013-08-02 21:01         ` Guenter Roeck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.