Re: [PATCH v1 2/4] hwmon: (pmbus/crps) Add Intel CRPS185 power supply

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Guenter,

Thanks for the review!

On 12/12/24 19:29, Guenter Roeck wrote:
On 12/12/24 13:49, Ninad Palsule wrote:
Add the driver to monitor Intel common redundant power supply (crps185)
with hwmon over pmbus.

Signed-off-by: Ninad Palsule <ninad@xxxxxxxxxxxxx>
---
  Documentation/hwmon/crps.rst |  95 +++++++++++

Documentation/hwmon/index.rst needs to be updated.
Added the entry. Sorry for that.

  MAINTAINERS                  |   7 +
  drivers/hwmon/pmbus/Kconfig  |   9 ++
  drivers/hwmon/pmbus/Makefile |   1 +
  drivers/hwmon/pmbus/crps.c   | 299 +++++++++++++++++++++++++++++++++++
  5 files changed, 411 insertions(+)
  create mode 100644 Documentation/hwmon/crps.rst
  create mode 100644 drivers/hwmon/pmbus/crps.c

diff --git a/Documentation/hwmon/crps.rst b/Documentation/hwmon/crps.rst
new file mode 100644
index 000000000000..81d5dfd68a46
--- /dev/null
+++ b/Documentation/hwmon/crps.rst
@@ -0,0 +1,95 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver crps
+========================

I am quite sure that this triggers a documentation warning.
Fixed it.

+
+Supported chips:
+
+  * Intel CRPS185
+
+    Prefix: 'crps185'
+
+    Addresses scanned: -
+
+Authors:
+    Ninad Palsule <ninad@xxxxxxxxxxxxx>
+
+

Is the documentation available somewhere ?
If yes, please add a reference. If not, add a comment explaining that
it is not available to the public.
There is no public spec, added the comment

+Description
+-----------
+
+This driver implements support for Common Redundant Power supply with PMBus

For _Intel_ ...
Updated.

+support.
+
+The driver is a client driver to the core PMBus driver.
+Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
+details.
+
+
+Sysfs entries
+-------------
+
+======================= ======================================================
+curr1_label        "iin"
+curr1_input        Measured input current
+curr1_max        Maximum input current
+curr1_max_alarm         Input maximum current high alarm
+curr1_crit              Critial high input current
+curr1_crit_alarm        Input critical current high alarm
+curr1_rated_max        Maximum rated input current
+
+curr2_label        "iout1"
+curr2_input        Measured output current
+curr2_max        Maximum output current
+curr2_max_alarm         Output maximum current high alarm
+curr2_crit            Critial high output current
+curr2_crit_alarm        Output critical current high alarm
+curr2_rated_max        Maximum rated output current
+
+in1_label        "vin"
+in1_input        Measured input voltage
+in1_crit                Critical input over voltage
+in1_crit_alarm          Critical input over voltage alarm
+in1_max                 Maximum input over voltage
+in1_max_alarm           Maximum input over voltage alarm
+in1_rated_min        Minimum rated input voltage
+in1_rated_max        Maximum rated input voltage
+
+in2_label        "vout1"
+in2_input        Measured input voltage
+in2_crit                Critical input over voltage
+in2_crit_alarm          Critical input over voltage alarm
+in2_lcrit               Critical input under voltage fault
+in2_lcrit_alarm         Critical input under voltage fault alarm
+in2_max                 Maximum input over voltage
+in2_max_alarm           Maximum input over voltage alarm
+in2_min                 Minimum input under voltage warning
+in2_min_alarm           Minimum input under voltage warning alarm
+in2_rated_min        Minimum rated input voltage
+in2_rated_max        Maximum rated input voltage
+
+power1_label        "pin"
+power1_input        Measured input power
+power1_alarm        Input power high alarm
+power1_max          Maximum input power
+power1_rated_max        Maximum rated input power
+
+temp[1-2]_input        Measured temperature
+temp[1-2]_crit         Critical temperature
+temp[1-2]_crit_alarm    Critical temperature alarm
+temp[1-2]_max          Maximum temperature
+temp[1-2]_max_alarm     Maximum temperature alarm
+temp[1-2]_rated_max     Maximum rated temperature
+
+fan1_alarm        Fan 1 warning.
+fan1_fault        Fan 1 fault.
+fan1_input        Fan 1 speed in RPM.
+fan1_target             Fan 1 target.
+======================= ======================================================
diff --git a/MAINTAINERS b/MAINTAINERS
index 637ddd44245f..6b31d545f0f1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6100,6 +6100,13 @@ L:    linux-input@xxxxxxxxxxxxxxx
  S:    Maintained
  F:    drivers/hid/hid-creative-sb0540.c
  +CRPS COMMON REDUNDANT PSU DRIVER

