inspur-ipsps.c (5870B)
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 32enum 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 43static 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 53static 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 sysfs_emit(buf, "%s\n", data); 74} 75 76static 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 sysfs_emit(buf, "%u.%02u%u-%u.%02u\n", 95 data[1], data[2]/* < 100 */, data[3]/*< 10*/, 96 data[4], data[5]/* < 100 */); 97} 98 99static 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 sysfs_emit(buf, "[%s] %s %s\n", 115 MODE_ACTIVE_STRING, 116 MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 117 case MODE_STANDBY: 118 return sysfs_emit(buf, "%s [%s] %s\n", 119 MODE_ACTIVE_STRING, 120 MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 121 case MODE_REDUNDANCY: 122 return sysfs_emit(buf, "%s %s [%s]\n", 123 MODE_ACTIVE_STRING, 124 MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 125 default: 126 return sysfs_emit(buf, "unspecified\n"); 127 } 128} 129 130static 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 157static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); 158static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); 159static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); 160static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); 161static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); 162static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); 163static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); 164 165static 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 176ATTRIBUTE_GROUPS(ipsps); 177 178static 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 189static struct pmbus_platform_data ipsps_pdata = { 190 .flags = PMBUS_SKIP_STATUS_CHECK, 191}; 192 193static int ipsps_probe(struct i2c_client *client) 194{ 195 client->dev.platform_data = &ipsps_pdata; 196 return pmbus_do_probe(client, &ipsps_info); 197} 198 199static const struct i2c_device_id ipsps_id[] = { 200 { "ipsps1", 0 }, 201 {} 202}; 203MODULE_DEVICE_TABLE(i2c, ipsps_id); 204 205#ifdef CONFIG_OF 206static const struct of_device_id ipsps_of_match[] = { 207 { .compatible = "inspur,ipsps1" }, 208 {} 209}; 210MODULE_DEVICE_TABLE(of, ipsps_of_match); 211#endif 212 213static struct i2c_driver ipsps_driver = { 214 .driver = { 215 .name = "inspur-ipsps", 216 .of_match_table = of_match_ptr(ipsps_of_match), 217 }, 218 .probe_new = ipsps_probe, 219 .id_table = ipsps_id, 220}; 221 222module_i2c_driver(ipsps_driver); 223 224MODULE_AUTHOR("John Wang"); 225MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); 226MODULE_LICENSE("GPL"); 227MODULE_IMPORT_NS(PMBUS);