Thread (26 messages) 26 messages, 7 authors, 2021-05-21

Re: [PATCH 4/6] hwmon: Add Delta TN48M CPLD HWMON driver

From: Robert Marko <robert.marko@sartura.hr>
Date: 2021-05-19 12:02:02
Also in: linux-gpio, linux-hwmon, lkml

On Fri, Apr 30, 2021 at 3:12 PM Guenter Roeck [off-list ref] wrote:
On 4/30/21 5:35 AM, Robert Marko wrote:
quoted
Delta TN48M has a CPLD that also monitors the power supply
statuses.

These are useful to be presented to the users, so lets
add a driver for HWMON part of the CPLD.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
---
 drivers/hwmon/Kconfig       |  10 +++
 drivers/hwmon/Makefile      |   1 +
 drivers/hwmon/tn48m-hwmon.c | 148 ++++++++++++++++++++++++++++++++++++
 drivers/mfd/tn48m-cpld.c    |   3 +
 include/linux/mfd/tn48m.h   |   1 +
 5 files changed, 163 insertions(+)
 create mode 100644 drivers/hwmon/tn48m-hwmon.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 54f04e61fb83..89271dfeb775 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1924,6 +1924,16 @@ config SENSORS_TMP513
        This driver can also be built as a module. If so, the module
        will be called tmp513.

+config SENSORS_TN48M
+     tristate "Delta Networks TN48M switch CPLD HWMON driver"
+     depends on MFD_TN48M_CPLD
+     help
+       If you say yes here you get support for Delta Networks TN48M
+       CPLD.
+
+       This driver can also be built as a module. If so, the module
+       will be called tn48m-hwmon.
+
 config SENSORS_VEXPRESS
      tristate "Versatile Express"
      depends on VEXPRESS_CONFIG
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index fe38e8a5c979..22e470057ffe 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -187,6 +187,7 @@ obj-$(CONFIG_SENSORS_TMP108)      += tmp108.o
 obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
 obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
 obj-$(CONFIG_SENSORS_TMP513) += tmp513.o
+obj-$(CONFIG_SENSORS_TN48M)  += tn48m-hwmon.o
 obj-$(CONFIG_SENSORS_VEXPRESS)       += vexpress-hwmon.o
 obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
 obj-$(CONFIG_SENSORS_VIA686A)        += via686a.o
