The branch main has been updated by adrian:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=418e7fd13b5aa201c0f9eb65ab2f9a0c1810ba23

commit 418e7fd13b5aa201c0f9eb65ab2f9a0c1810ba23
Author:     Nick Price <[email protected]>
AuthorDate: 2026-07-19 16:48:14 +0000
Commit:     Adrian Chadd <[email protected]>
CommitDate: 2026-07-19 16:48:14 +0000

    aq(4): program the Atlantic 2 multiqueue datapath
    
    Wire up the Atlantic 2 receive datapath: the action-resolver table (ART),
    multiqueue RSS, QoS, and interrupt moderation.
    
    RX action-resolver table: Atlantic 2 replaces Atlantic 1's discrete RX
    filter registers with an ART -- hardware computes a per-packet
    classification tag, then walks {tag, mask, action} rows to drop, assign a
    queue, or assign a TC.  aq_hw_art_filter_set() installs one row under the
    ART semaphore; aq_hw_init_rx_path() enables the resolver, tags L2
    unicast/broadcast, installs the unicast/all-multicast and VLAN drop rows,
    and assigns every 802.1p priority to TC 0 (mirroring the Atlantic 1
    user-priority map, since our RX side is a single 8-ring group in TC 0).
    Tag every enabled VLAN filter in the per-filter resolver-tag field -- a
    register the BSD ports never write -- because the VLAN drop row matches
    resolver tag 0, so without it all tagged receive was dead under VLAN
    filtering.  Promiscuous mode disables the drop rows rather than toggling
    the Atlantic 1 promiscuous bits; all ART callers surface a semaphore
    timeout consistently.  The Atlantic 1 RX_TCP_RSS_HASH and TPO2
    programming is gated to Atlantic 1.
    
    Multiqueue RSS and QoS: fill Atlantic 2's own per-TC redirection table
    (AQ2_RPF_RSS_REDIR), skipping the Atlantic 1 table and its write-enable
    handshake.  Program Atlantic 2's smaller packet-buffer sizes, its wider
    data-TC credit/weight fields, and its ring-to-TC map, using
    aq_hw_active_tcs() for the TC loops.
    
    RSS hash types: the Atlantic 2 resolver has per-protocol hash-type enables
    in REDIR2, so build the mask from aq_rss_hashconfig() instead of
    hardcoding every protocol -- UDP 4-tuple hashing now follows the kernel
    policy (off by default) with no L3L4 flow-filter workaround, and
    aq_hw_udp_rss_enable() is skipped on Atlantic 2.  The kernel-to-hardware
    hash-type mapping is a small static lookup table rather than a nine-branch
    chain, since the two bit spaces do not share a simple shift.
    
    Tx interrupt moderation: Atlantic 2's per-ring Tx moderation control
    register lives at a different address, but its field layout matches the
    value the driver already builds, so write that value straight to it; Rx
    moderation is shared.
    
    HW-validated on AQC107 <-> AQC113C: TCP RSS spreads across 7/8 RX queues
    under 16 parallel flows, rx_err=0.
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D58142
---
 sys/dev/aq/aq2_hw.h  |   9 +++
 sys/dev/aq/aq_hw.c   | 200 ++++++++++++++++++++++++++++++++++++++++++++++-----
 sys/dev/aq/aq_hw.h   |   2 +-
 sys/dev/aq/aq_main.c |  27 ++++---
 4 files changed, 210 insertions(+), 28 deletions(-)

diff --git a/sys/dev/aq/aq2_hw.h b/sys/dev/aq/aq2_hw.h
index a2c21162c2d9..21c4e0ddce09 100644
--- a/sys/dev/aq/aq2_hw.h
+++ b/sys/dev/aq/aq2_hw.h
@@ -125,6 +125,15 @@ aq_is_atlantic2(uint16_t device_id)
 #define  AQ2_RPF_REDIR2_INDEX                  (1 << 12)
 #define  AQ2_RPF_REDIR2_HASHTYPE               0x000001FF
 #define  AQ2_RPF_REDIR2_HASHTYPE_NONE          0
