git: 9e067f207dc2 - main - aq(4): observability controls and sysctl/header hygiene

Adrian Chadd <[email protected]> Sun, 02 Aug 2026 23:10:32 +0000
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a6fce68.4344a.18f9ce00__17582.1389466122$1785712347$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=9e067f207dc2da57df49812809cc5034030f61d1

commit 9e067f207dc2da57df49812809cc5034030f61d1
Author:     Nick Price <[email protected]>
AuthorDate: 2026-08-02 22:44:16 +0000
Commit:     Adrian Chadd <[email protected]>
CommitDate: 2026-08-02 22:44:16 +0000

    aq(4): observability controls and sysctl/header hygiene
    
    Fold the driver's observability and infrastructure work.
    
    Make aq_device.h self-contained: it declares struct aq_dev in terms of
    iflib, bitstring, socket, and ethernet types but included none of the
    headers that define them, compiling only because every includer happened
    to pull those first.  Include what it uses.  No functional change.
    
    Make the debug controls per-instance.  The debug and debug_categories
    sysctls were registered per device but pointed at file-scope globals, so
    writing dev.aq.1.debug also changed dev.aq.0.debug and a card could not
    be traced in isolation.  Move the level and category mask into struct
    aq_dev, reach them through the aq_dev back-pointer in struct aq_hw (wired
    up in attach_pre before the first firmware trace and guarded against a
    NULL deref), emit through device_printf() so each line carries its unit,
    and seed initial values from per-unit device hints so attach can be
    traced.
    
    Expose the PHY die temperature as dev.aq.N.temperature through a new
    firmware get_temp op: Atlantic 1 v2 reads it through the mailbox MPI
    control/state toggle, Atlantic 2 from the phy_health_monitor block in the
    OUT window (located at 0x13620 and confirmed by its ready bit).  Atlantic
    1 v1 has no sensor and exposes no node.  Because this is the first
    firmware accessor iflib does not serialise, add a per-instance mutex in
    struct aq_hw and take it across the v2 read-modify-write in set_mode(),
    get_stats(), get_mode(), and get_temp(); the v1 and Atlantic 2 paths do
    not need it and say so.
    
    Trace the Atlantic 2 firmware path, which previously emitted nothing at
    any debug level (aq2_fw.c did not even include aq_dbg.h): the boot
    handshake, reset policy, MAC address, and link mode set/read, using the
    existing dbg_init and dbg_fw categories, with the per-poll mode read at
    detail level.
    
    Scope the driver sysctls to a context freed at detach.  They were
    registered on the device newbus context, which newbus tears down only
    after DEVICE_DETACH returns, yet iflib frees the rings and softc inside
    DEVICE_DETACH -- a sysctl read racing detach could touch freed memory.
    Give the driver its own sysctl_ctx_list and free it at the start of
    aq_if_detach, draining in-flight readers first.
    
    Signed-off-by: Nick Price <[email protected]>
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D58434
---
 sys/dev/aq/aq2_fw.c    | 72 ++++++++++++++++++++++++++++++++++++++++----
 sys/dev/aq/aq2_hw.h    |  5 ++++
 sys/dev/aq/aq_dbg.c    | 18 +++++------
 sys/dev/aq/aq_dbg.h    | 66 ++++++++++++++++++++++++++++------------
 sys/dev/aq/aq_device.h | 16 ++++++++++
 sys/dev/aq/aq_fw.c     | 34 ++++++++++-----------
 sys/dev/aq/aq_fw.h     |  3 ++
 sys/dev/aq/aq_fw1x.c   | 11 +++----
 sys/dev/aq/aq_fw2x.c   | 76 ++++++++++++++++++++++++++++++++++++++--------
 sys/dev/aq/aq_hw.h     |  8 +++++
 sys/dev/aq/aq_main.c   | 81 +++++++++++++++++++++++++++++++++++++++++---------
 sys/dev/aq/aq_ring.c   | 10 +++----
 12 files changed, 310 insertions(+), 90 deletions(-)

diff --git a/sys/dev/aq/aq2_fw.c b/sys/dev/aq/aq2_fw.c
index d36b9e841624..a9353a9cfa96 100644
--- a/sys/dev/aq/aq2_fw.c
+++ b/sys/dev/aq/aq2_fw.c
@@ -32,6 +32,7 @@
 #include "aq_hw_llh.h"
 #include "aq2_hw.h"
 #include "aq_fw.h"
+#include "aq_dbg.h"
 
 static int aq2_fw_reset(struct aq_hw *hw);
 static int aq2_fw_set_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state mode,
@@ -40,6 +41,7 @@ static int aq2_fw_get_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state *mode,
     enum aq_fw_link_speed *speed, enum aq_fw_link_fc *fc);
 static int aq2_fw_get_mac_addr(struct aq_hw *hw, uint8_t *mac);
 static int aq2_fw_get_stats(struct aq_hw *hw, struct aq_hw_stats *stats);
+static int aq2_fw_get_temp(struct aq_hw *hw, int *temp_mc);
 
 /* Coherent OUT-window read, bracketed by the transaction id. */
 static int
@@ -97,6 +99,7 @@ aq2_fw_reboot(struct aq_hw *hw)
 		device_printf(hw->dev, "A2 firmware reboot timeout\n");
 		return (ETIMEDOUT);
 	}
+	trace(hw, dbg_init, "aq2> F/W boot started, %d ms", (20000 - timo) / 100);
 
 	for (timo = 200000; timo > 0; timo--) {
 		v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG);
@@ -111,6 +114,8 @@ aq2_fw_reboot(struct aq_hw *hw)
 		device_printf(hw->dev, "A2 firmware restart timeout\n");
 		return (ETIMEDOUT);
 	}
+	trace(hw, dbg_init, "aq2> F/W boot complete, %d ms",
+	    (200000 - timo) / 100);
 
 	v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG);
 	if (v & AQ2_MIF_BOOT_FAILED) {
@@ -152,9 +157,12 @@ aq2_fw_reboot(struct aq_hw *hw)
 		iface = "unknown";
 		break;
 	}