This is _INTEL_ CRPS.
Updated.

+M:    Ninad Palsule <ninad@xxxxxxxxxxxxx>
+L:    linux-hwmon@xxxxxxxxxxxxxxx
+S:    Maintained
+F:    Documentation/hwmon/crps.rst
+F:    drivers/hwmon/pmbus/crps.c
+
  CRYPTO API
  M:    Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
  M:    "David S. Miller" <davem@xxxxxxxxxxxxx>
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 22418a05ced0..56c4eb4b846e 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -85,6 +85,15 @@ config SENSORS_BPA_RS600
        This driver can also be built as a module. If so, the module will
        be called bpa-rs600.
  +config SENSORS_CRPS
+    tristate "Common Redundant Power Supply"

Again, this is an Intel product.
Fixed.

+    help
+      If you say yes here you get hardware monitoring support for the Common
+      Redundant Power Supply.
+
+      This driver can also be built as a module. If so, the module will
+      be called crps.
+
  config SENSORS_DELTA_AHE50DC_FAN
      tristate "Delta AHE-50DC fan control module"
      help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 3d3183f8d2a7..c7eb7739b7f8 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_SENSORS_XDPE122)    += xdpe12284.o
  obj-$(CONFIG_SENSORS_XDPE152)    += xdpe152c4.o
  obj-$(CONFIG_SENSORS_ZL6100)    += zl6100.o
  obj-$(CONFIG_SENSORS_PIM4328)    += pim4328.o
+obj-$(CONFIG_SENSORS_CRPS)    += crps.o
diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c
new file mode 100644
index 000000000000..44d309f81803
--- /dev/null
+++ b/drivers/hwmon/pmbus/crps.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2024 IBM Corp.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/pmbus.h>
+
+#include "pmbus.h"
+
+/* Intel crps185 specific commands. */
+#define CRPS185_MFR_IOUT_MAX        0xA6
+#define CRPS185_MFR_POUT_MAX        0xA7
+

I fail to see the point in those defines, even more so since
PMBUS_MFR_POUT_MAX and PMBUS_MFR_IOUT_MAX are used below.
Removed these defines.

+enum {
+    CRPS_DEBUGFS_PMBUS_REVISION = 0,
+    CRPS_DEBUGFS_MAX_POWER_OUT,
+    CRPS_DEBUGFS_MAX_CURRENT_OUT,
+    CRPS_DEBUGFS_NUM_ENTRIES
+};
+
+enum models { crps185 = 1, crps_unknown };

Pointless enum. The driver only supports a single power supply.
Removed the enum.

+
+struct crps {
+    enum models version;
+    struct i2c_client *client;
+
+    int debugfs_entries[CRPS_DEBUGFS_NUM_ENTRIES];
+};
+
+#define to_psu(x, y) container_of((x), struct crps, debugfs_entries[(y)])
+
+static struct pmbus_platform_data crps_pdata = {
+    .flags = PMBUS_SKIP_STATUS_CHECK,
+};

Did you confirm that it is needed ? If so, explain why it is needed.
I removed it and still driver is working. I added it because spec did not mention about PMBUS_STATUS_CML

