Author: emaste
Date: Wed Nov  6 19:51:40 2019
New Revision: 354410
URL: https://svnweb.freebsd.org/changeset/base/354410

Log:
  Introduce if_mgb driver for Microchip LAN743x PCIe NIC
  
  The Microchip LAN7430 is a PCIe 10/100/1000 Ethernet MAC with integrated
  PHY, and the LAN7431 is a MAC with RGMII interface.
  
  To be connected to the build after further testing and review.
  Committing now so that changes like r354345 (adding a common
  ETHER_IS_ZERO macro) will update this driver too.
  
  Submitted by: Gerald ND Aryeetey <aryeeteygerald_rogers.com>
  Sponsored by: The FreeBSD Foundation
  Differential Revision:        https://reviews.freebsd.org/D20079

Added:
  head/sys/dev/mgb/
  head/sys/dev/mgb/if_mgb.c   (contents, props changed)
  head/sys/dev/mgb/if_mgb.h   (contents, props changed)
  head/sys/modules/mgb/
  head/sys/modules/mgb/Makefile   (contents, props changed)

Added: head/sys/dev/mgb/if_mgb.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/mgb/if_mgb.c   Wed Nov  6 19:51:40 2019        (r354410)
@@ -0,0 +1,1634 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation, Inc.
+ *
+ * This driver was written by Gerald ND Aryeetey <gndar...@uwaterloo.ca>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Microchip LAN7430/LAN7431 PCIe to Gigabit Ethernet Controller driver.
+ *
+ * Product information:
+ * LAN7430 https://www.microchip.com/wwwproducts/en/LAN7430
+ *   - Integrated IEEE 802.3 compliant PHY
+ * LAN7431 https://www.microchip.com/wwwproducts/en/LAN7431
+ *   - RGMII Interface
+ *
+ * This driver uses the iflib interface and the default 'ukphy' PHY driver.
+ *
+ * UNIMPLEMENTED FEATURES
+ * ----------------------
+ * A number of features supported by LAN743X device are not yet implemented in
+ * this driver:
+ *
+ * - Multiple (up to 4) RX queues support
+ *   - Just needs to remove asserts and malloc multiple `rx_ring_data`
+ *     structs based on ncpus.
+ * - RX/TX Checksum Offloading support
+ * - VLAN support
+ * - Recieve Packet Filtering (Multicast Perfect/Hash Address) support
+ * - Wake on LAN (WoL) support
+ * - TX LSO support
+ * - Recieve Side Scaling (RSS) support
+ * - Debugging Capabilities:
+ *   - Could include MAC statistics and
+ *     error status registers in sysctl.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/iflib.h>
+
+#include <dev/mgb/if_mgb.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include "ifdi_if.h"
+#include "miibus_if.h"
+
+static pci_vendor_info_t mgb_vendor_info_array[] = {
+       PVID(MGB_MICROCHIP_VENDOR_ID, MGB_LAN7430_DEVICE_ID,
+           "Microchip LAN7430 PCIe Gigabit Ethernet Controller"),
+       PVID(MGB_MICROCHIP_VENDOR_ID, MGB_LAN7431_DEVICE_ID,
+           "Microchip LAN7431 PCIe Gigabit Ethernet Controller"),
+       PVID_END
+};
+
+/* Device methods */
+static device_register_t               mgb_register;
+
+/* IFLIB methods */
+static ifdi_attach_pre_t               mgb_attach_pre;
+static ifdi_attach_post_t              mgb_attach_post;
+static ifdi_detach_t                   mgb_detach;
+
+static ifdi_tx_queues_alloc_t          mgb_tx_queues_alloc;
+static ifdi_rx_queues_alloc_t          mgb_rx_queues_alloc;
+static ifdi_queues_free_t              mgb_queues_free;
+
+static ifdi_init_t                     mgb_init;
+static ifdi_stop_t                     mgb_stop;
+
+static ifdi_msix_intr_assign_t         mgb_msix_intr_assign;
+static ifdi_tx_queue_intr_enable_t     mgb_tx_queue_intr_enable;
+static ifdi_rx_queue_intr_enable_t     mgb_rx_queue_intr_enable;
+static ifdi_intr_enable_t              mgb_intr_enable_all;
+static ifdi_intr_disable_t             mgb_intr_disable_all;
+
+/* IFLIB_TXRX methods */
+static int                             mgb_isc_txd_encap(void *,
+                                           if_pkt_info_t);
+static void                            mgb_isc_txd_flush(void *,
+                                           uint16_t, qidx_t);
+static int                             mgb_isc_txd_credits_update(void *,
+                                           uint16_t, bool);
+static int                             mgb_isc_rxd_available(void *,
+                                           uint16_t, qidx_t, qidx_t);
+static int                             mgb_isc_rxd_pkt_get(void *,
+                                           if_rxd_info_t);
+static void                            mgb_isc_rxd_refill(void *,
+                                           if_rxd_update_t);
+static void                            mgb_isc_rxd_flush(void *,
+                                           uint16_t, uint8_t, qidx_t);
+
+/* Interrupts */
+static driver_filter_t                 mgb_legacy_intr;
+static driver_filter_t                 mgb_admin_intr;
+static driver_filter_t                 mgb_rxq_intr;
+static bool                            mgb_intr_test(struct mgb_softc *);
+
+/* MII methods */
+static miibus_readreg_t                        mgb_miibus_readreg;
+static miibus_writereg_t               mgb_miibus_writereg;
+static miibus_linkchg_t                        mgb_miibus_linkchg;
+static miibus_statchg_t                        mgb_miibus_statchg;
+
+static int                             mgb_media_change(if_t);
+static void                            mgb_media_status(if_t,
+                                           struct ifmediareq *);
+
+/* Helper/Test functions */
+static int                             mgb_test_bar(struct mgb_softc *);
+static int                             mgb_alloc_regs(struct mgb_softc *);
+static int                             mgb_release_regs(struct mgb_softc *);
+
+static void                            mgb_get_ethaddr(struct mgb_softc *,
+                                           struct ether_addr *);
+
+static int                             mgb_wait_for_bits(struct mgb_softc *,
+                                           int, int, int);
+
+/* H/W init, reset and teardown helpers */
+static int                             mgb_hw_init(struct mgb_softc *);
+static int                             mgb_hw_teardown(struct mgb_softc *);
+static int                             mgb_hw_reset(struct mgb_softc *);
+static int                             mgb_mac_init(struct mgb_softc *);
+static int                             mgb_dmac_reset(struct mgb_softc *);
+static int                             mgb_phy_reset(struct mgb_softc *);
+
+static int                             mgb_dma_init(struct mgb_softc *);
+static int                             mgb_dma_tx_ring_init(struct mgb_softc *,
+                                           int);
+static int                             mgb_dma_rx_ring_init(struct mgb_softc *,
+                                           int);
+
+static int                             mgb_dmac_control(struct mgb_softc *,
+                                           int, int, enum mgb_dmac_cmd);
+static int                             mgb_fct_control(struct mgb_softc *,
+                                           int, int, enum mgb_fct_cmd);
+
+/*********************************************************************
+ *  FreeBSD Device Interface Entry Points
+ *********************************************************************/
+
+static device_method_t mgb_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_register,      mgb_register),
+       DEVMETHOD(device_probe,         iflib_device_probe),
+       DEVMETHOD(device_attach,        iflib_device_attach),
+       DEVMETHOD(device_detach,        iflib_device_detach),
+       DEVMETHOD(device_shutdown,      iflib_device_shutdown),
+       DEVMETHOD(device_suspend,       iflib_device_suspend),
+       DEVMETHOD(device_resume,        iflib_device_resume),
+
+       /* MII Interface */
+       DEVMETHOD(miibus_readreg,       mgb_miibus_readreg),
+       DEVMETHOD(miibus_writereg,      mgb_miibus_writereg),
+       DEVMETHOD(miibus_linkchg,       mgb_miibus_linkchg),
+       DEVMETHOD(miibus_statchg,       mgb_miibus_statchg),
+
+       DEVMETHOD_END
+};
+
+static driver_t mgb_driver = {
+       "mgb", mgb_methods, sizeof(struct mgb_softc)
+};
+
+devclass_t mgb_devclass;
+DRIVER_MODULE(mgb, pci, mgb_driver, mgb_devclass, NULL, NULL);
+IFLIB_PNP_INFO(pci, mgb, mgb_vendor_info_array);
+MODULE_VERSION(mgb, 1);
+
+#if 0 /* MIIBUS_DEBUG */
+/* If MIIBUS debug stuff is in attach then order matters. Use below instead. */
+DRIVER_MODULE_ORDERED(miibus, mgb, miibus_driver, miibus_devclass, NULL, NULL,
+    SI_ORDER_ANY);
+#endif /* MIIBUS_DEBUG */
+DRIVER_MODULE(miibus, mgb, miibus_driver, miibus_devclass, NULL, NULL);
+
+MODULE_DEPEND(mgb, pci, 1, 1, 1);
+MODULE_DEPEND(mgb, ether, 1, 1, 1);
+MODULE_DEPEND(mgb, miibus, 1, 1, 1);
+MODULE_DEPEND(mgb, iflib, 1, 1, 1);
+
+static device_method_t mgb_iflib_methods[] = {
+       DEVMETHOD(ifdi_attach_pre, mgb_attach_pre),
+       DEVMETHOD(ifdi_attach_post, mgb_attach_post),
+       DEVMETHOD(ifdi_detach, mgb_detach),
+
+       DEVMETHOD(ifdi_init, mgb_init),
+       DEVMETHOD(ifdi_stop, mgb_stop),
+
+       DEVMETHOD(ifdi_tx_queues_alloc, mgb_tx_queues_alloc),
+       DEVMETHOD(ifdi_rx_queues_alloc, mgb_rx_queues_alloc),
+       DEVMETHOD(ifdi_queues_free, mgb_queues_free),
+
+       DEVMETHOD(ifdi_msix_intr_assign, mgb_msix_intr_assign),
+       DEVMETHOD(ifdi_tx_queue_intr_enable, mgb_tx_queue_intr_enable),
+       DEVMETHOD(ifdi_rx_queue_intr_enable, mgb_rx_queue_intr_enable),
+       DEVMETHOD(ifdi_intr_enable, mgb_intr_enable_all),
+       DEVMETHOD(ifdi_intr_disable, mgb_intr_disable_all),
+
+
+#if 0 /* Not yet implemented IFLIB methods */
+       /*
+        * Set multicast addresses, mtu and promiscuous mode
+        */
+       DEVMETHOD(ifdi_multi_set, mgb_multi_set),
+       DEVMETHOD(ifdi_mtu_set, mgb_mtu_set),
+       DEVMETHOD(ifdi_promisc_set, mgb_promisc_set),
+
+       /*
+        * Needed for VLAN support
+        */
+       DEVMETHOD(ifdi_vlan_register, mgb_vlan_register),
+       DEVMETHOD(ifdi_vlan_unregister, mgb_vlan_unregister),
+
+       /*
+        * Needed for WOL support
+        * at the very least.
+        */
+       DEVMETHOD(ifdi_shutdown, mgb_shutdown),
+       DEVMETHOD(ifdi_suspend, mgb_suspend),
+       DEVMETHOD(ifdi_resume, mgb_resume),
+#endif /* UNUSED_IFLIB_METHODS */
+       DEVMETHOD_END
+};
+
+static driver_t mgb_iflib_driver = {
+       "mgb", mgb_iflib_methods, sizeof(struct mgb_softc)
+};
+
+struct if_txrx mgb_txrx  = {
+       .ift_txd_encap = mgb_isc_txd_encap,
+       .ift_txd_flush = mgb_isc_txd_flush,
+       .ift_txd_credits_update = mgb_isc_txd_credits_update,
+       .ift_rxd_available = mgb_isc_rxd_available,
+       .ift_rxd_pkt_get = mgb_isc_rxd_pkt_get,
+       .ift_rxd_refill = mgb_isc_rxd_refill,
+       .ift_rxd_flush = mgb_isc_rxd_flush,
+
+       .ift_legacy_intr = mgb_legacy_intr
+};
+
+struct if_shared_ctx mgb_sctx_init = {
+       .isc_magic = IFLIB_MAGIC,
+
+       .isc_q_align = PAGE_SIZE,
+       .isc_admin_intrcnt = 1,
+       .isc_flags = IFLIB_DRIVER_MEDIA /* | IFLIB_HAS_RXCQ | IFLIB_HAS_TXCQ*/,
+
+       .isc_vendor_info = mgb_vendor_info_array,
+       .isc_driver_version = "1",
+       .isc_driver = &mgb_iflib_driver,
+       /* 2 queues per set for TX and RX (ring queue, head writeback queue) */
+       .isc_ntxqs = 2,
+
+       .isc_tx_maxsize = MGB_DMA_MAXSEGS  * MCLBYTES,
+       /* .isc_tx_nsegments = MGB_DMA_MAXSEGS, */
+       .isc_tx_maxsegsize = MCLBYTES,
+
+       .isc_ntxd_min = {1, 1}, /* Will want to make this bigger */
+       .isc_ntxd_max = {MGB_DMA_RING_SIZE, 1},
+       .isc_ntxd_default = {MGB_DMA_RING_SIZE, 1},
+
+       .isc_nrxqs = 2,
+
+       .isc_rx_maxsize = MCLBYTES,
+       .isc_rx_nsegments = 1,
+       .isc_rx_maxsegsize = MCLBYTES,
+
+       .isc_nrxd_min = {1, 1}, /* Will want to make this bigger */
+       .isc_nrxd_max = {MGB_DMA_RING_SIZE, 1},
+       .isc_nrxd_default = {MGB_DMA_RING_SIZE, 1},
+
+       .isc_nfl = 1, /*one free list since there is only one queue */
+#if 0 /* UNUSED_CTX */
+
+       .isc_tso_maxsize = MGB_TSO_MAXSIZE + sizeof(struct ether_vlan_header),
+       .isc_tso_maxsegsize = MGB_TX_MAXSEGSIZE,
+#endif /* UNUSED_CTX */
+};
+
+/*********************************************************************/
+
+
+static void *
+mgb_register(device_t dev)
+{
+
+       return (&mgb_sctx_init);
+}
+
+static int
+mgb_attach_pre(if_ctx_t ctx)
+{
+       struct mgb_softc *sc;
+       if_softc_ctx_t scctx;
+       int error, phyaddr, rid;
+       struct ether_addr hwaddr;
+       struct mii_data *miid;
+
+       sc = iflib_get_softc(ctx);
+       sc->ctx = ctx;
+       sc->dev = iflib_get_dev(ctx);
+       scctx = iflib_get_softc_ctx(ctx);
+
+       /* IFLIB required setup */
+       scctx->isc_txrx = &mgb_txrx;
+       scctx->isc_tx_nsegments = MGB_DMA_MAXSEGS;
+       /* Ring desc queues */
+       scctx->isc_txqsizes[0] = sizeof(struct mgb_ring_desc) *
+           scctx->isc_ntxd[0];
+       scctx->isc_rxqsizes[0] = sizeof(struct mgb_ring_desc) *
+           scctx->isc_nrxd[0];
+
+       /* Head WB queues */
+       scctx->isc_txqsizes[1] = sizeof(uint32_t) * scctx->isc_ntxd[1];
+       scctx->isc_rxqsizes[1] = sizeof(uint32_t) * scctx->isc_nrxd[1];
+
+       /* XXX: Must have 1 txqset, but can have up to 4 rxqsets */
+       scctx->isc_nrxqsets = 1;
+       scctx->isc_ntxqsets = 1;
+
+       /* scctx->isc_tx_csum_flags = (CSUM_TCP | CSUM_UDP) |
+           (CSUM_TCP_IPV6 | CSUM_UDP_IPV6) | CSUM_TSO */
+       scctx->isc_tx_csum_flags = 0;
+       scctx->isc_capabilities = scctx->isc_capenable = 0;
+#if 0
+       /*
+        * CSUM, TSO and VLAN support are TBD
+        */
+           IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6 |
+           IFCAP_TSO4 | IFCAP_TSO6 |
+           IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 |
+           IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING |
+           IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO |
+           IFCAP_JUMBO_MTU;
+       scctx->isc_capabilities |= IFCAP_LRO | IFCAP_VLAN_HWFILTER;
+#endif
+
+       /* get the BAR */
+       error = mgb_alloc_regs(sc);
+       if (error != 0) {
+               device_printf(sc->dev,
+                   "Unable to allocate bus resource: registers.\n");
+               goto fail;
+       }
+
+       error = mgb_test_bar(sc);
+       if (error != 0)
+               goto fail;
+
+       error = mgb_hw_init(sc);
+       if (error != 0) {
+               device_printf(sc->dev,
+                   "MGB device init failed. (err: %d)\n", error);
+               goto fail;
+       }
+
+       switch (pci_get_device(sc->dev))
+       {
+       case MGB_LAN7430_DEVICE_ID:
+               phyaddr = 1;
+               break;
+       case MGB_LAN7431_DEVICE_ID:
+       default:
+               phyaddr = MII_PHY_ANY;
+               break;
+       }
+
+       /* XXX: Would be nice(r) if locked methods were here */
+       error = mii_attach(sc->dev, &sc->miibus, iflib_get_ifp(ctx),
+           mgb_media_change, mgb_media_status,
+           BMSR_DEFCAPMASK, phyaddr, MII_OFFSET_ANY, MIIF_DOPAUSE);
+       if (error != 0) {
+               device_printf(sc->dev, "Failed to attach MII interface\n");
+               goto fail;
+       }
+
+       miid = device_get_softc(sc->miibus);
+       scctx->isc_media = &miid->mii_media;
+
+       scctx->isc_msix_bar = pci_msix_table_bar(sc->dev);
+       /** Setup PBA BAR **/
+       rid = pci_msix_pba_bar(sc->dev);
+       if (rid != scctx->isc_msix_bar) {
+               sc->pba = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
+                   &rid, RF_ACTIVE);
+               if (sc->pba == NULL) {
+                       error = ENXIO;
+                       device_printf(sc->dev, "Failed to setup PBA BAR\n");
+                       goto fail;
+               }
+       }
+
+       mgb_get_ethaddr(sc, &hwaddr);
+       if (ETHER_IS_BROADCAST(hwaddr.octet) ||
+           ETHER_IS_MULTICAST(hwaddr.octet) ||
+           ETHER_IS_ZERO(hwaddr.octet))
+               ether_gen_addr(iflib_get_ifp(ctx), &hwaddr);
+
+       /*
+        * XXX: if the MAC address was generated the linux driver
+        * writes it back to the device.
+        */
+       iflib_set_mac(ctx, hwaddr.octet);
+
+       /* Map all vectors to vector 0 (admin interrupts) by default. */
+       CSR_WRITE_REG(sc, MGB_INTR_VEC_RX_MAP, 0);
+       CSR_WRITE_REG(sc, MGB_INTR_VEC_TX_MAP, 0);
+       CSR_WRITE_REG(sc, MGB_INTR_VEC_OTHER_MAP, 0);
+
+       return (0);
+
+fail:
+       mgb_detach(ctx);
+       return (error);
+}
+
+static int
+mgb_attach_post(if_ctx_t ctx)
+{
+       struct mgb_softc *sc;
+
+       sc = iflib_get_softc(ctx);
+
+       device_printf(sc->dev, "Interrupt test: %s\n",
+           (mgb_intr_test(sc) ? "PASS" : "FAIL"));
+
+       return (0);
+}
+
+static int
+mgb_detach(if_ctx_t ctx)
+{
+       struct mgb_softc *sc;
+       int error;
+
+       sc = iflib_get_softc(ctx);
+
+       /* XXX: Should report errors but still detach everything. */
+       error = mgb_hw_teardown(sc);
+
+       /* Release IRQs */
+       iflib_irq_free(ctx, &sc->rx_irq);
+       iflib_irq_free(ctx, &sc->admin_irq);
+
+       if (sc->miibus != NULL)
+               device_delete_child(sc->dev, sc->miibus);
+
+       if (sc->pba != NULL)
+               error = bus_release_resource(sc->dev, SYS_RES_MEMORY,
+                   rman_get_rid(sc->pba), sc->pba);
+       sc->pba = NULL;
+
+       error = mgb_release_regs(sc);
+
+       return (error);
+}
+
+static int
+mgb_media_change(if_t ifp)
+{
+       struct mii_data *miid;
+       struct mii_softc *miisc;
+       struct mgb_softc *sc;
+       if_ctx_t ctx;
+       int needs_reset;
+
+       ctx = if_getsoftc(ifp);
+       sc = iflib_get_softc(ctx);
+       miid = device_get_softc(sc->miibus);
+       LIST_FOREACH(miisc, &miid->mii_phys, mii_list)
+               PHY_RESET(miisc);
+
+       needs_reset = mii_mediachg(miid);
+       if (needs_reset != 0)
+               ifp->if_init(ctx);
+       return (needs_reset);
+}
+
+static void
+mgb_media_status(if_t ifp, struct ifmediareq *ifmr)
+{
+       struct mgb_softc *sc;
+       struct mii_data *miid;
+
+       sc = iflib_get_softc(if_getsoftc(ifp));
+       miid = device_get_softc(sc->miibus);
+       if ((if_getflags(ifp) & IFF_UP) == 0)
+               return;
+
+       mii_pollstat(miid);
+       ifmr->ifm_active = miid->mii_media_active;
+       ifmr->ifm_status = miid->mii_media_status;
+}
+
+static int
+mgb_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs,
+    int ntxqsets)
+{
+       struct mgb_softc *sc;
+       struct mgb_ring_data *rdata;
+       int q;
+
+       sc = iflib_get_softc(ctx);
+       KASSERT(ntxqsets == 1, ("ntxqsets = %d", ntxqsets));
+       rdata = &sc->tx_ring_data;
+       for (q = 0; q < ntxqsets; q++) {
+               KASSERT(ntxqs == 2, ("ntxqs = %d", ntxqs));
+               /* Ring */
+               rdata->ring = (struct mgb_ring_desc *) vaddrs[q * ntxqs + 0];
+               rdata->ring_bus_addr = paddrs[q * ntxqs + 0];
+
+               /* Head WB */
+               rdata->head_wb = (uint32_t *) vaddrs[q * ntxqs + 1];
+               rdata->head_wb_bus_addr = paddrs[q * ntxqs + 1];
+       }
+       return 0;
+}
+
+static int
+mgb_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs,
+    int nrxqsets)
+{
+       struct mgb_softc *sc;
+       struct mgb_ring_data *rdata;
+       int q;
+
+       sc = iflib_get_softc(ctx);
+       KASSERT(nrxqsets == 1, ("nrxqsets = %d", nrxqsets));
+       rdata = &sc->rx_ring_data;
+       for (q = 0; q < nrxqsets; q++) {
+               KASSERT(nrxqs == 2, ("nrxqs = %d", nrxqs));
+               /* Ring */
+               rdata->ring = (struct mgb_ring_desc *) vaddrs[q * nrxqs + 0];
+               rdata->ring_bus_addr = paddrs[q * nrxqs + 0];
+
+               /* Head WB */
+               rdata->head_wb = (uint32_t *) vaddrs[q * nrxqs + 1];
+               rdata->head_wb_bus_addr = paddrs[q * nrxqs + 1];
+       }
+       return 0;
+}
+
+static void
+mgb_queues_free(if_ctx_t ctx)
+{
+       struct mgb_softc *sc;
+
+       sc = iflib_get_softc(ctx);
+
+       memset(&sc->rx_ring_data, 0, sizeof(struct mgb_ring_data));
+       memset(&sc->tx_ring_data, 0, sizeof(struct mgb_ring_data));
+}
+
+static void
+mgb_init(if_ctx_t ctx)
+{
+       struct mgb_softc *sc;
+       struct mii_data *miid;
+       int error;
+
+       sc = iflib_get_softc(ctx);
+       miid = device_get_softc(sc->miibus);
+       device_printf(sc->dev, "running init ...\n");
+
+       mgb_dma_init(sc);
+
+       /* XXX: Turn off perfect filtering, turn on (broad|multi|uni)cast rx */
+       CSR_CLEAR_REG(sc, MGB_RFE_CTL, MGB_RFE_ALLOW_PERFECT_FILTER);
+       CSR_UPDATE_REG(sc, MGB_RFE_CTL,
+           MGB_RFE_ALLOW_BROADCAST |
+           MGB_RFE_ALLOW_UNICAST |
+           MGB_RFE_ALLOW_UNICAST);
+
+       error = mii_mediachg(miid);
+       KASSERT(!error, ("mii_mediachg returned: %d", error));
+}
+
+#ifdef DEBUG
+static void
+mgb_dump_some_stats(struct mgb_softc *sc)
+{
+       int i;
+       int first_stat = 0x1200;
+       int last_stat = 0x12FC;
+
+       for (i = first_stat; i <= last_stat; i += 4)
+               if (CSR_READ_REG(sc, i) != 0)
+                       device_printf(sc->dev, "0x%04x: 0x%08x\n", i,
+                           CSR_READ_REG(sc, i));
+       char *stat_names[] = {
+               "MAC_ERR_STS ",
+               "FCT_INT_STS ",
+               "DMAC_CFG ",
+               "DMAC_CMD ",
+               "DMAC_INT_STS ",
+               "DMAC_INT_EN ",
+               "DMAC_RX_ERR_STS0 ",
+               "DMAC_RX_ERR_STS1 ",
+               "DMAC_RX_ERR_STS2 ",
+               "DMAC_RX_ERR_STS3 ",
+               "INT_STS ",
+               "INT_EN ",
+               "INT_VEC_EN ",
+               "INT_VEC_MAP0 ",
+               "INT_VEC_MAP1 ",
+               "INT_VEC_MAP2 ",
+               "TX_HEAD0",
+               "TX_TAIL0",
+               "DMAC_TX_ERR_STS0 ",
+               NULL
+       };
+       int stats[] = {
+               0x114,
+               0xA0,
+               0xC00,
+               0xC0C,
+               0xC10,
+               0xC14,
+               0xC60,
+               0xCA0,
+               0xCE0,
+               0xD20,
+               0x780,
+               0x788,
+               0x794,
+               0x7A0,
+               0x7A4,
+               0x780,
+               0xD58,
+               0xD5C,
+               0xD60,
+               0x0
+       };
+       i = 0;
+       printf("==============================\n");
+       while (stats[i++])
+               device_printf(sc->dev, "%s at offset 0x%04x = 0x%08x\n",
+                   stat_names[i - 1], stats[i - 1],
+                   CSR_READ_REG(sc, stats[i - 1]));
+       printf("==== TX RING DESCS ====\n");
+       for (i = 0; i < MGB_DMA_RING_SIZE; i++)
+               device_printf(sc->dev, "ring[%d].data0=0x%08x\n"
+                   "ring[%d].data1=0x%08x\n"
+                   "ring[%d].data2=0x%08x\n"
+                   "ring[%d].data3=0x%08x\n",
+                   i, sc->tx_ring_data.ring[i].ctl,
+                   i, sc->tx_ring_data.ring[i].addr.low,
+                   i, sc->tx_ring_data.ring[i].addr.high,
+                   i, sc->tx_ring_data.ring[i].sts);
+       device_printf(sc->dev, "==== DUMP_TX_DMA_RAM ====\n");
+       int i;
+       CSR_WRITE_REG(sc, 0x24, 0xF); // DP_SEL & TX_RAM_0
+       for (i = 0; i < 128; i++) {
+               CSR_WRITE_REG(sc, 0x2C, i); // DP_ADDR
+
+               CSR_WRITE_REG(sc, 0x28, 0); // DP_CMD
+
+               while ((CSR_READ_REG(sc, 0x24) & 0x80000000) == 0) // DP_SEL & 
READY
+                       DELAY(1000);
+
+               device_printf(sc->dev, "DMAC_TX_RAM_0[%u]=%08x\n", i,
+                   CSR_READ_REG(sc, 0x30)); // DP_DATA
+       }
+}
+#endif
+
+static void
+mgb_stop(if_ctx_t ctx)
+{
+       struct mgb_softc *sc ;
+       if_softc_ctx_t scctx;
+       int i;
+
+       sc = iflib_get_softc(ctx);
+       scctx = iflib_get_softc_ctx(ctx);
+
+       /* XXX: Could potentially timeout */
+       for (i = 0; i < scctx->isc_nrxqsets; i++) {
+               mgb_dmac_control(sc, MGB_DMAC_RX_START, 0, DMAC_STOP);
+               mgb_fct_control(sc, MGB_FCT_RX_CTL, 0, FCT_DISABLE);
+       }
+       for (i = 0; i < scctx->isc_ntxqsets; i++) {
+               mgb_dmac_control(sc, MGB_DMAC_TX_START, 0, DMAC_STOP);
+               mgb_fct_control(sc, MGB_FCT_TX_CTL, 0, FCT_DISABLE);
+       }
+}
+
+static int
+mgb_legacy_intr(void *xsc)
+{
+       struct mgb_softc *sc;
+
+       sc = xsc;
+       iflib_admin_intr_deferred(sc->ctx);
+       return (FILTER_HANDLED);
+}
+
+static int
+mgb_rxq_intr(void *xsc)
+{
+       struct mgb_softc *sc;
+       if_softc_ctx_t scctx;
+       uint32_t intr_sts, intr_en;
+       int qidx;
+
+       sc = xsc;
+       scctx = iflib_get_softc_ctx(sc->ctx);
+
+       intr_sts = CSR_READ_REG(sc, MGB_INTR_STS);
+       intr_en = CSR_READ_REG(sc, MGB_INTR_ENBL_SET);
+       intr_sts &= intr_en;
+
+       for (qidx = 0; qidx < scctx->isc_nrxqsets; qidx++) {
+               if ((intr_sts & MGB_INTR_STS_RX(qidx))){
+                       CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR,
+                           MGB_INTR_STS_RX(qidx));
+                       CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_RX(qidx));
+               }
+       }
+       return (FILTER_SCHEDULE_THREAD);
+}
+
+static int
+mgb_admin_intr(void *xsc)
+{
+       struct mgb_softc *sc;
+       if_softc_ctx_t scctx;
+       uint32_t intr_sts, intr_en;
+       int qidx;
+
+       sc = xsc;
+       scctx = iflib_get_softc_ctx(sc->ctx);
+
+       intr_sts = CSR_READ_REG(sc, MGB_INTR_STS);
+       intr_en = CSR_READ_REG(sc, MGB_INTR_ENBL_SET);
+       intr_sts &= intr_en;
+
+       /*
+        * NOTE: Debugging printfs here
+        * will likely cause interrupt test failure.
+        */
+
+       /* TODO: shouldn't continue if suspended */
+       if ((intr_sts & MGB_INTR_STS_ANY) == 0)
+       {
+               device_printf(sc->dev, "non-mgb interrupt triggered.\n");
+               return (FILTER_SCHEDULE_THREAD);
+       }
+       if ((intr_sts &  MGB_INTR_STS_TEST) != 0)
+       {
+               sc->isr_test_flag = true;
+               CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_TEST);
+               return (FILTER_HANDLED);
+       }
+       if ((intr_sts & MGB_INTR_STS_RX_ANY) != 0)
+       {
+               for (qidx = 0; qidx < scctx->isc_nrxqsets; qidx++) {
+                       if ((intr_sts & MGB_INTR_STS_RX(qidx))){
+                               iflib_rx_intr_deferred(sc->ctx, qidx);
+                       }
+               }
+               return (FILTER_HANDLED);
+       }
+       /* XXX: TX interrupts should not occur */
+       if ((intr_sts & MGB_INTR_STS_TX_ANY) != 0)
+       {
+               for (qidx = 0; qidx < scctx->isc_ntxqsets; qidx++) {
+                       if ((intr_sts & MGB_INTR_STS_RX(qidx))) {
+                               /* clear the interrupt sts and run handler */
+                               CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR,
+                                   MGB_INTR_STS_TX(qidx));
+                               CSR_WRITE_REG(sc, MGB_INTR_STS,
+                                   MGB_INTR_STS_TX(qidx));
+                               iflib_tx_intr_deferred(sc->ctx, qidx);
+                       }
+               }
+               return (FILTER_HANDLED);
+       }
+
+       return (FILTER_SCHEDULE_THREAD);
+}
+
+static int
+mgb_msix_intr_assign(if_ctx_t ctx, int msix)
+{
+       struct mgb_softc *sc;
+       if_softc_ctx_t scctx;
+       int error, i, vectorid;
+       char irq_name[16];
+
+       sc = iflib_get_softc(ctx);
+       scctx = iflib_get_softc_ctx(ctx);
+
+       KASSERT(scctx->isc_nrxqsets == 1 && scctx->isc_ntxqsets == 1,
+           ("num rxqsets/txqsets != 1 "));
+
+       /*
+        * First vector should be admin interrupts, others vectors are TX/RX
+        *
+        * RIDs start at 1, and vector ids start at 0.
+        */
+       vectorid = 0;
+       error = iflib_irq_alloc_generic(ctx, &sc->admin_irq, vectorid + 1,
+           IFLIB_INTR_ADMIN, mgb_admin_intr, sc, 0, "admin");
+       if (error) {
+               device_printf(sc->dev,
+                   "Failed to register admin interrupt handler\n");
+               return (error);
+       }
+
+       for (i = 0; i < scctx->isc_nrxqsets; i++) {
+               vectorid++;
+               snprintf(irq_name, sizeof(irq_name), "rxq%d", i);
+               error = iflib_irq_alloc_generic(ctx, &sc->rx_irq, vectorid + 1,
+                   IFLIB_INTR_RX, mgb_rxq_intr, sc, i, irq_name);
+               if (error) {
+                       device_printf(sc->dev,
+                           "Failed to register rxq %d interrupt handler\n", i);
+                       return (error);
+               }
+               CSR_UPDATE_REG(sc, MGB_INTR_VEC_RX_MAP,
+                   MGB_INTR_VEC_MAP(vectorid, i));
+       }
+
+       /* Not actually mapping hw TX interrupts ... */
+       for (i = 0; i < scctx->isc_ntxqsets; i++) {
+               snprintf(irq_name, sizeof(irq_name), "txq%d", i);
+               iflib_softirq_alloc_generic(ctx, NULL, IFLIB_INTR_TX, NULL, i,
+                   irq_name);
+       }
+
+       return (0);
+}
+
+static void
+mgb_intr_enable_all(if_ctx_t ctx)
+{
+       struct mgb_softc *sc;
+       if_softc_ctx_t scctx;
+       int i, dmac_enable = 0, intr_sts = 0, vec_en = 0;
+
+       sc = iflib_get_softc(ctx);
+       scctx = iflib_get_softc_ctx(ctx);
+       intr_sts |= MGB_INTR_STS_ANY;
+       vec_en |= MGB_INTR_STS_ANY;
+
+       for (i = 0; i < scctx->isc_nrxqsets; i++) {
+               intr_sts |= MGB_INTR_STS_RX(i);
+               dmac_enable |= MGB_DMAC_RX_INTR_ENBL(i);
+               vec_en |= MGB_INTR_RX_VEC_STS(i);
+       }
+
+       /* TX interrupts aren't needed ... */
+
+       CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET, intr_sts);
+       CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_SET, vec_en);
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, dmac_enable);
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_SET, dmac_enable);
+}
+
+static void
+mgb_intr_disable_all(if_ctx_t ctx)
+{
+       struct mgb_softc *sc;
+
+       sc = iflib_get_softc(ctx);
+       CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR, UINT32_MAX);
+       CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_CLR, UINT32_MAX);
+       CSR_WRITE_REG(sc, MGB_INTR_STS, UINT32_MAX);
+
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_CLR, UINT32_MAX);
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, UINT32_MAX);
+}
+
+static int
+mgb_rx_queue_intr_enable(if_ctx_t ctx, uint16_t qid)
+{
+       /* called after successful rx isr */
+       struct mgb_softc *sc;
+
+       sc = iflib_get_softc(ctx);
+       CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_SET, MGB_INTR_RX_VEC_STS(qid));
+       CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET, MGB_INTR_STS_RX(qid));
+
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, MGB_DMAC_RX_INTR_ENBL(qid));
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_SET, MGB_DMAC_RX_INTR_ENBL(qid));
+       return (0);
+}
+
+static int
+mgb_tx_queue_intr_enable(if_ctx_t ctx, uint16_t qid)
+{
+       /* XXX: not called (since tx interrupts not used) */
+       struct mgb_softc *sc;
+
+       sc = iflib_get_softc(ctx);
+
+       CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET, MGB_INTR_STS_TX(qid));
+
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, MGB_DMAC_TX_INTR_ENBL(qid));
+       CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_SET, MGB_DMAC_TX_INTR_ENBL(qid));
+       return (0);
+}
+
+static bool
+mgb_intr_test(struct mgb_softc *sc)
+{
+       int i;
+
+       sc->isr_test_flag = false;
+       CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_TEST);
+       CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_SET, MGB_INTR_STS_ANY);
+       CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET,
+           MGB_INTR_STS_ANY | MGB_INTR_STS_TEST);
+       CSR_WRITE_REG(sc, MGB_INTR_SET, MGB_INTR_STS_TEST);
+       if (sc->isr_test_flag)
+               return true;
+       for (i = 0; i < MGB_TIMEOUT; i++) {
+               DELAY(10);
+               if (sc->isr_test_flag)
+                       break;
+       }
+       CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR, MGB_INTR_STS_TEST);
+       CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_TEST);
+       return sc->isr_test_flag;
+}
+
+static int
+mgb_isc_txd_encap(void *xsc , if_pkt_info_t ipi)
+{
+       struct mgb_softc *sc;
+       if_softc_ctx_t scctx;
+       struct mgb_ring_data *rdata;
+       struct mgb_ring_desc *txd;
+       bus_dma_segment_t *segs;
+       qidx_t pidx, nsegs;
+       int i;
+
+       KASSERT(ipi->ipi_qsidx == 0,
+           ("tried to refill TX Channel %d.\n", ipi->ipi_qsidx));
+       sc = xsc;
+       scctx = iflib_get_softc_ctx(sc->ctx);
+       rdata = &sc->tx_ring_data;
+
+       pidx = ipi->ipi_pidx;
+       segs = ipi->ipi_segs;
+       nsegs = ipi->ipi_nsegs;
+
+       /* For each seg, create a descriptor */
+       for (i = 0; i < nsegs; ++i) {
+               KASSERT(nsegs == 1, ("Multisegment packet !!!!!\n"));
+               txd = &rdata->ring[pidx];

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to