summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/dmv177/sonic/sonic.c
diff options
context:
space:
mode:
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;
+}