mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next 0/4] ionic: report on-board sensors through an hwmon auxiliary driver
@ 2026-11-03 18:14 Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 1/4] ionic: register auxiliary devices from a table of device types Nikhil P. Rao
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Nikhil P. Rao @ 2026-11-03 18:14 UTC (permalink / raw)
  To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Guenter Roeck, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Kees Cook, Gustavo A. R. Silva
  Cc: netdev, linux-kernel, linux-hwmon, linux-doc, linux-hardening,
	Abhijit Gangurde, Nikhil P. Rao, Arun Kumar Prakash, Eric Joyner

AMD Pensando adapters can report board and ASIC sensors (temperature,
voltage and power) through the device firmware. This series exposes
them through hwmon.

Following the discussion in [1], ionic creates an "ionic.hwmon"
auxiliary device and a new driver in drivers/hwmon/ binds to it.
Guenter suggested taking the series through net-next with his Ack on
the hwmon driver, and agreed to his off-list reply being quoted here:

  > For b), an Ack from me and applying it through the network
  > subsystem would probably be the easiest approach, but I am open to
  > anything.

The set of sensors depends on the board and the firmware, so firmware
describes it at runtime:

 - Patch 1 registers ionic's auxiliary devices from a table of device
   types, so a second type can be added next to the RDMA device.
 - Patch 2 adds the device interface: a capability bit,
   IONIC_CMD_HWMON_GETCAPS, which returns a buffer describing the sensor
   types, their channels, and each channel's attributes, permissions and
   label, and IONIC_CMD_HWMON_GETATTR, which reads one attribute.
 - Patch 3 creates the "ionic.hwmon" auxiliary device when the firmware
   advertises the capability, and exports the calls the hwmon driver uses
   to issue those commands.
 - Patch 4 adds the ionic-hwmon driver. It parses the capability buffer
   into hwmon channel configuration at probe, and serves each sensor read
   with one GETATTR admin command. Only standard hwmon attributes are
   exposed.

Tested on an AMD Pensando adapter, including driver unbind and PCI
reset with concurrent sensor reads.

This series was developed with the help of an LLM-based coding
assistant (Claude).

[1] https://lore.kernel.org/all/20261009035120.3098398-1-nikhil.rao@amd.com/

---
Arun Kumar Prakash (2):
      ionic: add device interface definitions for hardware monitoring
      hwmon: add AMD Pensando ionic sensor driver

Nikhil P. Rao (2):
      ionic: register auxiliary devices from a table of device types
      ionic: add an auxiliary device for hardware monitoring

 Documentation/hwmon/index.rst                      |   1 +
 Documentation/hwmon/ionic-hwmon.rst                |  61 +++
 MAINTAINERS                                        |   9 +
 drivers/hwmon/Kconfig                              |  13 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/ionic-hwmon.c                        | 492 +++++++++++++++++++++
 drivers/net/ethernet/pensando/ionic/ionic.h        |   3 +
 drivers/net/ethernet/pensando/ionic/ionic_aux.c    | 203 +++++++--
 drivers/net/ethernet/pensando/ionic/ionic_aux.h    |   2 +-
 .../net/ethernet/pensando/ionic/ionic_bus_pci.c    |  14 +-
 drivers/net/ethernet/pensando/ionic/ionic_dev.h    |   8 +
 drivers/net/ethernet/pensando/ionic/ionic_if.h     | 104 +++++
 drivers/net/ethernet/pensando/ionic/ionic_lif.h    |   8 +-
 drivers/net/ethernet/pensando/ionic/ionic_main.c   |   4 +
 include/linux/ionic/ionic_hwmon.h                  | 178 ++++++++
 15 files changed, 1069 insertions(+), 32 deletions(-)
---
base-commit: 4dd16e31135ea8437a720222df34e8745f385d6d
change-id: 20261102-pr46-b4-4967f18fd3a2

Best regards,
--  
Nikhil P. Rao <nikhil.rao@amd.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next 1/4] ionic: register auxiliary devices from a table of device types
  2026-11-03 18:14 [PATCH net-next 0/4] ionic: report on-board sensors through an hwmon auxiliary driver Nikhil P. Rao
