/*
*******************************************************************
*******************************************************************
** **
** 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>
/*
* Debug levels
*
*/
#define SONIC_DEBUG_NONE 0x0000
#define SONIC_DEBUG_ALL 0xFFFF
#define SONIC_DEBUG_PRINT_REGISTERS 0x0001
#define SONIC_DEBUG_MEMORY 0x0002
#define SONIC_DEBUG_MEMORY_ALLOCATE 0x0004
#define SONIC_DEBUG_FRAGMENTS 0x0008
#define SONIC_DEBUG_CAM 0x0008
#define SONIC_DEBUG_DESCRIPTORS 0x0010
#define SONIC_DEBUG_ERRORS 0x0020
#define SONIC_DEBUG (SONIC_DEBUG_NONE)
/*
* XXX
*/
#include <dmv170.h>
/*
* Use the top line if you want more symbols.
*/
#define SONIC_STATIC
/* #define SONIC_STATIC static */
/*
* 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
*/
#define SONIC_DCR \
(DCR_DW32 | DCR_WAIT0 | DCR_PO0 | DCR_PO1 | DCR_RFT4 | DCR_TFT8)
#ifndef SONIC_DCR
# define SONIC_DCR (DCR_DW32 | DCR_TFT28)
#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 0
/*
* 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.
*/
void *sonic;
/*
* Interrupt vector
*/
rtems_vector_number vector;
/*
* Task waiting for transmit resources
*/
rtems_id txWaitTid;
/*
* Receive resource area
*/
int rdaCount;
ReceiveResourcePointer_t rsa;
ReceiveResourcePointer_t rea;
CamDescriptorPointer_t cdp;
ReceiveDescriptorPointer_t rda;
ReceiveDescriptorPointer_t rdp_last;
/*
* Transmit descriptors
*/
int tdaCount;
TransmitDescriptorPointer_t tdaHead; /* Last filled */
TransmitDescriptorPointer_t tdaTail; /* Next to retire */
int tdaActiveCount;
/*
* 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;
};
SONIC_STATIC struct sonic sonic[NSONIC];
/*
******************************************************************
* *
* Support Routines *
* *
******************************************************************
*/
void sonic_write_register(
void *base,
unsigned32 regno,
unsigned32 value
);
unsigned32 sonic_read_register(
void *base,
unsigned32 regno
);
void sonic_enable_interrupts(
void *rp,
unsigned32 mask
)
{
rtems_interrupt_level level;
rtems_interrupt_disable( level );
sonic_write_register(
rp,
SONIC_REG_IMR,
sonic_read_register(rp, SONIC_REG_IMR) | mask
);
rtems_interrupt_enable( level );
}
/*
* Allocate non-cacheable memory on a single 64k page.
* Very simple minded -- just keeps trying till the memory is on a single page.
*/
SONIC_STATIC void * sonic_allocate(unsigned int nbytes)
{
void *p;
unsigned long a1, a2;
for (;;) {
/*
* ===CACHE===
* Change malloc to malloc_noncacheable_guarded.
*/
p = calloc(1, nbytes);
if (p == NULL)
rtems_panic ("No memory!");
a1 = (unsigned long)p;
a2 = a1 + nbytes - 1;
if ((a1 >> 16) == (a2 >> 16))
break;
}
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_ALLOCATE)
printf( "sonic_allocate %d bytes at %p\n", nbytes, p );
#endif
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.
*/
SONIC_STATIC int sonic_stop (struct iface *iface)
{
int i;
struct sonic *dp = &sonic[iface->dev];
void *rp = dp->sonic;
/*
* Stop the transmitter and receiver.
*/
sonic_write_register( rp, SONIC_REG_CR, CR_HTX | CR_RXDIS );
/*
* Wait for things to stop.
* For safety's sake, there is an alternate exit.
*/
i = 0;
while (sonic_read_register( rp, SONIC_REG_CR ) & (CR_RXEN | CR_TXP)) {
if (++i == 10000)
break;
}
/*
* Reset the device
*/
sonic_write_register( rp, SONIC_REG_CR, CR_RST );
sonic_write_register( rp, SONIC_REG_IMR, 0 );
return 0;
}
/*
* Show interface statistics
*/
SONIC_STATIC void sonic_show (struct iface *iface)
{
struct sonic *dp = &sonic[iface->dev];
printf (" Total Interrupts:%-8lu", dp->Interrupts);
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 *
* *
******************************************************************
*/
SONIC_STATIC rtems_isr sonic_interrupt_handler (rtems_vector_number v)
{
struct sonic *dp = sonic;
unsigned32 isr, imr;
void *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;
dp->Interrupts++;
isr = sonic_read_register( rp, SONIC_REG_ISR );
imr = sonic_read_register( rp, SONIC_REG_IMR );
/*
* Packet received or receive buffer area exceeded?
*/
if ((imr & (IMR_PRXEN | IMR_RBAEEN)) &&
(isr & (ISR_PKTRX | ISR_RBAE))) {
imr &= ~(IMR_PRXEN | IMR_RBAEEN);
dp->rxInterrupts++;
rtems_event_send (dp->iface->rxproc, INTERRUPT_EVENT);
}
/*
* Packet started, transmitter done or transmitter error?
*/
if ((imr & (IMR_PINTEN | IMR_PTXEN | IMR_TXEREN))
&& (isr & (ISR_PINT | ISR_TXDN | ISR_TXER))) {
imr &= ~(IMR_PINTEN | IMR_PTXEN | IMR_TXEREN);
dp->txInterrupts++;
rtems_event_send (dp->txWaitTid, INTERRUPT_EVENT);
}
sonic_write_register( rp, SONIC_REG_IMR, imr );
}
/*
******************************************************************
* *
* Transmitter Routines *
* *
******************************************************************
*/
/*
* Soak up transmit descriptors that have been sent.
*/
SONIC_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)) {
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
printf( "retire TDA %p (0x%04x)\n", dp->tdaTail, status );
#endif
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
if ( status != 0x0001 )
printf( "ERROR: retire TDA %p (0x%04x)\n", dp->tdaTail, status );
#endif
/*
* 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) {
void *rp = dp->sonic;
sonic_write_register( rp, SONIC_REG_CTDA, link );
sonic_write_register( rp, SONIC_REG_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 and reset a couple of fields
*/
dp->tdaActiveCount--;
free_p ((struct mbuf **)&dp->tdaTail->mbufp);
dp->tdaTail->frag[0].frag_link = LSW(dp->tdaTail->link_pad);
dp->tdaTail->frag_count = 0;
/*
* Move to the next transmit descriptor
*/
dp->tdaTail = dp->tdaTail->next;
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
printf( "next TDA %p\n", dp->tdaTail );
#endif
}
}
/*
* 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).
*/
SONIC_STATIC int sonic_raw (struct iface *iface, struct mbuf **bpp)
{
struct sonic *dp = &sonic[iface->dev];
void *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) {
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
printf( "TX: conflict delay\n" );
#endif
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) {
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
puts( "Wait for more TDAs" );
#endif
/*
* Find out who we are
*/
if (dp->txWaitTid == 0)
rtems_task_ident (RTEMS_SELF, 0, &dp->txWaitTid);
/*
* Clear old events.
*/
sonic_write_register( rp, SONIC_REG_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.
*/
sonic_enable_interrupts( rp, (IMR_PINTEN | IMR_PTXEN | IMR_TXEREN) );
/*
* Wait for interrupt
*/
rtems_ka9q_event_receive (INTERRUPT_EVENT,
RTEMS_WAIT|RTEMS_EVENT_ANY,
RTEMS_NO_TIMEOUT);
sonic_write_register( rp, SONIC_REG_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;
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
printf( "fp %p 0x%04x%04x %d\n",
fp, fp->frag_msw, fp->frag_lsw, fp->frag_size );
#endif
/*
* 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++;
fp->frag_lsw = LSW(padBuf);
fp->frag_msw = MSW(padBuf);
fp->frag_size = padSize;
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
printf( "PAD fp %p 0x%04x%04x %d\n",
fp, fp->frag_msw, fp->frag_lsw, fp->frag_size );
#endif
packetSize += padSize;
i++;
}
/*
* Fill Transmit Descriptor
*/
tdp->pkt_size = packetSize;
tdp->frag_count = i + 1;
tdp->status = 0;
/*
* Chain onto list and start transmission.
*/
tdp->linkp = &(fp+1)->frag_link;
*tdp->linkp = LSW(tdp->next) | TDA_LINK_EOL;
if ( dp->tdaHead->frag_count )
*dp->tdaHead->linkp &= ~TDA_LINK_EOL;
dp->tdaActiveCount++;
dp->tdaHead = tdp;
sonic_write_register( rp, SONIC_REG_CR, CR_TXP );
/*
* 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.
*/
SONIC_STATIC void sonic_rda_wait(
struct sonic *dp,
ReceiveDescriptorPointer_t rdp
)
{
int i;
void *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 (sonic_read_register( rp, SONIC_REG_ISR ) & ISR_RBAE) {
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
printf( "ERROR: looks like a giant packet -- RBAE\n" );
#endif
/*
* One more check to soak up any Receive Descriptors
* that may already have been handed back to the driver.
*/
if (rdp->in_use == RDA_IN_USE) {
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
printf( "ERROR: nope just an RBAE\n" );
#endif
break;
}
/*
* Check my interpretation of the SONIC manual.
*/
#if 0
if (sonic_read_register( rp, SONIC_REG_CR ) & CR_RXEN)
rtems_panic ("SONIC RBAE/RXEN");
#endif
/*
* 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.
*/
#if 0
for (i = 0 ; i < 2 ; i++) {
if (sonic_read_register( rp, SONIC_REG_RRP ) ==
sonic_read_register( rp, SONIC_REG_RSA ))
sonic_write_register(
rp,
SONIC_REG_RRP,
sonic_read_register( rp, SONIC_REG_REA )
);
sonic_write_register(
rp,
SONIC_REG_RRP,
sonic_read_register(rp, SONIC_REG_RRP) - sizeof(ReceiveResource_t)
);
}
#endif
/*
* Restart reception
*/
sonic_write_register( rp, SONIC_REG_ISR, ISR_RBAE );
#if 0
sonic_write_register( rp, SONIC_REG_CR, CR_RXEN );
#endif
}
/*
* Clear old packet-received events.
*/
sonic_write_register( rp, SONIC_REG_ISR, ISR_PKTRX );
/*
* Has Receive Descriptor become available?
*/
if (rdp->in_use == RDA_IN_USE)
break;
/*
* Enable interrupts.
*/
sonic_enable_interrupts( rp, (IMR_PRXEN | IMR_RBAEEN) );
/*
* Wait for interrupt.
*/
rtems_ka9q_event_receive (INTERRUPT_EVENT,
RTEMS_WAIT|RTEMS_EVENT_ANY,
RTEMS_NO_TIMEOUT);
}
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
printf( "RDA %p\n", rdp );
#endif
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
if (rdp->status & 0x000E)
printf( "ERROR: RDA %p (0x%04x)\n", rdp, rdp->status );
#endif
}
/*
* SCC reader task
*/
SONIC_STATIC void sonic_rx (int dev, void *p1, void *p2)
{
struct iface *iface = (struct iface *)p1;
struct sonic *dp = (struct sonic *)p2;
void *rp = dp->sonic;
struct mbuf *bp;
rtems_unsigned16 status;
ReceiveDescriptorPointer_t rdp;
ReceiveResourcePointer_t rwp, rea;
rtems_unsigned16 newMissedTally, oldMissedTally;
int continuousCount;
rwp = dp->rsa;
rea = dp->rea;
rdp = dp->rda;
/*
* Start the receiver
*/
oldMissedTally = sonic_read_register( rp, SONIC_REG_MPT );
sonic_write_register( rp, SONIC_REG_CR, CR_RRRA );
sonic_write_register( rp, SONIC_REG_CR, CR_RXEN );
/*
* Input packet handling loop
*/
continuousCount = 0;
for (;;) {
/*
* Wait till SONIC supplies a Receive Descriptor.
*/
if (rdp->in_use == RDA_FREE) {
continuousCount = 0;
sonic_rda_wait (dp, rdp);
}
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
printf( "Incoming packet %p status=0x%04x\n", rdp, rdp->status );
#endif
/*
* 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) {
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
printf( "ERROR: RX processed too many in a row\n" );
#endif
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.
*/
/* XXX figure out whether this is valid or not */
#if 0
if ((LSW(p) != rwp->buff_ptr_lsw)
|| (MSW(p) != rwp->buff_ptr_msw))
rtems_panic ("SONIC RDA/RRA");
#endif
/*
* 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) {
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
printf( "Wrapping RWP from %p to %p\n", rwp, dp->rsa );
#endif
rwp = dp->rsa;
}
sonic_write_register( rp, SONIC_REG_RWP , LSW(rwp) );
/*
* Tell the SONIC to reread the RRA.
*/
if (sonic_read_register( rp, SONIC_REG_ISR ) & ISR_RBE)
sonic_write_register( rp, SONIC_REG_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 = sonic_read_register( rp, SONIC_REG_MPT );
if (newMissedTally != oldMissedTally) {
dp->rxMissed += (newMissedTally - oldMissedTally) & 0xFFFF;
newMissedTally = oldMissedTally;
}
/*
* Move to next receive descriptor
*/
rdp->in_use = RDA_FREE;
rdp = rdp->next;
rdp->link &= ~RDA_LINK_EOL;
}
}
/*
******************************************************************
* *
* Initialization Routines *
* *
******************************************************************
*/
/*
* Initialize the SONIC hardware
*/
SONIC_STATIC void sonic_initialize_hardware(
struct sonic *dp,
int broadcastFlag
)
{
void *rp = dp->sonic;
int i;
unsigned char *hwaddr;
rtems_isr_entry old_handler;
TransmitDescriptorPointer_t tdp;
ReceiveDescriptorPointer_t ordp, rdp;
ReceiveResourcePointer_t rwp;
struct mbuf *bp;
CamDescriptorPointer_t cdp;
/*
* The Revision B SONIC has a horrible bug known as the "Zero
* Length Packet bug". The initial board used to develop this
* driver had a newer revision of the SONIC so there was no reason
* to check for this. If you have the Revision B SONIC chip, then
* you need to add some code to the RX path to handle this weirdness.
*/
if ( sonic_read_register( rp, SONIC_REG_SR ) < SONIC_REVISION_C ) {
rtems_fatal_error_occurred( 0x0BADF00D ); /* don't eat this part :) */
}
/*
* 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.
*
* NOTE: sonic_allocate() zeroes all of the memory allocated.
*/
dp->tdaActiveCount = 0;
dp->tdaTail = sonic_allocate(dp->tdaCount * sizeof *tdp);
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
printf( "tdaTail = %p\n", dp->tdaTail );
#endif
tdp = dp->tdaTail;
for (i = 0 ; i < dp->tdaCount ; i++) {
/*
* status, pkt_config, pkt_size, and all fragment fields
* are set to zero by sonic_allocate.
*/
/* XXX not used by the BSD drivers
if (i & 1)
tdp->pkt_config = TDA_CONFIG_PINT;
*/
tdp->frag_count = 0;
tdp->frag[0].frag_link = LSW(tdp + 1);
tdp->link_pad = LSW(tdp + 1) | TDA_LINK_EOL;
tdp->linkp = &((tdp + 1)->frag[0].frag_link);
tdp->next = (TransmitDescriptor_t *)(tdp + 1);
tdp++;
}
tdp--;
dp->tdaHead = tdp;
tdp->link_pad = LSW(dp->tdaTail) | TDA_LINK_EOL;
tdp->next = (TransmitDescriptor_t *)dp->tdaTail;
tdp->linkp = &dp->tdaTail->frag[0].frag_link;
/*
* Set up circular linked list in Receive Descriptor Area.
* Leaves dp->rda pointing at the `beginning' of the list.
*
* NOTE: The RDA and CDP must have the same MSW for their addresses.
*/
dp->rda = sonic_allocate(
(dp->rdaCount * sizeof(ReceiveDescriptor_t)) +
sizeof(CamDescriptor_t) );
dp->cdp = (CamDescriptorPointer_t) ((unsigned char *)dp->rda +
(dp->rdaCount * sizeof(ReceiveDescriptor_t)));
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
printf( "rda area = %p\n", dp->rda );
printf( "cdp area = %p\n", dp->cdp );
#endif
ordp = rdp = dp->rda;
for (i = 0 ; i < dp->rdaCount ; i++) {
/*
* status, byte_count, pkt_ptr0, pkt_ptr1, and seq_no are set
* to zero by sonic_allocate.
*/
rdp->link = LSW(rdp + 1);
rdp->in_use = RDA_FREE;
rdp->next = (ReceiveDescriptor_t *)(rdp + 1);
ordp = rdp;
rdp++;
}
/*
* Link the last desriptor to the 1st one and mark it as the end
* of the list.
*/
ordp->next = dp->rda;
ordp->link = LSW(dp->rda) | RDA_LINK_EOL;
dp->rdp_last = rdp;
/*
* 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);
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
printf( "rsa area = %p\n", dp->rsa );
#endif
/*
* 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;
}
dp->rea = rwp;
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
printf( "rea area = %p\n", dp->rea );
#endif
/*
* Issue a software reset.
*/
sonic_write_register( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
/*
* Set up data configuration registers.
*/
sonic_write_register( rp, SONIC_REG_DCR, SONIC_DCR );
sonic_write_register( rp, SONIC_REG_DCR2, SONIC_DC2 );
sonic_write_register( rp, SONIC_REG_CR, CR_STP | CR_RXDIS | CR_HTX );
/*
* Mask all interrupts
*/
sonic_write_register( rp, SONIC_REG_IMR, 0x3fff );
/*
* Clear outstanding interrupts.
*/
sonic_write_register( rp, SONIC_REG_ISR, 0x7FFF );
/*
* Clear the tally counters
*/
sonic_write_register( rp, SONIC_REG_CRCT, 0xFFFF );
sonic_write_register( rp, SONIC_REG_FAET, 0xFFFF );
sonic_write_register( rp, SONIC_REG_MPT, 0xFFFF );
sonic_write_register( rp, SONIC_REG_RSC, 0 );
/*
* Set the Receiver mode
*
* Enable/disable reception of broadcast packets
*/
if (broadcastFlag)
sonic_write_register( rp, SONIC_REG_RCR, RCR_BRD );
else
sonic_write_register( rp, SONIC_REG_RCR, 0 );
/*
* Set up Resource Area pointers
*/
sonic_write_register( rp, SONIC_REG_URRA, MSW(dp->rsa) );
sonic_write_register( rp, SONIC_REG_RSA, LSW(dp->rsa) );
sonic_write_register( rp, SONIC_REG_REA, LSW(dp->rea) );
sonic_write_register( rp, SONIC_REG_RRP, LSW(dp->rsa) );
sonic_write_register( rp, SONIC_REG_RWP, LSW(dp->rsa) ); /* XXX was rea */
sonic_write_register( rp, SONIC_REG_URDA, MSW(dp->rda) );
sonic_write_register( rp, SONIC_REG_CRDA, LSW(dp->rda) );
sonic_write_register( rp, SONIC_REG_UTDA, MSW(dp->tdaTail) );
sonic_write_register( rp, SONIC_REG_CTDA, LSW(dp->tdaTail) );
/*
* Set End Of Buffer Count register to the value recommended
* in Note 1 of Section 3.4.4.4 of the SONIC data sheet.
*/
sonic_write_register( rp, SONIC_REG_EOBC, RBUF_WC - 2 );
/*
* Issue the load RRA command
*/
sonic_write_register( rp, SONIC_REG_CR, CR_RRRA );
while (sonic_read_register( rp, SONIC_REG_CR ) & CR_RRRA)
continue;
/*
* Remove device reset
*/
sonic_write_register( rp, SONIC_REG_CR, 0 );
/*
* Set up the SONIC CAM with our hardware address.
*/
hwaddr = dp->iface->hwaddr;
cdp = dp->cdp;
#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
printf( "hwaddr: %2x:%2x:%2x:%2x:%2x:%2x\n",
hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
#endif
cdp->cep = 0; /* Fill first and only entry in CAM */
cdp->cap0 = hwaddr[1] << 8 | hwaddr[0];
cdp->cap1 = hwaddr[3] << 8 | hwaddr[2];
cdp->cap2 = hwaddr[5] << 8 | hwaddr[4];
cdp->ce = 0x0001; /* Enable first entry in CAM */
sonic_write_register( rp, SONIC_REG_CDC, 1 ); /* 1 entry in CDA */
sonic_write_register( rp, SONIC_REG_CDP, LSW(cdp) );
sonic_write_register( rp, SONIC_REG_CR, CR_LCAM ); /* Load the CAM */
while (sonic_read_register( rp, SONIC_REG_CR ) & CR_LCAM)
continue;
/*
* Verify that CAM was properly loaded.
*/
sonic_write_register( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
sonic_write_register( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
printf ("Loaded 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,
sonic_read_register( rp, SONIC_REG_CAP2 ),
sonic_read_register( rp, SONIC_REG_CAP1 ),
sonic_read_register( rp, SONIC_REG_CAP0 ),
sonic_read_register( rp, SONIC_REG_CE ));
#endif
sonic_write_register( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
if ((sonic_read_register( rp, SONIC_REG_CAP2 ) != cdp->cap2)
|| (sonic_read_register( rp, SONIC_REG_CAP1 ) != cdp->cap1)
|| (sonic_read_register( rp, SONIC_REG_CAP0 ) != cdp->cap0)
|| (sonic_read_register( rp, SONIC_REG_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,
sonic_read_register( rp, SONIC_REG_CAP2 ),
sonic_read_register( rp, SONIC_REG_CAP1 ),
sonic_read_register( rp, SONIC_REG_CAP0 ),
sonic_read_register( rp, SONIC_REG_CE ));
rtems_panic ("SONIC LCAM");
}
sonic_write_register(rp, SONIC_REG_CR, CR_TXP | CR_RXEN | CR_STP);
/*
* Attach SONIC interrupt handler
*/
sonic_write_register( rp, SONIC_REG_IMR, 0 );
old_handler = set_vector(sonic_interrupt_handler, dp->vector, 0);
/*
* 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;
}
/*
* zero out the control structure
*/
memset( dp, 0, sizeof(struct sonic) );
/*
* 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;
}
#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
#include <stdio.h>
char SONIC_Reg_name[64][6]= {
"CR", /* 0x00 */
"DCR", /* 0x01 */
"RCR", /* 0x02 */
"TCR", /* 0x03 */
"IMR", /* 0x04 */
"ISR", /* 0x05 */
"UTDA", /* 0x06 */
"CTDA", /* 0x07 */
"0x08", /* 0x08 */
"0x09", /* 0x09 */
"0x0A", /* 0x0A */
"0x0B", /* 0x0B */
"0x0C", /* 0x0C */
"URDA", /* 0x0D */
"CRDA", /* 0x0E */
"0x0F", /* 0x0F */
"0x10", /* 0x10 */
"0x11", /* 0x11 */
"0x12", /* 0x12 */
"EOBC", /* 0x13 */
"URRA", /* 0x14 */
"RSA", /* 0x15 */
"REA", /* 0x16 */
"RRP", /* 0x17 */
"RWP", /* 0x18 */
"0x19", /* 0x19 */
"0x1A", /* 0x1A */
"0x1B", /* 0x1B */
"0x1C", /* 0x1C */
"0x0D", /* 0x1D */
"0x1E", /* 0x1E */
"0x1F", /* 0x1F */
"0x20", /* 0x20 */
"CEP", /* 0x21 */
"CAP2", /* 0x22 */
"CAP1", /* 0x23 */
"CAP0", /* 0x24 */
"CE", /* 0x25 */
"CDP", /* 0x26 */
"CDC", /* 0x27 */
"SR", /* 0x28 */
"WT0", /* 0x29 */
"WT1", /* 0x2A */
"RSC", /* 0x2B */
"CRCT", /* 0x2C */
"FAET", /* 0x2D */
"MPT", /* 0x2E */
"MDT", /* 0x2F */
"0x30", /* 0x30 */
"0x31", /* 0x31 */
"0x32", /* 0x32 */
"0x33", /* 0x33 */
"0x34", /* 0x34 */
"0x35", /* 0x35 */
"0x36", /* 0x36 */
"0x37", /* 0x37 */
"0x38", /* 0x38 */
"0x39", /* 0x39 */
"0x3A", /* 0x3A */
"0x3B", /* 0x3B */
"0x3C", /* 0x3C */
"0x3D", /* 0x3D */
"0x3E", /* 0x3E */
"DCR2" /* 0x3F */
};
#endif
void sonic_write_register(
void *base,
unsigned32 regno,
unsigned32 value
)
{
volatile unsigned32 *p = base;
#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
printf( "%p Write 0x%04x to %s (0x%02x)\n",
&p[regno], value, SONIC_Reg_name[regno], regno );
fflush( stdout );
#endif
p[regno] = value;
}
unsigned32 sonic_read_register(
void *base,
unsigned32 regno
)
{
volatile unsigned32 *p = base;
unsigned32 value;
value = p[regno];
#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
printf( "%p Read 0x%04x from %s (0x%02x)\n",
&p[regno], value, SONIC_Reg_name[regno], regno );
fflush( stdout );
#endif
return value;
}