8b97be16ab
Use definitions from this file for match_podule() rather than hardcoded values. Added packet filter support.
1299 lines
30 KiB
C
1299 lines
30 KiB
C
/* $NetBSD: if_eh.c,v 1.11 1996/10/14 23:55:01 mark Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1995 Melvin Tang-Richardson.
|
|
* All rights reserved.
|
|
*
|
|
* Essential things to do
|
|
* - Multicast support
|
|
* - Testing
|
|
* - 16-bit dma's
|
|
* - Buffer overflow handling (This must be done a certain way) [Nuts]
|
|
* - Enabling card interrupts (Need i cubed info)
|
|
*
|
|
* Other things to do
|
|
* - Pipelined transmitts
|
|
* - Dynamic buffer allocation
|
|
* - Shared RAM if I cubed give me info
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by RiscBSD.
|
|
* 4. The name of the company nor the name of the author may be used to
|
|
* endorse or promote products derived from this software without specific
|
|
* prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY RISCBSD ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL RISCBSD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* if_eh.c
|
|
*
|
|
* Ether H driver.
|
|
*/
|
|
|
|
/* Some system includes *****************************************************/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/cdefs.h>
|
|
#include <sys/time.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/param.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/device.h>
|
|
#include <machine/bootconfig.h>
|
|
|
|
/* Hardware related include chip/card/bus ***********************************/
|
|
|
|
#include <machine/io.h>
|
|
#include <machine/irqhandler.h>
|
|
#include <machine/katelib.h>
|
|
#include <arm32/podulebus/podulebus.h>
|
|
#include <arm32/podulebus/if_ehreg.h>
|
|
#include <arm32/podulebus/podules.h>
|
|
|
|
/* Includes for interfacing with the network subsystem **********************/
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/netisr.h>
|
|
#include <net/route.h>
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/if_ether.h>
|
|
#endif
|
|
|
|
#ifdef NS
|
|
#include <netns/ns.h>
|
|
#include <netns/ns_if.h>
|
|
#endif
|
|
|
|
#include "bpfilter.h"
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#include <net/bpfdesc.h>
|
|
#endif
|
|
|
|
/****************************************************************************/
|
|
/* Some useful definitions **************************************************/
|
|
/****************************************************************************/
|
|
|
|
#define TXBUF_SIZE (1522)
|
|
#define NTXBUF (1)
|
|
|
|
#define RXBUF_SIZE (256)
|
|
#define NRXBUF (26)
|
|
|
|
#define ETHER_MIN_LEN (64)
|
|
#define ETHER_MAX_LEN (1522)
|
|
|
|
#define PRINTF printf
|
|
|
|
/****************************************************************************/
|
|
/* Prototype internal data structures ***************************************/
|
|
/****************************************************************************/
|
|
|
|
struct eh_softc {
|
|
/* Kernel interface variables */
|
|
struct device sc_dev;
|
|
int sc_podule_number;
|
|
podule_t *sc_podule;
|
|
irqhandler_t sc_ih;
|
|
|
|
/* Network subsystem interface */
|
|
struct arpcom sc_arpcom;
|
|
int promisc;
|
|
|
|
/* Driver defined state variables */
|
|
int sc_flags;
|
|
|
|
/* Hardware defined state */
|
|
int sc_base;
|
|
int sc_reg;
|
|
int sc_nxtpkt;
|
|
|
|
/* Card memory map */
|
|
int sc_physstart;
|
|
int sc_physend;
|
|
int sc_tbs[NTXBUF]; /* Transmit buffer start */
|
|
int sc_rbs;
|
|
int sc_rbd;
|
|
|
|
/* Txrx parameters */
|
|
int sc_txcur; /* Fill this buffer next */
|
|
int sc_txlst; /* Buffer in use */
|
|
};
|
|
|
|
/* sc_flags */
|
|
|
|
#define MODE_MASK (3)
|
|
#define MODE_DEAD (0)
|
|
#define MODE_PIO (1)
|
|
#define MODE_SHRAM (2)
|
|
#define EH_BROKEN 16
|
|
|
|
/****************************************************************************/
|
|
/* Function and data prototypes *********************************************/
|
|
/****************************************************************************/
|
|
|
|
int ehprobe __P((struct device *parent, void *match, void *aux));
|
|
void ehattach __P((struct device *parent, struct device *self, void *aux));
|
|
void ehstart __P((struct ifnet *ifp));
|
|
int ehioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data));
|
|
void ehwatchdog __P((struct ifnet *ifp));
|
|
void eh_stop_controller __P((struct eh_softc *sc));
|
|
void eh_start_controller __P((struct eh_softc *sc));
|
|
void eh_transmit_command __P((struct eh_softc *sc));
|
|
int eh_copyin __P((struct eh_softc *sc, int src, char *dest, int len));
|
|
int eh_copyout __P((struct eh_softc *sc, char *src, int dest, int len));
|
|
void ehinit __P((struct eh_softc *sc));
|
|
int ehstop __P((struct eh_softc *sc));
|
|
int ehintr __P((void *arg));
|
|
void eh_shutdown __P((void *arg));
|
|
|
|
struct cfattach eh_ca = {
|
|
sizeof(struct eh_softc), ehprobe, ehattach
|
|
};
|
|
|
|
struct cfdriver eh_cd = {
|
|
NULL, "eh", DV_DULL, NULL, 0
|
|
};
|
|
|
|
/****************************************************************************/
|
|
/* Some useful macros *******************************************************/
|
|
/****************************************************************************/
|
|
|
|
#define SetReg(r,v) WriteWord ( sc->sc_reg+(r), (v)|((v)<<16) )
|
|
|
|
#define GetReg(r) ReadByte ( sc->sc_reg+(r) )
|
|
|
|
#define PAGE(n) SetReg ( EH_COMMAND, \
|
|
((GetReg(EH_COMMAND)&COM_MASK_PAGE)|(n<<6)) )
|
|
|
|
/* When initiating a remote dma, reset the TXP bit. This Does Not abort */
|
|
/* the transmit, but allows us overlap this remote dma */
|
|
|
|
#define REMOTE_DMA(n) SetReg ( EH_COMMAND, \
|
|
((GetReg(EH_COMMAND)&(COM_MASK_DMA))|n) )
|
|
|
|
/****************************************************************************/
|
|
/* Bus attachment code ******************************************************/
|
|
/****************************************************************************/
|
|
|
|
int
|
|
ehprobe(parent, match, aux)
|
|
struct device *parent;
|
|
void *match;
|
|
void *aux;
|
|
{
|
|
struct podule_attach_args *pa = (void *) aux;
|
|
|
|
/* Look for a network slot interface */
|
|
|
|
if (matchpodule(pa, MANUFACTURER_ICUBED, PODULE_ICUBED_ETHERH, -1) == 0)
|
|
return(0);
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
void
|
|
ehattach(parent, self, aux)
|
|
struct device *parent;
|
|
struct device *self;
|
|
void *aux;
|
|
{
|
|
struct eh_softc *sc = (void *) self;
|
|
struct podule_attach_args *pa = (void *)aux;
|
|
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
|
|
int irq;
|
|
int counter;
|
|
int temp;
|
|
|
|
/* Check a few things about the attach args */
|
|
|
|
if (pa->pa_podule_number == -1)
|
|
panic("Podule has disappeared !");
|
|
|
|
sc->sc_podule_number = pa->pa_podule_number;
|
|
sc->sc_podule = pa->pa_podule;
|
|
podules[sc->sc_podule_number].attached = 1;
|
|
|
|
/*
|
|
* MESS MESS MESS
|
|
*
|
|
* This needs a serious clean up. Alot of this code was in the probe function
|
|
* but required the softc structure. As a temporary measure until I rewrite it
|
|
* I have just bolted in the probe code here.
|
|
*/
|
|
|
|
/* Start to fill in the softc with the trivial variables */
|
|
|
|
sc->sc_flags = MODE_PIO; /* Select pio mode until I get info from i^3 */
|
|
sc->sc_base = sc->sc_podule->fast_base;
|
|
sc->sc_reg = sc->sc_base + 0x800;
|
|
|
|
/* The probe could be tidied up a little */
|
|
|
|
/* Reset the card */
|
|
SetReg(EH_RESET, 0xff);
|
|
delay(10000);
|
|
|
|
/* Explicitly abort remote DMA, mainly to set a legal state */
|
|
PAGE(0);
|
|
eh_stop_controller(sc);
|
|
SetReg(EH_COMMAND, 0x20); /* This is wrong */
|
|
|
|
SetReg(EH_ISR, 0xf); /* Clear any previous work */
|
|
|
|
temp = GetReg(EH_CONFIGA);
|
|
temp = GetReg(EH_CONFIGA);
|
|
temp &= ~0x38;
|
|
SetReg(EH_CONFIGA, temp | 3<<3);
|
|
|
|
temp = GetReg(EH_CONFIGA);
|
|
temp = GetReg(EH_CONFIGA);
|
|
|
|
#ifdef ETHERH_TP
|
|
/* Select TP */
|
|
temp = GetReg(EH_CONFIGB);
|
|
temp &= ~7; /* Enable link test and TP */
|
|
SetReg(EH_CONFIGB, temp);
|
|
#endif /* ETHERH_TP */
|
|
|
|
/* Make the thing interrupt and test it */
|
|
|
|
eh_start_controller(sc);
|
|
|
|
/* Temporary initialisation, to allow a transmit test */
|
|
|
|
SetReg(EH_PSTART, 16384 >> 8);
|
|
SetReg(EH_PSTOP, 16896 >> 8);
|
|
SetReg(EH_BNRY, 16384 >> 8);
|
|
|
|
SetReg(EH_TCR, TCR_NORMALLOOP); /* Erk!! No, put us in loopback */
|
|
SetReg(EH_DCR, DCR_WTS|DCR_LS|DCR_LAS);
|
|
SetReg(EH_RCR, 0);
|
|
|
|
/* Transmit some garbage into the loopback */
|
|
|
|
SetReg(EH_TPSR, 0x5000);
|
|
SetReg(EH_TBCR0, 0);
|
|
SetReg(EH_TBCR1, 3);
|
|
|
|
eh_transmit_command(sc);
|
|
|
|
/* The card shouldn't be this quick, so use it as a test */
|
|
|
|
if ((GetReg(EH_TSR)&TSR_DONE) != 0)
|
|
PRINTF("eh: Card responded rather quickly %02x\n", GetReg(EH_TSR));
|
|
|
|
for (counter=500; counter>0; counter--) {
|
|
if ((GetReg(EH_TSR)&TSR_DONE) != 0)
|
|
break;
|
|
|
|
delay(500);
|
|
}
|
|
|
|
if (counter == 0) {
|
|
printf(": card faulty\n");
|
|
sc->sc_flags |= EH_BROKEN;
|
|
return;
|
|
}
|
|
|
|
/* Let it sleep since we're not doing anything with it for now */
|
|
|
|
SetReg(EH_ISR, 0);
|
|
SetReg(EH_DCR, DCR_WTS|DCR_LS|DCR_LAS);
|
|
|
|
/* Test the board ram. This code will change */
|
|
|
|
/*
|
|
* Due to problems with some ether H cards the boot option ehbug
|
|
* can be used to skip this test.
|
|
* If you system hangs during the eh probe try this option.
|
|
*/
|
|
|
|
#define NLEN (0x2000)
|
|
#define NBASE (0x4000)
|
|
|
|
if (boot_args) {
|
|
char *ptr;
|
|
|
|
ptr = strstr(boot_args, "ehbug");
|
|
if (!ptr) {
|
|
char *test_data;
|
|
char *read_buffer;
|
|
MALLOC(test_data, char *, NLEN, M_TEMP, M_NOWAIT);
|
|
if (test_data == NULL)
|
|
panic("Cannot allocate temporary memory for buffer test (1)\n");
|
|
MALLOC(read_buffer, char *, NLEN, M_TEMP, M_NOWAIT);
|
|
if (read_buffer == NULL)
|
|
panic("Cannot allocate temporary memory for buffer test (1)\n");
|
|
|
|
printf("1.");
|
|
|
|
/* Fill the test_data block */
|
|
|
|
for (counter=0; counter<NLEN; counter++) {
|
|
test_data[counter] = (char)(counter&0xff);
|
|
if (test_data[counter] == 0)
|
|
test_data[counter]=0x23;
|
|
}
|
|
|
|
printf("2.");
|
|
|
|
eh_copyout(sc, test_data, NBASE, NLEN);
|
|
printf("3.");
|
|
delay(10000);
|
|
eh_copyin(sc, NBASE, read_buffer, NLEN);
|
|
printf("4.");
|
|
|
|
if (bcmp(test_data, read_buffer, NLEN))
|
|
PRINTF("Block test failed\n");
|
|
printf("5.");
|
|
FREE(test_data, M_TEMP);
|
|
FREE(read_buffer, M_TEMP);
|
|
}
|
|
}
|
|
/* This is always true for our ether h */
|
|
|
|
sc->sc_physstart = 0x4000;
|
|
sc->sc_physend = 0x6000;
|
|
|
|
/* Get out ethernet address */
|
|
|
|
/*
|
|
* The machine id is a 24 bit number so two bytes worth is
|
|
* not good enough for uniqueness.
|
|
* Do what we do for the EtherB card as this card uses the same slot
|
|
*/
|
|
|
|
sc->sc_arpcom.ac_enaddr[0] = 0x00;
|
|
sc->sc_arpcom.ac_enaddr[1] = 0x00;
|
|
sc->sc_arpcom.ac_enaddr[2] = 0xa4;
|
|
sc->sc_arpcom.ac_enaddr[3] = bootconfig.machine_id[2] + 0x10;
|
|
sc->sc_arpcom.ac_enaddr[4] = bootconfig.machine_id[1];
|
|
sc->sc_arpcom.ac_enaddr[5] = bootconfig.machine_id[0];
|
|
|
|
/* Copy the ether addr cards filter */
|
|
|
|
PAGE(1);
|
|
for (counter=0; counter<6; counter++)
|
|
SetReg(((counter+1)<<2), sc->sc_arpcom.ac_enaddr[counter]);
|
|
PAGE(0);
|
|
|
|
/* Fill in my application form to attach to the networking system */
|
|
|
|
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
|
|
ifp->if_softc = sc;
|
|
ifp->if_start = ehstart;
|
|
ifp->if_ioctl = ehioctl;
|
|
ifp->if_watchdog = ehwatchdog;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS;
|
|
|
|
/* Signed, dated then sent */
|
|
|
|
if_attach(ifp);
|
|
ether_ifattach(ifp);
|
|
|
|
/* Attach to bpf filter if it is present. */
|
|
|
|
#if NBPFILTER > 0
|
|
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
|
|
#endif
|
|
|
|
/* Not print some stuff for the attach line, starting with the address */
|
|
|
|
PRINTF(" address %s", ether_sprintf(sc->sc_arpcom.ac_enaddr));
|
|
|
|
/* Now the transfer mode we're operating in */
|
|
|
|
switch (sc->sc_flags & MODE_MASK) {
|
|
case MODE_PIO:
|
|
PRINTF(" pio mode");
|
|
break;
|
|
case MODE_SHRAM:
|
|
PRINTF(" ram mode");
|
|
break;
|
|
default:
|
|
panic("Ether H probe set an unknown mode");
|
|
}
|
|
|
|
/* Get an irq handler installed */
|
|
|
|
SetReg(EH_IMR, 0);
|
|
SetReg(EH_ISR, 0xff);
|
|
|
|
sc->sc_ih.ih_func = ehintr;
|
|
sc->sc_ih.ih_arg = sc;
|
|
sc->sc_ih.ih_level = IPL_NET;
|
|
sc->sc_ih.ih_name = "net: eh";
|
|
|
|
irq = (sc->sc_podule_number>=MAX_PODULES) ? IRQ_NETSLOT : IRQ_PODULE;
|
|
|
|
if (irq_claim(irq, &sc->sc_ih))
|
|
panic("Cannot install IRQ handler");
|
|
|
|
/* This loopbacks the card on reset, and stops riscos locking */
|
|
|
|
if (shutdownhook_establish(eh_shutdown, (void *)sc)==NULL)
|
|
panic("Cannot install shutdown handler");
|
|
|
|
/* Reprogram CONFIGA. I dont think we should do this any more */
|
|
|
|
temp = GetReg(EH_CONFIGA);
|
|
temp = GetReg(EH_CONFIGA);
|
|
temp &= ~0x38;
|
|
SetReg(EH_CONFIGA, temp | (3<<3));
|
|
|
|
/* And that's it */
|
|
|
|
PRINTF("\n");
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* Net subsystem interface **************************************************/
|
|
/****************************************************************************/
|
|
|
|
#define NEXT_TXBUF (((sc->sc_txcur+1)>=NTXBUF) ? 0 : (sc->sc_txcur+1))
|
|
|
|
void
|
|
ehstart(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct eh_softc *sc = ifp->if_softc;
|
|
int cur;
|
|
struct mbuf *m0, *m;
|
|
int nextbuf;
|
|
char *buffer;
|
|
char txbuf[TXBUF_SIZE];
|
|
int len = 0;
|
|
|
|
PAGE(0);
|
|
|
|
if ((ifp->if_flags & IFF_OACTIVE) != 0)
|
|
return;
|
|
|
|
/* This is for pipelined transmit. At present just use 1 buffer */
|
|
|
|
for (;;) {
|
|
|
|
/* The #ifdef PIPELINE_TRANSMIT code is total screwed */
|
|
|
|
#ifdef PIPELINE_TRANSMIT
|
|
nextbuf = NEXT_TXBUF;
|
|
if ( nextbuf == sc->sc_txlst )
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
break;
|
|
}
|
|
#else
|
|
nextbuf = 0;
|
|
#endif
|
|
|
|
/*** Speed up - In future, copy the bufs direct to the card ***/
|
|
|
|
/* Copy a frame out of the mbufs */
|
|
|
|
IF_DEQUEUE(&sc->sc_arpcom.ac_if.if_snd, m);
|
|
if ( !m )
|
|
break;
|
|
|
|
/* Give the packet to the bpf, if any. */
|
|
#if NBPFILTER > 0
|
|
if (sc->sc_arpcom.ac_if.if_bpf)
|
|
bpf_mtap(sc->sc_arpcom.ac_if.if_bpf, m);
|
|
#endif
|
|
|
|
buffer = txbuf;
|
|
|
|
len = 0;
|
|
for ( m0=m; m && (len + m->m_len) < TXBUF_SIZE; m=m->m_next ) {
|
|
bcopy(mtod(m,caddr_t), buffer, m->m_len);
|
|
buffer+=m->m_len;
|
|
len+=m->m_len;
|
|
}
|
|
|
|
m_freem(m0);
|
|
len = max ( len, ETHER_MIN_LEN );
|
|
|
|
/* And blat it to the card */
|
|
|
|
if (eh_copyout ( sc, txbuf, sc->sc_tbs[nextbuf], len ) == 0) {
|
|
printf("ehstart: failed to load packet\n");
|
|
continue;
|
|
}
|
|
|
|
#ifdef PIPELINE_TRANSMIT
|
|
sc->sc_txlst = nextbuf;
|
|
#endif
|
|
PAGE(0);
|
|
|
|
SetReg ( EH_TPSR, sc->sc_tbs[nextbuf]>>8 );
|
|
SetReg ( EH_TBCR0, len&0xff );
|
|
SetReg ( EH_TBCR1, (len>>8) & 0xff );
|
|
|
|
cur = GetReg(EH_COMMAND);
|
|
|
|
eh_transmit_command (sc );
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
ifp->if_timer = 30;
|
|
|
|
#ifndef PIPELINE_TRANSMIT
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#define IZSET(a,b) ((a->if_flags&b)!=0)
|
|
#define IZCLR(a,b) ((a->if_flags&b)==0)
|
|
#define DOSET(a,b) (a->if_flags|=b)
|
|
#define DOCLR(a,b) (a->if_flags&=~b)
|
|
|
|
int
|
|
ehioctl(ifp, cmd, data)
|
|
struct ifnet *ifp;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
{
|
|
struct eh_softc *sc = ifp->if_softc;
|
|
struct ifaddr *ifa = (struct ifaddr *)data;
|
|
int s = splimp();
|
|
int error = 0;
|
|
|
|
switch (cmd) {
|
|
case SIOCSIFADDR:
|
|
ifp->if_flags |= IFF_UP;
|
|
switch ( ifa->ifa_addr->sa_family ) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
ehinit(sc);
|
|
arp_ifinit(&sc->sc_arpcom, ifa);
|
|
break;
|
|
#endif
|
|
default:
|
|
ehinit(sc);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
sc->promisc = ifp->if_flags & ( IFF_PROMISC | IFF_ALLMULTI );
|
|
|
|
if ( IZCLR(ifp,IFF_UP) && IZSET(ifp,IFF_RUNNING) ) {
|
|
/* Interface marked down, but still running */
|
|
ehstop(sc);
|
|
DOCLR(ifp,IFF_RUNNING);
|
|
} else if ( IZSET(ifp,IFF_UP) && IZCLR(ifp,IFF_RUNNING) ) {
|
|
/* Just marked up, not running yet */
|
|
ehinit(sc);
|
|
} else {
|
|
/* Reset to invoke changes in other registers */
|
|
ehstop(sc);
|
|
ehinit(sc);
|
|
}
|
|
|
|
default:
|
|
error = EINVAL;
|
|
}
|
|
|
|
splx(s);
|
|
return error;
|
|
}
|
|
|
|
void
|
|
ehwatchdog(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct eh_softc *sc = ifp->if_softc;
|
|
|
|
log (LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname );
|
|
sc->sc_arpcom.ac_if.if_oerrors++;
|
|
|
|
ehinit (sc);
|
|
}
|
|
|
|
/*
|
|
* void eh_stop_controller ( struct eh_softc *sc )
|
|
*
|
|
* Software reset command, takes the controller offline, no packets will be
|
|
* received or transmitted. Any reception or transmission inprogres will
|
|
* continue to completion before entering the reset state.
|
|
*/
|
|
|
|
void
|
|
eh_stop_controller(sc)
|
|
struct eh_softc *sc;
|
|
{
|
|
SetReg ( EH_COMMAND, ((GetReg(EH_COMMAND)&0xfc)|1) );
|
|
delay ( 10000 ); /* Change this to wait on the ISR bit */
|
|
}
|
|
|
|
/*
|
|
* void eh_start_controller ( struct eh_softc *sc )
|
|
*
|
|
* Activte the NIC core after either power up or when the NIC Core has
|
|
* been placed in a reset mode by eh_stop_controller or an error.
|
|
*/
|
|
|
|
void
|
|
eh_start_controller(sc)
|
|
struct eh_softc *sc;
|
|
{
|
|
int temp = GetReg(EH_COMMAND);
|
|
SetReg ( EH_COMMAND, ( (temp&0xfc)|2) );
|
|
}
|
|
|
|
void
|
|
eh_transmit_command(sc)
|
|
struct eh_softc *sc;
|
|
{
|
|
/* Explicit set here. There is no other reasonable combination */
|
|
SetReg ( EH_COMMAND, COM_ABORT|COM_STA|COM_TXP);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* Remote DMA handling ******************************************************/
|
|
/****************************************************************************/
|
|
|
|
/* Remote DMA handling is going to undergo a major overhaul real soon now */
|
|
|
|
/* We musn't fail */
|
|
|
|
inline void
|
|
eh_ensure_dma_ok(sc)
|
|
struct eh_softc *sc;
|
|
{
|
|
register int isr = GetReg ( EH_ISR );
|
|
register int status = GetReg ( EH_COMMAND );
|
|
int dummy;
|
|
|
|
if ((status&COM_ABORT)==0) {
|
|
SetReg ( EH_COMMAND, status|COM_ABORT );
|
|
while ((isr&ISR_RDC)==0) {
|
|
dummy = GetReg ( EH_DATA_PORT );
|
|
delay(50);
|
|
}
|
|
}
|
|
|
|
/* Reset the COM_TXP bit. This does not abort transmission */
|
|
|
|
SetReg(EH_COMMAND, 0x22);
|
|
}
|
|
|
|
inline int
|
|
eh_ensure_dma_completed(sc, type)
|
|
struct eh_softc *sc;
|
|
int type;
|
|
{
|
|
register int status = GetReg ( EH_COMMAND );
|
|
|
|
if ( (status&COM_ABORT)==0 ) {
|
|
int dummy=0;
|
|
printf ( "EHDEBUG: Remote %d wasn't finished\n", type );
|
|
SetReg ( EH_COMMAND, status|COM_ABORT );
|
|
switch ( type ) {
|
|
case COM_READ:
|
|
dummy = GetReg ( EH_DATA_PORT );
|
|
break;
|
|
case COM_WRITE:
|
|
SetReg ( EH_DATA_PORT, dummy );
|
|
break;
|
|
}
|
|
ehinit (sc);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Do an eh_copyin, but take into consideration ring wrap */
|
|
|
|
int
|
|
eh_copyring(sc, src, dest, len)
|
|
struct eh_softc *sc;
|
|
int src;
|
|
char *dest;
|
|
int len;
|
|
{
|
|
if ( (src+len)>sc->sc_rbd ) {
|
|
register int in = sc->sc_rbd - src;
|
|
if ( eh_copyin ( sc, src, dest, in )==0 ) {
|
|
printf ( "copyring failed pass 1\n" );
|
|
return 0;
|
|
}
|
|
if ( eh_copyin ( sc, sc->sc_rbs, dest+in, len-in )==0 ) {
|
|
printf ( "copyring failed pass 2\n" );
|
|
return 0;
|
|
}
|
|
} else {
|
|
if ( eh_copyin ( sc, src, dest, len )==0 ) {
|
|
printf ( "copyring faild on 1 pass copy\n" );
|
|
return 0;
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int
|
|
eh_copyin(sc, src, dest, len)
|
|
struct eh_softc *sc;
|
|
int src;
|
|
char *dest;
|
|
int len;
|
|
{
|
|
int counter;
|
|
int s;
|
|
short *dst = (short *)dest;
|
|
|
|
s = splimp(); /* Erm, maybe not needed now */
|
|
|
|
if (len & 1)
|
|
len++;
|
|
if (src & 1) {
|
|
printf ( "EHDEBUG: Copying from odd address\n" );
|
|
src++;
|
|
}
|
|
|
|
/* Remote DMA to the DP83905 must be done with care. If we dont */
|
|
/* do it exactly right, it punishes us by locking the bus. */
|
|
|
|
eh_ensure_dma_ok (sc);
|
|
|
|
/* Set up the DMA */
|
|
|
|
SetReg ( EH_RSAR0, src & 0xff );
|
|
SetReg ( EH_RSAR1, (src>>8) & 0xff );
|
|
SetReg ( EH_RBCR0, len & 0xff );
|
|
SetReg ( EH_RBCR1, (len>>8) & 0xff );
|
|
|
|
/* Set the DMA running */
|
|
/* REMOTE_DMA ( COM_READ ); */ /* SetReg ( EH_COMMAND, 0x0a ); */
|
|
SetReg ( EH_COMMAND, COM_READ|COM_STA );
|
|
|
|
for ( counter=0; counter<len; counter+=2 )
|
|
*(dst++)=ReadShort ( sc->sc_reg + EH_DATA_PORT );
|
|
|
|
splx(s);
|
|
|
|
if ( eh_ensure_dma_completed ( sc, COM_READ ) )
|
|
return 0;
|
|
|
|
return len;
|
|
}
|
|
|
|
int
|
|
eh_copyout(sc, src, dest, len)
|
|
struct eh_softc *sc;
|
|
char *src;
|
|
int dest;
|
|
int len;
|
|
{
|
|
int counter;
|
|
int s;
|
|
short *sr = (short *)src;
|
|
|
|
if (len & 1)
|
|
len++;
|
|
if (dest & 1) {
|
|
printf ( "EHDEBUG: Copying to odd address\n" );
|
|
dest++;
|
|
}
|
|
|
|
s = splimp(); /* Erm, maybe not needed now */
|
|
|
|
/* Remote DMA to the DP83905 must be done with care. If we dont */
|
|
/* do it exactly right, it punishes us by locking the bus. */
|
|
|
|
eh_ensure_dma_ok (sc);
|
|
|
|
/* Set up the DMA */
|
|
|
|
SetReg ( EH_RSAR0, dest & 0xff );
|
|
SetReg ( EH_RSAR1, (dest>>8) & 0xff );
|
|
SetReg ( EH_RBCR0, len & 0xff );
|
|
SetReg ( EH_RBCR1, (len>>8) & 0xff );
|
|
|
|
/* Set the DMA running */
|
|
/*REMOTE_DMA ( COM_WRITE );*/ /* SetReg ( EH_COMMAND, 0x0a ); */
|
|
SetReg ( EH_COMMAND, COM_WRITE|COM_STA );
|
|
|
|
for ( counter=0; counter<len; counter+=2 ) {
|
|
WriteShort ( sc->sc_reg + EH_DATA_PORT, *(sr) );
|
|
sr++;
|
|
}
|
|
|
|
splx(s);
|
|
|
|
if ( eh_ensure_dma_completed ( sc, COM_WRITE ) )
|
|
return 0;
|
|
|
|
return len;
|
|
}
|
|
|
|
#define ALLOCBLK(v,s) (v)=card_freestart; \
|
|
card_freestart+=((s)+0xff)&(~0xff)
|
|
|
|
void
|
|
ehinit(sc)
|
|
struct eh_softc *sc;
|
|
{
|
|
int card_freestart;
|
|
int counter;
|
|
|
|
PAGE(0);
|
|
|
|
/* Lay out the cards ram */
|
|
card_freestart = sc->sc_physstart;
|
|
|
|
/* Allocate receive buffers */
|
|
|
|
ALLOCBLK ( sc->sc_rbs, (RXBUF_SIZE*NRXBUF) );
|
|
sc->sc_rbd = card_freestart;
|
|
sc->sc_nxtpkt = sc->sc_rbs + 0x100;
|
|
|
|
/* Allocate transmit buffers */
|
|
for ( counter=0; counter<NTXBUF; counter++ ) {
|
|
ALLOCBLK ( sc->sc_tbs[counter], TXBUF_SIZE );
|
|
}
|
|
|
|
if ( card_freestart > sc->sc_physend ) {
|
|
printf ( "eh: card short of ram %02x required %02x present\n",
|
|
card_freestart, sc->sc_physstart );
|
|
sc->sc_arpcom.ac_if.if_flags &= ~IFF_RUNNING;
|
|
sc->sc_arpcom.ac_if.if_flags |= IFF_OACTIVE;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Bring the controller up
|
|
*
|
|
* The ordering here is critical. Dont change unless you know what
|
|
* you're doing
|
|
*/
|
|
|
|
/* Reset the card */
|
|
|
|
SetReg ( EH_RESET, 0xff );
|
|
delay (50000);
|
|
SetReg ( EH_RESET, 0x00 );
|
|
delay (50000);
|
|
|
|
/* 1. Program the command register for page 0 */
|
|
|
|
SetReg ( EH_COMMAND, 0x21 );
|
|
|
|
/* 2. Initialize the Data configuration register */
|
|
|
|
SetReg ( EH_DCR, DCR_WTS|DCR_LS|(0<<5) );
|
|
|
|
/* 3. Clear RBCR if using remote DMA */
|
|
|
|
SetReg ( EH_RBCR0, 0 );
|
|
SetReg ( EH_RBCR1, 0 );
|
|
|
|
/* 4. Initialise RCR */
|
|
|
|
SetReg ( EH_RCR, 0x04 );
|
|
|
|
/* 5. Place the controller in loopback mode 1 or 2 TCR=02H or 04H */
|
|
|
|
SetReg ( EH_TCR, 0x02 );
|
|
|
|
/* 6. Initiliase Receive ring buffer BNDRY PSTART PSTOP */
|
|
|
|
SetReg ( EH_BNRY, sc->sc_rbs >> 8 );
|
|
SetReg ( EH_PSTART, sc->sc_rbs >> 8 );
|
|
SetReg ( EH_PSTOP, sc->sc_rbd >> 8 );
|
|
|
|
/* 7. Clear ISR */
|
|
|
|
SetReg ( EH_ISR, 0xff );
|
|
|
|
/* 8. Initialise IMR */
|
|
|
|
SetReg ( EH_IMR, ISR_PRX|ISR_PTX|ISR_RXE|ISR_TXE|ISR_OVW|ISR_CNT );
|
|
|
|
/* 9. Program command register for page 1 COM=0x61 */
|
|
/* Initialise PAR MAR and CURR */
|
|
|
|
PAGE(1);
|
|
|
|
SetReg ( EH_CURR, sc->sc_nxtpkt >> 8 );
|
|
|
|
for ( counter=0; counter<6; counter++ )
|
|
SetReg ( ((counter+1)<<2), sc->sc_arpcom.ac_enaddr[counter] );
|
|
|
|
/* Put controller into start mode COM = 0x22 */
|
|
|
|
SetReg ( EH_COMMAND, 0x22 );
|
|
|
|
/* Initialise the TCR */
|
|
|
|
SetReg ( EH_TCR, TCR_NORMALLOOP );
|
|
|
|
/* Interface is up */
|
|
|
|
sc->sc_arpcom.ac_if.if_flags |= IFF_RUNNING;
|
|
sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
|
|
|
|
ehstart(&sc->sc_arpcom.ac_if);
|
|
}
|
|
|
|
int
|
|
ehstop(sc)
|
|
struct eh_softc *sc;
|
|
{
|
|
int s = splimp();
|
|
eh_stop_controller(sc);
|
|
SetReg(EH_DCR, DCR_LS|DCR_LAS);
|
|
splx(s);
|
|
return 0;
|
|
}
|
|
|
|
#define INTR_ACK(s) SetReg ( EH_ISR, s )
|
|
|
|
/* In need of tidying */
|
|
|
|
#undef MYTEST
|
|
|
|
void
|
|
eh_rint(sc)
|
|
struct eh_softc *sc;
|
|
{
|
|
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
|
|
struct mbuf *top, **mp, *m;
|
|
struct ether_header eh;
|
|
/* struct ether_header *eh;*/
|
|
struct eh_rxhdr hdr;
|
|
int status = GetReg(EH_RSR);
|
|
int rxstatus=0;
|
|
int totlen, len;
|
|
int ptr;
|
|
int rbend;
|
|
int thispacket;
|
|
int bnry;
|
|
|
|
#ifdef MYTEST
|
|
int old;
|
|
#endif
|
|
|
|
/* Get the end of the ring buffer */
|
|
|
|
PAGE(1);
|
|
rbend = GetReg ( EH_CURR ) << 8;
|
|
PAGE(0);
|
|
|
|
/* If we have something to receive then receive it */
|
|
|
|
if ( status&RSR_PRX ) {
|
|
/* Remove all the packets from the ring buffer */
|
|
for ( ptr=sc->sc_nxtpkt; ptr!=rbend ; ptr = sc->sc_nxtpkt ) {
|
|
|
|
thispacket = ptr;
|
|
/* Copy the ether h receive header */
|
|
|
|
if ( eh_copyring ( sc, ptr, (char *)&hdr, 4 ) != 4 ) {
|
|
printf ( "eh: Failed copying in header\n" );
|
|
printf ( "eh: thispacket %02x\n", thispacket );
|
|
ehinit (sc);
|
|
return;
|
|
}
|
|
|
|
rxstatus = hdr.rx_status;
|
|
totlen = (hdr.rx_rbc1<<8) + hdr.rx_rbc0;
|
|
|
|
ptr+=4;
|
|
|
|
/* Check the packet's status */
|
|
if ( hdr.rx_status&(RSR_CRC|RSR_FAE|RSR_FO|RSR_MPA) ) {
|
|
printf ( "eh: intact packet is corrupt %02x %04x\n",
|
|
hdr.rx_status, ptr );
|
|
}
|
|
|
|
if (((hdr.rx_nxtpkt<<8)>sc->sc_rbd) || ((hdr.rx_nxtpkt<<8)<sc->sc_rbs)) {
|
|
printf ( "eh: ring buffer empty %04x %04x\n",
|
|
hdr.rx_nxtpkt, ptr );
|
|
ehinit (sc);
|
|
return;
|
|
}
|
|
|
|
if (0)
|
|
printf ( "pulling packet at %08x, next at %08x\n", thispacket, sc->sc_nxtpkt );
|
|
|
|
if ( totlen>ETHER_MAX_LEN ) {
|
|
printf ( "eh: giant packet received %04x\n", totlen );
|
|
totlen = ETHER_MAX_LEN;
|
|
}
|
|
|
|
/* Copy the ether header */
|
|
|
|
if ((eh_copyring ( sc, ptr, (char *)&eh, sizeof (eh)))!=sizeof(eh)) {
|
|
printf ( "eh: Failed copying in ethenet header\n" );
|
|
return;
|
|
}
|
|
ptr+=sizeof(eh);
|
|
totlen-=sizeof(eh);
|
|
|
|
/* Copy the packet into mbufs */
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if ( m==0 )
|
|
return;
|
|
m->m_pkthdr.rcvif = ifp;
|
|
m->m_pkthdr.len = totlen;
|
|
len = MHLEN;
|
|
top = 0;
|
|
mp = ⊤
|
|
|
|
while ( totlen > 0 ) {
|
|
if (top) {
|
|
MGET(m, M_DONTWAIT, MT_DATA);
|
|
if ( m==0 ) {
|
|
m_freem(top);
|
|
goto skip;
|
|
}
|
|
len = MLEN;
|
|
}
|
|
if ( totlen >= MINCLSIZE ) {
|
|
MCLGET(m, M_DONTWAIT);
|
|
if (m->m_flags & M_EXT)
|
|
len = MCLBYTES;
|
|
}
|
|
m->m_len = len = min ( totlen, len );
|
|
|
|
if ((eh_copyring ( sc, ptr, mtod(m, caddr_t), len))!=len)
|
|
printf ( "eh: failed copying in buffer %d\n", len );
|
|
|
|
ptr += len;
|
|
totlen -= len;
|
|
*mp = m;
|
|
mp = &m->m_next;
|
|
}
|
|
|
|
m = top;
|
|
ifp->if_ipackets++;
|
|
/* We assume the the header fits in one mbuf */
|
|
/* eh = mtod(m, struct ether_header *);*/
|
|
|
|
#if NBPFILTER > 0
|
|
/*
|
|
* Check if there's a BPF listener on this interface.
|
|
* If so, hand off the raw packet to bpf.
|
|
*/
|
|
if (ifp->if_bpf) {
|
|
/* char buf[1600];
|
|
char *buffer = buf;
|
|
int len;
|
|
struct mbuf *m0;
|
|
|
|
bcopy(&eh, buffer, sizeof(struct ether_header));
|
|
buffer += sizeof(struct ether_header);
|
|
|
|
len = 16;
|
|
for ( m0=m; m0 && (len + m0->m_len) < 1600; m0=m0->m_next ) {
|
|
bcopy(mtod(m,caddr_t), buffer, m0->m_len);
|
|
buffer+=m0->m_len;
|
|
len+=m0->m_len;
|
|
}
|
|
|
|
bpf_tap(sc->sc_arpcom.ac_if.if_bpf, buf, len);*/
|
|
|
|
struct mbuf m0;
|
|
m0.m_len = sizeof(struct ether_header);
|
|
m0.m_data = (caddr_t)&eh;
|
|
m0.m_next = m;
|
|
|
|
bpf_mtap(ifp->if_bpf, &m0);
|
|
|
|
/*
|
|
* Note that the interface cannot be in promiscuous mode if
|
|
* there are no BPF listeners. And if we are in promiscuous
|
|
* mode, we have to check if this packet is really ours.
|
|
*/
|
|
if ((ifp->if_flags & IFF_PROMISC) &&
|
|
(eh.ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
|
|
bcmp(eh.ether_dhost, sc->sc_arpcom.ac_enaddr,
|
|
sizeof(eh.ether_dhost)) != 0) {
|
|
m_freem(m);
|
|
goto skip;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* We assume the the header fits in one mbuf */
|
|
/* m_adj(m, sizeof(struct ether_header));*/
|
|
ether_input(ifp, &eh, m);
|
|
skip:
|
|
|
|
/* Ok, I'm done with this packet */
|
|
|
|
sc->sc_nxtpkt = hdr.rx_nxtpkt << 8;
|
|
|
|
/* Set the boundary pointer on the ring buffer */
|
|
|
|
bnry = (sc->sc_nxtpkt>>8)-1;
|
|
if ( bnry < (sc->sc_rbs>>8) )
|
|
bnry = (sc->sc_rbd>>8)-1;
|
|
SetReg ( EH_BNRY, bnry );
|
|
|
|
PAGE(1);
|
|
rbend = GetReg ( EH_CURR ) << 8;
|
|
PAGE(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
ehintr(arg)
|
|
void *arg;
|
|
{
|
|
struct eh_softc *sc = arg;
|
|
register int isr = GetReg ( EH_ISR ); /* Is char of int faster ? */
|
|
register int times = 0;
|
|
|
|
PAGE(0);
|
|
|
|
if ( isr & ISR_RXE ) {
|
|
int status = GetReg ( EH_RSR );
|
|
PRINTF ( "eh: Receive Error:" );
|
|
if ( status&RSR_PRX )
|
|
PRINTF ( " packet received intact (this should happen)" );
|
|
if ( status&RSR_CRC )
|
|
PRINTF ( " crc error" );
|
|
if ( status&RSR_FAE )
|
|
PRINTF ( " frame alignment error" );
|
|
if ( status&RSR_FO )
|
|
PRINTF ( " fifo overrun" );
|
|
if ( status&RSR_MPA )
|
|
PRINTF ( " missed packet" );
|
|
if ( status&RSR_DIS )
|
|
PRINTF ( " receiver disabled" );
|
|
printf ( "\n" );
|
|
INTR_ACK ( ISR_RXE );
|
|
ehinit (sc);
|
|
return 0;
|
|
}
|
|
|
|
if ( isr & ISR_PRX ) {
|
|
eh_rint (sc);
|
|
INTR_ACK ( ISR_PRX );
|
|
}
|
|
|
|
if ( isr & ISR_PTX ) {
|
|
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
|
|
int status;
|
|
INTR_ACK ( ISR_PTX );
|
|
ifp->if_timer=0;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
status = GetReg ( EH_TSR );
|
|
|
|
if ( status&TSR_PTX )
|
|
ifp->if_opackets++;
|
|
|
|
if ( status&TSR_COL )
|
|
ifp->if_collisions+=GetReg(EH_NCR);
|
|
|
|
if ( status&TSR_ABT )
|
|
PRINTF ( "eh: too many collisions\n" );
|
|
|
|
if ( status&TSR_CRS )
|
|
PRINTF ( "eh: no carrier\n" );
|
|
|
|
if ( status&TSR_CDH )
|
|
PRINTF ( "eh: tranceiver failure, no heartbeat \n" );
|
|
|
|
if ( status&TSR_OWC )
|
|
PRINTF ( "eh: out of window \n" );
|
|
|
|
if ( status&(TSR_ABT|TSR_CRS|TSR_FU|TSR_CDH|TSR_OWC) )
|
|
ifp->if_oerrors++;
|
|
|
|
ehstart(ifp);
|
|
}
|
|
|
|
if ( isr & ISR_TXE ) {
|
|
INTR_ACK ( ISR_TXE );
|
|
PRINTF ( "ehintr: Transmit error\n" );
|
|
}
|
|
|
|
if ( isr & ISR_OVW ) {
|
|
INTR_ACK ( ISR_OVW );
|
|
PRINTF ( "ehintr: Counter overflow\n" );
|
|
}
|
|
|
|
if ( isr & ISR_RST ) {
|
|
INTR_ACK ( ISR_RST );
|
|
PRINTF ( "ehintr: Reset status\n" );
|
|
}
|
|
|
|
if ((times++)>16) {
|
|
PRINTF ( "eh: possible interrupt jammed on." );
|
|
SetReg ( EH_IMR, 0x0 );
|
|
}
|
|
|
|
/* Dont do this for the mo until I'm sure.
|
|
|
|
isr = GetReg ( EH_ISR );
|
|
|
|
if ( (isr&GetReg(EH_IMR))!=0 )
|
|
goto more;
|
|
*/
|
|
|
|
if ( (isr&GetReg(EH_IMR))!=0 )
|
|
printf ( "eh: Interrupt not serviced\n" );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* Auxilary routines ********************************************************/
|
|
/****************************************************************************/
|
|
|
|
void
|
|
eh_shutdown(arg)
|
|
void *arg;
|
|
{
|
|
struct eh_softc *sc = (struct eh_softc *)arg;
|
|
|
|
/* On shutdown put us in loopback */
|
|
|
|
SetReg(EH_DCR, DCR_LAS);
|
|
SetReg(EH_TCR, TCR_NICMOD);
|
|
|
|
/* and program remote dma so riscos doesnt lock */
|
|
|
|
SetReg(EH_RSAR0, 0);
|
|
SetReg(EH_RSAR1, 1);
|
|
SetReg(EH_RBCR0, 0);
|
|
SetReg(EH_RBCR1, 1);
|
|
}
|