-	device_printf(hw->dev, "Atlantic 2 %s, firmware %u.%u.%u\n", iface,
-	    hw->fw_version.major_version, hw->fw_version.minor_version,
-	    hw->fw_version.build_number);
+	if (!hw->fw_announced || bootverbose) {
+		device_printf(hw->dev, "Atlantic 2 %s, firmware %u.%u.%u\n",
+		    iface, hw->fw_version.major_version,
+		    hw->fw_version.minor_version, hw->fw_version.build_number);
+		hw->fw_announced = true;
+	}
 
 	/* Base row added to every action-resolver-table index. */
 	err = aq2_fw_interface_buffer_read(hw,
@@ -165,6 +173,8 @@ aq2_fw_reboot(struct aq_hw *hw)
 	hw->art_filter_base_index = ((filter_caps[2] &
 	    AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX) >>
 	    AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX_SHIFT) * 8;
+	trace(hw, dbg_init, "aq2> ART filter base index = %u",
+	    hw->art_filter_base_index);
 
 	return (0);
 }
@@ -173,11 +183,17 @@ aq2_fw_reboot(struct aq_hw *hw)
 static int
 aq2_fw_wait_shared_ack(struct aq_hw *hw)
 {
+	int err;
+
 	AQ_WRITE_REG(hw, AQ2_MIF_HOST_FINISHED_STATUS_WRITE_REG,
 	    AQ2_MIF_HOST_FINISHED_STATUS_ACK);
-	return (AQ_HW_WAIT_FOR((AQ_READ_REG(hw,
+	err = AQ_HW_WAIT_FOR((AQ_READ_REG(hw,
 	    AQ2_MIF_HOST_FINISHED_STATUS_READ_REG) &
-	    AQ2_MIF_HOST_FINISHED_STATUS_ACK) == 0, 100, 1000));
+	    AQ2_MIF_HOST_FINISHED_STATUS_ACK) == 0, 100, 1000);
+	if (err != 0)
+		trace_error(hw, dbg_fw, "aq2> shared buffer ack timed out");
+
+	return (err);
 }
 
 static int
@@ -203,6 +219,9 @@ aq2_fw_reset(struct aq_hw *hw)
 	v &= ~AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_RX_QUEUE_TX_INDEX;
 	AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_REQUEST_POLICY_REG, v);
 
+	trace(hw, dbg_init, "aq2> reset: mtu %u, request policy %#x",
+	    HW_ATL_B0_MTU_JUMBO, v);
+
 	err = aq2_fw_wait_shared_ack(hw);
 	if (err != 0)
 		device_printf(hw->dev, "A2 firmware reset timed out\n");
@@ -225,9 +244,11 @@ aq2_fw_get_mac_addr(struct aq_hw *hw, uint8_t *mac)
 	mac_addr[0] = htole32(mac_addr[0]);
 	mac_addr[1] = htole32(mac_addr[1]);
 	memcpy(mac, (uint8_t *)mac_addr, ETHER_ADDR_LEN);
+	trace(hw, dbg_init, "aq2> MAC addr %6D", mac, ":");
 	return (0);
 }
 
+/* No fw_mtx: the IN window is written only from iflib-serialised paths. */
 static int
 aq2_fw_set_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state mode,
     enum aq_fw_link_speed speed)
@@ -279,6 +300,9 @@ aq2_fw_set_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state mode,
 		    AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_SHUTDOWN);
 	}
 
+	trace(hw, dbg_init, "aq2> set mode %d, speed mask %#x, link options %#x",
+	    mode, speed, v);
+
 	/* Options acked before ACTIVE so bring-up negotiates the new mask. */
 	AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_LINK_OPTIONS_REG, v);
 	if (mode == MPI_INIT) {
@@ -338,6 +362,9 @@ aq2_fw_get_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state *modep,
 	if (fcp != NULL)
 		*fcp = fc;
 
+	trace_detail(hw, dbg_init,
+	    "aq2> get mode: link status %#x, speed %d, fc %d", v, speed, fc);
+
 	return (0);
 }
 
@@ -400,8 +427,11 @@ aq2_fw_get_stats(struct aq_hw *hw, struct aq_hw_stats *stats)
 
 	err = aq2_fw_interface_buffer_read(hw, AQ2_FW_INTERFACE_OUT_STATS_REG,
 	    (uint32_t *)&u, sizeof(u));
-	if (err != 0)
+	if (err != 0) {
+		trace_error(hw, dbg_fw,
+		    "aq2> statistics read FAILED, error %d", err);
 		return (err);
+	}
 
 	if (hw->aq2_iface == AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0) {
 		stats->uprc = u.a0.rx_unicast_frames;
@@ -434,16 +464,46 @@ aq2_fw_get_stats(struct aq_hw *hw, struct aq_hw_stats *stats)
 		stats->bptc = u.b0.tx_broadcast_frames;
 		stats->erpt = u.b0.tx_errors;
 		stats->ptc = u.b0.tx_good_frames;
+	} else {
+		trace_warn(hw, dbg_fw,
+		    "aq2> unknown F/W interface version %u, no statistics",
+		    hw->aq2_iface);
 	}
 
 	return (0);
 }
 
+static int
+aq2_fw_get_temp(struct aq_hw *hw, int *temp_mc)
+{
+	uint32_t raw;
+	int err;
+
+	err = aq2_fw_interface_buffer_read(hw,
+	    AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_REG, &raw, sizeof(raw));
+	if (err != 0)
+		return (err);
+
+	/* The F/W zeroes the block until the PHY reports in. */
+	if ((raw & AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_READY) == 0) {
+		trace_detail(hw, dbg_fw, "aq2> PHY health monitor not ready");
+		return (ENXIO);
+	}
+
+	/* F/W reports whole degrees Celsius. */
+	*temp_mc = (int)(int8_t)((raw &
+	    AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE) >>
+	    AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE_S) * 1000;
+
+	return (0);
+}
+
 const struct aq_firmware_ops aq2_fw_ops = {
 	.reset = aq2_fw_reset,
 	.set_mode = aq2_fw_set_mode,
 	.get_mode = aq2_fw_get_mode,
 	.get_mac_addr = aq2_fw_get_mac_addr,
 	.get_stats = aq2_fw_get_stats,
+	.get_temp = aq2_fw_get_temp,
 	.led_control = NULL,
 };
