summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1998-05-30 10:09:14 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1998-05-30 10:09:14 +0000
commitc932d85019761fbafef02fc72119e4bcd4e50978 (patch)
tree20d32f1b768006bfee2385a9cf03a4411373e502 /c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c
parentchanged version to 980527 (diff)
downloadrtems-c932d85019761fbafef02fc72119e4bcd4e50978.tar.bz2
New files -- from rtems-LM-980406 which was based on an RTEMS from 12/97.
This was called the dmv170 BSP in that source tree but since the DMV171 is now obsolete, we have transitioned to the DMV177 and have no intention of checking compatibility with any other models.
Diffstat (limited to 'c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c')
-rw-r--r--c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c1176
1 files changed, 1176 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c b/c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c
new file mode 100644
index 0000000000..cd748d59b7
--- /dev/null
+++ b/c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c
@@ -0,0 +1,1176 @@
+/*
+ *******************************************************************
+ *******************************************************************
+ ** **
+ ** RTEMS/KA9Q DRIVER FOR NATIONAL DP83932 `SONIC' **
+ ** SYSTEMS-ORIENTED NETWORK INTERFACE CONTROLLER **
+ ** **
+ *******************************************************************
+ *******************************************************************
+ */
+
+/*
+ * $Revision$ $Date$ $Author$
+ * $State$
+ */
+
+/*
+ * References:
+ * 1) DP83932C-20/25/33 MHz SONIC(TM) Systems-Oriented Network Interface
+ * Controller data sheet. TL/F/10492, RRD-B30M105, National Semiconductor,
+ * 1995.
+ *
+ * 2) Software Driver Programmer's Guide for the DP83932 SONIC(TM),
+ * Application Note 746, Wesley Lee and Mike Lui, TL/F/11140,
+ * RRD-B30M75, National Semiconductor, March, 1991.
+ *
+ * 3) SVME/DMV-171 Single Board Computer Documentation Package, #805905,
+ * DY 4 Systems Inc., Kanata, Ontario, September, 1996.
+ */
+#include "sonic.h"
+
+#include <rtems/error.h>
+#include <ka9q/rtems_ka9q.h>
+#include <ka9q/global.h>
+#include <ka9q/domain.h>
+#include <ka9q/enet.h>
+#include <ka9q/iface.h>
+#include <ka9q/netuser.h>
+#include <ka9q/trace.h>
+#include <ka9q/commands.h>
+
+/*
+ * Number of devices supported by this driver
+ */
+#ifndef NSONIC
+# define NSONIC 1
+#endif
+
+/*
+ * Default location of device registers
+ */
+#ifndef SONIC_BASE_ADDRESS
+# define SONIC_BASE_ADDRESS 0xF3000000
+# warning "Using default SONIC_BASE_ADDRESS."
+#endif
+
+/*
+ * Default interrupt vector
+ */
+#ifndef SONIC_VECTOR
+# define SONIC_VECTOR 1
+# warning "Using default SONIC_VECTOR."
+#endif
+
+/*
+ * Default device configuration register values
+ * Conservative, generic values.
+ * DCR:
+ * No extended bus mode
+ * Unlatched bus retry
+ * Programmable outputs unused
+ * Asynchronous bus mode
+ * User definable pins unused
+ * No wait states (access time controlled by DTACK*)
+ * 32-bit DMA
+ * Empty/Fill DMA mode
+ * Maximum Transmit/Receive FIFO
+ * DC2:
+ * Extended programmable outputs unused
+ * Normal HOLD request
+ * Packet compress output unused
+ * No reject on CAM match
+ */
+#ifndef SONIC_DCR
+# define SONIC_DCR (DCR_DW | DCR_TFT1 | DCR_TFT0)
+#endif
+#ifndef SONIC_DC2
+# define SONIC_DC2 (0)
+#endif
+
+/*
+ * Default sizes of transmit and receive descriptor areas
+ */
+#define RDA_COUNT 20
+#define TDA_COUNT 10
+
+/*
+ *
+ * As suggested by National Application Note 746, make the
+ * receive resource area bigger than the receive descriptor area.
+ */
+#define RRA_EXTRA_COUNT 3
+
+/*
+ * RTEMS event used by interrupt handler to signal daemons.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * Largest Ethernet frame.
+ */
+#define MAXIMUM_FRAME_SIZE 1518
+
+/*
+ * Receive buffer size.
+ * Allow for a pointer, plus a full ethernet frame (including Frame
+ * Check Sequence) rounded up to a 4-byte boundary.
+ */
+#define RBUF_SIZE ((sizeof (void *) + (MAXIMUM_FRAME_SIZE) + 3) & ~3)
+#define RBUF_WC ((((MAXIMUM_FRAME_SIZE) + 3) & ~3) / 2)
+
+/*
+ * Macros for manipulating 32-bit pointers as 16-bit fragments
+ */
+#define LSW(p) ((rtems_unsigned16)((rtems_unsigned32)(p)))
+#define MSW(p) ((rtems_unsigned16)((rtems_unsigned32)(p) >> 16))
+#define PTR(m,l) ((void*)(((rtems_unsigned16)(m)<<16)|(rtems_unsigned16)(l)))
+
+/*
+ * Hardware-specific storage
+ */
+struct sonic {
+ /*
+ * Connection to KA9Q
+ */
+ struct iface *iface;
+
+ /*
+ * Default location of device registers
+ * ===CACHE===
+ * This area must be non-cacheable, guarded.
+ */
+ volatile struct SonicRegisters *sonic;
+
+ /*
+ * Interrupt vector
+ */
+ rtems_vector_number vector;
+
+ /*
+ * Task waiting for transmit resources
+ */
+ rtems_id txWaitTid;
+
+ /*
+ * Receive resource area
+ */
+ int rdaCount;
+ ReceiveResourcePointer_t rsa;
+
+ /*
+ * Transmit descriptors
+ */
+ int tdaCount;
+ TransmitDescriptorPointer_t tdaHead; /* Last filled */
+ TransmitDescriptorPointer_t tdaTail; /* Next to retire */
+ int tdaActiveCount;
+
+ /*
+ * Statistics
+ */
+ 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;
+};
+static struct sonic sonic[NSONIC];
+
+/*
+ ******************************************************************
+ * *
+ * Support Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Allocate non-cacheable memory on a single 64k page.
+ * Very simple minded -- just keeps trying till the memory is on a single page.
+ */
+static void *
+sonic_allocate (unsigned int nbytes)
+{
+ void *p;
+ unsigned long a1, a2;
+
+ for (;;) {
+ /*
+ * ===CACHE===
+ * Change malloc to malloc_noncacheable_guarded.
+ */
+ p = malloc (nbytes);
+ if (p == NULL)
+ rtems_panic ("No memory!");
+ a1 = (unsigned long)p;
+ a2 = a1 + nbytes - 1;
+ if ((a1 >> 16) == (a2 >> 16))
+ break;
+ }
+ return p;
+}
+
+/*
+ * Shut down the interface.
+ * This is a pretty simple-minded routine. It doesn't worry
+ * about cleaning up mbufs, shutting down daemons, etc.
+ */
+static int
+sonic_stop (struct iface *iface)
+{
+ int i;
+ struct sonic *dp = &sonic[iface->dev];
+ volatile struct SonicRegisters *rp = dp->sonic;
+
+ /*
+ * Stop the transmitter and receiver.
+ */
+ rp->cr = CR_HTX | CR_RXDIS;
+
+ /*
+ * Wait for things to stop.
+ * For safety's sake, there is an alternate exit.
+ */
+ i = 0;
+ while (rp->cr & (CR_RXEN | CR_TXP)) {
+ if (++i == 10000)
+ break;
+ }
+
+ /*
+ * Reset the device
+ */
+ rp->cr = CR_RST;
+ rp->imr = 0;
+ return 0;
+}
+
+/*
+ * Show interface statistics
+ */
+static void
+sonic_show (struct iface *iface)
+{
+ struct sonic *dp = &sonic[iface->dev];
+
+ printf (" Rx Interrupts:%-8lu", dp->rxInterrupts);
+ printf (" Giant:%-8lu", dp->rxGiant);
+ printf (" Non-octet:%-8lu\n", dp->rxNonOctet);
+ printf (" Bad CRC:%-8lu", dp->rxBadCRC);
+ printf (" Collision:%-8lu", dp->rxCollision);
+ printf (" Missed:%-8lu\n", dp->rxMissed);
+
+ printf ( " Tx Interrupts:%-8lu", dp->txInterrupts);
+ printf ( " Deferred:%-8lu", dp->txDeferred);
+ printf (" Lost Carrier:%-8lu\n", dp->txLostCarrier);
+ printf ( "Single Collisions:%-8lu", dp->txSingleCollision);
+ printf ( "Multiple Collisions:%-8lu", dp->txMultipleCollision);
+ printf ("Excessive Collisions:%-8lu\n", dp->txExcessiveCollision);
+ printf ( " Total Collisions:%-8lu", dp->txCollision);
+ printf ( " Late Collision:%-8lu", dp->txLateCollision);
+ printf (" Underrun:%-8lu\n", dp->txUnderrun);
+ printf ( " Raw output wait:%-8lu\n", dp->txRawWait);
+}
+
+/*
+ ******************************************************************
+ * *
+ * Interrupt Handler *
+ * *
+ ******************************************************************
+ */
+static rtems_isr
+sonic_interrupt_handler (rtems_vector_number v)
+{
+ struct sonic *dp = sonic;
+ volatile struct SonicRegisters *rp;
+
+#if (NSONIC > 1)
+ /*
+ * Find the device which requires service
+ */
+ for (;;) {
+ if (dp->vector == v)
+ break;
+ if (++dp == &sonic[NSONIC])
+ return; /* Spurious interrupt? */
+ }
+#endif /* NSONIC > 1 */
+
+ /*
+ * Get pointer to SONIC registers
+ */
+ rp = dp->sonic;
+
+ /*
+ * Packet received or receive buffer area exceeded?
+ */
+ if ((rp->imr & (IMR_PRXEN | IMR_RBAEEN))
+ && (rp->isr & (ISR_PKTRX | ISR_RBAE))) {
+ rp->imr &= ~(IMR_PRXEN | IMR_RBAEEN);
+ dp->rxInterrupts++;
+ rtems_event_send (dp->iface->rxproc, INTERRUPT_EVENT);
+ }
+
+ /*
+ * Packet started, transmitter done or transmitter error?
+ */
+ if ((rp->imr & (IMR_PINTEN | IMR_PTXEN | IMR_TXEREN))
+ && (rp->isr & (ISR_PINT | ISR_TXDN | ISR_TXER))) {
+ rp->imr &= ~(IMR_PINTEN | IMR_PTXEN | IMR_TXEREN);
+ dp->txInterrupts++;
+ rtems_event_send (dp->txWaitTid, INTERRUPT_EVENT);
+ }
+}
+
+/*
+ ******************************************************************
+ * *
+ * Transmitter Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Soak up transmit descriptors that have been sent.
+ */
+static void
+sonic_retire_tda (struct sonic *dp)
+{
+ rtems_unsigned16 status;
+ unsigned int collisions;
+
+ /*
+ * Repeat for all completed transmit descriptors.
+ */
+ while ((dp->tdaActiveCount != 0)
+ && ((status = dp->tdaTail->status) != 0)) {
+ /*
+ * Check for errors which stop the transmitter.
+ */
+ if (status & (TDA_STATUS_EXD |
+ TDA_STATUS_EXC |
+ TDA_STATUS_FU |
+ TDA_STATUS_BCM)) {
+ /*
+ * Restart the transmitter if there are
+ * packets waiting to go.
+ */
+ rtems_unsigned16 link;
+ link = *(dp->tdaTail->linkp);
+
+ if ((link & TDA_LINK_EOL) == 0) {
+ volatile struct SonicRegisters *rp = dp->sonic;
+
+ rp->ctda = link;
+ rp->cr = CR_TXP;
+ }
+ }
+
+ /*
+ * Update network statistics
+ */
+ collisions = (status & TDA_STATUS_COLLISION_MASK) >> TDA_STATUS_COLLISION_SHIFT;
+ if (collisions) {
+ if (collisions == 1)
+ dp->txSingleCollision++;
+ else
+ dp->txMultipleCollision++;
+ dp->txCollision += collisions;
+ }
+ if (status & TDA_STATUS_EXC)
+ dp->txExcessiveCollision++;
+ if (status & TDA_STATUS_OWC)
+ dp->txLateCollision++;
+ if (status & TDA_STATUS_EXD)
+ dp->txExcessiveDeferral++;
+ if (status & TDA_STATUS_DEF)
+ dp->txDeferred++;
+ if (status & TDA_STATUS_FU)
+ dp->txUnderrun++;
+ if (status & TDA_STATUS_CRSL)
+ dp->txLostCarrier++;
+
+ /*
+ * Free the packet
+ */
+ dp->tdaActiveCount--;
+ free_p ((struct mbuf **)&dp->tdaTail->mbufp);
+
+ /*
+ * Move to the next transmit descriptor
+ */
+ dp->tdaTail = dp->tdaTail->next;
+ }
+}
+
+/*
+ * Send raw packet (caller provides header).
+ * This code runs in the context of the interface transmit
+ * task (most packets) or in the context of the network
+ * task (for ARP requests).
+ */
+static int
+sonic_raw (struct iface *iface, struct mbuf **bpp)
+{
+ struct sonic *dp = &sonic[iface->dev];
+ volatile struct SonicRegisters *rp = dp->sonic;
+ struct mbuf *bp;
+ TransmitDescriptorPointer_t tdp;
+ volatile struct TransmitDescriptorFragLink *fp;
+ unsigned int packetSize;
+ int i;
+ static char padBuf[64];
+
+ /*
+ * Update the log.
+ */
+ iface->rawsndcnt++;
+ iface->lastsent = secclock ();
+ dump (iface, IF_TRACE_OUT, *bpp);
+
+ /*
+ * It would not do to have two tasks active in the transmit
+ * loop at the same time.
+ * The blocking is simple-minded since the odds of two tasks
+ * simultaneously attempting to use this code are low. The only
+ * way that two tasks can try to run here is:
+ * 1) Task A enters this code and ends up having to
+ * wait for a transmit buffer descriptor.
+ * 2) Task B gains control and tries to transmit a packet.
+ * The RTEMS/KA9Q scheduling semaphore ensures that there
+ * are no race conditions associated with manipulating the
+ * txWaitTid variable.
+ */
+ if (dp->txWaitTid) {
+ dp->txRawWait++;
+ while (dp->txWaitTid)
+ rtems_ka9q_ppause (10);
+ }
+
+ /*
+ * Free up transmit descriptors.
+ */
+ sonic_retire_tda (dp);
+
+ /*
+ * Wait for transmit descriptor to become available.
+ */
+ if (dp->tdaActiveCount == dp->tdaCount) {
+ /*
+ * Find out who we are
+ */
+ if (dp->txWaitTid == 0)
+ rtems_task_ident (RTEMS_SELF, 0, &dp->txWaitTid);
+
+ /*
+ * Clear old events.
+ */
+ rp->isr = ISR_PINT | ISR_TXDN | ISR_TXER;
+
+ /*
+ * Wait for transmit descriptor to become available.
+ * Note that the transmit descriptors are checked
+ * *before* * entering the wait loop -- this catches
+ * the possibility that a transmit descriptor became
+ * available between the `if' the started this block,
+ * and the clearing of the interrupt status register.
+ */
+ sonic_retire_tda (dp);
+ while (dp->tdaActiveCount == dp->tdaCount) {
+ /*
+ * Enable transmitter interrupts.
+ */
+ rp->imr |= (IMR_PINTEN | IMR_PTXEN | IMR_TXEREN);
+
+ /*
+ * Wait for interrupt
+ */
+ rtems_ka9q_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT);
+ rp->isr = ISR_PINT | ISR_TXDN | ISR_TXER;
+ sonic_retire_tda (dp);
+ }
+ }
+
+ /*
+ * Get the head of the packet mbuf chain.
+ */
+ bp = *bpp;
+
+ /*
+ * Fill in the transmit descriptor fragment descriptors.
+ * ===CACHE===
+ * If data cache is operating in write-back mode, flush cached
+ * data to memory.
+ */
+ tdp = dp->tdaHead->next;
+ tdp->mbufp = bp;
+ packetSize = 0;
+ fp = tdp->frag;
+ for (i = 0 ; i < MAXIMUM_FRAGS_PER_DESCRIPTOR ; i++, fp++) {
+ fp->frag_lsw = LSW(bp->data);
+ fp->frag_msw = MSW(bp->data);
+ fp->frag_size = bp->cnt;
+ packetSize += bp->cnt;
+
+ /*
+ * Break out of the loop if this mbuf is the last in the frame.
+ */
+ if ((bp = bp->next) == NULL)
+ break;
+ }
+
+ /*
+ * Pad short packets.
+ */
+ if ((packetSize < 64) && (i < MAXIMUM_FRAGS_PER_DESCRIPTOR)) {
+ int padSize = 64 - packetSize;
+ fp->frag_lsw = LSW(padBuf);
+ fp->frag_msw = MSW(padBuf);
+ fp->frag_size = padSize;
+ packetSize += padSize;
+ i++;
+ fp++;
+ }
+
+ /*
+ * Fill Transmit Descriptor
+ */
+ tdp->pkt_size = packetSize;
+ tdp->frag_count = i;
+ tdp->status = 0;
+
+ /*
+ * Chain onto list and start transmission.
+ */
+ tdp->linkp = &fp->frag_link;
+ *tdp->linkp = LSW(tdp->next) | TDA_LINK_EOL;
+ *dp->tdaHead->linkp &= ~TDA_LINK_EOL;
+ rp->cr = CR_TXP;
+ dp->tdaActiveCount++;
+ dp->tdaHead = tdp;
+
+ /*
+ * Let KA9Q know the packet is on the way.
+ */
+ dp->txWaitTid = 0;
+ *bpp = NULL;
+ return 0;
+}
+
+/*
+ ******************************************************************
+ * *
+ * Receiver Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Wait for SONIC to hand over a Receive Descriptor.
+ */
+static void
+sonic_rda_wait (struct sonic *dp, ReceiveDescriptorPointer_t rdp)
+{
+ int i;
+ volatile struct SonicRegisters *rp = dp->sonic;
+
+ /*
+ * Wait for Receive Descriptor.
+ * The order of the tests is very important.
+ * The RDA is checked after RBAE is detected. This ensures that
+ * the driver processes all RDA entries before reusing the RRA
+ * entry holding the giant packet.
+ * The event wait is done after the RDA and RBAE checks. This
+ * catches the possibility that a Receive Descriptor became ready
+ * between the call to this function and the clearing of the
+ * interrupt status register bit.
+ */
+ for (;;) {
+ /*
+ * Has a giant packet arrived?
+ * The National DP83932C data sheet is very vague on what
+ * happens under this condition. The description of the
+ * Interrupt Status Register (Section 4.3.6) states,
+ * ``Reception is aborted and the SONIC fetches the next
+ * available resource descriptors in the RRA. The buffer
+ * space is not re-used and an RDA is not setup for the
+ * truncated packet.''
+ * I take ``Reception is aborted'' to mean that the RXEN
+ * bit in the Command Register is cleared and must be set
+ * by the driver to begin reception again.
+ * Unfortunately, an alternative interpretation could be
+ * that only reception of the current packet is aborted.
+ * This would be more difficult to recover from....
+ */
+ if (rp->isr & ISR_RBAE) {
+ /*
+ * One more check to soak up any Receive Descriptors
+ * that may already have been handed back to the driver.
+ */
+ if (rdp->in_use == 0)
+ break;
+
+ /*
+ * Check my interpretation of the SONIC manual.
+ */
+ if (rp->cr & CR_RXEN)
+ rtems_panic ("SONIC RBAE/RXEN");
+
+ /*
+ * Update statistics
+ */
+ dp->rxGiant++;
+
+ /*
+ * Reuse receive buffer.
+ * Again, the manual is subject to interpretation. The
+ * RRP register is described as, `the lower address of
+ * the next descriptor the SONIC will read.''
+ * Since, acording to the ISR/RBAE notes, the SONIC has
+ * ``fetched the next available resource descriptor in
+ * the RRA'', I interpret this to mean that that the
+ * driver has to move the RRP back *two* entries to
+ * reuse the receive buffer holding the giant packet.
+ */
+ for (i = 0 ; i < 2 ; i++) {
+ if (rp->rrp == rp->rsa)
+ rp->rrp = rp->rea;
+ rp->rrp -= sizeof (ReceiveResource_t);
+ }
+
+ /*
+ * Restart reception
+ */
+ rp->isr = ISR_RBAE;
+ rp->cr = CR_RXEN;
+ }
+
+ /*
+ * Clear old packet-received events.
+ */
+ rp->isr = ISR_PKTRX;
+
+ /*
+ * Has Receive Descriptor become available?
+ */
+ if (rdp->in_use == 0)
+ break;
+
+ /*
+ * Enable interrupts.
+ */
+ rp->imr |= IMR_PRXEN | IMR_RBAEEN;
+
+ /*
+ * Wait for interrupt.
+ */
+ rtems_ka9q_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT);
+ }
+}
+
+/*
+ * SCC reader task
+ */
+static void
+sonic_rx (int dev, void *p1, void *p2)
+{
+ struct iface *iface = (struct iface *)p1;
+ struct sonic *dp = (struct sonic *)p2;
+ volatile struct SonicRegisters *rp = dp->sonic;
+ struct mbuf *bp;
+ rtems_unsigned16 status;
+ ReceiveDescriptor_t *rda;
+ ReceiveDescriptorPointer_t ordp, rdp;
+ ReceiveResourcePointer_t rwp, rea;
+ rtems_unsigned16 newMissedTally, oldMissedTally;
+ int i;
+ int continuousCount;
+
+ /*
+ * Set up list in Receive Resource Area.
+ * Allocate space for incoming packets.
+ */
+ rwp = dp->rsa;
+ for (i = 0 ; i < (dp->rdaCount + RRA_EXTRA_COUNT) ; i++, rwp++) {
+ struct mbuf **mbp;
+
+ /*
+ * Allocate memory for buffer.
+ * Place a pointer to the mbuf at the beginning of the buffer
+ * so we can find the mbuf when the SONIC returns the buffer
+ * to the driver.
+ */
+ bp = ambufw (RBUF_SIZE);
+ mbp = (struct mbuf **)bp->data;
+ bp->data += sizeof *mbp;
+ *mbp = bp;
+
+ /*
+ * Set up RRA entry
+ */
+ rwp->buff_ptr_lsw = LSW(bp->data);
+ rwp->buff_ptr_msw = MSW(bp->data);
+ rwp->buff_wc_lsw = RBUF_WC;
+ rwp->buff_wc_msw = 0;
+ }
+ rea = rwp;
+
+ /*
+ * Set up remaining Receive Resource Area pointers
+ */
+ rp->rsa = LSW(dp->rsa);
+ rp->rrp = LSW(dp->rsa);
+ rp->rea = LSW(rea);
+ rp->rwp = LSW(rea);
+
+ /*
+ * Set End Of Buffer Count register to the value recommended
+ * in Note 1 of Section 3.4.4.4 of the SONIC data sheet.
+ */
+ rp->eobc = RBUF_WC - 2;
+
+ /*
+ * Set up circular linked list in Receive Descriptor Area.
+ * Leaves ordp pointing at the `end' of the list and
+ * rdp pointing at the `beginning' of the list.
+ */
+ rda = sonic_allocate (dp->rdaCount * sizeof *rda);
+ ordp = rdp = rda;
+ for (i = 0 ; i < dp->rdaCount ; i++) {
+ /*
+ * Set up RDA entry
+ */
+ if (i == (dp->rdaCount - 1))
+ rdp->next = rda;
+ else
+ rdp->next = (ReceiveDescriptor_t *)(rdp + 1);
+ rdp->in_use = 1;
+ ordp = rdp;
+ rdp = rdp->next;
+ ordp->link = LSW(rdp);
+ }
+ ordp->link |= RDA_LINK_EOL;
+ rp->urda = MSW(rdp);
+ rp->crda = LSW(rdp);
+
+ /*
+ * Start the receiver
+ */
+ oldMissedTally = rp->mpt;
+ rp->cr = CR_RRRA;
+ rp->cr = CR_RXEN;
+
+ /*
+ * Input packet handling loop
+ */
+ continuousCount = 0;
+ for (;;) {
+ /*
+ * Wait till SONIC supplies a Receive Descriptor.
+ */
+ if (rdp->in_use) {
+ continuousCount = 0;
+ sonic_rda_wait (dp, rdp);
+ }
+
+ /*
+ * Check that packet is valid
+ */
+ status = rdp->status;
+ if (status & RDA_STATUS_PRX) {
+ struct mbuf **mbp;
+ void *p;
+
+ /*
+ * Get the mbuf pointer
+ */
+ p = PTR(rdp->pkt_msw, rdp->pkt_lsw);
+ mbp = (struct mbuf **)p - 1;
+ bp = *mbp;
+
+ /*
+ * Pass the packet up the chain.
+ * The mbuf count is reduced to remove
+ * the frame check sequence at the end
+ * of the packet.
+ * ===CACHE===
+ * Invalidate cache entries for this memory.
+ */
+ bp->cnt = rdp->byte_count - sizeof (uint32);
+ net_route (iface, &bp);
+
+ /*
+ * Give the network code a chance to digest the
+ * packet. This guards against a flurry of
+ * incoming packets (usually an ARP storm) from
+ * using up all the available memory.
+ */
+ if (++continuousCount >= dp->rdaCount)
+ kwait_null ();
+
+ /*
+ * Sanity check that Receive Resource Area is
+ * still in sync with Receive Descriptor Area
+ * The buffer reported in the Receive Descriptor
+ * should be the same as the buffer in the Receive
+ * Resource we are about to reuse.
+ */
+ if ((LSW(p) != rwp->buff_ptr_lsw)
+ || (MSW(p) != rwp->buff_ptr_msw))
+ rtems_panic ("SONIC RDA/RRA");
+
+ /*
+ * Allocate a new mbuf.
+ */
+ bp = ambufw (RBUF_SIZE);
+ mbp = (struct mbuf **)bp->data;
+ bp->data += sizeof *mbp;
+ *mbp = bp;
+
+ /*
+ * Reuse Receive Resource.
+ */
+ rwp->buff_ptr_lsw = LSW(bp->data);
+ rwp->buff_ptr_msw = MSW(bp->data);
+ rwp++;
+ if (rwp == rea)
+ rwp = dp->rsa;
+ rp->rwp = LSW(rwp);
+
+ /*
+ * Tell the SONIC to reread the RRA.
+ */
+ if (rp->isr & ISR_RBE)
+ rp->isr = ISR_RBE;
+ }
+ else {
+ if (status & RDA_STATUS_COL)
+ dp->rxCollision++;
+ if (status & RDA_STATUS_FAER)
+ dp->rxNonOctet++;
+ else if (status & RDA_STATUS_CRCR)
+ dp->rxBadCRC++;
+ }
+
+ /*
+ * Count missed packets
+ */
+ newMissedTally = rp->mpt;
+ if (newMissedTally != oldMissedTally) {
+ dp->rxMissed += (newMissedTally - oldMissedTally) & 0xFFFF;
+ newMissedTally = oldMissedTally;
+ }
+
+ /*
+ * Move to next receive descriptor
+ */
+ rdp->link |= RDA_LINK_EOL;
+ rdp->in_use = 1;
+ ordp->link &= ~RDA_LINK_EOL;
+ ordp = rdp;
+ rdp = rdp->next;
+ }
+}
+
+/*
+ ******************************************************************
+ * *
+ * Initialization Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Initialize the SONIC hardware
+ */
+static void
+sonic_initialize_hardware (struct sonic *dp, int broadcastFlag)
+{
+ volatile struct SonicRegisters *rp = dp->sonic;
+ int i;
+ unsigned char *hwaddr;
+ rtems_status_code sc;
+ rtems_isr_entry old_handler;
+ TransmitDescriptorPointer_t otdp, tdp;
+ struct CamDescriptor{
+ rtems_unsigned32 cep;
+ rtems_unsigned32 cap0;
+ rtems_unsigned32 cap1;
+ rtems_unsigned32 cap2;
+ rtems_unsigned32 ce;
+ };
+ volatile struct CamDescriptor *cdp;
+
+ /*
+ * Issue a software reset if necessary.
+ */
+ if ((rp->cr & CR_RST) == 0)
+ rp->cr = CR_RST;
+
+ /*
+ * Set up data configuration registers.
+ */
+ rp->dcr = SONIC_DCR;
+ rp->dcr2 = SONIC_DC2;
+
+ /*
+ * Remove device reset
+ */
+ rp->cr = 0;
+
+ /*
+ * Clear outstanding interrupts.
+ */
+ rp->isr = 0x7FFF;
+
+ /*
+ * Allocate the receive resource area.
+ * In accordance with National Application Note 746, make the
+ * receive resource area bigger than the receive descriptor area.
+ * This has the useful side effect of making the receive resource
+ * area big enough to hold the CAM descriptor area.
+ */
+ dp->rsa = sonic_allocate ((dp->rdaCount + RRA_EXTRA_COUNT) * sizeof *dp->rsa);
+ rp->urra = MSW(dp->rsa);
+
+ /*
+ * Set up the SONIC CAM with our hardware address.
+ * Use the Receive Resource Area to hold the CAM Descriptor Area.
+ */
+ hwaddr = dp->iface->hwaddr;
+ cdp = (struct CamDescriptor *)dp->rsa;
+ cdp->cep = 0; /* Fill first entry in CAM */
+ cdp->cap2 = hwaddr[0] << 8 | hwaddr[1];
+ cdp->cap1 = hwaddr[2] << 8 | hwaddr[3];
+ cdp->cap0 = hwaddr[4] << 8 | hwaddr[5];
+ cdp->ce = 0x0001; /* Enable first entry in CAM */
+ rp->cdc = 1; /* One entry in CDA */
+ rp->cdp = LSW(cdp);
+ rp->cr = CR_LCAM; /* Load the CAM */
+ while (rp->cr & CR_LCAM)
+ continue;
+
+ /*
+ * Verify that CAM was properly loaded.
+ */
+ rp->cep = 0; /* Select first entry in CAM */
+ if ((rp->cap2 != cdp->cap2)
+ || (rp->cap1 != cdp->cap1)
+ || (rp->cap0 != cdp->cap0)
+ || (rp->ce != cdp->ce)) {
+ printf ("Failed to load Ethernet address into SONIC CAM.\n"
+ " Wrote %04x%04x%04x - %#x\n"
+ " Read %04x%04x%04x - %#x\n",
+ cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
+ rp->cap2, rp->cap1, rp->cap0, rp->ce);
+ rtems_panic ("SONIC LCAM");
+ }
+
+ /*
+ * Set up circular linked list in Transmit Descriptor Area.
+ * Use the PINT bit in the transmit configuration field to
+ * request an interrupt on every other transmitted packet.
+ */
+ dp->tdaActiveCount = 0;
+ dp->tdaTail = sonic_allocate (dp->tdaCount * sizeof *tdp);
+ otdp = tdp = dp->tdaTail;
+ for (i = 0 ; i < dp->tdaCount ; i++) {
+ if (i & 1)
+ tdp->pkt_config = TDA_CONFIG_PINT;
+ else
+ tdp->pkt_config = 0;
+ if (i == (dp->tdaCount - 1))
+ tdp->next = (TransmitDescriptor_t *)dp->tdaTail;
+ else
+ tdp->next = (TransmitDescriptor_t *)(tdp + 1);
+ otdp = tdp;
+ tdp = tdp->next;
+ }
+ dp->tdaHead = otdp;
+ dp->tdaHead->linkp = &dp->tdaHead->frag[0].frag_link;
+ rp->utda = MSW(dp->tdaTail);
+ rp->ctda = LSW(dp->tdaTail);
+
+ /*
+ * Enable/disable reception of broadcast packets
+ */
+ if (broadcastFlag)
+ rp->rcr = RCR_BRD;
+ else
+ rp->rcr = 0;
+
+ /*
+ * Attach SONIC interrupt handler
+ */
+ rp->imr = 0;
+ sc = rtems_interrupt_catch (sonic_interrupt_handler, dp->vector, &old_handler);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_panic ("Can't attach SONIC interrupt handler: %s\n",
+ rtems_status_text (sc));
+
+ /*
+ * Remainder of hardware initialization is
+ * done by the receive and transmit daemons.
+ */
+}
+
+/*
+ * Attach an SONIC driver to the system
+ * This is the only `extern' function in the driver.
+ *
+ * argv[0]: interface label, e.g. "rtems"
+ * The remainder of the arguments are optional key/value pairs:
+ * mtu ## -- maximum transmission unit, default 1500
+ * broadcast y/n -- accept or ignore broadcast packets, default yes
+ * rbuf ## -- Set number of receive descriptor entries
+ * tbuf ## -- Set number of transmit descriptor entries
+ * ip ###.###.###.### -- IP address
+ * ether ##:##:##:##:##:## -- Ethernet address
+ * reg ###### -- Address of SONIC device registers
+ * vector ### -- SONIC interrupt vector
+ */
+int
+rtems_ka9q_driver_attach (int argc, char *argv[], void *p)
+{
+ struct sonic *dp;
+ struct iface *iface;
+ char *cp;
+ int argIndex;
+ int broadcastFlag;
+ char cbuf[30];
+
+ /*
+ * Find an unused entry
+ */
+ dp = sonic;
+ for (;;) {
+ if (dp == &sonic[NSONIC]) {
+ printf ("No more SONIC devices.\n");
+ return -1;
+ }
+ if (dp->iface == NULL)
+ break;
+ dp++;
+ }
+ if (if_lookup (argv[0]) != NULL) {
+ printf ("Interface %s already exists\n", argv[0]);
+ return -1;
+ }
+
+ /*
+ * Create an inteface descriptor
+ */
+ iface = callocw (1, sizeof *iface);
+ iface->name = strdup (argv[0]);
+ iface->dev = dp - sonic;;
+
+ /*
+ * Set default values
+ */
+ broadcastFlag = 1;
+ dp->txWaitTid = 0;
+ dp->rdaCount = RDA_COUNT;
+ dp->tdaCount = TDA_COUNT;
+ iface->mtu = 1500;
+ iface->addr = Ip_addr;
+ iface->hwaddr = mallocw (EADDR_LEN);
+ memset (iface->hwaddr, 0x08, EADDR_LEN);
+ dp->sonic = (struct SonicRegisters *)SONIC_BASE_ADDRESS;
+ dp->vector = SONIC_VECTOR;
+
+ /*
+ * Parse remaining arguments
+ */
+ for (argIndex = 1 ; argIndex < (argc - 1) ; argIndex++) {
+ if (strcmp ("mtu", argv[argIndex]) == 0) {
+ iface->mtu = strtoul (argv[++argIndex], NULL, 0);
+ }
+ else if (strcmp ("broadcast", argv[argIndex]) == 0) {
+ if (*argv[++argIndex] == 'n')
+ broadcastFlag = 0;
+ }
+ else if (strcmp ("rbuf", argv[argIndex]) == 0) {
+ /*
+ * The minimum RDA count is 2. A single-entry RDA
+ * would be difficult to use since the SONIC does
+ * not release (in_use = 0) the RDA that has the
+ * EOL bit set.
+ */
+ dp->rdaCount = strtoul (argv[++argIndex], NULL, 0);
+ if ((dp->rdaCount <= 1) || (dp->rdaCount > 200)) {
+ printf ("RDA option (%d) is invalid.\n", dp->rdaCount);
+ return -1;
+ }
+ }
+ else if (strcmp ("tbuf", argv[argIndex]) == 0) {
+ dp->tdaCount = strtoul (argv[++argIndex], NULL, 0);
+ if ((dp->tdaCount <= 1) || (dp->tdaCount > 200)) {
+ printf ("TDA option (%d) is invalid.\n", dp->tdaCount);
+ return -1;
+ }
+ }
+ else if (strcmp ("ip", argv[argIndex]) == 0) {
+ iface->addr = resolve (argv[++argIndex]);
+ }
+ else if (strcmp ("ether", argv[argIndex]) == 0) {
+ gether (iface->hwaddr, argv[++argIndex]);
+ }
+ else if (strcmp ("reg", argv[argIndex]) == 0) {
+ dp->sonic = (struct SonicRegisters *)strtoul (argv[++argIndex], NULL, 0);
+ }
+ else if (strcmp ("vector", argv[argIndex]) == 0) {
+ dp->vector = strtoul (argv[++argIndex], NULL, 0);
+ }
+ else {
+ printf ("Argument %d (%s) is invalid.\n", argIndex, argv[argIndex]);
+ return -1;
+ }
+ }
+ printf ("Ethernet address: %s\n", pether (cbuf, iface->hwaddr));
+ iface->raw = sonic_raw;
+ iface->stop = sonic_stop;
+ iface->show = sonic_show;
+ dp->iface = iface;
+ setencap (iface, "Ethernet");
+
+ /*
+ * Set up SONIC hardware
+ */
+ sonic_initialize_hardware (dp, broadcastFlag);
+
+ /*
+ * Chain onto list of interfaces
+ */
+ iface->next = Ifaces;
+ Ifaces = iface;
+
+ /*
+ * Start I/O daemons
+ */
+ cp = if_name (iface, " tx");
+ iface->txproc = newproc (cp, 2048, if_tx, iface->dev, iface, NULL, 0);
+ free (cp);
+ cp = if_name (iface, " rx");
+ iface->rxproc = newproc (cp, 2048, sonic_rx, iface->dev, iface, dp, 0);
+ free (cp);
+ return 0;
+}