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