Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: pmbus: Add Inspur Power System power supply driver

Add the driver to monitor Inspur Power System power supplies
with hwmon over pmbus.

This driver adds sysfs attributes for additional power supply data,
including vendor, model, part_number, serial number,
firmware revision, hardware revision, and psu mode(active/standby).

Signed-off-by: John Wang <wangzqbj@inspur.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Link: https://lore.kernel.org/r/20190819091509.29276-1-wangzqbj@inspur.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

John Wang and committed by
Guenter Roeck
edd2a4d6 06a1c69c

+317
+79
Documentation/hwmon/inspur-ipsps1.rst
··· 1 + Kernel driver inspur-ipsps1 2 + ======================= 3 + 4 + Supported chips: 5 + 6 + * Inspur Power System power supply unit 7 + 8 + Author: John Wang <wangzqbj@inspur.com> 9 + 10 + Description 11 + ----------- 12 + 13 + This driver supports Inspur Power System power supplies. This driver 14 + is a client to the core PMBus driver. 15 + 16 + Usage Notes 17 + ----------- 18 + 19 + This driver does not auto-detect devices. You will have to instantiate the 20 + devices explicitly. Please see Documentation/i2c/instantiating-devices for 21 + details. 22 + 23 + Sysfs entries 24 + ------------- 25 + 26 + The following attributes are supported: 27 + 28 + ======================= ====================================================== 29 + curr1_input Measured input current 30 + curr1_label "iin" 31 + curr1_max Maximum current 32 + curr1_max_alarm Current high alarm 33 + curr2_input Measured output current in mA. 34 + curr2_label "iout1" 35 + curr2_crit Critical maximum current 36 + curr2_crit_alarm Current critical high alarm 37 + curr2_max Maximum current 38 + curr2_max_alarm Current high alarm 39 + 40 + fan1_alarm Fan 1 warning. 41 + fan1_fault Fan 1 fault. 42 + fan1_input Fan 1 speed in RPM. 43 + 44 + in1_alarm Input voltage under-voltage alarm. 45 + in1_input Measured input voltage in mV. 46 + in1_label "vin" 47 + in2_input Measured output voltage in mV. 48 + in2_label "vout1" 49 + in2_lcrit Critical minimum output voltage 50 + in2_lcrit_alarm Output voltage critical low alarm 51 + in2_max Maximum output voltage 52 + in2_max_alarm Output voltage high alarm 53 + in2_min Minimum output voltage 54 + in2_min_alarm Output voltage low alarm 55 + 56 + power1_alarm Input fault or alarm. 57 + power1_input Measured input power in uW. 58 + power1_label "pin" 59 + power1_max Input power limit 60 + power2_max_alarm Output power high alarm 61 + power2_max Output power limit 62 + power2_input Measured output power in uW. 63 + power2_label "pout" 64 + 65 + temp[1-3]_input Measured temperature 66 + temp[1-2]_max Maximum temperature 67 + temp[1-3]_max_alarm Temperature high alarm 68 + 69 + vendor Manufacturer name 70 + model Product model 71 + part_number Product part number 72 + serial_number Product serial number 73 + fw_version Firmware version 74 + hw_version Hardware version 75 + mode Work mode. Can be set to active or 76 + standby, when set to standby, PSU will 77 + automatically switch between standby 78 + and redundancy mode. 79 + ======================= ======================================================
+9
drivers/hwmon/pmbus/Kconfig
··· 46 46 This driver can also be built as a module. If so, the module will 47 47 be called ibm-cffps. 48 48 49 + config SENSORS_INSPUR_IPSPS 50 + tristate "INSPUR Power System Power Supply" 51 + help 52 + If you say yes here you get hardware monitoring support for the INSPUR 53 + Power System power supply. 54 + 55 + This driver can also be built as a module. If so, the module will 56 + be called inspur-ipsps. 57 + 49 58 config SENSORS_IR35221 50 59 tristate "Infineon IR35221" 51 60 help
+1
drivers/hwmon/pmbus/Makefile
··· 7 7 obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o 8 8 obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o 9 9 obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o 10 + obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o 10 11 obj-$(CONFIG_SENSORS_IR35221) += ir35221.o 11 12 obj-$(CONFIG_SENSORS_IR38064) += ir38064.o 12 13 obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
+228
drivers/hwmon/pmbus/inspur-ipsps.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright 2019 Inspur Corp. 4 + */ 5 + 6 + #include <linux/debugfs.h> 7 + #include <linux/device.h> 8 + #include <linux/fs.h> 9 + #include <linux/i2c.h> 10 + #include <linux/module.h> 11 + #include <linux/pmbus.h> 12 + #include <linux/hwmon-sysfs.h> 13 + 14 + #include "pmbus.h" 15 + 16 + #define IPSPS_REG_VENDOR_ID 0x99 17 + #define IPSPS_REG_MODEL 0x9A 18 + #define IPSPS_REG_FW_VERSION 0x9B 19 + #define IPSPS_REG_PN 0x9C 20 + #define IPSPS_REG_SN 0x9E 21 + #define IPSPS_REG_HW_VERSION 0xB0 22 + #define IPSPS_REG_MODE 0xFC 23 + 24 + #define MODE_ACTIVE 0x55 25 + #define MODE_STANDBY 0x0E 26 + #define MODE_REDUNDANCY 0x00 27 + 28 + #define MODE_ACTIVE_STRING "active" 29 + #define MODE_STANDBY_STRING "standby" 30 + #define MODE_REDUNDANCY_STRING "redundancy" 31 + 32 + enum ipsps_index { 33 + vendor, 34 + model, 35 + fw_version, 36 + part_number, 37 + serial_number, 38 + hw_version, 39 + mode, 40 + num_regs, 41 + }; 42 + 43 + static const u8 ipsps_regs[num_regs] = { 44 + [vendor] = IPSPS_REG_VENDOR_ID, 45 + [model] = IPSPS_REG_MODEL, 46 + [fw_version] = IPSPS_REG_FW_VERSION, 47 + [part_number] = IPSPS_REG_PN, 48 + [serial_number] = IPSPS_REG_SN, 49 + [hw_version] = IPSPS_REG_HW_VERSION, 50 + [mode] = IPSPS_REG_MODE, 51 + }; 52 + 53 + static ssize_t ipsps_string_show(struct device *dev, 54 + struct device_attribute *devattr, 55 + char *buf) 56 + { 57 + u8 reg; 58 + int rc; 59 + char *p; 60 + char data[I2C_SMBUS_BLOCK_MAX + 1]; 61 + struct i2c_client *client = to_i2c_client(dev->parent); 62 + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 63 + 64 + reg = ipsps_regs[attr->index]; 65 + rc = i2c_smbus_read_block_data(client, reg, data); 66 + if (rc < 0) 67 + return rc; 68 + 69 + /* filled with printable characters, ending with # */ 70 + p = memscan(data, '#', rc); 71 + *p = '\0'; 72 + 73 + return snprintf(buf, PAGE_SIZE, "%s\n", data); 74 + } 75 + 76 + static ssize_t ipsps_fw_version_show(struct device *dev, 77 + struct device_attribute *devattr, 78 + char *buf) 79 + { 80 + u8 reg; 81 + int rc; 82 + u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 }; 83 + struct i2c_client *client = to_i2c_client(dev->parent); 84 + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 85 + 86 + reg = ipsps_regs[attr->index]; 87 + rc = i2c_smbus_read_block_data(client, reg, data); 88 + if (rc < 0) 89 + return rc; 90 + 91 + if (rc != 6) 92 + return -EPROTO; 93 + 94 + return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n", 95 + data[1], data[2]/* < 100 */, data[3]/*< 10*/, 96 + data[4], data[5]/* < 100 */); 97 + } 98 + 99 + static ssize_t ipsps_mode_show(struct device *dev, 100 + struct device_attribute *devattr, char *buf) 101 + { 102 + u8 reg; 103 + int rc; 104 + struct i2c_client *client = to_i2c_client(dev->parent); 105 + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 106 + 107 + reg = ipsps_regs[attr->index]; 108 + rc = i2c_smbus_read_byte_data(client, reg); 109 + if (rc < 0) 110 + return rc; 111 + 112 + switch (rc) { 113 + case MODE_ACTIVE: 114 + return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n", 115 + MODE_ACTIVE_STRING, 116 + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 117 + case MODE_STANDBY: 118 + return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n", 119 + MODE_ACTIVE_STRING, 120 + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 121 + case MODE_REDUNDANCY: 122 + return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n", 123 + MODE_ACTIVE_STRING, 124 + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 125 + default: 126 + return snprintf(buf, PAGE_SIZE, "unspecified\n"); 127 + } 128 + } 129 + 130 + static ssize_t ipsps_mode_store(struct device *dev, 131 + struct device_attribute *devattr, 132 + const char *buf, size_t count) 133 + { 134 + u8 reg; 135 + int rc; 136 + struct i2c_client *client = to_i2c_client(dev->parent); 137 + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 138 + 139 + reg = ipsps_regs[attr->index]; 140 + if (sysfs_streq(MODE_STANDBY_STRING, buf)) { 141 + rc = i2c_smbus_write_byte_data(client, reg, 142 + MODE_STANDBY); 143 + if (rc < 0) 144 + return rc; 145 + return count; 146 + } else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) { 147 + rc = i2c_smbus_write_byte_data(client, reg, 148 + MODE_ACTIVE); 149 + if (rc < 0) 150 + return rc; 151 + return count; 152 + } 153 + 154 + return -EINVAL; 155 + } 156 + 157 + static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); 158 + static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); 159 + static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); 160 + static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); 161 + static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); 162 + static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); 163 + static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); 164 + 165 + static struct attribute *ipsps_attrs[] = { 166 + &sensor_dev_attr_vendor.dev_attr.attr, 167 + &sensor_dev_attr_model.dev_attr.attr, 168 + &sensor_dev_attr_part_number.dev_attr.attr, 169 + &sensor_dev_attr_serial_number.dev_attr.attr, 170 + &sensor_dev_attr_hw_version.dev_attr.attr, 171 + &sensor_dev_attr_fw_version.dev_attr.attr, 172 + &sensor_dev_attr_mode.dev_attr.attr, 173 + NULL, 174 + }; 175 + 176 + ATTRIBUTE_GROUPS(ipsps); 177 + 178 + static struct pmbus_driver_info ipsps_info = { 179 + .pages = 1, 180 + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | 181 + PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | 182 + PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | 183 + PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | 184 + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | 185 + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, 186 + .groups = ipsps_groups, 187 + }; 188 + 189 + static struct pmbus_platform_data ipsps_pdata = { 190 + .flags = PMBUS_SKIP_STATUS_CHECK, 191 + }; 192 + 193 + static int ipsps_probe(struct i2c_client *client, 194 + const struct i2c_device_id *id) 195 + { 196 + client->dev.platform_data = &ipsps_pdata; 197 + return pmbus_do_probe(client, id, &ipsps_info); 198 + } 199 + 200 + static const struct i2c_device_id ipsps_id[] = { 201 + { "ipsps1", 0 }, 202 + {} 203 + }; 204 + MODULE_DEVICE_TABLE(i2c, ipsps_id); 205 + 206 + #ifdef CONFIG_OF 207 + static const struct of_device_id ipsps_of_match[] = { 208 + { .compatible = "inspur,ipsps1" }, 209 + {} 210 + }; 211 + MODULE_DEVICE_TABLE(of, ipsps_of_match); 212 + #endif 213 + 214 + static struct i2c_driver ipsps_driver = { 215 + .driver = { 216 + .name = "inspur-ipsps", 217 + .of_match_table = of_match_ptr(ipsps_of_match), 218 + }, 219 + .probe = ipsps_probe, 220 + .remove = pmbus_do_remove, 221 + .id_table = ipsps_id, 222 + }; 223 + 224 + module_i2c_driver(ipsps_driver); 225 + 226 + MODULE_AUTHOR("John Wang"); 227 + MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); 228 + MODULE_LICENSE("GPL");