xref: /openbmc/linux/drivers/hwmon/ibmpex.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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