Source-Changes-HG archive

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

[src/trunk]: src/sys/arch/powerpc/ibm4xx/dev Add a driver the for IBM 405gp (...



details:   https://anonhg.NetBSD.org/src/rev/f1c896c2ffc6
branches:  trunk
changeset: 535050:f1c896c2ffc6
user:      simonb <simonb%NetBSD.org@localhost>
date:      Fri Aug 09 04:17:26 2002 +0000

description:
Add a driver the for IBM 405gp (and possibly other IBM 4xx cpus) ethernet
MAC (emac).  Much thanks to Jason Thorpe for debugging help writing this
driver.  Tested on the walnut, and an earlier version of this driver works
on the OpenBlockSS.

diffstat:

 sys/arch/powerpc/ibm4xx/dev/if_emac.c |  1423 ++++++++++++++++++++++++++++++++-
 1 files changed, 1390 insertions(+), 33 deletions(-)

diffs (truncated from 1501 to 300 lines):

diff -r 0ef513e8c22a -r f1c896c2ffc6 sys/arch/powerpc/ibm4xx/dev/if_emac.c
--- a/sys/arch/powerpc/ibm4xx/dev/if_emac.c     Fri Aug 09 04:13:20 2002 +0000
+++ b/sys/arch/powerpc/ibm4xx/dev/if_emac.c     Fri Aug 09 04:17:26 2002 +0000
@@ -1,10 +1,10 @@
-/*     $NetBSD: if_emac.c,v 1.2 2002/03/15 21:10:46 eeh Exp $  */
+/*     $NetBSD: if_emac.c,v 1.3 2002/08/09 04:17:26 simonb Exp $       */
 
 /*
- * Copyright 2001 Wasabi Systems, Inc.
+ * Copyright 2001, 2002 Wasabi Systems, Inc.
  * All rights reserved.
  *
- * Written by Simon Burge and Eduardo Horvath for Wasabi Systems, Inc.
+ * Written by Simon Burge and Jason Thorpe for Wasabi Systems, Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -35,24 +35,16 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "rnd.h"
 #include "bpfilter.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/callout.h>
 #include <sys/mbuf.h>
-#include <sys/malloc.h>
 #include <sys/kernel.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
-#include <sys/errno.h>
-#include <sys/device.h>
-#include <sys/queue.h>
 
-#if NRND > 0
-#include <sys/rnd.h>
-#endif
+#include <uvm/uvm_extern.h>            /* for PAGE_SIZE */
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -63,13 +55,76 @@
 #include <net/bpf.h>
 #endif
 
-#include <machine/autoconf.h>
 #include <machine/bus.h>
-#include <machine/walnut.h>                                                            /* XXX - this file shouldn't depend on board-data */
+#include <machine/autoconf.h>  /* for mainbus_attach_args; should use imb4xx opb */
 
-#include <dev/mii/mii.h>
+#include <powerpc/ibm4xx/ibm405gp.h>
+#include <powerpc/ibm4xx/mal405gp.h>
+#include <powerpc/ibm4xx/dcr405gp.h>
+#include <powerpc/ibm4xx/dev/if_emacreg.h>
+
 #include <dev/mii/miivar.h>
 
+/*
+ * Transmit descriptor list size.  There are two Tx channels, each with
+ * up to 256 hardware descriptors available.  We currently use one Tx
+ * channel.  We tell the upper layers that they can queue a lot of
+ * packets, and we go ahead and manage up to 64 of them at a time.  We
+ * allow up to 16 DMA segments per packet.
+ */
+#define        EMAC_NTXSEGS            16
+#define        EMAC_TXQUEUELEN         64
+#define        EMAC_TXQUEUELEN_MASK    (EMAC_TXQUEUELEN - 1)
+#define        EMAC_TXQUEUE_GC         (EMAC_TXQUEUELEN / 4)
+#define        EMAC_NTXDESC            256
+#define        EMAC_NTXDESC_MASK       (EMAC_NTXDESC - 1)
+#define        EMAC_NEXTTX(x)          (((x) + 1) & EMAC_NTXDESC_MASK)
+#define        EMAC_NEXTTXS(x)         (((x) + 1) & EMAC_TXQUEUELEN_MASK)
+
+/*
+ * Receive descriptor list size.  There is one Rx channel with up to 256
+ * hardware descriptors available.  We allocate 64 receive descriptors,
+ * each with a 2k buffer (MCLBYTES).
+ */
+#define        EMAC_NRXDESC            64
+#define        EMAC_NRXDESC_MASK       (EMAC_NRXDESC - 1)
+#define        EMAC_NEXTRX(x)          (((x) + 1) & EMAC_NRXDESC_MASK)
+#define        EMAC_PREVRX(x)          (((x) - 1) & EMAC_NRXDESC_MASK)
+
+/*
+ * Transmit/receive descriptors that are DMA'd to the EMAC.
+ */
+struct emac_control_data {
+       struct mal_descriptor ecd_txdesc[EMAC_NTXDESC];
+       struct mal_descriptor ecd_rxdesc[EMAC_NRXDESC];
+};
+
+#define        EMAC_CDOFF(x)           offsetof(struct emac_control_data, x)
+#define        EMAC_CDTXOFF(x)         EMAC_CDOFF(ecd_txdesc[(x)])
+#define        EMAC_CDRXOFF(x)         EMAC_CDOFF(ecd_rxdesc[(x)])
+
+/*
+ * Software state for transmit jobs.
+ */
+struct emac_txsoft {
+       struct mbuf *txs_mbuf;          /* head of mbuf chain */
+       bus_dmamap_t txs_dmamap;        /* our DMA map */
+       int txs_firstdesc;              /* first descriptor in packet */
+       int txs_lastdesc;               /* last descriptor in packet */
+       int txs_ndesc;                  /* # of descriptors used */
+};
+
+/*
+ * Software state for receive descriptors.
+ */
+struct emac_rxsoft {
+       struct mbuf *rxs_mbuf;          /* head of mbuf chain */
+       bus_dmamap_t rxs_dmamap;        /* our DMA map */
+};
+
+/*
+ * Software state per device.
+ */
 struct emac_softc {
        struct device sc_dev;           /* generic device information */
        bus_space_tag_t sc_st;          /* bus space tag */
@@ -77,33 +132,159 @@
        bus_dma_tag_t sc_dmat;          /* bus DMA tag */
        struct ethercom sc_ethercom;    /* ethernet common data */
        void *sc_sdhook;                /* shutdown hook */
+       void *sc_powerhook;             /* power management hook */
+
+       struct mii_data sc_mii;         /* MII/media information */
+       struct callout sc_callout;      /* tick callout */
+
+       u_int32_t sc_mr1;               /* copy of Mode Register 1 */
+
+       bus_dmamap_t sc_cddmamap;       /* control data dma map */
+#define        sc_cddma        sc_cddmamap->dm_segs[0].ds_addr
+
+       /* Software state for transmit/receive descriptors. */
+       struct emac_txsoft sc_txsoft[EMAC_TXQUEUELEN];
+       struct emac_rxsoft sc_rxsoft[EMAC_NRXDESC];
+
+       /* Control data structures. */
+       struct emac_control_data *sc_control_data;
+#define        sc_txdescs      sc_control_data->ecd_txdesc
+#define        sc_rxdescs      sc_control_data->ecd_rxdesc
+
+#ifdef EMAC_EVENT_COUNTERS
+       struct evcnt sc_ev_rxintr;      /* Rx interrupts */
+       struct evcnt sc_ev_txintr;      /* Tx interrupts */
+       struct evcnt sc_ev_rxde;        /* Rx descriptor interrupts */
+       struct evcnt sc_ev_txde;        /* Tx descriptor interrupts */
+       struct evcnt sc_ev_wol;         /* Wake-On-Lan interrupts */
+       struct evcnt sc_ev_serr;        /* MAL system error interrupts */
+       struct evcnt sc_ev_intr;        /* General EMAC interrupts */
+
+       struct evcnt sc_ev_txreap;      /* Calls to Tx descriptor reaper */
+       struct evcnt sc_ev_txsstall;    /* Tx stalled due to no txs */
+       struct evcnt sc_ev_txdstall;    /* Tx stalled due to no txd */
+       struct evcnt sc_ev_txdrop;      /* Tx packets dropped (too many segs) */
+       struct evcnt sc_ev_tu;          /* Tx underrun */
+#endif /* EMAC_EVENT_COUNTERS */
+
+       int sc_txfree;                  /* number of free Tx descriptors */
+       int sc_txnext;                  /* next ready Tx descriptor */
+
+       int sc_txsfree;                 /* number of free Tx jobs */
+       int sc_txsnext;                 /* next ready Tx job */
+       int sc_txsdirty;                /* dirty Tx jobs */
+
+       int sc_rxptr;                   /* next ready RX descriptor/descsoft */
 };
 
+#ifdef EMAC_EVENT_COUNTERS
+#define        EMAC_EVCNT_INCR(ev)     (ev)->ev_count++
+#else
+#define        EMAC_EVCNT_INCR(ev)     /* nothing */
+#endif
+
+#define        EMAC_CDTXADDR(sc, x)    ((sc)->sc_cddma + EMAC_CDTXOFF((x)))
+#define        EMAC_CDRXADDR(sc, x)    ((sc)->sc_cddma + EMAC_CDRXOFF((x)))
+
+#define        EMAC_CDTXSYNC(sc, x, n, ops)                                    \
+do {                                                                   \
+       int __x, __n;                                                   \
+                                                                       \
+       __x = (x);                                                      \
+       __n = (n);                                                      \
+                                                                       \
+       /* If it will wrap around, sync to the end of the ring. */      \
+       if ((__x + __n) > EMAC_NTXDESC) {                               \
+               bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap,       \
+                   EMAC_CDTXOFF(__x), sizeof(struct mal_descriptor) *  \
+                   (EMAC_NTXDESC - __x), (ops));                       \
+               __n -= (EMAC_NTXDESC - __x);                            \
+               __x = 0;                                                \
+       }                                                               \
+                                                                       \
+       /* Now sync whatever is left. */                                \
+       bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap,               \
+           EMAC_CDTXOFF(__x), sizeof(struct mal_descriptor) * __n, (ops)); \
+} while (/*CONSTCOND*/0)
+
+#define        EMAC_CDRXSYNC(sc, x, ops)                                       \
+do {                                                                   \
+       bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap,               \
+           EMAC_CDRXOFF((x)), sizeof(struct mal_descriptor), (ops));   \
+} while (/*CONSTCOND*/0)
+
+#define        EMAC_INIT_RXDESC(sc, x)                                         \
+do {                                                                   \
+       struct emac_rxsoft *__rxs = &(sc)->sc_rxsoft[(x)];              \
+       struct mal_descriptor *__rxd = &(sc)->sc_rxdescs[(x)];          \
+       struct mbuf *__m = __rxs->rxs_mbuf;                             \
+                                                                       \
+       /*                                                              \
+        * Note: We scoot the packet forward 2 bytes in the buffer      \
+        * so that the payload after the Ethernet header is aligned     \
+        * to a 4-byte boundary.                                        \
+        */                                                             \
+       __m->m_data = __m->m_ext.ext_buf + 2;                           \
+                                                                       \
+       __rxd->md_data = __rxs->rxs_dmamap->dm_segs[0].ds_addr + 2;     \
+       __rxd->md_data_len = __m->m_ext.ext_size - 2;                   \
+       __rxd->md_stat_ctrl = MAL_RX_EMPTY | MAL_RX_INTERRUPT |         \
+           /* Set wrap on last descriptor. */                          \
+           (((x) == EMAC_NRXDESC - 1) ? MAL_RX_WRAP : 0);              \
+       EMAC_CDRXSYNC((sc), (x), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); \
+} while (/*CONSTCOND*/0)
+
+#define        EMAC_WRITE(sc, reg, val) \
+       bus_space_write_stream_4((sc)->sc_st, (sc)->sc_sh, (reg), (val))
+#define        EMAC_READ(sc, reg) \
+       bus_space_read_stream_4((sc)->sc_st, (sc)->sc_sh, (reg))
+
 static int     emac_match(struct device *, struct cfdata *, void *);
 static void    emac_attach(struct device *, struct device *, void *);
