diff -urpNX ../dontdiff linux-ia64/Documentation/Configure.help linux-generic/Documentation/Configure.help --- linux-ia64/Documentation/Configure.help Tue Aug 27 12:59:18 2002 +++ linux-generic/Documentation/Configure.help Tue Aug 27 09:25:08 2002 @@ -3935,6 +3941,14 @@ CONFIG_ENVCTRL inserted in and removed from the running kernel whenever you want). The module will be called envctrl.o. If you want to compile it as a module, say M here and read . + +Baseboard Management Controller +CONFIG_BMC + Support for the Baseboard Management Controller. This controller + interprets IPMI commands which can do things like change your + fan speed, log information into NVRAM or report CPU temperature. + + If you are unsure, say N # Choice: x86type Processor family diff -urpNX ../dontdiff linux-ia64/arch/i386/config.in linux-generic/arch/i386/config.in --- linux-ia64/arch/i386/config.in Tue Aug 27 12:59:23 2002 +++ linux-generic/arch/i386/config.in Tue Aug 27 09:25:10 2002 @@ -293,6 +293,8 @@ fi endmenu +source drivers/misc/Config.in + source drivers/mtd/Config.in source drivers/parport/Config.in diff -urpNX ../dontdiff linux-ia64/arch/ia64/config.in linux-generic/arch/ia64/config.in --- linux-ia64/arch/ia64/config.in Tue Aug 27 12:59:24 2002 +++ linux-generic/arch/ia64/config.in Tue Aug 27 09:25:10 2002 @@ -104,6 +98,8 @@ bool 'BSD Process Accounting' CONFIG_BSD bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC + +source drivers/misc/Config.in if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then define_bool CONFIG_ACPI y diff -urpNX ../dontdiff linux-ia64/drivers/Makefile linux-generic/drivers/Makefile --- linux-ia64/drivers/Makefile Tue Aug 27 12:59:48 2002 +++ linux-generic/drivers/Makefile Wed Aug 28 08:54:02 2002 @@ -8,7 +8,7 @@ mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \ message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ - fc4 net/hamradio i2c acpi bluetooth + fc4 net/hamradio i2c acpi bluetooth misc subdir-y := parport char block net sound misc media cdrom hotplug subdir-m := $(subdir-y) diff -urpNX ../dontdiff linux-ia64/drivers/misc/Config.in linux-generic/drivers/misc/Config.in --- linux-ia64/drivers/misc/Config.in Fri Jan 25 13:11:21 2002 +++ linux-generic/drivers/misc/Config.in Mon Aug 12 10:26:31 2002 @@ -4,4 +4,6 @@ mainmenu_option next_comment comment 'Misc devices' +tristate 'Baseboard Management Controller' CONFIG_BMC + endmenu diff -urpNX ../dontdiff linux-ia64/drivers/misc/Makefile linux-generic/drivers/misc/Makefile --- linux-ia64/drivers/misc/Makefile Fri Jan 25 13:11:21 2002 +++ linux-generic/drivers/misc/Makefile Mon Aug 12 10:26:31 2002 @@ -9,8 +9,8 @@ # parent makes.. # +obj-$(CONFIG_BMC) += bmc.o + O_TARGET := misc.o include $(TOPDIR)/Rules.make - -fastdep: diff -urpNX ../dontdiff linux-ia64/drivers/misc/bmc.c linux-generic/drivers/misc/bmc.c --- linux-ia64/drivers/misc/bmc.c Wed Dec 31 17:00:00 1969 +++ linux-generic/drivers/misc/bmc.c Tue Aug 27 08:52:34 2002 @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2002 Hewlett-Packard Inc., All Rights Reserved. + * Authors: Hamilton Coutinho, Matthew Wilcox, Al Viro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver sends IPMI commands to the Management Controller found in + * many HP machines. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIN_BMC_REQUEST_SIZE 2 +#define MAX_BMC_BUFFER_SIZE 68 /* 68 for Zirconlite */ +#define MIN_BMC_RESPONSE_SIZE 3 /* nfLn, cmd, cCode */ + +#define BMC_LUN 0 /* BMC only responds to this LUN */ +#define CCODE_UNKNOWN 0xff +#define NETFN_OF(x) (((x)>>2) & 0x3f) /* extract netFn from nfLn */ + +#define WAIT_1_MS 1000 /* in uSecs */ +#define BMC_TIME_OUT (5000 * WAIT_1_MS) /* Maximum time the BMC can do its IO for an IRP */ + +/* Values to pass to DelayRetryFunc() */ + +#define KCS_READY_DELAY (WAIT_1_MS) +#define BMC_RESPONSE_DELAY (10 * WAIT_1_MS) /* how long to wait for a response after sending req */ +#define BMC_RETRY_DELAY (60 * WAIT_1_MS) /* how long to wait after an error before retrying */ +#define FORCE_DELAY 0xFFFFFFFF /* if retryCount has this value, force a delay */ + +struct bmc_request { + unsigned char nfLn; + unsigned char cmd; + unsigned char data[1]; /* actually a variable-length array */ +}; + +struct bmc_response { + unsigned char nfLn; + unsigned char cmd; + unsigned char cCode; + unsigned char data[1]; /* actually a variable-length array */ +}; + +struct filp_private { + unsigned int len; + struct bmc_response response; +}; + +/* Keyboard Controller Style Interface addresses and defines */ + +#define MAX_INVALID_RESPONSE_COUNT 1 /* number of BMC request send retries. */ + +/* Status register bit flags (from spec) */ +#define KCS_STATE_MASK 0xc0 +#define KCS_IDLE_STATE 0x00 +#define KCS_READ_STATE 0x40 +#define KCS_WRITE_STATE 0x80 +#define KCS_ERROR_STATE 0xc0 + +#define KCS_IBF 0x02 +#define KCS_OBF 0x01 + +/* KCS commands (from spec) */ +#define WRITE_START 0x61 +#define WRITE_END 0x62 +#define READ_CMD 0x68 + +/* State machine states */ +#define TRANSFER_INIT 1 /* initialize */ +#define TRANSFER_START 2 /* start the writing phase of the request */ +#define TRANSFER_NEXT 3 /* send the next byte of a request */ +#define TRANSFER_END 4 /* send last byte of request */ +#define RECEIVE_START 5 /* wait for BMC to transition to read state */ +#define RECEIVE_INIT 6 /* wait for first data byte to become available */ +#define RECEIVE_NEXT 7 /* collect data byte and ask for more */ +#define RECEIVE_INIT2 8 /* wait for next data byte to become available */ +#define RECEIVE_END 9 /* terminate the reading operation */ +#define MACHINE_END 10 /* quit and return to the user. */ +#define TRANSFER_ERROR 0 + +/* status values */ +#define STATUS_SUCCESS 0 /* Operation ok */ +#define STATUS_FAILURE 1 /* Operation not ok */ + + +#if 0 +#define KdPrint( a ) printk a +#else +#define KdPrint( a ) +#endif + +static int iomem; /* indicates whether to use writeb or outb */ +static unsigned long raw_addr; /* physical address */ +static unsigned char *base_addr; /* virtual address */ +static int base_port; /* port address */ + +static unsigned long mc_timeout; + +static void set_timeout(unsigned long microseconds) +{ + mc_timeout = jiffies + (microseconds * HZ) / 1000000; +} + +static int is_timeout(void) +{ + return time_after(jiffies, mc_timeout); +} + +/* DelayRetryFunc - inserts a delay into the execution of the current thread + * retryCount: A counter, incremented by the calling function, to be tested + * against the constant 0x50. Up until it reaches that value, + * this function just does a return. The net effect is that the + * calling function together with this function, spins on the CPU. + * delayTime: Delay interval to be used in uSec + */ +static void DelayRetryFunc(unsigned long retryCount, unsigned long delayTime) +{ + /* The following value is the number of times the DelayRetryFunc() + * just returns instead of doing a real OS delay. Intel chose this + * number in its sample driver. + */ + const unsigned long NO_RETRY_DELAY_COUNT = 0x50; + + if (retryCount < NO_RETRY_DELAY_COUNT && retryCount != FORCE_DELAY) + return; + +#if 1 + if (delayTime < 2000) { + udelay(delayTime); + } else { + mdelay(delayTime / 1000); + } +#else + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((delayTime * HZ)/1000000); +#endif +} + +static unsigned char read_bmc_data(void) +{ + if (iomem) { + return readb(base_addr); + } else { + return inb(base_port); + } +} + +static void write_bmc_data(unsigned char data) +{ + if (iomem) { + writeb(data, base_addr); + } else { + outb(data, base_port); + } +} + +static unsigned char read_bmc_status(void) +{ + if (iomem) { + return readb(base_addr + 1); + } else { + return inb(base_port + 1); + } +} + +static void write_bmc_cmd(unsigned char cmd) +{ + if (iomem) { + writeb(cmd, base_addr + 1); + } else { + outb(cmd, base_port + 1); + } +} + +/* get_bmc_state - returns the STATE bits of the STATUS register */ +static unsigned char get_bmc_state(void) +{ + return (read_bmc_status() & KCS_STATE_MASK); +} + +/* OBFset - returns the value of the OBF flag */ +static int OBFset(void) +{ + return (read_bmc_status() & KCS_OBF) == KCS_OBF; +} + +/* ClearOBF - Ensures OBF flag is clear */ +static void ClearOBF(void) +{ + read_bmc_data(); +} + +/* WaitOnOBF - Waits for Output Buffer Full Flag to be set. + * Returns false if we time out waiting. + */ +static int WaitOnOBF(void) +{ + unsigned long retryCount = 0; + + while (!OBFset()) { + DelayRetryFunc(retryCount++, KCS_READY_DELAY); + + if (is_timeout()) + return 0; + } + return 1; +} + +/* WaitOnIBF - Wait until the Input Buffer Flag is clear. */ +static void WaitOnIBF(void) +{ + unsigned long retryCount = 0; + + while (!is_timeout()) { + unsigned char status = read_bmc_status(); + if ((status & KCS_IBF) == 0) { + KdPrint(("IBF cleared!\n")); + break; + } + + DelayRetryFunc(retryCount++, KCS_READY_DELAY); + } +} + + +/** + * __execute_bmc_command - sends an IPMI command to the BMC + * request: the input buffer. Contains "raw" data for writing to the BMC + * request_length: amount of data in request to send. + * response: the output buffer. "Raw" data from the BMC is put here. + * response_length: On entry contains the max number of bytes that can be + * written to response. Updated to the number of bytes written on exit. + * + * returns 0 on success and -EIO on error. + * + * Low level routine for executing any IPMI command. Communicates + * directly with the Baseboard Management Controller. + * Refer to figures 8-2 and 8-3 of the IPMI Specification v1.0 (Document + * revision 1.1, dated November 15, 1999). Some tweaks were made to + * these figures in the "IPMI Addenda, Errata and Clarifications" + * revision 3 dated June 6, 2000. + */ + +static long __execute_bmc_command(struct bmc_request *request, + unsigned long request_length, struct bmc_response *response, + int *response_length) +{ + int status, invalidRespCount = 0; + unsigned char *transmitBuf = (unsigned char *) request; + unsigned char *receiveBuf = (unsigned char *) response; + int machineState = TRANSFER_INIT; + unsigned long request_ctr=0, receive_ctr=0; + + status = STATUS_SUCCESS; + response->cCode = CCODE_UNKNOWN; + + set_timeout(BMC_TIME_OUT); + + KdPrint(("Start state machine \n")); + + while (1) { + /* The three cases below are the only exits from this loop */ + if (is_timeout()) { + printk(KERN_ERR "bmcdev : bmc timeout\n"); + status = STATUS_FAILURE; + break; + } else if (machineState == MACHINE_END) { + status = STATUS_SUCCESS; + KdPrint(("In MACHINE_END\n")); + break; + } else if (invalidRespCount > MAX_INVALID_RESPONSE_COUNT) { + /* Intel's sample driver doesn't change the variable + * status to a failure in this case. We do. + */ + status = STATUS_FAILURE; + KdPrint(("Too many bad responses!\n")); + break; + } + + switch (machineState) { + + case TRANSFER_INIT: + KdPrint(("Entering TRANSFER_INIT!\n")); + + request_ctr = 0; + + WaitOnIBF(); + ClearOBF(); + + machineState = TRANSFER_START; + /* fall through */ + + case TRANSFER_START: + KdPrint(("Entering TRANSFER_START! Ready to write WRITE_START\n\n")); + + write_bmc_cmd(WRITE_START); + WaitOnIBF(); + + KdPrint(("TRANSFER_START: after WaitonIBF\n")); + + if (get_bmc_state() != KCS_WRITE_STATE) { + machineState = TRANSFER_ERROR; + break; + } + KdPrint(("TRANSFER_START: after get_bmc_state\n")); + + ClearOBF(); + KdPrint(("TRANSFER_START: after ClearOBF\n")); + + machineState = TRANSFER_NEXT; + /* fall through, again */ + + case TRANSFER_NEXT: + KdPrint(("Entering TRANSFER_NEXT!\n\n")); + /* check for end of request, transition to the END + * state which will transfer our last byte + */ + if (request_ctr == (request_length - 1)) { + machineState = TRANSFER_END; + break; + } + +#if 0 /* Following was commented out in Intel IPMI driver. We keep it + * commented out even though the flowchart in the IPMI spec has it in. + */ + if (get_bmc_state() != KCS_WRITE_STATE) { + machineState = TRANSFER_ERROR; + break; + } +#endif + write_bmc_data(transmitBuf[request_ctr++]); + KdPrint(("*** Sent data : %X ***\n", transmitBuf[request_ctr-1])); + + WaitOnIBF(); + +#if 0 + if (get_bmc_state() != KCS_WRITE_STATE) { + machineState = TRANSFER_ERROR; + break; + + } +#endif + ClearOBF(); + + break; + + case TRANSFER_END: + KdPrint(("Entering TRANSFER_END!\n\n")); + + /* Transfer the last byte of the request and + * transition to Reading. + */ + +#if 0 /* Following was commented out in Intel reference driver. */ + WaitOnIBF( context ); + if (!ClearObf(context)) { + machineState = TRANSFER_ERROR; + break; + } +#endif + + write_bmc_cmd(WRITE_END); + + WaitOnIBF(); + + if (get_bmc_state() != KCS_WRITE_STATE) { + machineState = TRANSFER_ERROR; + break; + } + + ClearOBF(); + + KdPrint(("Write next byte\n")); + write_bmc_data(transmitBuf[request_ctr++]); + KdPrint(("*** Sent data : %X ***\n", transmitBuf[request_ctr-1])); + + WaitOnIBF(); + + machineState = RECEIVE_START; + /* fall through */ + + case RECEIVE_START: + KdPrint(("Entering RECEIVE_START!\n\n")); + + /* request ended, now listen for a response */ + switch (get_bmc_state()) { + case KCS_ERROR_STATE: + machineState = TRANSFER_ERROR; + break; + + case KCS_WRITE_STATE: + case KCS_IDLE_STATE: + /* response not ready, wait for it */ + DelayRetryFunc( FORCE_DELAY, BMC_RESPONSE_DELAY ); + break; + + case KCS_READ_STATE: + /* response ready, setup to receive */ + receive_ctr = 0; + machineState = RECEIVE_INIT; + break; + } + break; + + case RECEIVE_INIT: + KdPrint(("Entering RECEIVE_INIT!\n\n")); + + /* wait for a data byte */ + switch (get_bmc_state()) { + case KCS_ERROR_STATE: + case KCS_WRITE_STATE: + machineState = TRANSFER_ERROR; + break; + + case KCS_IDLE_STATE: + /* BMC is done sending data */ + machineState = RECEIVE_END; + break; + + case KCS_READ_STATE: + /* The following was rewritten from + * the IPMI reference driver to match + * the flowchart in figure 8-3 of IPMI + * Addenda, Errata and Clarifications + */ + if (WaitOnOBF()) + machineState = RECEIVE_NEXT; + else + machineState = TRANSFER_ERROR; + break; + + default: + DelayRetryFunc(FORCE_DELAY, KCS_READY_DELAY); + } + break; + + case RECEIVE_NEXT: + KdPrint(("Entering RECEIVE_NEXT!\n\n")); + + /* Read the next data byte from the BMC */ + + if (receive_ctr >= *response_length) { + machineState = TRANSFER_ERROR; + break; + } + + receiveBuf[receive_ctr++] = read_bmc_data(); + + KdPrint(("*** The data returned is %x *** \n", receiveBuf[receive_ctr-1])); + + write_bmc_data(READ_CMD); + + WaitOnIBF(); + + machineState = RECEIVE_INIT2; + + break; + + case RECEIVE_INIT2: + KdPrint(("Entering RECEIVE_INIT2!\n\n")); + + /* wait for a data byte */ + switch (get_bmc_state()) { + case KCS_ERROR_STATE: + case KCS_WRITE_STATE: + machineState = TRANSFER_ERROR; + break; + + case KCS_IDLE_STATE: + /* BMC is done sending data */ + if (WaitOnOBF()) { + ClearOBF(); + machineState = RECEIVE_END; + } else { + machineState = TRANSFER_ERROR; + } + + break; + + case KCS_READ_STATE: + + if (WaitOnOBF()) { + machineState = RECEIVE_NEXT; + } else { + machineState = TRANSFER_ERROR; + } + + break; + } + break; + + case RECEIVE_END: + KdPrint(("Entering RECEIVE_END!\n\n")); + + /* check for size, appropriate response netfun (which + * should be the original netfun with last bit set), + * and command + */ + if (receive_ctr < MIN_BMC_RESPONSE_SIZE || + NETFN_OF(response->nfLn) != (NETFN_OF(request->nfLn) | 1) || + response->cmd != request->cmd ) { + machineState = TRANSFER_ERROR; + break; + } + + /* all done, return triumphant */ + *response_length = receive_ctr; + machineState = MACHINE_END; + + break; + + case TRANSFER_ERROR: + KdPrint(("Entering TRANSFER_ERROR!\n\n")); + + default: + /* + * We've failed somehow, or maybe it was the BMC fault. + * Try again, as per comm spec. allow 60 ms. for + * controllers to recover + */ + DelayRetryFunc(FORCE_DELAY, BMC_RETRY_DELAY); + receive_ctr = request_ctr = 0; + ++invalidRespCount; + machineState = TRANSFER_INIT; + + if (invalidRespCount <= MAX_INVALID_RESPONSE_COUNT) + printk(KERN_ERR "bmcdev: retrying command\n"); + break; + } /* switch */ + } /* while(TRUE) */ + + return (status == STATUS_SUCCESS) ? 0 : -EIO; +} + +static DECLARE_MUTEX(chip_sem); + +static long execute_bmc_command(struct bmc_request *request, + unsigned long request_len, struct bmc_response *response, + int *response_len) +{ + long result; + down(&chip_sem); + result = __execute_bmc_command(request, request_len, response, + response_len); + up(&chip_sem); + return result; +} + +#ifdef CONFIG_ACPI +struct SPMITable { + s8 Signature[4]; + u32 Length; + u8 Revision; + u8 Checksum; + s8 OEMID[6]; + s8 OEMTableID[8]; + s8 OEMRevision[4]; + s8 CreatorID[4]; + s8 CreatorRevision[4]; + s16 InterfaceType; + s16 SpecificationRevision; + u8 InterruptType; + u8 GPE; + s16 Reserved; + u64 GlobalSystemInterrupt; + u8 BaseAddress[12]; + u8 UID[4]; +} __attribute__ ((packed)); + +static unsigned long acpi_find_bmc(void) +{ + acpi_status status; + acpi_table_header *spmi; + static unsigned long io_base = 0; + + if (io_base != 0) + return io_base; + + status = acpi_get_firmware_table("SPMI", 1, + ACPI_LOGICAL_ADDRESSING, &spmi); + + if (status != AE_OK) { + printk(KERN_ERR "bmcdev : SPMI table not found.\n"); + return 0; + } + + memcpy(&io_base, ((struct SPMITable *)spmi)->BaseAddress, + sizeof(io_base)); + + return io_base; +} +#endif + +static unsigned long pci_find_bmc(void) +{ + return 0; +} + +static int isa_find_bmc(void) +{ + return 0; +} + +static spinlock_t busy_lock; + +static int bmc_open(struct inode *inode, struct file *filp) +{ + filp->private_data = NULL; + return 0; +} + +static int bmc_release(struct inode *inode, struct file *filp) +{ + kfree(filp->private_data); + return 0; +} + +static ssize_t bmc_write(struct file *file, const char *buf, size_t count, + loff_t *ppos) +{ + int err, ccode; + struct bmc_request *request; + struct filp_private *private; + struct bmc_response *response; + int response_size = MAX_BMC_BUFFER_SIZE; + + if (count >= MAX_BMC_BUFFER_SIZE || count < MIN_BMC_REQUEST_SIZE) + return -EINVAL; + + private = kmalloc(MAX_BMC_BUFFER_SIZE + 1, GFP_KERNEL); + if (!private) + return -ENOMEM; + private->len = 0; + response = &private->response; + + err = -EBUSY; + spin_lock(&busy_lock); + if (file->private_data) + goto out; + file->private_data = private; + spin_unlock(&busy_lock); + + err = -ENOMEM; + request = kmalloc(count, GFP_KERNEL); + if (!request) + goto out1; + err = -EFAULT; + if (copy_from_user(request, buf, count)) + goto out2; + + err = -EIO; + if ((request->nfLn & 0x03) != BMC_LUN) + goto out2; + /* Ensure this is a request, not a response */ + if ((request->nfLn & 0x04) != 0) + goto out2; + + err = execute_bmc_command(request, count, response, &response_size); + + if (err < 0) + goto out2; + + /* Check the completion code */ + err = -EINVAL; + ccode = response->cCode; + if ((response->cCode == 0x7f) || (response->cCode == 0xbf) || + (response->cCode >= 0xd5 && response->cCode < 0xff)) + goto out2; + + private->len = response_size; + + kfree(request); + return count; + +out2: + kfree(request); +out1: + spin_lock(&busy_lock); + file->private_data = NULL; +out: + spin_unlock(&busy_lock); + kfree(response); + return err; +} + +static ssize_t bmc_read(struct file *filp, char *buf, size_t count, + loff_t *ppos) +{ + struct filp_private *p; + int len = 0; + loff_t offs = *ppos; + + spin_lock(&busy_lock); + p = filp->private_data; + if (p) { + len = p->len; + filp->private_data = NULL; + } + spin_unlock(&busy_lock); + if (len == 0) + goto out; + if (offs >= 0 && offs <= len) { + if ((count > len) || (offs + count > len)) + count = len - offs; + if (copy_to_user(buf, &p->response + offs, count)) + count = -EFAULT; + } else { + count = 0; + } + + out: + kfree(p); + return count; +} + +static struct file_operations bmc_operations = { + open: bmc_open, + release: bmc_release, + read: bmc_read, + write: bmc_write, +}; + +static int bmc_statfs(struct super_block *sb, struct statfs *buf) +{ + buf->f_type = sb->s_magic; + buf->f_bsize = PAGE_CACHE_SIZE; + buf->f_namelen = 255; + return 0; +} + +static struct super_operations s_ops = { + statfs: bmc_statfs, + put_inode: force_delete, +}; + +static struct super_block *bmc_read_super(struct super_block * sb, void * data, int silent) +{ + struct inode *inode; + sb->s_magic = 0x424d43; + sb->s_op = &s_ops; + inode = new_inode(sb); + if (!inode) + return NULL; + inode->i_mode = S_IFREG|0600; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_size = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_fop = &bmc_operations; + sb->s_root = d_alloc_root(inode); + if (!sb->s_root) { + iput(inode); + return NULL; + } + return sb; +} + +static DECLARE_FSTYPE(bmc_fs_type, "bmc", bmc_read_super, FS_SINGLE); + +#define MAX_DEVICE_OPEN_RETRIES 1000 +#define SEL_ENTRY_SIZE 16 +#define COMMAND_HEADER 2 +#define RESPONSE_SIZE 3 +#define MAX_RETRY_COUNT 30 + +/* Write in SEL a event when System Panic occurs */ +static int WritePanicEvent(struct notifier_block *this, unsigned long command, void *ptr) +{ + int err; + struct bmc_request *ibuffer; + struct bmc_response *obuffer; + unsigned int ilen, olen; + int RetryCount = 0; + + ilen = SEL_ENTRY_SIZE + COMMAND_HEADER; + ibuffer = kmalloc(ilen, GFP_KERNEL); + if (!ibuffer) + goto out; + + olen = RESPONSE_SIZE + COMMAND_HEADER; + obuffer = kmalloc(olen, GFP_KERNEL); + if (!obuffer) + goto out; + + ibuffer->nfLn = 0x28; /* STORAGE */ + ibuffer->cmd = 0x44; + ibuffer->data[2]=0x02; /* SEL_RT_STANDARD2 */ + ibuffer->data[7]=0x21; /* SEL_GI_NSA */ + ibuffer->data[8]=0x00; + ibuffer->data[9]=0x03; /* SEL_EV_STANDARD3 */ + ibuffer->data[10]=0x20; /* SENSOR_TYPE */ + ibuffer->data[11]=0x00; + ibuffer->data[12]=0x6F; /* EVENT_TYPE */ + ibuffer->data[13]=0x01; + ibuffer->data[14]=0xFF; + ibuffer->data[15]=0xFF; + err = __execute_bmc_command(ibuffer, ilen, obuffer, &olen); + while (err) { + /* Check Retry Count */ + if (RetryCount < MAX_RETRY_COUNT) + RetryCount++; + else + break; + /* Sleep before retrying */ + DelayRetryFunc(RetryCount, BMC_RETRY_DELAY); + err = __execute_bmc_command(ibuffer, ilen, obuffer, &olen); + } +out: + return 0; +} + +static void *reserve_bmc_memory(unsigned long address) +{ + if (!request_mem_region(address, 4, "bmcdev")) { + printk(KERN_ERR "bmcdev: bmc address already in use\n"); + return 0; + } + return ioremap(address, 4); +} + +static int reserve_bmc_port(int address) +{ + if (!request_region(address, 4, "bmcdev")) { + printk(KERN_ERR "bmcdev: bmc address already in use\n"); + return 0; + } + return 1; +} + +static void release_bmc_resources(void) +{ + if (iomem) { + release_mem_region(raw_addr, 4); + iounmap(base_addr); + } else { + release_region(base_port, 4); + } +} + +/* + * There are three ways to find a BMC device. It may be described in the + * ACPI tables, it may be a PCI device, or we may have to probe IO port space + * for it. Disgusting. + */ +static int find_bmc(void) +{ +#ifdef CONFIG_ACPI + raw_addr = acpi_find_bmc(); + if (raw_addr) { + iomem = 1; + base_addr = reserve_bmc_memory(raw_addr); + return (base_addr != 0); + } +#endif + raw_addr = pci_find_bmc(); + if (raw_addr) { + iomem = 1; + base_addr = reserve_bmc_memory(raw_addr); + return (base_addr != 0); + } + + base_port = isa_find_bmc(); + if (base_port) { + iomem = 0; + return reserve_bmc_port(base_port); + } + + return 0; +} + +static struct notifier_block panic_block = { WritePanicEvent, NULL, 0 }; + +static int __init bmc_init(void) +{ + int result; + + printk(KERN_INFO "BMC module initialising\n"); + + if (!find_bmc()) + return -EBUSY; + + result = register_filesystem(&bmc_fs_type); + if (result < 0) + goto init_error; + + /* WritePanicEvent(NULL,0,NULL); */ + notifier_chain_register(&panic_notifier_list, &panic_block); + + return 0; + + init_error: + release_bmc_resources(); + return result; +} + +static void __exit bmc_exit(void) +{ + notifier_chain_unregister(&panic_notifier_list, &panic_block); + unregister_filesystem(&bmc_fs_type); + release_bmc_resources(); +} + +MODULE_LICENSE("GPL"); + +module_init(bmc_init); +module_exit(bmc_exit); diff -urpNX ../dontdiff linux-ia64/kernel/ksyms.c linux-generic/kernel/ksyms.c --- linux-ia64/kernel/ksyms.c Tue Aug 27 13:00:49 2002 +++ linux-generic/kernel/ksyms.c Tue Aug 27 09:26:33 2002 @@ -462,6 +462,7 @@ EXPORT_SYMBOL(nr_running); /* misc */ EXPORT_SYMBOL(panic); +EXPORT_SYMBOL_GPL(panic_notifier_list); EXPORT_SYMBOL(__out_of_line_bug); EXPORT_SYMBOL(sprintf); EXPORT_SYMBOL(snprintf);