linux/drivers/watchdog/sch311x_wdt.c
<<
>>
Prefs
   1/*
   2 *      sch311x_wdt.c - Driver for the SCH311x Super-I/O chips
   3 *                      integrated watchdog.
   4 *
   5 *      (c) Copyright 2008 Wim Van Sebroeck <wim@iguana.be>.
   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 *      Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
  13 *      provide warranty for any of this software. This material is
  14 *      provided "AS-IS" and at no charge.
  15 */
  16
  17/*
  18 *      Includes, defines, variables, module parameters, ...
  19 */
  20
  21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  22
  23/* Includes */
  24#include <linux/module.h>               /* For module specific items */
  25#include <linux/moduleparam.h>          /* For new moduleparam's */
  26#include <linux/types.h>                /* For standard types (like size_t) */
  27#include <linux/errno.h>                /* For the -ENODEV/... values */
  28#include <linux/kernel.h>               /* For printk/... */
  29#include <linux/miscdevice.h>           /* For MODULE_ALIAS_MISCDEV
  30                                                        (WATCHDOG_MINOR) */
  31#include <linux/watchdog.h>             /* For the watchdog specific items */
  32#include <linux/init.h>                 /* For __init/__exit/... */
  33#include <linux/fs.h>                   /* For file operations */
  34#include <linux/platform_device.h>      /* For platform_driver framework */
  35#include <linux/ioport.h>               /* For io-port access */
  36#include <linux/spinlock.h>             /* For spin_lock/spin_unlock/... */
  37#include <linux/uaccess.h>              /* For copy_to_user/put_user/... */
  38#include <linux/io.h>                   /* For inb/outb/... */
  39
  40/* Module and version information */
  41#define DRV_NAME        "sch311x_wdt"
  42
  43/* Runtime registers */
  44#define GP60                    0x47
  45#define WDT_TIME_OUT            0x65
  46#define WDT_VAL                 0x66
  47#define WDT_CFG                 0x67
  48#define WDT_CTRL                0x68
  49
  50/* internal variables */
  51static unsigned long sch311x_wdt_is_open;
  52static char sch311x_wdt_expect_close;
  53static struct platform_device *sch311x_wdt_pdev;
  54
  55static int sch311x_ioports[] = { 0x2e, 0x4e, 0x162e, 0x164e, 0x00 };
  56
  57static struct { /* The devices private data */
  58        /* the Runtime Register base address */
  59        unsigned short runtime_reg;
  60        /* The card's boot status */
  61        int boot_status;
  62        /* the lock for io operations */
  63        spinlock_t io_lock;
  64} sch311x_wdt_data;
  65
  66/* Module load parameters */
  67static unsigned short force_id;
  68module_param(force_id, ushort, 0);
  69MODULE_PARM_DESC(force_id, "Override the detected device ID");
  70
  71#define WATCHDOG_TIMEOUT 60             /* 60 sec default timeout */
  72static int timeout = WATCHDOG_TIMEOUT;  /* in seconds */
  73module_param(timeout, int, 0);
  74MODULE_PARM_DESC(timeout,
  75        "Watchdog timeout in seconds. 1<= timeout <=15300, default="
  76                __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
  77
  78static bool nowayout = WATCHDOG_NOWAYOUT;
  79module_param(nowayout, bool, 0);
  80MODULE_PARM_DESC(nowayout,
  81        "Watchdog cannot be stopped once started (default="
  82                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  83
  84/*
  85 *      Super-IO functions
  86 */
  87
  88static inline void sch311x_sio_enter(int sio_config_port)
  89{
  90        outb(0x55, sio_config_port);
  91}
  92
  93static inline void sch311x_sio_exit(int sio_config_port)
  94{
  95        outb(0xaa, sio_config_port);
  96}
  97
  98static inline int sch311x_sio_inb(int sio_config_port, int reg)
  99{
 100        outb(reg, sio_config_port);
 101        return inb(sio_config_port + 1);
 102}
 103
 104static inline void sch311x_sio_outb(int sio_config_port, int reg, int val)
 105{
 106        outb(reg, sio_config_port);
 107        outb(val, sio_config_port + 1);
 108}
 109
 110/*
 111 *      Watchdog Operations
 112 */
 113
 114static void sch311x_wdt_set_timeout(int t)
 115{
 116        unsigned char timeout_unitame="L13io_enter(int    5 117
 118        /* When new timeout is bigger then 255 seconds, we will use minutes */
 119        if (t > 255) {
 120                timeout_unitame="L13enter(int    5 121                t /= 63enter(int    5 122        }
 123
 124        /* -- Watchdog Timeout --
 125         * Bit 0-6 (Reserved)
 126         * Bit 7   WDT Time-out Value Units Select
 127         *         (0"L1Minutes, 1"L1Seconds)
 128         */
 129        outb(timeout_unitame=, sch311x_wdt_data.runtime_reg + WDT_TIME_OUT);
 130
 131        /* -- Watchdog Timer Time-out Value --
 132         * Bit 0-7 Binary coded units (0=Disabled, 1..255)
 133         */
 134        outb(t, sch311x_wdt_data.runtime_reg + WDT_VAL);
 135}
 136
 137static void sch311x_wdt_start(void)
 138{
 139        unsigned char t;
 140
 141        spin_lock(&sch311x_wdt_data.io_lock);
 142
 143        /* set watchdog's timeout */
 144        sch311x_wdt_set_timeout(timeout);
 145        /* enable the watchdog */
 146        /* -- General Purpose I/O Bit 6.0 --
 147         * Bit 0,   In/Out: 0"L1Output, 1"L1Input
 148         * Bit 1,   Polarity: 0"L1No1Invert, 1"L1Invert
 149         * Bit 2-3, Function select: 00"L1GPI/O, 01"L1LED1, 11"L1WDT,
 150         *                           10"L1Either Edge Triggered Intr.4
 151         * Bit 4-6  (Reserved)
 152         * Bit 7,   Output Type: 0"L1Push1Pull Bit, 1"L1Open Drain
 153         */
 154        t = inb(sch311x_wdt_data.runtime_reg + GP60);
 155        outb((t & ~0x0d) | 0x0c, sch311x_wdt_data.runtime_reg + GP60);
 156
 157        spin_unlock(&sch311x_wdt_data.io_lock);
 158
 159}
 160
 161static void sch311x_wdt_stop(void)
 162{
 163        unsigned char t;
 164
 165        spin_lock(&sch311x_wdt_data.io_lock);
 166
 167        /* stop the watchdog */
 168        t = inb(sch311x_wdt_data.runtime_reg + GP60);
 169        outb((t & ~0x0d) | 0x01, sch311x_wdt_data.runtime_reg + GP60);
 170        /* disable timeout by setting it to 0"*/
 171        sch311x_wdt_set_timeout(0);
 172
 173        spin_unlock(&sch311x_wdt_data.io_lock);
 174}
 175
 176static void sch311x_wdt_keepalive(void)
 177{
 178        spin_lock(&sch311x_wdt_data.io_lock);
 179        sch311x_wdt_set_timeout(timeout);
 180        spin_unlock(&sch311x_wdt_data.io_lock);
 181}
 182
 183static int sch311x_wdt_set_heartbeat(int t)
 184{
 185        if (t < 1 || t > (255*60))
 186                return -EINVAL;
 187
 188        /* When new timeout is bigger then 255 seconds,
 189         * we will round up to minutes (with a max of 255) */
 190        if (t > 255)
 191                t = (((t - 1) / 60) + 1) * 63enter(int    5 192
 193        timeout = t;
 194        return 3enter(int    5 195}
 196
 197static void sch311x_wdt_get_status(int *status)
 198{
 199        unsigned char new_status;
 200
 201        *status"L13enter(int    5 202
 203        spin_lock(&sch311x_wdt_data.io_lock);
 204
 205        /* -- Watchdog timer control --
 206         * Bit 0   Status Bit: 0"L1Timer counting, 1"L1Timeout occurred
 207         * Bit 1   Reserved
 208         * Bit 2   Force1Timeout: 1"L1Forces WD timeout event (self-cleaning)
 209         * Bit 3   P20 Force1Timeout enabled:
 210         *          0"L1P20 activity does not generate the WD timeout event
 211         *          1"L1P20 Allows rising edge of P20, from the keyboard
 212         *              controller, to force the WD timeout event.
 213         * Bit 4-7 Reserved
 214         */
 215        new_status = inb(sch311x_wdt_data.runtime_reg + WDT_CTRL);
 216        if (new_status & 0x01)
 217                *status"|= WDIOF_CARDRESET;
 218
 219        spin_unlock(&sch311x_wdt_data.io_lock);
 220}
 221
 222/*
 223 *      /dev/watchdog handling
 224 */
 225
 226static ssize_t sch311x_wdt_write(struct file *file, const char __user *buf,
 227                                                size_t count, loff_t *ppos)
 228{
 229        if (count) {
 230                if (!nowayout) {
 231                        size_t i;
 232
 233                        sch311x_wdt_expect_close"L13enter(int    5 234
 235                        for (i"L13e i != counte i++) {
 236                                char c;
file * 22get_user" class="sref">get_user(c, buf + i))
 238                                        return -EFAULT;
 239        ef">file * 22c" class="sref">c == 'V')
 24Spss="li233"> 233                        EFAULT;
 220}
 220}
 233              atic void io_lock);
 220}
 class="sref">i !=  220}
  96}
 187
 233              ioctass="sref">outb<            ioctawrite" class="sref">sch311x_wdt_write(struct file *sch311x_wdt_cm_PARM_DESC(buf,
 23333333333333333333333333s="line" long"233"> 233  ardt_data.ppos)
i++) {
 220}
 193        hrefL193"> ef="=0" class="line" name="L220"> 220}
 233  ardt_data. 220}
 220}
