summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/dev/dev_dc21143.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/dev/dev_dc21143.c')
-rw-r--r--cfe/cfe/dev/dev_dc21143.c2388
1 files changed, 2388 insertions, 0 deletions
diff --git a/cfe/cfe/dev/dev_dc21143.c b/cfe/cfe/dev/dev_dc21143.c
new file mode 100644
index 0000000..f133cbb
--- /dev/null
+++ b/cfe/cfe/dev/dev_dc21143.c
@@ -0,0 +1,2388 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * DC2114x Ethernet Driver File: dev_dc21143.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"
+#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_irq.h"
+
+#include "pcivar.h"
+#include "pcireg.h"
+
+#include "dc21143.h"
+#include "mii.h"
+
+/* This is a driver for specific configurations of the DC21041,
+ DC21140A and DC21143, not a generic Tulip driver. The prefix
+ "tulip_" is used to indicate generic Tulip functions, while
+ "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.
+
+ The 21140 driver assumes that the PHY uses a standard MII interface
+ for both 100BT and 10BT. Example cards come from DEC (National DP83840
+ Twister PHY) and Netgear (Level One PHY).
+
+ The 21143 driver assumes 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 (Kendin KS8761 PHY). There is no
+ support for MII or 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
+
+#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 */
+
+/* The following must be a multiple of 4. In late versions of the
+ tulip, the rx DMA engine also apparently writes beyond 1520 bytes
+ for received packets > 1516 bytes (WriteInvalidate only?). */
+#define ETH_BUFFER_SIZE 1536
+
+typedef struct eth_pkt_s {
+ queue_t next;
+ void *devctx;
+ unsigned char *buffer;
+ unsigned int flags;
+ int length;
+ /* packet data goes here, should always be longword aligned */
+} eth_pkt_t;
+
+/* packet flags */
+#define ETH_TX_SETUP 1
+
+static void
+show_packet(eth_pkt_t *pkt)
+{
+ int i;
+ int n = (pkt->length < 32 ? pkt->length : 32);
+
+ xprintf("[%4d]:", 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;
+ uint32_t rxd_bufaddr1;
+ uint32_t rxd_bufaddr2;
+} rx_dscr;
+
+typedef struct tx_dscr {
+ uint32_t txd_flags;
+ uint32_t txd_bufsize;
+ uint32_t txd_bufaddr1;
+ uint32_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;
+
+
+typedef enum {
+ eth_state_uninit,
+ eth_state_setup,
+ eth_state_off,
+ eth_state_on,
+ eth_state_broken
+} eth_state_t;
+
+#define ETH_PKTPOOL_SIZE 20
+#define ETH_PKTBUF_SIZE (sizeof(eth_pkt_t) + ((ETH_BUFFER_SIZE+7)&~7))
+
+typedef struct tulip_softc {
+ uint32_t membase;
+ uint8_t irq; /* interrupt mapping (not currently used) */
+ 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;
+ int bus_errors;
+
+ /* These fields are the chip startup values. */
+// uint16_t media; /* media type */
+ uint32_t opmode; /* operating mode */
+ uint32_t intmask; /* interrupt mask */
+
+ /* This field is set before calling dc2114x_hwinit */
+ int linkspeed; /* encoding from cfe_ioctl */
+
+ /* 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 */
+
+ eth_pkt_t **rxdscrinfo;
+ eth_pkt_t **txdscrinfo;
+
+ /* 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 */
+
+ /* These fields describe the PHY */
+ int mii_addr;
+
+} tulip_softc;
+
+
+/* Driver parameterization */
+
+#define MAXRXDSCR 16
+#define MAXTXDSCR 16
+#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 PHYSADDR only works with 32-bit addresses, but the
+ so does the Tulip. */
+#define PHYSADDR(sc,x) (K0_TO_PHYS((uintptr_t)(x)))
+
+#define PCIADDR(a,b) ((uint32_t) (b))
+#define PCIADDRX(a,b) ((uint32_t) (b) | 0x20000000)
+#define PCI_TO_CPU(a) (a)
+
+#if __long64
+#define READCSR(sc,csr) \
+ (*((volatile uint32_t *) \
+ (PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((sc)->membase)+(csr)))))
+
+#define WRITECSR(sc,csr,val) \
+ (*((volatile uint32_t *) \
+ (PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((sc)->membase)+(csr)))) = (val))
+
+#else
+#define READCSR(sc,csr) \
+ (hs_read32(PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((sc)->membase)+(csr))))
+
+#define WRITECSR(sc,csr,val) \
+ (hs_write32(PHYS_TO_XKSEG_UNCACHED(PCI_TO_CPU((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(s)
+ *
+ * Allocate a packet from the free list.
+ *
+ * Input parameters:
+ * s - eth structure
+ *
+ * Return value:
+ * pointer to packet structure, or NULL if none available
+ ********************************************************************* */
+#define ETHPKT_ALIGN ((uintptr_t) 4)
+
+static eth_pkt_t *
+eth_alloc_pkt(tulip_softc *s)
+{
+ uintptr_t addr;
+ eth_pkt_t *pkt;
+
+ pkt = (eth_pkt_t *) q_deqnext(&s->freelist);
+ if (!pkt) return NULL;
+
+ addr = (uintptr_t) (pkt+1);
+ if (addr & (ETHPKT_ALIGN-1)) {
+ addr = (addr + ETHPKT_ALIGN) & ~(ETHPKT_ALIGN-1);
+ }
+
+ pkt->buffer = (uint8_t *) addr;
+ pkt->length = ETH_BUFFER_SIZE;
+ pkt->flags = 0;
+
+ return pkt;
+}
+
+
+/* *********************************************************************
+ * ETH_FREE_PKT(s,pkt)
+ *
+ * Return a packet to the free list
+ *
+ * Input parameters:
+ * s - sbmac structure
+ * pkt - packet to return
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void
+eth_free_pkt(tulip_softc *s, eth_pkt_t *pkt)
+{
+ q_enqueue(&s->freelist, &pkt->next);
+}
+
+
+/* *********************************************************************
+ * ETH_INITFREELIST(s)
+ *
+ * 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:
+ * s - eth structure
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void
+eth_initfreelist(tulip_softc *s)
+{
+ int idx;
+ unsigned char *ptr;
+ eth_pkt_t *pkt;
+
+ q_init(&s->freelist);
+
+ ptr = s->pktpool;
+ for (idx = 0; idx < ETH_PKTPOOL_SIZE; idx++) {
+ pkt = (eth_pkt_t *) ptr;
+ eth_free_pkt(s, pkt);
+ ptr += ETH_PKTBUF_SIZE;
+ }
+}
+
+
+/* Descriptor ring management */
+
+static int
+tulip_add_rcvbuf(tulip_softc *sc, eth_pkt_t *pkt)
+{
+ volatile rx_dscr *rxd;
+ volatile rx_dscr *nextrxd;
+ int idx;
+ 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;
+
+ /* Save this packet pointer. */
+ idx = rxd - sc->rxdscr_start;
+ sc->rxdscrinfo[idx] = pkt;
+
+ rxd->rxd_bufsize = V_RDES1_BUF1SIZE(ETH_BUFFER_SIZE) | flags;
+ rxd->rxd_bufaddr1 = PCIADDRX(sc, PHYSADDR(sc, pkt->buffer));
+ rxd->rxd_bufaddr2 = 0;
+ rxd->rxd_flags = M_RDES0_OWNADAP;
+
+ /* success, advance the pointer */
+ sc->rxdscr_add = nextrxd;
+ sc->rxdscr_onring++;
+
+ return 0;
+}
+
+static void
+tulip_fillrxring(tulip_softc *sc)
+{
+ eth_pkt_t *pkt;
+
+ while (sc->rxdscr_onring < MINRXRING) {
+ 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 (sbeth_pkt structure)
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+static void
+tulip_rx_callback(tulip_softc *sc, eth_pkt_t *pkt)
+{
+ if (TULIP_DEBUG) show_packet(pkt); /* debug */
+
+ q_enqueue(&sc->rxqueue, &pkt->next);
+
+ tulip_fillrxring(sc);
+}
+
+
+static void
+tulip_procrxring(tulip_softc *sc)
+{
+ volatile rx_dscr *rxd;
+ eth_pkt_t *pkt;
+ eth_pkt_t *newpkt;
+ int idx;
+ uint32_t flags;
+
+ for (;;) {
+ rxd = (volatile rx_dscr *) sc->rxdscr_remove;
+ idx = rxd - sc->rxdscr_start;
+
+ flags = rxd->rxd_flags;
+
+ if (flags & M_RDES0_OWNADAP) {
+ /* end of ring, no more packets */
+ break;
+ }
+
+ pkt = sc->rxdscrinfo[idx];
+
+ /* Drop error packets */
+ if (flags & M_RDES0_ERRORSUM) {
+ xprintf("DC2114x: rx error %04X\n", 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;
+ sc->rxdscr_onring--;
+ }
+}
+
+
+static int
+tulip_add_txbuf(tulip_softc *sc, eth_pkt_t *pkt)
+{
+ volatile tx_dscr *txd;
+ volatile tx_dscr *nexttxd;
+ int idx;
+ 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;
+
+ /* Save this packet pointer. */
+ idx = txd - sc->txdscr_start;
+ sc->txdscrinfo[idx] = pkt;
+
+ 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 = PCIADDRX(sc, PHYSADDR(sc, 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)
+{
+ tulip_add_txbuf(sc, pkt);
+
+ WRITECSR(sc, R_CSR_TXPOLL, 1);
+ return 0;
+}
+
+
+static void
+tulip_proctxring(tulip_softc *sc)
+{
+ volatile tx_dscr *txd;
+ eth_pkt_t *pkt;
+ int idx;
+ uint32_t flags;
+
+ for (;;) {
+ txd = (volatile tx_dscr *) sc->txdscr_remove;
+ idx = txd - sc->txdscr_start;
+
+ flags = txd->txd_flags;
+
+ if (txd == sc->txdscr_add) {
+ /* ring is empty, no buffers to process */
+ break;
+ }
+
+ if (flags & M_RDES0_OWNADAP) {
+ /* Reached a packet still being transmitted */
+ break;
+ }
+
+ /* Check for a completed setup packet */
+ pkt = sc->txdscrinfo[idx];
+ if (pkt->flags & ETH_TX_SETUP) {
+ if (sc->state == eth_state_setup) {
+ /* check flag bits */
+ 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 transmit descriptors */
+
+ 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 and lookaside lists */
+ sc->rxdscrmem = KMALLOC(MAXRXDSCR*sizeof(rx_dscr),sizeof(rx_dscr));
+ sc->txdscrmem = KMALLOC(MAXTXDSCR*sizeof(tx_dscr),sizeof(tx_dscr));
+ sc->rxdscrinfo = KMALLOC(MAXRXDSCR*sizeof(eth_pkt_t *),sizeof(eth_pkt_t));
+ sc->txdscrinfo = KMALLOC(MAXTXDSCR*sizeof(eth_pkt_t *),sizeof(eth_pkt_t));
+
+ /* Allocate buffer pool */
+ sc->pktpool = KMALLOC(ETH_PKTPOOL_SIZE*ETH_PKTBUF_SIZE, 0);
+ 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;
+ int idx;
+
+ /* 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) {
+ idx = txd - sc->txdscr_start;
+ eth_free_pkt(sc, sc->txdscrinfo[idx]);
+ txd->txd_flags &=~ M_TDES0_OWNADAP;
+
+ 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) {
+ idx = rxd - sc->rxdscr_start;
+ eth_free_pkt(sc, sc->rxdscrinfo[idx]);
+ rxd->rxd_flags &=~ M_RDES0_OWNADAP;
+
+ rxd++;
+ if (rxd == sc->rxdscr_end)
+ rxd = sc->rxdscr_start;
+ sc->rxdscr_onring--;
+ }
+
+ /* 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_ADDR_OFFSET 0x14
+#define SROM_CRC_OFFSET (SROM_SIZE-2)
+
+
+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
+ ***************************************************************************/
+
+#define STORED_CRC(rom) \
+ ((rom)[SROM_CRC_OFFSET] | ((rom)[SROM_CRC_OFFSET+1] << 8))
+
+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_OFFSET);
+ if (crc != STORED_CRC(dest)) {
+ crc = srom_calc_crc(sc, dest, 94); /* "alternative" */
+ if (crc != STORED_CRC(dest)) {
+ xprintf("DC2114x: Invalid SROM CRC, calc %04x, stored %04x\n",
+ crc, STORED_CRC(dest));
+ 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_OFFSET], ENET_ADDR_LEN);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+/****************************************************************************
+ * 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 /* XXX sc->phy.index */, 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 /* XXX sc->phy.index */, 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 != 0xffff || id2 != 0xffff) &&
+ (id1 != 0x0000 || id2 != 0x0000)) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+#if 0
+static void
+mii_dump(tulip_softc *sc)
+{
+ int i;
+ uint16_t r;
+
+ for (i = 0; i <=6; ++i) {
+ r = mii_read_register(sc, i);
+ if (r != 0) xprintf("MII_REG%02x: %04x\n", i, r);
+ }
+ for (i = 21; i <= 25; ++i) {
+ r = mii_read_register(sc, i);
+ if (r != 0) printf("MII_REG%02x: %04x\n", i, r);
+ }
+}
+#else
+#define mii_dump(sc)
+#endif
+
+
+#if 0
+static void
+tulip_getaddr(tulip_softc *sc, uint8_t *buf)
+{
+ memcpy(buf, sc->hwaddr, ENET_ADDR_LEN);
+}
+#endif
+
+
+/* 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, M_CSR15_DEFAULT_VALUE);
+ opmode = M_CSR6_SPEED_10;
+ break;
+ case ETHER_SPEED_10FDX:
+ WRITECSR(sc, R_CSR_SIAMODE1, M_CSR14_10BT_FD);
+ WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE);
+ opmode = M_CSR6_SPEED_10 | M_CSR6_FULLDUPLEX;
+ break;
+ case ETHER_SPEED_100HDX:
+ WRITECSR(sc, R_CSR_SIAMODE1, 0);
+ WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE);
+ opmode = M_CSR6_SPEED_100;
+ break;
+ case ETHER_SPEED_100FDX:
+ WRITECSR(sc, R_CSR_SIAMODE1, 0);
+ WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_DEFAULT_VALUE);
+ 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, M_CSR15_DEFAULT_VALUE);
+ 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 <= 130; count++) {
+ tempword = READCSR(sc, R_CSR_STATUS);
+ if (tempword & M_CSR5_LINKPASS)
+ break;
+ cfe_sleep(CFE_HZ/100);
+ }
+
+ if (count > 130)
+ xprintf("DC21143: Link autonegotiation failed\n");
+
+ /* 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("DC21143: Negotiated ");
+ if (tempword & 0x01000000) { /* 100FD */
+ xprintf("100Mb/s FDX");
+ linkspeed = ETHER_SPEED_100FDX;
+ }
+ else if (tempword & 0x00800000) { /* 100HD */
+ xprintf("100Mb/s HDX");
+ linkspeed = ETHER_SPEED_100HDX;
+ }
+ else if (tempword & 0x00400000) { /* 10FD */
+ xprintf("10Mb/s FDX");
+ linkspeed = ETHER_SPEED_10FDX;
+ }
+ else if (tempword & 0x00200000) { /* 10HD */
+ xprintf("10Mb/s HDX");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ xprintf("\n");
+ }
+ else {
+ /* no link partner negotiation */
+ /* disable link for 1.3 seconds to break any existing connections */
+
+ /* xprintf("Disabling link, setting to 10mb half duplex\n"); */
+ 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 100mb/s HDX\n");
+ linkspeed = ETHER_SPEED_100HDX;
+ }
+ else if ((tempword & 0x04) == 0) {
+ /* 10 mb signal present */
+ xprintf("No link partner... setting to 10mb/s HDX\n");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ else {
+ /* couldn't determine line speed, so set to 10mbs */
+ xprintf("Unknown; defaulting to 10Mb/s 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 */
+ WRITECSR(sc, R_CSR_SIAMODE2, (v &~ 0xFFFF) | M_CSR15_GP_AUIBNC);
+
+ WRITECSR(sc, R_CSR_SIAMODE0, M_CSR13_CONN_NOT_RESET);
+}
+
+static void
+dc21143_hwinit(tulip_softc *sc)
+{
+ uint32_t v;
+ uint32_t csr6word, csr14word;
+
+ /* CSR0 - bus mode */
+ WRITECSR(sc, R_CSR_SIAMODE2, M_CSR15_CONFIG_GEPS_LEDS);
+#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_ALIGN128) |
+ 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 |
+ M_CSR6_PCSFUNC |
+ M_CSR6_SCRAMMODE;
+ WRITECSR(sc, R_CSR_OPMODE, v);
+
+ /* About to muck the SIA, reset it.(?) */
+ /* WRITECSR(sc, R_CSR_SIASTATUS, 1); */
+
+ /* 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, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start)));
+ WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start)));
+
+ 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)
+{
+ uint16_t control;
+ uint16_t pcr;
+ uint32_t opmode = 0;
+
+ 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);
+#if 0
+ xprintf("setspeed PHY\n"); mii_dump(sc);
+#endif
+}
+
+static void
+dc21140_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);
+#if 0
+ xprintf("query PHY\n"); mii_dump(sc);
+#endif
+
+ 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("DC21140: PHY reset failed\n");
+ 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;
+ }
+#if 0
+ xprintf("done PHY\n"); mii_dump(sc);
+#endif
+ }
+
+ if ((status & BMSR_ANCOMPLETE) != 0) {
+ /* A link partner was negogiated... */
+
+ uint16_t remote = mii_read_register(sc, MII_ANLPAR);
+
+ autoneg = 1;
+ xprintf("DC21140: Negotiated ");
+ if ((remote & ANLPAR_TXFD) != 0) {
+ xprintf("100Mb/s FDX");
+ linkspeed = ETHER_SPEED_100FDX;
+ }
+ else if ((remote & ANLPAR_TXHD) != 0) {
+ xprintf("100Mb/s HDX");
+ linkspeed = ETHER_SPEED_100HDX;
+ }
+ else if ((remote & ANLPAR_10FD) != 0) {
+ xprintf("10Mb/s FDX");
+ linkspeed = ETHER_SPEED_10FDX;
+ }
+ else if ((remote & ANLPAR_10HD) != 0) {
+ xprintf("10Mb/s HDX");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ xprintf("\n");
+ }
+ else {
+ /* no link partner negotiation */
+
+ autoneg = 0;
+ xprintf("DC21140: MII negotiation failed, assuming 10BT\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);
+ dc21140_set_speed(sc, linkspeed, autoneg);
+
+ status = mii_read_register(sc, MII_BMSR); /* clear latching bits */
+#if 0
+ xprintf("final PHY\n"); mii_dump(sc);
+#endif
+}
+
+static void
+dc21140_set_loopback(tulip_softc *sc, int mode)
+{
+ if (mode == ETHER_LOOPBACK_EXT)
+ xprintf("DC21140: external loopback mode NYI\n");
+}
+
+static void
+dc21140_hwinit(tulip_softc *sc)
+{
+ uint32_t v;
+ uint32_t csr6word;
+
+ /* The following assume GP port bits wired per the DEC reference design.
+ XXX Interpret the srom initialization strings instead */
+ WRITECSR(sc, R_CSR_GENPORT, M_CSR12_CONTROL | 0x1F);
+ WRITECSR(sc, R_CSR_GENPORT, 0); /* release PHY reset */
+
+ /* Select MII interface */
+ WRITECSR(sc, R_CSR_OPMODE, M_CSR6_PORTSEL);
+ RESET_ADAPTER(sc);
+
+ 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_ALIGN128) |
+ 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 */
+ 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, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start)));
+ WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start)));
+
+ if (sc->linkspeed == ETHER_SPEED_AUTO) {
+ dc21140_autonegotiate(sc);
+ }
+ else {
+ dc21140_set_speed(sc, sc->linkspeed, 0);
+ }
+}
+
+
+static void
+dc21041_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);
+ WRITECSR(sc, R_CSR_SIAMODE1, 0x7A3F);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0008);
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0xEF01);
+}
+
+static void
+dc21041_hwinit(tulip_softc *sc)
+{
+ uint32_t v;
+ uint32_t opmode;
+
+ /* CSR0 - bus mode */
+ v = V_CSR0_SKIPLEN(0) |
+ V_CSR0_CACHEALIGN(K_CSR0_ALIGN128) |
+ V_CSR0_BURSTLEN(K_CSR0_BURST32);
+#ifdef __MIPSEB
+ v |= M_CSR0_BIGENDIAN; /* big-endian data serialization */
+#endif
+ WRITECSR(sc, R_CSR_BUSMODE, v);
+
+ /* set initial interrupt mask here? */
+
+ WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start)));
+ WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start)));
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0);
+
+ /* For now, always force 10BT, HDX */
+ WRITECSR(sc, R_CSR_SIAMODE1, 0x7F3F);
+ WRITECSR(sc, R_CSR_SIAMODE2, 0x0008);
+ opmode = M_CSR6_SPEED_10;
+
+ WRITECSR(sc, R_CSR_SIAMODE0, 0xEF01);
+ cfe_sleep(CFE_HZ/10);
+
+ opmode |= V_CSR6_THRESHCONTROL(K_CSR6_TXTHRES_128_72);
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+}
+
+
+static void
+tulip_hwinit(tulip_softc *sc)
+{
+ if (sc->state == eth_state_uninit) {
+ RESET_ADAPTER(sc);
+ sc->state = eth_state_off;
+ sc->bus_errors = 0;
+
+ switch (sc->device) {
+ case K_PCI_ID_DC21041:
+ dc21041_hwinit(sc);
+ break;
+ case K_PCI_ID_DC21140:
+ dc21140_hwinit(sc);
+ break;
+ case K_PCI_ID_DC21143:
+ dc21143_hwinit(sc);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+static void
+tulip_isr(tulip_softc *sc)
+{
+ uint32_t status;
+ uint32_t csr5;
+
+ for (;;) {
+
+ /* Read the interrupt status. */
+ csr5 = READCSR(sc, R_CSR_STATUS);
+// status &= sc->intmask; /* keep only the bits we like. */
+ 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) {
+ xprintf("DC21143: bus error %02x\n", 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 (status & M_CSR5_RXINT) {
+ tulip_procrxring(sc);
+ }
+
+ if (status & M_CSR5_TXINT) {
+ tulip_proctxring(sc);
+ }
+
+ if (status & (M_CSR5_TXUNDERFLOW | M_CSR5_RXBUFUNAVAIL)) {
+ if (status & M_CSR5_TXUNDERFLOW) {
+ xprintf("DC21143: tx underrun, %08x\n", 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;
+ int idx;
+ tulip_cam *cam;
+ eth_pkt_t *pkt;
+
+ tulip_hwinit(sc);
+
+ WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start)));
+ WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start)));
+
+ opmode = READCSR(sc, R_CSR_OPMODE);
+ opmode &=~ M_CSR6_OPMODE; /* no loopback */
+ opmode |= (M_CSR6_TXSTART | M_CSR6_RXSTART);
+
+ sc->intmask = 0;
+ WRITECSR(sc, R_CSR_INTMASK, 0); /* no interrupts */
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+
+ 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;
+ if (tulip_transmit(sc, pkt) != 0) {
+ xprintf("DC21143: failed setup\n");
+ dumpstat(sc);
+ eth_free_pkt(sc, pkt);
+ return;
+ }
+ sc->state = eth_state_setup;
+ }
+}
+
+static void
+tulip_stop(tulip_softc *sc)
+{
+ uint32_t opmode;
+ uint32_t status;
+ int count;
+
+ WRITECSR(sc, R_CSR_INTMASK, 0);
+ sc->intmask = 0;
+
+ 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 <= 130; count++) {
+ status = READCSR(sc, R_CSR_STATUS);
+ if ((status & (M_CSR5_RXPROCSTATE | M_CSR5_TXPROCSTATE)) == 0)
+ break;
+ cfe_sleep(CFE_HZ/10);
+ }
+ if (count > 130) {
+ xprintf("DC21143: idle state not achieved\n");
+ dumpstat(sc);
+ }
+}
+
+
+static void
+tulip_start_loopback(tulip_softc *sc, int mode)
+{
+ uint32_t opmode;
+
+ opmode = READCSR(sc, R_CSR_OPMODE);
+
+ switch (sc->device) {
+ 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);
+
+ WRITECSR(sc, R_CSR_RXRING, PCIADDRX(sc, PHYSADDR(sc, sc->rxdscr_start)));
+ WRITECSR(sc, R_CSR_TXRING, PCIADDRX(sc, PHYSADDR(sc, sc->txdscr_start)));
+
+ sc->intmask = 0; /* no interrupts */
+ WRITECSR(sc, R_CSR_INTMASK, 0);
+
+ opmode &=~ (M_CSR6_OPMODE | M_CSR6_FULLDUPLEX);
+ opmode |= M_CSR6_PORTSEL;
+ if (mode == ETHER_LOOPBACK_EXT)
+ opmode |= M_CSR6_EXTLOOPBACK;
+ else
+ opmode |= M_CSR6_INTLOOPBACK;
+ opmode |= M_CSR6_TXSTART | M_CSR6_RXSTART;
+ WRITECSR(sc, R_CSR_OPMODE, opmode);
+}
+
+
+/* *********************************************************************
+ * 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);
+
+/* *********************************************************************
+ * 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,
+ NULL
+};
+
+/* *********************************************************************
+ * CFE Device Driver descriptor
+ ********************************************************************* */
+
+const cfe_driver_t dc21143drv = {
+ "DC2114x 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[], int host)
+{
+ tulip_softc *softc;
+ uint32_t device;
+ uint32_t class;
+ uint32_t reg;
+ phys_addr_t pa;
+ char descr[100];
+ uint8_t romaddr[ENET_ADDR_LEN];
+#if 0 /* temporary */
+ int i;
+ uint8_t srom[SROM_SIZE];
+#endif
+
+ 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("DC2114x: No memory to complete probe\n");
+ return 0;
+ }
+
+ memset(softc, 0, sizeof(*softc));
+
+ softc->membase = (uint32_t)pa;
+
+ /* If we are in Host mode, we can receive interrupts. Otherwise,
+ we can see the CSRs but our CPU will not get interrupts. */
+ if (host)
+ softc->irq = pci_conf_read(tag, R_CFG_CFIT) & 0xFF;
+ else
+ softc->irq = 0xFF;
+
+ softc->tag = tag;
+ softc->device = PCI_PRODUCT(device);
+ softc->revision = PCI_REVISION(class);
+#if 1
+ softc->linkspeed = ETHER_SPEED_AUTO; /* select autonegotiation */
+#else
+ softc->linkspeed = ETHER_SPEED_100HDX; /* 100 Mbps, full duplex */
+#endif
+ memcpy(softc->hwaddr, hwaddr, ENET_ADDR_LEN);
+
+ tulip_init(softc);
+
+ /* Prefer address in srom */
+ if (srom_read_addr(softc, romaddr) == 0)
+ memcpy(softc->hwaddr, romaddr, ENET_ADDR_LEN);
+
+ softc->state = eth_state_uninit;
+
+ xsprintf(descr, "%s at 0x%X (%02x-%02x-%02x-%02x-%02x-%02x)",
+ drv->drv_description, 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 DC2114x Ethernet controllers.
+ * For each, creates a context structure and attaches to the
+ * specified MAC devices.
+ *
+ * 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)
+ continue;
+ switch (PCI_PRODUCT(device)) {
+ case K_PCI_ID_DC21041:
+ break;
+ case K_PCI_ID_DC21140:
+ break;
+ case K_PCI_ID_DC21143:
+ break;
+ default:
+ continue;
+ }
+
+ tulip_ether_attach(drv, tag, index, hwaddr, 1);
+
+ n++;
+ eth_incr_hwaddr(hwaddr, 1);
+ }
+}
+
+
+/* The functions below are called via the dispatch vector for the 2114x. */
+
+/* *********************************************************************
+ * 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);
+
+ tulip_start(softc);
+ softc->state = eth_state_on;
+
+ 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 (softc->state != eth_state_on) return -1;
+
+ tulip_isr(softc);
+
+ pkt = (eth_pkt_t *) q_deqnext(&(softc->rxqueue));
+
+ if (pkt == NULL) {
+ buffer->buf_retlen = 0;
+ return 0;
+ }
+
+ blen = buffer->buf_length;
+ if (blen > pkt->length) blen = pkt->length;
+
+ memcpy(buffer->buf_ptr, pkt->buffer, blen);
+ buffer->buf_retlen = blen;
+
+ eth_free_pkt(softc,pkt);
+ tulip_fillrxring(softc);
+ tulip_isr(softc);
+
+ 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 (softc->state != eth_state_on) return -1;
+
+ tulip_isr(softc);
+
+ 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 (softc->state != eth_state_on) return -1;
+
+ pkt = eth_alloc_pkt(softc);
+ if (!pkt) return -1;
+
+ blen = buffer->buf_length;
+ if (blen > pkt->length) blen = pkt->length;
+
+ memcpy(pkt->buffer, buffer->buf_ptr, blen);
+ pkt->length = blen;
+
+ tulip_isr(softc);
+
+ if (tulip_transmit(softc, pkt) != 0) {
+ eth_free_pkt(softc,pkt);
+ return -1;
+ }
+
+ tulip_isr(softc);
+
+ 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 mode;
+
+ 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; /* need to do this carefully */
+
+ /* XXX IOCTLs to set speed, etc.? */
+
+ case IOCTL_ETHER_GETLOOPBACK:
+ *((int *) buffer) = 0; /* XXX place holder */
+ return 0;
+
+ case IOCTL_ETHER_SETLOOPBACK:
+ tulip_stop(softc);
+ tulip_resetrings(softc);
+ mode = *((int *) buffer->buf_ptr);
+ if (mode == ETHER_LOOPBACK_INT || mode == ETHER_LOOPBACK_EXT) {
+ tulip_start_loopback(softc, mode);
+ }
+ else if (mode == ETHER_LOOPBACK_OFF) {
+ 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;
+
+ tulip_stop(softc);
+ softc->state = eth_state_off;
+
+ /* resynchronize descriptor rings */
+ tulip_resetrings(softc);
+
+ return 0;
+}