Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/broadcom Ethernet driver for BCM53XXX (not quit...



details:   https://anonhg.NetBSD.org/src/rev/58e07b300813
branches:  trunk
changeset: 781858:58e07b300813
user:      matt <matt%NetBSD.org@localhost>
date:      Thu Oct 04 00:14:24 2012 +0000

description:
Ethernet driver for BCM53XXX (not quite working).

diffstat:

 sys/arch/arm/broadcom/bcm53xx_eth.c |  1466 ++++++++++++++++++++++++++++++++++-
 1 files changed, 1450 insertions(+), 16 deletions(-)

diffs (truncated from 1551 to 300 lines):

diff -r 631f9b653c6e -r 58e07b300813 sys/arch/arm/broadcom/bcm53xx_eth.c
--- a/sys/arch/arm/broadcom/bcm53xx_eth.c       Thu Oct 04 00:01:48 2012 +0000
+++ b/sys/arch/arm/broadcom/bcm53xx_eth.c       Thu Oct 04 00:14:24 2012 +0000
@@ -33,27 +33,86 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.c,v 1.1 2012/09/01 00:04:44 matt Exp $");
+__KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.c,v 1.2 2012/10/04 00:14:24 matt Exp $");
 
 #include <sys/param.h>
+#include <sys/atomic.h>
 #include <sys/bus.h>
 #include <sys/device.h>
+#include <sys/ioctl.h>
 #include <sys/intr.h>
+#include <sys/kmem.h>
 #include <sys/mutex.h>
+#include <sys/socket.h>
 #include <sys/systm.h>
 
 #include <net/if.h>
 #include <net/if_ether.h>
 #include <net/if_media.h>
 
+#include <net/if_dl.h>
+
+#include <net/bpf.h>
+
 #include <dev/mii/miivar.h>
 
 #include <arm/broadcom/bcm53xx_reg.h>
 #include <arm/broadcom/bcm53xx_var.h>
 
+#define        BCMETH_RCVOFFSET        6
+#define        BCMETH_MAXTXMBUFS       32
+#define        BCMETH_NTXSEGS          30
+#define        BCMETH_MAXRXMBUFS       255
+#define        BCMETH_MINRXMBUFS       32
+#define        BCMETH_NRXSEGS          1
+
 static int bcmeth_ccb_match(device_t, cfdata_t, void *);
 static void bcmeth_ccb_attach(device_t, device_t, void *);
 
+struct bcmeth_txqueue {
+       bus_dmamap_t txq_descmap;
+       struct gmac_txdb *txq_consumer;
+       struct gmac_txdb *txq_producer;
+       struct gmac_txdb *txq_first;
+       struct gmac_txdb *txq_last;
+       struct ifqueue txq_mbufs;
+       struct mbuf *txq_next;
+       size_t txq_free;
+       size_t txq_threshold;
+       size_t txq_lastintr;
+       bus_size_t txq_reg_xmtaddrlo;
+       bus_size_t txq_reg_xmtptr;
+       bus_size_t txq_reg_xmtctl;
+       bus_size_t txq_reg_xmtsts0;
+       bus_dma_segment_t txq_descmap_seg;
+};
+
+struct bcmeth_rxqueue {
+       bus_dmamap_t rxq_descmap;
+       struct gmac_rxdb *rxq_consumer;
+       struct gmac_rxdb *rxq_producer;
+       struct gmac_rxdb *rxq_first;
+       struct gmac_rxdb *rxq_last;
+       struct mbuf *rxq_mhead;
+       struct mbuf **rxq_mtail;
+       struct mbuf *rxq_mconsumer;
+       size_t rxq_inuse;
+       size_t rxq_threshold;
+       bus_size_t rxq_reg_rcvaddrlo;
+       bus_size_t rxq_reg_rcvptr;
+       bus_size_t rxq_reg_rcvctl;
+       bus_size_t rxq_reg_rcvsts0;
+       bus_dma_segment_t rxq_descmap_seg;
+};
+
+struct bcmeth_mapcache {
+       u_int dmc_nmaps;
+       u_int dmc_maxseg;
+       u_int dmc_maxmaps;
+       u_int dmc_maxmapsize;
+       bus_dmamap_t dmc_maps[0];
+};
+
 struct bcmeth_softc {
        device_t sc_dev;
        bus_space_tag_t sc_bst;
@@ -62,13 +121,76 @@
        kmutex_t *sc_lock;
        kmutex_t *sc_hwlock;
        struct ethercom sc_ec;
-#define        sc_if           sc_ec.ec_if;
-       void *sc_sih;
+#define        sc_if           sc_ec.ec_if
+       struct ifmedia sc_media;
+       void *sc_soft_ih;
        void *sc_ih;
+
+       struct bcmeth_rxqueue sc_rxq;
+       struct bcmeth_txqueue sc_txq;
+
+       uint32_t sc_maxfrm;
+       uint32_t sc_cmdcfg;
+       uint32_t sc_intmask;
+       volatile uint32_t sc_soft_flags;
+#define        SOFT_RXINTR             0x01
+#define        SOFT_RXUNDERFLOW        0x02
+#define        SOFT_TXINTR             0x04
+#define        SOFT_REINIT             0x08
+
+       struct evcnt sc_ev_intr;
+       struct evcnt sc_ev_soft_intr;
+       struct evcnt sc_ev_tx_stall;
+
+       struct ifqueue sc_rx_bufcache;
+       struct bcmeth_mapcache *sc_rx_mapcache;     
+       struct bcmeth_mapcache *sc_tx_mapcache;
+
+       uint8_t sc_enaddr[ETHER_ADDR_LEN];
 };
 
+static void bcmeth_ifstart(struct ifnet *);
+static void bcmeth_ifwatchdog(struct ifnet *);
+static int bcmeth_ifinit(struct ifnet *);
+static void bcmeth_ifstop(struct ifnet *, int);
+static int bcmeth_ifioctl(struct ifnet *, u_long, void *);
+
+static int bcmeth_mapcache_create(struct bcmeth_softc *,
+    struct bcmeth_mapcache **, size_t, size_t, size_t);
+static void bcmeth_mapcache_destroy(struct bcmeth_softc *,
+    struct bcmeth_mapcache *);
+static bus_dmamap_t bcmeth_mapcache_get(struct bcmeth_softc *,
+    struct bcmeth_mapcache *);
+static void bcmeth_mapcache_put(struct bcmeth_softc *,
+    struct bcmeth_mapcache *, bus_dmamap_t);
+
+static int bcmeth_txq_attach(struct bcmeth_softc *,
+    struct bcmeth_txqueue *, u_int);
+static void bcmeth_txq_purge(struct bcmeth_softc *,
+    struct bcmeth_txqueue *);
+static void bcmeth_txq_reset(struct bcmeth_softc *,
+    struct bcmeth_txqueue *);
+static bool bcmeth_txq_consume(struct bcmeth_softc *,
+    struct bcmeth_txqueue *);
+static bool bcmeth_txq_produce(struct bcmeth_softc *,
+    struct bcmeth_txqueue *, struct mbuf *m);
+static bool bcmeth_txq_active_p(struct bcmeth_softc *,
+    struct bcmeth_txqueue *);
+
+static int bcmeth_rxq_attach(struct bcmeth_softc *,
+    struct bcmeth_rxqueue *, u_int);
+static bool bcmeth_rxq_produce(struct bcmeth_softc *,
+    struct bcmeth_rxqueue *);
+static void bcmeth_rxq_purge(struct bcmeth_softc *,
+    struct bcmeth_rxqueue *, bool);
+static void bcmeth_rxq_reset(struct bcmeth_softc *,
+    struct bcmeth_rxqueue *);
+
 static int bcmeth_intr(void *);
-static void bcmeth_softint(void *);
+static void bcmeth_soft_intr(void *);
+
+static int bcmeth_mediachange(struct ifnet *);
+static void bcmeth_mediastatus(struct ifnet *, struct ifmediareq *);
 
 static inline uint32_t
 bcmeth_read_4(struct bcmeth_softc *sc, bus_size_t o)
@@ -106,25 +228,76 @@
 bcmeth_ccb_attach(device_t parent, device_t self, void *aux)
 {
        struct bcmeth_softc * const sc = device_private(self);
+       struct ethercom * const ec = &sc->sc_ec;
+       struct ifnet * const ifp = &ec->ec_if;
        struct bcmccb_attach_args * const ccbaa = aux;
        const struct bcm_locators * const loc = &ccbaa->ccbaa_loc;
-
-       sc->sc_dev = self;
-       sc->sc_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET);
-       sc->sc_hwlock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM);
+       const char * const xname = device_xname(self);
+       prop_dictionary_t dict = device_properties(self);
+       int error;
 
        sc->sc_bst = ccbaa->ccbaa_ccb_bst;
        sc->sc_dmat = ccbaa->ccbaa_dmat;
        bus_space_subregion(sc->sc_bst, ccbaa->ccbaa_ccb_bsh,
            loc->loc_offset, loc->loc_size, &sc->sc_bsh);
 