@ 2026-11-03 18:14 ` Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 2/4] ionic: add device interface definitions for hardware monitoring Nikhil P. Rao
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Nikhil P. Rao @ 2026-11-03 18:14 UTC (permalink / raw)
  To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Guenter Roeck, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Kees Cook, Gustavo A. R. Silva
  Cc: netdev, linux-kernel, linux-hwmon, linux-doc, linux-hardening,
	Abhijit Gangurde, Nikhil P. Rao

A later patch adds a hardware monitoring auxiliary device alongside the
rdma one. Describe the auxiliary device types in a table, so that adding
one takes an enum value and a table entry.

Allocate the auxiliary device id once per ionic device instead of on
every add, so all of a device's auxiliary devices share one id, for
example ionic.rdma.0 and ionic.hwmon.0.

Reviewed-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic.h        |  1 +
 drivers/net/ethernet/pensando/ionic/ionic_aux.c    | 94 +++++++++++++++-------
 drivers/net/ethernet/pensando/ionic/ionic_aux.h    |  2 +-
 .../net/ethernet/pensando/ionic/ionic_bus_pci.c    | 14 +++-
 drivers/net/ethernet/pensando/ionic/ionic_lif.h    |  7 +-
 5 files changed, 86 insertions(+), 32 deletions(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index 85198e6a806e6..555a77db1b1e7 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -49,6 +49,7 @@ struct ionic {
 	struct ionic_identity ident;
 	struct workqueue_struct *wq;
 	struct ionic_lif *lif;
+	int aux_id;
 	unsigned int nnqs_per_lif;
 	unsigned int neqs_per_lif;
 	unsigned int ntxqs_per_lif;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_aux.c b/drivers/net/ethernet/pensando/ionic/ionic_aux.c
index 4f193d0ee87ad..370a7eff3e283 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_aux.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_aux.c
@@ -6,41 +6,47 @@
 #include "ionic_lif.h"
 #include "ionic_aux.h"
 
-static DEFINE_IDA(aux_ida);
+struct ionic_aux_type_info {
+	const char *name;
+	bool (*supported)(struct ionic *ionic);
+	int (*prepare)(struct ionic *ionic);
+};
+
+static bool ionic_rdma_supported(struct ionic *ionic)
+{
+	return le64_to_cpu(ionic->ident.lif.capabilities) & IONIC_LIF_CAP_RDMA;
+}
+
+static const struct ionic_aux_type_info ionic_aux_types[IONIC_AUX_MAX] = {
+	[IONIC_AUX_RDMA] = {
+		.name = "rdma",
+		.supported = ionic_rdma_supported,
+	},
+};
 
 static void ionic_auxbus_release(struct device *dev)
 {
 	struct ionic_aux_dev *ionic_adev;
 
 	ionic_adev = container_of(dev, struct ionic_aux_dev, adev.dev);
-	ida_free(&aux_ida, ionic_adev->adev.id);
 	kfree(ionic_adev);
 }
 
-int ionic_auxbus_register(struct ionic_lif *lif)
+static struct ionic_aux_dev *ionic_auxbus_add(struct ionic_lif *lif,
+					      const char *name)
 {
 	struct ionic_aux_dev *ionic_adev;
 	struct auxiliary_device *aux_dev;
-	int err, id;
-
-	if (!(le64_to_cpu(lif->ionic->ident.lif.capabilities) & IONIC_LIF_CAP_RDMA))
-		return 0;
+	int err;
 
 	ionic_adev = kzalloc_obj(*ionic_adev);
 	if (!ionic_adev)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	aux_dev = &ionic_adev->adev;
 
-	id = ida_alloc(&aux_ida, GFP_KERNEL);
-	if (id < 0) {
-		dev_err(lif->ionic->dev, "Failed to allocate aux id: %d\n", id);
-		kfree(ionic_adev);
-		return id;
-	}
-
-	aux_dev->id = id;
-	aux_dev->name = "rdma";
+	aux_dev->id = lif->ionic->aux_id;
+	aux_dev->name = name;
 	aux_dev->dev.parent = &lif->ionic->pdev->dev;
 	aux_dev->dev.release = ionic_auxbus_release;
 	ionic_adev->lif = lif;
@@ -48,9 +54,8 @@ int ionic_auxbus_register(struct ionic_lif *lif)
 	if (err) {
 		dev_err(lif->ionic->dev, "Failed to initialize %s aux device: %d\n",
 			aux_dev->name, err);
-		ida_free(&aux_ida, id);
 		kfree(ionic_adev);
-		return err;
+		return ERR_PTR(err);
 	}
 
 	err = auxiliary_device_add(aux_dev);
@@ -58,24 +63,57 @@ int ionic_auxbus_register(struct ionic_lif *lif)
 		dev_err(lif->ionic->dev, "Failed to add %s aux device: %d\n",
 			aux_dev->name, err);
 		auxiliary_device_uninit(aux_dev);
-		return err;
+		return ERR_PTR(err);
+	}
+
+	return ionic_adev;
+}
+
+static void ionic_auxbus_del(struct ionic_aux_dev *ionic_adev)
+{
+	auxiliary_device_delete(&ionic_adev->adev);
+	auxiliary_device_uninit(&ionic_adev->adev);
+}
+
+void ionic_auxbus_register(struct ionic_lif *lif)
+{
+	const struct ionic_aux_type_info *type;
+	struct ionic_aux_dev *ionic_adev;
+	int i;
+
+	mutex_lock(&lif->adev_lock);
+
+	for (i = 0; i < IONIC_AUX_MAX; i++) {
+		type = &ionic_aux_types[i];
+
+		if (!type->supported(lif->ionic))
+			continue;
+
+		if (type->prepare && type->prepare(lif->ionic))
+			continue;
+
+		ionic_adev = ionic_auxbus_add(lif, type->name);
+		if (!IS_ERR(ionic_adev))
+			lif->aux_devs[i] = ionic_adev;
 	}
 
-	lif->ionic_adev = ionic_adev;
-	return 0;
+	mutex_unlock(&lif->adev_lock);
 }
 
 void ionic_auxbus_unregister(struct ionic_lif *lif)
 {
+	int i;
+
 	mutex_lock(&lif->adev_lock);
-	if (!lif->ionic_adev)
-		goto out;
 
-	auxiliary_device_delete(&lif->ionic_adev->adev);
-	auxiliary_device_uninit(&lif->ionic_adev->adev);
+	for (i = IONIC_AUX_MAX - 1; i >= 0; i--) {
+		if (!lif->aux_devs[i])
+			continue;
+
+		ionic_auxbus_del(lif->aux_devs[i]);
+		lif->aux_devs[i] = NULL;
+	}
 
-	lif->ionic_adev = NULL;
-out:
 	mutex_unlock(&lif->adev_lock);
 }
 
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_aux.h b/drivers/net/ethernet/pensando/ionic/ionic_aux.h
index f5528a9f187db..215f2f67646be 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_aux.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_aux.h
@@ -4,7 +4,7 @@
 #ifndef _IONIC_AUX_H_
 #define _IONIC_AUX_H_
 
-int ionic_auxbus_register(struct ionic_lif *lif);
+void ionic_auxbus_register(struct ionic_lif *lif);
 void ionic_auxbus_unregister(struct ionic_lif *lif);
 
 #endif /* _IONIC_AUX_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index ddc2f9770df45..9bedcb7224abc 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -20,6 +20,8 @@ static const struct pci_device_id ionic_id_table[] = {
 };
 MODULE_DEVICE_TABLE(pci, ionic_id_table);
 
+static DEFINE_IDA(ionic_ida);
+
 int ionic_bus_get_irq(struct ionic *ionic, unsigned int num)
 {
 	return pci_irq_vector(ionic->pdev, num);
@@ -321,12 +323,17 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	pci_set_drvdata(pdev, ionic);
 	mutex_init(&ionic->dev_cmd_lock);
 
+	err = ida_alloc(&ionic_ida, GFP_KERNEL);
+	if (err < 0)
+		goto err_out;
+	ionic->aux_id = err;
+
 	/* Query system for DMA addressing limitation for the device. */
 	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(IONIC_ADDR_LEN));
 	if (err) {
 		dev_err(dev, "Unable to obtain 64-bit DMA for consistent allocations, aborting.  err=%d\n",
 			err);
-		goto err_out;
+		goto err_out_free_id;
 	}
 
 #ifdef CONFIG_PPC64
@@ -336,7 +343,7 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	err = ionic_setup_one(ionic);
 	if (err)
-		goto err_out;
+		goto err_out_free_id;
 
 	/* Allocate and init the LIF */
 	err = ionic_lif_size(ionic);
@@ -401,6 +408,8 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	ionic_dev_teardown(ionic);
 	ionic_clear_pci(ionic);
 	ionic_debugfs_del_dev(ionic);
+err_out_free_id:
+	ida_free(&ionic_ida, ionic->aux_id);
 err_out:
 	mutex_destroy(&ionic->dev_cmd_lock);
 	ionic_devlink_free(ionic);
@@ -437,6 +446,7 @@ static void ionic_remove(struct pci_dev *pdev)
 	ionic_dev_teardown(ionic);
 	ionic_clear_pci(ionic);
 	ionic_debugfs_del_dev(ionic);
+	ida_free(&ionic_ida, ionic->aux_id);
 	mutex_destroy(&ionic->dev_cmd_lock);
 	ionic_devlink_free(ionic);
 }
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index e790f6b9441e2..d04503b19c513 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -180,6 +180,11 @@ struct ionic_qtype_info {
 
 struct ionic_phc;
 
+enum ionic_aux_type {
+	IONIC_AUX_RDMA,
+	IONIC_AUX_MAX,
+};
+
 #define IONIC_LIF_NAME_MAX_SZ		32
 struct ionic_lif {
 	struct net_device *netdev;
@@ -225,7 +230,7 @@ struct ionic_lif {
 	dma_addr_t info_pa;
 	u32 info_sz;
 	struct ionic_qtype_info qtype_info[IONIC_QTYPE_MAX];
-	struct ionic_aux_dev *ionic_adev;
+	struct ionic_aux_dev *aux_devs[IONIC_AUX_MAX];
 	struct mutex adev_lock;	/* lock for aux_dev actions */
 
 	u8 rss_hash_key[IONIC_RSS_HASH_KEY_SIZE];

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next 2/4] ionic: add device interface definitions for hardware monitoring
  2026-11-03 18:14 [PATCH net-next 0/4] ionic: report on-board sensors through an hwmon auxiliary driver Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 1/4] ionic: register auxiliary devices from a table of device types Nikhil P. Rao
@ 2026-11-03 18:14 ` Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 3/4] ionic: add an auxiliary device " Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 4/4] hwmon: add AMD Pensando ionic sensor driver Nikhil P. Rao
  3 siblings, 0 replies; 6+ messages in thread
From: Nikhil P. Rao @ 2026-11-03 18:14 UTC (permalink / raw)
  To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Guenter Roeck, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Kees Cook, Gustavo A. R. Silva
  Cc: netdev, linux-kernel, linux-hwmon, linux-doc, linux-hardening,
	Abhijit Gangurde, Arun Kumar Prakash, Eric Joyner, Nikhil P. Rao

From: Arun Kumar Prakash <ArunKumar.Prakash@amd.com>

Add the admin queue commands the device uses to describe and report its
sensors: IONIC_CMD_HWMON_GETCAPS returns a buffer enumerating the sensor
types, their channel counts and per-channel attribute capabilities, and
IONIC_CMD_HWMON_GETATTR reads a single attribute of a single channel.

The capability buffer has a variable layout that depends on the number
of sensor types the firmware supports, so it cannot be expressed as a C
struct and its format is documented in a comment instead.

The sensor types, attribute IDs and capability buffer layout go in a
new public header, include/linux/ionic/ionic_hwmon.h, because the hwmon
driver that parses the buffer lives outside the ionic directory.

Signed-off-by: Arun Kumar Prakash <ArunKumar.Prakash@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
Reviewed-by: Brett Creeley <brett.creeley@amd.com>
Co-developed-by: Nikhil P. Rao <nikhil.rao@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
 MAINTAINERS                                      |   1 +
 drivers/net/ethernet/pensando/ionic/ionic_dev.h  |   8 ++
 drivers/net/ethernet/pensando/ionic/ionic_if.h   | 104 +++++++++++++++++
 drivers/net/ethernet/pensando/ionic/ionic_main.c |   4 +
 include/linux/ionic/ionic_hwmon.h                | 138 +++++++++++++++++++++++
 5 files changed, 255 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 09713dc4c6a7..220b26c73b37 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21804,6 +21804,7 @@ L:	netdev@vger.kernel.org
 S:	Maintained
 F:	Documentation/networking/device_drivers/ethernet/pensando/ionic.rst
 F:	drivers/net/ethernet/pensando/
+F:	include/linux/ionic/
 
 PER-CPU MEMORY ALLOCATOR
 M:	Dennis Zhou <dennis@kernel.org>
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
index db90e39a1442..869adb0a0e0c 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
@@ -9,6 +9,7 @@
 #include <linux/workqueue.h>
 #include <linux/skbuff.h>
 #include <linux/bpf_trace.h>
+#include <linux/ionic/ionic_hwmon.h>
 
 #include "ionic_if.h"
 #include "ionic_regs.h"
@@ -116,6 +117,13 @@ static_assert(sizeof(ionic_rx_filter_del_comp) == 16);
 static_assert(sizeof(struct ionic_rdma_reset_cmd) == 64);
 static_assert(sizeof(struct ionic_rdma_queue_cmd) == 64);
 
