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