Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

573 lines
14 KiB
C

#ifndef LEBOOT
#ifndef lint
static char sccsid[] = "@(#)if_le.c 1.1 92/07/30 SMI";
#endif
#endif
#ifndef sun2
/*
* For Sun-3/25 debugging we want DEBUG on. For production, turn it off.
*/
/* #define DEBUG */
/* #define DEBUG1 */
#define LANCEBUG 1 /* Rev C Lance chip bug */
/*
* Parameters controlling TIMEBOMB action: times out if chip hangs.
*
*/
#define TIMEBOMB 1000000 /* One million times around is all... */
/*
* Sun Lance Ethernet Controller interface
*/
#include <stand/saio.h>
#include <stand/param.h>
#include <sys/socket.h>
#include <sunif/if_lereg.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <mon/idprom.h>
#include <mon/cpu.map.h>
#ifdef sun3x
#include <mon/cpu.addrs.h>
#endif
/* Determine whether we are PROM or not. */
#ifdef LEBOOT
#define PROM
#endif LEBOOT
int lancexmit(), lancepoll(), lancereset(), myetheraddr(), lancestats();
struct saif leif = {
lancexmit,
lancepoll,
lancereset,
myetheraddr,
lancestats
};
#define LANCERBUFSIZ 1600
#define LANCETBUFSIZ 1600
#define LANCERBUFFS 8 /* how many receive buffs */
struct lance_softc {
char es_scrat[PROTOSCRATCH]; /* work space for nd */
struct le_device *es_lance; /* Device register address */
struct ether_addr es_enaddr; /* Our Ethernet address */
struct le_init_block es_ib; /* Initialization block */
struct le_md *es_rmdp[LANCERBUFFS]; /* Receive Desc Ring
ptrs */
struct le_md *es_tmdp; /* Transmit Desc Ring ptr */
u_char es_rbuf[LANCERBUFFS][LANCERBUFSIZ]; /* Receive
Buffers */
#ifndef PROM
u_char es_tbuf[LANCETBUFSIZ]; /* Transmit Buffer */
#endif PROM
int es_next_rmd; /* Next descriptor in ring */
/*
* The transmit and receive ring descriptors. The es_{r,t}mdp
* entries point into these arrays. We allocate an extra one of
* each so that we can guarantee 8-byte alignment. (That is, we
* don't necessarily point locations an integral number of le_mds
* from the start of the arrays.
*/
struct le_md es_rmd[LANCERBUFFS+1],
es_tmd[2];
};
/*
* Need to initialize the Ethernet control reg to:
* Reset is active
* Loopback is NOT active
* Interrupt enable is not active.
*/
#ifdef sun3x
u_long lancestd[] = {pa_AMD_ETHER};
#else
u_long lancestd[] = {VIOPG_AMD_ETHER << BYTES_PG_SHIFT};
#endif
struct devinfo lanceinfo = {
sizeof (struct le_device),
sizeof (struct lance_softc),
0, /* Local bytes (we use dma) */
1, /* Standard addr count */
lancestd, /* Standard addrs */
MAP_OBIO, /* Device type */
0, /* transfer size handled by ND */
};
int lanceprobe(), xxboot(), lanceopen(), lanceclose(), etherstrategy();
int nullsys();
struct boottab ledriver = {
"le", lanceprobe, xxboot, lanceopen, lanceclose,
etherstrategy, "le: Sun/Lance Ethernet", &lanceinfo,
};
extern struct ether_addr etherbroadcastaddr;
/* Statistics counters for lancestats() */
static struct {
int ipackets;
int opackets;
int collis;
int defer;
int crcerrs;
int framerrs;
int missed;
int ofloerrs;
int ufloerrs;
} stats;
/*
* Probe for device.
* Must return -1 for failure for monitor probe
*/
/*ARGSUSED*/
int
lanceprobe(sip)
struct saioreq *sip;
{
struct idprom id;
if (IDFORM_1 == idprom(IDFORM_1, &id) &&
(id.id_machine == IDM_SUN3_M25 ||
id.id_machine == IDM_SUN3_F ||
id.id_machine == IDM_SUN3X_HYDRA ||
id.id_machine == IDM_SUN4_STINGRAY))
return (0);
else
return (-1);
}
/*
* Open Lance Ethernet nd connection, return -1 for errors.
*/
lanceopen(sip)
struct saioreq *sip;
{
register int result;
#ifdef DEBUG1
printf("le: lanceopen[\n");
#endif
sip->si_sif = &leif;
if ( lanceinit(sip) || (result = etheropen(sip)) < 0 ) {
lanceclose(sip); /* Make sure we kill chip */
#ifdef DEBUG1
printf("le: lanceopen --> -1\n");
#endif
return (-1);
}
#ifdef DEBUG1
printf("le: lanceopen --> %x\n", result);
#endif
return (result);
}
/*
* Set up memory maps and Ethernet chip.
* Returns 1 for error (after printing message), 0 for ok.
*/
/*ARGSUSED*/
int
lanceinit(sip)
struct saioreq *sip;
{
register struct lance_softc *es;
struct idprom id;
/*
* Our locals were obtined from DMA space, now put the locals
* pointer in the standard place. This is OK since close()
* will only deallocate from devdata if its size in devinfo is >0.
*/
es = (struct lance_softc *)sip->si_dmaaddr;
sip->si_devdata = (caddr_t)es;
es->es_lance = (struct le_device *) sip->si_devaddr;
if (IDFORM_1 != idprom(IDFORM_1, &id)) {
printf("le: No ID PROM\n");
return 1;
}
return lancereset(es, sip);
}
/*
* Basic Lance initialization
* Returns 1 for error (after printing message), 0 for ok.
*/
/*ARGSUSED*/
int
lancereset(es, sip)
register struct lance_softc *es;
struct saioreq *sip;
{
register struct le_device *le = es->es_lance;
register struct le_init_block *ib = &es->es_ib;
struct idprom id;
int timeout = TIMEBOMB;
int i;
#ifdef DEBUG1
printf("le: lancereset(%x, %x)\n", es, sip);
#endif
/* Reset the chip */
le->le_rap = LE_CSR0;
le->le_csr = LE_STOP;
/* Perform the basic initialization */
/* Construct the initialization block */
bzero((caddr_t)&es->es_ib, sizeof (struct le_init_block));
/* Leave the mode word 0 for normal operating mode */
myetheraddr(&es->es_enaddr);
/* Oh, for a consistent byte ordering among processors */
ib->ib_padr[0] = es->es_enaddr.ether_addr_octet[1];
ib->ib_padr[1] = es->es_enaddr.ether_addr_octet[0];
ib->ib_padr[2] = es->es_enaddr.ether_addr_octet[3];
ib->ib_padr[3] = es->es_enaddr.ether_addr_octet[2];
ib->ib_padr[4] = es->es_enaddr.ether_addr_octet[5];
ib->ib_padr[5] = es->es_enaddr.ether_addr_octet[4];
/* Leave address filter 0 -- we don't want Multicast packets */
/*
* Set up transmit and receive ring pointers,
* taking the 8-byte alignment requirement into account.
*/
es->es_rmdp[0] = (struct le_md *) ((u_long)(&es->es_rmd[0]) & ~07) + 1;
for (i = 1; i < LANCERBUFFS; i++)
es->es_rmdp[i] = es->es_rmdp[i-1] + 1;
es->es_tmdp = (struct le_md *) ((u_long)(&es->es_tmd[0]) & ~07) + 1;
ib->ib_rdrp.drp_laddr = (long)es->es_rmdp[0];
ib->ib_rdrp.drp_haddr = (long)es->es_rmdp[0] >> 16;
ib->ib_rdrp.drp_len = 3; /* 2 to the 1 power = 2 */
ib->ib_tdrp.drp_laddr = (long)es->es_tmdp;
ib->ib_tdrp.drp_haddr = (long)es->es_tmdp >> 16;
ib->ib_tdrp.drp_len = 0; /* 2 to the 0 power = 1 */
/* Clear all the descriptors */
bzero((caddr_t)es->es_rmdp[0], LANCERBUFFS * sizeof (struct le_md));
bzero((caddr_t)es->es_tmdp, sizeof (struct le_md));
/* Give the init block to the chip */
le->le_rap = LE_CSR1; /* select the low address register */
le->le_rdp = (long)ib & 0xffff;
le->le_rap = LE_CSR2; /* select the high address register */
le->le_rdp = ((long)ib >> 16) & 0xff;
if (IDFORM_1 != idprom(IDFORM_1, &id)) {
printf("le: No ID PROM\n");
return 1;
}
le->le_rap = LE_CSR3; /* Bus Master control register */
if (id.id_machine == IDM_SUN4_STINGRAY || id.id_machine == IDM_SUN3X_HYDRA )
le->le_rdp = LE_BSWP+LE_ACON+LE_BCON;
else
le->le_rdp = LE_BSWP;
le->le_rap = LE_CSR0; /* main control/status register */
le->le_csr = LE_INIT;
while( ! (le->le_csr & LE_IDON) ) {
if (timeout-- <= 0) {
printf("le: cannot initialize\n");
return (1);
}
}
le->le_csr = LE_IDON; /* Clear the indication */
/* Hang out the receive buffers */
es->es_next_rmd = 0;
for (i = 0; i < LANCERBUFFS; i++)
install_buf_in_rmd(es->es_rbuf[i], es->es_rmdp[i]);
le->le_csr = LE_STRT;
#ifdef DEBUG1
printf("le: lancereset returns OK\n");
#endif
return 0; /* It all worked! */
}
install_buf_in_rmd(buffer, rmd)
u_char *buffer;
register struct le_md *rmd;
{
rmd->lmd_ladr = (u_short)buffer;
rmd->lmd_hadr = (long)buffer >> 16;
rmd->lmd_bcnt = -LANCERBUFSIZ;
rmd->lmd_mcnt = 0;
rmd->lmd_flags = LMD_OWN;
}
/*
* Transmit a packet.
* If possible, just points to the packet without copying it anywhere.
*/
lancexmit(es, buf, count)
register struct lance_softc *es;
char *buf;
int count;
{
register struct le_device *le = es->es_lance;
struct le_md *tmd = es->es_tmdp; /* Transmit Msg. Descriptor */
caddr_t tbuf;
int timeout = TIMEBOMB;
#ifdef DEBUG2
printf( "xmit np_blkno %x\n",
((struct ndpack *)(buf+14))->np_blkno);
#endif
/*
* We assume the buffer is in an area accessible by the chip.
* The caller of xmit is currently ndxmit(), which only sends
* an nd structure, which we happen to know is allocated in the
* right area (in fact, it's part of the struct nd which
* is the first thing in our own es structure). If we wish to
* use xmit for some other purpose, the buffer might not be
* accessible by the chip, so to be general we ought to copy
* into some accessible place. HOWEVER, PROM space is really tight,
* so this generality is not free.
*/
#ifdef PROM
tbuf = buf;
#else PROM
/* FIXME, constant address masks here! */
#ifdef sun3x
if ( ((int)buf & 0xFF000000) == 0xFF000000) { /* we can point to it
*/
#else
if ( ((int)buf & 0x0F000000) == 0x0F000000) { /* we can point to it */
#endif sun3x
tbuf = buf;
} else {
tbuf = (caddr_t)es->es_tbuf;
bcopy((caddr_t)buf, tbuf, count);
}
#endif PROM
tmd->lmd_hadr = (int)tbuf >> 16;
tmd->lmd_ladr = (u_short) tbuf;
tmd->lmd_bcnt = -count;
#ifdef notdef
if (tmd->lmd_bcnt < -MINPACKET)
tmd->lmd_bcnt = -MINPACKET;
#endif
tmd->lmd_flags3 = 0;
tmd->lmd_flags = LMD_STP | LMD_ENP | LMD_OWN;
le->le_rap = LE_CSR0;
le->le_csr = LE_TDMD;
do {
if ( le->le_csr & LE_TINT ) {
le->le_csr = LE_TINT; /* Clear interrupt */
break;
}
} while ( --timeout > 0);
if (tmd->lmd_flags & TMD_DEF)
stats.defer++;
if (tmd->lmd_flags & TMD_ONE)
stats.collis++;
else if (tmd->lmd_flags & TMD_MORE)
stats.collis += 2;
if (tmd->lmd_flags3 & TMD_UFLO)
stats.ufloerrs++;
if (tmd->lmd_flags3 & TMD_LCAR)
printf("le: Ethernet cable problem\n");
if ( (tmd->lmd_flags & LMD_ERR)
|| (tmd->lmd_flags3 & TMD_BUFF)
|| (timeout <= 0) ) {
#ifdef DEBUG
printf("le: xmit failed - tmd1 flags %x tmd3 %x\n",
tmd->lmd_flags, tmd->lmd_flags3);
#endif
return (1);
}
stats.opackets++;
return (0);
}
int
lancepoll(es, buf)
register struct lance_softc *es;
char *buf;
{
register struct le_device *le = es->es_lance;
register struct le_md *rmd;
register struct ether_header *header;
int length;
#ifdef never
register struct le_md *next_rmd;
#endif never
#ifdef DEBUG1
printf("le: poll\n");
#endif
le->le_rap = LE_CSR0;
if ( ! (le->le_csr & LE_RINT) ) {
rmd = es->es_rmdp[es->es_next_rmd];
if ((rmd->lmd_flags & LMD_OWN) == 0) {
#ifdef never
printf ("le: one waiting 0x%x\n",
rmd->lmd_mcnt - 4);
#endif never
goto cheat;
}
return (0); /* No packet yet */
}
cheat:
stats.ipackets++;
if (le->le_csr & LE_MISS) {
#ifdef DEBUG1
printf ("le: missed packet\n");
#endif DEBUG
stats.missed++;
le->le_csr |= LE_MISS;
}
#ifdef DEBUG1
printf("le: received packet\n");
#endif
le->le_csr = LE_RINT; /* Clear interrupt */
rmd = es->es_rmdp[es->es_next_rmd];
if (rmd->lmd_flags & RMD_OFLO)
stats.ofloerrs++;
else {
if ((rmd->lmd_flags & RMD_FRAM) && (rmd->lmd_flags & LMD_ENP))
stats.framerrs++;
if ((rmd->lmd_flags & RMD_CRC) && (rmd->lmd_flags & LMD_ENP))
stats.crcerrs++;
}
if ( (rmd->lmd_flags & ~RMD_OFLO) != (LMD_STP|LMD_ENP) ) {
#ifdef DEBUG
printf("Receive packet error - rmd flags %x\n",rmd->lmd_flags);
#endif
length = 0;
goto restorebuf;
}
/* Get input data length and a pointer to the ethernet header */
length = rmd->lmd_mcnt - 4; /* don't count the 4 CRC bytes */
header = (struct ether_header *)es->es_rbuf[es->es_next_rmd];
#ifdef LANCEBUG
/*
* Check for unreported packet errors. Rev C of the LANCE chip
* has a bug which can cause "random" bytes to be prepended to
* the start of the packet. The work-around is to make sure that
* the Ethernet destination address in the packet matches our
* address.
*/
#define ether_addr_not_equal(a,b) \
( ( *(short *)(&a.ether_addr_octet[0]) != \
*(short *)(&b.ether_addr_octet[0]) ) \
|| ( *(short *)(&a.ether_addr_octet[2]) != \
*(short *)(&b.ether_addr_octet[2]) ) \
|| ( *(short *)(&a.ether_addr_octet[4]) != \
*(short *)(&b.ether_addr_octet[4]) ) \
)
if( ether_addr_not_equal(header->ether_dhost, es->es_enaddr)
&& ether_addr_not_equal(header->ether_dhost, etherbroadcastaddr) ) {
printf("le: LANCE Rev C Extra Byte(s) bug; Packet punted\n");
length = 0;
/* Don't return directly; restore the buffer first */
}
#endif LANCEBUG
#ifdef DEBUG2
if( header->ether_dhost.ether_addr_octet[0] == 0xff )
printf("Broadcast packet\n");
else printf("recv np_blkno %x\n",
((struct ndpack *)(buf+14))->np_blkno);
#endif
/* Copy packet to user's buffer */
if ( length > 0 )
bcopy((caddr_t)header, buf, length);
restorebuf:
rmd->lmd_mcnt = 0;
rmd->lmd_flags = LMD_OWN;
/* Get ready to use the other buffer next time */
/* What about errors ? */
es->es_next_rmd =
(es->es_next_rmd == (LANCERBUFFS - 1)) ? 0 : es->es_next_rmd + 1;
#ifdef never
next_rmd = es->es_rmdp[es->es_next_rmd];
if ((next_rmd->lmd_flags & LMD_OWN) == 0)
printf ("le: one waiting\n");
#endif never
return (length);
}
/*
* Close down Lance Ethernet device.
* On the Model 25, we reset the chip and take it off the wire, since
* it is sharing main memory with us (occasionally reading and writing),
* and most programs don't know how to deal with that -- they just assume
* that main memory is theirs to play with.
*/
lanceclose(sip)
struct saioreq *sip;
{
struct lance_softc *es = (struct lance_softc *) sip->si_devdata;
struct le_device *le = es->es_lance;
/* Reset the chip */
le->le_rap = LE_CSR0;
le->le_csr = LE_STOP;
}
lancestats()
{
printf("le: ipackets %d opackets %d collis %d defer %d crcerrs %d framerrs %d missed %d ofloerrs %d ufloerrs %d\n",
stats.ipackets,
stats.opackets,
stats.collis,
stats.defer,
stats.crcerrs,
stats.framerrs,
stats.missed,
stats.ofloerrs,
stats.ufloerrs);
}
#endif !sun2