+
+static int     emac_add_rxbuf(struct emac_softc *, int);
+static int     emac_init(struct ifnet *);
+static int     emac_ioctl(struct ifnet *, u_long, caddr_t);
+static void    emac_reset(struct emac_softc *);
+static void    emac_rxdrain(struct emac_softc *);
+static int     emac_txreap(struct emac_softc *);
+static void    emac_shutdown(void *);
+static void    emac_start(struct ifnet *);
+static void    emac_stop(struct ifnet *, int);
+static void    emac_watchdog(struct ifnet *);
+
+static int     emac_wol_intr(void *);
+static int     emac_serr_intr(void *);
+static int     emac_txeob_intr(void *);
+static int     emac_rxeob_intr(void *);
+static int     emac_txde_intr(void *);
+static int     emac_rxde_intr(void *);
 static int     emac_intr(void *);
 
+static int     emac_mediachange(struct ifnet *);
+static void    emac_mediastatus(struct ifnet *, struct ifmediareq *);
+static int     emac_mii_readreg(struct device *, int, int);
+static void    emac_mii_statchg(struct device *);
+static void    emac_mii_tick(void *);
+static uint32_t        emac_mii_wait(struct emac_softc *);
+static void    emac_mii_writereg(struct device *, int, int, int);
+
+int            emac_copy_small = 0;
+
 struct cfattach emac_ca = {
        sizeof(struct emac_softc), emac_match, emac_attach
 };
 
-static int probe_done = 0;
-
 static int
 emac_match(struct device *parent, struct cfdata *cf, void *aux)
 {
+       struct mainbus_attach_args *maa = aux;
 
-       /*
-        * XXX probe!
-        * This won't work on some of the NP family processors that have
-        * multiple EMACs
-        */
+       /* match only on-chip ethernet devices */
+       if (strcmp(maa->mb_name, cf->cf_driver->cd_name) == 0)
+               return (1);
 
-       if (probe_done)
-               return 0;
-
-       probe_done = 1;
-       return 1;
+       return (0);
 }
 
 static void
@@ -111,23 +292,1199 @@
 {
        struct mainbus_attach_args *maa = aux;
        struct emac_softc *sc = (struct emac_softc *)self;
+       struct ifnet *ifp = &sc->sc_ethercom.ec_if;



Home | Main Index | Thread Index | Old Index