813 lines
17 KiB
C
813 lines
17 KiB
C
/*
|
|
* National Semiconductor DP8390 and clone
|
|
* Network Interface Controller.
|
|
*/
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/error.h"
|
|
#include "../port/netif.h"
|
|
|
|
#include "etherif.h"
|
|
#include "ether8390.h"
|
|
|
|
enum { /* NIC core registers */
|
|
Cr = 0x00, /* command register, all pages */
|
|
|
|
/* Page 0, read */
|
|
Clda0 = 0x01, /* current local DMA address 0 */
|
|
Clda1 = 0x02, /* current local DMA address 1 */
|
|
Bnry = 0x03, /* boundary pointer (R/W) */
|
|
Tsr = 0x04, /* transmit status register */
|
|
Ncr = 0x05, /* number of collisions register */
|
|
Fifo = 0x06, /* FIFO */
|
|
Isr = 0x07, /* interrupt status register (R/W) */
|
|
Crda0 = 0x08, /* current remote DMA address 0 */
|
|
Crda1 = 0x09, /* current remote DMA address 1 */
|
|
Rsr = 0x0C, /* receive status register */
|
|
Ref0 = 0x0D, /* frame alignment errors */
|
|
Ref1 = 0x0E, /* CRC errors */
|
|
Ref2 = 0x0F, /* missed packet errors */
|
|
|
|
/* Page 0, write */
|
|
Pstart = 0x01, /* page start register */
|
|
Pstop = 0x02, /* page stop register */
|
|
Tpsr = 0x04, /* transmit page start address */
|
|
Tbcr0 = 0x05, /* transmit byte count register 0 */
|
|
Tbcr1 = 0x06, /* transmit byte count register 1 */
|
|
Rsar0 = 0x08, /* remote start address register 0 */
|
|
Rsar1 = 0x09, /* remote start address register 1 */
|
|
Rbcr0 = 0x0A, /* remote byte count register 0 */
|
|
Rbcr1 = 0x0B, /* remote byte count register 1 */
|
|
Rcr = 0x0C, /* receive configuration register */
|
|
Tcr = 0x0D, /* transmit configuration register */
|
|
Dcr = 0x0E, /* data configuration register */
|
|
Imr = 0x0F, /* interrupt mask */
|
|
|
|
/* Page 1, read/write */
|
|
Par0 = 0x01, /* physical address register 0 */
|
|
Curr = 0x07, /* current page register */
|
|
Mar0 = 0x08, /* multicast address register 0 */
|
|
};
|
|
|
|
enum { /* Cr */
|
|
Stp = 0x01, /* stop */
|
|
Sta = 0x02, /* start */
|
|
Txp = 0x04, /* transmit packet */
|
|
Rd0 = 0x08, /* remote DMA command */
|
|
Rd1 = 0x10,
|
|
Rd2 = 0x20,
|
|
RdREAD = Rd0, /* remote read */
|
|
RdWRITE = Rd1, /* remote write */
|
|
RdSEND = Rd1|Rd0, /* send packet */
|
|
RdABORT = Rd2, /* abort/complete remote DMA */
|
|
Ps0 = 0x40, /* page select */
|
|
Ps1 = 0x80,
|
|
Page0 = 0x00,
|
|
Page1 = Ps0,
|
|
Page2 = Ps1,
|
|
};
|
|
|
|
enum { /* Isr/Imr */
|
|
Prx = 0x01, /* packet received */
|
|
Ptx = 0x02, /* packet transmitted */
|
|
Rxe = 0x04, /* receive error */
|
|
Txe = 0x08, /* transmit error */
|
|
Ovw = 0x10, /* overwrite warning */
|
|
Cnt = 0x20, /* counter overflow */
|
|
Rdc = 0x40, /* remote DMA complete */
|
|
Rst = 0x80, /* reset status */
|
|
};
|
|
|
|
enum { /* Dcr */
|
|
Wts = 0x01, /* word transfer select */
|
|
Bos = 0x02, /* byte order select */
|
|
Las = 0x04, /* long address select */
|
|
Ls = 0x08, /* loopback select */
|
|
Arm = 0x10, /* auto-initialise remote */
|
|
Ft0 = 0x20, /* FIFO threshold select */
|
|
Ft1 = 0x40,
|
|
Ft1WORD = 0x00,
|
|
Ft2WORD = Ft0,
|
|
Ft4WORD = Ft1,
|
|
Ft6WORD = Ft1|Ft0,
|
|
};
|
|
|
|
enum { /* Tcr */
|
|
Crc = 0x01, /* inhibit CRC */
|
|
Lb0 = 0x02, /* encoded loopback control */
|
|
Lb1 = 0x04,
|
|
LpbkNORMAL = 0x00, /* normal operation */
|
|
LpbkNIC = Lb0, /* internal NIC module loopback */
|
|
LpbkENDEC = Lb1, /* internal ENDEC module loopback */
|
|
LpbkEXTERNAL = Lb1|Lb0, /* external loopback */
|
|
Atd = 0x08, /* auto transmit disable */
|
|
Ofst = 0x10, /* collision offset enable */
|
|
};
|
|
|
|
enum { /* Tsr */
|
|
Ptxok = 0x01, /* packet transmitted */
|
|
Col = 0x04, /* transmit collided */
|
|
Abt = 0x08, /* tranmit aborted */
|
|
Crs = 0x10, /* carrier sense lost */
|
|
Fu = 0x20, /* FIFO underrun */
|
|
Cdh = 0x40, /* CD heartbeat */
|
|
Owc = 0x80, /* out of window collision */
|
|
};
|
|
|
|
enum { /* Rcr */
|
|
Sep = 0x01, /* save errored packets */
|
|
Ar = 0x02, /* accept runt packets */
|
|
Ab = 0x04, /* accept broadcast */
|
|
Am = 0x08, /* accept multicast */
|
|
Pro = 0x10, /* promiscuous physical */
|
|
Mon = 0x20, /* monitor mode */
|
|
};
|
|
|
|
enum { /* Rsr */
|
|
Prxok = 0x01, /* packet received intact */
|
|
Crce = 0x02, /* CRC error */
|
|
Fae = 0x04, /* frame alignment error */
|
|
Fo = 0x08, /* FIFO overrun */
|
|
Mpa = 0x10, /* missed packet */
|
|
Phy = 0x20, /* physical/multicast address */
|
|
Dis = 0x40, /* receiver disabled */
|
|
Dfr = 0x80, /* deferring */
|
|
};
|
|
|
|
typedef struct Hdr Hdr;
|
|
struct Hdr {
|
|
uchar status;
|
|
uchar next;
|
|
uchar len0;
|
|
uchar len1;
|
|
};
|
|
|
|
void
|
|
dp8390getea(Ether* ether, uchar* ea)
|
|
{
|
|
Dp8390 *ctlr;
|
|
uchar cr;
|
|
int i;
|
|
|
|
ctlr = ether->ctlr;
|
|
|
|
/*
|
|
* Get the ethernet address from the chip.
|
|
* Take care to restore the command register
|
|
* afterwards.
|
|
*/
|
|
ilock(ctlr);
|
|
cr = regr(ctlr, Cr) & ~Txp;
|
|
regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
|
|
for(i = 0; i < Eaddrlen; i++)
|
|
ea[i] = regr(ctlr, Par0+i);
|
|
regw(ctlr, Cr, cr);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
void
|
|
dp8390setea(Ether* ether)
|
|
{
|
|
int i;
|
|
uchar cr;
|
|
Dp8390 *ctlr;
|
|
|
|
ctlr = ether->ctlr;
|
|
|
|
/*
|
|
* Set the ethernet address into the chip.
|
|
* Take care to restore the command register
|
|
* afterwards. Don't care about multicast
|
|
* addresses as multicast is never enabled
|
|
* (currently).
|
|
*/
|
|
ilock(ctlr);
|
|
cr = regr(ctlr, Cr) & ~Txp;
|
|
regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
|
|
for(i = 0; i < Eaddrlen; i++)
|
|
regw(ctlr, Par0+i, ether->ea[i]);
|
|
regw(ctlr, Cr, cr);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static void*
|
|
_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
|
|
{
|
|
uchar cr;
|
|
int timo;
|
|
|
|
/*
|
|
* Read some data at offset 'from' in the card's memory
|
|
* using the DP8390 remote DMA facility, and place it at
|
|
* 'to' in main memory, via the I/O data port.
|
|
*/
|
|
cr = regr(ctlr, Cr) & ~Txp;
|
|
regw(ctlr, Cr, Page0|RdABORT|Sta);
|
|
regw(ctlr, Isr, Rdc);
|
|
|
|
/*
|
|
* Set up the remote DMA address and count.
|
|
*/
|
|
len = ROUNDUP(len, ctlr->width);
|
|
regw(ctlr, Rbcr0, len & 0xFF);
|
|
regw(ctlr, Rbcr1, (len>>8) & 0xFF);
|
|
regw(ctlr, Rsar0, from & 0xFF);
|
|
regw(ctlr, Rsar1, (from>>8) & 0xFF);
|
|
|
|
/*
|
|
* Start the remote DMA read and suck the data
|
|
* out of the I/O port.
|
|
*/
|
|
regw(ctlr, Cr, Page0|RdREAD|Sta);
|
|
rdread(ctlr, to, len);
|
|
|
|
/*
|
|
* Wait for the remote DMA to complete. The timeout
|
|
* is necessary because this routine may be called on
|
|
* a non-existent chip during initialisation and, due
|
|
* to the miracles of the bus, it's possible to get this
|
|
* far and still be talking to a slot full of nothing.
|
|
*/
|
|
for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
|
|
;
|
|
|
|
regw(ctlr, Isr, Rdc);
|
|
regw(ctlr, Cr, cr);
|
|
|
|
return to;
|
|
}
|
|
|
|
void*
|
|
dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
|
|
{
|
|
void *v;
|
|
|
|
ilock(ctlr);
|
|
v = _dp8390read(ctlr, to, from, len);
|
|
iunlock(ctlr);
|
|
|
|
return v;
|
|
}
|
|
|
|
static void*
|
|
dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len)
|
|
{
|
|
ulong crda;
|
|
uchar cr;
|
|
int timo, width;
|
|
|
|
top:
|
|
/*
|
|
* Write some data to offset 'to' in the card's memory
|
|
* using the DP8390 remote DMA facility, reading it at
|
|
* 'from' in main memory, via the I/O data port.
|
|
*/
|
|
cr = regr(ctlr, Cr) & ~Txp;
|
|
regw(ctlr, Cr, Page0|RdABORT|Sta);
|
|
regw(ctlr, Isr, Rdc);
|
|
|
|
len = ROUNDUP(len, ctlr->width);
|
|
|
|
/*
|
|
* Set up the remote DMA address and count.
|
|
* This is straight from the DP8390[12D] datasheet,
|
|
* hence the initial set up for read.
|
|
* Assumption here that the A7000 EtherV card will
|
|
* never need a dummyrr.
|
|
*/
|
|
if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){
|
|
if(ctlr->width == 2)
|
|
width = 1;
|
|
else
|
|
width = 0;
|
|
crda = to-1-width;
|
|
regw(ctlr, Rbcr0, (len+1+width) & 0xFF);
|
|
regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF);
|
|
regw(ctlr, Rsar0, crda & 0xFF);
|
|
regw(ctlr, Rsar1, (crda>>8) & 0xFF);
|
|
regw(ctlr, Cr, Page0|RdREAD|Sta);
|
|
|
|
for(timo=0;; timo++){
|
|
if(timo > 10000){
|
|
print("ether8390: dummyrr timeout; assuming nodummyrr\n");
|
|
ctlr->dummyrr = 0;
|
|
goto top;
|
|
}
|
|
crda = regr(ctlr, Crda0);
|
|
crda |= regr(ctlr, Crda1)<<8;
|
|
if(crda == to){
|
|
/*
|
|
* Start the remote DMA write and make sure
|
|
* the registers are correct.
|
|
*/
|
|
regw(ctlr, Cr, Page0|RdWRITE|Sta);
|
|
|
|
crda = regr(ctlr, Crda0);
|
|
crda |= regr(ctlr, Crda1)<<8;
|
|
if(crda != to)
|
|
panic("crda write %lud to %lud", crda, to);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
regw(ctlr, Rsar0, to & 0xFF);
|
|
regw(ctlr, Rsar1, (to>>8) & 0xFF);
|
|
regw(ctlr, Rbcr0, len & 0xFF);
|
|
regw(ctlr, Rbcr1, (len>>8) & 0xFF);
|
|
regw(ctlr, Cr, Page0|RdWRITE|Sta);
|
|
}
|
|
|
|
/*
|
|
* Pump the data into the I/O port
|
|
* then wait for the remote DMA to finish.
|
|
*/
|
|
rdwrite(ctlr, from, len);
|
|
for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
|
|
;
|
|
|
|
regw(ctlr, Isr, Rdc);
|
|
regw(ctlr, Cr, cr);
|
|
|
|
return (void*)to;
|
|
}
|
|
|
|
static void
|
|
ringinit(Dp8390* ctlr)
|
|
{
|
|
regw(ctlr, Pstart, ctlr->pstart);
|
|
regw(ctlr, Pstop, ctlr->pstop);
|
|
regw(ctlr, Bnry, ctlr->pstop-1);
|
|
|
|
regw(ctlr, Cr, Page1|RdABORT|Stp);
|
|
regw(ctlr, Curr, ctlr->pstart);
|
|
regw(ctlr, Cr, Page0|RdABORT|Stp);
|
|
|
|
ctlr->nxtpkt = ctlr->pstart;
|
|
}
|
|
|
|
static uchar
|
|
getcurr(Dp8390* ctlr)
|
|
{
|
|
uchar cr, curr;
|
|
|
|
cr = regr(ctlr, Cr) & ~Txp;
|
|
regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
|
|
curr = regr(ctlr, Curr);
|
|
regw(ctlr, Cr, cr);
|
|
|
|
return curr;
|
|
}
|
|
|
|
static void
|
|
receive(Ether* ether)
|
|
{
|
|
Dp8390 *ctlr;
|
|
uchar curr, *p;
|
|
Hdr hdr;
|
|
ulong count, data, len;
|
|
Block *bp;
|
|
|
|
ctlr = ether->ctlr;
|
|
for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){
|
|
data = ctlr->nxtpkt*Dp8390BufSz;
|
|
if(ctlr->ram)
|
|
memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr));
|
|
else
|
|
_dp8390read(ctlr, &hdr, data, sizeof(Hdr));
|
|
|
|
/*
|
|
* Don't believe the upper byte count, work it
|
|
* out from the software next-page pointer and
|
|
* the current next-page pointer.
|
|
*/
|
|
if(hdr.next > ctlr->nxtpkt)
|
|
len = hdr.next - ctlr->nxtpkt - 1;
|
|
else
|
|
len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1;
|
|
if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
|
|
len--;
|
|
|
|
len = ((len<<8)|hdr.len0)-4;
|
|
|
|
/*
|
|
* Chip is badly scrogged, reinitialise the ring.
|
|
*/
|
|
if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop
|
|
|| len < 60 || len > sizeof(Etherpkt)){
|
|
print("dp8390: H%2.2ux+%2.2ux+%2.2ux+%2.2ux,%lud\n",
|
|
hdr.status, hdr.next, hdr.len0, hdr.len1, len);
|
|
regw(ctlr, Cr, Page0|RdABORT|Stp);
|
|
ringinit(ctlr);
|
|
regw(ctlr, Cr, Page0|RdABORT|Sta);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If it's a good packet read it in to the software buffer.
|
|
* If the packet wraps round the hardware ring, read it in
|
|
* two pieces.
|
|
*/
|
|
if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && (bp = iallocb(len))){
|
|
p = bp->rp;
|
|
bp->wp = p+len;
|
|
data += sizeof(Hdr);
|
|
|
|
if((data+len) >= ctlr->pstop*Dp8390BufSz){
|
|
count = ctlr->pstop*Dp8390BufSz - data;
|
|
if(ctlr->ram)
|
|
memmove(p, (void*)(ether->mem+data), count);
|
|
else
|
|
_dp8390read(ctlr, p, data, count);
|
|
p += count;
|
|
data = ctlr->pstart*Dp8390BufSz;
|
|
len -= count;
|
|
}
|
|
if(len){
|
|
if(ctlr->ram)
|
|
memmove(p, (void*)(ether->mem+data), len);
|
|
else
|
|
_dp8390read(ctlr, p, data, len);
|
|
}
|
|
|
|
/*
|
|
* Copy the packet to whoever wants it.
|
|
*/
|
|
etheriq(ether, bp, 1);
|
|
}
|
|
|
|
/*
|
|
* Finished with this packet, update the
|
|
* hardware and software ring pointers.
|
|
*/
|
|
ctlr->nxtpkt = hdr.next;
|
|
|
|
hdr.next--;
|
|
if(hdr.next < ctlr->pstart)
|
|
hdr.next = ctlr->pstop-1;
|
|
regw(ctlr, Bnry, hdr.next);
|
|
}
|
|
}
|
|
|
|
static void
|
|
txstart(Ether* ether)
|
|
{
|
|
int len;
|
|
Dp8390 *ctlr;
|
|
Block *bp;
|
|
uchar minpkt[ETHERMINTU], *rp;
|
|
|
|
ctlr = ether->ctlr;
|
|
|
|
/*
|
|
* This routine is called both from the top level and from interrupt
|
|
* level and expects to be called with ctlr already locked.
|
|
*/
|
|
if(ctlr->txbusy)
|
|
return;
|
|
bp = qget(ether->oq);
|
|
if(bp == nil)
|
|
return;
|
|
|
|
/*
|
|
* Make sure the packet is of minimum length;
|
|
* copy it to the card's memory by the appropriate means;
|
|
* start the transmission.
|
|
*/
|
|
len = BLEN(bp);
|
|
rp = bp->rp;
|
|
if(len < ETHERMINTU){
|
|
rp = minpkt;
|
|
memmove(rp, bp->rp, len);
|
|
memset(rp+len, 0, ETHERMINTU-len);
|
|
len = ETHERMINTU;
|
|
}
|
|
|
|
if(ctlr->ram)
|
|
memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len);
|
|
else
|
|
dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len);
|
|
freeb(bp);
|
|
|
|
regw(ctlr, Tbcr0, len & 0xFF);
|
|
regw(ctlr, Tbcr1, (len>>8) & 0xFF);
|
|
regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
|
|
|
|
ether->outpackets++;
|
|
ctlr->txbusy = 1;
|
|
}
|
|
|
|
static void
|
|
transmit(Ether* ether)
|
|
{
|
|
Dp8390 *ctlr;
|
|
|
|
ctlr = ether->ctlr;
|
|
|
|
ilock(ctlr);
|
|
txstart(ether);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static void
|
|
overflow(Ether *ether)
|
|
{
|
|
Dp8390 *ctlr;
|
|
uchar txp;
|
|
int resend;
|
|
|
|
ctlr = ether->ctlr;
|
|
|
|
/*
|
|
* The following procedure is taken from the DP8390[12D] datasheet,
|
|
* it seems pretty adamant that this is what has to be done.
|
|
*/
|
|
txp = regr(ctlr, Cr) & Txp;
|
|
regw(ctlr, Cr, Page0|RdABORT|Stp);
|
|
delay(2);
|
|
regw(ctlr, Rbcr0, 0);
|
|
regw(ctlr, Rbcr1, 0);
|
|
|
|
resend = 0;
|
|
if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0)
|
|
resend = 1;
|
|
|
|
regw(ctlr, Tcr, LpbkNIC);
|
|
regw(ctlr, Cr, Page0|RdABORT|Sta);
|
|
receive(ether);
|
|
regw(ctlr, Isr, Ovw);
|
|
regw(ctlr, Tcr, LpbkNORMAL);
|
|
|
|
if(resend)
|
|
regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
|
|
}
|
|
|
|
static void
|
|
interrupt(Ureg*, void* arg)
|
|
{
|
|
Ether *ether;
|
|
Dp8390 *ctlr;
|
|
uchar isr, r;
|
|
|
|
ether = arg;
|
|
ctlr = ether->ctlr;
|
|
|
|
/*
|
|
* While there is something of interest,
|
|
* clear all the interrupts and process.
|
|
*/
|
|
ilock(ctlr);
|
|
regw(ctlr, Imr, 0x00);
|
|
while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){
|
|
if(isr & Ovw){
|
|
overflow(ether);
|
|
regw(ctlr, Isr, Ovw);
|
|
ether->overflows++;
|
|
}
|
|
|
|
/*
|
|
* Packets have been received.
|
|
* Take a spin round the ring.
|
|
*/
|
|
if(isr & (Rxe|Prx)){
|
|
receive(ether);
|
|
regw(ctlr, Isr, Rxe|Prx);
|
|
}
|
|
|
|
/*
|
|
* A packet completed transmission, successfully or
|
|
* not. Start transmission on the next buffered packet,
|
|
* and wake the output routine.
|
|
*/
|
|
if(isr & (Txe|Ptx)){
|
|
r = regr(ctlr, Tsr);
|
|
if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){
|
|
print("dp8390: Tsr %#2.2ux", r);
|
|
ether->oerrs++;
|
|
}
|
|
|
|
regw(ctlr, Isr, Txe|Ptx);
|
|
|
|
if(isr & Ptx)
|
|
ether->outpackets++;
|
|
ctlr->txbusy = 0;
|
|
txstart(ether);
|
|
}
|
|
|
|
if(isr & Cnt){
|
|
ether->frames += regr(ctlr, Ref0);
|
|
ether->crcs += regr(ctlr, Ref1);
|
|
ether->buffs += regr(ctlr, Ref2);
|
|
regw(ctlr, Isr, Cnt);
|
|
}
|
|
}
|
|
regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static uchar allmar[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
|
static void
|
|
setfilter(Ether *ether, Dp8390 *ctlr)
|
|
{
|
|
uchar r, cr;
|
|
int i;
|
|
uchar *mar;
|
|
|
|
r = Ab;
|
|
mar = 0;
|
|
if(ether->prom){
|
|
r |= Pro|Am;
|
|
mar = allmar;
|
|
} else if(ether->nmaddr){
|
|
r |= Am;
|
|
mar = ctlr->mar;
|
|
}
|
|
if(mar){
|
|
cr = regr(ctlr, Cr) & ~Txp;
|
|
regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
|
|
for(i = 0; i < 8; i++)
|
|
regw(ctlr, Mar0+i, *(mar++));
|
|
regw(ctlr, Cr, cr);
|
|
}
|
|
regw(ctlr, Rcr, r);
|
|
}
|
|
|
|
static void
|
|
promiscuous(void *arg, int )
|
|
{
|
|
Ether *ether;
|
|
Dp8390 *ctlr;
|
|
|
|
ether = arg;
|
|
ctlr = ether->ctlr;
|
|
|
|
ilock(ctlr);
|
|
setfilter(ether, ctlr);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static void
|
|
setbit(Dp8390 *ctlr, int bit, int on)
|
|
{
|
|
int i, h;
|
|
|
|
i = bit/8;
|
|
h = bit%8;
|
|
if(on){
|
|
if(++(ctlr->mref[bit]) == 1)
|
|
ctlr->mar[i] |= 1<<h;
|
|
} else {
|
|
if(--(ctlr->mref[bit]) <= 0){
|
|
ctlr->mref[bit] = 0;
|
|
ctlr->mar[i] &= ~(1<<h);
|
|
}
|
|
}
|
|
}
|
|
|
|
static uchar reverse[64];
|
|
|
|
static void
|
|
multicast(void* arg, uchar *addr, int on)
|
|
{
|
|
Ether *ether;
|
|
Dp8390 *ctlr;
|
|
int i;
|
|
ulong h;
|
|
|
|
ether = arg;
|
|
ctlr = ether->ctlr;
|
|
if(reverse[1] == 0){
|
|
for(i = 0; i < 64; i++)
|
|
reverse[i] = ((i&1)<<5) | ((i&2)<<3) | ((i&4)<<1)
|
|
| ((i&8)>>1) | ((i&16)>>3) | ((i&32)>>5);
|
|
}
|
|
|
|
/*
|
|
* change filter bits
|
|
*/
|
|
h = ethercrc(addr, 6);
|
|
ilock(ctlr);
|
|
setbit(ctlr, reverse[h&0x3f], on);
|
|
setfilter(ether, ctlr);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static void
|
|
attach(Ether* ether)
|
|
{
|
|
Dp8390 *ctlr;
|
|
uchar r;
|
|
|
|
ctlr = ether->ctlr;
|
|
|
|
/*
|
|
* Enable the chip for transmit/receive.
|
|
* The init routine leaves the chip in monitor
|
|
* mode. Clear the missed-packet counter, it
|
|
* increments while in monitor mode.
|
|
* Sometimes there's an interrupt pending at this
|
|
* point but there's nothing in the Isr, so
|
|
* any pending interrupts are cleared and the
|
|
* mask of acceptable interrupts is enabled here.
|
|
*/
|
|
r = Ab;
|
|
if(ether->prom)
|
|
r |= Pro;
|
|
if(ether->nmaddr)
|
|
r |= Am;
|
|
ilock(ctlr);
|
|
regw(ctlr, Isr, 0xFF);
|
|
regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
|
|
regw(ctlr, Rcr, r);
|
|
r = regr(ctlr, Ref2);
|
|
regw(ctlr, Tcr, LpbkNORMAL);
|
|
iunlock(ctlr);
|
|
USED(r);
|
|
}
|
|
|
|
static void
|
|
disable(Dp8390* ctlr)
|
|
{
|
|
int timo;
|
|
|
|
/*
|
|
* Stop the chip. Set the Stp bit and wait for the chip
|
|
* to finish whatever was on its tiny mind before it sets
|
|
* the Rst bit.
|
|
* The timeout is needed because there may not be a real
|
|
* chip there if this is called when probing for a device
|
|
* at boot.
|
|
*/
|
|
regw(ctlr, Cr, Page0|RdABORT|Stp);
|
|
regw(ctlr, Rbcr0, 0);
|
|
regw(ctlr, Rbcr1, 0);
|
|
for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--)
|
|
;
|
|
}
|
|
|
|
int
|
|
dp8390reset(Ether* ether)
|
|
{
|
|
Dp8390 *ctlr;
|
|
|
|
ctlr = ether->ctlr;
|
|
|
|
/*
|
|
* This is the initialisation procedure described
|
|
* as 'mandatory' in the datasheet, with references
|
|
* to the 3C503 technical reference manual.
|
|
*/
|
|
disable(ctlr);
|
|
if(ctlr->width != 1)
|
|
regw(ctlr, Dcr, Ft4WORD|Ls|Wts);
|
|
else
|
|
regw(ctlr, Dcr, Ft4WORD|Ls);
|
|
|
|
regw(ctlr, Rbcr0, 0);
|
|
regw(ctlr, Rbcr1, 0);
|
|
|
|
regw(ctlr, Tcr, LpbkNIC);
|
|
regw(ctlr, Rcr, Mon);
|
|
|
|
/*
|
|
* Init the ring hardware and software ring pointers.
|
|
* Can't initialise ethernet address as it may not be
|
|
* known yet.
|
|
*/
|
|
ringinit(ctlr);
|
|
regw(ctlr, Tpsr, ctlr->tstart);
|
|
|
|
/*
|
|
* Clear any pending interrupts and mask then all off.
|
|
*/
|
|
regw(ctlr, Isr, 0xFF);
|
|
regw(ctlr, Imr, 0);
|
|
|
|
/*
|
|
* Leave the chip initialised,
|
|
* but in monitor mode.
|
|
*/
|
|
regw(ctlr, Cr, Page0|RdABORT|Sta);
|
|
|
|
/*
|
|
* Set up the software configuration.
|
|
*/
|
|
ether->attach = attach;
|
|
ether->transmit = transmit;
|
|
ether->interrupt = interrupt;
|
|
ether->ifstat = 0;
|
|
|
|
ether->promiscuous = promiscuous;
|
|
ether->multicast = multicast;
|
|
ether->arg = ether;
|
|
|
|
return 0;
|
|
}
|