git: 98b5640786f8 - main - bnxt: Add led(4) identification support

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Thu, 13 Aug 2026 15:33:09 UTC
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=98b5640786f874949e441bee7c4ad5740cd6a649

commit 98b5640786f874949e441bee7c4ad5740cd6a649
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-12 01:29:31 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-13 15:32:07 +0000

    bnxt: Add led(4) identification support
    
    Query the firmware for the LEDs on each physical port and expose
    /dev/led/bnxt* only when alternate blinking is supported.  Configure
    every LED in the advertised group for identification and restore its
    default firmware state before a function reset.
    
    This follows the DPDK and Linux bnxt HWRM identification paths.
    
    Reviewed against: DPDK, Linux
    
    Reviewed by:    Sumit Saxena <sumit.saxena@broadcom.com>
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
---
 share/man/man4/bnxt.4            |   7 ++-
 sys/dev/bnxt/bnxt_en/bnxt.h      |  15 ++++++
 sys/dev/bnxt/bnxt_en/bnxt_hwrm.c | 106 ++++++++++++++++++++++++++++++++++++++-
 sys/dev/bnxt/bnxt_en/bnxt_hwrm.h |   2 +
 sys/dev/bnxt/bnxt_en/if_bnxt.c   |  41 +++++++++++++++
 5 files changed, 169 insertions(+), 2 deletions(-)

diff --git a/share/man/man4/bnxt.4 b/share/man/man4/bnxt.4
index 5e3ee27426d5..cedab9b83d00 100644
--- a/share/man/man4/bnxt.4
+++ b/share/man/man4/bnxt.4
@@ -23,7 +23,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 .\" THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd December 10, 2025
+.Dd August 11, 2026
 .Dt BNXT 4
 .Os
 .Sh NAME
@@ -52,6 +52,11 @@ BCM574XX, BCM575XX, and BCM576XX Ethernet controllers.
 .Pp
 For more information on configuring this device, see
 .Xr ifconfig 8 .
+.Pp
+On physical functions whose firmware reports identification LED support,
+the LEDs for each port are controlled through its
+.Pa /dev/led/bnxt*
+device node.
 .Sh HARDWARE
 The
 .Nm
diff --git a/sys/dev/bnxt/bnxt_en/bnxt.h b/sys/dev/bnxt/bnxt_en/bnxt.h
index 7e58139f1ab6..d4167301ffff 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt.h
@@ -468,6 +468,18 @@ struct bnxt_link_info {
 	uint8_t		active_lanes;
 };
 
