xref: /openbmc/linux/drivers/hwmon/ibmaem.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28808a793SDarrick J. Wong /*
3eb93b7dfSDarrick J. Wong  * A hwmon driver for the IBM System Director Active Energy Manager (AEM)
4eb93b7dfSDarrick J. Wong  * temperature/power/energy sensors and capping functionality.
58808a793SDarrick J. Wong  * Copyright (C) 2008 IBM
68808a793SDarrick J. Wong  *
75407e051SDarrick J. Wong  * Author: Darrick J. Wong <darrick.wong@oracle.com>
88808a793SDarrick J. Wong  */
98808a793SDarrick J. Wong 
1055d705ccSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1155d705ccSJoe Perches 
128808a793SDarrick J. Wong #include <linux/ipmi.h>
138808a793SDarrick J. Wong #include <linux/module.h>
148808a793SDarrick J. Wong #include <linux/hwmon.h>
158808a793SDarrick J. Wong #include <linux/hwmon-sysfs.h>
168808a793SDarrick J. Wong #include <linux/jiffies.h>
178808a793SDarrick J. Wong #include <linux/mutex.h>
188808a793SDarrick J. Wong #include <linux/kdev_t.h>
198808a793SDarrick J. Wong #include <linux/spinlock.h>
208808a793SDarrick J. Wong #include <linux/idr.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
228808a793SDarrick J. Wong #include <linux/sched.h>
238808a793SDarrick J. Wong #include <linux/platform_device.h>
248808a793SDarrick J. Wong #include <linux/math64.h>
258808a793SDarrick J. Wong #include <linux/time.h>
26fa845740SJean Delvare #include <linux/err.h>
278808a793SDarrick J. Wong 
288808a793SDarrick J. Wong #define REFRESH_INTERVAL	(HZ)
298808a793SDarrick J. Wong #define IPMI_TIMEOUT		(30 * HZ)
308808a793SDarrick J. Wong #define DRVNAME			"aem"
318808a793SDarrick J. Wong 
328808a793SDarrick J. Wong #define AEM_NETFN		0x2E
338808a793SDarrick J. Wong 
348808a793SDarrick J. Wong #define AEM_FIND_FW_CMD		0x80
358808a793SDarrick J. Wong #define AEM_ELEMENT_CMD		0x81
368808a793SDarrick J. Wong #define AEM_FW_INSTANCE_CMD	0x82
378808a793SDarrick J. Wong 
388808a793SDarrick J. Wong #define AEM_READ_ELEMENT_CFG	0x80
398808a793SDarrick J. Wong #define AEM_READ_BUFFER		0x81
408808a793SDarrick J. Wong #define AEM_READ_REGISTER	0x82
418808a793SDarrick J. Wong #define AEM_WRITE_REGISTER	0x83
428808a793SDarrick J. Wong #define AEM_SET_REG_MASK	0x84
438808a793SDarrick J. Wong #define AEM_CLEAR_REG_MASK	0x85
448808a793SDarrick J. Wong #define AEM_READ_ELEMENT_CFG2	0x86
458808a793SDarrick J. Wong 
468808a793SDarrick J. Wong #define AEM_CONTROL_ELEMENT	0
478808a793SDarrick J. Wong #define AEM_ENERGY_ELEMENT	1
488808a793SDarrick J. Wong #define AEM_CLOCK_ELEMENT	4
498808a793SDarrick J. Wong #define AEM_POWER_CAP_ELEMENT	7
508808a793SDarrick J. Wong #define AEM_EXHAUST_ELEMENT	9
518808a793SDarrick J. Wong #define AEM_POWER_ELEMENT	10
528808a793SDarrick J. Wong 
538808a793SDarrick J. Wong #define AEM_MODULE_TYPE_ID	0x0001
548808a793SDarrick J. Wong 
558808a793SDarrick J. Wong #define AEM2_NUM_ENERGY_REGS	2
568808a793SDarrick J. Wong #define AEM2_NUM_PCAP_REGS	6
578808a793SDarrick J. Wong #define AEM2_NUM_TEMP_REGS	2
588808a793SDarrick J. Wong #define AEM2_NUM_SENSORS	14
598808a793SDarrick J. Wong 
608808a793SDarrick J. Wong #define AEM1_NUM_ENERGY_REGS	1
618808a793SDarrick J. Wong #define AEM1_NUM_SENSORS	3
628808a793SDarrick J. Wong 
638808a793SDarrick J. Wong /* AEM 2.x has more energy registers */
648808a793SDarrick J. Wong #define AEM_NUM_ENERGY_REGS	AEM2_NUM_ENERGY_REGS
658808a793SDarrick J. Wong /* AEM 2.x needs more sensor files */
668808a793SDarrick J. Wong #define AEM_NUM_SENSORS		AEM2_NUM_SENSORS
678808a793SDarrick J. Wong 
688808a793SDarrick J. Wong #define POWER_CAP		0
698808a793SDarrick J. Wong #define POWER_CAP_MAX_HOTPLUG	1
708808a793SDarrick J. Wong #define POWER_CAP_MAX		2
718808a793SDarrick J. Wong #define	POWER_CAP_MIN_WARNING	3
728808a793SDarrick J. Wong #define POWER_CAP_MIN		4
738808a793SDarrick J. Wong #define	POWER_AUX		5
748808a793SDarrick J. Wong 
758808a793SDarrick J. Wong #define AEM_DEFAULT_POWER_INTERVAL 1000
768808a793SDarrick J. Wong #define AEM_MIN_POWER_INTERVAL	200
778808a793SDarrick J. Wong #define UJ_PER_MJ		1000L
788808a793SDarrick J. Wong 
7965807044SJonathan Cameron static DEFINE_IDA(aem_ida);
808808a793SDarrick J. Wong 
81fe2d5ffcSDarrick J. Wong static struct platform_driver aem_driver = {
82fe2d5ffcSDarrick J. Wong 	.driver = {
838808a793SDarrick J. Wong 		.name = DRVNAME,
848808a793SDarrick J. Wong 		.bus = &platform_bus_type,
85fe2d5ffcSDarrick J. Wong 	}
868808a793SDarrick J. Wong };
878808a793SDarrick J. Wong 
888808a793SDarrick J. Wong struct aem_ipmi_data {
898808a793SDarrick J. Wong 	struct completion	read_complete;
908808a793SDarrick J. Wong 	struct ipmi_addr	address;
913f901c86SCorey Minyard 	struct ipmi_user	*user;
928808a793SDarrick J. Wong 	int			interface;
938808a793SDarrick J. Wong 
948808a793SDarrick J. Wong 	struct kernel_ipmi_msg	tx_message;
958808a793SDarrick J. Wong 	long			tx_msgid;
968808a793SDarrick J. Wong 
978808a793SDarrick J. Wong 	void			*rx_msg_data;
988808a793SDarrick J. Wong 	unsigned short		rx_msg_len;
998808a793SDarrick J. Wong 	unsigned char		rx_result;
1008808a793SDarrick J. Wong 	int			rx_recv_type;
1018808a793SDarrick J. Wong 
1028808a793SDarrick J. Wong 	struct device		*bmc_device;
1038808a793SDarrick J. Wong };
1048808a793SDarrick J. Wong 
1058808a793SDarrick J. Wong struct aem_ro_sensor_template {
1068808a793SDarrick J. Wong 	char *label;
1078808a793SDarrick J. Wong 	ssize_t (*show)(struct device *dev,
1088808a793SDarrick J. Wong 			struct device_attribute *devattr,
1098808a793SDarrick J. Wong 			char *buf);
1108808a793SDarrick J. Wong 	int index;
1118808a793SDarrick J. Wong };
1128808a793SDarrick J. Wong 
1138808a793SDarrick J. Wong struct aem_rw_sensor_template {
1148808a793SDarrick J. Wong 	char *label;
1158808a793SDarrick J. Wong 	ssize_t (*show)(struct device *dev,
1168808a793SDarrick J. Wong 			struct device_attribute *devattr,
1178808a793SDarrick J. Wong 			char *buf);
1188808a793SDarrick J. Wong 	ssize_t (*set)(struct device *dev,
1198808a793SDarrick J. Wong 		       struct device_attribute *devattr,
1208808a793SDarrick J. Wong 		       const char *buf, size_t count);
1218808a793SDarrick J. Wong 	int index;
1228808a793SDarrick J. Wong };
1238808a793SDarrick J. Wong 
1248808a793SDarrick J. Wong struct aem_data {
1258808a793SDarrick J. Wong 	struct list_head	list;
1268808a793SDarrick J. Wong 
1278808a793SDarrick J. Wong 	struct device		*hwmon_dev;
1288808a793SDarrick J. Wong 	struct platform_device	*pdev;
1298808a793SDarrick J. Wong 	struct mutex		lock;
130952a11caSPaul Fertser 	bool			valid;
1318808a793SDarrick J. Wong 	unsigned long		last_updated;	/* In jiffies */
1328808a793SDarrick J. Wong 	u8			ver_major;
1338808a793SDarrick J. Wong 	u8			ver_minor;
1348808a793SDarrick J. Wong 	u8			module_handle;
1358808a793SDarrick J. Wong 	int			id;
1368808a793SDarrick J. Wong 	struct aem_ipmi_data	ipmi;
1378808a793SDarrick J. Wong 
138da8ebe4eSJean Delvare 	/* Function and buffer to update sensors */
1398808a793SDarrick J. Wong 	void (*update)(struct aem_data *data);
140da8ebe4eSJean Delvare 	struct aem_read_sensor_resp *rs_resp;
1418808a793SDarrick J. Wong 
1428808a793SDarrick J. Wong 	/*
1438808a793SDarrick J. Wong 	 * AEM 1.x sensors:
1448808a793SDarrick J. Wong 	 * Available sensors:
1458808a793SDarrick J. Wong 	 * Energy meter
1468808a793SDarrick J. Wong 	 * Power meter
1478808a793SDarrick J. Wong 	 *
1488808a793SDarrick J. Wong 	 * AEM 2.x sensors:
1498808a793SDarrick J. Wong 	 * Two energy meters
1508808a793SDarrick J. Wong 	 * Two power meters
1518808a793SDarrick J. Wong 	 * Two temperature sensors
1528808a793SDarrick J. Wong 	 * Six power cap registers
1538808a793SDarrick J. Wong 	 */
1548808a793SDarrick J. Wong 
1558808a793SDarrick J. Wong 	/* sysfs attrs */
1568808a793SDarrick J. Wong 	struct sensor_device_attribute	sensors[AEM_NUM_SENSORS];
1578808a793SDarrick J. Wong 
1588808a793SDarrick J. Wong 	/* energy use in mJ */
1598808a793SDarrick J. Wong 	u64			energy[AEM_NUM_ENERGY_REGS];
1608808a793SDarrick J. Wong 
1618808a793SDarrick J. Wong 	/* power sampling interval in ms */
1628808a793SDarrick J. Wong 	unsigned long		power_period[AEM_NUM_ENERGY_REGS];
1638808a793SDarrick J. Wong 
1648808a793SDarrick J. Wong 	/* Everything past here is for AEM2 only */
1658808a793SDarrick J. Wong 
1668808a793SDarrick J. Wong 	/* power caps in dW */
1678808a793SDarrick J. Wong 	u16			pcap[AEM2_NUM_PCAP_REGS];
1688808a793SDarrick J. Wong 
1698808a793SDarrick J. Wong 	/* exhaust temperature in C */
1708808a793SDarrick J. Wong 	u8			temp[AEM2_NUM_TEMP_REGS];
1718808a793SDarrick J. Wong };
1728808a793SDarrick J. Wong 
1738808a793SDarrick J. Wong /* Data structures returned by the AEM firmware */
1748808a793SDarrick J. Wong struct aem_iana_id {
1758808a793SDarrick J. Wong 	u8			bytes[3];
1768808a793SDarrick J. Wong };
1778808a793SDarrick J. Wong static struct aem_iana_id system_x_id = {
1788808a793SDarrick J. Wong 	.bytes = {0x4D, 0x4F, 0x00}
1798808a793SDarrick J. Wong };
1808808a793SDarrick J. Wong 
1818808a793SDarrick J. Wong /* These are used to find AEM1 instances */
1828808a793SDarrick J. Wong struct aem_find_firmware_req {
1838808a793SDarrick J. Wong 	struct aem_iana_id	id;
1848808a793SDarrick J. Wong 	u8			rsvd;
185a064d5bdSAl Viro 	__be16			index;
186a064d5bdSAl Viro 	__be16			module_type_id;
1878808a793SDarrick J. Wong } __packed;
1888808a793SDarrick J. Wong 
1898808a793SDarrick J. Wong struct aem_find_firmware_resp {
1908808a793SDarrick J. Wong 	struct aem_iana_id	id;
1918808a793SDarrick J. Wong 	u8			num_instances;
1928808a793SDarrick J. Wong } __packed;
1938808a793SDarrick J. Wong 
1948808a793SDarrick J. Wong /* These are used to find AEM2 instances */
1958808a793SDarrick J. Wong struct aem_find_instance_req {
1968808a793SDarrick J. Wong 	struct aem_iana_id	id;
1978808a793SDarrick J. Wong 	u8			instance_number;
198a064d5bdSAl Viro 	__be16			module_type_id;
1998808a793SDarrick J. Wong } __packed;
2008808a793SDarrick J. Wong 
2018808a793SDarrick J. Wong struct aem_find_instance_resp {
2028808a793SDarrick J. Wong 	struct aem_iana_id	id;
2038808a793SDarrick J. Wong 	u8			num_instances;
2048808a793SDarrick J. Wong 	u8			major;
2058808a793SDarrick J. Wong 	u8			minor;
2068808a793SDarrick J. Wong 	u8			module_handle;
2078808a793SDarrick J. Wong 	u16			record_id;
2088808a793SDarrick J. Wong } __packed;
2098808a793SDarrick J. Wong 
2108808a793SDarrick J. Wong /* These are used to query sensors */
2118808a793SDarrick J. Wong struct aem_read_sensor_req {
2128808a793SDarrick J. Wong 	struct aem_iana_id	id;
2138808a793SDarrick J. Wong 	u8			module_handle;
2148808a793SDarrick J. Wong 	u8			element;
2158808a793SDarrick J. Wong 	u8			subcommand;
2168808a793SDarrick J. Wong 	u8			reg;
2178808a793SDarrick J. Wong 	u8			rx_buf_size;
2188808a793SDarrick J. Wong } __packed;
2198808a793SDarrick J. Wong 
2208808a793SDarrick J. Wong struct aem_read_sensor_resp {
2218808a793SDarrick J. Wong 	struct aem_iana_id	id;
222afde6416SGustavo A. R. Silva 	u8			bytes[];
2238808a793SDarrick J. Wong } __packed;
2248808a793SDarrick J. Wong 
2258808a793SDarrick J. Wong /* Data structures to talk to the IPMI layer */
2268808a793SDarrick J. Wong struct aem_driver_data {
2278808a793SDarrick J. Wong 	struct list_head	aem_devices;
2288808a793SDarrick J. Wong 	struct ipmi_smi_watcher	bmc_events;
2298808a793SDarrick J. Wong 	struct ipmi_user_hndl	ipmi_hndlrs;
2308808a793SDarrick J. Wong };
2318808a793SDarrick J. Wong 
2328808a793SDarrick J. Wong static void aem_register_bmc(int iface, struct device *dev);
2338808a793SDarrick J. Wong static void aem_bmc_gone(int iface);
2348808a793SDarrick J. Wong static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
2358808a793SDarrick J. Wong 
2368808a793SDarrick J. Wong static void aem_remove_sensors(struct aem_data *data);
2378808a793SDarrick J. Wong static int aem1_find_sensors(struct aem_data *data);
2388808a793SDarrick J. Wong static int aem2_find_sensors(struct aem_data *data);
2398808a793SDarrick J. Wong static void update_aem1_sensors(struct aem_data *data);
2408808a793SDarrick J. Wong static void update_aem2_sensors(struct aem_data *data);
2418808a793SDarrick J. Wong 
2428808a793SDarrick J. Wong static struct aem_driver_data driver_data = {
2438808a793SDarrick J. Wong 	.aem_devices = LIST_HEAD_INIT(driver_data.aem_devices),
2448808a793SDarrick J. Wong 	.bmc_events = {
2458808a793SDarrick J. Wong 		.owner = THIS_MODULE,
2468808a793SDarrick J. Wong 		.new_smi = aem_register_bmc,
2478808a793SDarrick J. Wong 		.smi_gone = aem_bmc_gone,
2488808a793SDarrick J. Wong 	},
2498808a793SDarrick J. Wong 	.ipmi_hndlrs = {
2508808a793SDarrick J. Wong 		.ipmi_recv_hndl = aem_msg_handler,
2518808a793SDarrick J. Wong 	},
2528808a793SDarrick J. Wong };
2538808a793SDarrick J. Wong 
2548808a793SDarrick J. Wong /* Functions to talk to the IPMI layer */
2558808a793SDarrick J. Wong 
2568808a793SDarrick J. Wong /* Initialize IPMI address, message buffers and user data */
aem_init_ipmi_data(struct aem_ipmi_data * data,int iface,struct device * bmc)2578808a793SDarrick J. Wong static int aem_init_ipmi_data(struct aem_ipmi_data *data, int iface,
2588808a793SDarrick J. Wong 			      struct device *bmc)
2598808a793SDarrick J. Wong {
2608808a793SDarrick J. Wong 	int err;
2618808a793SDarrick J. Wong 
2628808a793SDarrick J. Wong 	init_completion(&data->read_complete);
2638808a793SDarrick J. Wong 	data->bmc_device = bmc;
2648808a793SDarrick J. Wong 
2658808a793SDarrick J. Wong 	/* Initialize IPMI address */
2668808a793SDarrick J. Wong 	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
2678808a793SDarrick J. Wong 	data->address.channel = IPMI_BMC_CHANNEL;
2688808a793SDarrick J. Wong 	data->address.data[0] = 0;
2698808a793SDarrick J. Wong 	data->interface = iface;
2708808a793SDarrick J. Wong 
2718808a793SDarrick J. Wong 	/* Initialize message buffers */
2728808a793SDarrick J. Wong 	data->tx_msgid = 0;
2738808a793SDarrick J. Wong 	data->tx_message.netfn = AEM_NETFN;
2748808a793SDarrick J. Wong 
2758808a793SDarrick J. Wong 	/* Create IPMI messaging interface user */
2768808a793SDarrick J. Wong 	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
2778808a793SDarrick J. Wong 			       data, &data->user);
2788808a793SDarrick J. Wong 	if (err < 0) {
279b55f3757SGuenter Roeck 		dev_err(bmc,
280b55f3757SGuenter Roeck 			"Unable to register user with IPMI interface %d\n",
281b55f3757SGuenter Roeck 			data->interface);
28212ca0b56SSachin Kamat 		return err;
2838808a793SDarrick J. Wong 	}
2848808a793SDarrick J. Wong 
2858808a793SDarrick J. Wong 	return 0;
2868808a793SDarrick J. Wong }
2878808a793SDarrick J. Wong 
2888808a793SDarrick J. Wong /* Send an IPMI command */
aem_send_message(struct aem_ipmi_data * data)2898808a793SDarrick J. Wong static int aem_send_message(struct aem_ipmi_data *data)
2908808a793SDarrick J. Wong {
2918808a793SDarrick J. Wong 	int err;
2928808a793SDarrick J. Wong 
2938808a793SDarrick J. Wong 	err = ipmi_validate_addr(&data->address, sizeof(data->address));
2948808a793SDarrick J. Wong 	if (err)
2958808a793SDarrick J. Wong 		goto out;
2968808a793SDarrick J. Wong 
2978808a793SDarrick J. Wong 	data->tx_msgid++;
2988808a793SDarrick J. Wong 	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
2998808a793SDarrick J. Wong 				   &data->tx_message, data, 0, 0, 0);
3008808a793SDarrick J. Wong 	if (err)
3018808a793SDarrick J. Wong 		goto out1;
3028808a793SDarrick J. Wong 
3038808a793SDarrick J. Wong 	return 0;
3048808a793SDarrick J. Wong out1:
3058808a793SDarrick J. Wong 	dev_err(data->bmc_device, "request_settime=%x\n", err);
3068808a793SDarrick J. Wong 	return err;
3078808a793SDarrick J. Wong out:
3088808a793SDarrick J. Wong 	dev_err(data->bmc_device, "validate_addr=%x\n", err);
3098808a793SDarrick J. Wong 	return err;
3108808a793SDarrick J. Wong }
3118808a793SDarrick J. Wong 
3128808a793SDarrick J. Wong /* Dispatch IPMI messages to callers */
aem_msg_handler(struct ipmi_recv_msg * msg,void * user_msg_data)3138808a793SDarrick J. Wong static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
3148808a793SDarrick J. Wong {
3158808a793SDarrick J. Wong 	unsigned short rx_len;
3168808a793SDarrick J. Wong 	struct aem_ipmi_data *data = user_msg_data;
3178808a793SDarrick J. Wong 
3188808a793SDarrick J. Wong 	if (msg->msgid != data->tx_msgid) {
319b55f3757SGuenter Roeck 		dev_err(data->bmc_device,
320b55f3757SGuenter Roeck 			"Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
3218808a793SDarrick J. Wong 			(int)msg->msgid,
3228808a793SDarrick J. Wong 			(int)data->tx_msgid);
3238808a793SDarrick J. Wong 		ipmi_free_recv_msg(msg);
3248808a793SDarrick J. Wong 		return;
3258808a793SDarrick J. Wong 	}
3268808a793SDarrick J. Wong 
3278808a793SDarrick J. Wong 	data->rx_recv_type = msg->recv_type;
3288808a793SDarrick J. Wong 	if (msg->msg.data_len > 0)
3298808a793SDarrick J. Wong 		data->rx_result = msg->msg.data[0];
3308808a793SDarrick J. Wong 	else
3318808a793SDarrick J. Wong 		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
3328808a793SDarrick J. Wong 
3338808a793SDarrick J. Wong 	if (msg->msg.data_len > 1) {
3348808a793SDarrick J. Wong 		rx_len = msg->msg.data_len - 1;
3358808a793SDarrick J. Wong 		if (data->rx_msg_len < rx_len)
3368808a793SDarrick J. Wong 			rx_len = data->rx_msg_len;
3378808a793SDarrick J. Wong 		data->rx_msg_len = rx_len;
3388808a793SDarrick J. Wong 		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
3398808a793SDarrick J. Wong 	} else
3408808a793SDarrick J. Wong 		data->rx_msg_len = 0;
3418808a793SDarrick J. Wong 
3428808a793SDarrick J. Wong 	ipmi_free_recv_msg(msg);
3438808a793SDarrick J. Wong 	complete(&data->read_complete);
3448808a793SDarrick J. Wong }
3458808a793SDarrick J. Wong 
3468808a793SDarrick J. Wong /* Sensor support functions */
3478808a793SDarrick J. Wong 
348da8ebe4eSJean Delvare /* Read a sensor value; must be called with data->lock held */
aem_read_sensor(struct aem_data * data,u8 elt,u8 reg,void * buf,size_t size)3498808a793SDarrick J. Wong static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
3508808a793SDarrick J. Wong 			   void *buf, size_t size)
3518808a793SDarrick J. Wong {
3528808a793SDarrick J. Wong 	int rs_size, res;
3538808a793SDarrick J. Wong 	struct aem_read_sensor_req rs_req;
354da8ebe4eSJean Delvare 	/* Use preallocated rx buffer */
355da8ebe4eSJean Delvare 	struct aem_read_sensor_resp *rs_resp = data->rs_resp;
3568808a793SDarrick J. Wong 	struct aem_ipmi_data *ipmi = &data->ipmi;
3578808a793SDarrick J. Wong 
3588808a793SDarrick J. Wong 	/* AEM registers are 1, 2, 4 or 8 bytes */
3598808a793SDarrick J. Wong 	switch (size) {
3608808a793SDarrick J. Wong 	case 1:
3618808a793SDarrick J. Wong 	case 2:
3628808a793SDarrick J. Wong 	case 4:
3638808a793SDarrick J. Wong 	case 8:
3648808a793SDarrick J. Wong 		break;
3658808a793SDarrick J. Wong 	default:
3668808a793SDarrick J. Wong 		return -EINVAL;
3678808a793SDarrick J. Wong 	}
3688808a793SDarrick J. Wong 
3698808a793SDarrick J. Wong 	rs_req.id = system_x_id;
3708808a793SDarrick J. Wong 	rs_req.module_handle = data->module_handle;
3718808a793SDarrick J. Wong 	rs_req.element = elt;
3728808a793SDarrick J. Wong 	rs_req.subcommand = AEM_READ_REGISTER;
3738808a793SDarrick J. Wong 	rs_req.reg = reg;
3748808a793SDarrick J. Wong 	rs_req.rx_buf_size = size;
3758808a793SDarrick J. Wong 
3768808a793SDarrick J. Wong 	ipmi->tx_message.cmd = AEM_ELEMENT_CMD;
3778808a793SDarrick J. Wong 	ipmi->tx_message.data = (char *)&rs_req;
3788808a793SDarrick J. Wong 	ipmi->tx_message.data_len = sizeof(rs_req);
3798808a793SDarrick J. Wong 
3808808a793SDarrick J. Wong 	rs_size = sizeof(*rs_resp) + size;
3818808a793SDarrick J. Wong 	ipmi->rx_msg_data = rs_resp;
3828808a793SDarrick J. Wong 	ipmi->rx_msg_len = rs_size;
3838808a793SDarrick J. Wong 
3848808a793SDarrick J. Wong 	aem_send_message(ipmi);
3858808a793SDarrick J. Wong 
3868808a793SDarrick J. Wong 	res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT);
38766a89b21SJulia Lawall 	if (!res) {
38866a89b21SJulia Lawall 		res = -ETIMEDOUT;
38966a89b21SJulia Lawall 		goto out;
39066a89b21SJulia Lawall 	}
3918808a793SDarrick J. Wong 
3928808a793SDarrick J. Wong 	if (ipmi->rx_result || ipmi->rx_msg_len != rs_size ||
3938808a793SDarrick J. Wong 	    memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) {
39466a89b21SJulia Lawall 		res = -ENOENT;
39566a89b21SJulia Lawall 		goto out;
3968808a793SDarrick J. Wong 	}
3978808a793SDarrick J. Wong 
3988808a793SDarrick J. Wong 	switch (size) {
3998808a793SDarrick J. Wong 	case 1: {
4008808a793SDarrick J. Wong 		u8 *x = buf;
4018808a793SDarrick J. Wong 		*x = rs_resp->bytes[0];
4028808a793SDarrick J. Wong 		break;
4038808a793SDarrick J. Wong 	}
4048808a793SDarrick J. Wong 	case 2: {
4058808a793SDarrick J. Wong 		u16 *x = buf;
406a064d5bdSAl Viro 		*x = be16_to_cpup((__be16 *)rs_resp->bytes);
4078808a793SDarrick J. Wong 		break;
4088808a793SDarrick J. Wong 	}
4098808a793SDarrick J. Wong 	case 4: {
4108808a793SDarrick J. Wong 		u32 *x = buf;
411a064d5bdSAl Viro 		*x = be32_to_cpup((__be32 *)rs_resp->bytes);
4128808a793SDarrick J. Wong 		break;
4138808a793SDarrick J. Wong 	}
4148808a793SDarrick J. Wong 	case 8: {
4158808a793SDarrick J. Wong 		u64 *x = buf;
416a064d5bdSAl Viro 		*x = be64_to_cpup((__be64 *)rs_resp->bytes);
4178808a793SDarrick J. Wong 		break;
4188808a793SDarrick J. Wong 	}
4198808a793SDarrick J. Wong 	}
42066a89b21SJulia Lawall 	res = 0;
4218808a793SDarrick J. Wong 
42266a89b21SJulia Lawall out:
42366a89b21SJulia Lawall 	return res;
4248808a793SDarrick J. Wong }
4258808a793SDarrick J. Wong 
4268808a793SDarrick J. Wong /* Update AEM energy registers */
update_aem_energy_one(struct aem_data * data,int which)427bb15e7f2SDarrick J. Wong static void update_aem_energy_one(struct aem_data *data, int which)
428bb15e7f2SDarrick J. Wong {
429bb15e7f2SDarrick J. Wong 	aem_read_sensor(data, AEM_ENERGY_ELEMENT, which,
430bb15e7f2SDarrick J. Wong 			&data->energy[which], 8);
431bb15e7f2SDarrick J. Wong }
432bb15e7f2SDarrick J. Wong 
update_aem_energy(struct aem_data * data)4338808a793SDarrick J. Wong static void update_aem_energy(struct aem_data *data)
4348808a793SDarrick J. Wong {
435bb15e7f2SDarrick J. Wong 	update_aem_energy_one(data, 0);
4368808a793SDarrick J. Wong 	if (data->ver_major < 2)
4378808a793SDarrick J. Wong 		return;
438bb15e7f2SDarrick J. Wong 	update_aem_energy_one(data, 1);
4398808a793SDarrick J. Wong }
4408808a793SDarrick J. Wong 
4418808a793SDarrick J. Wong /* Update all AEM1 sensors */
update_aem1_sensors(struct aem_data * data)4428808a793SDarrick J. Wong static void update_aem1_sensors(struct aem_data *data)
4438808a793SDarrick J. Wong {
4448808a793SDarrick J. Wong 	mutex_lock(&data->lock);
4458808a793SDarrick J. Wong 	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
4468808a793SDarrick J. Wong 	    data->valid)
4478808a793SDarrick J. Wong 		goto out;
4488808a793SDarrick J. Wong 
4498808a793SDarrick J. Wong 	update_aem_energy(data);
4508808a793SDarrick J. Wong out:
4518808a793SDarrick J. Wong 	mutex_unlock(&data->lock);
4528808a793SDarrick J. Wong }
4538808a793SDarrick J. Wong 
4548808a793SDarrick J. Wong /* Update all AEM2 sensors */
update_aem2_sensors(struct aem_data * data)4558808a793SDarrick J. Wong static void update_aem2_sensors(struct aem_data *data)
4568808a793SDarrick J. Wong {
4578808a793SDarrick J. Wong 	int i;
4588808a793SDarrick J. Wong 
4598808a793SDarrick J. Wong 	mutex_lock(&data->lock);
4608808a793SDarrick J. Wong 	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
4618808a793SDarrick J. Wong 	    data->valid)
4628808a793SDarrick J. Wong 		goto out;
4638808a793SDarrick J. Wong 
4648808a793SDarrick J. Wong 	update_aem_energy(data);
4658808a793SDarrick J. Wong 	aem_read_sensor(data, AEM_EXHAUST_ELEMENT, 0, &data->temp[0], 1);
4668808a793SDarrick J. Wong 	aem_read_sensor(data, AEM_EXHAUST_ELEMENT, 1, &data->temp[1], 1);
4678808a793SDarrick J. Wong 
4688808a793SDarrick J. Wong 	for (i = POWER_CAP; i <= POWER_AUX; i++)
4698808a793SDarrick J. Wong 		aem_read_sensor(data, AEM_POWER_CAP_ELEMENT, i,
4708808a793SDarrick J. Wong 				&data->pcap[i], 2);
4718808a793SDarrick J. Wong out:
4728808a793SDarrick J. Wong 	mutex_unlock(&data->lock);
4738808a793SDarrick J. Wong }
4748808a793SDarrick J. Wong 
4758808a793SDarrick J. Wong /* Delete an AEM instance */
aem_delete(struct aem_data * data)4768808a793SDarrick J. Wong static void aem_delete(struct aem_data *data)
4778808a793SDarrick J. Wong {
4788808a793SDarrick J. Wong 	list_del(&data->list);
4798808a793SDarrick J. Wong 	aem_remove_sensors(data);
480da8ebe4eSJean Delvare 	kfree(data->rs_resp);
4818808a793SDarrick J. Wong 	hwmon_device_unregister(data->hwmon_dev);
4828808a793SDarrick J. Wong 	ipmi_destroy_user(data->ipmi.user);
48395de3b25SJean Delvare 	platform_set_drvdata(data->pdev, NULL);
4848808a793SDarrick J. Wong 	platform_device_unregister(data->pdev);
485be1ca367Skeliu 	ida_free(&aem_ida, data->id);
4868808a793SDarrick J. Wong 	kfree(data);
4878808a793SDarrick J. Wong }
4888808a793SDarrick J. Wong 
4898808a793SDarrick J. Wong /* Probe functions for AEM1 devices */
4908808a793SDarrick J. Wong 
4918808a793SDarrick J. Wong /* Retrieve version and module handle for an AEM1 instance */
aem_find_aem1_count(struct aem_ipmi_data * data)4928808a793SDarrick J. Wong static int aem_find_aem1_count(struct aem_ipmi_data *data)
4938808a793SDarrick J. Wong {
4948808a793SDarrick J. Wong 	int res;
4958808a793SDarrick J. Wong 	struct aem_find_firmware_req	ff_req;
4968808a793SDarrick J. Wong 	struct aem_find_firmware_resp	ff_resp;
4978808a793SDarrick J. Wong 
4988808a793SDarrick J. Wong 	ff_req.id = system_x_id;
4998808a793SDarrick J. Wong 	ff_req.index = 0;
5008808a793SDarrick J. Wong 	ff_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
5018808a793SDarrick J. Wong 
5028808a793SDarrick J. Wong 	data->tx_message.cmd = AEM_FIND_FW_CMD;
5038808a793SDarrick J. Wong 	data->tx_message.data = (char *)&ff_req;
5048808a793SDarrick J. Wong 	data->tx_message.data_len = sizeof(ff_req);
5058808a793SDarrick J. Wong 
5068808a793SDarrick J. Wong 	data->rx_msg_data = &ff_resp;
5078808a793SDarrick J. Wong 	data->rx_msg_len = sizeof(ff_resp);
5088808a793SDarrick J. Wong 
5098808a793SDarrick J. Wong 	aem_send_message(data);
5108808a793SDarrick J. Wong 
5118808a793SDarrick J. Wong 	res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT);
5128808a793SDarrick J. Wong 	if (!res)
5138808a793SDarrick J. Wong 		return -ETIMEDOUT;
5148808a793SDarrick J. Wong 
5158808a793SDarrick J. Wong 	if (data->rx_result || data->rx_msg_len != sizeof(ff_resp) ||
5168808a793SDarrick J. Wong 	    memcmp(&ff_resp.id, &system_x_id, sizeof(system_x_id)))
5178808a793SDarrick J. Wong 		return -ENOENT;
5188808a793SDarrick J. Wong 
5198808a793SDarrick J. Wong 	return ff_resp.num_instances;
5208808a793SDarrick J. Wong }
5218808a793SDarrick J. Wong 
5228808a793SDarrick J. Wong /* Find and initialize one AEM1 instance */
aem_init_aem1_inst(struct aem_ipmi_data * probe,u8 module_handle)5238808a793SDarrick J. Wong static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
5248808a793SDarrick J. Wong {
5258808a793SDarrick J. Wong 	struct aem_data *data;
5268808a793SDarrick J. Wong 	int i;
5278808a793SDarrick J. Wong 	int res = -ENOMEM;
5288808a793SDarrick J. Wong 
5298808a793SDarrick J. Wong 	data = kzalloc(sizeof(*data), GFP_KERNEL);
5308808a793SDarrick J. Wong 	if (!data)
5318808a793SDarrick J. Wong 		return res;
5328808a793SDarrick J. Wong 	mutex_init(&data->lock);
5338808a793SDarrick J. Wong 
5348808a793SDarrick J. Wong 	/* Copy instance data */
5358808a793SDarrick J. Wong 	data->ver_major = 1;
5368808a793SDarrick J. Wong 	data->ver_minor = 0;
5378808a793SDarrick J. Wong 	data->module_handle = module_handle;
5388808a793SDarrick J. Wong 	for (i = 0; i < AEM1_NUM_ENERGY_REGS; i++)
5398808a793SDarrick J. Wong 		data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
5408808a793SDarrick J. Wong 
5418808a793SDarrick J. Wong 	/* Create sub-device for this fw instance */
542be1ca367Skeliu 	data->id = ida_alloc(&aem_ida, GFP_KERNEL);
54365807044SJonathan Cameron 	if (data->id < 0)
5448808a793SDarrick J. Wong 		goto id_err;
5458808a793SDarrick J. Wong 
5468808a793SDarrick J. Wong 	data->pdev = platform_device_alloc(DRVNAME, data->id);
5478808a793SDarrick J. Wong 	if (!data->pdev)
5488808a793SDarrick J. Wong 		goto dev_err;
549fe2d5ffcSDarrick J. Wong 	data->pdev->dev.driver = &aem_driver.driver;
5508808a793SDarrick J. Wong 
5518808a793SDarrick J. Wong 	res = platform_device_add(data->pdev);
5528808a793SDarrick J. Wong 	if (res)
553*d0e51022SYang Yingliang 		goto dev_add_err;
5548808a793SDarrick J. Wong 
55595de3b25SJean Delvare 	platform_set_drvdata(data->pdev, data);
5568808a793SDarrick J. Wong 
5578808a793SDarrick J. Wong 	/* Set up IPMI interface */
558547a1c99SJean Delvare 	res = aem_init_ipmi_data(&data->ipmi, probe->interface,
559547a1c99SJean Delvare 				 probe->bmc_device);
560547a1c99SJean Delvare 	if (res)
5618808a793SDarrick J. Wong 		goto ipmi_err;
5628808a793SDarrick J. Wong 
5638808a793SDarrick J. Wong 	/* Register with hwmon */
5648808a793SDarrick J. Wong 	data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
5658808a793SDarrick J. Wong 	if (IS_ERR(data->hwmon_dev)) {
566b55f3757SGuenter Roeck 		dev_err(&data->pdev->dev,
567b55f3757SGuenter Roeck 			"Unable to register hwmon device for IPMI interface %d\n",
5688808a793SDarrick J. Wong 			probe->interface);
569547a1c99SJean Delvare 		res = PTR_ERR(data->hwmon_dev);
5708808a793SDarrick J. Wong 		goto hwmon_reg_err;
5718808a793SDarrick J. Wong 	}
5728808a793SDarrick J. Wong 
5738808a793SDarrick J. Wong 	data->update = update_aem1_sensors;
574da8ebe4eSJean Delvare 	data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
575da8ebe4eSJean Delvare 	if (!data->rs_resp) {
576da8ebe4eSJean Delvare 		res = -ENOMEM;
577da8ebe4eSJean Delvare 		goto alloc_resp_err;
578da8ebe4eSJean Delvare 	}
5798808a793SDarrick J. Wong 
5808808a793SDarrick J. Wong 	/* Find sensors */
581547a1c99SJean Delvare 	res = aem1_find_sensors(data);
582547a1c99SJean Delvare 	if (res)
5838808a793SDarrick J. Wong 		goto sensor_err;
5848808a793SDarrick J. Wong 
5858808a793SDarrick J. Wong 	/* Add to our list of AEM devices */
5868808a793SDarrick J. Wong 	list_add_tail(&data->list, &driver_data.aem_devices);
5878808a793SDarrick J. Wong 
5888808a793SDarrick J. Wong 	dev_info(data->ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
5898808a793SDarrick J. Wong 		 data->ver_major, data->ver_minor,
5908808a793SDarrick J. Wong 		 data->module_handle);
5918808a793SDarrick J. Wong 	return 0;
5928808a793SDarrick J. Wong 
5938808a793SDarrick J. Wong sensor_err:
594da8ebe4eSJean Delvare 	kfree(data->rs_resp);
595da8ebe4eSJean Delvare alloc_resp_err:
5968808a793SDarrick J. Wong 	hwmon_device_unregister(data->hwmon_dev);
5978808a793SDarrick J. Wong hwmon_reg_err:
5988808a793SDarrick J. Wong 	ipmi_destroy_user(data->ipmi.user);
5998808a793SDarrick J. Wong ipmi_err:
60095de3b25SJean Delvare 	platform_set_drvdata(data->pdev, NULL);
601*d0e51022SYang Yingliang 	platform_device_del(data->pdev);
602*d0e51022SYang Yingliang dev_add_err:
603*d0e51022SYang Yingliang 	platform_device_put(data->pdev);
6048808a793SDarrick J. Wong dev_err:
605be1ca367Skeliu 	ida_free(&aem_ida, data->id);
6068808a793SDarrick J. Wong id_err:
6078808a793SDarrick J. Wong 	kfree(data);
6088808a793SDarrick J. Wong 
6098808a793SDarrick J. Wong 	return res;
6108808a793SDarrick J. Wong }
6118808a793SDarrick J. Wong 
6128808a793SDarrick J. Wong /* Find and initialize all AEM1 instances */
aem_init_aem1(struct aem_ipmi_data * probe)6139d84c9e8SJean Delvare static void aem_init_aem1(struct aem_ipmi_data *probe)
6148808a793SDarrick J. Wong {
6158808a793SDarrick J. Wong 	int num, i, err;
6168808a793SDarrick J. Wong 
6178808a793SDarrick J. Wong 	num = aem_find_aem1_count(probe);
6188808a793SDarrick J. Wong 	for (i = 0; i < num; i++) {
6198808a793SDarrick J. Wong 		err = aem_init_aem1_inst(probe, i);
6208808a793SDarrick J. Wong 		if (err) {
6218808a793SDarrick J. Wong 			dev_err(probe->bmc_device,
6228808a793SDarrick J. Wong 				"Error %d initializing AEM1 0x%X\n",
6238808a793SDarrick J. Wong 				err, i);
6248808a793SDarrick J. Wong 		}
6258808a793SDarrick J. Wong 	}
6268808a793SDarrick J. Wong }
6278808a793SDarrick J. Wong 
6288808a793SDarrick J. Wong /* Probe functions for AEM2 devices */
6298808a793SDarrick J. Wong 
6308808a793SDarrick J. Wong /* Retrieve version and module handle for an AEM2 instance */
aem_find_aem2(struct aem_ipmi_data * data,struct aem_find_instance_resp * fi_resp,int instance_num)6318808a793SDarrick J. Wong static int aem_find_aem2(struct aem_ipmi_data *data,
6328808a793SDarrick J. Wong 			    struct aem_find_instance_resp *fi_resp,
6338808a793SDarrick J. Wong 			    int instance_num)
6348808a793SDarrick J. Wong {
6358808a793SDarrick J. Wong 	int res;
6368808a793SDarrick J. Wong 	struct aem_find_instance_req fi_req;
6378808a793SDarrick J. Wong 
6388808a793SDarrick J. Wong 	fi_req.id = system_x_id;
6398808a793SDarrick J. Wong 	fi_req.instance_number = instance_num;
6408808a793SDarrick J. Wong 	fi_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
6418808a793SDarrick J. Wong 
6428808a793SDarrick J. Wong 	data->tx_message.cmd = AEM_FW_INSTANCE_CMD;
6438808a793SDarrick J. Wong 	data->tx_message.data = (char *)&fi_req;
6448808a793SDarrick J. Wong 	data->tx_message.data_len = sizeof(fi_req);
6458808a793SDarrick J. Wong 
6468808a793SDarrick J. Wong 	data->rx_msg_data = fi_resp;
6478808a793SDarrick J. Wong 	data->rx_msg_len = sizeof(*fi_resp);
6488808a793SDarrick J. Wong 
6498808a793SDarrick J. Wong 	aem_send_message(data);
6508808a793SDarrick J. Wong 
6518808a793SDarrick J. Wong 	res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT);
6528808a793SDarrick J. Wong 	if (!res)
6538808a793SDarrick J. Wong 		return -ETIMEDOUT;
6548808a793SDarrick J. Wong 
6558808a793SDarrick J. Wong 	if (data->rx_result || data->rx_msg_len != sizeof(*fi_resp) ||
6569c5413eaSDarrick J. Wong 	    memcmp(&fi_resp->id, &system_x_id, sizeof(system_x_id)) ||
6579c5413eaSDarrick J. Wong 	    fi_resp->num_instances <= instance_num)
6588808a793SDarrick J. Wong 		return -ENOENT;
6598808a793SDarrick J. Wong 
6608808a793SDarrick J. Wong 	return 0;
6618808a793SDarrick J. Wong }
6628808a793SDarrick J. Wong 
6638808a793SDarrick J. Wong /* Find and initialize one AEM2 instance */
aem_init_aem2_inst(struct aem_ipmi_data * probe,struct aem_find_instance_resp * fi_resp)6648808a793SDarrick J. Wong static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
6658808a793SDarrick J. Wong 			      struct aem_find_instance_resp *fi_resp)
6668808a793SDarrick J. Wong {
6678808a793SDarrick J. Wong 	struct aem_data *data;
6688808a793SDarrick J. Wong 	int i;
6698808a793SDarrick J. Wong 	int res = -ENOMEM;
6708808a793SDarrick J. Wong 
6718808a793SDarrick J. Wong 	data = kzalloc(sizeof(*data), GFP_KERNEL);
6728808a793SDarrick J. Wong 	if (!data)
6738808a793SDarrick J. Wong 		return res;
6748808a793SDarrick J. Wong 	mutex_init(&data->lock);
6758808a793SDarrick J. Wong 
6768808a793SDarrick J. Wong 	/* Copy instance data */
6778808a793SDarrick J. Wong 	data->ver_major = fi_resp->major;
6788808a793SDarrick J. Wong 	data->ver_minor = fi_resp->minor;
6798808a793SDarrick J. Wong 	data->module_handle = fi_resp->module_handle;
6808808a793SDarrick J. Wong 	for (i = 0; i < AEM2_NUM_ENERGY_REGS; i++)
6818808a793SDarrick J. Wong 		data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
6828808a793SDarrick J. Wong 
6838808a793SDarrick J. Wong 	/* Create sub-device for this fw instance */
684be1ca367Skeliu 	data->id = ida_alloc(&aem_ida, GFP_KERNEL);
68565807044SJonathan Cameron 	if (data->id < 0)
6868808a793SDarrick J. Wong 		goto id_err;
6878808a793SDarrick J. Wong 
6888808a793SDarrick J. Wong 	data->pdev = platform_device_alloc(DRVNAME, data->id);
6898808a793SDarrick J. Wong 	if (!data->pdev)
6908808a793SDarrick J. Wong 		goto dev_err;
691fe2d5ffcSDarrick J. Wong 	data->pdev->dev.driver = &aem_driver.driver;
6928808a793SDarrick J. Wong 
6938808a793SDarrick J. Wong 	res = platform_device_add(data->pdev);
6948808a793SDarrick J. Wong 	if (res)
695*d0e51022SYang Yingliang 		goto dev_add_err;
6968808a793SDarrick J. Wong 
69795de3b25SJean Delvare 	platform_set_drvdata(data->pdev, data);
6988808a793SDarrick J. Wong 
6998808a793SDarrick J. Wong 	/* Set up IPMI interface */
700547a1c99SJean Delvare 	res = aem_init_ipmi_data(&data->ipmi, probe->interface,
701547a1c99SJean Delvare 				 probe->bmc_device);
702547a1c99SJean Delvare 	if (res)
7038808a793SDarrick J. Wong 		goto ipmi_err;
7048808a793SDarrick J. Wong 
7058808a793SDarrick J. Wong 	/* Register with hwmon */
7068808a793SDarrick J. Wong 	data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
7078808a793SDarrick J. Wong 	if (IS_ERR(data->hwmon_dev)) {
708b55f3757SGuenter Roeck 		dev_err(&data->pdev->dev,
709b55f3757SGuenter Roeck 			"Unable to register hwmon device for IPMI interface %d\n",
7108808a793SDarrick J. Wong 			probe->interface);
711547a1c99SJean Delvare 		res = PTR_ERR(data->hwmon_dev);
7128808a793SDarrick J. Wong 		goto hwmon_reg_err;
7138808a793SDarrick J. Wong 	}
7148808a793SDarrick J. Wong 
7158808a793SDarrick J. Wong 	data->update = update_aem2_sensors;
716da8ebe4eSJean Delvare 	data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
717da8ebe4eSJean Delvare 	if (!data->rs_resp) {
718da8ebe4eSJean Delvare 		res = -ENOMEM;
719da8ebe4eSJean Delvare 		goto alloc_resp_err;
720da8ebe4eSJean Delvare 	}
7218808a793SDarrick J. Wong 
7228808a793SDarrick J. Wong 	/* Find sensors */
723547a1c99SJean Delvare 	res = aem2_find_sensors(data);
724547a1c99SJean Delvare 	if (res)
7258808a793SDarrick J. Wong 		goto sensor_err;
7268808a793SDarrick J. Wong 
7278808a793SDarrick J. Wong 	/* Add to our list of AEM devices */
7288808a793SDarrick J. Wong 	list_add_tail(&data->list, &driver_data.aem_devices);
7298808a793SDarrick J. Wong 
7308808a793SDarrick J. Wong 	dev_info(data->ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
7318808a793SDarrick J. Wong 		 data->ver_major, data->ver_minor,
7328808a793SDarrick J. Wong 		 data->module_handle);
7338808a793SDarrick J. Wong 	return 0;
7348808a793SDarrick J. Wong 
7358808a793SDarrick J. Wong sensor_err:
736da8ebe4eSJean Delvare 	kfree(data->rs_resp);
737da8ebe4eSJean Delvare alloc_resp_err:
7388808a793SDarrick J. Wong 	hwmon_device_unregister(data->hwmon_dev);
7398808a793SDarrick J. Wong hwmon_reg_err:
7408808a793SDarrick J. Wong 	ipmi_destroy_user(data->ipmi.user);
7418808a793SDarrick J. Wong ipmi_err:
74295de3b25SJean Delvare 	platform_set_drvdata(data->pdev, NULL);
743*d0e51022SYang Yingliang 	platform_device_del(data->pdev);
744*d0e51022SYang Yingliang dev_add_err:
745*d0e51022SYang Yingliang 	platform_device_put(data->pdev);
7468808a793SDarrick J. Wong dev_err:
747be1ca367Skeliu 	ida_free(&aem_ida, data->id);
7488808a793SDarrick J. Wong id_err:
7498808a793SDarrick J. Wong 	kfree(data);
7508808a793SDarrick J. Wong 
7518808a793SDarrick J. Wong 	return res;
7528808a793SDarrick J. Wong }
7538808a793SDarrick J. Wong 
7548808a793SDarrick J. Wong /* Find and initialize all AEM2 instances */
aem_init_aem2(struct aem_ipmi_data * probe)7559d84c9e8SJean Delvare static void aem_init_aem2(struct aem_ipmi_data *probe)
7568808a793SDarrick J. Wong {
7578808a793SDarrick J. Wong 	struct aem_find_instance_resp fi_resp;
7588808a793SDarrick J. Wong 	int err;
7598808a793SDarrick J. Wong 	int i = 0;
7608808a793SDarrick J. Wong 
7618808a793SDarrick J. Wong 	while (!aem_find_aem2(probe, &fi_resp, i)) {
7628808a793SDarrick J. Wong 		if (fi_resp.major != 2) {
763b55f3757SGuenter Roeck 			dev_err(probe->bmc_device,
764b55f3757SGuenter Roeck 				"Unknown AEM v%d; please report this to the maintainer.\n",
7658808a793SDarrick J. Wong 				fi_resp.major);
7668808a793SDarrick J. Wong 			i++;
7678808a793SDarrick J. Wong 			continue;
7688808a793SDarrick J. Wong 		}
7698808a793SDarrick J. Wong 		err = aem_init_aem2_inst(probe, &fi_resp);
7708808a793SDarrick J. Wong 		if (err) {
7718808a793SDarrick J. Wong 			dev_err(probe->bmc_device,
7728808a793SDarrick J. Wong 				"Error %d initializing AEM2 0x%X\n",
7738808a793SDarrick J. Wong 				err, fi_resp.module_handle);
7748808a793SDarrick J. Wong 		}
7758808a793SDarrick J. Wong 		i++;
7768808a793SDarrick J. Wong 	}
7778808a793SDarrick J. Wong }
7788808a793SDarrick J. Wong 
7798808a793SDarrick J. Wong /* Probe a BMC for AEM firmware instances */
aem_register_bmc(int iface,struct device * dev)7808808a793SDarrick J. Wong static void aem_register_bmc(int iface, struct device *dev)
7818808a793SDarrick J. Wong {
7828808a793SDarrick J. Wong 	struct aem_ipmi_data probe;
7838808a793SDarrick J. Wong 
7848808a793SDarrick J. Wong 	if (aem_init_ipmi_data(&probe, iface, dev))
7858808a793SDarrick J. Wong 		return;
7868808a793SDarrick J. Wong 
7878808a793SDarrick J. Wong 	/* Ignore probe errors; they won't cause problems */
7888808a793SDarrick J. Wong 	aem_init_aem1(&probe);
7898808a793SDarrick J. Wong 	aem_init_aem2(&probe);
7908808a793SDarrick J. Wong 
7918808a793SDarrick J. Wong 	ipmi_destroy_user(probe.user);
7928808a793SDarrick J. Wong }
7938808a793SDarrick J. Wong 
7948808a793SDarrick J. Wong /* Handle BMC deletion */
aem_bmc_gone(int iface)7958808a793SDarrick J. Wong static void aem_bmc_gone(int iface)
7968808a793SDarrick J. Wong {
7978808a793SDarrick J. Wong 	struct aem_data *p1, *next1;
7988808a793SDarrick J. Wong 
7998808a793SDarrick J. Wong 	list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list)
8008808a793SDarrick J. Wong 		if (p1->ipmi.interface == iface)
8018808a793SDarrick J. Wong 			aem_delete(p1);
8028808a793SDarrick J. Wong }
8038808a793SDarrick J. Wong 
8048808a793SDarrick J. Wong /* sysfs support functions */
8058808a793SDarrick J. Wong 
8068808a793SDarrick J. Wong /* AEM device name */
name_show(struct device * dev,struct device_attribute * devattr,char * buf)807f6861c0eSGuenter Roeck static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
8088808a793SDarrick J. Wong 			 char *buf)
8098808a793SDarrick J. Wong {
8108808a793SDarrick J. Wong 	struct aem_data *data = dev_get_drvdata(dev);
8118808a793SDarrick J. Wong 
8128808a793SDarrick J. Wong 	return sprintf(buf, "%s%d\n", DRVNAME, data->ver_major);
8138808a793SDarrick J. Wong }
814f6861c0eSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(name, name, 0);
8158808a793SDarrick J. Wong 
8168808a793SDarrick J. Wong /* AEM device version */
version_show(struct device * dev,struct device_attribute * devattr,char * buf)817f6861c0eSGuenter Roeck static ssize_t version_show(struct device *dev,
818f6861c0eSGuenter Roeck 			    struct device_attribute *devattr, char *buf)
8198808a793SDarrick J. Wong {
8208808a793SDarrick J. Wong 	struct aem_data *data = dev_get_drvdata(dev);
8218808a793SDarrick J. Wong 
8228808a793SDarrick J. Wong 	return sprintf(buf, "%d.%d\n", data->ver_major, data->ver_minor);
8238808a793SDarrick J. Wong }
824f6861c0eSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(version, version, 0);
8258808a793SDarrick J. Wong 
8268808a793SDarrick J. Wong /* Display power use */
aem_show_power(struct device * dev,struct device_attribute * devattr,char * buf)8278808a793SDarrick J. Wong static ssize_t aem_show_power(struct device *dev,
8288808a793SDarrick J. Wong 			      struct device_attribute *devattr,
8298808a793SDarrick J. Wong 			      char *buf)
8308808a793SDarrick J. Wong {
8318808a793SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8328808a793SDarrick J. Wong 	struct aem_data *data = dev_get_drvdata(dev);
8338808a793SDarrick J. Wong 	u64 before, after, delta, time;
8348808a793SDarrick J. Wong 	signed long leftover;
8358808a793SDarrick J. Wong 
8368808a793SDarrick J. Wong 	mutex_lock(&data->lock);
837bb15e7f2SDarrick J. Wong 	update_aem_energy_one(data, attr->index);
838d659f9b1SThomas Gleixner 	time = ktime_get_ns();
8398808a793SDarrick J. Wong 	before = data->energy[attr->index];
8408808a793SDarrick J. Wong 
8418808a793SDarrick J. Wong 	leftover = schedule_timeout_interruptible(
8428808a793SDarrick J. Wong 			msecs_to_jiffies(data->power_period[attr->index])
8438808a793SDarrick J. Wong 		   );
8448808a793SDarrick J. Wong 	if (leftover) {
8458808a793SDarrick J. Wong 		mutex_unlock(&data->lock);
8468808a793SDarrick J. Wong 		return 0;
8478808a793SDarrick J. Wong 	}
8488808a793SDarrick J. Wong 
849bb15e7f2SDarrick J. Wong 	update_aem_energy_one(data, attr->index);
850d659f9b1SThomas Gleixner 	time = ktime_get_ns() - time;
8518808a793SDarrick J. Wong 	after = data->energy[attr->index];
8528808a793SDarrick J. Wong 	mutex_unlock(&data->lock);
8538808a793SDarrick J. Wong 
8548808a793SDarrick J. Wong 	delta = (after - before) * UJ_PER_MJ;
8558808a793SDarrick J. Wong 
8568808a793SDarrick J. Wong 	return sprintf(buf, "%llu\n",
8578808a793SDarrick J. Wong 		(unsigned long long)div64_u64(delta * NSEC_PER_SEC, time));
8588808a793SDarrick J. Wong }
8598808a793SDarrick J. Wong 
8608808a793SDarrick J. Wong /* Display energy use */
aem_show_energy(struct device * dev,struct device_attribute * devattr,char * buf)8618808a793SDarrick J. Wong static ssize_t aem_show_energy(struct device *dev,
8628808a793SDarrick J. Wong 			       struct device_attribute *devattr,
8638808a793SDarrick J. Wong 			       char *buf)
8648808a793SDarrick J. Wong {
8658808a793SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8668808a793SDarrick J. Wong 	struct aem_data *a = dev_get_drvdata(dev);
867bb15e7f2SDarrick J. Wong 	mutex_lock(&a->lock);
868bb15e7f2SDarrick J. Wong 	update_aem_energy_one(a, attr->index);
869bb15e7f2SDarrick J. Wong 	mutex_unlock(&a->lock);
8708808a793SDarrick J. Wong 
8718808a793SDarrick J. Wong 	return sprintf(buf, "%llu\n",
8728808a793SDarrick J. Wong 			(unsigned long long)a->energy[attr->index] * 1000);
8738808a793SDarrick J. Wong }
8748808a793SDarrick J. Wong 
8758808a793SDarrick J. Wong /* Display power interval registers */
aem_show_power_period(struct device * dev,struct device_attribute * devattr,char * buf)8768808a793SDarrick J. Wong static ssize_t aem_show_power_period(struct device *dev,
8778808a793SDarrick J. Wong 				     struct device_attribute *devattr,
8788808a793SDarrick J. Wong 				     char *buf)
8798808a793SDarrick J. Wong {
8808808a793SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8818808a793SDarrick J. Wong 	struct aem_data *a = dev_get_drvdata(dev);
8828808a793SDarrick J. Wong 	a->update(a);
8838808a793SDarrick J. Wong 
8848808a793SDarrick J. Wong 	return sprintf(buf, "%lu\n", a->power_period[attr->index]);
8858808a793SDarrick J. Wong }
8868808a793SDarrick J. Wong 
8878808a793SDarrick J. Wong /* Set power interval registers */
aem_set_power_period(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)8888808a793SDarrick J. Wong static ssize_t aem_set_power_period(struct device *dev,
8898808a793SDarrick J. Wong 				    struct device_attribute *devattr,
8908808a793SDarrick J. Wong 				    const char *buf, size_t count)
8918808a793SDarrick J. Wong {
8928808a793SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8938808a793SDarrick J. Wong 	struct aem_data *a = dev_get_drvdata(dev);
8948808a793SDarrick J. Wong 	unsigned long temp;
8958808a793SDarrick J. Wong 	int res;
8968808a793SDarrick J. Wong 
897179c4fdbSFrans Meulenbroeks 	res = kstrtoul(buf, 10, &temp);
8988808a793SDarrick J. Wong 	if (res)
8998808a793SDarrick J. Wong 		return res;
9008808a793SDarrick J. Wong 
9018808a793SDarrick J. Wong 	if (temp < AEM_MIN_POWER_INTERVAL)
9028808a793SDarrick J. Wong 		return -EINVAL;
9038808a793SDarrick J. Wong 
9048808a793SDarrick J. Wong 	mutex_lock(&a->lock);
9058808a793SDarrick J. Wong 	a->power_period[attr->index] = temp;
9068808a793SDarrick J. Wong 	mutex_unlock(&a->lock);
9078808a793SDarrick J. Wong 
9088808a793SDarrick J. Wong 	return count;
9098808a793SDarrick J. Wong }
9108808a793SDarrick J. Wong 
9118808a793SDarrick J. Wong /* Discover sensors on an AEM device */
aem_register_sensors(struct aem_data * data,const struct aem_ro_sensor_template * ro,const struct aem_rw_sensor_template * rw)9128808a793SDarrick J. Wong static int aem_register_sensors(struct aem_data *data,
913449278d9SJulia Lawall 				const struct aem_ro_sensor_template *ro,
914449278d9SJulia Lawall 				const struct aem_rw_sensor_template *rw)
9158808a793SDarrick J. Wong {
9168808a793SDarrick J. Wong 	struct device *dev = &data->pdev->dev;
9178808a793SDarrick J. Wong 	struct sensor_device_attribute *sensors = data->sensors;
9188808a793SDarrick J. Wong 	int err;
9198808a793SDarrick J. Wong 
9208808a793SDarrick J. Wong 	/* Set up read-only sensors */
9218808a793SDarrick J. Wong 	while (ro->label) {
9223cdb2052SGuenter Roeck 		sysfs_attr_init(&sensors->dev_attr.attr);
9238808a793SDarrick J. Wong 		sensors->dev_attr.attr.name = ro->label;
924f6861c0eSGuenter Roeck 		sensors->dev_attr.attr.mode = 0444;
9258808a793SDarrick J. Wong 		sensors->dev_attr.show = ro->show;
9268808a793SDarrick J. Wong 		sensors->index = ro->index;
9278808a793SDarrick J. Wong 
9288808a793SDarrick J. Wong 		err = device_create_file(dev, &sensors->dev_attr);
9298808a793SDarrick J. Wong 		if (err) {
9308808a793SDarrick J. Wong 			sensors->dev_attr.attr.name = NULL;
9318808a793SDarrick J. Wong 			goto error;
9328808a793SDarrick J. Wong 		}
9338808a793SDarrick J. Wong 		sensors++;
9348808a793SDarrick J. Wong 		ro++;
9358808a793SDarrick J. Wong 	}
9368808a793SDarrick J. Wong 
9378808a793SDarrick J. Wong 	/* Set up read-write sensors */
9388808a793SDarrick J. Wong 	while (rw->label) {
9393cdb2052SGuenter Roeck 		sysfs_attr_init(&sensors->dev_attr.attr);
9408808a793SDarrick J. Wong 		sensors->dev_attr.attr.name = rw->label;
941f6861c0eSGuenter Roeck 		sensors->dev_attr.attr.mode = 0644;
9428808a793SDarrick J. Wong 		sensors->dev_attr.show = rw->show;
9438808a793SDarrick J. Wong 		sensors->dev_attr.store = rw->set;
9448808a793SDarrick J. Wong 		sensors->index = rw->index;
9458808a793SDarrick J. Wong 
9468808a793SDarrick J. Wong 		err = device_create_file(dev, &sensors->dev_attr);
9478808a793SDarrick J. Wong 		if (err) {
9488808a793SDarrick J. Wong 			sensors->dev_attr.attr.name = NULL;
9498808a793SDarrick J. Wong 			goto error;
9508808a793SDarrick J. Wong 		}
9518808a793SDarrick J. Wong 		sensors++;
9528808a793SDarrick J. Wong 		rw++;
9538808a793SDarrick J. Wong 	}
9548808a793SDarrick J. Wong 
9558808a793SDarrick J. Wong 	err = device_create_file(dev, &sensor_dev_attr_name.dev_attr);
9568808a793SDarrick J. Wong 	if (err)
9578808a793SDarrick J. Wong 		goto error;
9588808a793SDarrick J. Wong 	err = device_create_file(dev, &sensor_dev_attr_version.dev_attr);
9598808a793SDarrick J. Wong 	return err;
9608808a793SDarrick J. Wong 
9618808a793SDarrick J. Wong error:
9628808a793SDarrick J. Wong 	aem_remove_sensors(data);
9638808a793SDarrick J. Wong 	return err;
9648808a793SDarrick J. Wong }
9658808a793SDarrick J. Wong 
9668808a793SDarrick J. Wong /* sysfs support functions for AEM2 sensors */
9678808a793SDarrick J. Wong 
9688808a793SDarrick J. Wong /* Display temperature use */
aem2_show_temp(struct device * dev,struct device_attribute * devattr,char * buf)9698808a793SDarrick J. Wong static ssize_t aem2_show_temp(struct device *dev,
9708808a793SDarrick J. Wong 			      struct device_attribute *devattr,
9718808a793SDarrick J. Wong 			      char *buf)
9728808a793SDarrick J. Wong {
9738808a793SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
9748808a793SDarrick J. Wong 	struct aem_data *a = dev_get_drvdata(dev);
9758808a793SDarrick J. Wong 	a->update(a);
9768808a793SDarrick J. Wong 
9778808a793SDarrick J. Wong 	return sprintf(buf, "%u\n", a->temp[attr->index] * 1000);
9788808a793SDarrick J. Wong }
9798808a793SDarrick J. Wong 
9808808a793SDarrick J. Wong /* Display power-capping registers */
aem2_show_pcap_value(struct device * dev,struct device_attribute * devattr,char * buf)9818808a793SDarrick J. Wong static ssize_t aem2_show_pcap_value(struct device *dev,
9828808a793SDarrick J. Wong 				    struct device_attribute *devattr,
9838808a793SDarrick J. Wong 				    char *buf)
9848808a793SDarrick J. Wong {
9858808a793SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
9868808a793SDarrick J. Wong 	struct aem_data *a = dev_get_drvdata(dev);
9878808a793SDarrick J. Wong 	a->update(a);
9888808a793SDarrick J. Wong 
9898808a793SDarrick J. Wong 	return sprintf(buf, "%u\n", a->pcap[attr->index] * 100000);
9908808a793SDarrick J. Wong }
9918808a793SDarrick J. Wong 
9928808a793SDarrick J. Wong /* Remove sensors attached to an AEM device */
aem_remove_sensors(struct aem_data * data)9938808a793SDarrick J. Wong static void aem_remove_sensors(struct aem_data *data)
9948808a793SDarrick J. Wong {
9958808a793SDarrick J. Wong 	int i;
9968808a793SDarrick J. Wong 
9978808a793SDarrick J. Wong 	for (i = 0; i < AEM_NUM_SENSORS; i++) {
9988808a793SDarrick J. Wong 		if (!data->sensors[i].dev_attr.attr.name)
9998808a793SDarrick J. Wong 			continue;
10008808a793SDarrick J. Wong 		device_remove_file(&data->pdev->dev,
10018808a793SDarrick J. Wong 				   &data->sensors[i].dev_attr);
10028808a793SDarrick J. Wong 	}
10038808a793SDarrick J. Wong 
10048808a793SDarrick J. Wong 	device_remove_file(&data->pdev->dev,
10058808a793SDarrick J. Wong 			   &sensor_dev_attr_name.dev_attr);
10068808a793SDarrick J. Wong 	device_remove_file(&data->pdev->dev,
10078808a793SDarrick J. Wong 			   &sensor_dev_attr_version.dev_attr);
10088808a793SDarrick J. Wong }
10098808a793SDarrick J. Wong 
10108808a793SDarrick J. Wong /* Sensor probe functions */
10118808a793SDarrick J. Wong 
10128808a793SDarrick J. Wong /* Description of AEM1 sensors */
1013449278d9SJulia Lawall static const struct aem_ro_sensor_template aem1_ro_sensors[] = {
10148808a793SDarrick J. Wong {"energy1_input",  aem_show_energy, 0},
10158808a793SDarrick J. Wong {"power1_average", aem_show_power,  0},
10168808a793SDarrick J. Wong {NULL,		   NULL,	    0},
10178808a793SDarrick J. Wong };
10188808a793SDarrick J. Wong 
1019449278d9SJulia Lawall static const struct aem_rw_sensor_template aem1_rw_sensors[] = {
10208808a793SDarrick J. Wong {"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
10218808a793SDarrick J. Wong {NULL,			    NULL,                  NULL,                 0},
10228808a793SDarrick J. Wong };
10238808a793SDarrick J. Wong 
10248808a793SDarrick J. Wong /* Description of AEM2 sensors */
1025449278d9SJulia Lawall static const struct aem_ro_sensor_template aem2_ro_sensors[] = {
10268808a793SDarrick J. Wong {"energy1_input",	  aem_show_energy,	0},
10278808a793SDarrick J. Wong {"energy2_input",	  aem_show_energy,	1},
10288808a793SDarrick J. Wong {"power1_average",	  aem_show_power,	0},
10298808a793SDarrick J. Wong {"power2_average",	  aem_show_power,	1},
10308808a793SDarrick J. Wong {"temp1_input",		  aem2_show_temp,	0},
10318808a793SDarrick J. Wong {"temp2_input",		  aem2_show_temp,	1},
10328808a793SDarrick J. Wong 
10338808a793SDarrick J. Wong {"power4_average",	  aem2_show_pcap_value,	POWER_CAP_MAX_HOTPLUG},
10348808a793SDarrick J. Wong {"power5_average",	  aem2_show_pcap_value,	POWER_CAP_MAX},
10358808a793SDarrick J. Wong {"power6_average",	  aem2_show_pcap_value,	POWER_CAP_MIN_WARNING},
10368808a793SDarrick J. Wong {"power7_average",	  aem2_show_pcap_value,	POWER_CAP_MIN},
10378808a793SDarrick J. Wong 
10388808a793SDarrick J. Wong {"power3_average",	  aem2_show_pcap_value,	POWER_AUX},
10398808a793SDarrick J. Wong {"power_cap",		  aem2_show_pcap_value,	POWER_CAP},
10408808a793SDarrick J. Wong {NULL,                    NULL,                 0},
10418808a793SDarrick J. Wong };
10428808a793SDarrick J. Wong 
1043449278d9SJulia Lawall static const struct aem_rw_sensor_template aem2_rw_sensors[] = {
10448808a793SDarrick J. Wong {"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
10458808a793SDarrick J. Wong {"power2_average_interval", aem_show_power_period, aem_set_power_period, 1},
10468808a793SDarrick J. Wong {NULL,			    NULL,                  NULL,                 0},
10478808a793SDarrick J. Wong };
10488808a793SDarrick J. Wong 
10498808a793SDarrick J. Wong /* Set up AEM1 sensor attrs */
aem1_find_sensors(struct aem_data * data)10508808a793SDarrick J. Wong static int aem1_find_sensors(struct aem_data *data)
10518808a793SDarrick J. Wong {
10528808a793SDarrick J. Wong 	return aem_register_sensors(data, aem1_ro_sensors, aem1_rw_sensors);
10538808a793SDarrick J. Wong }
10548808a793SDarrick J. Wong 
10558808a793SDarrick J. Wong /* Set up AEM2 sensor attrs */
aem2_find_sensors(struct aem_data * data)10568808a793SDarrick J. Wong static int aem2_find_sensors(struct aem_data *data)
10578808a793SDarrick J. Wong {
10588808a793SDarrick J. Wong 	return aem_register_sensors(data, aem2_ro_sensors, aem2_rw_sensors);
10598808a793SDarrick J. Wong }
10608808a793SDarrick J. Wong 
10618808a793SDarrick J. Wong /* Module init/exit routines */
10628808a793SDarrick J. Wong 
aem_init(void)10638808a793SDarrick J. Wong static int __init aem_init(void)
10648808a793SDarrick J. Wong {
10658808a793SDarrick J. Wong 	int res;
10668808a793SDarrick J. Wong 
1067fe2d5ffcSDarrick J. Wong 	res = driver_register(&aem_driver.driver);
10688808a793SDarrick J. Wong 	if (res) {
106955d705ccSJoe Perches 		pr_err("Can't register aem driver\n");
10708808a793SDarrick J. Wong 		return res;
10718808a793SDarrick J. Wong 	}
10728808a793SDarrick J. Wong 
10738808a793SDarrick J. Wong 	res = ipmi_smi_watcher_register(&driver_data.bmc_events);
10748808a793SDarrick J. Wong 	if (res)
10758808a793SDarrick J. Wong 		goto ipmi_reg_err;
10768808a793SDarrick J. Wong 	return 0;
10778808a793SDarrick J. Wong 
10788808a793SDarrick J. Wong ipmi_reg_err:
1079fe2d5ffcSDarrick J. Wong 	driver_unregister(&aem_driver.driver);
10808808a793SDarrick J. Wong 	return res;
10818808a793SDarrick J. Wong 
10828808a793SDarrick J. Wong }
10838808a793SDarrick J. Wong 
aem_exit(void)10848808a793SDarrick J. Wong static void __exit aem_exit(void)
10858808a793SDarrick J. Wong {
10868808a793SDarrick J. Wong 	struct aem_data *p1, *next1;
10878808a793SDarrick J. Wong 
10888808a793SDarrick J. Wong 	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
1089fe2d5ffcSDarrick J. Wong 	driver_unregister(&aem_driver.driver);
10908808a793SDarrick J. Wong 	list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list)
10918808a793SDarrick J. Wong 		aem_delete(p1);
10928808a793SDarrick J. Wong }
10938808a793SDarrick J. Wong 
10945407e051SDarrick J. Wong MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
1095eb93b7dfSDarrick J. Wong MODULE_DESCRIPTION("IBM AEM power/temp/energy sensor driver");
10968808a793SDarrick J. Wong MODULE_LICENSE("GPL");
10978808a793SDarrick J. Wong 
10988808a793SDarrick J. Wong module_init(aem_init);
10998808a793SDarrick J. Wong module_exit(aem_exit);
110021d93c14SDarrick J. Wong 
110121d93c14SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
110221d93c14SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
110321d93c14SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
110421d93c14SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
110521d93c14SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
110621d93c14SDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
11078070408bSDarrick J. Wong MODULE_ALIAS("dmi:bvnIBM:*:pnIBMBladeHC10-*");
1108