+#define  AQ2_RPF_REDIR2_HASHTYPE_IP            (1 << 0)
+#define  AQ2_RPF_REDIR2_HASHTYPE_TCP4          (1 << 1)
+#define  AQ2_RPF_REDIR2_HASHTYPE_UDP4          (1 << 2)
+#define  AQ2_RPF_REDIR2_HASHTYPE_IP6           (1 << 3)
+#define  AQ2_RPF_REDIR2_HASHTYPE_TCP6          (1 << 4)
+#define  AQ2_RPF_REDIR2_HASHTYPE_UDP6          (1 << 5)
+#define  AQ2_RPF_REDIR2_HASHTYPE_IP6EX         (1 << 6)
+#define  AQ2_RPF_REDIR2_HASHTYPE_TCP6EX                (1 << 7)
+#define  AQ2_RPF_REDIR2_HASHTYPE_UDP6EX                (1 << 8)
 #define  AQ2_RPF_REDIR2_HASHTYPE_ALL           0x000001FF
 
 #define AQ2_RX_Q_TC_MAP_REG(i)                 (0x5900 + (i) * 4)
diff --git a/sys/dev/aq/aq_hw.c b/sys/dev/aq/aq_hw.c
index bbcc79805a4e..5dbdec172096 100644
--- a/sys/dev/aq/aq_hw.c
+++ b/sys/dev/aq/aq_hw.c
@@ -37,6 +37,7 @@
 #include <sys/endian.h>
 #include <sys/socket.h>
 #include <machine/cpu.h>
+#include <net/rss_config.h>
 
 #include "aq_hw.h"
 #include "aq2_hw.h"
@@ -405,9 +406,11 @@ aq_hw_qos_set(struct aq_hw *hw)
        uint32_t buff_size = 0U;
        uint32_t n_tcs;
        unsigned int i_priority = 0U;
+       unsigned int i;
        int err = 0;
 
        AQ_DBG_ENTER();
+
        /* TPS Descriptor rate init */
        tps_tx_pkt_shed_desc_rate_curr_time_res_set(hw, 0x0U);
        tps_tx_pkt_shed_desc_rate_lim_set(hw, 0xA);
@@ -421,11 +424,22 @@ aq_hw_qos_set(struct aq_hw *hw)
 
        /* One TC per active 8-ring group; share the buffer across them. */
        n_tcs = aq_hw_active_tcs(hw);
