11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
257c7c3a0SDarrick J. Wong /*
357c7c3a0SDarrick J. Wong * A hwmon driver for the IBM PowerExecutive temperature/power sensors
457c7c3a0SDarrick J. Wong * Copyright (C) 2007 IBM
557c7c3a0SDarrick J. Wong *
65407e051SDarrick J. Wong * Author: Darrick J. Wong <darrick.wong@oracle.com>
757c7c3a0SDarrick J. Wong */
857c7c3a0SDarrick J. Wong
957c7c3a0SDarrick J. Wong #include <linux/ipmi.h>
1057c7c3a0SDarrick J. Wong #include <linux/module.h>
1157c7c3a0SDarrick J. Wong #include <linux/hwmon.h>
1257c7c3a0SDarrick J. Wong #include <linux/hwmon-sysfs.h>
1357c7c3a0SDarrick J. Wong #include <linux/jiffies.h>
1457c7c3a0SDarrick J. Wong #include <linux/mutex.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
16fa845740SJean Delvare #include <linux/err.h>
1757c7c3a0SDarrick J. Wong
1857c7c3a0SDarrick J. Wong #define REFRESH_INTERVAL (2 * HZ)
1957c7c3a0SDarrick J. Wong #define DRVNAME "ibmpex"
2057c7c3a0SDarrick J. Wong
2157c7c3a0SDarrick J. Wong #define PEX_GET_VERSION 1
2257c7c3a0SDarrick J. Wong #define PEX_GET_SENSOR_COUNT 2
2357c7c3a0SDarrick J. Wong #define PEX_GET_SENSOR_NAME 3
2457c7c3a0SDarrick J. Wong #define PEX_RESET_HIGH_LOW 4
2557c7c3a0SDarrick J. Wong #define PEX_GET_SENSOR_DATA 6
2657c7c3a0SDarrick J. Wong
2757c7c3a0SDarrick J. Wong #define PEX_NET_FUNCTION 0x3A
2857c7c3a0SDarrick J. Wong #define PEX_COMMAND 0x3C
2957c7c3a0SDarrick J. Wong
extract_value(const char * data,int offset)3057c7c3a0SDarrick J. Wong static inline u16 extract_value(const char *data, int offset)
3157c7c3a0SDarrick J. Wong {
3229041b4bSHarvey Harrison return be16_to_cpup((__be16 *)&data[offset]);
3357c7c3a0SDarrick J. Wong }
3457c7c3a0SDarrick J. Wong
3557c7c3a0SDarrick J. Wong #define TEMP_SENSOR 1
3657c7c3a0SDarrick J. Wong #define POWER_SENSOR 2
3757c7c3a0SDarrick J. Wong
3857c7c3a0SDarrick J. Wong #define PEX_SENSOR_TYPE_LEN 3
3957c7c3a0SDarrick J. Wong static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
4057c7c3a0SDarrick J. Wong static u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D};
4157c7c3a0SDarrick J. Wong
4257c7c3a0SDarrick J. Wong #define PEX_MULT_LEN 2
4357c7c3a0SDarrick J. Wong static u8 const watt_sensor_sig[] = {0x41, 0x43};
4457c7c3a0SDarrick J. Wong
4557c7c3a0SDarrick J. Wong #define PEX_NUM_SENSOR_FUNCS 3
46a0fc74d4SRasmus Villemoes static const char * const sensor_name_suffixes[] = {
47a0fc74d4SRasmus Villemoes "",
48a0fc74d4SRasmus Villemoes "_lowest",
49a0fc74d4SRasmus Villemoes "_highest"
5057c7c3a0SDarrick J. Wong };
5157c7c3a0SDarrick J. Wong
5257c7c3a0SDarrick J. Wong static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
5357c7c3a0SDarrick J. Wong static void ibmpex_register_bmc(int iface, struct device *dev);
5457c7c3a0SDarrick J. Wong static void ibmpex_bmc_gone(int iface);
5557c7c3a0SDarrick J. Wong
5657c7c3a0SDarrick J. Wong struct ibmpex_sensor_data {
5757c7c3a0SDarrick J. Wong int in_use;
5857c7c3a0SDarrick J. Wong s16 values[PEX_NUM_SENSOR_FUNCS];
5957c7c3a0SDarrick J. Wong int multiplier;
6057c7c3a0SDarrick J. Wong
6157c7c3a0SDarrick J. Wong struct sensor_device_attribute_2 attr[PEX_NUM_SENSOR_FUNCS];
6257c7c3a0SDarrick J. Wong };
6357c7c3a0SDarrick J. Wong
6457c7c3a0SDarrick J. Wong struct ibmpex_bmc_data {
6557c7c3a0SDarrick J. Wong struct list_head list;
6657c7c3a0SDarrick J. Wong struct device *hwmon_dev;
6757c7c3a0SDarrick J. Wong struct device *bmc_device;
6857c7c3a0SDarrick J. Wong struct mutex lock;
69952a11caSPaul Fertser bool valid;
7057c7c3a0SDarrick J. Wong unsigned long last_updated; /* In jiffies */
7157c7c3a0SDarrick J. Wong
7257c7c3a0SDarrick J. Wong struct ipmi_addr address;
7357c7c3a0SDarrick J. Wong struct completion read_complete;
743f901c86SCorey Minyard struct ipmi_user *user;
7557c7c3a0SDarrick J. Wong int interface;
7657c7c3a0SDarrick J. Wong
7757c7c3a0SDarrick J. Wong struct kernel_ipmi_msg tx_message;
7857c7c3a0SDarrick J. Wong unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH];
7957c7c3a0SDarrick J. Wong long tx_msgid;
8057c7c3a0SDarrick J. Wong
8157c7c3a0SDarrick J. Wong unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH];
8257c7c3a0SDarrick J. Wong unsigned long rx_msg_len;
8357c7c3a0SDarrick J. Wong unsigned char rx_result;
8457c7c3a0SDarrick J. Wong int rx_recv_type;
8557c7c3a0SDarrick J. Wong
8657c7c3a0SDarrick J. Wong unsigned char sensor_major;
8757c7c3a0SDarrick J. Wong unsigned char sensor_minor;
8857c7c3a0SDarrick J. Wong
8957c7c3a0SDarrick J. Wong unsigned char num_sensors;
9057c7c3a0SDarrick J. Wong struct ibmpex_sensor_data *sensors;
9157c7c3a0SDarrick J. Wong };
9257c7c3a0SDarrick J. Wong
9357c7c3a0SDarrick J. Wong struct ibmpex_driver_data {
9457c7c3a0SDarrick J. Wong struct list_head bmc_data;
9557c7c3a0SDarrick J. Wong struct ipmi_smi_watcher bmc_events;
9657c7c3a0SDarrick J. Wong struct ipmi_user_hndl ipmi_hndlrs;
9757c7c3a0SDarrick J. Wong };
9857c7c3a0SDarrick J. Wong
9957c7c3a0SDarrick J. Wong static struct ibmpex_driver_data driver_data = {
10057c7c3a0SDarrick J. Wong .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
10157c7c3a0SDarrick J. Wong .bmc_events = {
10257c7c3a0SDarrick J. Wong .owner = THIS_MODULE,
10357c7c3a0SDarrick J. Wong .new_smi = ibmpex_register_bmc,
10457c7c3a0SDarrick J. Wong .smi_gone = ibmpex_bmc_gone,
10557c7c3a0SDarrick J. Wong },
10657c7c3a0SDarrick J. Wong .ipmi_hndlrs = {
10757c7c3a0SDarrick J. Wong .ipmi_recv_hndl = ibmpex_msg_handler,
10857c7c3a0SDarrick J. Wong },
10957c7c3a0SDarrick J. Wong };
11057c7c3a0SDarrick J. Wong
ibmpex_send_message(struct ibmpex_bmc_data * data)11157c7c3a0SDarrick J. Wong static int ibmpex_send_message(struct ibmpex_bmc_data *data)
11257c7c3a0SDarrick J. Wong {
11357c7c3a0SDarrick J. Wong int err;
11457c7c3a0SDarrick J. Wong
11557c7c3a0SDarrick J. Wong err = ipmi_validate_addr(&data->address, sizeof(data->address));
11657c7c3a0SDarrick J. Wong if (err)
11757c7c3a0SDarrick J. Wong goto out;
11857c7c3a0SDarrick J. Wong
11957c7c3a0SDarrick J. Wong data->tx_msgid++;
12057c7c3a0SDarrick J. Wong err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
12157c7c3a0SDarrick J. Wong &data->tx_message, data, 0, 0, 0);
12257c7c3a0SDarrick J. Wong if (err)
12357c7c3a0SDarrick J. Wong goto out1;
12457c7c3a0SDarrick J. Wong
12557c7c3a0SDarrick J. Wong return 0;
12657c7c3a0SDarrick J. Wong out1:
1272ecb044eSDarrick J. Wong dev_err(data->bmc_device, "request_settime=%x\n", err);
12857c7c3a0SDarrick J. Wong return err;
12957c7c3a0SDarrick J. Wong out:
1302ecb044eSDarrick J. Wong dev_err(data->bmc_device, "validate_addr=%x\n", err);
13157c7c3a0SDarrick J. Wong return err;
13257c7c3a0SDarrick J. Wong }
13357c7c3a0SDarrick J. Wong
ibmpex_ver_check(struct ibmpex_bmc_data * data)13457c7c3a0SDarrick J. Wong static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
13557c7c3a0SDarrick J. Wong {
13657c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_VERSION;
13757c7c3a0SDarrick J. Wong data->tx_message.data_len = 1;
13857c7c3a0SDarrick J. Wong ibmpex_send_message(data);
13957c7c3a0SDarrick J. Wong
14057c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete);
14157c7c3a0SDarrick J. Wong
14257c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len != 6)
14357c7c3a0SDarrick J. Wong return -ENOENT;
14457c7c3a0SDarrick J. Wong
14557c7c3a0SDarrick J. Wong data->sensor_major = data->rx_msg_data[0];
14657c7c3a0SDarrick J. Wong data->sensor_minor = data->rx_msg_data[1];
14757c7c3a0SDarrick J. Wong
148b55f3757SGuenter Roeck dev_info(data->bmc_device,
149b55f3757SGuenter Roeck "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n",
15057c7c3a0SDarrick J. Wong data->sensor_major,
15157c7c3a0SDarrick J. Wong data->sensor_minor,
15257c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 2),
15357c7c3a0SDarrick J. Wong data->rx_msg_data[4],
15457c7c3a0SDarrick J. Wong data->rx_msg_data[5],
15557c7c3a0SDarrick J. Wong data->interface);
15657c7c3a0SDarrick J. Wong
15757c7c3a0SDarrick J. Wong return 0;
15857c7c3a0SDarrick J. Wong }
15957c7c3a0SDarrick J. Wong
ibmpex_query_sensor_count(struct ibmpex_bmc_data * data)16057c7c3a0SDarrick J. Wong static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
16157c7c3a0SDarrick J. Wong {
16257c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
16357c7c3a0SDarrick J. Wong data->tx_message.data_len = 1;
16457c7c3a0SDarrick J. Wong ibmpex_send_message(data);
16557c7c3a0SDarrick J. Wong
16657c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete);
16757c7c3a0SDarrick J. Wong
16857c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len != 1)
16957c7c3a0SDarrick J. Wong return -ENOENT;
17057c7c3a0SDarrick J. Wong
17157c7c3a0SDarrick J. Wong return data->rx_msg_data[0];
17257c7c3a0SDarrick J. Wong }
17357c7c3a0SDarrick J. Wong
ibmpex_query_sensor_name(struct ibmpex_bmc_data * data,int sensor)17457c7c3a0SDarrick J. Wong static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
17557c7c3a0SDarrick J. Wong {
17657c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
17757c7c3a0SDarrick J. Wong data->tx_msg_data[1] = sensor;
17857c7c3a0SDarrick J. Wong data->tx_message.data_len = 2;
17957c7c3a0SDarrick J. Wong ibmpex_send_message(data);
18057c7c3a0SDarrick J. Wong
18157c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete);
18257c7c3a0SDarrick J. Wong
18357c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len < 1)
18457c7c3a0SDarrick J. Wong return -ENOENT;
18557c7c3a0SDarrick J. Wong
18657c7c3a0SDarrick J. Wong return 0;
18757c7c3a0SDarrick J. Wong }
18857c7c3a0SDarrick J. Wong
ibmpex_query_sensor_data(struct ibmpex_bmc_data * data,int sensor)18957c7c3a0SDarrick J. Wong static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
19057c7c3a0SDarrick J. Wong {
19157c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
19257c7c3a0SDarrick J. Wong data->tx_msg_data[1] = sensor;
19357c7c3a0SDarrick J. Wong data->tx_message.data_len = 2;
19457c7c3a0SDarrick J. Wong ibmpex_send_message(data);
19557c7c3a0SDarrick J. Wong
19657c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete);
19757c7c3a0SDarrick J. Wong
19857c7c3a0SDarrick J. Wong if (data->rx_result || data->rx_msg_len < 26) {
1992ecb044eSDarrick J. Wong dev_err(data->bmc_device, "Error reading sensor %d.\n",
20057c7c3a0SDarrick J. Wong sensor);
20157c7c3a0SDarrick J. Wong return -ENOENT;
20257c7c3a0SDarrick J. Wong }
20357c7c3a0SDarrick J. Wong
20457c7c3a0SDarrick J. Wong return 0;
20557c7c3a0SDarrick J. Wong }
20657c7c3a0SDarrick J. Wong
ibmpex_reset_high_low_data(struct ibmpex_bmc_data * data)20757c7c3a0SDarrick J. Wong static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
20857c7c3a0SDarrick J. Wong {
20957c7c3a0SDarrick J. Wong data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
21057c7c3a0SDarrick J. Wong data->tx_message.data_len = 1;
21157c7c3a0SDarrick J. Wong ibmpex_send_message(data);
21257c7c3a0SDarrick J. Wong
21357c7c3a0SDarrick J. Wong wait_for_completion(&data->read_complete);
21457c7c3a0SDarrick J. Wong
21557c7c3a0SDarrick J. Wong return 0;
21657c7c3a0SDarrick J. Wong }
21757c7c3a0SDarrick J. Wong
ibmpex_update_device(struct ibmpex_bmc_data * data)21857c7c3a0SDarrick J. Wong static void ibmpex_update_device(struct ibmpex_bmc_data *data)
21957c7c3a0SDarrick J. Wong {
22057c7c3a0SDarrick J. Wong int i, err;
22157c7c3a0SDarrick J. Wong
22257c7c3a0SDarrick J. Wong mutex_lock(&data->lock);
22357c7c3a0SDarrick J. Wong if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
22457c7c3a0SDarrick J. Wong data->valid)
22557c7c3a0SDarrick J. Wong goto out;
22657c7c3a0SDarrick J. Wong
22757c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++) {
22857c7c3a0SDarrick J. Wong if (!data->sensors[i].in_use)
22957c7c3a0SDarrick J. Wong continue;
23057c7c3a0SDarrick J. Wong err = ibmpex_query_sensor_data(data, i);
23157c7c3a0SDarrick J. Wong if (err)
23257c7c3a0SDarrick J. Wong continue;
23357c7c3a0SDarrick J. Wong data->sensors[i].values[0] =
23457c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 16);
23557c7c3a0SDarrick J. Wong data->sensors[i].values[1] =
23657c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 18);
23757c7c3a0SDarrick J. Wong data->sensors[i].values[2] =
23857c7c3a0SDarrick J. Wong extract_value(data->rx_msg_data, 20);
23957c7c3a0SDarrick J. Wong }
24057c7c3a0SDarrick J. Wong
24157c7c3a0SDarrick J. Wong data->last_updated = jiffies;
242952a11caSPaul Fertser data->valid = true;
24357c7c3a0SDarrick J. Wong
24457c7c3a0SDarrick J. Wong out:
24557c7c3a0SDarrick J. Wong mutex_unlock(&data->lock);
24657c7c3a0SDarrick J. Wong }
24757c7c3a0SDarrick J. Wong
get_bmc_data(int iface)24857c7c3a0SDarrick J. Wong static struct ibmpex_bmc_data *get_bmc_data(int iface)
24957c7c3a0SDarrick J. Wong {
25057c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *p, *next;
25157c7c3a0SDarrick J. Wong
25257c7c3a0SDarrick J. Wong list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
25357c7c3a0SDarrick J. Wong if (p->interface == iface)
25457c7c3a0SDarrick J. Wong return p;
25557c7c3a0SDarrick J. Wong
25657c7c3a0SDarrick J. Wong return NULL;
25757c7c3a0SDarrick J. Wong }
25857c7c3a0SDarrick J. Wong
name_show(struct device * dev,struct device_attribute * devattr,char * buf)25909cb4161SGuenter Roeck static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
26057c7c3a0SDarrick J. Wong char *buf)
26157c7c3a0SDarrick J. Wong {
26257c7c3a0SDarrick J. Wong return sprintf(buf, "%s\n", DRVNAME);
26357c7c3a0SDarrick J. Wong }
26409cb4161SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(name, name, 0);
26557c7c3a0SDarrick J. Wong
ibmpex_show_sensor(struct device * dev,struct device_attribute * devattr,char * buf)26657c7c3a0SDarrick J. Wong static ssize_t ibmpex_show_sensor(struct device *dev,
26757c7c3a0SDarrick J. Wong struct device_attribute *devattr,
26857c7c3a0SDarrick J. Wong char *buf)
26957c7c3a0SDarrick J. Wong {
27057c7c3a0SDarrick J. Wong struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
27157c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
27257c7c3a0SDarrick J. Wong int mult = data->sensors[attr->index].multiplier;
27357c7c3a0SDarrick J. Wong ibmpex_update_device(data);
27457c7c3a0SDarrick J. Wong
27557c7c3a0SDarrick J. Wong return sprintf(buf, "%d\n",
27657c7c3a0SDarrick J. Wong data->sensors[attr->index].values[attr->nr] * mult);
27757c7c3a0SDarrick J. Wong }
27857c7c3a0SDarrick J. Wong
ibmpex_high_low_store(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)27909cb4161SGuenter Roeck static ssize_t ibmpex_high_low_store(struct device *dev,
28057c7c3a0SDarrick J. Wong struct device_attribute *devattr,
28109cb4161SGuenter Roeck const char *buf, size_t count)
28257c7c3a0SDarrick J. Wong {
28357c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
28457c7c3a0SDarrick J. Wong
28557c7c3a0SDarrick J. Wong ibmpex_reset_high_low_data(data);
28657c7c3a0SDarrick J. Wong
28757c7c3a0SDarrick J. Wong return count;
28857c7c3a0SDarrick J. Wong }
28957c7c3a0SDarrick J. Wong
29009cb4161SGuenter Roeck static SENSOR_DEVICE_ATTR_WO(reset_high_low, ibmpex_high_low, 0);
29157c7c3a0SDarrick J. Wong
is_power_sensor(const char * sensor_id,int len)29257c7c3a0SDarrick J. Wong static int is_power_sensor(const char *sensor_id, int len)
29357c7c3a0SDarrick J. Wong {
29457c7c3a0SDarrick J. Wong if (len < PEX_SENSOR_TYPE_LEN)
29557c7c3a0SDarrick J. Wong return 0;
29657c7c3a0SDarrick J. Wong
29757c7c3a0SDarrick J. Wong if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))
29857c7c3a0SDarrick J. Wong return 1;
29957c7c3a0SDarrick J. Wong return 0;
30057c7c3a0SDarrick J. Wong }
30157c7c3a0SDarrick J. Wong
is_temp_sensor(const char * sensor_id,int len)30257c7c3a0SDarrick J. Wong static int is_temp_sensor(const char *sensor_id, int len)
30357c7c3a0SDarrick J. Wong {
30457c7c3a0SDarrick J. Wong if (len < PEX_SENSOR_TYPE_LEN)
30557c7c3a0SDarrick J. Wong return 0;
30657c7c3a0SDarrick J. Wong
30757c7c3a0SDarrick J. Wong if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))
30857c7c3a0SDarrick J. Wong return 1;
30957c7c3a0SDarrick J. Wong return 0;
31057c7c3a0SDarrick J. Wong }
31157c7c3a0SDarrick J. Wong
power_sensor_multiplier(struct ibmpex_bmc_data * data,const char * sensor_id,int len)312df9cb033SDarrick J. Wong static int power_sensor_multiplier(struct ibmpex_bmc_data *data,
313df9cb033SDarrick J. Wong const char *sensor_id, int len)
31457c7c3a0SDarrick J. Wong {
31557c7c3a0SDarrick J. Wong int i;
31657c7c3a0SDarrick J. Wong
317df9cb033SDarrick J. Wong if (data->sensor_major == 2)
318df9cb033SDarrick J. Wong return 1000000;
319df9cb033SDarrick J. Wong
32057c7c3a0SDarrick J. Wong for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)
32157c7c3a0SDarrick J. Wong if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))
32257c7c3a0SDarrick J. Wong return 1000000;
32357c7c3a0SDarrick J. Wong
32457c7c3a0SDarrick J. Wong return 100000;
32557c7c3a0SDarrick J. Wong }
32657c7c3a0SDarrick J. Wong
create_sensor(struct ibmpex_bmc_data * data,int type,int counter,int sensor,int func)32757c7c3a0SDarrick J. Wong static int create_sensor(struct ibmpex_bmc_data *data, int type,
32857c7c3a0SDarrick J. Wong int counter, int sensor, int func)
32957c7c3a0SDarrick J. Wong {
33057c7c3a0SDarrick J. Wong int err;
33157c7c3a0SDarrick J. Wong char *n;
33257c7c3a0SDarrick J. Wong
33357c7c3a0SDarrick J. Wong n = kmalloc(32, GFP_KERNEL);
33457c7c3a0SDarrick J. Wong if (!n)
33557c7c3a0SDarrick J. Wong return -ENOMEM;
33657c7c3a0SDarrick J. Wong
33757c7c3a0SDarrick J. Wong if (type == TEMP_SENSOR)
338a0fc74d4SRasmus Villemoes sprintf(n, "temp%d_input%s",
339a0fc74d4SRasmus Villemoes counter, sensor_name_suffixes[func]);
34057c7c3a0SDarrick J. Wong else if (type == POWER_SENSOR)
341a0fc74d4SRasmus Villemoes sprintf(n, "power%d_average%s",
342a0fc74d4SRasmus Villemoes counter, sensor_name_suffixes[func]);
34357c7c3a0SDarrick J. Wong
344fb794e0fSGuenter Roeck sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr);
34557c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].dev_attr.attr.name = n;
34609cb4161SGuenter Roeck data->sensors[sensor].attr[func].dev_attr.attr.mode = 0444;
34757c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;
34857c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].index = sensor;
34957c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].nr = func;
35057c7c3a0SDarrick J. Wong
35157c7c3a0SDarrick J. Wong err = device_create_file(data->bmc_device,
35257c7c3a0SDarrick J. Wong &data->sensors[sensor].attr[func].dev_attr);
35357c7c3a0SDarrick J. Wong if (err) {
35457c7c3a0SDarrick J. Wong data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;
35557c7c3a0SDarrick J. Wong kfree(n);
35657c7c3a0SDarrick J. Wong return err;
35757c7c3a0SDarrick J. Wong }
35857c7c3a0SDarrick J. Wong
35957c7c3a0SDarrick J. Wong return 0;
36057c7c3a0SDarrick J. Wong }
36157c7c3a0SDarrick J. Wong
ibmpex_find_sensors(struct ibmpex_bmc_data * data)36257c7c3a0SDarrick J. Wong static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)
36357c7c3a0SDarrick J. Wong {
36457c7c3a0SDarrick J. Wong int i, j, err;
36557c7c3a0SDarrick J. Wong int sensor_type;
36657c7c3a0SDarrick J. Wong int sensor_counter;
36757c7c3a0SDarrick J. Wong int num_power = 0;
36857c7c3a0SDarrick J. Wong int num_temp = 0;
36957c7c3a0SDarrick J. Wong
37057c7c3a0SDarrick J. Wong err = ibmpex_query_sensor_count(data);
37157c7c3a0SDarrick J. Wong if (err <= 0)
37257c7c3a0SDarrick J. Wong return -ENOENT;
37357c7c3a0SDarrick J. Wong data->num_sensors = err;
37457c7c3a0SDarrick J. Wong
3756396bb22SKees Cook data->sensors = kcalloc(data->num_sensors, sizeof(*data->sensors),
37657c7c3a0SDarrick J. Wong GFP_KERNEL);
37757c7c3a0SDarrick J. Wong if (!data->sensors)
37857c7c3a0SDarrick J. Wong return -ENOMEM;
37957c7c3a0SDarrick J. Wong
38057c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++) {
38157c7c3a0SDarrick J. Wong err = ibmpex_query_sensor_name(data, i);
38257c7c3a0SDarrick J. Wong if (err)
38357c7c3a0SDarrick J. Wong continue;
38457c7c3a0SDarrick J. Wong
38557c7c3a0SDarrick J. Wong if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {
38657c7c3a0SDarrick J. Wong sensor_type = POWER_SENSOR;
38757c7c3a0SDarrick J. Wong num_power++;
38857c7c3a0SDarrick J. Wong sensor_counter = num_power;
38957c7c3a0SDarrick J. Wong data->sensors[i].multiplier =
390df9cb033SDarrick J. Wong power_sensor_multiplier(data,
391df9cb033SDarrick J. Wong data->rx_msg_data,
39257c7c3a0SDarrick J. Wong data->rx_msg_len);
39357c7c3a0SDarrick J. Wong } else if (is_temp_sensor(data->rx_msg_data,
39457c7c3a0SDarrick J. Wong data->rx_msg_len)) {
39557c7c3a0SDarrick J. Wong sensor_type = TEMP_SENSOR;
39657c7c3a0SDarrick J. Wong num_temp++;
39757c7c3a0SDarrick J. Wong sensor_counter = num_temp;
398ffda6857SDarrick J. Wong data->sensors[i].multiplier = 1000;
39957c7c3a0SDarrick J. Wong } else
40057c7c3a0SDarrick J. Wong continue;
40157c7c3a0SDarrick J. Wong
40257c7c3a0SDarrick J. Wong data->sensors[i].in_use = 1;
40357c7c3a0SDarrick J. Wong
40457c7c3a0SDarrick J. Wong /* Create attributes */
40557c7c3a0SDarrick J. Wong for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
40657c7c3a0SDarrick J. Wong err = create_sensor(data, sensor_type, sensor_counter,
40757c7c3a0SDarrick J. Wong i, j);
40857c7c3a0SDarrick J. Wong if (err)
40957c7c3a0SDarrick J. Wong goto exit_remove;
41057c7c3a0SDarrick J. Wong }
41157c7c3a0SDarrick J. Wong }
41257c7c3a0SDarrick J. Wong
41357c7c3a0SDarrick J. Wong err = device_create_file(data->bmc_device,
41457c7c3a0SDarrick J. Wong &sensor_dev_attr_reset_high_low.dev_attr);
41557c7c3a0SDarrick J. Wong if (err)
41657c7c3a0SDarrick J. Wong goto exit_remove;
41757c7c3a0SDarrick J. Wong
41857c7c3a0SDarrick J. Wong err = device_create_file(data->bmc_device,
41957c7c3a0SDarrick J. Wong &sensor_dev_attr_name.dev_attr);
42057c7c3a0SDarrick J. Wong if (err)
42157c7c3a0SDarrick J. Wong goto exit_remove;
42257c7c3a0SDarrick J. Wong
42357c7c3a0SDarrick J. Wong return 0;
42457c7c3a0SDarrick J. Wong
42557c7c3a0SDarrick J. Wong exit_remove:
42657c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device,
42757c7c3a0SDarrick J. Wong &sensor_dev_attr_reset_high_low.dev_attr);
42857c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
42957c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++)
43057c7c3a0SDarrick J. Wong for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
43157c7c3a0SDarrick J. Wong if (!data->sensors[i].attr[j].dev_attr.attr.name)
43257c7c3a0SDarrick J. Wong continue;
43357c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device,
43457c7c3a0SDarrick J. Wong &data->sensors[i].attr[j].dev_attr);
43557c7c3a0SDarrick J. Wong kfree(data->sensors[i].attr[j].dev_attr.attr.name);
43657c7c3a0SDarrick J. Wong }
43757c7c3a0SDarrick J. Wong
43857c7c3a0SDarrick J. Wong kfree(data->sensors);
43957c7c3a0SDarrick J. Wong return err;
44057c7c3a0SDarrick J. Wong }
44157c7c3a0SDarrick J. Wong
ibmpex_register_bmc(int iface,struct device * dev)44257c7c3a0SDarrick J. Wong static void ibmpex_register_bmc(int iface, struct device *dev)
44357c7c3a0SDarrick J. Wong {
44457c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data;
44557c7c3a0SDarrick J. Wong int err;
44657c7c3a0SDarrick J. Wong
44757c7c3a0SDarrick J. Wong data = kzalloc(sizeof(*data), GFP_KERNEL);
4486d33d304SJingoo Han if (!data)
44957c7c3a0SDarrick J. Wong return;
45057c7c3a0SDarrick J. Wong
45157c7c3a0SDarrick J. Wong data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
45257c7c3a0SDarrick J. Wong data->address.channel = IPMI_BMC_CHANNEL;
45357c7c3a0SDarrick J. Wong data->address.data[0] = 0;
45457c7c3a0SDarrick J. Wong data->interface = iface;
45557c7c3a0SDarrick J. Wong data->bmc_device = dev;
45657c7c3a0SDarrick J. Wong
45757c7c3a0SDarrick J. Wong /* Create IPMI messaging interface user */
45857c7c3a0SDarrick J. Wong err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
45957c7c3a0SDarrick J. Wong data, &data->user);
46057c7c3a0SDarrick J. Wong if (err < 0) {
461b55f3757SGuenter Roeck dev_err(dev,
462b55f3757SGuenter Roeck "Unable to register user with IPMI interface %d\n",
463b55f3757SGuenter Roeck data->interface);
46457c7c3a0SDarrick J. Wong goto out;
46557c7c3a0SDarrick J. Wong }
46657c7c3a0SDarrick J. Wong
46757c7c3a0SDarrick J. Wong mutex_init(&data->lock);
46857c7c3a0SDarrick J. Wong
46957c7c3a0SDarrick J. Wong /* Initialize message */
47057c7c3a0SDarrick J. Wong data->tx_msgid = 0;
47157c7c3a0SDarrick J. Wong init_completion(&data->read_complete);
47257c7c3a0SDarrick J. Wong data->tx_message.netfn = PEX_NET_FUNCTION;
47357c7c3a0SDarrick J. Wong data->tx_message.cmd = PEX_COMMAND;
47457c7c3a0SDarrick J. Wong data->tx_message.data = data->tx_msg_data;
47557c7c3a0SDarrick J. Wong
47657c7c3a0SDarrick J. Wong /* Does this BMC support PowerExecutive? */
47757c7c3a0SDarrick J. Wong err = ibmpex_ver_check(data);
47857c7c3a0SDarrick J. Wong if (err)
47957c7c3a0SDarrick J. Wong goto out_user;
48057c7c3a0SDarrick J. Wong
48157c7c3a0SDarrick J. Wong /* Register the BMC as a HWMON class device */
48257c7c3a0SDarrick J. Wong data->hwmon_dev = hwmon_device_register(data->bmc_device);
48357c7c3a0SDarrick J. Wong
48457c7c3a0SDarrick J. Wong if (IS_ERR(data->hwmon_dev)) {
485b55f3757SGuenter Roeck dev_err(data->bmc_device,
486b55f3757SGuenter Roeck "Unable to register hwmon device for IPMI interface %d\n",
48757c7c3a0SDarrick J. Wong data->interface);
4884cfdbe7fSDarrick J. Wong goto out_user;
48957c7c3a0SDarrick J. Wong }
49057c7c3a0SDarrick J. Wong
49157c7c3a0SDarrick J. Wong /* finally add the new bmc data to the bmc data list */
49257c7c3a0SDarrick J. Wong dev_set_drvdata(dev, data);
49357c7c3a0SDarrick J. Wong list_add_tail(&data->list, &driver_data.bmc_data);
49457c7c3a0SDarrick J. Wong
49557c7c3a0SDarrick J. Wong /* Now go find all the sensors */
49657c7c3a0SDarrick J. Wong err = ibmpex_find_sensors(data);
49757c7c3a0SDarrick J. Wong if (err) {
4982ecb044eSDarrick J. Wong dev_err(data->bmc_device, "Error %d finding sensors\n", err);
49957c7c3a0SDarrick J. Wong goto out_register;
50057c7c3a0SDarrick J. Wong }
50157c7c3a0SDarrick J. Wong
50257c7c3a0SDarrick J. Wong return;
50357c7c3a0SDarrick J. Wong
50457c7c3a0SDarrick J. Wong out_register:
505e2a87785SGaosheng Cui list_del(&data->list);
50657c7c3a0SDarrick J. Wong hwmon_device_unregister(data->hwmon_dev);
50757c7c3a0SDarrick J. Wong out_user:
50857c7c3a0SDarrick J. Wong ipmi_destroy_user(data->user);
50957c7c3a0SDarrick J. Wong out:
51057c7c3a0SDarrick J. Wong kfree(data);
51157c7c3a0SDarrick J. Wong }
51257c7c3a0SDarrick J. Wong
ibmpex_bmc_delete(struct ibmpex_bmc_data * data)51357c7c3a0SDarrick J. Wong static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)
51457c7c3a0SDarrick J. Wong {
51557c7c3a0SDarrick J. Wong int i, j;
51657c7c3a0SDarrick J. Wong
51757c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device,
51857c7c3a0SDarrick J. Wong &sensor_dev_attr_reset_high_low.dev_attr);
51957c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device, &sensor_dev_attr_name.dev_attr);
52057c7c3a0SDarrick J. Wong for (i = 0; i < data->num_sensors; i++)
52157c7c3a0SDarrick J. Wong for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {
52257c7c3a0SDarrick J. Wong if (!data->sensors[i].attr[j].dev_attr.attr.name)
52357c7c3a0SDarrick J. Wong continue;
52457c7c3a0SDarrick J. Wong device_remove_file(data->bmc_device,
52557c7c3a0SDarrick J. Wong &data->sensors[i].attr[j].dev_attr);
52657c7c3a0SDarrick J. Wong kfree(data->sensors[i].attr[j].dev_attr.attr.name);
52757c7c3a0SDarrick J. Wong }
52857c7c3a0SDarrick J. Wong
52957c7c3a0SDarrick J. Wong list_del(&data->list);
53057c7c3a0SDarrick J. Wong dev_set_drvdata(data->bmc_device, NULL);
53157c7c3a0SDarrick J. Wong hwmon_device_unregister(data->hwmon_dev);
53257c7c3a0SDarrick J. Wong ipmi_destroy_user(data->user);
53357c7c3a0SDarrick J. Wong kfree(data->sensors);
53457c7c3a0SDarrick J. Wong kfree(data);
53557c7c3a0SDarrick J. Wong }
53657c7c3a0SDarrick J. Wong
ibmpex_bmc_gone(int iface)53757c7c3a0SDarrick J. Wong static void ibmpex_bmc_gone(int iface)
53857c7c3a0SDarrick J. Wong {
53957c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *data = get_bmc_data(iface);
54057c7c3a0SDarrick J. Wong
54157c7c3a0SDarrick J. Wong if (!data)
54257c7c3a0SDarrick J. Wong return;
54357c7c3a0SDarrick J. Wong
54457c7c3a0SDarrick J. Wong ibmpex_bmc_delete(data);
54557c7c3a0SDarrick J. Wong }
54657c7c3a0SDarrick J. Wong
ibmpex_msg_handler(struct ipmi_recv_msg * msg,void * user_msg_data)54757c7c3a0SDarrick J. Wong static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
54857c7c3a0SDarrick J. Wong {
549*554df454SXU pengfei struct ibmpex_bmc_data *data = user_msg_data;
55057c7c3a0SDarrick J. Wong
55157c7c3a0SDarrick J. Wong if (msg->msgid != data->tx_msgid) {
552b55f3757SGuenter Roeck dev_err(data->bmc_device,
553b55f3757SGuenter Roeck "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
55457c7c3a0SDarrick J. Wong (int)msg->msgid,
55557c7c3a0SDarrick J. Wong (int)data->tx_msgid);
55657c7c3a0SDarrick J. Wong ipmi_free_recv_msg(msg);
55757c7c3a0SDarrick J. Wong return;
55857c7c3a0SDarrick J. Wong }
55957c7c3a0SDarrick J. Wong
56057c7c3a0SDarrick J. Wong data->rx_recv_type = msg->recv_type;
56157c7c3a0SDarrick J. Wong if (msg->msg.data_len > 0)
56257c7c3a0SDarrick J. Wong data->rx_result = msg->msg.data[0];
56357c7c3a0SDarrick J. Wong else
56457c7c3a0SDarrick J. Wong data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
56557c7c3a0SDarrick J. Wong
56657c7c3a0SDarrick J. Wong if (msg->msg.data_len > 1) {
56757c7c3a0SDarrick J. Wong data->rx_msg_len = msg->msg.data_len - 1;
56857c7c3a0SDarrick J. Wong memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
56957c7c3a0SDarrick J. Wong } else
57057c7c3a0SDarrick J. Wong data->rx_msg_len = 0;
57157c7c3a0SDarrick J. Wong
57257c7c3a0SDarrick J. Wong ipmi_free_recv_msg(msg);
57357c7c3a0SDarrick J. Wong complete(&data->read_complete);
57457c7c3a0SDarrick J. Wong }
57557c7c3a0SDarrick J. Wong
ibmpex_init(void)57657c7c3a0SDarrick J. Wong static int __init ibmpex_init(void)
57757c7c3a0SDarrick J. Wong {
57857c7c3a0SDarrick J. Wong return ipmi_smi_watcher_register(&driver_data.bmc_events);
57957c7c3a0SDarrick J. Wong }
58057c7c3a0SDarrick J. Wong
ibmpex_exit(void)58157c7c3a0SDarrick J. Wong static void __exit ibmpex_exit(void)
58257c7c3a0SDarrick J. Wong {
58357c7c3a0SDarrick J. Wong struct ibmpex_bmc_data *p, *next;
58457c7c3a0SDarrick J. Wong
58557c7c3a0SDarrick J. Wong ipmi_smi_watcher_unregister(&driver_data.bmc_events);
58657c7c3a0SDarrick J. Wong list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
58757c7c3a0SDarrick J. Wong ibmpex_bmc_delete(p);
58857c7c3a0SDarrick J. Wong }
58957c7c3a0SDarrick J. Wong
5905407e051SDarrick J. Wong MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
59157c7c3a0SDarrick J. Wong MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
59257c7c3a0SDarrick J. Wong MODULE_LICENSE("GPL");
59357c7c3a0SDarrick J. Wong
59457c7c3a0SDarrick J. Wong module_init(ibmpex_init);
59557c7c3a0SDarrick J. Wong module_exit(ibmpex_exit);
596ff921361SDarrick J. Wong
597ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
598ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
599ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
600ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
601ff921361SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
602