From 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 21:39:35 +0000 Subject: 20060303 --- os/pc/ether79c960.c | 523 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 523 insertions(+) create mode 100644 os/pc/ether79c960.c (limited to 'os/pc/ether79c960.c') diff --git a/os/pc/ether79c960.c b/os/pc/ether79c960.c new file mode 100644 index 00000000..f74574cd --- /dev/null +++ b/os/pc/ether79c960.c @@ -0,0 +1,523 @@ +/* + * AM79C960 + * PCnet Single-Chip Ethernet Controller for ISA Bus + * To do: + * only issue transmit interrupt if necessary? + * dynamically increase rings as necessary? + * use Blocks as receive buffers? + * currently hardwires 10Base-T + */ +#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" + +#define chatty 1 +#define DPRINT if(chatty)print + +enum { + Lognrdre = 6, + Nrdre = (1<ctlr; + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + port = ether->port; + outs(port+Rdp, Iena|Strt); + iunlock(ctlr); +} + +static void +ringinit(Ctlr* ctlr) +{ + int i, x; + + /* + * Initialise the receive and transmit buffer rings. The ring + * entries must be aligned on 16-byte boundaries. + * + * This routine is protected by ctlr->init. + */ + if(ctlr->rdr == 0) + ctlr->rdr = xspanalloc(Nrdre*sizeof(Rdre), 0x10, 0); + if(ctlr->rrb == 0) + ctlr->rrb = xalloc(Nrdre*Rbsize); + + x = PADDR(ctlr->rrb); + if ((x >> 24)&0xFF) + panic("ether79c960: address>24bit"); + for(i = 0; i < Nrdre; i++){ + ctlr->rdr[i].rbadr = x&0xFFFF; + ctlr->rdr[i].rmd1 = Own|(x>>16)&0xFF; + x += Rbsize; + ctlr->rdr[i].rmd2 = 0xF000|-Rbsize&0x0FFF; + ctlr->rdr[i].rmd3 = 0; + } + ctlr->rdrx = 0; + + if(ctlr->tdr == 0) + ctlr->tdr = xspanalloc(Ntdre*sizeof(Tdre), 0x10, 0); + if(ctlr->trb == 0) + ctlr->trb = xalloc(Ntdre*Rbsize); + + x = PADDR(ctlr->trb); + if ((x >> 24)&0xFF) + panic("ether79c960: address>24bit"); + for(i = 0; i < Ntdre; i++){ + ctlr->tdr[i].tbadr = x&0xFFFF; + ctlr->tdr[i].tmd1 = (x>>16)&0xFF; + x += Rbsize; + ctlr->tdr[i].tmd2 = 0xF000|-Rbsize&0x0FFF; + } + ctlr->tdrx = 0; +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + int port, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Put the chip into promiscuous mode. First we must wait until + * anyone transmitting is done, then we can stop the chip and put + * it in promiscuous mode. Restarting is made harder by the chip + * reloading the transmit and receive descriptor pointers with their + * base addresses when Strt is set (unlike the older Lance chip), + * so the rings must be re-initialised. + */ + ilock(ctlr); + if(ctlr->init){ + iunlock(ctlr); + return; + } + ctlr->init = 1; + iunlock(ctlr); + + outs(port+Rdp, Stop); + + outs(port+Rap, 15); + x = ins(port+Rdp) & ~Prom; + if(on) + x |= Prom; /* BUG: multicast ... */ + outs(port+Rdp, x); + outs(port+Rap, 0); + + ringinit(ctlr); + + ilock(ctlr); + ctlr->init = 0; + outs(port+Rdp, Iena|Strt); + iunlock(ctlr); +} + +static int +owntdre(void* arg) +{ + return (((Tdre*)arg)->tmd1 & Own) == 0; +} + +static void +txstart(Ether *ether) +{ + int port; + Ctlr *ctlr; + Tdre *tdre; + Etherpkt *pkt; + Block *bp; + int n; + + port = ether->port; + ctlr = ether->ctlr; + + if(ctlr->init) + return; + + /* + * Take the next transmit buffer, if it is free. + */ + tdre = &ctlr->tdr[ctlr->tdrx]; + if(owntdre(tdre) == 0) + return; + bp = qget(ether->oq); + if(bp == nil) + return; + + /* + * Copy the packet to the transmit buffer and fill in our + * source ethernet address. There's no need to pad to ETHERMINTU + * here as we set ApadXmit in CSR4. + */ + n = BLEN(bp); + pkt = KADDR(tdre->tbadr|(tdre->tmd1&0xFF)<<16); + memmove(pkt->d, bp->rp, n); + memmove(pkt->s, ether->ea, sizeof(pkt->s)); + freeb(bp); + + /* + * Give ownership of the descriptor to the chip, increment the + * software ring descriptor pointer and tell the chip to poll. + */ + tdre->tmd3 = 0; + tdre->tmd2 = 0xF000|(-n)&0x0FFF; + tdre->tmd1 |= Own|Stp|Enp; + ctlr->tdrx = NEXT(ctlr->tdrx, Ntdre); + outs(port+Rdp, Iena|Tdmd); + + ether->outpackets++; +} + +static void +transmit(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, csr0, status; + Ctlr *ctlr; + Rdre *rdre; + Etherpkt *pkt; + Block *bp; + int len; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + csr0 = ins(port+Rdp); + outs(port+Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena); + if(csr0 & (Babl|Miss|Merr)) + print("AMD70C960#%d: csr0 = 0x%uX\n", ether->ctlrno, csr0); + + /* + * Receiver interrupt: run round the descriptor ring logging + * errors and passing valid receive data up to the higher levels + * until we encounter a descriptor still owned by the chip. + */ + if(csr0 & Rint){ + rdre = &ctlr->rdr[ctlr->rdrx]; + while(((status = rdre->rmd1) & Own) == 0){ + if(status & RxErr){ + if(status & RxBuff) + ether->buffs++; + if(status & RxCrc) + ether->crcs++; + if(status & RxOflo) + ether->overflows++; + } + else { + len = (rdre->rmd3 & 0x0FFF)-4; + if((bp = iallocb(len)) != nil){ + ether->inpackets++; + pkt = KADDR(rdre->rbadr|(rdre->rmd1&0xFF)<<16); + memmove(bp->wp, pkt, len); + bp->wp += len; + etheriq(ether, bp, 1); + } + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + rdre->rmd3 = 0; + rdre->rmd2 = 0xF000|-Rbsize&0x0FFF; + rdre->rmd1 |= Own; + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + rdre = &ctlr->rdr[ctlr->rdrx]; + } + } + + /* + * Transmitter interrupt: start next block if waiting for free descriptor. + */ + if(csr0 & Tint){ + lock(ctlr); + txstart(ether); + unlock(ctlr); + } +} + +static int +reset(Ether* ether) +{ + int port, x, i; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + if(ether->port == 0) + ether->port = 0x300; + if(ether->irq == 0) + ether->irq = 10; + if(ether->irq == 2) + ether->irq = 9; + if(ether->dma == 0) + ether->dma = 5; + port = ether->port; + + if(port == 0 || ether->dma == 0) + return -1; + + /* + * Allocate a controller structure and start to fill in the + * initialisation block (must be DWORD aligned). + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + ilock(ctlr); + ctlr->init = 1; + + /* + * Set the auto pad transmit in CSR4. + */ + /*outs(port+Rdp, 0x00);/**/ + ins(port+Sreset); /**/ + delay(1); + outs(port+Rap, 0); + outs(port+Rdp, Stop); + + outs(port+Rap, 4); + x = ins(port+Rdp) & 0xFFFF; + outs(port+Rdp, ApadXmt|x); + + outs(port+Rap, 0); + + /* + * Check if we are going to override the adapter's station address. + * If not, read it from the I/O-space and set in ether->ea prior to loading the + * station address in the initialisation block. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i=0; i<6; i++) + ether->ea[i] = inb(port + Aprom + i); + } + + ctlr->iblock.rlen = Lognrdre<<5; + ctlr->iblock.tlen = Logntdre<<5; + memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr)); + + ringinit(ctlr); + + x = PADDR(ctlr->rdr); + ctlr->iblock.rdra0 = x&0xFFFF; + ctlr->iblock.rdra16 = (x >> 16)&0xFF; + x = PADDR(ctlr->tdr); + ctlr->iblock.tdra0 = x&0xFFFF; + ctlr->iblock.tdra16 = (x >> 16)&0xFF; + + /* + * set the DMA controller to cascade mode for bus master + */ + switch(ether->dma){ + case 5: + outb(0xd6, 0xc1); outb(0xd4, 1); break; + case 6: + outb(0xd6, 0xc2); outb(0xd4, 2); break; + case 7: + outb(0xd6, 0xc3); outb(0xd4, 3); break; + } + + /* + * Ensure 10Base-T (for now) + */ + ctlr->iblock.mode = TenBaseT; + outs(port+Rap, 2); + x = ins(port+Idp); + x &= ~Isamedia; + x |= Isa10; + x |= Isaawake; + outs(port+Idp, x); + + /* + * Point the chip at the initialisation block and tell it to go. + * Mask the Idon interrupt and poll for completion. Strt and interrupt + * enables will be set later when we're ready to attach to the network. + */ + x = PADDR(&ctlr->iblock); + if((x>>24)&0xFF) + panic("ether79c960: address>24bit"); + outs(port+Rap, 1); + outs(port+Rdp, x & 0xFFFF); + outs(port+Rap, 2); + outs(port+Rdp, (x>>16) & 0xFF); + outs(port+Rap, 3); + outs(port+Rdp, Idonm); + outs(port+Rap, 0); + outs(port+Rdp, Init); + + while((ins(port+Rdp) & Idon) == 0) + ; + outs(port+Rdp, Idon|Stop); + ctlr->init = 0; + iunlock(ctlr); + + ether->port = port; + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = 0; + + ether->promiscuous = promiscuous; + ether->arg = ether; + + return 0; +} + +void +ether79c960link(void) +{ + addethercard("AMD79C960", reset); +} -- cgit v1.2.3