summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/dev/dev_tulip.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/dev/dev_tulip.c')
-rw-r--r--cfe/cfe/dev/dev_tulip.c2985
1 files changed, 2985 insertions, 0 deletions
diff --git a/cfe/cfe/dev/dev_tulip.c b/cfe/cfe/dev/dev_tulip.c
new file mode 100644
index 0000000..dec6b56
--- /dev/null
+++ b/cfe/cfe/dev/dev_tulip.c
@@ -0,0 +1,2985 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * DC21x4x Ethernet Driver File: dev_tulip.c
+ *
+ *********************************************************************
+ *
+ * Copyright 2000,2001,2002,2003
+ * Broadcom Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and
+ * conditions. Subject to these conditions, you may download,
+ * copy, install, use, modify and distribute modified or unmodified
+ * copies of this software in source and/or binary form. No title
+ * or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions
+ * as they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or
+ * logo of Broadcom Corporation. The "Broadcom Corporation"
+ * name may not be used to endorse or promote products derived
+ * from this software without the prior written permission of
+ * Broadcom Corporation.
+ *
+ * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
+ * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
+ * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR 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), EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************* */
+
+#include "sbmips.h"
+
+#ifndef _SB_MAKE64
+#define _SB_MAKE64(x) ((uint64_t)(x))
+#endif
+#ifndef _SB_MAKEMASK1
+#define _SB_MAKEMASK1(n) (_SB_MAKE64(1) << _SB_MAKE64(n))
+#endif
+
+#include "lib_types.h"
+#include "lib_hssubr.h"
+#include "lib_malloc.h"
+#include "lib_string.h"
+#define blockcopy memcpy
+#include "lib_printf.h"
+#include "lib_queue.h"
+
+#include "cfe_iocb.h"
+#include "cfe_device.h"
+#include "cfe_ioctl.h"
+#include "cfe_timer.h"
+#include "cfe_error.h"
+#include "cfe_irq.h"
+
+#include "pcivar.h"
+#include "pcireg.h"
+
+#include "dc21143.h"
+#include "mii.h"
+
+/* This is a driver for specific configurations of the DC21040, DC21041,
+ DC21140A and DC21143, not a generic Tulip driver. The prefix
+ "tulip_" is used to indicate generic Tulip functions, while
+ "dc21040_", "dc21041_", "dc21140_" or "dc21143_" indicates functions
+ specific to a chip variant.
+
+ The 21041 driver assumes a 10BT HD interface, since autonegotiation
+ is known to be broken in the early revisons of that chip. Example
+ cards come from DEC and SMC. Essentially the same driver is used
+ for 21040 cards.
+
+ The 21140 driver assumes that the PHY uses a standard MII interface
+ for both 100BT and 10BT. Example cards come from DEC (National DP83840
+ plus Twister PHY) and Netgear (Level One PHY).
+
+ Some early 21140 boards are exceptions and use SYM plus SRL
+ with different PHY chips for 10 and 100 (limited support).
+
+ The 21143 driver assumes by default that the PHY uses the SYM ("5
+ wire") interface for 100BT with pass-through for 10BT. Example
+ cards come from DEC (MicroLinear ML6694 PHY) and Znyx (QS6611 or
+ Kendin KS8761 PHY). It also supports an MII interface for
+ recognized adapters. An example card comes from Adaptec (National
+ DP83840A and Twister PHY). There is no support for AUI interfaces.
+
+ This SB1250 version takes advantage of DMA coherence and uses
+ "preserve bit lanes" addresses for all accesses that cross the
+ ZBbus-PCI bridge. */
+
+#ifndef TULIP_DEBUG
+#define TULIP_DEBUG 0
+#endif
+
+#ifndef TULIP_TUNE
+#define TULIP_TUNE 1
+#endif
+
+/* Set IPOLL to drive processing through the pseudo-interrupt
+ dispatcher. Set XPOLL to drive processing by an external polling
+ agent. Setting both is ok. */
+
+#ifndef IPOLL
+#define IPOLL 0
+#endif
+#ifndef XPOLL
+#define XPOLL 1
+#endif
+
+#define ENET_ADDR_LEN 6 /* size of an ethernet address */
+#define MAX_ETHER_PACK 1518 /* max size of a packet */
+#define CRC_SIZE 4 /* size of CRC field */
+
+/* Packet buffers. For tulip, the packet must be aligned to a 32-bit
+ word boundary, and we would like it aligned to a cache line
+ boundary for performance. */
+
+#define CACHE_ALIGN 32
+
+#if __long64
+typedef struct eth_pkt_s {
+ queue_t next; /* 16 */
+ uint8_t *buffer; /* 8 */
+ uint32_t flags; /* 4 */
+ int32_t length; /* 4 */
+ uint8_t data[MAX_ETHER_PACK];
+} eth_pkt_t;
+#else
+typedef struct eth_pkt_s {
+ queue_t next; /* 8 */
+ uint8_t *buffer; /* 4 */
+ uint32_t flags; /* 4 */
+ int32_t length; /* 4 */
+ uint32_t unused[3]; /* 12 */
+ uint8_t data[MAX_ETHER_PACK];
+} eth_pkt_t;
+#endif
+
+#define ETH_PKTBUF_LINES ((sizeof(eth_pkt_t) + (CACHE_ALIGN-1))/CACHE_ALIGN)
+#define ETH_PKTBUF_SIZE (ETH_PKTBUF_LINES*CACHE_ALIGN)
+#define ETH_PKTBUF_OFFSET (offsetof(eth_pkt_t, data))
+
+#define ETH_PKT_BASE(data) ((eth_pkt_t *)((data) - ETH_PKTBUF_OFFSET))
+
+/* packet flags */
+#define ETH_TX_SETUP 1 /* assumes Perfect Filtering format */
+
+static void
+show_packet(char c, eth_pkt_t *pkt)
+{
+ int i;
+ int n = (pkt->length < 32 ? pkt->length : 32);
+
+ xprintf("%c[%4d]:", c, pkt->length);
+ for (i = 0; i < n; i++) {
+ if (i % 4 == 0)
+ xprintf(" ");
+ xprintf("%02x", pkt->buffer[i]);
+ }
+ xprintf("\n");
+}
+
+
+/* Descriptor structures */
+
+typedef struct rx_dscr {
+ uint32_t rxd_flags;
+ uint32_t rxd_bufsize;
+ pci_addr_t rxd_bufaddr1;
+ pci_addr_t rxd_bufaddr2;
+} rx_dscr;
+
+typedef struct tx_dscr {
+ uint32_t txd_flags;
+ uint32_t txd_bufsize;
+ pci_addr_t txd_bufaddr1;
+ pci_addr_t txd_bufaddr2;
+} tx_dscr;
+
+/* CAM structure */
+
+typedef union {
+ struct {
+ uint32_t physical[CAM_PERFECT_ENTRIES][3];
+ } p;
+ struct {
+ uint32_t hash[32];
+ uint32_t mbz[7];
+ uint32_t physical[3];
+ } h;
+} tulip_cam;
+
+
+/* Driver data structures */
+
+typedef enum {
+ eth_state_uninit,
+ eth_state_setup,
+ eth_state_off,
+ eth_state_on,
+ eth_state_broken
+} eth_state_t;
+
+#define ETH_PKTPOOL_SIZE 32
+#define ETH_PKT_SIZE MAX_ETHER_PACK
+
+typedef struct tulip_softc {
+ uint32_t membase;
+ uint8_t irq; /* interrupt mapping (used if IPOLL) */
+ pcitag_t tag; /* tag for configuration registers */
+
+ uint8_t hwaddr[ENET_ADDR_LEN];
+ uint16_t device; /* chip device code */
+ uint8_t revision; /* chip revision and step (Table 3-7) */
+
+ /* current state */
+ eth_state_t state;
+
+ /* These fields are the chip startup values. */
+// uint16_t media; /* media type */
+ uint32_t opmode; /* operating mode */
+ uint32_t intmask; /* interrupt mask */
+ uint32_t gpdata; /* output bits for csr15 (21143) */
+
+ /* These fields are set before calling dc21x4x_hwinit */
+ int linkspeed; /* encodings from cfe_ioctl */
+ int loopback;
+
+ /* Packet free list */
+ queue_t freelist;
+ uint8_t *pktpool;
+ queue_t rxqueue;
+
+ /* The descriptor tables */
+ uint8_t *rxdscrmem; /* receive descriptors */
+ uint8_t *txdscrmem; /* transmit descriptors */
+
+ /* These fields keep track of where we are in tx/rx processing */
+ volatile rx_dscr *rxdscr_start; /* beginning of ring */
+ volatile rx_dscr *rxdscr_end; /* end of ring */
+ volatile rx_dscr *rxdscr_remove; /* next one we expect tulip to use */
+ volatile rx_dscr *rxdscr_add; /* next place to put a buffer */
+ int rxdscr_onring;
+
+ volatile tx_dscr *txdscr_start; /* beginning of ring */
+ volatile tx_dscr *txdscr_end; /* end of ring */
+ volatile tx_dscr *txdscr_remove; /* next one we will use for tx */
+ volatile tx_dscr *txdscr_add; /* next place to put a buffer */
+
+ cfe_devctx_t *devctx;
+
+ /* These fields describe the PHY */
+ enum {SRL, MII, SYM} phy_type;
+ int mii_addr;
+
+ /* Statistics */
+ uint32_t inpkts;
+ uint32_t outpkts;
+ uint32_t interrupts;
+ uint32_t rx_interrupts;
+ uint32_t tx_interrupts;
+ uint32_t bus_errors;
+} tulip_softc;
+
+
+/* Entry to and exit from critical sections (currently relative to
+ interrupts only, not SMP) */
+
+#if CFG_INTERRUPTS
+#define CS_ENTER(sc) cfe_disable_irq(sc->irq)
+#define CS_EXIT(sc) cfe_enable_irq(sc->irq)
+#else
+#define CS_ENTER(sc) ((void)0)
+#define CS_EXIT(sc) ((void)0)
+#endif
+
+
+/* Driver parameterization */
+
+#define MAXRXDSCR 32
+#define MAXTXDSCR 32
+#define MINRXRING 8
+
+#define MEDIA_UNKNOWN 0
+#define MEDIA_AUI 1
+#define MEDIA_BNC 2
+#define MEDIA_UTP_FULL_DUPLEX 3
+#define MEDIA_UTP_NO_LINK_TEST 4
+#define MEDIA_UTP 5
+
+/* Prototypes */
+
+static void tulip_ether_probe(cfe_driver_t *drv,
+ unsigned long probe_a, unsigned long probe_b,
+ void *probe_ptr);
+
+
+/* Address mapping macros */
+
+/* Note that PTR_TO_PHYS only works with 32-bit addresses, but then
+ so does the Tulip. */
+#define PTR_TO_PHYS(x) (K0_TO_PHYS((uintptr_t)(x)))
+#define PHYS_TO_PTR(a) ((uint8_t *)PHYS_TO_K0(a))
+
+/* All mappings through the PCI host bridge use match bits mode. */
+#define PHYS_TO_PCI(a) ((uint32_t) (a) | 0x20000000)
+#define PCI_TO_PHYS(a) ((uint32_t) (a) & 0x1FFFFFFF)
+
+#define PCI_TO_PTR(a) (PHYS_TO_PTR(PCI_TO_PHYS(a)))
+#define PTR_TO_PCI(x) (PHYS_TO_PCI(PTR_TO_PHYS(x)))
+
+#if __long64
+#define READCSR(sc,csr) \
+ (*((volatile uint32_t *) (PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)))))
+
+#define WRITECSR(sc,csr,val) \
+ (*((volatile uint32_t *) \
+ (PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)))) = (val))
+
+#else
+#define READCSR(sc,csr) \
+ (hs_read32(PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr))))
+
+#define WRITECSR(sc,csr,val) \
+ (hs_write32(PHYS_TO_XKSEG_UNCACHED((sc)->membase + (csr)), (val)))
+
+#endif
+
+#define RESET_ADAPTER(sc) \
+ { \
+ WRITECSR((sc), R_CSR_BUSMODE, M_CSR0_SWRESET); \
+ cfe_sleep(CFE_HZ/10); \
+ }
+
+
+/* Debugging */
+
+static void
+dumpstat(tulip_softc *sc)
+{
+ xprintf("-- CSR 5 = %08X CSR 6 = %08x\n",
+ READCSR(sc, R_CSR_STATUS), READCSR(sc, R_CSR_OPMODE));
+}
+
+static void
+dumpcsrs(tulip_softc *sc)
+{
+ int idx;
+
+ xprintf("-------------\n");
+ for (idx = 0; idx < 16; idx++) {
+ xprintf("CSR %2d = %08X\n", idx, READCSR(sc, idx*8));
+ }
+ xprintf("-------------\n");
+}
+
+
+/* Packet management */
+
+/* *********************************************************************
+ * ETH_ALLOC_PKT(sc)
+ *
+ * Allocate a packet from the free list.
+ *
+ * Input parameters:
+ * sc - eth structure
+ *
+ * Return value:
+ * pointer to packet structure, or NULL if none available
+ ********************************************************************* */
+static eth_pkt_t *
+eth_alloc_pkt(tulip_softc *sc)
+{
+ eth_pkt_t *pkt;
+
+ CS_ENTER(sc);
+ pkt = (eth_pkt_t *) q_deqnext(&sc->freelist);
+ CS_EXIT(sc);
+ if (!pkt) return NULL;
+
+ pkt->buffer = pkt->data;
+ pkt->length = ETH_PKT_SIZE;
+ pkt->flags = 0;
+
+ return pkt;
+}
+
+
+/* *********************************************************************
+ * ETH_FREE_PKT(sc,pkt)
+ *
+ * Return a packet to the free list
+ *
+ * Input parameters:
+ * sc - sbmac structure
+ * pkt - packet to return
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void
+eth_free_pkt(tulip_softc *sc, eth_pkt_t *pkt)
+{
+ CS_ENTER(sc);
+ q_enqueue(&sc->freelist, &pkt->next);
+ CS_EXIT(sc);
+}
+
+
+/* *********************************************************************
+ * ETH_INITFREELIST(sc)
+ *
+ * Initialize the buffer free list for this mac. The memory
+ * allocated to the free list is carved up and placed on a linked
+ * list of buffers for use by the mac.
+ *
+ * Input parameters:
+ * sc - eth structure
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void
+eth_initfreelist(tulip_softc *sc)
+{
+ int idx;
+ uint8_t *ptr;
+ eth_pkt_t *pkt;
+
+ q_init(&sc->freelist);
+
+ ptr = sc->pktpool;
+ for (idx = 0; idx < ETH_PKTPOOL_SIZE; idx++) {
+ pkt = (eth_pkt_t *) ptr;
+ eth_free_pkt(sc, pkt);
+ ptr += ETH_PKTBUF_SIZE;
+ }
+}
+
+
+/* Utilities */
+
+static const char *
+tulip_devname(tulip_softc *sc)
+{
+ return (sc->devctx != NULL ? cfe_device_name(sc->devctx) : "eth?");
+}
+
+
+/* Descriptor ring management */
+
+static int
+tulip_add_rcvbuf(tulip_softc *sc, eth_pkt_t *pkt)
+{
+ volatile rx_dscr *rxd;
+ volatile rx_dscr *nextrxd;
+ uint32_t flags = 0;
+
+ rxd = sc->rxdscr_add;
+
+ /* Figure out where the next descriptor will go */
+ nextrxd = rxd+1;
+ if (nextrxd == sc->rxdscr_end) {
+ nextrxd = sc->rxdscr_start;
+ flags = M_RDES1_ENDOFRING;
+ }
+
+ /*
+ * If the next one is the same as our remove pointer,
+ * the ring is considered full. (it actually has room for
+ * one more, but we reserve the remove == add case for "empty")
+ */
+ if (nextrxd == sc->rxdscr_remove) return -1;
+
+ rxd->rxd_bufsize = V_RDES1_BUF1SIZE(1520) | flags;
+ rxd->rxd_bufaddr1 = PTR_TO_PCI(pkt->buffer);
+ rxd->rxd_bufaddr2 = 0;
+ rxd->rxd_flags = M_RDES0_OWNADAP;
+
+ /* success, advance the pointer */
+ sc->rxdscr_add = nextrxd;
+ CS_ENTER(sc);
+ sc->rxdscr_onring++;
+ CS_EXIT(sc);
+
+ return 0;
+}
+
+static void
+tulip_fillrxring(tulip_softc *sc)
+{
+ eth_pkt_t *pkt;
+
+ while (1) {
+ CS_ENTER(sc);
+ if (sc->rxdscr_onring >= MINRXRING) {
+ CS_EXIT(sc);
+ break;
+ }
+ CS_EXIT(sc);
+ pkt = eth_alloc_pkt(sc);
+ if (pkt == NULL) {
+ /* could not allocate a buffer */
+ break;
+ }
+ if (tulip_add_rcvbuf(sc, pkt) != 0) {
+ /* could not add buffer to ring */
+ eth_free_pkt(sc, pkt);
+ break;
+ }
+ }
+}
+
+
+/* *********************************************************************
+ * TULIP_RX_CALLBACK(sc, pkt)
+ *
+ * Receive callback routine. This routine is invoked when a
+ * buffer queued for receives is filled. In this simple driver,
+ * all we do is add the packet to a per-MAC queue for later
+ * processing, and try to put a new packet in the place of the one
+ * that was removed from the queue.
+ *
+ * Input parameters:
+ * sc - interface
+ * ptk - packet context (eth_pkt structure)
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void
+tulip_rx_callback(tulip_softc *sc, eth_pkt_t *pkt)
+{
+ if (TULIP_DEBUG) show_packet('>', pkt); /* debug */
+
+ CS_ENTER(sc);
+ q_enqueue(&sc->rxqueue, &pkt->next);
+ CS_EXIT(sc);
+ sc->inpkts++;
+
+ tulip_fillrxring(sc);
+}
+
+
+static void
+tulip_procrxring(tulip_softc *sc)
+{
+ volatile rx_dscr *rxd;
+ eth_pkt_t *pkt;
+ eth_pkt_t *newpkt;
+ uint32_t flags;
+
+ for (;;) {
+ rxd = (volatile rx_dscr *) sc->rxdscr_remove;
+
+ flags = rxd->rxd_flags;
+ if (flags & M_RDES0_OWNADAP) {
+ /* end of ring, no more packets */
+ break;
+ }
+
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufaddr1));
+
+ /* Drop error packets */
+ if (flags & M_RDES0_ERRORSUM) {
+ xprintf("%s: rx error %04X\n", tulip_devname(sc), flags & 0xFFFF);
+ tulip_add_rcvbuf(sc, pkt);
+ goto next;
+ }
+
+ /* Pass up the packet */
+ pkt->length = G_RDES0_FRAMELEN(flags) - CRC_SIZE;
+ tulip_rx_callback(sc, pkt);
+
+ /* put a buffer back on the ring to replace this one */
+ newpkt = eth_alloc_pkt(sc);
+ if (newpkt) tulip_add_rcvbuf(sc, newpkt);
+
+next:
+ /* update the pointer, accounting for buffer wrap. */
+ rxd++;
+ if (rxd == sc->rxdscr_end)
+ rxd = sc->rxdscr_start;
+
+ sc->rxdscr_remove = (rx_dscr *) rxd;
+ CS_ENTER(sc);
+ sc->rxdscr_onring--;
+ CS_EXIT(sc);
+ }
+}
+
+
+static int
+tulip_add_txbuf(tulip_softc *sc, eth_pkt_t *pkt)
+{
+ volatile tx_dscr *txd;
+ volatile tx_dscr *nexttxd;
+ uint32_t bufsize = 0;
+
+ txd = sc->txdscr_add;
+
+ /* Figure out where the next descriptor will go */
+ nexttxd = (txd+1);
+ if (nexttxd == sc->txdscr_end) {
+ nexttxd = sc->txdscr_start;
+ bufsize = M_TDES1_ENDOFRING;
+ }
+
+ /* If the next one is the same as our remove pointer,
+ the ring is considered full. (it actually has room for
+ one more, but we reserve the remove == add case for "empty") */
+
+ if (nexttxd == sc->txdscr_remove) return -1;
+
+ bufsize |= V_TDES1_BUF1SIZE(pkt->length) |
+ M_TDES1_FIRSTSEG | M_TDES1_LASTSEG | M_TDES1_INTERRUPT;
+ if (pkt->flags & ETH_TX_SETUP) {
+ /* For a setup packet, FIRSTSEG and LASTSEG should be clear (!) */
+ bufsize ^= M_TDES1_SETUP | M_TDES1_FIRSTSEG | M_TDES1_LASTSEG;
+ }
+ txd->txd_bufsize = bufsize;
+ txd->txd_bufaddr1 = PTR_TO_PCI(pkt->buffer);
+ txd->txd_bufaddr2 = 0;
+ txd->txd_flags = M_TDES0_OWNADAP;
+
+ /* success, advance the pointer */
+ sc->txdscr_add = nexttxd;
+
+ return 0;
+}
+
+
+static int
+tulip_transmit(tulip_softc *sc,eth_pkt_t *pkt)
+{
+ int rv;
+
+ if (TULIP_DEBUG) show_packet('<', pkt); /* debug */
+
+ rv = tulip_add_txbuf(sc, pkt);
+ sc->outpkts++;
+
+ WRITECSR(sc, R_CSR_TXPOLL, 1);
+ return rv;
+}
+
+
+static void
+tulip_proctxring(tulip_softc *sc)
+{
+ volatile tx_dscr *txd;
+ eth_pkt_t *pkt;
+ uint32_t flags;
+
+ for (;;) {
+ txd = (volatile tx_dscr *) sc->txdscr_remove;
+
+ if (txd == sc->txdscr_add) {
+ /* ring is empty, no buffers to process */
+ break;
+ }
+
+ flags = txd->txd_flags;
+ if (flags & M_TDES0_OWNADAP) {
+ /* Reached a packet still being transmitted */
+ break;
+ }
+
+ /* Check for a completed setup packet */
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufaddr1));
+ if (pkt->flags & ETH_TX_SETUP) {
+ if (sc->state == eth_state_setup) {
+ uint32_t opmode;
+
+ /* check flag bits */
+ opmode = READCSR(sc, R_CSR_OPMODE);
+ opmode |= M_CSR6_RXSTART;
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+ sc->inpkts = sc->outpkts = 0;
+ sc->state =eth_state_on;
+ }
+ pkt->flags &=~ ETH_TX_SETUP;
+ }
+
+ /* Just free the packet */
+ eth_free_pkt(sc, pkt);
+
+ /* update the pointer, accounting for buffer wrap. */
+ txd++;
+ if (txd == sc->txdscr_end)
+ txd = sc->txdscr_start;
+
+ sc->txdscr_remove = (tx_dscr *) txd;
+ }
+}
+
+
+static void
+tulip_initrings(tulip_softc *sc)
+{
+ volatile tx_dscr *txd;
+ volatile rx_dscr *rxd;
+
+ /* Claim ownership of all descriptors for the driver */
+
+ for (txd = sc->txdscr_start; txd != sc->txdscr_end; txd++)
+ txd->txd_flags = 0;
+ for (rxd = sc->rxdscr_start; rxd != sc->rxdscr_end; rxd++)
+ rxd->rxd_flags = 0;
+
+ /* Init the ring pointers */
+
+ sc->txdscr_add = sc->txdscr_remove = sc->txdscr_start;
+ sc->rxdscr_add = sc->rxdscr_remove = sc->rxdscr_start;
+ sc->rxdscr_onring = 0;
+
+ /* Add stuff to the receive ring */
+
+ tulip_fillrxring(sc);
+}
+
+
+static int
+tulip_init(tulip_softc *sc)
+{
+ /* Allocate descriptor rings */
+ sc->rxdscrmem = KMALLOC(MAXRXDSCR*sizeof(rx_dscr), sizeof(rx_dscr));
+ sc->txdscrmem = KMALLOC(MAXTXDSCR*sizeof(tx_dscr), sizeof(tx_dscr));
+
+ /* Allocate buffer pool */
+ sc->pktpool = KMALLOC(ETH_PKTPOOL_SIZE*ETH_PKTBUF_SIZE, CACHE_ALIGN);
+ eth_initfreelist(sc);
+ q_init(&sc->rxqueue);
+
+ /* Fill in pointers to the rings */
+ sc->rxdscr_start = (rx_dscr *) (sc->rxdscrmem);
+ sc->rxdscr_end = sc->rxdscr_start + MAXRXDSCR;
+ sc->rxdscr_add = sc->rxdscr_start;
+ sc->rxdscr_remove = sc->rxdscr_start;
+ sc->rxdscr_onring = 0;
+
+ sc->txdscr_start = (tx_dscr *) (sc->txdscrmem);
+ sc->txdscr_end = sc->txdscr_start + MAXTXDSCR;
+ sc->txdscr_add = sc->txdscr_start;
+ sc->txdscr_remove = sc->txdscr_start;
+
+ tulip_initrings(sc);
+
+ return 0;
+}
+
+
+static void
+tulip_resetrings(tulip_softc *sc)
+{
+ volatile tx_dscr *txd;
+ volatile rx_dscr *rxd;
+ eth_pkt_t *pkt;
+
+ /* Free already-sent descriptors and buffers */
+ tulip_proctxring(sc);
+
+ /* Free any pending but unsent */
+ txd = (volatile tx_dscr *) sc->txdscr_remove;
+ while (txd != sc->txdscr_add) {
+ txd->txd_flags &=~ M_TDES0_OWNADAP;
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufaddr1));
+ eth_free_pkt(sc, pkt);
+
+ txd++;
+ if (txd == sc->txdscr_end)
+ txd = sc->txdscr_start;
+ }
+ sc->txdscr_add = sc->txdscr_remove;
+
+ /* Discard any received packets as well as all free buffers */
+ rxd = (volatile rx_dscr *) sc->rxdscr_remove;
+ while (rxd != sc->rxdscr_add) {
+ rxd->rxd_flags &=~ M_RDES0_OWNADAP;
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufaddr1));
+ eth_free_pkt(sc, pkt);
+
+ rxd++;
+ if (rxd == sc->rxdscr_end)
+ rxd = sc->rxdscr_start;
+ CS_ENTER(sc);
+ sc->rxdscr_onring--;
+ CS_EXIT(sc);
+ }
+
+ /* Reestablish the initial state. */
+ tulip_initrings(sc);
+}
+
+
+/* CRCs */
+
+#define IEEE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- either endian */
+
+static uint32_t
+tulip_crc32(const uint8_t *databuf, unsigned int datalen)
+{
+ unsigned int idx, bit, data;
+ uint32_t crc;
+
+ crc = 0xFFFFFFFFUL;
+ for (idx = 0; idx < datalen; idx++)
+ for (data = *databuf++, bit = 0; bit < 8; bit++, data >>= 1)
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? IEEE_CRC32_POLY : 0);
+ return crc;
+}
+
+#define tulip_mchash(mca) (tulip_crc32((mca), 6) & 0x1FF)
+
+
+/* Serial ROM access */
+
+/****************************************************************************
+ * tulip_spin(sc, ns)
+ *
+ * Spin for a short interval (nominally in nanoseconds)
+ *
+ * Input Parameters: ns - minimum required nsec.
+ *
+ * The delay loop uses uncached PCI reads, each of which requires
+ * at least 3 PCI bus clocks (45 ns at 66 MHz) to complete. The
+ * actual delay will be longer (much longer if preempted).
+ ***************************************************************************/
+
+#define PCI_MIN_DELAY 45
+
+static void
+tulip_spin(tulip_softc *sc, long nanoseconds)
+{
+ long delay;
+ volatile uint32_t t;
+
+ for (delay = nanoseconds; delay > 0; delay -= PCI_MIN_DELAY)
+ t = READCSR(sc, R_CSR_BUSMODE);
+}
+
+
+/*
+ * Delays below are chosen to meet specs for NS93C64 (slow M variant).
+ * Current parts are faster.
+ * Reference: NS Memory Data Book, 1994
+ */
+
+#define SROM_SIZE 128
+#define SROM_MAX_CYCLES 32
+
+#define SROM_CMD_BITS 3
+#define SROM_ADDR_BITS 6
+
+#define K_SROM_READ_CMD 06
+#define K_SROM_WRITE_CMD 05
+
+#define SROM_VENDOR_INDEX 0x00
+#define SROM_FORMAT_INDEX 0x12
+#define SROM_ADDR_INDEX 0x14
+
+#define SROM_DEVICE0_INDEX 0x1A
+#define SROM_LEAF0_OFFSET_INDEX 0x1B
+
+#define SROM_CRC_INDEX (SROM_SIZE-2)
+/* Note recent chips supporting wake-on-lan have CRC in bytes 94, 95 */
+
+#define SROM_WORD(rom,offset) ((rom)[offset] | ((rom)[offset+1] << 8))
+
+static void
+srom_idle_state(tulip_softc *sc)
+{
+ uint32_t csr9;
+ unsigned int i;
+
+ csr9 = READCSR(sc, R_CSR_ROM_MII);
+
+ csr9 |= M_CSR9_SROMCHIPSEL;
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 100); /* CS setup (Tcss=100) */
+
+ /* Run the clock through the maximum number of pending read cycles */
+ for (i = 0; i < SROM_MAX_CYCLES*2; i++) {
+ csr9 ^= M_CSR9_SROMCLOCK;
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 1000); /* SK period (Fsk=0.5MHz) */
+ }
+
+ /* Deassert SROM Chip Select */
+ csr9 &=~ M_CSR9_SROMCHIPSEL;
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 50); /* CS recovery (Tsks=50) */
+}
+
+static void
+srom_send_command_bit(tulip_softc *sc, unsigned int data)
+{
+ uint32_t csr9;
+
+ csr9 = READCSR(sc, R_CSR_ROM_MII);
+
+ /* Place the data bit on the bus */
+ if (data == 1)
+ csr9 |= M_CSR9_SROMDATAIN;
+ else
+ csr9 &=~ M_CSR9_SROMDATAIN;
+
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 360); /* setup: Tdis=200 */
+
+ /* Now clock the data into the SROM */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_SROMCLOCK);
+ tulip_spin(sc, 900); /* clock high, Tskh=500 */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 450); /* clock low, Tskl=250 */
+
+ /* Now clear the data bit */
+ csr9 &=~ M_CSR9_SROMDATAIN; /* data invalid, Tidh=20 for SK^ */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 270); /* min cycle, 1/Fsk=2000 */
+}
+
+static uint16_t
+srom_read_bit(tulip_softc *sc)
+{
+ uint32_t csr9;
+
+ csr9 = READCSR(sc, R_CSR_ROM_MII);
+
+ /* Generate a clock cycle before doing a read */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_SROMCLOCK); /* rising edge */
+ tulip_spin(sc, 1000); /* clock high, Tskh=500, Tpd=1000 */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9); /* falling edge */
+ tulip_spin(sc, 1000); /* clock low, 1/Fsk=2000 */
+
+ csr9 = READCSR(sc, R_CSR_ROM_MII);
+ return ((csr9 & M_CSR9_SROMDATAOUT) != 0 ? 1 : 0);
+}
+
+#define CMD_BIT_MASK (1 << (SROM_CMD_BITS+SROM_ADDR_BITS-1))
+
+static uint16_t
+srom_read_word(tulip_softc *sc, unsigned int index)
+{
+ uint16_t command, word;
+ uint32_t csr9;
+ unsigned int i;
+
+ csr9 = READCSR(sc, R_CSR_ROM_MII) | M_CSR9_SROMCHIPSEL;
+
+ /* Assert the SROM CS line */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 100); /* CS setup, Tcss = 100 */
+
+ /* Send the read command to the SROM */
+ command = (K_SROM_READ_CMD << SROM_ADDR_BITS) | index;
+ for (i = 0; i < SROM_CMD_BITS+SROM_ADDR_BITS; i++) {
+ srom_send_command_bit(sc, (command & CMD_BIT_MASK) != 0 ? 1 : 0);
+ command <<= 1;
+ }
+
+ /* Now read the bits from the SROM (MSB first) */
+ word = 0;
+ for (i = 0; i < 16; ++i) {
+ word <<= 1;
+ word |= srom_read_bit(sc);
+ }
+
+ /* Clear the SROM CS Line, CS hold, Tcsh = 0 */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9 &~ M_CSR9_SROMCHIPSEL);
+
+ return word;
+}
+
+
+/****************************************************************************
+ * srom_calc_crc()
+ *
+ * Calculate the CRC of the SROM and return it. We compute the
+ * CRC per Appendix A of the 21140A ROM/external register data
+ * sheet (EC-QPQWA-TE).
+ ***************************************************************************/
+
+static uint16_t
+srom_calc_crc(tulip_softc *sc, uint8_t srom[], int length)
+{
+ uint32_t crc = tulip_crc32(srom, length) ^ 0xFFFFFFFF;
+
+ return (uint16_t)(crc & 0xFFFF);
+}
+
+/****************************************************************************
+ * srom_read_all(sc, uint8_t dest)
+ *
+ * Read the entire SROM into the srom array
+ *
+ * Input parameters:
+ * sc - tulip state
+ ***************************************************************************/
+
+static int
+srom_read_all(tulip_softc *sc, uint8_t dest[])
+{
+ int i;
+ uint16_t crc, temp;
+
+ WRITECSR(sc, R_CSR_ROM_MII, M_CSR9_SERROMSEL|M_CSR9_ROMREAD);
+
+ srom_idle_state(sc);
+
+ for (i = 0; i < SROM_SIZE/2; i++) {
+ temp = srom_read_word(sc, i);
+ dest[2*i] = temp & 0xFF;
+ dest[2*i+1] =temp >> 8;
+ }
+
+ WRITECSR(sc, R_CSR_ROM_MII, 0); /* CS hold, Tcsh=0 */
+
+ crc = srom_calc_crc(sc, dest, SROM_CRC_INDEX);
+ if (crc != SROM_WORD(dest, SROM_CRC_INDEX)) {
+ crc = srom_calc_crc(sc, dest, 94); /* "alternative" */
+ if (crc != SROM_WORD(dest, 94)) {
+ xprintf("%s: Invalid SROM CRC, calc %04x, stored %04x\n",
+ tulip_devname(sc), crc, SROM_WORD(dest, 94));
+ return 0/*-1*/;
+ }
+ }
+ return 0;
+}
+
+static int
+srom_read_addr(tulip_softc *sc, uint8_t buf[])
+{
+ uint8_t srom[SROM_SIZE];
+
+ if (srom_read_all(sc, srom) == 0) {
+ memcpy(buf, &srom[SROM_ADDR_INDEX], ENET_ADDR_LEN);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+/****************************************************************************
+ * earom_read_all(sc, uint8_t dest)
+ *
+ * Read the entire Ethernet address ROM into the srom array (21040 only)
+ *
+ * Input parameters:
+ * sc - tulip state
+ ***************************************************************************/
+
+static int
+earom_read_all(tulip_softc *sc, uint8_t dest[])
+{
+ int i;
+ uint32_t csr9;
+
+ WRITECSR(sc, R_CSR_ROM_MII, 0); /* reset pointer */
+
+ for (i = 0; i < SROM_SIZE; i++) {
+ for (;;) {
+ csr9 = READCSR(sc, R_CSR_ROM_MII);
+ if ((csr9 & M_CSR9_DATANOTVALID) == 0)
+ break;
+ POLL(); /* XXX need a timeout */
+ }
+ dest[i] = G_CSR9_ROMDATA(csr9);
+ }
+
+ return 0;
+}
+
+static int
+earom_read_addr(tulip_softc *sc, uint8_t buf[])
+{
+ uint8_t srom[SROM_SIZE];
+
+ if (earom_read_all(sc, srom) == 0) {
+ memcpy(buf, &srom[0], ENET_ADDR_LEN);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+rom_read_all(tulip_softc *sc, uint8_t buf[])
+{
+ if (sc->device == K_PCI_ID_DC21040)
+ return earom_read_all(sc, buf);
+ else
+ return srom_read_all(sc, buf);
+}
+
+static int
+rom_read_addr(tulip_softc *sc, uint8_t buf[])
+{
+ if (sc->device == K_PCI_ID_DC21040)
+ return earom_read_addr(sc, buf);
+ else
+ return srom_read_addr(sc, buf);
+}
+
+#if 0
+static void
+rom_dump(uint8_t srom[])
+{
+ int i;
+
+ xprintf("DC21x4x: SROM data:");
+ for (i = 0; i < SROM_SIZE; i++) {
+ if (i % 16 == 0)
+ xprintf("\n %02x: ", i);
+ xprintf(" %02x", srom[i]);
+ }
+ xprintf("\n");
+}
+#else
+#define rom_dump(srom)
+#endif
+
+
+/****************************************************************************
+ * MII access utility routines
+ ***************************************************************************/
+
+/* MII clock limited to 2.5 MHz, transactions end with MDIO tristated */
+
+static void
+mii_write_bits(tulip_softc *sc, uint32_t data, unsigned int count)
+{
+ uint32_t csr9;
+ uint32_t bitmask;
+
+ csr9 = READCSR(sc, R_CSR_ROM_MII) &~ (M_CSR9_MDC | M_CSR9_MIIMODE);
+
+ for (bitmask = 1 << (count-1); bitmask != 0; bitmask >>= 1) {
+ csr9 &=~ M_CSR9_MDO;
+ if ((data & bitmask) != 0) csr9 |= M_CSR9_MDO;
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+
+ tulip_spin(sc, 2000); /* setup */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC);
+ tulip_spin(sc, 2000); /* hold */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ }
+}
+
+static void
+mii_turnaround(tulip_softc *sc)
+{
+ uint32_t csr9;
+
+ csr9 = READCSR(sc, R_CSR_ROM_MII) | M_CSR9_MIIMODE;
+
+ /* stop driving data */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 2000); /* setup */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC);
+ tulip_spin(sc, 2000); /* clock high */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+
+ /* read back and check for 0 here? */
+}
+
+/****************************************************************************
+ * mii_read_register
+ *
+ * This routine reads a register from the PHY chip using the MII
+ * serial management interface.
+ *
+ * Input parameters:
+ * index - index of register to read (0-31)
+ *
+ * Return value:
+ * word read from register
+ ***************************************************************************/
+
+static uint16_t
+mii_read_register(tulip_softc *sc, unsigned int index)
+{
+ /* Send the command and address to the PHY. The sequence is
+ a synchronization sequence (32 1 bits)
+ a "start" command (2 bits)
+ a "read" command (2 bits)
+ the PHY addr (5 bits)
+ the register index (5 bits)
+ */
+ uint32_t csr9;
+ uint16_t word;
+ int i;
+
+ mii_write_bits(sc, 0xFF, 8);
+ mii_write_bits(sc, 0xFFFFFFFF, 32);
+ mii_write_bits(sc, MII_COMMAND_START, 2);
+ mii_write_bits(sc, MII_COMMAND_READ, 2);
+ mii_write_bits(sc, sc->mii_addr, 5);
+ mii_write_bits(sc, index, 5);
+
+ mii_turnaround(sc);
+
+ csr9 = (READCSR(sc, R_CSR_ROM_MII) &~ M_CSR9_MDC) | M_CSR9_MIIMODE;
+ word = 0;
+
+ for (i = 0; i < 16; i++) {
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 2000); /* clock width low */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9 | M_CSR9_MDC);
+ tulip_spin(sc, 2000); /* clock width high */
+ WRITECSR(sc, R_CSR_ROM_MII, csr9);
+ tulip_spin(sc, 1000); /* output delay */
+ word <<= 1;
+ if ((READCSR(sc, R_CSR_ROM_MII) & M_CSR9_MDI) != 0)
+ word |= 0x0001;
+ }
+
+ return word;
+
+ /* reset to output mode? */
+}
+
+/****************************************************************************
+ * mii_write_register
+ *
+ * This routine writes a register in the PHY chip using the MII
+ * serial management interface.
+ *
+ * Input parameters:
+ * index - index of register to write (0-31)
+ * value - word to write
+ ***************************************************************************/
+
+static void
+mii_write_register(tulip_softc *sc, unsigned int index, uint16_t value)
+{
+ mii_write_bits(sc, 0xFF, 8);
+ mii_write_bits(sc, 0xFFFFFFFF, 32);
+ mii_write_bits(sc, MII_COMMAND_START, 2);
+ mii_write_bits(sc, MII_COMMAND_WRITE, 2);
+ mii_write_bits(sc, sc->mii_addr, 5);
+ mii_write_bits(sc, index, 5);
+ mii_write_bits(sc, MII_COMMAND_ACK, 2);
+ mii_write_bits(sc, value, 16);
+
+ /* reset to input mode? */
+}
+
+
+static int
+mii_probe(tulip_softc *sc)
+{
+ int i;
+ uint16_t id1, id2;
+
+ for (i = 0; i < 32; i++) {
+ sc->mii_addr = i;
+ id1 = mii_read_register(sc, MII_PHYIDR1);
+ id2 = mii_read_register(sc, MII_PHYIDR2);
+ if ((id1 != 0x0000 && id1 != 0xFFFF) ||
+ (id2 != 0x0000 && id2 != 0xFFFF)) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+#if 0
+#define OUI_NAT_SEMI 0x080017
+#define OUI_LEVEL_ONE 0x1E0400 /* 0x00207B, bit reversed and truncated */
+
+static void
+mii_dump(tulip_softc *sc, const char *label)
+{
+ int i;
+ uint16_t r;
+ uint32_t oui, part;
+
+ xprintf("%s\n", label);
+ oui = 0;
+ for (i = 0; i <= 6; ++i) {
+ r = mii_read_register(sc, i);
+ if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r);
+ if (i == MII_PHYIDR1) {
+ oui = r << 6;
+ }
+ else if (i == MII_PHYIDR2) {
+ oui |= (r >> 10) & 0x3F;
+ part = (r >> 4) & 0x3F;
+ }
+ }
+ if (oui == OUI_NAT_SEMI) { /* DP83840, DP83840A */
+ for (i = 0x15; i <= 0x19; ++i) {
+ r = mii_read_register(sc, i);
+ if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r);
+ }
+ }
+ else if (oui == OUI_LEVEL_ONE) { /* LXT970, etc. */
+ for (i = 0x10; i <= 0x14; ++i) {
+ r = mii_read_register(sc, i);
+ if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r);
+ }
+ }
+}
+#else
+#define mii_dump(sc,label)
+#endif
+
+
+/* The following functions are suitable for all tulips with MII
+ interfaces. */
+
+static void
+mii_set_speed(tulip_softc *sc, int speed, int autoneg)
+{
+ uint16_t control;
+ uint16_t pcr;
+ uint32_t opmode = 0;
+
+ /* This is really just for NS DP83840/A. Needed? */
+ pcr = mii_read_register(sc, 0x17);
+ pcr |= (0x400|0x100|0x40|0x20);
+ mii_write_register(sc, 0x17, pcr);
+
+ control = mii_read_register(sc, MII_BMCR);
+
+ if (!autoneg) {
+ control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN);
+ mii_write_register(sc, MII_BMCR, control);
+ control &=~ (BMCR_SPEED0 | BMCR_SPEED1 | BMCR_DUPLEX);
+ }
+
+ switch (speed) {
+ case ETHER_SPEED_10HDX:
+ default:
+ opmode = M_CSR6_SPEED_10_MII;
+ break;
+ case ETHER_SPEED_10FDX:
+ control |= BMCR_DUPLEX;
+ opmode = M_CSR6_SPEED_10_MII | M_CSR6_FULLDUPLEX;
+ break;
+ case ETHER_SPEED_100HDX:
+ control |= BMCR_SPEED100;
+ opmode = M_CSR6_SPEED_100_MII;
+ break;
+ case ETHER_SPEED_100FDX:
+ control |= BMCR_SPEED100 | BMCR_DUPLEX ;
+ opmode = M_CSR6_SPEED_100_MII | M_CSR6_FULLDUPLEX;
+ break;
+ }
+
+ if (!autoneg)
+ mii_write_register(sc, MII_BMCR, control);
+
+ opmode |= M_CSR6_MBO;
+#if TULIP_TUNE
+ opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72);
+#else
+ opmode |= M_CSR6_STOREFWD;
+#endif
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+ mii_dump(sc, "setspeed PHY");
+}
+
+static void
+mii_autonegotiate(tulip_softc *sc)
+{
+ uint16_t control, status, cap;
+ unsigned int timeout;
+ int linkspeed;
+ int autoneg;
+
+ linkspeed = ETHER_SPEED_UNKNOWN;
+
+ /* Read twice to clear latching bits */
+ status = mii_read_register(sc, MII_BMSR);
+ status = mii_read_register(sc, MII_BMSR);
+ mii_dump(sc, "query PHY");
+
+ if ((status & (BMSR_AUTONEG | BMSR_LINKSTAT)) ==
+ (BMSR_AUTONEG | BMSR_LINKSTAT))
+ control = mii_read_register(sc, MII_BMCR);
+ else {
+ /* reset the PHY */
+ mii_write_register(sc, MII_BMCR, BMCR_RESET);
+ timeout = 3000;
+ for (;;) {
+ control = mii_read_register(sc, MII_BMCR);
+ if ((control && BMCR_RESET) == 0) break;
+ cfe_sleep(CFE_HZ/2);
+ timeout -= 500;
+ if (timeout <= 0) break;
+ }
+ if ((control & BMCR_RESET) != 0) {
+ xprintf("%s: PHY reset failed\n", tulip_devname(sc));
+ return;
+ }
+
+ status = mii_read_register(sc, MII_BMSR);
+ cap = ((status >> 6) & (ANAR_TXFD | ANAR_TXHD | ANAR_10FD | ANAR_10HD))
+ | PSB_802_3;
+ mii_write_register(sc, MII_ANAR, cap);
+ control |= (BMCR_ANENABLE | BMCR_RESTARTAN);
+ mii_write_register(sc, MII_BMCR, control);
+
+ timeout = 3000;
+ for (;;) {
+ status = mii_read_register(sc, MII_BMSR);
+ if ((status & BMSR_ANCOMPLETE) != 0) break;
+ cfe_sleep(CFE_HZ/2);
+ timeout -= 500;
+ if (timeout <= 0) break;
+ }
+ mii_dump(sc, "done PHY");
+ }
+
+ xprintf("%s: Link speed: ", tulip_devname(sc));
+ if ((status & BMSR_ANCOMPLETE) != 0) {
+ /* A link partner was negogiated... */
+
+ uint16_t remote = mii_read_register(sc, MII_ANLPAR);
+
+ autoneg = 1;
+ if ((remote & ANLPAR_TXFD) != 0) {
+ xprintf("100BaseT FDX");
+ linkspeed = ETHER_SPEED_100FDX;
+ }
+ else if ((remote & ANLPAR_TXHD) != 0) {
+ xprintf("100BaseT HDX");
+ linkspeed = ETHER_SPEED_100HDX;
+ }
+ else if ((remote & ANLPAR_10FD) != 0) {
+ xprintf("10BaseT FDX");
+ linkspeed = ETHER_SPEED_10FDX;
+ }
+ else if ((remote & ANLPAR_10HD) != 0) {
+ xprintf("10BaseT HDX");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ xprintf("\n");
+ }
+ else {
+ /* no link partner negotiation */
+
+ autoneg = 0;
+ xprintf("Unknown, assuming 10BaseT\n");
+ control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN);
+ mii_write_register(sc, MII_BMCR, control);
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+
+ if ((status & BMSR_LINKSTAT) == 0)
+ mii_write_register(sc, MII_BMCR, control);
+ mii_set_speed(sc, linkspeed, autoneg);
+
+ status = mii_read_register(sc, MII_BMSR); /* clear latching bits */
+ mii_dump(sc, "final PHY");
+}
+
+
+/* Chip specific code */
+
+static void
+dc21143_set_speed(tulip_softc *sc, int speed)
+{
+ uint32_t opmode = 0;
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+
+ switch (speed) {
+ case ETHER_SPEED_AUTO:
+ break;
+ case ETHER_SPEED_10HDX:
+ default:
+ WRITECSR(sc, R_CSR_SIAMODE1, M_CSR14_10BT_HD);
+ WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata);
+ opmode = M_CSR6_SPEED_10;
+ break;
+ case ETHER_SPEED_10FDX:
+ WRITECSR(sc, R_CSR_SIAMODE1, M_CSR14_10BT_FD);
+ WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata);
+ opmode = M_CSR6_SPEED_10 | M_CSR6_FULLDUPLEX;
+ break;
+ case ETHER_SPEED_100HDX:
+ WRITECSR(sc, R_CSR_SIAMODE1, 0);
+ WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata);
+ opmode = M_CSR6_SPEED_100;
+ break;
+ case ETHER_SPEED_100FDX:
+ WRITECSR(sc, R_CSR_SIAMODE1, 0);
+ WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata);
+ opmode = M_CSR6_SPEED_100 | M_CSR6_FULLDUPLEX;
+ break;
+ }
+
+ WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET);
+
+ opmode |= M_CSR6_MBO;
+#if TULIP_TUNE
+ opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72);
+#else
+ opmode |= M_CSR6_STOREFWD;
+#endif
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+}
+
+static void
+dc21143_autonegotiate(tulip_softc *sc)
+{
+ uint32_t opmode;
+ uint32_t tempword;
+ int count;
+ int linkspeed;
+
+ linkspeed = ETHER_SPEED_UNKNOWN;
+
+ /* Program the media setup into the CSRs. */
+ /* reset SIA */
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+
+ /* set to speed_10, fullduplex to start_nway */
+ opmode =
+ M_CSR6_SPEED_10 |
+ M_CSR6_FULLDUPLEX |
+ M_CSR6_MBO;
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+
+ /* Choose advertised capabilities */
+ tempword =
+ M_CSR14_100BASETHALFDUP |
+ M_CSR14_100BASETFULLDUP |
+ M_CSR14_HALFDUPLEX10BASET;
+ WRITECSR(sc, R_CSR_SIAMODE1, tempword);
+
+ /* Enable autonegotiation */
+ tempword |= M_CSR14_AUTONEGOTIATE | 0xFFFF;
+ WRITECSR(sc, R_CSR_SIAMODE1, tempword);
+ WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata);
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+ WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET);
+
+ /* STATE check nway, poll until a valid 10/100mbs signal seen */
+ WRITECSR(sc, R_CSR_STATUS, M_CSR5_LINKPASS); /* try to clear this... */
+
+ /* (Re)start negotiation */
+ tempword = READCSR(sc, R_CSR_SIASTATUS);
+ tempword &=~ M_CSR12_AUTONEGARBIT;
+ tempword |= V_CSR12_AUTONEGARBIT(0x1);
+
+ for (count = 0; count <= 13; count++) {
+ tempword = READCSR(sc, R_CSR_STATUS);
+ if (tempword & M_CSR5_LINKPASS)
+ break;
+ cfe_sleep(CFE_HZ/10);
+ }
+
+ if (count > 13)
+ xprintf("%s: Link autonegotiation failed\n", tulip_devname(sc));
+
+ /* STATE configure nway, check to see if any abilities common to us.
+ If they do, set to highest mode, if not, we will see if the partner
+ will do 100mb or 10mb - then set it */
+
+ tempword = READCSR(sc, R_CSR_SIASTATUS);
+ /* clear the autonegogiate complete bit */
+ WRITECSR(sc, R_CSR_STATUS, M_CSR5_LINKPASS);
+
+ if (tempword & M_CSR12_LINKPARTNEG) {
+ /* A link partner was negogiated... */
+
+ xprintf("%s: Link speed: ", tulip_devname(sc));
+ if (tempword & 0x01000000) { /* 100FD */
+ xprintf("100BaseT FDX");
+ linkspeed = ETHER_SPEED_100FDX;
+ }
+ else if (tempword & 0x00800000) { /* 100HD */
+ xprintf("100BaseT HDX");
+ linkspeed = ETHER_SPEED_100HDX;
+ }
+ else if (tempword & 0x00400000) { /* 10FD */
+ xprintf("10BaseT FDX");
+ linkspeed = ETHER_SPEED_10FDX;
+ }
+ else if (tempword & 0x00200000) { /* 10HD */
+ xprintf("10BaseT HDX");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ xprintf("\n");
+ }
+ else {
+ /* no link partner negotiation */
+ /* disable link for 1.3 seconds to break any existing connections */
+
+ xprintf("%s: ", tulip_devname(sc));
+ dc21143_set_speed(sc, ETHER_SPEED_10HDX);
+ cfe_sleep(CFE_HZ/8);
+
+ tempword = READCSR(sc, R_CSR_SIASTATUS);
+
+ if ((tempword & 0x02) == 0) {
+ /* 100 mb signal present set to 100mb */
+ xprintf("No link partner... setting to 100BaseT HDX\n");
+ linkspeed = ETHER_SPEED_100HDX;
+ }
+ else if ((tempword & 0x04) == 0) {
+ /* 10 mb signal present */
+ xprintf("No link partner... setting to 10BaseT HDX\n");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ else {
+ /* couldn't determine line speed, so set to 10mbs */
+ xprintf("Unknown; defaulting to 10BaseT HDX\n");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ }
+
+ dc21143_set_speed(sc, linkspeed);
+}
+
+static void
+dc21143_set_loopback(tulip_softc *sc, int mode)
+{
+ uint32_t v;
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+ if (mode == ETHER_LOOPBACK_EXT) {
+ /* deal with CSRs 13-15 */
+ }
+ cfe_sleep(CFE_HZ/10); /* check this */
+
+ /* Update the SIA registers */
+ v = READCSR(sc, R_CSR_SIAMODE0);
+ WRITECSR(sc, R_CSR_SIAMODE0, v &~ 0xFFFF);
+ v = READCSR(sc, R_CSR_SIAMODE1);
+ WRITECSR(sc, R_CSR_SIAMODE1, v &~ 0xFFFF);
+ v = READCSR(sc, R_CSR_SIAMODE2);
+ WRITECSR(sc, R_CSR_SIAMODE2, v | 0xC000); /* WC of HCKR, RMP */
+ if (mode == ETHER_LOOPBACK_OFF)
+ WRITECSR(sc, R_CSR_SIAMODE2, sc->gpdata);
+ else
+ WRITECSR(sc, R_CSR_SIAMODE2, (v &~ 0xFFFF) | M_CSR15_GP_AUIBNC);
+
+ WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET);
+
+ sc->loopback = mode;
+}
+
+/* Known vendors with cards requiring special initialization. */
+#define K_PCI_VENDOR_COGENT 0x1109 /* inherited by Adaptec */
+#define K_PCI_VENDOR_PHOBOS 0x13D8
+#define K_PCI_VENDOR_ZNYZ 0x110D
+#define K_PCI_VENDOR_KINGSTON 0x2646
+
+static void
+dc21143_hwinit(tulip_softc *sc, uint8_t srom[])
+{
+ uint32_t v;
+ uint32_t csr6word, csr14word;
+
+ if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_COGENT) {
+ /* Cogent/Adaptec MII (ANA-6911A). */
+ sc->phy_type = MII;
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0821 << 16);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0001 << 16);
+ cfe_sleep(CFE_HZ/10);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0000 << 16);
+ cfe_sleep(CFE_HZ/2);
+ sc->gpdata = 0;
+ }
+ else if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_ZNYZ) {
+ /* Znyz 34xQ adapters */
+ sc->phy_type = SYM;
+
+ /* The ZX345Q with wake-on-LAN enabled apparently clears ANE and
+ TAS on power up (but not cold reset) */
+ WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFFFFFF);
+
+ /* The following is a reset workaround for QS/Kendin PHYs
+ as suggested by an Intel app note. Bit 0x40000 is the PHY
+ reset (low true) on Znyx cards. */
+ WRITECSR(sc, R_CSR_SIAMODE2,
+ M_CSR15_GP_CONTROLWRITE |
+ 0xF0000 | /* all outputs */
+ M_CSR15_GP_LED1 |
+ M_CSR15_GP_AUIBNC);
+ cfe_sleep(CFE_HZ/5);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x40000); /* release reset */
+ cfe_sleep(CFE_HZ/5);
+ sc->gpdata = 0x40000 | M_CSR15_GP_AUIBNC;
+ }
+ else if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_KINGSTON) {
+ /* Kingston KNE100TX */
+ sc->phy_type = MII;
+ sc->gpdata = 0;
+ }
+ else if (SROM_WORD(srom, SROM_VENDOR_INDEX) == K_PCI_VENDOR_PHOBOS) {
+ /* Phobos 430TX quad card */
+ sc->phy_type = MII;
+#if 0 /* per EEPROM */
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x080E << 16);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x000E << 16);
+ cfe_sleep(CFE_HZ/10);
+ sc->gpdata = 0x0E;
+#else /* following Adaptec 21143 with MII interface */
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0821 << 16);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0001 << 16);
+ cfe_sleep(CFE_HZ/10);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0000 << 16);
+ cfe_sleep(CFE_HZ/2);
+ sc->gpdata = 0;
+#endif
+ }
+ else {
+ /* Most 21143 cards use the SYM interface. */
+ sc->phy_type = SYM;
+ WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_CONFIG_GEPS_LEDS);
+ sc->gpdata = M_CSR15_DEFAULT_VALUE;
+ }
+
+ if (sc->phy_type == MII) {
+ mii_probe(sc);
+ }
+
+ /* CSR0 - bus mode */
+#if TULIP_TUNE
+ v = V_CSR0_SKIPLEN(0) |
+ V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) |
+ M_CSR0_READMULTENAB | M_CSR0_READLINEENAB |
+ M_CSR0_WRITEINVALENAB |
+ V_CSR0_BURSTLEN(K_CSR0_BURST32);
+#else
+ v = V_CSR0_SKIPLEN(0) |
+ V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) |
+ V_CSR0_BURSTLEN(K_CSR0_BURST32);
+#endif
+#ifdef __MIPSEB
+ v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */
+#endif
+ WRITECSR(sc, R_CSR_BUSMODE, v);
+
+ /* CSR6 - operation mode */
+ v = M_CSR6_PORTSEL |
+#if TULIP_TUNE
+ V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72) |
+#else
+ M_CSR6_STOREFWD |
+#endif
+ M_CSR6_MBO;
+ if (sc->phy_type == SYM)
+ v |= M_CSR6_PCSFUNC |M_CSR6_SCRAMMODE;
+ WRITECSR(sc, R_CSR_OPMODE, v);
+
+ /* About to muck with the SIA, reset it.(?) */
+ /* WRITECSR(sc, R_CSR_SIASTATUS, 0); */
+
+ /* Must shut off all transmit/receive in order to attempt to
+ achieve Full Duplex */
+ csr6word = READCSR(sc, R_CSR_OPMODE);
+ WRITECSR(sc, R_CSR_OPMODE, csr6word &~ (M_CSR6_TXSTART | M_CSR6_RXSTART));
+ csr6word = READCSR(sc, R_CSR_OPMODE);
+
+ WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start));
+ WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start));
+
+ if (sc->phy_type == MII) {
+ if (sc->linkspeed == ETHER_SPEED_AUTO)
+ mii_autonegotiate(sc);
+ else
+ mii_set_speed(sc, sc->linkspeed, 0);
+ }
+ else {
+ if (sc->linkspeed == ETHER_SPEED_AUTO) {
+ dc21143_autonegotiate(sc);
+ }
+ else {
+ /* disable autonegotiate so we can set full duplex to on */
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+ csr14word = READCSR(sc, R_CSR_SIAMODE1);
+ csr14word &=~ M_CSR14_AUTONEGOTIATE;
+ WRITECSR(sc, R_CSR_SIAMODE1, csr14word);
+ WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET);
+
+ dc21143_set_speed(sc, sc->linkspeed);
+ }
+ }
+}
+
+
+static void
+dc21140_set_speed(tulip_softc *sc, int speed, int autoneg)
+{
+ mii_set_speed(sc, speed, autoneg);
+}
+
+static void
+dc21140_set_loopback(tulip_softc *sc, int mode)
+{
+ if (mode == ETHER_LOOPBACK_EXT) {
+ xprintf("%s: external loopback mode NYI\n", tulip_devname(sc));
+ mode = ETHER_LOOPBACK_OFF;
+ }
+ else if (mode != ETHER_LOOPBACK_INT)
+ mode = ETHER_LOOPBACK_OFF;
+
+ sc->loopback = mode;
+}
+
+static void
+dc21140_hwinit(tulip_softc *sc, uint8_t srom[])
+{
+ uint16_t leaf;
+ uint8_t gpr_control, gpr_data;
+ uint32_t v;
+ uint32_t opmode;
+
+ if (srom[SROM_FORMAT_INDEX] == 0 || srom[SROM_FORMAT_INDEX] > 4) {
+ gpr_control = 0x1F;
+ gpr_data = 0x00;
+ sc->phy_type = MII; /* Most 21140 cards use MII */
+ }
+ else {
+ leaf = SROM_WORD(srom, SROM_LEAF0_OFFSET_INDEX);
+ gpr_control = srom[leaf+2];
+ /* XXX We should parse and check all the leaf info */
+ if ((srom[leaf+4] & 0x80) == 0) {
+ gpr_data = 0x85; /* SYM, 100 Mb/s */
+ sc->phy_type = SYM;
+ }
+ else {
+ gpr_data = 0x00; /* MII */
+ sc->phy_type = MII;
+ }
+ }
+
+ /* Assume that we will use MII or SYM interface */
+ WRITECSR(sc, R_CSR_OPMODE, M_CSR6_PORTSEL);
+ RESET_ADAPTER(sc);
+
+ WRITECSR(sc, R_CSR_GENPORT, M_CSR12_CONTROL | gpr_control);
+ WRITECSR(sc, R_CSR_GENPORT, gpr_data); /* setup PHY */
+
+ if (sc->phy_type == MII) {
+ mii_probe(sc);
+ }
+
+ /* CSR0 - bus mode */
+#if TULIP_TUNE
+ v = V_CSR0_SKIPLEN(0) |
+ V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) |
+ M_CSR0_READMULTENAB | M_CSR0_READLINEENAB |
+ M_CSR0_WRITEINVALENAB |
+ V_CSR0_BURSTLEN(K_CSR0_BURST32);
+#else
+ v = V_CSR0_SKIPLEN(0) |
+ V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) |
+ V_CSR0_BURSTLEN(K_CSR0_BURST32);
+#endif
+#ifdef __MIPSEB
+ v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */
+#endif
+ WRITECSR(sc, R_CSR_BUSMODE, v);
+
+ /* CSR6 - operation mode */
+ v = M_CSR6_PORTSEL |
+#if TULIP_TUNE
+ V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72) |
+#else
+ M_CSR6_STOREFWD |
+#endif
+ M_CSR6_MBO;
+ WRITECSR(sc, R_CSR_OPMODE, v);
+
+ /* Must shut off all transmit/receive in order to attempt to
+ achieve Full Duplex */
+ opmode = READCSR(sc, R_CSR_OPMODE);
+ WRITECSR(sc, R_CSR_OPMODE, opmode &~ (M_CSR6_TXSTART | M_CSR6_RXSTART));
+ opmode = READCSR(sc, R_CSR_OPMODE);
+
+ WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start));
+ WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start));
+
+ if (sc->phy_type == MII) {
+ if (sc->linkspeed == ETHER_SPEED_AUTO)
+ mii_autonegotiate(sc);
+ else
+ mii_set_speed(sc, sc->linkspeed, 0);
+ }
+ else {
+ /* XXX The 21140 requires a soft reset after changing PORTSEL.
+ For now, remain committed to the SYM port (100 Mb/s) */
+ switch (sc->linkspeed) {
+ default:
+ sc->linkspeed = ETHER_SPEED_100HDX; /* for now */
+ /* fall through */
+ case ETHER_SPEED_100HDX:
+ opmode |= M_CSR6_SPEED_100;
+ break;
+ case ETHER_SPEED_100FDX:
+ opmode |= M_CSR6_SPEED_100 | M_CSR6_FULLDUPLEX;
+ break;
+ }
+
+ /* XXX Need to reset and reinitialize if we choose SPEED_10 above */
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+ }
+}
+
+
+static void
+dc21041_set_speed(tulip_softc *sc, int speed)
+{
+ uint32_t opmode = 0;
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+
+ /* For now, always force 10BT, HDX (21041, Table 3-62) */
+ switch (speed) {
+ case ETHER_SPEED_10HDX:
+ default:
+ WRITECSR(sc, R_CSR_SIAMODE1, 0x7F3F);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0008);
+ opmode = M_CSR6_SPEED_10;
+ break;
+ }
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0xEF00 | M_CSR13_CONN_NOT_RESET);
+ cfe_sleep(CFE_HZ/10);
+
+ opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72);
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+}
+
+static void
+dc21041_set_loopback(tulip_softc *sc, int mode)
+{
+ /* For now, always assume 10BT */
+ uint32_t mode0;
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+ cfe_sleep(CFE_HZ/10); /* check this */
+
+ /* Update the SIA registers */
+ if (mode == ETHER_LOOPBACK_EXT) {
+ /* NB: this is really just internal but through the 10BT endec */
+ WRITECSR(sc, R_CSR_SIAMODE1, 0x7A3F);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0008);
+ mode0 = 0;
+ }
+ else if (mode == ETHER_LOOPBACK_INT) {
+ /* MAC internal loopback, no SIA */
+ WRITECSR(sc, R_CSR_SIAMODE1, 0x0000);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x000E);
+ mode0 = M_CSR13_CONN_AUI_10BT;
+ }
+ else {
+ mode = ETHER_LOOPBACK_OFF;
+ WRITECSR(sc, R_CSR_SIAMODE1, 0x7F3F);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0008);
+ mode0 = 0;
+ }
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0xEF00 | mode0 | M_CSR13_CONN_NOT_RESET );
+
+ sc->loopback = mode;
+}
+
+static void
+dc21041_hwinit(tulip_softc *sc, uint8_t srom[])
+{
+ uint32_t v;
+
+ sc->phy_type = SRL;
+
+ /* CSR0 - bus mode */
+ v = V_CSR0_SKIPLEN(0) |
+ V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) |
+ V_CSR0_BURSTLEN(K_CSR0_BURST32);
+#ifdef __MIPSEB
+ v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */
+#endif
+ WRITECSR(sc, R_CSR_BUSMODE, v);
+
+ WRITECSR(sc, R_CSR_INTMASK, 0);
+
+ WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start));
+ WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start));
+
+ /* For now, always force 10BT, HDX (21041, Table 3-62) */
+ dc21041_set_speed(sc, ETHER_SPEED_10HDX);
+}
+
+
+static void
+dc21040_set_speed(tulip_softc *sc, int speed)
+{
+ uint32_t opmode = 0;
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+
+ /* For now, force 10BT, HDX unless FDX requested (21040, Table 3-53) */
+ switch (speed) {
+ case ETHER_SPEED_10HDX:
+ default:
+ WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFF);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0000);
+ opmode = 0;
+ break;
+ case ETHER_SPEED_10FDX:
+ WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFD);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0000);
+ opmode = M_CSR6_FULLDUPLEX;
+ break;
+ }
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0xEF00 | M_CSR13_CONN_NOT_RESET);
+ cfe_sleep(CFE_HZ/10);
+
+ opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72);
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+}
+
+static void
+dc21040_set_loopback(tulip_softc *sc, int mode)
+{
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+ cfe_sleep(CFE_HZ/10); /* check this */
+
+ /* Update the SIA registers */
+ if (mode == ETHER_LOOPBACK_EXT) {
+ /* NB: this is on-chip loopback through the 10BT endec */
+ WRITECSR(sc, R_CSR_SIAMODE1, 0xFEFB);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0008);
+ }
+ else if (mode == ETHER_LOOPBACK_INT) {
+ /* MAC internal loopback, no SIA */
+ WRITECSR(sc, R_CSR_SIAMODE1, 0x0000);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0000);
+ }
+ else {
+ mode = ETHER_LOOPBACK_OFF;
+ WRITECSR(sc, R_CSR_SIAMODE1, 0xFFFF);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0000);
+ }
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0x8F00 | M_CSR13_CONN_NOT_RESET );
+
+ sc->loopback = mode;
+}
+
+static void
+dc21040_hwinit(tulip_softc *sc, uint8_t srom[])
+{
+ uint32_t v;
+
+ sc->phy_type = SRL;
+
+ /* CSR0 - bus mode */
+ v = V_CSR0_SKIPLEN(0) |
+ V_CSR0_CACHEALIGN(K_CSR0_ALIGN32) |
+ V_CSR0_BURSTLEN(K_CSR0_BURST32);
+#ifdef __MIPSEB
+ v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */
+#endif
+ WRITECSR(sc, R_CSR_BUSMODE, v);
+
+ WRITECSR(sc, R_CSR_INTMASK, 0);
+
+ dc21040_set_speed(sc, sc->linkspeed);
+}
+
+
+static void
+tulip_hwinit(tulip_softc *sc)
+{
+ if (sc->state == eth_state_uninit) {
+ uint8_t srom[SROM_SIZE];
+
+ /* Wake-on-LAN apparently powers up with PORTSEL = 1 */
+ WRITECSR(sc, R_CSR_OPMODE,
+ READCSR(sc, R_CSR_OPMODE) &~ M_CSR6_PORTSEL);
+
+ RESET_ADAPTER(sc);
+ sc->state = eth_state_off;
+ sc->bus_errors = 0;
+
+ rom_read_all(sc, srom); /* XXX read just once? */
+ rom_dump(srom);
+
+ switch (sc->device) {
+ case K_PCI_ID_DC21040:
+ dc21040_hwinit(sc, srom);
+ break;
+ case K_PCI_ID_DC21041:
+ dc21041_hwinit(sc, srom);
+ break;
+ case K_PCI_ID_DC21140:
+ dc21140_hwinit(sc, srom);
+ break;
+ case K_PCI_ID_DC21143:
+ dc21143_hwinit(sc, srom);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+tulip_setaddr(tulip_softc *sc)
+{
+ int idx;
+ tulip_cam *cam;
+ eth_pkt_t *pkt;
+
+ pkt = eth_alloc_pkt(sc);
+ if (pkt) {
+ pkt->length = CAM_SETUP_BUFFER_SIZE;
+ cam = (tulip_cam *) pkt->buffer;
+
+#ifdef __MIPSEB
+ cam->p.physical[0][0] = (((uint32_t) sc->hwaddr[0] << 8) |
+ (uint32_t) sc->hwaddr[1]) << 16;
+ cam->p.physical[0][1] = (((uint32_t) sc->hwaddr[2] << 8) |
+ (uint32_t) sc->hwaddr[3]) << 16;
+ cam->p.physical[0][2] = (((uint32_t) sc->hwaddr[4] << 8) |
+ (uint32_t) sc->hwaddr[5]) << 16;
+ for (idx = 1; idx < CAM_PERFECT_ENTRIES; idx++) {
+ cam->p.physical[idx][0] = 0xFFFF0000;
+ cam->p.physical[idx][1] = 0xFFFF0000;
+ cam->p.physical[idx][2] = 0xFFFF0000;
+ }
+#else
+ cam->p.physical[0][0] = ((uint32_t) sc->hwaddr[0]) |
+ (((uint32_t) sc->hwaddr[1]) << 8);
+ cam->p.physical[0][1] = ((uint32_t) sc->hwaddr[2]) |
+ (((uint32_t) sc->hwaddr[3]) << 8);
+ cam->p.physical[0][2] = ((uint32_t) sc->hwaddr[4]) |
+ (((uint32_t) sc->hwaddr[5]) << 8);
+ for (idx = 1; idx < CAM_PERFECT_ENTRIES; idx++) {
+ cam->p.physical[idx][0] = 0x0000FFFF;
+ cam->p.physical[idx][1] = 0x0000FFFF;
+ cam->p.physical[idx][2] = 0x0000FFFF;
+ }
+#endif
+
+ pkt->flags |= ETH_TX_SETUP;
+ sc->state = eth_state_setup;
+ if (tulip_transmit(sc, pkt) != 0) {
+ xprintf("%s: failed setup\n", tulip_devname(sc));
+ dumpstat(sc);
+ eth_free_pkt(sc, pkt);
+ }
+ }
+}
+
+static void
+tulip_setspeed(tulip_softc *sc, int speed)
+{
+ switch (sc->device) {
+ case K_PCI_ID_DC21040:
+ dc21040_set_speed(sc, speed);
+ break;
+ case K_PCI_ID_DC21041:
+ dc21041_set_speed(sc, speed);
+ break;
+ case K_PCI_ID_DC21140:
+ dc21140_set_speed(sc, speed, 0);
+ break;
+ case K_PCI_ID_DC21143:
+ dc21143_set_speed(sc, speed);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+tulip_setloopback(tulip_softc *sc, int mode)
+{
+ switch (sc->device) {
+ case K_PCI_ID_DC21040:
+ dc21040_set_loopback(sc, mode);
+ break;
+ case K_PCI_ID_DC21041:
+ dc21041_set_loopback(sc, mode);
+ break;
+ case K_PCI_ID_DC21140:
+ dc21140_set_loopback(sc, mode);
+ break;
+ case K_PCI_ID_DC21143:
+ dc21143_set_loopback(sc, mode);
+ break;
+ default:
+ break;
+ }
+ cfe_sleep(CFE_HZ/10);
+}
+
+
+static void
+tulip_isr(void *arg)
+{
+ uint32_t status;
+ uint32_t csr5;
+ tulip_softc *sc = (tulip_softc *)arg;
+
+#if IPOLL
+ sc->interrupts++;
+#endif
+
+ for (;;) {
+
+ /* Read the interrupt status. */
+ csr5 = READCSR(sc, R_CSR_STATUS);
+ status = csr5 & (
+ M_CSR5_RXINT | M_CSR5_RXBUFUNAVAIL |
+ M_CSR5_TXINT | M_CSR5_TXUNDERFLOW |
+ M_CSR5_FATALBUSERROR);
+
+ /* if there are no more interrupts, leave now. */
+ if (status == 0) break;
+
+ /* Clear the pending interrupt. */
+ WRITECSR(sc, R_CSR_STATUS, status);
+
+ /* Now, test each unmasked bit in the interrupt register and
+ handle each interrupt type appropriately. */
+
+ if (status & M_CSR5_FATALBUSERROR) {
+ WRITECSR(sc, R_CSR_INTMASK, 0);
+
+ xprintf("%s: bus error %02x\n",
+ tulip_devname(sc), G_CSR5_ERRORBITS(csr5));
+ dumpstat(sc);
+ sc->bus_errors++;
+ if (sc->bus_errors >= 2) {
+ dumpcsrs(sc);
+ RESET_ADAPTER(sc);
+ sc->state = eth_state_off;
+ sc->bus_errors = 0;
+ }
+#if IPOLL
+ else
+ WRITECSR(sc, R_CSR_INTMASK, sc->intmask);
+#endif
+ }
+
+ if (status & M_CSR5_RXINT) {
+#if IPOLL
+ sc->rx_interrupts++;
+#endif
+ tulip_procrxring(sc);
+ }
+
+ if (status & M_CSR5_TXINT) {
+#if IPOLL
+ sc->tx_interrupts++;
+#endif
+ tulip_proctxring(sc);
+ }
+
+ if (status & (M_CSR5_TXUNDERFLOW | M_CSR5_RXBUFUNAVAIL)) {
+ if (status & M_CSR5_TXUNDERFLOW) {
+ xprintf("%s: tx underrun, %08x\n", tulip_devname(sc), csr5);
+ /* Try to restart */
+ WRITECSR(sc, R_CSR_TXPOLL, 1);
+ }
+ if (status & M_CSR5_RXBUFUNAVAIL) {
+ /* Try to restart */
+ WRITECSR(sc, R_CSR_RXPOLL, 1);
+ }
+ }
+ }
+}
+
+
+static void
+tulip_start(tulip_softc *sc)
+{
+ uint32_t opmode;
+
+ tulip_hwinit(sc);
+
+ WRITECSR(sc, R_CSR_RXRING, PTR_TO_PCI(sc->rxdscr_start));
+ WRITECSR(sc, R_CSR_TXRING, PTR_TO_PCI(sc->txdscr_start));
+
+ opmode = READCSR(sc, R_CSR_OPMODE);
+ opmode &=~ M_CSR6_OPMODE; /* no loopback */
+ if (sc->loopback != ETHER_LOOPBACK_OFF) {
+ opmode &=~ M_CSR6_FULLDUPLEX;
+ opmode |= M_CSR6_PORTSEL;
+ if (sc->loopback == ETHER_LOOPBACK_EXT)
+ opmode |= M_CSR6_EXTLOOPBACK;
+ else
+ opmode |= M_CSR6_INTLOOPBACK;
+ }
+
+ sc->intmask = 0;
+ WRITECSR(sc, R_CSR_INTMASK, 0); /* no interrupts */
+ WRITECSR(sc, R_CSR_STATUS, 0x1FFFF); /* clear any pending */
+ READCSR(sc, R_CSR_STATUS); /* push the write */
+
+ sc->interrupts = 0;
+ sc->rx_interrupts = sc->tx_interrupts = 0;
+
+#if IPOLL
+ cfe_request_irq(sc->irq, tulip_isr, sc, CFE_IRQ_FLAGS_SHARED, 0);
+
+ sc->intmask = M_CSR7_RXINT | M_CSR7_TXINT |
+ M_CSR7_NORMALINT;
+ sc->intmask |= M_CSR7_FATALBUSERROR | M_CSR7_TXUNDERFLOW |
+ M_CSR7_ABNORMALINT;
+ WRITECSR(sc, R_CSR_INTMASK, sc->intmask);
+#endif
+
+ if (sc->loopback == ETHER_LOOPBACK_OFF) {
+ opmode |= M_CSR6_TXSTART;
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+ tulip_setaddr(sc);
+ }
+ else {
+ opmode |= M_CSR6_TXSTART | M_CSR6_RXSTART;
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+ }
+}
+
+static void
+tulip_stop(tulip_softc *sc)
+{
+ uint32_t opmode;
+ uint32_t status;
+ int count;
+
+ WRITECSR(sc, R_CSR_INTMASK, 0);
+ sc->intmask = 0;
+#if IPOLL
+ cfe_free_irq(sc->irq, 0);
+#endif
+ WRITECSR(sc, R_CSR_STATUS, 0x1FFFF);
+ opmode = READCSR(sc, R_CSR_OPMODE);
+ opmode &=~ (M_CSR6_TXSTART | M_CSR6_RXSTART);
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+
+ /* wait for any DMA activity to terminate */
+ for (count = 0; count <= 13; count++) {
+ status = READCSR(sc, R_CSR_STATUS);
+ if ((status & (M_CSR5_RXPROCSTATE | M_CSR5_TXPROCSTATE)) == 0)
+ break;
+ cfe_sleep(CFE_HZ/10);
+ }
+ if (count > 13) {
+ xprintf("%s: idle state not achieved\n", tulip_devname(sc));
+ dumpstat(sc);
+ RESET_ADAPTER(sc);
+ sc->state = eth_state_uninit;
+#if 1
+ sc->linkspeed = ETHER_SPEED_AUTO;
+#endif
+ }
+ else if (sc->loopback != ETHER_LOOPBACK_OFF) {
+ tulip_setloopback(sc, ETHER_LOOPBACK_OFF);
+ opmode &=~ M_CSR6_OPMODE;
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+ }
+
+ if (sc->outpkts > 1) {
+ /* heuristic: suppress stats for initial mode changes */
+ xprintf("%s: %d sent, %d received, %d interrupts\n",
+ tulip_devname(sc), sc->outpkts, sc->inpkts, sc->interrupts);
+ xprintf(" %d rx interrupts, %d tx interrupts\n",
+ sc->rx_interrupts, sc->tx_interrupts);
+ }
+}
+
+
+/* *********************************************************************
+ * ETH_PARSE_XDIGIT(c)
+ *
+ * Parse a hex digit, returning its value
+ *
+ * Input parameters:
+ * c - character
+ *
+ * Return value:
+ * hex value, or -1 if invalid
+ ********************************************************************* */
+static int
+eth_parse_xdigit(char c)
+{
+ int digit;
+
+ if ((c >= '0') && (c <= '9')) digit = c - '0';
+ else if ((c >= 'a') && (c <= 'f')) digit = c - 'a' + 10;
+ else if ((c >= 'A') && (c <= 'F')) digit = c - 'A' + 10;
+ else digit = -1;
+
+ return digit;
+}
+
+/* *********************************************************************
+ * ETH_PARSE_HWADDR(str,hwaddr)
+ *
+ * Convert a string in the form xx:xx:xx:xx:xx:xx into a 6-byte
+ * Ethernet address.
+ *
+ * Input parameters:
+ * str - string
+ * hwaddr - pointer to hardware address
+ *
+ * Return value:
+ * 0 if ok, else -1
+ ********************************************************************* */
+static int
+eth_parse_hwaddr(char *str, uint8_t *hwaddr)
+{
+ int digit1, digit2;
+ int idx = ENET_ADDR_LEN;
+
+ while (*str && (idx > 0)) {
+ digit1 = eth_parse_xdigit(*str);
+ if (digit1 < 0) return -1;
+ str++;
+ if (!*str) return -1;
+
+ if ((*str == ':') || (*str == '-')) {
+ digit2 = digit1;
+ digit1 = 0;
+ }
+ else {
+ digit2 = eth_parse_xdigit(*str);
+ if (digit2 < 0) return -1;
+ str++;
+ }
+
+ *hwaddr++ = (digit1 << 4) | digit2;
+ idx--;
+
+ if ((*str == ':') || (*str == '-'))
+ str++;
+ }
+ return 0;
+}
+
+/* *********************************************************************
+ * ETH_INCR_HWADDR(hwaddr,incr)
+ *
+ * Increment a 6-byte Ethernet hardware address, with carries
+ *
+ * Input parameters:
+ * hwaddr - pointer to hardware address
+ * incr - desired increment
+ *
+ * Return value:
+ * none
+ ********************************************************************* */
+static void
+eth_incr_hwaddr(uint8_t *hwaddr, unsigned incr)
+{
+ int idx;
+ int carry;
+
+ idx = 5;
+ carry = incr;
+ while (idx >= 0 && carry != 0) {
+ unsigned sum = hwaddr[idx] + carry;
+
+ hwaddr[idx] = sum & 0xFF;
+ carry = sum >> 8;
+ idx--;
+ }
+}
+
+
+/* *********************************************************************
+ * Declarations for CFE Device Driver Interface routines
+ ********************************************************************* */
+
+static int tulip_ether_open(cfe_devctx_t *ctx);
+static int tulip_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
+static int tulip_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat);
+static int tulip_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
+static int tulip_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
+static int tulip_ether_close(cfe_devctx_t *ctx);
+#if 0
+static void tulip_ether_reset(void *softc);
+#endif
+
+/* *********************************************************************
+ * CFE Device Driver dispatch structure
+ ********************************************************************* */
+
+const static cfe_devdisp_t tulip_ether_dispatch = {
+ tulip_ether_open,
+ tulip_ether_read,
+ tulip_ether_inpstat,
+ tulip_ether_write,
+ tulip_ether_ioctl,
+ tulip_ether_close,
+ NULL, /* tulip_ether_poll */
+ NULL /* tulip_ether_reset */
+};
+
+/* *********************************************************************
+ * CFE Device Driver descriptor
+ ********************************************************************* */
+
+const cfe_driver_t dc21143drv = {
+ "DC21x4x Ethernet",
+ "eth",
+ CFE_DEV_NETWORK,
+ &tulip_ether_dispatch,
+ tulip_ether_probe
+};
+
+
+static int
+tulip_ether_attach(cfe_driver_t *drv,
+ pcitag_t tag, int index, uint8_t hwaddr[])
+{
+ tulip_softc *softc;
+ uint32_t device;
+ uint32_t class;
+ uint32_t reg;
+ phys_addr_t pa;
+ const char *devname;
+ char descr[100];
+ uint8_t romaddr[ENET_ADDR_LEN];
+
+ device = pci_conf_read(tag, R_CFG_CFID);
+ class = pci_conf_read(tag, R_CFG_CFRV);
+
+ reg = pci_conf_read(tag, R_CFG_CPMS);
+
+ reg = pci_conf_read(tag, R_CFG_CFDD);
+ pci_conf_write(tag, R_CFG_CFDD, 0);
+ reg = pci_conf_read(tag, R_CFG_CFDD);
+
+#if 1
+ /* Use memory space for the CSRs */
+ pci_map_mem(tag, R_CFG_CBMA, PCI_MATCH_BITS, &pa);
+#else
+ /* Use i/o space for the CSRs */
+ pci_map_io(tag, R_CFG_CBIO, PCI_MATCH_BITS, &pa);
+#endif
+
+ softc = (tulip_softc *) KMALLOC(sizeof(tulip_softc), 0);
+ if (softc == NULL) {
+ xprintf("DC21x4x: No memory to complete probe\n");
+ return 0;
+ }
+ memset(softc, 0, sizeof(*softc));
+
+ softc->membase = (uint32_t)pa;
+ softc->irq = pci_conf_read(tag, R_CFG_CFIT) & 0xFF;
+
+ softc->tag = tag;
+ softc->device = PCI_PRODUCT(device);
+ softc->revision = PCI_REVISION(class);
+ softc->devctx = NULL;
+
+#if 1
+ softc->linkspeed = ETHER_SPEED_AUTO; /* select autonegotiation */
+#else
+ softc->linkspeed = ETHER_SPEED_100FDX; /* 100 Mbps, full duplex */
+#endif
+ softc->loopback = ETHER_LOOPBACK_OFF;
+ memcpy(softc->hwaddr, hwaddr, ENET_ADDR_LEN);
+
+ tulip_init(softc);
+
+ /* Prefer address in srom */
+ if (rom_read_addr(softc, romaddr) == 0) {
+ memcpy(softc->hwaddr, romaddr, ENET_ADDR_LEN);
+ }
+
+ softc->state = eth_state_uninit;
+
+ switch (PCI_PRODUCT(device)) {
+ case K_PCI_ID_DC21040:
+ devname = "DC21040"; break;
+ case K_PCI_ID_DC21041:
+ devname = "DC21041"; break;
+ case K_PCI_ID_DC21140:
+ devname = "DC21140"; break;
+ case K_PCI_ID_DC21143:
+ devname = "DC21143"; break;
+ default:
+ devname = "DC21x4x"; break;
+ }
+
+ xsprintf(descr, "%s Ethernet at 0x%X (%02X-%02X-%02X-%02X-%02X-%02X)",
+ devname, softc->membase,
+ softc->hwaddr[0], softc->hwaddr[1], softc->hwaddr[2],
+ softc->hwaddr[3], softc->hwaddr[4], softc->hwaddr[5]);
+
+ cfe_attach(drv, softc, NULL, descr);
+ return 1;
+}
+
+
+/* *********************************************************************
+ * TULIP_ETHER_PROBE(drv,probe_a,probe_b,probe_ptr)
+ *
+ * Probe and install drivers for all DC21x4x Ethernet controllers.
+ * For each, create a context structure and attach to the
+ * specified network device.
+ *
+ * Input parameters:
+ * drv - driver descriptor
+ * probe_a - not used
+ * probe_b - not used
+ * probe_ptr - string pointer to hardware address for the first
+ * MAC, in the form xx:xx:xx:xx:xx:xx
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void
+tulip_ether_probe(cfe_driver_t *drv,
+ unsigned long probe_a, unsigned long probe_b,
+ void *probe_ptr)
+{
+ int index;
+ int n;
+ uint8_t hwaddr[ENET_ADDR_LEN];
+
+ if (probe_ptr)
+ eth_parse_hwaddr((char *) probe_ptr, hwaddr);
+ else {
+ /* use default address 40-00-00-10-11-11 */
+ hwaddr[0] = 0x40; hwaddr[1] = 0x00; hwaddr[2] = 0x00;
+ hwaddr[3] = 0x10; hwaddr[4] = 0x11; hwaddr[5] = 0x11;
+ }
+
+ n = 0;
+ index = 0;
+ for (;;) {
+ pcitag_t tag;
+ pcireg_t device;
+
+ if (pci_find_class(PCI_CLASS_NETWORK, index, &tag) != 0)
+ break;
+
+ index++;
+
+ device = pci_conf_read(tag, R_CFG_CFID);
+ if (PCI_VENDOR(device) == K_PCI_VENDOR_DEC) {
+#if 0 /* this currently (2.1.1) generates a bad code in PIC mode */
+ switch (PCI_PRODUCT(device)) {
+ case K_PCI_ID_DC21040:
+ case K_PCI_ID_DC21041:
+ case K_PCI_ID_DC21140:
+ case K_PCI_ID_DC21143:
+ tulip_ether_attach(drv, tag, n, hwaddr);
+ n++;
+ eth_incr_hwaddr(hwaddr, 1);
+ break;
+ default:
+ break;
+ }
+#else
+ if (PCI_PRODUCT(device) == K_PCI_ID_DC21040 ||
+ PCI_PRODUCT(device) == K_PCI_ID_DC21041 ||
+ PCI_PRODUCT(device) == K_PCI_ID_DC21140 ||
+ PCI_PRODUCT(device) == K_PCI_ID_DC21143) {
+
+ tulip_ether_attach(drv, tag, n, hwaddr);
+ n++;
+ eth_incr_hwaddr(hwaddr, 1);
+ }
+#endif
+ }
+ }
+}
+
+
+/* The functions below are called via the dispatch vector for the 21x4x. */
+
+/* *********************************************************************
+ * TULIP_ETHER_OPEN(ctx)
+ *
+ * Open the Ethernet device. The MAC is reset, initialized, and
+ * prepared to receive and send packets.
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ *
+ * Return value:
+ * status, 0 = ok
+ ********************************************************************* */
+static int
+tulip_ether_open(cfe_devctx_t *ctx)
+{
+ tulip_softc *softc = ctx->dev_softc;
+
+ if (softc->state == eth_state_on)
+ tulip_stop(softc);
+
+ softc->devctx = ctx;
+ tulip_start(softc);
+
+#if XPOLL
+ tulip_isr(softc);
+#endif
+
+ return 0;
+}
+
+/* *********************************************************************
+ * TULIP_ETHER_READ(ctx,buffer)
+ *
+ * Read a packet from the Ethernet device. If no packets are
+ * available, the read will succeed but return 0 bytes.
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ * buffer - pointer to buffer descriptor.
+ *
+ * Return value:
+ * status, 0 = ok
+ ********************************************************************* */
+static int
+tulip_ether_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
+{
+ tulip_softc *softc = ctx->dev_softc;
+ eth_pkt_t *pkt;
+ int blen;
+
+#if XPOLL
+ tulip_isr(softc);
+#endif
+
+ if (softc->state != eth_state_on) return -1;
+
+ CS_ENTER(softc);
+ pkt = (eth_pkt_t *) q_deqnext(&(softc->rxqueue));
+ CS_EXIT(softc);
+
+ if (pkt == NULL) {
+ buffer->buf_retlen = 0;
+ return 0;
+ }
+
+ blen = buffer->buf_length;
+ if (blen > pkt->length) blen = pkt->length;
+
+ blockcopy(buffer->buf_ptr, pkt->buffer, blen);
+ buffer->buf_retlen = blen;
+
+ eth_free_pkt(softc, pkt);
+ tulip_fillrxring(softc);
+
+#if XPOLL
+ tulip_isr(softc);
+#endif
+
+ return 0;
+}
+
+/* *********************************************************************
+ * TULIP_ETHER_INPSTAT(ctx,inpstat)
+ *
+ * Check for received packets on the Ethernet device
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ * inpstat - pointer to input status structure
+ *
+ * Return value:
+ * status, 0 = ok
+ ********************************************************************* */
+static int
+tulip_ether_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
+{
+ tulip_softc *softc = ctx->dev_softc;
+
+#if XPOLL
+ tulip_isr(softc);
+#endif
+
+ if (softc->state != eth_state_on) return -1;
+
+ /* We avoid an interlock here because the result is a hint and an
+ interrupt cannot turn a non-empty queue into an empty one. */
+ inpstat->inp_status = (q_isempty(&(softc->rxqueue))) ? 0 : 1;
+
+ return 0;
+}
+
+/* *********************************************************************
+ * TULIP_ETHER_WRITE(ctx,buffer)
+ *
+ * Write a packet to the Ethernet device.
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ * buffer - pointer to buffer descriptor.
+ *
+ * Return value:
+ * status, 0 = ok
+ ********************************************************************* */
+static int
+tulip_ether_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
+{
+ tulip_softc *softc = ctx->dev_softc;
+ eth_pkt_t *pkt;
+ int blen;
+
+#if XPOLL
+ tulip_isr(softc);
+#endif
+
+ if (softc->state != eth_state_on) return -1;
+
+ pkt = eth_alloc_pkt(softc);
+ if (!pkt) return CFE_ERR_NOMEM;
+
+ blen = buffer->buf_length;
+ if (blen > pkt->length) blen = pkt->length;
+
+ blockcopy(pkt->buffer, buffer->buf_ptr, blen);
+ pkt->length = blen;
+
+ if (tulip_transmit(softc, pkt) != 0) {
+ eth_free_pkt(softc,pkt);
+ return CFE_ERR_IOERR;
+ }
+
+#if XPOLL
+ tulip_isr(softc);
+#endif
+
+ return 0;
+}
+
+/* *********************************************************************
+ * TULIP_ETHER_IOCTL(ctx,buffer)
+ *
+ * Do device-specific I/O control operations for the device
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ * buffer - pointer to buffer descriptor.
+ *
+ * Return value:
+ * status, 0 = ok
+ ********************************************************************* */
+static int
+tulip_ether_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
+{
+ tulip_softc *softc = ctx->dev_softc;
+ int *argp;
+ int mode;
+ int speed;
+
+ switch ((int)buffer->buf_ioctlcmd) {
+ case IOCTL_ETHER_GETHWADDR:
+ memcpy(buffer->buf_ptr, softc->hwaddr, sizeof(softc->hwaddr));
+ return 0;
+
+ case IOCTL_ETHER_SETHWADDR:
+ return -1; /* not supported */
+
+ case IOCTL_ETHER_GETSPEED:
+ argp = (int *) buffer->buf_ptr;
+ *argp = softc->linkspeed;
+ return 0;
+
+ case IOCTL_ETHER_SETSPEED:
+ tulip_stop(softc);
+ tulip_resetrings(softc);
+ speed = *((int *) buffer->buf_ptr);
+ tulip_setspeed(softc, speed);
+ tulip_start(softc);
+ softc->state = eth_state_on;
+ return 0;
+
+ case IOCTL_ETHER_GETLINK:
+ argp = (int *) buffer->buf_ptr;
+ *argp = softc->linkspeed;
+ return 0;
+
+ case IOCTL_ETHER_GETLOOPBACK:
+ *((int *) buffer) = softc->loopback;
+ return 0;
+
+ case IOCTL_ETHER_SETLOOPBACK:
+ tulip_stop(softc);
+ tulip_resetrings(softc);
+ mode = *((int *) buffer->buf_ptr);
+ softc->loopback = ETHER_LOOPBACK_OFF; /* default */
+ if (mode == ETHER_LOOPBACK_INT || mode == ETHER_LOOPBACK_EXT) {
+ tulip_setloopback(softc, mode);
+ }
+ tulip_start(softc);
+ softc->state = eth_state_on;
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+/* *********************************************************************
+ * TULIP_ETHER_CLOSE(ctx)
+ *
+ * Close the Ethernet device.
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ *
+ * Return value:
+ * status, 0 = ok
+ ********************************************************************* */
+static int
+tulip_ether_close(cfe_devctx_t *ctx)
+{
+ tulip_softc *softc = ctx->dev_softc;
+
+ softc->state = eth_state_off;
+ tulip_stop(softc);
+
+ /* resynchronize descriptor rings */
+ tulip_resetrings(softc);
+
+ softc->devctx = NULL;
+ return 0;
+}
+
+
+#if 0
+/* *********************************************************************
+ * TULIP_ETHER_POLL(ctx,ticks)
+ *
+ * TBD
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ * ticks- current time in ticks
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void
+tulip_ether_poll(cfe_devctx_t *ctx, int64_t ticks)
+{
+}
+
+/* *********************************************************************
+ * TULIP_ETHER_RESET(softc)
+ *
+ * This routine is called when CFE is restarted after a
+ * program exits. We can clean up pending I/Os here.
+ *
+ * Input parameters:
+ * softc - pointer to tulip_softc
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void
+tulip_ether_reset(void *softc)
+{
+ tulip_softc *sc = (tulip_softc *)softc;
+
+ /* Turn off the Ethernet interface. */
+
+ RESET_ADAPTER(sc);
+}
+#endif