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