From 1f8bcb3289fbae3274d5dee79b0be10c712291fb Mon Sep 17 00:00:00 2001 From: "Mark D. Studebaker" Date: Sun, 17 Nov 2002 21:03:39 +0000 Subject: [PATCH] ipmi ipmb support. Not even close to working yet. git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1617 7894878c-1315-0410-8ee3-d5d059ff63e0 --- kernel/busses/i2c-ipmb.c | 423 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 kernel/busses/i2c-ipmb.c diff --git a/kernel/busses/i2c-ipmb.c b/kernel/busses/i2c-ipmb.c new file mode 100644 index 00000000..8481ec44 --- /dev/null +++ b/kernel/busses/i2c-ipmb.c @@ -0,0 +1,423 @@ +/* + i2c-ipmb.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2002 Mark D. Studebaker + + 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This implements an i2c adapter for the BMC IPMB. +*/ + +#include +#include +#include +#include +#include +#include "version.h" + +static void i2c_ipmb_inc_use(struct i2c_adapter *adapter); +static void i2c_ipmb_dec_use(struct i2c_adapter *adapter); +static u32 i2c_ipmb_func(struct i2c_adapter *adapter); +int ipmb_access(struct i2c_adapter *adap,struct i2c_msg msgs[], + int num); + + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_ipmb_init(void); +static int __init i2c_ipmb_cleanup(void); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* I2C Data */ +static struct i2c_algorithm i2c_ipmb_algorithm = { + .name = "IPMB algorithm", + .id = I2C_ALGO_IPMB, + .master_xfer = ipmb_access, + .smbus_xfer = NULL, + .slave_send = NULL, + .slave_recv = NULL, + .algo_control = NULL, + .functionality = i2c_ipmb_func, +}; + +#define MAX_IPMB_ADAPTERS 8 +static struct i2c_adapter i2c_ipmb_adapter[MAX_IPMB_ADAPTERS]; +/* +static struct i2c_adapter i2c_ipmb_adapter = { + .name = "IPMB adapter", + .id = I2C_ALGO_SMBUS | I2C_HW_IPMB, + .algo = &i2c_ipmb_algorithm, + .algo_data = NULL, + .inc_use = &i2c_ipmb_inc_use, + .dec_use = &i2c_ipmb_dec_use, + .data = NULL, +}; +*/ + +/* IPMI Data */ +#define IPMI_IPMB_CHANNEL 0 +static ipmi_user_t i2c_ipmb_user; +static unsigned char ipmi_version_major; +static unsigned char ipmi_version_minor; +static const char msgdata[IPMI_MAX_ADDR_SIZE]; /* ?? */ +static struct ipmi_system_interface_addr address = { + IPMI_SYSTEM_INTERFACE_ADDR_TYPE, + IPMI_BMC_CHANNEL, + 0 +}; /* send address */ +static struct ipmi_ipmb_addr ipmb_address = { + IPMI_IPMB_ADDR_TYPE, + IPMI_IPMB_CHANNEL, + 0, + 0 +}; /* send address */ +static long msgid; /* message ID */ +static int interfaces; /* number of BMC's found */ +static struct ipmi_msg tx_message; /* send message */ +static unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH + 50]; +static unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH + 50]; /* sloppy */ + +/* IPMI Message defs */ +/* Network Function Codes */ +#define IPMI_NETFN_APP 0x06 +/* Commands */ +#define IPMI_ENABLE_CHANNEL 0x32 +#define IPMI_SEND_MSG 0x34 +#define IPMI_GET_CHANNEL_INFO 0x42 /* unfortunately, IPMI 1.5 only */ +#define IPMI_MASTER_WR 0x52 + + +/************** Message Sending **************/ + +static void ipmb_i2c_send_message(struct ipmi_addr *address, + int id, struct ipmi_msg * msg) +{ + int err; + + if((err = ipmi_request(i2c_ipmb_user, address, id, msg, 0))) + printk(KERN_INFO "i2c-ipmb.o: ipmi_request error %d\n", + err); +} + +/* not used */ +static void ipmb_i2c_bmc_send_message(int id, struct ipmi_msg * msg) +{ + address.channel = IPMI_BMC_CHANNEL; + ipmb_i2c_send_message((struct ipmi_addr *) &address, id, msg); +} + +/* this is for sending commands like master w/r */ +static void ipmb_i2c_ipmb_send_message(int id, struct ipmi_msg * msg) +{ + address.channel = IPMI_IPMB_CHANNEL; +/* + address.channel = IPMI_BMC_CHANNEL; +*/ + ipmb_i2c_send_message((struct ipmi_addr *) &address, id, msg); +} + +/* not used */ +/* this is for an smi message, not for things like master w/r */ +static void ipmb_i2c_ipmb_send_smi(int addr, int id, struct ipmi_msg * msg) +{ + ipmb_address.slave_addr = addr; + ipmb_address.lun = 0; /* pass in */ + ipmb_i2c_send_message((struct ipmi_addr *) &ipmb_address, id, msg); +} + +/* not used */ +/* Compose and send a "Enable Channel Receive" message */ +static void ipmb_enable_channel_rcv(int channel, int code) +{ + tx_message.netfn = IPMI_NETFN_APP; + tx_message.cmd = IPMI_ENABLE_CHANNEL; + tx_message.data_len = 2; + tx_message.data = tx_msg_data; + tx_msg_data[0] = channel; + tx_msg_data[1] = code; + ipmb_i2c_bmc_send_message(msgid++, &tx_message); +} + +/* not used */ +/* Compose and send a "Get Channel Info" message (1.5 only) */ +static void ipmb_get_channel_info(int channel) +{ + tx_message.netfn = IPMI_NETFN_APP; + tx_message.cmd = IPMI_GET_CHANNEL_INFO; + tx_message.data_len = 1; + tx_message.data = tx_msg_data; + tx_msg_data[0] = channel; + ipmb_i2c_bmc_send_message(msgid++, &tx_message); +} + +/* Compose and send a "Master W/R" message */ +static void ipmb_master_wr(int bus, u8 addr, u8 rdcount, + u8 wrcount, u8 *wrdata) +{ + tx_message.netfn = IPMI_NETFN_APP; + tx_message.cmd = IPMI_MASTER_WR; + tx_message.data_len = 3 + wrcount; + tx_msg_data[0] = bus & 0x0f; + tx_msg_data[1] = addr << 1; + tx_msg_data[2] = rdcount; + if(wrcount > 0) + memcpy(tx_msg_data + 3, wrdata, wrcount); + tx_message.data = tx_msg_data; + ipmb_i2c_ipmb_send_message(msgid++, &tx_message); +} + +int xchan; +int xbus; +/* look for channels. IPMI 1.5 defines multiple channels and an + easy way to get the information. + We don't bother using SDR type 14 for IPMI 1.0; that isn't + always present anyway. We could try "enable message channel receive" + with a channel state = 2 (query) but 1.0 is likely to have only + IPMB busses anyway. Therefore, we assume there is only + the IPMB at channel 0. +*/ +static void ipmb_get_all_channel_info(void) + +{ + if(ipmi_version_major > 1 || + (ipmi_version_major == 1 && ipmi_version_minor >= 5)) + ipmb_get_channel_info(0); +/* + else + scan SDR's for type 14 + else { + for all xchan 0-15 + ipmb_enable_channel_rcv(xchan, 2); +*/ + xbus = 0; + ipmb_master_wr(xbus, 0x2d, 1, 1, "\0"); +/* +*/ +/* + else + assume IPMB at channel 0 only +*/ +} + +/************** Message Receiving **************/ + +/* not used */ +static void ipmb_rcv_channel_info(struct ipmi_msg *msg) +{ + u8 channel, type, protocol; + + channel = msg->data[1] & 0x0f; + type = msg->data[2] & 0x7f; + protocol = msg->data[3] & 0x1f; + printk(KERN_INFO "i2c-ipmb.o: Channel %d; type 0x%x; protocol 0x%x\n", + channel, type, protocol); +} +/* + if ((rv = i2c_add_adapter(&i2c_ipmb_adapter))) { + printk(KERN_ERR "i2c-ipmb.o: Adapter registration failed, " + "module i2c-ipmb.o is not inserted\n."); + return; + } +*/ +/* + if(channel < 7) + ipmb_get_channel_info(channel + 1); +*/ +static void ipmb_rcv_master_resp(struct ipmi_msg *msg) +{ + printk(KERN_INFO "i2c-ipmb.o: bus %d good?\n", + xbus); + if(++xbus > 0x0f) + return; + ipmb_master_wr(xbus, 0x2d, 1, 1, "\0"); +} + +/* not used */ +static void ipmb_rcv_channel_enable(struct ipmi_msg *msg) +{ + int state; + + state = msg->data[2] & 1; + + printk(KERN_INFO "i2c-ipmb.o: Channel %d; state %d\n", + xchan, state); + if(++xchan > 0x0f) + return; + ipmb_enable_channel_rcv(xchan, 2); +} + +static void ipmb_i2c_msg_handler(struct ipmi_recv_msg *msg, + void *handler_data) +{ + int rcvid = msg->msgid & 0xffffff; + int client = (msg->msgid >> 24) & 0xf; + + if (msg->msg.data[0] != 0) + printk(KERN_WARNING "IPMI BMC response: Error 0x%x on cmd 0x%x/0x%x\n", + msg->msg.data[0], msg->msg.netfn, msg->msg.cmd); +/* + else +*/ + ipmb_rcv_master_resp(&(msg->msg)); + + + + + + ipmi_free_recv_msg(msg); +} + +static struct ipmi_user_hndl ipmb_hndlrs = +{ + .ipmi_recv_hndl = ipmb_i2c_msg_handler, +}; + +/*************** I2C funtions *******************/ + +/* Return -1 on error. */ +int ipmb_access(struct i2c_adapter *adap,struct i2c_msg msgs[], + int num) +{ + + + + + + +} + +void i2c_ipmb_inc_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void i2c_ipmb_dec_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static u32 i2c_ipmb_func(struct i2c_adapter *adapter) +{ + return 0; /* fixme */ +} + +/**************** Initialization ****************/ + +/* callback for each BMC found */ +static void ipmb_register(int ipmi_intf) +{ + unsigned long flags; + int rv; + + if(interfaces > 0) { /* 1 max for now */ + printk(KERN_INFO + "i2c-ipmb.o: Additional IPMI interface %d not supported\n", + ipmi_intf); + return; + } + + rv = ipmi_create_user(ipmi_intf, &ipmb_hndlrs, NULL, &i2c_ipmb_user); + if (rv < 0) { + printk(KERN_ERR "i2c-ipmb.o: Unable to register with ipmi\n"); + return; + } + + ipmi_get_version(i2c_ipmb_user, &ipmi_version_major, + &ipmi_version_minor); + printk(KERN_INFO + "i2c-ipmb.o: Registered IPMI interface %d with version %d.%d\n", + ipmi_intf, ipmi_version_major, ipmi_version_minor); + interfaces++; + + ipmb_get_all_channel_info(); +} + +static void ipmb_new_smi(int if_num) +{ + ipmb_register(if_num); +} + +static void ipmb_smi_gone(int if_num) +{ + if (interfaces >= 1) { +/* + i2c_del_adapter(&i2c_ipmb_adapter); +*/ + ipmi_destroy_user(i2c_ipmb_user); + interfaces--; + } +} + +static struct ipmi_smi_watcher smi_watcher = +{ + .new_smi = ipmb_new_smi, + .smi_gone = ipmb_smi_gone +}; + +int __init i2c_ipmb_init(void) +{ + int rv; + + printk(KERN_INFO "i2c-ipmb.o version %s (%s)\n", LM_VERSION, LM_DATE); + + rv = ipmi_smi_watcher_register(&smi_watcher); + if (rv) { + printk(KERN_WARNING + "ipmi_watchdog: can't register smi watcher\n"); + return rv; + } + + printk(KERN_INFO "i2c-ipmb.o: BMC access for i2c modules initialized.\n"); + return 0; +} + +int __init ipmi_cleanup(void) +{ + ipmi_smi_watcher_unregister(&smi_watcher); + ipmb_smi_gone(0); + return 0; +} + +#ifdef MODULE +MODULE_AUTHOR("M. D. Studebaker "); +MODULE_DESCRIPTION("IPMB-BMC access through i2c"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return i2c_ipmb_init(); +} + +int cleanup_module(void) +{ + return ipmi_cleanup(); +} + +#endif /* MODULE */