linux/drivers/watchdog/i6300esb.c
<<
>>
Prefs
   1/*
   2 *      i6300esb:       Watchdog timer driver for Intel 6300ESB chipset
   3 *
   4 *      (c) Copyright 2004 Google Inc.
   5 *      (c) Copyright 2005 David Härdeman <david@2gen.com>
   6 *
   7 *      This program is free software; you can redistribute it and/or
   8 *      modify it under the terms of the GNU General Public License
   9 *      as published by the Free Software Foundation; either version
  10 *      2 of the License, or (at your option) any later version.
  11 *
  12 *      based on i810-tco.c which is in turn based on softdog.c
  13 *
  14 *      The timer is implemented in the following I/O controller hubs:
  15 *      (See the intel documentation on http://developer.intel.com.)
  16 *      6300ESB chip : document number 300641-004
  17 *
  18 *  2004YYZZ Ross Biro
  19 *      Initial version 0.01
  20 *  2004YYZZ Ross Biro
  21 *      Version 0.02
  22 *  20050210 David Härdeman <david@2gen.com>
  23 *      Ported driver to kernel 2.6
  24 */
  25
  26/*
  27 *      Includes, defines, variables, module parameters, ...
  28 */
  29
  30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  31
  32#include <linux/module.h>
  33#include <linux/types.h>
  34#include <linux/kernel.h>
  35#include <linux/fs.h>
  36#include <linux/mm.h>
  37#include <linux/miscdevice.h>
  38#include <linux/watchdog.h>
  39#include <linux/init.h>
  40#include <linux/pci.h>
  41#include <linux/ioport.h>
  42#include <linux/uaccess.h>
  43#include <linux/io.h>
  44
  45/* Module and version information */
  46#define ESB_VERSION "0.05"
  47#define ESB_MODULE_NAME "i6300ESB timer"
  48#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION
  49
  50/* PCI configuration registers */
  51#define ESB_CONFIG_REG  0x60            /* Config register                   */
  52#define ESB_LOCK_REG    0x68            /* WDT lock register                 */
  53
  54/* Memory mapped registers */
  55#define ESB_TIMER1_REG (BASEADDR + 0x00)/* Timer1 value after each reset     */
  56#define ESB_TIMER2_REG (BASEADDR + 0x04)/* Timer2 value after each reset     */
  57#define ESB_GINTSR_REG (BASEADDR + 0x08)/* General Interrupt Status Register */
  58#define ESB_RELOAD_REG (BASEADDR + 0x0c)/* Reload register                   */
  59
  60/* Lock register bits */
  61#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
  62#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
  63#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
  64
  65/* Config register bits */
  66#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
  67#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
  68#define ESB_WDT_INTTYPE (0x03 << 0)   /* Interrupt type on timer1 timeout  */
  69
  70/* Reload register bits */
  71#define ESB_WDT_TIMEOUT (0x01 << 9)    /* Watchdog timed out                */
  72#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
  73
  74/* Magic constants */
  75#define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
  76#define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
  77
  78/* internal variables */
  79static void __iomem *BASEADDR;
  80static DEFINE_SPINLOCK(esb_lock); /* Guards the hardware */
  81static unsigned long timer_alive;
  82static struct pci_dev *esb_pci;
  83static unsigned short triggered; /* The status of the watchdog upon boot */
  84static char esb_expect_close;
  85
  86/* We can only use 1 card due to the /dev/watchdog restriction */
  87static int cards_found;
  88
  89/* module parameters */
  90/* 30 sec default heartbeat (1 < heartbeat < 2*1023) */
  91#define WATCHDOG_HEARTBEAT 30
  92static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
  93module_param(heartbeat, int, 0);
  94MODULE_PARM_DESC(heartbeat,
  95                "Watchdog heartbeat in seconds. (1<heartbeat<2046, default="
  96                                __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
  97
  98static bool nowayout = WATCHDOG_NOWAYOUT;
  99module_param(nowayout, bool, 0);
 100MODULE_PARM_DESC(nowayout,
 101                "Watchdog cannot be stopped once started (default="
 102                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 103
 104/*
 105 * Some i6300ESB specific functions
 106 */
 107
 108/*
 109 * Prepare for reloading the timer by unlocking the proper registers.
 110 * This is performed by first writing 0x80 followed by 0x86 to the
 111 * reload register. After this the appropriate registers can be written
 112 * to once before they need to be unlocked again.
 113 */
 114static inline void esb_unlock_registers(void)
 115{
 116        writew(ESB_UNLOCK1, ESB_RELOAD_REG);
 117        writew(ESB_UNLOCK2, ESB_RELOAD_REG);
 118}
 119
 120static int esb_timer_start(void)
 121{
 122        u8 val;
 123
 124        spin_lock(&esb_lock);
 125        esb_unlock_registers();
 126        writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
 127        /* Enable or Enable + Lock? */
 128        val = ESB_WDT_ENABLE | (nowayout ? ESB_WDT_LOCK : 0x00);
 129        pci_write_config_byte(esb_pci, ESB_LOCK_REG, val);
 130        spin_unlock(&esb_lock);
 131        return 0;
 132}
 133
 134static int esb_timer_stop(void)
 135{
 136        u8 val;
 137
 138        spin_lock(&esb_lock);
 139        /* First, reset timers as suggested by the docs */
 140        esb_unlock_registers();
 141        writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
 142        /* Then disable the WDT */
 143        pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x0);
 144        pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val);
 145        spin_unlock(&esb_lock);
 146
 147        /* Returns 0 if the timer was disabled, non-zero otherwise */
 148        return val & ESB_WDT_ENABLE;
 149}
 150
 151static void esb_timer_keepalive(void)
 152{
 153        spin_lock(&esb_lock);
 154        esb_unlock_registers();
 155        writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
 156        /* FIXME: Do we need to flush anything here? */
 157        spin_unlock(&esb_lock);
 158}
 159
 160static int esb_timer_set_heartbeat(int time)
 161{
 162        u32 val;
 163
 164        if (time < 0x1 || time > (2 * 0x03ff))
 165                return -EINVAL;
 166
 167        spin_lock(&esb_lock);
 168
 169        /* We shift by 9, so if we are passed a value of 1 sec,
 170         * val will be 1 << 9 = 512, then write that to two
 171         * timers => 2 * 512 = 1024 (which is decremented at 1KHz)
 172         */
 173        val = time << 9;
 174
 175        /* Write timer 1 */
 176        esb_unlock_registers();
 177        writel(val, ESB_TIMER1_REG);
 178
 179        /* Write timer 2 */
 180        esb_unlock_registers();
 181        writel(val, ESB_TIMER2_REG);
 182
 183        /* Reload */
 184        esb_unlock_registers();
 185        writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
 186
 187        /* FIXME: Do we need to flush everything out? */
 188
 189        /* Done */
 190        heartbeat = time;
 191        spin_unlock(&esb_lock);
 192        return 0;
 193}
 194
 195/*
 196 *      /dev/watchdog handling
 197 */
 198
 199static int esb_open(struct inode *inode, struct file *file)
 200{
 201        /* /dev/watchdog can only be opened once */
 202        if (test_and_set_bit(0, &timer_alive))
 203                return -EBUSY;
 204
 205        /* Reload and activate timer */
 206        esb_timer_start();
 207
 208        return nonseekable_open(inode, file);
 209}
 210
 211static int esb_release(struct inode *inode, struct file *file)
 212{
 213        /* Shut off the timer. */
 214        if (esb_expect_close == 42)
 215                esb_timer_stop();
 216        else {
 217                pr_crit("Unexpected close, not stopping watchdog!\n");
 218                esb_timer_keepalive();
 219        }
 220        clear_bit(0, &timer_alive);
 221        esb_expect_close = 0;
 222        return 0;
 223}
 224
 225static ssize_t esb_write(struct file *file, const char __user *data,
 226                          size_t len, loff_t *ppos)
 227{
 228        /* See if we got the magic character 'V' and reload the timer */
 229        if (len) {
 230                if (!nowayout) {
 231                        size_t i;
 232
 233                        /* note: just in case someone wrote the magic character
 234                         * five months ago... */
 235                        esb_expect_close = 0;
 236
 237                        /* scan to see whether or not we got the
 238                         * magic character */
 239                        for (i = 0; i != len; i++) {
 240                                char c;
 241                                if (get_user(c, data + i))
 242                                        return -EFAULT;
 243                                if (c == 'V')
 244                                        esb_expect_close = 42;
 245                        }
 246                }
 247
 248                /* someone wrote to us, we should reload the timer */
 249                esb_timer_keepalive();
 250        }
 251        return len;
 252}
 253
 254static long esb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 255{
 256        int new_options, retval = -EINVAL;
 257        int new_heartbeat;
 258        void __user *argp = (void __user *)arg;
 259        int __user *p = argp;
 260        static const struct watchdog_info ident = {
 261                .options =              WDIOF_SETTIMEOUT |
 262                                        WDIOF_KEEPALIVEPING |
 263                                        WDIOF_MAGICCLOSE,
 264                .firmware_version =     0,
 265                .identity =             ESB_MODULE_NAME,
 266        };
 267
 268        switch (cmd) {
 269        case WDIOC_GETSUPPORT:
 270                return copy_to_user(argp, &ident,
 271                                        sizeof(ident)) ? -EFAULT : 0;
 272
 273        case WDIOC_GETSTATUS:
 274                return put_user(0, p);
 275
 276        case WDIOC_GETBOOTSTATUS:
 277                return put_user(triggered, p);
 278
 279        case WDIOC_SETOPTIONS:
 280        {
 281                if (get_user(new_options, p))
 282                        return -EFAULT;
 283
 284                if (new_options & WDIOS_DISABLECARD) {
 285                        esb_timer_stop();
 286                        retval = 0;
 287                }
 288
 289                if (new_options & WDIOS_ENABLECARD) {
 290                        esb_timer_start();
);
 192        return20;
 193}
 194<2a>
/*
 249                
 */
 198<2a>
Cptions =         case file)
 200{
 281             7" id="L257" class="line" name="L257"> 25="sref">get_user(new_options, 3dog can o3ly be opened once */
 282                        return -timer_alive 160static int              7" id="L257" class="line" name="L257"> 25w_options" class="sref">new_options, 3d4ive" cl3sine" name="L193"> 193;
 165                return - 204<3a>
 249                
/* someone wrote to us3" class="3ref">esb_timer_start3);
 207<3a>
CpGions =         case  198<3);
 277            sb.c#L190" id="L190" class="line" namlass="sref">put_user(triggered, 39" class=3line" name="L209"> 209}
 210<3a>
 165                return -file)
 212{
/* S3ut off the timer. */
 == 32)
 3p" class=3sref">esb_timer_stop3);
 3p class="3216"> 216        els3 {
 13t stoppin3 watchdog!\n"esb_timer_keepalive3);
fop" class="line" name=fop"info" class="sref">watchdog_info  219      3 }
identass="line" nameTHIS">identde=argp" class="sref">argp, &timer_aliveargp, &esb_expect_close =30;
static static argp, & 222        return30;
argp, &/* S3line" name="L223"> 223}
    ="+code=__user" cle5argp, & 224<3a>
argp, &data,
ppos)
 227{
miscdev"L211" class="line"miscdevinfo" class="sref">watchdog_info 
argp, &len3 {
 217               rgp,rit(argp, &nowayout3 {
 191        argp, &i;
 232<3a>
 3       * 3ive months ago... */
 3 " class=3">esb_expect_close =30;
 1336" class3"line" name="L236"> 236<3a>
watchdog_info put_uPCI_DEVICE_ID_INTEc_ref"9="L254" class="PCI_DEVICE_ID_INTEc_ref"9ions<, }rgp" class="sref">argp, &  li630sb.c#L197" id="L197" class="line" name="L197"> 13ref="+cod3=i" class="sref">i++3 {
c;
put_u>identiDEVICE_Tturn pci_read_confe class_tbd="L254" class="lineass_tbdionsiEFAULT;
 3an class=3string">'V')
      exit>rout;V&sb.c#L195" id="L195" class="line" name="L195"> 3ass="sref3>esb_expect_close = 32;
 13r" class=3a>                      3 }
 246              3 }
 259new_options, 347" class3"line" name="L247"> 247<3a>