+/* HWMON commands */
+static_assert(sizeof(struct ionic_hwmon_getattr_cmd) == 64);
+static_assert(sizeof(struct ionic_hwmon_getattr_comp) == 16);
+static_assert(sizeof(struct ionic_hwmon_getcaps_cmd) == 64);
+static_assert(sizeof(struct ionic_hwmon_getcaps_comp) == 16);
+static_assert(sizeof(struct ionic_hwmon_chan_caps) == 164);
+
 /* Events */
 static_assert(sizeof(struct ionic_notifyq_cmd) == 4);
 static_assert(sizeof(union ionic_notifyq_comp) == 64);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h
index e911fad545d6..271731a35d9f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_if.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h
@@ -60,6 +60,10 @@ enum ionic_cmd_opcode {
 	/* CMB command */
 	IONIC_CMD_DISCOVER_CMB			= 80,
 
+	/* Hardware monitoring commands */
+	IONIC_CMD_HWMON_GETATTR			= 110,
+	IONIC_CMD_HWMON_GETCAPS			= 111,
+
 	/* QoS commands */
 	IONIC_CMD_QOS_CLASS_IDENTIFY		= 240,
 	IONIC_CMD_QOS_CLASS_INIT		= 241,
@@ -274,11 +278,13 @@ union ionic_drv_identity {
  * enum ionic_dev_capability - Device capabilities
  * @IONIC_DEV_CAP_VF_CTRL:     Device supports VF ctrl operations
  * @IONIC_DEV_CAP_DISC_CMB:    Device supports CMB discovery operations
+ * @IONIC_DEV_CAP_HWMON:       Device supports hardware monitoring
  * @IONIC_DEV_CAP_EXTRA_STATS: Device supports extra stats schema
  */
 enum ionic_dev_capability {
 	IONIC_DEV_CAP_VF_CTRL        = BIT(0),
 	IONIC_DEV_CAP_DISC_CMB       = BIT(1),
+	IONIC_DEV_CAP_HWMON          = BIT(3),
 	IONIC_DEV_CAP_EXTRA_STATS    = BIT(4),
 };
 
@@ -2526,6 +2532,100 @@ struct ionic_qos_clear_stats_cmd {
 
 typedef struct ionic_admin_comp ionic_qos_reset_comp;
 
+/**
+ * struct ionic_hwmon_getattr_cmd - Hardware monitoring get attribute command
+ * @opcode:      Command opcode (IONIC_CMD_HWMON_GETATTR)
+ * @rsvd:        reserved byte(s)
+ * @lif_index:   LIF index
+ * @sensor_type: Sensor type from enum ionic_hwmon_sensor_type
+ * @channel:     Channel index within the sensor type
+ * @attr:        Attribute ID from sensor-specific attribute enum
+ * @rsvd2:       reserved byte(s)
+ *
+ * A *_ATTR_LABEL attribute ID must never be sent in this command.  The label is
+ * already reported in ionic_hwmon_chan_caps.label; the attr_caps LABEL bit only
+ * signals that a label is present.
+ */
+struct ionic_hwmon_getattr_cmd {
+	u8      opcode;
+	u8      rsvd;
+	__le16  lif_index;
+	u8      sensor_type;
+	u8      channel;
+	u8      attr;
+	u8      rsvd2[57];
+};
+
+/**
+ * struct ionic_hwmon_getattr_comp - Hardware monitor get attribute completion
+ * @status:     Status of the command (enum ionic_status_code)
+ * @rsvd:       reserved byte(s)
+ * @comp_index: Index in the descriptor ring for which this is the completion
+ * @attr_value: Attribute value
+ * @rsvd2:      reserved byte(s)
+ * @color:      Color bit
+ *
+ * attr_value depends on the (sensor_type, attr) pair being read:
+ *
+ *   temperature INPUT, CRIT   millidegrees Celsius, signed 32-bit
+ *   temperature CRIT_ALARM    0 or 1
+ *   voltage INPUT             millivolts
+ *   power INPUT, MAX          milliwatts
+ */
+struct ionic_hwmon_getattr_comp {
+	u8      status;
+	u8      rsvd;
+	__le16  comp_index;
+	__le32  attr_value;
+	u8      rsvd2[7];
+	u8      color;
+};
+
+/**
+ * struct ionic_hwmon_getcaps_cmd - Hardware monitoring get capabilities command
+ * @opcode:         Command opcode (IONIC_CMD_HWMON_GETCAPS)
+ * @ver:            Highest capability buffer layout version supported by
+ *                  the driver
+ * @lif_index:      LIF index
+ * @resp_buf_len:   Length of response buffer
+ * @resp_dma_addr:  DMA address for response data
+ * @rsvd2:          reserved byte(s)
+ *
+ * The device writes the capability buffer in the highest layout version it
+ * supports that does not exceed @ver, and reports that version in the
+ * completion.  IONIC_HWMON_CAPS_VERSION_0 is the layout documented in
+ * include/linux/ionic/ionic_hwmon.h.
+ */
+struct ionic_hwmon_getcaps_cmd {
+	u8      opcode;
+	u8      ver;
+	__le16  lif_index;
+	__le32  resp_buf_len;
+	__le64  resp_dma_addr;
+	u8      rsvd2[48];
+};
+
+/**
+ * struct ionic_hwmon_getcaps_comp - Hardware monitoring get caps completion
+ * @status:     Status of the command (enum ionic_status_code)
+ * @rsvd:       reserved byte(s)
+ * @comp_index: Index in the descriptor ring for which this is the completion
+ * @ver:        Layout version of the capability buffer written by the device
+ * @rsvd2:      reserved byte(s)
+ * @color:      Color bit
+ *
+ * Devices that predate @ver leave it zero, which is
+ * IONIC_HWMON_CAPS_VERSION_0.
+ */
+struct ionic_hwmon_getcaps_comp {
+	u8      status;
+	u8      rsvd;
+	__le16  comp_index;
+	u8      ver;
+	u8      rsvd2[10];
+	u8      color;
+};
+
 /**
  * struct ionic_fw_download_cmd - Firmware download command
  * @opcode:	opcode
@@ -3306,6 +3406,8 @@ union ionic_adminq_cmd {
 	struct ionic_rx_filter_del_cmd rx_filter_del;
 	struct ionic_rdma_reset_cmd rdma_reset;
 	struct ionic_rdma_queue_cmd rdma_queue;
+	struct ionic_hwmon_getattr_cmd hwmon_getattr;
+	struct ionic_hwmon_getcaps_cmd hwmon_getcaps;
 	struct ionic_fw_download_cmd fw_download;
 	struct ionic_fw_control_cmd fw_control;
 };
@@ -3319,6 +3421,8 @@ union ionic_adminq_comp {
 	struct ionic_lif_getattr_comp lif_getattr;
 	struct ionic_admin_comp lif_setphc;
 	struct ionic_rx_filter_add_comp rx_filter_add;
+	struct ionic_hwmon_getattr_comp hwmon_getattr;
+	struct ionic_hwmon_getcaps_comp hwmon_getcaps;
 	struct ionic_fw_control_comp fw_control;
 };
 
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 38b47b959a11..9955ab77d86d 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -185,6 +185,10 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
 		return "IONIC_CMD_VF_GETATTR";
 	case IONIC_CMD_VF_SETATTR:
 		return "IONIC_CMD_VF_SETATTR";
+	case IONIC_CMD_HWMON_GETATTR:
+		return "IONIC_CMD_HWMON_GETATTR";
+	case IONIC_CMD_HWMON_GETCAPS:
+		return "IONIC_CMD_HWMON_GETCAPS";
 	default:
 		return "DEVCMD_UNKNOWN";
 	}
diff --git a/include/linux/ionic/ionic_hwmon.h b/include/linux/ionic/ionic_hwmon.h
new file mode 100644
index 000000000000..62667e3d7135
--- /dev/null
+++ b/include/linux/ionic/ionic_hwmon.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR Linux-OpenIB) OR BSD-2-Clause */
+/* Copyright(c) 2026 Advanced Micro Devices, Inc */
+
+#ifndef _LINUX_IONIC_HWMON_H_
+#define _LINUX_IONIC_HWMON_H_
+
+#include <linux/types.h>
+
+#define IONIC_HWMON_CAPS_BUF_SIZE	4096
+#define IONIC_HWMON_CAPS_VERSION_0	0
+
+/**
+ * enum ionic_hwmon_sensor_type - Hardware monitoring sensor types
+ * @IONIC_HWMON_TYPE_TEMP:      Temperature sensors
+ * @IONIC_HWMON_TYPE_VOLTAGE:   Voltage sensors
+ * @IONIC_HWMON_TYPE_POWER:     Power sensors
+ * @IONIC_HWMON_TYPE_CHIP:      Chip wide attributes
+ * @IONIC_HWMON_TYPE_CURRENT:   Current sensors
+ * @IONIC_HWMON_TYPE_ENERGY:    Energy sensors
+ * @IONIC_HWMON_TYPE_HUMIDITY:  Humidity sensors
+ * @IONIC_HWMON_TYPE_FAN:       Fan speed sensors
+ * @IONIC_HWMON_TYPE_PWM:       PWM controllers
+ * @IONIC_HWMON_TYPE_INTRUSION: Intrusion detection
+ * @IONIC_HWMON_TYPE_MAX:       Number of sensor types defined by this interface
+ *
+ * These values do not necessarily match enum hwmon_sensor_types.
+ */
+enum ionic_hwmon_sensor_type {
+	IONIC_HWMON_TYPE_TEMP		= 0,
+	IONIC_HWMON_TYPE_VOLTAGE	= 1,
+	IONIC_HWMON_TYPE_POWER		= 2,
+	IONIC_HWMON_TYPE_CHIP		= 3,
+	IONIC_HWMON_TYPE_CURRENT	= 4,
+	IONIC_HWMON_TYPE_ENERGY		= 5,
+	IONIC_HWMON_TYPE_HUMIDITY	= 6,
+	IONIC_HWMON_TYPE_FAN		= 7,
+	IONIC_HWMON_TYPE_PWM		= 8,
+	IONIC_HWMON_TYPE_INTRUSION	= 9,
+	IONIC_HWMON_TYPE_MAX		= 10,
+};
+
+/**
+ * enum ionic_hwmon_temp_attr - Temperature sensor attributes
+ * @IONIC_HWMON_TEMP_ATTR_INPUT:      Temperature input reading
+ * @IONIC_HWMON_TEMP_ATTR_LABEL:      Temperature sensor label
+ * @IONIC_HWMON_TEMP_ATTR_CRIT:       Critical temperature threshold
+ * @IONIC_HWMON_TEMP_ATTR_CRIT_ALARM: Critical temperature alarm status
+ */
+enum ionic_hwmon_temp_attr {
+	IONIC_HWMON_TEMP_ATTR_INPUT		= 0,
+	IONIC_HWMON_TEMP_ATTR_LABEL		= 1,
+	IONIC_HWMON_TEMP_ATTR_CRIT		= 2,
+	IONIC_HWMON_TEMP_ATTR_CRIT_ALARM	= 3,
+};
+
+/**
+ * enum ionic_hwmon_voltage_attr - Voltage sensor attributes
+ * @IONIC_HWMON_VOLTAGE_ATTR_INPUT: Voltage input reading
+ * @IONIC_HWMON_VOLTAGE_ATTR_LABEL: Voltage sensor label
+ */
+enum ionic_hwmon_voltage_attr {
+	IONIC_HWMON_VOLTAGE_ATTR_INPUT	= 0,
+	IONIC_HWMON_VOLTAGE_ATTR_LABEL	= 1,
+};
+
+/**
+ * enum ionic_hwmon_power_attr - Power sensor attributes
+ * @IONIC_HWMON_POWER_ATTR_INPUT: Power input reading
+ * @IONIC_HWMON_POWER_ATTR_LABEL: Power sensor label
+ * @IONIC_HWMON_POWER_ATTR_MAX:   Maximum power threshold
+ */
+enum ionic_hwmon_power_attr {
+	IONIC_HWMON_POWER_ATTR_INPUT	= 0,
+	IONIC_HWMON_POWER_ATTR_LABEL	= 1,
+	IONIC_HWMON_POWER_ATTR_MAX	= 2,
+};
+
+/**
+ * struct ionic_hwmon_chan_caps - Per-channel capability descriptor
+ * @attr_caps:  Attribute bitmask where bit N indicates attribute N is supported
+ *              (use sensor-type-specific attribute enums:
+ *              ionic_hwmon_temp_attr, ionic_hwmon_voltage_attr,
+ *              ionic_hwmon_power_attr)
+ * @mode:       Permissions for attribute N, as a single octal rwx digit
+ *              (4 = readable).  Zero hides the attribute.  Values above 7
+ *              are not a mode and hide the attribute too.
+ * @label:      NUL-terminated channel label string
+ *
+ * Every channel reports at least one attribute that its sensor type defines.
+ * Firmware that adds attributes keeps reporting one that older drivers
+ * recognise, and uses @mode to control what is visible.
+ *
+ * The structure needs to be packed since the size of n_channels in the
+ * capability buffer layout documented below is variable, so the channels
+ * array is not guaranteed to start on a 4 byte boundary.
+ */
+struct ionic_hwmon_chan_caps {
+	__le32	attr_caps;
+	__le32	mode[32];
+	char	label[32];
+} __packed;
+
+#define IONIC_HWMON_MODE_MASK		0x7
+#define IONIC_HWMON_MODE_READ		4
+
+/*
+ * Hardware monitoring capabilities buffer layout
+ *
+ * The buffer is a struct ionic_hwmon_caps_hdr followed by the channel
+ * descriptors, so where the descriptors start depends on num_types:
+ *
+ *   struct ionic_hwmon_caps_buf {
+ *       __le16  num_types;
+ *       __le16  n_channels[num_types];
+ *       struct ionic_hwmon_chan_caps channels[total_channels];
+ *   };
+ *
+ * total_channels is the sum of n_channels, and the channels of each sensor
+ * type are contiguous, in n_channels order.  A driver reads at most
+ * IONIC_HWMON_TYPE_MAX entries of n_channels and ignores the channels of
+ * any further types, which follow the ones it knows.
+ *
+ * num_types and n_channels come from the device, and the completion does not
+ * report how much was written, so a parser checks every offset against the
+ * resp_buf_len sent in IONIC_CMD_HWMON_GETCAPS.
+ */
+
+/**
+ * struct ionic_hwmon_caps_hdr - Start of the capabilities buffer
+ * @num_types:  Number of sensor types described by firmware
+ * @n_channels: Channel count for each sensor type, num_types entries
+ */
+struct ionic_hwmon_caps_hdr {
+	__le16	num_types;
+	__le16	n_channels[];
+};
+
+#endif /* _LINUX_IONIC_HWMON_H_ */

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next 3/4] ionic: add an auxiliary device for hardware monitoring
  2026-11-03 18:14 [PATCH net-next 0/4] ionic: report on-board sensors through an hwmon auxiliary driver Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 1/4] ionic: register auxiliary devices from a table of device types Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 2/4] ionic: add device interface definitions for hardware monitoring Nikhil P. Rao
