brintos

brintos / linux-shallow public Read only

0
0
Text · 14.0 KiB · 228c5f6 Raw
596 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * A hwmon driver for the IBM PowerExecutive temperature/power sensors4 * Copyright (C) 2007 IBM5 *6 * Author: Darrick J. Wong <darrick.wong@oracle.com>7 */8 9#include <linux/ipmi.h>10#include <linux/module.h>11#include <linux/hwmon.h>12#include <linux/hwmon-sysfs.h>13#include <linux/jiffies.h>14#include <linux/mutex.h>15#include <linux/slab.h>16#include <linux/err.h>17 18#define REFRESH_INTERVAL	(2 * HZ)19#define DRVNAME			"ibmpex"20 21#define PEX_GET_VERSION		122#define PEX_GET_SENSOR_COUNT	223#define PEX_GET_SENSOR_NAME	324#define PEX_RESET_HIGH_LOW	425#define PEX_GET_SENSOR_DATA	626 27#define PEX_NET_FUNCTION	0x3A28#define PEX_COMMAND		0x3C29 30static inline u16 extract_value(const char *data, int offset)31{32	return be16_to_cpup((__be16 *)&data[offset]);33}34 35#define TEMP_SENSOR		136#define POWER_SENSOR		237 38#define PEX_SENSOR_TYPE_LEN	339static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};40static u8 const temp_sensor_sig[]  = {0x74, 0x65, 0x6D};41 42#define PEX_MULT_LEN		243static u8 const watt_sensor_sig[]  = {0x41, 0x43};44 45#define PEX_NUM_SENSOR_FUNCS	346static const char * const sensor_name_suffixes[] = {47	"",48	"_lowest",49	"_highest"50};51 52static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);53static void ibmpex_register_bmc(int iface, struct device *dev);54static void ibmpex_bmc_gone(int iface);55 56struct ibmpex_sensor_data {57	int			in_use;58	s16			values[PEX_NUM_SENSOR_FUNCS];59	int			multiplier;60 61	struct sensor_device_attribute_2	attr[PEX_NUM_SENSOR_FUNCS];62};63 64struct ibmpex_bmc_data {65	struct list_head	list;66	struct device		*hwmon_dev;67	struct device		*bmc_device;68	struct mutex		lock;69	bool			valid;70	unsigned long		last_updated;	/* In jiffies */71 72	struct ipmi_addr	address;73	struct completion	read_complete;74	struct ipmi_user	*user;75	int			interface;76 77	struct kernel_ipmi_msg	tx_message;78	unsigned char		tx_msg_data[IPMI_MAX_MSG_LENGTH];79	long			tx_msgid;80 81	unsigned char		rx_msg_data[IPMI_MAX_MSG_LENGTH];82	unsigned long		rx_msg_len;83	unsigned char		rx_result;84	int			rx_recv_type;85 86	unsigned char		sensor_major;87	unsigned char		sensor_minor;88 89	unsigned char		num_sensors;90	struct ibmpex_sensor_data	*sensors;91};92 93struct ibmpex_driver_data {94	struct list_head	bmc_data;95	struct ipmi_smi_watcher	bmc_events;96	struct ipmi_user_hndl	ipmi_hndlrs;97};98 99static struct ibmpex_driver_data driver_data = {100	.bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),101	.bmc_events = {102		.owner = THIS_MODULE,103		.new_smi = ibmpex_register_bmc,104		.smi_gone = ibmpex_bmc_gone,105	},106	.ipmi_hndlrs = {107		.ipmi_recv_hndl = ibmpex_msg_handler,108	},109};110 111static int ibmpex_send_message(struct ibmpex_bmc_data *data)112{113	int err;114 115	err = ipmi_validate_addr(&data->address, sizeof(data->address));116	if (err)117		goto out;118 119	data->tx_msgid++;120	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,121				   &data->tx_message, data, 0, 0, 0);122	if (err)123		goto out1;124 125	return 0;126out1:127	dev_err(data->bmc_device, "request_settime=%x\n", err);128	return err;129out:130	dev_err(data->bmc_device, "validate_addr=%x\n", err);131	return err;132}133 134static int ibmpex_ver_check(struct ibmpex_bmc_data *data)135{136	data->tx_msg_data[0] = PEX_GET_VERSION;137	data->tx_message.data_len = 1;138	ibmpex_send_message(data);139 140	wait_for_completion(&data->read_complete);141 142	if (data->rx_result || data->rx_msg_len != 6)143		return -ENOENT;144 145	data->sensor_major = data->rx_msg_data[0];146	data->sensor_minor = data->rx_msg_data[1];147 148	dev_info(data->bmc_device,149		 "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n",150		 data->sensor_major,151		 data->sensor_minor,152		 extract_value(data->rx_msg_data, 2),153		 data->rx_msg_data[4],154		 data->rx_msg_data[5],155		 data->interface);156 157	return 0;158}159 160static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)161{162	data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;163	data->tx_message.data_len = 1;164	ibmpex_send_message(data);165 166	wait_for_completion(&data->read_complete);167 168	if (data->rx_result || data->rx_msg_len != 1)169		return -ENOENT;170 171	return data->rx_msg_data[0];172}173 174static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)175{176	data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;177	data->tx_msg_data[1] = sensor;178	data->tx_message.data_len = 2;179	ibmpex_send_message(data);180 181	wait_for_completion(&data->read_complete);182 183	if (data->rx_result || data->rx_msg_len < 1)184		return -ENOENT;185 186	return 0;187}188 189static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)190{191	data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;192	data->tx_msg_data[1] = sensor;193	data->tx_message.data_len = 2;194	ibmpex_send_message(data);195 196	wait_for_completion(&data->read_complete);197 198	if (data->rx_result || data->rx_msg_len < 26) {199		dev_err(data->bmc_device, "Error reading sensor %d.\n",200			sensor);201		return -ENOENT;202	}203 204	return 0;205}206 207static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)208{209	data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;210	data->tx_message.data_len = 1;211	ibmpex_send_message(data);212 213	wait_for_completion(&data->read_complete);214 215	return 0;216}217 218static void ibmpex_update_device(struct ibmpex_bmc_data *data)219{220	int i, err;221 222	mutex_lock(&data->lock);223	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&224	    data->valid)225		goto out;226 227	for (i = 0; i < data->num_sensors; i++) {228		if (!data->sensors[i].in_use)229			continue;230		err = ibmpex_query_sensor_data(data, i);231		if (err)232			continue;233		data->sensors[i].values[0] =234			extract_value(data->rx_msg_data, 16);235		data->sensors[i].values[1] =236			extract_value(data->rx_msg_data, 18);237		data->sensors[i].values[2] =238			extract_value(data->rx_msg_data, 20);239	}240 241	data->last_updated = jiffies;242	data->valid = true;243 244out:245	mutex_unlock(&data->lock);246}247 248static struct ibmpex_bmc_data *get_bmc_data(int iface)249{250	struct ibmpex_bmc_data *p, *next;251 252	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)253		if (p->interface == iface)254			return p;255 256	return NULL;257}258 259static DEVICE_STRING_ATTR_RO(name, 0444, DRVNAME);260 261static ssize_t ibmpex_show_sensor(struct device *dev,262				  struct device_attribute *devattr,263				  char *buf)264{265	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);266	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);267	int mult = data->sensors[attr->index].multiplier;268	ibmpex_update_device(data);269 270	return sprintf(buf, "%d\n",271		       data->sensors[attr->index].values[attr->nr] * mult);272}273 274static ssize_t ibmpex_high_low_store(struct device *dev,275				     struct device_attribute *devattr,276				     const char *buf, size_t count)277{278	struct ibmpex_bmc_data *data = dev_get_drvdata(dev);279 280	ibmpex_reset_high_low_data(data);281 282	return count;283}284 285static SENSOR_DEVICE_ATTR_WO(reset_high_low, ibmpex_high_low, 0);286 287static int is_power_sensor(const char *sensor_id, int len)288{289	if (len < PEX_SENSOR_TYPE_LEN)290		return 0;291 292	if (!memcmp(sensor_id, power_sensor_sig, PEX_SENSOR_TYPE_LEN))293		return 1;294	return 0;295}296 297static int is_temp_sensor(const char *sensor_id, int len)298{299	if (len < PEX_SENSOR_TYPE_LEN)300		return 0;301 302	if (!memcmp(sensor_id, temp_sensor_sig, PEX_SENSOR_TYPE_LEN))303		return 1;304	return 0;305}306 307static int power_sensor_multiplier(struct ibmpex_bmc_data *data,308				   const char *sensor_id, int len)309{310	int i;311 312	if (data->sensor_major == 2)313		return 1000000;314 315	for (i = PEX_SENSOR_TYPE_LEN; i < len - 1; i++)316		if (!memcmp(&sensor_id[i], watt_sensor_sig, PEX_MULT_LEN))317			return 1000000;318 319	return 100000;320}321 322static int create_sensor(struct ibmpex_bmc_data *data, int type,323			 int counter, int sensor, int func)324{325	int err;326	char *n;327 328	n = kmalloc(32, GFP_KERNEL);329	if (!n)330		return -ENOMEM;331 332	if (type == TEMP_SENSOR)333		sprintf(n, "temp%d_input%s",334			counter, sensor_name_suffixes[func]);335	else if (type == POWER_SENSOR)336		sprintf(n, "power%d_average%s",337			counter, sensor_name_suffixes[func]);338 339	sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr);340	data->sensors[sensor].attr[func].dev_attr.attr.name = n;341	data->sensors[sensor].attr[func].dev_attr.attr.mode = 0444;342	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor;343	data->sensors[sensor].attr[func].index = sensor;344	data->sensors[sensor].attr[func].nr = func;345 346	err = device_create_file(data->bmc_device,347				 &data->sensors[sensor].attr[func].dev_attr);348	if (err) {349		data->sensors[sensor].attr[func].dev_attr.attr.name = NULL;350		kfree(n);351		return err;352	}353 354	return 0;355}356 357static int ibmpex_find_sensors(struct ibmpex_bmc_data *data)358{359	int i, j, err;360	int sensor_type;361	int sensor_counter;362	int num_power = 0;363	int num_temp = 0;364 365	err = ibmpex_query_sensor_count(data);366	if (err <= 0)367		return -ENOENT;368	data->num_sensors = err;369 370	data->sensors = kcalloc(data->num_sensors, sizeof(*data->sensors),371				GFP_KERNEL);372	if (!data->sensors)373		return -ENOMEM;374 375	for (i = 0; i < data->num_sensors; i++) {376		err = ibmpex_query_sensor_name(data, i);377		if (err)378			continue;379 380		if (is_power_sensor(data->rx_msg_data, data->rx_msg_len)) {381			sensor_type = POWER_SENSOR;382			num_power++;383			sensor_counter = num_power;384			data->sensors[i].multiplier =385				power_sensor_multiplier(data,386							data->rx_msg_data,387							data->rx_msg_len);388		} else if (is_temp_sensor(data->rx_msg_data,389					  data->rx_msg_len)) {390			sensor_type = TEMP_SENSOR;391			num_temp++;392			sensor_counter = num_temp;393			data->sensors[i].multiplier = 1000;394		} else395			continue;396 397		data->sensors[i].in_use = 1;398 399		/* Create attributes */400		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {401			err = create_sensor(data, sensor_type, sensor_counter,402					    i, j);403			if (err)404				goto exit_remove;405		}406	}407 408	err = device_create_file(data->bmc_device,409			&sensor_dev_attr_reset_high_low.dev_attr);410	if (err)411		goto exit_remove;412 413	err = device_create_file(data->bmc_device, &dev_attr_name.attr);414	if (err)415		goto exit_remove;416 417	return 0;418 419exit_remove:420	device_remove_file(data->bmc_device,421			   &sensor_dev_attr_reset_high_low.dev_attr);422	device_remove_file(data->bmc_device, &dev_attr_name.attr);423	for (i = 0; i < data->num_sensors; i++)424		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {425			if (!data->sensors[i].attr[j].dev_attr.attr.name)426				continue;427			device_remove_file(data->bmc_device,428				&data->sensors[i].attr[j].dev_attr);429			kfree(data->sensors[i].attr[j].dev_attr.attr.name);430		}431 432	kfree(data->sensors);433	return err;434}435 436static void ibmpex_register_bmc(int iface, struct device *dev)437{438	struct ibmpex_bmc_data *data;439	int err;440 441	data = kzalloc(sizeof(*data), GFP_KERNEL);442	if (!data)443		return;444 445	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;446	data->address.channel = IPMI_BMC_CHANNEL;447	data->address.data[0] = 0;448	data->interface = iface;449	data->bmc_device = dev;450 451	/* Create IPMI messaging interface user */452	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,453			       data, &data->user);454	if (err < 0) {455		dev_err(dev,456			"Unable to register user with IPMI interface %d\n",457			data->interface);458		goto out;459	}460 461	mutex_init(&data->lock);462 463	/* Initialize message */464	data->tx_msgid = 0;465	init_completion(&data->read_complete);466	data->tx_message.netfn = PEX_NET_FUNCTION;467	data->tx_message.cmd = PEX_COMMAND;468	data->tx_message.data = data->tx_msg_data;469 470	/* Does this BMC support PowerExecutive? */471	err = ibmpex_ver_check(data);472	if (err)473		goto out_user;474 475	/* Register the BMC as a HWMON class device */476	data->hwmon_dev = hwmon_device_register(data->bmc_device);477 478	if (IS_ERR(data->hwmon_dev)) {479		dev_err(data->bmc_device,480			"Unable to register hwmon device for IPMI interface %d\n",481			data->interface);482		goto out_user;483	}484 485	/* finally add the new bmc data to the bmc data list */486	dev_set_drvdata(dev, data);487	list_add_tail(&data->list, &driver_data.bmc_data);488 489	/* Now go find all the sensors */490	err = ibmpex_find_sensors(data);491	if (err) {492		dev_err(data->bmc_device, "Error %d finding sensors\n", err);493		goto out_register;494	}495 496	return;497 498out_register:499	list_del(&data->list);500	hwmon_device_unregister(data->hwmon_dev);501out_user:502	ipmi_destroy_user(data->user);503out:504	kfree(data);505}506 507static void ibmpex_bmc_delete(struct ibmpex_bmc_data *data)508{509	int i, j;510 511	device_remove_file(data->bmc_device,512			   &sensor_dev_attr_reset_high_low.dev_attr);513	device_remove_file(data->bmc_device, &dev_attr_name.attr);514	for (i = 0; i < data->num_sensors; i++)515		for (j = 0; j < PEX_NUM_SENSOR_FUNCS; j++) {516			if (!data->sensors[i].attr[j].dev_attr.attr.name)517				continue;518			device_remove_file(data->bmc_device,519				&data->sensors[i].attr[j].dev_attr);520			kfree(data->sensors[i].attr[j].dev_attr.attr.name);521		}522 523	list_del(&data->list);524	dev_set_drvdata(data->bmc_device, NULL);525	hwmon_device_unregister(data->hwmon_dev);526	ipmi_destroy_user(data->user);527	kfree(data->sensors);528	kfree(data);529}530 531static void ibmpex_bmc_gone(int iface)532{533	struct ibmpex_bmc_data *data = get_bmc_data(iface);534 535	if (!data)536		return;537 538	ibmpex_bmc_delete(data);539}540 541static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)542{543	struct ibmpex_bmc_data *data = user_msg_data;544 545	if (msg->msgid != data->tx_msgid) {546		dev_err(data->bmc_device,547			"Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",548			(int)msg->msgid,549			(int)data->tx_msgid);550		ipmi_free_recv_msg(msg);551		return;552	}553 554	data->rx_recv_type = msg->recv_type;555	if (msg->msg.data_len > 0)556		data->rx_result = msg->msg.data[0];557	else558		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;559 560	if (msg->msg.data_len > 1) {561		data->rx_msg_len = msg->msg.data_len - 1;562		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);563	} else564		data->rx_msg_len = 0;565 566	ipmi_free_recv_msg(msg);567	complete(&data->read_complete);568}569 570static int __init ibmpex_init(void)571{572	return ipmi_smi_watcher_register(&driver_data.bmc_events);573}574 575static void __exit ibmpex_exit(void)576{577	struct ibmpex_bmc_data *p, *next;578 579	ipmi_smi_watcher_unregister(&driver_data.bmc_events);580	list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)581		ibmpex_bmc_delete(p);582}583 584MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");585MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");586MODULE_LICENSE("GPL");587 588module_init(ibmpex_init);589module_exit(ibmpex_exit);590 591MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");592MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");593MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");594MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");595MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");596