watchdog_info  208devic="L211" class="lass_en> 208devic= 277            idev"L211" class="ladevef="_ & esb_timer_keepalive3);
            ir_erlass="line" name=r_erl 277 217               fails="linen> 20 devic=_crit("Unexpected close, n3="line" n3me="L250"> 250      3 }
 165                return -len;
 252}
 253<3a>
            idev"L211" class="ladevef=", /a>               href="+code=identity" class="sref">identity =_ & arg)
 217               fails="linrequme=  184as_crit("Unexpected close, n3=" class=3line" name="L255"> 255{
 205"> 165                return -EINVAL;
new_heartbeat;
arg;
            idev"L211" class="ladevef=", / class="string">"Unexpected close, n3f="+code=3rgp" class="sref">argp;
 & ident 3 {
      ss="lng here, BASEADDR has"linbe se30sb.c#L197" id="L197" class="line" name="L197"> 13" class="3ref">WDIOF_SETTIMEOUT 217               fails="linget BASEADDR_crit("Unexpected close, n3lass="sre3">WDIOF_KEEPALIVEPING 165                return -WDIOF_MAGICCLOSE,
f3rmware_version =    30,
ESB_MODULE_NAME,
 189        <3"line" na3e="L266"> 266       3};
                return - 267<3a>
                return -cmd3 {
                return -WDIOC_GETSUPPORT:
       case ident,
"Unexpected close, n3=EFAULT" 3lass="sref">EFAULT :30;
 205">       case  272<3a>
 208devic="L211" class="lass_dis> 208devic= 277            idev"L211" class="ladevef="_class="string">"Unexpected close, n3=" class=3"sref">WDIOC_GETSTATUS:
       case f3ode=p" class="sref">p 275<3a>
WDIOC_GETBOOTSTATUS:
p_line"nitdevic="L211" class="lline"nitdevic= 277new_options, 378" class3"line" name="L278"> 278<3a>
 & WDIOC_SETOPTIONS:
                return - 280      3 {
esb_tass="luhref="dic const structval2"L211" class="lval2ef="165                return -pEFAULT;
 189        <383" class3"line" name="L283"> 283<3a>
:89" class="line" name="L189"> 189        <38"sref">f3f">WDIOS_DISABLECARD3 {
 = " class="line" name="L189"> 189        <385" class3sref">esb_timer_stop3);
 189        <38lass="sr3lass="sref">retval =30;
 189        <38 href="+3"> 287              3 }
 20d.ss" class="line" name="L189"> 189        <388" class3"line" name="L288"> 288<3a>
 189 has"lwo      s, it#"linbe se3up sin  a                    /* scan t3 class="s3ef">WDIOS_ENABLECARD3 {
       1  1sults in verwaterrupt         expirya>                /* scan t3 "line" n3ref">esb_timer_start3);
/* scan t3 EFAULT" 31" id="L    ="+code=l/* scan t3 ode=EFAU3"> 192        return30;
 189        <303" class3line" name="L193"> 193}
 189        <3nlass="sr3"line" name="L194"> 194<3a>
"Unexpected close, n395/*
 189        <3n href="+3 class="comment"> */
 191        val1"L211" class="lval1ef="_class="string">"Unexpected close, n398" class3"line" name="L198"> 198<3a>
                if (            hreftew"LOCKef="_options" class="sref">new_options, 4f="+code=4ile" class="sref">file)
            ir_warss="line" name="ar_wars 277 217               me="L230  lready se3_crit("Unexpected close, n40" class=4line" name="L200"> 200{
"Unexpected close, n402 class=4l" id="L    ="+code=l
 189 m        dis> 200it#f 237 189        <4alive" cl4ss="sref">timer_alive            e class="L254" class="lineassL211a>               hrefLOCK_RE            hrefLOCK_RE/x00 class="string">"Unexpected close, n404ive" cl4sine" name="L193"> 193;
 204<4a>
 189 was"previously"l=put_use3*189" class="line" name="L189"> 189        <4a6" class4" class="comment">/*
            e cle" nam_ 184" class="line" name=e" nam_ 184" 277< class="string">"Unexpected close, n407" class4"v/watchdog handling"Unexpected close, n407" class4"line" name="L207"> 207<4a>
new_options, 4f9" class4"line" name="L198"> 198<4);
op_optRESEtef="165                return - 209}
                return - 210<4a>
 189        <4f="+code=4ile" class="sref">file)
            e cle" nam_ 184" class="line" name=e" nam_ 184" 277< class="string">"Unexpected close, n42" class=4line" name="L212"> 212{
"Unexpected close, n4ent">/* S4ut off the timer. */
 == 42)
 189        <4p" class=4sref">esb_timer_stop4);
            e cl60" id="L160" class="line" name="L160"> 160static int          sb.c#L190" id="L190" class="line" nam_class="string">"Unexpected close, n4e7" class4216"> 216        els4 {
esb_timer_keepalive4);
 259argp, & 219      4 }
 259 271             gp" class="sref">argp, &timer_aliveesb_expect_close =40;
 271     re1ef="165                return - 222        return40;