diff --git a/drivers/hwmon/tn48m-hwmon.c b/drivers/hwmon/tn48m-hwmon.c
new file mode 100644
index 000000000000..80fd18d74f1d
--- /dev/null
+++ b/drivers/hwmon/tn48m-hwmon.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Delta TN48M CPLD HWMON driver
+ *
+ * Copyright 2020 Sartura Ltd
+ *
+ * Author: Robert Marko <robert.marko@sartura.hr>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/tn48m.h>
+
+#define PSU1_PRESENT_MASK    BIT(0)
+#define PSU2_PRESENT_MASK    BIT(1)
+#define PSU1_POWERGOOD_MASK  BIT(2)
+#define PSU2_POWERGOOD_MASK  BIT(3)
+#define PSU1_ALERT_MASK              BIT(4)
+#define PSU2_ALERT_MASK              BIT(5)
+
+static int board_id_get(struct tn48m_data *data)
+{
+     unsigned int regval;
+
+     regmap_read(data->regmap, BOARD_ID, &regval);
+
+     switch (regval) {
+     case BOARD_ID_TN48M:
+             return BOARD_ID_TN48M;
+     case BOARD_ID_TN48M_P:
+             return BOARD_ID_TN48M_P;
+     default:
+             return -EINVAL;
+     }
+}
+
+static ssize_t psu_present_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+     struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+     struct tn48m_data *data = dev_get_drvdata(dev);
+     unsigned int regval, status;
+
+     if (board_id_get(data) == BOARD_ID_TN48M_P) {
+             regmap_read(data->regmap, attr2->nr, &regval);
+
+             if (attr2->index == 1)
+                     status = !FIELD_GET(PSU1_PRESENT_MASK, regval);
+             else
+                     status = !FIELD_GET(PSU2_PRESENT_MASK, regval);
+     } else
+             status = 0;
+
+     return sprintf(buf, "%d\n", status);
+}
+
+static ssize_t psu_pg_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+     struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+     struct tn48m_data *data = dev_get_drvdata(dev);
+     unsigned int regval, status;
+
+     regmap_read(data->regmap, attr2->nr, &regval);
+
+     if (attr2->index == 1)
+             status = FIELD_GET(PSU1_POWERGOOD_MASK, regval);
+     else
+             status = FIELD_GET(PSU2_POWERGOOD_MASK, regval);
+
+     return sprintf(buf, "%d\n", status);
+}
+
+static ssize_t psu_alert_show(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+     struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+     struct tn48m_data *data = dev_get_drvdata(dev);
+     unsigned int regval, status;
+
+     if (board_id_get(data) == BOARD_ID_TN48M_P) {
+             regmap_read(data->regmap, attr2->nr, &regval);
+
+             if (attr2->index == 1)
+                     status = !FIELD_GET(PSU1_ALERT_MASK, regval);
+             else
+                     status = !FIELD_GET(PSU2_ALERT_MASK, regval);
+     } else
+             status = 0;
+
+     return sprintf(buf, "%d\n", status);
+}
+
+static SENSOR_DEVICE_ATTR_2_RO(psu1_present, psu_present, PSU_STATUS, 1);
+static SENSOR_DEVICE_ATTR_2_RO(psu2_present, psu_present, PSU_STATUS, 2);
+static SENSOR_DEVICE_ATTR_2_RO(psu1_pg, psu_pg, PSU_STATUS, 1);
+static SENSOR_DEVICE_ATTR_2_RO(psu2_pg, psu_pg, PSU_STATUS, 2);
+static SENSOR_DEVICE_ATTR_2_RO(psu1_alert, psu_alert, PSU_STATUS, 1);
+static SENSOR_DEVICE_ATTR_2_RO(psu2_alert, psu_alert, PSU_STATUS, 2);
+
+static struct attribute *tn48m_hwmon_attrs[] = {
+     &sensor_dev_attr_psu1_present.dev_attr.attr,
+     &sensor_dev_attr_psu2_present.dev_attr.attr,
+     &sensor_dev_attr_psu1_pg.dev_attr.attr,
+     &sensor_dev_attr_psu2_pg.dev_attr.attr,
+     &sensor_dev_attr_psu1_alert.dev_attr.attr,
+     &sensor_dev_attr_psu2_alert.dev_attr.attr,
Literally none of those attributes are standard hwmon attributes.
I don't know what this is, but it is not a hardware monitoring driver.
Yes, I agree that it does not expose any of the standard attributes, but these
are the only ones the CPLD exposes.

I don't know where else to put them, MFD driver did not seem logical to me.
quoted
+     NULL
+};
+
+ATTRIBUTE_GROUPS(tn48m_hwmon);
+
+static int tn48m_hwmon_probe(struct platform_device *pdev)
+{
+     struct tn48m_data *data = dev_get_drvdata(pdev->dev.parent);
+     struct device *hwmon_dev;
+
+     hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+                                                        "tn48m_hwmon",
+                                                        data,
+                                                        tn48m_hwmon_groups);
Please use devm_hwmon_device_register_with_info() to register hwmon devices.
Of course, that only makes sense for actual hardware monitoring drivers
which do support standard attributes.
Yes, devm_hwmon_device_register_with_info() made no sense without any of the
standard attributes.

Robert
quoted
+     return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct platform_device_id tn48m_hwmon_id_table[] = {
+     { "delta,tn48m-hwmon", },
+     { }
+};
+MODULE_DEVICE_TABLE(platform, tn48m_hwmon_id_table);
+
+static struct platform_driver tn48m_hwmon_driver = {
+     .driver = {
+             .name = "tn48m-hwmon",
+     },
+     .probe = tn48m_hwmon_probe,
+     .id_table = tn48m_hwmon_id_table,
+};
+module_platform_driver(tn48m_hwmon_driver);
+
+MODULE_AUTHOR("Robert Marko [off-list ref]");
+MODULE_DESCRIPTION("Delta TN48M CPLD HWMON driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tn48m-cpld.c b/drivers/mfd/tn48m-cpld.c
index f22a15ddd22d..4d837aca01e7 100644
--- a/drivers/mfd/tn48m-cpld.c
+++ b/drivers/mfd/tn48m-cpld.c
@@ -20,6 +20,9 @@
 static const struct mfd_cell tn48m_cell[] = {
      {
              .name = "delta,tn48m-gpio",
+     },
+     {
+             .name = "delta,tn48m-hwmon",
      }
 };
diff --git a/include/linux/mfd/tn48m.h b/include/linux/mfd/tn48m.h
index 9cc2b04c8d69..eb2cfc3a5db7 100644
--- a/include/linux/mfd/tn48m.h
+++ b/include/linux/mfd/tn48m.h
@@ -22,6 +22,7 @@
 #define SFP_TX_DISABLE               0x31
 #define SFP_PRESENT          0x3a
 #define SFP_LOS                      0x40
+#define PSU_STATUS           0xa

 struct tn48m_data {
      struct device *dev;

-- 
Robert Marko
Staff Embedded Linux Engineer
Sartura Ltd.
Lendavska ulica 16a
10000 Zagreb, Croatia
Email: robert.marko@sartura.hr
Web: www.sartura.hr
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help