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