root/drivers/watchdog/ibmasr.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __asr_toggle
  2. asr_toggle
  3. asr_enable
  4. asr_disable
  5. asr_get_base_address
  6. asr_write
  7. asr_ioctl
  8. asr_open
  9. asr_release
  10. ibmasr_init
  11. ibmasr_exit

   1 /*
   2  * IBM Automatic Server Restart driver.
   3  *
   4  * Copyright (c) 2005 Andrey Panin <pazke@donpac.ru>
   5  *
   6  * Based on driver written by Pete Reynolds.
   7  * Copyright (c) IBM Corporation, 1998-2004.
   8  *
   9  * This software may be used and distributed according to the terms
  10  * of the GNU Public License, incorporated herein by reference.
  11  */
  12 
  13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14 
  15 #include <linux/fs.h>
  16 #include <linux/kernel.h>
  17 #include <linux/module.h>
  18 #include <linux/pci.h>
  19 #include <linux/timer.h>
  20 #include <linux/miscdevice.h>
  21 #include <linux/watchdog.h>
  22 #include <linux/dmi.h>
  23 #include <linux/io.h>
  24 #include <linux/uaccess.h>
  25 
  26 
  27 enum {
  28         ASMTYPE_UNKNOWN,
  29         ASMTYPE_TOPAZ,
  30         ASMTYPE_JASPER,
  31         ASMTYPE_PEARL,
  32         ASMTYPE_JUNIPER,
  33         ASMTYPE_SPRUCE,
  34 };
  35 
  36 #define TOPAZ_ASR_REG_OFFSET    4
  37 #define TOPAZ_ASR_TOGGLE        0x40
  38 #define TOPAZ_ASR_DISABLE       0x80
  39 
  40 /* PEARL ASR S/W REGISTER SUPERIO PORT ADDRESSES */
  41 #define PEARL_BASE      0xe04
  42 #define PEARL_WRITE     0xe06
  43 #define PEARL_READ      0xe07
  44 
  45 #define PEARL_ASR_DISABLE_MASK  0x80    /* bit 7: disable = 1, enable = 0 */
  46 #define PEARL_ASR_TOGGLE_MASK   0x40    /* bit 6: 0, then 1, then 0 */
  47 
  48 /* JASPER OFFSET FROM SIO BASE ADDR TO ASR S/W REGISTERS. */
  49 #define JASPER_ASR_REG_OFFSET   0x38
  50 
  51 #define JASPER_ASR_DISABLE_MASK 0x01    /* bit 0: disable = 1, enable = 0 */
  52 #define JASPER_ASR_TOGGLE_MASK  0x02    /* bit 1: 0, then 1, then 0 */
  53 
  54 #define JUNIPER_BASE_ADDRESS    0x54b   /* Base address of Juniper ASR */
  55 #define JUNIPER_ASR_DISABLE_MASK 0x01   /* bit 0: disable = 1 enable = 0 */
  56 #define JUNIPER_ASR_TOGGLE_MASK 0x02    /* bit 1: 0, then 1, then 0 */
  57 
  58 #define SPRUCE_BASE_ADDRESS     0x118e  /* Base address of Spruce ASR */
  59 #define SPRUCE_ASR_DISABLE_MASK 0x01    /* bit 1: disable = 1 enable = 0 */
  60 #define SPRUCE_ASR_TOGGLE_MASK  0x02    /* bit 0: 0, then 1, then 0 */
  61 
  62 
  63 static bool nowayout = WATCHDOG_NOWAYOUT;
  64 
  65 static unsigned long asr_is_open;
  66 static char asr_expect_close;
  67 
  68 static unsigned int asr_type, asr_base, asr_length;
  69 static unsigned int asr_read_addr, asr_write_addr;
  70 static unsigned char asr_toggle_mask, asr_disable_mask;
  71 static DEFINE_SPINLOCK(asr_lock);
  72 
  73 static void __asr_toggle(void)
  74 {
  75         unsigned char reg;
  76 
  77         reg = inb(asr_read_addr);
  78 
  79         outb(reg & ~asr_toggle_mask, asr_write_addr);
  80         reg = inb(asr_read_addr);
  81 
  82         outb(reg | asr_toggle_mask, asr_write_addr);
  83         reg = inb(asr_read_addr);
  84 
  85         outb(reg & ~asr_toggle_mask, asr_write_addr);
  86         reg = inb(asr_read_addr);
  87 }
  88 
  89 static void asr_toggle(void)
  90 {
  91         spin_lock(&asr_lock);
  92         __asr_toggle();
  93         spin_unlock(&asr_lock);
  94 }
  95 
  96 static void asr_enable(void)
  97 {
  98         unsigned char reg;
  99 
 100         spin_lock(&asr_lock);
 101         if (asr_type == ASMTYPE_TOPAZ) {
 102                 /* asr_write_addr == asr_read_addr */
 103                 reg = inb(asr_read_addr);
 104                 outb(reg & ~(TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE),
 105                      asr_read_addr);
 106         } else {
 107                 /*
 108                  * First make sure the hardware timer is reset by toggling
 109                  * ASR hardware timer line.
 110                  */
 111                 __asr_toggle();
 112 
 113                 reg = inb(asr_read_addr);
 114                 outb(reg & ~asr_disable_mask, asr_write_addr);
 115         }
 116         reg = inb(asr_read_addr);
 117         spin_unlock(&asr_lock);
 118 }
 119 
 120 static void asr_disable(void)
 121 {
 122         unsigned char reg;
 123 
 124         spin_lock(&asr_lock);
 125         reg = inb(asr_read_addr);
 126 
 127         if (asr_type == ASMTYPE_TOPAZ)
 128                 /* asr_write_addr == asr_read_addr */
 129                 outb(reg | TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE,
 130                      asr_read_addr);
 131         else {
 132                 outb(reg | asr_toggle_mask, asr_write_addr);
 133                 reg = inb(asr_read_addr);
 134 
 135                 outb(reg | asr_disable_mask, asr_write_addr);
 136         }
 137         reg = inb(asr_read_addr);
 138         spin_unlock(&asr_lock);
 139 }
 140 
 141 static int __init asr_get_base_address(void)
 142 {
 143         unsigned char low, high;
 144         const char *type = "";
 145 
 146         asr_length = 1;
 147 
 148         switch (asr_type) {
 149         case ASMTYPE_TOPAZ:
 150                 /* SELECT SuperIO CHIP FOR QUERYING
 151                    (WRITE 0x07 TO BOTH 0x2E and 0x2F) */
 152                 outb(0x07, 0x2e);
 153                 outb(0x07, 0x2f);
 154 
 155                 /* SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS */
 156                 outb(0x60, 0x2e);
 157                 high = inb(0x2f);
 158 
 159                 /* SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS */
 160                 outb(0x61, 0x2e);
 161                 low = inb(0x2f);
 162 
 163                 asr_base = (high << 16) | low;
 164                 asr_read_addr = asr_write_addr =
 165                         asr_base + TOPAZ_ASR_REG_OFFSET;
 166                 asr_length = 5;
 167 
 168                 break;
 169 
 170         case ASMTYPE_JASPER:
 171                 type = "Jaspers ";
 172 #if 0
 173                 u32 r;
 174                 /* Suggested fix */
 175                 pdev = pci_get_bus_and_slot(0, DEVFN(0x1f, 0));
 176                 if (pdev == NULL)
 177                         return -ENODEV;
 178                 pci_read_config_dword(pdev, 0x58, &r);
 179                 asr_base = r & 0xFFFE;
 180                 pci_dev_put(pdev);
 181 #else
 182                 /* FIXME: need to use pci_config_lock here,
 183                    but it's not exported */
 184 
 185 /*              spin_lock_irqsave(&pci_config_lock, flags);*/
 186 
 187                 /* Select the SuperIO chip in the PCI I/O port register */
 188                 outl(0x8000f858, 0xcf8);
 189 
 190                 /* BUS 0, Slot 1F, fnc 0, offset 58 */
 191 
 192                 /*
 193                  * Read the base address for the SuperIO chip.
 194                  * Only the lower 16 bits are valid, but the address is word
 195                  * aligned so the last bit must be masked off.
 196                  */
 197                 asr_base = inl(0xcfc) & 0xfffe;
 198 
 199 /*              spin_unlock_irqrestore(&pci_config_lock, flags);*/
 200 #endif
 201                 asr_read_addr = asr_write_addr =
 202                         asr_base + JASPER_ASR_REG_OFFSET;
 203                 asr_toggle_mask = JASPER_ASR_TOGGLE_MASK;
 204                 asr_disable_mask = JASPER_ASR_DISABLE_MASK;
 205                 asr_length = JASPER_ASR_REG_OFFSET + 1;
 206 
 207                 break;
 208 
 209         case ASMTYPE_PEARL:
 210                 type = "Pearls ";
 211                 asr_base = PEARL_BASE;
 212                 asr_read_addr = PEARL_READ;
 213                 asr_write_addr = PEARL_WRITE;
 214                 asr_toggle_mask = PEARL_ASR_TOGGLE_MASK;
 215                 asr_disable_mask = PEARL_ASR_DISABLE_MASK;
 216                 asr_length = 4;
 217                 break;
 218 
 219         case ASMTYPE_JUNIPER:
 220                 type = "Junipers ";
 221                 asr_base = JUNIPER_BASE_ADDRESS;
 222                 asr_read_addr = asr_write_addr = asr_base;
 223                 asr_toggle_mask = JUNIPER_ASR_TOGGLE_MASK;
 224                 asr_disable_mask = JUNIPER_ASR_DISABLE_MASK;
 225                 break;
 226 
 227         case ASMTYPE_SPRUCE:
 228                 type = "Spruce's ";
 229                 asr_base = SPRUCE_BASE_ADDRESS;
 230                 asr_read_addr = asr_write_addr = asr_base;
 231                 asr_toggle_mask = SPRUCE_ASR_TOGGLE_MASK;
 232                 asr_disable_mask = SPRUCE_ASR_DISABLE_MASK;
 233                 break;
 234         }
 235 
 236         if (!request_region(asr_base, asr_length, "ibmasr")) {
 237                 pr_err("address %#x already in use\n", asr_base);
 238                 return -EBUSY;
 239         }
 240 
 241         pr_info("found %sASR @ addr %#x\n", type, asr_base);
 242 
 243         return 0;
 244 }
 245 
 246 
 247 static ssize_t asr_write(struct file *file, const char __user *buf,
 248                          size_t count, loff_t *ppos)
 249 {
 250         if (count) {
 251                 if (!nowayout) {
 252                         size_t i;
 253 
 254                         /* In case it was set long ago */
 255                         asr_expect_close = 0;
 256 
 257                         for (i = 0; i != count; i++) {
 258                                 char c;
 259                                 if (get_user(c, buf + i))
 260                                         return -EFAULT;
 261                                 if (c == 'V')
 262                                         asr_expect_close = 42;
 263                         }
 264                 }
 265                 asr_toggle();
 266         }
 267         return count;
 268 }
 269 
 270 static long asr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 271 {
 272         static const struct watchdog_info ident = {
 273                 .options =      WDIOF_KEEPALIVEPING |
 274                                 WDIOF_MAGICCLOSE,
 275                 .identity =     "IBM ASR",
 276         };
 277         void __user *argp = (void __user *)arg;
 278         int __user *p = argp;
 279         int heartbeat;
 280 
 281         switch (cmd) {
 282         case WDIOC_GETSUPPORT:
 283                 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
 284         case WDIOC_GETSTATUS:
 285         case WDIOC_GETBOOTSTATUS:
 286                 return put_user(0, p);
 287         case WDIOC_SETOPTIONS:
 288         {
 289                 int new_options, retval = -EINVAL;
 290                 if (get_user(new_options, p))
 291                         return -EFAULT;
 292                 if (new_options & WDIOS_DISABLECARD) {
 293                         asr_disable();
 294                         retval = 0;
 295                 }
 296                 if (new_options & WDIOS_ENABLECARD) {
 297                         asr_enable();
 298                         asr_toggle();
 299                         retval = 0;
 300                 }
 301                 return retval;
 302         }
 303         case WDIOC_KEEPALIVE:
 304                 asr_toggle();
 305                 return 0;
 306         /*
 307          * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT
 308          * and WDIOC_GETTIMEOUT always returns 256.
 309          */
 310         case WDIOC_GETTIMEOUT:
 311                 heartbeat = 256;
 312                 return put_user(heartbeat, p);
 313         default:
 314                 return -ENOTTY;
 315         }
 316 }
 317 
 318 static int asr_open(struct inode *inode, struct file *file)
 319 {
 320         if (test_and_set_bit(0, &asr_is_open))
 321                 return -EBUSY;
 322 
 323         asr_toggle();
 324         asr_enable();
 325 
 326         return stream_open(inode, file);
 327 }
 328 
 329 static int asr_release(struct inode *inode, struct file *file)
 330 {
 331         if (asr_expect_close == 42)
 332                 asr_disable();
 333         else {
 334                 pr_crit("unexpected close, not stopping watchdog!\n");
 335                 asr_toggle();
 336         }
 337         clear_bit(0, &asr_is_open);
 338         asr_expect_close = 0;
 339         return 0;
 340 }
 341 
 342 static const struct file_operations asr_fops = {
 343         .owner =                THIS_MODULE,
 344         .llseek =               no_llseek,
 345         .write =                asr_write,
 346         .unlocked_ioctl =       asr_ioctl,
 347         .open =                 asr_open,
 348         .release =              asr_release,
 349 };
 350 
 351 static struct miscdevice asr_miscdev = {
 352         .minor =        WATCHDOG_MINOR,
 353         .name =         "watchdog",
 354         .fops =         &asr_fops,
 355 };
 356 
 357 
 358 struct ibmasr_id {
 359         const char *desc;
 360         int type;
 361 };
 362 
 363 static struct ibmasr_id ibmasr_id_table[] __initdata = {
 364         { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ },
 365         { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL },
 366         { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER },
 367         { "IBM Automatic Server Restart - Machine Type 8482", ASMTYPE_JUNIPER },
 368         { "IBM Automatic Server Restart - Machine Type 8648", ASMTYPE_SPRUCE },
 369         { NULL }
 370 };
 371 
 372 static int __init ibmasr_init(void)
 373 {
 374         struct ibmasr_id *id;
 375         int rc;
 376 
 377         for (id = ibmasr_id_table; id->desc; id++) {
 378                 if (dmi_find_device(DMI_DEV_TYPE_OTHER, id->desc, NULL)) {
 379                         asr_type = id->type;
 380                         break;
 381                 }
 382         }
 383 
 384         if (!asr_type)
 385                 return -ENODEV;
 386 
 387         rc = asr_get_base_address();
 388         if (rc)
 389                 return rc;
 390 
 391         rc = misc_register(&asr_miscdev);
 392         if (rc < 0) {
 393                 release_region(asr_base, asr_length);
 394                 pr_err("failed to register misc device\n");
 395                 return rc;
 396         }
 397 
 398         return 0;
 399 }
 400 
 401 static void __exit ibmasr_exit(void)
 402 {
 403         if (!nowayout)
 404                 asr_disable();
 405 
 406         misc_deregister(&asr_miscdev);
 407 
 408         release_region(asr_base, asr_length);
 409 }
 410 
 411 module_init(ibmasr_init);
 412 module_exit(ibmasr_exit);
 413 
 414 module_param(nowayout, bool, 0);
 415 MODULE_PARM_DESC(nowayout,
 416         "Watchdog cannot be stopped once started (default="
 417                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 418 
 419 MODULE_DESCRIPTION("IBM Automatic Server Restart driver");
 420 MODULE_AUTHOR("Andrey Panin");
 421 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */