[PATCH net-next v4 4/6] eth: fbnic: parse the alarm range reported by firmware
From: Zinc Lim
Date: Wed Oct 14 2026 - 18:26:37 EST
Firmware reports which range a sensor is in: normal, below min, above
max, above crit. It sends the range in an unsolicited event on every
trip point crossing, including the crossing back into the normal range,
and in the response to a sensor read. Parse both and keep the last
reported range per sensor, so the driver holds a condition the hardware
reported.
The range is cached from the mailbox thread in both cases, so an event
and a read response cannot be applied out of the order firmware sent
them. It is seeded alongside the rest of the sensor cache in
fbnic_mbx_init(), before the mailbox interrupt is enabled, since an
event can arrive as soon as it is.
Firmware predating range reporting sends ALARM_DEPRECATED for every
threshold event and omits the range from the read response entirely.
Neither is cached, as neither says which limit was crossed.
Subsequent patches in this series will consume the cached range.
Signed-off-by: Zinc Lim <limzhineng2@xxxxxxxxx>
---
drivers/net/ethernet/meta/fbnic/fbnic.h | 2 +
drivers/net/ethernet/meta/fbnic/fbnic_fw.c | 84 ++++++++++++++++++++++
drivers/net/ethernet/meta/fbnic/fbnic_fw.h | 23 ++++++
3 files changed, 109 insertions(+)
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic.h b/drivers/net/ethernet/meta/fbnic/fbnic.h
index 857a64b518f3..93bf7004b67d 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic.h
@@ -26,6 +26,8 @@ struct fbnic_hwmon_cache {
unsigned long last_read;
s32 temp_mdeg;
s32 volt_mv;
+ u8 temp_alarm;
+ u8 volt_alarm;
};
struct fbnic_dev {
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
index d9a00a2ef34d..7863f6ba1e7d 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
@@ -121,6 +121,8 @@ void fbnic_mbx_init(struct fbnic_dev *fbd)
*/
fbd->hwmon_cache.temp_mdeg = FBNIC_SENSOR_NO_DATA;
fbd->hwmon_cache.volt_mv = FBNIC_SENSOR_NO_DATA;
+ fbd->hwmon_cache.temp_alarm = FBNIC_FW_TSENE_ALARM_UNKNOWN;
+ fbd->hwmon_cache.volt_alarm = FBNIC_FW_TSENE_ALARM_UNKNOWN;
/* Reinitialize mailbox memory */
for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++)
@@ -1466,6 +1468,8 @@ static const struct fbnic_tlv_index fbnic_tsene_read_resp_index[] = {
FBNIC_TLV_ATTR_S32(FBNIC_FW_TSENE_THERM),
FBNIC_TLV_ATTR_S32(FBNIC_FW_TSENE_VOLT),
FBNIC_TLV_ATTR_S32(FBNIC_FW_TSENE_ERROR),
+ FBNIC_TLV_ATTR_U32(FBNIC_FW_TSENE_THERM_ALARM_STATE),
+ FBNIC_TLV_ATTR_U32(FBNIC_FW_TSENE_VOLT_ALARM_STATE),
FBNIC_TLV_ATTR_LAST
};
@@ -1476,6 +1480,7 @@ static int fbnic_fw_parse_tsene_read_resp(void *opaque,
struct fbnic_dev *fbd = opaque;
s32 err_resp;
int err = 0;
+ u32 alarm;
/* Verify we have a completion pointer to provide with data */
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd,
@@ -1497,6 +1502,20 @@ static int fbnic_fw_parse_tsene_read_resp(void *opaque,
cmpl_data->u.tsene.millivolts =
fta_get_sint(results, FBNIC_FW_TSENE_VOLT);
+ /* Cache here so that it and the threshold event land in the order firmware
+ * sent them. Firmware predating alarm state reporting sends neither
+ * attribute.
+ */
+ alarm = fbnic_tlv_attr_get_unsigned(results[FBNIC_FW_TSENE_THERM_ALARM_STATE],
+ FBNIC_FW_TSENE_ALARM_UNKNOWN);
+ if (alarm != FBNIC_FW_TSENE_ALARM_UNKNOWN)
+ WRITE_ONCE(fbd->hwmon_cache.temp_alarm, alarm);
+
+ alarm = fbnic_tlv_attr_get_unsigned(results[FBNIC_FW_TSENE_VOLT_ALARM_STATE],
+ FBNIC_FW_TSENE_ALARM_UNKNOWN);
+ if (alarm != FBNIC_FW_TSENE_ALARM_UNKNOWN)
+ WRITE_ONCE(fbd->hwmon_cache.volt_alarm, alarm);
+
msg_err:
cmpl_data->result = err_resp ? : err;
complete(&cmpl_data->done);
@@ -1689,6 +1708,68 @@ fbnic_fw_parser_test(void *opaque, struct fbnic_tlv_msg **results)
return err;
}
+static const struct fbnic_tlv_index fbnic_threshold_exceeded_resp_index[] = {
+ FBNIC_TLV_ATTR_U32(FBNIC_FW_TSENE_THERM_ALARM),
+ FBNIC_TLV_ATTR_U32(FBNIC_FW_TSENE_VOLT_ALARM),
+ FBNIC_TLV_ATTR_S32(FBNIC_FW_TSENE_THERMAL),
+ FBNIC_TLV_ATTR_S32(FBNIC_FW_TSENE_VOLTAGE),
+ FBNIC_TLV_ATTR_LAST
+};
+
+static int fbnic_fw_parse_threshold_exceeded_resp(void *opaque,
+ struct fbnic_tlv_msg
+ **results)
+{
+ struct fbnic_dev *fbd = opaque;
+ bool rec_flag = false;
+ u32 alarm;
+ s32 value;
+
+ if (results[FBNIC_FW_TSENE_THERM_ALARM]) {
+ bool have_temp = !!results[FBNIC_FW_TSENE_THERMAL];
+
+ alarm = fta_get_uint(results, FBNIC_FW_TSENE_THERM_ALARM);
+ value = fta_get_sint(results, FBNIC_FW_TSENE_THERMAL);
+ if (!have_temp)
+ dev_err(fbd->dev, "Thermal alarm %u received with no temperature!\n",
+ alarm);
+ else if (alarm != FBNIC_FW_TSENE_ALARM_NORMAL)
+ dev_err(fbd->dev, "Thermal alarm %u, temperature is currently %dmC!\n",
+ alarm, value);
+
+ /* Old firmware reports no range, leave the cache alone */
+ if (alarm != FBNIC_FW_TSENE_ALARM_DEPRECATED)
+ WRITE_ONCE(fbd->hwmon_cache.temp_alarm, alarm);
+ rec_flag = true;
+ }
+
+ if (results[FBNIC_FW_TSENE_VOLT_ALARM]) {
+ bool have_volt = !!results[FBNIC_FW_TSENE_VOLTAGE];
+
+ alarm = fta_get_uint(results, FBNIC_FW_TSENE_VOLT_ALARM);
+ value = fta_get_sint(results, FBNIC_FW_TSENE_VOLTAGE);
+ if (!have_volt)
+ dev_err(fbd->dev, "Voltage alarm %u received with no voltage!\n",
+ alarm);
+ else if (alarm != FBNIC_FW_TSENE_ALARM_NORMAL)
+ dev_err(fbd->dev, "Voltage alarm %u, voltage is currently %dmV!\n",
+ alarm, value);
+
+ /* Old firmware reports no range, leave the cache alone */
+ if (alarm != FBNIC_FW_TSENE_ALARM_DEPRECATED)
+ WRITE_ONCE(fbd->hwmon_cache.volt_alarm, alarm);
+ rec_flag = true;
+ }
+
+ if (!rec_flag) {
+ dev_warn(fbd->dev,
+ "Threshold exceeded message received but no alarm found!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = {
FBNIC_TLV_PARSER(TEST, fbnic_tlv_test_index, fbnic_fw_parser_test),
FBNIC_TLV_PARSER(FW_CAP_RESP, fbnic_fw_cap_resp_index,
@@ -1717,6 +1798,9 @@ static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = {
FBNIC_TLV_PARSER(TSENE_READ_RESP,
fbnic_tsene_read_resp_index,
fbnic_fw_parse_tsene_read_resp),
+ FBNIC_TLV_PARSER(SENSOR_THRESHOLD_EXCEEDED_RESP,
+ fbnic_threshold_exceeded_resp_index,
+ fbnic_fw_parse_threshold_exceeded_resp),
FBNIC_TLV_PARSER(LOG_MSG_REQ,
fbnic_fw_log_req_index,
fbnic_fw_parse_log_req),
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h
index dde2929f8660..3b4787277408 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h
@@ -239,6 +239,7 @@ enum {
FBNIC_TLV_MSG_ID_QSFP_READ_RESP = 0x39,
FBNIC_TLV_MSG_ID_TSENE_READ_REQ = 0x3C,
FBNIC_TLV_MSG_ID_TSENE_READ_RESP = 0x3D,
+ FBNIC_TLV_MSG_ID_SENSOR_THRESHOLD_EXCEEDED_RESP = 0x40,
FBNIC_TLV_MSG_ID_LOG_SEND_LOGS_REQ = 0x43,
FBNIC_TLV_MSG_ID_LOG_MSG_REQ = 0x44,
FBNIC_TLV_MSG_ID_LOG_MSG_RESP = 0x45,
@@ -305,9 +306,31 @@ enum {
FBNIC_FW_TSENE_THERM = 0x0,
FBNIC_FW_TSENE_VOLT = 0x1,
FBNIC_FW_TSENE_ERROR = 0x2,
+ FBNIC_FW_TSENE_THERM_ALARM_STATE = 0x3,
+ FBNIC_FW_TSENE_VOLT_ALARM_STATE = 0x4,
FBNIC_FW_TSENE_MSG_MAX
};
+enum {
+ FBNIC_FW_TSENE_THERM_ALARM = 0x0,
+ FBNIC_FW_TSENE_VOLT_ALARM = 0x1,
+ FBNIC_FW_TSENE_THERMAL = 0x2,
+ FBNIC_FW_TSENE_VOLTAGE = 0x3,
+ FBNIC_FW_TSENE_EXCEEDED_MSG_MAX,
+};
+
+enum {
+ FBNIC_FW_TSENE_ALARM_NORMAL = 0x0,
+ FBNIC_FW_TSENE_ALARM_DEPRECATED = 0x1,
+ FBNIC_FW_TSENE_ALARM_BELOW_MIN = 0x2,
+ FBNIC_FW_TSENE_ALARM_ABOVE_MAX = 0x3,
+ FBNIC_FW_TSENE_ALARM_CRIT = 0x4,
+ /* Driver internal, never sent by firmware. 0xff so its out of
+ * range wherever the range is held in a u8.
+ */
+ FBNIC_FW_TSENE_ALARM_UNKNOWN = 0xff,
+};
+
enum {
FBNIC_FW_OWNERSHIP_FLAG = 0x0,
FBNIC_FW_OWNERSHIP_TIME = 0x1,
--
2.53.0-Meta