net/sfc/base: import 5xxx/6xxx family support
authorAndrew Rybchenko <arybchenko@solarflare.com>
Tue, 29 Nov 2016 16:18:41 +0000 (16:18 +0000)
committerFerruh Yigit <ferruh.yigit@intel.com>
Tue, 17 Jan 2017 18:39:25 +0000 (19:39 +0100)
EFSYS_OPT_SIENA should be enabled to use it.

From Solarflare Communications Inc.

Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
18 files changed:
drivers/net/sfc/base/efx_check.h
drivers/net/sfc/base/efx_ev.c
drivers/net/sfc/base/efx_filter.c
drivers/net/sfc/base/efx_impl.h
drivers/net/sfc/base/efx_intr.c
drivers/net/sfc/base/efx_mac.c
drivers/net/sfc/base/efx_mcdi.c
drivers/net/sfc/base/efx_nic.c
drivers/net/sfc/base/efx_phy.c
drivers/net/sfc/base/efx_rx.c
drivers/net/sfc/base/efx_tx.c
drivers/net/sfc/base/siena_flash.h [new file with mode: 0644]
drivers/net/sfc/base/siena_impl.h [new file with mode: 0644]
drivers/net/sfc/base/siena_mac.c [new file with mode: 0644]
drivers/net/sfc/base/siena_mcdi.c [new file with mode: 0644]
drivers/net/sfc/base/siena_nic.c [new file with mode: 0644]
drivers/net/sfc/base/siena_phy.c [new file with mode: 0644]
drivers/net/sfc/base/siena_sram.c [new file with mode: 0644]

index 470f73c..190ac46 100644 (file)
 
 #if EFSYS_OPT_CHECK_REG
 /* Verify chip implements accessed registers */
+# if !(EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD)
 #  error "CHECK_REG requires SIENA or HUNTINGTON or MEDFORD"
+# endif
 #endif /* EFSYS_OPT_CHECK_REG */
 
 #if EFSYS_OPT_DECODE_INTR_FATAL
 /* Decode fatal errors */
+# if !EFSYS_OPT_SIENA
 #  error "INTR_FATAL requires SIENA"
+# endif
 #endif /* EFSYS_OPT_DECODE_INTR_FATAL */
 
 #ifdef EFSYS_OPT_FALCON_NIC_CFG_OVERRIDE
@@ -61,7 +65,9 @@
 
 #if EFSYS_OPT_FILTER
 /* Support hardware packet filters */
+# if !(EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD)
 #  error "FILTER requires SIENA or HUNTINGTON or MEDFORD"
+# endif
 #endif /* EFSYS_OPT_FILTER */
 
 #ifdef EFSYS_OPT_MAC_FALCON_GMAC
 
 #if EFSYS_OPT_MCDI
 /* Support management controller messages */
+# if !(EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD)
 #  error "MCDI requires SIENA or HUNTINGTON or MEDFORD"
+# endif
 #endif /* EFSYS_OPT_MCDI */
 
+#if (EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD)
+# if !EFSYS_OPT_MCDI
+#  error "SIENA or HUNTINGTON or MEDFORD requires MCDI"
+# endif
+#endif
+
 #if EFSYS_OPT_MCDI_LOGGING
 /* Support MCDI logging */
 # if !EFSYS_OPT_MCDI