sch311xme="L220_info void i !adess     i++) {
i !oplass=="L236" class="line"ode=status" class="srefKEEPALIVEP          i++) {
 227     e=status" class="srefSETE_STRING(i++) {
 238     e=status" class="srefMAGICCLOSEG(buf,
file rmware_f" cass="L239" cla= 1href="+code=buf" class="sref">buf,
dessity="sref">i !adessity="L2       (buf,
 220}
 232
film_PARM_DESC(i++) {
i++) {
py_tofile, const char">py_tofile<9"> 22get_user" clasardc void i !adess    ,   235        dess="sref">i !adess    ) i))
EFAULT;
EFAULT;
 218
i++) {
i++) {
 1"> 197static void _get_status(int *EFAULT;
 pu * *9"> 22get_user" clas>_get_status(int *EFAULT;
  96}
i++) {
 pu * *9"> 22get_user" clas>spin_unlock(&EFAULT;
 196
i++) {
i++) {
i !oplass=="L2(outb 186                return -EINVAL;
 200
file * 22get_user" clasoplass=="sref">i !oplass=="L2(i))
EFAULT;
fioplass=="sref">i !oplass=="L23_statue=status" class="srS_DISABLE=WDIG(i++) {
spin_unlockatic void EFAULT;
outbsch311x_wdt_expect_close"L13enter  96}
fioplass=="sref">i !oplass=="L23_statue=status" class="srS_ENABLE=WDIG(i++) {
spin_unlockatic void EFAULT;
outbsch311x_wdt_expect_close"L13enter  96}
 s="class="sref">outbsch311x_wdt_expect_close"L13enter   5 122        }
i++) {
 233              atic void io_lock);
(int    5EFAULT;
 196
i++) {
file * 22get_user" clashrefL193"> 193        hrefL193"> ef="(i))
EFAULT;
spin_unlockaatic int  193        hrefL193"> ef="+i))
 186                return -EINVAL;
