From: Zinc Lim <limzhineng2@gmail.com>
To: alexanderduyck@fb.com, kuba@kernel.org, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@kernel.org, pabeni@redhat.com,
linux@roeck-us.net, horms@kernel.org, hmohsin@meta.com,
mike.marciniszyn@gmail.com
Cc: kernel-team@meta.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org,
zinclim@meta.com, limzhineng2@gmail.com
Subject: [PATCH net-next v4 3/6] eth: fbnic: report temperature and voltage thresholds via hwmon
Date: Wed, 14 Oct 2026 15:22:21 -0700 [thread overview]
Message-ID: <20261014222225.2803660-4-limzhineng2@gmail.com> (raw)
In-Reply-To: <20261014222225.2803660-1-limzhineng2@gmail.com>
The firmware capability response carries per-board temperature
(min/max/crit) and voltage (min/max) thresholds. Parse and store them in
fbnic_fw_cap, and expose them through the hwmon interface as
temp1_{min,max,crit} and in0_{min,max}.
Zero is a valid threshold, so the fields are reset to the
FBNIC_SENSOR_NO_DATA sentinel in fbnic_mbx_init() as
fbnic_fw_parse_cap_resp() can return before it reaches the threshold
assignments.
Older firmware that do not report thresholds result in
fbnic_hwmon_is_visible() not exposing the thresholds in the sysfs,
only temp1_input and in0_input.
The mailbox is reinitialized on resume and AER recovery while the hwmon
device stays registered, so reads also reject the sentinel with -ENODATA
rather than reporting it as a value.
Signed-off-by: Zinc Lim <limzhineng2@gmail.com>
---
drivers/net/ethernet/meta/fbnic/fbnic_fw.c | 35 ++++++-
drivers/net/ethernet/meta/fbnic/fbnic_fw.h | 14 +++
drivers/net/ethernet/meta/fbnic/fbnic_hwmon.c | 98 ++++++++++++++++---
3 files changed, 129 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
index 82307f212f58..d9a00a2ef34d 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
@@ -95,15 +95,25 @@ static void fbnic_mbx_reset_desc_ring(struct fbnic_dev *fbd, int mbx_idx)
FBNIC_IPC_MBX_DESC_HOST_CMPL);
}
+static const struct fbnic_threshold fbnic_threshold_no_data = {
+ .min = FBNIC_SENSOR_NO_DATA,
+ .max = FBNIC_SENSOR_NO_DATA,
+ .crit = FBNIC_SENSOR_NO_DATA,
+};
+
void fbnic_mbx_init(struct fbnic_dev *fbd)
{
+ struct fbnic_fw_cap fw_cap_reset;
int i;
/* Initialize lock to protect Tx ring */
spin_lock_init(&fbd->fw_tx_lock);
- /* Reset FW Capabilities */
- memset(&fbd->fw_cap, 0, sizeof(fbd->fw_cap));
+ /* Reset FW Capabilities and seed thresholds with sentinel */
+ memset(&fw_cap_reset, 0, sizeof(fw_cap_reset));
+ fw_cap_reset.temp = fbnic_threshold_no_data;
+ fw_cap_reset.volt = fbnic_threshold_no_data;
+ fbd->fw_cap = fw_cap_reset;
/* Seed cache with sentinel so the first read always refreshes. The
* cache mirrors what firmware reported, so drop it when the mailbox
@@ -595,6 +605,11 @@ static const struct fbnic_tlv_index fbnic_fw_cap_resp_index[] = {
FBNIC_TLV_ATTR_STRING(FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR,
FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE),
FBNIC_TLV_ATTR_U32(FBNIC_FW_CAP_RESP_ANTI_ROLLBACK_VERSION),
+ FBNIC_TLV_ATTR_S32(FBNIC_FW_CAP_RESP_TEMP_MIN),
+ FBNIC_TLV_ATTR_S32(FBNIC_FW_CAP_RESP_TEMP_MAX),
+ FBNIC_TLV_ATTR_S32(FBNIC_FW_CAP_RESP_TEMP_CRIT),
+ FBNIC_TLV_ATTR_S32(FBNIC_FW_CAP_RESP_VOLT_MIN),
+ FBNIC_TLV_ATTR_S32(FBNIC_FW_CAP_RESP_VOLT_MAX),
FBNIC_TLV_ATTR_LAST
};
@@ -742,6 +757,22 @@ static int fbnic_fw_parse_cap_resp(void *opaque, struct fbnic_tlv_msg **results)
fbd->fw_cap.anti_rollback_version =
fta_get_uint(results, FBNIC_FW_CAP_RESP_ANTI_ROLLBACK_VERSION);
+ fbd->fw_cap.temp.min =
+ fbnic_tlv_attr_get_signed(results[FBNIC_FW_CAP_RESP_TEMP_MIN],
+ FBNIC_SENSOR_NO_DATA);
+ fbd->fw_cap.temp.max =
+ fbnic_tlv_attr_get_signed(results[FBNIC_FW_CAP_RESP_TEMP_MAX],
+ FBNIC_SENSOR_NO_DATA);
+ fbd->fw_cap.temp.crit =
+ fbnic_tlv_attr_get_signed(results[FBNIC_FW_CAP_RESP_TEMP_CRIT],
+ FBNIC_SENSOR_NO_DATA);
+ fbd->fw_cap.volt.min =
+ fbnic_tlv_attr_get_signed(results[FBNIC_FW_CAP_RESP_VOLT_MIN],
+ FBNIC_SENSOR_NO_DATA);
+ fbd->fw_cap.volt.max =
+ fbnic_tlv_attr_get_signed(results[FBNIC_FW_CAP_RESP_VOLT_MAX],
+ FBNIC_SENSOR_NO_DATA);
+
return 0;
}
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h
index ddedb7c0e0a7..dde2929f8660 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h
@@ -66,6 +66,12 @@ enum {
*/
#define FBNIC_SENSOR_NO_DATA S32_MIN
+struct fbnic_threshold {
+ s32 min;
+ s32 max;
+ s32 crit;
+};
+
struct fbnic_fw_cap {
unsigned long state;
struct {
@@ -79,6 +85,8 @@ struct fbnic_fw_cap {
u8 link_speed;
u8 link_fec;
u32 anti_rollback_version;
+ struct fbnic_threshold temp;
+ struct fbnic_threshold volt;
};
struct fbnic_fw_completion {
@@ -261,6 +269,12 @@ enum {
FBNIC_FW_CAP_RESP_UEFI_VERSION = 0x11,
FBNIC_FW_CAP_RESP_UEFI_COMMIT_STR = 0x12,
FBNIC_FW_CAP_RESP_ANTI_ROLLBACK_VERSION = 0x15,
+ /* 0x16 and 0x17 are reserved for future use */
+ FBNIC_FW_CAP_RESP_TEMP_MIN = 0x18,
+ FBNIC_FW_CAP_RESP_TEMP_MAX = 0x19,
+ FBNIC_FW_CAP_RESP_TEMP_CRIT = 0x1a,
+ FBNIC_FW_CAP_RESP_VOLT_MIN = 0x1b,
+ FBNIC_FW_CAP_RESP_VOLT_MAX = 0x1c,
FBNIC_FW_CAP_RESP_MSG_MAX
};
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_hwmon.c b/drivers/net/ethernet/meta/fbnic/fbnic_hwmon.c
index 0c3fe2a37227..4df808f850cf 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_hwmon.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_hwmon.c
@@ -7,24 +7,43 @@
#include "fbnic.h"
#include "fbnic_mac.h"
-static int fbnic_hwmon_sensor_id(enum hwmon_sensor_types type)
+static umode_t fbnic_hwmon_thr_visible(s32 thr)
{
- if (type == hwmon_temp)
- return FBNIC_SENSOR_TEMP;
- if (type == hwmon_in)
- return FBNIC_SENSOR_VOLTAGE;
-
- return -EOPNOTSUPP;
+ return thr == FBNIC_SENSOR_NO_DATA ? 0 : 0444;
}
static umode_t fbnic_hwmon_is_visible(const void *drvdata,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
- if (type == hwmon_temp && attr == hwmon_temp_input)
- return 0444;
- if (type == hwmon_in && attr == hwmon_in_input)
- return 0444;
+ const struct fbnic_dev *fbd = drvdata;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ case hwmon_temp_min:
+ return fbnic_hwmon_thr_visible(fbd->fw_cap.temp.min);
+ case hwmon_temp_max:
+ return fbnic_hwmon_thr_visible(fbd->fw_cap.temp.max);
+ case hwmon_temp_crit:
+ return fbnic_hwmon_thr_visible(fbd->fw_cap.temp.crit);
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ return 0444;
+ case hwmon_in_min:
+ return fbnic_hwmon_thr_visible(fbd->fw_cap.volt.min);
+ case hwmon_in_max:
+ return fbnic_hwmon_thr_visible(fbd->fw_cap.volt.max);
+ }
+ break;
+ default:
+ break;
+ }
return 0;
}
@@ -92,14 +111,58 @@ static int fbnic_hwmon_sensor_read(struct fbnic_dev *fbd, int id, long *val)
return err;
}
+static int fbnic_hwmon_read_threshold(long thr, long *val)
+{
+ if (thr == FBNIC_SENSOR_NO_DATA)
+ return -ENODATA;
+
+ *val = thr;
+ return 0;
+}
+
+static int fbnic_hwmon_temp_read(struct fbnic_dev *fbd, u32 attr, long *val)
+{
+ switch (attr) {
+ case hwmon_temp_input:
+ return fbnic_hwmon_sensor_read(fbd, FBNIC_SENSOR_TEMP, val);
+ case hwmon_temp_min:
+ return fbnic_hwmon_read_threshold(fbd->fw_cap.temp.min, val);
+ case hwmon_temp_max:
+ return fbnic_hwmon_read_threshold(fbd->fw_cap.temp.max, val);
+ case hwmon_temp_crit:
+ return fbnic_hwmon_read_threshold(fbd->fw_cap.temp.crit, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int fbnic_hwmon_in_read(struct fbnic_dev *fbd, u32 attr, long *val)
+{
+ switch (attr) {
+ case hwmon_in_input:
+ return fbnic_hwmon_sensor_read(fbd, FBNIC_SENSOR_VOLTAGE, val);
+ case hwmon_in_min:
+ return fbnic_hwmon_read_threshold(fbd->fw_cap.volt.min, val);
+ case hwmon_in_max:
+ return fbnic_hwmon_read_threshold(fbd->fw_cap.volt.max, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int fbnic_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct fbnic_dev *fbd = dev_get_drvdata(dev);
- int id;
- id = fbnic_hwmon_sensor_id(type);
- return id < 0 ? id : fbnic_hwmon_sensor_read(fbd, id, val);
+ switch (type) {
+ case hwmon_temp:
+ return fbnic_hwmon_temp_read(fbd, attr, val);
+ case hwmon_in:
+ return fbnic_hwmon_in_read(fbd, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
static const struct hwmon_ops fbnic_hwmon_ops = {
@@ -108,8 +171,11 @@ static const struct hwmon_ops fbnic_hwmon_ops = {
};
static const struct hwmon_channel_info *fbnic_hwmon_info[] = {
- HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
- HWMON_CHANNEL_INFO(in, HWMON_I_INPUT),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX),
NULL
};
--
2.53.0-Meta
next prev parent reply other threads:[~2026-10-14 22:22 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 22:22 [PATCH net-next v4 0/6] eth: fbnic: expand hwmon sensor support Zinc Lim
2026-10-14 22:22 ` [PATCH net-next v4 1/6] eth: fbnic: move sensor read logic out of fbnic_mac Zinc Lim
2026-10-15 22:22 ` sashiko-bot
2026-10-14 22:22 ` [PATCH net-next v4 2/6] eth: fbnic: cache hwmon sensor readings Zinc Lim
2026-10-15 22:22 ` sashiko-bot
2026-10-18 22:50 ` netdev-bot+sashiko
2026-10-14 22:22 ` Zinc Lim [this message]
2026-10-15 22:22 ` [PATCH net-next v4 3/6] eth: fbnic: report temperature and voltage thresholds via hwmon sashiko-bot
2026-10-18 22:50 ` netdev-bot+sashiko
2026-10-14 22:22 ` [PATCH net-next v4 4/6] eth: fbnic: parse the alarm range reported by firmware Zinc Lim
2026-10-15 22:22 ` sashiko-bot
2026-10-18 22:50 ` netdev-bot+sashiko
2026-10-14 22:22 ` [PATCH net-next v4 5/6] eth: fbnic: report temperature and voltage alarms via hwmon Zinc Lim
2026-10-15 22:22 ` sashiko-bot
2026-10-18 22:50 ` netdev-bot+sashiko
2026-10-14 22:22 ` [PATCH net-next v4 6/6] eth: fbnic: derive the alarm range on older firmware Zinc Lim
2026-10-15 22:22 ` sashiko-bot
2026-10-18 22:50 ` netdev-bot+sashiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261014222225.2803660-4-limzhineng2@gmail.com \
--to=limzhineng2@gmail.com \
--cc=alexanderduyck@fb.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=hmohsin@meta.com \
--cc=horms@kernel.org \
--cc=kernel-team@meta.com \
--cc=kuba@kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=mike.marciniszyn@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=zinclim@meta.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.