diff --git a/kernel/busses/Module.mk b/kernel/busses/Module.mk index a28b748f..0e1ef067 100644 --- a/kernel/busses/Module.mk +++ b/kernel/busses/Module.mk @@ -25,6 +25,9 @@ KERNELBUSSESDIR := $(MODULE_DIR) # defined value verbatim into the command-list of rules... # These targets are NOT included in 'mkpatch' ... KERNELBUSSESTARGETS := +ifeq ($(shell if grep -q '^CONFIG_IPMI_HANDLER=' $(LINUX)/.config; then echo 1; fi),1) +KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ipmi.o +endif KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis630.o KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis645.o diff --git a/kernel/busses/i2c-ipmi.c b/kernel/busses/i2c-ipmi.c new file mode 100644 index 00000000..2f327b24 --- /dev/null +++ b/kernel/busses/i2c-ipmi.c @@ -0,0 +1,259 @@ +/* + i2c-bmc.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 +*/ + +#include +#include +#include +#include +#include +#include +#include "version.h" + + +static void bmc_inc_use(struct i2c_adapter *adapter); +static void bmc_dec_use(struct i2c_adapter *adapter); +static u32 bmc_func(struct i2c_adapter *adapter); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_bmc_init(void); +static int __init bmc_cleanup(void); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* I2C Data */ +/* This is the actual algorithm we define */ +static struct i2c_algorithm bmc_algorithm = { + /* name */ "IPMI algorithm", + /* id */ I2C_ALGO_IPMI, + /* master_xfer */ NULL, + /* smbus_access */ NULL, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ &bmc_func, +}; + +static struct i2c_adapter bmc_adapter = { + /* name */ "IPMI adapter", + /* id */ I2C_ALGO_IPMI | I2C_HW_IPMI, + /* algorithm */ &bmc_algorithm, + /* algo_data */ NULL, + /* inc_use */ &bmc_inc_use, + /* dec_use */ &bmc_dec_use, + /* data */ NULL, + /* Other fields not initialized */ +}; + +/* IPMI Data */ +static ipmi_user_t i2c_bmc_user; +static ipmi_user_t i2c_ipmi_user; +static unsigned char ipmi_version_major; +static unsigned char ipmi_version_minor; +static const char msgdata[IPMI_MAX_ADDR_SIZE]; /* ?? */ +static struct ipmi_addr address = { + IPMI_SYSTEM_INTERFACE_ADDR_TYPE, + IPMI_BMC_CHANNEL, + NULL +}; /* send address */ +static struct ipmi_msg message; /* send message */ +static long msgid; /* message ID */ +static int interfaces; +static int (*rcv_callback)(struct i2c_client *client,unsigned int cmd, void *arg); + +/* Used in bmc_init/cleanup */ +/* +static int __initdata bmc_initialized; +*/ + +void bmc_inc_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void bmc_dec_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* We can't do a thing... */ +static u32 bmc_func(struct i2c_adapter *adapter) +{ + return 0; +} + +/*-----------------------------------*/ + +void bmc_i2c_send_message(int id, struct ipmi_msg * msg) +{ + ipmi_request(i2c_bmc_user, &address, id, msg, 0); +} + +void bmcclient_i2c_send_message(struct i2c_client *client, int id, struct ipmi_msg * msg) +{ + id = (id & 0xffff) | 0x10000; + rcv_callback = client->driver->command; + bmc_i2c_send_message(id, msg); +} + + +static void ipmi_i2c_msg_handler(struct ipmi_recv_msg *msg, + void *handler_data) +{ + int rcvid = msg->msgid & 0xffff; + int client = (msg->msgid >> 16) & 0xf; + + if (msg->msg.data[0] != 0) { + printk(KERN_ERR "IPMI BMC response: Error 0x%x on cmd 0x%x\n", + msg->msg.data[0], + msg->msg.cmd); + } else { +/* + printk(KERN_INFO "IPMI BMC response: No error on cmd 0x%x\n", + msg->msg.cmd); +*/ + } + + + if(client == 1 && rcv_callback != NULL) + (*rcv_callback)(NULL, client, msg); + else + ipmi_free_recv_msg(msg); +} + +static struct ipmi_user_hndl ipmi_hndlrs = +{ + .ipmi_recv_hndl = ipmi_i2c_msg_handler, +}; + +/* callback for each BMC found */ +static void ipmi_register_bmc(int ipmi_intf) +{ + unsigned long flags; + int rv = -EBUSY; + + if(interfaces > 0) { /* 1 max for now */ + printk(KERN_INFO + "i2c-bmc.o: Additional IPMI interface %d not supported\n", + ipmi_intf); + return; + } + + rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &i2c_bmc_user); + if (rv < 0) { + printk(KERN_ERR "i2c-bmc.o: Unable to register with ipmi\n"); + return; + } + + ipmi_get_version(i2c_bmc_user, + &ipmi_version_major, + &ipmi_version_minor); + + if ((rv = i2c_add_adapter(&bmc_adapter))) { + printk(KERN_ERR "i2c-bmc.o: Adapter registration failed, " + "module i2c-bmc.o is not inserted\n."); + return; + } + + printk(KERN_INFO + "i2c-bmc.o: Registered IPMI interface %d with version %d.%d\n", + ipmi_intf, ipmi_version_major, ipmi_version_minor); + interfaces++; +} + + +static void ipmi_new_smi(int if_num) +{ + ipmi_register_bmc(if_num); +} + +static void ipmi_smi_gone(int if_num) +{ + /* TBD */ +} + +static struct ipmi_smi_watcher smi_watcher = +{ + .new_smi = ipmi_new_smi, + .smi_gone = ipmi_smi_gone +}; + +int __init i2c_bmc_init(void) +{ + int rv; + + printk(KERN_INFO "i2c-bmc.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-bmc.o: BMC access for i2c modules initialized.\n"); + return 0; +} + +int __init bmc_cleanup(void) +{ + ipmi_smi_watcher_unregister(&smi_watcher); + if (interfaces >= 1) { + i2c_del_adapter(&bmc_adapter); + ipmi_destroy_user(i2c_bmc_user); + interfaces--; + } + return 0; +} + +#ifdef MODULE +EXPORT_SYMBOL(bmcclient_i2c_send_message); + +MODULE_AUTHOR("M. D. Studebaker "); +MODULE_DESCRIPTION("BMC bus access through i2c"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return i2c_bmc_init(); +} + +int cleanup_module(void) +{ + return bmc_cleanup(); +} + +#endif /* MODULE */ diff --git a/kernel/chips/Module.mk b/kernel/chips/Module.mk index 086d688c..e931d9f0 100644 --- a/kernel/chips/Module.mk +++ b/kernel/chips/Module.mk @@ -25,6 +25,9 @@ KERNELCHIPSDIR := $(MODULE_DIR) # defined value verbatim into the command-list of rules... # These targets are NOT included in 'mkpatch' ... KERNELCHIPSTARGETS := +ifeq ($(shell if grep -q '^CONFIG_IPMI_HANDLER=' $(LINUX)/.config; then echo 1; fi),1) +KERNELCHIPSTARGETS += $(MODULE_DIR)/bmcsensors.o +endif KERNELCHIPSTARGETS += $(MODULE_DIR)/smartbatt.o KERNELCHIPSTARGETS += $(MODULE_DIR)/smbus-arp.o KERNELCHIPSTARGETS += $(MODULE_DIR)/vt8231.o diff --git a/kernel/chips/bmcsensors.c b/kernel/chips/bmcsensors.c new file mode 100644 index 00000000..6236d422 --- /dev/null +++ b/kernel/chips/bmcsensors.c @@ -0,0 +1,905 @@ +/* + bmcsensors.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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "version.h" +#include "sensors.h" +#include + +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +SENSORS_INSMOD_1(bmcsensors); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +struct bmcsensors_data { + struct semaphore lock; + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 alarms; +}; + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_bmcsensors_init(void); +static int __init bmcsensors_cleanup(void); + +static int bmcsensors_attach_adapter(struct i2c_adapter *adapter); +static int bmcsensors_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int bmcsensors_detach_client(struct i2c_client *client); +static int bmcsensors_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void bmcsensors_inc_use(struct i2c_client *client); +static void bmcsensors_dec_use(struct i2c_client *client); + +static void bmcsensors_update_client(struct i2c_client *client); +static void bmcsensors_init_client(struct i2c_client *client); +static int bmcsensors_find(int *address); + + +static void bmcsensors_all(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bmcsensors_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bmcsensors_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bmcsensors_pwm(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +void bmcsensors_get_sdr(u16 resid, u16 record, u8 offset); +void bmcsensors_get_reading(struct i2c_client *client, int i); + +static int bmcsensors_id = 0; + +static struct i2c_driver bmcsensors_driver = { + /* name */ "BMC Sensors driver", + /* id */ I2C_DRIVERID_BMCSENSORS, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &bmcsensors_attach_adapter, + /* detach_client */ &bmcsensors_detach_client, + /* command */ &bmcsensors_command, + /* inc_use */ &bmcsensors_inc_use, + /* dec_use */ &bmcsensors_dec_use +}; + +static struct bmcsensors_data bmc_data; +struct i2c_client bmc_client = { + "BMC Sensors", + 1, /* fake should be 0 */ + 0, + 0, + NULL, /* adapter */ + &bmcsensors_driver, + & bmc_data, + 0 +}; + +static bmcsensors_initialized; + + +#define MAX_SDR_ENTRIES 50 +#define SDR_LIMITS 13 +#define SDR_MAX_ID_LENGTH 16 +#define SDR_MAX_UNPACKED_ID_LENGTH ((SDR_MAX_ID_LENGTH * 4 / 3) + 2) +struct sdrdata { + /* reverse lookup from sysctl */ + int sysctl; + /* retrieved from SDR, not expected to change */ + u8 stype; + u8 number; + u8 linear; + s16 m; + s16 b; + u8 k; + u8 limits[SDR_LIMITS]; + u8 string_type; + u8 id_length; + u8 id[SDR_MAX_ID_LENGTH]; + /* retrieved from reading */ + u8 reading; + u8 status; + u8 thresholds; +}; +static struct sdrdata sdrd[MAX_SDR_ENTRIES]; +static int sdrd_count; + +#define MAX_PROC_ENTRIES (MAX_SDR_ENTRIES + 5) +#define MAX_PROCNAME_SIZE 8 +static ctl_table *bmcsensors_dir_table; +static char *bmcsensors_proc_name_pool; + +#define IPMI_SDR_SIZE 67 +static int ipmi_sdr_partial_size = IPMI_SDR_SIZE; +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 */ +int rx_msg_data_offset; +static long msgid; /* message ID */ +static u16 resid; + +enum states {STATE_INIT, STATE_RESERVE, STATE_SDR, STATE_SDRPARTIAL, STATE_READING, STATE_DONE}; +int state; +static int receive_counter; + + +/* IPMI Message defs */ +/* Network Function Codes */ +#define IPMI_NETFN_SENSOR 0x04 +#define IPMI_NETFN_STORAGE 0x0A +/* Commands */ +#define IPMI_RESERVE_SDR 0x22 +#define IPMI_GET_SDR 0x23 +#define IPMI_GET_SENSOR_STATE_READING 0x2D + +/* SDR defs */ +#define STYPE_TEMP 0x01 +#define STYPE_VOLT 0x02 +#define STYPE_CURR 0x03 +#define STYPE_FAN 0x04 + +/* get rid of this ***************/ +#define STYPE_MAX 0x29 /* the last sensor type we are interested in */ +static u8 bmcs_count[STYPE_MAX + 1] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static const u8 bmcs_max[STYPE_MAX + 1] = { +0, 10, 10, 10, 10, 0, 0, 0}; /* more zeros*/ + +/************************************/ + + +/* unpack based on string type, convert to normal, null terminate */ +static void ipmi_sprintf(u8 * to, u8 * from, u8 type, u8 length) +{ + static const u8 *bcdplus = "0123456789 -.:,_"; + int i; + + switch (type) { + case 0: /* unicode */ + for(i = 0; i < length; i++) + *to++ = *from++ & 0x7f; + *to = 0; + break; + case 1: /* BCD Plus */ + for(i = 0; i < length; i++) + *to++ = bcdplus[*from++ & 0x0f]; + *to = 0; + break; + case 2: /* packed ascii */ /* if not a mult. of 3 this will run over */ + for(i = 0; i < length; i += 3) { + *to++ = *from & 0x3f; + *to++ = *from++ >> 6 | ((*from & 0xf) << 2); + *to++ = *from++ >> 4 | ((*from & 0x3) << 4); + *to++ = (*from++ >> 2) & 0x3f; + } + *to = 0; + break; + case 3: /* normal */ + if(length > 1) + memcpy(to, from, length); + to[length] = 0; + break; + } +} + + + + + +/* After we have received all the SDR entries and picked out the ones + we are interested in, build a table of the /proc entries and register. +*/ +static void bmcsensors_build_proc_table() +{ + int i; + int temps = 0, volts = 0, currs = 0, fans = 0; + u8 id[SDR_MAX_UNPACKED_ID_LENGTH]; + + if(!(bmcsensors_dir_table = kmalloc((sdrd_count + 1) * sizeof(struct ctl_table), GFP_KERNEL))) { + printk(KERN_ERR "bmcsensors.o: no memory\n"); + return; /* do more than this */ /* ^^ add 1 or more for alarms, etc. */ + } + if(!(bmcsensors_proc_name_pool = kmalloc((sdrd_count + 0) * MAX_PROCNAME_SIZE, GFP_KERNEL))) { + kfree(bmcsensors_dir_table); + printk(KERN_ERR "bmcsensors.o: no memory\n"); + return; /* do more than this */ /* ^^ add 1 or more for alarms, etc. */ + } + + for(i = 0; i < sdrd_count; i++) { + bmcsensors_dir_table[i].procname = bmcsensors_proc_name_pool + (i * MAX_PROCNAME_SIZE); + bmcsensors_dir_table[i].data = NULL; + bmcsensors_dir_table[i].maxlen = 0; + bmcsensors_dir_table[i].mode = 0644; + bmcsensors_dir_table[i].child = NULL; + bmcsensors_dir_table[i].proc_handler = &i2c_proc_real; + bmcsensors_dir_table[i].strategy = &i2c_sysctl_real; + bmcsensors_dir_table[i].de = NULL; + + switch(sdrd[i].stype) { + case(STYPE_TEMP) : + bmcsensors_dir_table[i].ctl_name = BMCSENSORS_SYSCTL_TEMP1 + temps; + sprintf((char *)bmcsensors_dir_table[i].procname, "temp%d", ++temps); + bmcsensors_dir_table[i].extra1 = &bmcsensors_all; + break; + case(STYPE_VOLT) : + bmcsensors_dir_table[i].ctl_name = BMCSENSORS_SYSCTL_IN1 + volts; + sprintf((char *)bmcsensors_dir_table[i].procname, "in%d", ++volts); + bmcsensors_dir_table[i].extra1 = &bmcsensors_all; + break; + case(STYPE_CURR) : + bmcsensors_dir_table[i].ctl_name = BMCSENSORS_SYSCTL_CURR1 + currs; + sprintf((char *)bmcsensors_dir_table[i].procname, "curr%d", ++currs); + bmcsensors_dir_table[i].extra1 = &bmcsensors_all; + break; + case(STYPE_FAN) : + bmcsensors_dir_table[i].ctl_name = BMCSENSORS_SYSCTL_FAN1 + fans; + sprintf((char *)bmcsensors_dir_table[i].procname, "fan%d", ++fans); + bmcsensors_dir_table[i].extra1 = &bmcsensors_all; + break; + default: /* ?? */ + printk(KERN_INFO "bmcsensors.o: unk stype\n"); + continue; + } + sdrd[i].sysctl = bmcsensors_dir_table[i].ctl_name; + if(sdrd[i].linear != 0) { + printk(KERN_INFO "bmcsensors.o: sensor %d: (type 0x%x) nonlinear function 0x%.2x unsupported\n", + i, sdrd[i].stype, sdrd[i].linear); + } + ipmi_sprintf(id, sdrd[i].id, sdrd[i].string_type, sdrd[i].id_length); + printk(KERN_INFO "bmcsensors.o: registering sensor %d: '%s' (type 0x%x) as %s " + "(m=%d; b=%d; k1=%d; k2=%d)\n", + i, id, sdrd[i].stype, bmcsensors_dir_table[i].procname, + sdrd[i].m, sdrd[i].b,sdrd[i].k & 0xf, sdrd[i].k >> 4); + } + bmcsensors_dir_table[sdrd_count].ctl_name = 0; + + if ((i = i2c_register_entry(&bmc_client, "bmc", + bmcsensors_dir_table, + THIS_MODULE)) < 0) { + printk(KERN_INFO "bmcsensors.o: i2c registration failed.\n"); + kfree(bmcsensors_dir_table); + kfree(bmcsensors_proc_name_pool); + return; + } + bmcsensors_initialized = 3; + bmc_data.sysctl_id = i; + + bmcsensors_init_client(&bmc_client); + printk(KERN_INFO "bmcsensors.o: registered %d temp, %d volt, %d current, %d fan sensors\n", + temps, volts, currs, fans); + bmcsensors_update_client(&bmc_client); +} + + +/* Process a sensor reading response */ +static int bmcsensors_rcv_reading_msg(struct ipmi_msg *msg) +{ + if(receive_counter >= sdrd_count) { + /* shouldn't happen */ + receive_counter = 0; + return STATE_DONE; + } + sdrd[receive_counter].reading = msg->data[1]; + sdrd[receive_counter].status = msg->data[2]; + sdrd[receive_counter].thresholds = msg->data[3]; +#ifdef DEBUG + printk(KERN_DEBUG "bmcsensors.o: sensor %d (type %d) reading %d\n", + receive_counter, sdrd[receive_counter].stype, msg->data[1]); +#endif + if(++receive_counter >= sdrd_count) { + receive_counter = 0; + return STATE_DONE; + } + bmcsensors_get_reading(&bmc_client, receive_counter); /* don't really need to pass client */ + return STATE_READING; +} + +/* Process a SDR response */ +static int bmcsensors_rcv_sdr_msg(struct ipmi_msg *msg, int state) +{ + u16 record, nextrecord; + int version, type, length, owner, lun, number, entity, instance, init; + int capab, stype, code; + int id_length; + int i; + int rstate = STATE_SDR; + struct ipmi_msg txmsg; + unsigned char * data; + + + if(msg->data[0] != 0) { + /* cut request in half and try again */ + ipmi_sdr_partial_size /= 2; + if(ipmi_sdr_partial_size < 8) { + printk(KERN_INFO "bmcsensors.o: IPMI buffers too small, giving up\n"); + return STATE_DONE; + } + printk(KERN_INFO "bmcsensors.o: Reducing SDR request size to %d\n", ipmi_sdr_partial_size); + bmcsensors_get_sdr(resid, 0, 0); + return STATE_SDR; + } + if(ipmi_sdr_partial_size < IPMI_SDR_SIZE) { + if(rx_msg_data_offset == 0) { + memcpy(rx_msg_data, msg->data, ipmi_sdr_partial_size + 3); + rx_msg_data_offset = ipmi_sdr_partial_size + 3; + } else { + memcpy(rx_msg_data + rx_msg_data_offset, msg->data + 3, ipmi_sdr_partial_size); + rx_msg_data_offset += ipmi_sdr_partial_size; + } + if(rx_msg_data_offset > IPMI_SDR_SIZE + 3) { + /* got last chunk */ + rx_msg_data_offset = 0; + data = rx_msg_data; + } else { + /* get more */ + record = (rx_msg_data[4] << 8) | rx_msg_data[3]; + bmcsensors_get_sdr(resid, record, rx_msg_data_offset - 3); + return STATE_SDR; + } + } else { + data = msg->data; /* got it in one chunk */ + } + + nextrecord = (data[2] << 8) | data[1]; + version = data[5]; + type = data[6]; + if(type == 1 || type == 2) { /* known SDR type */ + owner = data[8]; + lun = data[9]; + entity = data[11]; + init = data[13]; + capab = data[14]; + stype = data[15]; + + if(stype <= STYPE_MAX) { /* known sensor type */ + if(bmcs_count[stype] >= bmcs_max[stype]) { + if(bmcs_max[stype] > 0) { + printk(KERN_INFO "bmcsensors.o: Limit of %d exceeded for sensor type 0x%x\n", bmcs_max[stype], stype); +#ifdef DEBUG + } else { + printk(KERN_INFO "bmcsensors.o: Ignoring unsupported sensor type 0x%x\n", stype); +#endif + } + } else { + if(sdrd_count >= MAX_SDR_ENTRIES) { + printk(KERN_INFO "bmcsensors.o: Limit of %d exceeded for total sensors\n", MAX_SDR_ENTRIES); + } else { + /* add SDR database entry */ + sdrd[sdrd_count].stype = stype; + sdrd[sdrd_count].number = data[10]; + if(type == 1) { + sdrd[sdrd_count].linear = data[26] & 0x7f; + sdrd[sdrd_count].m = data[27]; + sdrd[sdrd_count].m |= ((u16) (data[28] & 0xc0)) << 2; + if(sdrd[sdrd_count].m & 0x0200) + sdrd[sdrd_count].m |= 0xfc00; /* sign extend */ + sdrd[sdrd_count].b = data[29]; + sdrd[sdrd_count].b |= ((u16) (data[30] & 0xc0)) << 2; + if(sdrd[sdrd_count].b & 0x0200) + sdrd[sdrd_count].b |= 0xfc00; /* sign extend */ + sdrd[sdrd_count].k = data[32]; + for(i = 0; i < SDR_LIMITS; i++) + sdrd[sdrd_count].limits[i] = data[34 + i]; + sdrd[sdrd_count].string_type = data[50] >> 6; + id_length = data[50] & 0x1f; + if(id_length > 0) + memcpy(sdrd[sdrd_count].id, &data[51], id_length); + sdrd[sdrd_count].id_length = id_length; + } else { + sdrd[sdrd_count].m = 1; + sdrd[sdrd_count].b = 0; + sdrd[sdrd_count].k = 0; + sdrd[sdrd_count].string_type = data[34] >> 6; + id_length = data[34] & 0x1f; + if(id_length > 0) + memcpy(sdrd[sdrd_count].id, &data[35], id_length); + sdrd[sdrd_count].id_length = id_length; + } + bmcs_count[stype]++; + sdrd_count++; + } + } + } +/* + } else { + printk(KERN_INFO "bmcsensors.o: Ignoring sensor type 0x%x\n", stype); + } +*/ + /* + printk(KERN_INFO "bmcsensors.o: STATE_SDR: record = 0x%x; version = 0x%x, type = 0x%x; length=0x%x\n", + record, version, type, length); + printk(KERN_INFO "bmcsensors.o: STATE_SDR: owner = 0x%x; lun = 0x%x, number = 0x%x; entity=0x%x\n", + owner, lun, number, entity); + printk(KERN_INFO "bmcsensors.o: STATE_SDR: instance = 0x%x; init = 0x%x, capab = 0x%x; stype=0x%x\n", + instance, init, capab, stype); + printk(KERN_INFO "bmcsensors.o: STATE_SDR: code = 0x%x\n", + code); + printk(KERN_INFO "bmcsensors.o: STATE_SDR: next = 0x%x\n", nextrecord); + } else { + printk(KERN_INFO "bmcsensors.o: unknown STATE_SDR type 0x%x\n", type); + */ + } + + if(nextrecord == 0xFFFF) { + rstate = STATE_READING; + if(sdrd_count == 0) { + printk(KERN_INFO "bmcsensors.o: No recognized sensors found.\n"); + } else { + printk(KERN_INFO "bmcsensors.o: found %d temp, %d volt, %d current, %d fan sensors\n", +bmcs_count[1], bmcs_count[2], bmcs_count[3], bmcs_count[4]); + bmcsensors_build_proc_table(); + } + } else { + bmcsensors_get_sdr(resid, nextrecord, 0); + } + return rstate; +} + +/* Process incoming messages based on internal state */ +static void bmcsensors_rcv_msg(struct ipmi_msg *msg) +{ + + switch(state) { + case STATE_INIT: + case STATE_RESERVE: + resid = (msg->data[2] << 8) || msg->data[1]; + bmcsensors_get_sdr(resid, 0, 0); + state = STATE_SDR; + break; + + case STATE_SDR: + case STATE_SDRPARTIAL: + state = bmcsensors_rcv_sdr_msg(msg, state); + break; + + + case STATE_READING: + state = bmcsensors_rcv_reading_msg(msg); + break; + + case STATE_DONE: + break; + + default: + state = STATE_INIT; + } +} + + +/* Incoming message handler */ +static void bmcsensors_msg_handler(struct ipmi_recv_msg *msg, + void * handler_data) +{ + if (msg->msg.data[0] != 0 && msg->msg.data[0] != 0xca) { + printk(KERN_ERR "BMCsensors response: Error %x on cmd %x; state = %d; probably fatal.\n", + msg->msg.data[0], msg->msg.cmd, state); + } else { + bmcsensors_rcv_msg(&(msg->msg)); + } + ipmi_free_recv_msg(msg); +} + +/************** Message Sending **************/ + +/* Send an IPMI message */ +static void bmcsensors_send_message(struct ipmi_msg * msg) +{ +#ifdef DEBUG + printk(KERN_INFO "bmcsensors.o: Send BMC msg, cmd: 0x%x\n", + msg->cmd); +#endif + bmcclient_i2c_send_message(&bmc_client, msgid++, msg); + +} + +/* Compose and send a "reserve SDR" message */ +void bmcsensors_reserve_sdr(void) +{ + tx_message.netfn = IPMI_NETFN_STORAGE; + tx_message.cmd = IPMI_RESERVE_SDR; + tx_message.data_len = 0; + tx_message.data = NULL; + bmcsensors_send_message(&tx_message); +} + +/* Componse and send a "get SDR" message */ +void bmcsensors_get_sdr(u16 resid, u16 record, u8 offset) +{ + tx_message.netfn = IPMI_NETFN_STORAGE; + tx_message.cmd = IPMI_GET_SDR; + tx_message.data_len = 6; + tx_message.data = tx_msg_data; + tx_msg_data[0] = resid & 0xff; + tx_msg_data[1] = resid >> 8; + tx_msg_data[2] = record & 0xff; + tx_msg_data[3] = record >> 8; + tx_msg_data[4] = offset; + tx_msg_data[5] = ipmi_sdr_partial_size; + bmcsensors_send_message(&tx_message); +} + +/* Compose and send a "get sensor reading" message */ +void bmcsensors_get_reading(struct i2c_client *client, int i) +{ + tx_message.netfn = IPMI_NETFN_SENSOR; + tx_message.cmd = IPMI_GET_SENSOR_STATE_READING; + tx_message.data_len = 1; + tx_message.data = tx_msg_data; + tx_msg_data[0] = sdrd[i].number; + bmcsensors_send_message(&tx_message); +} + +/**************** Initialization ****************/ + +int bmcsensors_attach_adapter(struct i2c_adapter *adapter) +{ +/* + not really i2c or isa so i2c_detect not required +*/ + printk(KERN_INFO "bmcsensors.o: in bmcsensors_detect() for ID %x\n", + adapter->algo->id); + + if(adapter->algo->id != I2C_ALGO_IPMI) { + return 0; + } + + if(bmcsensors_initialized >= 2) { + printk(KERN_INFO "bmcsensors.o: limit of 1\n"); + return 0; + } + + return bmcsensors_detect(adapter, 0, 0, 0); +} + +int bmcsensors_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int err, i; + + bmc_client.id = 0; + bmc_client.adapter = adapter; + bmc_data.valid = 0; +/* + init_MUTEX(bmc_data.update_lock); +*/ + + if ((err = i2c_attach_client(&bmc_client))) { + printk(KERN_ERR "attach client error in bmcsensors_detect()\n"); + return err; + } + bmcsensors_initialized = 2; + + /* initialize some key data */ + state = STATE_INIT; + sdrd_count = 0; + receive_counter = 0; + rx_msg_data_offset = 0; + ipmi_sdr_partial_size = IPMI_SDR_SIZE; + for(i = 0; i <= STYPE_MAX; i++) + bmcs_count[i] = 0; + + /* send our first message, which kicks things off */ + bmcsensors_reserve_sdr(); + /* don't call i2c_register_entry until we scan the SDR's */ + return 0; +} + +int bmcsensors_detach_client(struct i2c_client *client) +{ + int err; + + if(bmcsensors_initialized >= 3) { + kfree(bmcsensors_dir_table); + kfree(bmcsensors_proc_name_pool); + i2c_deregister_entry(((struct bmcsensors_data *) (client->data))-> + sysctl_id); + } + + if ((err = i2c_detach_client(client))) { +/* + printk + ("bmcsensors.o: Client deregistration failed, client not detached.\n"); +*/ + return err; + } + + return 0; +} + +int bmcsensors_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + bmcsensors_msg_handler((struct ipmi_recv_msg *) arg, NULL); + return 0; +} + +void bmcsensors_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void bmcsensors_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +void bmcsensors_init_client(struct i2c_client *client) +{ +/*******************************************/ + + + +} + +void bmcsensors_update_client(struct i2c_client *client) +{ + struct bmcsensors_data *data = client->data; + int i; + +/* + down(&data->update_lock); +*/ + + if ((jiffies - data->last_updated > 3 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + /* don't start an update cycle if one already in progress */ + if(state != STATE_READING) { + state = STATE_READING; + printk(KERN_DEBUG "bmcsensors.o: Starting update\n"); + bmcsensors_get_reading(client, 0); + } + } + +/* + up(&data->update_lock); +*/ +} + + +/************* /proc callback helper functions *********/ + +/* need better way to map from sysctl to sdrd record number */ +int find_sdrd(int sysctl) +{ + int i; + + for(i = 0; i < sdrd_count; i++) + if(sdrd[i].sysctl == sysctl) + return i; + return -1; +} + +/* IPMI V1.5 Section 30 */ +static const int exps[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000}; + +static int decplaces(i) +{ + u8 k2; + + k2 = sdrd[i].k >> 4; + if(k2 < 8) + return 0; + else + return 16 - k2; +} + +static long convert_reading(i) +{ + u8 k1, k2; + long r; + + k1 = sdrd[i].k & 0x0f; + k2 = sdrd[i].k >> 4; + + r = sdrd[i].reading * sdrd[i].m; + if(k1 < 8) + r += sdrd[i].b * exps[k1]; + else + r += sdrd[i].b / exps[16 - k1]; + if(k2 < 8) + r *= exps[k2]; +/* + taken care of by nrels_mag + else + r /= exps[16 - k2]; +*/ + return r; +} + + +/************** /proc callbacks *****************/ + +void bmcsensors_all(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int i; + struct bmcsensors_data *data = client->data; + int nr = ctl_name - BMCSENSORS_SYSCTL_TEMP1 + 1; + + if((i = find_sdrd(ctl_name)) < 0) { + *nrels_mag = 0; + return; + } + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = decplaces(i); + else if (operation == SENSORS_PROC_REAL_READ) { + bmcsensors_update_client(client); + results[0] = convert_reading(i); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + } + } +} + +void bmcsensors_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bmcsensors_data *data = client->data; +#if 0 + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bmcsensors_update_client(client); + results[0] = data->alarms; + *nrels_mag = 1; + } +#endif +} + +void bmcsensors_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct bmcsensors_data *data = client->data; + int old; + +#if 0 + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bmcsensors_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = bmcsensors_read_value(client, BMCSENSORS_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + bmcsensors_write_value(client, BMCSENSORS_REG_FANDIV, old); + } + } +#endif +} + +void bmcsensors_pwm(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bmcsensors_data *data = client->data; +#if 0 + int nr = 1 + ctl_name - BMCSENSORS_SYSCTL_PWM1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bmcsensors_update_client(client); + results[0] = PWM_FROM_REG(data->pwm[nr - 1]); + results[1] = data->pwm[nr - 1] >> 7; + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->pwm[nr - 1] &= 0x80; + data->pwm[nr - 1] |= PWM_TO_REG(results[0]); + if (*nrels_mag >= 2) { + if(results[1] && (!(data->pwm[nr-1] & 0x80))) { + /* output PWM */ + bmcsensors_write_value(client, + BMCSENSORS_REG_PPIN(nr), 0x04); + data->pwm[nr - 1] |= 0x80; + } + } + bmcsensors_write_value(client, BMCSENSORS_REG_PWM(nr), + data->pwm[nr - 1]); + } + } +#endif +} + +int __init sensors_bmcsensors_init(void) +{ + int res, addr; + + printk(KERN_INFO "bmcsensors.o version %s (%s)\n", LM_VERSION, LM_DATE); + bmcsensors_initialized = 0; + + if ((res = i2c_add_driver(&bmcsensors_driver))) { + printk(KERN_ERR + "bmcsensors.o: Driver registration failed, module not inserted.\n"); + bmcsensors_cleanup(); + return res; + } + bmcsensors_initialized = 1; + return 0; +} + +int __init bmcsensors_cleanup(void) +{ + int res; + + if (bmcsensors_initialized >= 1) { + if ((res = i2c_del_driver(&bmcsensors_driver))) { + printk(KERN_WARNING + "bmcsensors.o: Driver deregistration failed, module not removed.\n"); + return res; + } + bmcsensors_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Mark D. Studebaker "); +MODULE_DESCRIPTION("IPMI BMC sensors"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return sensors_bmcsensors_init(); +} + +int cleanup_module(void) +{ + return bmcsensors_cleanup(); +} + +#endif /* MODULE */ diff --git a/kernel/include/sensors.h b/kernel/include/sensors.h index 4f1d7559..9f2695cd 100644 --- a/kernel/include/sensors.h +++ b/kernel/include/sensors.h @@ -683,5 +683,11 @@ #define SMARTBATT_SYSCTL_ALARMS 1005 #define SMARTBATT_SYSCTL_CHARGE 1006 +#define BMCSENSORS_SYSCTL_IN1 1000 +#define BMCSENSORS_SYSCTL_TEMP1 1100 +#define BMCSENSORS_SYSCTL_CURR1 1200 +#define BMCSENSORS_SYSCTL_FAN1 1300 +#define BMCSENSORS_SYSCTL_ALARMS 5000 + #endif /* def SENSORS_SENSORS_H */