forked from Imagelibrary/rtems
1550 lines
42 KiB
C
1550 lines
42 KiB
C
/*
|
|
*******************************************************************
|
|
*******************************************************************
|
|
** **
|
|
** 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_enable_interrupts( rp, (IMR_PINTEN | IMR_PTXEN | IMR_TXEREN) );
|
|
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 (sonic_read_register( rp, SONIC_REG_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 (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)
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Restart reception
|
|
*/
|
|
sonic_write_register( rp, SONIC_REG_ISR, ISR_RBAE );
|
|
sonic_write_register( rp, SONIC_REG_CR, CR_RXEN );
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|