📄 etherga620.c
字号:
}}static voidga620interrupt(Ureg*, void* arg){ int csr, ie, work; Ctlr *ctlr; Ether *edev; uvlong tsc0, tsc1; edev = arg; ctlr = edev->ctlr; if(!(csr32r(ctlr, Mhc) & Is)) return; cycles(&tsc0); ctlr->interrupts++; csr32w(ctlr, Hi, 1); ie = 0; work = 0; while(ie < 2){ if(ctlr->rrrci != ctlr->rrrpi[0]){ ga620receive(edev); work = 1; } if(_ga620transmit(edev) != 0) work = 1; csr = csr32r(ctlr, Eci); if(csr != ctlr->epi[0]){ ga620event(ctlr, csr, ctlr->epi[0]); work = 1; } if(ctlr->nrsr <= NrsrLO) ga620replenish(ctlr); if(work == 0){ if(ie == 0) csr32w(ctlr, Hi, 0); ie++; } work = 0; } cycles(&tsc1); ctlr->ticks += tsc1-tsc0;}static voidga620lmw(Ctlr* ctlr, int addr, int* data, int len){ int i, l, lmw, v; /* * Write to or clear ('data' == nil) 'len' bytes of the NIC * local memory at address 'addr'. * The destination address and count should be 32-bit aligned. */ v = 0; while(len > 0){ /* * 1) Set the window. The (Lmwsz-1) bits are ignored * in Wba when accessing through the local memory window; * 2) Find the minimum of how many bytes still to * transfer and how many left in this window; * 3) Create the offset into the local memory window in the * shared memory space then copy (or zero) the data; * 4) Bump the counts. */ csr32w(ctlr, Wba, addr); l = ROUNDUP(addr+1, Lmwsz) - addr; if(l > len) l = len; lmw = Lmw + (addr & (Lmwsz-1)); for(i = 0; i < l; i += 4){ if(data != nil) v = *data++; csr32w(ctlr, lmw+i, v); } len -= l; addr += l; }}static intga620init(Ether* edev){ Ctlr *ctlr; Host64 host64; int csr, ea, i, flags; ctlr = edev->ctlr; /* * Load the MAC address. */ ea = (edev->ea[0]<<8)|edev->ea[1]; csr32w(ctlr, Mac, ea); ea = (edev->ea[2]<<24)|(edev->ea[3]<<16)|(edev->ea[4]<<8)|edev->ea[5]; csr32w(ctlr, Mac+4, ea); /* * General Information Block. */ ctlr->gib = malloc(sizeof(Gib)); sethost64(&host64, ctlr->gib); csr32w(ctlr, Gip, host64.hi); csr32w(ctlr, Gip+4, host64.lo); /* * Event Ring. * This is located in host memory. Allocate the ring, * tell the NIC where it is and initialise the indices. */ ctlr->er = malign(sizeof(Ere)*Ner); sethost64(&ctlr->gib->ercb.addr, ctlr->er); sethost64(&ctlr->gib->epp, ctlr->epi); csr32w(ctlr, Eci, 0); /* * Command Ring. * This is located in the General Communications Region * and so the value placed in the Rcb is unused, the NIC * knows where it is. Stick in the value according to * the datasheet anyway. * Initialise the ring and indices. */ ctlr->gib->crcb.addr.lo = Cr-0x400; for(i = 0; i < Ncr*4; i += 4) csr32w(ctlr, Cr+i, 0); csr32w(ctlr, Cpi, 0); csr32w(ctlr, Cci, 0); /* * Send Ring. * This ring is either in NIC memory at a fixed location depending * on how big the ring is or it is in host memory. If in NIC * memory it is accessed via the Local Memory Window; with a send * ring size of 128 the window covers the whole ring and then need * only be set once: * ctlr->sr = (uchar*)ctlr->nic+Lmw; * ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr); * ctlr->gib->srcb.addr.lo = Sr; * There is nowhere in the Sbd to hold the Block* associated * with this entry so an external array must be kept. */ ctlr->sr = malign(sizeof(Sbd)*Nsr); sethost64(&ctlr->gib->srcb.addr, ctlr->sr); if(ctlr->hardwarecksum) flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing; else flags = HostRing; if(ctlr->coalupdateonly) flags |= CoalUpdateOnly; ctlr->gib->srcb.control = (Nsr<<16)|flags; sethost64(&ctlr->gib->scp, ctlr->sci); csr32w(ctlr, Spi, 0); ctlr->srb = malloc(sizeof(Block*)*Nsr); /* * Receive Standard Ring. */ ctlr->rsr = malign(sizeof(Rbd)*Nrsr); sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr); if(ctlr->hardwarecksum) flags = TcpUdpCksum|NoPseudoHdrCksum; else flags = 0; ctlr->gib->rsrcb.control = ((ETHERMAXTU+4)<<16)|flags; csr32w(ctlr, Rspi, 0); /* * Jumbo and Mini Rings. Unused for now. */ ctlr->gib->rjrcb.control = RingDisabled; ctlr->gib->rmrcb.control = RingDisabled; /* * Receive Return Ring. * This is located in host memory. Allocate the ring, * tell the NIC where it is and initialise the indices. */ ctlr->rrr = malign(sizeof(Rbd)*Nrrr); sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr); ctlr->gib->rrrcb.control = (Nrrr<<16)|0; sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi); ctlr->rrrci = 0; /* * Refresh Stats Pointer. * For now just point it at the existing statistics block. */ sethost64(&ctlr->gib->rsp, ctlr->gib->statistics); /* * DMA configuration. * Use the recommended values. */ csr32w(ctlr, DMArc, 0x80); csr32w(ctlr, DMAwc, 0x80); /* * Transmit Buffer Ratio. * Set to 1/3 of available buffer space (units are 1/64ths) * if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC). */ if(NrjrHI > 0 || Nsr > 128) csr32w(ctlr, Tbr, 64/3); else csr32w(ctlr, Tbr, 4); /* * Tuneable parameters. * These defaults are based on the tuning hints in the Alteon * Host/NIC Software Interface Definition and example software. */ ctlr->rct = 1/*100*/; csr32w(ctlr, Rct, ctlr->rct); ctlr->sct = 0; csr32w(ctlr, Sct, ctlr->sct); ctlr->st = 1000000; csr32w(ctlr, St, ctlr->st); ctlr->smcbd = Nsr/4; csr32w(ctlr, SmcBD, ctlr->smcbd); ctlr->rmcbd = 4/*6*/; csr32w(ctlr, RmcBD, ctlr->rmcbd); /* * Enable DMA Assist Logic. */ csr = csr32r(ctlr, DMAas) & ~0x03; csr32w(ctlr, DMAas, csr|0x01); /* * Link negotiation. * The bits are set here but the NIC must be given a command * once it is running to set negotiation in motion. */ csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref); csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB); /* * A unique index for this controller and the maximum packet * length expected. * For now only standard packets are expected. */ csr32w(ctlr, Ifx, 1); csr32w(ctlr, IfMTU, ETHERMAXTU+4); /* * Enable Interrupts. * There are 3 ways to mask interrupts - a bit in the Mhc (which * is already cleared), the Mi register and the Hi mailbox. * Writing to the Hi mailbox has the side-effect of clearing the * PCI interrupt. */ csr32w(ctlr, Mi, 0); csr32w(ctlr, Hi, 0); /* * Start the firmware. */ csr32w(ctlr, CPUApc, tigon2FwStartAddr); csr = csr32r(ctlr, CPUAstate) & ~CPUhalt; csr32w(ctlr, CPUAstate, csr); return 0;}static intat24c32io(Ctlr* ctlr, char* op, int data){ char *lp, *p; int i, loop, mlc, r; mlc = csr32r(ctlr, Mlc); r = 0; loop = -1; lp = nil; for(p = op; *p != '\0'; p++){ switch(*p){ default: return -1; case ' ': continue; case ':': /* start of 8-bit loop */ if(lp != nil) return -1; lp = p; loop = 7; continue; case ';': /* end of 8-bit loop */ if(lp == nil) return -1; loop--; if(loop >= 0) p = lp; else lp = nil; continue; case 'C': /* assert clock */ mlc |= EEclk; break; case 'c': /* deassert clock */ mlc &= ~EEclk; break; case 'D': /* next bit in 'data' byte */ if(loop < 0) return -1; if(data & (1<<loop)) mlc |= EEdo; else mlc &= ~EEdo; break; case 'E': /* enable data output */ mlc |= EEdoe; break; case 'e': /* disable data output */ mlc &= ~EEdoe; break; case 'I': /* input bit */ i = (csr32r(ctlr, Mlc) & EEdi) != 0; if(loop >= 0) r |= (i<<loop); else r = i; continue; case 'O': /* assert data output */ mlc |= EEdo; break; case 'o': /* deassert data output */ mlc &= ~EEdo; break; } csr32w(ctlr, Mlc, mlc); microdelay(1); } if(loop >= 0) return -1; return r;}static intat24c32r(Ctlr* ctlr, int addr){ int data; /* * Read a byte at address 'addr' from the Atmel AT24C32 * Serial EEPROM. The 2-wire EEPROM access is controlled * by 4 bits in Mlc. See the AT24C32 datasheet for * protocol details. */ /* * Start condition - a high to low transition of data * with the clock high must precede any other command. */ at24c32io(ctlr, "OECoc", 0); /* * Perform a random read at 'addr'. A dummy byte * write sequence is performed to clock in the device * and data word addresses (0 and 'addr' respectively). */ data = -1; if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0) goto stop; if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0) goto stop; if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0) goto stop; /* * Now send another start condition followed by a * request to read the device. The EEPROM responds * by clocking out the data. */ at24c32io(ctlr, "OECoc", 0); if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0) goto stop; data = at24c32io(ctlr, ":CIc;", 0xA1);stop: /* * Stop condition - a low to high transition of data * with the clock high is a stop condition. After a read * sequence, the stop command will place the EEPROM in * a standby power mode. */ at24c32io(ctlr, "oECOc", 0); return data;}static intga620detach(Ctlr* ctlr){ int timeo; /* * Hard reset (don't know which endian so catch both); * enable for little-endian mode; * wait for code to be loaded from serial EEPROM or flash; * make sure CPU A is halted. */ csr32w(ctlr, Mhc, (Hr<<24)|Hr); csr32w(ctlr, Mhc, ((Eews|Ci)<<24)|(Eews|Ci)); microdelay(1); for(timeo = 0; timeo < 500000; timeo++){ if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie) break; microdelay(1); } if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie) return -1; csr32w(ctlr, CPUAstate, CPUhalt); /* * After reset, CPU B seems to be stuck in 'CPUrf'. * Worry about it later. */ csr32w(ctlr, CPUBstate, CPUhalt); return 0;}static voidga620shutdown(Ether* ether){print("ga620shutdown\n"); ga620detach(ether->ctlr);}static intga620reset(Ctlr* ctlr){ int cls, csr, i, r; if(ga620detach(ctlr) < 0) return -1; /* * Tigon 2 PCI NICs have 512KB SRAM per bank. * Clear out any lingering serial EEPROM state * bits. */ csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask); csr32w(ctlr, Mlc, SRAM512|csr); csr = csr32r(ctlr, Mc); csr32w(ctlr, Mc, SyncSRAM|csr); /* * Initialise PCI State register. * If PCI Write-and-Invalidate is enabled set the max write DMA * value to the host cache-line size (32 on Pentium or later). */ csr = csr32r(ctlr, Ps) & (PCI32|PCI66); csr |= PCIwcmd|PCIrcmd|PCImrm; if(ctlr->pcidev->pcr & 0x0010){ cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4; if(cls != 32) pcicfgw8(ctlr->pcidev, PciCLS, 32/4); csr |= PCIwm32; } csr32w(ctlr, Ps, csr); /* * Operating Mode. */ csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD); /* * Snarf the MAC address from the serial EEPROM. */ for(i = 0; i < Eaddrlen; i++){ if((r = at24c32r(ctlr, 0x8E+i)) == -1) return -1; ctlr->ea[i] = r; } /* * Load the firmware. */ ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen); ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen); ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen); ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen); ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen); return 0;}static voidga620pci(void){ void *mem; Pcidev *p; Ctlr *ctlr; p = nil; while(p = pcimatch(p, 0, 0)){ if(p->ccrb != 0x02 || p->ccru != 0) continue; switch((p->did<<16)|p->vid){ default: continue; case (0x620A<<16)|0x1385: /* Netgear GA620 */ case (0x630A<<16)|0x1385: /* Netgear GA620T */ case (0x0001<<16)|0x12AE: /* Alteon Acenic fiber * and DEC DEGPA-SA */ case (0x0002<<16)|0x12AE: /* Alteon Acenic copper */ case (0x0009<<16)|0x10A9: /* SGI Acenic */ break; } mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size); if(mem == 0){ print("ga620: can't map %8.8luX\n", p->mem[0].bar); continue; } ctlr = malloc(sizeof(Ctlr)); ctlr->port = p->mem[0].bar & ~0x0F; ctlr->pcidev = p; ctlr->id = (p->did<<16)|p->vid; ctlr->nic = mem; if(ga620reset(ctlr)){ free(ctlr); continue; } if(ctlrhead != nil) ctlrtail->next = ctlr; else ctlrhead = ctlr; ctlrtail = ctlr; }}static intga620pnp(Ether* edev){ Ctlr *ctlr; uchar ea[Eaddrlen]; if(ctlrhead == nil) ga620pci(); /* * Any adapter matches if no edev->port is supplied, * otherwise the ports must match. */ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ if(ctlr->active) continue; if(edev->port == 0 || edev->port == ctlr->port){ ctlr->active = 1; break; } } if(ctlr == nil) return -1; edev->ctlr = ctlr; edev->port = ctlr->port; edev->irq = ctlr->pcidev->intl; edev->tbdf = ctlr->pcidev->tbdf; edev->mbps = 1000; /* * Check if the adapter's station address is to be overridden. * If not, read it from the EEPROM and set in ether->ea prior to * loading the station address in the hardware. */ memset(ea, 0, Eaddrlen); if(memcmp(ea, edev->ea, Eaddrlen) == 0) memmove(edev->ea, ctlr->ea, Eaddrlen); ga620init(edev); /* * Linkage to the generic ethernet driver. */ edev->attach = ga620attach; edev->transmit = ga620transmit; edev->interrupt = ga620interrupt; edev->ifstat = ga620ifstat; edev->ctl = ga620ctl; edev->shutdown = ga620shutdown; edev->arg = edev; edev->promiscuous = nil; return 0;}voidetherga620link(void){ addethercard("GA620", ga620pnp);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -