hwmon: (pmbus) Move pec attribute to I2C device
authorGuenter Roeck <linux@roeck-us.net>
Fri, 20 May 2022 18:47:21 +0000 (11:47 -0700)
committerGuenter Roeck <linux@roeck-us.net>
Wed, 13 Jul 2022 15:38:17 +0000 (08:38 -0700)
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>
Documentation/ABI/testing/sysfs-class-hwmon
Documentation/hwmon/pmbus-core.rst
drivers/hwmon/pmbus/pmbus_core.c

index 653d4c7..7271781 100644 (file)
@@ -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
index e7e0c9e..84c5a4e 100644 (file)
@@ -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
 =============
 
index 0291202..e670b86 100644 (file)
@@ -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)) {