diff options
author | Ralf Corsepius <ralf.corsepius@rtems.org> | 2006-01-09 15:41:42 +0000 |
---|---|---|
committer | Ralf Corsepius <ralf.corsepius@rtems.org> | 2006-01-09 15:41:42 +0000 |
commit | 9d8ad283c2ac3b3ec694a5e6a3dcdabc3a4841d1 (patch) | |
tree | e5882b80a81d5c645b80e72ee0ade5189e4d8163 /c/src/libchip | |
parent | 2006-01-09 Ralf Corsepius <ralf.corsepius@rtems.org> (diff) | |
download | rtems-9d8ad283c2ac3b3ec694a5e6a3dcdabc3a4841d1.tar.bz2 |
2006-01-09 Ralf Corsepius <ralf.corsepius@rtems.org>
* libchip/network/smc91111.c
libchip/network/smc91111config.h
libchip/network/smc91111exp.h
libchip/network/smc91111.h: New. Merger from rtems-4-6-branch.
* libchip/Makefile.am: Reflect adding smc91111*.
Diffstat (limited to 'c/src/libchip')
-rw-r--r-- | c/src/libchip/Makefile.am | 10 | ||||
-rw-r--r-- | c/src/libchip/network/smc91111.c | 1588 | ||||
-rw-r--r-- | c/src/libchip/network/smc91111.h | 552 | ||||
-rw-r--r-- | c/src/libchip/network/smc91111config.h | 132 | ||||
-rw-r--r-- | c/src/libchip/network/smc91111exp.h | 19 |
5 files changed, 2297 insertions, 4 deletions
diff --git a/c/src/libchip/Makefile.am b/c/src/libchip/Makefile.am index 63f1da3f77..f964b6ef26 100644 --- a/c/src/libchip/Makefile.am +++ b/c/src/libchip/Makefile.am @@ -29,16 +29,18 @@ endif # network if LIBCHIP if HAS_NETWORKING +noinst_LIBRARIES += libnetchip.a +libnetchip_a_CPPFLAGS = $(AM_CPPFLAGS) +libnetchip_a_CPPFLAGS += -D__INSIDE_RTEMS_BSD_TCPIP_STACK__ include_libchip_HEADERS += network/cs8900.h network/i82586var.h \ network/if_fxpvar.h network/sonic.h include_libchip_HEADERS += network/open_eth.h network/if_dcreg.h - -libnetchip_CPPFLAGS = -D__INSIDE_RTEMS_BSD_TCPIP_STACK__ -noinst_LIBRARIES += libnetchip.a libnetchip_a_SOURCES = network/cs8900.c network/dec21140.c network/i82586.c \ network/sonic.c network/if_fxp.c network/elnk.c network/open_eth.c \ network/if_dc.c -libnetchip_a_CPPFLAGS = $(AM_CPPFLAGS) $(libnetchip_CPPFLAGS) +# FIXME: These don't compile +include_libchip_HEADERS += network/smc91111.h network/smc91111exp.h +# libnetchip_a_SOURCES += network/smc91111.c network/smc91111config.h endif endif diff --git a/c/src/libchip/network/smc91111.c b/c/src/libchip/network/smc91111.c new file mode 100644 index 0000000000..94ea6fc6b0 --- /dev/null +++ b/c/src/libchip/network/smc91111.c @@ -0,0 +1,1588 @@ +/* + * $Id$ + */ + +#include <rtems.h> +#include <errno.h> + +/* + * This driver currently only supports architectures with the old style + * exception processing. The following checks try to keep this + * from being compiled on systems which can't support this driver. + * + * NOTE: As of 28 September 2005, this has only been tested on the SPARC, + * so that is all it is enabled for. + */ + +#if defined(__sparc__) + #define SMC91111_SUPPORTED +#endif + +#if defined(SMC91111_SUPPORTED) + +#include <bsp.h> + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> + +#include <sys/param.h> +#include <sys/mbuf.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#define SMC91111_INTERRUPT_EVENT RTEMS_EVENT_1 /* RTEMS event used by interrupt handler to signal driver tasks. This must not be any of the events used by the network task synchronization. */ +#define SMC91111_START_TRANSMIT_EVENT RTEMS_EVENT_2 /* RTEMS event used to start transmit/receive daemon. This must not be the same as INTERRUPT_EVENT. */ +#define SMC91111_TX_WAIT_EVENT RTEMS_EVENT_3 /* event to send when tx buffers become available */ + +/* Set to perms of: + 0 disables all debug output + 1 for process debug output + 2 for added data IO output: get_reg, put_reg + 4 for packet allocation/free output + 8 for only startup status, so we can tell we're installed OK + 16 dump phy read/write + 32 precise register dump + 64 dump packets +*/ +/*#define DEBUG (-1)*/ +/*#define DEBUG (-1 & ~(16))*/ +#define DEBUG (0) + +#include "smc91111config.h" +#include <libchip/smc91111.h> + +struct lan91cxx_priv_data smc91111; + +int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd); +static cyg_uint16 lan91cxx_read_phy(struct lan91cxx_priv_data *cpd, + cyg_uint8 phyaddr, cyg_uint8 phyreg); +static void lan91cxx_write_phy(struct lan91cxx_priv_data *cpd, + cyg_uint8 phyaddr, cyg_uint8 phyreg, + cyg_uint16 value); +static void lan91cxx_start(struct ifnet *ifp); +static void smc91111_start(struct ifnet *ifp); +static int smc_probe(struct lan91cxx_priv_data *cpd); +static void smc91111_stop(struct lan91cxx_priv_data *cpd); +static void smc91111_init(void *arg); +static void lan91cxx_finish_sent(struct lan91cxx_priv_data *cpd); +#if 0 +static int lan91cxx_phy_fixed(struct lan91cxx_priv_data *cpd); +static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd); +#endif + +#define min(l,r) ((l) < (r) ? (l) : (r)) +#define max(l,r) ((l) > (r) ? (l) : (r)) + +/* \ ------------- Interrupt ------------- \ */ +rtems_isr lan91cxx_interrupt_handler(rtems_vector_number v) +{ + struct lan91cxx_priv_data *cpd = &smc91111; + unsigned short irq, event; + unsigned short oldbase; + unsigned short oldpointer; + INCR_STAT(cpd, interrupts); + DEBUG_FUNCTION(); + + HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), oldbase); + oldpointer = get_reg(cpd, LAN91CXX_POINTER); + + /* Get the (unmasked) requests */ + irq = get_reg(cpd, LAN91CXX_INTERRUPT); + event = irq & (irq >> 8) & 0xff; + if (0 == event) + return; + + /*put_reg(cpd, LAN91CXX_INTERRUPT, irq ); *//* ack interrupts */ + + if (event & LAN91CXX_INTERRUPT_ERCV_INT) { + db_printf("Early receive interrupt"); + } else if (event & LAN91CXX_INTERRUPT_EPH_INT) { + db_printf("ethernet protocol handler failures"); + } else if (event & LAN91CXX_INTERRUPT_RX_OVRN_INT) { + db_printf("receive overrun"); + } else if (event & LAN91CXX_INTERRUPT_ALLOC_INT) { + db_printf("allocation interrupt"); + } else { + + if (event & LAN91CXX_INTERRUPT_TX_SET) { + DEBUG_puts("#tx error\n"); + db_printf("#*tx irq\n"); + lan91cxx_finish_sent(cpd); + put_reg(cpd, LAN91CXX_INTERRUPT, + (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT); + + /*rtems_event_send (cpd->txDaemonTid, SMC91111_INTERRUPT_EVENT); */ + /*put_reg(cpd, LAN91CXX_INTERRUPT, (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT); */ + /*rtems_event_send (cpd->txDaemonTid, SMC91111_TX_WAIT_EVENT); */ + } + if (event & LAN91CXX_INTERRUPT_RCV_INT) { + db_printf("#*rx irq\n"); + rtems_event_send(cpd->rxDaemonTid, + SMC91111_INTERRUPT_EVENT); + } + if (event & + ~(LAN91CXX_INTERRUPT_TX_SET | LAN91CXX_INTERRUPT_RCV_INT)) + db_printf("Unknown interrupt\n"); + } + db_printf("out %s\n", __FUNCTION__); + + put_reg(cpd, LAN91CXX_POINTER, oldpointer); + HAL_WRITE_UINT16(cpd->base + (LAN91CXX_BS), oldbase); +} + +/* \ ------------- Rx receive ------------- \ */ + + /**/ +/* This function is called as a result of the "readpacket()" call.*/ +/* Its job is to actually fetch data for a packet from the hardware once*/ +/* memory buffers have been allocated for the packet. Note that the buffers*/ +/* may come in pieces, using a mbuf list. */ +static void lan91cxx_recv(struct lan91cxx_priv_data *cpd, struct mbuf *m) +{ + struct ifnet *ifp = &cpd->arpcom.ac_if; + struct ether_header *eh; + short mlen = 0, plen; + char *start; + rxd_t *data = NULL, val, lp; + struct mbuf *n; + lp = 0; + dbg_prefix = "<"; + + DEBUG_FUNCTION(); + INCR_STAT(cpd, rx_deliver); + + /* ############ read packet ############ */ + + put_reg(cpd, LAN91CXX_POINTER, + (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ | + LAN91CXX_POINTER_AUTO_INCR)); + val = get_data(cpd); + + /* packet length (minus header/footer) */ +#ifdef LAN91CXX_32BIT_RX + val = CYG_LE32_TO_CPU(val); + plen = (val >> 16) - 6; +#else + val = CYG_LE16_TO_CPU(val); + plen = get_data(cpd); + plen = CYG_LE16_TO_CPU(plen) - 6; +#endif + + if (LAN91CXX_RX_STATUS_IS_ODD(cpd, val)) + plen++; + + for (n = m; n; n = n->m_next) { +#ifdef LAN91CXX_32BIT_RX + if (mlen == 2) { +#if DEBUG & 64 + db_printf("Appending to last apacket\n"); +#endif + + val = get_data(cpd); + *((unsigned short *)data) = (val >> 16) & 0xffff; + plen -= 2; + data = (rxd_t *) n->m_data; + start = (char *)data; + mlen = n->m_len; + if ((data) && (mlen > 1)) { + *((unsigned short *)data)++ = (val & 0xffff); + plen -= 2; + mlen -= 2; + } + } else { + data = (rxd_t *) n->m_data; + start = (char *)data; + mlen = n->m_len; + } +#else + data = (rxd_t *) n->m_data; + start = (char *)data; + mlen = n->m_len; +#endif + + db1_printf("<[packet : mlen 0x%x, plen 0x%x]\n", mlen, plen); + + if (data) { + while (mlen >= sizeof(*data)) { +#ifdef LAN91CXX_32BIT_RX + val = get_data(cpd); + *((unsigned short *)data)++ = + (val >> 16) & 0xffff; + *((unsigned short *)data)++ = (val & 0xffff); +#else + *data++ = get_data(cpd); +#endif + mlen -= sizeof(*data); + plen -= sizeof(*data); + } + } else { /* must actively discard ie. read it from the chip anyway. */ + while (mlen >= sizeof(*data)) { + (void)get_data(cpd); + mlen -= sizeof(*data); + plen -= sizeof(*data); + } + } + +#if DEBUG & 64 + lp = 0; + while (((int)start) < ((int)data)) { + unsigned char a = *(start++); + unsigned char b = *(start++); + db64_printf("%02x %02x ", a, b); + lp += 2; + if (lp >= 16) { + db64_printf("\n"); + lp = 0; + } + } + db64_printf(" \n"); + +#endif + } + val = get_data(cpd); /* Read control word (and potential data) unconditionally */ +#ifdef LAN91CXX_32BIT_RX + if (plen & 2) { + if (data) { + *((unsigned short *)data)++ = (val >> 16) & 0xffff; + val <<= 16; + } + } + if (plen & 1) + *(unsigned char *)data = val >> 24; +#else + val = CYG_LE16_TO_CPU(val); + cp = (unsigned char *)data; + + CYG_ASSERT(val & LAN91CXX_CONTROLBYTE_RX, "Controlbyte is not for Rx"); + CYG_ASSERT((1 == mlen) == (0 != LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)), + "Controlbyte does not match"); + if (data && (1 == mlen) && LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)) { + cval = val & 0x00ff; /* last byte contains data */ + *cp = cval; + } +#endif + + val = get_reg(cpd, LAN91CXX_FIFO_PORTS); + if (0x8000 & val) { /* Then the Rx FIFO is empty */ + db4_printf + ("<+Rx packet NOT freed, stat is %x (expected %x)\n", + val, cpd->rxpacket); + } else { + db4_printf("<+Rx packet freed %x (expected %x)\n", + 0xff & (val >> 8), cpd->rxpacket); + } + + CYG_ASSERT((0xff & (val >> 8)) == cpd->rxpacket, + "Unexpected rx packet"); + + /* ############ free packet ############ */ + /* Free packet */ + put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame); + + dbg_prefix = ""; + + /* Remove the mac header. This is different from the NetBSD stack. */ + eh = mtod(m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + m->m_len -= sizeof(struct ether_header); + m->m_pkthdr.len -= sizeof(struct ether_header); + + ether_input(ifp, eh, m); + +} + +/* allocate mbuf chain */ +static struct mbuf *smc91111_allocmbufchain(int totlen, struct ifnet *ifp) +{ + + struct mbuf *m, *m0, *newm; + int len; + + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 == 0) + return (0); + m0->m_pkthdr.rcvif = ifp; + m0->m_pkthdr.len = totlen; + len = MHLEN; + m = m0; + + /* This loop goes through and allocates mbufs for all the data we will be copying in. */ + while (totlen > 0) { + if (totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) + goto bad; + len = MCLBYTES; + } + + if (m == m0) { + caddr_t newdata = (caddr_t) + ALIGN(m->m_data + + sizeof(struct ether_header)) - + sizeof(struct ether_header); + len -= newdata - m->m_data; + m->m_data = newdata; + } + + m->m_len = len = min(totlen, len); + + totlen -= len; + if (totlen > 0) { + MGET(newm, M_DONTWAIT, MT_DATA); + if (newm == 0) + goto bad; + len = MLEN; + m = m->m_next = newm; + } + } + return (m0); + + bad: + m_freem(m0); + return (0); +} + +static int readpacket(struct lan91cxx_priv_data *cpd) +{ + struct mbuf *m; + unsigned short stat, complen; + struct ifnet *ifp = &cpd->arpcom.ac_if; +#ifdef LAN91CXX_32BIT_RX + cyg_uint32 val; +#endif + + DEBUG_FUNCTION(); + + /* ############ read packet nr ############ */ + stat = get_reg(cpd, LAN91CXX_FIFO_PORTS); + db1_printf("+LAN91CXX_FIFO_PORTS: 0x%04x\n", stat); + + if (0x8000 & stat) { + /* Then the Rx FIFO is empty */ + db4_printf("!RxEvent with empty fifo\n"); + return 0; + } + + INCR_STAT(cpd, rx_count); + + db4_printf("+Rx packet allocated %x (previous %x)\n", + 0xff & (stat >> 8), cpd->rxpacket); + + /* There is an Rx Packet ready */ + cpd->rxpacket = 0xff & (stat >> 8); + + /* ############ read packet header ############ */ + /* Read status and (word) length */ + put_reg(cpd, LAN91CXX_POINTER, + (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ | + LAN91CXX_POINTER_AUTO_INCR | 0x0000)); +#ifdef LAN91CXX_32BIT_RX + val = get_data(cpd); + val = CYG_LE32_TO_CPU(val); + stat = val & 0xffff; + complen = ((val >> 16) & 0xffff) - 6; /* minus header/footer words */ +#else + stat = get_data(cpd); + stat = CYG_LE16_TO_CPU(stat); + complen = get_data(cpd); + complen = CYG_LE16_TO_CPU(len) - 6; /* minus header/footer words */ +#endif + +#ifdef KEEP_STATISTICS + if (stat & LAN91CXX_RX_STATUS_ALIGNERR) + INCR_STAT(cpd, rx_align_errors); + /*if ( stat & LAN91CXX_RX_STATUS_BCAST ) INCR_STAT( ); */ + if (stat & LAN91CXX_RX_STATUS_BADCRC) + INCR_STAT(cpd, rx_crc_errors); + if (stat & LAN91CXX_RX_STATUS_TOOLONG) + INCR_STAT(cpd, rx_too_long_frames); + if (stat & LAN91CXX_RX_STATUS_TOOSHORT) + INCR_STAT(cpd, rx_short_frames); + /*if ( stat & LAN91CXX_RX_STATUS_MCAST ) INCR_STAT( ); */ +#endif /* KEEP_STATISTICS */ + + if ((stat & LAN91CXX_RX_STATUS_BAD) == 0) { + INCR_STAT(cpd, rx_good); + /* Then it's OK */ + + if (LAN91CXX_RX_STATUS_IS_ODD(cpd, stat)) + complen++; + +#if DEBUG & 1 + db_printf("good rx - stat: 0x%04x, len: 0x%04x\n", stat, + complen); +#endif + /* Check for bogusly short packets; can happen in promisc mode: */ + /* Asserted against and checked by upper layer driver. */ + if (complen > sizeof(struct ether_header)) { + /* then it is acceptable; offer the data to the network stack */ + + complen = ((complen + 3) & ~3); + + m = smc91111_allocmbufchain(complen, ifp); + { + struct mbuf *n = m; + db_printf("mbuf-chain:"); + while (n) { + db_printf("[%x:%x]", + (unsigned int)(n-> + m_data), + (unsigned int)(n->m_len)); + n = n->m_next; + } + db_printf("\n"); + } + + if (m) { + /* fetch packet data into mbuf chain */ + lan91cxx_recv(cpd, m); + return 1; + } + } + /*(sc->funs->eth_drv->recv)(sc, len); */ + } + + /* Not OK for one reason or another... */ + db1_printf("!bad rx: stat: 0x%04x, len: 0x%04x\n", stat, complen); + + /* ############ free packet ############ */ + /* Free packet */ + put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame); + return 1; + +} + +static void smc91111_rxDaemon(void *arg) +{ + struct lan91cxx_priv_data *cpd = arg; + rtems_event_set events; + DEBUG_FUNCTION(); + + for (;;) { + rtems_bsdnet_event_receive(INTERRUPT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + + /* read until read fifo is empty */ + while (!(get_reg(cpd, LAN91CXX_FIFO_PORTS) & 0x8000)) { + readpacket(cpd); + } + } +} + +/* \ ------------- Tx send ------------- \ */ + +static void sendpacket(struct ifnet *ifp, struct mbuf *m) +{ + struct lan91cxx_priv_data *cpd = ifp->if_softc; + int i, len, plen, tcr; + struct mbuf *n = m; + unsigned short *sdata = NULL; + unsigned short ints, control; + cyg_uint16 packet, status; + dbg_prefix = ">"; + DEBUG_FUNCTION(); + + cpd->txbusy = 1; + + /* Worry about the TX engine stopping. */ + tcr = get_reg(cpd, LAN91CXX_TCR); + if (0 == (LAN91CXX_TCR_TXENA & tcr)) { + db1_printf("> ENGINE RESTART: tcr %x\n", tcr); + tcr |= LAN91CXX_TCR_TXENA; + put_reg(cpd, LAN91CXX_TCR, tcr); + } + + /* ############ packet allocation ############ */ + + /* Find packet length */ + plen = 0; + while (n) { + plen += n->m_len; + n = n->m_next; + } + + /* Alloc new TX packet */ + do { + put_reg(cpd, LAN91CXX_MMU_COMMAND, + LAN91CXX_MMU_alloc_for_tx | ((plen >> 8) & 0x07)); + + i = 1024 * 1024; + do { + status = get_reg(cpd, LAN91CXX_INTERRUPT); + } while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT) + && (--i > 0)); + if (i) + packet = get_reg(cpd, LAN91CXX_PNR); + else + packet = 0xffff; + db1_printf(">+allocated packet %04x\n", packet); + + packet = packet >> 8; + if (packet & 0x80) { + /* Hm.. Isn't this a dead end? */ + db1_printf("Allocation failed! Retrying...\n"); + continue; + } + } while (0); + + db4_printf(">+Tx packet allocated %x (previous %x)\n", + packet, cpd->txpacket); + + cpd->txpacket = packet; + + /* ############ assemble packet data ############ */ + + /* prepare send */ + put_reg(cpd, LAN91CXX_PNR, packet); + /* Note: Check FIFO state here before continuing? */ + put_reg(cpd, LAN91CXX_POINTER, LAN91CXX_POINTER_AUTO_INCR | 0x0000); + /* Pointer is now set, and the proper bank is selected for */ + /* data writes. */ + + /* Prepare header: */ + put_data(cpd, CYG_CPU_TO_LE16(0)); /* reserve space for status word */ + /* packet length (includes status, byte-count and control shorts) */ + put_data(cpd, CYG_CPU_TO_LE16(0x7FE & (plen + 6))); /* Always even, always < 15xx(dec) */ + + /* Put data into buffer */ + n = m; + while (n) { + sdata = (unsigned short *)n->m_data; + len = n->m_len; + + CYG_ASSERT((0 == (len & 1) + || !(n->m_next)), "!odd length"); + CYG_ASSERT(sdata, "!No sg data pointer here"); + + while (len >= sizeof(*sdata)) { + put_data(cpd, *sdata++); + len -= sizeof(*sdata); + } + n = n->m_next; + } +#if DEBUG & 64 + n = m; + while (n) { + int lp = 0; + unsigned char *start = (unsigned char *)n->m_data; + len = n->m_len; + while (len > 0) { + unsigned char a = *(start++); + unsigned char b = *(start++); + db64_printf("%02x %02x ", a, b); + lp += 2; + if (lp >= 16) { + db64_printf("\n"); + lp = 0; + } + len -= 2; + } + n = n->m_next; + } + db64_printf(" \n"); +#endif + + m_freem(m); + CYG_ASSERT(sdata, "!No sg data pointer outside"); + + /* Lay down the control short unconditionally at the end. */ + /* (or it might use random memory contents) */ + control = 0; + if (1 & plen) { + /* Need to set ODD flag and insert the data */ + unsigned char onebyte = *(unsigned char *)sdata; + control = onebyte; + control |= LAN91CXX_CONTROLBYTE_ODD; + } + control |= LAN91CXX_CONTROLBYTE_CRC; /* Just in case... */ + put_data(cpd, CYG_CPU_TO_LE16(control)); + + /* ############ start transmit ############ */ + + /* Ack TX empty int and unmask it. */ + ints = get_reg(cpd, LAN91CXX_INTERRUPT) & 0xff00; + put_reg(cpd, LAN91CXX_INTERRUPT, + ints | LAN91CXX_INTERRUPT_TX_EMPTY_INT); + put_reg(cpd, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_INT_M); /* notify on error only (Autorelease) */ + + /* Enqueue the packet */ + put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_enq_packet); + + ints = get_reg(cpd, LAN91CXX_INTERRUPT); + db1_printf(">END: ints at TX: %04x\n", ints); + dbg_prefix = ""; +} + +void smc91111_txDaemon(void *arg) +{ + struct lan91cxx_priv_data *cpd = arg; + struct ifnet *ifp = &cpd->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + DEBUG_FUNCTION(); + + for (;;) { + /* + * Wait for packet + */ + + rtems_bsdnet_event_receive + (SMC91111_START_TRANSMIT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); + + /*IF_DEQUEUE (&ifp->if_snd, m); + if (m) { + sendpacket (ifp, m); + } */ + + for (;;) { + IF_DEQUEUE(&ifp->if_snd, m); + if (!m) + break; + sendpacket(ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + + } + +} + +/* start transmit */ +static void smc91111_start(struct ifnet *ifp) +{ + struct lan91cxx_priv_data *cpd = ifp->if_softc; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + rtems_event_send(cpd->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; + +} + +/* called after a tx error interrupt, freet the packet */ +static void lan91cxx_finish_sent(struct lan91cxx_priv_data *cpd) +{ + unsigned short packet, tcr; + int success = 1; + int saved_packet; + + DEBUG_FUNCTION(); + + INCR_STAT(cpd, tx_complete); + + saved_packet = get_reg(cpd, LAN91CXX_PNR); + + /* Ack and mask TX interrupt set */ + /*ints = get_reg(cpd, LAN91CXX_INTERRUPT) & 0xff00; + ints |= LAN91CXX_INTERRUPT_TX_SET_ACK; + ints &= ~LAN91CXX_INTERRUPT_TX_SET_M; + put_reg(cpd, LAN91CXX_INTERRUPT, ints); */ + + /* Get number of completed packet and read the status word */ + packet = get_reg(cpd, LAN91CXX_FIFO_PORTS); + db1_printf("%s:START: fifo %04x \n", __FUNCTION__, packet); + + { + unsigned short reg; + + reg = get_reg(cpd, LAN91CXX_EPH_STATUS); + + /* Covering each bit in turn... */ + if (reg & LAN91CXX_STATUS_TX_UNRN) + INCR_STAT(cpd, tx_underrun); + if (reg & LAN91CXX_STATUS_LOST_CARR) + INCR_STAT(cpd, tx_carrier_loss); + if (reg & LAN91CXX_STATUS_LATCOL) + INCR_STAT(cpd, tx_late_collisions); + if (reg & LAN91CXX_STATUS_TX_DEFR) + INCR_STAT(cpd, tx_deferred); + if (reg & LAN91CXX_STATUS_SQET) + INCR_STAT(cpd, tx_sqetesterrors); + if (reg & LAN91CXX_STATUS_16COL) + INCR_STAT(cpd, tx_max_collisions); + if (reg & LAN91CXX_STATUS_MUL_COL) + INCR_STAT(cpd, tx_mult_collisions); + if (reg & LAN91CXX_STATUS_SNGL_COL) + INCR_STAT(cpd, tx_single_collisions); + if (reg & LAN91CXX_STATUS_TX_SUC) + INCR_STAT(cpd, tx_good); + + cpd->stats.tx_total_collisions = + cpd->stats.tx_late_collisions + + cpd->stats.tx_max_collisions + + cpd->stats.tx_mult_collisions + + cpd->stats.tx_single_collisions; + + /* We do not need to look in the Counter Register (LAN91CXX_COUNTER) + because it just mimics the info we already have above. */ + } + + /* We do not really care about Tx failure. Ethernet is not a reliable + medium. But we do care about the TX engine stopping. */ + tcr = get_reg(cpd, LAN91CXX_TCR); + if (0 == (LAN91CXX_TCR_TXENA & tcr)) { + db1_printf("%s: ENGINE RESTART: tcr %x \n", __FUNCTION__, tcr); + tcr |= LAN91CXX_TCR_TXENA; + put_reg(cpd, LAN91CXX_TCR, tcr); + success = 0; /* And treat this as an error... */ + } + + packet &= 0xff; + + /* and then free the packet */ + put_reg(cpd, LAN91CXX_PNR, cpd->txpacket); + put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_rel_packet); + + while (get_reg(cpd, LAN91CXX_MMU_COMMAND) & LAN91CXX_MMU_COMMAND_BUSY) ; + /* Don't change Packet Number Reg until busy bit is cleared */ + /* Per LAN91C111 Spec, Page 50 */ + put_reg(cpd, LAN91CXX_PNR, saved_packet); + +} + +/* \ ------------- Helpers ------------- \ */ + +/* + * Show interface statistics + */ +static void smc91111_stats(struct lan91cxx_priv_data *priv) +{ + printf("tx_good :%-8d", priv->stats.tx_good); + printf("tx_max_collisions :%-8d", priv->stats.tx_max_collisions); + printf("tx_late_collisions :%-8d", priv->stats.tx_late_collisions); + printf("tx_underrun :%-8d", priv->stats.tx_underrun); + printf("tx_carrier_loss :%-8d", priv->stats.tx_carrier_loss); + printf("tx_deferred :%-8d", priv->stats.tx_deferred); + printf("tx_sqetesterrors :%-8d", priv->stats.tx_sqetesterrors); + printf("tx_single_collisions:%-8d", priv->stats.tx_single_collisions); + printf("tx_mult_collisions :%-8d", priv->stats.tx_mult_collisions); + printf("tx_total_collisions :%-8d", priv->stats.tx_total_collisions); + printf("rx_good :%-8d", priv->stats.rx_good); + printf("rx_crc_errors :%-8d", priv->stats.rx_crc_errors); + printf("rx_align_errors :%-8d", priv->stats.rx_align_errors); + printf("rx_resource_errors :%-8d", priv->stats.rx_resource_errors); + printf("rx_overrun_errors :%-8d", priv->stats.rx_overrun_errors); + printf("rx_collisions :%-8d", priv->stats.rx_collisions); + printf("rx_short_frames :%-8d", priv->stats.rx_short_frames); + printf("rx_too_long_frames :%-8d", priv->stats.rx_too_long_frames); + printf("rx_symbol_errors :%-8d", priv->stats.rx_symbol_errors); + printf("interrupts :%-8d", priv->stats.interrupts); + printf("rx_count :%-8d", priv->stats.rx_count); + printf("rx_deliver :%-8d", priv->stats.rx_deliver); + printf("rx_resource :%-8d", priv->stats.rx_resource); + printf("rx_restart :%-8d", priv->stats.rx_restart); + printf("tx_count :%-8d", priv->stats.tx_count); + printf("tx_complete :%-8d", priv->stats.tx_complete); + printf("tx_dropped :%-8d", priv->stats.tx_dropped); +} + +/* + * Driver ioctl handler + */ +static int smc91111_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct lan91cxx_priv_data *cpd = ifp->if_softc; + int error = 0; + DEBUG_FUNCTION(); + + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + db_printf("SIOCSIFADDR\n"); + ether_ioctl(ifp, command, data); + break; + + case SIOCSIFFLAGS: + db_printf("SIOCSIFFLAGS\n"); + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + smc91111_stop(cpd); + break; + + case IFF_UP: + smc91111_init(cpd); + break; + + case IFF_UP | IFF_RUNNING: + smc91111_stop(cpd); + smc91111_init(cpd); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + db_printf("SIO_RTEMS_SHOW_STATS\n"); + smc91111_stats(cpd); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + + return error; +} + +/* + * Attach an SMC91111 driver to the system + */ +int _rtems_smc91111_driver_attach (struct rtems_bsdnet_ifconfig *config, + struct scmv91111_configuration * chip) +{ + struct ifnet *ifp; + struct lan91cxx_priv_data *cpd; + int unitNumber; + char *unitName; + int mtu; + DEBUG_FUNCTION(); + +/* /\* activate io area *\/ */ +/* switch (sparc_leon23_get_psr_version()) { */ +/* case 0: */ +/* case 2: */ +/* db_printf("Activating Leon2 io port\n"); */ +/* /\*configure pio *\/ */ +/* *((volatile unsigned int *)0x80000000) |= 0x10f80000; */ +/* *((volatile unsigned int *)0x800000A8) |= */ +/* (0xe0 | chip->vector) << (8 * (chip->pio - 4)); */ +/* break; */ +/* default: */ +/* { */ +/* unsigned long irq_pio, irq_mctrl, addr_pio, addr_mctrl; */ +/* if ((addr_pio = */ +/* amba_find_apbslv_addr(VENDOR_GAISLER, */ +/* GAISLER_PIOPORT, &irq_pio)) */ +/* && (addr_mctrl = */ +/* amba_find_apbslv_addr(VENDOR_ESA, */ +/* ESA_MCTRL, &irq_mctrl))) { */ +/* LEON3_IOPORT_Regs_Map *io = */ +/* (LEON3_IOPORT_Regs_Map *) addr_pio; */ +/* db_printf */ +/* ("Activating Leon3 io port for smsc_lan91cxx (pio:%x mctrl:%x)\n", */ +/* (unsigned int)addr_pio, */ +/* (unsigned int)addr_mctrl); */ +/* *((volatile unsigned int *)addr_mctrl) |= 0x10f80000; /\*mctrl ctrl 1 *\/ */ +/* io->irqmask |= (1 << chip->pio); */ +/* io->irqpol |= (1 << chip->pio); */ +/* io->irqedge |= (1 << chip->pio); */ +/* io->iodir &= ~(1 << chip->pio); */ +/* } else { */ +/* return 0; */ +/* } */ +/* } */ +/* } */ + + /* parse driver name */ + if ((unitNumber = + rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) { + db_printf("Unitnumber < 0: %d\n", unitNumber); + return 0; + } + + db_printf("Unitnumber: %d, baseaddr: 0x%p\n", unitNumber, chip->baseaddr); + + cpd = &smc91111; + ifp = &cpd->arpcom.ac_if; + memset(cpd, 0, sizeof(*cpd)); + + cpd->config = *chip; + cpd->base = chip->baseaddr; + + if (smc_probe(cpd)) { + return 0; + } + + if (config->hardware_address) { + memcpy(cpd->arpcom.ac_enaddr, config->hardware_address, + ETHER_ADDR_LEN); + } else { + /* dummy default address */ + cpd->arpcom.ac_enaddr[0] = 0x12; + cpd->arpcom.ac_enaddr[1] = 0x13; + cpd->arpcom.ac_enaddr[2] = 0x14; + cpd->arpcom.ac_enaddr[3] = 0x15; + cpd->arpcom.ac_enaddr[4] = 0x16; + cpd->arpcom.ac_enaddr[5] = 0x17; + } + + cpd->enaddr[0] = cpd->arpcom.ac_enaddr[0]; + cpd->enaddr[1] = cpd->arpcom.ac_enaddr[1]; + cpd->enaddr[2] = cpd->arpcom.ac_enaddr[2]; + cpd->enaddr[3] = cpd->arpcom.ac_enaddr[3]; + cpd->enaddr[4] = cpd->arpcom.ac_enaddr[4]; + cpd->enaddr[5] = cpd->arpcom.ac_enaddr[5]; + cpd->rpc_cur_mode = + LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK | + LAN91CXX_RPCR_ANEG; + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + + /* + * Set up network interface values + */ + ifp->if_softc = cpd; + ifp->if_unit = unitNumber; + ifp->if_name = unitName; + ifp->if_mtu = mtu; + ifp->if_init = smc91111_init; + ifp->if_ioctl = smc91111_ioctl; + ifp->if_start = smc91111_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* + * Attach the interface + */ + if_attach(ifp); + ether_ifattach(ifp); + +#ifdef DEBUG + printf("SMC91111 : driver has been attached\n"); +#endif + + return 1; +}; + +/* \ ------------- Initialization ------------- \ */ + +/* + * Initialize and start the device + */ +static void smc91111_init(void *arg) +{ + struct lan91cxx_priv_data *cpd = arg; + struct ifnet *ifp = &cpd->arpcom.ac_if; + DEBUG_FUNCTION(); + + if (cpd->txDaemonTid == 0) { + + lan91cxx_hardware_init(cpd); + lan91cxx_start(ifp); + + cpd->rxDaemonTid = rtems_bsdnet_newproc("DCrx", 4096, + smc91111_rxDaemon, cpd); + cpd->txDaemonTid = + rtems_bsdnet_newproc("DCtx", 4096, smc91111_txDaemon, cpd); + } + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; +} + +/* + * Stop the device + */ +static void smc91111_stop(struct lan91cxx_priv_data *cpd) +{ + struct ifnet *ifp = &cpd->arpcom.ac_if; + DEBUG_FUNCTION(); + + ifp->if_flags &= ~IFF_RUNNING; + + /* Reset chip */ + put_reg(cpd, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST); + put_reg(cpd, LAN91CXX_RCR, 0); + cpd->txbusy = cpd->within_send = 0; + +} + +int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd) +{ + unsigned short val; + int i; + + DEBUG_FUNCTION(); + + cpd->txbusy = cpd->within_send = 0; + + /* install interrupt vector */ + db_printf("Install lan91cxx irqvector at %d\n", cpd->config.vector); + set_vector(lan91cxx_interrupt_handler, cpd->config.vector, 1); + + /* Reset chip */ + put_reg(cpd, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST); + put_reg(cpd, LAN91CXX_RCR, 0); + HAL_DELAY_US(100000); + put_reg(cpd, LAN91CXX_CONFIG, 0x9000); + put_reg(cpd, LAN91CXX_RCR, 0); + put_reg(cpd, LAN91CXX_TCR, 0); + put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu); + + val = get_reg(cpd, LAN91CXX_EPH_STATUS); + /* probe chip by reading the signature in BS register */ + val = get_banksel(cpd); + db9_printf("LAN91CXX - supposed BankReg @ %x = %04x\n", + (unsigned int)(cpd->base + LAN91CXX_BS), val); + + if ((0xff00 & val) != 0x3300) { + printf("No 91Cxx signature"); + printf("smsc_lan91cxx_init: No 91Cxx signature found\n"); + return 0; + } + + val = get_reg(cpd, LAN91CXX_REVISION); + + db9_printf("LAN91CXX - type: %01x, rev: %01x\n", + (val >> 4) & 0xf, val & 0xf); + + /* Set RevA flag for LAN91C111 so we can cope with the odd-bit bug. */ + cpd->c111_reva = (val == 0x3390); + + /* The controller may provide a function used to set up the ESA */ + if (cpd->config_enaddr) + (*cpd->config_enaddr) (cpd); + + db9_printf("LAN91CXX - status: %04x\n", val); + /* Use statically configured ESA from the private data */ + db9_printf + ("LAN91CXX - static ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", + cpd->enaddr[0], cpd->enaddr[1], cpd->enaddr[2], + cpd->enaddr[3], cpd->enaddr[4], cpd->enaddr[5]); + /* Set up hardware address */ + for (i = 0; i < sizeof(cpd->enaddr); i += 2) + put_reg(cpd, LAN91CXX_IA01 + i / 2, + cpd->enaddr[i] | (cpd->enaddr[i + 1] << 8)); + + return 1; +} + +/* + This function is called to "start up" the interface. It may be called + multiple times, even when the hardware is already running. It will be + called whenever something "hardware oriented" changes and should leave + the hardware ready to send/receive packets. +*/ +static void lan91cxx_start(struct ifnet *ifp) +{ + struct lan91cxx_priv_data *cpd = ifp->if_softc; + + cyg_uint16 intr; + cyg_uint16 phy_ctl; + int delay; + DEBUG_FUNCTION(); + + HAL_DELAY_US(100000); + + /* 91C111 Errata. Internal PHY comes up disabled. Must enable here. */ + phy_ctl = lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_CTRL); + phy_ctl &= ~LAN91CXX_PHY_CTRL_MII_DIS; + lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CTRL, phy_ctl); + + /* Start auto-negotiation */ + put_reg(cpd, LAN91CXX_RPCR, + LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK | + LAN91CXX_RPCR_ANEG); + cpd->rpc_cur_mode = + LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK | + LAN91CXX_RPCR_ANEG; + + /* wait for auto-negotiation to finish. */ + /* give it ~5 seconds before giving up (no cable?) */ + delay = 50; + while (!(lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_STAT) & 0x20)) { + if (--delay <= 0) { + printf("Timeout autonegotiation\n"); + break; + } + HAL_DELAY_US(100000); + } + + put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu); + + put_reg(cpd, LAN91CXX_INTERRUPT, 0); /* disable interrupts */ + intr = get_reg(cpd, LAN91CXX_INTERRUPT); + put_reg(cpd, LAN91CXX_INTERRUPT, intr & /* ack old interrupts */ + (LAN91CXX_INTERRUPT_TX_INT | + LAN91CXX_INTERRUPT_TX_EMPTY_INT | + LAN91CXX_INTERRUPT_RX_OVRN_INT | LAN91CXX_INTERRUPT_ERCV_INT)); + put_reg(cpd, LAN91CXX_RCR, + LAN91CXX_RCR_STRIP_CRC | LAN91CXX_RCR_RXEN | + LAN91CXX_RCR_ALMUL); + put_reg(cpd, LAN91CXX_TCR, LAN91CXX_TCR_TXENA | LAN91CXX_TCR_PAD_EN); + put_reg(cpd, LAN91CXX_CONTROL, LAN91CXX_CONTROL_AUTO_RELEASE); /* */ + put_reg(cpd, LAN91CXX_INTERRUPT, /* enable interrupts */ + LAN91CXX_INTERRUPT_RCV_INT_M); + + if ((0 +#ifdef ETH_DRV_FLAGS_PROMISC_MODE + != (flags & ETH_DRV_FLAGS_PROMISC_MODE) +#endif + ) || (ifp->if_flags & IFF_PROMISC) + ) { + /* Then we select promiscuous mode. */ + unsigned short rcr; + rcr = get_reg(cpd, LAN91CXX_RCR); + rcr |= LAN91CXX_RCR_PRMS; + put_reg(cpd, LAN91CXX_RCR, rcr); + } +} + +/* \ ------------- Probe ------------- \ */ + +static const char *chip_ids[15] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + /* 6 */ "SMC91C96", + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + /* 9 */ "SMC91C11xFD", + NULL, NULL, + NULL, NULL, NULL +}; + +static int smc_probe(struct lan91cxx_priv_data *cpd) +{ + unsigned short bank; + unsigned short revision_register; + + DEBUG_FUNCTION(); + + /* First, see if the high byte is 0x33 */ + HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank); + bank = CYG_LE16_TO_CPU(bank); + if ((bank & 0xFF00) != 0x3300) { + db_printf("<1>Smc probe bank check 1 failed.\n"); + return -ENODEV; + } + /* The above MIGHT indicate a device, but I need to write to further + test this. */ + HAL_WRITE_UINT16(cpd->base + (LAN91CXX_BS), CYG_CPU_TO_LE16(0 >> 3)); + HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank); + bank = CYG_LE16_TO_CPU(bank); + if ((bank & 0xFF00) != 0x3300) { + db_printf("<1>Smc probe bank check 2 failed.\n"); + return -ENODEV; + } +#if SMC_DEBUG > 3 + { + unsigned short bank16, bank16_0, bank16_1; + HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank16); + bank = CYG_LE16_TO_CPU(bank); + HAL_READ_UINT8(cpd->base + (LAN91CXX_BS), bank16_0); + HAL_READ_UINT8(cpd->base + (LAN91CXX_BS + 1), bank16_1); + + db_printf + ("smc_probe:Bank read as a 16 bit value:0x%04x\n", bank16); + db_printf + ("smc_probe:Bank read as an 8 bit value:0x%02x\n", + bank16_0); + db_printf + ("smc_probe:Bank + 1 read as an 8 bit value:0x%02x\n", + bank16_1); + } +#endif + + /* check if the revision register is something that I recognize. + These might need to be added to later, as future revisions + could be added. */ + revision_register = get_reg(cpd, LAN91CXX_REVISION); + if (!chip_ids[(revision_register >> 4) & 0xF]) { + /* I don't recognize this chip, so... */ + db_printf + ("smc_probe: IO %x: Unrecognized revision register:" + " %x, Contact author. \n", (unsigned int)cpd->base, + revision_register); + + return -ENODEV; + } + db_printf("LAN91CXX(0x%x) - type: %s, rev: %01x\n", + revision_register, + chip_ids[(revision_register >> 4) & 0xF], + revision_register & 0xf); + + /* Set RevA flag for LAN91C111 so we can cope with the odd-bit bug. */ + if (revision_register == 0x3390) { + db_printf("!Revision A\n"); + } + + return 0; +} + +#if 0 +/* \ ------------- PHY read/write ------------- \ */ +/*Sets the PHY to a configuration as determined by the user*/ +static int lan91cxx_phy_fixed(struct lan91cxx_priv_data *cpd) +{ + int my_fixed_caps; + int cfg1; + + DEBUG_FUNCTION(); + db4_printf("lan91cxx_phy_fixed: full duplex: %d, speed: %d\n", + cpd->config.ctl_rfduplx, cpd->config.ctl_rspeed); + + /* Enter Link Disable state */ + cfg1 = lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_CONFIG1); + cfg1 |= PHY_CFG1_LNKDIS; + lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CONFIG1, cfg1); + + /* Set our fixed capabilities, Disable auto-negotiation */ + my_fixed_caps = 0; + + if (cpd->config.ctl_rfduplx) + my_fixed_caps |= LAN91CXX_PHY_CTRL_DPLX; + + if (cpd->config.ctl_rspeed == 100) + my_fixed_caps |= LAN91CXX_PHY_CTRL_SPEED; + + /* Write capabilities to the phy control register */ + lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CTRL, my_fixed_caps); + + /* Re-Configure the Receive/Phy Control register */ + put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode); + + return (1); +} +#endif + +#if 0 +/*Configures the specified PHY using Autonegotiation. */ +static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd) +{ + + unsigned int phyaddr; + unsigned int my_phy_caps; /* My PHY capabilities */ + unsigned int my_ad_caps; /* My Advertised capabilities */ + unsigned int status = 0; + int failed = 0, delay; + + DEBUG_FUNCTION(); + + /* Set the blocking flag */ + cpd->autoneg_active = 1; + + /* Get the detected phy address */ + phyaddr = cpd->phyaddr; + + /* Reset the PHY, setting all other bits to zero */ + lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG, PHY_CNTL_RST); + + /* Wait for the reset to complete, or time out */ + delay = 50; + while (delay--) { + if (!(lan91cxx_read_phy(cpd, 0, PHY_CNTL_REG) + & PHY_CNTL_RST)) { + break; + } + HAL_DELAY_US(100000); + } + + if (delay < 1) { + db_printf("smc91111:!PHY reset timed out\n"); + goto smc_phy_configure_exit; + } + + /* Read PHY Register 18, Status Output */ + cpd->lastPhy18 = lan91cxx_read_phy(cpd, 0, PHY_INT_REG); + + /* Enable PHY Interrupts (for register 18) */ + /* Interrupts listed here are disabled */ + lan91cxx_write_phy(cpd, 0, PHY_MASK_REG, + PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD + | PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB | + PHY_INT_SPDDET | PHY_INT_DPLXDET); + + /* Configure the Receive/Phy Control register */ + put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode); + + /* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */ + my_phy_caps = lan91cxx_read_phy(cpd, phyaddr, PHY_STAT_REG); + my_ad_caps = PHY_AD_CSMA; /* I am CSMA capable */ + + if (my_phy_caps & PHY_STAT_CAP_T4) + my_ad_caps |= PHY_AD_T4; + + if (my_phy_caps & PHY_STAT_CAP_TXF) + my_ad_caps |= PHY_AD_TX_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TXH) + my_ad_caps |= PHY_AD_TX_HDX; + + if (my_phy_caps & PHY_STAT_CAP_TF) + my_ad_caps |= PHY_AD_10_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TH) + my_ad_caps |= PHY_AD_10_HDX; + + /* Disable capabilities not selected by our user */ + if (cpd->config.ctl_rspeed != 100) { + my_ad_caps &= ~(PHY_AD_T4 | PHY_AD_TX_FDX | PHY_AD_TX_HDX); + } + + if (!cpd->config.ctl_rfduplx) { + my_ad_caps &= ~(PHY_AD_TX_FDX | PHY_AD_10_FDX); + } + + /* Update our Auto-Neg Advertisement Register */ + lan91cxx_write_phy(cpd, 0, PHY_AD_REG, my_ad_caps); + + db4_printf("smc91111:phy caps=%x\n", my_phy_caps); + db4_printf("smc91111:phy advertised caps=%x\n", my_ad_caps); + + /* If the user requested no auto neg, then go set his request */ + if (!(cpd->config.ctl_autoneg)) { + lan91cxx_phy_fixed(cpd); + + goto smc_phy_configure_exit; + } + + /* Restart auto-negotiation process in order to advertise my caps */ + lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST); + + /* wait for auto-negotiation to finish. */ + /* give it ~5 seconds before giving up (no cable?) */ + delay = 50; + while (! + ((status = + lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_STAT)) & 0x20)) { + if (--delay <= 0) { + printf("Timeout autonegotiation\n"); + failed = 1; + break; + } + + /* Restart auto-negotiation if remote fault */ + if (status & PHY_STAT_REM_FLT) { + db_printf("smc91111:PHY remote fault detected\n"); + + /* Restart auto-negotiation */ + db_printf("smc91111:PHY restarting auto-negotiation\n"); + lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | + PHY_CNTL_ANEG_RST | + PHY_CNTL_SPEED | PHY_CNTL_DPLX); + } + HAL_DELAY_US(100000); + } + + /* Fail if we detected an auto-negotiate remote fault */ + if (status & PHY_STAT_REM_FLT) { + db_printf("smc91111:PHY remote fault detected\n"); + failed = 1; + } + + /* The smc_phy_interrupt() routine will be called to update lastPhy18 */ + + /* Set our sysctl parameters to match auto-negotiation results */ + if (cpd->lastPhy18 & PHY_INT_SPDDET) { + db_printf("smc91111:PHY 100BaseT\n"); + cpd->rpc_cur_mode |= LAN91CXX_RPCR_SPEED; + } else { + db_printf("smc91111:PHY 10BaseT\n"); + cpd->rpc_cur_mode &= ~LAN91CXX_RPCR_SPEED; + } + + if (cpd->lastPhy18 & PHY_INT_DPLXDET) { + db_printf("smc91111:PHY Full Duplex\n"); + cpd->rpc_cur_mode |= LAN91CXX_RPCR_DPLX; + } else { + db_printf("smc91111:PHY Half Duplex\n"); + cpd->rpc_cur_mode &= ~LAN91CXX_RPCR_DPLX; + } + + /* Re-Configure the Receive/Phy Control register */ + put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode); + + smc_phy_configure_exit: + + /* Exit auto-negotiation */ + cpd->autoneg_active = 0; +} +#endif + +static cyg_uint16 +lan91cxx_read_phy(struct lan91cxx_priv_data *cpd, cyg_uint8 phyaddr, + cyg_uint8 phyreg) +{ + int i, mask, input_idx, clk_idx = 0; + cyg_uint16 mii_reg, value; + cyg_uint8 bits[64]; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + + /* Start code <01> */ + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + + /* Read command <10> */ + bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + + /* Output the PHY address, msb first */ + for (mask = 0x10; mask; mask >>= 1) { + if (phyaddr & mask) + bits[clk_idx++] = + LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + else + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + } + + /* Output the phy register number, msb first */ + for (mask = 0x10; mask; mask >>= 1) { + if (phyreg & mask) + bits[clk_idx++] = + LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + else + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + } + + /* Tristate and turnaround (1 bit times) */ + bits[clk_idx++] = 0; + + /* Input starts at this bit time */ + input_idx = clk_idx; + + /* Will input 16 bits */ + for (i = 0; i < 16; ++i) + bits[clk_idx++] = 0; + + /* Final clock bit */ + bits[clk_idx++] = 0; + + /* Get the current MII register value */ + mii_reg = get_reg(cpd, LAN91CXX_MGMT); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK | + LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO); + HAL_DELAY_US(50); + + /* Clock all 64 cycles */ + for (i = 0; i < sizeof(bits); ++i) { + /* Clock Low - output data */ + put_reg(cpd, LAN91CXX_MGMT, mii_reg | bits[i]); + HAL_DELAY_US(50); + + /* Clock Hi - input data */ + put_reg(cpd, LAN91CXX_MGMT, + mii_reg | bits[i] | LAN91CXX_MGMT_MCLK); + HAL_DELAY_US(50); + + bits[i] |= get_reg(cpd, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI; + } + + /* Return to idle state */ + put_reg(cpd, LAN91CXX_MGMT, mii_reg); + HAL_DELAY_US(50); + + /* Recover input data */ + for (value = 0, i = 0; i < 16; ++i) { + value <<= 1; + if (bits[input_idx++] & LAN91CXX_MGMT_MDI) + value |= 1; + } + + db16_printf("phy_read : %d : %04x\n", phyreg, value); + return value; +} + +static void +lan91cxx_write_phy(struct lan91cxx_priv_data *cpd, cyg_uint8 phyaddr, + cyg_uint8 phyreg, cyg_uint16 value) +{ + int i, mask, clk_idx = 0; + cyg_uint16 mii_reg; + cyg_uint8 bits[65]; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + + /* Start code <01> */ + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + + /* Write command <01> */ + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + + /* Output the PHY address, msb first */ + for (mask = 0x10; mask; mask >>= 1) { + if (phyaddr & mask) + bits[clk_idx++] = + LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + else + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + } + + /* Output the phy register number, msb first */ + for (mask = 0x10; mask; mask >>= 1) { + if (phyreg & mask) + bits[clk_idx++] = + LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + else + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + } + + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; + + /* Write out 16 bits of data, msb first */ + for (mask = 0x8000; mask; mask >>= 1) { + if (value & mask) + bits[clk_idx++] = + LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; + else + bits[clk_idx++] = LAN91CXX_MGMT_MDOE; + } + + /* Final clock bit (tristate) */ + bits[clk_idx++] = 0; + + /* Get the current MII register value */ + mii_reg = get_reg(cpd, LAN91CXX_MGMT); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK | + LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO); + HAL_DELAY_US(50); + + /* Clock all cycles */ + for (i = 0; i < sizeof(bits); ++i) { + /* Clock Low - output data */ + put_reg(cpd, LAN91CXX_MGMT, mii_reg | bits[i]); + HAL_DELAY_US(50); + + /* Clock Hi - input data */ + put_reg(cpd, LAN91CXX_MGMT, + mii_reg | bits[i] | LAN91CXX_MGMT_MCLK); + HAL_DELAY_US(50); + +/* bits[i] |= get_reg(cpd, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI;*/ + } + + /* Return to idle state */ + put_reg(cpd, LAN91CXX_MGMT, mii_reg); + HAL_DELAY_US(50); + + db16_printf("phy_write: %d : %04x\n", phyreg, value); +} + +#endif diff --git a/c/src/libchip/network/smc91111.h b/c/src/libchip/network/smc91111.h new file mode 100644 index 0000000000..0201acd3d6 --- /dev/null +++ b/c/src/libchip/network/smc91111.h @@ -0,0 +1,552 @@ +/* + * $Id$ + */ + +#ifndef _SMC91111_H_ +#define _SMC91111_H_ + +#include <libchip/smc91111exp.h> + + +#define LAN91CXX_TCR 0x00 +#define LAN91CXX_EPH_STATUS 0x01 +#define LAN91CXX_RCR 0x02 +#define LAN91CXX_COUNTER 0x03 +#define LAN91CXX_MIR 0x04 +#define LAN91CXX_MCR 0x05 /* Other than 91C111*/ +#define LAN91CXX_RPCR 0x05 /* 91C111 only*/ +#define LAN91CXX_RESERVED_0 0x06 +#define LAN91CXX_BS 0x07 +#define LAN91CXX_CONFIG 0x08 +#define LAN91CXX_BASE_REG 0x09 +#define LAN91CXX_IA01 0x0a +#define LAN91CXX_IA23 0x0b +#define LAN91CXX_IA45 0x0c +#define LAN91CXX_GENERAL 0x0d /* 91C96 - was "RESERVED_1" for others*/ +#define LAN91CXX_CONTROL 0x0e +#define LAN91CXX_BS2 0x0f +#define LAN91CXX_MMU_COMMAND 0x10 +#define LAN91CXX_PNR 0x11 +#define LAN91CXX_FIFO_PORTS 0x12 +#define LAN91CXX_POINTER 0x13 +#define LAN91CXX_DATA_HIGH 0x14 +#define LAN91CXX_DATA 0x15 +#define LAN91CXX_INTERRUPT 0x16 +#define LAN91CXX_BS3 0x17 +#define LAN91CXX_MT01 0x18 +#define LAN91CXX_MT23 0x19 +#define LAN91CXX_MT45 0x1a +#define LAN91CXX_MT67 0x1b +#define LAN91CXX_MGMT 0x1c +#define LAN91CXX_REVISION 0x1d +#define LAN91CXX_ERCV 0x1e +#define LAN91CXX_BS4 0x1f + +#define LAN91CXX_RCR_SOFT_RST 0x8000 /* soft reset*/ +#define LAN91CXX_RCR_FILT_CAR 0x4000 /* filter carrier*/ +#define LAN91CXX_RCR_ABORT_ENB 0x2000 /* abort on collision*/ +#define LAN91CXX_RCR_STRIP_CRC 0x0200 /* strip CRC*/ +#define LAN91CXX_RCR_RXEN 0x0100 /* enable RX*/ +#define LAN91CXX_RCR_ALMUL 0x0004 /* receive all muticasts*/ +#define LAN91CXX_RCR_PRMS 0x0002 /* promiscuous*/ +#define LAN91CXX_RCR_RX_ABORT 0x0001 /* set when abort due to long frame*/ + +#define LAN91CXX_TCR_SWFDUP 0x8000 /* Switched Full Duplex mode*/ +#define LAN91CXX_TCR_ETEN_TYPE 0x4000 /* ETEN type (91C96) 0 <=> like a 91C94*/ +#define LAN91CXX_TCR_EPH_LOOP 0x2000 /* loopback mode*/ +#define LAN91CXX_TCR_STP_SQET 0x1000 /* Stop transmission on SQET error*/ +#define LAN91CXX_TCR_FDUPLX 0x0800 /* full duplex*/ +#define LAN91CXX_TCR_MON_CSN 0x0400 /* monitor carrier during tx (91C96)*/ +#define LAN91CXX_TCR_NOCRC 0x0100 /* does not append CRC to frames*/ +#define LAN91CXX_TCR_PAD_EN 0x0080 /* pads frames with 00 to min length*/ +#define LAN91CXX_TCR_FORCOL 0x0004 /* force collision*/ +#define LAN91CXX_TCR_LLOOP 0x0002 /* local loopback (91C96)*/ +#define LAN91CXX_TCR_TXENA 0x0001 /* enable*/ + +#define LAN91CXX_POINTER_RCV 0x8000 +#define LAN91CXX_POINTER_AUTO_INCR 0x4000 +#define LAN91CXX_POINTER_READ 0x2000 +#define LAN91CXX_POINTER_ETEN 0x1000 +#define LAN91CXX_POINTER_NOT_EMPTY 0x0800 + + +#define LAN91CXX_INTERRUPT_TX_IDLE_M 0x8000 /* (91C96)*/ +#define LAN91CXX_INTERRUPT_ERCV_INT_M 0x4000 +#define LAN91CXX_INTERRUPT_EPH_INT_M 0x2000 +#define LAN91CXX_INTERRUPT_RX_OVRN_INT_M 0x1000 +#define LAN91CXX_INTERRUPT_ALLOC_INT_M 0x0800 +#define LAN91CXX_INTERRUPT_TX_EMPTY_INT_M 0x0400 +#define LAN91CXX_INTERRUPT_TX_INT_M 0x0200 +#define LAN91CXX_INTERRUPT_RCV_INT_M 0x0100 +#define LAN91CXX_INTERRUPT_TX_IDLE 0x0080 /* (91C96)*/ +#define LAN91CXX_INTERRUPT_ERCV_INT 0x0040 /* also ack*/ +#define LAN91CXX_INTERRUPT_EPH_INT 0x0020 +#define LAN91CXX_INTERRUPT_RX_OVRN_INT 0x0010 /* also ack*/ +#define LAN91CXX_INTERRUPT_ALLOC_INT 0x0008 +#define LAN91CXX_INTERRUPT_TX_EMPTY_INT 0x0004 /* also ack*/ +#define LAN91CXX_INTERRUPT_TX_INT 0x0002 /* also ack*/ +#define LAN91CXX_INTERRUPT_RCV_INT 0x0001 + +#define LAN91CXX_INTERRUPT_TX_SET 0x0006 /* TX_EMPTY + TX*/ +#define LAN91CXX_INTERRUPT_TX_SET_ACK 0x0004 /* TX_EMPTY and not plain TX*/ +#define LAN91CXX_INTERRUPT_TX_FIFO_ACK 0x0002 /* TX alone*/ +#define LAN91CXX_INTERRUPT_TX_SET_M 0x0600 /* TX_EMPTY + TX*/ + +#define LAN91CXX_CONTROL_RCV_BAD 0x4000 +#define LAN91CXX_CONTROL_AUTO_RELEASE 0x0800 +#define LAN91CXX_CONTROL_LE_ENABLE 0x0080 +#define LAN91CXX_CONTROL_CR_ENABLE 0x0040 +#define LAN91CXX_CONTROL_TE_ENABLE 0x0020 + +/* These are for setting the MAC address in the 91C96 serial EEPROM*/ +#define LAN91CXX_CONTROL_EEPROM_SELECT 0x0004 +#define LAN91CXX_CONTROL_RELOAD 0x0002 +#define LAN91CXX_CONTROL_STORE 0x0001 +#define LAN91CXX_CONTROL_EEPROM_BUSY 0x0003 +#define LAN91CXX_ESA_EEPROM_OFFSET 0x0020 + +#define LAN91CXX_STATUS_TX_UNRN 0x8000 +#define LAN91CXX_STATUS_LINK_OK 0x4000 +#define LAN91CXX_STATUS_CTR_ROL 0x1000 +#define LAN91CXX_STATUS_EXC_DEF 0x0800 +#define LAN91CXX_STATUS_LOST_CARR 0x0400 +#define LAN91CXX_STATUS_LATCOL 0x0200 +#define LAN91CXX_STATUS_WAKEUP 0x0100 +#define LAN91CXX_STATUS_TX_DEFR 0x0080 +#define LAN91CXX_STATUS_LTX_BRD 0x0040 +#define LAN91CXX_STATUS_SQET 0x0020 +#define LAN91CXX_STATUS_16COL 0x0010 +#define LAN91CXX_STATUS_LTX_MULT 0x0008 +#define LAN91CXX_STATUS_MUL_COL 0x0004 +#define LAN91CXX_STATUS_SNGL_COL 0x0002 +#define LAN91CXX_STATUS_TX_SUC 0x0001 + +#define LAN91CXX_MMU_COMMAND_BUSY 0x0001 + +#define LAN91CXX_MMU_noop 0x0000 +#define LAN91CXX_MMU_alloc_for_tx 0x0020 +#define LAN91CXX_MMU_reset_mmu 0x0040 +#define LAN91CXX_MMU_rem_rx_frame 0x0060 +#define LAN91CXX_MMU_rem_tx_frame 0x0070 /* (91C96) only when TX stopped*/ +#define LAN91CXX_MMU_remrel_rx_frame 0x0080 +#define LAN91CXX_MMU_rel_packet 0x00a0 +#define LAN91CXX_MMU_enq_packet 0x00c0 +#define LAN91CXX_MMU_reset_tx_fifo 0x00e0 + +#define LAN91CXX_CONTROLBYTE_CRC 0x1000 +#define LAN91CXX_CONTROLBYTE_ODD 0x2000 +#define LAN91CXX_CONTROLBYTE_RX 0x4000 + +#define LAN91CXX_RX_STATUS_ALIGNERR 0x8000 +#define LAN91CXX_RX_STATUS_BCAST 0x4000 +#define LAN91CXX_RX_STATUS_BADCRC 0x2000 +#define LAN91CXX_RX_STATUS_ODDFRM 0x1000 +#define LAN91CXX_RX_STATUS_TOOLONG 0x0800 +#define LAN91CXX_RX_STATUS_TOOSHORT 0x0400 +#define LAN91CXX_RX_STATUS_HASHVALMASK 0x007e /* MASK*/ +#define LAN91CXX_RX_STATUS_MCAST 0x0001 +#define LAN91CXX_RX_STATUS_BAD \ + (LAN91CXX_RX_STATUS_ALIGNERR | \ + LAN91CXX_RX_STATUS_BADCRC | \ + LAN91CXX_RX_STATUS_TOOLONG | \ + LAN91CXX_RX_STATUS_TOOSHORT) + +#define LAN91CXX_RX_STATUS_IS_ODD(__cpd,__stat) ((__stat) & LAN91CXX_RX_STATUS_ODDFRM) +#define LAN91CXX_CONTROLBYTE_IS_ODD(__cpd,__val) ((__val) & LAN91CXX_CONTROLBYTE_ODD) + +/* Attribute memory registers in PCMCIA mode*/ +#define LAN91CXX_ECOR 0x8000 +#define LAN91CXX_ECOR_RESET (1<<7) +#define LAN91CXX_ECOR_LEVIRQ (1<<6) +#define LAN91CXX_ECOR_ATTWR (1<<2) +#define LAN91CXX_ECOR_ENABLE (1<<0) + +#define LAN91CXX_ECSR 0x8002 +#define LAN91CXX_ECSR_IOIS8 (1<<5) +#define LAN91CXX_ECSR_PWRDWN (1<<2) +#define LAN91CXX_ECSR_INTR (1<<1) + +/* These are for manipulating the MII interface*/ +#define LAN91CXX_MGMT_MDO 0x0001 +#define LAN91CXX_MGMT_MDI 0x0002 +#define LAN91CXX_MGMT_MCLK 0x0004 +#define LAN91CXX_MGMT_MDOE 0x0008 + +/* Internal PHY registers (91c111)*/ +#define LAN91CXX_PHY_CTRL 0 +#define LAN91CXX_PHY_STAT 1 +#define LAN91CXX_PHY_ID1 2 +#define LAN91CXX_PHY_ID2 3 +#define LAN91CXX_PHY_AUTO_AD 4 +#define LAN91CXX_PHY_AUTO_CAP 5 +#define LAN91CXX_PHY_CONFIG1 16 +#define LAN91CXX_PHY_CONFIG2 17 +#define LAN91CXX_PHY_STATUS_OUT 18 +#define LAN91CXX_PHY_MASK 19 + +/* PHY control bits*/ +#define LAN91CXX_PHY_CTRL_COLTST (1 << 7) +#define LAN91CXX_PHY_CTRL_DPLX (1 << 8) +#define LAN91CXX_PHY_CTRL_ANEG_RST (1 << 9) +#define LAN91CXX_PHY_CTRL_MII_DIS (1 << 10) +#define LAN91CXX_PHY_CTRL_PDN (1 << 11) +#define LAN91CXX_PHY_CTRL_ANEG_EN (1 << 12) +#define LAN91CXX_PHY_CTRL_SPEED (1 << 13) +#define LAN91CXX_PHY_CTRL_LPBK (1 << 14) +#define LAN91CXX_PHY_CTRL_RST (1 << 15) + +// PHY Configuration Register 1 +#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled +#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled +#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down +#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler +#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable +#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled +#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm) +#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db +#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time + +// PHY Configuration Register 2 +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled +#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled +#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt) +#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo + +// PHY Status Output (and Interrupt status) Register +#define PHY_INT_REG 0x12 // Status Output (Interrupt Status) +#define PHY_INT_INT 0x8000 // 1=bits have changed since last read +#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected +#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync +#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx +#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx +#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx +#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected +#define PHY_INT_JAB 0x0100 // 1=Jabber detected +#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode +#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex + +// PHY Interrupt/Status Mask Register +#define PHY_MASK_REG 0x13 // Interrupt Mask + +#define LAN91CXX_RPCR_LEDA_LINK (0 << 2) +#define LAN91CXX_RPCR_LEDA_TXRX (4 << 2) +#define LAN91CXX_RPCR_LEDA_RX (6 << 2) +#define LAN91CXX_RPCR_LEDA_TX (7 << 2) +#define LAN91CXX_RPCR_LEDB_LINK (0 << 5) +#define LAN91CXX_RPCR_LEDB_TXRX (4 << 5) +#define LAN91CXX_RPCR_LEDB_RX (6 << 5) +#define LAN91CXX_RPCR_LEDB_TX (7 << 5) +#define LAN91CXX_RPCR_ANEG (1 << 11) +#define LAN91CXX_RPCR_DPLX (1 << 12) +#define LAN91CXX_RPCR_SPEED (1 << 13) + +// PHY Control Register +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 // 1=PHY Reset +#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback +#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs +#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation +#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode +#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled +#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate +#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex +#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test + +// PHY Status Register +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable +#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable +#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable +#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable +#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable +#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble +#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed +#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected +#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable +#define PHY_STAT_LINK 0x0004 // 1=valid link +#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition +#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented +#define PHY_STAT_RESERVED 0x0780 // Reserved bits mask. + +// PHY Identifier Registers +#define PHY_ID1_REG 0x02 // PHY Identifier 1 +#define PHY_ID2_REG 0x03 // PHY Identifier 2 + +// PHY Auto-Negotiation Advertisement Register +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page +#define PHY_AD_ACK 0x4000 // 1=got link code word from remote +#define PHY_AD_RF 0x2000 // 1=advertise remote fault +#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4 +#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX +#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX +#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX +#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX +#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA + + +static int debugflag_out = 0; + +#define dbc_printf(lvl,format, args...) do { \ + if (!debugflag_out) { \ + if (lvl & DEBUG) { \ + char buf[1024]; \ + sprintf(buf,format,##args); \ + DEBUG_puts(buf); \ + }} \ +} while(0) + +#define db64_printf(format, args...) dbc_printf(64,format,##args); +#define db16_printf(format, args...) dbc_printf(16,format,##args); +#define db9_printf(format, args...) dbc_printf(9,format,##args); +#define db4_printf(format, args...) dbc_printf(4,format,##args); +#define db2_printf(format, args...) dbc_printf(2,format,##args); +#define db1_printf(format, args...) dbc_printf(1,format,##args); +#define db_printf(format, args...) dbc_printf(0xffff,format,##args); + +#if DEBUG & 1 +#define DEBUG_FUNCTION() do { db_printf("# %s\n", __FUNCTION__); } while (0) +#else +#define DEBUG_FUNCTION() do {} while(0) +#endif + + +/* ------------------------------------------------------------------------*/ + +struct smsc_lan91cxx_stats { + unsigned int tx_good ; + unsigned int tx_max_collisions ; + unsigned int tx_late_collisions ; + unsigned int tx_underrun ; + unsigned int tx_carrier_loss ; + unsigned int tx_deferred ; + unsigned int tx_sqetesterrors ; + unsigned int tx_single_collisions; + unsigned int tx_mult_collisions ; + unsigned int tx_total_collisions ; + unsigned int rx_good ; + unsigned int rx_crc_errors ; + unsigned int rx_align_errors ; + unsigned int rx_resource_errors ; + unsigned int rx_overrun_errors ; + unsigned int rx_collisions ; + unsigned int rx_short_frames ; + unsigned int rx_too_long_frames ; + unsigned int rx_symbol_errors ; + unsigned int interrupts ; + unsigned int rx_count ; + unsigned int rx_deliver ; + unsigned int rx_resource ; + unsigned int rx_restart ; + unsigned int tx_count ; + unsigned int tx_complete ; + unsigned int tx_dropped ; +}; +#define INCR_STAT(c,n) (((c)->stats.n)++) + +struct lan91cxx_priv_data; + +typedef struct lan91cxx_priv_data { + + /* frontend */ + struct arpcom arpcom; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + scmv91111_configuration_t config; + + /* backend */ + int rpc_cur_mode; + int autoneg_active; + int phyaddr; + unsigned int lastPhy18; + + int txbusy; /* A packet has been sent*/ + unsigned long txkey; /* Used to ack when packet sent*/ + unsigned short* base; /* Base I/O address of controller*/ + /* (as it comes out of reset)*/ + int interrupt; /* Interrupt vector used by controller*/ + unsigned char enaddr[6]; /* Controller ESA*/ + /* Function to configure the ESA - may fetch ESA from EPROM or */ + /* RedBoot config option. Use of the 'config_enaddr()' function*/ + /* is depreciated in favor of the 'provide_esa()' function and*/ + /* 'hardwired_esa' boolean*/ + void (*config_enaddr)(struct lan91cxx_priv_data* cpd); + int hardwired_esa; + int txpacket; + int rxpacket; + int within_send; + int c111_reva; /* true if this is a revA LAN91C111*/ + struct smsc_lan91cxx_stats stats; +} lan91cxx_priv_data; + +/* ------------------------------------------------------------------------*/ + +#ifdef LAN91CXX_32BIT_RX +typedef unsigned int rxd_t; +#else +typedef unsigned short rxd_t; +#endif + +typedef struct _debug_regs_pair { + int reg; char *name; struct _debug_regs_pair *bits; +} debug_regs_pair; + +static debug_regs_pair debug_regs[] = { + {LAN91CXX_TCR , "LAN91CXX_TCR" ,0}, + {LAN91CXX_EPH_STATUS , "LAN91CXX_EPH_STATUS",0}, + {LAN91CXX_RCR , "LAN91CXX_RCR" ,0}, + {LAN91CXX_COUNTER , "LAN91CXX_COUNTER" ,0}, + {LAN91CXX_MIR , "LAN91CXX_MIR" ,0}, + {LAN91CXX_MCR , "LAN91CXX_MCR" ,0}, + {LAN91CXX_RPCR , "LAN91CXX_RPCR" ,0}, + {LAN91CXX_RESERVED_0 , "LAN91CXX_RESERVED_0",0}, + {LAN91CXX_BS , "LAN91CXX_BS" ,0}, + {LAN91CXX_CONFIG , "LAN91CXX_CONFIG" ,0}, + {LAN91CXX_BASE_REG , "LAN91CXX_BASE_REG" ,0}, + {LAN91CXX_IA01 , "LAN91CXX_IA01" ,0}, + {LAN91CXX_IA23 , "LAN91CXX_IA23" ,0}, + {LAN91CXX_IA45 , "LAN91CXX_IA45" ,0}, + {LAN91CXX_GENERAL , "LAN91CXX_GENERAL" ,0}, + {LAN91CXX_CONTROL , "LAN91CXX_CONTROL" ,0}, + {LAN91CXX_BS2 , "LAN91CXX_BS2" ,0}, + {LAN91CXX_MMU_COMMAND, "LAN91CXX_MMU_COMMAND",0}, + {LAN91CXX_PNR , "LAN91CXX_PNR" ,0}, + {LAN91CXX_FIFO_PORTS , "LAN91CXX_FIFO_PORTS" ,0}, + {LAN91CXX_POINTER , "LAN91CXX_POINTER" ,0}, + {LAN91CXX_DATA_HIGH , "LAN91CXX_DATA_HIGH" ,0}, + {LAN91CXX_DATA , "LAN91CXX_DATA" ,0}, + {LAN91CXX_INTERRUPT , "LAN91CXX_INTERRUPT" ,0}, + {LAN91CXX_BS3 , "LAN91CXX_BS3" ,0}, + {LAN91CXX_MT01 , "LAN91CXX_MT01" ,0}, + {LAN91CXX_MT23 , "LAN91CXX_MT23" ,0}, + {LAN91CXX_MT45 , "LAN91CXX_MT45" ,0}, + {LAN91CXX_MT67 , "LAN91CXX_MT67" ,0}, +/*{LAN91CXX_MGMT , "LAN91CXX_MGMT" ,0}, */ + {LAN91CXX_REVISION , "LAN91CXX_REVISION" ,0}, + {LAN91CXX_ERCV , "LAN91CXX_ERCV" ,0}, + {LAN91CXX_BS4 , "LAN91CXX_BS4" ,0}, + + + + {-1,0} +}; + +static char *dbg_prefix = ""; + +#ifndef SMSC_PLATFORM_DEFINED_GET_REG +static __inline__ unsigned short +get_reg(struct lan91cxx_priv_data *cpd, int regno) +{ + unsigned short val; debug_regs_pair *dbg = debug_regs; int c; + uint32_t Irql; + + /*rtems_interrupt_disable(Irql);*/ + + HAL_WRITE_UINT16(cpd->base+(LAN91CXX_BS), CYG_CPU_TO_LE16(regno>>3)); + HAL_READ_UINT16(cpd->base+((regno&0x7)), val); + val = CYG_LE16_TO_CPU(val); + + /*rtems_interrupt_enable(Irql);*/ + +#ifdef DEBUG & 32 + while ((c = dbg->reg) != -1) { + if (c == regno) { + db_printf("%sread reg [%d:%x] -> 0x%04x (%-20s)\n", dbg_prefix, regno>>3,(regno&0x7)*2, val, dbg->name); + break; + } + dbg++; + } +#else + db2_printf("%sread reg %d:%x -> 0x%04x\n", dbg_prefix, regno>>3,(regno&0x7)*2, val); +#endif + + return val; +} +#endif /* SMSC_PLATFORM_DEFINED_GET_REG*/ + +#ifndef SMSC_PLATFORM_DEFINED_PUT_REG +static __inline__ void +put_reg(struct lan91cxx_priv_data *cpd, int regno, unsigned short val) +{ + debug_regs_pair *dbg = debug_regs; int c; + uint32_t Irql; + +#ifdef DEBUG & 32 + while ((c = dbg->reg) != -1) { + if (c == regno) { + db_printf("%swrite reg [%d:%x] <- 0x%04x (%-20s)\n", dbg_prefix, regno>>3, (regno&0x07)*2, val, dbg->name); + break; + } + dbg++; + } +#else + db2_printf("%swrite reg %d:%x <- 0x%04x\n", dbg_prefix, regno>>3,(regno&0x7)*2, val); +#endif + + /*rtems_interrupt_disable(Irql);*/ + + HAL_WRITE_UINT16(cpd->base+(LAN91CXX_BS), CYG_CPU_TO_LE16(regno>>3)); + HAL_WRITE_UINT16(cpd->base+((regno&0x7)), CYG_CPU_TO_LE16(val)); + + /*rtems_interrupt_enable(Irql);*/ + +} +#endif /* SMSC_PLATFORM_DEFINED_PUT_REG*/ + +#ifndef SMSC_PLATFORM_DEFINED_PUT_DATA +/* ------------------------------------------------------------------------*/ +/* Assumes bank2 has been selected*/ +static __inline__ void +put_data(struct lan91cxx_priv_data *cpd, unsigned short val) +{ + db2_printf("%s[wdata] <- 0x%04x\n", dbg_prefix, val); + + HAL_WRITE_UINT16(cpd->base+((LAN91CXX_DATA & 0x7)), val); + +} +#endif /* SMSC_PLATFORM_DEFINED_PUT_DATA*/ + +#ifndef SMSC_PLATFORM_DEFINED_GET_DATA +/* Assumes bank2 has been selected*/ +static __inline__ rxd_t +get_data(struct lan91cxx_priv_data *cpd) +{ + rxd_t val; + +#ifdef LAN91CXX_32BIT_RX + HAL_READ_UINT32(cpd->base+((LAN91CXX_DATA_HIGH & 0x7)), val); +#else + HAL_READ_UINT16(cpd->base+((LAN91CXX_DATA & 0x7)), val); +#endif + + db2_printf("%s[rdata] -> 0x%08x\n", dbg_prefix, val); + return val; +} +#endif /* SMSC_PLATFORM_DEFINED_GET_DATA*/ + +/* ------------------------------------------------------------------------*/ +/* Read the bank register (this one is bank-independent)*/ +#ifndef SMSC_PLATFORM_DEFINED_GET_BANKSEL +static __inline__ unsigned short +get_banksel(struct lan91cxx_priv_data *cpd) +{ + unsigned short val; + + HAL_READ_UINT16(cpd->base+(LAN91CXX_BS), val); + val = CYG_LE16_TO_CPU(val); + db2_printf("read bank sel val 0x%04x\n", val); + return val; +} +#endif + + + + + +#endif /* _SMC_91111_H_ */ + + diff --git a/c/src/libchip/network/smc91111config.h b/c/src/libchip/network/smc91111config.h new file mode 100644 index 0000000000..5c293e37fb --- /dev/null +++ b/c/src/libchip/network/smc91111config.h @@ -0,0 +1,132 @@ +/* + * $Id$ + */ + +#ifndef _SMC91111_CONFIG_H_ +#define _SMC91111_CONFIG_H_ + +/* + * RTEMS event used by interrupt handler to signal driver tasks. + * This must not be any of the events used by the network task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + + /* event to send when tx buffers become available */ +#define SMC91111_TX_WAIT_EVENT RTEMS_EVENT_3 + + +/* Number of OCs supported by this driver*/ +#define NOCDRIVER 1 + +/* Receive buffer size -- Allow for a full ethernet packet including CRC */ +#define RBUF_SIZE 1536 + +#define ET_MINLEN 64 /* minimum message length */ + +#if (MCLBYTES < RBUF_SIZE) +# error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +/* ----------------- cygdriver params ----------------- */ + +#define LAN91CXX_32BIT_RX +#define LAN91CXX_IS_LAN91C111 + +/* ----------------- compat layer ----------------- */ + +/* ------------------------------------------------------------------------- + Define basic types for using integers in memory and structures; + depends on compiler defaults and CPU type. */ + +typedef unsigned char cyg_uint8 ; +typedef signed char cyg_int8 ; +typedef unsigned short cyg_uint16 ; +typedef signed short cyg_int16 ; +typedef unsigned int cyg_uint32 ; +typedef signed int cyg_int32 ; +typedef unsigned int cyg_bool ; + +typedef cyg_uint32 CYG_WORD; +typedef cyg_uint8 CYG_BYTE; +typedef cyg_uint16 CYG_WORD16; +typedef cyg_uint32 CYG_WORD32; + +#ifndef CYG_SWAP16 +# define CYG_SWAP16(_x_) \ + ({ cyg_uint16 _x = (_x_); ((_x << 8) | (_x >> 8)); }) +#endif + +#ifndef CYG_SWAP32 +# define CYG_SWAP32(_x_) \ + ({ cyg_uint32 _x = (_x_); \ + ((_x << 24) | \ + ((0x0000FF00UL & _x) << 8) | \ + ((0x00FF0000UL & _x) >> 8) | \ + (_x >> 24)); }) +#endif + +# define CYG_CPU_TO_BE16(_x_) (_x_) +# define CYG_CPU_TO_BE32(_x_) (_x_) +# define CYG_BE16_TO_CPU(_x_) (_x_) +# define CYG_BE32_TO_CPU(_x_) (_x_) + +# define CYG_CPU_TO_LE16(_x_) CYG_SWAP16((_x_)) +# define CYG_CPU_TO_LE32(_x_) CYG_SWAP32((_x_)) +# define CYG_LE16_TO_CPU(_x_) CYG_SWAP16((_x_)) +# define CYG_LE32_TO_CPU(_x_) CYG_SWAP32((_x_)) + +#define CYG_MACRO_START do { +#define CYG_MACRO_END } while (0) +#define HAL_IO_BARRIER() \ + asm volatile ( "" : : : "memory" ) + +#define HAL_READ_UINT8( _register_, _value_ ) \ + CYG_MACRO_START \ + ((_value_) = *((volatile CYG_BYTE *)(_register_))); \ + HAL_IO_BARRIER (); \ + CYG_MACRO_END + +#define HAL_WRITE_UINT8( _register_, _value_ ) \ + CYG_MACRO_START \ + (*((volatile CYG_BYTE *)(_register_)) = (_value_)); \ + HAL_IO_BARRIER (); \ + CYG_MACRO_END + +#define HAL_READ_UINT16( _register_, _value_ ) \ + CYG_MACRO_START \ + ((_value_) = *((volatile CYG_WORD16 *)(_register_))); \ + HAL_IO_BARRIER (); \ + CYG_MACRO_END + +#define HAL_WRITE_UINT16( _register_, _value_ ) \ + CYG_MACRO_START \ + (*((volatile CYG_WORD16 *)(_register_)) = (_value_)); \ + HAL_IO_BARRIER (); \ + CYG_MACRO_END + +#define HAL_READ_UINT32( _register_, _value_ ) \ + CYG_MACRO_START \ + ((_value_) = *((volatile CYG_WORD32 *)(_register_))); \ + HAL_IO_BARRIER (); \ + CYG_MACRO_END + +#define HAL_READ_UINT16( _register_, _value_ ) \ + CYG_MACRO_START \ + ((_value_) = *((volatile CYG_WORD16 *)(_register_))); \ + HAL_IO_BARRIER (); \ + CYG_MACRO_END + +#define CYG_ASSERT(c,p) do { if (!(c)) { while(1) { printf(p);} }; } while(0) + +#define HAL_DELAY_US(p) rtems_task_wake_after (TOD_MICROSECONDS_TO_TICKS (p)) + + +#endif /* _SMC_91111_CONFIG_H_ */ + + diff --git a/c/src/libchip/network/smc91111exp.h b/c/src/libchip/network/smc91111exp.h new file mode 100644 index 0000000000..72e41fa0d9 --- /dev/null +++ b/c/src/libchip/network/smc91111exp.h @@ -0,0 +1,19 @@ +/* + * $Id$ + */ + +#ifndef _SMC91111_EXP_H_ +#define _SMC91111_EXP_H_ + +typedef struct scmv91111_configuration { + void *baseaddr; + unsigned int vector; + unsigned int pio; + unsigned int ctl_rspeed; + unsigned int ctl_rfduplx; + unsigned int ctl_autoneg; +} scmv91111_configuration_t; + +#endif /* _SMC_91111_EXP_H_ */ + + |