18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2019 Inspur Corp. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/fs.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/pmbus.h> 128c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "pmbus.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define IPSPS_REG_VENDOR_ID 0x99 178c2ecf20Sopenharmony_ci#define IPSPS_REG_MODEL 0x9A 188c2ecf20Sopenharmony_ci#define IPSPS_REG_FW_VERSION 0x9B 198c2ecf20Sopenharmony_ci#define IPSPS_REG_PN 0x9C 208c2ecf20Sopenharmony_ci#define IPSPS_REG_SN 0x9E 218c2ecf20Sopenharmony_ci#define IPSPS_REG_HW_VERSION 0xB0 228c2ecf20Sopenharmony_ci#define IPSPS_REG_MODE 0xFC 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define MODE_ACTIVE 0x55 258c2ecf20Sopenharmony_ci#define MODE_STANDBY 0x0E 268c2ecf20Sopenharmony_ci#define MODE_REDUNDANCY 0x00 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define MODE_ACTIVE_STRING "active" 298c2ecf20Sopenharmony_ci#define MODE_STANDBY_STRING "standby" 308c2ecf20Sopenharmony_ci#define MODE_REDUNDANCY_STRING "redundancy" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cienum ipsps_index { 338c2ecf20Sopenharmony_ci vendor, 348c2ecf20Sopenharmony_ci model, 358c2ecf20Sopenharmony_ci fw_version, 368c2ecf20Sopenharmony_ci part_number, 378c2ecf20Sopenharmony_ci serial_number, 388c2ecf20Sopenharmony_ci hw_version, 398c2ecf20Sopenharmony_ci mode, 408c2ecf20Sopenharmony_ci num_regs, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const u8 ipsps_regs[num_regs] = { 448c2ecf20Sopenharmony_ci [vendor] = IPSPS_REG_VENDOR_ID, 458c2ecf20Sopenharmony_ci [model] = IPSPS_REG_MODEL, 468c2ecf20Sopenharmony_ci [fw_version] = IPSPS_REG_FW_VERSION, 478c2ecf20Sopenharmony_ci [part_number] = IPSPS_REG_PN, 488c2ecf20Sopenharmony_ci [serial_number] = IPSPS_REG_SN, 498c2ecf20Sopenharmony_ci [hw_version] = IPSPS_REG_HW_VERSION, 508c2ecf20Sopenharmony_ci [mode] = IPSPS_REG_MODE, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic ssize_t ipsps_string_show(struct device *dev, 548c2ecf20Sopenharmony_ci struct device_attribute *devattr, 558c2ecf20Sopenharmony_ci char *buf) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci u8 reg; 588c2ecf20Sopenharmony_ci int rc; 598c2ecf20Sopenharmony_ci char *p; 608c2ecf20Sopenharmony_ci char data[I2C_SMBUS_BLOCK_MAX + 1]; 618c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 628c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci reg = ipsps_regs[attr->index]; 658c2ecf20Sopenharmony_ci rc = i2c_smbus_read_block_data(client, reg, data); 668c2ecf20Sopenharmony_ci if (rc < 0) 678c2ecf20Sopenharmony_ci return rc; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* filled with printable characters, ending with # */ 708c2ecf20Sopenharmony_ci p = memscan(data, '#', rc); 718c2ecf20Sopenharmony_ci *p = '\0'; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", data); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic ssize_t ipsps_fw_version_show(struct device *dev, 778c2ecf20Sopenharmony_ci struct device_attribute *devattr, 788c2ecf20Sopenharmony_ci char *buf) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci u8 reg; 818c2ecf20Sopenharmony_ci int rc; 828c2ecf20Sopenharmony_ci u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 }; 838c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 848c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci reg = ipsps_regs[attr->index]; 878c2ecf20Sopenharmony_ci rc = i2c_smbus_read_block_data(client, reg, data); 888c2ecf20Sopenharmony_ci if (rc < 0) 898c2ecf20Sopenharmony_ci return rc; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (rc != 6) 928c2ecf20Sopenharmony_ci return -EPROTO; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n", 958c2ecf20Sopenharmony_ci data[1], data[2]/* < 100 */, data[3]/*< 10*/, 968c2ecf20Sopenharmony_ci data[4], data[5]/* < 100 */); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic ssize_t ipsps_mode_show(struct device *dev, 1008c2ecf20Sopenharmony_ci struct device_attribute *devattr, char *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci u8 reg; 1038c2ecf20Sopenharmony_ci int rc; 1048c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 1058c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci reg = ipsps_regs[attr->index]; 1088c2ecf20Sopenharmony_ci rc = i2c_smbus_read_byte_data(client, reg); 1098c2ecf20Sopenharmony_ci if (rc < 0) 1108c2ecf20Sopenharmony_ci return rc; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci switch (rc) { 1138c2ecf20Sopenharmony_ci case MODE_ACTIVE: 1148c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n", 1158c2ecf20Sopenharmony_ci MODE_ACTIVE_STRING, 1168c2ecf20Sopenharmony_ci MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 1178c2ecf20Sopenharmony_ci case MODE_STANDBY: 1188c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n", 1198c2ecf20Sopenharmony_ci MODE_ACTIVE_STRING, 1208c2ecf20Sopenharmony_ci MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 1218c2ecf20Sopenharmony_ci case MODE_REDUNDANCY: 1228c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n", 1238c2ecf20Sopenharmony_ci MODE_ACTIVE_STRING, 1248c2ecf20Sopenharmony_ci MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 1258c2ecf20Sopenharmony_ci default: 1268c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "unspecified\n"); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic ssize_t ipsps_mode_store(struct device *dev, 1318c2ecf20Sopenharmony_ci struct device_attribute *devattr, 1328c2ecf20Sopenharmony_ci const char *buf, size_t count) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u8 reg; 1358c2ecf20Sopenharmony_ci int rc; 1368c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 1378c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci reg = ipsps_regs[attr->index]; 1408c2ecf20Sopenharmony_ci if (sysfs_streq(MODE_STANDBY_STRING, buf)) { 1418c2ecf20Sopenharmony_ci rc = i2c_smbus_write_byte_data(client, reg, 1428c2ecf20Sopenharmony_ci MODE_STANDBY); 1438c2ecf20Sopenharmony_ci if (rc < 0) 1448c2ecf20Sopenharmony_ci return rc; 1458c2ecf20Sopenharmony_ci return count; 1468c2ecf20Sopenharmony_ci } else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) { 1478c2ecf20Sopenharmony_ci rc = i2c_smbus_write_byte_data(client, reg, 1488c2ecf20Sopenharmony_ci MODE_ACTIVE); 1498c2ecf20Sopenharmony_ci if (rc < 0) 1508c2ecf20Sopenharmony_ci return rc; 1518c2ecf20Sopenharmony_ci return count; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return -EINVAL; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); 1588c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); 1598c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); 1608c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); 1618c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); 1628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); 1638c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct attribute *ipsps_attrs[] = { 1668c2ecf20Sopenharmony_ci &sensor_dev_attr_vendor.dev_attr.attr, 1678c2ecf20Sopenharmony_ci &sensor_dev_attr_model.dev_attr.attr, 1688c2ecf20Sopenharmony_ci &sensor_dev_attr_part_number.dev_attr.attr, 1698c2ecf20Sopenharmony_ci &sensor_dev_attr_serial_number.dev_attr.attr, 1708c2ecf20Sopenharmony_ci &sensor_dev_attr_hw_version.dev_attr.attr, 1718c2ecf20Sopenharmony_ci &sensor_dev_attr_fw_version.dev_attr.attr, 1728c2ecf20Sopenharmony_ci &sensor_dev_attr_mode.dev_attr.attr, 1738c2ecf20Sopenharmony_ci NULL, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ipsps); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic struct pmbus_driver_info ipsps_info = { 1798c2ecf20Sopenharmony_ci .pages = 1, 1808c2ecf20Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | 1818c2ecf20Sopenharmony_ci PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | 1828c2ecf20Sopenharmony_ci PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | 1838c2ecf20Sopenharmony_ci PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | 1848c2ecf20Sopenharmony_ci PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | 1858c2ecf20Sopenharmony_ci PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, 1868c2ecf20Sopenharmony_ci .groups = ipsps_groups, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic struct pmbus_platform_data ipsps_pdata = { 1908c2ecf20Sopenharmony_ci .flags = PMBUS_SKIP_STATUS_CHECK, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int ipsps_probe(struct i2c_client *client) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci client->dev.platform_data = &ipsps_pdata; 1968c2ecf20Sopenharmony_ci return pmbus_do_probe(client, &ipsps_info); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic const struct i2c_device_id ipsps_id[] = { 2008c2ecf20Sopenharmony_ci { "ipsps1", 0 }, 2018c2ecf20Sopenharmony_ci {} 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ipsps_id); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2068c2ecf20Sopenharmony_cistatic const struct of_device_id ipsps_of_match[] = { 2078c2ecf20Sopenharmony_ci { .compatible = "inspur,ipsps1" }, 2088c2ecf20Sopenharmony_ci {} 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ipsps_of_match); 2118c2ecf20Sopenharmony_ci#endif 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic struct i2c_driver ipsps_driver = { 2148c2ecf20Sopenharmony_ci .driver = { 2158c2ecf20Sopenharmony_ci .name = "inspur-ipsps", 2168c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ipsps_of_match), 2178c2ecf20Sopenharmony_ci }, 2188c2ecf20Sopenharmony_ci .probe_new = ipsps_probe, 2198c2ecf20Sopenharmony_ci .remove = pmbus_do_remove, 2208c2ecf20Sopenharmony_ci .id_table = ipsps_id, 2218c2ecf20Sopenharmony_ci}; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cimodule_i2c_driver(ipsps_driver); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ciMODULE_AUTHOR("John Wang"); 2268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); 2278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 228