18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * A hwmon driver for the IBM System Director Active Energy Manager (AEM)
48c2ecf20Sopenharmony_ci * temperature/power/energy sensors and capping functionality.
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 IBM
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/ipmi.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
158c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
168c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/kdev_t.h>
198c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
208c2ecf20Sopenharmony_ci#include <linux/idr.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/sched.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/math64.h>
258c2ecf20Sopenharmony_ci#include <linux/time.h>
268c2ecf20Sopenharmony_ci#include <linux/err.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define REFRESH_INTERVAL	(HZ)
298c2ecf20Sopenharmony_ci#define IPMI_TIMEOUT		(30 * HZ)
308c2ecf20Sopenharmony_ci#define DRVNAME			"aem"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define AEM_NETFN		0x2E
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define AEM_FIND_FW_CMD		0x80
358c2ecf20Sopenharmony_ci#define AEM_ELEMENT_CMD		0x81
368c2ecf20Sopenharmony_ci#define AEM_FW_INSTANCE_CMD	0x82
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define AEM_READ_ELEMENT_CFG	0x80
398c2ecf20Sopenharmony_ci#define AEM_READ_BUFFER		0x81
408c2ecf20Sopenharmony_ci#define AEM_READ_REGISTER	0x82
418c2ecf20Sopenharmony_ci#define AEM_WRITE_REGISTER	0x83
428c2ecf20Sopenharmony_ci#define AEM_SET_REG_MASK	0x84
438c2ecf20Sopenharmony_ci#define AEM_CLEAR_REG_MASK	0x85
448c2ecf20Sopenharmony_ci#define AEM_READ_ELEMENT_CFG2	0x86
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define AEM_CONTROL_ELEMENT	0
478c2ecf20Sopenharmony_ci#define AEM_ENERGY_ELEMENT	1
488c2ecf20Sopenharmony_ci#define AEM_CLOCK_ELEMENT	4
498c2ecf20Sopenharmony_ci#define AEM_POWER_CAP_ELEMENT	7
508c2ecf20Sopenharmony_ci#define AEM_EXHAUST_ELEMENT	9
518c2ecf20Sopenharmony_ci#define AEM_POWER_ELEMENT	10
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define AEM_MODULE_TYPE_ID	0x0001
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define AEM2_NUM_ENERGY_REGS	2
568c2ecf20Sopenharmony_ci#define AEM2_NUM_PCAP_REGS	6
578c2ecf20Sopenharmony_ci#define AEM2_NUM_TEMP_REGS	2
588c2ecf20Sopenharmony_ci#define AEM2_NUM_SENSORS	14
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define AEM1_NUM_ENERGY_REGS	1
618c2ecf20Sopenharmony_ci#define AEM1_NUM_SENSORS	3
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* AEM 2.x has more energy registers */
648c2ecf20Sopenharmony_ci#define AEM_NUM_ENERGY_REGS	AEM2_NUM_ENERGY_REGS
658c2ecf20Sopenharmony_ci/* AEM 2.x needs more sensor files */
668c2ecf20Sopenharmony_ci#define AEM_NUM_SENSORS		AEM2_NUM_SENSORS
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define POWER_CAP		0
698c2ecf20Sopenharmony_ci#define POWER_CAP_MAX_HOTPLUG	1
708c2ecf20Sopenharmony_ci#define POWER_CAP_MAX		2
718c2ecf20Sopenharmony_ci#define	POWER_CAP_MIN_WARNING	3
728c2ecf20Sopenharmony_ci#define POWER_CAP_MIN		4
738c2ecf20Sopenharmony_ci#define	POWER_AUX		5
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define AEM_DEFAULT_POWER_INTERVAL 1000
768c2ecf20Sopenharmony_ci#define AEM_MIN_POWER_INTERVAL	200
778c2ecf20Sopenharmony_ci#define UJ_PER_MJ		1000L
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic DEFINE_IDA(aem_ida);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic struct platform_driver aem_driver = {
828c2ecf20Sopenharmony_ci	.driver = {
838c2ecf20Sopenharmony_ci		.name = DRVNAME,
848c2ecf20Sopenharmony_ci		.bus = &platform_bus_type,
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistruct aem_ipmi_data {
898c2ecf20Sopenharmony_ci	struct completion	read_complete;
908c2ecf20Sopenharmony_ci	struct ipmi_addr	address;
918c2ecf20Sopenharmony_ci	struct ipmi_user	*user;
928c2ecf20Sopenharmony_ci	int			interface;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	struct kernel_ipmi_msg	tx_message;
958c2ecf20Sopenharmony_ci	long			tx_msgid;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	void			*rx_msg_data;
988c2ecf20Sopenharmony_ci	unsigned short		rx_msg_len;
998c2ecf20Sopenharmony_ci	unsigned char		rx_result;
1008c2ecf20Sopenharmony_ci	int			rx_recv_type;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	struct device		*bmc_device;
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistruct aem_ro_sensor_template {
1068c2ecf20Sopenharmony_ci	char *label;
1078c2ecf20Sopenharmony_ci	ssize_t (*show)(struct device *dev,
1088c2ecf20Sopenharmony_ci			struct device_attribute *devattr,
1098c2ecf20Sopenharmony_ci			char *buf);
1108c2ecf20Sopenharmony_ci	int index;
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistruct aem_rw_sensor_template {
1148c2ecf20Sopenharmony_ci	char *label;
1158c2ecf20Sopenharmony_ci	ssize_t (*show)(struct device *dev,
1168c2ecf20Sopenharmony_ci			struct device_attribute *devattr,
1178c2ecf20Sopenharmony_ci			char *buf);
1188c2ecf20Sopenharmony_ci	ssize_t (*set)(struct device *dev,
1198c2ecf20Sopenharmony_ci		       struct device_attribute *devattr,
1208c2ecf20Sopenharmony_ci		       const char *buf, size_t count);
1218c2ecf20Sopenharmony_ci	int index;
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistruct aem_data {
1258c2ecf20Sopenharmony_ci	struct list_head	list;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	struct device		*hwmon_dev;
1288c2ecf20Sopenharmony_ci	struct platform_device	*pdev;
1298c2ecf20Sopenharmony_ci	struct mutex		lock;
1308c2ecf20Sopenharmony_ci	char			valid;
1318c2ecf20Sopenharmony_ci	unsigned long		last_updated;	/* In jiffies */
1328c2ecf20Sopenharmony_ci	u8			ver_major;
1338c2ecf20Sopenharmony_ci	u8			ver_minor;
1348c2ecf20Sopenharmony_ci	u8			module_handle;
1358c2ecf20Sopenharmony_ci	int			id;
1368c2ecf20Sopenharmony_ci	struct aem_ipmi_data	ipmi;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* Function and buffer to update sensors */
1398c2ecf20Sopenharmony_ci	void (*update)(struct aem_data *data);
1408c2ecf20Sopenharmony_ci	struct aem_read_sensor_resp *rs_resp;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/*
1438c2ecf20Sopenharmony_ci	 * AEM 1.x sensors:
1448c2ecf20Sopenharmony_ci	 * Available sensors:
1458c2ecf20Sopenharmony_ci	 * Energy meter
1468c2ecf20Sopenharmony_ci	 * Power meter
1478c2ecf20Sopenharmony_ci	 *
1488c2ecf20Sopenharmony_ci	 * AEM 2.x sensors:
1498c2ecf20Sopenharmony_ci	 * Two energy meters
1508c2ecf20Sopenharmony_ci	 * Two power meters
1518c2ecf20Sopenharmony_ci	 * Two temperature sensors
1528c2ecf20Sopenharmony_ci	 * Six power cap registers
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* sysfs attrs */
1568c2ecf20Sopenharmony_ci	struct sensor_device_attribute	sensors[AEM_NUM_SENSORS];
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* energy use in mJ */
1598c2ecf20Sopenharmony_ci	u64			energy[AEM_NUM_ENERGY_REGS];
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* power sampling interval in ms */
1628c2ecf20Sopenharmony_ci	unsigned long		power_period[AEM_NUM_ENERGY_REGS];
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* Everything past here is for AEM2 only */
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* power caps in dW */
1678c2ecf20Sopenharmony_ci	u16			pcap[AEM2_NUM_PCAP_REGS];
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* exhaust temperature in C */
1708c2ecf20Sopenharmony_ci	u8			temp[AEM2_NUM_TEMP_REGS];
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/* Data structures returned by the AEM firmware */
1748c2ecf20Sopenharmony_cistruct aem_iana_id {
1758c2ecf20Sopenharmony_ci	u8			bytes[3];
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_cistatic struct aem_iana_id system_x_id = {
1788c2ecf20Sopenharmony_ci	.bytes = {0x4D, 0x4F, 0x00}
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* These are used to find AEM1 instances */
1828c2ecf20Sopenharmony_cistruct aem_find_firmware_req {
1838c2ecf20Sopenharmony_ci	struct aem_iana_id	id;
1848c2ecf20Sopenharmony_ci	u8			rsvd;
1858c2ecf20Sopenharmony_ci	__be16			index;
1868c2ecf20Sopenharmony_ci	__be16			module_type_id;
1878c2ecf20Sopenharmony_ci} __packed;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistruct aem_find_firmware_resp {
1908c2ecf20Sopenharmony_ci	struct aem_iana_id	id;
1918c2ecf20Sopenharmony_ci	u8			num_instances;
1928c2ecf20Sopenharmony_ci} __packed;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/* These are used to find AEM2 instances */
1958c2ecf20Sopenharmony_cistruct aem_find_instance_req {
1968c2ecf20Sopenharmony_ci	struct aem_iana_id	id;
1978c2ecf20Sopenharmony_ci	u8			instance_number;
1988c2ecf20Sopenharmony_ci	__be16			module_type_id;
1998c2ecf20Sopenharmony_ci} __packed;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistruct aem_find_instance_resp {
2028c2ecf20Sopenharmony_ci	struct aem_iana_id	id;
2038c2ecf20Sopenharmony_ci	u8			num_instances;
2048c2ecf20Sopenharmony_ci	u8			major;
2058c2ecf20Sopenharmony_ci	u8			minor;
2068c2ecf20Sopenharmony_ci	u8			module_handle;
2078c2ecf20Sopenharmony_ci	u16			record_id;
2088c2ecf20Sopenharmony_ci} __packed;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/* These are used to query sensors */
2118c2ecf20Sopenharmony_cistruct aem_read_sensor_req {
2128c2ecf20Sopenharmony_ci	struct aem_iana_id	id;
2138c2ecf20Sopenharmony_ci	u8			module_handle;
2148c2ecf20Sopenharmony_ci	u8			element;
2158c2ecf20Sopenharmony_ci	u8			subcommand;
2168c2ecf20Sopenharmony_ci	u8			reg;
2178c2ecf20Sopenharmony_ci	u8			rx_buf_size;
2188c2ecf20Sopenharmony_ci} __packed;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistruct aem_read_sensor_resp {
2218c2ecf20Sopenharmony_ci	struct aem_iana_id	id;
2228c2ecf20Sopenharmony_ci	u8			bytes[];
2238c2ecf20Sopenharmony_ci} __packed;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/* Data structures to talk to the IPMI layer */
2268c2ecf20Sopenharmony_cistruct aem_driver_data {
2278c2ecf20Sopenharmony_ci	struct list_head	aem_devices;
2288c2ecf20Sopenharmony_ci	struct ipmi_smi_watcher	bmc_events;
2298c2ecf20Sopenharmony_ci	struct ipmi_user_hndl	ipmi_hndlrs;
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void aem_register_bmc(int iface, struct device *dev);
2338c2ecf20Sopenharmony_cistatic void aem_bmc_gone(int iface);
2348c2ecf20Sopenharmony_cistatic void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void aem_remove_sensors(struct aem_data *data);
2378c2ecf20Sopenharmony_cistatic int aem1_find_sensors(struct aem_data *data);
2388c2ecf20Sopenharmony_cistatic int aem2_find_sensors(struct aem_data *data);
2398c2ecf20Sopenharmony_cistatic void update_aem1_sensors(struct aem_data *data);
2408c2ecf20Sopenharmony_cistatic void update_aem2_sensors(struct aem_data *data);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic struct aem_driver_data driver_data = {
2438c2ecf20Sopenharmony_ci	.aem_devices = LIST_HEAD_INIT(driver_data.aem_devices),
2448c2ecf20Sopenharmony_ci	.bmc_events = {
2458c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
2468c2ecf20Sopenharmony_ci		.new_smi = aem_register_bmc,
2478c2ecf20Sopenharmony_ci		.smi_gone = aem_bmc_gone,
2488c2ecf20Sopenharmony_ci	},
2498c2ecf20Sopenharmony_ci	.ipmi_hndlrs = {
2508c2ecf20Sopenharmony_ci		.ipmi_recv_hndl = aem_msg_handler,
2518c2ecf20Sopenharmony_ci	},
2528c2ecf20Sopenharmony_ci};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/* Functions to talk to the IPMI layer */
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/* Initialize IPMI address, message buffers and user data */
2578c2ecf20Sopenharmony_cistatic int aem_init_ipmi_data(struct aem_ipmi_data *data, int iface,
2588c2ecf20Sopenharmony_ci			      struct device *bmc)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	int err;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	init_completion(&data->read_complete);
2638c2ecf20Sopenharmony_ci	data->bmc_device = bmc;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* Initialize IPMI address */
2668c2ecf20Sopenharmony_ci	data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
2678c2ecf20Sopenharmony_ci	data->address.channel = IPMI_BMC_CHANNEL;
2688c2ecf20Sopenharmony_ci	data->address.data[0] = 0;
2698c2ecf20Sopenharmony_ci	data->interface = iface;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* Initialize message buffers */
2728c2ecf20Sopenharmony_ci	data->tx_msgid = 0;
2738c2ecf20Sopenharmony_ci	data->tx_message.netfn = AEM_NETFN;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Create IPMI messaging interface user */
2768c2ecf20Sopenharmony_ci	err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
2778c2ecf20Sopenharmony_ci			       data, &data->user);
2788c2ecf20Sopenharmony_ci	if (err < 0) {
2798c2ecf20Sopenharmony_ci		dev_err(bmc,
2808c2ecf20Sopenharmony_ci			"Unable to register user with IPMI interface %d\n",
2818c2ecf20Sopenharmony_ci			data->interface);
2828c2ecf20Sopenharmony_ci		return err;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/* Send an IPMI command */
2898c2ecf20Sopenharmony_cistatic int aem_send_message(struct aem_ipmi_data *data)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	int err;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	err = ipmi_validate_addr(&data->address, sizeof(data->address));
2948c2ecf20Sopenharmony_ci	if (err)
2958c2ecf20Sopenharmony_ci		goto out;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	data->tx_msgid++;
2988c2ecf20Sopenharmony_ci	err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
2998c2ecf20Sopenharmony_ci				   &data->tx_message, data, 0, 0, 0);
3008c2ecf20Sopenharmony_ci	if (err)
3018c2ecf20Sopenharmony_ci		goto out1;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ciout1:
3058c2ecf20Sopenharmony_ci	dev_err(data->bmc_device, "request_settime=%x\n", err);
3068c2ecf20Sopenharmony_ci	return err;
3078c2ecf20Sopenharmony_ciout:
3088c2ecf20Sopenharmony_ci	dev_err(data->bmc_device, "validate_addr=%x\n", err);
3098c2ecf20Sopenharmony_ci	return err;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/* Dispatch IPMI messages to callers */
3138c2ecf20Sopenharmony_cistatic void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	unsigned short rx_len;
3168c2ecf20Sopenharmony_ci	struct aem_ipmi_data *data = user_msg_data;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (msg->msgid != data->tx_msgid) {
3198c2ecf20Sopenharmony_ci		dev_err(data->bmc_device,
3208c2ecf20Sopenharmony_ci			"Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
3218c2ecf20Sopenharmony_ci			(int)msg->msgid,
3228c2ecf20Sopenharmony_ci			(int)data->tx_msgid);
3238c2ecf20Sopenharmony_ci		ipmi_free_recv_msg(msg);
3248c2ecf20Sopenharmony_ci		return;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	data->rx_recv_type = msg->recv_type;
3288c2ecf20Sopenharmony_ci	if (msg->msg.data_len > 0)
3298c2ecf20Sopenharmony_ci		data->rx_result = msg->msg.data[0];
3308c2ecf20Sopenharmony_ci	else
3318c2ecf20Sopenharmony_ci		data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (msg->msg.data_len > 1) {
3348c2ecf20Sopenharmony_ci		rx_len = msg->msg.data_len - 1;
3358c2ecf20Sopenharmony_ci		if (data->rx_msg_len < rx_len)
3368c2ecf20Sopenharmony_ci			rx_len = data->rx_msg_len;
3378c2ecf20Sopenharmony_ci		data->rx_msg_len = rx_len;
3388c2ecf20Sopenharmony_ci		memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
3398c2ecf20Sopenharmony_ci	} else
3408c2ecf20Sopenharmony_ci		data->rx_msg_len = 0;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	ipmi_free_recv_msg(msg);
3438c2ecf20Sopenharmony_ci	complete(&data->read_complete);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci/* Sensor support functions */
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci/* Read a sensor value; must be called with data->lock held */
3498c2ecf20Sopenharmony_cistatic int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg,
3508c2ecf20Sopenharmony_ci			   void *buf, size_t size)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	int rs_size, res;
3538c2ecf20Sopenharmony_ci	struct aem_read_sensor_req rs_req;
3548c2ecf20Sopenharmony_ci	/* Use preallocated rx buffer */
3558c2ecf20Sopenharmony_ci	struct aem_read_sensor_resp *rs_resp = data->rs_resp;
3568c2ecf20Sopenharmony_ci	struct aem_ipmi_data *ipmi = &data->ipmi;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* AEM registers are 1, 2, 4 or 8 bytes */
3598c2ecf20Sopenharmony_ci	switch (size) {
3608c2ecf20Sopenharmony_ci	case 1:
3618c2ecf20Sopenharmony_ci	case 2:
3628c2ecf20Sopenharmony_ci	case 4:
3638c2ecf20Sopenharmony_ci	case 8:
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	default:
3668c2ecf20Sopenharmony_ci		return -EINVAL;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	rs_req.id = system_x_id;
3708c2ecf20Sopenharmony_ci	rs_req.module_handle = data->module_handle;
3718c2ecf20Sopenharmony_ci	rs_req.element = elt;
3728c2ecf20Sopenharmony_ci	rs_req.subcommand = AEM_READ_REGISTER;
3738c2ecf20Sopenharmony_ci	rs_req.reg = reg;
3748c2ecf20Sopenharmony_ci	rs_req.rx_buf_size = size;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	ipmi->tx_message.cmd = AEM_ELEMENT_CMD;
3778c2ecf20Sopenharmony_ci	ipmi->tx_message.data = (char *)&rs_req;
3788c2ecf20Sopenharmony_ci	ipmi->tx_message.data_len = sizeof(rs_req);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	rs_size = sizeof(*rs_resp) + size;
3818c2ecf20Sopenharmony_ci	ipmi->rx_msg_data = rs_resp;
3828c2ecf20Sopenharmony_ci	ipmi->rx_msg_len = rs_size;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	aem_send_message(ipmi);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT);
3878c2ecf20Sopenharmony_ci	if (!res) {
3888c2ecf20Sopenharmony_ci		res = -ETIMEDOUT;
3898c2ecf20Sopenharmony_ci		goto out;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (ipmi->rx_result || ipmi->rx_msg_len != rs_size ||
3938c2ecf20Sopenharmony_ci	    memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) {
3948c2ecf20Sopenharmony_ci		res = -ENOENT;
3958c2ecf20Sopenharmony_ci		goto out;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	switch (size) {
3998c2ecf20Sopenharmony_ci	case 1: {
4008c2ecf20Sopenharmony_ci		u8 *x = buf;
4018c2ecf20Sopenharmony_ci		*x = rs_resp->bytes[0];
4028c2ecf20Sopenharmony_ci		break;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci	case 2: {
4058c2ecf20Sopenharmony_ci		u16 *x = buf;
4068c2ecf20Sopenharmony_ci		*x = be16_to_cpup((__be16 *)rs_resp->bytes);
4078c2ecf20Sopenharmony_ci		break;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	case 4: {
4108c2ecf20Sopenharmony_ci		u32 *x = buf;
4118c2ecf20Sopenharmony_ci		*x = be32_to_cpup((__be32 *)rs_resp->bytes);
4128c2ecf20Sopenharmony_ci		break;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	case 8: {
4158c2ecf20Sopenharmony_ci		u64 *x = buf;
4168c2ecf20Sopenharmony_ci		*x = be64_to_cpup((__be64 *)rs_resp->bytes);
4178c2ecf20Sopenharmony_ci		break;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci	res = 0;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ciout:
4238c2ecf20Sopenharmony_ci	return res;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci/* Update AEM energy registers */
4278c2ecf20Sopenharmony_cistatic void update_aem_energy_one(struct aem_data *data, int which)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	aem_read_sensor(data, AEM_ENERGY_ELEMENT, which,
4308c2ecf20Sopenharmony_ci			&data->energy[which], 8);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic void update_aem_energy(struct aem_data *data)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	update_aem_energy_one(data, 0);
4368c2ecf20Sopenharmony_ci	if (data->ver_major < 2)
4378c2ecf20Sopenharmony_ci		return;
4388c2ecf20Sopenharmony_ci	update_aem_energy_one(data, 1);
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/* Update all AEM1 sensors */
4428c2ecf20Sopenharmony_cistatic void update_aem1_sensors(struct aem_data *data)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	mutex_lock(&data->lock);
4458c2ecf20Sopenharmony_ci	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
4468c2ecf20Sopenharmony_ci	    data->valid)
4478c2ecf20Sopenharmony_ci		goto out;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	update_aem_energy(data);
4508c2ecf20Sopenharmony_ciout:
4518c2ecf20Sopenharmony_ci	mutex_unlock(&data->lock);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci/* Update all AEM2 sensors */
4558c2ecf20Sopenharmony_cistatic void update_aem2_sensors(struct aem_data *data)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	int i;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	mutex_lock(&data->lock);
4608c2ecf20Sopenharmony_ci	if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
4618c2ecf20Sopenharmony_ci	    data->valid)
4628c2ecf20Sopenharmony_ci		goto out;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	update_aem_energy(data);
4658c2ecf20Sopenharmony_ci	aem_read_sensor(data, AEM_EXHAUST_ELEMENT, 0, &data->temp[0], 1);
4668c2ecf20Sopenharmony_ci	aem_read_sensor(data, AEM_EXHAUST_ELEMENT, 1, &data->temp[1], 1);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	for (i = POWER_CAP; i <= POWER_AUX; i++)
4698c2ecf20Sopenharmony_ci		aem_read_sensor(data, AEM_POWER_CAP_ELEMENT, i,
4708c2ecf20Sopenharmony_ci				&data->pcap[i], 2);
4718c2ecf20Sopenharmony_ciout:
4728c2ecf20Sopenharmony_ci	mutex_unlock(&data->lock);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/* Delete an AEM instance */
4768c2ecf20Sopenharmony_cistatic void aem_delete(struct aem_data *data)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	list_del(&data->list);
4798c2ecf20Sopenharmony_ci	aem_remove_sensors(data);
4808c2ecf20Sopenharmony_ci	kfree(data->rs_resp);
4818c2ecf20Sopenharmony_ci	hwmon_device_unregister(data->hwmon_dev);
4828c2ecf20Sopenharmony_ci	ipmi_destroy_user(data->ipmi.user);
4838c2ecf20Sopenharmony_ci	platform_set_drvdata(data->pdev, NULL);
4848c2ecf20Sopenharmony_ci	platform_device_unregister(data->pdev);
4858c2ecf20Sopenharmony_ci	ida_simple_remove(&aem_ida, data->id);
4868c2ecf20Sopenharmony_ci	kfree(data);
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/* Probe functions for AEM1 devices */
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci/* Retrieve version and module handle for an AEM1 instance */
4928c2ecf20Sopenharmony_cistatic int aem_find_aem1_count(struct aem_ipmi_data *data)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	int res;
4958c2ecf20Sopenharmony_ci	struct aem_find_firmware_req	ff_req;
4968c2ecf20Sopenharmony_ci	struct aem_find_firmware_resp	ff_resp;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	ff_req.id = system_x_id;
4998c2ecf20Sopenharmony_ci	ff_req.index = 0;
5008c2ecf20Sopenharmony_ci	ff_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	data->tx_message.cmd = AEM_FIND_FW_CMD;
5038c2ecf20Sopenharmony_ci	data->tx_message.data = (char *)&ff_req;
5048c2ecf20Sopenharmony_ci	data->tx_message.data_len = sizeof(ff_req);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	data->rx_msg_data = &ff_resp;
5078c2ecf20Sopenharmony_ci	data->rx_msg_len = sizeof(ff_resp);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	aem_send_message(data);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT);
5128c2ecf20Sopenharmony_ci	if (!res)
5138c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (data->rx_result || data->rx_msg_len != sizeof(ff_resp) ||
5168c2ecf20Sopenharmony_ci	    memcmp(&ff_resp.id, &system_x_id, sizeof(system_x_id)))
5178c2ecf20Sopenharmony_ci		return -ENOENT;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	return ff_resp.num_instances;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci/* Find and initialize one AEM1 instance */
5238c2ecf20Sopenharmony_cistatic int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct aem_data *data;
5268c2ecf20Sopenharmony_ci	int i;
5278c2ecf20Sopenharmony_ci	int res = -ENOMEM;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
5308c2ecf20Sopenharmony_ci	if (!data)
5318c2ecf20Sopenharmony_ci		return res;
5328c2ecf20Sopenharmony_ci	mutex_init(&data->lock);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Copy instance data */
5358c2ecf20Sopenharmony_ci	data->ver_major = 1;
5368c2ecf20Sopenharmony_ci	data->ver_minor = 0;
5378c2ecf20Sopenharmony_ci	data->module_handle = module_handle;
5388c2ecf20Sopenharmony_ci	for (i = 0; i < AEM1_NUM_ENERGY_REGS; i++)
5398c2ecf20Sopenharmony_ci		data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/* Create sub-device for this fw instance */
5428c2ecf20Sopenharmony_ci	data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
5438c2ecf20Sopenharmony_ci	if (data->id < 0)
5448c2ecf20Sopenharmony_ci		goto id_err;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	data->pdev = platform_device_alloc(DRVNAME, data->id);
5478c2ecf20Sopenharmony_ci	if (!data->pdev)
5488c2ecf20Sopenharmony_ci		goto dev_err;
5498c2ecf20Sopenharmony_ci	data->pdev->dev.driver = &aem_driver.driver;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	res = platform_device_add(data->pdev);
5528c2ecf20Sopenharmony_ci	if (res)
5538c2ecf20Sopenharmony_ci		goto dev_add_err;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	platform_set_drvdata(data->pdev, data);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* Set up IPMI interface */
5588c2ecf20Sopenharmony_ci	res = aem_init_ipmi_data(&data->ipmi, probe->interface,
5598c2ecf20Sopenharmony_ci				 probe->bmc_device);
5608c2ecf20Sopenharmony_ci	if (res)
5618c2ecf20Sopenharmony_ci		goto ipmi_err;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* Register with hwmon */
5648c2ecf20Sopenharmony_ci	data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
5658c2ecf20Sopenharmony_ci	if (IS_ERR(data->hwmon_dev)) {
5668c2ecf20Sopenharmony_ci		dev_err(&data->pdev->dev,
5678c2ecf20Sopenharmony_ci			"Unable to register hwmon device for IPMI interface %d\n",
5688c2ecf20Sopenharmony_ci			probe->interface);
5698c2ecf20Sopenharmony_ci		res = PTR_ERR(data->hwmon_dev);
5708c2ecf20Sopenharmony_ci		goto hwmon_reg_err;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	data->update = update_aem1_sensors;
5748c2ecf20Sopenharmony_ci	data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
5758c2ecf20Sopenharmony_ci	if (!data->rs_resp) {
5768c2ecf20Sopenharmony_ci		res = -ENOMEM;
5778c2ecf20Sopenharmony_ci		goto alloc_resp_err;
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/* Find sensors */
5818c2ecf20Sopenharmony_ci	res = aem1_find_sensors(data);
5828c2ecf20Sopenharmony_ci	if (res)
5838c2ecf20Sopenharmony_ci		goto sensor_err;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/* Add to our list of AEM devices */
5868c2ecf20Sopenharmony_ci	list_add_tail(&data->list, &driver_data.aem_devices);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	dev_info(data->ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
5898c2ecf20Sopenharmony_ci		 data->ver_major, data->ver_minor,
5908c2ecf20Sopenharmony_ci		 data->module_handle);
5918c2ecf20Sopenharmony_ci	return 0;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cisensor_err:
5948c2ecf20Sopenharmony_ci	kfree(data->rs_resp);
5958c2ecf20Sopenharmony_cialloc_resp_err:
5968c2ecf20Sopenharmony_ci	hwmon_device_unregister(data->hwmon_dev);
5978c2ecf20Sopenharmony_cihwmon_reg_err:
5988c2ecf20Sopenharmony_ci	ipmi_destroy_user(data->ipmi.user);
5998c2ecf20Sopenharmony_ciipmi_err:
6008c2ecf20Sopenharmony_ci	platform_set_drvdata(data->pdev, NULL);
6018c2ecf20Sopenharmony_ci	platform_device_del(data->pdev);
6028c2ecf20Sopenharmony_cidev_add_err:
6038c2ecf20Sopenharmony_ci	platform_device_put(data->pdev);
6048c2ecf20Sopenharmony_cidev_err:
6058c2ecf20Sopenharmony_ci	ida_simple_remove(&aem_ida, data->id);
6068c2ecf20Sopenharmony_ciid_err:
6078c2ecf20Sopenharmony_ci	kfree(data);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	return res;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/* Find and initialize all AEM1 instances */
6138c2ecf20Sopenharmony_cistatic void aem_init_aem1(struct aem_ipmi_data *probe)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	int num, i, err;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	num = aem_find_aem1_count(probe);
6188c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
6198c2ecf20Sopenharmony_ci		err = aem_init_aem1_inst(probe, i);
6208c2ecf20Sopenharmony_ci		if (err) {
6218c2ecf20Sopenharmony_ci			dev_err(probe->bmc_device,
6228c2ecf20Sopenharmony_ci				"Error %d initializing AEM1 0x%X\n",
6238c2ecf20Sopenharmony_ci				err, i);
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/* Probe functions for AEM2 devices */
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/* Retrieve version and module handle for an AEM2 instance */
6318c2ecf20Sopenharmony_cistatic int aem_find_aem2(struct aem_ipmi_data *data,
6328c2ecf20Sopenharmony_ci			    struct aem_find_instance_resp *fi_resp,
6338c2ecf20Sopenharmony_ci			    int instance_num)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	int res;
6368c2ecf20Sopenharmony_ci	struct aem_find_instance_req fi_req;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	fi_req.id = system_x_id;
6398c2ecf20Sopenharmony_ci	fi_req.instance_number = instance_num;
6408c2ecf20Sopenharmony_ci	fi_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	data->tx_message.cmd = AEM_FW_INSTANCE_CMD;
6438c2ecf20Sopenharmony_ci	data->tx_message.data = (char *)&fi_req;
6448c2ecf20Sopenharmony_ci	data->tx_message.data_len = sizeof(fi_req);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	data->rx_msg_data = fi_resp;
6478c2ecf20Sopenharmony_ci	data->rx_msg_len = sizeof(*fi_resp);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	aem_send_message(data);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT);
6528c2ecf20Sopenharmony_ci	if (!res)
6538c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (data->rx_result || data->rx_msg_len != sizeof(*fi_resp) ||
6568c2ecf20Sopenharmony_ci	    memcmp(&fi_resp->id, &system_x_id, sizeof(system_x_id)) ||
6578c2ecf20Sopenharmony_ci	    fi_resp->num_instances <= instance_num)
6588c2ecf20Sopenharmony_ci		return -ENOENT;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	return 0;
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci/* Find and initialize one AEM2 instance */
6648c2ecf20Sopenharmony_cistatic int aem_init_aem2_inst(struct aem_ipmi_data *probe,
6658c2ecf20Sopenharmony_ci			      struct aem_find_instance_resp *fi_resp)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct aem_data *data;
6688c2ecf20Sopenharmony_ci	int i;
6698c2ecf20Sopenharmony_ci	int res = -ENOMEM;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
6728c2ecf20Sopenharmony_ci	if (!data)
6738c2ecf20Sopenharmony_ci		return res;
6748c2ecf20Sopenharmony_ci	mutex_init(&data->lock);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* Copy instance data */
6778c2ecf20Sopenharmony_ci	data->ver_major = fi_resp->major;
6788c2ecf20Sopenharmony_ci	data->ver_minor = fi_resp->minor;
6798c2ecf20Sopenharmony_ci	data->module_handle = fi_resp->module_handle;
6808c2ecf20Sopenharmony_ci	for (i = 0; i < AEM2_NUM_ENERGY_REGS; i++)
6818c2ecf20Sopenharmony_ci		data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/* Create sub-device for this fw instance */
6848c2ecf20Sopenharmony_ci	data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
6858c2ecf20Sopenharmony_ci	if (data->id < 0)
6868c2ecf20Sopenharmony_ci		goto id_err;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	data->pdev = platform_device_alloc(DRVNAME, data->id);
6898c2ecf20Sopenharmony_ci	if (!data->pdev)
6908c2ecf20Sopenharmony_ci		goto dev_err;
6918c2ecf20Sopenharmony_ci	data->pdev->dev.driver = &aem_driver.driver;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	res = platform_device_add(data->pdev);
6948c2ecf20Sopenharmony_ci	if (res)
6958c2ecf20Sopenharmony_ci		goto dev_add_err;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	platform_set_drvdata(data->pdev, data);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/* Set up IPMI interface */
7008c2ecf20Sopenharmony_ci	res = aem_init_ipmi_data(&data->ipmi, probe->interface,
7018c2ecf20Sopenharmony_ci				 probe->bmc_device);
7028c2ecf20Sopenharmony_ci	if (res)
7038c2ecf20Sopenharmony_ci		goto ipmi_err;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* Register with hwmon */
7068c2ecf20Sopenharmony_ci	data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
7078c2ecf20Sopenharmony_ci	if (IS_ERR(data->hwmon_dev)) {
7088c2ecf20Sopenharmony_ci		dev_err(&data->pdev->dev,
7098c2ecf20Sopenharmony_ci			"Unable to register hwmon device for IPMI interface %d\n",
7108c2ecf20Sopenharmony_ci			probe->interface);
7118c2ecf20Sopenharmony_ci		res = PTR_ERR(data->hwmon_dev);
7128c2ecf20Sopenharmony_ci		goto hwmon_reg_err;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	data->update = update_aem2_sensors;
7168c2ecf20Sopenharmony_ci	data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL);
7178c2ecf20Sopenharmony_ci	if (!data->rs_resp) {
7188c2ecf20Sopenharmony_ci		res = -ENOMEM;
7198c2ecf20Sopenharmony_ci		goto alloc_resp_err;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* Find sensors */
7238c2ecf20Sopenharmony_ci	res = aem2_find_sensors(data);
7248c2ecf20Sopenharmony_ci	if (res)
7258c2ecf20Sopenharmony_ci		goto sensor_err;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* Add to our list of AEM devices */
7288c2ecf20Sopenharmony_ci	list_add_tail(&data->list, &driver_data.aem_devices);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	dev_info(data->ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
7318c2ecf20Sopenharmony_ci		 data->ver_major, data->ver_minor,
7328c2ecf20Sopenharmony_ci		 data->module_handle);
7338c2ecf20Sopenharmony_ci	return 0;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cisensor_err:
7368c2ecf20Sopenharmony_ci	kfree(data->rs_resp);
7378c2ecf20Sopenharmony_cialloc_resp_err:
7388c2ecf20Sopenharmony_ci	hwmon_device_unregister(data->hwmon_dev);
7398c2ecf20Sopenharmony_cihwmon_reg_err:
7408c2ecf20Sopenharmony_ci	ipmi_destroy_user(data->ipmi.user);
7418c2ecf20Sopenharmony_ciipmi_err:
7428c2ecf20Sopenharmony_ci	platform_set_drvdata(data->pdev, NULL);
7438c2ecf20Sopenharmony_ci	platform_device_del(data->pdev);
7448c2ecf20Sopenharmony_cidev_add_err:
7458c2ecf20Sopenharmony_ci	platform_device_put(data->pdev);
7468c2ecf20Sopenharmony_cidev_err:
7478c2ecf20Sopenharmony_ci	ida_simple_remove(&aem_ida, data->id);
7488c2ecf20Sopenharmony_ciid_err:
7498c2ecf20Sopenharmony_ci	kfree(data);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	return res;
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci/* Find and initialize all AEM2 instances */
7558c2ecf20Sopenharmony_cistatic void aem_init_aem2(struct aem_ipmi_data *probe)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	struct aem_find_instance_resp fi_resp;
7588c2ecf20Sopenharmony_ci	int err;
7598c2ecf20Sopenharmony_ci	int i = 0;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	while (!aem_find_aem2(probe, &fi_resp, i)) {
7628c2ecf20Sopenharmony_ci		if (fi_resp.major != 2) {
7638c2ecf20Sopenharmony_ci			dev_err(probe->bmc_device,
7648c2ecf20Sopenharmony_ci				"Unknown AEM v%d; please report this to the maintainer.\n",
7658c2ecf20Sopenharmony_ci				fi_resp.major);
7668c2ecf20Sopenharmony_ci			i++;
7678c2ecf20Sopenharmony_ci			continue;
7688c2ecf20Sopenharmony_ci		}
7698c2ecf20Sopenharmony_ci		err = aem_init_aem2_inst(probe, &fi_resp);
7708c2ecf20Sopenharmony_ci		if (err) {
7718c2ecf20Sopenharmony_ci			dev_err(probe->bmc_device,
7728c2ecf20Sopenharmony_ci				"Error %d initializing AEM2 0x%X\n",
7738c2ecf20Sopenharmony_ci				err, fi_resp.module_handle);
7748c2ecf20Sopenharmony_ci		}
7758c2ecf20Sopenharmony_ci		i++;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci/* Probe a BMC for AEM firmware instances */
7808c2ecf20Sopenharmony_cistatic void aem_register_bmc(int iface, struct device *dev)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	struct aem_ipmi_data probe;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (aem_init_ipmi_data(&probe, iface, dev))
7858c2ecf20Sopenharmony_ci		return;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* Ignore probe errors; they won't cause problems */
7888c2ecf20Sopenharmony_ci	aem_init_aem1(&probe);
7898c2ecf20Sopenharmony_ci	aem_init_aem2(&probe);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	ipmi_destroy_user(probe.user);
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci/* Handle BMC deletion */
7958c2ecf20Sopenharmony_cistatic void aem_bmc_gone(int iface)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	struct aem_data *p1, *next1;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list)
8008c2ecf20Sopenharmony_ci		if (p1->ipmi.interface == iface)
8018c2ecf20Sopenharmony_ci			aem_delete(p1);
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/* sysfs support functions */
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci/* AEM device name */
8078c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *devattr,
8088c2ecf20Sopenharmony_ci			 char *buf)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	struct aem_data *data = dev_get_drvdata(dev);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return sprintf(buf, "%s%d\n", DRVNAME, data->ver_major);
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(name, name, 0);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci/* AEM device version */
8178c2ecf20Sopenharmony_cistatic ssize_t version_show(struct device *dev,
8188c2ecf20Sopenharmony_ci			    struct device_attribute *devattr, char *buf)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct aem_data *data = dev_get_drvdata(dev);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	return sprintf(buf, "%d.%d\n", data->ver_major, data->ver_minor);
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(version, version, 0);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci/* Display power use */
8278c2ecf20Sopenharmony_cistatic ssize_t aem_show_power(struct device *dev,
8288c2ecf20Sopenharmony_ci			      struct device_attribute *devattr,
8298c2ecf20Sopenharmony_ci			      char *buf)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8328c2ecf20Sopenharmony_ci	struct aem_data *data = dev_get_drvdata(dev);
8338c2ecf20Sopenharmony_ci	u64 before, after, delta, time;
8348c2ecf20Sopenharmony_ci	signed long leftover;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	mutex_lock(&data->lock);
8378c2ecf20Sopenharmony_ci	update_aem_energy_one(data, attr->index);
8388c2ecf20Sopenharmony_ci	time = ktime_get_ns();
8398c2ecf20Sopenharmony_ci	before = data->energy[attr->index];
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	leftover = schedule_timeout_interruptible(
8428c2ecf20Sopenharmony_ci			msecs_to_jiffies(data->power_period[attr->index])
8438c2ecf20Sopenharmony_ci		   );
8448c2ecf20Sopenharmony_ci	if (leftover) {
8458c2ecf20Sopenharmony_ci		mutex_unlock(&data->lock);
8468c2ecf20Sopenharmony_ci		return 0;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	update_aem_energy_one(data, attr->index);
8508c2ecf20Sopenharmony_ci	time = ktime_get_ns() - time;
8518c2ecf20Sopenharmony_ci	after = data->energy[attr->index];
8528c2ecf20Sopenharmony_ci	mutex_unlock(&data->lock);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	delta = (after - before) * UJ_PER_MJ;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	return sprintf(buf, "%llu\n",
8578c2ecf20Sopenharmony_ci		(unsigned long long)div64_u64(delta * NSEC_PER_SEC, time));
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci/* Display energy use */
8618c2ecf20Sopenharmony_cistatic ssize_t aem_show_energy(struct device *dev,
8628c2ecf20Sopenharmony_ci			       struct device_attribute *devattr,
8638c2ecf20Sopenharmony_ci			       char *buf)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8668c2ecf20Sopenharmony_ci	struct aem_data *a = dev_get_drvdata(dev);
8678c2ecf20Sopenharmony_ci	mutex_lock(&a->lock);
8688c2ecf20Sopenharmony_ci	update_aem_energy_one(a, attr->index);
8698c2ecf20Sopenharmony_ci	mutex_unlock(&a->lock);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return sprintf(buf, "%llu\n",
8728c2ecf20Sopenharmony_ci			(unsigned long long)a->energy[attr->index] * 1000);
8738c2ecf20Sopenharmony_ci}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci/* Display power interval registers */
8768c2ecf20Sopenharmony_cistatic ssize_t aem_show_power_period(struct device *dev,
8778c2ecf20Sopenharmony_ci				     struct device_attribute *devattr,
8788c2ecf20Sopenharmony_ci				     char *buf)
8798c2ecf20Sopenharmony_ci{
8808c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8818c2ecf20Sopenharmony_ci	struct aem_data *a = dev_get_drvdata(dev);
8828c2ecf20Sopenharmony_ci	a->update(a);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	return sprintf(buf, "%lu\n", a->power_period[attr->index]);
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci/* Set power interval registers */
8888c2ecf20Sopenharmony_cistatic ssize_t aem_set_power_period(struct device *dev,
8898c2ecf20Sopenharmony_ci				    struct device_attribute *devattr,
8908c2ecf20Sopenharmony_ci				    const char *buf, size_t count)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
8938c2ecf20Sopenharmony_ci	struct aem_data *a = dev_get_drvdata(dev);
8948c2ecf20Sopenharmony_ci	unsigned long temp;
8958c2ecf20Sopenharmony_ci	int res;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	res = kstrtoul(buf, 10, &temp);
8988c2ecf20Sopenharmony_ci	if (res)
8998c2ecf20Sopenharmony_ci		return res;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (temp < AEM_MIN_POWER_INTERVAL)
9028c2ecf20Sopenharmony_ci		return -EINVAL;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	mutex_lock(&a->lock);
9058c2ecf20Sopenharmony_ci	a->power_period[attr->index] = temp;
9068c2ecf20Sopenharmony_ci	mutex_unlock(&a->lock);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	return count;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci/* Discover sensors on an AEM device */
9128c2ecf20Sopenharmony_cistatic int aem_register_sensors(struct aem_data *data,
9138c2ecf20Sopenharmony_ci				const struct aem_ro_sensor_template *ro,
9148c2ecf20Sopenharmony_ci				const struct aem_rw_sensor_template *rw)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	struct device *dev = &data->pdev->dev;
9178c2ecf20Sopenharmony_ci	struct sensor_device_attribute *sensors = data->sensors;
9188c2ecf20Sopenharmony_ci	int err;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	/* Set up read-only sensors */
9218c2ecf20Sopenharmony_ci	while (ro->label) {
9228c2ecf20Sopenharmony_ci		sysfs_attr_init(&sensors->dev_attr.attr);
9238c2ecf20Sopenharmony_ci		sensors->dev_attr.attr.name = ro->label;
9248c2ecf20Sopenharmony_ci		sensors->dev_attr.attr.mode = 0444;
9258c2ecf20Sopenharmony_ci		sensors->dev_attr.show = ro->show;
9268c2ecf20Sopenharmony_ci		sensors->index = ro->index;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci		err = device_create_file(dev, &sensors->dev_attr);
9298c2ecf20Sopenharmony_ci		if (err) {
9308c2ecf20Sopenharmony_ci			sensors->dev_attr.attr.name = NULL;
9318c2ecf20Sopenharmony_ci			goto error;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci		sensors++;
9348c2ecf20Sopenharmony_ci		ro++;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	/* Set up read-write sensors */
9388c2ecf20Sopenharmony_ci	while (rw->label) {
9398c2ecf20Sopenharmony_ci		sysfs_attr_init(&sensors->dev_attr.attr);
9408c2ecf20Sopenharmony_ci		sensors->dev_attr.attr.name = rw->label;
9418c2ecf20Sopenharmony_ci		sensors->dev_attr.attr.mode = 0644;
9428c2ecf20Sopenharmony_ci		sensors->dev_attr.show = rw->show;
9438c2ecf20Sopenharmony_ci		sensors->dev_attr.store = rw->set;
9448c2ecf20Sopenharmony_ci		sensors->index = rw->index;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci		err = device_create_file(dev, &sensors->dev_attr);
9478c2ecf20Sopenharmony_ci		if (err) {
9488c2ecf20Sopenharmony_ci			sensors->dev_attr.attr.name = NULL;
9498c2ecf20Sopenharmony_ci			goto error;
9508c2ecf20Sopenharmony_ci		}
9518c2ecf20Sopenharmony_ci		sensors++;
9528c2ecf20Sopenharmony_ci		rw++;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	err = device_create_file(dev, &sensor_dev_attr_name.dev_attr);
9568c2ecf20Sopenharmony_ci	if (err)
9578c2ecf20Sopenharmony_ci		goto error;
9588c2ecf20Sopenharmony_ci	err = device_create_file(dev, &sensor_dev_attr_version.dev_attr);
9598c2ecf20Sopenharmony_ci	return err;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cierror:
9628c2ecf20Sopenharmony_ci	aem_remove_sensors(data);
9638c2ecf20Sopenharmony_ci	return err;
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci/* sysfs support functions for AEM2 sensors */
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci/* Display temperature use */
9698c2ecf20Sopenharmony_cistatic ssize_t aem2_show_temp(struct device *dev,
9708c2ecf20Sopenharmony_ci			      struct device_attribute *devattr,
9718c2ecf20Sopenharmony_ci			      char *buf)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
9748c2ecf20Sopenharmony_ci	struct aem_data *a = dev_get_drvdata(dev);
9758c2ecf20Sopenharmony_ci	a->update(a);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", a->temp[attr->index] * 1000);
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci/* Display power-capping registers */
9818c2ecf20Sopenharmony_cistatic ssize_t aem2_show_pcap_value(struct device *dev,
9828c2ecf20Sopenharmony_ci				    struct device_attribute *devattr,
9838c2ecf20Sopenharmony_ci				    char *buf)
9848c2ecf20Sopenharmony_ci{
9858c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
9868c2ecf20Sopenharmony_ci	struct aem_data *a = dev_get_drvdata(dev);
9878c2ecf20Sopenharmony_ci	a->update(a);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", a->pcap[attr->index] * 100000);
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci/* Remove sensors attached to an AEM device */
9938c2ecf20Sopenharmony_cistatic void aem_remove_sensors(struct aem_data *data)
9948c2ecf20Sopenharmony_ci{
9958c2ecf20Sopenharmony_ci	int i;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	for (i = 0; i < AEM_NUM_SENSORS; i++) {
9988c2ecf20Sopenharmony_ci		if (!data->sensors[i].dev_attr.attr.name)
9998c2ecf20Sopenharmony_ci			continue;
10008c2ecf20Sopenharmony_ci		device_remove_file(&data->pdev->dev,
10018c2ecf20Sopenharmony_ci				   &data->sensors[i].dev_attr);
10028c2ecf20Sopenharmony_ci	}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	device_remove_file(&data->pdev->dev,
10058c2ecf20Sopenharmony_ci			   &sensor_dev_attr_name.dev_attr);
10068c2ecf20Sopenharmony_ci	device_remove_file(&data->pdev->dev,
10078c2ecf20Sopenharmony_ci			   &sensor_dev_attr_version.dev_attr);
10088c2ecf20Sopenharmony_ci}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci/* Sensor probe functions */
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci/* Description of AEM1 sensors */
10138c2ecf20Sopenharmony_cistatic const struct aem_ro_sensor_template aem1_ro_sensors[] = {
10148c2ecf20Sopenharmony_ci{"energy1_input",  aem_show_energy, 0},
10158c2ecf20Sopenharmony_ci{"power1_average", aem_show_power,  0},
10168c2ecf20Sopenharmony_ci{NULL,		   NULL,	    0},
10178c2ecf20Sopenharmony_ci};
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic const struct aem_rw_sensor_template aem1_rw_sensors[] = {
10208c2ecf20Sopenharmony_ci{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
10218c2ecf20Sopenharmony_ci{NULL,			    NULL,                  NULL,                 0},
10228c2ecf20Sopenharmony_ci};
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci/* Description of AEM2 sensors */
10258c2ecf20Sopenharmony_cistatic const struct aem_ro_sensor_template aem2_ro_sensors[] = {
10268c2ecf20Sopenharmony_ci{"energy1_input",	  aem_show_energy,	0},
10278c2ecf20Sopenharmony_ci{"energy2_input",	  aem_show_energy,	1},
10288c2ecf20Sopenharmony_ci{"power1_average",	  aem_show_power,	0},
10298c2ecf20Sopenharmony_ci{"power2_average",	  aem_show_power,	1},
10308c2ecf20Sopenharmony_ci{"temp1_input",		  aem2_show_temp,	0},
10318c2ecf20Sopenharmony_ci{"temp2_input",		  aem2_show_temp,	1},
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci{"power4_average",	  aem2_show_pcap_value,	POWER_CAP_MAX_HOTPLUG},
10348c2ecf20Sopenharmony_ci{"power5_average",	  aem2_show_pcap_value,	POWER_CAP_MAX},
10358c2ecf20Sopenharmony_ci{"power6_average",	  aem2_show_pcap_value,	POWER_CAP_MIN_WARNING},
10368c2ecf20Sopenharmony_ci{"power7_average",	  aem2_show_pcap_value,	POWER_CAP_MIN},
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci{"power3_average",	  aem2_show_pcap_value,	POWER_AUX},
10398c2ecf20Sopenharmony_ci{"power_cap",		  aem2_show_pcap_value,	POWER_CAP},
10408c2ecf20Sopenharmony_ci{NULL,                    NULL,                 0},
10418c2ecf20Sopenharmony_ci};
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic const struct aem_rw_sensor_template aem2_rw_sensors[] = {
10448c2ecf20Sopenharmony_ci{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
10458c2ecf20Sopenharmony_ci{"power2_average_interval", aem_show_power_period, aem_set_power_period, 1},
10468c2ecf20Sopenharmony_ci{NULL,			    NULL,                  NULL,                 0},
10478c2ecf20Sopenharmony_ci};
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci/* Set up AEM1 sensor attrs */
10508c2ecf20Sopenharmony_cistatic int aem1_find_sensors(struct aem_data *data)
10518c2ecf20Sopenharmony_ci{
10528c2ecf20Sopenharmony_ci	return aem_register_sensors(data, aem1_ro_sensors, aem1_rw_sensors);
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci/* Set up AEM2 sensor attrs */
10568c2ecf20Sopenharmony_cistatic int aem2_find_sensors(struct aem_data *data)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	return aem_register_sensors(data, aem2_ro_sensors, aem2_rw_sensors);
10598c2ecf20Sopenharmony_ci}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci/* Module init/exit routines */
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic int __init aem_init(void)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	int res;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	res = driver_register(&aem_driver.driver);
10688c2ecf20Sopenharmony_ci	if (res) {
10698c2ecf20Sopenharmony_ci		pr_err("Can't register aem driver\n");
10708c2ecf20Sopenharmony_ci		return res;
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	res = ipmi_smi_watcher_register(&driver_data.bmc_events);
10748c2ecf20Sopenharmony_ci	if (res)
10758c2ecf20Sopenharmony_ci		goto ipmi_reg_err;
10768c2ecf20Sopenharmony_ci	return 0;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ciipmi_reg_err:
10798c2ecf20Sopenharmony_ci	driver_unregister(&aem_driver.driver);
10808c2ecf20Sopenharmony_ci	return res;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic void __exit aem_exit(void)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	struct aem_data *p1, *next1;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
10898c2ecf20Sopenharmony_ci	driver_unregister(&aem_driver.driver);
10908c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list)
10918c2ecf20Sopenharmony_ci		aem_delete(p1);
10928c2ecf20Sopenharmony_ci}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
10958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM AEM power/temp/energy sensor driver");
10968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cimodule_init(aem_init);
10998c2ecf20Sopenharmony_cimodule_exit(aem_exit);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*");
11028c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*");
11038c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
11048c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
11058c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
11068c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
11078c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:bvnIBM:*:pnIBMBladeHC10-*");
1108