[PATCH net-next 3/3] dpll: zl3073x: add hwmon support for input reference frequencies
From: Ivan Vecera
Date: Fri Mar 20 2026 - 07:03:09 EST
Expose measured input reference frequencies via the hwmon interface
using custom sysfs attributes (freqN_input and freqN_label) since
hwmon has no native frequency sensor type. The frequency values are
read from the cached measurements updated by the periodic work thread.
Cache the device ready state in struct zl3073x_dev so that
freq_input_show() can return -ENODATA without an I2C access when
the device firmware is not configured.
Signed-off-by: Ivan Vecera <ivecera@xxxxxxxxxx>
---
drivers/dpll/zl3073x/core.c | 4 +-
drivers/dpll/zl3073x/core.h | 2 +
drivers/dpll/zl3073x/hwmon.c | 86 +++++++++++++++++++++++++++++++++++-
3 files changed, 90 insertions(+), 2 deletions(-)
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index 67e65f8e7e7d4..5805f87167c20 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -874,7 +874,9 @@ int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full)
return rc;
}
- if (!FIELD_GET(ZL_INFO_READY, info)) {
+ zldev->ready = !!FIELD_GET(ZL_INFO_READY, info);
+
+ if (!zldev->ready) {
/* The ready bit indicates that the firmware was successfully
* configured and is ready for normal operation. If it is
* cleared then the configuration stored in flash is wrong
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
index 99440620407da..a416b8a65f41b 100644
--- a/drivers/dpll/zl3073x/core.h
+++ b/drivers/dpll/zl3073x/core.h
@@ -48,6 +48,7 @@ struct zl3073x_chip_info {
* @regmap: regmap to access device registers
* @info: detected chip info
* @multiop_lock: to serialize multiple register operations
+ * @ready: true if device firmware is configured and ready for normal operation
* @ref: array of input references' invariants
* @out: array of outs' invariants
* @synth: array of synths' invariants
@@ -63,6 +64,7 @@ struct zl3073x_dev {
struct regmap *regmap;
const struct zl3073x_chip_info *info;
struct mutex multiop_lock;
+ bool ready;
/* Invariants */
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
diff --git a/drivers/dpll/zl3073x/hwmon.c b/drivers/dpll/zl3073x/hwmon.c
index 4b44df4def820..96879609ce100 100644
--- a/drivers/dpll/zl3073x/hwmon.c
+++ b/drivers/dpll/zl3073x/hwmon.c
@@ -2,9 +2,11 @@
#include <linux/device.h>
#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
#include "core.h"
#include "hwmon.h"
+#include "ref.h"
#include "regs.h"
static int zl3073x_hwmon_read(struct device *dev,
@@ -55,6 +57,88 @@ static const struct hwmon_chip_info zl3073x_hwmon_chip_info = {
.info = zl3073x_hwmon_info,
};
+static ssize_t freq_input_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct zl3073x_dev *zldev = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(devattr)->index;
+ const struct zl3073x_ref *ref;
+
+ if (!zldev->ready)
+ return -ENODATA;
+
+ ref = zl3073x_ref_state_get(zldev, index);
+
+ return sysfs_emit(buf, "%u\n", zl3073x_ref_meas_freq_get(ref));
+}
+
+static ssize_t freq_label_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ static const char * const labels[] = {
+ "REF0P", "REF0N", "REF1P", "REF1N", "REF2P",
+ "REF2N", "REF3P", "REF3N", "REF4P", "REF4N",
+ };
+ int index = to_sensor_dev_attr(devattr)->index;
+
+ return sysfs_emit(buf, "%s\n", labels[index]);
+}
+
+static SENSOR_DEVICE_ATTR_RO(freq0_input, freq_input, 0);
+static SENSOR_DEVICE_ATTR_RO(freq1_input, freq_input, 1);
+static SENSOR_DEVICE_ATTR_RO(freq2_input, freq_input, 2);
+static SENSOR_DEVICE_ATTR_RO(freq3_input, freq_input, 3);
+static SENSOR_DEVICE_ATTR_RO(freq4_input, freq_input, 4);
+static SENSOR_DEVICE_ATTR_RO(freq5_input, freq_input, 5);
+static SENSOR_DEVICE_ATTR_RO(freq6_input, freq_input, 6);
+static SENSOR_DEVICE_ATTR_RO(freq7_input, freq_input, 7);
+static SENSOR_DEVICE_ATTR_RO(freq8_input, freq_input, 8);
+static SENSOR_DEVICE_ATTR_RO(freq9_input, freq_input, 9);
+
+static SENSOR_DEVICE_ATTR_RO(freq0_label, freq_label, 0);
+static SENSOR_DEVICE_ATTR_RO(freq1_label, freq_label, 1);
+static SENSOR_DEVICE_ATTR_RO(freq2_label, freq_label, 2);
+static SENSOR_DEVICE_ATTR_RO(freq3_label, freq_label, 3);
+static SENSOR_DEVICE_ATTR_RO(freq4_label, freq_label, 4);
+static SENSOR_DEVICE_ATTR_RO(freq5_label, freq_label, 5);
+static SENSOR_DEVICE_ATTR_RO(freq6_label, freq_label, 6);
+static SENSOR_DEVICE_ATTR_RO(freq7_label, freq_label, 7);
+static SENSOR_DEVICE_ATTR_RO(freq8_label, freq_label, 8);
+static SENSOR_DEVICE_ATTR_RO(freq9_label, freq_label, 9);
+
+static struct attribute *zl3073x_freq_attrs[] = {
+ &sensor_dev_attr_freq0_input.dev_attr.attr,
+ &sensor_dev_attr_freq0_label.dev_attr.attr,
+ &sensor_dev_attr_freq1_input.dev_attr.attr,
+ &sensor_dev_attr_freq1_label.dev_attr.attr,
+ &sensor_dev_attr_freq2_input.dev_attr.attr,
+ &sensor_dev_attr_freq2_label.dev_attr.attr,
+ &sensor_dev_attr_freq3_input.dev_attr.attr,
+ &sensor_dev_attr_freq3_label.dev_attr.attr,
+ &sensor_dev_attr_freq4_input.dev_attr.attr,
+ &sensor_dev_attr_freq4_label.dev_attr.attr,
+ &sensor_dev_attr_freq5_input.dev_attr.attr,
+ &sensor_dev_attr_freq5_label.dev_attr.attr,
+ &sensor_dev_attr_freq6_input.dev_attr.attr,
+ &sensor_dev_attr_freq6_label.dev_attr.attr,
+ &sensor_dev_attr_freq7_input.dev_attr.attr,
+ &sensor_dev_attr_freq7_label.dev_attr.attr,
+ &sensor_dev_attr_freq8_input.dev_attr.attr,
+ &sensor_dev_attr_freq8_label.dev_attr.attr,
+ &sensor_dev_attr_freq9_input.dev_attr.attr,
+ &sensor_dev_attr_freq9_label.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group zl3073x_freq_group = {
+ .attrs = zl3073x_freq_attrs,
+};
+
+static const struct attribute_group *zl3073x_hwmon_groups[] = {
+ &zl3073x_freq_group,
+ NULL,
+};
+
int zl3073x_hwmon_init(struct zl3073x_dev *zldev)
{
struct device *hwmon;
@@ -62,6 +146,6 @@ int zl3073x_hwmon_init(struct zl3073x_dev *zldev)
hwmon = devm_hwmon_device_register_with_info(zldev->dev, "zl3073x",
zldev,
&zl3073x_hwmon_chip_info,
- NULL);
+ zl3073x_hwmon_groups);
return PTR_ERR_OR_ZERO(hwmon);
}
--
2.52.0