+
+static const struct i2c_device_id crps_id[] = {
+    { "intel_crps185", crps185 },
+    {}
+};
+MODULE_DEVICE_TABLE(i2c, crps_id);
+
+/*
+ * Convert linear format word to machine format. 11 LSB side bits are two's
+ * complement integer mantissa and 5 MSB side bits are two's complement
+ * exponent
+ */
+static int crps_convert_linear(int rc)
+{
+    s16 exponent;
+    s32 mantissa;
+    s64 val;
+
+    exponent = ((s16)rc) >> 11;
+    mantissa = ((s16)((rc & 0x7ff) << 5)) >> 5;
+
+    val = mantissa;
+    if (exponent >= 0)
+        val <<= exponent;
+    else
+        val >>= -exponent;
+
+    return (int)val;
+}
+
+static ssize_t crps_debugfs_read(struct file *file, char __user *buf,
+                 size_t count, loff_t *ppos)
+{
+    int rc;
+    int *idxp = file->private_data;
+    int idx = *idxp;
+    struct crps *psu = to_psu(idxp, idx);
+    char data[2 * I2C_SMBUS_BLOCK_MAX] = { 0 };
+
+    rc = pmbus_lock_interruptible(psu->client);
+    if (rc)
+        return rc;
+
+    rc = pmbus_set_page(psu->client, 0, 0xff);
+    if (rc)
+        goto unlock;
+
+    switch (idx) {
+    case CRPS_DEBUGFS_PMBUS_REVISION:
+        rc = i2c_smbus_read_byte_data(psu->client, PMBUS_REVISION);
+        if (rc >= 0) {
+            if (psu->version == crps185) {
+                if (rc == 0)
+                    rc = sprintf(data, "%s", "1.0");
+                else if (rc == 0x11)
+                    rc = sprintf(data, "%s", "1.1");
+                else if (rc == 0x22)
+                    rc = sprintf(data, "%s", "1.2");
+                else
+                    rc = snprintf(data, 3, "0x%02x", rc);
+            } else {
+                rc = snprintf(data, 3, "%02x", rc);
+            }
+        }

This attribute should be added into the PMBus core.

Moved the attribute in the core.



+        break;
+    case CRPS_DEBUGFS_MAX_POWER_OUT:
+        rc = i2c_smbus_read_word_data(psu->client, PMBUS_MFR_POUT_MAX);
+        if (rc >= 0) {
+            rc = crps_convert_linear(rc);
+            rc = snprintf(data, I2C_SMBUS_BLOCK_MAX, "%d", rc);
+        }
+        break;
+    case CRPS_DEBUGFS_MAX_CURRENT_OUT:
+        rc = i2c_smbus_read_word_data(psu->client, PMBUS_MFR_IOUT_MAX);
+        if (rc >= 0) {
+            rc = crps_convert_linear(rc);
+            rc = snprintf(data, I2C_SMBUS_BLOCK_MAX, "%d", rc);
+        }
+        break;

What is the point of those two attributes ? There are already
standard sysfs attributes reporting those values.
Removed debugfs

+    default:
+        rc = -EINVAL;
+        break;
+    }
+
+unlock:
+    pmbus_unlock(psu->client);
+    if (rc < 0)
+        return rc;
+
+    data[rc] = '\n';
+    rc += 2;
+
+    return simple_read_from_buffer(buf, count, ppos, data, rc);
+}
+
+static const struct file_operations crps_debugfs_fops = {
+    .llseek = noop_llseek,
+    .read = crps_debugfs_read,
+    .open = simple_open,
+};
+
+static int crps_read_word_data(struct i2c_client *client, int page,
+                int phase, int reg)
+{
+    int rc;
+
+    switch (reg) {
+    case PMBUS_STATUS_WORD:
+        rc = pmbus_read_word_data(client, page, phase, reg);
+        if (rc < 0)
+            return rc;
+        break;

Why is this needed ?
You are right, removed it.

+    case PMBUS_OT_WARN_LIMIT:
+        rc = pmbus_read_word_data(client, page, phase,
+                      PMBUS_MFR_MAX_TEMP_1);
+        if (rc < 0)
+            return rc;
+        break;
+    case PMBUS_IOUT_OC_WARN_LIMIT:
+        rc = pmbus_read_word_data(client, page, phase,
+                      CRPS185_MFR_IOUT_MAX);
+        if (rc < 0)
+            return rc;
+        break;
+    case PMBUS_POUT_OP_WARN_LIMIT:
+        rc = pmbus_read_word_data(client, page, phase,
+                      CRPS185_MFR_POUT_MAX);
+        if (rc < 0)
+            return rc;
+        break;

The above three values are more than odd. They duplicate the respective
standard rated_max attributes as warning limits. Why ?
I realized that some of these registers exist. So removed it.

On top of that, on writes, the actual warning limits are overwritten.
That makes even less sense.
I have removed read function so now we don't need write too.

+    default:
+        rc = -ENODATA;
+        break;
+    }
+
+    return rc;
+}
+
+static struct pmbus_driver_info crps_info[] = {
+    [crps185] = {
+        .pages = 1,
+        /* PSU uses default linear data format. */
+        .func[0] = PMBUS_HAVE_PIN | PMBUS_HAVE_IOUT |
+            PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_IIN |
+            PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
+            PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+            PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
+            PMBUS_HAVE_STATUS_TEMP |
+            PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+        .read_word_data = crps_read_word_data,
+    },
+};
+
+#define to_psu(x, y) container_of((x), struct crps, debugfs_entries[(y)])
+
+static void crps_init_debugfs(struct crps *psu)
+{
+    struct i2c_client *client = psu->client;
+    struct dentry *debugfs;
+    int i;
+
+    /* Don't fail the probe if we can't create debugfs */
+    debugfs = pmbus_get_debugfs_dir(client);
+    if (!debugfs)
+        return;
+
+    for (i = 0; i < CRPS_DEBUGFS_NUM_ENTRIES; ++i)
+        psu->debugfs_entries[i] = i;
+
+    debugfs_create_file("pmbus_revision", 0444, debugfs,
+ &psu->debugfs_entries[CRPS_DEBUGFS_PMBUS_REVISION],
+                &crps_debugfs_fops);
+    debugfs_create_file("max_power_out", 0444, debugfs,
+ &psu->debugfs_entries[CRPS_DEBUGFS_MAX_POWER_OUT],
+                &crps_debugfs_fops);
+    debugfs_create_file("max_current_out", 0444, debugfs,
+ &psu->debugfs_entries[CRPS_DEBUGFS_MAX_CURRENT_OUT],
+                &crps_debugfs_fops);
+}
+
+static int crps_probe(struct i2c_client *client)
+{
+    int rc;
+    struct device *dev = &client->dev;
+    enum models vs = crps_unknown;
+    struct crps *psu;
+    const void *md = of_device_get_match_data(&client->dev);
+    const struct i2c_device_id *id = NULL;
+    char buf[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
+
+    if (md) {
+        vs = (uintptr_t)md;
+    } else {
+        id = i2c_match_id(crps_id, client);
+        if (id)
+            vs = (enum models)id->driver_data;
+    }
+
Consider using i2c_get_match_data().
Removed this code.

+    if (!vs || vs >= crps_unknown) {
+        dev_err(dev, "Version %d not supported\n", vs);
+        return -EINVAL;
+    }

This is very much pointless. The driver would not be instantiated withut match.
Yes, removed it.

+
+    rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+    if (rc < 0) {
+        dev_err(dev, "Failed to read PMBUS_MFR_MODEL\n");
+        return rc;

dev_err_probe().
Changed to use this function.

+    }
+    if (strncmp(buf, "03NK260", 7)) {

This should also check and ensure that rc == 7.
Good idea, Added this check.

+        buf[rc] = '\0';
+        dev_err(dev, "Model '%s' not supported\n", buf);

dev_err_probe()
Changed the function

+        return -ENODEV;
+    }
+
+    client->dev.platform_data = &crps_pdata;
+    rc = pmbus_do_probe(client, &crps_info[vs]);
+    if (rc) {
+        dev_err(dev, "Failed to probe %d\n", rc);
+        return rc;

dev_err_probe().
Changed this function.

+    }
+
+    /*
+     * Don't fail the probe if there isn't enough memory for debugfs.
+     */
+    psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);
+    if (!psu) {
+        dev_warn(dev, "Failed to allocate memory. debugfs are not supported.\n");
+        return 0;
+    }
+
+    psu->version = vs;
+    psu->client = client;
+

Drop all this. Add the PMBus version as standard debugfs attribute to the PMBus core if needed/wanted. The rest is already provided as standard sysfs attributes, and
reporting the same value in debugfs files adds no value.
Yes, Removed debugfs.

+    crps_init_debugfs(psu);
+
+    return 0;
+}
+
+static const struct of_device_id crps_of_match[] = {
+    {
+        .compatible = "intel,crps185",
+        .data = (void *)crps185
+    },
+    {}
+};
+MODULE_DEVICE_TABLE(of, crps_of_match);
+
+static struct i2c_driver crps_driver = {
+    .driver = {
+        .name = "crps",
+        .of_match_table = crps_of_match,
+    },
+    .probe = crps_probe,
+    .id_table = crps_id,
+};
+
+module_i2c_driver(crps_driver);
+
+MODULE_AUTHOR("Ninad Palsule");
+MODULE_DESCRIPTION("PMBus driver for Common Redundant power supplies");

Again, this is for _Intel_ power supplies.

Yes, Added Intel


Thanks and Regards,

Ninad Palsule


+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux