162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2019 Inspur Corp. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/debugfs.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/pmbus.h> 1262306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "pmbus.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define IPSPS_REG_VENDOR_ID 0x99 1762306a36Sopenharmony_ci#define IPSPS_REG_MODEL 0x9A 1862306a36Sopenharmony_ci#define IPSPS_REG_FW_VERSION 0x9B 1962306a36Sopenharmony_ci#define IPSPS_REG_PN 0x9C 2062306a36Sopenharmony_ci#define IPSPS_REG_SN 0x9E 2162306a36Sopenharmony_ci#define IPSPS_REG_HW_VERSION 0xB0 2262306a36Sopenharmony_ci#define IPSPS_REG_MODE 0xFC 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MODE_ACTIVE 0x55 2562306a36Sopenharmony_ci#define MODE_STANDBY 0x0E 2662306a36Sopenharmony_ci#define MODE_REDUNDANCY 0x00 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define MODE_ACTIVE_STRING "active" 2962306a36Sopenharmony_ci#define MODE_STANDBY_STRING "standby" 3062306a36Sopenharmony_ci#define MODE_REDUNDANCY_STRING "redundancy" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cienum ipsps_index { 3362306a36Sopenharmony_ci vendor, 3462306a36Sopenharmony_ci model, 3562306a36Sopenharmony_ci fw_version, 3662306a36Sopenharmony_ci part_number, 3762306a36Sopenharmony_ci serial_number, 3862306a36Sopenharmony_ci hw_version, 3962306a36Sopenharmony_ci mode, 4062306a36Sopenharmony_ci num_regs, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const u8 ipsps_regs[num_regs] = { 4462306a36Sopenharmony_ci [vendor] = IPSPS_REG_VENDOR_ID, 4562306a36Sopenharmony_ci [model] = IPSPS_REG_MODEL, 4662306a36Sopenharmony_ci [fw_version] = IPSPS_REG_FW_VERSION, 4762306a36Sopenharmony_ci [part_number] = IPSPS_REG_PN, 4862306a36Sopenharmony_ci [serial_number] = IPSPS_REG_SN, 4962306a36Sopenharmony_ci [hw_version] = IPSPS_REG_HW_VERSION, 5062306a36Sopenharmony_ci [mode] = IPSPS_REG_MODE, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic ssize_t ipsps_string_show(struct device *dev, 5462306a36Sopenharmony_ci struct device_attribute *devattr, 5562306a36Sopenharmony_ci char *buf) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci u8 reg; 5862306a36Sopenharmony_ci int rc; 5962306a36Sopenharmony_ci char *p; 6062306a36Sopenharmony_ci char data[I2C_SMBUS_BLOCK_MAX + 1]; 6162306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 6262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci reg = ipsps_regs[attr->index]; 6562306a36Sopenharmony_ci rc = i2c_smbus_read_block_data(client, reg, data); 6662306a36Sopenharmony_ci if (rc < 0) 6762306a36Sopenharmony_ci return rc; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* filled with printable characters, ending with # */ 7062306a36Sopenharmony_ci p = memscan(data, '#', rc); 7162306a36Sopenharmony_ci *p = '\0'; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", data); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic ssize_t ipsps_fw_version_show(struct device *dev, 7762306a36Sopenharmony_ci struct device_attribute *devattr, 7862306a36Sopenharmony_ci char *buf) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci u8 reg; 8162306a36Sopenharmony_ci int rc; 8262306a36Sopenharmony_ci u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 }; 8362306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 8462306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci reg = ipsps_regs[attr->index]; 8762306a36Sopenharmony_ci rc = i2c_smbus_read_block_data(client, reg, data); 8862306a36Sopenharmony_ci if (rc < 0) 8962306a36Sopenharmony_ci return rc; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (rc != 6) 9262306a36Sopenharmony_ci return -EPROTO; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return sysfs_emit(buf, "%u.%02u%u-%u.%02u\n", 9562306a36Sopenharmony_ci data[1], data[2]/* < 100 */, data[3]/*< 10*/, 9662306a36Sopenharmony_ci data[4], data[5]/* < 100 */); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic ssize_t ipsps_mode_show(struct device *dev, 10062306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci u8 reg; 10362306a36Sopenharmony_ci int rc; 10462306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 10562306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci reg = ipsps_regs[attr->index]; 10862306a36Sopenharmony_ci rc = i2c_smbus_read_byte_data(client, reg); 10962306a36Sopenharmony_ci if (rc < 0) 11062306a36Sopenharmony_ci return rc; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci switch (rc) { 11362306a36Sopenharmony_ci case MODE_ACTIVE: 11462306a36Sopenharmony_ci return sysfs_emit(buf, "[%s] %s %s\n", 11562306a36Sopenharmony_ci MODE_ACTIVE_STRING, 11662306a36Sopenharmony_ci MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 11762306a36Sopenharmony_ci case MODE_STANDBY: 11862306a36Sopenharmony_ci return sysfs_emit(buf, "%s [%s] %s\n", 11962306a36Sopenharmony_ci MODE_ACTIVE_STRING, 12062306a36Sopenharmony_ci MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 12162306a36Sopenharmony_ci case MODE_REDUNDANCY: 12262306a36Sopenharmony_ci return sysfs_emit(buf, "%s %s [%s]\n", 12362306a36Sopenharmony_ci MODE_ACTIVE_STRING, 12462306a36Sopenharmony_ci MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); 12562306a36Sopenharmony_ci default: 12662306a36Sopenharmony_ci return sysfs_emit(buf, "unspecified\n"); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic ssize_t ipsps_mode_store(struct device *dev, 13162306a36Sopenharmony_ci struct device_attribute *devattr, 13262306a36Sopenharmony_ci const char *buf, size_t count) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u8 reg; 13562306a36Sopenharmony_ci int rc; 13662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev->parent); 13762306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci reg = ipsps_regs[attr->index]; 14062306a36Sopenharmony_ci if (sysfs_streq(MODE_STANDBY_STRING, buf)) { 14162306a36Sopenharmony_ci rc = i2c_smbus_write_byte_data(client, reg, 14262306a36Sopenharmony_ci MODE_STANDBY); 14362306a36Sopenharmony_ci if (rc < 0) 14462306a36Sopenharmony_ci return rc; 14562306a36Sopenharmony_ci return count; 14662306a36Sopenharmony_ci } else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) { 14762306a36Sopenharmony_ci rc = i2c_smbus_write_byte_data(client, reg, 14862306a36Sopenharmony_ci MODE_ACTIVE); 14962306a36Sopenharmony_ci if (rc < 0) 15062306a36Sopenharmony_ci return rc; 15162306a36Sopenharmony_ci return count; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); 15862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); 15962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); 16062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); 16162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); 16262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); 16362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic struct attribute *ipsps_attrs[] = { 16662306a36Sopenharmony_ci &sensor_dev_attr_vendor.dev_attr.attr, 16762306a36Sopenharmony_ci &sensor_dev_attr_model.dev_attr.attr, 16862306a36Sopenharmony_ci &sensor_dev_attr_part_number.dev_attr.attr, 16962306a36Sopenharmony_ci &sensor_dev_attr_serial_number.dev_attr.attr, 17062306a36Sopenharmony_ci &sensor_dev_attr_hw_version.dev_attr.attr, 17162306a36Sopenharmony_ci &sensor_dev_attr_fw_version.dev_attr.attr, 17262306a36Sopenharmony_ci &sensor_dev_attr_mode.dev_attr.attr, 17362306a36Sopenharmony_ci NULL, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciATTRIBUTE_GROUPS(ipsps); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct pmbus_driver_info ipsps_info = { 17962306a36Sopenharmony_ci .pages = 1, 18062306a36Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | 18162306a36Sopenharmony_ci PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | 18262306a36Sopenharmony_ci PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | 18362306a36Sopenharmony_ci PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | 18462306a36Sopenharmony_ci PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | 18562306a36Sopenharmony_ci PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, 18662306a36Sopenharmony_ci .groups = ipsps_groups, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic struct pmbus_platform_data ipsps_pdata = { 19062306a36Sopenharmony_ci .flags = PMBUS_SKIP_STATUS_CHECK, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int ipsps_probe(struct i2c_client *client) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci client->dev.platform_data = &ipsps_pdata; 19662306a36Sopenharmony_ci return pmbus_do_probe(client, &ipsps_info); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct i2c_device_id ipsps_id[] = { 20062306a36Sopenharmony_ci { "ipsps1", 0 }, 20162306a36Sopenharmony_ci {} 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ipsps_id); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci#ifdef CONFIG_OF 20662306a36Sopenharmony_cistatic const struct of_device_id ipsps_of_match[] = { 20762306a36Sopenharmony_ci { .compatible = "inspur,ipsps1" }, 20862306a36Sopenharmony_ci {} 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ipsps_of_match); 21162306a36Sopenharmony_ci#endif 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic struct i2c_driver ipsps_driver = { 21462306a36Sopenharmony_ci .driver = { 21562306a36Sopenharmony_ci .name = "inspur-ipsps", 21662306a36Sopenharmony_ci .of_match_table = of_match_ptr(ipsps_of_match), 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci .probe = ipsps_probe, 21962306a36Sopenharmony_ci .id_table = ipsps_id, 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cimodule_i2c_driver(ipsps_driver); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ciMODULE_AUTHOR("John Wang"); 22562306a36Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); 22662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 22762306a36Sopenharmony_ciMODULE_IMPORT_NS(PMBUS); 228