linux/drivers/watchdog/ks8695_wdt.c
<<
>>
Prefs
   1/*
   2 * Watchdog driver for Kendin/Micrel KS8695.
   3 *
   4 * (C) 2007 Andrew Victor
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  12
  13#include <linux/bitops.h>
  14#include <linux/errno.h>
  15#include <linux/fs.h>
  16#include <linux/init.h>
  17#include <linux/kernel.h>
  18#include <linux/miscdevice.h>
  19#include <linux/module.h>
  20#include <linux/moduleparam.h>
  21#include <linux/platform_device.h>
  22#include <linux/types.h>
  23#include <linux/watchdog.h>
  24#include <linux/io.h>
  25#include <linux/uaccess.h>
  26#include <mach/hardware.h>
  27#include <mach/regs-timer.h>
  28
  29#define WDT_DEFAULT_TIME        5       /* seconds */
  30#define WDT_MAX_TIME            171     /* seconds */
  31
  32static int wdt_time = WDT_DEFAULT_TIME;
  33static bool nowayout = WATCHDOG_NOWAYOUT;
  34
  35module_param(wdt_time, int, 0);
  36MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
  37                                        __MODULE_STRING(WDT_DEFAULT_TIME) ")");
  38
  39#ifdef CONFIG_WATCHDOG_NOWAYOUT
  40module_param(nowayout, bool, 0);
  41MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  42                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  43#endif
  44
  45
  46static unsigned long ks8695wdt_busy;
  47static DEFINE_SPINLOCK(ks8695_lock);
  48
  49/* ......................................................................... */
  50
  51/*
  52 * Disable the watchdog.
  53 */
  54static inline void ks8695_wdt_stop(void)
  55{
  56        unsigned long tmcon;
  57
  58        spin_lock(&ks8695_lock);
  59        /* disable timer0 */
  60        tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
  61        __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
  62        spin_unlock(&ks8695_lock);
  63}
  64
  65/*
  66 * Enable and reset the watchdog.
  67 */
  68static inline void ks8695_wdt_start(void)
  69{
  70        unsigned long tmcon;
  71        unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
  72
  73        spin_lock(&ks8695_lock);
  74        /* disable timer0 */
  75        tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
  76        __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
  77
  78        /* program timer0 */
  79        __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC);
  80
  81        /* re-enable timer0 */
  82        tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
  83        __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
  84        spin_unlock(&ks8695_lock);
  85}
  86
  87/*
  88 * Reload the watchdog timer.  (ie, pat the watchdog)
  89 */
  90static inline void ks8695_wdt_reload(void)
  91{
  92        unsigned long tmcon;
  93
  94        spin_lock(&ks8695_lock);
  95        /* disable, then re-enable timer0 */
  96        tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
  97        __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
  98        __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
  99        spin_unlock(&ks8695_lock);
 100}
 101
 102/*
 103 * Change the watchdog time interval.
 104 */
 105static int ks8695_wdt_settimeout(int new_time)
 106{
 107        /*
 108         * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
 109         *
 110         * Since WDV is a 16-bit counter, the maximum period is
 111         * 65536 / 0.256 = 256 seconds.
 112         */
 113        if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
 114                return -EINVAL;
 115
 116        /* Set new watchdog time. It will be used when
 117           ks8695_wdt_start() is called. */
 118        wdt_time = new_time;
 119        return 0;
 120}
 121
 122/* ......................................................................... */
 123
 124/*
 125 * Watchdog device is opened, and watchdog starts running.
 126 */
 127static int ks8695_wdt_open(struct inode *inode, struct file *file)
 128{
 129        if (test_and_set_bit(0, &ks8695wdt_busy))
 130                return -EBUSY;
 131
 132        ks8695_wdt_start();
 133        return nonseekable_open(inode, file);
 134}
 135
 136/*
 137 * Close the watchdog device.
 138 * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
 139 *  disabled.
 140 */
 141static int ks8695_wdt_close(struct inode *inode, struct file *file)
 142{
 143        /* Disable the watchdog when file is closed */
 144        if (!nowayout)
 145                ks8695_wdt_stop();
 146        clear_bit(0, &ks8695wdt_busy);
 147        return 0;
 148}
 149
 150static const struct watchdog_info ks8695_wdt_info = {
 151        .identity       = "ks8695 watchdog",
 152        .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 153};
 154
 155/*
 156 * Handle commands from user-space.
 157 */
 158static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd,
 159                                                        unsigned long arg)
 160{
 161        void __user *argp = (void __user *)arg;
 162        int __user *p = argp;
 163        int new_value;
 164
 165        switch (cmd) {
 166        case WDIOC_GETSUPPORT:
 167                return copy_to_user(argp, &ks8695_wdt_info,
 168                                        sizeof(ks8695_wdt_info)) ? -EFAULT : 0;
 169        case WDIOC_GETSTATUS:
 170        case WDIOC_GETBOOTSTATUS:
 171                return put_user(0, p);
 172        case WDIOC_SETOPTIONS:
 173                if (get_user(new_value, p))
 174                        return -EFAULT;
 175                if (new_value & WDIOS_DISABLECARD)
 176                        ks8695_wdt_stop();
 177                if (new_value & WDIOS_ENABLECARD)
 178                        ks8695_wdt_start();
 179                return 0;
 180        case WDIOC_KEEPALIVE:
 181                ks8695_wdt_reload();    /* pat the watchdog */
 182                return 0;
 183        case WDIOC_SETTIMEOUT:
 184                if (get_user(new_value, p))
 185                        return -EFAULT;
 186                if (ks8695_wdt_settimeout(new_value))
 187                        return -EINVAL;
 188                /* Enable new time value */
 189                ks8695_wdt_start();
 190                /* Return current value */
 191                return put_user(wdt_time, p);
 192        case WDIOC_GETTIMEOUT:
 193                return put_user(wdt_time, p);
 194        default:
 195                return -ENOTTY;
 196        }
 197}
 198
 199/*
 200 * Pat the watchdog whenever device is written to.
 201 */
 202static ssize_t ks8695_wdt_write(struct file *file, const char *data,
 203                                                size_t len, loff_t *ppos)
 204{
 205        ks8695_wdt_reload();            /* pat the watchdog */
 206        return len;
 207}
 208
 209/* ......................................................................... */
 210
 211static const struct file_operations ks8695wdt_fops = {
 212        .owner          = THIS_MODULE,
 213        .llseek         = no_llseek,
 214        .unlocked_ioctl = ks8695_wdt_ioctl,
 215        .open           = ks8695_wdt_open,
 216        .release        = ks8695_wdt_close,
 217        .write          = ks8695_wdt_write,
 218};
 219
 220static struct miscdevice ks8695wdt_miscdev = {
 221        .minor          = WATCHDOG_MINOR,
 222        .name           = "watchdog",
 223        .fops           = &ks8695wdt_fops,
 224};
 225
 226static int __devinit ks8695wdt_probe(struct platform_device *pdev)
 227{
 228        int res;
 229
 230        if (ks8695wdt_miscdev.parent)
 231                return -EBUSY;
 232        ks8695wdt_miscdev.parent = &pdev->dev;
 233
 234        res = misc_register(&ks8695wdt_miscdev);
 235        if (res)
 236                return res;
 237
 238        pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n",
 239                wdt_time, nowayout ? ", nowayout" : "");
 240        return 0;
 241}
 242
 243static int __devexit ks8695wdt_remove(struct platform_device *pdev)
 244{
 245        int res;
 246
 247        res = misc_deregister(&ks8695wdt_miscdev);
 248        if (!res)
 249                ks8695wdt_miscdev.parent = NULL;
 250
 251        return res;
 252}
 253
 254static void ks8695wdt_shutdown(struct platform_device *pdev)
 255{
 256        ks8695_wdt_stop();
 257}
 258
 259#ifdef CONFIG_PM
 260
 261static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message)
 262{
 263        ks8695_wdt_stop();
 264        return 0;
 265}
 266
 267static int ks8695wdt_resume(struct platform_device *pdev)
 268{
 269        if (ks8695wdt_busy)
 270                ks8695_wdt_start();
 271        return 0;
 272}
 273
 274#else
 275#define ks8695wdt_suspend NULL
 276#define ks8695wdt_resume        NULL
 277#endif
 278
 279static struct platform_driver ks8695wdt_driver = {
 280        .probe          = ks8695wdt_probe,
 281        .remove         = __devexit_p(ks8695wdt_remove),
 282        .shutdown       = ks8695wdt_shutdown,
 283        .suspend        = ks8695wdt_suspend,
 284        .resume         = ks8695wdt_resume,
 285        .driver         = {
 286                .name   = "ks8695_wdt",
 287                .owner  = THIS_MODULE,
 288        },
 289};
 290
 291static int __init ks8695_wdt_init(void)
 292{
 293        /* Check that the heartbeat value is within range;
 294           if not reset to the default */
 295        if (ks8695_wdt_settimeout(wdt_time)) {
 296                ks8695_wdt_settimeout(WDT_DEFAULT_TIME);
 297                pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i"
 298                                        ", using %d\n", wdt_time, WDT_MAX_TIME);
 299        }
 300        return platform_driver_register(&ks8695wdt_driver);
 301}
 302
 303static void __exit ks8695_wdt_exit(void)
 304{
 305        platform_driver_unregister(&ks8695wdt_driver);
 306}
 307
 308module_init(ks8695_wdt_init);
 309module_exit(ks8695_wdt_exit);
 310
 311MODULE_AUTHOR("Andrew Victor");
 312MODULE_DESCRIPTION("Watchdog driver for KS8695");
 313MODULE_LICENSE("GPL");
 314MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 315MODULE_ALIAS("platform:ks8695_wdt");
 316
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.