linux/drivers/watchdog/rc32434_wdt.c
<<
>>
Prefs
   1/*
   2 *  IDT Interprise 79RC32434 watchdog driver
   3 *
   4 *  Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org>
   5 *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
   6 *
   7 *  based on
   8 *  SoftDog 0.05:       A Software Watchdog Device
   9 *
  10 *  (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
  11 *                                      All Rights Reserved.
  12 *
  13 *  This program is free software; you can redistribute it and/or
  14 *  modify it under the terms of the GNU General Public License
  15 *  as published by the Free Software Foundation; either version
  16 *  2 of the License, or (at your option) any later version.
  17 *
  18 */
  19
  20#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  21
  22#include <linux/module.h>               /* For module specific items */
  23#include <linux/moduleparam.h>          /* For new moduleparam's */
  24#include <linux/types.h>                /* For standard types (like size_t) */
  25#include <linux/errno.h>                /* For the -ENODEV/... values */
  26#include <linux/kernel.h>               /* For printk/panic/... */
  27#include <linux/fs.h>                   /* For file operations */
  28#include <linux/miscdevice.h>           /* For MODULE_ALIAS_MISCDEV
  29                                                        (WATCHDOG_MINOR) */
  30#include <linux/watchdog.h>             /* For the watchdog specific items */
  31#include <linux/init.h>                 /* For __init/__exit/... */
  32#include <linux/platform_device.h>      /* For platform_driver framework */
  33#include <linux/spinlock.h>             /* For spin_lock/spin_unlock/... */
  34#include <linux/uaccess.h>              /* For copy_to_user/put_user/... */
  35
  36#include <asm/mach-rc32434/integ.h>     /* For the Watchdog registers */
  37
  38#define VERSION "1.0"
  39
  40static struct {
  41        unsigned long inuse;
  42        spinlock_t io_lock;
  43} rc32434_wdt_device;
  44
  45static struct integ __iomem *wdt_reg;
  46
  47static int expect_close;
  48
  49/* Board internal clock speed in Hz,
  50 * the watchdog timer ticks at. */
  51extern unsigned int idt_cpu_freq;
  52
  53/* translate wtcompare value to seconds and vice versa */
  54#define WTCOMP2SEC(x)   (x / idt_cpu_freq)
  55#define SEC2WTCOMP(x)   (x * idt_cpu_freq)
  56
  57/* Use a default timeout of 20s. This should be
  58 * safe for CPU clock speeds up to 400MHz, as
  59 * ((2 ^ 32) - 1) / (400MHz / 2) = 21s.  */
  60#define WATCHDOG_TIMEOUT 20
  61
  62static int timeout = WATCHDOG_TIMEOUT;
  63module_param(timeout, int, 0);
  64MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default="
  65                __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
  66
  67static bool nowayout = WATCHDOG_NOWAYOUT;
  68module_param(nowayout, bool, 0);
  69MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  70        __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  71
  72/* apply or and nand masks to data read from addr and write back */
  73#define SET_BITS(addr, or, nand) \
  74        writel((readl(&addr) | or) & ~nand, &addr)
  75
  76static int rc32434_wdt_set(int new_timeout)
  77{
  78        int max_to = WTCOMP2SEC((u32)-1);
  79
  80        if (new_timeout < 0 || new_timeout > max_to) {
  81                pr_err("timeout value must be between 0 and %d\n", max_to);
  82                return -EINVAL;
  83        }
  84        timeout = new_timeout;
  85        spin_lock(&rc32434_wdt_device.io_lock);
  86        writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare);
  87        spin_unlock(&rc32434_wdt_device.io_lock);
  88
  89        return 0;
  90}
  91
  92static void rc32434_wdt_start(void)
  93{
  94        u32 or, nand;
  95
  96        spin_lock(&rc32434_wdt_device.io_lock);
  97
  98        /* zero the counter before enabling */
  99        writel(0, &wdt_reg->wtcount);
 100
 101        /* don't generate a non-maskable interrupt,
 102         * do a warm reset instead */
 103        nand = 1 << RC32434_ERR_WNE;
 104        or = 1 << RC32434_ERR_WRE;
 105
 106        /* reset the ERRCS timeout bit in case it's set */
 107        nand |= 1 << RC32434_ERR_WTO;
 108
 109        SET_BITS(wdt_reg->errcs, or, nand);
 110
 111        /* set the timeout (either default or based on module param) */
 112        rc32434_wdt_set(timeout);
 113
 114        /* reset WTC timeout bit and enable WDT */
 115        nand = 1 << RC32434_WTC_TO;
 116        or = 1 << RC32434_WTC_EN;
 117
 118        SET_BITS(wdt_reg->wtc, or, nand);
 119
 120        spin_unlock(&rc32434_wdt_device.io_lock);
 121        pr_info("Started watchdog timer\n");
 122}
 123
 124static void rc32434_wdt_stop(void)
 125{
 126        spin_lock(&rc32434_wdt_device.io_lock);
 127
 128        /* Disable WDT */
 129        SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN);
 130
 131        spin_unlock(&rc32434_wdt_device.io_lock);
 132        pr_info("Stopped watchdog timer\n");
 133}
 134
 135static void rc32434_wdt_ping(void)
 136{
 137        spin_lock(&rc32434_wdt_device.io_lock);
 138        writel(0, &wdt_reg->wtcount);
 139        spin_unlock(&rc32434_wdt_device.io_lock);
 140}
 141
 142static int rc32434_wdt_open(struct inode *inode, struct file *file)
 143{
 144        if (test_and_set_bit(0, &rc32434_wdt_device.inuse))
 145                return -EBUSY;
 146
 147        if (nowayout)
 148                __module_get(THIS_MODULE);
 149
 150        rc32434_wdt_start();
 151        rc32434_wdt_ping();
 152
 153        return nonseekable_open(inode, file);
 154}
 155
 156static int rc32434_wdt_release(struct inode *inode, struct file *file)
 157{
 158        if (expect_close == 42) {
 159                rc32434_wdt_stop();
 160                module_put(THIS_MODULE);
 161        } else {
 162                pr_crit("device closed unexpectedly. WDT will not stop!\n");
 163                rc32434_wdt_ping();
 164        }
 165        clear_bit(0, &rc32434_wdt_device.inuse);
 166        return 0;
 167}
 168
 169static ssize_t rc32434_wdt_write(struct file *file, const char *data,
 170                                size_t len, loff_t *ppos)
 171{
 172        if (len) {
 173                if (!nowayout) {
 174                        size_t i;
 175
 176                        /* In case it was set long ago */
 177                        expect_close = 0;
 178
 179                        for (i = 0; i != len; i++) {
 180                                char c;
 181                                if (get_user(c, data + i))
 182                                        return -EFAULT;
 183                                if (c == 'V')
 184                                        expect_close = 42;
 185                        }
 186                }
 187                rc32434_wdt_ping();
 188                return len;
 189        }
 190        return 0;
 191}
 192
 193static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
 194                                unsigned long arg)
 195{
 196        void __user *argp = (void __user *)arg;
 197        int new_timeout;
 198        unsigned int value;
 199        static const struct watchdog_info ident = {
 200                .options =              WDIOF_SETTIMEOUT |
 201                                        WDIOF_KEEPALIVEPING |
 202                                        WDIOF_MAGICCLOSE,
 203                .identity =             "RC32434_WDT Watchdog",
 204        };
 205        switch (cmd) {
 206        case WDIOC_GETSUPPORT:
 207                if (copy_to_user(argp, &ident, sizeof(ident)))
 208                        return -EFAULT;
 209                break;
 210        case WDIOC_GETSTATUS:
 211        case WDIOC_GETBOOTSTATUS:
 212                value = 0;
 213                if (copy_to_user(argp, &value, sizeof(int)))
 214                        return -EFAULT;
 215                break;
 216        case WDIOC_SETOPTIONS:
 217                if (copy_from_user(&value, argp, sizeof(int)))
 218                        return -EFAULT;
 219                switch (value) {
 220                case WDIOS_ENABLECARD:
 221                        rc32434_wdt_start();
 222                        break;
 223                case WDIOS_DISABLECARD:
 224                        rc32434_wdt_stop();
 225                        break;
 226                default:
 227                        return -EINVAL;
 228                }
 229                break;
 230        case WDIOC_KEEPALIVE:
 231                rc32434_wdt_ping();
 232                break;
 233        case WDIOC_SETTIMEOUT:
 234                if (copy_from_user(&new_timeout, argp, sizeof(int)))
 235                        return -EFAULT;
 236                if (rc32434_wdt_set(new_timeout))
 237                        return -EINVAL;
 238                /* Fall through */
 239        case WDIOC_GETTIMEOUT:
 240                return copy_to_user(argp, &timeout, sizeof(int));
 241        default:
 242                return -ENOTTY;
 243        }
 244
 245        return 0;
 246}
 247
 248static const struct file_operations rc32434_wdt_fops = {
 249        .owner          = THIS_MODULE,
 250        .llseek         = no_llseek,
 251        .write          = rc32434_wdt_write,
 252        .unlocked_ioctl = rc32434_wdt_ioctl,
 253        .open           = rc32434_wdt_open,
 254        .release        = rc32434_wdt_release,
 255};
 256
 257static struct miscdevice href="+code=argp" class="sref">miscdevice href=="sref">rc32434_wdt_releaseiscdevi 1582/a>  2     if (or, WDIOC_GETTATCHDOG_MINORhref="drivers/watchdog/rc32434_wdt.c#L255" id="L249" class="line" name="L159"> 1592/a>  25    . sref">no_llseek<">    .'V'an>,
rc32434_pan>,
 1602/a>  26    .rc32434_w= {
rc32434_wdt_fops = {
 1612/a>  2     }f="drivers/watchdog/rc32434_wdt.c#L256" id="L162" clas2="line" name="L162"> 1622/a>  26href="drivers/watchdog/rc32434_wdt.c#L193" id="2163" clas2="line" name="L163"> 1632/a>  26ic long rc32434_wdt_releaserc32434_wplatformclass="rc32434pevi 1642/a>  2     }="drivers/watchdog/rc32434_wdt.c#L249" id="L165" clas2="line" name="L165"> 1652/a>  2     rc32434_wde href="drivers/watchdog/rc32434_wdt.c#L198" id="2166" clas2="line" name="L166"> 1662/a>  2     return 0; href="+code=miscdevicresour="sref">rc32434_wdesour="or,  1672/a>}
26href="drivers/watchdog/rc32434_wdt.c#L248" id="L168" clas2="line" name="L168"> 1682/a>
<26    or, (desour="_by9"> sref">no_llseekplatformc/a>(desour="_by9"> ref="+code=argp" clasef=="sref">rc32434pevirc32434IORESOURCE_MEM'V'an>,
rb532asespan>);
 1692/a>st26    .or,  1702/a>  2                      +code=argp" clasr_er"sref">or, 'V'an>,
failed to;
 1712/a>{
27            ENOTTY;
DEVhref="drivers/watchdog/rc32434_wdt.c#L198" id="2172" clas2="line" name="L172"> 1722/a>  2     if ( 1732/a>  27href="drivers/watchdog/rc32434_wdt.c#L124" id="2174" clas2="line" name="L174"> 1742/a>  2              +code=write" clalass="sref">wdt_reg->no_llseekioremap_nocach ref="+code=argp" cla"sref">or, rc32434_w();
rc32434_wdesour="_t));ref="+code=argp" cla"sref">or,  1752/a>
<275   .wdt_reg-> 1762/a>  2                     <+code=argp" clasr_er"sref">or, 'V'an>,
failed to;
 1772/a>  2                     RC32434_WlaXI"href="drivers/watchdog/rc32434_wdt.c#L198" id="2168" clas2="line" name="L178"> 1782/a>
<278   if ( 1792/a>  27href="drivers/watchdog/rc32434_wdt.c#L150" id="2180" clas2="line" name="L180"> 1802/a>  28    clear_bitck" cctl<_in=";rc32434_wdt_device.io_lock);
 1812/a>  28href="drivers/watchdog/rc32434_wdt.c#L142" id="2182" clas2="line" name="L182"> 1822/a>  2              ss="comment">/* Fall throMake sureout (rc32434_ is!\n&qrunnpan>
 1832/a>  2              +code=rc32434_wdt_stop" class="sref">rc32434_wdt_stop();
 1842/a>  28href="drivers/watchdog/rc32434_wdt.c#L245" id="L185" clas2="line" name="L185"> 1852/a>  2              ss="comment">/* Fall throCheckoutaeout (he;
 1862/a>  2      ss="comment">         * do a warmref\n&qrERRCS oout (r based 
 1872/a>  28    if (rc32434_wdt_set(timeout);
 1882/a>  2             return rc32434_wdt_set(WDIOC_GETTATCHDOG_class=" href="drivers/watchdog/rc32434_wdt.c#L140" id="2169" clas2="line" name="L189"> 1892/a>  28            pr_info("Stopped wit and e) {
< must be between 0le WDt *7;d/span>);
 1902/a>  29                            c+code=new_timeoTTCOMP2SECsref">WDIOC_GETTTCOMP2SECn cla"+code=new_timeou="line" namocked_i      )-1ef="drivers/watchdog/rc32434_wdt.c#L241" id="L191" clas2="line" name="L191"> 1912/a>}
291   if ( 1922/a>
<2 href="drivers/watchdog/rc32434_wdt.c#L193" id="2193" clas2="line" name="L193"> 1932/a>st29             +code=rc32434_wd="sref">rc32434_wde href ="+code=WTCOMP2SEiscd>ist="sref">owneriscd>ist=";rc32434_wdt_releaseiscdevi 1942/a>  2              ef="+code=nowayout"d="sref">rc32434_wde href ref="0ef="drivers/watchdog/rc32434_wdt.c#L220" id="L195" clas2="line" name="L195"> 1952/a>{
29                    r+code=argp" clasr_er"sref">or, 'V'an>,
failed to;
<;ist="(rc32434_ >.);
 1962/a>  29                    unlocked_iomaphref="drivers/watchdog/rc32434_wdt.c#L198" id="2197" clas2="line" name="L197"> 1972/a>  2     int  1982/a>  29href="drivers/watchdog/rc32434_wdt.c#L179" id="2199" clas2="line" name="L199"> 1992/a>  29    pr_info("Stopped wquot;);
RC32434_WVERSIO"ode=ars="string">"Stopped w,quot;< margin:Dt *7;d sec/span>);
 2003/a>  3             .timeout);
 2013/a>  30href="drivers/watchdog/rc32434_wdt.c#L142" id="3202" clas3="line" name="L202"> 2023/a>  3              
 2033/a>  30href="drivers/watchdog/rc32434_wdt.c#L124" id="3204" clas3="line" name="L204"> 2043/a>  3     }+code=unlocked_iomapsref">unlocked_iomaphref="drivers/watchdog/rc32434_wdt.c#L242" id="3205" clas3="line" name="L205"> 2053/a>  3     switch (<+code=io_lock" cliomapsref">unlocked_cliomapref="+code=new_timeoalass="sref">wdt_reg-> 1963/a>  30    return 0;
rc32434_wde href="drivers/watchdog/rc32434_wdt.c#L198" id="3207" clas3="line" name="L207"> 2073/a>  30 href="drivers/watchdog/rc32434_wdt.c#L168" id="3208" clas3="line" name="L208"> 2083/a>  30href="drivers/watchdog/rc32434_wdt.c#L179" id="3209" clas3="line" name="L209"> 2093/a>  30ic rc32434_wdt_releasemov"ref=" href="+code=miscdevicplatformclass="sref">rc32434_wplatformclass="rc32434pevi 2103/a>  3     c="drivers/watchdog/rc32434_wdt.c#L220" id="3211" clas3="line" name="L211"> 2113/a>  31    owneriscd°t;ist=";rc32434_wdt_releaseiscdevi 2123/a>  3             <+code=io_lock" cliomapsref">unlocked_cliomapref="+code=new_timeoalass="sref">wdt_reg-> 2133/a>  31    return  2143/a>  31 href="drivers/watchdog/rc32434_wdt.c#L155" id="3215" clas3="line" name="L215"> 2153/a>  31href="drivers/watchdog/rc32434_wdt.c#L176" id="3216" clas3="line" name="L216"> 2163/a>  31ic int rc32434_wdt_open,shutdow"ref=" href="+code=miscdevicplatformclass="sref">rc32434_wplatformclass="rc32434pevi 2173/a>  31 href="drivers/watchdog/rc32434_wdt.c#L158" id="3218" clas3="line" name="L218"> 2183/a>  31    rc32434_wdt_stop();
 2193/a>  3      ="drivers/watchdog/rc32434_wdt.c#L155" id="3220" clas3="line" name="L220"> 2203/a>  32href="drivers/watchdog/rc32434_wdt.c#L131" id="3221" clas3="line" name="L221"> 2213/a>  3      ruct rc32434_wplatformcltchdoode=argp" class="sref">miscdevicltchdosref">rc32434_wf">miscdevicltchdo 2223/a>  32    .rc32434_wprob"ref= .rc32434_wdt_release 2233/a>  32    .rc32434_wd>mov"ref=.rc32434_wdt_releasemov"ref=="drivers/watchdog/rc32434_wdt.c#L204" id="3214" clas3="line" name="L224"> 2243/a>  32    .rc32434_wshutdow"ref=a href=="+code=rc32434_wdt_release" shutdow"sref">rc32434_wdt_open,shutdow"ref=="drivers/watchdog/rc32434_wdt.c#L204" id="3215" clas3="line" name="L225"> 2253/a>  3              "+code=release" ltchdosref">rc32434_wltchdo 2263/a>  3             default:
        "+code=release" 9"> sref">no_llseek<">    .'V'an>,
dt.c#L249" pan>);
 2273/a>  32    int  2283/a>  3      f="drivers/watchdog/rc32434_wdt.c#L256" id="3229" clas3="line" name="L229"> 2293/a>  32href="drivers/watchdog/rc32434_wdt.c#L150" id="3230" clas3="line" name="L230"> 2303/a>  3     c+code=rc32434_wi(rc32434_wi(miscdevicltchdosref">rc32434_wf">miscdevicltchdo 2313/a>  33href="drivers/watchdog/rc32434_wdt.c#L142" id="3232" clas3="line" name="L232"> 2323/a>  3      +code=argp" cla>,
WDIOC_GET>,
"Stopped wOndrej Zajs="k ref=santiago@crfreenet.orgef=",pan>);
 2333/a>  33            "Stopped wFlori"stFa="Llli ref=flori"s@
 2343/a>  33    }+code=unlocked_>,
:
WDIOC_GET>,
:
"Stopped wDtchdohref=ut (IDT DT Watc SoC(rc32434_pan>);
 2353/a>  3      +code=unlocked_>,
WDIOF_MAG>,
"Stopped wGPLpan>);
 2363/a>  3      +code=argp" cla>,
ENOTTY,
WDIOC_GETTATCHDOG_MINORhreff="drivers/watchdog/rc32434_wdt.c#L163" id="3227" clas3="line" name="L237"> 2373/a>  3      
LXR un ,=utis oselx"@e="ux.noref="
Redpill L="pro AS ,=providdohof L="uxruct ultpan>e WDs