linux/drivers/watchdog/bfin_wdt.c
<<
>>
Prefs
   1/*
   2 * Blackfin On-Chip Watchdog Driver
   3 *  Supports BF53[123]/BF53[467]/BF54[2489]/BF561
   4 *
   5 * Originally based on softdog.c
   6 * Copyright 2006-2007 Analog Devices Inc.
   7 * Copyright 2006-2007 Michele d'Amico
   8 * Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
   9 *
  10 * Enter bugs at http://blackfin.uclinux.org/
  11 *
  12 * Licensed under the GPL-2 or later.
  13 */
  14
  15#include <linux/platform_device.h>
  16#include <linux/module.h>
  17#include <linux/moduleparam.h>
  18#include <linux/types.h>
  19#include <linux/timer.h>
  20#include <linux/miscdevice.h>
  21#include <linux/watchdog.h>
  22#include <linux/fs.h>
  23#include <linux/notifier.h>
  24#include <linux/reboot.h>
  25#include <linux/init.h>
  26#include <linux/interrupt.h>
  27#include <linux/uaccess.h>
  28#include <asm/blackfin.h>
  29
  30#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
  31#define stampit() stamp("here i am")
  32#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); })
  33#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); })
  34
  35#define WATCHDOG_NAME "bfin-wdt"
  36#define PFX WATCHDOG_NAME ": "
  37
  38/* The BF561 has two watchdogs (one per core), but since Linux
  39 * only runs on core A, we'll just work with that one.
  40 */
  41#ifdef BF561_FAMILY
  42# define bfin_read_WDOG_CTL()    bfin_read_WDOGA_CTL()
  43# define bfin_read_WDOG_CNT()    bfin_read_WDOGA_CNT()
  44# define bfin_read_WDOG_STAT()   bfin_read_WDOGA_STAT()
  45# define bfin_write_WDOG_CTL(x)  bfin_write_WDOGA_CTL(x)
  46# define bfin_write_WDOG_CNT(x)  bfin_write_WDOGA_CNT(x)
  47# define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x)
  48#endif
  49
  50/* Bit in SWRST that indicates boot caused by watchdog */
  51#define SWRST_RESET_WDOG 0x4000
  52
  53/* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */
  54#define WDOG_EXPIRED 0x8000
  55
  56/* Masks for WDEV field in WDOG_CTL register */
  57#define ICTL_RESET   0x0
  58#define ICTL_NMI     0x2
  59#define ICTL_GPI     0x4
  60#define ICTL_NONE    0x6
  61#define ICTL_MASK    0x6
  62
  63/* Masks for WDEN field in WDOG_CTL register */
  64#define WDEN_MASK    0x0FF0
  65#define WDEN_ENABLE  0x0000
  66#define WDEN_DISABLE 0x0AD0
  67
  68/* some defaults */
  69#define WATCHDOG_TIMEOUT 20
  70
  71static unsigned int timeout = WATCHDOG_TIMEOUT;
  72static int nowayout = WATCHDOG_NOWAYOUT;
  73static struct watchdog_info bfin_wdt_info;
  74static unsigned long open_check;
  75static char expect_close;
  76static DEFINE_SPINLOCK(bfin_wdt_spinlock);
  77
  78/**
  79 *      bfin_wdt_keepalive - Keep the Userspace Watchdog Alive
  80 *
  81 *      The Userspace watchdog got a KeepAlive: schedule the next timeout.
  82 */
  83static int bfin_wdt_keepalive(void)
  84{
  85        stampit();
  86        bfin_write_WDOG_STAT(0);
  87        return 0;
  88}
  89
  90/**
  91 *      bfin_wdt_stop - Stop the Watchdog
  92 *
  93 *      Stops the on-chip watchdog.
  94 */
  95static int bfin_wdt_stop(void)
  96{
  97        stampit();
  98        bfin_write_WDOG_CTL(WDEN_DISABLE);
  99        return 0;
 100}
 101
 102/**
 103 *      bfin_wdt_start - Start the Watchdog
 104 *
 105 *      Starts the on-chip watchdog.  Automatically loads WDOG_CNT
 106 *      into WDOG_STAT for us.
 107 */
 108static int bfin_wdt_start(void)
 109{
 110        stampit();
 111        bfin_write_WDOG_CTL(WDEN_ENABLE | ICTL_RESET);
 112        return 0;
 113}
 114
 115/**
 116 *      bfin_wdt_running - Check Watchdog status
 117 *
 118 *      See if the watchdog is running.
 119 */
 120static int bfin_wdt_running(void)
 121{
 122        stampit();
 123        return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE);
 124}
 125
 126/**
 127 *      bfin_wdt_set_timeout - Set the Userspace Watchdog timeout
 128 *      @t: new timeout value (in seconds)
 129 *
 130 *      Translate the specified timeout in seconds into System Clock
 131 *      terms which is what the on-chip Watchdog requires.
 132 */
 133static int bfin_wdt_set_timeout(unsigned long t)
 134{
 135        u32 cnt;
 136        unsigned long flags;
 137
 138        stampit();
 139
 140        cnt = t * get_sclk();
 141        if (cnt < get_sclk()) {
 142                printk(KERN_WARNING PFX "timeout value is too large\n");
 143                return -EINVAL;
 144        }
 145
 146        spin_lock_irqsave(&bfin_wdt_spinlock, flags);
 147        {
 148                int run = bfin_wdt_running();
 149                bfin_wdt_stop();
 150                bfin_write_WDOG_CNT(cnt);
 151                if (run)
 152                        bfin_wdt_start();
 153        }
 154        spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
 155
 156        timeout = t;
 157
 158        return 0;
 159}
 160
 161/**
 162 *      bfin_wdt_open - Open the Device
 163 *      @inode: inode of device
 164 *      @file: file handle of device
 165 *
 166 *      Watchdog device is opened and started.
 167 */
 168static int bfin_wdt_open(struct inode *inode, struct file *file)
 169{
 170        stampit();
 171
 172        if (test_and_set_bit(0, &open_check))
 173                return -EBUSY;
 174
 175        if (nowayout)
 176                __module_get(THIS_MODULE);
 177
 178        bfin_wdt_keepalive();
 179        bfin_wdt_start();
 180
 181        return nonseekable_open(inode, file);
 182}
 183
 184/**
 185 *      bfin_wdt_close - Close the Device
 186 *      @inode: inode of device
 187 *      @file: file handle of device
 188 *
 189 *      Watchdog device is closed and stopped.
 190 */
 191static int bfin_wdt_release(struct inode *inode, struct file *file)
 192{
 193        stampit();
 194
 195        if (expect_close == 42)
 196                bfin_wdt_stop();
 197        else {
 198                printk(KERN_CRIT PFX
 199                        "Unexpected close, not stopping watchdog!\n");
 200                bfin_wdt_keepalive();
 201        }
 202        expect_close = 0;
 203        clear_bit(0, &open_check);
 204        return 0;
 205}
 206
 207/**
 208 *      bfin_wdt_write - Write to Device
 209 *      @file: file handle of device
 210 *      @buf: buffer to write
 211 *      @count: length of buffer
 212 *      @ppos: offset
 213 *
 214 *      Pings the watchdog on write.
 215 */
 216static ssize_t bfin_wdt_write(struct file *file, const char __user *data,
 217                                                size_t len, loff_t *ppos)
 218{
 219        stampit();
 220
 221        if (len) {
 222                if (!nowayout) {
 223                        size_t i;
 224
 225                        /* In case it was set long ago */
 226                        expect_close = 0;
 227
 228                        for (i = 0; i != len; i++) {
 229                                char c;
 230                                if (get_user(c, data + i))
 231                                        return -EFAULT;
 232                                if (c == 'V')
 233                                        expect_close = 42;
 234                        }
 235                }
 236                bfin_wdt_keepalive();
 237        }
 238
 239        return len;
 240}
 241
 242/**
 243 *      bfin_wdt_ioctl - Query Device
 244 *      @file: file handle of device
 245 *      @cmd: watchdog command
 246 *      @arg: argument
 247 *
 248 *      Query basic information from the device or ping it, as outlined by the
 249 *      watchdog API.
 250 */
 251static long bfin_wdt_ioctl(struct file *file,
 252                                unsigned int cmd, unsigned long arg)
 253{
 254        void __user *argp = (void __user *)arg;
 255        int __user *p = argp;
 256
 257        stampit();
 258
 259        switch (cmd) {
 260        case WDIOC_GETSUPPORT:
 261                if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info)))
 262                        return -EFAULT;
 263                else
 264                        return 0;
 265        case WDIOC_GETSTATUS:
 266        case WDIOC_GETBOOTSTATUS:
 267                return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p);
 268        case WDIOC_SETOPTIONS: {
 269                unsigned long flags;
 270                int options, ret = -EINVAL;
 271
 272                if (get_user(options, p))
 273                        return -EFAULT;
 274
 275                spin_lock_irqsave(&bfin_wdt_spinlock, flags);
 276                if (options & WDIOS_DISABLECARD) {
 277                        bfin_wdt_stop();
 278                        ret = 0;
 279                }
 280                if (options & WDIOS_ENABLECARD) {
 281                        bfin_wdt_start();
 282                        ret = 0;
 283                }
 284                spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
 285                return ret;
 286        }
 287        case WDIOC_KEEPALIVE:
 288                bfin_wdt_keepalive();
 289                return 0;
 290        case WDIOC_SETTIMEOUT: {
 291                int new_timeout;
 292
 293                if (get_user(new_timeout, p))
 294                        return -EFAULT;
 295                if (bfin_wdt_set_timeout(new_timeout))
 296                        return -EINVAL;
 297        }
 298        /* Fall */
 299        case WDIOC_GETTIMEOUT:
 300                return put_user(timeout, p);
 301        default:
 302                return -ENOTTY;
 303        }
 304}
 305
 306/**
 307 *      bfin_wdt_notify_sys - Notifier Handler
 308 *      @this: notifier block
 309 *      @code: notifier event
 310 *      @unused: unused
 311 *
 312 *      Handles specific events, such as turning off the watchdog during a
 313 *      shutdown event.
 314 */
 315static int bfin_wdt_notify_sys(struct notifier_block *this,
 316                                        unsigned long code, void *unused)
 317{
 318        stampit();
 319
 320        if (code == SYS_DOWN || code == SYS_HALT)
 321                bfin_wdt_stop();
 322
 323        return NOTIFY_DONE;
 324}
 325
 326#ifdef CONFIG_PM
 327static int state_before_suspend;
 328
 329/**
 330 *      bfin_wdt_suspend - suspend the watchdog
 331 *      @pdev: device being suspended
 332 *      @state: requested suspend state
 333 *
 334 *      Remember if the watchdog was running and stop it.
 335 *      TODO: is this even right?  Doesn't seem to be any
 336 *            standard in the watchdog world ...
 337 */
 338static int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state)
 339{
 340        stampit();
 341
 342        state_before_suspend = bfin_wdt_running();
 343        bfin_wdt_stop();
 344
 345        return 0;
 346}
 347
 348/**
 349 *      bfin_wdt_resume - resume the watchdog
 350 *      @pdev: device being resumed
 351 *
 352 *      If the watchdog was running, turn it back on.
 353 */
 354static int bfin_wdt_resume(struct platform_device *pdev)
 355{
 356        stampit();
 357
 358        if (state_before_suspend) {
 359                bfin_wdt_set_timeout(timeout);
 360                bfin_wdt_start();
 361        }
 362
 363        return 0;
 364}
 365#else
 366# define bfin_wdt_suspend NULL
 367# define bfin_wdt_resume NULL
 368#endif
 369
 370static const struct file_operations bfin_wdt_fops = {
 371        .owner          = THIS_MODULE,
 372        .llseek         = no_llseek,
 373        .write          = bfin_wdt_write,
 374        .unlocked_ioctl = bfin_wdt_ioctl,
 375        .open           = bfin_wdt_open,
 376        .release        = bfin_wdt_release,
 377};
 378
 379static struct miscdevice bfin_wdt_miscdev = {
 380        .minor    = WATCHDOG_MINOR,
 381        .name     = "watchdog",
 382        .fops     = &bfin_wdt_fops,
 383};
 384
 385static struct watchdog_info bfin_wdt_info = {
 386        .identity = "Blackfin Watchdog",
 387        .options  = WDIOF_SETTIMEOUT |
 388                    WDIOF_KEEPALIVEPING |
 389                    WDIOF_MAGICCLOSE,
 390};
 391
 392static struct notifier_block bfin_wdt_notifier = {
 393        .notifier_call = bfin_wdt_notify_sys,
 394};
 395
 396/**
 397 *      bfin_wdt_probe - Initialize module
 398 *
 399 *      Registers the misc device and notifier handler.  Actual device
 400 *      initialization is handled by bfin_wdt_open().
 401 */
 402static int __devinit bfin_wdt_probe(struct platform_device *pdev)
 403{
 404        int ret;
 405
 406        ret = register_reboot_notifier(&bfin_wdt_notifier);
 407        if (ret) {
 408                pr_devinit(KERN_ERR PFX
 409                        "cannot register reboot notifier (err=%d)\n", ret);
 410                return ret;
 411        }
 412
 413        ret = misc_register(&bfin_wdt_miscdev);
 414        if (ret) {
 415                pr_devinit(KERN_ERR PFX
 416                        "cannot register miscdev on minor=%d (err=%d)\n",
 417                                WATCHDOG_MINOR, ret);
 418                unregister_reboot_notifier(&bfin_wdt_notifier);
 419                return ret;
 420        }
 421
 422        pr_devinit(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n",
 423               timeout, nowayout);
 424
 425        return 0;
 426}
 427
 428/**
 429 *      bfin_wdt_remove - Initialize module
 430 *
 431 *      Unregisters the misc device and notifier handler.  Actual device
 432 *      deinitialization is handled by bfin_wdt_close().
 433 */
 434static int __devexit bfin_wdt_remove(struct platform_device *pdev)
 435{
 436        misc_deregister(&bfin_wdt_miscdev);
 437        unregister_reboot_notifier(&bfin_wdt_notifier);
 438        return 0;
 439}
 440
 441static struct platform_device *bfin_wdt_device;
 442
 443static struct platform_driver bfin_wdt_driver = {
 444        .probe     = bfin_wdt_probe,
 445        .remove    = __devexit_p(bfin_wdt_remove),
 446        .suspend   = bfin_wdt_suspend,
 447        .resume    = bfin_wdt_resume,
 448        .driver    = {
 449                .name  = WATCHDOG_NAME,
 450                .owner = THIS_MODULE,
 451        },
 452};
 453
 454/**
 455 *      bfin_wdt_init - Initialize module
 456 *
 457 *      Checks the module params and registers the platform device & driver.
 458 *      Real work is in the platform probe function.
 459 */
 460static int __init bfin_wdt_init(void)
 461{
 462        int ret;
 463
 464        stampit();
 465
 466        /* Check that the timeout value is within range */
 467        if (bfin_wdt_set_timeout(timeout))
 468                return -EINVAL;
 469
 470        /* Since this is an on-chip device and needs no board-specific
 471         * resources, we'll handle all the platform device stuff here.
 472         */
 473        ret = platform_driver_register(&bfin_wdt_driver);
 474        if (ret) {
 475                pr_init(KERN_ERR PFX "unable to register driver\n");
 476                return ret;
 477        }
 478
 479        bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME, -1, NULL, 0);
 480        if (IS_ERR(bfin_wdt_device)) {
 481                pr_init(KERN_ERR PFX "unable to register device\n");
 482                platform_driver_unregister(&bfin_wdt_driver);
 483                return PTR_ERR(bfin_wdt_device);
 484        }
 485
 486        return 0;
 487}
 488
 489/**
 490 *      bfin_wdt_exit - Deinitialize module
 491 *
 492 *      Back out the platform device & driver steps.  Real work is in the
 493 *      platform remove function.
 494 */
 495static void __exit bfin_wdt_exit(void)
 496{
 497        platform_device_unregister(bfin_wdt_device);
 498        platform_driver_unregister(&bfin_wdt_driver);
 499}
 500
 501module_init(bfin_wdt_init);
 502module_exit(bfin_wdt_exit);
 503
 504MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>");
 505MODULE_DESCRIPTION("Blackfin Watchdog Device Driver");
 506MODULE_LICENSE("GPL");
 507MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 508
 509module_param(timeout, uint, 0);
 510MODULE_PARM_DESC(timeout,
 511        "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default="
 512                __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
 513
 514module_param(nowayout, int, 0);
 515MODULE_PARM_DESC(nowayout,
 516        "Watchdog cannot be stopped once started (default="
 517                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 518