(int    5 233              atic void io_lock);
 205Fall"L224"> 224 */
i++) {
 pu * *9"> 22get_user" clasL193"> 193        EFAULT;
i++) {
 186   NOTTY        return -NOTTYef="+code=EINVAL" class="sref">EINVAL;
 122        }
 194        return 3enter<3ef="drive3s/watchdog/sch311x_wdt.c3L210"31d="L220" class="line" name="L220"> 220}
 221
 183sopes="sref">file 183sopes9"> 2" class="sref">sch311xin311="sref">i !an311ef="+code=__user" clain311="sref">i !an311ef=",d" class="sref">sch311x_wdt_write(struct file *i))
i++) {
outb( 20, unlock" class="sref"            issopes="sref">file 183sissopesef="+i))
 186   BUSY        return -BUSYef="+code=EINVAL" class="sref">EINVAL;
 1424"> 224 */
 127 224 */
 128         */
EFAULT;
 1eartbeat" class=onseek    sopes="sref">file 22get_user" clasin311="sref">i !an311ef=",d="+code=file" class="sref">file *EFAULT;
 181}
 232
 183s   sch311xin311="sref">i !an311ef="+code=__user" clain311="sref">i !an311ef=",d" class="sref">sch311x_wdt_write(struct file *i))
 184{
i++) {
spin_unlockatic void EFAULT;
i++) {
outb( 22="L229"> 22c" class="quot;Unpect_ce" nse" ,     x_wdp    s="sref"!\n"quot;> == EFAULT;
 233              atic void io_lock);
 181}
