/* ********************************************************************* * 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