/* S4line" name="L223"> 223}
 224<4a>
argp, &data,
 2prline" 277 217               Intel amp;hre Wgp(argp, &ppos)
            hrefVERSION nam_class="string">"Unexpected close, n47" class=4line" name="L227"> 227{

len4 {
            ir_erlass="line" name=r_erl 277 217               This 5" cla/only supports 1 devic=_crit("Unexpected close, n4flive" cl4lass="sref">nowayout4 {
                return -i;
 232<4a>
 237 2l89"> 189 is"lhere L189" class="line" name="L189"> 189        <4       * 4ive months ago... */
 189        <4 " class=4">esb_expect_close =40;
                return - 236<4a>
 189        <4 ss="sref4* magic character */
 189        <4ref="+cod4=i" class="sref">i++4 {
/x03ff /a> & c;
                return -i 2prline" 277 217               ss="line" valu="mu630be 1<ss="line"<2046, us    %d_crit(argp, &EFAULT;
         sb.c#L190" id="L190" class="line" nam_class="string">"Unexpected close, n4an class=4string">'V')
                      4 }
 189     make sur00it#doesn7 189        <4r7" class4"> 246              4 }
"Unexpected close, n4asee whet4"line" name="L247"> 247<4a>
3" cl89"> 189 sin  a  77">atcce has"access"linit L189" class="line" name="L189"> 189        <4ass="sref4>esb_timer_keepalive4);
 271     re1ef=" "+code=__user" clmisc_ 184"> 271     misc_ 184 277<91"> 191        "Unexpected close, n4="line" n4me="L250"> 250      4 }
 271     re1ef=" != 0 /a> & len;
 217               can73miscdev/on minol=%d (erl=%d)_crit(argp, & 252}
 271     re1ef="_class="string">"Unexpected close, n4=n class=4"line" name="L253"> 253<4a>