outb( 20, unlock" class="sref"            issopes="sref">file 183sissopesef="+de=io_lock" class="sref">io_lock);
sch311x_wdt_expect_close"L13enter<3ef="drive3s/watchdog/sch311x_wdt.c3L233"3id="L233" class="line" name="L194"> 194        return 3enter<3a>(int    5 174}
 225
 224 */
 224 */
         */
         */
sch311x> *i !> *i++) {
(i++) {
((i++) {
  i++) {
(int outb<+code=ed ioctaclass="k" class="sref"            ioctass="sref">outb<            ioctawrit,ref="+code=i" class="sref">i++) {
filefile 183sopes9"> ,ref="+code=i" class="sref">i++) {
i++) {
 220}
 218
sch311xmiscdevica href="+code=scmiscdevica="L23s="line" name="Lspin_unlockmiscdev href="+code=sch311x_wdt_exmiscdevclass="href="+code=i" class="sref">i++) {
(i++) {
( 22c" class="quot;s="sref""quot;> == i++) {
i++) {
 220}
 234
 224 */
 224 */
         */
 218
 183sproba href="+code=sch311x_wdt_exprobaclas2" class="sref">sch311xplatform_devica href="+code=scplatform_devicaa href="+code=file" cpdev href="+code=scpdevclasi))
i++) {
sch311xdevica href="+code=scdevicaa href="+code=file" cdev href="+code=scdevclass="unlock" class="sref"pdev href="+code=scpdevclas- 220}
 220}
 220}
outb(spin_unlock(&sch311x_wdt_data.io_lock);
 225
 230file 22get_user" clas>spin_unlock(&sch311x_wdt_data.(i++) {
 22get_user" clasdev href="+code=scdevclas,me="L229"> 22c" class="quot;Failed">  request regass 0x%04x-0x%04x.\n"quot;> == i++) {
spin_unlock(&sch311x_wdt_data.i++) {
spin_unlock(&sch311x_wdt_data.io_lock);
 186   BUSY        return -BUSYef="+code=EINVAL" class="sref">EINVAL;
  line" name="L61exf">outb(EINVAL;
 122        }
 220}
 230file 22get_user" clas>spin_unlock(&sch311x_wdt_data.i++) {
(i++) {
 22get_user" clasdev href="+code=scdevclas,me="L229"> 22c" class="quot;Failed">  request regass 0x%04x-0x%04x.\n"quot;> == i++) {
spin_unlock(&sch311x_wdt_data.i++) {
spin_unlock(&sch311x_wdt_data.runtime_reg + WDT_CTRL);
 186   BUSY        return -BUSYef="+code=EINVAL" class="sref">EINVAL;
  line" name="L61exf"_re: 1fileEINVAL;
 122        }
 232
 205Make sur   *at  *  ame="L23 i      ref"L1F"28         */
EFAULT;
 225
 14 Dis    0 Allows  and mouseass=ercommss and ss=errupt"28         */
 205        /* -- Watchdog timer control --
 2080 pan class="comment">         * Bit 1   Reserved
 209
 210
 211         * Bit 1   Reserved
   5 212         * Bit 1   Reserved
 213333333333330001=IRQ1,30010=(Invalid),30011=IRQ3">  1111=IRQ15orces WD timeout event (self-cleaning)
 214         */
(int    5new_statusspin_unlock(&sch311x_wdt_data.rF        rF +code=WDT_CTRL" class="sref">WDT_CTRL);
 196
 205Check  *at  *  rtbeat" c value i  within it>c         */
 208if     r clt >   *  default214         */
 193        i++) {
(WDT_CTRL);
 22get_user" clasdev href="+code=scdevclas,me="L229"> 22c" class="quot; == i++) {
(int    5 193        WDT_CTRL);
  96}
 234
 205Get  void   c booi214         */
spin_unlock(&WDT_CTRL);
 187
i !paressclas  220}
         */
spin_unlockmiscdev href="+code=sch311x_wdt_exmiscdevclas)=WDT_CTRL" class="sref">WDT_CTRL);
i++) {
 233  dev_er* 22get_user" clasdev href="+code=scdevclas,me="L229"> 22c" class="quot;can    r gast * miscdev on mino*=%d (er*=%d)\n"quot;> == i++) {
(WDT_CTRL);
  line" name="L61exf"_re: 1 220}
  96}
 196
 22get_user" clasdev href="+code=scdevclas,96" class="line" name="L196"> 196
 22c" class="quot;SMSC SCH196 == i++) {
 193                nowayrefclas)=WDT_CTRL" class="sref">WDT_CTRL);
 200
 194        return 3enter<4ef="drive4s/watchdog/sch311x_wdt.c4L222"42d="L232" class="line" name="L232"> 232