@ 2026-11-03 18:14 ` Nikhil P. Rao
  2026-11-03 18:14 ` [PATCH net-next 4/4] hwmon: add AMD Pensando ionic sensor driver Nikhil P. Rao
  3 siblings, 0 replies; 6+ messages in thread
From: Nikhil P. Rao @ 2026-11-03 18:14 UTC (permalink / raw)
  To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Guenter Roeck, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Kees Cook, Gustavo A. R. Silva
  Cc: netdev, linux-kernel, linux-hwmon, linux-doc, linux-hardening,
	Abhijit Gangurde, Nikhil P. Rao

When the firmware sets IONIC_DEV_CAP_HWMON, create an "hwmon" auxiliary
device so the adapter's sensors can be exposed by a driver in
drivers/hwmon/ rather than by the Ethernet driver.

The GETCAPS response buffer belongs to ionic for the life of the device,
not to the hwmon driver, so a command abandoned on timeout or unbind can
only write into that buffer, never into freed memory.

Reviewed-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic.h     |   2 +
 drivers/net/ethernet/pensando/ionic/ionic_aux.c | 109 ++++++++++++++++++++++++
 drivers/net/ethernet/pensando/ionic/ionic_lif.h |   1 +
 include/linux/ionic/ionic_hwmon.h               |  40 +++++++++
 4 files changed, 152 insertions(+)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h
index 555a77db1b1e..814d70b8623b 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic.h
@@ -64,6 +64,8 @@ struct ionic {
 	int num_vfs;
 	struct timer_list watchdog_timer;
 	int watchdog_period;
+	void *hwmon_caps_buf;
+	dma_addr_t hwmon_caps_pa;
 };
 
 int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_aux.c b/drivers/net/ethernet/pensando/ionic/ionic_aux.c