diff --git a/sys/dev/aq/aq2_hw.h b/sys/dev/aq/aq2_hw.h
index 21c4e0ddce09..d707e2dad51e 100644
--- a/sys/dev/aq/aq2_hw.h
+++ b/sys/dev/aq/aq2_hw.h
@@ -229,6 +229,11 @@ aq_is_atlantic2(uint16_t device_id)
 #define  AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0 0
 #define  AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_B0 1
 
+#define AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_REG	0x13620
+#define  AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_READY	0x00000001
+#define  AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE		0x0000ff00
+#define  AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE_S		8
+
 #define AQ2_FW_INTERFACE_OUT_STATS_REG		0x13700
 
 #define AQ2_FW_INTERFACE_OUT_LINK_STATUS_REG	0x13014
diff --git a/sys/dev/aq/aq_dbg.c b/sys/dev/aq/aq_dbg.c
index fb1408b993e7..44391322143d 100644
--- a/sys/dev/aq/aq_dbg.c
+++ b/sys/dev/aq/aq_dbg.c
@@ -45,18 +45,13 @@ __FBSDID("$FreeBSD$");
 #include "aq_dbg.h"
 
 
-int aq_dbg_level = lvl_error;
-uint32_t aq_dbg_categories = dbg_init | dbg_config | dbg_tx | dbg_rx | dbg_intr | dbg_fw;
-
-
-
 #define DESCR_FIELD(DESCR, BIT_BEGIN, BIT_END) \
 	((DESCR >> BIT_END) &\
 		(BIT(BIT_BEGIN - BIT_END + 1) -1))
 
 #define __field(TYPE, VAR) TYPE VAR;
 void