i++) {
filespin_unlock(&sch311x_wdt_data.WDT_CTRL);
filei++) {
filespin_unlock(&sch311x_wdt_data.WDT_CTRL);
sch311x_wdt_data.sch311x_wdt_expect_close"L13enter<4e8="drive4s/watchdog/sch311x_wdt.c4L228"42d="L2line" name="L61exf">outb(i++) {
sch311x_wdt_expect_close"L13enter<4ef="drive4s/watchdog/sch311x_wdt.c4L230"43d="L220" class="line" name="L220"> 220}
 221
 183sremoid sch311xplatform_devica href="+code=scplatform_devicaa href="+code=file" cpdev href="+code=scpdevclasi))
i++) {
(int    5 205Stop  *           */
 230        nowayrefclas)an class="comment">         */
spin_unlockatic void EFAULT;
 187
 205Der gast * 14         */
spin_unlockmiscdev href="+code=sch311x_wdt_exmiscdevclas)=WDT_CTRL" class="sref">WDT_CTRL);
filespin_unlock(&sch311x_wdt_data.WDT_CTRL);
filespin_unlock(&sch311x_wdt_data.WDT_CTRL);
sch311x_wdt_data.sch311x_wdt_expect_close"L13enter<4ef="drive4s/watchdog/sch311x_wdt.c4L143"44d="L233" class="line" name="L194"> 194        return 3enter<4e>(int  174}
 225
spin_unlockahutdows="sref">file 183sahutdowsef="2" class="sref">sch311xplatform_devica href="+code=scplatform_devicaa href="+code=file" cdev href="+code=scdevclas)an class="comment">         */
i++) {
 205Tline *  a h offss="we h1ve2a soft ahutdows 14         */
EFAULT;
 220}
 221
sch311xplatform_dline" href="+code=scplatform_dline"="L21s="line" name="Lspin_unlocki++) {
i++) {
i++) {
filefile 183sahutdowsef=",ref="+code=i" class="sref">i++) {
i++) {
(i++) {
((i++) {
i++) {
 220}
 221
outb(         */
i++) {
. 220}
 220}
 220}
 187
io_clafig_poc void EFAULT;
         */
 205Check devica ID. We curressly know about:         */
 211SCH1962 (0x7c),1SCH1964 (0x7d),3and SCH1966 (0x7f). 14         */
new_statusio_clafig_poc void EFAULT;
.i++) {
 186   NODEV 220}
  line" name="L61exf">outb(EINVAL;
 220}