index 370a7eff3e28..521f3bd9aee1 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_aux.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_aux.c
@@ -2,6 +2,8 @@
 /* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
 
 #include <linux/kernel.h>
+#include <linux/ionic/ionic_hwmon.h>
+
 #include "ionic.h"
 #include "ionic_lif.h"
 #include "ionic_aux.h"
@@ -17,11 +19,39 @@ static bool ionic_rdma_supported(struct ionic *ionic)
 	return le64_to_cpu(ionic->ident.lif.capabilities) & IONIC_LIF_CAP_RDMA;
 }
 
+static bool ionic_hwmon_supported(struct ionic *ionic)
+{
+	return le64_to_cpu(ionic->ident.dev.capabilities) & IONIC_DEV_CAP_HWMON;
+}
+
+static int ionic_hwmon_prepare(struct ionic *ionic)
+{
+	void *buf;
+
+	if (ionic->hwmon_caps_buf)
+		return 0;
+
+	buf = dmam_alloc_coherent(ionic->dev, IONIC_HWMON_CAPS_BUF_SIZE,
+				  &ionic->hwmon_caps_pa, GFP_KERNEL);
+	if (!buf) {
+		dev_warn(ionic->dev, "Failed to allocate hwmon buffer\n");
+		return -ENOMEM;
+	}
+
+	ionic->hwmon_caps_buf = buf;
+	return 0;
+}
+
 static const struct ionic_aux_type_info ionic_aux_types[IONIC_AUX_MAX] = {
 	[IONIC_AUX_RDMA] = {
 		.name = "rdma",
 		.supported = ionic_rdma_supported,
 	},
+	[IONIC_AUX_HWMON] = {
+		.name = "hwmon",
+		.supported = ionic_hwmon_supported,
+		.prepare = ionic_hwmon_prepare,
+	},
 };
 
 static void ionic_auxbus_release(struct device *dev)
@@ -117,6 +147,85 @@ void ionic_auxbus_unregister(struct ionic_lif *lif)
 	mutex_unlock(&lif->adev_lock);
 }
 
+static struct ionic_lif *ionic_hwmon_lif(struct auxiliary_device *adev)
+{
+	return container_of(adev, struct ionic_aux_dev, adev)->lif;
+}
+
+int ionic_hwmon_get_caps(struct auxiliary_device *adev, u8 max_ver,
+			 const void **buf, size_t *len, u8 *ver)
+{
+	struct ionic_lif *lif = ionic_hwmon_lif(adev);
+	struct ionic *ionic = lif->ionic;
+	struct ionic_admin_ctx ctx = {
+		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+		.comp.comp.status = IONIC_RC_EIO,
+		.cmd.hwmon_getcaps = {
+			.opcode = IONIC_CMD_HWMON_GETCAPS,
+			.ver = max_ver,
+			.lif_index = cpu_to_le16(lif->index),
+			.resp_buf_len = cpu_to_le32(IONIC_HWMON_CAPS_BUF_SIZE),
+			.resp_dma_addr = cpu_to_le64(ionic->hwmon_caps_pa),
+		},
+	};
+	int err;
+
+	if (!ionic->hwmon_caps_buf)
+		return -ENODEV;
+
+	memset(ionic->hwmon_caps_buf, 0, IONIC_HWMON_CAPS_BUF_SIZE);
+
+	err = ionic_adminq_post_wait_nomsg(lif, &ctx);
+	if (err)
+		return err;
+
+	if (ctx.comp.comp.status)
+		return ionic_error_to_errno(ctx.comp.comp.status);
+
+	*buf = ionic->hwmon_caps_buf;
+	*len = IONIC_HWMON_CAPS_BUF_SIZE;
+	*ver = ctx.comp.hwmon_getcaps.ver;
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ionic_hwmon_get_caps, "NET_IONIC");
+
+int ionic_hwmon_get_attr(struct auxiliary_device *adev, u8 type, u8 channel,
+			 u8 attr, u32 *val)
+{
+	struct ionic_lif *lif = ionic_hwmon_lif(adev);
+	struct ionic_admin_ctx ctx = {
+		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
+		.comp.comp.status = IONIC_RC_EIO,
+		.cmd.hwmon_getattr = {
+			.opcode = IONIC_CMD_HWMON_GETATTR,
+			.lif_index = cpu_to_le16(lif->index),
+			.sensor_type = type,
+			.channel = channel,
+			.attr = attr,
+		},
+	};
+	int err;
+
+	err = ionic_adminq_post_wait_nomsg(lif, &ctx);
+	if (err)
+		return err;
+
+	if (ctx.comp.comp.status)
+		return ionic_error_to_errno(ctx.comp.comp.status);
+
+	*val = le32_to_cpu(ctx.comp.hwmon_getattr.attr_value);
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ionic_hwmon_get_attr, "NET_IONIC");
+
+struct device *ionic_hwmon_dev(struct auxiliary_device *adev)
+{
+	return ionic_hwmon_lif(adev)->ionic->dev;
+}
+EXPORT_SYMBOL_NS_GPL(ionic_hwmon_dev, "NET_IONIC");
+
 void ionic_request_rdma_reset(struct ionic_lif *lif)
 {
 	struct ionic *ionic = lif->ionic;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index d04503b19c51..95bcfcded562 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -182,6 +182,7 @@ struct ionic_phc;
 
 enum ionic_aux_type {
 	IONIC_AUX_RDMA,
+	IONIC_AUX_HWMON,
 	IONIC_AUX_MAX,
 };
 
diff --git a/include/linux/ionic/ionic_hwmon.h b/include/linux/ionic/ionic_hwmon.h
index 62667e3d7135..bc82f2903386 100644
--- a/include/linux/ionic/ionic_hwmon.h
+++ b/include/linux/ionic/ionic_hwmon.h
@@ -135,4 +135,44 @@ struct ionic_hwmon_caps_hdr {
 	__le16	n_channels[];
 };
 
+struct auxiliary_device;
+struct device;
+
+/**
+ * ionic_hwmon_get_caps - Read the sensor capabilities from the device
+ * @adev:    ionic "hwmon" auxiliary device
+ * @max_ver: highest capabilities buffer layout version the caller parses
+ * @buf:     set to the capabilities buffer
+ * @len:     set to the length of @buf
+ * @ver:     set to the layout version of @buf, as reported by the device
+ *
+ * The buffer belongs to ionic and is overwritten by the next call.
+ *
+ * Return: zero or negative error status
+ */
+int ionic_hwmon_get_caps(struct auxiliary_device *adev, u8 max_ver,
+			 const void **buf, size_t *len, u8 *ver);
+
+/**
+ * ionic_hwmon_get_attr - Read one sensor attribute from the device
+ * @adev:    ionic "hwmon" auxiliary device
+ * @type:    sensor type from enum ionic_hwmon_sensor_type
+ * @channel: channel index within @type
+ * @attr:    attribute ID from the attribute enum for @type
+ * @val:     set to the attribute value, in the units documented for
+ *           struct ionic_hwmon_getattr_comp
+ *
+ * Return: zero or negative error status
+ */
+int ionic_hwmon_get_attr(struct auxiliary_device *adev, u8 type, u8 channel,
+			 u8 attr, u32 *val);
+
+/**
+ * ionic_hwmon_dev - Device to parent the hwmon device to
+ * @adev: ionic "hwmon" auxiliary device
+ *
+ * Return: the PCI function's device
+ */
+struct device *ionic_hwmon_dev(struct auxiliary_device *adev);
+
 #endif /* _LINUX_IONIC_HWMON_H_ */

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next 4/4] hwmon: add AMD Pensando ionic sensor driver
  2026-11-03 18:14 [PATCH net-next 0/4] ionic: report on-board sensors through an hwmon auxiliary driver Nikhil P. Rao
                   ` (2 preceding siblings ...)
  2026-11-03 18:14 ` [PATCH net-next 3/4] ionic: add an auxiliary device " Nikhil P. Rao
@ 2026-11-03 18:14 ` Nikhil P. Rao
  2026-11-03 20:50   ` Guenter Roeck
  3 siblings, 1 reply; 6+ messages in thread
From: Nikhil P. Rao @ 2026-11-03 18:14 UTC (permalink / raw)
  To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Guenter Roeck, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Kees Cook, Gustavo A. R. Silva
  Cc: netdev, linux-kernel, linux-hwmon, linux-doc, linux-hardening,
	Abhijit Gangurde, Arun Kumar Prakash, Eric Joyner, Nikhil P. Rao

From: Arun Kumar Prakash <ArunKumar.Prakash@amd.com>

Add an auxiliary driver that binds to the "ionic.hwmon" device created
by the ionic Ethernet driver and reports the adapter's temperature,
voltage and power sensors through hwmon.

The sensor set depends on the board and firmware, so the driver reads
it from the device at probe rather than hard-coding it. A sensor type
with a channel the driver does not recognise is skipped as a whole,
since hwmon channel numbers must match the firmware's.

The hwmon device is parented to the adapter's PCI function rather than
to the auxiliary device, so lm-sensors names it by PCI address instead
of as a virtual device.

Signed-off-by: Arun Kumar Prakash <ArunKumar.Prakash@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
Reviewed-by: Brett Creeley <brett.creeley@amd.com>
Co-developed-by: Nikhil P. Rao <nikhil.rao@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
 Documentation/hwmon/index.rst       |   1 +
 Documentation/hwmon/ionic-hwmon.rst |  61 +++++
 MAINTAINERS                         |   8 +
 drivers/hwmon/Kconfig               |  13 +
 drivers/hwmon/Makefile              |   1 +
 drivers/hwmon/ionic-hwmon.c         | 492 ++++++++++++++++++++++++++++++++++++
 6 files changed, 576 insertions(+)

diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 199ca79621e9..91ecaca9edcd 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -106,6 +106,7 @@ Hardware Monitoring Kernel Drivers
    ina3221
    inspur-ipsps1
    intel-m10-bmc-hwmon
+   ionic-hwmon
    ir35221
    ir38064
    ir36021