+       prop_data_t eaprop = prop_dictionary_get(dict, "mac-address");
+        if (eaprop == NULL) {
+               uint32_t mac0 = bcmeth_read_4(sc, UNIMAC_MAC_0);
+               uint32_t mac1 = bcmeth_read_4(sc, UNIMAC_MAC_1);
+               if ((mac0 == 0 && mac1 == 0) || (mac1 & 1)) {
+                       aprint_error(": mac-address property is missing\n");
+                       return;
+               }
+               sc->sc_enaddr[0] = (mac1 >> 0) & 0xff;
+               sc->sc_enaddr[1] = (mac1 >> 8) & 0xff;
+               sc->sc_enaddr[2] = (mac0 >> 0) & 0xff;
+               sc->sc_enaddr[3] = (mac0 >> 8) & 0xff;
+               sc->sc_enaddr[4] = (mac0 >> 16) & 0xff;
+               sc->sc_enaddr[5] = (mac0 >> 24) & 0xff;
+       } else {
+               KASSERT(prop_object_type(eaprop) == PROP_TYPE_DATA);
+               KASSERT(prop_data_size(eaprop) == ETHER_ADDR_LEN);
+               memcpy(sc->sc_enaddr, prop_data_data_nocopy(eaprop),
+                   ETHER_ADDR_LEN);
+       }
+       sc->sc_dev = self;
+       sc->sc_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET);
+       sc->sc_hwlock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM);
+
        bcmeth_write_4(sc, GMAC_INTMASK, 0);    // disable interrupts
 
        aprint_naive("\n");
        aprint_normal(": Gigabit Ethernet Controller\n");
 