rr8unmaief="165                return -arg)
 255{
            ir_ine" name="L260"> 2prline" 277 217               initialized (0x%p).+ss="line"=%d sec (me="L230=%d)_crit(argp, &EINVAL;
            BASEADDRions" class="srBASEADDRL211r/watchdog/i6300esb.c#L190" id="L190" class="line" namlass="sref">put_ume="L230" id="L190" clame="L230ef="_class="string">"Unexpected close, n4=see whet4s="sref">new_heartbeat;
arg;
                return -argp;
rr8unmaief="      case ident 4 {
            BASEADDRions" class="srBASEADDRL211_class="string">"Unexpected close, n4" class="4ref">WDIOF_SETTIMEOUT"Unexpected close, n4lass="sre4">WDIOF_KEEPALIVEPING"Unexpected close, n4ln class=4sref">WDIOF_MAGICCLOSE,
"Unexpected close, n4lss="sref4rmware_version =    40,
 271     re1ef="165                return -ESB_MODULE_NAME,
 266       4};
 267<4a>
_lineremos="line" name="L249">remos=  259 189        <4Eef="+cod4md" class="sref">cmd4 {
 & WDIOC_GETSUPPORT:
 189        <4"+code=id4nt" class="sref">ident,
 189        <4" class="4lass="sref">EFAULT :40;
 160 285                         272<4a>
WDIOC_GETSTATUS:
3L189" class="line" name="L189"> 189        <4"ss="sref4ode=p" class="sref">p 271     misc_de 184 277<91"> 191        "Unexpected close, n475" class4"line" name="L275"> 275<4a>
            iounmaine" name="L285"iounmai 277            BASEADDRions" class="srBASEADDRL211_class="string">"Unexpected close, n4class="sr4f">WDIOC_GETBOOTSTATUS:
"Unexpected close, n4a href="+4ode=p" class="sref">p"Unexpected close, n478" class4"line" name="L278"> 278<4a>
"Unexpected close, n4S" class=4sref">WDIOC_SETOPTIONS:
 280      4 {
"Unexpected close, n4=EFAULT" 4ode=p" class="sref">p_lineshutdow id="L199" class="lishutdow   259 189        <4code=EFAU4T" class="sref">EFAULT;
 &  283<4a>
 160 285                        f4f">WDIOS_DISABLECARD4 {
esb_timer_stop4);
retval =40;
 &  287              4 }
identity =rgp" class="sref">argp, & 288<4a>
 20class="line" naid_t> 20ef="drivers"+code=__user" cle5argp, &WDIOS_ENABLECARD4 {
argp, &esb_timer_start4);
remos= argp, &argp, & 192        return40;
 193}
 194<4a>
/*
            >identiAUTHORions" class="sr>identiAUTHOR 277 217               Ross Biro     Daver"Härdemacrit("Unexpected close, n4n href="+4 class="comment"> */
            >identiDESCRIPTION/a>            >identiDESCRIPTION 277 217               W9"> 189 5" cla/f 23Intel amp;hre chipsetsrit("Unexpected close, n4n8" class4"line" name="L198"> 198<4a>
            >identiLICENSentity" class="s>identiLICENSe 277 217               GPLrit("Unexpected close, n5f="+code=5ile" class="sref">file)
"Unexpected close, n5f1"+code=5ief">esb_timer_start5>{



esb_tifoo/a>"> T original LXR soft"> 2lby0 ass="strihttp://sourcefo ge.net/projects/lx>">LXR 0esbunityL211a>this experib.c#al >&quionlby0ass="strimailto:lx>@merux.no">lx>@merux.noL211.
esb_tisubfoo/a>"> lx>.merux.no kindly hosts="by ass="strihttp://www.redpill-merpro.no">Redpill Lerpro ASL211a>providsra> Leruxgg/i6ult