diff --git a/Documentation/hwmon/ionic-hwmon.rst b/Documentation/hwmon/ionic-hwmon.rst
new file mode 100644
index 000000000000..23035d617365
--- /dev/null
+++ b/Documentation/hwmon/ionic-hwmon.rst
@@ -0,0 +1,61 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: <isonum.txt>
+
+Kernel driver ionic-hwmon
+=========================
+
+Supported chips:
+
+  * AMD Pensando Ethernet adapters whose firmware advertises
+    ``IONIC_DEV_CAP_HWMON``
+
+    Prefix: 'ionic'
+
+Author:
+
+	Arun Kumar Prakash <ArunKumar.Prakash@amd.com>
+
+Description
+-----------
+
+AMD Pensando Ethernet adapters carry on-board sensors that the adapter
+firmware can report.  The ionic Ethernet driver creates an ``ionic.hwmon``
+auxiliary device for adapters whose firmware advertises sensors, and the
+ionic-hwmon driver binds to it and exposes the readings to the Linux hwmon
+subsystem, so userspace utilities such as ``sensors`` can read them.  The
+hwmon device is a child of the adapter's PCI function.
+
+The adapter reports which sensors it has when the hwmon device is created,
+so the files that appear depend on the adapter and its firmware version.
+The hwmon device is removed and re-created across a firmware recovery, and
+the sensors are read again when it is re-created.
+
+The labels are reported once, with the capabilities, and do not change while
+the device is bound.
+
+sysfs interface
+---------------
+
+Attribute files exist only for the channels and attributes the firmware
+reports, within ``/sys/class/hwmon/hwmon[X]``:
+
+========================= ======= =====================================
+Name                      Perm    Description
+========================= ======= =====================================
+``temp[X]_input``         RO      Temperature in millidegrees Celsius
+                                  (m\ |deg|\ C).  May read below zero.
+``temp[X]_label``         RO      Temperature sensor label.
+``temp[X]_crit``          RO      Critical temperature threshold, in
+                                  millidegrees Celsius.
+``temp[X]_crit_alarm``    RO      Critical temperature alarm indicator.
+``in[X]_input``           RO      Voltage in millivolts (mV).
+``in[X]_label``           RO      Voltage sensor label.
+``power[X]_input``        RO      Power in microwatts (uW).
+``power[X]_label``        RO      Power sensor label.
+``power[X]_max``          RO      Maximum power threshold, in
+                                  microwatts.
+========================= ======= =====================================
+
+Labels are strings supplied by the firmware.  A channel that reports an
+empty label has no ``_label`` attribute.
diff --git a/MAINTAINERS b/MAINTAINERS
index 220b26c73b37..a4934e9206e2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1276,6 +1276,14 @@ F:	Documentation/networking/device_drivers/ethernet/amd/pds_core.rst
 F:	drivers/net/ethernet/amd/pds_core/
 F:	include/linux/pds/
 
+AMD PENSANDO IONIC HARDWARE MONITORING DRIVER
+M:	Brett Creeley <brett.creeley@amd.com>
+L:	linux-hwmon@vger.kernel.org
+S:	Maintained
+F:	Documentation/hwmon/ionic-hwmon.rst
+F:	drivers/hwmon/ionic-hwmon.c
+F:	include/linux/ionic/ionic_hwmon.h
+
 AMD PENSANDO RDMA DRIVER
 M:	Abhijit Gangurde <abhijit.gangurde@amd.com>
 M:	Allen Hubbe <allen.hubbe@amd.com>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 18c682e1a475..a832eae289f8 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -914,6 +914,19 @@ config SENSORS_IIO_HWMON
 	  for those channels specified in the map.  This map can be provided
 	  either via platform data or the device tree bindings.
 
+config SENSORS_IONIC
+	tristate "AMD Pensando ionic adapter sensors"
+	depends on IONIC
+	help
+	  If you say yes here you get support for the on-board sensors of
+	  AMD Pensando Ethernet adapters, such as temperature, voltage and
+	  power.  The adapter firmware describes which sensors are present,
+	  so the set of attributes varies between adapters and firmware
+	  versions.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called ionic-hwmon.
+
 config SENSORS_I5500
 	tristate "Intel 5500/5520/X58 temperature sensor"
 	depends on X86 && PCI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d4c345ed7261..553e51cb3c9f 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -111,6 +111,7 @@ obj-$(CONFIG_SENSORS_INA2XX)	+= ina2xx.o
 obj-$(CONFIG_SENSORS_INA238)	+= ina238.o
 obj-$(CONFIG_SENSORS_INA3221)	+= ina3221.o
 obj-$(CONFIG_SENSORS_INTEL_M10_BMC_HWMON) += intel-m10-bmc-hwmon.o
+obj-$(CONFIG_SENSORS_IONIC)	+= ionic-hwmon.o
 obj-$(CONFIG_SENSORS_ISL28022)	+= isl28022.o
 obj-$(CONFIG_SENSORS_IT87)	+= it87.o
 obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
