--- linux-2.4.2-virgin/drivers/char/misc.c Sun Apr 8 18:22:17 2001 +++ linux-bmc/drivers/char/misc.c Tue Oct 30 17:18:07 2001 @@ -273,6 +273,9 @@ #ifdef CONFIG_TOSHIBA tosh_init(); #endif +#ifdef CONFIG_IPMI_KCS + ipmi_kcs_init(); +#endif if (devfs_register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); --- linux-2.4.2-virgin/drivers/char/Config.in Sun Apr 8 18:22:22 2001 +++ linux-bmc/drivers/char/Config.in Tue Oct 30 17:18:07 2001 @@ -81,6 +81,11 @@ dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT fi +tristate 'IPMI KCS Interface' CONFIG_IPMI_KCS +if [ "$CONFIG_IPMI_KCS" != "n" ]; then + bool ' BMC watchdog timer support' CONFIG_BMC_WDT +fi + source drivers/i2c/Config.in source drivers/sensors/Config.in --- linux-2.4.2-virgin/drivers/char/Makefile Sun Apr 8 18:22:17 2001 +++ linux-bmc/drivers/char/Makefile Tue Oct 30 17:18:08 2001 @@ -191,6 +191,8 @@ obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o +obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o + # Only one watchdog can succeed. We probe the hardware watchdog # drivers first, then the softdog driver. This means if your hardware # watchdog dies or is 'borrowed' for some reason the software watchdog --- linux-2.4.2-virgin/include/linux/miscdevice.h Sun Apr 8 18:47:26 2001 +++ linux-bmc/include/linux/miscdevice.h Tue Oct 30 17:32:42 2001 @@ -31,6 +31,9 @@ #define SGI_STREAMS_KEYBOARD 150 /* drivers/sgi/char/usema.c */ #define SGI_USEMACLONE 151 +#define IPMI_KCS_MINOR 173 +#define IPMI_BT_MINOR 210 +#define IPMI_SMIC_MINOR 211 #define TUN_MINOR 200 --- /dev/null Fri Mar 23 23:37:44 2001 +++ linux-bmc/drivers/char/ipmi_kcs.c Tue Oct 30 17:18:07 2001 @@ -0,0 +1,1211 @@ +/* + * Intelligent Platform Management Interface driver for Linux 2.2.x + * + * (c) Copyright 2000 San Mehat , All Rights Reserved. + * http://www.valinux.com + * + * 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. + * + * Neither San Mehat nor VA Linux Systems admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1999 San Mehat + * + * Release 0.04. - Initial Release + * + * Release 0.05. - Fixed ring buffer bugs... better buffer handling + * + * Release 0.06. - Changed polling freq to 1/10 sec + * + * Release 0.07. - Integrated watchdog commands into IOCTL's and added + * support for blinking front panel LED + * + * Release 0.08. - Sensor read commands added as ioctl + * + * Release 0.09. - Changed polling freq back to 1 second + * - Fixed possible bug where a chip status variable was + * not declared volatile. + * - Fixed buffer memory leak + * - Fixed ioctl return value problem + * - Changed architecture so that applications calling + * driver ioctl()'s are put to sleep after request + * is sent. The reply is handled by the normal + * driver polling timer queue and ring buffer + * + * Release 0.10. - Modified kcs_write routine so once a write is complete + * if the interface isn't in a 'READ STATE' it's okay. + * + * Release 0.12. - Added Intel Nightshade MB fixups since NS boards don't + * support pre-timeout NMI support + * - FRU download support added + * - /proc/ipmi created with fru data and driver status + * Release 0.13. - Added ioctl for setting asset tag + * - Fixed bug in /proc + * - Added asset tag max length field + * Release 1.00 - Added intelligent proc reading so that asset tag is + * refreshed whenever /proc/ipmi is read + * - Code cleanup + * - When asset tag is set with data whoes size is < maximum, + * pad the rest out with NULLs + * Release 1.10 - Fixed SMP bug which was causing command failures when + * /proc/ipmi was being read while a command was being + * executed (added semaphore) + * - Fixed /proc/ipmi so commands only get issued once + * Release 1.20 - Removed a bunch of useless warning msgs + * Release 1.30 - Added more stringent error checking when ISA state + * enters ERROR_STATE + * - Added better unexpected OBF handling on transactions + * - Explicitly set power sensor state to NO BLINKY on + * startup + * Release 2.0 - Re-wrote kcs state machine + * - Removed high level functions from driver + * - removed driver read() and write() capabilities + * - /proc/ipmi renamed to /proc/ipmi_kcs + * - /proc/ipmi_kcs now contains only BMC info + * + * Release 2.1 - Added support of watchdog timer through /dev/watchdog + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipmi_kcs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PROC_FS +#include +#endif + +#ifdef CONFIG_BMC_WDT +#include +#endif + +/* function prototypes */ + +int ipmi_kcs_init(void); + +static int kcs_open(struct inode *inode, struct file *file); +static int kcs_release(struct inode *inode, struct file *file); +static ssize_t kcs_read(struct file *file, char *buf, size_t count, + loff_t * ptr); +static ssize_t kcs_write(struct file *file, const char *buf, size_t count, + loff_t * ppos); +static long long kcs_llseek(struct file *file, long long offset, + int origin); +static int kcs_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +#ifdef CONFIG_BMC_WDT +static int wdt_is_open; +static int watchdog_set(int cmd); +static int watchdog_reset(void); +static int wdt_open(struct inode *inode, struct file *file); +static int wdt_release(struct inode *inode, struct file *file); +static ssize_t wdt_write(struct file *file, const char *buf, size_t count, + loff_t * ppos); +static int wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static struct file_operations wdt_fops = { + owner:THIS_MODULE, + write:wdt_write, + ioctl:wdt_ioctl, + open:wdt_open, + release:wdt_release, +}; + +static struct miscdevice wdt_miscdev = { + WATCHDOG_MINOR, + "bmc_wdt", + &wdt_fops +}; + +/* module parameters */ +static int wdt_margin = 60; /* watchdog timer interval in seconds */ +static int wdt_pre = 1; /* watchdog pre-timeout interval in seconds */ +static int wdt_action = 0x03; /* Default is no pre-timeout interrupt and power cycle upon timeout */ + +MODULE_PARM(wdt_margin, "i"); +MODULE_PARM(wdt_pre, "i"); +MODULE_PARM(wdt_action, "i"); + +#define WATCHDOG_ENABLE 0x01 +#define WATCHDOG_DISABLE 0x02 +#endif + +static unsigned char get_kcs_state(void); +static unsigned char read_kcs_data(void); +static void write_kcs_data(unsigned char data); +static void write_kcs_cmd(unsigned char cmd); +static int is_obf_set(void); +static int clear_obf(void); +static int wait_while_ibf(void); +static int get_deviceid(void); +static int kcs_do_xfer(BMC_REQUEST * request, int request_len, + BMC_RESPONSE * response, int *response_len); +static int old_kcs_do_xfer(BMC_REQUEST * request, int request_len, + BMC_RESPONSE * response, int *response_len); +static int new_kcs_do_xfer(BMC_REQUEST * request, int request_len, + BMC_RESPONSE * response, int *response_len); + +#ifdef CONFIG_PROC_FS +int ipmi_get_info(char *buf, char **start, off_t fpos, int length, + int *eof, void *unused); +#endif + +/* static globals */ +static int kcs_refcnt = 0; +static int driver_major = 2; +static int driver_minor = 1; +static int kcs_machine = 0; + +static struct { + unsigned int tx_good; + unsigned int tx_bad; +} kcs_stat; + +static DEVICE_ID_RESPONSE dev_id; +DECLARE_MUTEX(kcs_sem); + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *ipmi_proc_entry; +#endif + +static struct file_operations kcs_fops = { + owner:THIS_MODULE, + write:kcs_write, + ioctl:kcs_ioctl, + open:kcs_open, + release:kcs_release, + read:kcs_read, +}; + +static struct miscdevice kcs_miscdev = { + IPMI_KCS_MINOR, + "ipmi_kcs", + &kcs_fops +}; + +static struct ipmi_driver_info drv_inf; + +/***************/ + +static long long kcs_llseek(struct file *file, long long offset, + int origin) +{ + return -ESPIPE; +} + +static ssize_t kcs_write(struct file *file, const char *buf, size_t count, + loff_t * ppos) +{ + return (-ENOSYS); +} + +static ssize_t kcs_read(struct file *file, char *buf, size_t count, + loff_t * ptr) +{ + return (-ENOSYS); +} + +static int kcs_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case IOCTL_IPMI_XFER: + { + IPMI_XFER user_buffer; + int rc; + int response_len = sizeof(user_buffer.response); + + if (!arg) + return (-EFAULT); + if (copy_from_user + ((void *) &user_buffer, (void *) arg, + sizeof(IPMI_XFER))) + return (-EFAULT); + if ((user_buffer.request_len < 2) + || (user_buffer.response_len < 3)) + return (-EINVAL); + + rc = kcs_do_xfer((BMC_REQUEST *) & user_buffer. + request, user_buffer.request_len, + (BMC_RESPONSE *) & user_buffer. + response, &response_len); + user_buffer.response_len = response_len; + copy_to_user((void *) arg, (void *) &user_buffer, + sizeof(IPMI_XFER)); + return (rc); + } + case IOCTL_DRIVER_INFO: + { + struct ipmi_driver_info user_buffer; + + if (!arg) + return (-EFAULT); + if (copy_from_user + ((void *) &user_buffer, (void *) arg, + sizeof(user_buffer))) + return (-EFAULT); + copy_to_user((void *) arg, (void *) &drv_inf, + sizeof(drv_inf)); + return (0); + } + default: + return -EINVAL; + } + return (0); +} + +static int get_deviceid() +{ + unsigned char request_buffer[MAX_BUFFER_SIZE]; + unsigned char response_buffer[MAX_BUFFER_SIZE]; + BMC_REQUEST *bmc_req; + BMC_RESPONSE *bmc_resp; + int rc, response_len; + + memset(&dev_id, 0, sizeof(DEVICE_ID_RESPONSE)); + memset(request_buffer, 0, sizeof(request_buffer)); + memset(response_buffer, 0, sizeof(response_buffer)); + bmc_req = (BMC_REQUEST *) request_buffer; + bmc_resp = (BMC_RESPONSE *) response_buffer; + response_len = sizeof(response_buffer); + + bmc_req->lun = 0; + bmc_req->netfn = APP_REQUEST; + bmc_req->cmd = 0x01; /* GET_DEVICE_ID */ + +#if defined(__ia64__) + rc = new_kcs_do_xfer(bmc_req, 2, bmc_resp, &response_len); +#else + rc = old_kcs_do_xfer(bmc_req, 2, bmc_resp, &response_len); +#endif + if (bmc_resp->cc != 0x00) { + printk("[IPMI_KCS] get_deviceid() failed (0x%.2x)\n", + bmc_resp->cc); + return (-EIO); + } + memcpy(&dev_id, bmc_resp->data, sizeof(DEVICE_ID_RESPONSE)); + return (0); +} + +static int kcs_open(struct inode *inode, struct file *file) +{ + switch (MINOR(inode->i_rdev)) { + case IPMI_KCS_MINOR: + { + MOD_INC_USE_COUNT; + kcs_refcnt++; + return 0; + } + default: + return -ENODEV; + } +} + +static int kcs_release(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) == IPMI_KCS_MINOR) { + kcs_refcnt--; + } + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef CONFIG_BMC_WDT +static ssize_t wdt_write(struct file *file, const char *buf, size_t count, + loff_t * ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* + * Stop and then restart the watchdog timer. + */ + if (count) { + if (watchdog_set(WATCHDOG_DISABLE)) + return -EIO; + if (watchdog_set(WATCHDOG_ENABLE)) + return -EIO; + if (watchdog_reset()) + return -EIO; + return 1; + } + return 0; +} + +static int wdt_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + WDIOF_KEEPALIVEPING, 1, "BMC WDT" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user + ((struct watchdog_info *) arg, &ident, sizeof(ident))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + if (copy_to_user((int *) arg, &wdt_is_open, sizeof(int))) + return -EFAULT; + break; + + case WDIOC_KEEPALIVE: + if (watchdog_set(WATCHDOG_DISABLE)) + return -EIO; + if (watchdog_set(WATCHDOG_ENABLE)) + return -EIO; + if (watchdog_reset()) + return -EIO; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int watchdog_set(int cmd) +{ + unsigned char request_buffer[MAX_BUFFER_SIZE]; + unsigned char response_buffer[MAX_BUFFER_SIZE]; + BMC_REQUEST *bmc_req; + BMC_RESPONSE *bmc_resp; + int rc, response_len; + SET_WATCHDOG *set; + int fixup = 0; + + memset(request_buffer, 0, sizeof(request_buffer)); + memset(response_buffer, 0, sizeof(response_buffer)); + bmc_req = (BMC_REQUEST *) request_buffer; + bmc_resp = (BMC_RESPONSE *) response_buffer; + response_len = sizeof(response_buffer); + + bmc_req->lun = 0; + bmc_req->netfn = APP_REQUEST; + bmc_req->cmd = 0x24; /* Set Watchdog Timer */ + + set = (SET_WATCHDOG *) bmc_req->data; + + while (1) { + set->timer_use = 0x04; + + set->timeout_action = wdt_action & 0x0F; + set->pre_irq = (wdt_action >> 4) & 0x0F; + + if (cmd == WATCHDOG_DISABLE) { + set->timeout_action = 0x00; + set->pre_irq = 0x00; + } + set->pretimeout_interval = wdt_pre; + + if (fixup) { + set->pre_irq = 0x00; + set->pretimeout_interval = 0; + } + + set->tuefc_biosfrb2 = 0x00; + set->tuefc_biospost = 0x0; + set->tuefc_osload = 0x00; + set->tuefc_smsos = 0x01; + set->initial_count = wdt_margin * 10; + + rc = kcs_do_xfer(bmc_req, 2 + sizeof(SET_WATCHDOG), + bmc_resp, &response_len); + + if (bmc_resp->cc == 0xcc) { + fixup++; + if (fixup == 2) { + printk + ("[IPMI_KCS] Flakey NMI fixup failed\n"); + return (-EIO); + } + printk("[IPMI_KCS] Flakey NMI fixup enabled\n"); + continue; + } else if (bmc_resp->cc != 0x00) { + printk + ("[IPMI_KCS] Set watchdog timer failed (rc = 0x%.2x)\n", + bmc_resp->cc); + return (-EIO); + } + break; + } + return (0); +} + +static int watchdog_reset() +{ + unsigned char request_buffer[MAX_BUFFER_SIZE]; + unsigned char response_buffer[MAX_BUFFER_SIZE]; + BMC_REQUEST *bmc_req; + BMC_RESPONSE *bmc_resp; + int rc, response_len; + + memset(request_buffer, 0, sizeof(request_buffer)); + memset(response_buffer, 0, sizeof(response_buffer)); + bmc_req = (BMC_REQUEST *) request_buffer; + bmc_resp = (BMC_RESPONSE *) response_buffer; + response_len = sizeof(response_buffer); + + bmc_req->lun = 0; + bmc_req->netfn = APP_REQUEST; + bmc_req->cmd = 0x22; /* Reset Watchdog Timer */ + + rc = kcs_do_xfer(bmc_req, 2, bmc_resp, &response_len); + if (bmc_resp->cc != 0x00) { + printk("[IPMI_KCS] Reset Watchdog Timer failed (0x%.2x)\n", + bmc_resp->cc); + return (-EIO); + } + return (0); +} + +static int wdt_open(struct inode *inode, struct file *file) +{ + switch (MINOR(inode->i_rdev)) { + case WATCHDOG_MINOR: + if (wdt_is_open) + return -EBUSY; + + MOD_INC_USE_COUNT; + /* + * Activate + */ + wdt_is_open = 1; + if (watchdog_reset()) + return -EIO; + return 0; + default: + return -ENODEV; + } +} + +static int wdt_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + if (watchdog_set(WATCHDOG_DISABLE)) + return -EIO; +#endif + wdt_is_open = 0; + MOD_DEC_USE_COUNT; + } + unlock_kernel(); + return 0; +} + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the timer off */ + watchdog_set(WATCHDOG_DISABLE); + } + return NOTIFY_DONE; +} + +/* + * The BMC needs to learn about soft shutdowns in order to + * turn the watchdog timer off. + */ + +static struct notifier_block wdt_notifier = { + wdt_notify_sys, + NULL, + 0 +}; +#endif + +#ifdef MODULE +int init_module(void) +{ + return (ipmi_kcs_init()); +} + +void cleanup_module(void) +{ + printk("[IPMI_KCS] Driver shutting down.\n"); +#ifdef CONFIG_PROC_FS + remove_proc_entry("ipmi_kcs", 0); +#endif + misc_deregister(&kcs_miscdev); +#ifdef CONFIG_BMC_WDT + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); +#endif + release_region(KCS_BASE, 16); +} +#endif + +int ipmi_kcs_init() +{ + printk + ("IPMI KCS driver (San Mehat nettwerk@valinux.com) v%d.%d at io 0x%x\n", + driver_major, driver_minor, KCS_BASE); + request_region(KCS_BASE, 16, "ipmi_kcs"); + if ((inb_p(KCS_STATUS_REG) == 0xFF) && + (inb_p(KCS_DATAIN_REG) == 0xFF)) { + printk("--KCS ISA window not present. Driver exiting\n"); + release_region(KCS_BASE, 16); + return (-ENXIO); + } + + kcs_stat.tx_good = 0; + kcs_stat.tx_bad = 0; + memset(&drv_inf, 0, sizeof(drv_inf)); + strcpy(drv_inf.driver_name, "ipmi_kcs"); + drv_inf.major_ver = driver_major; + drv_inf.minor_ver = driver_minor; + + misc_register(&kcs_miscdev); +#ifdef CONFIG_BMC_WDT + misc_register(&wdt_miscdev); + register_reboot_notifier(&wdt_notifier); +#endif + get_deviceid(); + printk("--BMC version %x.%x, IPMI version %d.%d\n", + dev_id.major_firmware_revision, + dev_id.minor_firmware_revision, + dev_id.ipmi_version_major, dev_id.ipmi_version_minor); + if ((dev_id.ipmi_version_major == 0) && + (dev_id.ipmi_version_minor == 9)) { + printk("--Using legacy KCS state machine\n"); + kcs_machine = KCS_LEGACY; + drv_inf.flags |= KCS_FLAG_LEGACY; + } else { + printk("--Using new KCS state machine\n"); + kcs_machine = KCS_NEW; + } + if (!pci_present()) { + printk + ("--PCIBIOS not present. Unable to determine chipset vendor\n"); + drv_inf.flags |= KCS_FLAG_BLINKY; + } else { + int pci_index = 0; + unsigned char pci_bus, pci_device_fn; + unsigned short vendor, device; + + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_class(PCI_CLASS_BRIDGE_HOST << 8, + pci_index, + &pci_bus, + &pci_device_fn) != + PCIBIOS_SUCCESSFUL) { + break; + } + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + drv_inf.mb_chipset_vendor = vendor; + drv_inf.mb_chipset_device = device; + printk + ("--Motherboard Chipset vendor 0x%.4x, device 0x%.4x\n", + vendor, device); + if ((vendor == 0x8086) && (device == 0x71a0)) { + drv_inf.flags |= KCS_FLAG_BLINKY; + } + } + } + if (drv_inf.flags & KCS_FLAG_BLINKY) { + printk("--Intel Lancewood features enabled\n"); + } else + printk("--No vendor specific features enabled\n"); +#ifdef CONFIG_PROC_FS + if (!(ipmi_proc_entry = create_proc_entry("ipmi_kcs", 0, 0))) + printk(KERN_ERR, + "--ERROR: Unable to register /proc/ipmi_kcs\n"); + else + ipmi_proc_entry->read_proc = ipmi_get_info; + +#endif + return (0); +} + +static int kcs_do_xfer(BMC_REQUEST * request, + int request_len, + BMC_RESPONSE * response, int *response_len) +{ + int rc = 0; + + down(&kcs_sem); + switch (kcs_machine) { + case KCS_LEGACY: + { + rc = old_kcs_do_xfer(request, request_len, + response, response_len); + break; + } + case KCS_NEW: + { + rc = new_kcs_do_xfer(request, request_len, + response, response_len); + break; + } + default: + { + printk + ("[IPMI_KCS] Undefined or bad KCS state machine selected (%d)\n", + kcs_machine); + get_deviceid(); + if ((dev_id.ipmi_version_major == 0) && + (dev_id.ipmi_version_minor == 9)) { + printk + ("Recalibrated to use legacy KCS state machine\n"); + kcs_machine = KCS_LEGACY; + } else { + printk + ("Recalibrated to use new KCS state machine\n"); + kcs_machine = KCS_NEW; + } + rc = -EAGAIN; + break; + } + } + if (rc == 0) + kcs_stat.tx_good++; + else + kcs_stat.tx_bad++; + up(&kcs_sem); + return (rc); +} + +static int new_kcs_do_xfer(BMC_REQUEST * request, + int request_len, + BMC_RESPONSE * response, int *response_len) +{ + unsigned char *xmit_buffer, *recv_buffer; + int i = 0, rc = 0, state = SEND_INIT, bad = 0; + + xmit_buffer = (unsigned char *) request; + recv_buffer = (unsigned char *) response; + + while (1) { + if (state == END) + break; + else if (bad > 2) { + printk + ("[IPMI_KCS] Maximum retries exceeded. Aborting transfer\n"); + rc = -EIO; + break; + } + switch (state) { + case SEND_INIT: + { + i = 0; + state = SEND_START; + wait_while_ibf(); + if (clear_obf() != 0) { + state = ERROR; + break; + } + } + case SEND_START: + { + state = SEND_NEXT; + write_kcs_cmd(WRITE_START); + wait_while_ibf(); + if (get_kcs_state() != KCS_WRITE_STATE) { + state = ERROR; + break; + } + } + case SEND_NEXT: + { + if (i == (request_len - 1)) { + state = SEND_END; + break; + } + if (get_kcs_state() != KCS_WRITE_STATE) { + state = ERROR; + break; + } + write_kcs_data(xmit_buffer[i++]); + wait_while_ibf(); + if (get_kcs_state() != KCS_WRITE_STATE) { + state = ERROR; + break; + } + if (clear_obf() != 0) { + state = ERROR; + break; + } + break; + } + case SEND_END: + { + write_kcs_cmd(WRITE_END); + wait_while_ibf(); + if (get_kcs_state() != KCS_WRITE_STATE) { + state = ERROR; + break; + } + if (clear_obf() != 0) { + state = ERROR; + break; + } + write_kcs_data(xmit_buffer[i++]); + wait_while_ibf(); + state = RECV_START; + } + case RECV_START: + { + switch (get_kcs_state()) { + case KCS_ERROR_STATE: + { + state = ERROR; + break; + } + case KCS_WRITE_STATE: + case KCS_IDLE_STATE: + { + mdelay(BMC_RESPONSE_DELAY); + break; + } + case KCS_READ_STATE: + { + i = 0; + memset(recv_buffer, 0, + *response_len); + state = RECV_INIT; + break; + } + } + break; + } + case RECV_INIT: + { + switch (get_kcs_state()) { + case KCS_ERROR_STATE: + case KCS_WRITE_STATE: + { + state = ERROR; + break; + } + case KCS_IDLE_STATE: + { + state = RECV_END; + break; + } + case KCS_READ_STATE: + { + if (is_obf_set()) + state = RECV_NEXT; + else + mdelay(1); + break; + } + default: + { + mdelay(1); + break; + } + } + break; + } + case RECV_NEXT: + { + if (i >= *response_len) { + rc = -EOVERFLOW; + state = ERROR; + break; + } + recv_buffer[i++] = read_kcs_data(); + write_kcs_data(KCS_READ); + wait_while_ibf(); + state = RECV_INIT2; + break; + } + case RECV_INIT2: + { + switch (get_kcs_state()) { + case KCS_ERROR_STATE: + case KCS_WRITE_STATE: + { + state = ERROR; + break; + } + case KCS_IDLE_STATE: + { + if (wait_until_obf() == 0) { + clear_obf(); + state = RECV_END; + break; + } else { + state = ERROR; + break; + } + } + case KCS_READ_STATE: + { + if (wait_until_obf() == 0) + state = RECV_NEXT; + else + state = ERROR; + break; + } + } + break; + } + case RECV_END: + { + if ((i < MIN_BMC_RESPONSE_SIZE) || + (response->netfn != + (request->netfn | 0x01)) + || (response->cmd != request->cmd)) { + mdelay(BMC_RETRY_DELAY); + bad++; + state = SEND_INIT; + printk + ("[IPMI_KCS] Request/Response CMD/NETFN mismatch error\n"); + + printk + (" RQcmd/RQnetfn=0x%x/0x%x,RScmd/RSnetfn=0x%x/0x%x\n", + request->cmd, request->netfn, + response->cmd, + response->netfn); + break; + } + + *response_len = i; + rc = 0; + state = END; + break; + } + case ERROR: + default: + { + printk + ("[IPMI_KCS] BMC in bad state. Retrying transfer\n"); + mdelay(BMC_RETRY_DELAY); + bad++; + state = SEND_INIT; + break; + } + } + } + return (rc); +} + +static int old_kcs_do_xfer(BMC_REQUEST * request, + int request_len, + BMC_RESPONSE * response, int *response_len) +{ + unsigned char *xmit_buffer, *recv_buffer; + int i = 0, rc = 0, state = SEND_INIT, bad = 0; + + xmit_buffer = (unsigned char *) request; + recv_buffer = (unsigned char *) response; + + while (1) { + if (state == END) + break; + else if (bad > 2) { + printk + ("[IPMI_KCS] Maximum retries exceeded. Aborting transfer\n"); + rc = -EIO; + break; + } + switch (state) { + case SEND_INIT: + { + i = 0; + state = SEND_START; + wait_while_ibf(); + } + case SEND_START: + { + state = SEND_NEXT; + write_kcs_cmd(WRITE_START); + wait_while_ibf(); + } + case SEND_NEXT: + { + if (i == (request_len - 1)) { + state = SEND_END; + break; + } + if (get_kcs_state() != KCS_WRITE_STATE) { + state = ERROR; + break; + } + write_kcs_data(xmit_buffer[i++]); + wait_while_ibf(); + break; + } + case SEND_END: + { + wait_while_ibf(); + write_kcs_cmd(WRITE_END); + wait_while_ibf(); + if (get_kcs_state() != KCS_WRITE_STATE) { + state = ERROR; + break; + } + write_kcs_data(xmit_buffer[i++]); + wait_while_ibf(); + state = RECV_START; + } + case RECV_START: + { + switch (get_kcs_state()) { + case KCS_ERROR_STATE: + { + state = ERROR; + break; + } + case KCS_WRITE_STATE: + case KCS_IDLE_STATE: + { + mdelay(BMC_RESPONSE_DELAY); + break; + } + case KCS_READ_STATE: + { + i = 0; + memset(recv_buffer, 0, + *response_len); + state = RECV_INIT; + break; + } + } + break; + } + case RECV_INIT: + { + switch (get_kcs_state()) { + case KCS_ERROR_STATE: + case KCS_WRITE_STATE: + { + state = ERROR; + break; + } + case KCS_IDLE_STATE: + { + state = RECV_END; + break; + } + case KCS_READ_STATE: + { + if (is_obf_set()) + state = RECV_NEXT; + else + mdelay(1); + break; + } + default: + { + mdelay(1); + break; + } + } + break; + } + case RECV_NEXT: + { + if (i >= *response_len) { + rc = -EOVERFLOW; + state = ERROR; + break; + } + recv_buffer[i++] = read_kcs_data(); + wait_while_ibf(); + write_kcs_data(KCS_READ); + state = RECV_INIT2; + break; + } + case RECV_INIT2: + { + switch (get_kcs_state()) { + case KCS_ERROR_STATE: + case KCS_WRITE_STATE: + { + state = ERROR; + break; + } + case KCS_IDLE_STATE: + { + state = RECV_END; + break; + } + case KCS_READ_STATE: + { + if (is_obf_set()) + state = RECV_NEXT; + break; + } + } + break; + } + case RECV_END: + { + if ((i < MIN_BMC_RESPONSE_SIZE) || + (response->netfn != + (request->netfn | 0x01)) + || (response->cmd != request->cmd)) { + mdelay(BMC_RETRY_DELAY); + bad++; + state = SEND_INIT; + printk + ("[IPMI_KCS] Request/Response CMD/NETFN mismatch error\n"); + + printk + (" RQcmd/RQnetfn=0x%x/0x%x,RScmd/RSnetfn=0x%x/0x%x\n", + request->cmd, request->netfn, + response->cmd, + response->netfn); + break; + } + + *response_len = i; + rc = 0; + state = END; + break; + } + case ERROR: + default: + { + printk + ("[IPMI_KCS] BMC in bad state. Retrying transfer\n"); + mdelay(BMC_RETRY_DELAY); + bad++; + state = SEND_INIT; + break; + } + } + } + return (rc); +} + +#ifdef CONFIG_PROC_FS +int ipmi_get_info(char *buf, char **start, off_t fpos, int length, + int *eof, void *unused) +{ + char *p; + + if (get_deviceid() != 0) { + printk("[IPMI_KCS] Unable to get device ID\n"); + memset(&dev_id, 0, sizeof(dev_id)); + } + + p = buf; + p += sprintf(p, "Driver Version\t: %d.%d\n", + driver_major, driver_minor); + p += sprintf(p, "BMC Version\t: %x.%x\n", + dev_id.major_firmware_revision, + dev_id.minor_firmware_revision); + p += sprintf(p, "IPMI Version\t: %d.%d\n", + dev_id.ipmi_version_major, dev_id.ipmi_version_minor); + p += sprintf(p, "\nTotal Good Transactions\t: %d\n", + kcs_stat.tx_good); + p += sprintf(p, "Total Bad Transactions\t: %d\n", kcs_stat.tx_bad); + + return p - buf; +} +#endif + +/* + * kcs chip mashing stuff + */ +static int wait_while_ibf() +{ + unsigned char status_byte; + + status_byte = inb_p(KCS_STATUS_REG); + if ((status_byte & KCS_IBF) == 0) + return (0); + mdelay(KCS_READY_DELAY); + status_byte = inb_p(KCS_STATUS_REG); + if (status_byte & KCS_IBF) + return (-1); + return (0); +} + +static int wait_until_obf() +{ + int retries = 0; + + while (retries < 2) { + if (is_obf_set()) + return (0); + mdelay(KCS_READY_DELAY); + retries++; + } + return (-ETIMEDOUT); +} + +static unsigned char get_kcs_state() +{ + unsigned char cs; + + cs = inb_p(KCS_STATUS_REG); + return (cs & KCS_STATE_MASK); +} + +static unsigned char read_kcs_data() +{ + unsigned char data; + + data = inb_p(KCS_DATAOUT_REG); + return (data); +} + +static void write_kcs_data(unsigned char data) +{ + outb_p(data, KCS_DATAIN_REG); +} + +static void write_kcs_cmd(unsigned char cmd) +{ + outb_p(cmd, KCS_COMMAND_REG); +} + +static int is_obf_set() +{ + unsigned char cs; + + cs = inb_p(KCS_STATUS_REG); + return ((cs & KCS_OBF) == KCS_OBF); +} + +static int clear_obf() +{ + read_kcs_data(); + return (0); +} --- /dev/null Fri Mar 23 23:37:44 2001 +++ linux-bmc/drivers/char/ipmi_kcs.h Tue Oct 30 17:18:07 2001 @@ -0,0 +1,171 @@ +/* + * Intelligent Platform Management Interface driver for Linux 2.x + * + * (c) Copyright 1999 San Mehat & VA Linux Systems + * 1382 Bordeaux Dr. + * Sunnyvale, California + * 94089 + * + * http://www.valinux.com + * + * This driver is provided under the GNU public license, incorporated + * herein by reference. The driver is provided without warranty or + * support. + * + * + */ + +#include + +#define KCS_LEGACY 1 +#define KCS_NEW 2 + +#define KCS_READY_DELAY 5 +#define BMC_RESPONSE_DELAY 5 +#define BMC_RETRY_DELAY 60 + +#if defined(__ia64__) +#define KCS_BASE 0x8a2 +#else +#define KCS_BASE 0xca2 +#endif +#define KCS_STATUS_REG (KCS_BASE + 1) +#define KCS_COMMAND_REG (KCS_BASE + 1) +#define KCS_DATAIN_REG (KCS_BASE + 0) +#define KCS_DATAOUT_REG (KCS_BASE + 0) + +/* State bits based on S1 & S0 below */ +#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 +#define KCS_SMS_ATN 0x04 + +#define SEND_INIT 1 +#define SEND_START 2 +#define SEND_NEXT 3 +#define SEND_END 4 +#define RECV_START 5 +#define RECV_INIT 6 +#define RECV_NEXT 7 +#define RECV_INIT2 8 +#define RECV_END 9 +#define END 10 +#define ERROR 0 + +/* SMS Transfer Stream Control Codes */ +#define GET_STATUS_ABORT 0x60 +#define WRITE_START 0x61 +#define WRITE_END 0x62 +#define KCS_READ 0x68 + +#define MAX_INVALID_RESPONSE_COUNT 2 +#define MIN_BMC_RESPONSE_SIZE 3 +#define MAX_IMB_PACKET_SIZE 33 +#define MAX_BMC_RESPONSE_SIZE (MIN_BMC_RESPONSE_SIZE + MAX_IMB_PACKET_SIZE) +#define MAX_XFER_LENGTH (MAX_IMB_PACKET_SIZE * 2) + +#define MAX_BUFFER_SIZE 64 + +typedef struct bmc_response + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + unsigned char cc; + unsigned char data[1]; + }BMC_RESPONSE; + +typedef struct bmc_request + { + unsigned char lun :2; + unsigned char netfn :6; + + unsigned char cmd; + unsigned char data[1]; + }BMC_REQUEST; + +/* GET_DEVICE_ID RESPONSE */ +typedef struct device_id_response + { + unsigned char device_id; + + unsigned char device_revision :4; + unsigned char reserved :3; + unsigned char provides_sdr :1; + + unsigned char major_firmware_revision :7; + #define NORMAL_OPERATION 0 + #define DEVICE_BUSY 1 + unsigned char device_available :1; + + unsigned char minor_firmware_revision; + + unsigned char ipmi_version_major :4; + unsigned char ipmi_version_minor :4; + + unsigned char supports_sensor_device :1; + unsigned char supports_sdr_device :1; + unsigned char supports_sel_device :1; + unsigned char supports_fru_device :1; + unsigned char supports_ipmb_receiver :1; + unsigned char supports_ipmb_generator :1; + unsigned char supports_bridge :1; + unsigned char supports_chassis_device :1; + + unsigned char manufacturer_id1; + unsigned char manufacturer_id2; + unsigned char manufacturer_id3; + + unsigned short product_id; + } DEVICE_ID_RESPONSE; + +typedef struct set_watchdog + { + unsigned char timer_use :3; + unsigned char res1 :4; + unsigned char dontlog :1; + + unsigned char timeout_action :3; + unsigned char res2 :1; + unsigned char pre_irq :3; + unsigned char res3 :1; + + unsigned char pretimeout_interval; + + unsigned char tuefc_res1 :1; + unsigned char tuefc_biosfrb2 :1; + unsigned char tuefc_biospost :1; + unsigned char tuefc_osload :1; + unsigned char tuefc_smsos :1; + unsigned char tuefc_oem :1; + unsigned char tuefc_res2 :1; + unsigned char tuefc_res3 :1; + + unsigned short initial_count; + } SET_WATCHDOG; + +typedef struct get_watchdog_response + { + unsigned char timer_use :3; + unsigned char res1 :3; + unsigned char timer_status :1; + unsigned char sel_log :1; + + unsigned char timeout_act :3; + unsigned char res2 :1; + unsigned char pre_irq_act :3; + unsigned char res3 :1; + + unsigned char pre_timeout __attribute__ ((packed)); + + unsigned char timer_use_xp __attribute__ ((packed)); + + unsigned short init_count __attribute__ ((packed)); + unsigned short current_count __attribute__ ((packed)); + } GET_WATCHDOG_RESPONSE; --- /dev/null Fri Mar 23 23:37:44 2001 +++ linux-bmc/include/linux/ipmi_ioctls.h Tue Oct 30 17:18:07 2001 @@ -0,0 +1,152 @@ +/* + * Intelligent Platform Management Interface driver for Linux 2.x + * + * (c) Copyright 1999 San Mehat & VA Linux Systems + * 1382 Bordeaux Dr. + * Sunnyvale, California + * 94089 + * + * http://www.valinux.com + * + * This driver is provided under the GNU public license, incorporated + * herein by reference. The driver is provided without warranty or + * support. + * + * IOCTL definitions for IPMI drivers + */ + +/* + * Note: The following macros should be used on the IPMI_XFER structure. + * DO NOT try to muck with this structure directly.. use the macros + * to ensure future compatibility: + * + * INIT_XFER(IPMI_XFER *); + * -- Zero out a IPMI_XFER structure and initialize it for use + * + * SET_REQUEST_LUN(IPMI_XFER *, unsigned char lun); + * -- Set the request packet logical unit + * + * SET_REQUEST_NETFN(IPMI_XFER *, unsigned char netfn); + * -- Set the request packet network function code + * + * SET_REQUEST_CMD(IPMI_XFER *, unsigned char cmd); + * -- Set the request packet IPMI command code + * + * SET_REQUEST_DATA(IPMI_XFER *, unsigned char *data, int length); + * -- Set the request packet optional argument data field + * + * GET_RESPONSE_LUN(IPMI_XFER *, unsigned char lun); + * -- Get the response packet logical unit + * + * GET_RESPONSE_NETFN(IPMI_XFER *, unsigned char netfn); + * -- Get the response packet network function code + * + * GET_RESPONSE_CMD(IPMI_XFER *, unsigned char cmd); + * -- Get the response packet command + * + * GET_RESPONSE_CC(IPMI_XFER *, unsigned char cc); + * -- Get the response packet completion code + * + * GET_RESPONSE_DATA_LENGTH(IPMI_XFER *, int len); + * -- Get the response packet data length + * + * GET_RESPONSE_DATA(IPMI_XFER *, unsigned char *buffer); + * -- Copy the response packet data into local buffer + */ + +#ifndef _IPMI_IOCTLS_H +#define _IPMI_IOCTLS_H + +#define IOCTL_IPMI_XFER 0x01 +#define IOCTL_DRIVER_INFO 0x02 + +typedef struct ipmi_xfer + { + unsigned char request[64]; + unsigned char response[64]; + int request_len; + int response_len; + } IPMI_XFER; + +struct ipmi_driver_info + { + char driver_name[64]; /* Name of the driver */ + int major_ver; + int minor_ver; + unsigned short mb_chipset_vendor; /* PCI host bridge vendor tag */ + unsigned short mb_chipset_device; /* PCI host bridge vendor device id */ + unsigned int flags; /* driver specific flags */ + unsigned int reserved; + }; + +/* flags definitions for the 'ipmi_kcs' driver */ +#define KCS_FLAG_BLINKY 0x01 /* Set if blinky works (only on Intel L440GX) */ +#define KCS_FLAG_LEGACY 0x02 /* Set if using legacy KCS interface ( < IPMI 1.0) */ + +#define INIT_XFER(_xferp) \ + memset(_xferp, 0, sizeof(IPMI_XFER)); \ + _xferp->request_len = 2; \ + _xferp->response_len = sizeof(_xferp->response); + +#define SET_REQUEST_LUN(_xferp, _lun) \ + { \ + unsigned char _netfn_copy; \ + \ + _netfn_copy = (_xferp->request[0] & 0xFC); \ + _xferp->request[0] = _lun; \ + _xferp->request[0]|= _netfn_copy; \ + } + +#define SET_REQUEST_NETFN(_xferp, netfn) \ + { \ + unsigned char __lun_copy; \ + \ + __lun_copy = (_xferp->request[0] & 0x3); \ + _xferp->request[0] = (netfn << 2); \ + _xferp->request[0]|= __lun_copy; \ + } + +#define SET_REQUEST_CMD(_xferp, _cmd) \ + _xferp->request[1] = _cmd; + +#define SET_REQUEST_DATA(_xferp, datap, _len) \ + { \ + memcpy(&_xferp->request[2], datap, _len); \ + _xferp->request_len = (_len + 2); \ + } + +#define GET_RESPONSE_LUN(_xferp, _lun) \ + _lun = (_xferp->response[0] & 0x3); + +#define GET_RESPONSE_NETFN(_xferp, netfn) \ + netfn = ((_xferp->response[0] & 0xFC) >> 2); + +#define GET_RESPONSE_CMD(_xferp, _cmd) \ + _cmd = _xferp->response[1]; + +#define GET_RESPONSE_CC(_xferp, cc) \ + cc = _xferp->response[2]; + +#define GET_RESPONSE_DATA_LENGTH(_xferp, _len) \ + _len = (_xferp->response_len - 3); + +#define GET_RESPONSE_DATA(_xferp, datap) \ + memcpy(datap, &_xferp->response[3], (_xferp->response_len -3)); + +/* + * The Netfn codes + */ +#define CHASSIS_REQUEST 0x00 +#define CHASSIS_RESPONSE 0x01 +#define BRIDGE_REQUEST 0x02 +#define BRIDGE_RESPONSE 0x03 +#define SENSOR_REQUEST 0x04 +#define SENSOR_RESPONSE 0x05 +#define APP_REQUEST 0x06 +#define APP_RESPONSE 0x07 +#define FIRMWARE_REQUEST 0x08 +#define FIRMWARE_RESPONSE 0x09 +#define STORAGE_REQUEST 0x0A +#define STORAGE_RESPONSE 0x0B + +#endif