+#define BNXT_MAX_LED	4
+
+/* Layout of one LED capability record returned by HWRM_PORT_LED_QCAPS. */
+struct bnxt_led_info {
+	uint8_t		led_id;
+	uint8_t		led_type;
+	uint8_t		led_group_id;
+	uint8_t		unused;
+	uint16_t	led_state_caps;
+	uint16_t	led_color_caps;
+};
+
 enum bnxt_phy_type {
 	BNXT_MEDIA_CR = 0,
 	BNXT_MEDIA_LR,
@@ -1099,6 +1111,9 @@ struct bnxt_softc {
 	struct bnxt_bar_info	hwrm_bar;
 	struct bnxt_bar_info	doorbell_bar;
 	struct bnxt_link_info	link_info;
+	struct bnxt_led_info	leds[BNXT_MAX_LED];
+	uint8_t			num_leds;
+	bool			led_active;
 #define BNXT_FLAG_VF				0x0001
 #define BNXT_FLAG_NPAR				0x0002
 #define BNXT_FLAG_WOL_CAP			0x0004
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
index 5964511f8276..a6742b6ca37c 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.c
@@ -74,6 +74,31 @@ static void	bnxt_hwrm_set_eee(struct bnxt_softc *softc,
 	BNXT_TX_STATS_PRI_ENTRY(counter, 6),		\
 	BNXT_TX_STATS_PRI_ENTRY(counter, 7)
 
+#define BNXT_LED_DFLT_ENA				\
+	(HWRM_PORT_LED_CFG_INPUT_ENABLES_LED0_ID |	\
+	 HWRM_PORT_LED_CFG_INPUT_ENABLES_LED0_STATE |	\
+	 HWRM_PORT_LED_CFG_INPUT_ENABLES_LED0_BLINK_ON |	\
+	 HWRM_PORT_LED_CFG_INPUT_ENABLES_LED0_BLINK_OFF |	\
+	 HWRM_PORT_LED_CFG_INPUT_ENABLES_LED0_GROUP_ID)
+#define BNXT_LED_DFLT_ENA_SHIFT	6
+#define BNXT_LED_DFLT_ENABLES(_i)			\
+	(BNXT_LED_DFLT_ENA << (BNXT_LED_DFLT_ENA_SHIFT * (_i)))
+#define BNXT_LED_ALT_BLINK_CAP \
+	HWRM_PORT_LED_QCAPS_OUTPUT_LED0_STATE_CAPS_BLINK_ALT_SUPPORTED
+
+struct bnxt_led_cfg {
+	uint8_t		led_id;
+	uint8_t		led_state;
+	uint8_t		led_color;
+	uint8_t		unused;
+	uint16_t	led_blink_on;
+	uint16_t	led_blink_off;
+	uint8_t		led_group_id;
+	uint8_t		reserved;
+};
+
+CTASSERT(sizeof(struct bnxt_led_info) == 8);
+CTASSERT(sizeof(struct bnxt_led_cfg) == 10);
 
 long bnxt_rx_bytes_pri_arr_base_off[] = {BNXT_RX_STATS_PRI_ENTRIES(rx_bytes)};
 long bnxt_rx_pkts_pri_arr_base_off[] = {BNXT_RX_STATS_PRI_ENTRIES(rx_packets)};
@@ -1331,6 +1356,86 @@ fail:
 	return rc;
 }
 
+int
+bnxt_hwrm_port_led_qcaps(struct bnxt_softc *softc)
+{
+	struct hwrm_port_led_qcaps_output *resp;
+	struct hwrm_port_led_qcaps_input req = {0};
+	struct bnxt_led_info *led;
+	uint16_t caps;
+	int i, rc;
+
+	softc->num_leds = 0;
+	if (BNXT_VF(softc) || softc->hwrm_spec_code < 0x10601)
+		return (0);
+
+	resp = (void *)softc->hwrm_cmd_resp.idi_vaddr;
+	bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_LED_QCAPS);
+	req.port_id = htole16(softc->pf.port_id);
+
+	BNXT_HWRM_LOCK(softc);
+	rc = _hwrm_send_message(softc, &req, sizeof(req));
+	if (rc != 0)
+		goto out;
+
+	/* HWRM describes up to four LED records in both response and request. */
+	if (resp->num_leds == 0 || resp->num_leds > BNXT_MAX_LED)
+		goto out;
+	memcpy(softc->leds, &resp->led0_id,
+	    sizeof(softc->leds[0]) * resp->num_leds);
+	softc->num_leds = resp->num_leds;
+	for (i = 0; i < softc->num_leds; i++) {
+		led = &softc->leds[i];
+		caps = le16toh(led->led_state_caps);
+		if (led->led_group_id == 0 ||
+		    (caps & BNXT_LED_ALT_BLINK_CAP) == 0) {
+			softc->num_leds = 0;
+			break;
+		}
+	}
+out:
+	BNXT_HWRM_UNLOCK(softc);
+	return (rc);
+}
+
+int
+bnxt_hwrm_port_led_cfg(struct bnxt_softc *softc, bool led_on)
+{
+	struct hwrm_port_led_cfg_input req = {0};
+	struct bnxt_led_cfg *led_cfg;
+	uint32_t enables;
+	uint16_t duration;
+	uint8_t led_state;
+	int i;
+
+	if (BNXT_VF(softc) || softc->num_leds == 0)
+		return (EOPNOTSUPP);
+
+	bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_LED_CFG);
+	req.port_id = htole16(softc->pf.port_id);
+	req.num_leds = softc->num_leds;
+	led_state = HWRM_PORT_LED_CFG_INPUT_LED0_STATE_DEFAULT;
+	duration = 0;
+	if (led_on) {
+		led_state = HWRM_PORT_LED_CFG_INPUT_LED0_STATE_BLINKALT;
+		duration = htole16(500);
+	}
+
+	enables = 0;
+	led_cfg = (struct bnxt_led_cfg *)(void *)&req.led0_id;
+	for (i = 0; i < softc->num_leds; i++, led_cfg++) {
+		enables |= BNXT_LED_DFLT_ENABLES(i);
+		led_cfg->led_id = softc->leds[i].led_id;
+		led_cfg->led_state = led_state;
+		led_cfg->led_blink_on = duration;
+		led_cfg->led_blink_off = duration;
+		led_cfg->led_group_id = softc->leds[i].led_group_id;
+	}
+	req.enables = htole32(enables);
+
+	return (hwrm_send_message(softc, &req, sizeof(req)));
+}
+
 int
 bnxt_hwrm_func_qcfg(struct bnxt_softc *softc)
 {
@@ -3446,4 +3551,3 @@ bnxt_hwrm_get_dump_len(struct bnxt_softc *softc, uint16_t dump_type,
 get_dump_len_exit:
 	return (rc);
 }
-
diff --git a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
index b10981d9d53f..641a1e9ebe3a 100644
--- a/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
+++ b/sys/dev/bnxt/bnxt_en/bnxt_hwrm.h
@@ -69,6 +69,8 @@ int bnxt_hwrm_stat_ctx_alloc(struct bnxt_softc *softc, struct bnxt_cp_ring *cpr,
 int bnxt_hwrm_stat_ctx_free(struct bnxt_softc *softc, struct bnxt_cp_ring *cpr);
 int bnxt_hwrm_port_qstats(struct bnxt_softc *softc);
 int bnxt_hwrm_port_qstats_ext(struct bnxt_softc *softc);
+int bnxt_hwrm_port_led_qcaps(struct bnxt_softc *softc);
+int bnxt_hwrm_port_led_cfg(struct bnxt_softc *softc, bool led_on);
 int bnxt_hwrm_ring_grp_alloc(struct bnxt_softc *softc,
     struct bnxt_grp_info *grp);
 int bnxt_hwrm_ring_grp_free(struct bnxt_softc *softc, struct bnxt_grp_info *gr);
diff --git a/sys/dev/bnxt/bnxt_en/if_bnxt.c b/sys/dev/bnxt/bnxt_en/if_bnxt.c
index 8ecb4770fe0e..ec88b30edcde 100644
--- a/sys/dev/bnxt/bnxt_en/if_bnxt.c
+++ b/sys/dev/bnxt/bnxt_en/if_bnxt.c
@@ -217,6 +217,9 @@ static int bnxt_detach(if_ctx_t ctx);
 static void bnxt_init(if_ctx_t ctx);
 static int bnxt_init_hw(if_ctx_t ctx);
 static void bnxt_stop(if_ctx_t ctx);
+static void bnxt_if_led_func(if_ctx_t ctx, int onoff);
+static bool bnxt_if_led_supported(if_ctx_t ctx);
+static void bnxt_led_restore(struct bnxt_softc *softc);
 static void bnxt_multi_set(if_ctx_t ctx);
 static int bnxt_mtu_set(if_ctx_t ctx, uint32_t mtu);
 static void bnxt_media_status(if_ctx_t ctx, struct ifmediareq * ifmr);
@@ -342,6 +345,8 @@ static device_method_t bnxt_iflib_methods[] = {
 
 	DEVMETHOD(ifdi_init, bnxt_init),
 	DEVMETHOD(ifdi_stop, bnxt_stop),
+	DEVMETHOD(ifdi_led_func, bnxt_if_led_func),
+	DEVMETHOD(ifdi_led_supported, bnxt_if_led_supported),
 	DEVMETHOD(ifdi_multi_set, bnxt_multi_set),
 	DEVMETHOD(ifdi_mtu_set, bnxt_mtu_set),
 	DEVMETHOD(ifdi_media_status, bnxt_media_status),
@@ -2244,6 +2249,7 @@ static int bnxt_open(struct bnxt_softc *bp)
 	rc = bnxt_hwrm_func_qcaps(bp);
 	if (rc)
 		return rc;
+	(void)bnxt_hwrm_port_led_qcaps(bp);
 
 	bnxt_hwrm_dbg_qcaps(bp);
 
@@ -2251,6 +2257,8 @@ static int bnxt_open(struct bnxt_softc *bp)
 	rc = bnxt_drv_rgtr(bp);
 	if (rc)
 		return rc;
+	/* Retry a restore which could not complete before firmware reset. */
+	bnxt_led_restore(bp);
 	if (bp->hwrm_spec_code >= 0x10803) {
 		rc = bnxt_alloc_ctx_mem(bp);
 		if (rc) {
@@ -2728,6 +2736,7 @@ bnxt_attach_pre(if_ctx_t ctx)
 	rc = bnxt_hwrm_func_qcaps(softc);
 	if (rc)
 		goto failed;
+	(void)bnxt_hwrm_port_led_qcaps(softc);
 
 	/* Inform PF to approve MAC as default VF MAC. */
 	if (BNXT_VF(softc)) {
@@ -3151,6 +3160,7 @@ fail:
 static void
 bnxt_func_reset(struct bnxt_softc *softc)
 {
+	bnxt_led_restore(softc);
 
 	if (!BNXT_CHIP_P5_PLUS(softc)) {
 		bnxt_hwrm_func_reset(softc);
@@ -3493,6 +3503,37 @@ bnxt_stop(if_ctx_t ctx)
 	return;
 }
 
+static void
+bnxt_if_led_func(if_ctx_t ctx, int onoff)
+{
+	struct bnxt_softc *softc = iflib_get_softc(ctx);
+	bool active;
+
+	active = onoff != 0;
+	if (active == softc->led_active)
+		return;
+	if (bnxt_hwrm_port_led_cfg(softc, active) == 0)
+		softc->led_active = active;
+}
+
+static void
+bnxt_led_restore(struct bnxt_softc *softc)
+{
+
+	if (!softc->led_active)
+		return;
+	if (bnxt_hwrm_port_led_cfg(softc, false) == 0)
+		softc->led_active = false;
+}
+
+static bool
+bnxt_if_led_supported(if_ctx_t ctx)
+{
+	struct bnxt_softc *softc = iflib_get_softc(ctx);
+
+	return (softc->num_leds != 0);
+}
+
 static u_int
 bnxt_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
 {