diff --git a/drivers/hwmon/ionic-hwmon.c b/drivers/hwmon/ionic-hwmon.c
new file mode 100644
index 000000000000..78143df75a8f
--- /dev/null
+++ b/drivers/hwmon/ionic-hwmon.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2026 Advanced Micro Devices, Inc */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/ctype.h>
+#include <linux/hwmon.h>
+#include <linux/ionic/ionic_hwmon.h>
+#include <linux/module.h>
+
+struct ionic_hwmon {
+	struct auxiliary_device *adev;
+	struct device *dev;
+	struct hwmon_chip_info chip_info;
+	struct hwmon_channel_info chan_info[IONIC_HWMON_TYPE_MAX];
+	const struct hwmon_channel_info *info[IONIC_HWMON_TYPE_MAX + 1];
+	u16 n_channels[IONIC_HWMON_TYPE_MAX];
+	u16 total_channels;
+	struct ionic_hwmon_chan_caps channels[] __counted_by(total_channels);
+};
+
+static const enum hwmon_sensor_types
+ionic_hwmon_linux_type[IONIC_HWMON_TYPE_MAX] = {
+	[IONIC_HWMON_TYPE_TEMP]		= hwmon_temp,
+	[IONIC_HWMON_TYPE_VOLTAGE]	= hwmon_in,
+	[IONIC_HWMON_TYPE_POWER]	= hwmon_power,
+	[IONIC_HWMON_TYPE_CHIP]		= hwmon_chip,
+	[IONIC_HWMON_TYPE_CURRENT]	= hwmon_curr,
+	[IONIC_HWMON_TYPE_ENERGY]	= hwmon_energy,
+	[IONIC_HWMON_TYPE_HUMIDITY]	= hwmon_humidity,
+	[IONIC_HWMON_TYPE_FAN]		= hwmon_fan,
+	[IONIC_HWMON_TYPE_PWM]		= hwmon_pwm,
+	[IONIC_HWMON_TYPE_INTRUSION]	= hwmon_intrusion,
+};
+
+static const char * const ionic_hwmon_type_name[IONIC_HWMON_TYPE_MAX] = {
+	[IONIC_HWMON_TYPE_TEMP]		= "temp",
+	[IONIC_HWMON_TYPE_VOLTAGE]	= "in",
+	[IONIC_HWMON_TYPE_POWER]	= "power",
+	[IONIC_HWMON_TYPE_CHIP]		= "chip",
+	[IONIC_HWMON_TYPE_CURRENT]	= "curr",
+	[IONIC_HWMON_TYPE_ENERGY]	= "energy",
+	[IONIC_HWMON_TYPE_HUMIDITY]	= "humidity",
+	[IONIC_HWMON_TYPE_FAN]		= "fan",
+	[IONIC_HWMON_TYPE_PWM]		= "pwm",
+	[IONIC_HWMON_TYPE_INTRUSION]	= "intrusion",
+};
+
+static unsigned int ionic_hwmon_sysfs_channel(unsigned int ionic_type,
+					      unsigned int channel)
+{
+	enum hwmon_sensor_types type = ionic_hwmon_linux_type[ionic_type];
+
+	if (type == hwmon_in || type == hwmon_intrusion)
+		return channel;
+
+	return channel + 1;
+}
+
+static unsigned int ionic_hwmon_chan_idx(const struct ionic_hwmon *hwmon,
+					 enum hwmon_sensor_types type,
+					 int channel)
+{
+	unsigned int i, start = 0;
+
+	for (i = 0; i < IONIC_HWMON_TYPE_MAX; i++) {
+		if (ionic_hwmon_linux_type[i] == type)
+			break;
+		start += hwmon->n_channels[i];
+	}
+
+	return start + channel;
+}
+
+struct ionic_hwmon_attr {
+	u8 ionic;
+	u8 hwmon;
+};
+
+struct ionic_hwmon_attr_map {
+	const struct ionic_hwmon_attr *attrs;
+	unsigned int n_attrs;
+};
+
+static const struct ionic_hwmon_attr ionic_hwmon_temp_attr_map[] = {
+	{ IONIC_HWMON_TEMP_ATTR_INPUT,		hwmon_temp_input },
+	{ IONIC_HWMON_TEMP_ATTR_LABEL,		hwmon_temp_label },
+	{ IONIC_HWMON_TEMP_ATTR_CRIT,		hwmon_temp_crit },
+	{ IONIC_HWMON_TEMP_ATTR_CRIT_ALARM,	hwmon_temp_crit_alarm },
+};
+
+static const struct ionic_hwmon_attr ionic_hwmon_voltage_attr_map[] = {
+	{ IONIC_HWMON_VOLTAGE_ATTR_INPUT,	hwmon_in_input },
+	{ IONIC_HWMON_VOLTAGE_ATTR_LABEL,	hwmon_in_label },
+};
+
+static const struct ionic_hwmon_attr ionic_hwmon_power_attr_map[] = {
+	{ IONIC_HWMON_POWER_ATTR_INPUT,		hwmon_power_input },
+	{ IONIC_HWMON_POWER_ATTR_LABEL,		hwmon_power_label },
+	{ IONIC_HWMON_POWER_ATTR_MAX,		hwmon_power_max },
+};
+
+static const struct ionic_hwmon_attr_map ionic_hwmon_attr_maps[] = {
+	[hwmon_temp]	= { ionic_hwmon_temp_attr_map,
+			    ARRAY_SIZE(ionic_hwmon_temp_attr_map) },
+	[hwmon_in]	= { ionic_hwmon_voltage_attr_map,
+			    ARRAY_SIZE(ionic_hwmon_voltage_attr_map) },
+	[hwmon_power]	= { ionic_hwmon_power_attr_map,
+			    ARRAY_SIZE(ionic_hwmon_power_attr_map) },
+};
+
+static const struct ionic_hwmon_attr_map *
+ionic_hwmon_attr_map(enum hwmon_sensor_types type)
+{
+	if (type >= ARRAY_SIZE(ionic_hwmon_attr_maps) ||
+	    !ionic_hwmon_attr_maps[type].n_attrs)
+		return NULL;
+
+	return &ionic_hwmon_attr_maps[type];
+}
+
+static int ionic_hwmon_attr_to_ionic_attr(enum hwmon_sensor_types type,
+					  u32 attr, unsigned int *ionic_attr)
+{
+	const struct ionic_hwmon_attr_map *map = ionic_hwmon_attr_map(type);
+	unsigned int i;
+
+	if (!map)
+		return -EOPNOTSUPP;
+
+	for (i = 0; i < map->n_attrs; i++) {
+		if (map->attrs[i].hwmon == attr) {
+			*ionic_attr = map->attrs[i].ionic;
+			return 0;
+		}
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static void ionic_hwmon_check_labels(struct device *dev,
+				     struct ionic_hwmon *hwmon)
+{
+	unsigned int type, j, idx = 0;
+	char *label, *p;
+
+	for (type = 0; type < IONIC_HWMON_TYPE_MAX; type++) {
+		for (j = 0; j < hwmon->n_channels[type]; j++, idx++) {
+			label = hwmon->channels[idx].label;
+			label[sizeof(hwmon->channels[idx].label) - 1] = '\0';
+
+			for (p = label; *p; p++) {
+				if (isascii(*p) && isprint(*p))
+					continue;
+				dev_warn(dev,
+					 "%s%u_label is not printable, ignoring it\n",
+					 ionic_hwmon_type_name[type],
+					 ionic_hwmon_sysfs_channel(type, j));
+				label[0] = '\0';
+				break;
+			}
+		}
+	}
+}
+
+static struct ionic_hwmon *ionic_hwmon_create(struct device *dev,
+					      struct auxiliary_device *adev,
+					      const void *buf, size_t buf_len)
+{
+	unsigned int channels_offset, num_types, total_channels, i;
+	const struct ionic_hwmon_caps_hdr *hdr = buf;
+	u16 chan_counts[IONIC_HWMON_TYPE_MAX];
+	struct ionic_hwmon *hwmon;
+	u16 fw_num_types;
+
+	if (buf_len < sizeof(*hdr))
+		return ERR_PTR(-EINVAL);
+
+	fw_num_types = le16_to_cpu(READ_ONCE(hdr->num_types));
+
+	channels_offset = struct_size(hdr, n_channels, fw_num_types);
+	if (channels_offset > buf_len)
+		return ERR_PTR(-EINVAL);
+
+	num_types = min_t(unsigned int, fw_num_types, IONIC_HWMON_TYPE_MAX);
+
+	total_channels = 0;
+	for (i = 0; i < num_types; i++) {
+		chan_counts[i] = le16_to_cpu(READ_ONCE(hdr->n_channels[i]));
+		total_channels += chan_counts[i];
+	}
+
+	if (total_channels >
+	    (buf_len - channels_offset) / sizeof(struct ionic_hwmon_chan_caps))
+		return ERR_PTR(-EINVAL);
+
+	hwmon = devm_kzalloc(dev, struct_size(hwmon, channels, total_channels),
+			     GFP_KERNEL);
+	if (!hwmon)
+		return ERR_PTR(-ENOMEM);
+
+	hwmon->adev = adev;
+
+	for (i = 0; i < num_types; i++)
+		hwmon->n_channels[i] = chan_counts[i];
+
+	hwmon->total_channels = total_channels;
+	memcpy(hwmon->channels, buf + channels_offset,
+	       flex_array_size(hwmon, channels, total_channels));
+
+	ionic_hwmon_check_labels(dev, hwmon);
+
+	return hwmon;
+}
+
+static umode_t ionic_hwmon_file_mode(u32 mode)
+{
+	if ((mode & ~IONIC_HWMON_MODE_MASK) || !(mode & IONIC_HWMON_MODE_READ))
+		return 0;
+
+	/* No attribute is writable: hwmon refuses to register the device if
+	 * any attribute has write permission without a .write op.
+	 */
+	return 0444;
+}
+
+static bool ionic_hwmon_attr_is_label(enum hwmon_sensor_types type, u32 attr)
+{
+	return (type == hwmon_temp && attr == hwmon_temp_label) ||
+	       (type == hwmon_in && attr == hwmon_in_label) ||
+	       (type == hwmon_power && attr == hwmon_power_label);
+}
+
+static umode_t ionic_hwmon_is_visible(const void *data,
+				      enum hwmon_sensor_types type,
+				      u32 attr, int channel)
+{
+	const struct ionic_hwmon_chan_caps *chan_caps;
+	const struct ionic_hwmon *hwmon = data;
+	unsigned int idx, ionic_attr;
+
+	if (ionic_hwmon_attr_to_ionic_attr(type, attr, &ionic_attr))
+		return 0;
+
+	idx = ionic_hwmon_chan_idx(hwmon, type, channel);
+	chan_caps = &hwmon->channels[idx];
+
+	if (ionic_hwmon_attr_is_label(type, attr) && !chan_caps->label[0])
+		return 0;
+
+	return ionic_hwmon_file_mode(le32_to_cpu(chan_caps->mode[ionic_attr]));
+}
+
+static int ionic_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+			    u32 attr, int channel, long *val)
+{
+	struct ionic_hwmon *hwmon = dev_get_drvdata(dev);
+	enum ionic_hwmon_sensor_type sensor_type;
+	unsigned int ionic_attr;
+	u32 data;
+	int err;
+
+	err = ionic_hwmon_attr_to_ionic_attr(type, attr, &ionic_attr);
+	if (err)
+		return err;
+
+	switch (type) {
+	case hwmon_temp:
+		sensor_type = IONIC_HWMON_TYPE_TEMP;
+		break;
+	case hwmon_in:
+		sensor_type = IONIC_HWMON_TYPE_VOLTAGE;
+		break;
+	case hwmon_power:
+		sensor_type = IONIC_HWMON_TYPE_POWER;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	err = ionic_hwmon_get_attr(hwmon->adev, sensor_type, channel,
+				   ionic_attr, &data);
+	if (err)
+		return err;
+
+	/* Temperatures are signed millidegrees Celsius and can read below
+	 * zero, so convert them to s32 rather than doing a zero extension.
+	 */
+	if (type == hwmon_temp) {
+		if (attr == hwmon_temp_crit_alarm)
+			*val = !!data;
+		else
+			*val = (s32)data;
+	} else {
+		*val = data;
+	}
+
+	/* The device reports power in milliwatts, sysfs wants microwatts */
+	if (type == hwmon_power)
+		*val *= 1000;
+
+	return 0;
+}
+
+static int ionic_hwmon_read_label(struct device *dev,
+				  enum hwmon_sensor_types type,
+				  u32 attr, int channel, const char **str)
+{
+	struct ionic_hwmon *hwmon = dev_get_drvdata(dev);
+	unsigned int idx;
+
+	idx = ionic_hwmon_chan_idx(hwmon, type, channel);
+	*str = hwmon->channels[idx].label;
+
+	return 0;
+}
+
+static const struct hwmon_ops ionic_hwmon_ops = {
+	.is_visible = ionic_hwmon_is_visible,
+	.read = ionic_hwmon_read,
+	.read_string = ionic_hwmon_read_label,
+};
+
+static u32
+ionic_hwmon_attr_caps_to_config(u32 attr_caps,
+				const struct ionic_hwmon_attr_map *map)
+{
+	unsigned int i;
+	u32 config = 0;
+
+	for (i = 0; i < map->n_attrs; i++) {
+		if (attr_caps & BIT(map->attrs[i].ionic))
+			config |= BIT(map->attrs[i].hwmon);
+	}
+
+	return config;
+}
+
+static u32 *
+ionic_hwmon_type_config(struct device *dev, unsigned int ionic_type,
+			const struct ionic_hwmon_attr_map *map,
+			const struct ionic_hwmon_chan_caps *chan_caps,
+			unsigned int n_channels)
+{
+	unsigned int j;
+	u32 *config;
+
+	config = devm_kcalloc(dev, n_channels + 1, sizeof(*config), GFP_KERNEL);
+	if (!config)
+		return ERR_PTR(-ENOMEM);
+
+	for (j = 0; j < n_channels; j++) {
+		u32 attr_caps = le32_to_cpu(chan_caps[j].attr_caps);
+
+		config[j] = ionic_hwmon_attr_caps_to_config(attr_caps, map);
+		if (!config[j]) {
+			dev_warn(dev,
+				 "%s%u has no usable attributes, ignoring all %s channels\n",
+				 ionic_hwmon_type_name[ionic_type],
+				 ionic_hwmon_sysfs_channel(ionic_type, j),
+				 ionic_hwmon_type_name[ionic_type]);
+			devm_kfree(dev, config);
+			return NULL;
+		}
+	}
+
+	return config;
+}
+
+static int ionic_hwmon_build_channel_info(struct device *dev,
+					  struct ionic_hwmon *hwmon)
+{
+	unsigned int i, n_types, chan_idx;
+
+	chan_idx = 0;
+	n_types = 0;
+
+	for (i = 0; i < IONIC_HWMON_TYPE_MAX; i++) {
+		enum hwmon_sensor_types type = ionic_hwmon_linux_type[i];
+		unsigned int n_channels = hwmon->n_channels[i];
+		const struct ionic_hwmon_chan_caps *chan_caps;
+		const struct ionic_hwmon_attr_map *map;
+		struct hwmon_channel_info *chan_info;
+		u32 *config;
+
+		if (!n_channels)
+			continue;
+
+		chan_caps = &hwmon->channels[chan_idx];
+		chan_idx += n_channels;
+
+		map = ionic_hwmon_attr_map(type);
+		if (!map)
+			continue;
+
+		config = ionic_hwmon_type_config(dev, i, map, chan_caps,
+						 n_channels);
+		if (IS_ERR(config))
+			return PTR_ERR(config);
+
+		if (!config)
+			continue;
+
+		chan_info = &hwmon->chan_info[n_types];
+		chan_info->type = type;
+		chan_info->config = config;
+		hwmon->info[n_types++] = chan_info;
+	}
+
+	return n_types ? 0 : -ENODEV;
+}
+
+static int ionic_hwmon_probe(struct auxiliary_device *adev,
+			     const struct auxiliary_device_id *id)
+{
+	struct device *dev = &adev->dev;
+	struct ionic_hwmon *hwmon;
+	const void *buf;
+	size_t len;
+	int err;
+	u8 ver;
+
+	err = ionic_hwmon_get_caps(adev, IONIC_HWMON_CAPS_VERSION_0, &buf,
+				   &len, &ver);
+	if (err)
+		return dev_err_probe(dev, err,
+				     "cannot read sensor capabilities\n");
+
+	if (ver != IONIC_HWMON_CAPS_VERSION_0)
+		return dev_err_probe(dev, -EOPNOTSUPP,
+				     "unsupported capabilities version %u\n",
+				     ver);
+
+	hwmon = ionic_hwmon_create(dev, adev, buf, len);
+	if (IS_ERR(hwmon))
+		return dev_err_probe(dev, PTR_ERR(hwmon),
+				     "invalid sensor capabilities\n");
+
+	err = ionic_hwmon_build_channel_info(dev, hwmon);
+	if (err == -ENODEV) {
+		dev_info(dev, "no sensors this driver supports\n");
+		return err;
+	}
+	if (err)
+		return dev_err_probe(dev, err,
+				     "cannot build sensor configuration\n");
+
+	hwmon->chip_info.ops = &ionic_hwmon_ops;
+	hwmon->chip_info.info = hwmon->info;
+
+	/* The hwmon device is parented to the PCI function, not to adev, so
+	 * userspace finds the sensors by PCI endpoint.  Its chip_info and
+	 * config arrays are devres on adev, so it is unregistered in remove
+	 * rather than through devm.
+	 */
+	hwmon->dev = hwmon_device_register_with_info(ionic_hwmon_dev(adev),
+						     "ionic", hwmon,
+						     &hwmon->chip_info, NULL);
+	if (IS_ERR(hwmon->dev))
+		return dev_err_probe(dev, PTR_ERR(hwmon->dev),
+				     "cannot register hwmon device\n");
+
+	auxiliary_set_drvdata(adev, hwmon);
+
+	return 0;
+}
+
+static void ionic_hwmon_remove(struct auxiliary_device *adev)
+{
+	struct ionic_hwmon *hwmon = auxiliary_get_drvdata(adev);
+
+	/* Unregister before the devres on adev frees chip_info and config */
+	hwmon_device_unregister(hwmon->dev);
+}
+
+static const struct auxiliary_device_id ionic_hwmon_id_table[] = {
+	{ .name = "ionic.hwmon", },
+	{}
+};
+MODULE_DEVICE_TABLE(auxiliary, ionic_hwmon_id_table);
+
+static struct auxiliary_driver ionic_hwmon_driver = {
+	.name = "hwmon",
+	.probe = ionic_hwmon_probe,
+	.remove = ionic_hwmon_remove,
+	.id_table = ionic_hwmon_id_table,
+};
+module_auxiliary_driver(ionic_hwmon_driver);
+
+MODULE_AUTHOR("Arun Kumar Prakash <ArunKumar.Prakash@amd.com>");
+MODULE_DESCRIPTION("AMD Pensando ionic hardware monitoring driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("NET_IONIC");

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next 4/4] hwmon: add AMD Pensando ionic sensor driver
  2026-11-03 18:14 ` [PATCH net-next 4/4] hwmon: add AMD Pensando ionic sensor driver Nikhil P. Rao
@ 2026-11-03 20:50   ` Guenter Roeck
  0 siblings, 0 replies; 6+ messages in thread
From: Guenter Roeck @ 2026-11-03 20:50 UTC (permalink / raw)
  To: Nikhil P. Rao, Brett Creeley, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Kees Cook, Gustavo A. R. Silva
  Cc: netdev, linux-kernel, linux-hwmon, linux-doc, linux-hardening,
	Abhijit Gangurde, Arun Kumar Prakash, Eric Joyner

On 11/3/26 10:14, Nikhil P. Rao wrote:
> From: Arun Kumar Prakash <ArunKumar.Prakash@amd.com>
> 
> Add an auxiliary driver that binds to the "ionic.hwmon" device created
> by the ionic Ethernet driver and reports the adapter's temperature,
> voltage and power sensors through hwmon.
> 
> The sensor set depends on the board and firmware, so the driver reads
> it from the device at probe rather than hard-coding it. A sensor type
> with a channel the driver does not recognise is skipped as a whole,
> since hwmon channel numbers must match the firmware's.
> 
> The hwmon device is parented to the adapter's PCI function rather than
> to the auxiliary device, so lm-sensors names it by PCI address instead
> of as a virtual device.
> 
> Signed-off-by: Arun Kumar Prakash <ArunKumar.Prakash@amd.com>
> Signed-off-by: Eric Joyner <eric.joyner@amd.com>
> Reviewed-by: Brett Creeley <brett.creeley@amd.com>
> Co-developed-by: Nikhil P. Rao <nikhil.rao@amd.com>
> Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>

Acked-by: Guenter Roeck <linux@roeck-us.net>

Thanks,
Guenter


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-03 20:50 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 18:14 [PATCH net-next 0/4] ionic: report on-board sensors through an hwmon auxiliary driver Nikhil P. Rao
2026-11-03 18:14 ` [PATCH net-next 1/4] ionic: register auxiliary devices from a table of device types Nikhil P. Rao
2026-11-03 18:14 ` [PATCH net-next 2/4] ionic: add device interface definitions for hardware monitoring Nikhil P. Rao
2026-11-03 18:14 ` [PATCH net-next 3/4] ionic: add an auxiliary device " Nikhil P. Rao
2026-11-03 18:14 ` [PATCH net-next 4/4] hwmon: add AMD Pensando ionic sensor driver Nikhil P. Rao
2026-11-03 20:50   ` Guenter Roeck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®