18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * A hwmon driver for the IBM PowerExecutive temperature/power sensors 48c2ecf20Sopenharmony_ci * Copyright (C) 2007 IBM 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/ipmi.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 128c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 138c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define REFRESH_INTERVAL (2 * HZ) 198c2ecf20Sopenharmony_ci#define DRVNAME "ibmpex" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PEX_GET_VERSION 1 228c2ecf20Sopenharmony_ci#define PEX_GET_SENSOR_COUNT 2 238c2ecf20Sopenharmony_ci#define PEX_GET_SENSOR_NAME 3 248c2ecf20Sopenharmony_ci#define PEX_RESET_HIGH_LOW 4 258c2ecf20Sopenharmony_ci#define PEX_GET_SENSOR_DATA 6 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define PEX_NET_FUNCTION 0x3A 288c2ecf20Sopenharmony_ci#define PEX_COMMAND 0x3C 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic inline u16 extract_value(const char *data, int offset) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return be16_to_cpup((__be16 *)&data[offset]); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define TEMP_SENSOR 1 368c2ecf20Sopenharmony_ci#define POWER_SENSOR 2 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define PEX_SENSOR_TYPE_LEN 3 398c2ecf20Sopenharmony_cistatic u8 const power_sensor_sig[] = {0x70, 0x77, 0x72}; 408c2ecf20Sopenharmony_cistatic u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define PEX_MULT_LEN 2 438c2ecf20Sopenharmony_cistatic u8 const watt_sensor_sig[] = {0x41, 0x43}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define PEX_NUM_SENSOR_FUNCS 3 468c2ecf20Sopenharmony_cistatic const char * const sensor_name_suffixes[] = { 478c2ecf20Sopenharmony_ci "", 488c2ecf20Sopenharmony_ci "_lowest", 498c2ecf20Sopenharmony_ci "_highest" 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); 538c2ecf20Sopenharmony_cistatic void ibmpex_register_bmc(int iface, struct device *dev); 548c2ecf20Sopenharmony_cistatic void ibmpex_bmc_gone(int iface); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct ibmpex_sensor_data { 578c2ecf20Sopenharmony_ci int in_use; 588c2ecf20Sopenharmony_ci s16 values[PEX_NUM_SENSOR_FUNCS]; 598c2ecf20Sopenharmony_ci int multiplier; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 attr[PEX_NUM_SENSOR_FUNCS]; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct ibmpex_bmc_data { 658c2ecf20Sopenharmony_ci struct list_head list; 668c2ecf20Sopenharmony_ci struct device *hwmon_dev; 678c2ecf20Sopenharmony_ci struct device *bmc_device; 688c2ecf20Sopenharmony_ci struct mutex lock; 698c2ecf20Sopenharmony_ci char valid; 708c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci struct ipmi_addr address; 738c2ecf20Sopenharmony_ci struct completion read_complete; 748c2ecf20Sopenharmony_ci struct ipmi_user *user; 758c2ecf20Sopenharmony_ci int interface; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci struct kernel_ipmi_msg tx_message; 788c2ecf20Sopenharmony_ci unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH]; 798c2ecf20Sopenharmony_ci long tx_msgid; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH]; 828c2ecf20Sopenharmony_ci unsigned long rx_msg_len; 838c2ecf20Sopenharmony_ci unsigned char rx_result; 848c2ecf20Sopenharmony_ci int rx_recv_type; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci unsigned char sensor_major; 878c2ecf20Sopenharmony_ci unsigned char sensor_minor; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci unsigned char num_sensors; 908c2ecf20Sopenharmony_ci struct ibmpex_sensor_data *sensors; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct ibmpex_driver_data { 948c2ecf20Sopenharmony_ci struct list_head bmc_data; 958c2ecf20Sopenharmony_ci struct ipmi_smi_watcher bmc_events; 968c2ecf20Sopenharmony_ci struct ipmi_user_hndl ipmi_hndlrs; 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct ibmpex_driver_data driver_data = { 1008c2ecf20Sopenharmony_ci .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data), 1018c2ecf20Sopenharmony_ci .bmc_events = { 1028c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1038c2ecf20Sopenharmony_ci .new_smi = ibmpex_register_bmc, 1048c2ecf20Sopenharmony_ci .smi_gone = ibmpex_bmc_gone, 1058c2ecf20Sopenharmony_ci }, 1068c2ecf20Sopenharmony_ci .ipmi_hndlrs = { 1078c2ecf20Sopenharmony_ci .ipmi_recv_hndl = ibmpex_msg_handler, 1088c2ecf20Sopenharmony_ci }, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int ibmpex_send_message(struct ibmpex_bmc_data *data) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci int err; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci err = ipmi_validate_addr(&data->address, sizeof(data->address)); 1168c2ecf20Sopenharmony_ci if (err) 1178c2ecf20Sopenharmony_ci goto out; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci data->tx_msgid++; 1208c2ecf20Sopenharmony_ci err = ipmi_request_settime(data->user, &data->address, data->tx_msgid, 1218c2ecf20Sopenharmony_ci &data->tx_message, data, 0, 0, 0); 1228c2ecf20Sopenharmony_ci if (err) 1238c2ecf20Sopenharmony_ci goto out1; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ciout1: 1278c2ecf20Sopenharmony_ci dev_err(data->bmc_device, "request_settime=%x\n", err); 1288c2ecf20Sopenharmony_ci return err; 1298c2ecf20Sopenharmony_ciout: 1308c2ecf20Sopenharmony_ci dev_err(data->bmc_device, "validate_addr=%x\n", err); 1318c2ecf20Sopenharmony_ci return err; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int ibmpex_ver_check(struct ibmpex_bmc_data *data) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci data->tx_msg_data[0] = PEX_GET_VERSION; 1378c2ecf20Sopenharmony_ci data->tx_message.data_len = 1; 1388c2ecf20Sopenharmony_ci ibmpex_send_message(data); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci wait_for_completion(&data->read_complete); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (data->rx_result || data->rx_msg_len != 6) 1438c2ecf20Sopenharmony_ci return -ENOENT; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci data->sensor_major = data->rx_msg_data[0]; 1468c2ecf20Sopenharmony_ci data->sensor_minor = data->rx_msg_data[1]; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dev_info(data->bmc_device, 1498c2ecf20Sopenharmony_ci "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n", 1508c2ecf20Sopenharmony_ci data->sensor_major, 1518c2ecf20Sopenharmony_ci data->sensor_minor, 1528c2ecf20Sopenharmony_ci extract_value(data->rx_msg_data, 2), 1538c2ecf20Sopenharmony_ci data->rx_msg_data[4], 1548c2ecf20Sopenharmony_ci data->rx_msg_data[5], 1558c2ecf20Sopenharmony_ci data->interface); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT; 1638c2ecf20Sopenharmony_ci data->tx_message.data_len = 1; 1648c2ecf20Sopenharmony_ci ibmpex_send_message(data); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci wait_for_completion(&data->read_complete); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (data->rx_result || data->rx_msg_len != 1) 1698c2ecf20Sopenharmony_ci return -ENOENT; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return data->rx_msg_data[0]; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci data->tx_msg_data[0] = PEX_GET_SENSOR_NAME; 1778c2ecf20Sopenharmony_ci data->tx_msg_data[1] = sensor; 1788c2ecf20Sopenharmony_ci data->tx_message.data_len = 2; 1798c2ecf20Sopenharmony_ci ibmpex_send_message(data); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci wait_for_completion(&data->read_complete); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (data->rx_result || data->rx_msg_len < 1) 1848c2ecf20Sopenharmony_ci return -ENOENT; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci data->tx_msg_data[0] = PEX_GET_SENSOR_DATA; 1928c2ecf20Sopenharmony_ci data->tx_msg_data[1] = sensor; 1938c2ecf20Sopenharmony_ci data->tx_message.data_len = 2; 1948c2ecf20Sopenharmony_ci ibmpex_send_message(data); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci wait_for_completion(&data->read_complete); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (data->rx_result || data->rx_msg_len < 26) { 1998c2ecf20Sopenharmony_ci dev_err(data->bmc_device, "Error reading sensor %d.\n", 2008c2ecf20Sopenharmony_ci sensor); 2018c2ecf20Sopenharmony_ci return -ENOENT; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci data->tx_msg_data[0] = PEX_RESET_HIGH_LOW; 2108c2ecf20Sopenharmony_ci data->tx_message.data_len = 1; 2118c2ecf20Sopenharmony_ci ibmpex_send_message(data); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci wait_for_completion(&data->read_complete); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void ibmpex_update_device(struct ibmpex_bmc_data *data) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int i, err; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 2238c2ecf20Sopenharmony_ci if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) && 2248c2ecf20Sopenharmony_ci data->valid) 2258c2ecf20Sopenharmony_ci goto out; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci for (i = 0; i < data->num_sensors; i++) { 2288c2ecf20Sopenharmony_ci if (!data->sensors[i].in_use) 2298c2ecf20Sopenharmony_ci continue; 2308c2ecf20Sopenharmony_ci err = ibmpex_query_sensor_data(data, i); 2318c2ecf20Sopenharmony_ci if (err) 2328c2ecf20Sopenharmony_ci continue; 2338c2ecf20Sopenharmony_ci data->sensors[i].values[0] = 2348c2ecf20Sopenharmony_ci extract_value(data->rx_msg_data, 16); 2358c2ecf20Sopenharmony_ci data->sensors[i].values[1] = 2368c2ecf20Sopenharmony_ci extract_value(data->rx_msg_data, 18); 2378c2ecf20Sopenharmony_ci data->sensors[i].values[2] = 2388c2ecf20Sopenharmony_ci extract_value(data->rx_msg_data, 20); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci data->last_updated = jiffies; 2428c2ecf20Sopenharmony_ci data->valid = 1; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciout: 2458c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic struct ibmpex_bmc_data *get_bmc_data(int iface) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct ibmpex_bmc_data *p, *next; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, next, &driver_data.bmc_data, list) 2538c2ecf20Sopenharmony_ci if (p->interface == iface) 2548c2ecf20Sopenharmony_ci return p; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return NULL; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *devattr, 2608c2ecf20Sopenharmony_ci char *buf) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", DRVNAME); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(name, name, 0); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic ssize_t ibmpex_show_sensor(struct device *dev, 2678c2ecf20Sopenharmony_ci struct device_attribute *devattr, 2688c2ecf20Sopenharmony_ci char *buf) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); 2718c2ecf20Sopenharmony_ci struct ibmpex_bmc_data *data = dev_get_drvdata(dev); 2728c2ecf20Sopenharmony_ci int mult = data->sensors[attr->index].multiplier; 2738c2ecf20Sopenharmony_ci ibmpex_update_device(data); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 2768c2ecf20Sopenharmony_ci data->sensors[attr->index].values[attr->nr] * mult); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic ssize_t ibmpex_high_low_store(struct device *dev, 2808c2ecf20Sopenharmony_ci struct device_attribute *devattr, 2818c2ecf20Sopenharmony_ci const char *buf, size_t count) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct ibmpex_bmc_data *data = dev_get_drvdata(dev); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ibmpex_reset_high_low_data(data); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return count; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(reset_high_low, ibmpex_high_low, 0); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int is_power_sensor(const char *sensor_id, int len) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci if (len < PEX_SENSOR_TYPE_LEN) 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN)) 2988c2ecf20Sopenharmony_ci return 1; 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int is_temp_sensor(const char *sensor_id, int len) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci if (len < PEX_SENSOR_TYPE_LEN) 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN)) 3088c2ecf20Sopenharmony_ci return 1; 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int power_sensor_multiplier(struct ibmpex_bmc_data *data, 3138c2ecf20Sopenharmony_ci const char *sensor_id, int len) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci int i; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (data->sensor_major == 2) 3188c2ecf20Sopenharmony_ci return 1000000; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++) 3218c2ecf20Sopenharmony_ci if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN)) 3228c2ecf20Sopenharmony_ci return 1000000; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return 100000; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int create_sensor(struct ibmpex_bmc_data *data, int type, 3288c2ecf20Sopenharmony_ci int counter, int sensor, int func) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int err; 3318c2ecf20Sopenharmony_ci char *n; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci n = kmalloc(32, GFP_KERNEL); 3348c2ecf20Sopenharmony_ci if (!n) 3358c2ecf20Sopenharmony_ci return -ENOMEM; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (type == TEMP_SENSOR) 3388c2ecf20Sopenharmony_ci sprintf(n, "temp%d_input%s", 3398c2ecf20Sopenharmony_ci counter, sensor_name_suffixes[func]); 3408c2ecf20Sopenharmony_ci else if (type == POWER_SENSOR) 3418c2ecf20Sopenharmony_ci sprintf(n, "power%d_average%s", 3428c2ecf20Sopenharmony_ci counter, sensor_name_suffixes[func]); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr); 3458c2ecf20Sopenharmony_ci data->sensors[sensor].attr[func].dev_attr.attr.name = n; 3468c2ecf20Sopenharmony_ci data->sensors[sensor].attr[func].dev_attr.attr.mode = 0444; 3478c2ecf20Sopenharmony_ci data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor; 3488c2ecf20Sopenharmony_ci data->sensors[sensor].attr[func].index = sensor; 3498c2ecf20Sopenharmony_ci data->sensors[sensor].attr[func].nr = func; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci err = device_create_file(data->bmc_device, 3528c2ecf20Sopenharmony_ci &data->sensors[sensor].attr[func].dev_attr); 3538c2ecf20Sopenharmony_ci if (err) { 3548c2ecf20Sopenharmony_ci data->sensors[sensor].attr[func].dev_attr.attr.name = NULL; 3558c2ecf20Sopenharmony_ci kfree(n); 3568c2ecf20Sopenharmony_ci return err; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int ibmpex_find_sensors(struct ibmpex_bmc_data *data) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci int i, j, err; 3658c2ecf20Sopenharmony_ci int sensor_type; 3668c2ecf20Sopenharmony_ci int sensor_counter; 3678c2ecf20Sopenharmony_ci int num_power = 0; 3688c2ecf20Sopenharmony_ci int num_temp = 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci err = ibmpex_query_sensor_count(data); 3718c2ecf20Sopenharmony_ci if (err <= 0) 3728c2ecf20Sopenharmony_ci return -ENOENT; 3738c2ecf20Sopenharmony_ci data->num_sensors = err; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci data->sensors = kcalloc(data->num_sensors, sizeof(*data->sensors), 3768c2ecf20Sopenharmony_ci GFP_KERNEL); 3778c2ecf20Sopenharmony_ci if (!data->sensors) 3788c2ecf20Sopenharmony_ci return -ENOMEM; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci for (i = 0; i < data->num_sensors; i++) { 3818c2ecf20Sopenharmony_ci err = ibmpex_query_sensor_name(data, i); 3828c2ecf20Sopenharmony_ci if (err) 3838c2ecf20Sopenharmony_ci continue; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) { 3868c2ecf20Sopenharmony_ci sensor_type = POWER_SENSOR; 3878c2ecf20Sopenharmony_ci num_power++; 3888c2ecf20Sopenharmony_ci sensor_counter = num_power; 3898c2ecf20Sopenharmony_ci data->sensors[i].multiplier = 3908c2ecf20Sopenharmony_ci power_sensor_multiplier(data, 3918c2ecf20Sopenharmony_ci data->rx_msg_data, 3928c2ecf20Sopenharmony_ci data->rx_msg_len); 3938c2ecf20Sopenharmony_ci } else if (is_temp_sensor(data->rx_msg_data, 3948c2ecf20Sopenharmony_ci data->rx_msg_len)) { 3958c2ecf20Sopenharmony_ci sensor_type = TEMP_SENSOR; 3968c2ecf20Sopenharmony_ci num_temp++; 3978c2ecf20Sopenharmony_ci sensor_counter = num_temp; 3988c2ecf20Sopenharmony_ci data->sensors[i].multiplier = 1000; 3998c2ecf20Sopenharmony_ci } else 4008c2ecf20Sopenharmony_ci continue; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci data->sensors[i].in_use = 1; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Create attributes */ 4058c2ecf20Sopenharmony_ci for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) { 4068c2ecf20Sopenharmony_ci err = create_sensor(data, sensor_type, sensor_counter, 4078c2ecf20Sopenharmony_ci i, j); 4088c2ecf20Sopenharmony_ci if (err) 4098c2ecf20Sopenharmony_ci goto exit_remove; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci err = device_create_file(data->bmc_device, 4148c2ecf20Sopenharmony_ci &sensor_dev_attr_reset_high_low.dev_attr); 4158c2ecf20Sopenharmony_ci if (err) 4168c2ecf20Sopenharmony_ci goto exit_remove; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci err = device_create_file(data->bmc_device, 4198c2ecf20Sopenharmony_ci &sensor_dev_attr_name.dev_attr); 4208c2ecf20Sopenharmony_ci if (err) 4218c2ecf20Sopenharmony_ci goto exit_remove; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ciexit_remove: 4268c2ecf20Sopenharmony_ci device_remove_file(data->bmc_device, 4278c2ecf20Sopenharmony_ci &sensor_dev_attr_reset_high_low.dev_attr); 4288c2ecf20Sopenharmony_ci device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr); 4298c2ecf20Sopenharmony_ci for (i = 0; i < data->num_sensors; i++) 4308c2ecf20Sopenharmony_ci for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) { 4318c2ecf20Sopenharmony_ci if (!data->sensors[i].attr[j].dev_attr.attr.name) 4328c2ecf20Sopenharmony_ci continue; 4338c2ecf20Sopenharmony_ci device_remove_file(data->bmc_device, 4348c2ecf20Sopenharmony_ci &data->sensors[i].attr[j].dev_attr); 4358c2ecf20Sopenharmony_ci kfree(data->sensors[i].attr[j].dev_attr.attr.name); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci kfree(data->sensors); 4398c2ecf20Sopenharmony_ci return err; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void ibmpex_register_bmc(int iface, struct device *dev) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct ibmpex_bmc_data *data; 4458c2ecf20Sopenharmony_ci int err; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 4488c2ecf20Sopenharmony_ci if (!data) 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 4528c2ecf20Sopenharmony_ci data->address.channel = IPMI_BMC_CHANNEL; 4538c2ecf20Sopenharmony_ci data->address.data[0] = 0; 4548c2ecf20Sopenharmony_ci data->interface = iface; 4558c2ecf20Sopenharmony_ci data->bmc_device = dev; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Create IPMI messaging interface user */ 4588c2ecf20Sopenharmony_ci err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs, 4598c2ecf20Sopenharmony_ci data, &data->user); 4608c2ecf20Sopenharmony_ci if (err < 0) { 4618c2ecf20Sopenharmony_ci dev_err(dev, 4628c2ecf20Sopenharmony_ci "Unable to register user with IPMI interface %d\n", 4638c2ecf20Sopenharmony_ci data->interface); 4648c2ecf20Sopenharmony_ci goto out; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci mutex_init(&data->lock); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* Initialize message */ 4708c2ecf20Sopenharmony_ci data->tx_msgid = 0; 4718c2ecf20Sopenharmony_ci init_completion(&data->read_complete); 4728c2ecf20Sopenharmony_ci data->tx_message.netfn = PEX_NET_FUNCTION; 4738c2ecf20Sopenharmony_ci data->tx_message.cmd = PEX_COMMAND; 4748c2ecf20Sopenharmony_ci data->tx_message.data = data->tx_msg_data; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Does this BMC support PowerExecutive? */ 4778c2ecf20Sopenharmony_ci err = ibmpex_ver_check(data); 4788c2ecf20Sopenharmony_ci if (err) 4798c2ecf20Sopenharmony_ci goto out_user; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Register the BMC as a HWMON class device */ 4828c2ecf20Sopenharmony_ci data->hwmon_dev = hwmon_device_register(data->bmc_device); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 4858c2ecf20Sopenharmony_ci dev_err(data->bmc_device, 4868c2ecf20Sopenharmony_ci "Unable to register hwmon device for IPMI interface %d\n", 4878c2ecf20Sopenharmony_ci data->interface); 4888c2ecf20Sopenharmony_ci goto out_user; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* finally add the new bmc data to the bmc data list */ 4928c2ecf20Sopenharmony_ci dev_set_drvdata(dev, data); 4938c2ecf20Sopenharmony_ci list_add_tail(&data->list, &driver_data.bmc_data); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Now go find all the sensors */ 4968c2ecf20Sopenharmony_ci err = ibmpex_find_sensors(data); 4978c2ecf20Sopenharmony_ci if (err) { 4988c2ecf20Sopenharmony_ci dev_err(data->bmc_device, "Error %d finding sensors\n", err); 4998c2ecf20Sopenharmony_ci goto out_register; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ciout_register: 5058c2ecf20Sopenharmony_ci list_del(&data->list); 5068c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 5078c2ecf20Sopenharmony_ciout_user: 5088c2ecf20Sopenharmony_ci ipmi_destroy_user(data->user); 5098c2ecf20Sopenharmony_ciout: 5108c2ecf20Sopenharmony_ci kfree(data); 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void ibmpex_bmc_delete(struct ibmpex_bmc_data *data) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci int i, j; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci device_remove_file(data->bmc_device, 5188c2ecf20Sopenharmony_ci &sensor_dev_attr_reset_high_low.dev_attr); 5198c2ecf20Sopenharmony_ci device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr); 5208c2ecf20Sopenharmony_ci for (i = 0; i < data->num_sensors; i++) 5218c2ecf20Sopenharmony_ci for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) { 5228c2ecf20Sopenharmony_ci if (!data->sensors[i].attr[j].dev_attr.attr.name) 5238c2ecf20Sopenharmony_ci continue; 5248c2ecf20Sopenharmony_ci device_remove_file(data->bmc_device, 5258c2ecf20Sopenharmony_ci &data->sensors[i].attr[j].dev_attr); 5268c2ecf20Sopenharmony_ci kfree(data->sensors[i].attr[j].dev_attr.attr.name); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci list_del(&data->list); 5308c2ecf20Sopenharmony_ci dev_set_drvdata(data->bmc_device, NULL); 5318c2ecf20Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 5328c2ecf20Sopenharmony_ci ipmi_destroy_user(data->user); 5338c2ecf20Sopenharmony_ci kfree(data->sensors); 5348c2ecf20Sopenharmony_ci kfree(data); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void ibmpex_bmc_gone(int iface) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct ibmpex_bmc_data *data = get_bmc_data(iface); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (!data) 5428c2ecf20Sopenharmony_ci return; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ibmpex_bmc_delete(data); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (msg->msgid != data->tx_msgid) { 5528c2ecf20Sopenharmony_ci dev_err(data->bmc_device, 5538c2ecf20Sopenharmony_ci "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n", 5548c2ecf20Sopenharmony_ci (int)msg->msgid, 5558c2ecf20Sopenharmony_ci (int)data->tx_msgid); 5568c2ecf20Sopenharmony_ci ipmi_free_recv_msg(msg); 5578c2ecf20Sopenharmony_ci return; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci data->rx_recv_type = msg->recv_type; 5618c2ecf20Sopenharmony_ci if (msg->msg.data_len > 0) 5628c2ecf20Sopenharmony_ci data->rx_result = msg->msg.data[0]; 5638c2ecf20Sopenharmony_ci else 5648c2ecf20Sopenharmony_ci data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (msg->msg.data_len > 1) { 5678c2ecf20Sopenharmony_ci data->rx_msg_len = msg->msg.data_len - 1; 5688c2ecf20Sopenharmony_ci memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len); 5698c2ecf20Sopenharmony_ci } else 5708c2ecf20Sopenharmony_ci data->rx_msg_len = 0; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ipmi_free_recv_msg(msg); 5738c2ecf20Sopenharmony_ci complete(&data->read_complete); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int __init ibmpex_init(void) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci return ipmi_smi_watcher_register(&driver_data.bmc_events); 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic void __exit ibmpex_exit(void) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct ibmpex_bmc_data *p, *next; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ipmi_smi_watcher_unregister(&driver_data.bmc_events); 5868c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, next, &driver_data.bmc_data, list) 5878c2ecf20Sopenharmony_ci ibmpex_bmc_delete(p); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); 5918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver"); 5928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cimodule_init(ibmpex_init); 5958c2ecf20Sopenharmony_cimodule_exit(ibmpex_exit); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); 5988c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); 5998c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); 6008c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); 6018c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); 602