-       buff_size = AQ_HW_TXBUF_MAX / n_tcs;
+       buff_size = (IS_CHIP_FEATURE(hw, ATLANTIC2) ?
+           AQ2_HW_TXBUF_MAX : AQ_HW_TXBUF_MAX) / n_tcs;
 
        for (tc = 0; tc < n_tcs; tc++) {
-               tps_tx_pkt_shed_tc_data_max_credit_set(hw, 0xFFF, tc);
-               tps_tx_pkt_shed_tc_data_weight_set(hw, 0x64, tc);
+               if (IS_CHIP_FEATURE(hw, ATLANTIC2)) {
+                       /* Atlantic 2's data-TC credit/weight fields are wider. 
*/
+                       AQ_WRITE_REG_BIT(hw, TPS_DATA_TCT_REG(tc),
+                           TPS2_DATA_TCT_CREDIT_MAX,
+                           TPS2_DATA_TCT_CREDIT_MAX_SHIFT, 0xfff0);
+                       AQ_WRITE_REG_BIT(hw, TPS_DATA_TCT_REG(tc),
+                           TPS2_DATA_TCT_WEIGHT,
+                           TPS2_DATA_TCT_WEIGHT_SHIFT, 0x640);
+               } else {
+                       tps_tx_pkt_shed_tc_data_max_credit_set(hw, 0xFFF, tc);
+                       tps_tx_pkt_shed_tc_data_weight_set(hw, 0x64, tc);
+               }
                tps_tx_pkt_shed_desc_tc_max_credit_set(hw, 0x50, tc);
                tps_tx_pkt_shed_desc_tc_weight_set(hw, 0x1E, tc);
 
@@ -438,7 +452,8 @@ aq_hw_qos_set(struct aq_hw *hw)
 
        /* QoS Rx buf size per TC */
        tc = 0;
-       buff_size = AQ_HW_RXBUF_MAX;
+       buff_size = IS_CHIP_FEATURE(hw, ATLANTIC2) ?
+           AQ2_HW_RXBUF_MAX : AQ_HW_RXBUF_MAX;
 
        rpb_rx_pkt_buff_size_per_tc_set(hw, buff_size, tc);
        rpb_rx_buff_hi_threshold_per_tc_set(hw,
@@ -450,6 +465,20 @@ aq_hw_qos_set(struct aq_hw *hw)
        for (i_priority = 8U; i_priority--;)
                rpf_rpb_user_priority_tc_map_set(hw, i_priority, 0U);
 
+       /* Atlantic 2 ring -> TC map (TC = ring / HW_ATL_B0_RINGS_PER_TC). */
+       if (IS_CHIP_FEATURE(hw, ATLANTIC2)) {
+               AQ_WRITE_REG_BIT(hw, TPB_TX_BUF_REG,
+                   TPB_TX_BUF_TC_Q_RAND_MAP_EN,
+                   TPB_TX_BUF_TC_Q_RAND_MAP_EN_SHIFT, 1);
+               /* TX packs a byte per ring (4/reg); RX a nibble (8/reg). */
+               for (i = 0; i < HW_ATL_B0_RINGS_MAX / 4; i++)
+                       AQ_WRITE_REG(hw, AQ2_TX_Q_TC_MAP_REG(i),
+                           (i / 2) * 0x01010101U);
+               for (i = 0; i < HW_ATL_B0_RINGS_MAX / 8; i++)
+                       AQ_WRITE_REG(hw, AQ2_RX_Q_TC_MAP_REG(i),
+                           i * 0x11111111U);
+       }
+
        err = aq_hw_err_from_flags(hw);
        AQ_DBG_EXIT(err);
        return (err);
@@ -541,7 +570,8 @@ aq_hw_init_tx_path(struct aq_hw *hw)
        tdm_tx_desc_wr_wb_irq_en_set(hw, 1U);
 
        /* misc */
-       AQ_WRITE_REG(hw, 0x00007040U, 0x00010000U);//IS_CHIP_FEATURE(TPO2) ? 
0x00010000U : 0x00000000U);
+       AQ_WRITE_REG(hw, 0x00007040U,
+           IS_CHIP_FEATURE(hw, TPO2) ? 0x00010000U : 0x00000000U);
        tdm_tx_dca_en_set(hw, 0U);
        tdm_tx_dca_mode_set(hw, 0U);
 
@@ -552,17 +582,68 @@ aq_hw_init_tx_path(struct aq_hw *hw)
        return (err);
 }
 
