From: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
This patch introduces a new hardware monitoring (hwmon) driver for the
Altera SoCFPGA platform, enabling kernel support for monitoring voltage and
temperature sensors critical for device health and system stability.
Changes include:
- New driver implementation 'drivers/hwmon/altera-hwmon.c' providing sensor
reading and event handling capabilities tailored for SoCFPGA hardware.
- Build system integration by adding Kconfig and Makefile entries, allowing
users to enable the driver in kernel configuration.
- Documentation added in 'Documentation/hwmon/altera-hwmon.rst', detailing
driver features, usage instructions, device tree bindings, and
configuration options.
- Update to 'Documentation/hwmon/index.rst' to reference the new driver
documentation, improving discoverability and user guidance.
Signed-off-by: Khairul Anuar Romli <khairul.anuar.romli@altera.com>
Signed-off-by: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
---
Documentation/hwmon/altr-hwmon.rst | 32 +++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 2 +
drivers/hwmon/Kconfig | 10 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/altr-hwmon.c | 427 +++++++++++++++++++++++++++++
6 files changed, 473 insertions(+)
create mode 100644 Documentation/hwmon/altr-hwmon.rst
create mode 100644 drivers/hwmon/altr-hwmon.c
diff --git a/Documentation/hwmon/altr-hwmon.rst b/Documentation/hwmon/altr-hwmon.rst
new file mode 100644
index 000000000000..3ef1ca0d1686
--- /dev/null
+++ b/Documentation/hwmon/altr-hwmon.rst
@@ -0,0 +1,32 @@
+.. SPDX-License-Identifier: GPL-2.0
+Kernel driver altr-hwmon
+=========================
+
+Supported chips:
+
+ * Intel N5X
+ * Stratix10
+ * Agilex
+ * Agilex5
+
+Contributor: Kris Chaplin <kris.chaplin@intel.com>
+ Khairul Anuar Romli <khairul.anuar.romli@altera.com>
+ Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
+
+Description
+-----------
+
+This driver supports hardware monitoring for 64-Bit SoC FPGA and eASIC devices
+based around the Secure Device Manager and Stratix 10 Service layer.
+
+The following sensor types are supported
+
+ * temperature
+ * voltage
+
+
+Usage Notes
+-----------
+
+The driver relies on a device tree node to enumerate support present on the
+specific device. See Documentation/devicetree/bindings/hwmon/altr,socfpga-hwmon.yaml for details of the device-tree node.
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 85d7a686883e..d37d4cbbe8b5 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -40,6 +40,7 @@ Hardware Monitoring Kernel Drivers
adt7470
adt7475
aht10
+ altr-hwmon
amc6821
aquacomputer_d5next
asb100
diff --git a/MAINTAINERS b/MAINTAINERS
index 8ac7fef4563a..01f776fdbf6f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -942,6 +942,8 @@ M: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/altr,socfpga-hwmon.yaml
+F: Documentation/hwmon/altr-hwmon.rst
+F: drivers/hwmon/altr-hwmon.c
ALTERA MAILBOX DRIVER
M: Tien Sung Ang <tiensung.ang@altera.com>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index d9bac1e3057b..4351725831d3 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -2122,6 +2122,16 @@ config SENSORS_SMSC47M192
This driver can also be built as a module. If so, the module
will be called smsc47m192.
+config SENSORS_ALTERA_SOCFPGA
+ tristate "Altera SoC FPGA Hardware monitoring features"
+ depends on INTEL_STRATIX10_SERVICE
+ help
+ If you say yes here you get support for the temperature and
+ voltage sensors of Altera SoC FPGA devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called altera-hwmon
+
config SENSORS_SMSC47B397
tristate "SMSC LPC47B397-NC"
depends on HAS_IOPORT
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index eade8e3b1bde..4ae4726bd0cb 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -218,6 +218,7 @@ obj-$(CONFIG_SENSORS_SMPRO) += smpro-hwmon.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+obj-$(CONFIG_SENSORS_ALTERA_SOCFPGA) += altr-hwmon.o
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
diff --git a/drivers/hwmon/altr-hwmon.c b/drivers/hwmon/altr-hwmon.c
new file mode 100644
index 000000000000..beecfa8537a4
--- /dev/null
+++ b/drivers/hwmon/altr-hwmon.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Altera SoC FPGA hardware monitoring driver
+ *
+ * Copyright (c) 2021 Intel Corporation. All rights reserved
+ * Copyright (c) 2025 Altera Corporation. All rights reserved
+ *
+ * Contributors:
+ * Kris Chaplin <kris.chaplin@intel.com>
+ * Ang Tien Sung <tiensung.ang@altera.com>
+ * Adrian Ng Ho Yin <adrianhoyin.ng@altera.com>
+ * Khairul Anuar Romli <khairul.anuar.romli@altera.com>
+ * Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#define HWMON_TIMEOUT (msecs_to_jiffies(SVC_HWMON_REQUEST_TIMEOUT_MS))
+
+#define ETEMP_INACTIVE 0x80000000
+#define ETEMP_TOO_OLD 0x80000001
+#define ETEMP_NOT_PRESENT 0x80000002
+#define ETEMP_TIMEOUT 0x80000003
+#define ETEMP_CORRUPT 0x80000004
+#define ETEMP_BUSY 0x80000005
+#define ETEMP_NOT_INITIALIZED 0x800000FF
+
+#define ALTR_SOCFPGA_HWMON_MAXSENSORS 16
+#define ALTR_SOCFPGA_HWMON_TEMPERATURE "temperature"
+#define ALTR_SOCFPGA_HWMON_VOLTAGE "voltage"
+
+struct altr_socfpga_hwmon_priv {
+ struct stratix10_svc_chan *chan;
+ struct stratix10_svc_client client;
+ struct completion completion;
+ struct mutex lock; /* protect fpga mailbox access */
+ int temperature;
+ int voltage;
+ int temperature_channels;
+ int voltage_channels;
+ const char *altr_socfpga_volt_chan_names[ALTR_SOCFPGA_HWMON_MAXSENSORS];
+ const char *altr_socfpga_temp_chan_names[ALTR_SOCFPGA_HWMON_MAXSENSORS];
+ u32 altr_socfpga_volt_chan[ALTR_SOCFPGA_HWMON_MAXSENSORS];
+ u32 altr_socfpga_temp_chan[ALTR_SOCFPGA_HWMON_MAXSENSORS];
+};
+
+static umode_t altr_socfpga_is_visible(const void *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int chan)
+{
+ const struct altr_socfpga_hwmon_priv *priv = dev;
+
+ switch (type) {
+ case hwmon_temp:
+ if (chan < priv->temperature_channels)
+ return 0444;
+
+ return 0;
+ case hwmon_in:
+ if (chan < priv->voltage_channels)
+ return 0444;
+
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+static void altr_socfpga_readtemp_smc_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct altr_socfpga_hwmon_priv *priv = client->priv;
+ struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ priv->temperature = res->a0;
+ } else {
+ dev_err(client->dev, "%s returned 0x%lX\n",
+ __func__, res->a0);
+ }
+
+ complete(&priv->completion);
+}
+
+static void altr_socfpga_readvolt_smc_callback(struct stratix10_svc_client *client,
+ struct stratix10_svc_cb_data *data)
+{
+ struct altr_socfpga_hwmon_priv *priv = client->priv;
+ struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_OK)) {
+ priv->voltage = res->a0;
+ } else {
+ dev_err(client->dev, "%s returned 0x%lX\n",
+ __func__, res->a0);
+ }
+
+ complete(&priv->completion);
+}
+
+static int altr_socfpga_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int chan, long *val)
+{
+ struct altr_socfpga_hwmon_priv *priv = dev_get_drvdata(dev);
+ struct stratix10_svc_client_msg msg;
+ int ret;
+
+ mutex_lock(&priv->lock);
+
+ reinit_completion(&priv->completion);
+
+ switch (type) {
+ case hwmon_temp:
+ if (chan > 15)
+ return -EOPNOTSUPP;
+
+ /* To support Page at upper word and channel at lower word */
+ msg.arg[0] = (((u64)1 << (priv->altr_socfpga_temp_chan[chan] & 0xFFFF))
+ + (priv->altr_socfpga_temp_chan[chan] & 0xFFF0000));
+ priv->client.receive_cb = altr_socfpga_readtemp_smc_callback;
+ msg.command = COMMAND_HWMON_READTEMP;
+
+ ret = stratix10_svc_send(priv->chan, &msg);
+ if (ret < 0)
+ goto status_done;
+
+ ret = wait_for_completion_interruptible_timeout(&priv->completion,
+ HWMON_TIMEOUT);
+
+ if (!ret) {
+ dev_err(priv->client.dev,
+ "timeout waiting for SMC call\n");
+ ret = -ETIMEDOUT;
+ goto status_done;
+ } else if (ret < 0) {
+ dev_err(priv->client.dev,
+ "error %d waiting for SMC call\n", ret);
+ goto status_done;
+ } else {
+ ret = 0;
+ }
+
+ *val = ((long)(priv->temperature)) * 1000 / 256;
+
+ switch (priv->temperature) {
+ case ETEMP_INACTIVE:
+ case ETEMP_NOT_PRESENT:
+ case ETEMP_CORRUPT:
+ case ETEMP_NOT_INITIALIZED:
+ ret = -EOPNOTSUPP;
+ break;
+
+ case ETEMP_TIMEOUT:
+ case ETEMP_BUSY:
+ case ETEMP_TOO_OLD:
+ ret = -EAGAIN;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ break;
+
+ case hwmon_in: // Read voltage
+ if (chan > 15)
+ return -EOPNOTSUPP; // Channel outside of range
+
+ msg.arg[0] = ((u64)1 << priv->altr_socfpga_volt_chan[chan]);
+ priv->client.receive_cb = altr_socfpga_readvolt_smc_callback;
+ msg.command = COMMAND_HWMON_READVOLT;
+
+ ret = stratix10_svc_send(priv->chan, &msg);
+ if (ret < 0)
+ goto status_done;
+
+ ret = wait_for_completion_interruptible_timeout(&priv->completion,
+ HWMON_TIMEOUT);
+
+ if (!ret) {
+ dev_err(priv->client.dev,
+ "timeout waiting for SMC call\n");
+ ret = -ETIMEDOUT;
+ goto status_done;
+ } else if (ret < 0) {
+ dev_err(priv->client.dev,
+ "error %d waiting for SMC call\n", ret);
+ goto status_done;
+ } else {
+ ret = 0;
+ }
+
+ *val = ((long)(priv->voltage)) * 1000 / 65536;
+ ret = 0;
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+status_done:
+ stratix10_svc_done(priv->chan);
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int altr_socfpga_read_string(struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ int chan, const char **str)
+{
+ struct altr_socfpga_hwmon_priv *priv = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_in:
+ *str = priv->altr_socfpga_volt_chan_names[chan];
+ return 0;
+ case hwmon_temp:
+ *str = priv->altr_socfpga_temp_chan_names[chan];
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_ops altr_socfpga_hwmon_ops = {
+ .is_visible = altr_socfpga_is_visible,
+ .read = altr_socfpga_read,
+ .read_string = altr_socfpga_read_string,
+};
+
+static const struct hwmon_channel_info *altr_socfpga_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
+ NULL
+};
+
+static const struct hwmon_chip_info altr_socfpga_hwmon_chip_info = {
+ .ops = &altr_socfpga_hwmon_ops,
+ .info = altr_socfpga_hwmon_info,
+};
+
+static int altr_socfpga_add_channel(struct device *dev, const char *type,
+ u32 val, const char *label,
+ struct altr_socfpga_hwmon_priv *priv)
+{
+ if (!strcmp(type, ALTR_SOCFPGA_HWMON_TEMPERATURE)) {
+ if (priv->temperature_channels >= ALTR_SOCFPGA_HWMON_MAXSENSORS) {
+ dev_warn(dev,
+ "Can't add temp node %s, too many channels",
+ label);
+ return 0;
+ }
+
+ priv->altr_socfpga_temp_chan_names[priv->temperature_channels] = label;
+ priv->altr_socfpga_temp_chan[priv->temperature_channels] = val;
+ priv->temperature_channels++;
+ return 0;
+ }
+
+ if (!strcmp(type, ALTR_SOCFPGA_HWMON_VOLTAGE)) {
+ if (priv->voltage_channels >= ALTR_SOCFPGA_HWMON_MAXSENSORS) {
+ dev_warn(dev,
+ "Can't add voltage node %s, too many channels",
+ label);
+ return 0;
+ }
+
+ priv->altr_socfpga_volt_chan_names[priv->voltage_channels] = label;
+ priv->altr_socfpga_volt_chan[priv->voltage_channels] = val;
+ priv->voltage_channels++;
+ return 0;
+ }
+
+ dev_warn(dev, "unsupported sensor type %s", type);
+ return 0;
+}
+
+static int altr_socfpga_probe_child_from_dt(struct device *dev,
+ struct device_node *child,
+ struct altr_socfpga_hwmon_priv *priv)
+{
+ u32 val;
+ int ret;
+ struct device_node *grandchild;
+ const char *label;
+ const char *type;
+
+ of_property_read_string(child, "name", &type);
+ for_each_child_of_node(child, grandchild) {
+ ret = of_property_read_u32(grandchild, "reg", &val);
+ if (ret) {
+ dev_err(dev, "missing reg property of %pOFn\n",
+ grandchild);
+ return ret;
+ }
+ ret = of_property_read_string(grandchild, "label", &label);
+ if (ret) {
+ dev_err(dev, "missing label propoerty of %pOFn\n",
+ grandchild);
+ return ret;
+ }
+
+ altr_socfpga_add_channel(dev, type, val, label, priv);
+ }
+
+ return 0;
+}
+
+static int altr_socfpga_probe_from_dt(struct device *dev,
+ struct altr_socfpga_hwmon_priv *priv)
+{
+ const struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int ret;
+
+ /* Compatible with non-DT platforms */
+ if (!np)
+ return 0;
+
+ for_each_child_of_node(np, child) {
+ ret = altr_socfpga_probe_child_from_dt(dev, child, priv);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int altr_socfpga_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *hwmon_dev;
+ struct altr_socfpga_hwmon_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client.dev = dev;
+ priv->client.receive_cb = NULL;
+ priv->client.priv = priv;
+ priv->temperature_channels = 0;
+ priv->voltage_channels = 0;
+
+ ret = altr_socfpga_probe_from_dt(dev, priv);
+ if (ret) {
+ dev_err(dev, "Unable to probe from device tree\n");
+ return ret;
+ }
+
+ mutex_init(&priv->lock);
+
+ priv->chan = stratix10_svc_request_channel_byname(&priv->client,
+ SVC_CLIENT_HWMON);
+ if (IS_ERR(priv->chan)) {
+ dev_err(dev, "couldn't get service channel %s defering probe...\n",
+ SVC_CLIENT_HWMON);
+ return -EPROBE_DEFER;
+ }
+
+ dev_info(dev, "Initialized %d temperature and %d voltage channels",
+ priv->temperature_channels, priv->voltage_channels);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "altr_hwmon", priv,
+ &altr_socfpga_hwmon_chip_info,
+ NULL);
+
+ init_completion(&priv->completion);
+ platform_set_drvdata(pdev, priv);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static void altr_socfpga_hwmon_remove(struct platform_device *pdev)
+{
+ struct altr_socfpga_hwmon_priv *priv = platform_get_drvdata(pdev);
+
+ stratix10_svc_free_channel(priv->chan);
+}
+
+static const struct of_device_id altr_socfpga_of_match[] = {
+ { .compatible = "altr,socfpga-hwmon" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altr_socfpga_of_match);
+
+static struct platform_driver altr_socfpga_hwmon_driver = {
+ .driver = {
+ .name = "altr-hwmon",
+ .of_match_table = altr_socfpga_of_match,
+ },
+ .probe = altr_socfpga_hwmon_probe,
+ .remove = altr_socfpga_hwmon_remove,
+};
+module_platform_driver(altr_socfpga_hwmon_driver);
+
+MODULE_AUTHOR("Altera Corporation");
+MODULE_DESCRIPTION("Altera SoC FPGA hardware monitoring features");
+MODULE_LICENSE("GPL");
--
2.43.7
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on c6bb982894b51c0ebaf94bbeb55ccbd1d4145a22]
url: https://github.com/intel-lab-lkp/linux/commits/muhammadamirulasyraf-mohamadjamian-altera-com/dt-bindings-hwmon-add-altr-socfpga-hwmon-yaml-binding/20251216-145542
base: c6bb982894b51c0ebaf94bbeb55ccbd1d4145a22
patch link: https://lore.kernel.org/r/20251216064926.15817-4-muhammadamirulasyraf.mohamadjamian%40altera.com
patch subject: [PATCH v1 3/5] hwmon: (altr-hwmon): Add initial support for SoCFPGA
reproduce: (https://download.01.org/0day-ci/archive/20251222/202512221215.5HgD2pyk-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512221215.5HgD2pyk-lkp@intel.com/
All warnings (new ones prefixed by >>):
WARNING: No kernel-doc for file ./include/linux/hid_bpf.h
ERROR: Cannot find file ./include/linux/hid_bpf.h
WARNING: No kernel-doc for file ./include/linux/hid_bpf.h
ERROR: Cannot find file ./include/linux/hid.h
WARNING: No kernel-doc for file ./include/linux/hid.h
>> Documentation/hwmon/altr-hwmon.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent. [docutils]
ERROR: Cannot find file ./include/linux/i2c-atr.h
WARNING: No kernel-doc for file ./include/linux/i2c-atr.h
ERROR: Cannot find file ./include/linux/mutex.h
ERROR: Cannot find file ./include/linux/mutex.h
WARNING: No kernel-doc for file ./include/linux/mutex.h
vim +2 Documentation/hwmon/altr-hwmon.rst
> 2 Kernel driver altr-hwmon
3 =========================
4
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On Mon, Dec 15, 2025 at 10:49:24PM -0800, muhammadamirulasyraf.mohamadjamian@altera.com wrote:
> From: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
>
> This patch introduces a new hardware monitoring (hwmon) driver for the
> Altera SoCFPGA platform, enabling kernel support for monitoring voltage and
> temperature sensors critical for device health and system stability.
>
> Changes include:
> - New driver implementation 'drivers/hwmon/altera-hwmon.c' providing sensor
> reading and event handling capabilities tailored for SoCFPGA hardware.
>
> - Build system integration by adding Kconfig and Makefile entries, allowing
> users to enable the driver in kernel configuration.
>
> - Documentation added in 'Documentation/hwmon/altera-hwmon.rst', detailing
> driver features, usage instructions, device tree bindings, and
> configuration options.
>
> - Update to 'Documentation/hwmon/index.rst' to reference the new driver
> documentation, improving discoverability and user guidance.
>
> Signed-off-by: Khairul Anuar Romli <khairul.anuar.romli@altera.com>
> Signed-off-by: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
> ---
> Documentation/hwmon/altr-hwmon.rst | 32 +++
> Documentation/hwmon/index.rst | 1 +
> MAINTAINERS | 2 +
> drivers/hwmon/Kconfig | 10 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/altr-hwmon.c | 427 +++++++++++++++++++++++++++++
> 6 files changed, 473 insertions(+)
> create mode 100644 Documentation/hwmon/altr-hwmon.rst
> create mode 100644 drivers/hwmon/altr-hwmon.c
>
> diff --git a/Documentation/hwmon/altr-hwmon.rst b/Documentation/hwmon/altr-hwmon.rst
> new file mode 100644
> index 000000000000..3ef1ca0d1686
> --- /dev/null
> +++ b/Documentation/hwmon/altr-hwmon.rst
> @@ -0,0 +1,32 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +Kernel driver altr-hwmon
> +=========================
> +
> +Supported chips:
> +
> + * Intel N5X
> + * Stratix10
> + * Agilex
> + * Agilex5
> +
> +Contributor: Kris Chaplin <kris.chaplin@intel.com>
> + Khairul Anuar Romli <khairul.anuar.romli@altera.com>
> + Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
> +
> +Description
> +-----------
> +
> +This driver supports hardware monitoring for 64-Bit SoC FPGA and eASIC devices
> +based around the Secure Device Manager and Stratix 10 Service layer.
> +
> +The following sensor types are supported
> +
> + * temperature
> + * voltage
> +
> +
> +Usage Notes
> +-----------
> +
> +The driver relies on a device tree node to enumerate support present on the
> +specific device. See Documentation/devicetree/bindings/hwmon/altr,socfpga-hwmon.yaml for details of the device-tree node.
> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
> index 85d7a686883e..d37d4cbbe8b5 100644
> --- a/Documentation/hwmon/index.rst
> +++ b/Documentation/hwmon/index.rst
> @@ -40,6 +40,7 @@ Hardware Monitoring Kernel Drivers
> adt7470
> adt7475
> aht10
> + altr-hwmon
> amc6821
> aquacomputer_d5next
> asb100
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8ac7fef4563a..01f776fdbf6f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -942,6 +942,8 @@ M: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@
> L: linux-hwmon@vger.kernel.org
> S: Maintained
> F: Documentation/devicetree/bindings/hwmon/altr,socfpga-hwmon.yaml
> +F: Documentation/hwmon/altr-hwmon.rst
> +F: drivers/hwmon/altr-hwmon.c
>
> ALTERA MAILBOX DRIVER
> M: Tien Sung Ang <tiensung.ang@altera.com>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index d9bac1e3057b..4351725831d3 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -2122,6 +2122,16 @@ config SENSORS_SMSC47M192
> This driver can also be built as a module. If so, the module
> will be called smsc47m192.
>
> +config SENSORS_ALTERA_SOCFPGA
> + tristate "Altera SoC FPGA Hardware monitoring features"
> + depends on INTEL_STRATIX10_SERVICE
Why this cannot be compile tested?
...
> +static int altr_socfpga_probe_child_from_dt(struct device *dev,
> + struct device_node *child,
> + struct altr_socfpga_hwmon_priv *priv)
> +{
> + u32 val;
> + int ret;
> + struct device_node *grandchild;
> + const char *label;
> + const char *type;
> +
> + of_property_read_string(child, "name", &type);
> + for_each_child_of_node(child, grandchild) {
No, see my further comment.
> + ret = of_property_read_u32(grandchild, "reg", &val);
> + if (ret) {
> + dev_err(dev, "missing reg property of %pOFn\n",
> + grandchild);
> + return ret;
> + }
> + ret = of_property_read_string(grandchild, "label", &label);
> + if (ret) {
> + dev_err(dev, "missing label propoerty of %pOFn\n",
> + grandchild);
> + return ret;
> + }
> +
> + altr_socfpga_add_channel(dev, type, val, label, priv);
> + }
> +
> + return 0;
> +}
> +
> +static int altr_socfpga_probe_from_dt(struct device *dev,
> + struct altr_socfpga_hwmon_priv *priv)
> +{
> + const struct device_node *np = dev->of_node;
> + struct device_node *child;
> + int ret;
> +
> + /* Compatible with non-DT platforms */
> + if (!np)
> + return 0;
> +
> + for_each_child_of_node(np, child) {
> + ret = altr_socfpga_probe_child_from_dt(dev, child, priv);
> + if (ret) {
> + of_node_put(child);
Just use scoped. Please don't upstream old code, but take new drivers
and use them as your starting point. You just repeat issues we fixed or
old style we changed loong time ago.
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int altr_socfpga_hwmon_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device *hwmon_dev;
> + struct altr_socfpga_hwmon_priv *priv;
> + int ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->client.dev = dev;
> + priv->client.receive_cb = NULL;
> + priv->client.priv = priv;
> + priv->temperature_channels = 0;
> + priv->voltage_channels = 0;
> +
> + ret = altr_socfpga_probe_from_dt(dev, priv);
> + if (ret) {
> + dev_err(dev, "Unable to probe from device tree\n");
> + return ret;
No, syntax is return dev_err_probe
> + }
> +
> + mutex_init(&priv->lock);
> +
> + priv->chan = stratix10_svc_request_channel_byname(&priv->client,
> + SVC_CLIENT_HWMON);
> + if (IS_ERR(priv->chan)) {
> + dev_err(dev, "couldn't get service channel %s defering probe...\n",
> + SVC_CLIENT_HWMON);
No, you are now spamming the dmesg with useless deferrals. return
dev_err_probe
> + return -EPROBE_DEFER;
Why ignoring actual error?
> + }
> +
> + dev_info(dev, "Initialized %d temperature and %d voltage channels",
> + priv->temperature_channels, priv->voltage_channels);
Drop, pretty useless. Drivers are supposed to be silent on success.
> +
> + hwmon_dev = devm_hwmon_device_register_with_info(dev, "altr_hwmon", priv,
> + &altr_socfpga_hwmon_chip_info,
> + NULL);
> +
> + init_completion(&priv->completion);
> + platform_set_drvdata(pdev, priv);
> +
> + return PTR_ERR_OR_ZERO(hwmon_dev);
> +}
> +
> +static void altr_socfpga_hwmon_remove(struct platform_device *pdev)
> +{
> + struct altr_socfpga_hwmon_priv *priv = platform_get_drvdata(pdev);
> +
> + stratix10_svc_free_channel(priv->chan);
> +}
> +
> +static const struct of_device_id altr_socfpga_of_match[] = {
> + { .compatible = "altr,socfpga-hwmon" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, altr_socfpga_of_match);
> +
> +static struct platform_driver altr_socfpga_hwmon_driver = {
> + .driver = {
> + .name = "altr-hwmon",
> + .of_match_table = altr_socfpga_of_match,
> + },
> + .probe = altr_socfpga_hwmon_probe,
> + .remove = altr_socfpga_hwmon_remove,
> +};
> +module_platform_driver(altr_socfpga_hwmon_driver);
> +
> +MODULE_AUTHOR("Altera Corporation");
> +MODULE_DESCRIPTION("Altera SoC FPGA hardware monitoring features");
> +MODULE_LICENSE("GPL");
> --
> 2.43.7
>
On 18/12/2025 4:29 pm, Krzysztof Kozlowski wrote:
> On Mon, Dec 15, 2025 at 10:49:24PM -0800, muhammadamirulasyraf.mohamadjamian@altera.com wrote:
>> From: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
>>
>> This patch introduces a new hardware monitoring (hwmon) driver for the
>> Altera SoCFPGA platform, enabling kernel support for monitoring voltage and
>> temperature sensors critical for device health and system stability.
>>
>> Changes include:
>> - New driver implementation 'drivers/hwmon/altera-hwmon.c' providing sensor
>> reading and event handling capabilities tailored for SoCFPGA hardware.
>>
>> - Build system integration by adding Kconfig and Makefile entries, allowing
>> users to enable the driver in kernel configuration.
>>
>> - Documentation added in 'Documentation/hwmon/altera-hwmon.rst', detailing
>> driver features, usage instructions, device tree bindings, and
>> configuration options.
>>
>> - Update to 'Documentation/hwmon/index.rst' to reference the new driver
>> documentation, improving discoverability and user guidance.
>>
>> Signed-off-by: Khairul Anuar Romli <khairul.anuar.romli@altera.com>
>> Signed-off-by: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
>> ---
>> Documentation/hwmon/altr-hwmon.rst | 32 +++
>> Documentation/hwmon/index.rst | 1 +
>> MAINTAINERS | 2 +
>> drivers/hwmon/Kconfig | 10 +
>> drivers/hwmon/Makefile | 1 +
>> drivers/hwmon/altr-hwmon.c | 427 +++++++++++++++++++++++++++++
>> 6 files changed, 473 insertions(+)
>> create mode 100644 Documentation/hwmon/altr-hwmon.rst
>> create mode 100644 drivers/hwmon/altr-hwmon.c
>>
>> diff --git a/Documentation/hwmon/altr-hwmon.rst b/Documentation/hwmon/altr-hwmon.rst
>> new file mode 100644
>> index 000000000000..3ef1ca0d1686
>> --- /dev/null
>> +++ b/Documentation/hwmon/altr-hwmon.rst
>> @@ -0,0 +1,32 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +Kernel driver altr-hwmon
>> +=========================
>> +
>> +Supported chips:
>> +
>> + * Intel N5X
>> + * Stratix10
>> + * Agilex
>> + * Agilex5
>> +
>> +Contributor: Kris Chaplin <kris.chaplin@intel.com>
>> + Khairul Anuar Romli <khairul.anuar.romli@altera.com>
>> + Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
>> +
>> +Description
>> +-----------
>> +
>> +This driver supports hardware monitoring for 64-Bit SoC FPGA and eASIC devices
>> +based around the Secure Device Manager and Stratix 10 Service layer.
>> +
>> +The following sensor types are supported
>> +
>> + * temperature
>> + * voltage
>> +
>> +
>> +Usage Notes
>> +-----------
>> +
>> +The driver relies on a device tree node to enumerate support present on the
>> +specific device. See Documentation/devicetree/bindings/hwmon/altr,socfpga-hwmon.yaml for details of the device-tree node.
>> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
>> index 85d7a686883e..d37d4cbbe8b5 100644
>> --- a/Documentation/hwmon/index.rst
>> +++ b/Documentation/hwmon/index.rst
>> @@ -40,6 +40,7 @@ Hardware Monitoring Kernel Drivers
>> adt7470
>> adt7475
>> aht10
>> + altr-hwmon
>> amc6821
>> aquacomputer_d5next
>> asb100
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 8ac7fef4563a..01f776fdbf6f 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -942,6 +942,8 @@ M: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@
>> L: linux-hwmon@vger.kernel.org
>> S: Maintained
>> F: Documentation/devicetree/bindings/hwmon/altr,socfpga-hwmon.yaml
>> +F: Documentation/hwmon/altr-hwmon.rst
>> +F: drivers/hwmon/altr-hwmon.c
>>
>> ALTERA MAILBOX DRIVER
>> M: Tien Sung Ang <tiensung.ang@altera.com>
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index d9bac1e3057b..4351725831d3 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -2122,6 +2122,16 @@ config SENSORS_SMSC47M192
>> This driver can also be built as a module. If so, the module
>> will be called smsc47m192.
>>
>> +config SENSORS_ALTERA_SOCFPGA
>> + tristate "Altera SoC FPGA Hardware monitoring features"
>> + depends on INTEL_STRATIX10_SERVICE
>
> Why this cannot be compile tested?
>
> ...
>
>
>> +static int altr_socfpga_probe_child_from_dt(struct device *dev,
>> + struct device_node *child,
>> + struct altr_socfpga_hwmon_priv *priv)
>> +{
>> + u32 val;
>> + int ret;
>> + struct device_node *grandchild;
>> + const char *label;
>> + const char *type;
>> +
>> + of_property_read_string(child, "name", &type);
>> + for_each_child_of_node(child, grandchild) {
>
> No, see my further comment.
>
>> + ret = of_property_read_u32(grandchild, "reg", &val);
>> + if (ret) {
>> + dev_err(dev, "missing reg property of %pOFn\n",
>> + grandchild);
>> + return ret;
>> + }
>> + ret = of_property_read_string(grandchild, "label", &label);
>> + if (ret) {
>> + dev_err(dev, "missing label propoerty of %pOFn\n",
>> + grandchild);
>> + return ret;
>> + }
>> +
>> + altr_socfpga_add_channel(dev, type, val, label, priv);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int altr_socfpga_probe_from_dt(struct device *dev,
>> + struct altr_socfpga_hwmon_priv *priv)
>> +{
>> + const struct device_node *np = dev->of_node;
>> + struct device_node *child;
>> + int ret;
>> +
>> + /* Compatible with non-DT platforms */
>> + if (!np)
>> + return 0;
>> +
>> + for_each_child_of_node(np, child) {
>> + ret = altr_socfpga_probe_child_from_dt(dev, child, priv);
>> + if (ret) {
>> + of_node_put(child);
>
> Just use scoped. Please don't upstream old code, but take new drivers
> and use them as your starting point. You just repeat issues we fixed or
> old style we changed loong time ago.
>
>
>> + return ret;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int altr_socfpga_hwmon_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct device *hwmon_dev;
>> + struct altr_socfpga_hwmon_priv *priv;
>> + int ret;
>> +
>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + priv->client.dev = dev;
>> + priv->client.receive_cb = NULL;
>> + priv->client.priv = priv;
>> + priv->temperature_channels = 0;
>> + priv->voltage_channels = 0;
>> +
>> + ret = altr_socfpga_probe_from_dt(dev, priv);
>> + if (ret) {
>> + dev_err(dev, "Unable to probe from device tree\n");
>> + return ret;
>
> No, syntax is return dev_err_probe
>> + }
>> +
>> + mutex_init(&priv->lock);
>> +
>> + priv->chan = stratix10_svc_request_channel_byname(&priv->client,
>> + SVC_CLIENT_HWMON);
>> + if (IS_ERR(priv->chan)) {
>> + dev_err(dev, "couldn't get service channel %s defering probe...\n",
>> + SVC_CLIENT_HWMON);
>
> No, you are now spamming the dmesg with useless deferrals. return
> dev_err_probe
>
>
>> + return -EPROBE_DEFER;
>
>
> Why ignoring actual error?
>
>> + }
>> +
>> + dev_info(dev, "Initialized %d temperature and %d voltage channels",
>> + priv->temperature_channels, priv->voltage_channels);
>
> Drop, pretty useless. Drivers are supposed to be silent on success.
>
>
>> +
>> + hwmon_dev = devm_hwmon_device_register_with_info(dev, "altr_hwmon", priv,
>> + &altr_socfpga_hwmon_chip_info,
>> + NULL);
>> +
>> + init_completion(&priv->completion);
>> + platform_set_drvdata(pdev, priv);
>> +
>> + return PTR_ERR_OR_ZERO(hwmon_dev);
>> +}
>> +
>> +static void altr_socfpga_hwmon_remove(struct platform_device *pdev)
>> +{
>> + struct altr_socfpga_hwmon_priv *priv = platform_get_drvdata(pdev);
>> +
>> + stratix10_svc_free_channel(priv->chan);
>> +}
>> +
>> +static const struct of_device_id altr_socfpga_of_match[] = {
>> + { .compatible = "altr,socfpga-hwmon" },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, altr_socfpga_of_match);
>> +
>> +static struct platform_driver altr_socfpga_hwmon_driver = {
>> + .driver = {
>> + .name = "altr-hwmon",
>> + .of_match_table = altr_socfpga_of_match,
>> + },
>> + .probe = altr_socfpga_hwmon_probe,
>> + .remove = altr_socfpga_hwmon_remove,
>> +};
>> +module_platform_driver(altr_socfpga_hwmon_driver);
>> +
>> +MODULE_AUTHOR("Altera Corporation");
>> +MODULE_DESCRIPTION("Altera SoC FPGA hardware monitoring features");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.43.7
>>
We will ovrehaul the patches based on new linux driver implementation as
starting point.
© 2016 - 2026 Red Hat, Inc.