forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
5.7 KiB
227 lines
5.7 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Copyright 2019 Inspur Corp. |
|
*/ |
|
|
|
#include <linux/debugfs.h> |
|
#include <linux/device.h> |
|
#include <linux/fs.h> |
|
#include <linux/i2c.h> |
|
#include <linux/module.h> |
|
#include <linux/pmbus.h> |
|
#include <linux/hwmon-sysfs.h> |
|
|
|
#include "pmbus.h" |
|
|
|
#define IPSPS_REG_VENDOR_ID 0x99 |
|
#define IPSPS_REG_MODEL 0x9A |
|
#define IPSPS_REG_FW_VERSION 0x9B |
|
#define IPSPS_REG_PN 0x9C |
|
#define IPSPS_REG_SN 0x9E |
|
#define IPSPS_REG_HW_VERSION 0xB0 |
|
#define IPSPS_REG_MODE 0xFC |
|
|
|
#define MODE_ACTIVE 0x55 |
|
#define MODE_STANDBY 0x0E |
|
#define MODE_REDUNDANCY 0x00 |
|
|
|
#define MODE_ACTIVE_STRING "active" |
|
#define MODE_STANDBY_STRING "standby" |
|
#define MODE_REDUNDANCY_STRING "redundancy" |
|
|
|
enum ipsps_index { |
|
vendor, |
|
model, |
|
fw_version, |
|
part_number, |
|
serial_number, |
|
hw_version, |
|
mode, |
|
num_regs, |
|
}; |
|
|
|
static const u8 ipsps_regs[num_regs] = { |
|
[vendor] = IPSPS_REG_VENDOR_ID, |
|
[model] = IPSPS_REG_MODEL, |
|
[fw_version] = IPSPS_REG_FW_VERSION, |
|
[part_number] = IPSPS_REG_PN, |
|
[serial_number] = IPSPS_REG_SN, |
|
[hw_version] = IPSPS_REG_HW_VERSION, |
|
[mode] = IPSPS_REG_MODE, |
|
}; |
|
|
|
static ssize_t ipsps_string_show(struct device *dev, |
|
struct device_attribute *devattr, |
|
char *buf) |
|
{ |
|
u8 reg; |
|
int rc; |
|
char *p; |
|
char data[I2C_SMBUS_BLOCK_MAX + 1]; |
|
struct i2c_client *client = to_i2c_client(dev->parent); |
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
|
|
|
reg = ipsps_regs[attr->index]; |
|
rc = i2c_smbus_read_block_data(client, reg, data); |
|
if (rc < 0) |
|
return rc; |
|
|
|
/* filled with printable characters, ending with # */ |
|
p = memscan(data, '#', rc); |
|
*p = '\0'; |
|
|
|
return sysfs_emit(buf, "%s\n", data); |
|
} |
|
|
|
static ssize_t ipsps_fw_version_show(struct device *dev, |
|
struct device_attribute *devattr, |
|
char *buf) |
|
{ |
|
u8 reg; |
|
int rc; |
|
u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 }; |
|
struct i2c_client *client = to_i2c_client(dev->parent); |
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
|
|
|
reg = ipsps_regs[attr->index]; |
|
rc = i2c_smbus_read_block_data(client, reg, data); |
|
if (rc < 0) |
|
return rc; |
|
|
|
if (rc != 6) |
|
return -EPROTO; |
|
|
|
return sysfs_emit(buf, "%u.%02u%u-%u.%02u\n", |
|
data[1], data[2]/* < 100 */, data[3]/*< 10*/, |
|
data[4], data[5]/* < 100 */); |
|
} |
|
|
|
static ssize_t ipsps_mode_show(struct device *dev, |
|
struct device_attribute *devattr, char *buf) |
|
{ |
|
u8 reg; |
|
int rc; |
|
struct i2c_client *client = to_i2c_client(dev->parent); |
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
|
|
|
reg = ipsps_regs[attr->index]; |
|
rc = i2c_smbus_read_byte_data(client, reg); |
|
if (rc < 0) |
|
return rc; |
|
|
|
switch (rc) { |
|
case MODE_ACTIVE: |
|
return sysfs_emit(buf, "[%s] %s %s\n", |
|
MODE_ACTIVE_STRING, |
|
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); |
|
case MODE_STANDBY: |
|
return sysfs_emit(buf, "%s [%s] %s\n", |
|
MODE_ACTIVE_STRING, |
|
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); |
|
case MODE_REDUNDANCY: |
|
return sysfs_emit(buf, "%s %s [%s]\n", |
|
MODE_ACTIVE_STRING, |
|
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); |
|
default: |
|
return sysfs_emit(buf, "unspecified\n"); |
|
} |
|
} |
|
|
|
static ssize_t ipsps_mode_store(struct device *dev, |
|
struct device_attribute *devattr, |
|
const char *buf, size_t count) |
|
{ |
|
u8 reg; |
|
int rc; |
|
struct i2c_client *client = to_i2c_client(dev->parent); |
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
|
|
|
reg = ipsps_regs[attr->index]; |
|
if (sysfs_streq(MODE_STANDBY_STRING, buf)) { |
|
rc = i2c_smbus_write_byte_data(client, reg, |
|
MODE_STANDBY); |
|
if (rc < 0) |
|
return rc; |
|
return count; |
|
} else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) { |
|
rc = i2c_smbus_write_byte_data(client, reg, |
|
MODE_ACTIVE); |
|
if (rc < 0) |
|
return rc; |
|
return count; |
|
} |
|
|
|
return -EINVAL; |
|
} |
|
|
|
static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); |
|
static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); |
|
static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); |
|
static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); |
|
static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); |
|
static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); |
|
static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); |
|
|
|
static struct attribute *ipsps_attrs[] = { |
|
&sensor_dev_attr_vendor.dev_attr.attr, |
|
&sensor_dev_attr_model.dev_attr.attr, |
|
&sensor_dev_attr_part_number.dev_attr.attr, |
|
&sensor_dev_attr_serial_number.dev_attr.attr, |
|
&sensor_dev_attr_hw_version.dev_attr.attr, |
|
&sensor_dev_attr_fw_version.dev_attr.attr, |
|
&sensor_dev_attr_mode.dev_attr.attr, |
|
NULL, |
|
}; |
|
|
|
ATTRIBUTE_GROUPS(ipsps); |
|
|
|
static struct pmbus_driver_info ipsps_info = { |
|
.pages = 1, |
|
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | |
|
PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | |
|
PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | |
|
PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | |
|
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | |
|
PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, |
|
.groups = ipsps_groups, |
|
}; |
|
|
|
static struct pmbus_platform_data ipsps_pdata = { |
|
.flags = PMBUS_SKIP_STATUS_CHECK, |
|
}; |
|
|
|
static int ipsps_probe(struct i2c_client *client) |
|
{ |
|
client->dev.platform_data = &ipsps_pdata; |
|
return pmbus_do_probe(client, &ipsps_info); |
|
} |
|
|
|
static const struct i2c_device_id ipsps_id[] = { |
|
{ "ipsps1", 0 }, |
|
{} |
|
}; |
|
MODULE_DEVICE_TABLE(i2c, ipsps_id); |
|
|
|
#ifdef CONFIG_OF |
|
static const struct of_device_id ipsps_of_match[] = { |
|
{ .compatible = "inspur,ipsps1" }, |
|
{} |
|
}; |
|
MODULE_DEVICE_TABLE(of, ipsps_of_match); |
|
#endif |
|
|
|
static struct i2c_driver ipsps_driver = { |
|
.driver = { |
|
.name = "inspur-ipsps", |
|
.of_match_table = of_match_ptr(ipsps_of_match), |
|
}, |
|
.probe_new = ipsps_probe, |
|
.id_table = ipsps_id, |
|
}; |
|
|
|
module_i2c_driver(ipsps_driver); |
|
|
|
MODULE_AUTHOR("John Wang"); |
|
MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); |
|
MODULE_LICENSE("GPL"); |
|
MODULE_IMPORT_NS(PMBUS);
|
|
|