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