forked from Imagelibrary/rtems
Add and use <machine/rtems-bsd-kernel-space.h> and <machine/rtems-bsd-user-space.h> similar to the libbsd to avoid command line defines and defines scattered throught the code base. Simplify cpukit/libnetworking/Makefile.am. Update #3375.
1113 lines
30 KiB
C
1113 lines
30 KiB
C
/*
|
|
* RTEMS driver for TULIP based Ethernet Controller
|
|
*
|
|
* Copyright (C) 1999 Emmanuel Raguet. raguet@crf.canon.fr
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.org/license/LICENSE.
|
|
*
|
|
* ------------------------------------------------------------------------
|
|
* [22.05.2000,StWi/CWA] added support for the DEC/Intel 21143 chip
|
|
*
|
|
* Thanks go to Andrew Klossner who provided the vital information about the
|
|
* Intel 21143 chip. FWIW: The 21143 additions to this driver were initially
|
|
* tested with a PC386 BSP using a Kingston KNE100TX with 21143PD chip.
|
|
*
|
|
* The driver will automatically detect whether there is a 21140 or 21143
|
|
* network card in the system and activate support accordingly. It will
|
|
* look for the 21140 first. If the 21140 is not found the driver will
|
|
* look for the 21143.
|
|
*
|
|
* 2004-11-10, Joel/Richard - 21143 support works on MVME2100.
|
|
* ------------------------------------------------------------------------
|
|
*
|
|
* 2003-03-13, Greg Menke, gregory.menke@gsfc.nasa.gov
|
|
*
|
|
* Added support for up to 8 units (which is an arbitrary limit now),
|
|
* consolidating their support into a single pair of rx/tx daemons and a
|
|
* single ISR for all vectors servicing the DEC units. The driver now
|
|
* simply uses whatever INTERRUPT_LINE the card supplies, requiring it
|
|
* be configured either by the boot monitor or bspstart() hackery.
|
|
* Tested on a MCP750 PPC based system with 2 DEC21140 boards.
|
|
*
|
|
* Also fixed a few bugs related to board configuration, start and stop.
|
|
*
|
|
*/
|
|
|
|
#include <machine/rtems-bsd-kernel-space.h>
|
|
|
|
#include <rtems.h>
|
|
#include <inttypes.h>
|
|
|
|
/*
|
|
* This driver only supports architectures with the new style
|
|
* exception processing. The following checks try to keep this
|
|
* from being compiled on systems which can't support this driver.
|
|
*/
|
|
|
|
#if defined(__i386__)
|
|
#define DEC21140_SUPPORTED
|
|
#define PCI_DRAM_OFFSET 0
|
|
#endif
|
|
#if defined(__PPC__)
|
|
#define DEC21140_SUPPORTED
|
|
#endif
|
|
|
|
#include <bsp.h>
|
|
|
|
#if !defined(PCI_DRAM_OFFSET)
|
|
#undef DEC21140_SUPPORTED
|
|
#endif
|
|
|
|
#if defined(DEC21140_SUPPORTED)
|
|
#include <rtems/pci.h>
|
|
|
|
#if defined(__PPC__)
|
|
#include <libcpu/byteorder.h>
|
|
#include <libcpu/io.h>
|
|
#endif
|
|
|
|
#if defined(__i386__)
|
|
#include <libcpu/byteorder.h>
|
|
#include <libcpu/page.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <rtems/error.h>
|
|
#include <rtems/bspIo.h>
|
|
#include <rtems/rtems_bsdnet.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
|
|
#include <bsp/irq.h>
|
|
|
|
#ifdef malloc
|
|
#undef malloc
|
|
#endif
|
|
#ifdef free
|
|
#undef free
|
|
#endif
|
|
|
|
#define DEC_DEBUG
|
|
|
|
/* note: the 21143 isn't really a DEC, it's an Intel chip */
|
|
#define PCI_VENDOR_ID_DEC 0x1011
|
|
#define PCI_DEVICE_ID_DEC_21140 0x0009
|
|
#define PCI_DEVICE_ID_DEC_21143 0x0019
|
|
|
|
#define DRIVER_PREFIX "dc"
|
|
|
|
#define IO_MASK 0x3
|
|
#define MEM_MASK 0xF
|
|
|
|
/* command and status registers, 32-bit access, only if IO-ACCESS */
|
|
#define ioCSR0 0x00 /* bus mode register */
|
|
#define ioCSR1 0x08 /* transmit poll demand */
|
|
#define ioCSR2 0x10 /* receive poll demand */
|
|
#define ioCSR3 0x18 /* receive list base address */
|
|
#define ioCSR4 0x20 /* transmit list base address */
|
|
#define ioCSR5 0x28 /* status register */
|
|
#define ioCSR6 0x30 /* operation mode register */
|
|
#define ioCSR7 0x38 /* interrupt mask register */
|
|
#define ioCSR8 0x40 /* missed frame counter */
|
|
#define ioCSR9 0x48 /* Ethernet ROM register */
|
|
#define ioCSR10 0x50 /* reserved */
|
|
#define ioCSR11 0x58 /* full-duplex register */
|
|
#define ioCSR12 0x60 /* SIA status register */
|
|
#define ioCSR13 0x68
|
|
#define ioCSR14 0x70
|
|
#define ioCSR15 0x78 /* SIA general register */
|
|
|
|
/* command and status registers, 32-bit access, only if MEMORY-ACCESS */
|
|
#define memCSR0 0x00 /* bus mode register */
|
|
#define memCSR1 0x02 /* transmit poll demand */
|
|
#define memCSR2 0x04 /* receive poll demand */
|
|
#define memCSR3 0x06 /* receive list base address */
|
|
#define memCSR4 0x08 /* transmit list base address */
|
|
#define memCSR5 0x0A /* status register */
|
|
#define memCSR6 0x0C /* operation mode register */
|
|
#define memCSR7 0x0E /* interrupt mask register */
|
|
#define memCSR8 0x10 /* missed frame counter */
|
|
#define memCSR9 0x12 /* Ethernet ROM register */
|
|
#define memCSR10 0x14 /* reserved */
|
|
#define memCSR11 0x16 /* full-duplex register */
|
|
#define memCSR12 0x18 /* SIA status register */
|
|
#define memCSR13 0x1A
|
|
#define memCSR14 0x1C
|
|
#define memCSR15 0x1E /* SIA general register */
|
|
|
|
#define DEC_REGISTER_SIZE 0x100 /* to reserve virtual memory */
|
|
|
|
|
|
|
|
|
|
#define RESET_CHIP 0x00000001
|
|
#if defined(__PPC__)
|
|
#define CSR0_MODE 0x0030e002 /* 01b08000 */
|
|
#else
|
|
#define CSR0_MODE 0x0020e002 /* 01b08000 */
|
|
#endif
|
|
#define ROM_ADDRESS 0x00004800
|
|
#define CSR6_INIT 0x022cc000 /* 022c0000 020c0000 */
|
|
#define CSR6_TX 0x00002000
|
|
#define CSR6_TXRX 0x00002002
|
|
#define IT_SETUP 0x000100c0 /* 000100e0 */
|
|
#define CLEAR_IT 0xFFFFFFFF
|
|
#define NO_IT 0x00000000
|
|
|
|
/* message descriptor entry */
|
|
struct MD {
|
|
/* used by hardware */
|
|
volatile uint32_t status;
|
|
volatile uint32_t counts;
|
|
volatile uint32_t buf1, buf2;
|
|
/* used by software */
|
|
volatile struct mbuf *m;
|
|
volatile struct MD *next;
|
|
} __attribute__ ((packed));
|
|
|
|
/*
|
|
** These buffers allocated for each unit, so ensure
|
|
**
|
|
** rtems_bsdnet_config.mbuf_bytecount
|
|
** rtems_bsdnet_config.mbuf_cluster_bytecount
|
|
**
|
|
** are adequately sized to provide enough clusters and mbufs for all the
|
|
** units. The default bsdnet configuration is sufficient for one dec
|
|
** unit, but will be nearing exhaustion with 2 or more. Although a
|
|
** little expensive in memory, the following configuration should
|
|
** eliminate all mbuf/cluster issues;
|
|
**
|
|
** rtems_bsdnet_config.mbuf_bytecount = 128*1024;
|
|
** rtems_bsdnet_config.mbuf_cluster_bytecount = 256*1024;
|
|
*/
|
|
|
|
#define NRXBUFS 16 /* number of receive buffers */
|
|
#define NTXBUFS 16 /* number of transmit buffers */
|
|
|
|
/*
|
|
* Number of DEC boards supported by this driver
|
|
*/
|
|
#define NDECDRIVER 8
|
|
|
|
/*
|
|
* Receive buffer size -- Allow for a full ethernet packet including CRC
|
|
*/
|
|
#define RBUF_SIZE 1536
|
|
|
|
#define ET_MINLEN 60 /* minimum message length */
|
|
|
|
/*
|
|
** Events, one per unit. The event is sent to the rx task from the isr
|
|
** or from the stack to the tx task whenever a unit needs service. The
|
|
** rx/tx tasks identify the requesting unit(s) by their particular
|
|
** events so only requesting units are serviced.
|
|
*/
|
|
|
|
static rtems_event_set unit_signals[NDECDRIVER]= { RTEMS_EVENT_1,
|
|
RTEMS_EVENT_2,
|
|
RTEMS_EVENT_3,
|
|
RTEMS_EVENT_4,
|
|
RTEMS_EVENT_5,
|
|
RTEMS_EVENT_6,
|
|
RTEMS_EVENT_7,
|
|
RTEMS_EVENT_8 };
|
|
|
|
#if defined(__PPC__)
|
|
#define phys_to_bus(address) ((unsigned int)((address)) + PCI_DRAM_OFFSET)
|
|
#define bus_to_phys(address) ((unsigned int)((address)) - PCI_DRAM_OFFSET)
|
|
#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PPC_CACHE_ALIGNMENT
|
|
#else
|
|
extern void Wait_X_ms( unsigned int timeToWait );
|
|
#define phys_to_bus(address) ((unsigned int) ((address)))
|
|
#define bus_to_phys(address) ((unsigned int) ((address)))
|
|
#define rtems_bsp_delay_in_bus_cycles(cycle) Wait_X_ms( cycle/100 )
|
|
#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PG_SIZE
|
|
#endif
|
|
|
|
#if (MCLBYTES < RBUF_SIZE)
|
|
# error "Driver must have MCLBYTES > RBUF_SIZE"
|
|
#endif
|
|
|
|
/*
|
|
* Per-device data
|
|
*/
|
|
struct dec21140_softc {
|
|
|
|
struct arpcom arpcom;
|
|
|
|
rtems_irq_connect_data irqInfo;
|
|
rtems_event_set ioevent;
|
|
|
|
int numRxbuffers, numTxbuffers;
|
|
|
|
volatile struct MD *MDbase;
|
|
volatile struct MD *nextRxMD;
|
|
volatile unsigned char *bufferBase;
|
|
int acceptBroadcast;
|
|
|
|
volatile struct MD *TxMD;
|
|
volatile struct MD *SentTxMD;
|
|
int PendingTxCount;
|
|
int TxSuspended;
|
|
|
|
unsigned int port;
|
|
volatile uint32_t *base;
|
|
|
|
/*
|
|
* Statistics
|
|
*/
|
|
unsigned long rxInterrupts;
|
|
unsigned long rxNotFirst;
|
|
unsigned long rxNotLast;
|
|
unsigned long rxGiant;
|
|
unsigned long rxNonOctet;
|
|
unsigned long rxRunt;
|
|
unsigned long rxBadCRC;
|
|
unsigned long rxOverrun;
|
|
unsigned long rxCollision;
|
|
|
|
unsigned long txInterrupts;
|
|
unsigned long txDeferred;
|
|
unsigned long txHeartbeat;
|
|
unsigned long txLateCollision;
|
|
unsigned long txRetryLimit;
|
|
unsigned long txUnderrun;
|
|
unsigned long txLostCarrier;
|
|
unsigned long txRawWait;
|
|
};
|
|
|
|
static struct dec21140_softc dec21140_softc[NDECDRIVER];
|
|
static rtems_id rxDaemonTid;
|
|
static rtems_id txDaemonTid;
|
|
|
|
void dec21140_txDaemon (void *arg);
|
|
|
|
/*
|
|
* This routine reads a word (16 bits) from the serial EEPROM.
|
|
*/
|
|
/* EEPROM_Ctrl bits. */
|
|
#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
|
|
#define EE_CS 0x01 /* EEPROM chip select. */
|
|
#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
|
|
#define EE_WRITE_0 0x01
|
|
#define EE_WRITE_1 0x05
|
|
#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
|
|
#define EE_ENB (0x4800 | EE_CS)
|
|
|
|
/* The EEPROM commands include the alway-set leading bit. */
|
|
#define EE_WRITE_CMD (5 << 6)
|
|
#define EE_READ_CMD (6 << 6)
|
|
#define EE_ERASE_CMD (7 << 6)
|
|
|
|
static int eeget16(volatile uint32_t *ioaddr, int location)
|
|
{
|
|
int i;
|
|
unsigned short retval = 0;
|
|
int read_cmd = location | EE_READ_CMD;
|
|
|
|
st_le32(ioaddr, EE_ENB & ~EE_CS);
|
|
st_le32(ioaddr, EE_ENB);
|
|
|
|
/* Shift the read command bits out. */
|
|
for (i = 10; i >= 0; i--) {
|
|
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
|
|
st_le32(ioaddr, EE_ENB | dataval);
|
|
rtems_bsp_delay_in_bus_cycles(200);
|
|
st_le32(ioaddr, EE_ENB | dataval | EE_SHIFT_CLK);
|
|
rtems_bsp_delay_in_bus_cycles(200);
|
|
st_le32(ioaddr, EE_ENB | dataval); /* Finish EEPROM a clock tick. */
|
|
rtems_bsp_delay_in_bus_cycles(200);
|
|
}
|
|
st_le32(ioaddr, EE_ENB);
|
|
|
|
for (i = 16; i > 0; i--) {
|
|
st_le32(ioaddr, EE_ENB | EE_SHIFT_CLK);
|
|
rtems_bsp_delay_in_bus_cycles(200);
|
|
retval = (retval << 1) | ((ld_le32(ioaddr) & EE_DATA_READ) ? 1 : 0);
|
|
st_le32(ioaddr, EE_ENB);
|
|
rtems_bsp_delay_in_bus_cycles(200);
|
|
}
|
|
|
|
/* Terminate the EEPROM access. */
|
|
st_le32(ioaddr, EE_ENB & ~EE_CS);
|
|
return ( ((retval<<8)&0xff00) | ((retval>>8)&0xff) );
|
|
}
|
|
|
|
static void no_op(const rtems_irq_connect_data* irq)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* DEC21140 interrupt handler
|
|
*/
|
|
static rtems_isr
|
|
dec21140Enet_interrupt_handler ( struct dec21140_softc *sc )
|
|
{
|
|
volatile uint32_t *tbase;
|
|
uint32_t status;
|
|
|
|
tbase = (uint32_t*)(sc->base);
|
|
|
|
/*
|
|
* Read status
|
|
*/
|
|
status = ld_le32(tbase+memCSR5);
|
|
st_le32((tbase+memCSR5), status);
|
|
|
|
/*
|
|
* Frame received?
|
|
*/
|
|
if( status & 0x000000c0 )
|
|
{
|
|
sc->rxInterrupts++;
|
|
rtems_bsdnet_event_send(rxDaemonTid, sc->ioevent);
|
|
}
|
|
}
|
|
|
|
static rtems_isr
|
|
dec21140Enet_interrupt_handler_entry(void)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
** Check all the initialized dec units for interrupt service
|
|
*/
|
|
|
|
for(i=0; i< NDECDRIVER; i++ )
|
|
{
|
|
if( dec21140_softc[i].base )
|
|
dec21140Enet_interrupt_handler( &dec21140_softc[i] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the ethernet hardware
|
|
*/
|
|
static void
|
|
dec21140Enet_initialize_hardware (struct dec21140_softc *sc)
|
|
{
|
|
int i,st;
|
|
volatile uint32_t *tbase;
|
|
volatile unsigned char *cp, *setup_frm, *eaddrs;
|
|
volatile unsigned char *buffer;
|
|
volatile struct MD *rmd;
|
|
|
|
|
|
#ifdef DEC_DEBUG
|
|
printk("dec2114x : %02x:%02x:%02x:%02x:%02x:%02x name '%s%d', io %x, mem %x, int %d\n",
|
|
sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1],
|
|
sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3],
|
|
sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5],
|
|
sc->arpcom.ac_if.if_name, sc->arpcom.ac_if.if_unit,
|
|
sc->port, (unsigned) sc->base, sc->irqInfo.name );
|
|
#endif
|
|
|
|
tbase = sc->base;
|
|
|
|
/*
|
|
* WARNING : First write in CSR6
|
|
* Then Reset the chip ( 1 in CSR0)
|
|
*/
|
|
st_le32( (tbase+memCSR6), CSR6_INIT);
|
|
st_le32( (tbase+memCSR0), RESET_CHIP);
|
|
rtems_bsp_delay_in_bus_cycles(200);
|
|
|
|
st_le32( (tbase+memCSR7), NO_IT);
|
|
|
|
/*
|
|
* Init CSR0
|
|
*/
|
|
st_le32( (tbase+memCSR0), CSR0_MODE);
|
|
|
|
/*
|
|
* Init RX ring
|
|
*/
|
|
cp = (volatile unsigned char *)malloc(((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD))
|
|
+ (sc->numTxbuffers*RBUF_SIZE)
|
|
+ CPU_CACHE_ALIGNMENT_FOR_BUFFER);
|
|
sc->bufferBase = cp;
|
|
cp += (CPU_CACHE_ALIGNMENT_FOR_BUFFER - (int)cp) & (CPU_CACHE_ALIGNMENT_FOR_BUFFER - 1);
|
|
#if defined(__i386__)
|
|
#ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA
|
|
if (_CPU_is_paging_enabled())
|
|
_CPU_change_memory_mapping_attribute
|
|
(NULL, cp,
|
|
((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD))
|
|
+ (sc->numTxbuffers*RBUF_SIZE),
|
|
PTE_CACHE_DISABLE | PTE_WRITABLE);
|
|
#endif
|
|
#endif
|
|
rmd = (volatile struct MD*)cp;
|
|
sc->MDbase = rmd;
|
|
sc->nextRxMD = sc->MDbase;
|
|
|
|
buffer = cp + ((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD));
|
|
st_le32( (tbase+memCSR3), (long)(phys_to_bus((long)(sc->MDbase))));
|
|
|
|
for (i=0 ; i<sc->numRxbuffers; i++)
|
|
{
|
|
struct mbuf *m;
|
|
|
|
/* allocate an mbuf for each receive descriptor */
|
|
MGETHDR (m, M_WAIT, MT_DATA);
|
|
MCLGET (m, M_WAIT);
|
|
m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
|
|
rmd->m = m;
|
|
|
|
rmd->buf2 = phys_to_bus(rmd+1);
|
|
rmd->buf1 = phys_to_bus(mtod(m, void *));
|
|
rmd->status = 0x80000000;
|
|
rmd->counts = 0xfdc00000 | (RBUF_SIZE);
|
|
rmd->next = rmd+1;
|
|
rmd++;
|
|
}
|
|
/*
|
|
* mark last RX buffer.
|
|
*/
|
|
sc->MDbase [sc->numRxbuffers-1].buf2 = 0;
|
|
sc->MDbase [sc->numRxbuffers-1].counts = 0xfec00000 | (RBUF_SIZE);
|
|
sc->MDbase [sc->numRxbuffers-1].next = sc->MDbase;
|
|
|
|
|
|
|
|
/*
|
|
* Init TX ring
|
|
*/
|
|
st_le32( (tbase+memCSR4), (long)(phys_to_bus((long)(rmd))) );
|
|
for (i=0 ; i<sc->numTxbuffers; i++){
|
|
(rmd+i)->buf2 = phys_to_bus(rmd+i+1);
|
|
(rmd+i)->buf1 = phys_to_bus(buffer + (i*RBUF_SIZE));
|
|
(rmd+i)->counts = 0x01000000;
|
|
(rmd+i)->status = 0x0;
|
|
(rmd+i)->next = rmd+i+1;
|
|
(rmd+i)->m = 0;
|
|
}
|
|
|
|
/*
|
|
* mark last TX buffer.
|
|
*/
|
|
(rmd+sc->numTxbuffers-1)->buf2 = phys_to_bus(rmd);
|
|
(rmd+sc->numTxbuffers-1)->next = rmd;
|
|
|
|
|
|
/*
|
|
* Build setup frame
|
|
*/
|
|
setup_frm = (volatile unsigned char *)(bus_to_phys(rmd->buf1));
|
|
eaddrs = (unsigned char *)(sc->arpcom.ac_enaddr);
|
|
/* Fill the buffer with our physical address. */
|
|
for (i = 1; i < 16; i++) {
|
|
*setup_frm++ = eaddrs[0];
|
|
*setup_frm++ = eaddrs[1];
|
|
*setup_frm++ = eaddrs[0];
|
|
*setup_frm++ = eaddrs[1];
|
|
*setup_frm++ = eaddrs[2];
|
|
*setup_frm++ = eaddrs[3];
|
|
*setup_frm++ = eaddrs[2];
|
|
*setup_frm++ = eaddrs[3];
|
|
*setup_frm++ = eaddrs[4];
|
|
*setup_frm++ = eaddrs[5];
|
|
*setup_frm++ = eaddrs[4];
|
|
*setup_frm++ = eaddrs[5];
|
|
}
|
|
|
|
/* Add the broadcast address when doing perfect filtering */
|
|
memset((void*) setup_frm, 0xff, 12);
|
|
rmd->counts = 0x09000000 | 192 ;
|
|
rmd->status = 0x80000000;
|
|
st_le32( (tbase+memCSR6), CSR6_INIT | CSR6_TX);
|
|
st_le32( (tbase+memCSR1), 1);
|
|
|
|
while (rmd->status != 0x7fffffff);
|
|
rmd->counts = 0x01000000;
|
|
|
|
sc->TxMD = rmd+1;
|
|
|
|
sc->irqInfo.hdl = (rtems_irq_hdl)dec21140Enet_interrupt_handler_entry;
|
|
sc->irqInfo.on = no_op;
|
|
sc->irqInfo.off = no_op;
|
|
sc->irqInfo.isOn = NULL;
|
|
|
|
#ifdef DEC_DEBUG
|
|
printk( "dec2114x: Installing IRQ %d\n", sc->irqInfo.name );
|
|
#endif
|
|
#ifdef BSP_SHARED_HANDLER_SUPPORT
|
|
st = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );
|
|
#else
|
|
st = BSP_install_rtems_irq_handler( &sc->irqInfo );
|
|
#endif
|
|
|
|
if (!st)
|
|
rtems_panic ("dec2114x : Interrupt name %d already in use\n", sc->irqInfo.name );
|
|
}
|
|
|
|
static void
|
|
dec21140_rxDaemon (void *arg)
|
|
{
|
|
volatile struct MD *rmd;
|
|
struct dec21140_softc *sc;
|
|
struct ifnet *ifp;
|
|
struct ether_header *eh;
|
|
struct mbuf *m;
|
|
unsigned int i,len;
|
|
rtems_event_set events;
|
|
|
|
for (;;)
|
|
{
|
|
|
|
rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
|
|
RTEMS_WAIT|RTEMS_EVENT_ANY,
|
|
RTEMS_NO_TIMEOUT,
|
|
&events);
|
|
|
|
for(i=0; i< NDECDRIVER; i++ )
|
|
{
|
|
sc = &dec21140_softc[i];
|
|
if( sc->base )
|
|
{
|
|
if( events & sc->ioevent )
|
|
{
|
|
ifp = &sc->arpcom.ac_if;
|
|
rmd = sc->nextRxMD;
|
|
|
|
/*
|
|
** Read off all the packets we've received on this unit
|
|
*/
|
|
while((rmd->status & 0x80000000) == 0)
|
|
{
|
|
/* printk("unit %i rx\n", ifp->if_unit ); */
|
|
|
|
/* pass on the packet in the mbuf */
|
|
len = (rmd->status >> 16) & 0x7ff;
|
|
m = (struct mbuf *)(rmd->m);
|
|
m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
|
|
eh = mtod (m, struct ether_header *);
|
|
m->m_data += sizeof(struct ether_header);
|
|
ether_input (ifp, eh, m);
|
|
|
|
/* get a new mbuf for the 21140 */
|
|
MGETHDR (m, M_WAIT, MT_DATA);
|
|
MCLGET (m, M_WAIT);
|
|
m->m_pkthdr.rcvif = ifp;
|
|
rmd->m = m;
|
|
rmd->buf1 = phys_to_bus(mtod(m, void *));
|
|
|
|
/* mark the descriptor as ready to receive */
|
|
rmd->status = 0x80000000;
|
|
|
|
rmd=rmd->next;
|
|
}
|
|
|
|
sc->nextRxMD = rmd;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
sendpacket (struct ifnet *ifp, struct mbuf *m)
|
|
{
|
|
struct dec21140_softc *dp = ifp->if_softc;
|
|
volatile struct MD *tmd;
|
|
volatile unsigned char *temp;
|
|
struct mbuf *n;
|
|
unsigned int len;
|
|
volatile uint32_t *tbase;
|
|
|
|
tbase = dp->base;
|
|
/*
|
|
* Waiting for Transmitter ready
|
|
*/
|
|
|
|
tmd = dp->TxMD;
|
|
n = m;
|
|
|
|
while ((tmd->status & 0x80000000) != 0)
|
|
{
|
|
tmd=tmd->next;
|
|
}
|
|
|
|
len = 0;
|
|
temp = (volatile unsigned char *)(bus_to_phys(tmd->buf1));
|
|
|
|
for (;;)
|
|
{
|
|
len += m->m_len;
|
|
memcpy((void*) temp, (char *)m->m_data, m->m_len);
|
|
temp += m->m_len ;
|
|
if ((m = m->m_next) == NULL)
|
|
break;
|
|
}
|
|
|
|
if (len < ET_MINLEN) len = ET_MINLEN;
|
|
tmd->counts = 0xe1000000 | (len & 0x7ff);
|
|
tmd->status = 0x80000000;
|
|
|
|
st_le32( (tbase+memCSR1), 0x1);
|
|
|
|
m_freem(n);
|
|
|
|
dp->TxMD = tmd->next;
|
|
}
|
|
|
|
/*
|
|
* Driver transmit daemon
|
|
*/
|
|
void
|
|
dec21140_txDaemon (void *arg)
|
|
{
|
|
struct dec21140_softc *sc;
|
|
struct ifnet *ifp;
|
|
struct mbuf *m;
|
|
int i;
|
|
rtems_event_set events;
|
|
|
|
for (;;)
|
|
{
|
|
/*
|
|
* Wait for packets bound for any of the dec units
|
|
*/
|
|
rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
|
|
RTEMS_EVENT_ANY | RTEMS_WAIT,
|
|
RTEMS_NO_TIMEOUT, &events);
|
|
|
|
for(i=0; i< NDECDRIVER; i++ )
|
|
{
|
|
sc = &dec21140_softc[i];
|
|
if( sc->base )
|
|
{
|
|
if( events & sc->ioevent )
|
|
{
|
|
ifp = &sc->arpcom.ac_if;
|
|
|
|
/*
|
|
* Send packets till queue is empty
|
|
*/
|
|
for(;;)
|
|
{
|
|
IF_DEQUEUE(&ifp->if_snd, m);
|
|
if( !m ) break;
|
|
/* printk("unit %i tx\n", ifp->if_unit ); */
|
|
sendpacket (ifp, m);
|
|
}
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
dec21140_start (struct ifnet *ifp)
|
|
{
|
|
struct dec21140_softc *sc = ifp->if_softc;
|
|
rtems_bsdnet_event_send( txDaemonTid, sc->ioevent );
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
}
|
|
|
|
/*
|
|
* Initialize and start the device
|
|
*/
|
|
static void
|
|
dec21140_init (void *arg)
|
|
{
|
|
struct dec21140_softc *sc = arg;
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
volatile uint32_t *tbase;
|
|
|
|
/*
|
|
* Set up DEC21140 hardware if its not already been done
|
|
*/
|
|
if( !sc->irqInfo.hdl )
|
|
{
|
|
dec21140Enet_initialize_hardware (sc);
|
|
}
|
|
|
|
/*
|
|
* Enable RX and TX
|
|
*/
|
|
tbase = sc->base;
|
|
st_le32( (tbase+memCSR5), IT_SETUP);
|
|
st_le32( (tbase+memCSR7), IT_SETUP);
|
|
st_le32( (tbase+memCSR6), CSR6_INIT | CSR6_TXRX);
|
|
|
|
/*
|
|
* Tell the world that we're running.
|
|
*/
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
}
|
|
|
|
/*
|
|
* Stop the device
|
|
*/
|
|
static void
|
|
dec21140_stop (struct dec21140_softc *sc)
|
|
{
|
|
volatile uint32_t *tbase;
|
|
struct ifnet *ifp = &sc->arpcom.ac_if;
|
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
|
|
/*
|
|
* Stop the transmitter
|
|
*/
|
|
tbase = sc->base;
|
|
st_le32( (tbase+memCSR7), NO_IT);
|
|
st_le32( (tbase+memCSR6), CSR6_INIT);
|
|
|
|
/* free((void*)sc->bufferBase); */
|
|
}
|
|
|
|
/*
|
|
* Show interface statistics
|
|
*/
|
|
static void
|
|
dec21140_stats (struct dec21140_softc *sc)
|
|
{
|
|
printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
|
|
printf (" Not First:%-8lu", sc->rxNotFirst);
|
|
printf (" Not Last:%-8lu\n", sc->rxNotLast);
|
|
printf (" Giant:%-8lu", sc->rxGiant);
|
|
printf (" Runt:%-8lu", sc->rxRunt);
|
|
printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
|
|
printf (" Bad CRC:%-8lu", sc->rxBadCRC);
|
|
printf (" Overrun:%-8lu", sc->rxOverrun);
|
|
printf (" Collision:%-8lu\n", sc->rxCollision);
|
|
|
|
printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
|
|
printf (" Deferred:%-8lu", sc->txDeferred);
|
|
printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
|
|
printf (" No Carrier:%-8lu", sc->txLostCarrier);
|
|
printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
|
|
printf (" Late Collision:%-8lu\n", sc->txLateCollision);
|
|
printf (" Underrun:%-8lu", sc->txUnderrun);
|
|
printf (" Raw output wait:%-8lu\n", sc->txRawWait);
|
|
}
|
|
|
|
/*
|
|
* Driver ioctl handler
|
|
*/
|
|
static int
|
|
dec21140_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
|
|
{
|
|
struct dec21140_softc *sc = ifp->if_softc;
|
|
int error = 0;
|
|
|
|
switch (command) {
|
|
case SIOCGIFADDR:
|
|
case SIOCSIFADDR:
|
|
ether_ioctl (ifp, command, data);
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
|
|
case IFF_RUNNING:
|
|
dec21140_stop (sc);
|
|
break;
|
|
|
|
case IFF_UP:
|
|
dec21140_init (sc);
|
|
break;
|
|
|
|
case IFF_UP | IFF_RUNNING:
|
|
dec21140_stop (sc);
|
|
dec21140_init (sc);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SIO_RTEMS_SHOW_STATS:
|
|
dec21140_stats (sc);
|
|
break;
|
|
|
|
/*
|
|
* FIXME: All sorts of multicast commands need to be added here!
|
|
*/
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/*
|
|
int iftap(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m )
|
|
{
|
|
int i;
|
|
|
|
if( ifp->if_unit == 1 ) return 0;
|
|
|
|
printf("unit %i, src ", ifp->if_unit );
|
|
for(i=0; i< ETHER_ADDR_LEN; i++) printf("%02x", (char) eh->ether_shost[i] );
|
|
printf(" dest ");
|
|
for(i=0; i< ETHER_ADDR_LEN; i++) printf("%02x", (char) eh->ether_dhost[i] );
|
|
printf("\n");
|
|
|
|
return -1;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* Attach an DEC21140 driver to the system
|
|
*/
|
|
int
|
|
rtems_dec21140_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
|
|
{
|
|
struct dec21140_softc *sc;
|
|
struct ifnet *ifp;
|
|
char *unitName;
|
|
int unitNumber;
|
|
int mtu;
|
|
unsigned char cvalue;
|
|
#if defined(__i386__)
|
|
uint32_t value;
|
|
uint8_t interrupt;
|
|
#endif
|
|
int pbus, pdev, pfun;
|
|
#if defined(__PPC__)
|
|
int tmp;
|
|
uint32_t lvalue;
|
|
#endif
|
|
|
|
/*
|
|
* Get the instance number for the board we're going to configure
|
|
* from the user.
|
|
*/
|
|
if( (unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) == -1 )
|
|
{
|
|
return 0;
|
|
}
|
|
if( strcmp(unitName, DRIVER_PREFIX) )
|
|
{
|
|
printk("dec2114x : unit name '%s' not %s\n", unitName, DRIVER_PREFIX );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find the board
|
|
*/
|
|
if ( pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21140,
|
|
unitNumber-1, &pbus, &pdev, &pfun) == -1 ) {
|
|
if ( pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21143,
|
|
unitNumber-1, &pbus, &pdev, &pfun) != -1 ) {
|
|
|
|
/* the 21143 chip must be enabled before it can be accessed */
|
|
#if defined(__i386__)
|
|
pci_write_config_dword(pbus, pdev, pfun, 0x40, 0 );
|
|
#else
|
|
pci_write_config_dword(pbus, pdev, pfun, 0x40, PCI_DEVICE_ID_DEC_21143);
|
|
#endif
|
|
|
|
} else {
|
|
printk("dec2114x : device '%s' not found on PCI bus\n", config->name );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#ifdef DEC_DEBUG
|
|
else {
|
|
printk("dec21140 : found device '%s', bus 0x%02x, dev 0x%02x, func 0x%02x\n",
|
|
config->name, pbus, pdev, pfun);
|
|
}
|
|
#endif
|
|
|
|
if ((unitNumber < 1) || (unitNumber > NDECDRIVER))
|
|
{
|
|
printk("dec2114x : unit %i is invalid, must be (1 <= n <= %d)\n",
|
|
unitNumber, NDECDRIVER);
|
|
return 0;
|
|
}
|
|
|
|
sc = &dec21140_softc[unitNumber - 1];
|
|
ifp = &sc->arpcom.ac_if;
|
|
if (ifp->if_softc != NULL)
|
|
{
|
|
printk("dec2114x : unit %i already in use.\n", unitNumber );
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** Get this unit's rx/tx event
|
|
*/
|
|
sc->ioevent = unit_signals[unitNumber-1];
|
|
|
|
/*
|
|
** Save the buffer counts
|
|
*/
|
|
sc->numRxbuffers = (config->rbuf_count) ? config->rbuf_count : NRXBUFS;
|
|
sc->numTxbuffers = (config->xbuf_count) ? config->xbuf_count : NTXBUFS;
|
|
|
|
|
|
/*
|
|
* Get card address spaces & retrieve its isr vector
|
|
*/
|
|
#if defined(__i386__)
|
|
|
|
pci_read_config_dword(pbus, pdev, pfun, 16, &value);
|
|
sc->port = value & ~IO_MASK;
|
|
|
|
pci_read_config_dword(pbus, pdev, pfun, 20, &value);
|
|
if (_CPU_is_paging_enabled())
|
|
_CPU_map_phys_address((void **) &(sc->base),
|
|
(void *)(value & ~MEM_MASK),
|
|
DEC_REGISTER_SIZE ,
|
|
PTE_CACHE_DISABLE | PTE_WRITABLE);
|
|
else
|
|
sc->base = (uint32_t *)(value & ~MEM_MASK);
|
|
|
|
pci_read_config_byte(pbus, pdev, pfun, 60, &interrupt);
|
|
cvalue = interrupt;
|
|
#endif
|
|
#if defined(__PPC__)
|
|
(void)pci_read_config_dword(pbus,
|
|
pdev,
|
|
pfun,
|
|
PCI_BASE_ADDRESS_0,
|
|
&lvalue);
|
|
|
|
sc->port = lvalue & (unsigned int)(~IO_MASK);
|
|
|
|
(void)pci_read_config_dword(pbus,
|
|
pdev,
|
|
pfun,
|
|
PCI_BASE_ADDRESS_1,
|
|
&lvalue);
|
|
|
|
tmp = (unsigned int)(lvalue & (unsigned int)(~MEM_MASK))
|
|
+ (unsigned int)PCI_MEM_BASE;
|
|
|
|
sc->base = (uint32_t*)(tmp);
|
|
|
|
pci_read_config_byte(pbus,
|
|
pdev,
|
|
pfun,
|
|
PCI_INTERRUPT_LINE,
|
|
&cvalue);
|
|
|
|
#endif
|
|
|
|
/*
|
|
** Prep the board
|
|
*/
|
|
|
|
pci_write_config_word(pbus, pdev, pfun,
|
|
PCI_COMMAND,
|
|
(uint16_t) ( PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER ) );
|
|
|
|
/*
|
|
** Store the interrupt name, we'll use it later when we initialize
|
|
** the board.
|
|
*/
|
|
memset(&sc->irqInfo,0,sizeof(rtems_irq_connect_data));
|
|
sc->irqInfo.name = cvalue;
|
|
|
|
|
|
#ifdef DEC_DEBUG
|
|
printk("dec2114x : unit %d base address %p.\n", unitNumber, sc->base);
|
|
#endif
|
|
|
|
|
|
/*
|
|
** Setup ethernet address
|
|
*/
|
|
if (config->hardware_address) {
|
|
memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
|
|
ETHER_ADDR_LEN);
|
|
}
|
|
else {
|
|
union {char c[64]; unsigned short s[32];} rombuf;
|
|
int i;
|
|
|
|
for (i=0; i<32; i++){
|
|
rombuf.s[i] = eeget16( sc->base + memCSR9, i);
|
|
}
|
|
#if defined(__i386__)
|
|
for (i=0 ; i<(ETHER_ADDR_LEN/2); i++){
|
|
sc->arpcom.ac_enaddr[2*i] = rombuf.c[20+2*i+1];
|
|
sc->arpcom.ac_enaddr[2*i+1] = rombuf.c[20+2*i];
|
|
}
|
|
#endif
|
|
#if defined(__PPC__)
|
|
memcpy (sc->arpcom.ac_enaddr, rombuf.c+20, ETHER_ADDR_LEN);
|
|
#endif
|
|
}
|
|
|
|
if (config->mtu)
|
|
mtu = config->mtu;
|
|
else
|
|
mtu = ETHERMTU;
|
|
|
|
sc->acceptBroadcast = !config->ignore_broadcast;
|
|
|
|
/*
|
|
* Set up network interface values
|
|
*/
|
|
|
|
/* ifp->if_tap = iftap; */
|
|
|
|
ifp->if_softc = sc;
|
|
ifp->if_unit = unitNumber;
|
|
ifp->if_name = unitName;
|
|
ifp->if_mtu = mtu;
|
|
ifp->if_init = dec21140_init;
|
|
ifp->if_ioctl = dec21140_ioctl;
|
|
ifp->if_start = dec21140_start;
|
|
ifp->if_output = ether_output;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
|
|
if (ifp->if_snd.ifq_maxlen == 0)
|
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
|
|
|
/*
|
|
* Attach the interface
|
|
*/
|
|
if_attach (ifp);
|
|
ether_ifattach (ifp);
|
|
|
|
#ifdef DEC_DEBUG
|
|
printk( "dec2114x : driver attached\n" );
|
|
#endif
|
|
|
|
/*
|
|
* Start driver tasks if this is the first dec unit initialized
|
|
*/
|
|
if (txDaemonTid == 0)
|
|
{
|
|
rxDaemonTid = rtems_bsdnet_newproc( "DCrx", 4096,
|
|
dec21140_rxDaemon, NULL);
|
|
|
|
txDaemonTid = rtems_bsdnet_newproc( "DCtx", 4096,
|
|
dec21140_txDaemon, NULL);
|
|
#ifdef DEC_DEBUG
|
|
printk( "dec2114x : driver tasks created\n" );
|
|
#endif
|
|
}
|
|
|
|
return 1;
|
|
};
|
|
|
|
#endif /* DEC21140_SUPPORTED */
|