From b759b04efa5c93f080ab2c07e217a15256985537 Mon Sep 17 00:00:00 2001 From: Jay Monkman Date: Thu, 15 Jul 2004 06:16:16 +0000 Subject: 2004-07-15 Jay Monkman * .cvsignore, ChangeLog, Makefile.am, README, bsp_specs, configure.ac, times, console/uarts.c, include/.cvsignore, include/bsp.h, include/tm27.h, network/.cvsignore, network/network.c, start/.cvsignore, start/start.S, startup/.cvsignore, startup/bspstart.c, startup/exit.c, startup/linkcmds, startup/memmap.c: New files. --- c/src/lib/libbsp/arm/csb337/network/.cvsignore | 14 + c/src/lib/libbsp/arm/csb337/network/network.c | 665 +++++++++++++++++++++++++ 2 files changed, 679 insertions(+) create mode 100644 c/src/lib/libbsp/arm/csb337/network/.cvsignore create mode 100644 c/src/lib/libbsp/arm/csb337/network/network.c (limited to 'c/src/lib/libbsp/arm/csb337/network') diff --git a/c/src/lib/libbsp/arm/csb337/network/.cvsignore b/c/src/lib/libbsp/arm/csb337/network/.cvsignore new file mode 100644 index 0000000000..d29e5050f5 --- /dev/null +++ b/c/src/lib/libbsp/arm/csb337/network/.cvsignore @@ -0,0 +1,14 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.cache +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +missing +mkinstalldirs diff --git a/c/src/lib/libbsp/arm/csb337/network/network.c b/c/src/lib/libbsp/arm/csb337/network/network.c new file mode 100644 index 0000000000..69a16d2e61 --- /dev/null +++ b/c/src/lib/libbsp/arm/csb337/network/network.c @@ -0,0 +1,665 @@ +/* + * AT91RM9200 ethernet driver + * + * Copyright (c) 2003 by Cogent Computer Systems + * Written by Mike Kelly + * and Jay Monkman + * + * $Id$ + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + + +/* interrupt stuff */ +#define EMAC_INT_PRIORITY 0 /* lowest priority */ + +/* RTEMS event used by interrupt handler to start receive daemon. */ +#define START_RECEIVE_EVENT RTEMS_EVENT_1 + +/* RTEMS event used to start transmit daemon. */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +rtems_isr at91rm9200_emac_isr(rtems_vector_number vector); +static void at91rm9200_emac_isr_on(const rtems_irq_connect_data *unused); +static void at91rm9200_emac_isr_off(const rtems_irq_connect_data *unused); +static int at91rm9200_emac_isr_is_on(const rtems_irq_connect_data *irq); + +/* Replace the first value with the clock's interrupt name. */ +rtems_irq_connect_data at91rm9200_emac_isr_data = { + AT91RM9200_INT_EMAC, + (rtems_irq_hdl)at91rm9200_emac_isr, + at91rm9200_emac_isr_on, + at91rm9200_emac_isr_off, + at91rm9200_emac_isr_is_on, + 3, /* unused for ARM */ + 0 }; /* unused for ARM */ + + +/* use the values defined in linkcmds for our use of SRAM */ +extern void * at91rm9200_emac_rxbuf_hdrs; +extern void * at91rm9200_emac_txbuf; +extern void * at91rm9200_emac_rxbufs; + +/* Set up EMAC hardware */ +/* Number of Receive and Transmit Buffers and Buffer Descriptors */ +#define NUM_RXBDS 8 +#define NUM_TXBDS 1 +#define RX_BUFFER_SIZE 0x600 + +/* use internal SRAM for buffers and descriptors + * also insure that the receive descriptors + * start on a 64byte boundary + * Receive Buffer Descriptor Header + */ + +typedef struct +{ + unsigned long address; + unsigned long status; +} RXBUF_HDR; + +RXBUF_HDR *rxbuf_hdrs; +unsigned char *txbuf; +unsigned char *rxbuf; + +int delay_cnt; + +/* + * Hardware-specific storage + */ +typedef struct +{ + /* + * Connection to networking code + * This entry *must* be the first in the sonic_softc structure. + */ + struct arpcom arpcom; + + /* + * Interrupt vector + */ + rtems_vector_number vector; + + /* + * Indicates configuration + */ + int acceptBroadcast; + + /* + * Task waiting for interrupts + */ + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * current receive header + */ + int rx_buf_idx; + + + + /* + * Statistics + */ + unsigned long Interrupts; + unsigned long rxInterrupts; + unsigned long rxMissed; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxBadCRC; + unsigned long rxCollision; + + unsigned long txInterrupts; + unsigned long txSingleCollision; + unsigned long txMultipleCollision; + unsigned long txCollision; + unsigned long txDeferred; + unsigned long txUnderrun; + unsigned long txLateCollision; + unsigned long txExcessiveCollision; + unsigned long txExcessiveDeferral; + unsigned long txLostCarrier; + unsigned long txRawWait; +} at91rm9200_emac_softc_t; + +static at91rm9200_emac_softc_t softc; + + +/* The AT91RM9200 ethernet fifos are very undersized. Therefore + * we use the internal SRAM to hold 4 receive packets and one + * transmit packet. Note that the AT91RM9200 can only queue + * one transmit packet at a time. + */ + +/* function prototypes */ +int rtems_at91rm9200_emac_attach (struct rtems_bsdnet_ifconfig *config, + void *chip); +void at91rm9200_emac_init(void *arg); +void at91rm9200_emac_init_hw(at91rm9200_emac_softc_t *sc); +void at91rm9200_emac_start(struct ifnet *ifp); +void at91rm9200_emac_stop (at91rm9200_emac_softc_t *sc); +void at91rm9200_emac_txDaemon (void *arg); +void at91rm9200_emac_sendpacket (struct ifnet *ifp, struct mbuf *m); +void at91rm9200_emac_rxDaemon(void *arg); +void at91rm9200_emac_stats (at91rm9200_emac_softc_t *sc); +static int at91rm9200_emac_ioctl (struct ifnet *ifp, + int command, + caddr_t data); + + +int rtems_at91rm9200_emac_attach ( + struct rtems_bsdnet_ifconfig *config, + void *chip /* only one ethernet, so no chip number */ + ) +{ + struct ifnet *ifp; + int mtu; + int unitnumber; + char *unitname; + + /* an array of receive buffer descriptors */ + rxbuf_hdrs = (RXBUF_HDR *)&at91rm9200_emac_rxbuf_hdrs; + + /* one transmit buffer, 1536 bytes maximum */ + txbuf = (char *)&at91rm9200_emac_txbuf; + + /* receive buffers starting address */ + rxbuf = (char *)&at91rm9200_emac_rxbufs; + /* + * Parse driver name + */ + if ((unitnumber = rtems_bsdnet_parse_driver_name (config, &unitname)) < 0) + return 0; + + /* + * Is driver free? + */ + if (unitnumber != 0) { + printf ("Bad AT91RM9200 EMAC unit number.\n"); + return 0; + } + ifp = &softc.arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf ("Driver already in use.\n"); + return 0; + } + + /* + * zero out the control structure + */ + + memset( &softc, 0, sizeof(softc) ); + + + /* get the MAC address from the chip */ + softc.arpcom.ac_enaddr[0] = (EMAC_REG(EMAC_SA1L) >> 0) & 0xff; + softc.arpcom.ac_enaddr[1] = (EMAC_REG(EMAC_SA1L) >> 8) & 0xff; + softc.arpcom.ac_enaddr[2] = (EMAC_REG(EMAC_SA1L) >> 16) & 0xff; + softc.arpcom.ac_enaddr[3] = (EMAC_REG(EMAC_SA1L) >> 24) & 0xff; + softc.arpcom.ac_enaddr[4] = (EMAC_REG(EMAC_SA1H) >> 0) & 0xff; + softc.arpcom.ac_enaddr[5] = (EMAC_REG(EMAC_SA1H) >> 8) & 0xff; + + if (config->mtu) { + mtu = config->mtu; + } else { + mtu = ETHERMTU; + } + + softc.acceptBroadcast = !config->ignore_broadcast; + + /* + * Set up network interface values + */ + ifp->if_softc = &softc; + ifp->if_unit = unitnumber; + ifp->if_name = unitname; + ifp->if_mtu = mtu; + ifp->if_init = at91rm9200_emac_init; + ifp->if_ioctl = at91rm9200_emac_ioctl; + ifp->if_start = at91rm9200_emac_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST; + if (ifp->if_snd.ifq_maxlen == 0) { + ifp->if_snd.ifq_maxlen = ifqmaxlen; + } + + softc.rx_buf_idx = 0; + + /* + * Attach the interface + */ + if_attach (ifp); + ether_ifattach (ifp); + return 1; +} + +void at91rm9200_emac_init(void *arg) +{ + at91rm9200_emac_softc_t *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + /* + *This is for stuff that only gets done once (at91rm9200_emac_init() + * gets called multiple times + */ + if (sc->txDaemonTid == 0) { + /* Set up EMAC hardware */ + at91rm9200_emac_init_hw(sc); + + /* Start driver tasks */ + sc->rxDaemonTid = rtems_bsdnet_newproc("ENrx", + 4096, + at91rm9200_emac_rxDaemon, + sc); + sc->txDaemonTid = rtems_bsdnet_newproc("ENtx", + 4096, + at91rm9200_emac_txDaemon, + sc); + } /* if txDaemonTid */ + + /* set our priority in the AIC */ + AIC_SMR_REG(AIC_SMR_EMAC) = AIC_SMR_PRIOR(EMAC_INT_PRIORITY); + + /* install the interrupt handler */ + BSP_install_rtems_irq_handler(&at91rm9200_emac_isr_data); + + /* EMAC doesn't support promiscuous, so ignore requests */ + if (ifp->if_flags & IFF_PROMISC) { + printf ("Warning - AT91RM9200 Ethernet driver" + " doesn't support Promiscuous Mode!\n"); + } + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* Enable TX/RX and clear the statistics counters */ + EMAC_REG(EMAC_CTL) = (EMAC_CTL_TE | EMAC_CTL_RE | EMAC_CTL_CSR); + + /* clear any pending interrupts */ + EMAC_REG(EMAC_TSR) = 0xffffffff; + EMAC_REG(EMAC_RSR) = 0xffffffff; + +} /* at91rm9200_emac_init() */ + +void at91rm9200_emac_init_hw(at91rm9200_emac_softc_t *sc) +{ + int i; + + /* initialize our receive buffer descriptors */ + for (i = 0; i < NUM_RXBDS-1; i++) { + rxbuf_hdrs[i].address = (unsigned long)(&rxbuf[i * RX_BUFFER_SIZE]); + rxbuf_hdrs[i].status = 0x00000000; + } + + /* last one needs the wrapbit set as well */ + rxbuf_hdrs[i].address = ((unsigned long)(&rxbuf[i * RX_BUFFER_SIZE]) | + RXBUF_ADD_WRAP); + rxbuf_hdrs[i].status = 0x00000000; + + /* point to our receive buffer queue */ + EMAC_REG(EMAC_RBQP) = (unsigned long)rxbuf_hdrs; + + /* clear any left over status bits */ + EMAC_REG(EMAC_RSR) &= ~(EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA); + + /* set the MII clock divder to MCK/64 */ + EMAC_REG(EMAC_CFG) = (EMAC_CFG_CLK_64 | EMAC_CFG_BIG | EMAC_CFG_FD); + + /* enable the MII interface */ + EMAC_REG(EMAC_CTL) = EMAC_CTL_MPE; + + /* Set PHY LED2 to combined Link/Activity and enable pulse stretching */ + EMAC_REG(EMAC_MAN) = (0x01 << 30 | /* Start of Frame Delimiter */ + 0x01 << 28 | /* Operation, 0x01 = Write */ + 0x00 << 23 | /* Phy Number */ + 0x14 << 18 | /* Phy Register */ + 0x02 << 16 | /* must be 0x02 */ + 0x0D0A); /* Write data (0x0000 if read) */ + +} /* at91rm9200_emac_init_hw() */ + +void at91rm9200_emac_start(struct ifnet *ifp) +{ + at91rm9200_emac_softc_t *sc = ifp->if_softc; + + rtems_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +void at91rm9200_emac_stop (at91rm9200_emac_softc_t *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Stop the transmitter and receiver. + */ + EMAC_REG(EMAC_CTL) &= ~(EMAC_CTL_TE | EMAC_CTL_RE); +} + +/* + * Driver transmit daemon + */ +void at91rm9200_emac_txDaemon (void *arg) +{ + at91rm9200_emac_softc_t *sc = (at91rm9200_emac_softc_t *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) + { + /* turn on TX interrupt, then wait for one */ + EMAC_REG(EMAC_IER) = EMAC_INT_TCOM; /* Transmit complete */ + + rtems_bsdnet_event_receive( + START_TRANSMIT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + + /* Send packets till queue is empty */ + for (;;) + { + /* Get the next mbuf chain to transmit. */ + IF_DEQUEUE(&ifp->if_snd, m); + if (!m) + break; + at91rm9200_emac_sendpacket (ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + +/* Send packet */ +void at91rm9200_emac_sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct mbuf *l = NULL; + unsigned int pkt_offset = 0; + delay_cnt = 0; + /* printf("at91rm9200_emac_sendpacket %p\n", m); */ + + /* Wait for EMAC Transmit Queue to become available. */ + while (((EMAC_REG(EMAC_TSR) & EMAC_TSR_COMP) == 0) && + ((EMAC_REG(EMAC_TSR) & EMAC_TSR_TXIDLE) == 0)) + + { + delay_cnt++; +/* sleep(0); make sure we don't hog the cpu */ + continue; + } + + /* copy the mbuf chain into the transmit buffer */ + l = m; + while (l != NULL) { + memcpy(((char *)txbuf + pkt_offset), /* offset into pkt for mbuf */ + (char *)mtod(l, void *), /* cast to void */ + l->m_len); /* length of this mbuf */ + + pkt_offset += l->m_len; /* update offset */ + l = l->m_next; /* get next mbuf, if any */ + } + + /* free the mbuf chain we just copied */ + m_freem(m); + + /* clear any pending status */ + EMAC_REG(EMAC_TSR) = (EMAC_TSR_OVR | EMAC_TSR_COL | EMAC_TSR_RLE + | EMAC_TSR_COMP | EMAC_TSR_UND); + + /* tell the EMAC about our buffer */ + EMAC_REG(EMAC_TAR) = (unsigned long)txbuf; + EMAC_REG(EMAC_TCR) = (unsigned long)pkt_offset; +} /* at91rm9200_emac_sendpacket () */ + + +/* SONIC reader task */ +void at91rm9200_emac_rxDaemon(void *arg) +{ + at91rm9200_emac_softc_t *sc = (at91rm9200_emac_softc_t *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + struct ether_header *eh; + rtems_event_set events; + int pktlen; + + /* Input packet handling loop */ + for (;;) { + /* turn on RX interrupts, then wait for one */ + EMAC_REG(EMAC_IER) = (EMAC_INT_RCOM | /* Receive complete */ + EMAC_INT_RBNA | /* Receive buf not available */ + EMAC_INT_ROVR); /* Receive overrun */ + + rtems_bsdnet_event_receive( + START_RECEIVE_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + + if (EMAC_REG(EMAC_RSR) & EMAC_RSR_BNA) { + printf("1: EMAC_BNA\n"); + } + + if (EMAC_REG(EMAC_RSR) & EMAC_RSR_OVR) { + printf("1: EMAC_OVR\n"); + } + + /* clear the receive status as we do not use it anyway */ + EMAC_REG(EMAC_RSR) = (EMAC_RSR_REC | EMAC_RSR_OVR | EMAC_RSR_BNA); + + /* scan the buffer descriptors looking for any with data in them */ + while (rxbuf_hdrs[sc->rx_buf_idx].address & RXBUF_ADD_OWNED) { + pktlen = rxbuf_hdrs[sc->rx_buf_idx].status & RXBUF_STAT_LEN_MASK; + + /* get an mbuf this packet */ + MGETHDR(m, M_WAIT, MT_DATA); + + /* now get a cluster pointed to by the mbuf */ + /* since an mbuf by itself is too small */ + MCLGET(m, M_WAIT); + + /* set the type of mbuf to ifp (ethernet I/F) */ + m->m_pkthdr.rcvif = ifp; + m->m_nextpkt = 0; + + /* copy the packet into the cluster pointed to by the mbuf */ + memcpy((char *)m->m_ext.ext_buf, + (char *)(rxbuf_hdrs[sc->rx_buf_idx].address & 0xfffffffc), + pktlen); + + /* Release the buffer ASAP back to the EMAC */ + rxbuf_hdrs[sc->rx_buf_idx].address &= ~RXBUF_ADD_OWNED; + + /* set the length of the mbuf */ + m->m_len = pktlen - (sizeof(struct ether_header) + 4); + m->m_pkthdr.len = m->m_len; + + /* strip off the ethernet header from the mbuf */ + /* but save the pointer to it */ + eh = mtod (m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + + /* increment the buffer index */ + sc->rx_buf_idx++; + if (sc->rx_buf_idx >= NUM_RXBDS) { + sc->rx_buf_idx = 0; + } + + /* give all this stuff to the stack */ + ether_input(ifp, eh, m); + + } /* while ADD_OWNED = 0 */ + + if (EMAC_REG(EMAC_RSR) & EMAC_RSR_BNA) { + printf("2:EMAC_BNA\n"); + } + if (EMAC_REG(EMAC_RSR) & EMAC_RSR_OVR) { + printf("2:EMAC_OVR\n"); + } + + + } /* for (;;) */ +} /* at91rm9200_emac_rxDaemon() */ + + +/* Show interface statistics */ +void at91rm9200_emac_stats (at91rm9200_emac_softc_t *sc) +{ + printf (" Total Interrupts:%-8lu", sc->Interrupts); + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Giant:%-8lu", sc->rxGiant); + printf (" Non-octet:%-8lu\n", sc->rxNonOctet); + printf (" Bad CRC:%-8lu", sc->rxBadCRC); + printf (" Collision:%-8lu", sc->rxCollision); + printf (" Missed:%-8lu\n", sc->rxMissed); + + printf ( " Tx Interrupts:%-8lu", sc->txInterrupts); + printf ( " Deferred:%-8lu", sc->txDeferred); + printf (" Lost Carrier:%-8lu\n", sc->txLostCarrier); + printf ( "Single Collisions:%-8lu", sc->txSingleCollision); + printf ( "Multiple Collisions:%-8lu", sc->txMultipleCollision); + printf ("Excessive Collisions:%-8lu\n", sc->txExcessiveCollision); + printf ( " Total Collisions:%-8lu", sc->txCollision); + printf ( " Late Collision:%-8lu", sc->txLateCollision); + printf (" Underrun:%-8lu\n", sc->txUnderrun); + printf ( " Raw output wait:%-8lu\n", sc->txRawWait); +} + + +/* Enables at91rm9200_emac interrupts. */ +static void at91rm9200_emac_isr_on(const rtems_irq_connect_data *unused) +{ + /* Enable various TX/RX interrupts */ + EMAC_REG(EMAC_IER) = (EMAC_INT_RCOM | /* Receive complete */ + EMAC_INT_RBNA | /* Receive buffer not available */ + EMAC_INT_TCOM | /* Transmit complete */ + EMAC_INT_ROVR | /* Receive overrun */ + EMAC_INT_ABT); /* Abort on DMA transfer */ + + return; +} + +/* Disables at91rm9200_emac interrupts */ +static void at91rm9200_emac_isr_off(const rtems_irq_connect_data *unused) +{ + /* disable all various TX/RX interrupts */ + EMAC_REG(EMAC_IDR) = 0xffffffff; + return; +} + +/* Tests to see if at91rm9200_emac interrupts are enabled, and + * returns non-0 if so. + * If interrupt is not enabled, returns 0. + */ +static int at91rm9200_emac_isr_is_on(const rtems_irq_connect_data *irq) +{ + return EMAC_REG(EMAC_IMR); /* any interrupts enabled? */ +} + +/* Driver ioctl handler */ +static int +at91rm9200_emac_ioctl (struct ifnet *ifp, int command, caddr_t data) +{ + at91rm9200_emac_softc_t *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + ether_ioctl (ifp, command, data); + break; + + case SIOCSIFFLAGS: + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) + { + case IFF_RUNNING: + at91rm9200_emac_stop (sc); + break; + + case IFF_UP: + at91rm9200_emac_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + at91rm9200_emac_stop (sc); + at91rm9200_emac_init (sc); + break; + + default: + break; + } /* switch (if_flags) */ + break; + + case SIO_RTEMS_SHOW_STATS: + at91rm9200_emac_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } /* switch (command) */ + return error; +} + +/* interrupt handler */ +rtems_isr at91rm9200_emac_isr (rtems_vector_number v) +{ + unsigned long status32; + + /* get the ISR status and determine RX or TX */ + status32 = EMAC_REG(EMAC_ISR); + + if (status32 & EMAC_INT_ABT) { + EMAC_REG(EMAC_IDR) = EMAC_INT_ABT; /* disable it */ + rtems_panic("AT91RM9200 Ethernet MAC has received an Abort.\n"); + } + + if (status32 & (EMAC_INT_RCOM | /* Receive complete */ + EMAC_INT_RBNA | /* Receive buffer not available */ + EMAC_INT_ROVR)) { /* Receive overrun */ + + /* disable the RX interrupts */ + EMAC_REG(EMAC_IDR) = (EMAC_INT_RCOM | /* Receive complete */ + EMAC_INT_RBNA | /* Receive buf not available */ + EMAC_INT_ROVR); /* Receive overrun */ + + rtems_event_send (softc.rxDaemonTid, START_RECEIVE_EVENT); + } + + if (status32 & EMAC_INT_TCOM) { /* Transmit buffer register empty */ + + /* disable the TX interrupts */ + EMAC_REG(EMAC_IDR) = EMAC_INT_TCOM; + + rtems_event_send (softc.txDaemonTid, START_TRANSMIT_EVENT); + } +} + -- cgit v1.2.3