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