+/* Atlantic 2: install one action-resolver-table row under the ART semaphore. 
*/
+static int
+aq2_art_filter_set(struct aq_hw *hw, uint32_t idx, uint32_t tag,
+    uint32_t mask, uint32_t action)
+{
+       if (AQ_HW_WAIT_FOR(reg_glb_cpu_sem_get(hw, AQ2_ART_SEM_INDEX) == 1U,
+           10U, 1000U) != 0) {
+               device_printf(hw->dev, "ART semaphore timeout, idx %u\n", idx);
+               return (EBUSY);
+       }
+
+       idx += hw->art_filter_base_index;
+       AQ_WRITE_REG(hw, AQ2_RPF_ACT_ART_REQ_TAG_REG(idx), tag);
+       AQ_WRITE_REG(hw, AQ2_RPF_ACT_ART_REQ_MASK_REG(idx), mask);
+       AQ_WRITE_REG(hw, AQ2_RPF_ACT_ART_REQ_ACTION_REG(idx), action);
+
+       reg_glb_cpu_sem_set(hw, 1U, AQ2_ART_SEM_INDEX);
+       return (0);
+}
+
 static int
 aq_hw_init_rx_path(struct aq_hw *hw)
 {
        unsigned int control_reg_val = 0U;
        int i;
-       int err;
+       int err = 0;
 
        AQ_DBG_ENTER();
        /* Rx TC/RSS number config */
        rpb_rpf_rx_traf_class_mode_set(hw, 1U);
 
+       /*
+        * Atlantic 2: program the RSS hash types the kernel honors.  UDP is
+        * excluded by default (see aq_rss_hashconfig()); clearing its per-cast
+        * bits keeps fragmented UDP on a single queue without the A1 L3L4
+        * flow-filter workaround.
+        */
+       if (IS_CHIP_FEATURE(hw, ATLANTIC2)) {
+               static const struct {
+                       u_int           kern;
+                       uint32_t        hw;
+               } ht_map[] = {
+                       { RSS_HASHTYPE_RSS_IPV4,        
AQ2_RPF_REDIR2_HASHTYPE_IP },
+                       { RSS_HASHTYPE_RSS_TCP_IPV4,    
AQ2_RPF_REDIR2_HASHTYPE_TCP4 },
+                       { RSS_HASHTYPE_RSS_UDP_IPV4,    
AQ2_RPF_REDIR2_HASHTYPE_UDP4 },
+                       { RSS_HASHTYPE_RSS_IPV6,        
AQ2_RPF_REDIR2_HASHTYPE_IP6 },
+                       { RSS_HASHTYPE_RSS_TCP_IPV6,    
AQ2_RPF_REDIR2_HASHTYPE_TCP6 },
+                       { RSS_HASHTYPE_RSS_UDP_IPV6,    
AQ2_RPF_REDIR2_HASHTYPE_UDP6 },
+                       { RSS_HASHTYPE_RSS_IPV6_EX,     
AQ2_RPF_REDIR2_HASHTYPE_IP6EX },
+                       { RSS_HASHTYPE_RSS_TCP_IPV6_EX, 
AQ2_RPF_REDIR2_HASHTYPE_TCP6EX },
+                       { RSS_HASHTYPE_RSS_UDP_IPV6_EX, 
AQ2_RPF_REDIR2_HASHTYPE_UDP6EX },
+               };
+               u_int hc = aq_rss_hashconfig();
+               uint32_t ht = 0;
+
+               for (i = 0; i < (int)nitems(ht_map); i++)
+                       if (hc & ht_map[i].kern)
+                               ht |= ht_map[i].hw;
+               AQ_WRITE_REG_BIT(hw, AQ2_RPF_REDIR2_REG,
+                   AQ2_RPF_REDIR2_HASHTYPE, 0, ht);
+       }
+
        /* Rx flow control */
        rpb_rx_flow_ctl_mode_set(hw, 1U);
 
@@ -589,13 +670,38 @@ aq_hw_init_rx_path(struct aq_hw *hw)
        /* Rx Interrupts */
        rdm_rx_desc_wr_wb_irq_en_set(hw, 1U);
 
-       /* misc */
-       control_reg_val = 0x000F0000U; //RPF2
+       if (IS_CHIP_FEATURE(hw, ATLANTIC2)) {
+               /* Enable the resolver table and tag L2 unicast/broadcast. */
+               AQ_WRITE_REG_BIT(hw, AQ2_RPF_REC_TAB_ENABLE_REG,
+                   AQ2_RPF_REC_TAB_ENABLE_MASK, 0, 
AQ2_RPF_REC_TAB_ENABLE_MASK);
+               AQ_WRITE_REG_BIT(hw, RPF_L2UC_MSW_REG(0),
+                   RPF_L2UC_MSW_TAG, RPF_L2UC_MSW_TAG_SHIFT, 1);
+               AQ_WRITE_REG_BIT(hw, AQ2_RPF_L2BC_TAG_REG,
+                   AQ2_RPF_L2BC_TAG_MASK, 0, 1);
+
+               /* Drop rows (promisc lifts them); all PCP -> TC 0. */
+               err = aq2_art_filter_set(hw, AQ2_RPF_INDEX_L2_PROMISC_OFF, 0,
+                   AQ2_RPF_TAG_UC_MASK | AQ2_RPF_TAG_ALLMC_MASK,
+                   AQ2_ART_ACTION_DROP);
+               if (err == 0)
+                       err = aq2_art_filter_set(hw,
+                           AQ2_RPF_INDEX_VLAN_PROMISC_OFF, 0,
+                           AQ2_RPF_TAG_VLAN_MASK | AQ2_RPF_TAG_UNTAG_MASK,
+                           AQ2_ART_ACTION_DROP);
+               for (i = 0; err == 0 && i < 8; i++)
+                       err = aq2_art_filter_set(hw, AQ2_RPF_INDEX_PCP_TO_TC + 
i,
+                           ((uint32_t)i << AQ2_RPF_TAG_PCP_SHIFT),
+                           AQ2_RPF_TAG_PCP_MASK,
+                           AQ2_ART_ACTION_ASSIGN_TC(0));
+       } else {
+               /* misc */
+               control_reg_val = 0x000F0000U; //RPF2
 
-       /* RSS hash type set for IP/TCP */
-       control_reg_val |= 0x1EU;
+               /* RSS hash type set for IP/TCP */
+               control_reg_val |= 0x1EU;
 
-       AQ_WRITE_REG(hw, 0x00005040U, control_reg_val);
+               AQ_WRITE_REG(hw, 0x00005040U, control_reg_val);
+       }
 
        rpfl2broadcast_en_set(hw, 1U);
        rpfl2broadcast_flr_act_set(hw, 1U);
@@ -604,7 +710,8 @@ aq_hw_init_rx_path(struct aq_hw *hw)
        rdm_rx_dca_en_set(hw, 0U);
        rdm_rx_dca_mode_set(hw, 0U);
 
-       err = aq_hw_err_from_flags(hw);
+       if (err == 0)
+               err = aq_hw_err_from_flags(hw);
        AQ_DBG_EXIT(err);
        return (err);
 }
