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"); 229