index 942dac6..59f4d02 100644 (file)
 
 
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_ev_init(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_ev_fini(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_ev_qcreate(
+       __in            efx_nic_t *enp,
+       __in            unsigned int index,
+       __in            efsys_mem_t *esmp,
+       __in            size_t n,
+       __in            uint32_t id,
+       __in            uint32_t us,
+       __in            uint32_t flags,
+       __in            efx_evq_t *eep);
+
+static                 void
+siena_ev_qdestroy(
+       __in            efx_evq_t *eep);
+
+static __checkReturn   efx_rc_t
+siena_ev_qprime(
+       __in            efx_evq_t *eep,
+       __in            unsigned int count);
+
+static                 void
+siena_ev_qpost(
+       __in    efx_evq_t *eep,
+       __in    uint16_t data);
+
+static __checkReturn   efx_rc_t
+siena_ev_qmoderate(
+       __in            efx_evq_t *eep,
+       __in            unsigned int us);
+
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_SIENA
+static const efx_ev_ops_t      __efx_ev_siena_ops = {
+       siena_ev_init,                          /* eevo_init */
+       siena_ev_fini,                          /* eevo_fini */
+       siena_ev_qcreate,                       /* eevo_qcreate */
+       siena_ev_qdestroy,                      /* eevo_qdestroy */
+       siena_ev_qprime,                        /* eevo_qprime */
+       siena_ev_qpost,                         /* eevo_qpost */
+       siena_ev_qmoderate,                     /* eevo_qmoderate */
+};
+#endif /* EFSYS_OPT_SIENA */
+
+
        __checkReturn   efx_rc_t
 efx_ev_init(
        __in            efx_nic_t *enp)
@@ -55,6 +110,11 @@ efx_ev_init(
        }
 
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               eevop = &__efx_ev_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                EFSYS_ASSERT(0);
@@ -440,3 +500,726 @@ fail1:
        return (rc);
 }
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_ev_init(
+       __in            efx_nic_t *enp)
+{
+       efx_oword_t oword;
+
+       /*
+        * Program the event queue for receive and transmit queue
+        * flush events.
+        */
+       EFX_BAR_READO(enp, FR_AZ_DP_CTRL_REG, &oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_FLS_EVQ_ID, 0);
+       EFX_BAR_WRITEO(enp, FR_AZ_DP_CTRL_REG, &oword);
+
+       return (0);
+
+}
+
+static  __checkReturn   boolean_t
+siena_ev_rx_not_ok(
+       __in            efx_evq_t *eep,
+       __in            efx_qword_t *eqp,
+       __in            uint32_t label,
+       __in            uint32_t id,
+       __inout         uint16_t *flagsp)
+{
+       boolean_t ignore = B_FALSE;
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_TOBE_DISC) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_TOBE_DISC);
+               EFSYS_PROBE(tobe_disc);
+               /*
+                * Assume this is a unicast address mismatch, unless below
+                * we find either FSF_AZ_RX_EV_ETH_CRC_ERR or
+                * EV_RX_PAUSE_FRM_ERR is set.
+                */
+               (*flagsp) |= EFX_ADDR_MISMATCH;
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_FRM_TRUNC) != 0) {
+               EFSYS_PROBE2(frm_trunc, uint32_t, label, uint32_t, id);
+               EFX_EV_QSTAT_INCR(eep, EV_RX_FRM_TRUNC);
+               (*flagsp) |= EFX_DISCARD;
+
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_ETH_CRC_ERR) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_ETH_CRC_ERR);
+               EFSYS_PROBE(crc_err);
+               (*flagsp) &= ~EFX_ADDR_MISMATCH;
+               (*flagsp) |= EFX_DISCARD;
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_PAUSE_FRM_ERR) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_PAUSE_FRM_ERR);
+               EFSYS_PROBE(pause_frm_err);
+               (*flagsp) &= ~EFX_ADDR_MISMATCH;
+               (*flagsp) |= EFX_DISCARD;
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_BUF_OWNER_ID_ERR) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_BUF_OWNER_ID_ERR);
+               EFSYS_PROBE(owner_id_err);
+               (*flagsp) |= EFX_DISCARD;
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_IPV4_HDR_CHKSUM_ERR);
+               EFSYS_PROBE(ipv4_err);
+               (*flagsp) &= ~EFX_CKSUM_IPV4;
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_TCP_UDP_CHKSUM_ERR);
+               EFSYS_PROBE(udp_chk_err);
+               (*flagsp) &= ~EFX_CKSUM_TCPUDP;
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_IP_FRAG_ERR) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_IP_FRAG_ERR);
+
+               /*
+                * If IP is fragmented FSF_AZ_RX_EV_IP_FRAG_ERR is set. This
+                * causes FSF_AZ_RX_EV_PKT_OK to be clear. This is not an error
+                * condition.
+                */
+               (*flagsp) &= ~(EFX_PKT_TCP | EFX_PKT_UDP | EFX_CKSUM_TCPUDP);
+       }
+
+       return (ignore);
+}
+
+static __checkReturn   boolean_t
+siena_ev_rx(
+       __in            efx_evq_t *eep,
+       __in            efx_qword_t *eqp,
+       __in            const efx_ev_callbacks_t *eecp,
+       __in_opt        void *arg)
+{
+       uint32_t id;
+       uint32_t size;
+       uint32_t label;
+       boolean_t ok;
+       uint32_t hdr_type;
+       boolean_t is_v6;
+       uint16_t flags;
+       boolean_t ignore;
+       boolean_t should_abort;
+
+       EFX_EV_QSTAT_INCR(eep, EV_RX);
+
+       /* Basic packet information */
+       id = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_DESC_PTR);
+       size = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_BYTE_CNT);
+       label = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_Q_LABEL);
+       ok = (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_PKT_OK) != 0);
+
+       hdr_type = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_HDR_TYPE);
+
+       is_v6 = (EFX_QWORD_FIELD(*eqp, FSF_CZ_RX_EV_IPV6_PKT) != 0);
+
+       /*
+        * If packet is marked as OK and packet type is TCP/IP or
+        * UDP/IP or other IP, then we can rely on the hardware checksums.
+        */
+       switch (hdr_type) {
+       case FSE_AZ_RX_EV_HDR_TYPE_IPV4V6_TCP:
+               flags = EFX_PKT_TCP | EFX_CKSUM_TCPUDP;
+               if (is_v6) {
+                       EFX_EV_QSTAT_INCR(eep, EV_RX_TCP_IPV6);
+                       flags |= EFX_PKT_IPV6;
+               } else {
+                       EFX_EV_QSTAT_INCR(eep, EV_RX_TCP_IPV4);
+                       flags |= EFX_PKT_IPV4 | EFX_CKSUM_IPV4;
+               }
+               break;
+
+       case FSE_AZ_RX_EV_HDR_TYPE_IPV4V6_UDP:
+               flags = EFX_PKT_UDP | EFX_CKSUM_TCPUDP;
+               if (is_v6) {
+                       EFX_EV_QSTAT_INCR(eep, EV_RX_UDP_IPV6);
+                       flags |= EFX_PKT_IPV6;
+               } else {
+                       EFX_EV_QSTAT_INCR(eep, EV_RX_UDP_IPV4);
+                       flags |= EFX_PKT_IPV4 | EFX_CKSUM_IPV4;
+               }
+               break;
+
+       case FSE_AZ_RX_EV_HDR_TYPE_IPV4V6_OTHER:
+               if (is_v6) {
+                       EFX_EV_QSTAT_INCR(eep, EV_RX_OTHER_IPV6);
+                       flags = EFX_PKT_IPV6;
+               } else {
+                       EFX_EV_QSTAT_INCR(eep, EV_RX_OTHER_IPV4);
+                       flags = EFX_PKT_IPV4 | EFX_CKSUM_IPV4;
+               }
+               break;
+
+       case FSE_AZ_RX_EV_HDR_TYPE_OTHER:
+               EFX_EV_QSTAT_INCR(eep, EV_RX_NON_IP);
+               flags = 0;
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               flags = 0;
+               break;
+       }
+
+       /* Detect errors included in the FSF_AZ_RX_EV_PKT_OK indication */
+       if (!ok) {
+               ignore = siena_ev_rx_not_ok(eep, eqp, label, id, &flags);
+               if (ignore) {
+                       EFSYS_PROBE4(rx_complete, uint32_t, label, uint32_t, id,
+                           uint32_t, size, uint16_t, flags);
+
+                       return (B_FALSE);
+               }
+       }
+
+       /* If we're not discarding the packet then it is ok */
+       if (~flags & EFX_DISCARD)
+               EFX_EV_QSTAT_INCR(eep, EV_RX_OK);
+
+       /* Detect multicast packets that didn't match the filter */
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_MCAST_PKT) != 0) {
+               EFX_EV_QSTAT_INCR(eep, EV_RX_MCAST_PKT);
+
+               if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_MCAST_HASH_MATCH) != 0) {
+                       EFX_EV_QSTAT_INCR(eep, EV_RX_MCAST_HASH_MATCH);
+               } else {
+                       EFSYS_PROBE(mcast_mismatch);
+                       flags |= EFX_ADDR_MISMATCH;
+               }
+       } else {
+               flags |= EFX_PKT_UNICAST;
+       }
+
+       /*
+        * The packet parser in Siena can abort parsing packets under
+        * certain error conditions, setting the PKT_NOT_PARSED bit
+        * (which clears PKT_OK). If this is set, then don't trust
+        * the PKT_TYPE field.
+        */
+       if (!ok) {
+               uint32_t parse_err;
+
+               parse_err = EFX_QWORD_FIELD(*eqp, FSF_CZ_RX_EV_PKT_NOT_PARSED);
+               if (parse_err != 0)
+                       flags |= EFX_CHECK_VLAN;
+       }
+
+       if (~flags & EFX_CHECK_VLAN) {
+               uint32_t pkt_type;
+
+               pkt_type = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_PKT_TYPE);
+               if (pkt_type >= FSE_AZ_RX_EV_PKT_TYPE_VLAN)
+                       flags |= EFX_PKT_VLAN_TAGGED;
+       }
+
+       EFSYS_PROBE4(rx_complete, uint32_t, label, uint32_t, id,
+           uint32_t, size, uint16_t, flags);
+
+       EFSYS_ASSERT(eecp->eec_rx != NULL);
+       should_abort = eecp->eec_rx(arg, label, id, size, flags);
+
+       return (should_abort);
+}
+
+static __checkReturn   boolean_t
+siena_ev_tx(
+       __in            efx_evq_t *eep,
+       __in            efx_qword_t *eqp,
+       __in            const efx_ev_callbacks_t *eecp,
+       __in_opt        void *arg)
+{
+       uint32_t id;
+       uint32_t label;
+       boolean_t should_abort;
+
+       EFX_EV_QSTAT_INCR(eep, EV_TX);
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_COMP) != 0 &&
+           EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_ERR) == 0 &&
+           EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_TOO_BIG) == 0 &&
+           EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_WQ_FF_FULL) == 0) {
+
+               id = EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_DESC_PTR);
+               label = EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_Q_LABEL);
+
+               EFSYS_PROBE2(tx_complete, uint32_t, label, uint32_t, id);
+
+               EFSYS_ASSERT(eecp->eec_tx != NULL);
+               should_abort = eecp->eec_tx(arg, label, id);
+
+               return (should_abort);
+       }
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_COMP) != 0)
+               EFSYS_PROBE3(bad_event, unsigned int, eep->ee_index,
+                           uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_1),
+                           uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_0));
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_ERR) != 0)
+               EFX_EV_QSTAT_INCR(eep, EV_TX_PKT_ERR);
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_TOO_BIG) != 0)
+               EFX_EV_QSTAT_INCR(eep, EV_TX_PKT_TOO_BIG);
+
+       if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_WQ_FF_FULL) != 0)
+               EFX_EV_QSTAT_INCR(eep, EV_TX_WQ_FF_FULL);
+
+       EFX_EV_QSTAT_INCR(eep, EV_TX_UNEXPECTED);
+       return (B_FALSE);
+}
+
+static __checkReturn   boolean_t
+siena_ev_global(
+       __in            efx_evq_t *eep,
+       __in            efx_qword_t *eqp,
+       __in            const efx_ev_callbacks_t *eecp,
+       __in_opt        void *arg)
+{
+       _NOTE(ARGUNUSED(eqp, eecp, arg))
+
+       EFX_EV_QSTAT_INCR(eep, EV_GLOBAL);
+
+       return (B_FALSE);
+}
+
+static __checkReturn   boolean_t
+siena_ev_driver(
+       __in            efx_evq_t *eep,
+       __in            efx_qword_t *eqp,
+       __in            const efx_ev_callbacks_t *eecp,
+       __in_opt        void *arg)
+{
+       boolean_t should_abort;
+
+       EFX_EV_QSTAT_INCR(eep, EV_DRIVER);
+       should_abort = B_FALSE;
+
+       switch (EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBCODE)) {
+       case FSE_AZ_TX_DESCQ_FLS_DONE_EV: {
+               uint32_t txq_index;
+
+               EFX_EV_QSTAT_INCR(eep, EV_DRIVER_TX_DESCQ_FLS_DONE);
+
+               txq_index = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
+
+               EFSYS_PROBE1(tx_descq_fls_done, uint32_t, txq_index);
+
+               EFSYS_ASSERT(eecp->eec_txq_flush_done != NULL);
+               should_abort = eecp->eec_txq_flush_done(arg, txq_index);
+
+               break;
+       }
+       case FSE_AZ_RX_DESCQ_FLS_DONE_EV: {
+               uint32_t rxq_index;
+               uint32_t failed;
+
+               rxq_index = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_RX_DESCQ_ID);
+               failed = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL);
+
+               EFSYS_ASSERT(eecp->eec_rxq_flush_done != NULL);
+               EFSYS_ASSERT(eecp->eec_rxq_flush_failed != NULL);
+
+               if (failed) {
+                       EFX_EV_QSTAT_INCR(eep, EV_DRIVER_RX_DESCQ_FLS_FAILED);
+
+                       EFSYS_PROBE1(rx_descq_fls_failed, uint32_t, rxq_index);
+
+                       should_abort = eecp->eec_rxq_flush_failed(arg,
+                                                                   rxq_index);
+               } else {
+                       EFX_EV_QSTAT_INCR(eep, EV_DRIVER_RX_DESCQ_FLS_DONE);
+
+                       EFSYS_PROBE1(rx_descq_fls_done, uint32_t, rxq_index);
+
+                       should_abort = eecp->eec_rxq_flush_done(arg, rxq_index);
+               }
+
+               break;
+       }
+       case FSE_AZ_EVQ_INIT_DONE_EV:
+               EFSYS_ASSERT(eecp->eec_initialized != NULL);
+               should_abort = eecp->eec_initialized(arg);
+
+               break;
+
+       case FSE_AZ_EVQ_NOT_EN_EV:
+               EFSYS_PROBE(evq_not_en);
+               break;
+
+       case FSE_AZ_SRM_UPD_DONE_EV: {
+               uint32_t code;
+
+               EFX_EV_QSTAT_INCR(eep, EV_DRIVER_SRM_UPD_DONE);
+
+               code = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
+
+               EFSYS_ASSERT(eecp->eec_sram != NULL);
+               should_abort = eecp->eec_sram(arg, code);
+
+               break;
+       }
+       case FSE_AZ_WAKE_UP_EV: {
+               uint32_t id;
+
+               id = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
+
+               EFSYS_ASSERT(eecp->eec_wake_up != NULL);
+               should_abort = eecp->eec_wake_up(arg, id);
+
+               break;
+       }
+       case FSE_AZ_TX_PKT_NON_TCP_UDP:
+               EFSYS_PROBE(tx_pkt_non_tcp_udp);
+               break;
+
+       case FSE_AZ_TIMER_EV: {
+               uint32_t id;
+
+               id = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
+
+               EFSYS_ASSERT(eecp->eec_timer != NULL);
+               should_abort = eecp->eec_timer(arg, id);
+
+               break;
+       }
+       case FSE_AZ_RX_DSC_ERROR_EV:
+               EFX_EV_QSTAT_INCR(eep, EV_DRIVER_RX_DSC_ERROR);
+
+               EFSYS_PROBE(rx_dsc_error);
+
+               EFSYS_ASSERT(eecp->eec_exception != NULL);
+               should_abort = eecp->eec_exception(arg,
+                       EFX_EXCEPTION_RX_DSC_ERROR, 0);
+
+               break;
+
+       case FSE_AZ_TX_DSC_ERROR_EV:
+               EFX_EV_QSTAT_INCR(eep, EV_DRIVER_TX_DSC_ERROR);
+
+               EFSYS_PROBE(tx_dsc_error);
+
+               EFSYS_ASSERT(eecp->eec_exception != NULL);
+               should_abort = eecp->eec_exception(arg,
+                       EFX_EXCEPTION_TX_DSC_ERROR, 0);
+
+               break;
+
+       default:
+               break;
+       }
+
+       return (should_abort);
+}
+
+static __checkReturn   boolean_t
+siena_ev_drv_gen(
+       __in            efx_evq_t *eep,
+       __in            efx_qword_t *eqp,
+       __in            const efx_ev_callbacks_t *eecp,
+       __in_opt        void *arg)
+{
+       uint32_t data;
+       boolean_t should_abort;
+
+       EFX_EV_QSTAT_INCR(eep, EV_DRV_GEN);
+
+       data = EFX_QWORD_FIELD(*eqp, FSF_AZ_EV_DATA_DW0);
+       if (data >= ((uint32_t)1 << 16)) {
+               EFSYS_PROBE3(bad_event, unsigned int, eep->ee_index,
+                           uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_1),
+                           uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_0));
+               return (B_TRUE);
+       }
+
+       EFSYS_ASSERT(eecp->eec_software != NULL);
+       should_abort = eecp->eec_software(arg, (uint16_t)data);
+
+       return (should_abort);
+}
+
+#if EFSYS_OPT_MCDI
+
+static __checkReturn   boolean_t
+siena_ev_mcdi(
+       __in            efx_evq_t *eep,
+       __in            efx_qword_t *eqp,
+       __in            const efx_ev_callbacks_t *eecp,
+       __in_opt        void *arg)
+{
+       efx_nic_t *enp = eep->ee_enp;
+       unsigned int code;
+       boolean_t should_abort = B_FALSE;
+
+       EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+
+       if (enp->en_family != EFX_FAMILY_SIENA)
+               goto out;
+
+       EFSYS_ASSERT(eecp->eec_link_change != NULL);
+       EFSYS_ASSERT(eecp->eec_exception != NULL);
+
+       EFX_EV_QSTAT_INCR(eep, EV_MCDI_RESPONSE);
+
+       code = EFX_QWORD_FIELD(*eqp, MCDI_EVENT_CODE);
+       switch (code) {
+       case MCDI_EVENT_CODE_BADSSERT:
+               efx_mcdi_ev_death(enp, EINTR);
+               break;
+
+       case MCDI_EVENT_CODE_CMDDONE:
+               efx_mcdi_ev_cpl(enp,
+                   MCDI_EV_FIELD(eqp, CMDDONE_SEQ),
+                   MCDI_EV_FIELD(eqp, CMDDONE_DATALEN),
+                   MCDI_EV_FIELD(eqp, CMDDONE_ERRNO));
+               break;
+
+       case MCDI_EVENT_CODE_LINKCHANGE: {
+               efx_link_mode_t link_mode;
+
+               siena_phy_link_ev(enp, eqp, &link_mode);
+               should_abort = eecp->eec_link_change(arg, link_mode);
+               break;
+       }
+       case MCDI_EVENT_CODE_SENSOREVT: {
+               should_abort = B_FALSE;
+               break;
+       }
+       case MCDI_EVENT_CODE_SCHEDERR:
+               /* Informational only */
+               break;
+
+       case MCDI_EVENT_CODE_REBOOT:
+               efx_mcdi_ev_death(enp, EIO);
+               break;
+
+       case MCDI_EVENT_CODE_MAC_STATS_DMA:
+               break;
+
+       case MCDI_EVENT_CODE_FWALERT: {
+               uint32_t reason = MCDI_EV_FIELD(eqp, FWALERT_REASON);
+
+               if (reason == MCDI_EVENT_FWALERT_REASON_SRAM_ACCESS)
+                       should_abort = eecp->eec_exception(arg,
+                               EFX_EXCEPTION_FWALERT_SRAM,
+                               MCDI_EV_FIELD(eqp, FWALERT_DATA));
+               else
+                       should_abort = eecp->eec_exception(arg,
+                               EFX_EXCEPTION_UNKNOWN_FWALERT,
+                               MCDI_EV_FIELD(eqp, DATA));
+               break;
+       }
+
+       default:
+               EFSYS_PROBE1(mc_pcol_error, int, code);
+               break;
+       }
+
+out:
+       return (should_abort);
+}
+
+#endif /* EFSYS_OPT_MCDI */
+
+static __checkReturn   efx_rc_t
+siena_ev_qprime(
+       __in            efx_evq_t *eep,
+       __in            unsigned int count)
+{
+       efx_nic_t *enp = eep->ee_enp;
+       uint32_t rptr;
+       efx_dword_t dword;
+
+       rptr = count & eep->ee_mask;
+
+       EFX_POPULATE_DWORD_1(dword, FRF_AZ_EVQ_RPTR, rptr);
+
+       EFX_BAR_TBL_WRITED(enp, FR_AZ_EVQ_RPTR_REG, eep->ee_index,
+                           &dword, B_FALSE);
+
+       return (0);
+}
+
+static         void
+siena_ev_qpost(
+       __in    efx_evq_t *eep,
+       __in    uint16_t data)
+{
+       efx_nic_t *enp = eep->ee_enp;
+       efx_qword_t ev;
+       efx_oword_t oword;
+
+       EFX_POPULATE_QWORD_2(ev, FSF_AZ_EV_CODE, FSE_AZ_EV_CODE_DRV_GEN_EV,
+           FSF_AZ_EV_DATA_DW0, (uint32_t)data);
+
+       EFX_POPULATE_OWORD_3(oword, FRF_AZ_DRV_EV_QID, eep->ee_index,
+           EFX_DWORD_0, EFX_QWORD_FIELD(ev, EFX_DWORD_0),
+           EFX_DWORD_1, EFX_QWORD_FIELD(ev, EFX_DWORD_1));
+
+       EFX_BAR_WRITEO(enp, FR_AZ_DRV_EV_REG, &oword);
+}
+
+static __checkReturn   efx_rc_t
+siena_ev_qmoderate(
+       __in            efx_evq_t *eep,
+       __in            unsigned int us)
+{
+       efx_nic_t *enp = eep->ee_enp;
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       unsigned int locked;
+       efx_dword_t dword;
+       efx_rc_t rc;
+
+       if (us > encp->enc_evq_timer_max_us) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       /* If the value is zero then disable the timer */
+       if (us == 0) {
+               EFX_POPULATE_DWORD_2(dword,
+                   FRF_CZ_TC_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS,
+                   FRF_CZ_TC_TIMER_VAL, 0);
+       } else {
+               unsigned int ticks;
+
+               if ((rc = efx_ev_usecs_to_ticks(enp, us, &ticks)) != 0)
+                       goto fail2;
+
+               EFSYS_ASSERT(ticks > 0);
+               EFX_POPULATE_DWORD_2(dword,
+                   FRF_CZ_TC_TIMER_MODE, FFE_CZ_TIMER_MODE_INT_HLDOFF,
+                   FRF_CZ_TC_TIMER_VAL, ticks - 1);
+       }
+
+       locked = (eep->ee_index == 0) ? 1 : 0;
+
+       EFX_BAR_TBL_WRITED(enp, FR_BZ_TIMER_COMMAND_REGP0,
+           eep->ee_index, &dword, locked);
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static __checkReturn   efx_rc_t
+siena_ev_qcreate(
+       __in            efx_nic_t *enp,
+       __in            unsigned int index,
+       __in            efsys_mem_t *esmp,
+       __in            size_t n,
+       __in            uint32_t id,
+       __in            uint32_t us,
+       __in            uint32_t flags,
+       __in            efx_evq_t *eep)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       uint32_t size;
+       efx_oword_t oword;
+       efx_rc_t rc;
+       boolean_t notify_mode;
+
+       _NOTE(ARGUNUSED(esmp))
+
+       EFX_STATIC_ASSERT(ISP2(EFX_EVQ_MAXNEVS));
+       EFX_STATIC_ASSERT(ISP2(EFX_EVQ_MINNEVS));
+
+       if (!ISP2(n) || (n < EFX_EVQ_MINNEVS) || (n > EFX_EVQ_MAXNEVS)) {
+               rc = EINVAL;
+               goto fail1;
+       }
+       if (index >= encp->enc_evq_limit) {
+               rc = EINVAL;
+               goto fail2;
+       }
+       for (size = 0; (1 << size) <= (EFX_EVQ_MAXNEVS / EFX_EVQ_MINNEVS);
+           size++)
+               if ((1 << size) == (int)(n / EFX_EVQ_MINNEVS))
+                       break;
+       if (id + (1 << size) >= encp->enc_buftbl_limit) {
+               rc = EINVAL;
+               goto fail4;
+       }
+
+       /* Set up the handler table */
+       eep->ee_rx      = siena_ev_rx;
+       eep->ee_tx      = siena_ev_tx;
+       eep->ee_driver  = siena_ev_driver;
+       eep->ee_global  = siena_ev_global;
+       eep->ee_drv_gen = siena_ev_drv_gen;
+#if EFSYS_OPT_MCDI
+       eep->ee_mcdi    = siena_ev_mcdi;
+#endif /* EFSYS_OPT_MCDI */
+
+       notify_mode = ((flags & EFX_EVQ_FLAGS_NOTIFY_MASK) !=
+           EFX_EVQ_FLAGS_NOTIFY_INTERRUPT);
+
+       /* Set up the new event queue */
+       EFX_POPULATE_OWORD_3(oword, FRF_CZ_TIMER_Q_EN, 1,
+           FRF_CZ_HOST_NOTIFY_MODE, notify_mode,
+           FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS);
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_TIMER_TBL, index, &oword, B_TRUE);
+
+       EFX_POPULATE_OWORD_3(oword, FRF_AZ_EVQ_EN, 1, FRF_AZ_EVQ_SIZE, size,
+           FRF_AZ_EVQ_BUF_BASE_ID, id);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_EVQ_PTR_TBL, index, &oword, B_TRUE);
+
+       /* Set initial interrupt moderation */
+       siena_ev_qmoderate(eep, us);
+
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_SIENA
+
+static         void
+siena_ev_qdestroy(
+       __in    efx_evq_t *eep)
+{
+       efx_nic_t *enp = eep->ee_enp;
+       efx_oword_t oword;
+
+       /* Purge event queue */
+       EFX_ZERO_OWORD(oword);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_EVQ_PTR_TBL,
+           eep->ee_index, &oword, B_TRUE);
+
+       EFX_ZERO_OWORD(oword);
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_TIMER_TBL, eep->ee_index, &oword, B_TRUE);
+}
+
+static         void
+siena_ev_fini(
+       __in    efx_nic_t *enp)
+{
+       _NOTE(ARGUNUSED(enp))
+}
+
+#endif /* EFSYS_OPT_SIENA */
index 560562d..429f2b3 100644 (file)
 
 #if EFSYS_OPT_FILTER
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_filter_init(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_filter_fini(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_filter_restore(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_filter_add(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec,
+       __in            boolean_t may_replace);
+
+static __checkReturn   efx_rc_t
+siena_filter_delete(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec);
+
+static __checkReturn   efx_rc_t
+siena_filter_supported_filters(
+       __in            efx_nic_t *enp,
+       __out           uint32_t *list,
+       __out           size_t *length);
+
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_SIENA
+static const efx_filter_ops_t  __efx_filter_siena_ops = {
+       siena_filter_init,              /* efo_init */
+       siena_filter_fini,              /* efo_fini */
+       siena_filter_restore,           /* efo_restore */
+       siena_filter_add,               /* efo_add */
+       siena_filter_delete,            /* efo_delete */
+       siena_filter_supported_filters, /* efo_supported_filters */
+       NULL,                           /* efo_reconfigure */
+};
+#endif /* EFSYS_OPT_SIENA */
+
        __checkReturn   efx_rc_t
 efx_filter_insert(
        __in            efx_nic_t *enp,
@@ -96,6 +141,11 @@ efx_filter_init(
        EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_FILTER));
 
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               efop = &__efx_filter_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                EFSYS_ASSERT(0);
@@ -329,4 +379,996 @@ efx_filter_spec_set_mc_def(
 
 
 
+#if EFSYS_OPT_SIENA
+
+/*
+ * "Fudge factors" - difference between programmed value and actual depth.
+ * Due to pipelined implementation we need to program H/W with a value that
+ * is larger than the hop limit we want.
+ */
+#define        FILTER_CTL_SRCH_FUDGE_WILD 3
+#define        FILTER_CTL_SRCH_FUDGE_FULL 1
+
+/*
+ * Hard maximum hop limit.  Hardware will time-out beyond 200-something.
+ * We also need to avoid infinite loops in efx_filter_search() when the
+ * table is full.
+ */
+#define        FILTER_CTL_SRCH_MAX 200
+
+static __checkReturn   efx_rc_t
+siena_filter_spec_from_gen_spec(
+       __out           siena_filter_spec_t *sf_spec,
+       __in            efx_filter_spec_t *gen_spec)
+{
+       efx_rc_t rc;
+       boolean_t is_full = B_FALSE;
+
+       if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX)
+               EFSYS_ASSERT3U(gen_spec->efs_flags, ==, EFX_FILTER_FLAG_TX);
+       else
+               EFSYS_ASSERT3U(gen_spec->efs_flags, &, EFX_FILTER_FLAG_RX);
+
+       /* Falconsiena only has one RSS context */
+       if ((gen_spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) &&
+           gen_spec->efs_rss_context != 0) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       sf_spec->sfs_flags = gen_spec->efs_flags;
+       sf_spec->sfs_dmaq_id = gen_spec->efs_dmaq_id;
+
+       switch (gen_spec->efs_match_flags) {
+       case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+           EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT:
+               is_full = B_TRUE;
+               /* Fall through */
+       case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT: {
+               uint32_t rhost, host1, host2;
+               uint16_t rport, port1, port2;
+
+               if (gen_spec->efs_ether_type != EFX_ETHER_TYPE_IPV4) {
+                       rc = ENOTSUP;
+                       goto fail2;
+               }
+               if (gen_spec->efs_loc_port == 0 ||
+                   (is_full && gen_spec->efs_rem_port == 0)) {
+                       rc = EINVAL;
+                       goto fail3;
+               }
+               switch (gen_spec->efs_ip_proto) {
+               case EFX_IPPROTO_TCP:
+                       if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_TX_TCP_FULL :
+                                   EFX_SIENA_FILTER_TX_TCP_WILD);
+                       } else {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_RX_TCP_FULL :
+                                   EFX_SIENA_FILTER_RX_TCP_WILD);
+                       }
+                       break;
+               case EFX_IPPROTO_UDP:
+                       if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_TX_UDP_FULL :
+                                   EFX_SIENA_FILTER_TX_UDP_WILD);
+                       } else {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_RX_UDP_FULL :
+                                   EFX_SIENA_FILTER_RX_UDP_WILD);
+                       }
+                       break;
+               default:
+                       rc = ENOTSUP;
+                       goto fail4;
+               }
+               /*
+                * The filter is constructed in terms of source and destination,
+                * with the odd wrinkle that the ports are swapped in a UDP
+                * wildcard filter. We need to convert from local and remote
+                * addresses (zero for a wildcard).
+                */
+               rhost = is_full ? gen_spec->efs_rem_host.eo_u32[0] : 0;
+               rport = is_full ? gen_spec->efs_rem_port : 0;
+               if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                       host1 = gen_spec->efs_loc_host.eo_u32[0];
+                       host2 = rhost;
+               } else {
+                       host1 = rhost;
+                       host2 = gen_spec->efs_loc_host.eo_u32[0];
+               }
+               if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                       if (sf_spec->sfs_type ==
+                           EFX_SIENA_FILTER_TX_UDP_WILD) {
+                               port1 = rport;
+                               port2 = gen_spec->efs_loc_port;
+                       } else {
+                               port1 = gen_spec->efs_loc_port;
+                               port2 = rport;
+                       }
+               } else {
+                       if (sf_spec->sfs_type ==
+                           EFX_SIENA_FILTER_RX_UDP_WILD) {
+                               port1 = gen_spec->efs_loc_port;
+                               port2 = rport;
+                       } else {
+                               port1 = rport;
+                               port2 = gen_spec->efs_loc_port;
+                       }
+               }
+               sf_spec->sfs_dword[0] = (host1 << 16) | port1;
+               sf_spec->sfs_dword[1] = (port2 << 16) | (host1 >> 16);
+               sf_spec->sfs_dword[2] = host2;
+               break;
+       }
+
+       case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
+               is_full = B_TRUE;
+               /* Fall through */
+       case EFX_FILTER_MATCH_LOC_MAC:
+               if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                       sf_spec->sfs_type = (is_full ?
+                           EFX_SIENA_FILTER_TX_MAC_FULL :
+                           EFX_SIENA_FILTER_TX_MAC_WILD);
+               } else {
+                       sf_spec->sfs_type = (is_full ?
+                           EFX_SIENA_FILTER_RX_MAC_FULL :
+                           EFX_SIENA_FILTER_RX_MAC_WILD);
+               }
+               sf_spec->sfs_dword[0] = is_full ? gen_spec->efs_outer_vid : 0;
+               sf_spec->sfs_dword[1] =
+                   gen_spec->efs_loc_mac[2] << 24 |
+                   gen_spec->efs_loc_mac[3] << 16 |
+                   gen_spec->efs_loc_mac[4] <<  8 |
+                   gen_spec->efs_loc_mac[5];
+               sf_spec->sfs_dword[2] =
+                   gen_spec->efs_loc_mac[0] << 8 |
+                   gen_spec->efs_loc_mac[1];
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               rc = ENOTSUP;
+               goto fail5;
+       }
+
+       return (0);
+
+fail5:
+       EFSYS_PROBE(fail5);
+fail4:
+       EFSYS_PROBE(fail4);
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+/*
+ * The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit
+ * key derived from the n-tuple.
+ */
+static                 uint16_t
+siena_filter_tbl_hash(
+       __in            uint32_t key)
+{
+       uint16_t tmp;
+
+       /* First 16 rounds */
+       tmp = 0x1fff ^ (uint16_t)(key >> 16);
+       tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
+       tmp = tmp ^ tmp >> 9;
+
+       /* Last 16 rounds */
+       tmp = tmp ^ tmp << 13 ^ (uint16_t)(key & 0xffff);
+       tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
+       tmp = tmp ^ tmp >> 9;
+
+       return (tmp);
+}
+
+/*
+ * To allow for hash collisions, filter search continues at these
+ * increments from the first possible entry selected by the hash.
+ */
+static                 uint16_t
+siena_filter_tbl_increment(
+       __in            uint32_t key)
+{
+       return ((uint16_t)(key * 2 - 1));
+}
+
+static __checkReturn   boolean_t
+siena_filter_test_used(
+       __in            siena_filter_tbl_t *sftp,
+       __in            unsigned int index)
+{
+       EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
+       return ((sftp->sft_bitmap[index / 32] & (1 << (index % 32))) != 0);
+}
+
+static                 void
+siena_filter_set_used(
+       __in            siena_filter_tbl_t *sftp,
+       __in            unsigned int index)
+{
+       EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
+       sftp->sft_bitmap[index / 32] |= (1 << (index % 32));
+       ++sftp->sft_used;
+}
+
+static                 void
+siena_filter_clear_used(
+       __in            siena_filter_tbl_t *sftp,
+       __in            unsigned int index)
+{
+       EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
+       sftp->sft_bitmap[index / 32] &= ~(1 << (index % 32));
+
+       --sftp->sft_used;
+       EFSYS_ASSERT3U(sftp->sft_used, >=, 0);
+}
+
+
+static                 siena_filter_tbl_id_t
+siena_filter_tbl_id(
+       __in            siena_filter_type_t type)
+{
+       siena_filter_tbl_id_t tbl_id;
+
+       switch (type) {
+       case EFX_SIENA_FILTER_RX_TCP_FULL:
+       case EFX_SIENA_FILTER_RX_TCP_WILD:
+       case EFX_SIENA_FILTER_RX_UDP_FULL:
+       case EFX_SIENA_FILTER_RX_UDP_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_RX_IP;
+               break;
+
+       case EFX_SIENA_FILTER_RX_MAC_FULL:
+       case EFX_SIENA_FILTER_RX_MAC_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_RX_MAC;
+               break;
+
+       case EFX_SIENA_FILTER_TX_TCP_FULL:
+       case EFX_SIENA_FILTER_TX_TCP_WILD:
+       case EFX_SIENA_FILTER_TX_UDP_FULL:
+       case EFX_SIENA_FILTER_TX_UDP_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_TX_IP;
+               break;
+
+       case EFX_SIENA_FILTER_TX_MAC_FULL:
+       case EFX_SIENA_FILTER_TX_MAC_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_TX_MAC;
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               tbl_id = EFX_SIENA_FILTER_NTBLS;
+               break;
+       }
+       return (tbl_id);
+}
+
+static                 void
+siena_filter_reset_search_depth(
+       __inout         siena_filter_t *sfp,
+       __in            siena_filter_tbl_id_t tbl_id)
+{
+       switch (tbl_id) {
+       case EFX_SIENA_FILTER_TBL_RX_IP:
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] = 0;
+               break;
+
+       case EFX_SIENA_FILTER_TBL_RX_MAC:
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] = 0;
+               break;
+
+       case EFX_SIENA_FILTER_TBL_TX_IP:
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] = 0;
+               break;
+
+       case EFX_SIENA_FILTER_TBL_TX_MAC:
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] = 0;
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               break;
+       }
+}
+
+static                 void
+siena_filter_push_rx_limits(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       efx_oword_t oword;
+
+       EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
+
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_FULL_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] +
+           FILTER_CTL_SRCH_FUDGE_FULL);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_WILD_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] +
+           FILTER_CTL_SRCH_FUDGE_WILD);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_FULL_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] +
+           FILTER_CTL_SRCH_FUDGE_FULL);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_WILD_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] +
+           FILTER_CTL_SRCH_FUDGE_WILD);
+
+       if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC].sft_size) {
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
+                   sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] +
+                   FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
+                   sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] +
+                   FILTER_CTL_SRCH_FUDGE_WILD);
+       }
+
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
+}
+
+static                 void
+siena_filter_push_tx_limits(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       efx_oword_t oword;
+
+       EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword);
+
+       if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP].sft_size != 0) {
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] +
+                   FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] +
+                   FILTER_CTL_SRCH_FUDGE_WILD);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] +
+                   FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] +
+                   FILTER_CTL_SRCH_FUDGE_WILD);
+       }
+
+       if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC].sft_size != 0) {
+               EFX_SET_OWORD_FIELD(
+                       oword, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
+                       sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] +
+                       FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(
+                       oword, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
+                       sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] +
+                       FILTER_CTL_SRCH_FUDGE_WILD);
+       }
+
+       EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword);
+}
+
+/* Build a filter entry and return its n-tuple key. */
+static __checkReturn   uint32_t
+siena_filter_build(
+       __out           efx_oword_t *filter,
+       __in            siena_filter_spec_t *spec)
+{
+       uint32_t dword3;
+       uint32_t key;
+       uint8_t  type  = spec->sfs_type;
+       uint32_t flags = spec->sfs_flags;
+
+       switch (siena_filter_tbl_id(type)) {
+       case EFX_SIENA_FILTER_TBL_RX_IP: {
+               boolean_t is_udp = (type == EFX_SIENA_FILTER_RX_UDP_FULL ||
+                   type == EFX_SIENA_FILTER_RX_UDP_WILD);
+               EFX_POPULATE_OWORD_7(*filter,
+                   FRF_BZ_RSS_EN,
+                   (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
+                   FRF_BZ_SCATTER_EN,
+                   (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
+                   FRF_AZ_TCP_UDP, is_udp,
+                   FRF_AZ_RXQ_ID, spec->sfs_dmaq_id,
+                   EFX_DWORD_2, spec->sfs_dword[2],
+                   EFX_DWORD_1, spec->sfs_dword[1],
+                   EFX_DWORD_0, spec->sfs_dword[0]);
+               dword3 = is_udp;
+               break;
+       }
+
+       case EFX_SIENA_FILTER_TBL_RX_MAC: {
+               boolean_t is_wild = (type == EFX_SIENA_FILTER_RX_MAC_WILD);
+               EFX_POPULATE_OWORD_7(*filter,
+                   FRF_CZ_RMFT_RSS_EN,
+                   (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
+                   FRF_CZ_RMFT_SCATTER_EN,
+                   (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
+                   FRF_CZ_RMFT_RXQ_ID, spec->sfs_dmaq_id,
+                   FRF_CZ_RMFT_WILDCARD_MATCH, is_wild,
+                   FRF_CZ_RMFT_DEST_MAC_DW1, spec->sfs_dword[2],
+                   FRF_CZ_RMFT_DEST_MAC_DW0, spec->sfs_dword[1],
+                   FRF_CZ_RMFT_VLAN_ID, spec->sfs_dword[0]);
+               dword3 = is_wild;
+               break;
+       }
+
+       case EFX_SIENA_FILTER_TBL_TX_IP: {
+               boolean_t is_udp = (type == EFX_SIENA_FILTER_TX_UDP_FULL ||
+                   type == EFX_SIENA_FILTER_TX_UDP_WILD);
+               EFX_POPULATE_OWORD_5(*filter,
+                   FRF_CZ_TIFT_TCP_UDP, is_udp,
+                   FRF_CZ_TIFT_TXQ_ID, spec->sfs_dmaq_id,
+                   EFX_DWORD_2, spec->sfs_dword[2],
+                   EFX_DWORD_1, spec->sfs_dword[1],
+                   EFX_DWORD_0, spec->sfs_dword[0]);
+               dword3 = is_udp | spec->sfs_dmaq_id << 1;
+               break;
+       }
+
+       case EFX_SIENA_FILTER_TBL_TX_MAC: {
+               boolean_t is_wild = (type == EFX_SIENA_FILTER_TX_MAC_WILD);
+               EFX_POPULATE_OWORD_5(*filter,
+                   FRF_CZ_TMFT_TXQ_ID, spec->sfs_dmaq_id,
+                   FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
+                   FRF_CZ_TMFT_SRC_MAC_DW1, spec->sfs_dword[2],
+                   FRF_CZ_TMFT_SRC_MAC_DW0, spec->sfs_dword[1],
+                   FRF_CZ_TMFT_VLAN_ID, spec->sfs_dword[0]);
+               dword3 = is_wild | spec->sfs_dmaq_id << 1;
+               break;
+       }
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               return (0);
+       }
+
+       key =
+           spec->sfs_dword[0] ^
+           spec->sfs_dword[1] ^
+           spec->sfs_dword[2] ^
+           dword3;
+
+       return (key);
+}
+
+static __checkReturn           efx_rc_t
+siena_filter_push_entry(
+       __inout                 efx_nic_t *enp,
+       __in                    siena_filter_type_t type,
+       __in                    int index,
+       __in                    efx_oword_t *eop)
+{
+       efx_rc_t rc;
+
+       switch (type) {
+       case EFX_SIENA_FILTER_RX_TCP_FULL:
+       case EFX_SIENA_FILTER_RX_TCP_WILD:
+       case EFX_SIENA_FILTER_RX_UDP_FULL:
+       case EFX_SIENA_FILTER_RX_UDP_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       case EFX_SIENA_FILTER_RX_MAC_FULL:
+       case EFX_SIENA_FILTER_RX_MAC_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_CZ_RX_MAC_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       case EFX_SIENA_FILTER_TX_TCP_FULL:
+       case EFX_SIENA_FILTER_TX_TCP_WILD:
+       case EFX_SIENA_FILTER_TX_UDP_FULL:
+       case EFX_SIENA_FILTER_TX_UDP_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       case EFX_SIENA_FILTER_TX_MAC_FULL:
+       case EFX_SIENA_FILTER_TX_MAC_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_MAC_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               rc = ENOTSUP;
+               goto fail1;
+       }
+       return (0);
+
+fail1:
+       return (rc);
+}
+
+
+static __checkReturn   boolean_t
+siena_filter_equal(
+       __in            const siena_filter_spec_t *left,
+       __in            const siena_filter_spec_t *right)
+{
+       siena_filter_tbl_id_t tbl_id;
+
+       tbl_id = siena_filter_tbl_id(left->sfs_type);
+
+
+       if (left->sfs_type != right->sfs_type)
+               return (B_FALSE);
+
+       if (memcmp(left->sfs_dword, right->sfs_dword,
+               sizeof (left->sfs_dword)))
+               return (B_FALSE);
+
+       if ((tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
+               tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC) &&
+           left->sfs_dmaq_id != right->sfs_dmaq_id)
+               return (B_FALSE);
+
+       return (B_TRUE);
+}
+
+static __checkReturn   efx_rc_t
+siena_filter_search(
+       __in            siena_filter_tbl_t *sftp,
+       __in            siena_filter_spec_t *spec,
+       __in            uint32_t key,
+       __in            boolean_t for_insert,
+       __out           int *filter_index,
+       __out           unsigned int *depth_required)
+{
+       unsigned int hash, incr, filter_idx, depth;
+
+       hash = siena_filter_tbl_hash(key);
+       incr = siena_filter_tbl_increment(key);
+
+       filter_idx = hash & (sftp->sft_size - 1);
+       depth = 1;
+
+       for (;;) {
+               /*
+                * Return success if entry is used and matches this spec
+                * or entry is unused and we are trying to insert.
+                */
+               if (siena_filter_test_used(sftp, filter_idx) ?
+                   siena_filter_equal(spec,
+                   &sftp->sft_spec[filter_idx]) :
+                   for_insert) {
+                       *filter_index = filter_idx;
+                       *depth_required = depth;
+                       return (0);
+               }
+
+               /* Return failure if we reached the maximum search depth */
+               if (depth == FILTER_CTL_SRCH_MAX)
+                       return (for_insert ? EBUSY : ENOENT);
+
+               filter_idx = (filter_idx + incr) & (sftp->sft_size - 1);
+               ++depth;
+       }
+}
+
+static                 void
+siena_filter_clear_entry(
+       __in            efx_nic_t *enp,
+       __in            siena_filter_tbl_t *sftp,
+       __in            int index)
+{
+       efx_oword_t filter;
+
+       if (siena_filter_test_used(sftp, index)) {
+               siena_filter_clear_used(sftp, index);
+
+               EFX_ZERO_OWORD(filter);
+               siena_filter_push_entry(enp,
+                   sftp->sft_spec[index].sfs_type,
+                   index, &filter);
+
+               memset(&sftp->sft_spec[index],
+                   0, sizeof (sftp->sft_spec[0]));
+       }
+}
+
+                       void
+siena_filter_tbl_clear(
+       __in            efx_nic_t *enp,
+       __in            siena_filter_tbl_id_t tbl_id)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
+       int index;
+       efsys_lock_state_t state;
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       for (index = 0; index < sftp->sft_size; ++index) {
+               siena_filter_clear_entry(enp, sftp, index);
+       }
+
+       if (sftp->sft_used == 0)
+               siena_filter_reset_search_depth(sfp, tbl_id);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+}
+
+static __checkReturn   efx_rc_t
+siena_filter_init(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp;
+       siena_filter_tbl_t *sftp;
+       int tbl_id;
+       efx_rc_t rc;
+
+       EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (siena_filter_t), sfp);
+
+       if (!sfp) {
+               rc = ENOMEM;
+               goto fail1;
+       }
+
+       enp->en_filter.ef_siena_filter = sfp;
+
+       switch (enp->en_family) {
+       case EFX_FAMILY_SIENA:
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_IP];
+               sftp->sft_size = FR_AZ_RX_FILTER_TBL0_ROWS;
+
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC];
+               sftp->sft_size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS;
+
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP];
+               sftp->sft_size = FR_CZ_TX_FILTER_TBL0_ROWS;
+
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC];
+               sftp->sft_size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS;
+               break;
+
+       default:
+               rc = ENOTSUP;
+               goto fail2;
+       }
+
+       for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
+               unsigned int bitmap_size;
+
+               sftp = &sfp->sf_tbl[tbl_id];
+               if (sftp->sft_size == 0)
+                       continue;
+
+               EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
+                   sizeof (uint32_t));
+               bitmap_size =
+                   (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
+
+               EFSYS_KMEM_ALLOC(enp->en_esip, bitmap_size, sftp->sft_bitmap);
+               if (!sftp->sft_bitmap) {
+                       rc = ENOMEM;
+                       goto fail3;
+               }
+
+               EFSYS_KMEM_ALLOC(enp->en_esip,
+                   sftp->sft_size * sizeof (*sftp->sft_spec),
+                   sftp->sft_spec);
+               if (!sftp->sft_spec) {
+                       rc = ENOMEM;
+                       goto fail4;
+               }
+               memset(sftp->sft_spec, 0,
+                   sftp->sft_size * sizeof (*sftp->sft_spec));
+       }
+
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+
+fail3:
+       EFSYS_PROBE(fail3);
+
+fail2:
+       EFSYS_PROBE(fail2);
+       siena_filter_fini(enp);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
+static                 void
+siena_filter_fini(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
+
+       if (sfp == NULL)
+               return;
+
+       for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
+               siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
+               unsigned int bitmap_size;
+
+               EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
+                   sizeof (uint32_t));
+               bitmap_size =
+                   (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
+
+               if (sftp->sft_bitmap != NULL) {
+                       EFSYS_KMEM_FREE(enp->en_esip, bitmap_size,
+                           sftp->sft_bitmap);
+                       sftp->sft_bitmap = NULL;
+               }
+
+               if (sftp->sft_spec != NULL) {
+                       EFSYS_KMEM_FREE(enp->en_esip, sftp->sft_size *
+                           sizeof (*sftp->sft_spec), sftp->sft_spec);
+                       sftp->sft_spec = NULL;
+               }
+       }
+
+       EFSYS_KMEM_FREE(enp->en_esip, sizeof (siena_filter_t),
+           enp->en_filter.ef_siena_filter);
+}
+
+/* Restore filter state after a reset */
+static __checkReturn   efx_rc_t
+siena_filter_restore(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+       siena_filter_tbl_t *sftp;
+       siena_filter_spec_t *spec;
+       efx_oword_t filter;
+       int filter_idx;
+       efsys_lock_state_t state;
+       uint32_t key;
+       efx_rc_t rc;
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
+               sftp = &sfp->sf_tbl[tbl_id];
+               for (filter_idx = 0;
+                       filter_idx < sftp->sft_size;
+                       filter_idx++) {
+                       if (!siena_filter_test_used(sftp, filter_idx))
+                               continue;
+
+                       spec = &sftp->sft_spec[filter_idx];
+                       if ((key = siena_filter_build(&filter, spec)) == 0) {
+                               rc = EINVAL;
+                               goto fail1;
+                       }
+                       if ((rc = siena_filter_push_entry(enp,
+                                   spec->sfs_type, filter_idx, &filter)) != 0)
+                               goto fail2;
+               }
+       }
+
+       siena_filter_push_rx_limits(enp);
+       siena_filter_push_tx_limits(enp);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+
+       return (rc);
+}
+
+static  __checkReturn  efx_rc_t
+siena_filter_add(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec,
+       __in            boolean_t may_replace)
+{
+       efx_rc_t rc;
+       siena_filter_spec_t sf_spec;
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+       siena_filter_tbl_t *sftp;
+       siena_filter_spec_t *saved_sf_spec;
+       efx_oword_t filter;
+       int filter_idx;
+       unsigned int depth;
+       efsys_lock_state_t state;
+       uint32_t key;
+
+
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
+               goto fail1;
+
+       tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
+       sftp = &sfp->sf_tbl[tbl_id];
+
+       if (sftp->sft_size == 0) {
+               rc = EINVAL;
+               goto fail2;
+       }
+
+       key = siena_filter_build(&filter, &sf_spec);
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       rc = siena_filter_search(sftp, &sf_spec, key, B_TRUE,
+           &filter_idx, &depth);
+       if (rc != 0)
+               goto fail3;
+
+       EFSYS_ASSERT3U(filter_idx, <, sftp->sft_size);
+       saved_sf_spec = &sftp->sft_spec[filter_idx];
+
+       if (siena_filter_test_used(sftp, filter_idx)) {
+               if (may_replace == B_FALSE) {
+                       rc = EEXIST;
+                       goto fail4;
+               }
+       }
+       siena_filter_set_used(sftp, filter_idx);
+       *saved_sf_spec = sf_spec;
+
+       if (sfp->sf_depth[sf_spec.sfs_type] < depth) {
+               sfp->sf_depth[sf_spec.sfs_type] = depth;
+               if (tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
+                   tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC)
+                       siena_filter_push_tx_limits(enp);
+               else
+                       siena_filter_push_rx_limits(enp);
+       }
+
+       siena_filter_push_entry(enp, sf_spec.sfs_type,
+           filter_idx, &filter);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+
+fail3:
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       EFSYS_PROBE(fail3);
+
+fail2:
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
+static  __checkReturn  efx_rc_t
+siena_filter_delete(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec)
+{
+       efx_rc_t rc;
+       siena_filter_spec_t sf_spec;
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+       siena_filter_tbl_t *sftp;
+       efx_oword_t filter;
+       int filter_idx;
+       unsigned int depth;
+       efsys_lock_state_t state;
+       uint32_t key;
+
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
+               goto fail1;
+
+       tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
+       sftp = &sfp->sf_tbl[tbl_id];
+
+       key = siena_filter_build(&filter, &sf_spec);
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       rc = siena_filter_search(sftp, &sf_spec, key, B_FALSE,
+           &filter_idx, &depth);
+       if (rc != 0)
+               goto fail2;
+
+       siena_filter_clear_entry(enp, sftp, filter_idx);
+       if (sftp->sft_used == 0)
+               siena_filter_reset_search_depth(sfp, tbl_id);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       return (0);
+
+fail2:
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
+#define        MAX_SUPPORTED 4
+
+static __checkReturn   efx_rc_t
+siena_filter_supported_filters(
+       __in            efx_nic_t *enp,
+       __out           uint32_t *list,
+       __out           size_t *length)
+{
+       int index = 0;
+       uint32_t rx_matches[MAX_SUPPORTED];
+       efx_rc_t rc;
+
+       if (list == NULL) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       rx_matches[index++] =
+           EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+           EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
+
+       rx_matches[index++] =
+           EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
+
+       if (enp->en_features & EFX_FEATURE_MAC_HEADER_FILTERS) {
+               rx_matches[index++] =
+                   EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_LOC_MAC;
+
+               rx_matches[index++] = EFX_FILTER_MATCH_LOC_MAC;
+       }
+
+       EFSYS_ASSERT3U(index, <=, MAX_SUPPORTED);
+
+       *length = index;
+       memcpy(list, rx_matches, *length);
+
+       return (0);
+
+fail1:
+
+       return (rc);
+}
+
+#undef MAX_SUPPORTED
+
+#endif /* EFSYS_OPT_SIENA */
+
 #endif /* EFSYS_OPT_FILTER */
index c6ec808..8d85f3f 100644 (file)
 #endif
 
 
+#if EFSYS_OPT_SIENA
+#include "siena_impl.h"
+#endif /* EFSYS_OPT_SIENA */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -274,9 +278,70 @@ typedef struct efx_nic_ops_s {
 
 #if EFSYS_OPT_FILTER
 
+#if EFSYS_OPT_SIENA
+
+typedef struct siena_filter_spec_s {
+       uint8_t         sfs_type;
+       uint32_t        sfs_flags;
+       uint32_t        sfs_dmaq_id;
+       uint32_t        sfs_dword[3];
+} siena_filter_spec_t;
+
+typedef enum siena_filter_type_e {
+       EFX_SIENA_FILTER_RX_TCP_FULL,   /* TCP/IPv4 {dIP,dTCP,sIP,sTCP} */
+       EFX_SIENA_FILTER_RX_TCP_WILD,   /* TCP/IPv4 {dIP,dTCP,  -,   -} */
+       EFX_SIENA_FILTER_RX_UDP_FULL,   /* UDP/IPv4 {dIP,dUDP,sIP,sUDP} */
+       EFX_SIENA_FILTER_RX_UDP_WILD,   /* UDP/IPv4 {dIP,dUDP,  -,   -} */
+       EFX_SIENA_FILTER_RX_MAC_FULL,   /* Ethernet {dMAC,VLAN} */
+       EFX_SIENA_FILTER_RX_MAC_WILD,   /* Ethernet {dMAC,   -} */
+
+       EFX_SIENA_FILTER_TX_TCP_FULL,   /* TCP/IPv4 {dIP,dTCP,sIP,sTCP} */
+       EFX_SIENA_FILTER_TX_TCP_WILD,   /* TCP/IPv4 {  -,   -,sIP,sTCP} */
+       EFX_SIENA_FILTER_TX_UDP_FULL,   /* UDP/IPv4 {dIP,dTCP,sIP,sTCP} */
+       EFX_SIENA_FILTER_TX_UDP_WILD,   /* UDP/IPv4 {  -,   -,sIP,sUDP} */
+       EFX_SIENA_FILTER_TX_MAC_FULL,   /* Ethernet {sMAC,VLAN} */
+       EFX_SIENA_FILTER_TX_MAC_WILD,   /* Ethernet {sMAC,   -} */
+
+       EFX_SIENA_FILTER_NTYPES
+} siena_filter_type_t;
+
+typedef enum siena_filter_tbl_id_e {
+       EFX_SIENA_FILTER_TBL_RX_IP = 0,
+       EFX_SIENA_FILTER_TBL_RX_MAC,
+       EFX_SIENA_FILTER_TBL_TX_IP,
+       EFX_SIENA_FILTER_TBL_TX_MAC,
+       EFX_SIENA_FILTER_NTBLS
+} siena_filter_tbl_id_t;
+
+typedef struct siena_filter_tbl_s {
+       int                     sft_size;       /* number of entries */
+       int                     sft_used;       /* active count */
+       uint32_t                *sft_bitmap;    /* active bitmap */
+       siena_filter_spec_t     *sft_spec;      /* array of saved specs */
+} siena_filter_tbl_t;
+
+typedef struct siena_filter_s {
+       siena_filter_tbl_t      sf_tbl[EFX_SIENA_FILTER_NTBLS];
+       unsigned int            sf_depth[EFX_SIENA_FILTER_NTYPES];
+} siena_filter_t;
+
+#endif /* EFSYS_OPT_SIENA */
+
 typedef struct efx_filter_s {
+#if EFSYS_OPT_SIENA
+       siena_filter_t          *ef_siena_filter;
+#endif /* EFSYS_OPT_SIENA */
 } efx_filter_t;
 
+#if EFSYS_OPT_SIENA
+
+extern                 void
+siena_filter_tbl_clear(
+       __in            efx_nic_t *enp,
+       __in            siena_filter_tbl_id_t tbl);
+
+#endif /* EFSYS_OPT_SIENA */
+
 #endif /* EFSYS_OPT_FILTER */
 
 #if EFSYS_OPT_MCDI
@@ -341,6 +406,11 @@ struct efx_nic_s {
 #endif /* EFSYS_OPT_MCDI */
        uint32_t                en_vport_id;
        union {
+#if EFSYS_OPT_SIENA
+               struct {
+                       int                     enu_unused;
+               } siena;
+#endif /* EFSYS_OPT_SIENA */
                int     enu_unused;
        } en_u;
 };
index fb1812b..ecc09d3 100644 (file)
 #include "efx_impl.h"
 
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_intr_init(
+       __in            efx_nic_t *enp,
+       __in            efx_intr_type_t type,
+       __in            efsys_mem_t *esmp);
+
+static                 void
+siena_intr_enable(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_intr_disable(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_intr_disable_unlocked(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_intr_trigger(
+       __in            efx_nic_t *enp,
+       __in            unsigned int level);
+
+static                 void
+siena_intr_fini(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_intr_status_line(
+       __in            efx_nic_t *enp,
+       __out           boolean_t *fatalp,
+       __out           uint32_t *qmaskp);
+
+static                 void
+siena_intr_status_message(
+       __in            efx_nic_t *enp,
+       __in            unsigned int message,
+       __out           boolean_t *fatalp);
+
+static                 void
+siena_intr_fatal(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   boolean_t
+siena_intr_check_fatal(
+       __in            efx_nic_t *enp);
+
+
+#endif /* EFSYS_OPT_SIENA */
+
+
+#if EFSYS_OPT_SIENA
+static const efx_intr_ops_t    __efx_intr_siena_ops = {
+       siena_intr_init,                /* eio_init */
+       siena_intr_enable,              /* eio_enable */
+       siena_intr_disable,             /* eio_disable */
+       siena_intr_disable_unlocked,    /* eio_disable_unlocked */
+       siena_intr_trigger,             /* eio_trigger */
+       siena_intr_status_line,         /* eio_status_line */
+       siena_intr_status_message,      /* eio_status_message */
+       siena_intr_fatal,               /* eio_fatal */
+       siena_intr_fini,                /* eio_fini */
+};
+#endif /* EFSYS_OPT_SIENA */
+
        __checkReturn   efx_rc_t
 efx_intr_init(
        __in            efx_nic_t *enp,
@@ -57,6 +124,11 @@ efx_intr_init(
        enp->en_mod_flags |= EFX_MOD_INTR;
 
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               eiop = &__efx_intr_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                EFSYS_ASSERT(B_FALSE);
@@ -199,3 +271,276 @@ efx_intr_fatal(
 /* ************************************************************************* */
 /* ************************************************************************* */
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_intr_init(
+       __in            efx_nic_t *enp,
+       __in            efx_intr_type_t type,
+       __in            efsys_mem_t *esmp)
+{
+       efx_intr_t *eip = &(enp->en_intr);
+       efx_oword_t oword;
+
+       /*
+        * bug17213 workaround.
+        *
+        * Under legacy interrupts, don't share a level between fatal
+        * interrupts and event queue interrupts. Under MSI-X, they
+        * must share, or we won't get an interrupt.
+        */
+       if (enp->en_family == EFX_FAMILY_SIENA &&
+           eip->ei_type == EFX_INTR_LINE)
+               eip->ei_level = 0x1f;
+       else
+               eip->ei_level = 0;
+
+       /* Enable all the genuinely fatal interrupts */
+       EFX_SET_OWORD(oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_ILL_ADR_INT_KER_EN, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_RBUF_OWN_INT_KER_EN, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TBUF_OWN_INT_KER_EN, 0);
+       if (enp->en_family >= EFX_FAMILY_SIENA)
+               EFX_SET_OWORD_FIELD(oword, FRF_CZ_SRAM_PERR_INT_P_KER_EN, 0);
+       EFX_BAR_WRITEO(enp, FR_AZ_FATAL_INTR_REG_KER, &oword);
+
+       /* Set up the interrupt address register */
+       EFX_POPULATE_OWORD_3(oword,
+           FRF_AZ_NORM_INT_VEC_DIS_KER, (type == EFX_INTR_MESSAGE) ? 1 : 0,
+           FRF_AZ_INT_ADR_KER_DW0, EFSYS_MEM_ADDR(esmp) & 0xffffffff,
+           FRF_AZ_INT_ADR_KER_DW1, EFSYS_MEM_ADDR(esmp) >> 32);
+       EFX_BAR_WRITEO(enp, FR_AZ_INT_ADR_REG_KER, &oword);
+
+       return (0);
+}
+
+static                 void
+siena_intr_enable(
+       __in            efx_nic_t *enp)
+{
+       efx_intr_t *eip = &(enp->en_intr);
+       efx_oword_t oword;
+
+       EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_LEVE_SEL, eip->ei_level);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_DRV_INT_EN_KER, 1);
+       EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+}
+
+static                 void
+siena_intr_disable(
+       __in            efx_nic_t *enp)
+{
+       efx_oword_t oword;
+
+       EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_DRV_INT_EN_KER, 0);
+       EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+
+       EFSYS_SPIN(10);
+}
+
+static                 void
+siena_intr_disable_unlocked(
+       __in            efx_nic_t *enp)
+{
+       efx_oword_t oword;
+
+       EFSYS_BAR_READO(enp->en_esbp, FR_AZ_INT_EN_REG_KER_OFST,
+                       &oword, B_FALSE);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_DRV_INT_EN_KER, 0);
+       EFSYS_BAR_WRITEO(enp->en_esbp, FR_AZ_INT_EN_REG_KER_OFST,
+           &oword, B_FALSE);
+}
+
+static __checkReturn   efx_rc_t
+siena_intr_trigger(
+       __in            efx_nic_t *enp,
+       __in            unsigned int level)
+{
+       efx_intr_t *eip = &(enp->en_intr);
+       efx_oword_t oword;
+       unsigned int count;
+       uint32_t sel;
+       efx_rc_t rc;
+
+       /* bug16757: No event queues can be initialized */
+       EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV));
+
+       if (level >= EFX_NINTR_SIENA) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       if (level > EFX_MASK32(FRF_AZ_KER_INT_LEVE_SEL))
+               return (ENOTSUP); /* avoid EFSYS_PROBE() */
+
+       sel = level;
+
+       /* Trigger a test interrupt */
+       EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_LEVE_SEL, sel);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_KER, 1);
+       EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+
+       /*
+        * Wait up to 100ms for the interrupt to be raised before restoring
+        * KER_INT_LEVE_SEL. Ignore a failure to raise (the caller will
+        * observe this soon enough anyway), but always reset KER_INT_LEVE_SEL
+        */
+       count = 0;
+       do {
+               EFSYS_SPIN(100);        /* 100us */
+
+               EFX_BAR_READO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+       } while (EFX_OWORD_FIELD(oword, FRF_AZ_KER_INT_KER) && ++count < 1000);
+
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_KER_INT_LEVE_SEL, eip->ei_level);
+       EFX_BAR_WRITEO(enp, FR_AZ_INT_EN_REG_KER, &oword);
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static __checkReturn   boolean_t
+siena_intr_check_fatal(
+       __in            efx_nic_t *enp)
+{
+       efx_intr_t *eip = &(enp->en_intr);
+       efsys_mem_t *esmp = eip->ei_esmp;
+       efx_oword_t oword;
+
+       /* Read the syndrome */
+       EFSYS_MEM_READO(esmp, 0, &oword);
+
+       if (EFX_OWORD_FIELD(oword, FSF_AZ_NET_IVEC_FATAL_INT) != 0) {
+               EFSYS_PROBE(fatal);
+
+               /* Clear the fatal interrupt condition */
+               EFX_SET_OWORD_FIELD(oword, FSF_AZ_NET_IVEC_FATAL_INT, 0);
+               EFSYS_MEM_WRITEO(esmp, 0, &oword);
+
+               return (B_TRUE);
+       }
+
+       return (B_FALSE);
+}
+
+static                 void
+siena_intr_status_line(
+       __in            efx_nic_t *enp,
+       __out           boolean_t *fatalp,
+       __out           uint32_t *qmaskp)
+{
+       efx_intr_t *eip = &(enp->en_intr);
+       efx_dword_t dword;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR);
+
+       /*
+        * Read the queue mask and implicitly acknowledge the
+        * interrupt.
+        */
+       EFX_BAR_READD(enp, FR_BZ_INT_ISR0_REG, &dword, B_FALSE);
+       *qmaskp = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
+
+       EFSYS_PROBE1(qmask, uint32_t, *qmaskp);
+
+       if (*qmaskp & (1U << eip->ei_level))
+               *fatalp = siena_intr_check_fatal(enp);
+       else
+               *fatalp = B_FALSE;
+}
+
+static                 void
+siena_intr_status_message(
+       __in            efx_nic_t *enp,
+       __in            unsigned int message,
+       __out           boolean_t *fatalp)
+{
+       efx_intr_t *eip = &(enp->en_intr);
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR);
+
+       if (message == eip->ei_level)
+               *fatalp = siena_intr_check_fatal(enp);
+       else
+               *fatalp = B_FALSE;
+}
+
+
+static         void
+siena_intr_fatal(
+       __in    efx_nic_t *enp)
+{
+#if EFSYS_OPT_DECODE_INTR_FATAL
+       efx_oword_t fatal;
+       efx_oword_t mem_per;
+
+       EFX_BAR_READO(enp, FR_AZ_FATAL_INTR_REG_KER, &fatal);
+       EFX_ZERO_OWORD(mem_per);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_SRM_PERR_INT_KER) != 0 ||
+           EFX_OWORD_FIELD(fatal, FRF_AZ_MEM_PERR_INT_KER) != 0)
+               EFX_BAR_READO(enp, FR_AZ_MEM_STAT_REG, &mem_per);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_SRAM_OOB_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_SRAM_OOB, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_BUFID_DC_OOB_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_BUFID_DC_OOB, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_MEM_PERR_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_MEM_PERR,
+                   EFX_OWORD_FIELD(mem_per, EFX_DWORD_0),
+                   EFX_OWORD_FIELD(mem_per, EFX_DWORD_1));
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_RBUF_OWN_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_RBUF_OWN, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_TBUF_OWN_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_TBUF_OWN, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_RDESCQ_OWN_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_RDESQ_OWN, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_TDESCQ_OWN_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_TDESQ_OWN, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_EVQ_OWN_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_EVQ_OWN, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_EVF_OFLO_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_EVFF_OFLO, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_ILL_ADR_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_ILL_ADDR, 0, 0);
+
+       if (EFX_OWORD_FIELD(fatal, FRF_AZ_SRM_PERR_INT_KER) != 0)
+               EFSYS_ERR(enp->en_esip, EFX_ERR_SRAM_PERR,
+                   EFX_OWORD_FIELD(mem_per, EFX_DWORD_0),
+                   EFX_OWORD_FIELD(mem_per, EFX_DWORD_1));
+#else
+       EFSYS_ASSERT(0);
+#endif
+}
+
+static         void
+siena_intr_fini(
+       __in    efx_nic_t *enp)
+{
+       efx_oword_t oword;
+
+       /* Clear the interrupt address register */
+       EFX_ZERO_OWORD(oword);
+       EFX_BAR_WRITEO(enp, FR_AZ_INT_ADR_REG_KER, &oword);
+}
+
+#endif /* EFSYS_OPT_SIENA */
index 169dcf1..ce27376 100644 (file)
 #include "efx.h"
 #include "efx_impl.h"
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_mac_multicast_list_set(
+       __in            efx_nic_t *enp);
+
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_SIENA
+static const efx_mac_ops_t     __efx_siena_mac_ops = {
+       siena_mac_poll,                         /* emo_poll */
+       siena_mac_up,                           /* emo_up */
+       siena_mac_reconfigure,                  /* emo_addr_set */
+       siena_mac_reconfigure,                  /* emo_pdu_set */
+       siena_mac_pdu_get,                      /* emo_pdu_get */
+       siena_mac_reconfigure,                  /* emo_reconfigure */
+       siena_mac_multicast_list_set,           /* emo_multicast_list_set */
+       NULL,                                   /* emo_filter_set_default_rxq */
+       NULL,                           /* emo_filter_default_rxq_clear */
+};
+#endif /* EFSYS_OPT_SIENA */
+
        __checkReturn                   efx_rc_t
 efx_mac_pdu_set(
        __in                            efx_nic_t *enp,
@@ -465,6 +487,12 @@ efx_mac_select(
        int rc = EINVAL;
 
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               emop = &__efx_siena_mac_ops;
+               type = EFX_MAC_SIENA;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                rc = EINVAL;
@@ -487,3 +515,72 @@ fail1:
 }
 
 
+#if EFSYS_OPT_SIENA
+
+#define        EFX_MAC_HASH_BITS       (1 << 8)
+
+/* Compute the multicast hash as used on Falcon and Siena. */
+static void
+siena_mac_multicast_hash_compute(
+       __in_ecount(6*count)            uint8_t const *addrs,
+       __in                            int count,
+       __out                           efx_oword_t *hash_low,
+       __out                           efx_oword_t *hash_high)
+{
+       uint32_t crc, index;
+       int i;
+
+       EFSYS_ASSERT(hash_low != NULL);
+       EFSYS_ASSERT(hash_high != NULL);
+
+       EFX_ZERO_OWORD(*hash_low);
+       EFX_ZERO_OWORD(*hash_high);
+
+       for (i = 0; i < count; i++) {
+               /* Calculate hash bucket (IEEE 802.3 CRC32 of the MAC addr) */
+               crc = efx_crc32_calculate(0xffffffff, addrs, EFX_MAC_ADDR_LEN);
+               index = crc % EFX_MAC_HASH_BITS;
+               if (index < 128) {
+                       EFX_SET_OWORD_BIT(*hash_low, index);
+               } else {
+                       EFX_SET_OWORD_BIT(*hash_high, index - 128);
+               }
+
+               addrs += EFX_MAC_ADDR_LEN;
+       }
+}
+
+static __checkReturn   efx_rc_t
+siena_mac_multicast_list_set(
+       __in            efx_nic_t *enp)
+{
+       efx_port_t *epp = &(enp->en_port);
+       const efx_mac_ops_t *emop = epp->ep_emop;
+       efx_oword_t old_hash[2];
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
+
+       memcpy(old_hash, epp->ep_multicst_hash, sizeof (old_hash));
+
+       siena_mac_multicast_hash_compute(
+           epp->ep_mulcst_addr_list,
+           epp->ep_mulcst_addr_count,
+           &epp->ep_multicst_hash[0],
+           &epp->ep_multicst_hash[1]);
+
+       if ((rc = emop->emo_reconfigure(enp)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       memcpy(epp->ep_multicst_hash, old_hash, sizeof (old_hash));
+
+       return (rc);
+}
+
+#endif /* EFSYS_OPT_SIENA */
index 474c505..deb5d87 100644 (file)
 
 
 
+#if EFSYS_OPT_SIENA
+
+static const efx_mcdi_ops_t    __efx_mcdi_siena_ops = {
+       siena_mcdi_init,                /* emco_init */
+       siena_mcdi_send_request,        /* emco_send_request */
+       siena_mcdi_poll_reboot,         /* emco_poll_reboot */
+       siena_mcdi_poll_response,       /* emco_poll_response */
+       siena_mcdi_read_response,       /* emco_read_response */
+       siena_mcdi_fini,                /* emco_fini */
+       siena_mcdi_feature_supported,   /* emco_feature_supported */
+       siena_mcdi_get_timeout,         /* emco_get_timeout */
+};
+
+#endif /* EFSYS_OPT_SIENA */
+
+
+
        __checkReturn   efx_rc_t
 efx_mcdi_init(
        __in            efx_nic_t *enp,
@@ -66,6 +83,11 @@ efx_mcdi_init(
        EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
 
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               emcop = &__efx_mcdi_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                EFSYS_ASSERT(0);
index a551f0d..5a758ea 100644 (file)
@@ -39,6 +39,20 @@ efx_family(
 {
        if (venid == EFX_PCI_VENID_SFC) {
                switch (devid) {
+#if EFSYS_OPT_SIENA
+               case EFX_PCI_DEVID_SIENA_F1_UNINIT:
+                       /*
+                        * Hardware default for PF0 of uninitialised Siena.
+                        * manftest must be able to cope with this device id.
+                        */
+                       *efp = EFX_FAMILY_SIENA;
+                       return (0);
+
+               case EFX_PCI_DEVID_BETHPAGE:
+               case EFX_PCI_DEVID_SIENA:
+                       *efp = EFX_FAMILY_SIENA;
+                       return (0);
+#endif /* EFSYS_OPT_SIENA */
 
                case EFX_PCI_DEVID_FALCON:      /* Obsolete, not supported */
                default:
@@ -122,6 +136,22 @@ fail1:
        return (rc);
 }
 
+#if EFSYS_OPT_SIENA
+
+static const efx_nic_ops_t     __efx_nic_siena_ops = {
+       siena_nic_probe,                /* eno_probe */
+       NULL,                           /* eno_board_cfg */
+       NULL,                           /* eno_set_drv_limits */
+       siena_nic_reset,                /* eno_reset */
+       siena_nic_init,                 /* eno_init */
+       NULL,                           /* eno_get_vi_pool */
+       NULL,                           /* eno_get_bar_region */
+       siena_nic_fini,                 /* eno_fini */
+       siena_nic_unprobe,              /* eno_unprobe */
+};
+
+#endif /* EFSYS_OPT_SIENA */
+
 
        __checkReturn   efx_rc_t
 efx_nic_create(
@@ -148,6 +178,20 @@ efx_nic_create(
        enp->en_magic = EFX_NIC_MAGIC;
 
        switch (family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               enp->en_enop = &__efx_nic_siena_ops;
+               enp->en_features =
+                   EFX_FEATURE_IPV6 |
+                   EFX_FEATURE_LFSR_HASH_INSERT |
+                   EFX_FEATURE_LINK_EVENTS |
+                   EFX_FEATURE_PERIODIC_MAC_STATS |
+                   EFX_FEATURE_MCDI |
+                   EFX_FEATURE_LOOKAHEAD_SPLIT |
+                   EFX_FEATURE_MAC_HEADER_FILTERS |
+                   EFX_FEATURE_TX_SRC_FILTERS;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                rc = ENOTSUP;
index 7b9a330..a6a2af4 100644 (file)
 #include "efx_impl.h"
 
 
+#if EFSYS_OPT_SIENA
+static const efx_phy_ops_t     __efx_phy_siena_ops = {
+       siena_phy_power,                /* epo_power */
+       NULL,                           /* epo_reset */
+       siena_phy_reconfigure,          /* epo_reconfigure */
+       siena_phy_verify,               /* epo_verify */
+       siena_phy_oui_get,              /* epo_oui_get */
+};
+#endif /* EFSYS_OPT_SIENA */
+
        __checkReturn   efx_rc_t
 efx_phy_probe(
        __in            efx_nic_t *enp)
@@ -48,6 +58,11 @@ efx_phy_probe(
 
        /* Hook in operations structure */
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               epop = &__efx_phy_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
        default:
                rc = ENOTSUP;
                goto fail1;
index fd0af87..c1da6b8 100644 (file)
 #include "efx_impl.h"
 
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_rx_init(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_rx_fini(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_rx_prefix_pktlen(
+       __in            efx_nic_t *enp,
+       __in            uint8_t *buffer,
+       __out           uint16_t *lengthp);
+
+static                 void
+siena_rx_qpost(
+       __in            efx_rxq_t *erp,
+       __in_ecount(n)  efsys_dma_addr_t *addrp,
+       __in            size_t size,
+       __in            unsigned int n,
+       __in            unsigned int completed,
+       __in            unsigned int added);
+
+static                 void
+siena_rx_qpush(
+       __in            efx_rxq_t *erp,
+       __in            unsigned int added,
+       __inout         unsigned int *pushedp);
+
+static __checkReturn   efx_rc_t
+siena_rx_qflush(
+       __in            efx_rxq_t *erp);
+
+static                 void
+siena_rx_qenable(
+       __in            efx_rxq_t *erp);
+
+static __checkReturn   efx_rc_t
+siena_rx_qcreate(
+       __in            efx_nic_t *enp,
+       __in            unsigned int index,
+       __in            unsigned int label,
+       __in            efx_rxq_type_t type,
+       __in            efsys_mem_t *esmp,
+       __in            size_t n,
+       __in            uint32_t id,
+       __in            efx_evq_t *eep,
+       __in            efx_rxq_t *erp);
+
+static                 void
+siena_rx_qdestroy(
+       __in            efx_rxq_t *erp);
+
+#endif /* EFSYS_OPT_SIENA */
+
+
+#if EFSYS_OPT_SIENA
+static const efx_rx_ops_t __efx_rx_siena_ops = {
+       siena_rx_init,                          /* erxo_init */
+       siena_rx_fini,                          /* erxo_fini */
+       siena_rx_prefix_pktlen,                 /* erxo_prefix_pktlen */
+       siena_rx_qpost,                         /* erxo_qpost */
+       siena_rx_qpush,                         /* erxo_qpush */
+       siena_rx_qflush,                        /* erxo_qflush */
+       siena_rx_qenable,                       /* erxo_qenable */
+       siena_rx_qcreate,                       /* erxo_qcreate */
+       siena_rx_qdestroy,                      /* erxo_qdestroy */
+};
+#endif /* EFSYS_OPT_SIENA */
+
+
        __checkReturn   efx_rc_t
 efx_rx_init(
        __inout         efx_nic_t *enp)
@@ -53,6 +126,11 @@ efx_rx_init(
        }
 
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               erxop = &__efx_rx_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                EFSYS_ASSERT(0);
@@ -240,3 +318,342 @@ efx_pseudo_hdr_pkt_length_get(
        return (erxop->erxo_prefix_pktlen(enp, buffer, lengthp));
 }
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_rx_init(
+       __in            efx_nic_t *enp)
+{
+       efx_oword_t oword;
+       unsigned int index;
+
+       EFX_BAR_READO(enp, FR_AZ_RX_CFG_REG, &oword);
+
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_DESC_PUSH_EN, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_HASH_ALG, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_IP_HASH, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_TCP_SUP, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_HASH_INSRT_HDR, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_USR_BUF_SIZE, 0x3000 / 32);
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_CFG_REG, &oword);
+
+       /* Zero the RSS table */
+       for (index = 0; index < FR_BZ_RX_INDIRECTION_TBL_ROWS;
+           index++) {
+               EFX_ZERO_OWORD(oword);
+               EFX_BAR_TBL_WRITEO(enp, FR_BZ_RX_INDIRECTION_TBL,
+                                   index, &oword, B_TRUE);
+       }
+
+       return (0);
+}
+
+
+#define        EFX_RX_LFSR_HASH(_enp, _insert)                                 \
+       do {                                                            \
+               efx_oword_t oword;                                      \
+                                                                       \
+               EFX_BAR_READO((_enp), FR_AZ_RX_CFG_REG, &oword);        \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_HASH_ALG, 0);      \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_IP_HASH, 0);       \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_TCP_SUP, 0);       \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_HASH_INSRT_HDR,    \
+                   (_insert) ? 1 : 0);                                 \
+               EFX_BAR_WRITEO((_enp), FR_AZ_RX_CFG_REG, &oword);       \
+                                                                       \
+               if ((_enp)->en_family == EFX_FAMILY_SIENA) {            \
+                       EFX_BAR_READO((_enp), FR_CZ_RX_RSS_IPV6_REG3,   \
+                           &oword);                                    \
+                       EFX_SET_OWORD_FIELD(oword,                      \
+                           FRF_CZ_RX_RSS_IPV6_THASH_ENABLE, 0);        \
+                       EFX_BAR_WRITEO((_enp), FR_CZ_RX_RSS_IPV6_REG3,  \
+                           &oword);                                    \
+               }                                                       \
+                                                                       \
+               _NOTE(CONSTANTCONDITION)                                \
+       } while (B_FALSE)
+
+#define        EFX_RX_TOEPLITZ_IPV4_HASH(_enp, _insert, _ip, _tcp)             \
+       do {                                                            \
+               efx_oword_t oword;                                      \
+                                                                       \
+               EFX_BAR_READO((_enp), FR_AZ_RX_CFG_REG, &oword);        \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_HASH_ALG, 1);      \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_IP_HASH,           \
+                   (_ip) ? 1 : 0);                                     \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_TCP_SUP,           \
+                   (_tcp) ? 0 : 1);                                    \
+               EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_HASH_INSRT_HDR,    \
+                   (_insert) ? 1 : 0);                                 \
+               EFX_BAR_WRITEO((_enp), FR_AZ_RX_CFG_REG, &oword);       \
+                                                                       \
+               _NOTE(CONSTANTCONDITION)                                \
+       } while (B_FALSE)
+
+#define        EFX_RX_TOEPLITZ_IPV6_HASH(_enp, _ip, _tcp, _rc)                 \
+       do {                                                            \
+               efx_oword_t oword;                                      \
+                                                                       \
+               EFX_BAR_READO((_enp), FR_CZ_RX_RSS_IPV6_REG3, &oword);  \
+               EFX_SET_OWORD_FIELD(oword,                              \
+                   FRF_CZ_RX_RSS_IPV6_THASH_ENABLE, 1);                \
+               EFX_SET_OWORD_FIELD(oword,                              \
+                   FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE, (_ip) ? 1 : 0); \
+               EFX_SET_OWORD_FIELD(oword,                              \
+                   FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS, (_tcp) ? 0 : 1);   \
+               EFX_BAR_WRITEO((_enp), FR_CZ_RX_RSS_IPV6_REG3, &oword); \
+                                                                       \
+               (_rc) = 0;                                              \
+                                                                       \
+               _NOTE(CONSTANTCONDITION)                                \
+       } while (B_FALSE)
+
+
+/*
+ * Falcon/Siena pseudo-header
+ * --------------------------
+ *
+ * Receive packets are prefixed by an optional 16 byte pseudo-header.
+ * The pseudo-header is a byte array of one of the forms:
+ *
+ *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+ * xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.TT.TT.TT.TT
+ * xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.xx.LL.LL
+ *
+ * where:
+ *   TT.TT.TT.TT   Toeplitz hash (32-bit big-endian)
+ *   LL.LL         LFSR hash     (16-bit big-endian)
+ */
+
+static __checkReturn   efx_rc_t
+siena_rx_prefix_pktlen(
+       __in            efx_nic_t *enp,
+       __in            uint8_t *buffer,
+       __out           uint16_t *lengthp)
+{
+       _NOTE(ARGUNUSED(enp, buffer, lengthp))
+
+       /* Not supported by Falcon/Siena hardware */
+       EFSYS_ASSERT(0);
+       return (ENOTSUP);
+}
+
+
+static                 void
+siena_rx_qpost(
+       __in            efx_rxq_t *erp,
+       __in_ecount(n)  efsys_dma_addr_t *addrp,
+       __in            size_t size,
+       __in            unsigned int n,
+       __in            unsigned int completed,
+       __in            unsigned int added)
+{
+       efx_qword_t qword;
+       unsigned int i;
+       unsigned int offset;
+       unsigned int id;
+
+       /* The client driver must not overfill the queue */
+       EFSYS_ASSERT3U(added - completed + n, <=,
+           EFX_RXQ_LIMIT(erp->er_mask + 1));
+
+       id = added & (erp->er_mask);
+       for (i = 0; i < n; i++) {
+               EFSYS_PROBE4(rx_post, unsigned int, erp->er_index,
+                   unsigned int, id, efsys_dma_addr_t, addrp[i],
+                   size_t, size);
+
+               EFX_POPULATE_QWORD_3(qword,
+                   FSF_AZ_RX_KER_BUF_SIZE, (uint32_t)(size),
+                   FSF_AZ_RX_KER_BUF_ADDR_DW0,
+                   (uint32_t)(addrp[i] & 0xffffffff),
+                   FSF_AZ_RX_KER_BUF_ADDR_DW1,
+                   (uint32_t)(addrp[i] >> 32));
+
+               offset = id * sizeof (efx_qword_t);
+               EFSYS_MEM_WRITEQ(erp->er_esmp, offset, &qword);
+
+               id = (id + 1) & (erp->er_mask);
+       }
+}
+
+static                 void
+siena_rx_qpush(
+       __in    efx_rxq_t *erp,
+       __in    unsigned int added,
+       __inout unsigned int *pushedp)
+{
+       efx_nic_t *enp = erp->er_enp;
+       unsigned int pushed = *pushedp;
+       uint32_t wptr;
+       efx_oword_t oword;
+       efx_dword_t dword;
+
+       /* All descriptors are pushed */
+       *pushedp = added;
+
+       /* Push the populated descriptors out */
+       wptr = added & erp->er_mask;
+
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_RX_DESC_WPTR, wptr);
+
+       /* Only write the third DWORD */
+       EFX_POPULATE_DWORD_1(dword,
+           EFX_DWORD_0, EFX_OWORD_FIELD(oword, EFX_DWORD_3));
+
+       /* Guarantee ordering of memory (descriptors) and PIO (doorbell) */
+       EFX_DMA_SYNC_QUEUE_FOR_DEVICE(erp->er_esmp, erp->er_mask + 1,
+           wptr, pushed & erp->er_mask);
+       EFSYS_PIO_WRITE_BARRIER();
+       EFX_BAR_TBL_WRITED3(enp, FR_BZ_RX_DESC_UPD_REGP0,
+                           erp->er_index, &dword, B_FALSE);
+}
+
+static __checkReturn   efx_rc_t
+siena_rx_qflush(
+       __in    efx_rxq_t *erp)
+{
+       efx_nic_t *enp = erp->er_enp;
+       efx_oword_t oword;
+       uint32_t label;
+
+       label = erp->er_index;
+
+       /* Flush the queue */
+       EFX_POPULATE_OWORD_2(oword, FRF_AZ_RX_FLUSH_DESCQ_CMD, 1,
+           FRF_AZ_RX_FLUSH_DESCQ, label);
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_FLUSH_DESCQ_REG, &oword);
+
+       return (0);
+}
+
+static         void
+siena_rx_qenable(
+       __in    efx_rxq_t *erp)
+{
+       efx_nic_t *enp = erp->er_enp;
+       efx_oword_t oword;
+
+       EFSYS_ASSERT3U(erp->er_magic, ==, EFX_RXQ_MAGIC);
+
+       EFX_BAR_TBL_READO(enp, FR_AZ_RX_DESC_PTR_TBL,
+                           erp->er_index, &oword, B_TRUE);
+
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_RX_DC_HW_RPTR, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_RX_DESCQ_HW_RPTR, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_RX_DESCQ_EN, 1);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_DESC_PTR_TBL,
+                           erp->er_index, &oword, B_TRUE);
+}
+
+static __checkReturn   efx_rc_t
+siena_rx_qcreate(
+       __in            efx_nic_t *enp,
+       __in            unsigned int index,
+       __in            unsigned int label,
+       __in            efx_rxq_type_t type,
+       __in            efsys_mem_t *esmp,
+       __in            size_t n,
+       __in            uint32_t id,
+       __in            efx_evq_t *eep,
+       __in            efx_rxq_t *erp)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_oword_t oword;
+       uint32_t size;
+       boolean_t jumbo;
+       efx_rc_t rc;
+
+       _NOTE(ARGUNUSED(esmp))
+
+       EFX_STATIC_ASSERT(EFX_EV_RX_NLABELS ==
+           (1 << FRF_AZ_RX_DESCQ_LABEL_WIDTH));
+       EFSYS_ASSERT3U(label, <, EFX_EV_RX_NLABELS);
+       EFSYS_ASSERT3U(enp->en_rx_qcount + 1, <, encp->enc_rxq_limit);
+
+       EFX_STATIC_ASSERT(ISP2(EFX_RXQ_MAXNDESCS));
+       EFX_STATIC_ASSERT(ISP2(EFX_RXQ_MINNDESCS));
+
+       if (!ISP2(n) || (n < EFX_RXQ_MINNDESCS) || (n > EFX_RXQ_MAXNDESCS)) {
+               rc = EINVAL;
+               goto fail1;
+       }
+       if (index >= encp->enc_rxq_limit) {
+               rc = EINVAL;
+               goto fail2;
+       }
+       for (size = 0; (1 << size) <= (EFX_RXQ_MAXNDESCS / EFX_RXQ_MINNDESCS);
+           size++)
+               if ((1 << size) == (int)(n / EFX_RXQ_MINNDESCS))
+                       break;
+       if (id + (1 << size) >= encp->enc_buftbl_limit) {
+               rc = EINVAL;
+               goto fail3;
+       }
+
+       switch (type) {
+       case EFX_RXQ_TYPE_DEFAULT:
+               jumbo = B_FALSE;
+               break;
+
+       default:
+               rc = EINVAL;
+               goto fail4;
+       }
+
+       /* Set up the new descriptor queue */
+       EFX_POPULATE_OWORD_7(oword,
+           FRF_AZ_RX_DESCQ_BUF_BASE_ID, id,
+           FRF_AZ_RX_DESCQ_EVQ_ID, eep->ee_index,
+           FRF_AZ_RX_DESCQ_OWNER_ID, 0,
+           FRF_AZ_RX_DESCQ_LABEL, label,
+           FRF_AZ_RX_DESCQ_SIZE, size,
+           FRF_AZ_RX_DESCQ_TYPE, 0,
+           FRF_AZ_RX_DESCQ_JUMBO, jumbo);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_DESC_PTR_TBL,
+                           erp->er_index, &oword, B_TRUE);
+
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static         void
+siena_rx_qdestroy(
+       __in    efx_rxq_t *erp)
+{
+       efx_nic_t *enp = erp->er_enp;
+       efx_oword_t oword;
+
+       EFSYS_ASSERT(enp->en_rx_qcount != 0);
+       --enp->en_rx_qcount;
+
+       /* Purge descriptor queue */
+       EFX_ZERO_OWORD(oword);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_DESC_PTR_TBL,
+                           erp->er_index, &oword, B_TRUE);
+
+       /* Free the RXQ object */
+       EFSYS_KMEM_FREE(enp->en_esip, sizeof (efx_rxq_t), erp);
+}
+
+static         void
+siena_rx_fini(
+       __in    efx_nic_t *enp)
+{
+       _NOTE(ARGUNUSED(enp))
+}
+
+#endif /* EFSYS_OPT_SIENA */
index 4f0099f..7333f0a 100644 (file)
 
 #define        EFX_TX_QSTAT_INCR(_etp, _stat)
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_tx_init(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_tx_fini(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_tx_qcreate(
+       __in            efx_nic_t *enp,
+       __in            unsigned int index,
+       __in            unsigned int label,
+       __in            efsys_mem_t *esmp,
+       __in            size_t n,
+       __in            uint32_t id,
+       __in            uint16_t flags,
+       __in            efx_evq_t *eep,
+       __in            efx_txq_t *etp,
+       __out           unsigned int *addedp);
+
+static         void
+siena_tx_qdestroy(
+       __in    efx_txq_t *etp);
+
+static __checkReturn   efx_rc_t
+siena_tx_qpost(
+       __in            efx_txq_t *etp,
+       __in_ecount(n)  efx_buffer_t *eb,
+       __in            unsigned int n,
+       __in            unsigned int completed,
+       __inout         unsigned int *addedp);
+
+static                 void
+siena_tx_qpush(
+       __in    efx_txq_t *etp,
+       __in    unsigned int added,
+       __in    unsigned int pushed);
+
+static __checkReturn   efx_rc_t
+siena_tx_qpace(
+       __in            efx_txq_t *etp,
+       __in            unsigned int ns);
+
+static __checkReturn   efx_rc_t
+siena_tx_qflush(
+       __in            efx_txq_t *etp);
+
+static                 void
+siena_tx_qenable(
+       __in    efx_txq_t *etp);
+
+       __checkReturn   efx_rc_t
+siena_tx_qdesc_post(
+       __in            efx_txq_t *etp,
+       __in_ecount(n)  efx_desc_t *ed,
+       __in            unsigned int n,
+       __in            unsigned int completed,
+       __inout         unsigned int *addedp);
+
+       void
+siena_tx_qdesc_dma_create(
+       __in    efx_txq_t *etp,
+       __in    efsys_dma_addr_t addr,
+       __in    size_t size,
+       __in    boolean_t eop,
+       __out   efx_desc_t *edp);
+
+#endif /* EFSYS_OPT_SIENA */
+
+
+#if EFSYS_OPT_SIENA
+static const efx_tx_ops_t      __efx_tx_siena_ops = {
+       siena_tx_init,                          /* etxo_init */
+       siena_tx_fini,                          /* etxo_fini */
+       siena_tx_qcreate,                       /* etxo_qcreate */
+       siena_tx_qdestroy,                      /* etxo_qdestroy */
+       siena_tx_qpost,                         /* etxo_qpost */
+       siena_tx_qpush,                         /* etxo_qpush */
+       siena_tx_qpace,                         /* etxo_qpace */
+       siena_tx_qflush,                        /* etxo_qflush */
+       siena_tx_qenable,                       /* etxo_qenable */
+       NULL,                                   /* etxo_qpio_enable */
+       NULL,                                   /* etxo_qpio_disable */
+       NULL,                                   /* etxo_qpio_write */
+       NULL,                                   /* etxo_qpio_post */
+       siena_tx_qdesc_post,                    /* etxo_qdesc_post */
+       siena_tx_qdesc_dma_create,              /* etxo_qdesc_dma_create */
+       NULL,                                   /* etxo_qdesc_tso_create */
+       NULL,                                   /* etxo_qdesc_tso2_create */
+       NULL,                                   /* etxo_qdesc_vlantci_create */
+};
+#endif /* EFSYS_OPT_SIENA */
 
        __checkReturn   efx_rc_t
 efx_tx_init(
@@ -55,6 +150,11 @@ efx_tx_init(
        }
 
        switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               etxop = &__efx_tx_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
 
        default:
                EFSYS_ASSERT(0);
@@ -461,3 +561,391 @@ efx_tx_qdesc_vlantci_create(
 }
 
 
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_tx_init(
+       __in            efx_nic_t *enp)
+{
+       efx_oword_t oword;
+
+       /*
+        * Disable the timer-based TX DMA backoff and allow TX DMA to be
+        * controlled by the RX FIFO fill level (although always allow a
+        * minimal trickle).
+        */
+       EFX_BAR_READO(enp, FR_AZ_TX_RESERVED_REG, &oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_RX_SPACER, 0xfe);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_RX_SPACER_EN, 1);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_ONE_PKT_PER_Q, 1);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_PUSH_EN, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_DIS_NON_IP_EV, 1);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_PREF_THRESHOLD, 2);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_PREF_WD_TMR, 0x3fffff);
+
+       /*
+        * Filter all packets less than 14 bytes to avoid parsing
+        * errors.
+        */
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1);
+       EFX_BAR_WRITEO(enp, FR_AZ_TX_RESERVED_REG, &oword);
+
+       /*
+        * Do not set TX_NO_EOP_DISC_EN, since it limits packets to 16
+        * descriptors (which is bad).
+        */
+       EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_NO_EOP_DISC_EN, 0);
+       EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword);
+
+       return (0);
+}
+
+#define        EFX_TX_DESC(_etp, _addr, _size, _eop, _added)                   \
+       do {                                                            \
+               unsigned int id;                                        \
+               size_t offset;                                          \
+               efx_qword_t qword;                                      \
+                                                                       \
+               id = (_added)++ & (_etp)->et_mask;                      \
+               offset = id * sizeof (efx_qword_t);                     \
+                                                                       \
+               EFSYS_PROBE5(tx_post, unsigned int, (_etp)->et_index,   \
+                   unsigned int, id, efsys_dma_addr_t, (_addr),        \
+                   size_t, (_size), boolean_t, (_eop));                \
+                                                                       \
+               EFX_POPULATE_QWORD_4(qword,                             \
+                   FSF_AZ_TX_KER_CONT, (_eop) ? 0 : 1,                 \
+                   FSF_AZ_TX_KER_BYTE_COUNT, (uint32_t)(_size),        \
+                   FSF_AZ_TX_KER_BUF_ADDR_DW0,                         \
+                   (uint32_t)((_addr) & 0xffffffff),                   \
+                   FSF_AZ_TX_KER_BUF_ADDR_DW1,                         \
+                   (uint32_t)((_addr) >> 32));                         \
+               EFSYS_MEM_WRITEQ((_etp)->et_esmp, offset, &qword);      \
+                                                                       \
+               _NOTE(CONSTANTCONDITION)                                \
+       } while (B_FALSE)
+
+static __checkReturn   efx_rc_t
+siena_tx_qpost(
+       __in            efx_txq_t *etp,
+       __in_ecount(n)  efx_buffer_t *eb,
+       __in            unsigned int n,
+       __in            unsigned int completed,
+       __inout         unsigned int *addedp)
+{
+       unsigned int added = *addedp;
+       unsigned int i;
+       int rc = ENOSPC;
+
+       if (added - completed + n > EFX_TXQ_LIMIT(etp->et_mask + 1))
+               goto fail1;
+
+       for (i = 0; i < n; i++) {
+               efx_buffer_t *ebp = &eb[i];
+               efsys_dma_addr_t start = ebp->eb_addr;
+               size_t size = ebp->eb_size;
+               efsys_dma_addr_t end = start + size;
+
+               /* Fragments must not span 4k boundaries. */
+               EFSYS_ASSERT(P2ROUNDUP(start + 1, 4096) >= end);
+
+               EFX_TX_DESC(etp, start, size, ebp->eb_eop, added);
+       }
+
+       EFX_TX_QSTAT_INCR(etp, TX_POST);
+
+       *addedp = added;
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static         void
+siena_tx_qpush(
+       __in    efx_txq_t *etp,
+       __in    unsigned int added,
+       __in    unsigned int pushed)
+{
+       efx_nic_t *enp = etp->et_enp;
+       uint32_t wptr;
+       efx_dword_t dword;
+       efx_oword_t oword;
+
+       /* Push the populated descriptors out */
+       wptr = added & etp->et_mask;
+
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_TX_DESC_WPTR, wptr);
+
+       /* Only write the third DWORD */
+       EFX_POPULATE_DWORD_1(dword,
+           EFX_DWORD_0, EFX_OWORD_FIELD(oword, EFX_DWORD_3));
+
+       /* Guarantee ordering of memory (descriptors) and PIO (doorbell) */
+       EFX_DMA_SYNC_QUEUE_FOR_DEVICE(etp->et_esmp, etp->et_mask + 1,
+           wptr, pushed & etp->et_mask);
+       EFSYS_PIO_WRITE_BARRIER();
+       EFX_BAR_TBL_WRITED3(enp, FR_BZ_TX_DESC_UPD_REGP0,
+                           etp->et_index, &dword, B_FALSE);
+}
+
+#define        EFX_MAX_PACE_VALUE 20
+
+static __checkReturn   efx_rc_t
+siena_tx_qpace(
+       __in            efx_txq_t *etp,
+       __in            unsigned int ns)
+{
+       efx_nic_t *enp = etp->et_enp;
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_oword_t oword;
+       unsigned int pace_val;
+       unsigned int timer_period;
+       efx_rc_t rc;
+
+       if (ns == 0) {
+               pace_val = 0;
+       } else {
+               /*
+                * The pace_val to write into the table is s.t
+                * ns <= timer_period * (2 ^ pace_val)
+                */
+               timer_period = 104 / encp->enc_clk_mult;
+               for (pace_val = 1; pace_val <= EFX_MAX_PACE_VALUE; pace_val++) {
+                       if ((timer_period << pace_val) >= ns)
+                               break;
+               }
+       }
+       if (pace_val > EFX_MAX_PACE_VALUE) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       /* Update the pacing table */
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_TX_PACE, pace_val);
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_TX_PACE_TBL, etp->et_index,
+           &oword, B_TRUE);
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static __checkReturn   efx_rc_t
+siena_tx_qflush(
+       __in            efx_txq_t *etp)
+{
+       efx_nic_t *enp = etp->et_enp;
+       efx_oword_t oword;
+       uint32_t label;
+
+       efx_tx_qpace(etp, 0);
+
+       label = etp->et_index;
+
+       /* Flush the queue */
+       EFX_POPULATE_OWORD_2(oword, FRF_AZ_TX_FLUSH_DESCQ_CMD, 1,
+           FRF_AZ_TX_FLUSH_DESCQ, label);
+       EFX_BAR_WRITEO(enp, FR_AZ_TX_FLUSH_DESCQ_REG, &oword);
+
+       return (0);
+}
+
+static         void
+siena_tx_qenable(
+       __in    efx_txq_t *etp)
+{
+       efx_nic_t *enp = etp->et_enp;
+       efx_oword_t oword;
+
+       EFX_BAR_TBL_READO(enp, FR_AZ_TX_DESC_PTR_TBL,
+                           etp->et_index, &oword, B_TRUE);
+
+       EFSYS_PROBE5(tx_descq_ptr, unsigned int, etp->et_index,
+           uint32_t, EFX_OWORD_FIELD(oword, EFX_DWORD_3),
+           uint32_t, EFX_OWORD_FIELD(oword, EFX_DWORD_2),
+           uint32_t, EFX_OWORD_FIELD(oword, EFX_DWORD_1),
+           uint32_t, EFX_OWORD_FIELD(oword, EFX_DWORD_0));
+
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_DC_HW_RPTR, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_DESCQ_HW_RPTR, 0);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TX_DESCQ_EN, 1);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_TX_DESC_PTR_TBL,
+                           etp->et_index, &oword, B_TRUE);
+}
+
+static __checkReturn   efx_rc_t
+siena_tx_qcreate(
+       __in            efx_nic_t *enp,
+       __in            unsigned int index,
+       __in            unsigned int label,
+       __in            efsys_mem_t *esmp,
+       __in            size_t n,
+       __in            uint32_t id,
+       __in            uint16_t flags,
+       __in            efx_evq_t *eep,
+       __in            efx_txq_t *etp,
+       __out           unsigned int *addedp)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_oword_t oword;
+       uint32_t size;
+       efx_rc_t rc;
+
+       _NOTE(ARGUNUSED(esmp))
+
+       EFX_STATIC_ASSERT(EFX_EV_TX_NLABELS ==
+           (1 << FRF_AZ_TX_DESCQ_LABEL_WIDTH));
+       EFSYS_ASSERT3U(label, <, EFX_EV_TX_NLABELS);
+
+       EFSYS_ASSERT(ISP2(encp->enc_txq_max_ndescs));
+       EFX_STATIC_ASSERT(ISP2(EFX_TXQ_MINNDESCS));
+
+       if (!ISP2(n) || (n < EFX_TXQ_MINNDESCS) || (n > EFX_EVQ_MAXNEVS)) {
+               rc = EINVAL;
+               goto fail1;
+       }
+       if (index >= encp->enc_txq_limit) {
+               rc = EINVAL;
+               goto fail2;
+       }
+       for (size = 0;
+           (1 << size) <= (int)(encp->enc_txq_max_ndescs / EFX_TXQ_MINNDESCS);
+           size++)
+               if ((1 << size) == (int)(n / EFX_TXQ_MINNDESCS))
+                       break;
+       if (id + (1 << size) >= encp->enc_buftbl_limit) {
+               rc = EINVAL;
+               goto fail3;
+       }
+
+       /* Set up the new descriptor queue */
+       *addedp = 0;
+
+       EFX_POPULATE_OWORD_6(oword,
+           FRF_AZ_TX_DESCQ_BUF_BASE_ID, id,
+           FRF_AZ_TX_DESCQ_EVQ_ID, eep->ee_index,
+           FRF_AZ_TX_DESCQ_OWNER_ID, 0,
+           FRF_AZ_TX_DESCQ_LABEL, label,
+           FRF_AZ_TX_DESCQ_SIZE, size,
+           FRF_AZ_TX_DESCQ_TYPE, 0);
+
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_TX_NON_IP_DROP_DIS, 1);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_TX_IP_CHKSM_DIS,
+           (flags & EFX_TXQ_CKSUM_IPV4) ? 0 : 1);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_TX_TCP_CHKSM_DIS,
+           (flags & EFX_TXQ_CKSUM_TCPUDP) ? 0 : 1);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_TX_DESC_PTR_TBL,
+           etp->et_index, &oword, B_TRUE);
+
+       return (0);
+
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_tx_qdesc_post(
+       __in            efx_txq_t *etp,
+       __in_ecount(n)  efx_desc_t *ed,
+       __in            unsigned int n,
+       __in            unsigned int completed,
+       __inout         unsigned int *addedp)
+{
+       unsigned int added = *addedp;
+       unsigned int i;
+       efx_rc_t rc;
+
+       if (added - completed + n > EFX_TXQ_LIMIT(etp->et_mask + 1)) {
+               rc = ENOSPC;
+               goto fail1;
+       }
+
+       for (i = 0; i < n; i++) {
+               efx_desc_t *edp = &ed[i];
+               unsigned int id;
+               size_t offset;
+
+               id = added++ & etp->et_mask;
+               offset = id * sizeof (efx_desc_t);
+
+               EFSYS_MEM_WRITEQ(etp->et_esmp, offset, &edp->ed_eq);
+       }
+
+       EFSYS_PROBE3(tx_desc_post, unsigned int, etp->et_index,
+                   unsigned int, added, unsigned int, n);
+
+       EFX_TX_QSTAT_INCR(etp, TX_POST);
+
+       *addedp = added;
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
+       void
+siena_tx_qdesc_dma_create(
+       __in    efx_txq_t *etp,
+       __in    efsys_dma_addr_t addr,
+       __in    size_t size,
+       __in    boolean_t eop,
+       __out   efx_desc_t *edp)
+{
+       /* Fragments must not span 4k boundaries. */
+       EFSYS_ASSERT(P2ROUNDUP(addr + 1, 4096) >= addr + size);
+
+       EFSYS_PROBE4(tx_desc_dma_create, unsigned int, etp->et_index,
+                   efsys_dma_addr_t, addr,
+                   size_t, size, boolean_t, eop);
+
+       EFX_POPULATE_QWORD_4(edp->ed_eq,
+                           FSF_AZ_TX_KER_CONT, eop ? 0 : 1,
+                           FSF_AZ_TX_KER_BYTE_COUNT, (uint32_t)size,
+                           FSF_AZ_TX_KER_BUF_ADDR_DW0,
+                           (uint32_t)(addr & 0xffffffff),
+                           FSF_AZ_TX_KER_BUF_ADDR_DW1,
+                           (uint32_t)(addr >> 32));
+}
+
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_SIENA
+
+static         void
+siena_tx_qdestroy(
+       __in    efx_txq_t *etp)
+{
+       efx_nic_t *enp = etp->et_enp;
+       efx_oword_t oword;
+
+       /* Purge descriptor queue */
+       EFX_ZERO_OWORD(oword);
+
+       EFX_BAR_TBL_WRITEO(enp, FR_AZ_TX_DESC_PTR_TBL,
+                           etp->et_index, &oword, B_TRUE);
+}
+
+static         void
+siena_tx_fini(
+       __in    efx_nic_t *enp)
+{
+       _NOTE(ARGUNUSED(enp))
+}
+
+#endif /* EFSYS_OPT_SIENA */
diff --git a/drivers/net/sfc/base/siena_flash.h b/drivers/net/sfc/base/siena_flash.h
new file mode 100644 (file)
index 0000000..e270055
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2007-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#ifndef        _SYS_SIENA_FLASH_H
+#define        _SYS_SIENA_FLASH_H
+
+#pragma pack(1)
+
+/* Fixed locations near the start of flash (which may be in the internal PHY
+ * firmware header) point to the boot header.
+ *
+ * - parsed by MC boot ROM and firmware
+ * - reserved (but not parsed) by PHY firmware
+ * - opaque to driver
+ */
+
+#define        SIENA_MC_BOOT_PHY_FW_HDR_LEN (0x20)
+
+#define        SIENA_MC_BOOT_PTR_LOCATION (0x18)      /* First thing we try to boot */
+#define        SIENA_MC_BOOT_ALT_PTR_LOCATION (0x1c)  /* Alternative if that fails */
+
+#define        SIENA_MC_BOOT_HDR_LEN (0x200)
+
+#define        SIENA_MC_BOOT_MAGIC (0x51E4A001)
+#define        SIENA_MC_BOOT_VERSION (1)
+
+
+/*Structures supporting an arbitrary number of binary blobs in the flash image
+  intended to house code and tables for the satellite cpus*/
+/*thanks to random.org for:*/
+#define        BLOBS_HEADER_MAGIC (0xBDA3BBD4)
+#define        BLOB_HEADER_MAGIC  (0xA1478A91)
+
+typedef struct blobs_hdr_s {                   /* GENERATED BY scripts/genfwdef */
+       efx_dword_t     magic;
+       efx_dword_t     no_of_blobs;
+} blobs_hdr_t;
+
+typedef struct blob_hdr_s {                    /* GENERATED BY scripts/genfwdef */
+       efx_dword_t     magic;
+       efx_dword_t     cpu_type;
+       efx_dword_t     build_variant;
+       efx_dword_t     offset;
+       efx_dword_t     length;
+       efx_dword_t     checksum;
+} blob_hdr_t;
+
+#define        BLOB_CPU_TYPE_TXDI_TEXT (0)
+#define        BLOB_CPU_TYPE_RXDI_TEXT (1)
+#define        BLOB_CPU_TYPE_TXDP_TEXT (2)
+#define        BLOB_CPU_TYPE_RXDP_TEXT (3)
+#define        BLOB_CPU_TYPE_RXHRSL_HR_LUT (4)
+#define        BLOB_CPU_TYPE_RXHRSL_HR_LUT_CFG (5)
+#define        BLOB_CPU_TYPE_TXHRSL_HR_LUT (6)
+#define        BLOB_CPU_TYPE_TXHRSL_HR_LUT_CFG (7)
+#define        BLOB_CPU_TYPE_RXHRSL_HR_PGM  (8)
+#define        BLOB_CPU_TYPE_RXHRSL_SL_PGM  (9)
+#define        BLOB_CPU_TYPE_TXHRSL_HR_PGM  (10)
+#define        BLOB_CPU_TYPE_TXHRSL_SL_PGM  (11)
+#define        BLOB_CPU_TYPE_RXDI_VTBL0 (12)
+#define        BLOB_CPU_TYPE_TXDI_VTBL0 (13)
+#define        BLOB_CPU_TYPE_RXDI_VTBL1 (14)
+#define        BLOB_CPU_TYPE_TXDI_VTBL1 (15)
+#define        BLOB_CPU_TYPE_DUMPSPEC (32)
+#define        BLOB_CPU_TYPE_MC_XIP   (33)
+
+#define        BLOB_CPU_TYPE_INVALID (31)
+
+/*
+ * The upper four bits of the CPU type field specify the compression
+ * algorithm used for this blob.
+ */
+#define        BLOB_COMPRESSION_MASK (0xf0000000)
+#define        BLOB_CPU_TYPE_MASK    (0x0fffffff)
+
+#define        BLOB_COMPRESSION_NONE (0x00000000) /* Stored as is */
+#define        BLOB_COMPRESSION_LZ   (0x10000000) /* see lib/lzdecoder.c */
+
+typedef struct siena_mc_boot_hdr_s {           /* GENERATED BY scripts/genfwdef */
+       efx_dword_t     magic;                  /* = SIENA_MC_BOOT_MAGIC */
+       efx_word_t      hdr_version;            /* this structure definition is version 1 */
+       efx_byte_t      board_type;
+       efx_byte_t      firmware_version_a;
+       efx_byte_t      firmware_version_b;
+       efx_byte_t      firmware_version_c;
+       efx_word_t      checksum;               /* of whole header area + firmware image */
+       efx_word_t      firmware_version_d;
+       efx_byte_t      mcfw_subtype;
+       efx_byte_t      generation;             /* Valid for medford, SBZ for earlier chips */
+       efx_dword_t     firmware_text_offset;   /* offset to firmware .text */
+       efx_dword_t     firmware_text_size;     /* length of firmware .text, in bytes */
+       efx_dword_t     firmware_data_offset;   /* offset to firmware .data */
+       efx_dword_t     firmware_data_size;     /* length of firmware .data, in bytes */
+       efx_byte_t      spi_rate;               /* SPI rate for reading image, 0 is BootROM default */
+       efx_byte_t      spi_phase_adj;          /* SPI SDO/SCL phase adjustment, 0 is default (no adj) */
+       efx_word_t      xpm_sector;             /* The sector that contains the key, or 0xffff if unsigned (medford) SBZ (earlier) */
+       efx_dword_t     reserved_c[7];          /* (set to 0) */
+} siena_mc_boot_hdr_t;
+
+#define        SIENA_MC_BOOT_HDR_PADDING \
+       (SIENA_MC_BOOT_HDR_LEN - sizeof(siena_mc_boot_hdr_t))
+
+#define        SIENA_MC_STATIC_CONFIG_MAGIC (0xBDCF5555)
+#define        SIENA_MC_STATIC_CONFIG_VERSION (0)
+
+typedef struct siena_mc_static_config_hdr_s {  /* GENERATED BY scripts/genfwdef */
+       efx_dword_t     magic;                  /* = SIENA_MC_STATIC_CONFIG_MAGIC */
+       efx_word_t      length;                 /* of header area (i.e. not including VPD) */
+       efx_byte_t      version;
+       efx_byte_t      csum;                   /* over header area (i.e. not including VPD) */
+       efx_dword_t     static_vpd_offset;
+       efx_dword_t     static_vpd_length;
+       efx_dword_t     capabilities;
+       efx_byte_t      mac_addr_base[6];
+       efx_byte_t      green_mode_cal;         /* Green mode calibration result */
+       efx_byte_t      green_mode_valid;       /* Whether cal holds a valid value */
+       efx_word_t      mac_addr_count;
+       efx_word_t      mac_addr_stride;
+       efx_word_t      calibrated_vref;        /* Vref as measured during production */
+       efx_word_t      adc_vref;               /* Vref as read by ADC */
+       efx_dword_t     reserved2[1];           /* (write as zero) */
+       efx_dword_t     num_dbi_items;
+       struct {
+               efx_word_t      addr;
+               efx_word_t      byte_enables;
+               efx_dword_t     value;
+       } dbi[];
+} siena_mc_static_config_hdr_t;
+
+/* This prefixes a valid XIP partition */
+#define XIP_PARTITION_MAGIC (0x51DEC0DE)
+
+#define        SIENA_MC_DYNAMIC_CONFIG_MAGIC (0xBDCFDDDD)
+#define        SIENA_MC_DYNAMIC_CONFIG_VERSION (0)
+
+typedef struct siena_mc_fw_version_s {         /* GENERATED BY scripts/genfwdef */
+       efx_dword_t     fw_subtype;
+       efx_word_t      version_w;
+       efx_word_t      version_x;
+       efx_word_t      version_y;
+       efx_word_t      version_z;
+} siena_mc_fw_version_t;
+
+typedef struct siena_mc_dynamic_config_hdr_s { /* GENERATED BY scripts/genfwdef */
+       efx_dword_t     magic;                  /* = SIENA_MC_DYNAMIC_CONFIG_MAGIC */
+       efx_word_t      length;                 /* of header area (i.e. not including VPD) */
+       efx_byte_t      version;
+       efx_byte_t      csum;                   /* over header area (i.e. not including VPD) */
+       efx_dword_t     dynamic_vpd_offset;
+       efx_dword_t     dynamic_vpd_length;
+       efx_dword_t     num_fw_version_items;
+       siena_mc_fw_version_t   fw_version[];
+} siena_mc_dynamic_config_hdr_t;
+
+#define        SIENA_MC_EXPROM_SINGLE_MAGIC (0xAA55)  /* little-endian uint16_t */
+
+#define        SIENA_MC_EXPROM_COMBO_MAGIC (0xB0070102)  /* little-endian uint32_t */
+#define        SIENA_MC_EXPROM_COMBO_V2_MAGIC (0xB0070103)  /* little-endian uint32_t */
+
+typedef struct siena_mc_combo_rom_hdr_s {      /* GENERATED BY scripts/genfwdef */
+       efx_dword_t     magic;                  /* = SIENA_MC_EXPROM_COMBO_MAGIC or SIENA_MC_EXPROM_COMBO_V2_MAGIC */
+       union           {
+               struct {
+                       efx_dword_t     len1;   /* length of first image */
+                       efx_dword_t     len2;   /* length of second image */
+                       efx_dword_t     off1;   /* offset of first byte to edit to combine images */
+                       efx_dword_t     off2;   /* offset of second byte to edit to combine images */
+                       efx_word_t      infoblk0_off;/* infoblk offset */
+                       efx_word_t      infoblk1_off;/* infoblk offset */
+                       efx_byte_t      infoblk_len;/* length of space reserved for one infoblk structure */
+                       efx_byte_t      reserved[7];/* (set to 0) */
+               } v1;
+               struct {
+                       efx_dword_t     len1;   /* length of first image */
+                       efx_dword_t     len2;   /* length of second image */
+                       efx_dword_t     off1;   /* offset of first byte to edit to combine images */
+                       efx_dword_t     off2;   /* offset of second byte to edit to combine images */
+                       efx_word_t      infoblk_off;/* infoblk start offset */
+                       efx_word_t      infoblk_count;/* infoblk count  */
+                       efx_byte_t      infoblk_len;/* length of space reserved for one infoblk structure */
+                       efx_byte_t      reserved[7];/* (set to 0) */
+               } v2;
+       } data;
+} siena_mc_combo_rom_hdr_t;
+
+#pragma pack()
+
+#endif /* _SYS_SIENA_FLASH_H */
diff --git a/drivers/net/sfc/base/siena_impl.h b/drivers/net/sfc/base/siena_impl.h
new file mode 100644 (file)
index 0000000..2c2a098
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#ifndef _SYS_SIENA_IMPL_H
+#define        _SYS_SIENA_IMPL_H
+
+#include "efx.h"
+#include "efx_regs.h"
+#include "efx_mcdi.h"
+#include "siena_flash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define        SIENA_NVRAM_CHUNK 0x80
+
+extern __checkReturn   efx_rc_t
+siena_nic_probe(
+       __in            efx_nic_t *enp);
+
+extern __checkReturn   efx_rc_t
+siena_nic_reset(
+       __in            efx_nic_t *enp);
+
+extern __checkReturn   efx_rc_t
+siena_nic_init(
+       __in            efx_nic_t *enp);
+
+extern                 void
+siena_nic_fini(
+       __in            efx_nic_t *enp);
+
+extern                 void
+siena_nic_unprobe(
+       __in            efx_nic_t *enp);
+
+#define        SIENA_SRAM_ROWS 0x12000
+
+extern                 void
+siena_sram_init(
+       __in            efx_nic_t *enp);
+
+#if EFSYS_OPT_MCDI
+
+extern __checkReturn   efx_rc_t
+siena_mcdi_init(
+       __in            efx_nic_t *enp,
+       __in            const efx_mcdi_transport_t *mtp);
+
+extern                 void
+siena_mcdi_send_request(
+       __in                    efx_nic_t *enp,
+       __in_bcount(hdr_len)    void *hdrp,
+       __in                    size_t hdr_len,
+       __in_bcount(sdu_len)    void *sdup,
+       __in                    size_t sdu_len);
+
+extern __checkReturn   boolean_t
+siena_mcdi_poll_response(
+       __in            efx_nic_t *enp);
+
+extern                 void
+siena_mcdi_read_response(
+       __in                    efx_nic_t *enp,
+       __out_bcount(length)    void *bufferp,
+       __in                    size_t offset,
+       __in                    size_t length);
+
+extern                 efx_rc_t
+siena_mcdi_poll_reboot(
+       __in            efx_nic_t *enp);
+
+extern                 void
+siena_mcdi_fini(
+       __in            efx_nic_t *enp);
+
+extern __checkReturn   efx_rc_t
+siena_mcdi_feature_supported(
+       __in            efx_nic_t *enp,
+       __in            efx_mcdi_feature_id_t id,
+       __out           boolean_t *supportedp);
+
+extern                 void
+siena_mcdi_get_timeout(
+       __in            efx_nic_t *enp,
+       __in            efx_mcdi_req_t *emrp,
+       __out           uint32_t *timeoutp);
+
+#endif /* EFSYS_OPT_MCDI */
+
+typedef struct siena_link_state_s {
+       uint32_t                sls_adv_cap_mask;
+       uint32_t                sls_lp_cap_mask;
+       unsigned int            sls_fcntl;
+       efx_link_mode_t         sls_link_mode;
+       boolean_t               sls_mac_up;
+} siena_link_state_t;
+
+extern                 void
+siena_phy_link_ev(
+       __in            efx_nic_t *enp,
+       __in            efx_qword_t *eqp,
+       __out           efx_link_mode_t *link_modep);
+
+extern __checkReturn   efx_rc_t
+siena_phy_get_link(
+       __in            efx_nic_t *enp,
+       __out           siena_link_state_t *slsp);
+
+extern __checkReturn   efx_rc_t
+siena_phy_power(
+       __in            efx_nic_t *enp,
+       __in            boolean_t on);
+
+extern __checkReturn   efx_rc_t
+siena_phy_reconfigure(
+       __in            efx_nic_t *enp);
+
+extern __checkReturn   efx_rc_t
+siena_phy_verify(
+       __in            efx_nic_t *enp);
+
+extern __checkReturn   efx_rc_t
+siena_phy_oui_get(
+       __in            efx_nic_t *enp,
+       __out           uint32_t *ouip);
+
+extern __checkReturn   efx_rc_t
+siena_mac_poll(
+       __in            efx_nic_t *enp,
+       __out           efx_link_mode_t *link_modep);
+
+extern __checkReturn   efx_rc_t
+siena_mac_up(
+       __in            efx_nic_t *enp,
+       __out           boolean_t *mac_upp);
+
+extern __checkReturn   efx_rc_t
+siena_mac_reconfigure(
+       __in    efx_nic_t *enp);
+
+extern __checkReturn   efx_rc_t
+siena_mac_pdu_get(
+       __in    efx_nic_t *enp,
+       __out   size_t *pdu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SIENA_IMPL_H */
diff --git a/drivers/net/sfc/base/siena_mac.c b/drivers/net/sfc/base/siena_mac.c
new file mode 100644 (file)
index 0000000..71b0a9a
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2009-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_SIENA
+
+       __checkReturn   efx_rc_t
+siena_mac_poll(
+       __in            efx_nic_t *enp,
+       __out           efx_link_mode_t *link_modep)
+{
+       efx_port_t *epp = &(enp->en_port);
+       siena_link_state_t sls;
+       efx_rc_t rc;
+
+       if ((rc = siena_phy_get_link(enp, &sls)) != 0)
+               goto fail1;
+
+       epp->ep_adv_cap_mask = sls.sls_adv_cap_mask;
+       epp->ep_fcntl = sls.sls_fcntl;
+
+       *link_modep = sls.sls_link_mode;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       *link_modep = EFX_LINK_UNKNOWN;
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_mac_up(
+       __in            efx_nic_t *enp,
+       __out           boolean_t *mac_upp)
+{
+       siena_link_state_t sls;
+       efx_rc_t rc;
+
+       /*
+        * Because Siena doesn't *require* polling, we can't rely on
+        * siena_mac_poll() being executed to populate epp->ep_mac_up.
+        */
+       if ((rc = siena_phy_get_link(enp, &sls)) != 0)
+               goto fail1;
+
+       *mac_upp = sls.sls_mac_up;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_mac_reconfigure(
+       __in            efx_nic_t *enp)
+{
+       efx_port_t *epp = &(enp->en_port);
+       efx_oword_t multicast_hash[2];
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MAX(MC_CMD_SET_MAC_IN_LEN,
+                               MC_CMD_SET_MAC_OUT_LEN),
+                           MAX(MC_CMD_SET_MCAST_HASH_IN_LEN,
+                               MC_CMD_SET_MCAST_HASH_OUT_LEN))];
+       unsigned int fcntl;
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_SET_MAC;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_SET_MAC_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_SET_MAC_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, SET_MAC_IN_MTU, epp->ep_mac_pdu);
+       MCDI_IN_SET_DWORD(req, SET_MAC_IN_DRAIN, epp->ep_mac_drain ? 1 : 0);
+       EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t, SET_MAC_IN_ADDR),
+                           epp->ep_mac_addr);
+       MCDI_IN_POPULATE_DWORD_2(req, SET_MAC_IN_REJECT,
+                           SET_MAC_IN_REJECT_UNCST, !epp->ep_all_unicst,
+                           SET_MAC_IN_REJECT_BRDCST, !epp->ep_brdcst);
+
+       if (epp->ep_fcntl_autoneg)
+               /* efx_fcntl_set() has already set the phy capabilities */
+               fcntl = MC_CMD_FCNTL_AUTO;
+       else if (epp->ep_fcntl & EFX_FCNTL_RESPOND)
+               fcntl = (epp->ep_fcntl & EFX_FCNTL_GENERATE)
+                       ? MC_CMD_FCNTL_BIDIR
+                       : MC_CMD_FCNTL_RESPOND;
+       else
+               fcntl = MC_CMD_FCNTL_OFF;
+
+       MCDI_IN_SET_DWORD(req, SET_MAC_IN_FCNTL, fcntl);
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       /* Push multicast hash */
+
+       if (epp->ep_all_mulcst) {
+               /* A hash matching all multicast is all 1s */
+               EFX_SET_OWORD(multicast_hash[0]);
+               EFX_SET_OWORD(multicast_hash[1]);
+       } else if (epp->ep_mulcst) {
+               /* Use the hash set by the multicast list */
+               multicast_hash[0] = epp->ep_multicst_hash[0];
+               multicast_hash[1] = epp->ep_multicst_hash[1];
+       } else {
+               /* A hash matching no traffic is simply 0 */
+               EFX_ZERO_OWORD(multicast_hash[0]);
+               EFX_ZERO_OWORD(multicast_hash[1]);
+       }
+
+       /*
+        * Broadcast packets go through the multicast hash filter.
+        * The IEEE 802.3 CRC32 of the broadcast address is 0xbe2612ff
+        * so we always add bit 0xff to the mask (bit 0x7f in the
+        * second octword).
+        */
+       if (epp->ep_brdcst) {
+               /*
+                * NOTE: due to constant folding, some of this evaluates
+                * to null expressions, giving E_EXPR_NULL_EFFECT during
+                * lint on Illumos.  No good way to fix this without
+                * explicit coding the individual word/bit setting.
+                * So just suppress lint for this one line.
+                */
+               /* LINTED */
+               EFX_SET_OWORD_BIT(multicast_hash[1], 0x7f);
+       }
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_SET_MCAST_HASH;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_SET_MCAST_HASH_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_SET_MCAST_HASH_OUT_LEN;
+
+       memcpy(MCDI_IN2(req, uint8_t, SET_MCAST_HASH_IN_HASH0),
+           multicast_hash, sizeof (multicast_hash));
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail2;
+       }
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn           efx_rc_t
+siena_mac_pdu_get(
+       __in            efx_nic_t *enp,
+       __out           size_t *pdu)
+{
+       return (ENOTSUP);
+}
+
+#endif /* EFSYS_OPT_SIENA */
diff --git a/drivers/net/sfc/base/siena_mcdi.c b/drivers/net/sfc/base/siena_mcdi.c
new file mode 100644 (file)
index 0000000..63c29fc
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2012-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_SIENA && EFSYS_OPT_MCDI
+
+#define        SIENA_MCDI_PDU(_emip)                   \
+       (((emip)->emi_port == 1)                \
+       ? MC_SMEM_P0_PDU_OFST >> 2              \
+       : MC_SMEM_P1_PDU_OFST >> 2)
+
+#define        SIENA_MCDI_DOORBELL(_emip)              \
+       (((emip)->emi_port == 1)                \
+       ? MC_SMEM_P0_DOORBELL_OFST >> 2         \
+       : MC_SMEM_P1_DOORBELL_OFST >> 2)
+
+#define        SIENA_MCDI_STATUS(_emip)                \
+       (((emip)->emi_port == 1)                \
+       ? MC_SMEM_P0_STATUS_OFST >> 2           \
+       : MC_SMEM_P1_STATUS_OFST >> 2)
+
+
+                       void
+siena_mcdi_send_request(
+       __in                    efx_nic_t *enp,
+       __in_bcount(hdr_len)    void *hdrp,
+       __in                    size_t hdr_len,
+       __in_bcount(sdu_len)    void *sdup,
+       __in                    size_t sdu_len)
+{
+       efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+       efx_dword_t dword;
+       unsigned int pdur;
+       unsigned int dbr;
+       unsigned int pos;
+
+       EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
+
+       EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
+       pdur = SIENA_MCDI_PDU(emip);
+       dbr = SIENA_MCDI_DOORBELL(emip);
+
+       /* Write the header */
+       EFSYS_ASSERT3U(hdr_len, ==, sizeof (efx_dword_t));
+       dword = *(efx_dword_t *)hdrp;
+       EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
+
+       /* Write the payload */
+       for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) {
+               dword = *(efx_dword_t *)((uint8_t *)sdup + pos);
+               EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
+                   pdur + 1 + (pos >> 2), &dword, B_FALSE);
+       }
+
+       /* Ring the doorbell */
+       EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
+       EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
+}
+
+                       efx_rc_t
+siena_mcdi_poll_reboot(
+       __in            efx_nic_t *enp)
+{
+#if 1
+       /*
+        * XXX Bug 25922, bug 26099: This function is not being used
+        * properly.  Until its callers are fixed, it should always
+        * return 0.
+        */
+       _NOTE(ARGUNUSED(enp))
+       return (0);
+#else
+       efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+       unsigned int rebootr;
+       efx_dword_t dword;
+       uint32_t value;
+
+       EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
+       EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
+       rebootr = SIENA_MCDI_STATUS(emip);
+
+       EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
+       value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
+
+       if (value == 0)
+               return (0);
+
+       EFX_ZERO_DWORD(dword);
+       EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
+
+       if (value == MC_STATUS_DWORD_ASSERT)
+               return (EINTR);
+       else
+               return (EIO);
+#endif
+}
+
+extern __checkReturn   boolean_t
+siena_mcdi_poll_response(
+       __in            efx_nic_t *enp)
+{
+       efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+       efx_dword_t hdr;
+       unsigned int pdur;
+
+       EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
+       pdur = SIENA_MCDI_PDU(emip);
+
+       EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
+       return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
+}
+
+                       void
+siena_mcdi_read_response(
+       __in                    efx_nic_t *enp,
+       __out_bcount(length)    void *bufferp,
+       __in                    size_t offset,
+       __in                    size_t length)
+{
+       efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+       unsigned int pdur;
+       unsigned int pos;
+       efx_dword_t data;
+
+       EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
+       pdur = SIENA_MCDI_PDU(emip);
+
+       for (pos = 0; pos < length; pos += sizeof (efx_dword_t)) {
+               EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
+                   pdur + ((offset + pos) >> 2), &data, B_FALSE);
+               memcpy((uint8_t *)bufferp + pos, &data,
+                   MIN(sizeof (data), length - pos));
+       }
+}
+
+       __checkReturn   efx_rc_t
+siena_mcdi_init(
+       __in            efx_nic_t *enp,
+       __in            const efx_mcdi_transport_t *mtp)
+{
+       efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+       efx_oword_t oword;
+       unsigned int portnum;
+       efx_rc_t rc;
+
+       _NOTE(ARGUNUSED(mtp))
+
+       EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
+
+       /* Determine the port number to use for MCDI */
+       EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
+       portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
+
+       if (portnum == 0) {
+               /* Presumably booted from ROM; only MCDI port 1 will work */
+               emip->emi_port = 1;
+       } else if (portnum <= 2) {
+               emip->emi_port = portnum;
+       } else {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       /* Siena BootROM and firmware only support MCDIv1 */
+       emip->emi_max_version = 1;
+
+       /*
+        * Wipe the atomic reboot status so subsequent MCDI requests succeed.
+        * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
+        * assertion handler.
+        */
+       (void) siena_mcdi_poll_reboot(enp);
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+                       void
+siena_mcdi_fini(
+       __in            efx_nic_t *enp)
+{
+       _NOTE(ARGUNUSED(enp))
+}
+
+       __checkReturn   efx_rc_t
+siena_mcdi_feature_supported(
+       __in            efx_nic_t *enp,
+       __in            efx_mcdi_feature_id_t id,
+       __out           boolean_t *supportedp)
+{
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+
+       switch (id) {
+       case EFX_MCDI_FEATURE_FW_UPDATE:
+       case EFX_MCDI_FEATURE_LINK_CONTROL:
+       case EFX_MCDI_FEATURE_MACADDR_CHANGE:
+       case EFX_MCDI_FEATURE_MAC_SPOOFING:
+               *supportedp = B_TRUE;
+               break;
+       default:
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+/* Default timeout for MCDI command processing. */
+#define        SIENA_MCDI_CMD_TIMEOUT_US       (10 * 1000 * 1000)
+
+                       void
+siena_mcdi_get_timeout(
+       __in            efx_nic_t *enp,
+       __in            efx_mcdi_req_t *emrp,
+       __out           uint32_t *timeoutp)
+{
+       _NOTE(ARGUNUSED(enp, emrp))
+
+       *timeoutp = SIENA_MCDI_CMD_TIMEOUT_US;
+}
+
+
+#endif /* EFSYS_OPT_SIENA && EFSYS_OPT_MCDI */
diff --git a/drivers/net/sfc/base/siena_nic.c b/drivers/net/sfc/base/siena_nic.c
new file mode 100644 (file)
index 0000000..7be16dc
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2009-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+#include "mcdi_mon.h"
+
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_board_cfg(
+       __in            efx_nic_t *enp)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       uint8_t mac_addr[6];
+       efx_dword_t capabilities;
+       uint32_t board_type;
+       uint32_t nevq, nrxq, ntxq;
+       efx_rc_t rc;
+
+       /* External port identifier using one-based port numbering */
+       encp->enc_external_port = (uint8_t)enp->en_mcdi.em_emip.emi_port;
+
+       /* Board configuration */
+       if ((rc = efx_mcdi_get_board_cfg(enp, &board_type,
+                   &capabilities, mac_addr)) != 0)
+               goto fail1;
+
+       EFX_MAC_ADDR_COPY(encp->enc_mac_addr, mac_addr);
+
+       encp->enc_board_type = board_type;
+
+       /*
+        * There is no possibility to determine the number of PFs on Siena
+        * by issuing MCDI request, and it is not an easy task to find the
+        * value based on the board type, so 'enc_hw_pf_count' is set to 1
+        */
+       encp->enc_hw_pf_count = 1;
+
+       /* Additional capabilities */
+       encp->enc_clk_mult = 1;
+       if (EFX_DWORD_FIELD(capabilities, MC_CMD_CAPABILITIES_TURBO)) {
+               enp->en_features |= EFX_FEATURE_TURBO;
+
+               if (EFX_DWORD_FIELD(capabilities,
+                       MC_CMD_CAPABILITIES_TURBO_ACTIVE)) {
+                       encp->enc_clk_mult = 2;
+               }
+       }
+
+       encp->enc_evq_timer_quantum_ns =
+               EFX_EVQ_SIENA_TIMER_QUANTUM_NS / encp->enc_clk_mult;
+       encp->enc_evq_timer_max_us = (encp->enc_evq_timer_quantum_ns <<
+               FRF_CZ_TC_TIMER_VAL_WIDTH) / 1000;
+
+       /* When hash header insertion is enabled, Siena inserts 16 bytes */
+       encp->enc_rx_prefix_size = 16;
+
+       /* Alignment for receive packet DMA buffers */
+       encp->enc_rx_buf_align_start = 1;
+       encp->enc_rx_buf_align_end = 1;
+
+       /* Alignment for WPTR updates */
+       encp->enc_rx_push_align = 1;
+
+       /* Resource limits */
+       rc = efx_mcdi_get_resource_limits(enp, &nevq, &nrxq, &ntxq);
+       if (rc != 0) {
+               if (rc != ENOTSUP)
+                       goto fail2;
+
+               nevq = 1024;
+               nrxq = EFX_RXQ_LIMIT_TARGET;
+               ntxq = EFX_TXQ_LIMIT_TARGET;
+       }
+       encp->enc_evq_limit = nevq;
+       encp->enc_rxq_limit = MIN(EFX_RXQ_LIMIT_TARGET, nrxq);
+       encp->enc_txq_limit = MIN(EFX_TXQ_LIMIT_TARGET, ntxq);
+
+       encp->enc_txq_max_ndescs = 4096;
+
+       encp->enc_buftbl_limit = SIENA_SRAM_ROWS -
+           (encp->enc_txq_limit * EFX_TXQ_DC_NDESCS(EFX_TXQ_DC_SIZE)) -
+           (encp->enc_rxq_limit * EFX_RXQ_DC_NDESCS(EFX_RXQ_DC_SIZE));
+
+       encp->enc_hw_tx_insert_vlan_enabled = B_FALSE;
+       encp->enc_fw_assisted_tso_enabled = B_FALSE;
+       encp->enc_fw_assisted_tso_v2_enabled = B_FALSE;
+       encp->enc_fw_assisted_tso_v2_n_contexts = 0;
+       encp->enc_allow_set_mac_with_installed_filters = B_TRUE;
+       encp->enc_rx_packed_stream_supported = B_FALSE;
+       encp->enc_rx_var_packed_stream_supported = B_FALSE;
+
+       /* Siena supports two 10G ports, and 8 lanes of PCIe Gen2 */
+       encp->enc_required_pcie_bandwidth_mbps = 2 * 10000;
+       encp->enc_max_pcie_link_gen = EFX_PCIE_LINK_SPEED_GEN2;
+
+       encp->enc_fw_verified_nvram_update_required = B_FALSE;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static __checkReturn   efx_rc_t
+siena_phy_cfg(
+       __in            efx_nic_t *enp)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_rc_t rc;
+
+       /* Fill out fields in enp->en_port and enp->en_nic_cfg from MCDI */
+       if ((rc = efx_mcdi_get_phy_cfg(enp)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_nic_probe(
+       __in            efx_nic_t *enp)
+{
+       efx_port_t *epp = &(enp->en_port);
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       siena_link_state_t sls;
+       unsigned int mask;
+       efx_oword_t oword;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+
+       /* Test BIU */
+       if ((rc = efx_nic_biu_test(enp)) != 0)
+               goto fail1;
+
+       /* Clear the region register */
+       EFX_POPULATE_OWORD_4(oword,
+           FRF_AZ_ADR_REGION0, 0,
+           FRF_AZ_ADR_REGION1, (1 << 16),
+           FRF_AZ_ADR_REGION2, (2 << 16),
+           FRF_AZ_ADR_REGION3, (3 << 16));
+       EFX_BAR_WRITEO(enp, FR_AZ_ADR_REGION_REG, &oword);
+
+       /* Read clear any assertion state */
+       if ((rc = efx_mcdi_read_assertion(enp)) != 0)
+               goto fail2;
+
+       /* Exit the assertion handler */
+       if ((rc = efx_mcdi_exit_assertion_handler(enp)) != 0)
+               goto fail3;
+
+       /* Wrestle control from the BMC */
+       if ((rc = efx_mcdi_drv_attach(enp, B_TRUE)) != 0)
+               goto fail4;
+
+       if ((rc = siena_board_cfg(enp)) != 0)
+               goto fail5;
+
+       if ((rc = siena_phy_cfg(enp)) != 0)
+               goto fail6;
+
+       /* Obtain the default PHY advertised capabilities */
+       if ((rc = siena_nic_reset(enp)) != 0)
+               goto fail7;
+       if ((rc = siena_phy_get_link(enp, &sls)) != 0)
+               goto fail8;
+       epp->ep_default_adv_cap_mask = sls.sls_adv_cap_mask;
+       epp->ep_adv_cap_mask = sls.sls_adv_cap_mask;
+
+       encp->enc_features = enp->en_features;
+
+       return (0);
+
+fail8:
+       EFSYS_PROBE(fail8);
+fail7:
+       EFSYS_PROBE(fail7);
+fail6:
+       EFSYS_PROBE(fail6);
+fail5:
+       EFSYS_PROBE(fail5);
+fail4:
+       EFSYS_PROBE(fail4);
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_nic_reset(
+       __in            efx_nic_t *enp)
+{
+       efx_mcdi_req_t req;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+
+       /* siena_nic_reset() is called to recover from BADASSERT failures. */
+       if ((rc = efx_mcdi_read_assertion(enp)) != 0)
+               goto fail1;
+       if ((rc = efx_mcdi_exit_assertion_handler(enp)) != 0)
+               goto fail2;
+
+       /*
+        * Bug24908: ENTITY_RESET_IN_LEN is non zero but zero may be supplied
+        * for backwards compatibility with PORT_RESET_IN_LEN.
+        */
+       EFX_STATIC_ASSERT(MC_CMD_ENTITY_RESET_OUT_LEN == 0);
+
+       req.emr_cmd = MC_CMD_ENTITY_RESET;
+       req.emr_in_buf = NULL;
+       req.emr_in_length = 0;
+       req.emr_out_buf = NULL;
+       req.emr_out_length = 0;
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail3;
+       }
+
+       return (0);
+
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (0);
+}
+
+static                 void
+siena_nic_rx_cfg(
+       __in            efx_nic_t *enp)
+{
+       efx_oword_t oword;
+
+       /*
+        * RX_INGR_EN is always enabled on Siena, because we rely on
+        * the RX parser to be resiliant to missing SOP/EOP.
+        */
+       EFX_BAR_READO(enp, FR_AZ_RX_CFG_REG, &oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_BZ_RX_INGR_EN, 1);
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_CFG_REG, &oword);
+
+       /* Disable parsing of additional 802.1Q in Q packets */
+       EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
+       EFX_SET_OWORD_FIELD(oword, FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES, 0);
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
+}
+
+static                 void
+siena_nic_usrev_dis(
+       __in            efx_nic_t *enp)
+{
+       efx_oword_t     oword;
+
+       EFX_POPULATE_OWORD_1(oword, FRF_CZ_USREV_DIS, 1);
+       EFX_BAR_WRITEO(enp, FR_CZ_USR_EV_CFG, &oword);
+}
+
+       __checkReturn   efx_rc_t
+siena_nic_init(
+       __in            efx_nic_t *enp)
+{
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+
+       /* Enable reporting of some events (e.g. link change) */
+       if ((rc = efx_mcdi_log_ctrl(enp)) != 0)
+               goto fail1;
+
+       siena_sram_init(enp);
+
+       /* Configure Siena's RX block */
+       siena_nic_rx_cfg(enp);
+
+       /* Disable USR_EVents for now */
+       siena_nic_usrev_dis(enp);
+
+       /* bug17057: Ensure set_link is called */
+       if ((rc = siena_phy_reconfigure(enp)) != 0)
+               goto fail2;
+
+       enp->en_nic_cfg.enc_mcdi_max_payload_length = MCDI_CTL_SDU_LEN_MAX_V1;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+                       void
+siena_nic_fini(
+       __in            efx_nic_t *enp)
+{
+       _NOTE(ARGUNUSED(enp))
+}
+
+                       void
+siena_nic_unprobe(
+       __in            efx_nic_t *enp)
+{
+       (void) efx_mcdi_drv_attach(enp, B_FALSE);
+}
+
+#endif /* EFSYS_OPT_SIENA */
diff --git a/drivers/net/sfc/base/siena_phy.c b/drivers/net/sfc/base/siena_phy.c
new file mode 100644 (file)
index 0000000..0e3fc34
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2009-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_SIENA
+
+static                 void
+siena_phy_decode_cap(
+       __in            uint32_t mcdi_cap,
+       __out           uint32_t *maskp)
+{
+       uint32_t mask;
+
+       mask = 0;
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN))
+               mask |= (1 << EFX_PHY_CAP_10HDX);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN))
+               mask |= (1 << EFX_PHY_CAP_10FDX);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN))
+               mask |= (1 << EFX_PHY_CAP_100HDX);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN))
+               mask |= (1 << EFX_PHY_CAP_100FDX);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN))
+               mask |= (1 << EFX_PHY_CAP_1000HDX);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
+               mask |= (1 << EFX_PHY_CAP_1000FDX);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
+               mask |= (1 << EFX_PHY_CAP_10000FDX);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
+               mask |= (1 << EFX_PHY_CAP_PAUSE);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
+               mask |= (1 << EFX_PHY_CAP_ASYM);
+       if (mcdi_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
+               mask |= (1 << EFX_PHY_CAP_AN);
+
+       *maskp = mask;
+}
+
+static                 void
+siena_phy_decode_link_mode(
+       __in            efx_nic_t *enp,
+       __in            uint32_t link_flags,
+       __in            unsigned int speed,
+       __in            unsigned int fcntl,
+       __out           efx_link_mode_t *link_modep,
+       __out           unsigned int *fcntlp)
+{
+       boolean_t fd = !!(link_flags &
+                   (1 << MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN));
+       boolean_t up = !!(link_flags &
+                   (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN));
+
+       _NOTE(ARGUNUSED(enp))
+
+       if (!up)
+               *link_modep = EFX_LINK_DOWN;
+       else if (speed == 10000 && fd)
+               *link_modep = EFX_LINK_10000FDX;
+       else if (speed == 1000)
+               *link_modep = fd ? EFX_LINK_1000FDX : EFX_LINK_1000HDX;
+       else if (speed == 100)
+               *link_modep = fd ? EFX_LINK_100FDX : EFX_LINK_100HDX;
+       else if (speed == 10)
+               *link_modep = fd ? EFX_LINK_10FDX : EFX_LINK_10HDX;
+       else
+               *link_modep = EFX_LINK_UNKNOWN;
+
+       if (fcntl == MC_CMD_FCNTL_OFF)
+               *fcntlp = 0;
+       else if (fcntl == MC_CMD_FCNTL_RESPOND)
+               *fcntlp = EFX_FCNTL_RESPOND;
+       else if (fcntl == MC_CMD_FCNTL_BIDIR)
+               *fcntlp = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
+       else {
+               EFSYS_PROBE1(mc_pcol_error, int, fcntl);
+               *fcntlp = 0;
+       }
+}
+
+                       void
+siena_phy_link_ev(
+       __in            efx_nic_t *enp,
+       __in            efx_qword_t *eqp,
+       __out           efx_link_mode_t *link_modep)
+{
+       efx_port_t *epp = &(enp->en_port);
+       unsigned int link_flags;
+       unsigned int speed;
+       unsigned int fcntl;
+       efx_link_mode_t link_mode;
+       uint32_t lp_cap_mask;
+
+       /*
+        * Convert the LINKCHANGE speed enumeration into mbit/s, in the
+        * same way as GET_LINK encodes the speed
+        */
+       switch (MCDI_EV_FIELD(eqp, LINKCHANGE_SPEED)) {
+       case MCDI_EVENT_LINKCHANGE_SPEED_100M:
+               speed = 100;
+               break;
+       case MCDI_EVENT_LINKCHANGE_SPEED_1G:
+               speed = 1000;
+               break;
+       case MCDI_EVENT_LINKCHANGE_SPEED_10G:
+               speed = 10000;
+               break;
+       default:
+               speed = 0;
+               break;
+       }
+
+       link_flags = MCDI_EV_FIELD(eqp, LINKCHANGE_LINK_FLAGS);
+       siena_phy_decode_link_mode(enp, link_flags, speed,
+                                   MCDI_EV_FIELD(eqp, LINKCHANGE_FCNTL),
+                                   &link_mode, &fcntl);
+       siena_phy_decode_cap(MCDI_EV_FIELD(eqp, LINKCHANGE_LP_CAP),
+                           &lp_cap_mask);
+
+       /*
+        * It's safe to update ep_lp_cap_mask without the driver's port lock
+        * because presumably any concurrently running efx_port_poll() is
+        * only going to arrive at the same value.
+        *
+        * ep_fcntl has two meanings. It's either the link common fcntl
+        * (if the PHY supports AN), or it's the forced link state. If
+        * the former, it's safe to update the value for the same reason as
+        * for ep_lp_cap_mask. If the latter, then just ignore the value,
+        * because we can race with efx_mac_fcntl_set().
+        */
+       epp->ep_lp_cap_mask = lp_cap_mask;
+       if (epp->ep_phy_cap_mask & (1 << EFX_PHY_CAP_AN))
+               epp->ep_fcntl = fcntl;
+
+       *link_modep = link_mode;
+}
+
+       __checkReturn   efx_rc_t
+siena_phy_power(
+       __in            efx_nic_t *enp,
+       __in            boolean_t power)
+{
+       efx_rc_t rc;
+
+       if (!power)
+               return (0);
+
+       /* Check if the PHY is a zombie */
+       if ((rc = siena_phy_verify(enp)) != 0)
+               goto fail1;
+
+       enp->en_reset_flags |= EFX_RESET_PHY;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_phy_get_link(
+       __in            efx_nic_t *enp,
+       __out           siena_link_state_t *slsp)
+{
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_GET_LINK_IN_LEN,
+                           MC_CMD_GET_LINK_OUT_LEN)];
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_GET_LINK;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_LINK_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_LINK_OUT_LEN;
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_LEN) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       siena_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_CAP),
+                           &slsp->sls_adv_cap_mask);
+       siena_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_LP_CAP),
+                           &slsp->sls_lp_cap_mask);
+
+       siena_phy_decode_link_mode(enp, MCDI_OUT_DWORD(req, GET_LINK_OUT_FLAGS),
+                           MCDI_OUT_DWORD(req, GET_LINK_OUT_LINK_SPEED),
+                           MCDI_OUT_DWORD(req, GET_LINK_OUT_FCNTL),
+                           &slsp->sls_link_mode, &slsp->sls_fcntl);
+
+       slsp->sls_mac_up = MCDI_OUT_DWORD(req, GET_LINK_OUT_MAC_FAULT) == 0;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_phy_reconfigure(
+       __in            efx_nic_t *enp)
+{
+       efx_port_t *epp = &(enp->en_port);
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MAX(MC_CMD_SET_ID_LED_IN_LEN,
+                               MC_CMD_SET_ID_LED_OUT_LEN),
+                           MAX(MC_CMD_SET_LINK_IN_LEN,
+                               MC_CMD_SET_LINK_OUT_LEN))];
+       uint32_t cap_mask;
+       unsigned int led_mode;
+       unsigned int speed;
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_SET_LINK;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_SET_LINK_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_SET_LINK_OUT_LEN;
+
+       cap_mask = epp->ep_adv_cap_mask;
+       MCDI_IN_POPULATE_DWORD_10(req, SET_LINK_IN_CAP,
+               PHY_CAP_10HDX, (cap_mask >> EFX_PHY_CAP_10HDX) & 0x1,
+               PHY_CAP_10FDX, (cap_mask >> EFX_PHY_CAP_10FDX) & 0x1,
+               PHY_CAP_100HDX, (cap_mask >> EFX_PHY_CAP_100HDX) & 0x1,
+               PHY_CAP_100FDX, (cap_mask >> EFX_PHY_CAP_100FDX) & 0x1,
+               PHY_CAP_1000HDX, (cap_mask >> EFX_PHY_CAP_1000HDX) & 0x1,
+               PHY_CAP_1000FDX, (cap_mask >> EFX_PHY_CAP_1000FDX) & 0x1,
+               PHY_CAP_10000FDX, (cap_mask >> EFX_PHY_CAP_10000FDX) & 0x1,
+               PHY_CAP_PAUSE, (cap_mask >> EFX_PHY_CAP_PAUSE) & 0x1,
+               PHY_CAP_ASYM, (cap_mask >> EFX_PHY_CAP_ASYM) & 0x1,
+               PHY_CAP_AN, (cap_mask >> EFX_PHY_CAP_AN) & 0x1);
+
+       MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE, MC_CMD_LOOPBACK_NONE);
+       speed = 0;
+       MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_SPEED, speed);
+
+       MCDI_IN_SET_DWORD(req, SET_LINK_IN_FLAGS, 0);
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       /* And set the blink mode */
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_SET_ID_LED;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_SET_ID_LED_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_SET_ID_LED_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, SET_ID_LED_IN_STATE, MC_CMD_LED_DEFAULT);
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail2;
+       }
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_phy_verify(
+       __in            efx_nic_t *enp)
+{
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_GET_PHY_STATE_IN_LEN,
+                           MC_CMD_GET_PHY_STATE_OUT_LEN)];
+       uint32_t state;
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_GET_PHY_STATE;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_PHY_STATE_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_PHY_STATE_OUT_LEN;
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (req.emr_out_length_used < MC_CMD_GET_PHY_STATE_OUT_LEN) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       state = MCDI_OUT_DWORD(req, GET_PHY_STATE_OUT_STATE);
+       if (state != MC_CMD_PHY_STATE_OK) {
+               if (state != MC_CMD_PHY_STATE_ZOMBIE)
+                       EFSYS_PROBE1(mc_pcol_error, int, state);
+               rc = ENOTACTIVE;
+               goto fail3;
+       }
+
+       return (0);
+
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+siena_phy_oui_get(
+       __in            efx_nic_t *enp,
+       __out           uint32_t *ouip)
+{
+       _NOTE(ARGUNUSED(enp, ouip))
+
+       return (ENOTSUP);
+}
+
+#endif /* EFSYS_OPT_SIENA */
diff --git a/drivers/net/sfc/base/siena_sram.c b/drivers/net/sfc/base/siena_sram.c
new file mode 100644 (file)
index 0000000..411ef9d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2009-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_SIENA
+
+                       void
+siena_sram_init(
+       __in            efx_nic_t *enp)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_oword_t oword;
+       uint32_t rx_base, tx_base;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
+
+       rx_base = encp->enc_buftbl_limit;
+       tx_base = rx_base + (encp->enc_rxq_limit *
+           EFX_RXQ_DC_NDESCS(EFX_RXQ_DC_SIZE));
+
+       /* Initialize the transmit descriptor cache */
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_SRM_TX_DC_BASE_ADR, tx_base);
+       EFX_BAR_WRITEO(enp, FR_AZ_SRM_TX_DC_CFG_REG, &oword);
+
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_TX_DC_SIZE, EFX_TXQ_DC_SIZE);
+       EFX_BAR_WRITEO(enp, FR_AZ_TX_DC_CFG_REG, &oword);
+
+       /* Initialize the receive descriptor cache */
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_SRM_RX_DC_BASE_ADR, rx_base);
+       EFX_BAR_WRITEO(enp, FR_AZ_SRM_RX_DC_CFG_REG, &oword);
+
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_RX_DC_SIZE, EFX_RXQ_DC_SIZE);
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_DC_CFG_REG, &oword);
+
+       /* Set receive descriptor pre-fetch low water mark */
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_RX_DC_PF_LWM, 56);
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_DC_PF_WM_REG, &oword);
+
+       /* Set the event queue to use for SRAM updates */
+       EFX_POPULATE_OWORD_1(oword, FRF_AZ_SRM_UPD_EVQ_ID, 0);
+       EFX_BAR_WRITEO(enp, FR_AZ_SRM_UPD_EVQ_REG, &oword);
+}
+
+#endif /* EFSYS_OPT_SIENA */