linux/drivers/watchdog/pc87413_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *      NS pc87413-wdt Watchdog Timer driver for Linux 2.6.x.x
   4 *
   5 *      This code is based on wdt.c with original copyright.
   6 *
   7 *      (C) Copyright 2006 Sven Anders, <anders@anduras.de>
   8 *                     and Marcus Junker, <junker@anduras.de>
   9 *
  10 *      Neither Sven Anders, Marcus Junker nor ANDURAS AG
  11 *      admit liability nor provide warranty for any of this software.
  12 *      This material is provided "AS-IS" and at no charge.
  13 *
  14 *      Release 1.1
  15 */
  16
  17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  18
  19#include <linux/module.h>
  20#include <linux/types.h>
  21#include <linux/miscdevice.h>
  22#include <linux/watchdog.h>
  23#include <linux/ioport.h>
  24#include <linux/delay.h>
  25#include <linux/notifier.h>
  26#include <linux/fs.h>
  27#include <linux/reboot.h>
  28#include <linux/init.h>
  29#include <linux/spinlock.h>
  30#include <linux/moduleparam.h>
  31#include <linux/io.h>
  32#include <linux/uaccess.h>
  33
  34
  35/* #define DEBUG 1 */
  36
  37#define DEFAULT_TIMEOUT     1           /* 1 minute */
  38#define MAX_TIMEOUT         255
  39
  40#define VERSION             "1.1"
  41#define MODNAME             "pc87413 WDT"
  42#define DPFX                MODNAME " - DEBUG: "
  43
  44#define WDT_INDEX_IO_PORT   (io+0)      /* I/O port base (index register) */
  45#define WDT_DATA_IO_PORT    (WDT_INDEX_IO_PORT+1)
  46#define SWC_LDN             0x04
  47#define SIOCFG2             0x22        /* Serial IO register */
  48#define WDCTL               0x10        /* Watchdog-Timer-Control-Register */
  49#define WDTO                0x11        /* Watchdog timeout register */
  50#define WDCFG               0x12        /* Watchdog config register */
  51
  52#define IO_DEFAULT      0x2E            /* Address used on Portwell Boards */
  53
  54static int io = IO_DEFAULT;
  55static int swc_base_addr = -1;
  56
  57static int timeout = DEFAULT_TIMEOUT;   /* timeout value */
  58static unsigned long timer_enabled;     /* is the timer enabled? */
  59
  60static char expect_close;               /* is the close expected? */
  61
  62static DEFINE_SPINLOCK(io_lock);        /* to guard us from io races */
  63
  64static bool nowayout = WATCHDOG_NOWAYOUT;
  65
  66/* -- Low level function ----------------------------------------*/
  67
  68/* Select pins for Watchdog output */
  69
  70static inline void pc87413_select_wdt_out(void)
  71{
  72        unsigned int cr_data = 0;
  73
  74        /* Step 1: Select multiple pin,pin55,as WDT output */
  75
  76        outb_p(SIOCFG2, WDT_INDEX_IO_PORT);
  77
  78        cr_data = inb(WDT_DATA_IO_PORT);
  79
  80        cr_data |= 0x80; /* Set Bit7 to 1*/
  81        outb_p(SIOCFG2, WDT_INDEX_IO_PORT);
  82
  83        outb_p(cr_data, WDT_DATA_IO_PORT);
  84
  85#ifdef DEBUG
  86        pr_info(DPFX
  87                "Select multiple pin,pin55,as WDT output: Bit7 to 1: %d\n",
  88                                                                cr_data);
  89#endif
  90}
  91
  92/* Enable SWC functions */
  93
  94static inline void pc87413_enable_swc(void)
  95{
  96        unsigned int cr_data = 0;
  97
  98        /* Step 2: Enable SWC functions */
  99
 100        outb_p(0x07, WDT_INDEX_IO_PORT);        /* Point SWC_LDN (LDN=4) */
 101        outb_p(SWC_LDN, WDT_DATA_IO_PORT);
 102
 103        outb_p(0x30, WDT_INDEX_IO_PORT);        /* Read Index 0x30 First */
 104        cr_data = inb(WDT_DATA_IO_PORT);
 105        cr_data |= 0x01;                        /* Set Bit0 to 1 */
 106        outb_p(0x30, WDT_INDEX_IO_PORT);
 107        outb_p(cr_data, WDT_DATA_IO_PORT);      /* Index0x30_bit0P1 */
 108
 109#ifdef DEBUG
 110        pr_info(DPFX "pc87413 - Enable SWC functions\n");
 111#endif
 112}
 113
 114/* Read SWC I/O base address */
 115
 116static void pc87413_get_swc_base_addr(void)
 117{
 118        unsigned char addr_l, addr_h = 0;
 119
 120        /* Step 3: Read SWC I/O Base Address */
 121
 122        outb_p(0x60, WDT_INDEX_IO_PORT);        /* Read Index 0x60 */
 123        addr_h = inb(WDT_DATA_IO_PORT);
 124
 125        outb_p(0x61, WDT_INDEX_IO_PORT);        /* Read Index 0x61 */
 126
 127        addr_l = inb(WDT_DATA_IO_PORT);
 128
 129        swc_base_addr = (addr_h << 8) + addr_l;
 130#ifdef DEBUG
 131        pr_info(DPFX
 132                "Read SWC I/O Base Address: low %d, high %d, res %d\n",
 133                                                addr_l, addr_h, swc_base_addr);
 134#endif
 135}
 136
 137/* Select Bank 3 of SWC */
 138
 139static inline void pc87413_swc_bank3(void)
 140{
 141        /* Step 4: Select Bank3 of SWC */
 142        outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f);
 143#ifdef DEBUG
 144        pr_info(DPFX "Select Bank3 of SWC\n");
 145#endif
 146}
 147
 148/* Set watchdog timeout to x minutes */
 149
 150static inline void pc87413_programm_wdto(char pc87413_time)
 151{
 152        /* Step 5: Programm WDTO, Twd. */
 153        outb_p(pc87413_time, swc_base_addr + WDTO);
 154#ifdef DEBUG
 155        pr_info(DPFX "Set WDTO to %d minutes\n", pc87413_time);
 156#endif
 157}
 158
 159/* Enable WDEN */
 160
 161static inline void pc87413_enable_wden(void)
 162{
 163        /* Step 6: Enable WDEN */
 164        outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL);
 165#ifdef DEBUG
 166        pr_info(DPFX "Enable WDEN\n");
 167#endif
 168}
 169
 170/* Enable SW_WD_TREN */
 171static inline void pc87413_enable_sw_wd_tren(void)
 172{
 173        /* Enable SW_WD_TREN */
 174        outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG);
 175#ifdef DEBUG
 176        pr_info(DPFX "Enable SW_WD_TREN\n");
 177#endif
 178}
 179
 180/* Disable SW_WD_TREN */
 181
 182static inline void pc87413_disable_sw_wd_tren(void)
 183{
 184        /* Disable SW_WD_TREN */
 185        outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG);
 186#ifdef DEBUG
 187        pr_info(DPFX "pc87413 - Disable SW_WD_TREN\n");
 188#endif
 189}
 190
 191/* Enable SW_WD_TRG */
 192
 193static inline void pc87413_enable_sw_wd_trg(void)
 194{
 195        /* Enable SW_WD_TRG */
 196        outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL);
 197#ifdef DEBUG
 198        pr_info(DPFX "pc87413 - Enable SW_WD_TRG\n");
 199#endif
 200}
 201
 202/* Disable SW_WD_TRG */
 203
 204static inline void pc87413_disable_sw_wd_trg(void)
 205{
 206        /* Disable SW_WD_TRG */
 207        outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL);
 208#ifdef DEBUG
 209        pr_info(DPFX "Disable SW_WD_TRG\n");
 210#endif
 211}
 212
 213/* -- Higher level functions ------------------------------------*/
 214
 215/* Enable the watchdog */
 216
 217static void pc87413_enable(void)
 218{
 219        spin_lock(&io_lock);
 220
 221        pc87413_swc_bank3();
 222        pc87413_programm_wdto(timeout);
 223        pc87413_enable_wden();
 224        pc87413_enable_sw_wd_tren();
 225        pc87413_enable_sw_wd_trg();
 226
 227        spin_unlock(&io_lock);
 228}
 229
 230/* Disable the watchdog */
 231
 232static void pc87413_disable(void)
 233{
 234        spin_lock(&io_lock);
 235
 236        pc87413_swc_bank3();
 237        pc87413_disable_sw_wd_tren();
 238        pc87413_disable_sw_wd_trg();
 239        pc87413_programm_wdto(0);
 240
 241        spin_unlock(&io_lock);
 242}
 243
 244/* Refresh the watchdog */
 245
 246static void pc87413_refresh(void)
 247{
 248        spin_lock(&io_lock);
 249
 250        pc87413_swc_bank3();
 251        pc87413_disable_sw_wd_tren();
 252        pc87413_disable_sw_wd_trg();
 253        pc87413_programm_wdto(timeout);
 254        pc87413_enable_wden();
 255        pc87413_enable_sw_wd_tren();
 256        pc87413_enable_sw_wd_trg();
 257
 258        spin_unlock(&io_lock);
 259}
 260
 261/* -- File operations -------------------------------------------*/
 262
 263/**
 264 *      pc87413_open:
 265 *      @inode: inode of device
 266 *      @file: file handle to device
 267 *
 268 */
 269
 270static int pc87413_open(struct inode *inode, struct file *file)
 271{
 272        /* /dev/watchdog can only be opened once */
 273
 274        if (test_and_set_bit(0, &timer_enabled))
 275                return -EBUSY;
 276
 277        if (nowayout)
 278                __module_get(THIS_MODULE);
 279
 280        /* Reload and activate timer */
 281        pc87413_refresh();
 282
 283        pr_info("Watchdog enabled. Timeout set to %d minute(s).\n", timeout);
 284
 285        return stream_open(inode, file);
 286}
 287
 288/**
 289 *      pc87413_release:
 290 *      @inode: inode to board
 291 *      @file: file handle to board
 292 *
 293 *      The watchdog has a configurable API. There is a religious dispute
 294 *      between people who want their watchdog to be able to shut down and
 295 *      those who want to be sure if the watchdog manager dies the machine
 296 *      reboots. In the former case we disable the counters, in the latter
 297 *      case you have to open it again very soon.
 298 */
 299
 300static int pc87413_release(struct inode *inode, struct file *file)
 301{
 302        /* Shut off the timer. */
 303
 304        if (expect_close == 42) {
 305                pc87413_disable();
 306                pr_info("Watchdog disabled, sleeping again...\n");
 307        } else {
 308                pr_crit("Unexpected close, not stopping watchdog!\n");
 309                pc87413_refresh();
 310        }
 311        clear_bit(0, &timer_enabled);
 312        expect_close = 0;
 313        return 0;
 314}
 315
 316/**
 317 *      pc87413_status:
 318 *
 319 *      return, if the watchdog is enabled (timeout is set...)
 320 */
 321
 322
 323static int pc87413_status(void)
 324{
 325          return 0; /* currently not supported */
 326}
 327
 328/**
 329 *      pc87413_write:
 330 *      @file: file handle to the watchdog
 331 *      @data: data buffer to write
 332 *      @len: length in bytes
 333 *      @ppos: pointer to the position to write. No seeks allowed
 334 *
 335 *      A write to a watchdog device is defined as a keepalive signal. Any
 336 *      write of data will do, as we we don't define content meaning.
 337 */
 338
 339static ssize_t pc87413_write(struct file *file, const char __user *data,
 340                             size_t len, loff_t *ppos)
 341{
 342        /* See if we got the magic character 'V' and reload the timer */
 343        if (len) {
 344                if (!nowayout) {
 345                        size_t i;
 346
 347                        /* reset expect flag */
 348                        expect_close = 0;
 349
 350                        /* scan to see whether or not we got the
 351                           magic character */
 352                        for (i = 0; i != len; i++) {
 353                                char c;
 354                                if (get_user(c, data + i))
 355                                        return -EFAULT;
 356                                if (c == 'V')
 357                                        expect_close = 42;
 358                        }
 359                }
 360
 361                /* someone wrote to us, we should reload the timer */
 362                pc87413_refresh();
 363        }
 364        return len;
 365}
 366
 367/**
 368 *      pc87413_ioctl:
 369 *      @file: file handle to the device
 370 *      @cmd: watchdog command
 371 *      @arg: argument pointer
 372 *
 373 *      The watchdog API defines a common set of functions for all watchdogs
 374 *      according to their available features. We only actually usefully support
 375 *      querying capabilities and current status.
 376 */
 377
 378static long pc87413_ioctl(struct file *file, unsigned int cmd,
 379                                                unsigned long arg)
 380{
 381        int new_timeout;
 382
 383        union {
 384                struct watchdog_info __user *ident;
 385                int __user *i;
 386        } uarg;
 387
 388        static const struct watchdog_info ident = {
 389                .options          = WDIOF_KEEPALIVEPING |
 390                                    WDIOF_SETTIMEOUT |
 391                                    WDIOF_MAGICCLOSE,
 392                .firmware_version = 1,
 393                .identity         = "PC87413(HF/F) watchdog",
 394        };
 395
 396        uarg.i = (int __user *)arg;
 397
 398        switch (cmd) {
 399        case WDIOC_GETSUPPORT:
 400                return copy_to_user(uarg.ident, &ident,
 401                                        sizeof(ident)) ? -EFAULT : 0;
 402        case WDIOC_GETSTATUS:
 403                return put_user(pc87413_status(), uarg.i);
 404        case WDIOC_GETBOOTSTATUS:
 405                return put_user(0, uarg.i);
 406        case WDIOC_SETOPTIONS:
 407        {
 408                int options, retval = -EINVAL;
 409                if (get_user(options, uarg.i))
 410                        return -EFAULT;
 411                if (options & WDIOS_DISABLECARD) {
 412                        pc87413_disable();
 413                        retval = 0;
 414                }
 415                if (options & WDIOS_ENABLECARD) {
 416                        pc87413_enable();
 417                        retval = 0;
 418                }
 419                return retval;
 420        }
 421        case WDIOC_KEEPALIVE:
 422                pc87413_refresh();
 423#ifdef DEBUG
 424                pr_info(DPFX "keepalive\n");
 425#endif
 426                return 0;
 427        case WDIOC_SETTIMEOUT:
 428                if (get_user(new_timeout, uarg.i))
 429                        return -EFAULT;
 430                /* the API states this is given in secs */
 431                new_timeout /= 60;
 432                if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
 433                        return -EINVAL;
 434                timeout = new_timeout;
 435                pc87413_refresh();
 436                fallthrough;    /* and return the new timeout */
 437        case WDIOC_GETTIMEOUT:
 438                new_timeout = timeout * 60;
 439                return put_user(new_timeout, uarg.i);
 440        default:
 441                return -ENOTTY;
 442        }
 443}
 444
 445/* -- Notifier funtions -----------------------------------------*/
 446
 447/**
 448 *      notify_sys:
 449 *      @this: our notifier block
 450 *      @code: the event being reported
 451 *      @unused: unused
 452 *
 453 *      Our notifier is called on system shutdowns. We want to turn the card
 454 *      off at reboot otherwise the machine will reboot again during memory
 455 *      test or worse yet during the following fsck. This would suck, in fact
 456 *      trust me - if it happens it does suck.
 457 */
 458
 459static int pc87413_notify_sys(struct notifier_block *this,
 460                              unsigned long code,
 461                              void *unused)
 462{
 463        if (code == SYS_DOWN || code == SYS_HALT)
 464                /* Turn the card off */
 465                pc87413_disable();
 466        return NOTIFY_DONE;
 467}
 468
 469/* -- Module's structures ---------------------------------------*/
 470
 471static const struct file_operations pc87413_fops = {
 472        .owner          = THIS_MODULE,
 473        .llseek         = no_llseek,
 474        .write          = pc87413_write,
 475        .unlocked_ioctl = pc87413_ioctl,
 476        .compat_ioctl   = compat_ptr_ioctl,
 477        .open           = pc87413_open,
 478        .release        = pc87413_release,
 479};
 480
 481static struct notifier_block pc87413_notifier = {
 482        .notifier_call  = pc87413_notify_sys,
 483};
 484
 485static struct miscdevice pc87413_miscdev = {
 486        .minor          = WATCHDOG_MINOR,
 487        .name           = "watchdog",
 488        .fops           = &pc87413_fops,
 489};
 490
 491/* -- Module init functions -------------------------------------*/
 492
 493/**
 494 *      pc87413_init: module's "constructor"
 495 *
 496 *      Set up the WDT watchdog board. All we have to do is grab the
 497 *      resources we require and bitch if anyone beat us to them.
 498 *      The open() function will actually kick the board off.
 499 */
 500
 501static int __init pc87413_init(void)
 502{
 503        int ret;
 504
 505        pr_info("Version " VERSION " at io 0x%X\n",
 506                                                        WDT_INDEX_IO_PORT);
 507
 508        if (!request_muxed_region(io, 2, MODNAME))
 509                return -EBUSY;
 510
 511        ret = register_reboot_notifier(&pc87413_notifier);
 512        if (ret != 0)
 513                pr_err("cannot register reboot notifier (err=%d)\n", ret);
 514
 515        ret = misc_register(&pc87413_miscdev);
 516        if (ret != 0) {
 517                pr_err("cannot register miscdev on minor=%d (err=%d)\n",
 518                       WATCHDOG_MINOR, ret);
 519                goto reboot_unreg;
 520        }
 521        pr_info("initialized. timeout=%d min\n", timeout);
 522
 523        pc87413_select_wdt_out();
 524        pc87413_enable_swc();
 525        pc87413_get_swc_base_addr();
 526
 527        if (!request_region(swc_base_addr, 0x20, MODNAME)) {
 528                pr_err("cannot request SWC region at 0x%x\n", swc_base_addr);
 529                ret = -EBUSY;
 530                goto misc_unreg;
 531        }
 532
 533        pc87413_enable();
 534
 535        release_region(io, 2);
 536        return 0;
 537
 538misc_unreg:
 539        misc_deregister(&pc87413_miscdev);
 540reboot_unreg:
 541        unregister_reboot_notifier(&pc87413_notifier);
 542        release_region(io, 2);
 543        return ret;
 544}
 545
 546/**
 547 *      pc87413_exit: module's "destructor"
 548 *
 549 *      Unload the watchdog. You cannot do this with any file handles open.
 550 *      If your watchdog is set to continue ticking on close and you unload
 551 *      it, well it keeps ticking. We won't get the interrupt but the board
 552 *      will not touch PC memory so all is fine. You just have to load a new
 553 *      module in 60 seconds or reboot.
 554 */
 555
 556static void __exit pc87413_exit(void)
 557{
 558        /* Stop the timer before we leave */
 559        if (!nowayout) {
 560                pc87413_disable();
 561                pr_info("Watchdog disabled\n");
 562        }
 563
 564        misc_deregister(&pc87413_miscdev);
 565        unregister_reboot_notifier(&pc87413_notifier);
 566        release_region(swc_base_addr, 0x20);
 567
 568        pr_info("watchdog component driver removed\n");
 569}
 570
 571module_init(pc87413_init);
 572module_exit(pc87413_exit);
 573
 574MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
 575MODULE_AUTHOR("Marcus Junker <junker@anduras.de>");
 576MODULE_DESCRIPTION("PC87413 WDT driver");
 577MODULE_LICENSE("GPL");
 578
 579module_param_hw(io, int, ioport, 0);
 580MODULE_PARM_DESC(io, MODNAME " I/O port (default: "
 581                                        __MODULE_STRING(IO_DEFAULT) ").");
 582
 583module_param(timeout, int, 0);
 584MODULE_PARM_DESC(timeout,
 585                "Watchdog timeout in minutes (default="
 586                                __MODULE_STRING(DEFAULT_TIMEOUT) ").");
 587
 588module_param(nowayout, bool, 0);
 589MODULE_PARM_DESC(nowayout,
 590                "Watchdog cannot be stopped once started (default="
 591                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 592
 593