-trace_aq_tx_descr(int ring_idx, unsigned int pointer,
+trace_aq_tx_descr(struct aq_hw *hw, int ring_idx, unsigned int pointer,
     volatile uint64_t descr[2])
 {
 #if AQ_CFG_DEBUG_LVL > 2
@@ -92,7 +87,7 @@ trace_aq_tx_descr(int ring_idx, unsigned int pointer,
 	entry.des_typ = DESCR_FIELD(descr[1], 2, 0);
 
 
-	aq_log_detail("trace_aq_tx_descr ring=%d descr=%u pay_len=%u ct_en=%u ct_idx=%u rsvd2=0x%x tx_cmd=0x%x eop=%u dd=%u buf_len=%u rsvd1=%u des_typ=0x%x",
+	aq_log_detail(hw, "trace_aq_tx_descr ring=%d descr=%u pay_len=%u ct_en=%u ct_idx=%u rsvd2=0x%x tx_cmd=0x%x eop=%u dd=%u buf_len=%u rsvd1=%u des_typ=0x%x",
 		  entry.ring_idx, entry.pointer, entry.pay_len,
 		  entry.ct_en, entry.ct_idx, entry.rsvd2,
 		  entry.tx_cmd, entry.eop, entry.dd, entry.buf_len,
@@ -101,7 +96,8 @@ trace_aq_tx_descr(int ring_idx, unsigned int pointer,
 }
 
 void
-trace_aq_rx_descr(int ring_idx, unsigned int pointer, volatile uint64_t descr[2])
+trace_aq_rx_descr(struct aq_hw *hw, int ring_idx, unsigned int pointer,
+    volatile uint64_t descr[2])
 {
 #if AQ_CFG_DEBUG_LVL > 2
 	uint8_t dd;
@@ -142,7 +138,7 @@ trace_aq_rx_descr(int ring_idx, unsigned int pointer, volatile uint64_t descr[2]
 	eop = DESCR_FIELD(descr[1], 1, 1);
 	dd = DESCR_FIELD(descr[1], 0, 0);
 
-	printf("trace_aq_rx_descr ring=%d descr=%u rss_hash=0x%x hdr_len=%u sph=%u rx_cntl=%u rsvd=0x%x avb_ts=%u rdm_err=%u pkt_type=%u rss_type=%u vlan_tag=%u next_desp=%u pkt_len=%u rsc_cnt=%u rx_estat=0x%x rx_stat=0x%x eop=%u dd=%u\n",
+	aq_log_detail(hw, "trace_aq_rx_descr ring=%d descr=%u rss_hash=0x%x hdr_len=%u sph=%u rx_cntl=%u rsvd=0x%x avb_ts=%u rdm_err=%u pkt_type=%u rss_type=%u vlan_tag=%u next_desp=%u pkt_len=%u rsc_cnt=%u rx_estat=0x%x rx_stat=0x%x eop=%u dd=%u",
 		  ring_idx, pointer, rss_hash,
 		  hdr_len, sph, rx_cntl,
 		  rsvd, avb_ts, rdm_err,
@@ -153,7 +149,7 @@ trace_aq_rx_descr(int ring_idx, unsigned int pointer, volatile uint64_t descr[2]
 }
 
 void
-trace_aq_tx_context_descr(int ring_idx, unsigned int pointer,
+trace_aq_tx_context_descr(struct aq_hw *hw, int ring_idx, unsigned int pointer,
     volatile uint64_t descr[2])
 {
 #if AQ_CFG_DEBUG_LVL > 2
@@ -188,7 +184,7 @@ trace_aq_tx_context_descr(int ring_idx, unsigned int pointer,
 	__entry->ct_idx = DESCR_FIELD(descr[1], 3, 3);
 	__entry->des_typ = DESCR_FIELD(descr[1], 2, 0);
 
-	printf("trace_aq_tx_context_descr ring=%d descr=%u out_len=%u tun_len=%u resvd3=%lu mss_len=%u l4_len=%u l3_len=%u l2_len=%d ct_cmd=%u vlan_tag=%u ct_idx=%u des_typ=0x%x\n",
+	aq_log_detail(hw, "trace_aq_tx_context_descr ring=%d descr=%u out_len=%u tun_len=%u resvd3=%lu mss_len=%u l4_len=%u l3_len=%u l2_len=%d ct_cmd=%u vlan_tag=%u ct_idx=%u des_typ=0x%x",
 		  __entry->ring_idx, __entry->pointer, __entry->out_len,
 		  __entry->tun_len, __entry->resvd3, __entry->mss_len,
 		  __entry->l4_len, __entry->l3_len, __entry->l2_len,
diff --git a/sys/dev/aq/aq_dbg.h b/sys/dev/aq/aq_dbg.h
index 2e5e79ced195..148916e3cbda 100644
--- a/sys/dev/aq/aq_dbg.h
+++ b/sys/dev/aq/aq_dbg.h
@@ -40,6 +40,8 @@
 
 #include <sys/systm.h>
 #include <sys/syslog.h>
+
+#include "aq_device.h"
 /*
 Debug levels:
 0 - no debug
@@ -109,24 +111,50 @@ enum aq_debug_category
 
 #define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
 
-extern int aq_dbg_level;
-extern uint32_t aq_dbg_categories;
-
-#define aq_log_base(_lvl, _fmt, args...) do { if (aq_dbg_level >= (_lvl)) printf( "atlantic: " _fmt "\n", ##args); } while (0)
-
-#define aq_trace_base(_lvl, _cat, _fmt, args...) do { if (aq_dbg_level >= (_lvl) && ((_cat) & aq_dbg_categories)) { printf( "atlantic: " _fmt " @%s,%d\n", ##args, __FILENAME__, __LINE__); }} while (0)
-
-#define aq_log_warn(_fmt, args...)     aq_log_base(lvl_warn, "/!\\ " _fmt, ##args)
-#define aq_log(_fmt, args...)          aq_log_base(lvl_trace, _fmt, ##args)
-#define aq_log_detail(_fmt, args...)   aq_log_base(lvl_detail, _fmt, ##args)
-
-#define trace_error(_cat,_fmt, args...)   aq_trace_base(lvl_error, _cat, "[!] " _fmt, ##args)
-#define trace_warn(_cat, _fmt, args...)   aq_trace_base(lvl_warn, _cat, "/!\\ " _fmt, ##args)
-#define trace(_cat, _fmt, args...)   aq_trace_base(lvl_trace, _cat, _fmt, ##args)
-#define trace_detail(_cat, _fmt, args...)   aq_trace_base(lvl_detail, _cat, _fmt, ##args)
-
-void trace_aq_tx_descr(int ring_idx, unsigned int pointer, volatile uint64_t descr[2]);
-void trace_aq_rx_descr(int ring_idx, unsigned int pointer, volatile uint64_t descr[2]);
-void trace_aq_tx_context_descr(int ring_idx, unsigned int pointer, volatile uint64_t descr[2]);
+#define AQ_DBG_LEVEL_DEFAULT		lvl_error
+#define AQ_DBG_CATEGORIES_DEFAULT	(dbg_init | dbg_config | dbg_tx |	\
+					 dbg_rx | dbg_intr | dbg_fw)
+
+/* NULL until aq_if_attach_pre() wires it up; traces run before that. */
+#define AQ_DBG_SOFTC(_hw)	((struct aq_dev *)(_hw)->aq_dev)
+
+#define aq_log_base(_hw, _lvl, _fmt, args...) do {			\
+	const struct aq_dev *_sc = AQ_DBG_SOFTC(_hw);			\
+									\
+	if (_sc != NULL && _sc->dbg_level >= (_lvl))			\
+		device_printf((_hw)->dev, _fmt "\n", ##args);		\
+} while (0)
+
+#define aq_trace_base(_hw, _lvl, _cat, _fmt, args...) do {		\
+	const struct aq_dev *_sc = AQ_DBG_SOFTC(_hw);			\
+									\
+	if (_sc != NULL && _sc->dbg_level >= (_lvl) &&			\
+	    ((_cat) & _sc->dbg_categories))				\
+		device_printf((_hw)->dev, _fmt " @%s,%d\n", ##args,	\
+		    __FILENAME__, __LINE__);				\
+} while (0)
+
+#define aq_log_warn(_hw, _fmt, args...)					\
+	aq_log_base(_hw, lvl_warn, "/!\\ " _fmt, ##args)
+#define aq_log(_hw, _fmt, args...)					\
+	aq_log_base(_hw, lvl_trace, _fmt, ##args)
+#define aq_log_detail(_hw, _fmt, args...)				\
+	aq_log_base(_hw, lvl_detail, _fmt, ##args)
+
+#define trace_error(_hw, _cat, _fmt, args...)				\
+	aq_trace_base(_hw, lvl_error, _cat, "[!] " _fmt, ##args)
+#define trace_warn(_hw, _cat, _fmt, args...)				\
+	aq_trace_base(_hw, lvl_warn, _cat, "/!\\ " _fmt, ##args)
+#define trace(_hw, _cat, _fmt, args...)					\
+	aq_trace_base(_hw, lvl_trace, _cat, _fmt, ##args)
+#define trace_detail(_hw, _cat, _fmt, args...)				\
+	aq_trace_base(_hw, lvl_detail, _cat, _fmt, ##args)
+
+void trace_aq_tx_descr(struct aq_hw *hw, int ring_idx, unsigned int pointer,
+    volatile uint64_t descr[2]);
+void trace_aq_rx_descr(struct aq_hw *hw, int ring_idx, unsigned int pointer,
+    volatile uint64_t descr[2]);
+void trace_aq_tx_context_descr(struct aq_hw *hw, int ring_idx,
+    unsigned int pointer, volatile uint64_t descr[2]);
 
 #endif // AQ_DBG_H
diff --git a/sys/dev/aq/aq_device.h b/sys/dev/aq/aq_device.h
index acdf897562f0..6c2453d8277c 100644
--- a/sys/dev/aq/aq_device.h
+++ b/sys/dev/aq/aq_device.h
@@ -35,6 +35,17 @@
 #ifndef _AQ_DEVICE_H_
 #define _AQ_DEVICE_H_
 
+#include <sys/bitstring.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_var.h>
+#include <net/iflib.h>
+
 #include "aq_hw.h"
 
 enum aq_media_type {
@@ -138,6 +149,11 @@ struct aq_dev {
 
 	uint8_t			rss_key[HW_ATL_RSS_HASHKEY_SIZE];
 	uint8_t			rss_table[HW_ATL_RSS_INDIRECTION_TABLE_MAX];
+
+	int			dbg_level;
+	uint32_t		dbg_categories;
+
+	struct sysctl_ctx_list	aq_sysctl_ctx;
 };
 
 int aq_update_hw_stats(struct aq_dev *aq_dev);
diff --git a/sys/dev/aq/aq_fw.c b/sys/dev/aq/aq_fw.c
index a7dda9bedff0..7f9714d6c7ff 100644
--- a/sys/dev/aq/aq_fw.c
+++ b/sys/dev/aq/aq_fw.c
@@ -112,7 +112,7 @@ aq_fw_reset(struct aq_hw* hw)
 
 	hw->rbl_enabled = boot_exit_code != 0;
 
-	trace(dbg_init, "RBL enabled = %d", hw->rbl_enabled);
+	trace(hw, dbg_init, "RBL enabled = %d", hw->rbl_enabled);
 
 	/* Having FW version 0 is an indicator that cold start
 	 * is in progress. This means two things:
@@ -135,12 +135,12 @@ aq_fw_reset(struct aq_hw* hw)
 
 	switch (mode) {
 	case boot_mode_flb:
-		aq_log("FLB> F/W successfully loaded from flash.");
+		aq_log(hw, "FLB> F/W successfully loaded from flash.");
 		hw->flash_present = true;
 		return wait_init_mac_firmware(hw);
 
 	case boot_mode_rbl_flash:
-		aq_log("RBL> F/W loaded from flash. Host Bootload disabled.");
+		aq_log(hw, "RBL> F/W loaded from flash. Host Bootload disabled.");
 		hw->flash_present = true;
 		return wait_init_mac_firmware(hw);
 
@@ -153,7 +153,7 @@ aq_fw_reset(struct aq_hw* hw)
 		device_printf(hw->dev, "RBL> Host Bootload mode: this driver does not support Host Boot\n");
 		return (ENOTSUP);
 #else
-		trace(dbg_init, "RBL> Host Bootload mode");
+		trace(hw, dbg_init, "RBL> Host Bootload mode");
 		break;
 #endif // HOST_BOOT_DISABLE
 	}
@@ -172,16 +172,16 @@ aq_fw_ops_init(struct aq_hw* hw)
 	if (hw->fw_version.raw == 0)
 		hw->fw_version.raw = AQ_READ_REG(hw, 0x18);
 
-	aq_log("MAC F/W version is %d.%d.%d",
+	aq_log(hw, "MAC F/W version is %d.%d.%d",
 	    hw->fw_version.major_version, hw->fw_version.minor_version,
 	    hw->fw_version.build_number);
 
 	if (hw->fw_version.major_version == 1) {
-		trace(dbg_init, "using F/W ops v1.x");
+		trace(hw, dbg_init, "using F/W ops v1.x");
 		hw->fw_ops = &aq_fw1x_ops;
 		return (0);
 	} else if (hw->fw_version.major_version >= 2) {
-		trace(dbg_init, "using F/W ops v2.x");
+		trace(hw, dbg_init, "using F/W ops v2.x");
 		hw->fw_ops = &aq_fw2x_ops;
 		return (0);
 	}
@@ -253,12 +253,12 @@ mac_soft_reset_flb(struct aq_hw* hw)
 		}
 
 		if (flb_status == 0) {
-			trace_error(dbg_init,
+			trace_error(hw, dbg_init,
 			    "FLB> MAC kickstart failed: timed out");
 			return (ETIMEDOUT);
 		}
 
-		trace(dbg_init, "FLB> MAC kickstart done, %d ms", k);
+		trace(hw, dbg_init, "FLB> MAC kickstart done, %d ms", k);
 		/* FW reset */
 		reg_global_ctl2_set(hw, 0x80e0);
 		// Let Felicity hardware complete SMBUS transaction before
@@ -284,18 +284,18 @@ mac_soft_reset_flb(struct aq_hw* hw)
 	}
 
 	if (!restart_completed) {
-		trace_error(dbg_init, "FLB> Global Soft Reset failed");
+		trace_error(hw, dbg_init, "FLB> Global Soft Reset failed");
 		return (ETIMEDOUT);
 	}
 
-	trace(dbg_init, "FLB> F/W restart: %d ms", k * 10);
+	trace(hw, dbg_init, "FLB> F/W restart: %d ms", k * 10);
 	return (0);
 }
 
 static int
 mac_soft_reset_rbl(struct aq_hw* hw, enum aq_fw_bootloader_mode* mode)
 {
-	trace(dbg_init, "RBL> MAC reset STARTED!");
+	trace(hw, dbg_init, "RBL> MAC reset STARTED!");
 
 	reg_global_ctl2_set(hw, 0x40e1);
 	reg_glb_cpu_sem_set(hw, 1, 0);
@@ -329,20 +329,20 @@ mac_soft_reset_rbl(struct aq_hw* hw, enum aq_fw_bootloader_mode* mode)
 	}
 
 	if (rbl_status == 0 || rbl_status == 0xDEAD) {
-		trace_error(dbg_init, "RBL> RBL restart failed: timeout");
+		trace_error(hw, dbg_init, "RBL> RBL restart failed: timeout");
 		return (EBUSY);
 	}
 
 	if (rbl_status == RBL_STATUS_SUCCESS) {
 		if (mode)
 			*mode = boot_mode_rbl_flash;
-		trace(dbg_init, "RBL> reset complete! [Flash]");
+		trace(hw, dbg_init, "RBL> reset complete! [Flash]");
 	} else if (rbl_status == RBL_STATUS_HOST_BOOT) {
 		if (mode)
 			*mode = boot_mode_rbl_host_bootload;
-		trace(dbg_init, "RBL> reset complete! [Host Bootload]");
+		trace(hw, dbg_init, "RBL> reset complete! [Host Bootload]");
 	} else {
-		trace_error(dbg_init, "unknown RBL status 0x%x", rbl_status);
+		trace_error(hw, dbg_init, "unknown RBL status 0x%x", rbl_status);
 		return (EBUSY);
 	}
 
@@ -359,7 +359,7 @@ wait_init_mac_firmware(struct aq_hw* hw)
 		DELAY((1) * 1000);
 	}
 
-	trace_error(dbg_init,
+	trace_error(hw, dbg_init,
 	    "timeout waiting for reg 0x18. MAC f/w NOT READY");
 	return (EBUSY);
 }
diff --git a/sys/dev/aq/aq_fw.h b/sys/dev/aq/aq_fw.h
index 7bb35504eab6..ee45aedfc4e1 100644
--- a/sys/dev/aq/aq_fw.h
+++ b/sys/dev/aq/aq_fw.h
@@ -68,6 +68,9 @@ struct aq_firmware_ops
 	int (*get_mac_addr)(struct aq_hw* hw, uint8_t* mac_addr);
 	int (*get_stats)(struct aq_hw* hw, struct aq_hw_stats* stats);
 
+	/* Reports millidegrees Celsius. */
+	int (*get_temp)(struct aq_hw* hw, int* temp_mc);
+
 	int (*led_control)(struct aq_hw* hw, uint32_t mode);
 };
 
diff --git a/sys/dev/aq/aq_fw1x.c b/sys/dev/aq/aq_fw1x.c
index bac79497262c..771c62cc1303 100644
--- a/sys/dev/aq/aq_fw1x.c
+++ b/sys/dev/aq/aq_fw1x.c
@@ -194,10 +194,11 @@ aq_fw1x_reset(struct aq_hw* hw)
 		DELAY(10);
 	}
 
-	trace_error(dbg_init, "F/W 1.x reset finalize timeout");
+	trace_error(hw, dbg_init, "F/W 1.x reset finalize timeout");
 	return (EBUSY);
 }
 
+/* No fw_mtx here: the control register is written whole, never modified. */
 static int
 aq_fw1x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state mode,
     enum aq_fw_link_speed speed)
@@ -206,7 +207,7 @@ aq_fw1x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state mode,
 	state.mode = mpi_mode_to_fw1x(mode);
 	state.speed = link_speed_mask_to_fw1x(speed);
 
-	trace(dbg_init, "fw1x> set mode %d, rate mask = %#x; raw = %#x",
+	trace(hw, dbg_init, "fw1x> set mode %d, rate mask = %#x; raw = %#x",
 	     state.mode, state.speed, state.val);
 
 	AQ_WRITE_REG(hw, AQ_HW_MPI_CONTROL_ADR, state.val);
@@ -220,7 +221,7 @@ aq_fw1x_get_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state* mode,
 {
 	union aq_fw1x_state_reg state = { .val = AQ_READ_REG(hw, AQ_HW_MPI_STATE_ADR) };
 
-	trace(dbg_init, "fw1x> get_mode(): 0x36c -> %x, 0x368 -> %x",
+	trace(hw, dbg_init, "fw1x> get_mode(): 0x36c -> %x, 0x368 -> %x",
 	    state.val, AQ_READ_REG(hw, AQ_HW_MPI_CONTROL_ADR));
 
 	enum aq_hw_fw_mpi_state md = MPI_DEINIT;
@@ -263,7 +264,7 @@ aq_fw1x_get_mac_addr(struct aq_hw* hw, uint8_t* mac)
 
 	uint32_t efuse_shadow_addr = AQ_READ_REG(hw, 0x374);
 	if (efuse_shadow_addr == 0) {
-		trace_error(dbg_init, "couldn't read eFUSE Shadow Address");
+		trace_error(hw, dbg_init, "couldn't read eFUSE Shadow Address");
 		AQ_DBG_EXIT(EFAULT);
 		return (EFAULT);
 	}
@@ -282,7 +283,7 @@ aq_fw1x_get_mac_addr(struct aq_hw* hw, uint8_t* mac)
 
 	memcpy(mac, (uint8_t*)mac_addr, ETHER_ADDR_LEN);
 
-	trace(dbg_init, "fw1x> eFUSE MAC addr -> %02x-%02x-%02x-%02x-%02x-%02x",
+	trace(hw, dbg_init, "fw1x> eFUSE MAC addr -> %02x-%02x-%02x-%02x-%02x-%02x",
 	    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 
 	AQ_DBG_EXIT(0);
diff --git a/sys/dev/aq/aq_fw2x.c b/sys/dev/aq/aq_fw2x.c
index b757d23b9e1e..0f4bb7a89906 100644
--- a/sys/dev/aq/aq_fw2x.c
+++ b/sys/dev/aq/aq_fw2x.c
@@ -177,6 +177,7 @@ struct aq_fw2x_mailbox // struct fwHostInterface
 
 // MSM Statistics
 #define FW2X_CAP_STATISTICS (1ull << (32 + CAPS_HI_STATISTICS))
+#define FW2X_CAP_TEMPERATURE (1ull << (32 + CAPS_HI_TEMPERATURE))
 
 
 #define FW2X_RATE_MASK  (FW2X_RATE_100M | FW2X_RATE_1G | FW2X_RATE_2G5 | FW2X_RATE_5G | FW2X_RATE_10G)
@@ -249,11 +250,11 @@ aq_fw2x_reset(struct aq_hw* hw)
 	    (uint32_t*)&caps, sizeof caps/sizeof(uint32_t));
 	if (err == 0) {
 		hw->fw_caps = caps.caps_lo | ((uint64_t)caps.caps_hi << 32);
-		trace(dbg_init,
+		trace(hw, dbg_init,
 		     "fw2x> F/W capabilities mask = %llx",
 		     (unsigned long long)hw->fw_caps);
 	} else {
-		trace_error(dbg_init,
+		trace_error(hw, dbg_init,
 		     "fw2x> can't get F/W capabilities mask, error %d", err);
 	}
 
@@ -292,9 +293,12 @@ static int
 aq_fw2x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state mode,
     enum aq_fw_link_speed speed)
 {
-	uint64_t mpi_ctrl = get_mpi_ctrl(hw);
+	uint64_t mpi_ctrl;
 
 	AQ_DBG_ENTERA("speed=%d", speed);
+
+	mtx_lock(&hw->fw_mtx);
+	mpi_ctrl = get_mpi_ctrl(hw);
 	switch (mode) {
 	case MPI_INIT:
 		mpi_ctrl &= ~FW2X_RATE_MASK;
@@ -316,11 +320,14 @@ aq_fw2x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state mode,
 		break;
 
 	default:
-		trace_error(dbg_init, "fw2x> unknown MPI state %d", mode);
+		mtx_unlock(&hw->fw_mtx);
+		trace_error(hw, dbg_init, "fw2x> unknown MPI state %d", mode);
 		return (EINVAL);
 	}
 
 	set_mpi_ctrl(hw, mpi_ctrl);
+	mtx_unlock(&hw->fw_mtx);
+
 	AQ_DBG_EXIT(0);
 	return (0);
 }
@@ -329,10 +336,11 @@ static int
 aq_fw2x_get_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state* mode,
     enum aq_fw_link_speed* link_speed, enum aq_fw_link_fc* fc)
 {
-	uint64_t mpi_state = get_mpi_state(hw);
-	uint32_t rates = mpi_state & FW2X_RATE_MASK;
-
+	uint64_t mpi_state;
+	uint32_t rates;
 
+	mtx_lock(&hw->fw_mtx);
+	mpi_state = get_mpi_state(hw);
 	if (mode) {
 		uint64_t mpi_ctrl = get_mpi_ctrl(hw);
 		if (mpi_ctrl & FW2X_RATE_MASK)
@@ -340,6 +348,9 @@ aq_fw2x_get_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state* mode,
 		else
 		*mode = MPI_DEINIT;
 	}
+	mtx_unlock(&hw->fw_mtx);
+
+	rates = mpi_state & FW2X_RATE_MASK;
 
 	enum aq_fw_link_speed speed = aq_fw_none;
 
@@ -374,7 +385,7 @@ aq_fw2x_get_mac_addr(struct aq_hw* hw, uint8_t* mac)
 
 	uint32_t efuse_shadow_addr = AQ_READ_REG(hw, 0x364);
 	if (efuse_shadow_addr == 0) {
-		trace_error(dbg_init, "couldn't read eFUSE Shadow Address");
+		trace_error(hw, dbg_init, "couldn't read eFUSE Shadow Address");
 		AQ_DBG_EXIT(EFAULT);
 		return (EFAULT);
 	}
@@ -428,26 +439,64 @@ aq_fw2x_get_stats(struct aq_hw* hw, struct aq_hw_stats* stats)
 	int err;
 
 	if ((hw->fw_caps & FW2X_CAP_STATISTICS) == 0) {
-		trace_warn(dbg_fw, "fw2x> statistics not supported by F/W");
+		trace_warn(hw, dbg_fw, "fw2x> statistics not supported by F/W");
 		return (ENOTSUP);
 	}
 
 	/* Kick-and-read: take the F/W's previous snapshot, request the next. */
+	mtx_lock(&hw->fw_mtx);
 	err = aq_hw_fw_downld_dwords(hw,
 	    hw->mbox_addr + offsetof(struct aq_fw2x_mailbox, msm),
 	    (uint32_t*)&aq_fw2x_stats, sizeof aq_fw2x_stats/sizeof(uint32_t));
 
+	mpi_ctrl = get_mpi_ctrl(hw);
+	mpi_ctrl ^= FW2X_CAP_STATISTICS;
+	set_mpi_ctrl(hw, mpi_ctrl);
+	mtx_unlock(&hw->fw_mtx);
+
 	aq_fw2x_stats_to_fw_stats(stats, &aq_fw2x_stats);
 
 	if (err != 0)
-		trace_error(dbg_fw,
+		trace_error(hw, dbg_fw,
 		    "fw2x> download statistics data FAILED, error %d", err);
 
+	return (err);
+}
+
+static int
+aq_fw2x_get_temp(struct aq_hw* hw, int* temp_mc)
+{
+	uint64_t mpi_ctrl, req_bit;
+	uint32_t raw;
+	int err;
+
+	if ((hw->fw_caps & FW2X_CAP_TEMPERATURE) == 0)
+		return (ENOTSUP);
+
+	/* Toggle the request bit and wait for the F/W to echo it back. */
+	mtx_lock(&hw->fw_mtx);
 	mpi_ctrl = get_mpi_ctrl(hw);
-	mpi_ctrl ^= FW2X_CAP_STATISTICS;
-	set_mpi_ctrl(hw, mpi_ctrl);
+	req_bit = mpi_ctrl & FW2X_CAP_TEMPERATURE;
+	set_mpi_ctrl(hw, mpi_ctrl ^ FW2X_CAP_TEMPERATURE);
 
-	return (err);
+	err = AQ_HW_WAIT_FOR((get_mpi_state(hw) & FW2X_CAP_TEMPERATURE) !=
+	    req_bit, 1, 10000);
+	if (err == 0)
+		err = aq_hw_fw_downld_dwords(hw, hw->mbox_addr +
+		    offsetof(struct aq_fw2x_mailbox, phy_temperature),
+		    &raw, 1);
+	mtx_unlock(&hw->fw_mtx);
+
+	if (err != 0) {
+		trace_error(hw, dbg_fw,
+		    "fw2x> temperature read FAILED, error %d", err);
+		return (err);
+	}
+
+	/* F/W reports 1/256 degree Celsius. */
+	*temp_mc = (int)(int16_t)(raw & 0xffff) * 1000 / 256;
+
+	return (0);
 }
 
 static int
@@ -476,6 +525,7 @@ const struct aq_firmware_ops aq_fw2x_ops =
 
 	.get_mac_addr = aq_fw2x_get_mac_addr,
 	.get_stats = aq_fw2x_get_stats,
+	.get_temp = aq_fw2x_get_temp,
 
 	.led_control = aq_fw2x_led_control,
 };
diff --git a/sys/dev/aq/aq_hw.h b/sys/dev/aq/aq_hw.h
index ee824641294f..c0888f03ab23 100644
--- a/sys/dev/aq/aq_hw.h
+++ b/sys/dev/aq/aq_hw.h
@@ -38,6 +38,8 @@
 #include <sys/types.h>
 #include <sys/cdefs.h>
 #include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
 #include <machine/atomic.h>
 #include <machine/cpufunc.h>
 #include <machine/bus.h>
@@ -207,6 +209,12 @@ struct aq_hw {
 
 	/* Atlantic 2: firmware statistics interface version (A0/B0). */
 	uint32_t aq2_iface;
+
+	/* Atlantic 2: firmware banner already announced for this attach. */
+	bool fw_announced;
+
+	/* Serialises the F/W MPI control register and mailbox. */
+	struct mtx fw_mtx;
 };
 
 #define AQ_HW_MAC      0U
diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c
index a8b87daa59d4..7c3a88740f3b 100644
--- a/sys/dev/aq/aq_main.c
+++ b/sys/dev/aq/aq_main.c
@@ -332,7 +332,7 @@ aq_if_attach_pre(if_ctx_t ctx)
 	struct aq_dev *softc;
 	struct aq_hw *hw;
 	if_softc_ctx_t scctx;
-	int rc;
+	int dbg, rc;
 
 	AQ_DBG_ENTER();
 	softc = iflib_get_softc(ctx);
@@ -345,6 +345,9 @@ aq_if_attach_pre(if_ctx_t ctx)
 	softc->sctx = iflib_get_sctx(ctx);
 	scctx = softc->scctx;
 
+	mtx_init(&softc->hw.fw_mtx, device_get_nameunit(softc->dev),
+	    "aq firmware", MTX_DEF);
+
 	softc->mmio_rid = PCIR_BAR(0);
 	softc->mmio_res = bus_alloc_resource_any(softc->dev, SYS_RES_MEMORY,
 	    &softc->mmio_rid, RF_ACTIVE|RF_SHAREABLE);
@@ -361,6 +364,7 @@ aq_if_attach_pre(if_ctx_t ctx)
 	softc->hw.hw_tag = softc->mmio_tag;
 	softc->hw.hw_handle = softc->mmio_handle;
 	softc->hw.dev = softc->dev;
+	softc->hw.aq_dev = softc;
 	softc->hw.device_id = pci_get_device(softc->dev);
 	if (aq_is_atlantic2(softc->hw.device_id))
 		softc->hw.chip_features |= AQ_HW_CHIP_ATLANTIC2;
@@ -371,6 +375,15 @@ aq_if_attach_pre(if_ctx_t ctx)
 	hw->fc.fc_tx = 1;
 	softc->linkup = 0U;
 
+	softc->dbg_level = AQ_DBG_LEVEL_DEFAULT;
+	softc->dbg_categories = AQ_DBG_CATEGORIES_DEFAULT;
+	if (resource_int_value(device_get_name(softc->dev),
+	    device_get_unit(softc->dev), "debug", &dbg) == 0)
+		softc->dbg_level = dbg;
+	if (resource_int_value(device_get_name(softc->dev),
+	    device_get_unit(softc->dev), "debug_categories", &dbg) == 0)
+		softc->dbg_categories = dbg;
+
 	/* Look up ops and caps. */
 	rc = aq_hw_mpi_create(hw);
 	if (rc != 0) {
@@ -438,6 +451,8 @@ fail:
 	if (softc->mmio_res != NULL)
 		bus_release_resource(softc->dev, SYS_RES_MEMORY,
 		    softc->mmio_rid, softc->mmio_res);
+	/* iflib skips ifdi_detach when ifdi_attach_pre fails. */
+	mtx_destroy(&softc->hw.fw_mtx);
 
 	AQ_DBG_EXIT(rc);
 	return (rc);
@@ -503,6 +518,8 @@ aq_if_detach(if_ctx_t ctx)
 	AQ_DBG_ENTER();
 	softc = iflib_get_softc(ctx);
 
+	sysctl_ctx_free(&softc->aq_sysctl_ctx);
+
 	aq_hw_deinit(&softc->hw);
 
 	for (i = 0; i < softc->rx_rings_count; i++)
@@ -516,6 +533,8 @@ aq_if_detach(if_ctx_t ctx)
 
 	free(softc->vlan_tags, M_AQ);
 
+	mtx_destroy(&softc->hw.fw_mtx);
+
 	AQ_DBG_EXIT(0);
 	return (0);
 }
@@ -851,7 +870,7 @@ aq_mc_filter_apply(void *arg, struct sockaddr_dl *dl, u_int count)
 	mac_addr = LLADDR(dl);
 	aq_hw_mac_addr_set(hw, mac_addr, count + 1);
 
-	aq_log_detail("set %d mc address %6D", count + 1, mac_addr, ":");
+	aq_log_detail(hw, "set %d mc address %6D", count + 1, mac_addr, ":");
 	return (1);
 }
 
@@ -1055,13 +1074,14 @@ aq_if_msix_intr_assign(if_ctx_t ctx, int msix)
 		rc = iflib_irq_alloc_generic(ctx, &softc->rx_rings[i]->irq,
 		    vector + 1, IFLIB_INTR_RXTX, aq_isr_rx, softc->rx_rings[i],
 			softc->rx_rings[i]->index, irq_name);
-		device_printf(softc->dev, "Assign IRQ %u to rx ring %u\n",
-					  vector, softc->rx_rings[i]->index);
-
 		if (rc) {
 			device_printf(softc->dev, "failed to set up RX handler\n");
 			goto fail;
 		}
+		if (bootverbose)
+			device_printf(softc->dev,
+			    "Assign IRQ %u to rx ring %u\n", vector,
+			    softc->rx_rings[i]->index);
 
 		softc->rx_rings[i]->msix = vector;
 	}
@@ -1075,20 +1095,24 @@ aq_if_msix_intr_assign(if_ctx_t ctx, int msix)
 		    &softc->rx_rings[softc->tx_rings[i]->msix]->irq,
 		    IFLIB_INTR_TX, softc->tx_rings[i],
 		    softc->tx_rings[i]->index, irq_name);
-		device_printf(softc->dev, "Assign IRQ %u to tx ring %u\n",
-		    softc->tx_rings[i]->msix, softc->tx_rings[i]->index);
+		if (bootverbose)
+			device_printf(softc->dev,
+			    "tx ring %u shares IRQ %u\n",
+			    softc->tx_rings[i]->index,
+			    softc->tx_rings[i]->msix);
 	}
 
 	rc = iflib_irq_alloc_generic(ctx, &softc->irq, rx_vectors + 1,
 	    IFLIB_INTR_ADMIN, aq_linkstat_isr, softc, 0, "aq");
-	softc->msix = rx_vectors;
-	device_printf(softc->dev, "Assign IRQ %u to admin proc \n",
-	    rx_vectors);
 	if (rc) {
 		device_printf(iflib_get_dev(ctx),
 		    "Failed to register admin handler\n");
 		goto fail;
 	}
+	softc->msix = rx_vectors;
+	if (bootverbose)
+		device_printf(softc->dev, "Assign IRQ %u to admin proc\n",
+		    rx_vectors);
 	AQ_DBG_EXIT(0);
 	return (0);
 
@@ -1367,32 +1391,61 @@ aq_sysctl_print_rx_tail(SYSCTL_HANDLER_ARGS)
 	return (0);
 }
 
+static int
+aq_sysctl_temperature(SYSCTL_HANDLER_ARGS)
+{
+	struct aq_dev   *softc = arg1;
*** 107 LINES SKIPPED ***