@@ -628,6 +735,10 @@ aq_hw_mac_addr_set(struct aq_hw *hw, uint8_t *mac_addr, 
uint8_t index)
        rpfl2_uc_flr_en_set(hw, 0U, index);
        rpfl2unicast_dest_addresslsw_set(hw, l, index);
        rpfl2unicast_dest_addressmsw_set(hw, h, index);
+       /* Atlantic 2: classify this address into the resolver table. */
+       if (IS_CHIP_FEATURE(hw, ATLANTIC2))
+               AQ_WRITE_REG_BIT(hw, RPF_L2UC_MSW_REG(index),
+                   RPF_L2UC_MSW_TAG, RPF_L2UC_MSW_TAG_SHIFT, 1);
        rpfl2_uc_flr_en_set(hw, 1U, index);
 
        err = aq_hw_err_from_flags(hw);
@@ -686,6 +797,11 @@ aq_hw_init(struct aq_hw *hw, uint8_t *mac_addr, uint8_t 
adm_irq, bool msix)
 
        aq_hw_qos_set(hw);
 
+       /* Atlantic 2: turn on the new RPF / action-resolver engine. */
+       if (IS_CHIP_FEATURE(hw, ATLANTIC2))
+               AQ_WRITE_REG_BIT(hw, AQ2_RPF_NEW_CTRL_REG,
+                   AQ2_RPF_NEW_CTRL_ENABLE, 11, 1);
+
        err = aq_hw_err_from_flags(hw);
        if (err != 0)
                goto err_exit;
@@ -784,8 +900,13 @@ aq_hw_interrupt_moderation_set(struct aq_hw *hw)
        rdm_rdm_intr_moder_en_set(hw, active);
 
        for (int i = HW_ATL_B0_RINGS_MAX; i--;) {
-               reg_tx_intr_moder_ctrl_set(hw,  itr_tx, i);
-               reg_rx_intr_moder_ctrl_set(hw,  itr_rx, i);
+               /* A2 Tx moderation register moved; same layout.  Rx is shared. 
*/
+               if (IS_CHIP_FEATURE(hw, ATLANTIC2))
+                       AQ_WRITE_REG(hw, AQ2_TX_INTR_MODERATION_CTL_REG(i),
+                           itr_tx);
+               else
+                       reg_tx_intr_moder_ctrl_set(hw, itr_tx, i);
+               reg_rx_intr_moder_ctrl_set(hw, itr_rx, i);
        }
 
        err = aq_hw_err_from_flags(hw);
@@ -816,6 +937,12 @@ hw_atl_b0_hw_vlan_set(struct aq_hw *hw, struct 
aq_rx_filter_vlan *aq_vlans)
                                                   aq_vlans[i].vlan_id,
                                                   i);
                        hw_atl_rpf_vlan_flr_act_set(hw, 1U, i);
