157c7c3a0SDarrick J. Wong /* 257c7c3a0SDarrick J. Wong * A hwmon driver for the IBM PowerExecutive temperature/power sensors 357c7c3a0SDarrick J. Wong * Copyright (C) 2007 IBM 457c7c3a0SDarrick J. Wong * 557c7c3a0SDarrick J. Wong * Author: Darrick J. Wong <djwong@us.ibm.com> 657c7c3a0SDarrick J. Wong * 757c7c3a0SDarrick J. Wong * This program is free software; you can redistribute it and/or modify 857c7c3a0SDarrick J. Wong * it under the terms of the GNU General Public License as published by 957c7c3a0SDarrick J. Wong * the Free Software Foundation; either version 2 of the License, or 1057c7c3a0SDarrick J. Wong * (at your option) any later version. 1157c7c3a0SDarrick J. Wong * 1257c7c3a0SDarrick J. Wong * This program is distributed in the hope that it will be useful, 1357c7c3a0SDarrick J. Wong * but WITHOUT ANY WARRANTY; without even the implied warranty of 1457c7c3a0SDarrick J. Wong * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1557c7c3a0SDarrick J. Wong * GNU General Public License for more details. 1657c7c3a0SDarrick J. Wong * 1757c7c3a0SDarrick J. Wong * You should have received a copy of the GNU General Public License 1857c7c3a0SDarrick J. Wong * along with this program; if not, write to the Free Software 1957c7c3a0SDarrick J. Wong * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2057c7c3a0SDarrick J. Wong */ 2157c7c3a0SDarrick J. Wong 2257c7c3a0SDarrick J. Wong #include <linux/ipmi.h> 2357c7c3a0SDarrick J. Wong #include <linux/module.h> 2457c7c3a0SDarrick J. Wong #include <linux/hwmon.h> 2557c7c3a0SDarrick J. Wong #include <linux/hwmon-sysfs.h> 2657c7c3a0SDarrick J. Wong #include <linux/jiffies.h> 2757c7c3a0SDarrick J. Wong #include <linux/mutex.h> 2857c7c3a0SDarrick J. Wong 2957c7c3a0SDarrick J. Wong #define REFRESH_INTERVAL (2 * HZ) 3057c7c3a0SDarrick J. Wong #define DRVNAME "ibmpex" 3157c7c3a0SDarrick J. Wong 3257c7c3a0SDarrick J. Wong #define PEX_GET_VERSION 1 3357c7c3a0SDarrick J. Wong #define PEX_GET_SENSOR_COUNT 2 3457c7c3a0SDarrick J. Wong #define PEX_GET_SENSOR_NAME 3 3557c7c3a0SDarrick J. Wong #define PEX_RESET_HIGH_LOW 4 3657c7c3a0SDarrick J. Wong #define PEX_GET_SENSOR_DATA 6 3757c7c3a0SDarrick J. Wong 3857c7c3a0SDarrick J. Wong #define PEX_NET_FUNCTION 0x3A 3957c7c3a0SDarrick J. Wong #define PEX_COMMAND 0x3C 4057c7c3a0SDarrick J. Wong 4157c7c3a0SDarrick J. Wong static inline u16 extract_value(const char *data, int offset) 4257c7c3a0SDarrick J. Wong { 4357c7c3a0SDarrick J. Wong return be16_to_cpup((u16 *)&data[offset]); 4457c7c3a0SDarrick J. Wong } 4557c7c3a0SDarrick J. Wong 4657c7c3a0SDarrick J. Wong #define TEMP_SENSOR 1 4757c7c3a0SDarrick J. Wong #define POWER_SENSOR 2 4857c7c3a0SDarrick J. Wong 4957c7c3a0SDarrick J. Wong #define PEX_SENSOR_TYPE_LEN 3 5057c7c3a0SDarrick J. Wong static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72}; 5157c7c3a0SDarrick J. Wong static u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D}; 5257c7c3a0SDarrick J. Wong 5357c7c3a0SDarrick J. Wong #define PEX_MULT_LEN 2 5457c7c3a0SDarrick J. Wong static u8 const watt_sensor_sig[] = {0x41, 0x43}; 5557c7c3a0SDarrick J. Wong 5657c7c3a0SDarrick J. Wong #define PEX_NUM_SENSOR_FUNCS 3 5757c7c3a0SDarrick J. Wong static char const * const power_sensor_name_templates[] = { 5857c7c3a0SDarrick J. Wong "%s%d_average", 5957c7c3a0SDarrick J. Wong "%s%d_average_lowest", 6057c7c3a0SDarrick J. Wong "%s%d_average_highest" 6157c7c3a0SDarrick J. Wong }; 6257c7c3a0SDarrick J. Wong static char const * const temp_sensor_name_templates[] = { 6357c7c3a0SDarrick J. Wong "%s%d_input", 6457c7c3a0SDarrick J. Wong "%s%d_input_lowest", 6557c7c3a0SDarrick J. Wong "%s%d_input_highest" 6657c7c3a0SDarrick J. Wong }; 6757c7c3a0SDarrick J. Wong 6857c7c3a0SDarrick J. Wong static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); 6957c7c3a0SDarrick J. Wong static void ibmpex_register_bmc(int iface, struct device *dev); 7057c7c3a0SDarrick J. Wong static void ibmpex_bmc_gone(int iface); 7157c7c3a0SDarrick J. Wong 7257c7c3a0SDarrick J. Wong struct ibmpex_sensor_data { 7357c7c3a0SDarrick J. Wong int in_use; 7457c7c3a0SDarrick J. Wong s16 values[PEX_NUM_SENSOR_FUNCS]; 7557c7c3a0SDarrick J. Wong int multiplier; 7657c7c3a0SDarrick J. Wong 7757c7c3a0SDarrick J. Wong struct sensor_device_attribute_2 attr[PEX_NUM_SENSOR_FUNCS]; 7857c7c3a0SDarrick J. Wong }; 7957c7c3a0SDarrick J. Wong 8057c7c3a0SDarrick J. Wong struct ibmpex_bmc_data { 8157c7c3a0SDarrick J. Wong struct list_head list; 8257c7c3a0SDarrick J. Wong struct device *hwmon_dev; 8357c7c3a0SDarrick J. Wong struct device *bmc_device; 8457c7c3a0SDarrick J. Wong struct mutex lock; 8557c7c3a0SDarrick J. Wong char valid; 8657c7c3a0SDarrick J. Wong unsigned long last_updated; /* In jiffies */ 8757c7c3a0SDarrick J. Wong 8857c7c3a0SDarrick J. Wong struct ipmi_addr address; 8957c7c3a0SDarrick J. Wong struct completion read_complete; 9057c7c3a0SDarrick J. Wong ipmi_user_t user; 9157c7c3a0SDarrick J. Wong int interface; 9257c7c3a0SDarrick J. Wong 9357c7c3a0SDarrick J. Wong struct kernel_ipmi_msg tx_message; 9457c7c3a0SDarrick J. Wong unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH]; 9557c7c3a0SDarrick J. Wong long tx_msgid; 9657c7c3a0SDarrick J. Wong 9757c7c3a0SDarrick J. Wong unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH]; 9857c7c3a0SDarrick J. Wong unsigned long rx_msg_len; 9957c7c3a0SDarrick J. Wong unsigned char rx_result; 10057c7c3a0SDarrick J. Wong int rx_recv_type; 10157c7c3a0SDarrick J. Wong 10257c7c3a0SDarrick J. Wong unsigned char sensor_major; 10357c7c3a0SDarrick J. Wong unsigned char sensor_minor; 10457c7c3a0SDarrick J. Wong 10557c7c3a0SDarrick J. Wong unsigned char num_sensors; 10657c7c3a0SDarrick J. Wong struct ibmpex_sensor_data *sensors; 10757c7c3a0SDarrick J. Wong }; 10857c7c3a0SDarrick J. Wong 10957c7c3a0SDarrick J. Wong struct ibmpex_driver_data { 11057c7c3a0SDarrick J. Wong struct list_head bmc_data; 11157c7c3a0SDarrick J. Wong struct ipmi_smi_watcher bmc_events; 11257c7c3a0SDarrick J. Wong struct ipmi_user_hndl ipmi_hndlrs; 11357c7c3a0SDarrick J. Wong }; 11457c7c3a0SDarrick J. Wong 11557c7c3a0SDarrick J. Wong static struct ibmpex_driver_data driver_data = { 11657c7c3a0SDarrick J. Wong .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data), 11757c7c3a0SDarrick J. Wong .bmc_events = { 11857c7c3a0SDarrick J. Wong .owner = THIS_MODULE, 11957c7c3a0SDarrick J. Wong .new_smi = ibmpex_register_bmc, 12057c7c3a0SDarrick J. Wong .smi_gone = ibmpex_bmc_gone, 12157c7c3a0SDarrick J. Wong }, 12257c7c3a0SDarrick J. Wong .ipmi_hndlrs = { 12357c7c3a0SDarrick J. Wong .ipmi_recv_hndl = ibmpex_msg_handler, 12457c7c3a0SDarrick J. Wong }, 12557c7c3a0SDarrick J. Wong }; 12657c7c3a0SDarrick J. Wong 12757c7c3a0SDarrick J. Wong static int ibmpex_send_message(struct ibmpex_bmc_data *data) 12857c7c3a0SDarrick J. Wong { 12957c7c3a0SDarrick J. Wong int err; 13057c7c3a0SDarrick J. Wong 13157c7c3a0SDarrick J. Wong err = ipmi_validate_addr(&data->address, sizeof(data->address)); 13257c7c3a0SDarrick J. Wong if (err) 13357c7c3a0SDarrick J. Wong goto out; 13457c7c3a0SDarrick J. Wong 13557c7c3a0SDarrick J. Wong data->tx_msgid++; 13657c7c3a0SDarrick J. Wong err = ipmi_request_settime(data->user, &data->address, data->tx_msgid, 13757c7c3a0SDarrick J. Wong &data->tx_message, data, 0, 0, 0); 13857c7c3a0SDarrick J. Wong if (err) 13957c7c3a0SDarrick J. Wong goto out1; 14057c7c3a0SDarrick J. Wong 14157c7c3a0SDarrick J. Wong return 0; 14257c7c3a0SDarrick J. Wong out1: 1432ecb044eSDarrick J. Wong dev_err(data->bmc_device, "request_settime=%x\n", err); 14457c7c3a0SDarrick J. Wong return err; 14557c7c3a0SDarrick J. Wong out: 1462ecb044eSDarrick J. Wong dev_err(data->bmc_device, "validate_addr=%x\n", err); 14757c7c3a0SDarrick J. Wong return err; 14857c7c3a0SDarrick J. Wong } 14957c7c3a0SDarrick J. Wong 15057c7c3a0SDarrick J. Wong static int ibmpex_ver_check(struct ibmpex_bmc_data *data) 15157c7c3a0SDarrick J. Wong { 15257c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_VERSION; 15357c7c3a0SDarrick J. Wong data->tx_message.data_len = 1; 15457c7c3a0SDarrick J. Wong ibmpex_send_message(data); 15557c7c3a0SDarrick J. Wong 15657c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete); 15757c7c3a0SDarrick J. Wong 15857c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len != 6) 15957c7c3a0SDarrick J. Wong return -ENOENT; 16057c7c3a0SDarrick J. Wong 16157c7c3a0SDarrick J. Wong data->sensor_major = data->rx_msg_data[0]; 16257c7c3a0SDarrick J. Wong data->sensor_minor = data->rx_msg_data[1]; 16357c7c3a0SDarrick J. Wong 1642ecb044eSDarrick J. Wong dev_info(data->bmc_device, "Found BMC with sensor interface " 16557c7c3a0SDarrick J. Wong "v%d.%d %d-%02d-%02d on interface %d\n", 16657c7c3a0SDarrick J. Wong data->sensor_major, 16757c7c3a0SDarrick J. Wong data->sensor_minor, 16857c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 2), 16957c7c3a0SDarrick J. Wong data->rx_msg_data[4], 17057c7c3a0SDarrick J. Wong data->rx_msg_data[5], 17157c7c3a0SDarrick J. Wong data->interface); 17257c7c3a0SDarrick J. Wong 17357c7c3a0SDarrick J. Wong return 0; 17457c7c3a0SDarrick J. Wong } 17557c7c3a0SDarrick J. Wong 17657c7c3a0SDarrick J. Wong static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data) 17757c7c3a0SDarrick J. Wong { 17857c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT; 17957c7c3a0SDarrick J. Wong data->tx_message.data_len = 1; 18057c7c3a0SDarrick J. Wong ibmpex_send_message(data); 18157c7c3a0SDarrick J. Wong 18257c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete); 18357c7c3a0SDarrick J. Wong 18457c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len != 1) 18557c7c3a0SDarrick J. Wong return -ENOENT; 18657c7c3a0SDarrick J. Wong 18757c7c3a0SDarrick J. Wong return data->rx_msg_data[0]; 18857c7c3a0SDarrick J. Wong } 18957c7c3a0SDarrick J. Wong 19057c7c3a0SDarrick J. Wong static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor) 19157c7c3a0SDarrick J. Wong { 19257c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_SENSOR_NAME; 19357c7c3a0SDarrick J. Wong data->tx_msg_data[1] = sensor; 19457c7c3a0SDarrick J. Wong data->tx_message.data_len = 2; 19557c7c3a0SDarrick J. Wong ibmpex_send_message(data); 19657c7c3a0SDarrick J. Wong 19757c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete); 19857c7c3a0SDarrick J. Wong 19957c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len < 1) 20057c7c3a0SDarrick J. Wong return -ENOENT; 20157c7c3a0SDarrick J. Wong 20257c7c3a0SDarrick J. Wong return 0; 20357c7c3a0SDarrick J. Wong } 20457c7c3a0SDarrick J. Wong 20557c7c3a0SDarrick J. Wong static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor) 20657c7c3a0SDarrick J. Wong { 20757c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_SENSOR_DATA; 20857c7c3a0SDarrick J. Wong data->tx_msg_data[1] = sensor; 20957c7c3a0SDarrick J. Wong data->tx_message.data_len = 2; 21057c7c3a0SDarrick J. Wong ibmpex_send_message(data); 21157c7c3a0SDarrick J. Wong 21257c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete); 21357c7c3a0SDarrick J. Wong 21457c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len < 26) { 2152ecb044eSDarrick J. Wong dev_err(data->bmc_device, "Error reading sensor %d.\n", 21657c7c3a0SDarrick J. Wong sensor); 21757c7c3a0SDarrick J. Wong return -ENOENT; 21857c7c3a0SDarrick J. Wong } 21957c7c3a0SDarrick J. Wong 22057c7c3a0SDarrick J. Wong return 0; 22157c7c3a0SDarrick J. Wong } 22257c7c3a0SDarrick J. Wong 22357c7c3a0SDarrick J. Wong static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data) 22457c7c3a0SDarrick J. Wong { 22557c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_RESET_HIGH_LOW; 22657c7c3a0SDarrick J. Wong data->tx_message.data_len = 1; 22757c7c3a0SDarrick J. Wong ibmpex_send_message(data); 22857c7c3a0SDarrick J. Wong 22957c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete); 23057c7c3a0SDarrick J. Wong 23157c7c3a0SDarrick J. Wong return 0; 23257c7c3a0SDarrick J. Wong } 23357c7c3a0SDarrick J. Wong 23457c7c3a0SDarrick J. Wong static void ibmpex_update_device(struct ibmpex_bmc_data *data) 23557c7c3a0SDarrick J. Wong { 23657c7c3a0SDarrick J. Wong int i, err; 23757c7c3a0SDarrick J. Wong 23857c7c3a0SDarrick J. Wong mutex_lock(&data->lock); 23957c7c3a0SDarrick J. Wong if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) && 24057c7c3a0SDarrick J. Wong data->valid) 24157c7c3a0SDarrick J. Wong goto out; 24257c7c3a0SDarrick J. Wong 24357c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++) { 24457c7c3a0SDarrick J. Wong if (!data->sensors[i].in_use) 24557c7c3a0SDarrick J. Wong continue; 24657c7c3a0SDarrick J. Wong err = ibmpex_query_sensor_data(data, i); 24757c7c3a0SDarrick J. Wong if (err) 24857c7c3a0SDarrick J. Wong continue; 24957c7c3a0SDarrick J. Wong data->sensors[i].values[0] = 25057c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 16); 25157c7c3a0SDarrick J. Wong data->sensors[i].values[1] = 25257c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 18); 25357c7c3a0SDarrick J. Wong data->sensors[i].values[2] = 25457c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 20); 25557c7c3a0SDarrick J. Wong } 25657c7c3a0SDarrick J. Wong 25757c7c3a0SDarrick J. Wong data->last_updated = jiffies; 25857c7c3a0SDarrick J. Wong data->valid = 1; 25957c7c3a0SDarrick J. Wong 26057c7c3a0SDarrick J. Wong out: 26157c7c3a0SDarrick J. Wong mutex_unlock(&data->lock); 26257c7c3a0SDarrick J. Wong } 26357c7c3a0SDarrick J. Wong 26457c7c3a0SDarrick J. Wong static struct ibmpex_bmc_data *get_bmc_data(int iface) 26557c7c3a0SDarrick J. Wong { 26657c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *p, *next; 26757c7c3a0SDarrick J. Wong 26857c7c3a0SDarrick J. Wong list_for_each_entry_safe(p, next, &driver_data.bmc_data, list) 26957c7c3a0SDarrick J. Wong if (p->interface == iface) 27057c7c3a0SDarrick J. Wong return p; 27157c7c3a0SDarrick J. Wong 27257c7c3a0SDarrick J. Wong return NULL; 27357c7c3a0SDarrick J. Wong } 27457c7c3a0SDarrick J. Wong 27557c7c3a0SDarrick J. Wong static ssize_t show_name(struct device *dev, struct device_attribute *devattr, 27657c7c3a0SDarrick J. Wong char *buf) 27757c7c3a0SDarrick J. Wong { 27857c7c3a0SDarrick J. Wong return sprintf(buf, "%s\n", DRVNAME); 27957c7c3a0SDarrick J. Wong } 28057c7c3a0SDarrick J. Wong static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); 28157c7c3a0SDarrick J. Wong 28257c7c3a0SDarrick J. Wong static ssize_t ibmpex_show_sensor(struct device *dev, 28357c7c3a0SDarrick J. Wong struct device_attribute *devattr, 28457c7c3a0SDarrick J. Wong char *buf) 28557c7c3a0SDarrick J. Wong { 28657c7c3a0SDarrick J. Wong struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 28757c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data = dev_get_drvdata(dev); 28857c7c3a0SDarrick J. Wong int mult = data->sensors[attr->index].multiplier; 28957c7c3a0SDarrick J. Wong ibmpex_update_device(data); 29057c7c3a0SDarrick J. Wong 29157c7c3a0SDarrick J. Wong return sprintf(buf, "%d\n", 29257c7c3a0SDarrick J. Wong data->sensors[attr->index].values[attr->nr] * mult); 29357c7c3a0SDarrick J. Wong } 29457c7c3a0SDarrick J. Wong 29557c7c3a0SDarrick J. Wong static ssize_t ibmpex_reset_high_low(struct device *dev, 29657c7c3a0SDarrick J. Wong struct device_attribute *devattr, 29757c7c3a0SDarrick J. Wong const char *buf, 29857c7c3a0SDarrick J. Wong size_t count) 29957c7c3a0SDarrick J. Wong { 30057c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data = dev_get_drvdata(dev); 30157c7c3a0SDarrick J. Wong 30257c7c3a0SDarrick J. Wong ibmpex_reset_high_low_data(data); 30357c7c3a0SDarrick J. Wong 30457c7c3a0SDarrick J. Wong return count; 30557c7c3a0SDarrick J. Wong } 30657c7c3a0SDarrick J. Wong 30757c7c3a0SDarrick J. Wong static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL, 30857c7c3a0SDarrick J. Wong ibmpex_reset_high_low, 0); 30957c7c3a0SDarrick J. Wong 31057c7c3a0SDarrick J. Wong static int is_power_sensor(const char *sensor_id, int len) 31157c7c3a0SDarrick J. Wong { 31257c7c3a0SDarrick J. Wong if (len < PEX_SENSOR_TYPE_LEN) 31357c7c3a0SDarrick J. Wong return 0; 31457c7c3a0SDarrick J. Wong 31557c7c3a0SDarrick J. Wong if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN)) 31657c7c3a0SDarrick J. Wong return 1; 31757c7c3a0SDarrick J. Wong return 0; 31857c7c3a0SDarrick J. Wong } 31957c7c3a0SDarrick J. Wong 32057c7c3a0SDarrick J. Wong static int is_temp_sensor(const char *sensor_id, int len) 32157c7c3a0SDarrick J. Wong { 32257c7c3a0SDarrick J. Wong if (len < PEX_SENSOR_TYPE_LEN) 32357c7c3a0SDarrick J. Wong return 0; 32457c7c3a0SDarrick J. Wong 32557c7c3a0SDarrick J. Wong if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN)) 32657c7c3a0SDarrick J. Wong return 1; 32757c7c3a0SDarrick J. Wong return 0; 32857c7c3a0SDarrick J. Wong } 32957c7c3a0SDarrick J. Wong 330df9cb033SDarrick J. Wong static int power_sensor_multiplier(struct ibmpex_bmc_data *data, 331df9cb033SDarrick J. Wong const char *sensor_id, int len) 33257c7c3a0SDarrick J. Wong { 33357c7c3a0SDarrick J. Wong int i; 33457c7c3a0SDarrick J. Wong 335df9cb033SDarrick J. Wong if (data->sensor_major == 2) 336df9cb033SDarrick J. Wong return 1000000; 337df9cb033SDarrick J. Wong 33857c7c3a0SDarrick J. Wong for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++) 33957c7c3a0SDarrick J. Wong if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN)) 34057c7c3a0SDarrick J. Wong return 1000000; 34157c7c3a0SDarrick J. Wong 34257c7c3a0SDarrick J. Wong return 100000; 34357c7c3a0SDarrick J. Wong } 34457c7c3a0SDarrick J. Wong 34557c7c3a0SDarrick J. Wong static int create_sensor(struct ibmpex_bmc_data *data, int type, 34657c7c3a0SDarrick J. Wong int counter, int sensor, int func) 34757c7c3a0SDarrick J. Wong { 34857c7c3a0SDarrick J. Wong int err; 34957c7c3a0SDarrick J. Wong char *n; 35057c7c3a0SDarrick J. Wong 35157c7c3a0SDarrick J. Wong n = kmalloc(32, GFP_KERNEL); 35257c7c3a0SDarrick J. Wong if (!n) 35357c7c3a0SDarrick J. Wong return -ENOMEM; 35457c7c3a0SDarrick J. Wong 35557c7c3a0SDarrick J. Wong if (type == TEMP_SENSOR) 35657c7c3a0SDarrick J. Wong sprintf(n, temp_sensor_name_templates[func], "temp", counter); 35757c7c3a0SDarrick J. Wong else if (type == POWER_SENSOR) 35857c7c3a0SDarrick J. Wong sprintf(n, power_sensor_name_templates[func], "power", counter); 35957c7c3a0SDarrick J. Wong 36057c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].dev_attr.attr.name = n; 36157c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO; 36257c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor; 36357c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].index = sensor; 36457c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].nr = func; 36557c7c3a0SDarrick J. Wong 36657c7c3a0SDarrick J. Wong err = device_create_file(data->bmc_device, 36757c7c3a0SDarrick J. Wong &data->sensors[sensor].attr[func].dev_attr); 36857c7c3a0SDarrick J. Wong if (err) { 36957c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].dev_attr.attr.name = NULL; 37057c7c3a0SDarrick J. Wong kfree(n); 37157c7c3a0SDarrick J. Wong return err; 37257c7c3a0SDarrick J. Wong } 37357c7c3a0SDarrick J. Wong 37457c7c3a0SDarrick J. Wong return 0; 37557c7c3a0SDarrick J. Wong } 37657c7c3a0SDarrick J. Wong 37757c7c3a0SDarrick J. Wong static int ibmpex_find_sensors(struct ibmpex_bmc_data *data) 37857c7c3a0SDarrick J. Wong { 37957c7c3a0SDarrick J. Wong int i, j, err; 38057c7c3a0SDarrick J. Wong int sensor_type; 38157c7c3a0SDarrick J. Wong int sensor_counter; 38257c7c3a0SDarrick J. Wong int num_power = 0; 38357c7c3a0SDarrick J. Wong int num_temp = 0; 38457c7c3a0SDarrick J. Wong 38557c7c3a0SDarrick J. Wong err = ibmpex_query_sensor_count(data); 38657c7c3a0SDarrick J. Wong if (err <= 0) 38757c7c3a0SDarrick J. Wong return -ENOENT; 38857c7c3a0SDarrick J. Wong data->num_sensors = err; 38957c7c3a0SDarrick J. Wong 39057c7c3a0SDarrick J. Wong data->sensors = kzalloc(data->num_sensors * sizeof(*data->sensors), 39157c7c3a0SDarrick J. Wong GFP_KERNEL); 39257c7c3a0SDarrick J. Wong if (!data->sensors) 39357c7c3a0SDarrick J. Wong return -ENOMEM; 39457c7c3a0SDarrick J. Wong 39557c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++) { 39657c7c3a0SDarrick J. Wong err = ibmpex_query_sensor_name(data, i); 39757c7c3a0SDarrick J. Wong if (err) 39857c7c3a0SDarrick J. Wong continue; 39957c7c3a0SDarrick J. Wong 40057c7c3a0SDarrick J. Wong if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) { 40157c7c3a0SDarrick J. Wong sensor_type = POWER_SENSOR; 40257c7c3a0SDarrick J. Wong num_power++; 40357c7c3a0SDarrick J. Wong sensor_counter = num_power; 40457c7c3a0SDarrick J. Wong data->sensors[i].multiplier = 405df9cb033SDarrick J. Wong power_sensor_multiplier(data, 406df9cb033SDarrick J. Wong data->rx_msg_data, 40757c7c3a0SDarrick J. Wong data->rx_msg_len); 40857c7c3a0SDarrick J. Wong } else if (is_temp_sensor(data->rx_msg_data, 40957c7c3a0SDarrick J. Wong data->rx_msg_len)) { 41057c7c3a0SDarrick J. Wong sensor_type = TEMP_SENSOR; 41157c7c3a0SDarrick J. Wong num_temp++; 41257c7c3a0SDarrick J. Wong sensor_counter = num_temp; 413ffda6857SDarrick J. Wong data->sensors[i].multiplier = 1000; 41457c7c3a0SDarrick J. Wong } else 41557c7c3a0SDarrick J. Wong continue; 41657c7c3a0SDarrick J. Wong 41757c7c3a0SDarrick J. Wong data->sensors[i].in_use = 1; 41857c7c3a0SDarrick J. Wong 41957c7c3a0SDarrick J. Wong /* Create attributes */ 42057c7c3a0SDarrick J. Wong for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) { 42157c7c3a0SDarrick J. Wong err = create_sensor(data, sensor_type, sensor_counter, 42257c7c3a0SDarrick J. Wong i, j); 42357c7c3a0SDarrick J. Wong if (err) 42457c7c3a0SDarrick J. Wong goto exit_remove; 42557c7c3a0SDarrick J. Wong } 42657c7c3a0SDarrick J. Wong } 42757c7c3a0SDarrick J. Wong 42857c7c3a0SDarrick J. Wong err = device_create_file(data->bmc_device, 42957c7c3a0SDarrick J. Wong &sensor_dev_attr_reset_high_low.dev_attr); 43057c7c3a0SDarrick J. Wong if (err) 43157c7c3a0SDarrick J. Wong goto exit_remove; 43257c7c3a0SDarrick J. Wong 43357c7c3a0SDarrick J. Wong err = device_create_file(data->bmc_device, 43457c7c3a0SDarrick J. Wong &sensor_dev_attr_name.dev_attr); 43557c7c3a0SDarrick J. Wong if (err) 43657c7c3a0SDarrick J. Wong goto exit_remove; 43757c7c3a0SDarrick J. Wong 43857c7c3a0SDarrick J. Wong return 0; 43957c7c3a0SDarrick J. Wong 44057c7c3a0SDarrick J. Wong exit_remove: 44157c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, 44257c7c3a0SDarrick J. Wong &sensor_dev_attr_reset_high_low.dev_attr); 44357c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr); 44457c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++) 44557c7c3a0SDarrick J. Wong for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) { 44657c7c3a0SDarrick J. Wong if (!data->sensors[i].attr[j].dev_attr.attr.name) 44757c7c3a0SDarrick J. Wong continue; 44857c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, 44957c7c3a0SDarrick J. Wong &data->sensors[i].attr[j].dev_attr); 45057c7c3a0SDarrick J. Wong kfree(data->sensors[i].attr[j].dev_attr.attr.name); 45157c7c3a0SDarrick J. Wong } 45257c7c3a0SDarrick J. Wong 45357c7c3a0SDarrick J. Wong kfree(data->sensors); 45457c7c3a0SDarrick J. Wong return err; 45557c7c3a0SDarrick J. Wong } 45657c7c3a0SDarrick J. Wong 45757c7c3a0SDarrick J. Wong static void ibmpex_register_bmc(int iface, struct device *dev) 45857c7c3a0SDarrick J. Wong { 45957c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data; 46057c7c3a0SDarrick J. Wong int err; 46157c7c3a0SDarrick J. Wong 46257c7c3a0SDarrick J. Wong data = kzalloc(sizeof(*data), GFP_KERNEL); 46357c7c3a0SDarrick J. Wong if (!data) { 4642ecb044eSDarrick J. Wong dev_err(dev, "Insufficient memory for BMC interface.\n"); 46557c7c3a0SDarrick J. Wong return; 46657c7c3a0SDarrick J. Wong } 46757c7c3a0SDarrick J. Wong 46857c7c3a0SDarrick J. Wong data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 46957c7c3a0SDarrick J. Wong data->address.channel = IPMI_BMC_CHANNEL; 47057c7c3a0SDarrick J. Wong data->address.data[0] = 0; 47157c7c3a0SDarrick J. Wong data->interface = iface; 47257c7c3a0SDarrick J. Wong data->bmc_device = dev; 47357c7c3a0SDarrick J. Wong 47457c7c3a0SDarrick J. Wong /* Create IPMI messaging interface user */ 47557c7c3a0SDarrick J. Wong err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs, 47657c7c3a0SDarrick J. Wong data, &data->user); 47757c7c3a0SDarrick J. Wong if (err < 0) { 4782ecb044eSDarrick J. Wong dev_err(dev, "Unable to register user with IPMI " 4792ecb044eSDarrick J. Wong "interface %d\n", data->interface); 48057c7c3a0SDarrick J. Wong goto out; 48157c7c3a0SDarrick J. Wong } 48257c7c3a0SDarrick J. Wong 48357c7c3a0SDarrick J. Wong mutex_init(&data->lock); 48457c7c3a0SDarrick J. Wong 48557c7c3a0SDarrick J. Wong /* Initialize message */ 48657c7c3a0SDarrick J. Wong data->tx_msgid = 0; 48757c7c3a0SDarrick J. Wong init_completion(&data->read_complete); 48857c7c3a0SDarrick J. Wong data->tx_message.netfn = PEX_NET_FUNCTION; 48957c7c3a0SDarrick J. Wong data->tx_message.cmd = PEX_COMMAND; 49057c7c3a0SDarrick J. Wong data->tx_message.data = data->tx_msg_data; 49157c7c3a0SDarrick J. Wong 49257c7c3a0SDarrick J. Wong /* Does this BMC support PowerExecutive? */ 49357c7c3a0SDarrick J. Wong err = ibmpex_ver_check(data); 49457c7c3a0SDarrick J. Wong if (err) 49557c7c3a0SDarrick J. Wong goto out_user; 49657c7c3a0SDarrick J. Wong 49757c7c3a0SDarrick J. Wong /* Register the BMC as a HWMON class device */ 49857c7c3a0SDarrick J. Wong data->hwmon_dev = hwmon_device_register(data->bmc_device); 49957c7c3a0SDarrick J. Wong 50057c7c3a0SDarrick J. Wong if (IS_ERR(data->hwmon_dev)) { 5012ecb044eSDarrick J. Wong dev_err(data->bmc_device, "Unable to register hwmon " 5022ecb044eSDarrick J. Wong "device for IPMI interface %d\n", 50357c7c3a0SDarrick J. Wong data->interface); 5044cfdbe7fSDarrick J. Wong goto out_user; 50557c7c3a0SDarrick J. Wong } 50657c7c3a0SDarrick J. Wong 50757c7c3a0SDarrick J. Wong /* finally add the new bmc data to the bmc data list */ 50857c7c3a0SDarrick J. Wong dev_set_drvdata(dev, data); 50957c7c3a0SDarrick J. Wong list_add_tail(&data->list, &driver_data.bmc_data); 51057c7c3a0SDarrick J. Wong 51157c7c3a0SDarrick J. Wong /* Now go find all the sensors */ 51257c7c3a0SDarrick J. Wong err = ibmpex_find_sensors(data); 51357c7c3a0SDarrick J. Wong if (err) { 5142ecb044eSDarrick J. Wong dev_err(data->bmc_device, "Error %d finding sensors\n", err); 51557c7c3a0SDarrick J. Wong goto out_register; 51657c7c3a0SDarrick J. Wong } 51757c7c3a0SDarrick J. Wong 51857c7c3a0SDarrick J. Wong return; 51957c7c3a0SDarrick J. Wong 52057c7c3a0SDarrick J. Wong out_register: 52157c7c3a0SDarrick J. Wong hwmon_device_unregister(data->hwmon_dev); 52257c7c3a0SDarrick J. Wong out_user: 52357c7c3a0SDarrick J. Wong ipmi_destroy_user(data->user); 52457c7c3a0SDarrick J. Wong out: 52557c7c3a0SDarrick J. Wong kfree(data); 52657c7c3a0SDarrick J. Wong } 52757c7c3a0SDarrick J. Wong 52857c7c3a0SDarrick J. Wong static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data) 52957c7c3a0SDarrick J. Wong { 53057c7c3a0SDarrick J. Wong int i, j; 53157c7c3a0SDarrick J. Wong 53257c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, 53357c7c3a0SDarrick J. Wong &sensor_dev_attr_reset_high_low.dev_attr); 53457c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr); 53557c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++) 53657c7c3a0SDarrick J. Wong for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) { 53757c7c3a0SDarrick J. Wong if (!data->sensors[i].attr[j].dev_attr.attr.name) 53857c7c3a0SDarrick J. Wong continue; 53957c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, 54057c7c3a0SDarrick J. Wong &data->sensors[i].attr[j].dev_attr); 54157c7c3a0SDarrick J. Wong kfree(data->sensors[i].attr[j].dev_attr.attr.name); 54257c7c3a0SDarrick J. Wong } 54357c7c3a0SDarrick J. Wong 54457c7c3a0SDarrick J. Wong list_del(&data->list); 54557c7c3a0SDarrick J. Wong dev_set_drvdata(data->bmc_device, NULL); 54657c7c3a0SDarrick J. Wong hwmon_device_unregister(data->hwmon_dev); 54757c7c3a0SDarrick J. Wong ipmi_destroy_user(data->user); 54857c7c3a0SDarrick J. Wong kfree(data->sensors); 54957c7c3a0SDarrick J. Wong kfree(data); 55057c7c3a0SDarrick J. Wong } 55157c7c3a0SDarrick J. Wong 55257c7c3a0SDarrick J. Wong static void ibmpex_bmc_gone(int iface) 55357c7c3a0SDarrick J. Wong { 55457c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data = get_bmc_data(iface); 55557c7c3a0SDarrick J. Wong 55657c7c3a0SDarrick J. Wong if (!data) 55757c7c3a0SDarrick J. Wong return; 55857c7c3a0SDarrick J. Wong 55957c7c3a0SDarrick J. Wong ibmpex_bmc_delete(data); 56057c7c3a0SDarrick J. Wong } 56157c7c3a0SDarrick J. Wong 56257c7c3a0SDarrick J. Wong static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) 56357c7c3a0SDarrick J. Wong { 56457c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data; 56557c7c3a0SDarrick J. Wong 56657c7c3a0SDarrick J. Wong if (msg->msgid != data->tx_msgid) { 5672ecb044eSDarrick J. Wong dev_err(data->bmc_device, "Mismatch between received msgid " 5682ecb044eSDarrick J. Wong "(%02x) and transmitted msgid (%02x)!\n", 56957c7c3a0SDarrick J. Wong (int)msg->msgid, 57057c7c3a0SDarrick J. Wong (int)data->tx_msgid); 57157c7c3a0SDarrick J. Wong ipmi_free_recv_msg(msg); 57257c7c3a0SDarrick J. Wong return; 57357c7c3a0SDarrick J. Wong } 57457c7c3a0SDarrick J. Wong 57557c7c3a0SDarrick J. Wong data->rx_recv_type = msg->recv_type; 57657c7c3a0SDarrick J. Wong if (msg->msg.data_len > 0) 57757c7c3a0SDarrick J. Wong data->rx_result = msg->msg.data[0]; 57857c7c3a0SDarrick J. Wong else 57957c7c3a0SDarrick J. Wong data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; 58057c7c3a0SDarrick J. Wong 58157c7c3a0SDarrick J. Wong if (msg->msg.data_len > 1) { 58257c7c3a0SDarrick J. Wong data->rx_msg_len = msg->msg.data_len - 1; 58357c7c3a0SDarrick J. Wong memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len); 58457c7c3a0SDarrick J. Wong } else 58557c7c3a0SDarrick J. Wong data->rx_msg_len = 0; 58657c7c3a0SDarrick J. Wong 58757c7c3a0SDarrick J. Wong ipmi_free_recv_msg(msg); 58857c7c3a0SDarrick J. Wong complete(&data->read_complete); 58957c7c3a0SDarrick J. Wong } 59057c7c3a0SDarrick J. Wong 59157c7c3a0SDarrick J. Wong static int __init ibmpex_init(void) 59257c7c3a0SDarrick J. Wong { 59357c7c3a0SDarrick J. Wong return ipmi_smi_watcher_register(&driver_data.bmc_events); 59457c7c3a0SDarrick J. Wong } 59557c7c3a0SDarrick J. Wong 59657c7c3a0SDarrick J. Wong static void __exit ibmpex_exit(void) 59757c7c3a0SDarrick J. Wong { 59857c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *p, *next; 59957c7c3a0SDarrick J. Wong 60057c7c3a0SDarrick J. Wong ipmi_smi_watcher_unregister(&driver_data.bmc_events); 60157c7c3a0SDarrick J. Wong list_for_each_entry_safe(p, next, &driver_data.bmc_data, list) 60257c7c3a0SDarrick J. Wong ibmpex_bmc_delete(p); 60357c7c3a0SDarrick J. Wong } 60457c7c3a0SDarrick J. Wong 60557c7c3a0SDarrick J. Wong MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); 60657c7c3a0SDarrick J. Wong MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver"); 60757c7c3a0SDarrick J. Wong MODULE_LICENSE("GPL"); 60857c7c3a0SDarrick J. Wong 60957c7c3a0SDarrick J. Wong module_init(ibmpex_init); 61057c7c3a0SDarrick J. Wong module_exit(ibmpex_exit); 611*ff921361SDarrick J. Wong 612*ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); 613*ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); 614*ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); 615*ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); 616*ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); 617