git: 98b5640786f8 - main - bnxt: Add led(4) identification support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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)
{