hwmon: (pmbus) Move pec attribute to I2C device

Enabling and disabling PEC for PMBus devices is currently only supported
with a debugfs attribute, which requires debugfs to be enabled and is
thus less than perfect. Take the lm90 driver as example and add a 'pec'
attribute to the I2C device if both the I2C adapter and the PMBus device
support it. Remove the now obsolete 'pec' attribute from debugfs.

Cc: Andrew Jeffery <andrew@aj.id.au>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Guenter Roeck 2022-05-20 11:47:21 -07:00
Родитель 88084a3df1
Коммит f30ce040a7
3 изменённых файлов: 68 добавлений и 39 удалений

Просмотреть файл

@ -938,3 +938,12 @@ Description:
- 1: enable
RW
What: /sys/class/hwmon/hwmonX/device/pec
Description:
PEC support on I2C devices
- 0, off, n: disable
- 1, on, y: enable
RW

Просмотреть файл

@ -121,6 +121,15 @@ Specifically, it provides the following information.
non-standard PMBus commands to standard commands, or to augment standard
command return values with device specific information.
PEC Support
===========
Many PMBus devices support SMBus PEC (Packet Error Checking). If supported
by both the I2C adapter and by the PMBus chip, it is by default enabled.
If PEC is supported, the PMBus core driver adds an attribute named 'pec' to
the I2C device. This attribute can be used to control PEC support in the
communication with the PMBus chip.
API functions
=============

Просмотреть файл

@ -2388,6 +2388,42 @@ static int pmbus_read_status_word(struct i2c_client *client, int page)
return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD);
}
/* PEC attribute support */
static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
}
static ssize_t pec_store(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
bool enable;
int err;
err = kstrtobool(buf, &enable);
if (err < 0)
return err;
if (enable)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return count;
}
static DEVICE_ATTR_RW(pec);
static void pmbus_remove_pec(void *dev)
{
device_remove_file(dev, &dev_attr_pec);
}
static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
struct pmbus_driver_info *info)
{
@ -2474,6 +2510,20 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
return ret;
}
if (client->flags & I2C_CLIENT_PEC) {
/*
* If I2C_CLIENT_PEC is set here, both the I2C adapter and the
* chip support PEC. Add 'pec' attribute to client device to let
* the user control it.
*/
ret = device_create_file(dev, &dev_attr_pec);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, pmbus_remove_pec, dev);
if (ret)
return ret;
}
return 0;
}
@ -2782,42 +2832,6 @@ static int pmbus_debugfs_get_status(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
NULL, "0x%04llx\n");
static int pmbus_debugfs_get_pec(void *data, u64 *val)
{
struct i2c_client *client = data;
*val = !!(client->flags & I2C_CLIENT_PEC);
return 0;
}
static int pmbus_debugfs_set_pec(void *data, u64 val)
{
int rc;
struct i2c_client *client = data;
if (!val) {
client->flags &= ~I2C_CLIENT_PEC;
return 0;
}
if (val != 1)
return -EINVAL;
rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
if (rc < 0)
return rc;
if (!(rc & PB_CAPABILITY_ERROR_CHECK))
return -EOPNOTSUPP;
client->flags |= I2C_CLIENT_PEC;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec,
pmbus_debugfs_set_pec, "%llu\n");
static void pmbus_remove_debugfs(void *data)
{
struct dentry *entry = data;
@ -2853,9 +2867,6 @@ static int pmbus_init_debugfs(struct i2c_client *client,
if (!entries)
return -ENOMEM;
debugfs_create_file("pec", 0664, data->debugfs, client,
&pmbus_debugfs_ops_pec);
for (i = 0; i < data->info->pages; ++i) {
/* Check accessibility of status register if it's not page 0 */
if (!i || pmbus_check_status_register(client, i)) {