summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/dev/dev_dp83815.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/dev/dev_dp83815.c')
-rw-r--r--cfe/cfe/dev/dev_dp83815.c2290
1 files changed, 2290 insertions, 0 deletions
diff --git a/cfe/cfe/dev/dev_dp83815.c b/cfe/cfe/dev/dev_dp83815.c
new file mode 100644
index 0000000..b9fb644
--- /dev/null
+++ b/cfe/cfe/dev/dev_dp83815.c
@@ -0,0 +1,2290 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * NS DP83815 Ethernet Driver File: dev_dp83815.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 "dp83815.h"
+#include "mii.h"
+
+/* This is a driver for the National Semiconductor DP83815 (MacPhyter)
+ 10/100 MAC with integrated PHY.
+
+ The current version has been developed for the Netgear FA311 and
+ FA312 NICs. These include an EEPROM with automatically loaded
+ setup information that includes station address filtering.
+ Operation without such an EEPROM has not been tested.
+
+ This SB1250 version takes advantage of DMA coherence and uses
+ "preserve bit lanes" addresses for all accesses that cross the
+ ZBbus-PCI bridge. */
+
+#ifndef MACPHYTER_DEBUG
+#define MACPHYTER_DEBUG 0
+#endif
+#ifndef MACPHYTER_TEST
+#define MACPHYTER_TEST 0
+#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 MIN_ETHER_PACK 64 /* min size of a packet */
+#define MAX_ETHER_PACK 1518 /* max size of a packet */
+#define CRC_SIZE 4 /* size of CRC field */
+
+/* Packet buffers. For the DP83815, an rx packet must be aligned to a
+ 32-bit word boundary, and we would like it aligned to a cache line
+ boundary for performance. Also, the buffers "should" be allocated
+ in 32 byte multiples (5.3.2). */
+
+#define ETH_PKTBUF_LEN (((MAX_ETHER_PACK+31)/32)*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[ETH_PKTBUF_LEN];
+} 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[ETH_PKTBUF_LEN];
+} eth_pkt_t;
+#endif
+
+#define CACHE_ALIGN 32
+#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. NOTE: To avoid having descriptors straddle
+ cache lines, we append a pad word, ignored by DMA, to each. */
+
+typedef struct rx_dscr {
+ pci_addr_t rxd_link;
+ uint32_t rxd_cmdsts;
+ pci_addr_t rxd_bufptr;
+ uint32_t rxd_pad;
+} rx_dscr;
+
+typedef struct tx_dscr {
+ pci_addr_t txd_link;
+ uint32_t txd_cmdsts;
+ pci_addr_t txd_bufptr;
+ uint32_t txd_pad;
+} tx_dscr;
+
+
+/* Driver data structures */
+
+typedef enum {
+ eth_state_uninit,
+ eth_state_off,
+ eth_state_on,
+ eth_state_broken
+} eth_state_t;
+
+#define ETH_PKTPOOL_SIZE 32
+
+typedef struct dp83815_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 */
+
+ eth_state_t state; /* current state */
+ uint32_t intmask; /* interrupt mask */
+
+ /* These fields are set before calling dp83815_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 DMA 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 */
+ int phy_addr;
+ int phy_check;
+ uint32_t phy_status;
+
+ /* Statistics */
+ uint32_t inpkts;
+ uint32_t outpkts;
+ uint32_t interrupts;
+ uint32_t rx_interrupts;
+ uint32_t tx_interrupts;
+ uint32_t bus_errors;
+} dp83815_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
+
+
+/* Prototypes */
+
+static void dp83815_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 dp83815. */
+#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) \
+ { \
+ /* XXX */ \
+ }
+
+
+/* Debugging */
+
+static void
+dumpstat(dp83815_softc *sc)
+{
+ xprintf("-- CR = %08X CFG = %08x\n",
+ READCSR(sc, R_CR), READCSR(sc, R_CFG));
+}
+
+static void
+dumpcsrs(dp83815_softc *sc)
+{
+ int reg;
+
+ xprintf("-------------\n");
+ for (reg = 0; reg < R_MIBC; reg += 4) {
+ xprintf("CSR %02X = %08X\n", reg, READCSR(sc, reg));
+ }
+ 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(dp83815_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_PKTBUF_LEN;
+ 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(dp83815_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(dp83815_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 *
+dp83815_devname(dp83815_softc *sc)
+{
+ return (sc->devctx != NULL ? cfe_device_name(sc->devctx) : "eth?");
+}
+
+
+/* Descriptor ring management */
+
+static int
+dp83815_add_rcvbuf(dp83815_softc *sc, eth_pkt_t *pkt)
+{
+ volatile rx_dscr *rxd;
+ volatile rx_dscr *nextrxd;
+
+ rxd = sc->rxdscr_add;
+
+ /* Figure out where the next descriptor will go */
+ nextrxd = rxd+1;
+ if (nextrxd == sc->rxdscr_end) {
+ nextrxd = sc->rxdscr_start;
+ }
+
+ /*
+ * 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_bufptr = PTR_TO_PCI(pkt->buffer);
+ rxd->rxd_cmdsts = M_DES1_INTR | V_DES1_SIZE(ETH_PKTBUF_LEN);
+
+ /* success, advance the pointer */
+ sc->rxdscr_add = nextrxd;
+ CS_ENTER(sc);
+ sc->rxdscr_onring++;
+ CS_EXIT(sc);
+
+ return 0;
+}
+
+static void
+dp83815_fillrxring(dp83815_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 (dp83815_add_rcvbuf(sc, pkt) != 0) {
+ /* could not add buffer to ring */
+ eth_free_pkt(sc, pkt);
+ break;
+ }
+ }
+}
+
+
+/* *********************************************************************
+ * DP83815_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
+dp83815_rx_callback(dp83815_softc *sc, eth_pkt_t *pkt)
+{
+ if (MACPHYTER_DEBUG) show_packet('>', pkt); /* debug */
+
+ CS_ENTER(sc);
+ q_enqueue(&sc->rxqueue, &pkt->next);
+ CS_EXIT(sc);
+ sc->inpkts++;
+
+ dp83815_fillrxring(sc);
+}
+
+
+static void
+dp83815_procrxring(dp83815_softc *sc)
+{
+ volatile rx_dscr *rxd;
+ eth_pkt_t *pkt;
+ eth_pkt_t *newpkt;
+ uint32_t cmdsts;
+
+ for (;;) {
+ rxd = (volatile rx_dscr *) sc->rxdscr_remove;
+
+ cmdsts = rxd->rxd_cmdsts;
+ if ((cmdsts & M_DES1_OWN) == 0) {
+ /* end of ring, no more packets */
+ break;
+ }
+
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufptr));
+ pkt->length = G_DES1_SIZE(cmdsts) - CRC_SIZE;
+
+ /* Drop error packets */
+ if (cmdsts & M_DES1_RX_ERRORS) {
+#if MACPHYTER_DEBUG
+ if (pkt->length >= MIN_ETHER_PACK - CRC_SIZE)
+ xprintf("%s: rx error %08X\n", dp83815_devname(sc), cmdsts);
+#endif
+ dp83815_add_rcvbuf(sc, pkt);
+ goto next;
+ }
+
+ /* Pass up the packet */
+ dp83815_rx_callback(sc, pkt);
+
+ /* put a buffer back on the ring to replace this one */
+ newpkt = eth_alloc_pkt(sc);
+ if (newpkt) dp83815_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
+dp83815_add_txbuf(dp83815_softc *sc, eth_pkt_t *pkt)
+{
+ volatile tx_dscr *txd;
+ volatile tx_dscr *nexttxd;
+
+ txd = sc->txdscr_add;
+
+ /* Figure out where the next descriptor will go */
+ nexttxd = (txd+1);
+ if (nexttxd == sc->txdscr_end) {
+ nexttxd = sc->txdscr_start;
+ }
+
+ /* 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;
+
+ txd->txd_bufptr = PTR_TO_PCI(pkt->buffer);
+ txd->txd_cmdsts = M_DES1_INTR | M_DES1_OWN | V_DES1_SIZE(pkt->length);
+
+ /* success, advance the pointer */
+ sc->txdscr_add = nexttxd;
+
+ return 0;
+}
+
+
+static int
+dp83815_transmit(dp83815_softc *sc,eth_pkt_t *pkt)
+{
+ int rv;
+
+ if (MACPHYTER_DEBUG) show_packet('<', pkt); /* debug */
+
+ rv = dp83815_add_txbuf(sc, pkt);
+ sc->outpkts++;
+
+ WRITECSR(sc, R_CR, M_CR_TXE | M_CR_RXE);
+ return rv;
+}
+
+
+static void
+dp83815_proctxring(dp83815_softc *sc)
+{
+ volatile tx_dscr *txd;
+ eth_pkt_t *pkt;
+ uint32_t cmdsts;
+
+ for (;;) {
+ txd = (volatile tx_dscr *) sc->txdscr_remove;
+
+ if (txd == sc->txdscr_add) {
+ /* ring is empty, no buffers to process */
+ break;
+ }
+
+ cmdsts = txd->txd_cmdsts;
+ if (cmdsts & M_DES1_OWN) {
+ /* Reached a packet still being transmitted */
+ break;
+ }
+
+ /* Just free the packet */
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufptr));
+ 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
+dp83815_initrings(dp83815_softc *sc)
+{
+ volatile tx_dscr *txd, *txn;
+ volatile rx_dscr *rxd, *rxn;
+
+ /* Claim ownership of all descriptors for the driver */
+
+ for (txd = sc->txdscr_start; txd != sc->txdscr_end; txd++) {
+ txn = txd + 1;
+ if (txn == sc->txdscr_end) txn = sc->txdscr_start;
+ txd->txd_link = PTR_TO_PCI(txn);
+ txd->txd_cmdsts = 0;
+ txd->txd_pad = 0;
+ }
+ for (rxd = sc->rxdscr_start; rxd != sc->rxdscr_end; rxd++) {
+ rxn = rxd + 1;
+ if (rxn == sc->rxdscr_end) rxn = sc->rxdscr_start;
+ rxd->rxd_link = PTR_TO_PCI(rxn);
+ rxd->rxd_cmdsts = M_DES1_OWN;
+ rxd->rxd_pad = 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 */
+
+ dp83815_fillrxring(sc);
+}
+
+
+static int
+dp83815_init(dp83815_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;
+
+ dp83815_initrings(sc);
+
+ return 0;
+}
+
+
+static void
+dp83815_resetrings(dp83815_softc *sc)
+{
+ volatile tx_dscr *txd;
+ volatile rx_dscr *rxd;
+ eth_pkt_t *pkt;
+
+ /* Free already-sent descriptors and buffers */
+ dp83815_proctxring(sc);
+
+ /* Free any pending but unsent */
+ txd = (volatile tx_dscr *) sc->txdscr_remove;
+ while (txd != sc->txdscr_add) {
+ txd->txd_cmdsts &=~ M_DES1_OWN;
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(txd->txd_bufptr));
+ 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_cmdsts |= M_DES1_OWN;
+ pkt = ETH_PKT_BASE(PCI_TO_PTR(rxd->rxd_bufptr));
+ 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. */
+ dp83815_initrings(sc);
+}
+
+
+#if 0 /* XXX Multicast filtering not yet implemented. */
+/* CRCs */
+
+#define IEEE_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- either endian */
+
+static uint32_t
+dp83815_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 dp83815_mchash(mca) (dp83815_crc32((mca), 6) & 0x1FF)
+#endif
+
+
+#if MACPHYTER_TEST
+/* EEPROM access */
+
+/* Current NICs use the EEPROM auto-load feature and there is no need
+ for explicit EEPROM access. The following routines are included
+ for future applications and have been tested (Netgear FA311). */
+
+/****************************************************************************
+ * dp83815_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
+dp83815_spin(dp83815_softc *sc, long nanoseconds)
+{
+ long delay;
+ volatile uint32_t t;
+
+ for (delay = nanoseconds; delay > 0; delay -= PCI_MIN_DELAY)
+ t = READCSR(sc, R_SRR);
+}
+
+
+/*
+ * The recommended EEPROM is the NM9306.
+ * Delays below are chosen to meet specs for NS93C64 (slow M variant).
+ * Current parts are faster.
+ * Reference: NS Memory Data Book, 1994
+ */
+
+#define EEPROM_SIZE (2*0x0C)
+#define EEPROM_MAX_CYCLES 32
+
+#define EEPROM_CMD_BITS 3
+#define EEPROM_ADDR_BITS 6
+
+#define K_EEPROM_READ_CMD 06
+#define K_EEPROM_WRITE_CMD 05
+
+#define EEPROM_CRC_INDEX (EEPROM_SIZE-2)
+
+#define EEPROM_WORD(rom,offset) ((rom)[offset] | ((rom)[offset+1] << 8))
+
+static void
+eeprom_idle_state(dp83815_softc *sc)
+{
+ uint32_t ctrl;
+ unsigned int i;
+
+ ctrl = READCSR(sc, R_MEAR);
+
+ ctrl |= M_MEAR_EESEL;
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 100); /* CS setup (Tcss=100) */
+
+ /* Run the clock through the maximum number of pending read cycles */
+ for (i = 0; i < EEPROM_MAX_CYCLES*2; i++) {
+ ctrl ^= M_MEAR_EECLK;
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 1000); /* SK period (Fsk=0.5MHz) */
+ }
+
+ /* Deassert EEPROM Chip Select */
+ ctrl &=~ M_MEAR_EESEL;
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 50); /* CS recovery (Tsks=50) */
+}
+
+static void
+eeprom_send_command_bit(dp83815_softc *sc, unsigned int data)
+{
+ uint32_t ctrl;
+
+ ctrl = READCSR(sc, R_MEAR);
+
+ /* Place the data bit on the bus */
+ if (data == 1)
+ ctrl |= M_MEAR_EEDI;
+ else
+ ctrl &=~ M_MEAR_EEDI;
+
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 360); /* setup: Tdis=200 */
+
+ /* Now clock the data into the EEPROM */
+ WRITECSR(sc, R_MEAR, ctrl | M_MEAR_EECLK);
+ dp83815_spin(sc, 900); /* clock high, Tskh=500 */
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 450); /* clock low, Tskl=250 */
+
+ /* Now clear the data bit */
+ ctrl &=~ M_MEAR_EEDI; /* data invalid, Tidh=20 for SK^ */
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 270); /* min cycle, 1/Fsk=2000 */
+}
+
+static uint16_t
+eeprom_read_bit(dp83815_softc *sc)
+{
+ uint32_t ctrl;
+
+ ctrl = READCSR(sc, R_MEAR);
+
+ /* Generate a clock cycle before doing a read */
+ WRITECSR(sc, R_MEAR, ctrl | M_MEAR_EECLK); /* rising edge */
+ dp83815_spin(sc, 1000); /* clock high, Tskh=500, Tpd=1000 */
+ WRITECSR(sc, R_MEAR, ctrl); /* falling edge */
+ dp83815_spin(sc, 1000); /* clock low, 1/Fsk=2000 */
+
+ ctrl = READCSR(sc, R_MEAR);
+ return ((ctrl & M_MEAR_EEDO) != 0 ? 1 : 0);
+}
+
+#define CMD_BIT_MASK (1 << (EEPROM_CMD_BITS+EEPROM_ADDR_BITS-1))
+
+static uint16_t
+eeprom_read_word(dp83815_softc *sc, unsigned int index)
+{
+ uint16_t command, word;
+ uint32_t ctrl;
+ unsigned int i;
+
+ ctrl = READCSR(sc, R_MEAR) | M_MEAR_EESEL;
+
+ /* Assert the EEPROM CS line */
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 100); /* CS setup, Tcss = 100 */
+
+ /* Send the read command to the EEPROM */
+ command = (K_EEPROM_READ_CMD << EEPROM_ADDR_BITS) | index;
+ for (i = 0; i < EEPROM_CMD_BITS+EEPROM_ADDR_BITS; i++) {
+ eeprom_send_command_bit(sc, (command & CMD_BIT_MASK) != 0 ? 1 : 0);
+ command <<= 1;
+ }
+
+ /* Now read the bits from the EEPROM (MSB first) */
+ word = 0;
+ for (i = 0; i < 16; ++i) {
+ word <<= 1;
+ word |= eeprom_read_bit(sc);
+ }
+
+ /* Clear the EEPROM CS Line, CS hold, Tcsh = 0 */
+ WRITECSR(sc, R_MEAR, ctrl &~ M_MEAR_EESEL);
+
+ return word;
+}
+
+
+/****************************************************************************
+ * eeprom_checksum()
+ *
+ * Calculate the checksum of the EEPROM and return it. See Section
+ * 4.2.4 for the algorithm.
+ ***************************************************************************/
+
+static uint16_t
+eeprom_checksum(const uint8_t rom[])
+{
+ uint16_t sum;
+ int i;
+
+ sum = 0;
+ for (i = 0; i < EEPROM_SIZE-1; i++)
+ sum += rom[i];
+ sum ^= 0xFF;
+ return (((sum + 1) & 0xFF) << 8) | 0x55;
+}
+
+
+/****************************************************************************
+ * eeprom_read_all(sc, uint8_t dest)
+ *
+ * Read the entire EEPROM into the srom array
+ *
+ * Input parameters:
+ * sc - dp83815 state
+ ***************************************************************************/
+
+static int
+eeprom_read_all(dp83815_softc *sc, uint8_t dest[])
+{
+ int i;
+ uint16_t cksum, temp;
+
+ WRITECSR(sc, R_MEAR, M_MEAR_EESEL);
+
+ eeprom_idle_state(sc);
+
+ for (i = 0; i < EEPROM_SIZE/2; i++) {
+ temp = eeprom_read_word(sc, i);
+ dest[2*i] = temp & 0xFF;
+ dest[2*i+1] = temp >> 8;
+ }
+
+ WRITECSR(sc, R_MEAR, 0); /* CS hold, Tcsh=0 */
+
+ cksum = eeprom_checksum(dest);;
+ if (cksum != EEPROM_WORD(dest, EEPROM_CRC_INDEX)) {
+ xprintf("%s: Invalid EEPROM CHECKSUM, calc %04x, stored %04x\n",
+ dp83815_devname(sc),
+ cksum, EEPROM_WORD(dest, EEPROM_CRC_INDEX));
+ return 0/*-1*/;
+ }
+ return 0;
+}
+
+static int
+eeprom_read_addr(const uint8_t rom[], uint8_t buf[])
+{
+ uint16_t s;
+ unsigned offset, mask;
+ int i, j;
+
+ if (eeprom_checksum(rom) != EEPROM_WORD(rom, EEPROM_SIZE-2))
+ return -1;
+
+ s = 0;
+ offset = 2*6; mask = 0x1;
+ i = j = 0;
+ do {
+ s >>= 1;
+ if ((EEPROM_WORD(rom, offset) & mask) != 0) s |= 0x8000;
+ mask >>= 1;
+ if (mask == 0) {
+ offset +=2; mask = 0x8000;
+ }
+ i++;
+ if (i % 16 == 0) {
+ buf[j++] = s & 0xFF;
+ buf[j++] = s >> 8;
+ s = 0;
+ }
+ } while (i < ENET_ADDR_LEN*8);
+
+ return 0;
+}
+#endif /* MACPHYTER_TEST */
+
+#if 0
+static void
+eeprom_dump(uint8_t srom[])
+{
+ int i;
+
+ xprintf("DP83815: EEPROM data:");
+ for (i = 0; i < EEPROM_SIZE; i++) {
+ if (i % 16 == 0)
+ xprintf("\n %02x: ", i);
+ xprintf(" %02x", srom[i]);
+ }
+ xprintf("\n");
+}
+#else
+#define eeprom_dump(srom)
+#endif
+
+
+static int
+dp83815_get_pm_addr(dp83815_softc *sc, uint8_t buf[])
+{
+ uint32_t rfcr;
+ unsigned rfaddr;
+ unsigned i;
+ uint32_t rfdata;
+
+ rfcr = READCSR(sc, R_RFCR);
+ rfaddr = K_RFCR_PMATCH_ADDR;
+
+ for (i = 0; i < ENET_ADDR_LEN/2; i++) {
+ rfcr &=~ M_RFCR_RFADDR;
+ rfcr |= V_RFCR_RFADDR(rfaddr);
+ WRITECSR(sc, R_RFCR, rfcr);
+ rfdata = READCSR(sc, R_RFDR);
+ buf[2*i] = rfdata & 0xFF;
+ buf[2*i+1] = (rfdata >> 8) & 0xFF;
+ rfaddr += 2;
+ }
+
+ return 0;
+}
+
+
+#if MACPHYTER_TEST
+/* MII access */
+
+/* Current NICs use the internal PHY, which can be accessed more
+ simply via internal registers. The following routines are
+ primarily for management access to an external PHY and are retained
+ for future applications. They have been tested on a Netgear FA311. */
+
+/****************************************************************************
+ * MII access utility routines
+ ***************************************************************************/
+
+/* MII clock limited to 2.5 MHz (DP83815 allows 25 MHz), transactions
+ end with MDIO tristated */
+
+static void
+mii_write_bits(dp83815_softc *sc, uint32_t data, unsigned int count)
+{
+ uint32_t ctrl;
+ uint32_t bitmask;
+
+ ctrl = READCSR(sc, R_MEAR) & ~M_MEAR_MDC;
+ ctrl |= M_MEAR_MDDIR;
+
+ for (bitmask = 1 << (count-1); bitmask != 0; bitmask >>= 1) {
+ ctrl &=~ M_MEAR_MDIO;
+ if ((data & bitmask) != 0) ctrl |= M_MEAR_MDIO;
+ WRITECSR(sc, R_MEAR, ctrl);
+
+ dp83815_spin(sc, 2000); /* setup */
+ WRITECSR(sc, R_MEAR, ctrl | M_MEAR_MDC);
+ dp83815_spin(sc, 2000); /* hold */
+ WRITECSR(sc, R_MEAR, ctrl);
+ }
+}
+
+static void
+mii_turnaround(dp83815_softc *sc)
+{
+ uint32_t ctrl;
+
+ ctrl = READCSR(sc, R_MEAR) &~ M_MEAR_MDDIR;
+
+ /* stop driving data */
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 2000); /* setup */
+ WRITECSR(sc, R_MEAR, ctrl | M_MEAR_MDC);
+ dp83815_spin(sc, 2000); /* clock high */
+ WRITECSR(sc, R_MEAR, ctrl);
+
+ /* 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(dp83815_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 ctrl;
+ 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->phy_addr, 5);
+ mii_write_bits(sc, index, 5);
+
+ mii_turnaround(sc);
+
+ ctrl = READCSR(sc, R_MEAR) &~ (M_MEAR_MDC | M_MEAR_MDDIR);
+ word = 0;
+
+ for (i = 0; i < 16; i++) {
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 2000); /* clock width low */
+ WRITECSR(sc, R_MEAR, ctrl | M_MEAR_MDC);
+ dp83815_spin(sc, 2000); /* clock width high */
+ WRITECSR(sc, R_MEAR, ctrl);
+ dp83815_spin(sc, 1000); /* output delay */
+ word <<= 1;
+ if ((READCSR(sc, R_MEAR) & M_MEAR_MDIO) != 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(dp83815_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->phy_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(dp83815_softc *sc)
+{
+ int i;
+ uint16_t id1, id2;
+
+ /* Empirically, bit-banged access will return register 0 of the
+ integrated PHY for all registers of all unpopulated PHY
+ addresses. */
+ for (i = 0; i < 32; i++) {
+ sc->phy_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)) {
+ if (id1 != id2) return 0;
+ }
+ }
+ return -1;
+}
+
+#if 0
+#define OUI_NAT_SEMI 0x080017
+#define IDR_NAT_SEMI 0x080017 /* NSC does not bit-reverse each byte */
+#define PART_83815 0x02
+
+static void
+mii_dump(dp83815_softc *sc, const char *label)
+{
+ int i;
+ uint16_t r;
+ uint32_t idr, part;
+
+ xprintf("%s\n", label);
+ idr = 0;
+ for (i = 0; i <= 6; ++i) {
+ r = mii_read_register(sc, i);
+ xprintf("MII_REG%02x: %04x\n", i, r);
+ if (i == MII_PHYIDR1) {
+ idr |= r << 6;
+ }
+ else if (i == MII_PHYIDR2) {
+ idr |= (r >> 10) & 0x3F;
+ part = (r >> 4) & 0x3F;
+ }
+ }
+ if (idr == IDR_NAT_SEMI && part == PART_83815) {
+ r = mii_read_register(sc, 7);
+ xprintf("MII_REG%02x: %04x\n", i, r);
+ for (i = 0x10; i <= 0x16; ++i) {
+ r = mii_read_register(sc, i);
+ xprintf("MII_REG%02x: %04x\n", i, r);
+ }
+ for (i = 0x19; i <= 0x1A; ++i) {
+ r = mii_read_register(sc, i);
+ xprintf("MII_REG%02x: %04x\n", i, r);
+ }
+ }
+}
+#else
+#define mii_dump(sc,label)
+#endif
+
+
+/* The following functions are suitable for explicit MII access. */
+
+static void
+mii_set_speed(dp83815_softc *sc, int speed)
+{
+ uint16_t control;
+
+ control = mii_read_register(sc, MII_BMCR);
+
+ 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:
+ break;
+ case ETHER_SPEED_10FDX:
+ control |= BMCR_DUPLEX;
+ break;
+ case ETHER_SPEED_100HDX:
+ control |= BMCR_SPEED100;
+ break;
+ case ETHER_SPEED_100FDX:
+ control |= BMCR_SPEED100 | BMCR_DUPLEX ;
+ break;
+ }
+
+ mii_write_register(sc, MII_BMCR, control);
+}
+
+static void
+mii_autonegotiate(dp83815_softc *sc)
+{
+ uint16_t control, status, cap;
+ int timeout;
+ int linkspeed;
+
+ 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 = 3*CFE_HZ;
+ for (;;) {
+ control = mii_read_register(sc, MII_BMCR);
+ if ((control && BMCR_RESET) == 0 || timeout <= 0)
+ break;
+ cfe_sleep(CFE_HZ/2);
+ timeout -= CFE_HZ/2;
+ }
+ if ((control & BMCR_RESET) != 0) {
+ xprintf("%s: PHY reset failed\n", dp83815_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 = 3*CFE_HZ;
+ for (;;) {
+ status = mii_read_register(sc, MII_BMSR);
+ if ((status & BMSR_ANCOMPLETE) != 0 || timeout <= 0)
+ break;
+ cfe_sleep(CFE_HZ/2);
+ timeout -= CFE_HZ/2;
+ }
+ mii_dump(sc, "done PHY");
+ }
+
+ xprintf("%s: Link speed: ", dp83815_devname(sc));
+ if ((status & BMSR_ANCOMPLETE) != 0) {
+ /* A link partner was negogiated... */
+
+ uint16_t remote = mii_read_register(sc, MII_ANLPAR);
+
+ if ((remote & ANLPAR_TXFD) != 0) {
+ xprintf("100BaseT FDX\n");
+ linkspeed = ETHER_SPEED_100FDX;
+ }
+ else if ((remote & ANLPAR_TXHD) != 0) {
+ xprintf("100BaseT HDX\n");
+ linkspeed = ETHER_SPEED_100HDX;
+ }
+ else if ((remote & ANLPAR_10FD) != 0) {
+ xprintf("10BaseT FDX\n");
+ linkspeed = ETHER_SPEED_10FDX;
+ }
+ else if ((remote & ANLPAR_10HD) != 0) {
+ xprintf("10BaseT HDX\n");
+ linkspeed = ETHER_SPEED_10HDX;
+ }
+ }
+ else {
+ /* no link partner negotiation */
+ control &=~ (BMCR_ANENABLE | BMCR_RESTARTAN);
+ mii_write_register(sc, MII_BMCR, control);
+ xprintf("10BaseT HDX (assumed)\n");
+ linkspeed = ETHER_SPEED_10HDX;
+ if ((status & BMSR_LINKSTAT) == 0)
+ mii_write_register(sc, MII_BMCR, control);
+ mii_set_speed(sc, linkspeed);
+ }
+
+ status = mii_read_register(sc, MII_BMSR); /* clear latching bits */
+ mii_dump(sc, "final PHY");
+}
+#endif /* MACPHYTER_TEST */
+
+
+static void
+dp83815_phyupdate(dp83815_softc *sc, uint32_t status)
+{
+ xprintf("%s: Link speed: ", dp83815_devname(sc));
+ if ((status & M_CFG_LNKSTS) != 0) {
+ switch (status & (M_CFG_SPEED100 | M_CFG_FDUP)) {
+ case (M_CFG_SPEED100 | M_CFG_FDUP):
+ sc->linkspeed = ETHER_SPEED_100FDX;
+ xprintf("100BaseT FDX\n");
+ break;
+ case (M_CFG_SPEED100):
+ sc->linkspeed = ETHER_SPEED_100HDX;
+ xprintf("100BaseT HDX\n");
+ break;
+ case (M_CFG_FDUP):
+ sc->linkspeed = ETHER_SPEED_10FDX;
+ xprintf("10BaseT FDX\n");
+ break;
+ default:
+ sc->linkspeed = ETHER_SPEED_10HDX;
+ xprintf("10BaseT HDX\n");
+ break;
+ }
+ if ((status & M_CFG_SPEED100) != 0) {
+ uint32_t t;
+
+ /* This is a reputed fix that improves 100BT rx
+ performance on short cables with "a small number"
+ of DP83815 chips. It comes from Bruce at NatSemi
+ via the Soekris support web page (see appended
+ note). */
+
+ WRITECSR(sc, R_PGSEL, 0x0001);
+ (void)READCSR(sc, R_PGSEL); /* push */
+ t = READCSR(sc, R_DSPCFG);
+ WRITECSR(sc, R_DSPCFG, (t & 0xFFF) | 0x1000);
+ cfe_sleep(1);
+ t = READCSR(sc, R_TSTDAT) & 0xFF;
+ if ((t & 0x0080) == 0 || ((t > 0x00D8) && (t <= 0x00FF))) {
+ WRITECSR(sc, R_TSTDAT, 0x00E8);
+ t = READCSR(sc, R_DSPCFG);
+ WRITECSR(sc, R_DSPCFG, t | 0x0020);
+ }
+ WRITECSR(sc, R_PGSEL, 0);
+ }
+ if ((status & M_CFG_FDUP) != (sc->phy_status & M_CFG_FDUP)) {
+ uint32_t txcfg;
+
+ txcfg = READCSR(sc, R_TXCFG);
+ if (status & M_CFG_FDUP)
+ txcfg |= M_TXCFG_CSI;
+ else
+ txcfg &= ~M_TXCFG_CSI;
+ WRITECSR(sc, R_RXCFG, txcfg);
+ }
+ }
+ else {
+ xprintf("Unknown\n");
+ }
+
+ sc->phy_status = status;
+}
+
+static void
+dp83815_hwinit(dp83815_softc *sc)
+{
+ if (sc->state == eth_state_uninit) {
+ uint32_t cfg;
+ uint32_t txcfg, rxcfg;
+ uint32_t ready;
+ int timeout;
+
+ /* RESET_ADAPTER(sc); */
+ sc->state = eth_state_off;
+ sc->bus_errors = 0;
+
+ cfg = READCSR(sc, R_CFG);
+ cfg |= M_CFG_BEM; /* We will use match bits */
+ WRITECSR(sc, R_CFG, cfg);
+
+ sc->phy_status = 0;
+ dp83815_phyupdate(sc, cfg & M_CFG_LNKSUMMARY);
+
+ txcfg = READCSR(sc, R_TXCFG);
+ txcfg |= M_TXCFG_ATP;
+ /* XXX fix up FLTH, DRTH? */
+ WRITECSR(sc, R_TXCFG, txcfg);
+
+ rxcfg = READCSR(sc, R_RXCFG);
+ /* Set an aggressive rx drain threshhold of 16 (2*8) bytes */
+ rxcfg &= ~M_RXCFG_DRTH;
+ rxcfg |= V_RXCFG_DRTH(2);
+ WRITECSR(sc, R_RXCFG, rxcfg);
+
+#if MACPHYTER_TEST
+ {
+ uint8_t srom[EEPROM_SIZE];
+ uint8_t addr[ENET_ADDR_LEN];
+
+ eeprom_read_all(sc, srom);
+ eeprom_dump(srom);
+ xprintf(" checksum %04x\n", eeprom_checksum(srom));
+ if (eeprom_read_addr(srom, addr) == 0)
+ xprintf(" addr: %02x-%02x-%02x-%02x-%02x-%02x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+ mii_probe(sc);
+ xprintf("MII address %02x\n", sc->phy_addr);
+ mii_dump(sc, "DP83815 PHY:");
+ (void)mii_autonegotiate;
+ }
+#endif /* MACPHYTER_TEST */
+
+ /* XXX fix up rx filtering here. We are relying on the EEPROM. */
+
+ /* XXX This is inappropriate on a restart. */
+ timeout = 2*CFE_HZ;
+ ready = 0;
+ for (;;) {
+ ready |= READCSR(sc, R_ISR);
+ if ((ready & (M_INT_TXRCMP | M_INT_RXRCMP))
+ == (M_INT_TXRCMP | M_INT_RXRCMP) || timeout <= 0)
+ break;
+ cfe_sleep(CFE_HZ/10);
+ timeout -= CFE_HZ/10;
+ }
+ if ((ready & M_INT_TXRCMP) == 0)
+ xprintf("%s: tx reset failed\n", dp83815_devname(sc));
+ if ((ready & M_INT_RXRCMP) == 0)
+ xprintf("%s: rx reset failed\n", dp83815_devname(sc));
+ }
+}
+
+static void
+dp83815_setspeed(dp83815_softc *sc, int speed)
+{
+ /* XXX Not yet implemented - autonegotiation only. */
+}
+
+static void
+dp83815_setloopback(dp83815_softc *sc, int mode)
+{
+ /* XXX Not yet implemented. */
+}
+
+
+static void
+dp83815_isr(void *arg)
+{
+ dp83815_softc *sc = (dp83815_softc *)arg;
+ uint32_t status;
+ uint32_t isr;
+
+#if IPOLL
+ sc->interrupts++;
+#endif
+
+ for (;;) {
+
+ /* Read (and clear) the interrupt status. */
+ isr = READCSR(sc, R_ISR);
+ status = isr & sc->intmask;
+
+ /* if there are no more interrupts, leave now. */
+ if (status == 0) break;
+
+ /* Now, test each unmasked bit in the interrupt register and
+ handle each interrupt type appropriately. */
+
+ if (status & (M_INT_RTABT | M_INT_RMABT)) {
+ WRITECSR(sc, R_IER, 0);
+
+ xprintf("%s: bus error\n", dp83815_devname(sc));
+ 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_IMR, sc->intmask);
+#endif
+ }
+
+ if (status & M_INT_RXDESC) {
+#if IPOLL
+ sc->rx_interrupts++;
+#endif
+ dp83815_procrxring(sc);
+ }
+
+ if (status & M_INT_TXDESC) {
+#if IPOLL
+ sc->tx_interrupts++;
+#endif
+ dp83815_proctxring(sc);
+ }
+
+ if (status & (M_INT_TXURN | M_INT_RXORN)) {
+ if (status & M_INT_TXURN) {
+ xprintf("%s: tx underrun, %08x\n", dp83815_devname(sc), isr);
+ /* XXX Try to restart */
+ }
+ if (status & M_INT_RXORN) {
+ xprintf("%s: tx overrun, %08x\n", dp83815_devname(sc), isr);
+ /* XXX Try to restart */
+ }
+ }
+
+ if (status & M_INT_PHY) {
+ sc->intmask &= ~ M_INT_PHY;
+ WRITECSR(sc, R_IMR, sc->intmask);
+ (void)READCSR(sc, R_MISR); /* Clear at PHY */
+ sc->phy_check = 1;
+ }
+
+ }
+}
+
+static void
+dp83815_checkphy(dp83815_softc *sc)
+{
+ uint32_t cfg;
+ uint32_t status;
+
+ (void)READCSR(sc, R_MISR); /* Clear at PHY */
+ cfg = READCSR(sc, R_CFG);
+ status = cfg & M_CFG_LNKSUMMARY;
+ if (status != sc->phy_status) {
+ /* XXX Can we really do the phy update with active rx and tx? */
+ dp83815_phyupdate(sc, status);
+ }
+
+ sc->intmask |= M_INT_PHY;
+ WRITECSR(sc, R_IMR, sc->intmask);
+}
+
+
+static void
+dp83815_start(dp83815_softc *sc)
+{
+ dp83815_hwinit(sc);
+
+ /* Set up loopback here */
+
+ sc->intmask = 0;
+ WRITECSR(sc, R_IER, 0); /* no interrupts */
+ WRITECSR(sc, R_IMR, 0);
+ (void)READCSR(sc, R_ISR); /* clear any pending */
+
+ sc->phy_status = READCSR(sc, R_CFG) & M_CFG_LNKSUMMARY;
+ sc->phy_check = 0;
+
+ sc->intmask = M_INT_RXDESC | M_INT_TXDESC;
+ sc->intmask |= M_INT_RTABT | M_INT_RMABT | M_INT_RXORN | M_INT_TXURN;
+ sc->intmask |= M_INT_PHY;
+
+#if IPOLL
+ cfe_request_irq(sc->irq, dp83815_isr, sc, CFE_IRQ_FLAGS_SHARED, 0);
+ WRITECSR(sc, R_IMR, sc->intmask);
+ WRITECSR(sc, R_IER, M_IER_IE);
+#endif
+
+ (void)READCSR(sc, R_MISR); /* clear any pending */
+ WRITECSR(sc, R_MISR, MISR_MSKJAB | MISR_MSKRF | MISR_MSKFHF | MISR_MSKRHF);
+ WRITECSR(sc, R_MICR, MICR_INTEN);
+
+ WRITECSR(sc, R_TXDP, PTR_TO_PCI(sc->txdscr_start));
+ WRITECSR(sc, R_RXDP, PTR_TO_PCI(sc->rxdscr_start));
+
+ WRITECSR(sc, R_MIBC, M_MIBC_ACLR); /* zero hw MIB counters */
+
+ WRITECSR(sc, R_CR, M_CR_TXE | M_CR_RXE);
+ sc->state = eth_state_on;
+}
+
+static void
+dp83815_stop(dp83815_softc *sc)
+{
+ uint32_t status;
+ int count;
+
+ /* Make sure that no further interrutps will be processed. */
+ sc->intmask = 0;
+ WRITECSR(sc, R_IER, 0);
+ WRITECSR(sc, R_IMR, 0);
+
+#if IPOLL
+ (void)READCSR(sc, R_IER); /* Push */
+ cfe_free_irq(sc->irq, 0);
+#endif
+
+ WRITECSR(sc, R_CR, M_CR_TXD | M_CR_RXD);
+
+ /* wait for any DMA activity to terminate */
+ for (count = 0; count <= 13; count++) {
+ status = READCSR(sc, R_CR);
+ if ((status & (M_CR_TXE | M_CR_RXE)) == 0)
+ break;
+ cfe_sleep(CFE_HZ/10);
+ }
+ if (count > 13) {
+ xprintf("%s: idle state not achieved\n", dp83815_devname(sc));
+ dumpstat(sc);
+ RESET_ADAPTER(sc);
+ sc->state = eth_state_uninit;
+#if 1
+ sc->linkspeed = ETHER_SPEED_AUTO;
+#endif
+ }
+#if 0 /* XXX Not yet implemented. */
+ else if (sc->loopback != ETHER_LOOPBACK_OFF) {
+ dp83815_setloopback(sc, ETHER_LOOPBACK_OFF);
+ }
+#endif
+
+ (void)READCSR(sc, R_ISR); /* Clear any stragglers. */
+}
+
+
+/* *********************************************************************
+ * 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 dp83815_ether_open(cfe_devctx_t *ctx);
+static int dp83815_ether_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
+static int dp83815_ether_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat);
+static int dp83815_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
+static int dp83815_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
+static int dp83815_ether_close(cfe_devctx_t *ctx);
+static void dp83815_ether_poll(cfe_devctx_t *ctx, int64_t ticks);
+static void dp83815_ether_reset(void *softc);
+
+/* *********************************************************************
+ * CFE Device Driver dispatch structure
+ ********************************************************************* */
+
+const static cfe_devdisp_t dp83815_ether_dispatch = {
+ dp83815_ether_open,
+ dp83815_ether_read,
+ dp83815_ether_inpstat,
+ dp83815_ether_write,
+ dp83815_ether_ioctl,
+ dp83815_ether_close,
+ dp83815_ether_poll,
+ dp83815_ether_reset
+};
+
+/* *********************************************************************
+ * CFE Device Driver descriptor
+ ********************************************************************* */
+
+const cfe_driver_t dp83815drv = {
+ "DP83815 Ethernet",
+ "eth",
+ CFE_DEV_NETWORK,
+ &dp83815_ether_dispatch,
+ dp83815_ether_probe
+};
+
+
+static int
+dp83815_ether_attach(cfe_driver_t *drv,
+ pcitag_t tag, int index, uint8_t hwaddr[])
+{
+ dp83815_softc *sc;
+ uint32_t device;
+ uint32_t class;
+ phys_addr_t pa;
+ uint8_t promaddr[ENET_ADDR_LEN];
+ char descr[100];
+ uint32_t srr;
+
+ device = pci_conf_read(tag, R_CFGID);
+ class = pci_conf_read(tag, R_CFGRID);
+
+#if 1
+ /* Use memory space for the CSRs */
+ pci_map_mem(tag, R_CFGMA, PCI_MATCH_BITS, &pa);
+#else
+ /* Use i/o space for the CSRs */
+ pci_map_io(tag, R_CFGIOA, PCI_MATCH_BITS, &pa);
+#endif
+
+ sc = (dp83815_softc *) KMALLOC(sizeof(dp83815_softc), 0);
+
+ if (sc == NULL) {
+ xprintf("DP83815: No memory to complete probe\n");
+ return 0;
+ }
+ memset(sc, 0, sizeof(*sc));
+
+ sc->membase = (uint32_t)pa;
+ sc->irq = pci_conf_read(tag, R_CFGINT) & 0xFF;
+ sc->tag = tag;
+ sc->device = PCI_PRODUCT(device);
+ sc->revision = PCI_REVISION(class);
+ sc->devctx = NULL;
+
+#if 1
+ sc->linkspeed = ETHER_SPEED_AUTO; /* select autonegotiation */
+#else
+ sc->linkspeed = ETHER_SPEED_100FDX; /* 100 Mbps, full duplex */
+#endif
+ sc->loopback = ETHER_LOOPBACK_OFF;
+ memcpy(sc->hwaddr, hwaddr, ENET_ADDR_LEN);
+
+ srr = READCSR(sc, R_SRR);
+#if 0
+ /* The NS data sheet recommends the following for "optimal
+ performance" of CVNG parts. Tested on a sample of one CVNG
+ part on an NS "Macphyter Demo II" eval board, it seemed to
+ produce slightly less reliable initial behavior. */
+ if (G_SRR_REV(srr) == K_REV_CVNG) {
+ /* Update PHY DSP registers per data sheet. */
+ WRITECSR(sc, R_PGSEL, 0x0001);
+ (void)READCSR(sc, R_PGSEL); /* push */
+ WRITECSR(sc, R_PMDCSR, 0x189C);
+ WRITECSR(sc, R_TSTDAT, 0x0000);
+ WRITECSR(sc, R_DSPCFG, 0x5040);
+ WRITECSR(sc, R_SDCFG, 0x008C);
+ }
+#endif
+
+ dp83815_init(sc);
+
+ /* Prefer the address in EEPROM. This will be read into the
+ PMATCH register upon power up. Unfortunately, how to test for
+ completion of the auto-load (but see PTSCR_EELOAD_EN). */
+ if (dp83815_get_pm_addr(sc, promaddr) == 0) {
+ memcpy(sc->hwaddr, promaddr, ENET_ADDR_LEN);
+ }
+
+ sc->state = eth_state_uninit;
+
+ xsprintf(descr, "%s at 0x%X (%02x-%02x-%02x-%02x-%02x-%02x)",
+ drv->drv_description, sc->membase,
+ sc->hwaddr[0], sc->hwaddr[1], sc->hwaddr[2],
+ sc->hwaddr[3], sc->hwaddr[4], sc->hwaddr[5]);
+
+ cfe_attach(drv, sc, NULL, descr);
+ return 1;
+}
+
+
+/* *********************************************************************
+ * DP83815_ETHER_PROBE(drv,probe_a,probe_b,probe_ptr)
+ *
+ * Probe and install drivers for all dp83815 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
+dp83815_ether_probe(cfe_driver_t *drv,
+ unsigned long probe_a, unsigned long probe_b,
+ void *probe_ptr)
+{
+ int n;
+ uint8_t hwaddr[ENET_ADDR_LEN];
+
+ if (probe_ptr)
+ eth_parse_hwaddr((char *) probe_ptr, hwaddr);
+ else {
+ /* use default address 02-00-00-10-0B-00 */
+ hwaddr[0] = 0x02; hwaddr[1] = 0x00; hwaddr[2] = 0x00;
+ hwaddr[3] = 0x10; hwaddr[4] = 0x0B; hwaddr[5] = 0x00;
+ }
+
+ n = 0;
+ for (;;) {
+ pcitag_t tag;
+
+ if (pci_find_device(K_PCI_VENDOR_NSC, K_PCI_ID_DP83815, n, &tag) != 0)
+ break;
+ dp83815_ether_attach(drv, tag, n, hwaddr);
+ n++;
+ eth_incr_hwaddr(hwaddr, 1);
+ }
+}
+
+
+/* The functions below are called via the dispatch vector for the 83815. */
+
+/* *********************************************************************
+ * DP83815_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
+dp83815_ether_open(cfe_devctx_t *ctx)
+{
+ dp83815_softc *sc = ctx->dev_softc;
+
+ if (sc->state == eth_state_on)
+ dp83815_stop(sc);
+
+ sc->devctx = ctx;
+
+ sc->inpkts = sc->outpkts = 0;
+ sc->interrupts = 0;
+ sc->rx_interrupts = sc->tx_interrupts = 0;
+
+ dp83815_start(sc);
+
+#if XPOLL
+ dp83815_isr(sc);
+#endif
+
+ return 0;
+}
+
+/* *********************************************************************
+ * DP83815_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
+dp83815_ether_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
+{
+ dp83815_softc *sc = ctx->dev_softc;
+ eth_pkt_t *pkt;
+ int blen;
+
+#if XPOLL
+ dp83815_isr(sc);
+#endif
+
+ if (sc->state != eth_state_on) return -1;
+
+ CS_ENTER(sc);
+ pkt = (eth_pkt_t *) q_deqnext(&(sc->rxqueue));
+ CS_EXIT(sc);
+
+ 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(sc, pkt);
+ dp83815_fillrxring(sc);
+
+#if XPOLL
+ dp83815_isr(sc);
+#endif
+
+ return 0;
+}
+
+/* *********************************************************************
+ * DP83815_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
+dp83815_ether_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
+{
+ dp83815_softc *sc = ctx->dev_softc;
+
+#if XPOLL
+ dp83815_isr(sc);
+#endif
+
+ if (sc->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(&(sc->rxqueue))) ? 0 : 1;
+
+ return 0;
+}
+
+/* *********************************************************************
+ * DP83815_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
+dp83815_ether_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
+{
+ dp83815_softc *sc = ctx->dev_softc;
+ eth_pkt_t *pkt;
+ int blen;
+
+#if XPOLL
+ dp83815_isr(sc);
+#endif
+
+ if (sc->state != eth_state_on) return -1;
+
+ pkt = eth_alloc_pkt(sc);
+ 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 (dp83815_transmit(sc, pkt) != 0) {
+ eth_free_pkt(sc,pkt);
+ return CFE_ERR_IOERR;
+ }
+
+#if XPOLL
+ dp83815_isr(sc);
+#endif
+
+ return 0;
+}
+
+/* *********************************************************************
+ * DP83815_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
+dp83815_ether_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
+{
+ dp83815_softc *sc = ctx->dev_softc;
+ int *argp;
+ int mode;
+ int speed;
+
+ switch ((int)buffer->buf_ioctlcmd) {
+ case IOCTL_ETHER_GETHWADDR:
+ memcpy(buffer->buf_ptr, sc->hwaddr, sizeof(sc->hwaddr));
+ return 0;
+
+ case IOCTL_ETHER_SETHWADDR:
+ return -1; /* not supported */
+
+ case IOCTL_ETHER_GETSPEED:
+ argp = (int *) buffer->buf_ptr;
+ *argp = sc->linkspeed;
+ return 0;
+
+ case IOCTL_ETHER_SETSPEED:
+ dp83815_stop(sc);
+ dp83815_resetrings(sc);
+ speed = *((int *) buffer->buf_ptr);
+ dp83815_setspeed(sc, speed);
+ dp83815_start(sc);
+ sc->state = eth_state_on;
+ return 0;
+
+ case IOCTL_ETHER_GETLINK:
+ argp = (int *) buffer->buf_ptr;
+ *argp = sc->linkspeed;
+ return 0;
+
+ case IOCTL_ETHER_GETLOOPBACK:
+ *((int *) buffer) = sc->loopback;
+ return 0;
+
+ case IOCTL_ETHER_SETLOOPBACK:
+ dp83815_stop(sc);
+ dp83815_resetrings(sc);
+ mode = *((int *) buffer->buf_ptr);
+ sc->loopback = ETHER_LOOPBACK_OFF; /* default */
+ if (mode == ETHER_LOOPBACK_INT || mode == ETHER_LOOPBACK_EXT) {
+ dp83815_setloopback(sc, mode);
+ }
+ dp83815_start(sc);
+ sc->state = eth_state_on;
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+/* *********************************************************************
+ * DP83815_ETHER_CLOSE(ctx)
+ *
+ * Close the Ethernet device.
+ *
+ * Input parameters:
+ * ctx - device context (includes ptr to our softc)
+ *
+ * Return value:
+ * status, 0 = ok
+ ********************************************************************* */
+static int
+dp83815_ether_close(cfe_devctx_t *ctx)
+{
+ dp83815_softc *sc = ctx->dev_softc;
+
+ sc->state = eth_state_off;
+ dp83815_stop(sc);
+
+ /* resynchronize descriptor rings */
+ dp83815_resetrings(sc);
+
+ xprintf("%s: %d sent, %d received, %d interrupts\n",
+ dp83815_devname(sc), sc->outpkts, sc->inpkts, sc->interrupts);
+ xprintf(" %d rx interrupts, %d tx interrupts\n",
+ sc->rx_interrupts, sc->tx_interrupts);
+
+ sc->devctx = NULL;
+ return 0;
+}
+
+
+/* *********************************************************************
+ * DP83815_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
+dp83815_ether_poll(cfe_devctx_t *ctx, int64_t ticks)
+{
+ dp83815_softc *sc = ctx->dev_softc;
+
+ if (sc->phy_check) {
+ sc->phy_check = 0;
+ dp83815_checkphy(sc);
+ }
+}
+
+
+/* *********************************************************************
+ * DP83815_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 dp83815_softc
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void
+dp83815_ether_reset(void *softc)
+{
+ dp83815_softc *sc = (dp83815_softc *)softc;
+
+ /* Turn off the Ethernet interface. */
+
+ /* RESET_ADAPTER(sc); */
+
+ sc->state = eth_state_uninit;
+}