diff --git a/dev/etherpkt.hh b/dev/etherpkt.hh index c91322526..312d98f64 100644 --- a/dev/etherpkt.hh +++ b/dev/etherpkt.hh @@ -36,11 +36,68 @@ #include #include "sim/host.hh" - #include "base/refcnt.hh" +#define EADDR_LEN 6 + class Checkpoint; +struct pseudo_header +{ + uint32_t src_ip_addr; + uint32_t dest_ip_addr; + uint16_t protocol; + uint16_t len; +}; + +/** Ethernet header struct for casting purposes */ +struct eth_header +{ + uint8_t dest[EADDR_LEN]; + uint8_t src[EADDR_LEN]; + uint16_t type; +}; + +struct ip_header +{ + uint8_t vers_len; + uint8_t service_type; + uint16_t dgram_len; + uint16_t ID; + uint16_t flags_frag_offset; + uint8_t TTL; + uint8_t protocol; + uint16_t hdr_chksum; + uint32_t src_ip_addr; + uint32_t dest_ip_addr; + uint8_t *options; + uint8_t *transport_header; +}; + +struct tcp_header +{ + uint16_t src_port_num; + uint16_t dest_port_num; + uint32_t seq_num; + uint32_t ack_num; + uint8_t hdr_len; + uint8_t flags; + uint16_t rcv_window; + uint16_t chksum; + uint16_t urgent; + uint8_t *options; + uint8_t *data; +}; + +struct udp_header +{ + uint16_t src_port_num; + uint16_t dest_port_num; + uint16_t len; + uint16_t chksum; + uint8_t *data; +}; + /* * Reference counted class containing ethernet packet data */ @@ -61,6 +118,16 @@ class EtherPacket : public RefCounted bool IsMulticast() { return data[0] == 0x01; } bool IsBroadcast() { return data[0] == 0xff; } + ip_header *getIpHdr() { return (ip_header *) (data + 14); } + + void *getTransportHdr() { + ip_header *ip = getIpHdr(); + return (void *) (ip + (ip->vers_len & 0xf)); + } + + + typedef RefCountingPtr PacketPtr; + void serialize(std::ostream &os); void unserialize(Checkpoint *cp, const std::string §ion); }; diff --git a/dev/ns_gige.cc b/dev/ns_gige.cc new file mode 100644 index 000000000..e3c31597e --- /dev/null +++ b/dev/ns_gige.cc @@ -0,0 +1,1933 @@ +/* + * Copyright (c) 2003 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +/* @file + * Device module for modelling the National Semiconductor + * DP83820 ethernet controller. Does not support priority queueing + */ +#include +#include +#include + +#include "base/inet.hh" +#include "cpu/exec_context.hh" +#include "cpu/intr_control.hh" +#include "dev/dma.hh" +#include "dev/ns_gige.hh" +#include "dev/etherlink.hh" +#include "mem/functional_mem/memory_control.hh" +#include "mem/functional_mem/physical_memory.hh" +#include "sim/builder.hh" +#include "sim/host.hh" +#include "sim/sim_stats.hh" +#include "targetarch/vtophys.hh" + +using namespace std; + +/////////////////////////////////////////////////////////////////////// +// +// EtherDev PCI Device +// +EtherDev::EtherDev(const string &_name, DmaEngine *de, bool use_interface, + IntrControl *i, MemoryController *mmu, PhysicalMemory *pmem, + PCIConfigAll *cf, PciConfigData *cd, Tsunami *t, uint32_t bus, + uint32_t dev, uint32_t func, bool rx_filter, + const int eaddr[6], Tick tx_delay, Tick rx_delay, Addr addr, + Addr mask) + : PciDev(_name, mmu, cf, cd, bus, dev, func), tsunami(t), + addr(addr), mask(mask), txPacketLen(0), + txPacketBufPtr(NULL), rxPacketBufPtr(NULL), rxDescBufPtr(NULL), + fragLen(0), rxCopied(0), txState(txIdle), CTDD(false), txFifoCnt(0), + txFifoAvail(MAX_TX_FIFO_SIZE), txHalt(false), txPacketFlag(false), + txFragPtr(0), txDescCnt(0), rxState(rxIdle), CRDD(false), + rxPktBytes(0), rxFifoCnt(0), rxHalt(false), rxPacketFlag(false), + rxFragPtr(0), rxDescCnt(0), extstsEnable(false), maxTxBurst(0), + maxRxBurst(0), physmem(pmem), + rxDescDoneCB(this), rxDoneCB(this), txDescDoneCB(this), txDoneCB(this), + dma(de), readRequest(use_interface), writeRequest(use_interface), + readDescRequest(use_interface), writeDescRequest(use_interface), + interface(NULL), intctrl(i), txDelay(tx_delay), rxDelay(rx_delay), + txEvent(this), cpuPendingIntr(false), rxFilterEnable(rx_filter), + acceptBroadcast(false), acceptMulticast(false), acceptUnicast(false), + acceptPerfect(false), acceptArp(false) +{ + tsunami->ethernet = this; + + memset(®s, 0, sizeof(regs)); + regsReset(); + regs.perfectMatch[0] = eaddr[0]; + regs.perfectMatch[1] = eaddr[1]; + regs.perfectMatch[2] = eaddr[2]; + regs.perfectMatch[3] = eaddr[3]; + regs.perfectMatch[4] = eaddr[4]; + regs.perfectMatch[5] = eaddr[5]; + +} + +EtherDev::~EtherDev() +{} + +void +EtherDev::regStats() +{ + txBytes + .name(name() + ".txBytes") + .desc("Bytes Transmitted") + .prereq(txBytes) + ; + + rxBytes + .name(name() + ".rxBytes") + .desc("Bytes Received") + .prereq(rxBytes) + ; + + txPackets + .name(name() + ".txPackets") + .desc("Number of Packets Transmitted") + .prereq(txBytes) + ; + + rxPackets + .name(name() + ".rxPackets") + .desc("Number of Packets Received") + .prereq(rxBytes) + ; + + txBandwidth + .name(name() + ".txBandwidth") + .desc("Transmit Bandwidth (bits/s)") + .precision(0) + .prereq(txBytes) + ; + + rxBandwidth + .name(name() + ".rxBandwidth") + .desc("Receive Bandwidth (bits/s)") + .precision(0) + .prereq(rxBytes) + ; + + txPacketRate + .name(name() + ".txPPS") + .desc("Packet Tranmission Rate (packets/s)") + .precision(0) + .prereq(txBytes) + ; + + rxPacketRate + .name(name() + ".rxPPS") + .desc("Packet Reception Rate (packets/s)") + .precision(0) + .prereq(rxBytes) + ; + + txBandwidth = txBytes * Statistics::constant(8) / simSeconds; + rxBandwidth = rxBytes * Statistics::constant(8) / simSeconds; + txPacketRate = txPackets / simSeconds; + rxPacketRate = rxPackets / simSeconds; +} + +void +EtherDev::ReadConfig(int offset, int size, uint8_t *data) +{ + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::ReadConfig(offset, size, data); + else { + panic("need to do this\n"); + } +} + +void +EtherDev::WriteConfig(int offset, int size, uint32_t data) +{ + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::WriteConfig(offset, size, data); + else + panic("Need to do that\n"); +} + +Fault +EtherDev::read(MemReqPtr req, uint8_t *data) +{ + DPRINTF(Ethernet, "read va=%#x size=%d\n", req->vaddr, req->size); + + Addr daddr = req->paddr - addr; + + if (daddr > LAST) + panic("Accessing reserved register"); + + switch (req->size) { + case sizeof(uint32_t): + { + uint32_t ® = *(uint32_t *)data; + + switch (daddr) { + case CR: + reg = regs.command; + reg &= ~(CR_RXD | CR_TXD | CR_TXR | CR_RXR); + break; + + case CFG: + reg = regs.config; + break; + + case MEAR: + reg = regs.mear; + break; + + case PTSCR: + reg = regs.ptscr; + break; + + case ISR: + reg = regs.isr; + regs.isr = 0; + break; + + case IMR: + reg = regs.imr; + break; + + case IER: + reg = regs.ier; + break; + + case IHR: + reg = regs.ihr; + break; + + case TXDP: + reg = regs.txdp; + break; + + case TXDP_HI: + reg = regs.txdp_hi; + break; + + case TXCFG: + reg = regs.txcfg; + break; + + case GPIOR: + reg = regs.gpior; + break; + + case RXDP: + reg = regs.rxdp; + break; + + case RXDP_HI: + reg = regs.rxdp_hi; + break; + + case RXCFG: + reg = regs.rxcfg; + break; + + case PQCR: + reg = regs.pqcr; + break; + + case WCSR: + reg = regs.wcsr; + break; + + case PCR: + reg = regs.pcr; + break; + + case RFCR: + reg = regs.rfcr; + break; + + case RFDR: + + switch (regs.rfcr & RFCR_RFADDR) { + case 0x000: + reg = regs.perfectMatch[1] << 8; + reg += regs.perfectMatch[0]; + break; + case 0x002: + reg = regs.perfectMatch[3] << 8; + reg += regs.perfectMatch[2]; + break; + case 0x004: + reg = regs.perfectMatch[5] << 8; + reg += regs.perfectMatch[4]; + break; + default: + panic("reading from RFDR for something for other than PMATCH!\n"); + //didn't implement other RFDR functionality b/c driver didn't use + } + break; + + case SRR: + reg = regs.srr; + break; + + case MIBC: + reg = regs.mibc; + reg &= ~(MIBC_MIBS | MIBC_ACLR); + break; + + case VRCR: + reg = regs.vrcr; + break; + + case VTCR: + reg = regs.vtcr; + break; + + case VDR: + reg = regs.vdr; + break; + + case CCSR: + reg = regs.ccsr; + break; + + case TBICR: + reg = regs.tbicr; + break; + + case TBISR: + reg = regs.tbisr; + break; + + case TANAR: + reg = regs.tanar; + break; + + case TANLPAR: + reg = regs.tanlpar; + break; + + case TANER: + reg = regs.taner; + break; + + case TESR: + reg = regs.tesr; + break; + + default: + panic("reading unimplemented register: addr = %#x", daddr); + } + + DPRINTF(Ethernet, "read from %#x: data=%d data=%#x\n", daddr, reg, reg); + } + break; + + default: + panic("accessing register with invalid size: addr=%#x, size=%d", + daddr, req->size); + } + + return No_Fault; +} + +Fault +EtherDev::write(MemReqPtr req, const uint8_t *data) +{ + DPRINTF(Ethernet, "write va=%#x size=%d\n", req->vaddr, req->size); + + Addr daddr = req->paddr - addr; + + if (daddr > LAST && daddr <= RESERVED) + panic("Accessing reserved register"); + + if (daddr > RESERVED) + panic("higher memory accesses not implemented!\n"); + + if (req->size == sizeof(uint32_t)) { + uint32_t reg = *(uint32_t *)data; + DPRINTF(Ethernet, "write data=%d data=%#x\n", reg, reg); + + switch (daddr) { + case CR: + regs.command = reg; + if ((reg & (CR_TXE | CR_TXD)) == (CR_TXE | CR_TXD)) { + txHalt = true; + } else if (reg & CR_TXE) { + if (txState == txIdle) + txKick(); + } else if (reg & CR_TXD) { + txHalt = true; + } + + if ((reg & (CR_RXE | CR_RXD)) == (CR_RXE | CR_RXD)) { + rxHalt = true; + } else if (reg & CR_RXE) { + if (rxState == rxIdle) { + rxKick(); + } + } else if (reg & CR_RXD) { + rxHalt = true; + } + + if (reg & CR_TXR) + txReset(); + + if (reg & CR_RXR) + rxReset(); + + if (reg & CR_SWI) + devIntrPost(ISR_SWI); + + if (reg & CR_RST) { + txReset(); + rxReset(); + regsReset(); + } + break; + + case CFG: + regs.config = reg; + if (reg & CFG_LNKSTS || reg & CFG_SPDSTS || reg & CFG_DUPSTS + || reg & CFG_RESERVED || reg & CFG_T64ADDR + || reg & CFG_PCI64_DET) + panic("writing to read-only or reserved CFG bits!\n"); + +#if 0 + if (reg & CFG_TBI_EN) ; + if (reg & CFG_MODE_1000) ; +#endif + + if (reg & CFG_AUTO_1000) + panic("CFG_AUTO_1000 not implemented!\n"); + +#if 0 + if (reg & CFG_PINT_DUPSTS || reg & CFG_PINT_LNKSTS || reg & CFG_PINT_SPDSTS) ; + if (reg & CFG_TMRTEST) ; + if (reg & CFG_MRM_DIS) ; + if (reg & CFG_MWI_DIS) ; +#endif + + if (reg & CFG_T64ADDR) + panic("CFG_T64ADDR is read only register!\n"); + + if (reg & CFG_PCI64_DET) + panic("CFG_PCI64_DET is read only register!\n"); + +#if 0 + if (reg & CFG_DATA64_EN) ; + if (reg & CFG_M64ADDR) ; + if (reg & CFG_PHY_RST) ; + if (reg & CFG_PHY_DIS) ; +#endif + + if (reg & CFG_EXTSTS_EN) + extstsEnable = true; + else + extstsEnable = false; + +#if 0 + if (reg & CFG_REQALG) ; + if (reg & CFG_SB) ; + if (reg & CFG_POW) ; + if (reg & CFG_EXD) ; + if (reg & CFG_PESEL) ; + if (reg & CFG_BROM_DIS) ; + if (reg & CFG_EXT_125) ; + if (reg & CFG_BEM) ; +#endif + break; + + case MEAR: + regs.mear = reg; + /* since phy is completely faked, MEAR_MD* don't matter + and since the driver never uses MEAR_EE*, they don't matter */ +#if 0 + if (reg & MEAR_EEDI) ; + if (reg & MEAR_EEDO) ; //this one is read only + if (reg & MEAR_EECLK) ; + if (reg & MEAR_EESEL) ; + if (reg & MEAR_MDIO) ; + if (reg & MEAR_MDDIR) ; + if (reg & MEAR_MDC) ; +#endif + break; + + case PTSCR: + regs.ptscr = reg; + /* these control BISTs for various parts of chip - we don't care or do */ + break; + + case ISR: /* writing to the ISR has no effect */ + panic("ISR is a read only register!\n"); + + case IMR: + regs.imr = reg; + devIntrChangeMask(); + break; + + case IER: + regs.ier = reg; + break; + + case IHR: + regs.ihr = reg; + /* not going to implement real interrupt holdoff */ + break; + + case TXDP: + regs.txdp = (reg & 0xFFFFFFFC); + assert(txState == txIdle); + CTDD = false; + break; + + case TXDP_HI: + regs.txdp_hi = reg; + break; + + case TXCFG: + regs.txcfg = reg; +#if 0 + if (reg & TXCFG_CSI) ; + if (reg & TXCFG_HBI) ; + if (reg & TXCFG_MLB) ; + if (reg & TXCFG_ATP) ; + if (reg & TXCFG_ECRETRY) ; /* this could easily be implemented, but + considering the network is just a fake + pipe, wouldn't make sense to do this */ + + if (reg & TXCFG_BRST_DIS) ; +#endif + +#if 0 /* current 2.6 driver doesn't use these. if we upgrade, may need these */ + if (reg & TXCFG_MXDMA1024) + maxTxBurst = 1024; + + if (reg & TXCFG_MXDMA8) + maxTxBurst = 8; + + if (reg & TXCFG_MXDMA16) + maxTxBurst = 16; + + if (reg & TXCFG_MXDMA32) + maxTxBurst = 32; + + if (reg & TXCFG_MXDMA64) + maxTxBurst = 64; + + if (reg & TXCFG_MXDMA128) + maxTxBurst = 128; + + if (reg & TXCFG_MXDMA256) + maxTxBurst = 256; +#endif + + if (reg & TXCFG_MXDMA512) + maxTxBurst = 512; + + break; + + case GPIOR: + regs.gpior = reg; + /* these just control general purpose i/o pins, don't matter */ + break; + + case RXCFG: + regs.rxcfg = reg; +#if 0 + if (reg & RXCFG_AEP) ; + if (reg & RXCFG_ARP) ; + if (reg & RXCFG_STRIPCRC) ; + if (reg & RXCFG_RX_RD) ; + if (reg & RXCFG_ALP) ; + if (reg & RXCFG_AIRL) ; +#endif + + if (reg & RXCFG_MXDMA512) + maxRxBurst = 512; + +#if 0 + if (reg & (RXCFG_DRTH | RXCFG_DRTH0)) ; +#endif + break; + + case PQCR: + /* there is no priority queueing used in the linux 2.6 driver */ + regs.pqcr = reg; + break; + + case WCSR: + /* not going to implement wake on LAN */ + regs.wcsr = reg; + break; + + case PCR: + /* not going to implement pause control */ + regs.pcr = reg; + break; + + case RFCR: + regs.rfcr = reg; + rxFilterEnable = (reg & RFCR_RFEN) ? true : false; + + acceptBroadcast = (reg & RFCR_AAB) ? true : false; + + acceptMulticast = (reg & RFCR_AAM) ? true : false; + + acceptUnicast = (reg & RFCR_AAU) ? true : false; + + acceptPerfect = (reg & RFCR_APM) ? true : false; + + acceptArp = (reg & RFCR_AARP) ? true : false; + + if (reg & RFCR_APAT) + panic("RFCR_APAT not implemented!\n"); + + if (reg & RFCR_MHEN || reg & RFCR_UHEN) + panic("hash filtering not implemented!\n"); + + if (reg & RFCR_ULM) + panic("RFCR_ULM not implemented!\n"); + + break; + + case RFDR: + panic("the driver never writes to RFDR, something is wrong!\n"); + + case BRAR: + panic("the driver never uses BRAR, something is wrong!\n"); + + case BRDR: + panic("the driver never uses BRDR, something is wrong!\n"); + + case SRR: + panic("SRR is read only register!\n"); + + case MIBC: + panic("the driver never uses MIBC, something is wrong!\n"); + + case VRCR: + regs.vrcr = reg; + break; + + case VTCR: + regs.vtcr = reg; + break; + + case VDR: + panic("the driver never uses VDR, something is wrong!\n"); + break; + + case CCSR: + /* not going to implement clockrun stuff */ + regs.ccsr = reg; + break; + + case TBICR: + regs.tbicr = reg; + if (reg & TBICR_MR_LOOPBACK) + panic("TBICR_MR_LOOPBACK never used, something wrong!\n"); + + if (reg & TBICR_MR_AN_ENABLE) { + regs.tanlpar = regs.tanar; + regs.tbisr |= (TBISR_MR_AN_COMPLETE | TBISR_MR_LINK_STATUS); + } + +#if 0 + if (reg & TBICR_MR_RESTART_AN) ; +#endif + + break; + + case TBISR: + panic("TBISR is read only register!\n"); + + case TANAR: + regs.tanar = reg; + if (reg & TANAR_PS2) + panic("this isn't used in driver, something wrong!\n"); + + if (reg & TANAR_PS1) + panic("this isn't used in driver, something wrong!\n"); + break; + + case TANLPAR: + panic("this should only be written to by the fake phy!\n"); + + case TANER: + panic("TANER is read only register!\n"); + + case TESR: + regs.tesr = reg; + break; + + default: + panic("thought i covered all the register, what is this? addr=%#x", + daddr); + } + } else + panic("Invalid Request Size"); + + return No_Fault; +} + +void +EtherDev::devIntrPost(uint32_t interrupts) +{ +DPRINTF(Ethernet, "interrupt posted intr=%x isr=%x imr=%x\n", + interrupts, regs.isr, regs.imr); + +if (interrupts & ISR_RESERVE) + panic("Cannot set a reserved interrupt"); + +if (interrupts & ISR_TXRCMP) + regs.isr |= ISR_TXRCMP; + +if (interrupts & ISR_RXRCMP) + regs.isr |= ISR_RXRCMP; + +//ISR_DPERR not implemented +//ISR_SSERR not implemented +//ISR_RMABT not implemented +//ISR_RXSOVR not implemented +//ISR_HIBINT not implemented +//ISR_PHY not implemented +//ISR_PME not implemented + +if (interrupts & ISR_SWI) + regs.isr |= ISR_SWI; + +//ISR_MIB not implemented +//ISR_TXURN not implemented + + if (interrupts & ISR_TXIDLE) + regs.isr |= ISR_TXIDLE; + + if (interrupts & ISR_TXERR) + regs.isr |= ISR_TXERR; + + if (interrupts & ISR_TXDESC) + regs.isr |= ISR_TXDESC; + + if (interrupts & ISR_TXOK) + regs.isr |= ISR_TXOK; + + if (interrupts & ISR_RXORN) + regs.isr |= ISR_RXORN; + + if (interrupts & ISR_RXIDLE) + regs.isr |= ISR_RXIDLE; + +//ISR_RXEARLY not implemented + + if (interrupts & ISR_RXERR) + regs.isr |= ISR_RXERR; + + if (interrupts & ISR_RXOK) + regs.isr |= ISR_RXOK; + + if ((regs.isr & regs.imr)) + cpuIntrPost(); +} + +void +EtherDev::devIntrClear(uint32_t interrupts) +{ + DPRINTF(Ethernet, "interrupt cleared intr=%x isr=%x imr=%x\n", + interrupts, regs.isr, regs.imr); + + if (interrupts & ISR_RESERVE) + panic("Cannot clear a reserved interrupt"); + + if (interrupts & ISR_TXRCMP) + regs.isr &= ~ISR_TXRCMP; + + if (interrupts & ISR_RXRCMP) + regs.isr &= ~ISR_RXRCMP; + +//ISR_DPERR not implemented +//ISR_SSERR not implemented +//ISR_RMABT not implemented +//ISR_RXSOVR not implemented +//ISR_HIBINT not implemented +//ISR_PHY not implemented +//ISR_PME not implemented + + if (interrupts & ISR_SWI) + regs.isr &= ~ISR_SWI; + +//ISR_MIB not implemented +//ISR_TXURN not implemented + + if (interrupts & ISR_TXIDLE) + regs.isr &= ~ISR_TXIDLE; + + if (interrupts & ISR_TXERR) + regs.isr &= ~ISR_TXERR; + + if (interrupts & ISR_TXDESC) + regs.isr &= ~ISR_TXDESC; + + if (interrupts & ISR_TXOK) + regs.isr &= ~ISR_TXOK; + + if (interrupts & ISR_RXORN) + regs.isr &= ~ISR_RXORN; + + if (interrupts & ISR_RXIDLE) + regs.isr &= ~ISR_RXIDLE; + +//ISR_RXEARLY not implemented + + if (interrupts & ISR_RXERR) + regs.isr &= ~ISR_RXERR; + + if (interrupts & ISR_RXOK) + regs.isr &= ~ISR_RXOK; + + if ((regs.isr & regs.imr)) + cpuIntrPost(); + + if (!(regs.isr & regs.imr)) + cpuIntrClear(); +} + +void +EtherDev::devIntrChangeMask() +{ + DPRINTF(Ethernet, "iterrupt mask changed\n"); + + if (regs.isr & regs.imr) + cpuIntrPost(); + else + cpuIntrClear(); +} + +void +EtherDev::cpuIntrPost() +{ + if (!cpuPendingIntr) { + if (regs.ier) { + cpuPendingIntr = true; + intctrl->post(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); + } + } +} + +void +EtherDev::cpuIntrClear() +{ + if (cpuPendingIntr) { + cpuPendingIntr = false; + intctrl->clear(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); + } +} + +bool +EtherDev::cpuIntrPending() const +{ return cpuPendingIntr; } + +void +EtherDev::txReset() +{ + + DPRINTF(Ethernet, "transmit reset\n"); + + txPacketFlag = false; + CTDD = false; + txFifoCnt = 0; + txFifoAvail = 0; + txHalt = false; + txFifo.clear(); + descAddrFifo.clear(); + regs.command &= ~CR_TXE; + txState = txIdle; +} + +void +EtherDev::rxReset() +{ + DPRINTF(Ethernet, "receive reset\n"); + + rxPacketFlag = false; + CRDD = false; + fragLen = 0; + rxFifoCnt = 0; + rxHalt = false; + rxFifo.clear(); + regs.command &= ~CR_RXE; + rxState = rxIdle; +} + +/** + * This sets up a DMA transfer to read one data segment from the rxFifo into + * the buffer indicated by rxDescCache.bufptr. Assumes the value of rxFragPtr + * is already correctly set. + */ +void +EtherDev::writeOneFrag() +{ + /* i think there is no need for an "in use" warning here like in old */ + fragLen = rxFifo.front()->length; //length of whole packet + fragLen = (fragLen < rxDescCnt) ? fragLen : rxDescCnt; + + writePhys.addr = rxFragPtr; + writePhys.length = fragLen; + + // Set up DMA request area + writeRequest.init(&rxDoneCB, 0, false, &writePhys, 1, fragLen, + rxDescBufPtr, fragLen, curTick); + + dma->doTransfer(&readRequest); +} + +void +EtherDev::rxKick() +{ + DPRINTF(Ethernet, "receive state machine activated!\n"); + + if (CRDD) { + rxState = rxDescRefr; + readOneDesc(rx, LINK_LEN); + } else { + rxState = rxDescRead; + readOneDesc(rx); + } +} + +EtherDev::RxDescDone::RxDescDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::RxDescDone::name() const +{ + return ethernet->name() + ".rxDescDoneCB"; +} + +void +EtherDev::RxDescDone::process() +{ + DPRINTF(Ethernet, "receive descriptor done callback\n"); + ethernet->rxDescDone(); +} + +void +EtherDev::rxDescDone() +{ + if (rxState == rxDescRefr) { + if (rxDescCache.link == 0) { + rxState = rxIdle; + regs.command &= ~CR_RXE; + devIntrPost(ISR_RXIDLE); + return; + } else { + rxState = rxDescRead; + regs.rxdp = rxDescCache.link; + CRDD = false; + readOneDesc(rx); + } + } else if (rxState == rxDescRead) { + if (rxDescCache.cmdsts & CMDSTS_OWN) { + rxState = rxIdle; + regs.command &= ~CR_RXE; + devIntrPost(ISR_RXIDLE); + } else { + rxState = rxFifoBlock; + rxFragPtr = rxDescCache.bufptr; + rxDescCnt = rxDescCache.cmdsts & CMDSTS_LEN_MASK; + + if (!rxFifo.empty()) { + rxState = rxFragWrite; + if (!rxPacketFlag) { // reading a new packet + rxPacketBufPtr = rxFifo.front()->data; + rxPacketBufPtr -= rxDescCnt; + rxDescBufPtr = rxPacketBufPtr; + rxCopied = 0; + } else { + rxDescBufPtr = rxPacketBufPtr - rxDescCnt; + } + writeOneFrag(); + } + } + } else if (rxState == rxDescWrite) { + devIntrPost(ISR_RXOK); + + if (rxDescCache.cmdsts & CMDSTS_INTR) + devIntrPost(ISR_RXDESC); + + if (rxDescCache.link == 0 || ((rxPktBytes != 0) && rxHalt)) { + rxState = rxIdle; + regs.command &= ~CR_RXE; + devIntrPost(ISR_RXIDLE); + rxHalt = false; + } else { + rxState = rxDescRead; + regs.rxdp = rxDescCache.link; + CRDD = false; + readOneDesc(rx); + } + } +} + +EtherDev::RxDone::RxDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::RxDone::name() const +{ + return ethernet->name() + ".rxDoneCB"; +} + +void +EtherDev::RxDone::process() +{ + DPRINTF(Ethernet, "receive done callback\n"); + ethernet->rxDone(); +} + +void +EtherDev::rxDone() +{ + DPRINTF(Ethernet, "packet received to host memory\n"); + + if (!rxDescCache.cmdsts & CMDSTS_OWN) + panic("This descriptor is already owned by the driver!\n"); + + rxState = rxFifoBlock; + rxCopied += fragLen; + rxFifoCnt -= fragLen; + + if (rxDescCnt) { /* there is still data left in the descriptor */ + rxState = rxFragWrite; + rxDescBufPtr += fragLen; + writeOneFrag(); + } else { + rxState = rxDescWrite; + if (rxPktBytes == 0) { /* packet is done */ + rxDescCache.cmdsts |= CMDSTS_OWN; + rxDescCache.cmdsts &= ~CMDSTS_MORE; + rxDescCache.cmdsts |= CMDSTS_OK; + rxDescCache.cmdsts += rxCopied; //i.e. set CMDSTS_SIZE + + rxPacketFlag = false; + if (rxFilterEnable) { + rxDescCache.cmdsts &= ~CMDSTS_DEST_MASK; + if (rxFifo.front()->IsUnicast()) + rxDescCache.cmdsts |= CMDSTS_DEST_SELF; + if (rxFifo.front()->IsMulticast()) + rxDescCache.cmdsts |= CMDSTS_DEST_MULTI; + if (rxFifo.front()->IsBroadcast()) + rxDescCache.cmdsts |= CMDSTS_DEST_MASK; + } + + PacketPtr &pkt = rxFifo.front(); + eth_header *eth = (eth_header *) pkt->data; + if (eth->type == 0x800 && extstsEnable) { + rxDescCache.extsts |= EXTSTS_IPPKT; + if (!ipChecksum(pkt, false)) + rxDescCache.extsts |= EXTSTS_IPERR; + ip_header *ip = rxFifo.front()->getIpHdr(); + + if (ip->protocol == 6) { + rxDescCache.extsts |= EXTSTS_TCPPKT; + if (!tcpChecksum(pkt, false)) + rxDescCache.extsts |= EXTSTS_TCPERR; + } else if (ip->protocol == 17) { + rxDescCache.extsts |= EXTSTS_UDPPKT; + if (!udpChecksum(pkt, false)) + rxDescCache.extsts |= EXTSTS_UDPERR; + } + } + + rxFifo.front() = NULL; + rxFifo.pop_front(); + } else { /* just the descriptor is done */ + rxDescCache.cmdsts |= CMDSTS_OWN; + rxDescCache.cmdsts |= CMDSTS_MORE; + } + writeDescPhys.addr = regs.rxdp + LINK_LEN + BUFPTR_LEN; + writeDescPhys.length = CMDSTS_LEN; + + writeDescRequest.init(&rxDescDoneCB, 0, true, &writeDescPhys, 1, + CMDSTS_LEN, (uint8_t *) &rxDescCache.cmdsts, + CMDSTS_LEN, curTick); + } +} + +/** + * This sets up a DMA transfer to read one descriptor into the network device. + */ +void +EtherDev::readOneDesc(dir_t dir, uint32_t len) { + readDescPhys.addr = (dir == tx) ? regs.txdp : regs.rxdp; + readDescPhys.length = len; + + ns_desc *cache = (dir == tx) ? &txDescCache : &rxDescCache; + + /* THIS ASSUMES THAT DESC_LEN < regs.txcfg's maxdma value, + which is 512 bytes in the driver, so i'll just hard code it here */ + readDescRequest.init(&txDescDoneCB, 0, false, &readDescPhys, 1, + len, (uint8_t *) cache , len, curTick); + + dma->doTransfer(&readDescRequest); +} + +/** + * This sets up a DMA transfer to read one data segment of the descriptor in + * txDescCache. Assumes the value of txFragPtr is already correctly set + */ +void +EtherDev::readOneFrag() +{ + /* i think there is no need for an "in use" warning here like in old */ + fragLen = (txDescCnt < txFifoAvail) ? txDescCnt : txFifoAvail; + readPhys.addr = txFragPtr; + readPhys.length = fragLen; + + // Set up DMA request area + readRequest.init(&txDoneCB, 0, false, &readPhys, 1, fragLen, + txPacketBufPtr, fragLen, curTick); + + dma->doTransfer(&readRequest); +} + +void +EtherDev::transmit() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "nothing to transmit\n"); + return; + } + + if (interface->sendPacket(txFifo.front())) { + DPRINTF(Ethernet, "transmit packet\n"); + txBytes += txFifo.front()->length; + txPackets++; + + txFifoCnt -= txFifo.front()->length; + + txFifo.front() = NULL; + txFifo.pop_front(); + + txDescCache.cmdsts &= ~CMDSTS_OK; + } else { + txDescCache.cmdsts &= ~CMDSTS_ERR; + } + + txDescCache.cmdsts &= ~CMDSTS_OWN; + + writeDescPhys.addr = descAddrFifo.front() + LINK_LEN + BUFPTR_LEN; + writeDescPhys.length = CMDSTS_LEN; + + descAddrFifo.front() = 0; + descAddrFifo.pop_front(); + + writeDescRequest.init(&txDescDoneCB, 0, true, &writeDescPhys, 1, + writeDescPhys.length, + (uint8_t *) &(txDescCache.cmdsts), + writeDescPhys.length, curTick); + + dma->doTransfer(&writeDescRequest); + + transmit(); +} + +void +EtherDev::txKick() +{ + DPRINTF(Ethernet, "transmit state machine activated\n"); +#if 0 + if (DTRACE(Ethernet)) + txDump(); +#endif + + if (CTDD) { + txState = txDescRefr; + readOneDesc(tx, LINK_LEN); + } else { + txState = txDescRead; + readOneDesc(tx); + } +} + +EtherDev::TxDescDone::TxDescDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::TxDescDone::name() const +{ + return ethernet->name() + ".txDescDoneCB"; +} + +void +EtherDev::TxDescDone::process() +{ + DPRINTF(Ethernet, "transmit descriptor done callback\n"); + ethernet->txDescDone(); + +} + +void +EtherDev::txDescDone() +{ + if (txState == txFifoBlock) { + if (txDescCache.cmdsts & CMDSTS_OK) { + devIntrPost(ISR_TXOK); + } else if (txDescCache.cmdsts & CMDSTS_ERR) { + devIntrPost(ISR_TXERR); + } + } else if (txState == txDescRefr || txState == txDescWrite) { + + if (txState == txDescWrite) { + if (txDescCache.cmdsts & CMDSTS_INTR) { + devIntrPost(ISR_TXDESC); + } + } + + if (txDescCache.link == 0) { + txState = txIdle; + regs.command &= ~CR_TXE; + devIntrPost(ISR_TXIDLE); + return; + } else { + txState = txDescRead; + regs.txdp = txDescCache.link; + CTDD = false; + readOneDesc(tx); + } + } else if (txState == txDescRead) { + if (txDescCache.cmdsts & CMDSTS_OWN) { + txState = txFifoBlock; + txFragPtr = txDescCache.bufptr; + txDescCnt = txDescCache.cmdsts & CMDSTS_LEN_MASK; + + if (txFifoAvail >= ((regs.txcfg & TXCFG_FLTH_MASK) >> 8)) { + txState = txFragRead; + if (!txPacketFlag) { + txPacketFlag = true; + /* find the total length of this packet */ + txPacketLen = txDescCnt; + bool more = txDescCache.cmdsts & CMDSTS_MORE; + uint8_t *addr = (uint8_t *) regs.txdp; + while (more) { + addr = physmem->dma_addr(((ns_desc *) addr)->link, sizeof(ns_desc)); + /* !!!!!!mask needed? */ + txPacketLen += ((ns_desc *)addr)->cmdsts & CMDSTS_LEN_MASK; + more = ((ns_desc *) addr)->cmdsts & CMDSTS_MORE; + } + PacketPtr &packet = txDoneCB.packet; + packet = new EtherPacket; + packet->length = txPacketLen; + packet->data = new uint8_t[txPacketLen]; + txPacketBufPtr = packet->data; + } + readOneFrag(); + } + } else { + txState = txIdle; + regs.command &= ~CR_TXE; + devIntrPost(ISR_TXIDLE); + } + } +} + +EtherDev::TxDone::TxDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::TxDone::name() const +{ + return ethernet->name() + ".txDoneCB"; +} + + +void +EtherDev::TxDone::process() +{ + DPRINTF(Ethernet, "transmit done callback\n"); + ethernet->txDone(packet); +} + +void +EtherDev::txDone(PacketPtr packet) +{ + DPRINTF(Ethernet, "transmit done\n"); + + if (!txDescCache.cmdsts & CMDSTS_OWN) + panic("This descriptor is already owned by the driver!\n"); + + txState = txFifoBlock; + + txPacketBufPtr += fragLen; /* hope this ptr manipulation is right! */ + txDescCnt -= fragLen; + txFifoCnt += fragLen; + + if (txFifoCnt >= (regs.txcfg & TXCFG_DRTH_MASK)) { + if (txFifo.empty()) { + txFifoCnt -= (uint32_t) (txPacketBufPtr - packet->data); + } else { + transmit(); + } + } + + if (txDescCnt) { /* if there is still more data to go in this desc */ + if (txFifoAvail >= regs.txcfg & TXCFG_FLTH_MASK) { + txState = txFragRead; + readOneFrag(); + } + } else { /* this descriptor is done */ + /* but there is more descriptors for this packet */ + if (txDescCache.cmdsts & CMDSTS_MORE) { + txState = txDescWrite; + txDescCache.cmdsts &= ~CMDSTS_OWN; + writeDescPhys.addr = regs.txdp + LINK_LEN + BUFPTR_LEN; + writeDescPhys.length = CMDSTS_LEN; + + writeDescRequest.init(&txDescDoneCB, 0, true, &writeDescPhys, 1, + writeDescPhys.length, + (uint8_t*) &txDescCache.cmdsts, + writeDescPhys.length, curTick); + } else { /* this packet is totally done */ + /* deal with the the packet that just finished */ + if (regs.vtcr & VTCR_PPCHK && extstsEnable) { + if (txDescCache.extsts & EXTSTS_UDPPKT) { + udpChecksum(packet, true); + } else if (txDescCache.extsts & EXTSTS_TCPPKT) { + tcpChecksum(packet, true); + } else if (txDescCache.extsts & EXTSTS_IPPKT) { + ipChecksum(packet, true); + } + } + + txFifo.push_back(packet); + transmit(); + txPacketFlag = false; + descAddrFifo.push_back(regs.txdp); + + /* if there is not another descriptor ready for reading, go idle */ + if (txDescCache.link == 0 || txHalt) { + txState = txIdle; + devIntrPost(ISR_TXIDLE); + txHalt = false; + } else { /* else go read next descriptor */ + txState = txDescRead; + regs.txdp = txDescCache.link; + CTDD = false; + readOneDesc(tx); + } + } + } +} + +void +EtherDev::transferDone() +{ + if (txFifo.empty()) + return; + + DPRINTF(Ethernet, "schedule transmit\n"); + + if (txEvent.scheduled()) + txEvent.reschedule(curTick + 1); + else + txEvent.schedule(curTick + 1); +} + +void +EtherDev::txDump() const +{ +#if 0 + int i = tx_ptr; + for (int loop = 0; loop < tx_ring_len; loop++) { + es_desc *desc = &tx_ring[i]; + + if (desc->addr) + cprintf("desc[%d]: addr=%#x, len=%d, flags=%#x\n", + i, desc->addr, desc->length, desc->flags); + + if (++i >= tx_ring_len) + i = 0; + } +#endif +} + +void +EtherDev::rxDump() const +{ +#if 0 + int i = rx_ptr; + for (int loop = 0; loop < rx_ring_len; loop++) { + es_desc *desc = &rx_ring[i]; + + if (desc->addr) + cprintf("desc[%d]: addr=%#x, len=%d, flags=%#x\n", + i, desc->addr, desc->length, desc->flags); + + if (++i >= rx_ring_len) + i = 0; + } +#endif +} + +bool +EtherDev::rxFilter(PacketPtr packet) +{ + bool drop = true; + string type; + + if (packet->IsUnicast()) { + type = "unicast"; + + // If we're accepting all unicast addresses + if (acceptUnicast) + drop = false; + + // If we make a perfect match + if ((acceptPerfect) + && (memcmp(regs.perfectMatch, packet->data, sizeof(regs.perfectMatch)) == 0)) + drop = false; + + eth_header *eth = (eth_header *) packet->data; + if ((acceptArp) && (eth->type == 0x806)) + drop = false; + + } else if (packet->IsBroadcast()) { + type = "broadcast"; + + // if we're accepting broadcasts + if (acceptBroadcast) + drop = false; + + } else if (packet->IsMulticast()) { + type = "multicast"; + + // if we're accepting all multicasts + if (acceptMulticast) + drop = false; + + } else { + type = "unknown"; + + // oh well, punt on this one + } + + if (drop) { + DPRINTF(Ethernet, "rxFilter drop\n"); + DDUMP(EthernetData, packet->data, packet->length); + } + + return drop; +} + +bool +EtherDev::recvPacket(PacketPtr packet) +{ + rxBytes += packet->length; + rxPackets++; + + if (rxState == rxIdle) { + DPRINTF(Ethernet, "receive disabled...packet dropped\n"); + interface->recvDone(); + return true; + } + + if (rxFilterEnable && rxFilter(packet)) { + DPRINTF(Ethernet, "packet filtered...dropped\n"); + interface->recvDone(); + return true; + } + + if (rxFifoCnt + packet->length >= MAX_RX_FIFO_SIZE) { + DPRINTF(Ethernet, + "packet will not fit in receive buffer...packet dropped\n"); + devIntrPost(ISR_RXORN); + return false; + } + + rxFifo.push_back(packet); + rxPktBytes = packet->length; + rxFifoCnt += packet->length; + interface->recvDone(); + + return true; +} + +bool +EtherDev::udpChecksum(PacketPtr packet, bool gen) +{ + udp_header *hdr = (udp_header *) packet->getTransportHdr(); + + ip_header *ip = packet->getIpHdr(); + + pseudo_header *pseudo = new pseudo_header; + + pseudo->src_ip_addr = ip->src_ip_addr; + pseudo->dest_ip_addr = ip->dest_ip_addr; + pseudo->protocol = ip->protocol; + pseudo->len = hdr->len; + + uint16_t cksum = checksumCalc((uint16_t *) pseudo, (uint16_t *) hdr, + (uint32_t) hdr->len); + + delete pseudo; + if (gen) + hdr->chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +bool +EtherDev::tcpChecksum(PacketPtr packet, bool gen) +{ + tcp_header *hdr = (tcp_header *) packet->getTransportHdr(); + + ip_header *ip = packet->getIpHdr(); + + pseudo_header *pseudo = new pseudo_header; + + pseudo->src_ip_addr = ip->src_ip_addr; + pseudo->dest_ip_addr = ip->dest_ip_addr; + pseudo->protocol = ip->protocol; + pseudo->len = ip->dgram_len - (ip->vers_len & 0xf); + + uint16_t cksum = checksumCalc((uint16_t *) pseudo, (uint16_t *) hdr, + (uint32_t) pseudo->len); + + delete pseudo; + if (gen) + hdr->chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +bool +EtherDev::ipChecksum(PacketPtr packet, bool gen) +{ + ip_header *hdr = packet->getIpHdr(); + + uint16_t cksum = checksumCalc(NULL, (uint16_t *) hdr, (hdr->vers_len & 0xf)); + + if (gen) + hdr->hdr_chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +uint16_t +EtherDev::checksumCalc(uint16_t *pseudo, uint16_t *buf, uint32_t len) +{ + uint32_t sum = 0; + + uint16_t last_pad = 0; + if (len & 1) { + last_pad = buf[len/2] & 0xff; + len--; + sum += last_pad; + } + + if (pseudo) { + sum = pseudo[0] + pseudo[1] + pseudo[2] + + pseudo[3] + pseudo[4] + pseudo[5]; + } + + for (int i=0; i < (len/2); ++i) { + sum += buf[i]; + } + + while (sum >> 16) + sum = (sum >> 16) + (sum & 0xffff); + + return ~sum; +} + +//===================================================================== +// +// +void +dp_regs::serialize(ostream &os) +{ + SERIALIZE_SCALAR(command); + SERIALIZE_SCALAR(config); + SERIALIZE_SCALAR(isr); + SERIALIZE_SCALAR(imr); +} + +void +dp_regs::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(command); + UNSERIALIZE_SCALAR(config); + UNSERIALIZE_SCALAR(isr); + UNSERIALIZE_SCALAR(imr); +#if 0 + UNSERIALIZE_SCALAR(tx_ring); + UNSERIALIZE_SCALAR(rx_ring); + UNSERIALIZE_SCALAR(tx_ring_len); + UNSERIALIZE_SCALAR(rx_ring_len); + UNSERIALIZE_SCALAR(rom_addr); + UNSERIALIZE_SCALAR(rom_data); + UNSERIALIZE_SCALAR(rxfilt_ctl); + UNSERIALIZE_SCALAR(rxfilt_data); + + UNSERIALIZE_ARRAY(perfect,EADDR_LEN); + UNSERIALIZE_ARRAY(hash_table,ES_HASH_SIZE); + + UNSERIALIZE_SCALAR(tx_ring_ptr); + UNSERIALIZE_SCALAR(rx_ring_ptr); +#endif +} + +//--------------------------------------- + +void +EtherPacket::serialize(ostream &os) +{ + SERIALIZE_SCALAR(length); + SERIALIZE_ARRAY(data, length); +} + +void +EtherPacket::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(length); + data = new uint8_t[length]; + UNSERIALIZE_ARRAY(data, length); +} + +//--------------------------------------- + +void +EtherDev::serialize(ostream &os) +{ + +#if 0 + regs.serialize(os); + + // tx_ring & rx_ring are contained in the physmem... + SERIALIZE_SCALAR(cpuPendingIntr); + SERIALIZE_SCALAR(tx_ptr); + SERIALIZE_SCALAR(rx_ptr); + + SERIALIZE_SCALAR(rxDoneCB.ptr); + SERIALIZE_SCALAR(rxDoneCB.ignore); + + SERIALIZE_SCALAR(txDoneCB.ptr); + SERIALIZE_SCALAR(txDoneCB.ignore); + + for (int i=0; iserialize(os); + } + + // Serialize rxPacket, because its data is needed for writeRequest + if (rxPacketExists) { + nameOut(os, csprintf("%s.rxPacket", name())); + rxPacket->serialize(os); + } + + // create a section for the readRequest + nameOut(os, readRequest.name()); + paramOut(os, csprintf("parent"), name()); + paramOut(os, csprintf("id"), 0); + readRequest.serialize(os); + + // create a section for the writeRequest + nameOut(os, writeRequest.name()); + paramOut(os, csprintf("parent"), name()); + paramOut(os, csprintf("id"), 1); + writeRequest.serialize(os); + + //Redo the buffers, this time outputing them to the file + numTxPkts = 0; + for (pktiter_t p=txbuf.begin(); p!=txbuf.end(); ++p) { + nameOut(os, csprintf("%s.txbuf%d", name(),numTxPkts++)); + (*p)->serialize(os); + } + + numRxPkts = 0; + for (pktiter_t p=rxbuf.begin(); p!=rxbuf.end(); ++p) { + nameOut(os, csprintf("%s.rxbuf%d", name(),numRxPkts++)); + (*p)->serialize(os); + } +#endif +} + +void +EtherDev::unserialize(Checkpoint *cp, const std::string §ion) +{ +#if 0 + regs.unserialize(cp, section); + + UNSERIALIZE_SCALAR(cpuPendingIntr); + + // initialize the tx_ring + txReset(); + + // initialize the rx_ring + rxReset(); + + UNSERIALIZE_SCALAR(tx_ptr); + UNSERIALIZE_SCALAR(rx_ptr); + + PacketPtr p; + UNSERIALIZE_SCALAR(txbuf_len); + int numTxPkts; + UNSERIALIZE_SCALAR(numTxPkts); + for (int i=0; iunserialize(cp, csprintf("%s.txbuf%d", section, i)); + txbuf.push_back(p); + } + + UNSERIALIZE_SCALAR(rxbuf_len); + int numRxPkts; + UNSERIALIZE_SCALAR(numRxPkts); + for (int i=0; iunserialize(cp, csprintf("%s.rxbuf%d", section, i)); + rxbuf.push_back(p); + } + + UNSERIALIZE_SCALAR(rxDoneCB.ptr); + UNSERIALIZE_SCALAR(rxDoneCB.ignore); + + UNSERIALIZE_SCALAR(txDoneCB.ptr); + UNSERIALIZE_SCALAR(txDoneCB.ignore); + + for (int i=0; iunserialize(cp, csprintf("%s.txPacket", section)); + } + + // Unserialize the current rxPacket + bool rxPacketExists; + UNSERIALIZE_SCALAR(rxPacketExists); + + rxPacket = NULL; + if (rxPacketExists) { + rxPacket = new EtherPacket; + rxPacket->unserialize(cp, csprintf("%s.rxPacket", section)); + } + + std::string readReqName, writeReqName; + UNSERIALIZE_SCALAR(readReqName); + UNSERIALIZE_SCALAR(writeReqName); + + // Unserialize and fixup the readRequest + readRequest.unserialize(cp, readReqName); + readRequest.phys = readPhys; + readRequest.bufferCB = 0; + readRequest.dmaDoneCB = &txDoneCB; + readRequest.data = NULL; + + if (txDoneCB.packet) + readRequest.data = txDoneCB.packet->data; + + // Unserialize and fixup the writeRequest + writeRequest.unserialize(cp, writeReqName); + writeRequest.phys = writePhys; + writeRequest.bufferCB = 0; + writeRequest.dmaDoneCB = &rxDoneCB; + writeRequest.data = NULL; + + if (rxPacket) + writeRequest.data = rxPacket->data; +#endif +} + + +//===================================================================== + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherDevInt) + + SimObjectParam peer; + SimObjectParam device; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherDevInt) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherDevInt) + + INIT_PARAM_DFLT(peer, "peer interface", NULL), + INIT_PARAM(device, "Ethernet device of this interface") + +END_INIT_SIM_OBJECT_PARAMS(EtherDevInt) + +CREATE_SIM_OBJECT(EtherDevInt) +{ + EtherDevInt *dev_int = new EtherDevInt(getInstanceName(), device); + + EtherInt *p = (EtherInt *)peer; + if (p) { + dev_int->setPeer(p); + p->setPeer(dev_int); + } + + return dev_int; +} + +REGISTER_SIM_OBJECT("EtherDevInt", EtherDevInt) + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherDev) + + Param tx_delay; + Param rx_delay; + SimObjectParam engine; + Param use_interface; + SimObjectParam intr_ctrl; + SimObjectParam mmu; + SimObjectParam physmem; + Param addr; + Param mask; + Param rx_filter; + Param hardware_address; + SimObjectParam configspace; + SimObjectParam configdata; + SimObjectParam tsunami; + Param pci_bus; + Param pci_dev; + Param pci_func; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherDev) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherDev) + + INIT_PARAM_DFLT(tx_delay, "Transmit Delay", 1000), + INIT_PARAM_DFLT(rx_delay, "Receive Delay", 1000), + INIT_PARAM(engine, "DMA Engine"), + INIT_PARAM_DFLT(use_interface, "Use DMA Interface", true), + INIT_PARAM(intr_ctrl, "Interrupt Controller"), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(physmem, "Physical Memory"), + INIT_PARAM(addr, "Device Address"), + INIT_PARAM(mask, "Address Mask"), + INIT_PARAM_DFLT(rx_filter, "Enable Receive Filter", true), + INIT_PARAM_DFLT(hardware_address, "Ethernet Hardware Address", + "00:99:00:00:00:01"), + INIT_PARAM(configspace, "PCI Configspace"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(tsunami, "Tsunami"), + INIT_PARAM(pci_bus, "PCI bus"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code") + +END_INIT_SIM_OBJECT_PARAMS(EtherDev) + + +CREATE_SIM_OBJECT(EtherDev) +{ + int eaddr[6]; + sscanf(((string)hardware_address).c_str(), "%x:%x:%x:%x:%x:%x", + &eaddr[0], &eaddr[1], &eaddr[2], &eaddr[3], &eaddr[4], &eaddr[5]); + + return new EtherDev(getInstanceName(), engine, use_interface, + intr_ctrl, mmu, physmem, configspace, configdata, + tsunami, pci_bus, pci_dev, pci_func, rx_filter, eaddr, + tx_delay, rx_delay, addr, mask); +} + +REGISTER_SIM_OBJECT("EtherDev", EtherDev) diff --git a/dev/ns_gige.hh b/dev/ns_gige.hh new file mode 100644 index 000000000..eaece2551 --- /dev/null +++ b/dev/ns_gige.hh @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2003 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +/* @file + * Device module for modelling the National Semiconductor + * DP83820 ethernet controller + */ + +#ifndef __NS_GIGE_HH__ +#define __NS_GIGE_HH__ + +#include "dev/dma.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "sim/eventq.hh" +#include "dev/ns_gige_reg.h" +#include "base/statistics.hh" +#include "dev/pcidev.hh" +#include "dev/tsunami.hh" +#include "dev/pciconfigall.hh" + +/** defined by the NS83820 data sheet */ +#define MAX_TX_FIFO_SIZE 8192 +#define MAX_RX_FIFO_SIZE 32768 + +/** length of ethernet address in bytes */ +#define EADDR_LEN 6 + +/** Transmit State Machine states */ +enum tx_state { txIdle, txDescRefr, txDescRead, txFifoBlock, txFragRead, + txDescWrite }; + +/** Receive State Machine States */ +enum rx_state { rxIdle, rxDescRefr, rxDescRead, rxFifoBlock, rxFragWrite, + rxDescWrite, rxAdvance }; + +/** + * Ethernet device registers + */ +struct dp_regs { + uint32_t command; + uint32_t config; + uint32_t mear; + uint32_t ptscr; + uint32_t isr; + uint32_t imr; + uint32_t ier; + uint32_t ihr; + uint32_t txdp; + uint32_t txdp_hi; + uint32_t txcfg; + uint32_t gpior; + uint32_t rxdp; + uint32_t rxdp_hi; + uint32_t rxcfg; + uint32_t pqcr; + uint32_t wcsr; + uint32_t pcr; + uint32_t rfcr; + uint32_t rfdr; + uint32_t srr; + uint32_t mibc; + uint32_t vrcr; + uint32_t vtcr; + uint32_t vdr; + uint32_t ccsr; + uint32_t tbicr; + uint32_t tbisr; + uint32_t tanar; + uint32_t tanlpar; + uint32_t taner; + uint32_t tesr; + + /** for perfect match memory. the linux driver doesn't use any other ROM */ + uint8_t perfectMatch[EADDR_LEN]; + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +/** an enum indicating direction, transmit or receive, used as a param for + some fns */ +enum dir_t { tx, rx }; + +class DmaEngine; +class IntrControl; +class EtherDevInt; +class PhysicalMemory; + +/** + * NS DP82830 Ethernet device model + */ +class EtherDev : public PciDev, public DmaHolder +{ + private: + /** pointer to the chipset */ + Tsunami *tsunami; + + protected: + Addr addr; + Addr mask; + + /** device register file */ + dp_regs regs; + + /*** BASIC STRUCTURES FOR TX/RX ***/ + /* Data FIFOs */ + typedef std::deque pktbuf_t; + typedef pktbuf_t::iterator pktiter_t; + pktbuf_t txFifo; + pktbuf_t rxFifo; + + /** for the tx side, to track addrs to write updated cmdsts to */ + typedef std::deque txdpbuf_t; /* ASSUME32 */ + txdpbuf_t descAddrFifo; + + /** various helper vars */ + uint32_t txPacketLen; + uint8_t *txPacketBufPtr; + uint8_t *rxPacketBufPtr; + uint8_t *rxDescBufPtr; + uint32_t fragLen; + uint32_t rxCopied; + + /** DescCaches */ + ns_desc txDescCache; + ns_desc rxDescCache; + + /* tx State Machine */ + tx_state txState; + /** Current Transmit Descriptor Done */ + bool CTDD; + uint32_t txFifoCnt; /* amt of data in the txDataFifo in bytes (logical) */ + uint32_t txFifoAvail; /* current amt of free space in txDataFifo in byes */ + bool txHalt; + bool txPacketFlag; /* when set, indicates not working on a new packet */ + Addr txFragPtr; /* ptr to the next byte in the current fragment */ + uint32_t txDescCnt; /* count of bytes remaining in the current descriptor */ + + /** rx State Machine */ + rx_state rxState; + bool CRDD; /* Current Receive Descriptor Done */ + uint32_t rxPktBytes; /* num of bytes in the current packet being drained + from rxDataFifo */ + uint32_t rxFifoCnt; /* number of bytes in the rxFifo */ + bool rxHalt; + bool rxPacketFlag; /* when set, indicates not working on a new packet */ + Addr rxFragPtr; /* ptr to the next byte in current fragment */ + uint32_t rxDescCnt; /* count of bytes remaining in the current descriptor */ + + bool extstsEnable; + uint32_t maxTxBurst; + uint32_t maxRxBurst; + + PhysicalMemory *physmem; + + protected: + /** + * Receive dma for descriptors done callback + */ + class RxDescDone : public DmaCallback + { + public: + EtherDev *ethernet; + + public: + RxDescDone(EtherDev *e); + std::string name() const; + virtual void process(); + }; + + /** + * Receive dma done callback + */ + class RxDone : public DmaCallback + { + public: + EtherDev *ethernet; + + public: + RxDone(EtherDev *e); + std::string name() const; + virtual void process(); + }; + + /** + * Transmit dma for descriptors done callback + */ + class TxDescDone : public DmaCallback + { + public: + EtherDev *ethernet; + + public: + TxDescDone(EtherDev *e); + std::string name() const; + virtual void process(); + }; + + /* + * Transmit dma done callback + */ + class TxDone : public DmaCallback + { + public: + EtherDev *ethernet; + PacketPtr packet; + + public: + TxDone(EtherDev *e); + std::string name() const; + virtual void process(); + }; + + friend class TxDescDone; + friend class TxDone; + friend class RxDescDone; + friend class RxDone; + + RxDescDone rxDescDoneCB; + RxDone rxDoneCB; + TxDescDone txDescDoneCB; + TxDone txDoneCB; + + DmaEngine *dma; + DmaRequest readRequest; + DmaRequest writeRequest; + DmaRequest readDescRequest; + DmaRequest writeDescRequest; + PacketPtr rxPacket; + DmaPhys readPhys; + DmaPhys writePhys; + DmaPhys readDescPhys; + DmaPhys writeDescPhys; + + EtherDevInt *interface; + + protected: + IntrControl *intctrl; + Tick txDelay; + Tick rxDelay; + + void txReset(); + void rxReset(); + void regsReset() { + memset(®s, 0, sizeof(regs)); + regs.mear = 0x12; + regs.isr = 0x00608000; + regs.txcfg = 0x120; + regs.rxcfg = 0x4; + regs.srr = 0x0103; + regs.mibc = 0x2; + regs.vdr = 0x81; + regs.tesr = 0xc000; + } + + void txKick(); + void rxKick(); + + /* + * Retransmit event + */ + class TxEvent : public Event + { + protected: + EtherDev *dev; + + public: + TxEvent(EtherDev *_dev) + : Event(&mainEventQueue), dev(_dev) {} + void process() { dev->transmit(); } + virtual const char *description() { return "retransmit"; } + }; + friend class TxEvent; + TxEvent txEvent; + void transmit(); + + + void txDescDone(); + void rxDescDone(); + void txDone(PacketPtr packet); + void rxDone(); + + void txDump() const; + void rxDump() const; + + void devIntrPost(uint32_t interrupts); + void devIntrClear(uint32_t interrupts); + void devIntrChangeMask(); + + bool cpuPendingIntr; + void cpuIntrPost(); + void cpuIntrClear(); + + bool rxFilterEnable; + bool rxFilter(PacketPtr packet); + bool acceptBroadcast; + bool acceptMulticast; + bool acceptUnicast; + bool acceptPerfect; + bool acceptArp; + + bool udpChecksum(PacketPtr packet, bool gen); + bool tcpChecksum(PacketPtr packet, bool gen); + bool ipChecksum(PacketPtr packet, bool gen); + uint16_t checksumCalc(uint16_t *pseudo, uint16_t *buf, uint32_t len); + + public: + EtherDev(const std::string &name, DmaEngine *de, bool use_interface, + IntrControl *i, MemoryController *mmu, PhysicalMemory *pmem, + PCIConfigAll *cf, PciConfigData *cd, Tsunami *t, uint32_t bus, + uint32_t dev, uint32_t func, bool rx_filter, const int eaddr[6], + Tick tx_delay, Tick rx_delay, Addr addr, Addr mask); + ~EtherDev(); + + virtual void WriteConfig(int offset, int size, uint32_t data); + virtual void ReadConfig(int offset, int size, uint8_t *data); + + + + Fault read(MemReqPtr req, uint8_t *data); + Fault write(MemReqPtr req, const uint8_t *data); + + bool cpuIntrPending() const; + void cpuIntrAck() { cpuIntrClear(); } + + bool recvPacket(PacketPtr packet); + void transferDone(); + + void setInterface(EtherDevInt *i) { assert(!interface); interface = i; } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + virtual DmaRequest *find_dmareq(uint32_t &id) { + if (id == 0) + return(&readRequest); + else if (id == 1) + return(&writeRequest); + else + return(NULL); + } + + public: + void regStats(); + + private: + Statistics::Scalar<> txBytes; + Statistics::Scalar<> rxBytes; + Statistics::Scalar<> txPackets; + Statistics::Scalar<> rxPackets; + Statistics::Formula txBandwidth; + Statistics::Formula rxBandwidth; + Statistics::Formula txPacketRate; + Statistics::Formula rxPacketRate; + + void readOneDesc(dir_t dir, uint32_t len = sizeof(ns_desc)); + void readOneFrag(); + void writeOneFrag(); +}; + +/* + * Ethernet Interface for an Ethernet Device + */ +class EtherDevInt : public EtherInt +{ + private: + EtherDev *dev; + + public: + EtherDevInt(const std::string &name, EtherDev *d) + : EtherInt(name), dev(d) { dev->setInterface(this); } + + virtual bool recvPacket(PacketPtr &pkt) { return dev->recvPacket(pkt); } + virtual void sendDone() { dev->transferDone(); } +}; + +#endif // __NS_GIGE_HH__ diff --git a/dev/ns_gige_reg.h b/dev/ns_gige_reg.h new file mode 100644 index 000000000..5b0f961c0 --- /dev/null +++ b/dev/ns_gige_reg.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2003 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +/* Portions of code taken from: */ + +/* ns83820.c by Benjamin LaHaise with contributions. + * + * Questions/comments/discussion to linux-ns83820@kvack.org. + * + * $Revision: 1.34.2.23 $ + * + * Copyright 2001 Benjamin LaHaise. + * Copyright 2001, 2002 Red Hat. + * + * Mmmm, chocolate vanilla mocha... + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + + + +/* @file + * Ethernet device register definitions for the National + * Semiconductor DP83820 Ethernet controller + */ + +#ifndef _NS_GIGE_H +#define _NS_GIGE_H_ + +/* + * Configuration Register Map + */ +#define NS_ID 0x00 /* identification register */ +#define NS_CS 0x04 /* command and status register */ +#define NS_RID 0x08 /* revision ID register */ +#define NS_LAT 0x0C /* latency timer register */ +#define NS_IOA 0x10 /* IO base address register */ +#define NS_MA 0x14 /* memory address register */ +#define NS_MA1 0x18 /* memory address high dword register */ +#define NS_SID 0x2C /* subsystem identification register */ +#define NS_ROM 0x30 /* boot ROM configuration register */ +#define NS_CAPPTR 0x34 /* number of tx descriptors */ +#define NS_INT 0x3C /* interrupt select register */ +#define NS_PMCAP 0x40 /* power mgmt capabilities register */ +#define NS_PMCS 0x44 /* power mgmt control and status + register */ +/* Operational Register Map */ +#define CR 0x00 +#define CFG 0x04 +#define MEAR 0x08 +#define PTSCR 0x0c +#define ISR 0x10 +#define IMR 0x14 +#define IER 0x18 +#define IHR 0x1c +#define TXDP 0x20 +#define TXDP_HI 0x24 +#define TXCFG 0x28 +#define GPIOR 0x2c +#define RXDP 0x30 +#define RXDP_HI 0x34 +#define RXCFG 0x38 +#define PQCR 0x3c +#define WCSR 0x40 +#define PCR 0x44 +#define RFCR 0x48 +#define RFDR 0x4c +#define BRAR 0x50 +#define BRDR 0x54 +#define SRR 0x58 +#define MIBC 0x5c +#define VRCR 0xbc +#define VTCR 0xc0 +#define VDR 0xc4 +#define CCSR 0xcc +#define TBICR 0xe0 +#define TBISR 0xe4 +#define TANAR 0xe8 +#define TANLPAR 0xec +#define TANER 0xf0 +#define TESR 0xf4 +#define LAST 0xf4 +#define RESERVED 0xfc + +/* chip command register */ +#define CR_TXE 0x00000001 +#define CR_TXD 0x00000002 +#define CR_RXE 0x00000004 +#define CR_RXD 0x00000008 +#define CR_TXR 0x00000010 +#define CR_RXR 0x00000020 +#define CR_SWI 0x00000080 +#define CR_RST 0x00000100 + +/* configuration register */ +#define CFG_LNKSTS 0x80000000 +#define CFG_SPDSTS 0x60000000 +#define CFG_SPDSTS1 0x40000000 +#define CFG_SPDSTS0 0x20000000 +#define CFG_DUPSTS 0x10000000 +#define CFG_TBI_EN 0x01000000 +#define CFG_RESERVED 0x0e000000 +#define CFG_MODE_1000 0x00400000 +#define CFG_AUTO_1000 0x00200000 +#define CFG_PINT_CTL 0x001c0000 +#define CFG_PINT_DUPSTS 0x00100000 +#define CFG_PINT_LNKSTS 0x00080000 +#define CFG_PINT_SPDSTS 0x00040000 +#define CFG_TMRTEST 0x00020000 +#define CFG_MRM_DIS 0x00010000 +#define CFG_MWI_DIS 0x00008000 +#define CFG_T64ADDR 0x00004000 +#define CFG_PCI64_DET 0x00002000 +#define CFG_DATA64_EN 0x00001000 +#define CFG_M64ADDR 0x00000800 +#define CFG_PHY_RST 0x00000400 +#define CFG_PHY_DIS 0x00000200 +#define CFG_EXTSTS_EN 0x00000100 +#define CFG_REQALG 0x00000080 +#define CFG_SB 0x00000040 +#define CFG_POW 0x00000020 +#define CFG_EXD 0x00000010 +#define CFG_PESEL 0x00000008 +#define CFG_BROM_DIS 0x00000004 +#define CFG_EXT_125 0x00000002 +#define CFG_BEM 0x00000001 + +/* EEPROM access register */ +#define MEAR_EEDI 0x00000001 +#define MEAR_EEDO 0x00000002 +#define MEAR_EECLK 0x00000004 +#define MEAR_EESEL 0x00000008 +#define MEAR_MDIO 0x00000010 +#define MEAR_MDDIR 0x00000020 +#define MEAR_MDC 0x00000040 + +/* PCI test control register */ +#define PTSCR_EEBIST_FAIL 0x00000001 +#define PTSCR_EEBIST_EN 0x00000002 +#define PTSCR_EELOAD_EN 0x00000004 +#define PTSCR_RBIST_FAIL 0x000001b8 +#define PTSCR_RBIST_DONE 0x00000200 +#define PTSCR_RBIST_EN 0x00000400 +#define PTSCR_RBIST_RST 0x00002000 + +/* interrupt status register */ +#define ISR_RESERVE 0x80000000 +#define ISR_TXDESC3 0x40000000 +#define ISR_TXDESC2 0x20000000 +#define ISR_TXDESC1 0x10000000 +#define ISR_TXDESC0 0x08000000 +#define ISR_RXDESC3 0x04000000 +#define ISR_RXDESC2 0x02000000 +#define ISR_RXDESC1 0x01000000 +#define ISR_RXDESC0 0x00800000 +#define ISR_TXRCMP 0x00400000 +#define ISR_RXRCMP 0x00200000 +#define ISR_DPERR 0x00100000 +#define ISR_SSERR 0x00080000 +#define ISR_RMABT 0x00040000 +#define ISR_RTABT 0x00020000 +#define ISR_RXSOVR 0x00010000 +#define ISR_HIBINT 0x00008000 +#define ISR_PHY 0x00004000 +#define ISR_PME 0x00002000 +#define ISR_SWI 0x00001000 +#define ISR_MIB 0x00000800 +#define ISR_TXURN 0x00000400 +#define ISR_TXIDLE 0x00000200 +#define ISR_TXERR 0x00000100 +#define ISR_TXDESC 0x00000080 +#define ISR_TXOK 0x00000040 +#define ISR_RXORN 0x00000020 +#define ISR_RXIDLE 0x00000010 +#define ISR_RXEARLY 0x00000008 +#define ISR_RXERR 0x00000004 +#define ISR_RXDESC 0x00000002 +#define ISR_RXOK 0x00000001 + +/* transmit configuration register */ +#define TXCFG_CSI 0x80000000 +#define TXCFG_HBI 0x40000000 +#define TXCFG_MLB 0x20000000 +#define TXCFG_ATP 0x10000000 +#define TXCFG_ECRETRY 0x00800000 +#define TXCFG_BRST_DIS 0x00080000 +#define TXCFG_MXDMA1024 0x00000000 +#define TXCFG_MXDMA512 0x00700000 +#define TXCFG_MXDMA256 0x00600000 +#define TXCFG_MXDMA128 0x00500000 +#define TXCFG_MXDMA64 0x00400000 +#define TXCFG_MXDMA32 0x00300000 +#define TXCFG_MXDMA16 0x00200000 +#define TXCFG_MXDMA8 0x00100000 + +#define TXCFG_FLTH_MASK 0x0000ff00 +#define TXCFG_DRTH_MASK 0x000000ff + +/*general purpose I/O control register */ +#define GPIOR_GP5_OE 0x00000200 +#define GPIOR_GP4_OE 0x00000100 +#define GPIOR_GP3_OE 0x00000080 +#define GPIOR_GP2_OE 0x00000040 +#define GPIOR_GP1_OE 0x00000020 +#define GPIOR_GP3_OUT 0x00000004 +#define GPIOR_GP1_OUT 0x00000001 + +/* receive configuration register */ +#define RXCFG_AEP 0x80000000 +#define RXCFG_ARP 0x40000000 +#define RXCFG_STRIPCRC 0x20000000 +#define RXCFG_RX_FD 0x10000000 +#define RXCFG_ALP 0x08000000 +#define RXCFG_AIRL 0x04000000 +#define RXCFG_MXDMA512 0x00700000 +#define RXCFG_DRTH 0x0000003e +#define RXCFG_DRTH0 0x00000002 + +/* pause control status register */ +#define PCR_PSEN (1 << 31) +#define PCR_PS_MCAST (1 << 30) +#define PCR_PS_DA (1 << 29) +#define PCR_STHI_8 (3 << 23) +#define PCR_STLO_4 (1 << 23) +#define PCR_FFHI_8K (3 << 21) +#define PCR_FFLO_4K (1 << 21) +#define PCR_PAUSE_CNT 0xFFFE + +/*receive filter/match control register */ +#define RFCR_RFEN 0x80000000 +#define RFCR_AAB 0x40000000 +#define RFCR_AAM 0x20000000 +#define RFCR_AAU 0x10000000 +#define RFCR_APM 0x08000000 +#define RFCR_APAT 0x07800000 +#define RFCR_APAT3 0x04000000 +#define RFCR_APAT2 0x02000000 +#define RFCR_APAT1 0x01000000 +#define RFCR_APAT0 0x00800000 +#define RFCR_AARP 0x00400000 +#define RFCR_MHEN 0x00200000 +#define RFCR_UHEN 0x00100000 +#define RFCR_ULM 0x00080000 +#define RFCR_RFADDR 0x000003ff + +/* receive filter/match data register */ +#define RFDR_BMASK 0x00030000 +#define RFDR_RFDATA0 0x000000ff +#define RFDR_RFDATA1 0x0000ff00 + +/* management information base control register */ +#define MIBC_MIBS 0x00000008 +#define MIBC_ACLR 0x00000004 +#define MIBC_FRZ 0x00000002 +#define MIBC_WRN 0x00000001 + +/* VLAN/IP receive control register */ +#define VRCR_RUDPE 0x00000080 +#define VRCR_RTCPE 0x00000040 +#define VRCR_RIPE 0x00000020 +#define VRCR_IPEN 0x00000010 +#define VRCR_DUTF 0x00000008 +#define VRCR_DVTF 0x00000004 +#define VRCR_VTREN 0x00000002 +#define VRCR_VTDEN 0x00000001 + +/* VLAN/IP transmit control register */ +#define VTCR_PPCHK 0x00000008 +#define VTCR_GCHK 0x00000004 +#define VTCR_VPPTI 0x00000002 +#define VTCR_VGTI 0x00000001 + +/* Clockrun Control/Status Register */ +#define CCSR_CLKRUN_EN 0x00000001 + +/* TBI control register */ +#define TBICR_MR_LOOPBACK 0x00004000 +#define TBICR_MR_AN_ENABLE 0x00001000 +#define TBICR_MR_RESTART_AN 0x00000200 + +/* TBI status register */ +#define TBISR_MR_LINK_STATUS 0x00000020 +#define TBISR_MR_AN_COMPLETE 0x00000004 + +/* TBI auto-negotiation advertisement register */ +#define TANAR_PS2 0x00000100 +#define TANAR_PS1 0x00000080 +#define TANAR_HALF_DUP 0x00000040 +#define TANAR_FULL_DUP 0x00000020 + +/* + * descriptor format currently assuming link and bufptr + * are set for 32 bits,( may be wrong ) ASSUME32 + */ +struct ns_desc { + uint32_t link; /* link field to next descriptor in linked list */ + uint32_t bufptr; /* pointer to the first fragment or buffer */ + uint32_t cmdsts; /* command/status field */ + uint32_t extsts; /* extended status field for VLAN and IP info */ +}; + +/* ASSUME32 in bytes, how big the desc fields are */ +#define LINK_LEN 4 +#define BUFPTR_LEN 4 +#define CMDSTS_LEN 4 +#define EXTSTS_LEN 4 + +/* cmdsts flags for descriptors */ +#define CMDSTS_OWN 0x80000000 +#define CMDSTS_MORE 0x40000000 +#define CMDSTS_INTR 0x20000000 +#define CMDSTS_ERR 0x10000000 +#define CMDSTS_OK 0x08000000 +#define CMDSTS_LEN_MASK 0x0000ffff + +#define CMDSTS_DEST_MASK 0x01800000 +#define CMDSTS_DEST_SELF 0x00800000 +#define CMDSTS_DEST_MULTI 0x01000000 + +/* extended flags for descriptors */ +#define EXTSTS_UDPERR 0x00400000 +#define EXTSTS_UDPPKT 0x00200000 +#define EXTSTS_TCPERR 0x00100000 +#define EXTSTS_TCPPKT 0x00080000 +#define EXTSTS_IPERR 0x00040000 +#define EXTSTS_IPPKT 0x00020000 + + +/* speed status */ +#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0)) + +#endif /* _NS_GIGE_H_ */