+                       /* A2: tag the filter so the VLAN drop row passes it. */
+                       if (IS_CHIP_FEATURE(hw, ATLANTIC2))
+                               AQ_WRITE_REG_BIT(hw,
+                                   AQ2_RPF_VLAN_FLR_TAG_REG(i),
+                                   AQ2_RPF_VLAN_FLR_TAG,
+                                   AQ2_RPF_VLAN_FLR_TAG_SHIFT, 1U);
                        hw_atl_rpf_vlan_flr_en_set(hw, 1U, i);
                        if (aq_vlans[i].queue != 0xFF) {
                                hw_atl_rpf_vlan_rxq_flr_set(hw,
@@ -832,26 +959,45 @@ hw_atl_b0_hw_vlan_set(struct aq_hw *hw, struct 
aq_rx_filter_vlan *aq_vlans)
 int
 hw_atl_b0_hw_vlan_promisc_set(struct aq_hw *hw, bool promisc)
 {
+       int err;
+
+       /* A2: fallible ART write first; a timeout leaves the legacy bit as-is. 
*/
+       if (IS_CHIP_FEATURE(hw, ATLANTIC2)) {
+               err = aq2_art_filter_set(hw, AQ2_RPF_INDEX_VLAN_PROMISC_OFF,
+                   0, AQ2_RPF_TAG_VLAN_MASK | AQ2_RPF_TAG_UNTAG_MASK,
+                   promisc ? AQ2_ART_ACTION_DISABLE : AQ2_ART_ACTION_DROP);
+               if (err != 0)
+                       return (err);
+       }
        hw_atl_rpf_vlan_prom_mode_en_set(hw, promisc);
        return aq_hw_err_from_flags(hw);
 }
 
 
-void
+int
 aq_hw_set_promisc(struct aq_hw *hw, bool l2_promisc, bool vlan_promisc,
     bool mc_promisc)
 {
+       int err = 0;
+
        AQ_DBG_ENTERA("promisc %d, vlan_promisc %d, allmulti %d", l2_promisc,
            vlan_promisc, mc_promisc);
 
        rpfl2promiscuous_mode_en_set(hw, l2_promisc);
 
-       hw_atl_b0_hw_vlan_promisc_set(hw, l2_promisc | vlan_promisc);
+       err = hw_atl_b0_hw_vlan_promisc_set(hw, l2_promisc | vlan_promisc);
+
+       /* A2: promisc toggles the unicast drop row. */
+       if (err == 0 && IS_CHIP_FEATURE(hw, ATLANTIC2))
+               err = aq2_art_filter_set(hw, AQ2_RPF_INDEX_L2_PROMISC_OFF, 0,
+                   AQ2_RPF_TAG_UC_MASK | AQ2_RPF_TAG_ALLMC_MASK,
+                   l2_promisc ? AQ2_ART_ACTION_DISABLE : AQ2_ART_ACTION_DROP);
 
        rpfl2_accept_all_mc_packets_set(hw, mc_promisc);
        rpfl2multicast_flr_en_set(hw, mc_promisc, 0);
 
-       AQ_DBG_EXIT(0);
+       AQ_DBG_EXIT(err);
+       return (err);
 }
 
 int
@@ -919,6 +1065,26 @@ aq_hw_rss_set(struct aq_hw *hw,
 
        memset(bitary, 0, sizeof(bitary));
 
+       /* A2 per-TC redirection table; flat RX rings, same queue per TC. */
+       if (IS_CHIP_FEATURE(hw, ATLANTIC2)) {
+               uint32_t n_tcs = aq_hw_active_tcs(hw);
+               int tc, slot, q;
+               uint32_t word;
+
+               AQ_WRITE_REG_BIT(hw, AQ2_RPF_REDIR2_REG, AQ2_RPF_REDIR2_INDEX,
+                   12, 0);
+               for (slot = 0; slot < AQ2_RPF_RSS_REDIR_MAX; slot++) {
+                       q = rss_table[slot] &
+                           (HW_ATL_RSS_INDIRECTION_QUEUES_MAX - 1U);
+                       word = 0U;
+                       for (tc = 0; (uint32_t)tc < n_tcs; tc++)
+                               word |= (uint32_t)q << (5 * tc);
+                       AQ_WRITE_REG(hw, AQ2_RPF_RSS_REDIR_REG(0, slot), word);
+               }
+               /* A2 uses this table only; skip the legacy indirection writes. 
*/
+               return (aq_hw_err_from_flags(hw));
+       }
+
        for (i = HW_ATL_RSS_INDIRECTION_TABLE_MAX; i--;) {
                uint32_t bit_pos = i * HW_ATL_RSS_INDIRECTION_ENTRY_BITS;
                uint32_t word = bit_pos / 16U;
diff --git a/sys/dev/aq/aq_hw.h b/sys/dev/aq/aq_hw.h
index eb46641ef35d..a19a136ff172 100644
--- a/sys/dev/aq/aq_hw.h
+++ b/sys/dev/aq/aq_hw.h
@@ -383,7 +383,7 @@ int aq_hw_deinit(struct aq_hw *hw);
 int aq_hw_ver_match(const struct aq_hw_fw_version* ver_expected,
     const struct aq_hw_fw_version* ver_actual);
 
-void aq_hw_set_promisc(struct aq_hw *hw, bool l2_promisc, bool vlan_promisc,
+int aq_hw_set_promisc(struct aq_hw *hw, bool l2_promisc, bool vlan_promisc,
     bool mc_promisc);
 
 int aq_hw_set_power(struct aq_hw *hw, unsigned int power_state);
diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c
index f52407827bc5..b78407a56c67 100644
--- a/sys/dev/aq/aq_main.c
+++ b/sys/dev/aq/aq_main.c
@@ -773,9 +773,11 @@ aq_if_init(if_ctx_t ctx)
        aq_if_enable_intr(ctx);
        aq_hw_rss_hash_set(&softc->hw, softc->rss_key);
        aq_hw_rss_set(&softc->hw, softc->rss_table);
-       aq_hw_udp_rss_enable(hw, (aq_rss_hashconfig() &
-           (RSS_HASHTYPE_RSS_UDP_IPV4 | RSS_HASHTYPE_RSS_UDP_IPV6 |
-           RSS_HASHTYPE_RSS_UDP_IPV6_EX)) != 0);
+       /* A2 selects UDP hashing per-protocol in REDIR2; A1 uses the filter. */
+       if (!IS_CHIP_FEATURE(hw, ATLANTIC2))
+               aq_hw_udp_rss_enable(hw, (aq_rss_hashconfig() &
+                   (RSS_HASHTYPE_RSS_UDP_IPV4 | RSS_HASHTYPE_RSS_UDP_IPV6 |
+                   RSS_HASHTYPE_RSS_UDP_IPV6_EX)) != 0);
        aq_hw_set_link_speed(hw, hw->link_rate);
 
        /* iflib does not replay filter state after init; aq_hw_init() clears 
it. */
@@ -875,9 +877,12 @@ aq_if_multi_set(if_ctx_t ctx)
                if_foreach_llmaddr(ifp, &aq_mc_filter_apply, softc);
        }
 
-       aq_hw_set_promisc(hw, !!(if_getflags(ifp) & IFF_PROMISC),
+       if (aq_hw_set_promisc(hw, !!(if_getflags(ifp) & IFF_PROMISC),
            aq_is_vlan_promisc_required(softc),
-           !!(if_getflags(ifp) & IFF_ALLMULTI) || 
aq_is_mc_promisc_required(softc));
+           !!(if_getflags(ifp) & IFF_ALLMULTI) ||
+           aq_is_mc_promisc_required(softc)) != 0)
+               device_printf(softc->dev,
+                   "multicast filter update failed\n");
        AQ_DBG_EXIT(0);
 }
 
@@ -943,17 +948,18 @@ static int
 aq_if_promisc_set(if_ctx_t ctx, int flags)
 {
        struct aq_dev *softc;
+       int err;
 
        AQ_DBG_ENTER();
 
        softc = iflib_get_softc(ctx);
 
-       aq_hw_set_promisc(&softc->hw, !!(flags & IFF_PROMISC),
+       err = aq_hw_set_promisc(&softc->hw, !!(flags & IFF_PROMISC),
            aq_is_vlan_promisc_required(softc),
            !!(flags & IFF_ALLMULTI) || aq_is_mc_promisc_required(softc));
 
-       AQ_DBG_EXIT(0);
-       return (0);
+       AQ_DBG_EXIT(err);
+       return (err);
 }
 
 static void
@@ -1127,9 +1133,10 @@ aq_update_vlan_filters(struct aq_dev *softc)
        }
 
        hw_atl_b0_hw_vlan_set(hw, aq_vlans);
-       hw_atl_b0_hw_vlan_promisc_set(hw,
+       if (hw_atl_b0_hw_vlan_promisc_set(hw,
            aq_is_vlan_promisc_required(softc) ||
-           (if_getflags(iflib_get_ifp(softc->ctx)) & IFF_PROMISC) != 0);
+           (if_getflags(iflib_get_ifp(softc->ctx)) & IFF_PROMISC) != 0) != 0)
+               device_printf(softc->dev, "VLAN filter update failed\n");
 }
 
 /* VLAN support */

Reply via email to