-       sc->sc_sih = softint_establish(SOFTINT_MPSAFE | SOFTINT_NET,
-           bcmeth_softint, sc);
+       error = bcmeth_rxq_attach(sc, &sc->sc_rxq, 0);
+       if (error) {
+               aprint_error(": failed to init rxq: %d\n", error);
+               return;
+       }
+
+       error = bcmeth_txq_attach(sc, &sc->sc_txq, 0);
+       if (error) {
+               aprint_error(": failed to init txq: %d\n", error);
+               return;
+       }
+
+       error = bcmeth_mapcache_create(sc, &sc->sc_rx_mapcache, 
+           BCMETH_MAXRXMBUFS, MCLBYTES, BCMETH_NRXSEGS);
+       if (error) {
+               aprint_error(": failed to allocate rx dmamaps: %d\n", error);
+               return;
+       }
+
+       error = bcmeth_mapcache_create(sc, &sc->sc_tx_mapcache, 
+           BCMETH_MAXTXMBUFS, MCLBYTES, BCMETH_NTXSEGS);
+       if (error) {
+               aprint_error(": failed to allocate tx dmamaps: %d\n", error);
+               return;
+       }
+
+       sc->sc_soft_ih = softint_establish(SOFTINT_MPSAFE | SOFTINT_NET,
+           bcmeth_soft_intr, sc);
 
        sc->sc_ih = intr_establish(loc->loc_intrs[0], IPL_VM, IST_LEVEL,
            bcmeth_intr, sc);
@@ -136,29 +309,1290 @@
                aprint_normal_dev(self, "interrupting on irq %d\n",
                     loc->loc_intrs[0]);
        }
+
+       aprint_normal_dev(sc->sc_dev, "Ethernet address %s\n",
+           ether_sprintf(sc->sc_enaddr));
+
+       /*
+        * Since each port in plugged into the switch/flow-accelerator,
+        * we hard code at Gige Full-Duplex with Flow Control enabled.
+        */
+       int ifmedia = IFM_ETHER|IFM_1000_T|IFM_FDX;
+       //ifmedia |= IFM_FLOW|IFM_ETH_TXPAUSE|IFM_ETH_RXPAUSE;
+       ifmedia_init(&sc->sc_media, IFM_IMASK, bcmeth_mediachange,
+           bcmeth_mediastatus);
+       ifmedia_add(&sc->sc_media, ifmedia, 0, NULL);
+       ifmedia_set(&sc->sc_media, ifmedia);
+
+       ec->ec_capabilities = ETHERCAP_VLAN_MTU | ETHERCAP_JUMBO_MTU;
+
+       strlcpy(ifp->if_xname, xname, IFNAMSIZ);
+       ifp->if_softc = sc;
+       ifp->if_baudrate = IF_Mbps(1000);
+       ifp->if_capabilities = 0;
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_ioctl = bcmeth_ifioctl;
+       ifp->if_start = bcmeth_ifstart;
+       ifp->if_watchdog = bcmeth_ifwatchdog;
+       ifp->if_init = bcmeth_ifinit;
+       ifp->if_stop = bcmeth_ifstop;
+       IFQ_SET_READY(&ifp->if_snd);
+
+       bcmeth_ifstop(ifp, true);
+
+       /*
+        * Attach the interface.
+        */
+       if_attach(ifp);
+       ether_ifattach(ifp, sc->sc_enaddr);
+
+       evcnt_attach_dynamic(&sc->sc_ev_intr, EVCNT_TYPE_INTR,
+           NULL, xname, "intr");
+       evcnt_attach_dynamic(&sc->sc_ev_soft_intr, EVCNT_TYPE_INTR,
+           NULL, xname, "soft intr");
+       evcnt_attach_dynamic(&sc->sc_ev_tx_stall, EVCNT_TYPE_MISC,



Home | Main Index | Thread Index | Old Index