.EINVAL;
 218
 205Selec  logical devica A (ref="+c r gast *s) 14         */
new_statusio_clafig_poc void EFAULT;
 221
 205Check s="Logical Devica R gast * is curressly commve214         */
spin_uncio_inf">new_statusio_clafig_poc void          */
 22c" class="quot;Seems  *at LDN 0x0a i      commve...\n"quot;> == EFAULT;
 225
 14 Get  *  b1         */
spin_uncio_inf">new_statusio_clafig_poc void          */
new_statusio_clafig_poc void WDT_CTRL);
 230i++) {
 22c" class="quot;B1 == EFAULT;
 186   NODEV 220}
   5outb(EINVAL;
  96}
 220}
(int    5 225
 22c" class="quot;Foun  an SMSC SCH196%d chip  c 0x%04x\n"quot;> ==  220}
 187
outb(i++) {
outb(io_clafig_poc void EFAULT;
 1eartbeat" classer*sch311x_wdt_expect_close"L13enter<5ef="drive5s/watchdog/sch311x_wdt.c5L201"50d="L181" class="line" name="L181"> 181}
(int    5 232
outb(sinf">outb(sinf"ef="2void)an class="comment">         */
 184{
sch311x_wdt_expect_close"L13enter<5e6="drive5s/watchdog/sch311x_wdt.c5L206"50d="L216" clasunsigned ahoc ea href="+code=cadd*sch311x_wdt_expect_close"L13enter<5e7="drive5s/watchdog/sch311x_wdt.c5L207"50d="L187" class="line" name="L187"> 187
 230         */
         */
sch311x_wdt_expect_close"L13enter<5ef="drive5s/watchdog/sch311x_wdt.c5L211"51d="L221" class="line" name="L221"> 221
 230         */
 1L186"> 186   NODEV 220}
 234
(&sch311x_wdt_data. 220}
 196
spin_unlockEFAULT;
         */
 1eartbeat" classer*sch311x_wdt_expect_close"L13enter<5ef="drive5s/watchdog/sch311x_wdt.c5"driv52d="L200" class="line" name="L200"> 200
(i++) {
 + EFAULT;
 220}
i++) {
(EFAULT;
  line" name="L61unreg_platform_dline" href="+code=scunreg_platform_dline"clasref">sch311x_wdt_expect_close"L13enter<5ef="drive5s/watchdog/sch311x_wdt.c5L22"c527="L122" clas81" class="line" name="L181"> 181}
 218
sch311x_wdt_expect_close"L13enter<5ef="drive5s/watchdog/sch311x_wdt.c5L230"53d="L200" class="line" name="L200"> 200
i++) {
  96}
 1eartbeat" classer*sch311x_wdt_expect_close"L13e
(5nt    5 1745
 225outb__uncio_ex1" class="line" name="h311x_uncio_exf">outb(sinf"ef="2void)an class="comment">         */
i++) 5
EFAULT;
  96}
 2205
 221
outbmodule1x_/aTR_ERRt_stoe=status" classsch311x_/a>sinf">outb(sinL194"49d="L154" clasf="+code=file" cadd*outbmodule1unciTR_ERRt_stoe=status" classsch311uncio_exf">outb(sinL194"49d="L154" clasf="+code=file" cadd*(5nt  234
( 22c" class=Wim VFounebro 205)3_lwim@iguana.be> xet\n"quot;> == EFAULT5
( 22c" class="quot;SMSC SCHclasDog T  *  DEFAUL xet\n"quot;> == EFAULT5
( 22c" class=GPL xet\n"quot;> == EFAULT5
   NODEV  TR_ERRt_stoe=status"" clas+ATCHDOG_MINORG(EFAULT5
         */


The original LXRh1ve2ware byess od="L2an chttp://sourcspange.net/projects/lxeg>LXRh" nau_/ay lxe@chdux.no11x_w
lxe.chdux.no kindly hounrdrbyed="L2an chttp://www.redpill-chdpro.no">Redpill Lhdpro AS