/* * Copyright (c) 2006 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. * * Authors: Ali Saidi */ /* @file * Device model for Intel's 8254x line of gigabit ethernet controllers. * In particular an 82547 revision 2 (82547GI) MAC because it seems to have the * fewest workarounds in the driver. It will probably work with most of the * other MACs with slight modifications. */ #include "base/inet.hh" #include "dev/i8254xGBe.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" #include "sim/builder.hh" #include "sim/stats.hh" #include "sim/system.hh" using namespace iGbReg; IGbE::IGbE(Params *p) : PciDev(p), etherInt(NULL) { // Initialized internal registers per Intel documentation regs.tctl.reg = 0; regs.rctl.reg = 0; regs.ctrl.reg = 0; regs.ctrl.fd = 1; regs.ctrl.lrst = 1; regs.ctrl.speed = 2; regs.ctrl.frcspd = 1; regs.sts.reg = 0; regs.eecd.reg = 0; regs.eecd.fwe = 1; regs.eecd.ee_type = 1; regs.eerd.reg = 0; regs.icd.reg = 0; regs.imc.reg = 0; regs.rctl.reg = 0; regs.tctl.reg = 0; regs.manc.reg = 0; regs.pba.rxa = 0x30; regs.pba.txa = 0x10; eeOpBits = 0; eeAddrBits = 0; eeDataBits = 0; eeOpcode = 0; // clear all 64 16 bit words of the eeprom memset(&flash, 0, EEPROM_SIZE*2); // Magic happy checksum value flash[0] = 0xBABA; } Tick IGbE::writeConfig(PacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) PciDev::writeConfig(pkt); else panic("Device specific PCI config space not implemented.\n"); /// /// Some work may need to be done here based for the pci COMMAND bits. /// return pioDelay; } Tick IGbE::read(PacketPtr pkt) { int bar; Addr daddr; if (!getBAR(pkt->getAddr(), bar, daddr)) panic("Invalid PCI memory access to unmapped memory.\n"); // Only Memory register BAR is allowed assert(bar == 0); // Only 32bit accesses allowed assert(pkt->getSize() == 4); //DPRINTF(Ethernet, "Read device register %#X\n", daddr); pkt->allocate(); /// /// Handle read of register here /// switch (daddr) { case CTRL: pkt->set(regs.ctrl.reg); break; case STATUS: pkt->set(regs.sts.reg); break; case EECD: pkt->set(regs.eecd.reg); break; case EERD: pkt->set(regs.eerd.reg); break; case ICR: pkt->set(regs.icd.reg); break; case IMC: pkt->set(regs.imc.reg); break; case RCTL: pkt->set(regs.rctl.reg); break; case TCTL: pkt->set(regs.tctl.reg); break; case PBA: pkt->set(regs.pba.reg); break; case WUC: case LEDCTL: pkt->set(0); // We don't care, so just return 0 break; case MANC: pkt->set(regs.manc.reg); break; default: if (!(daddr >= VFTA && daddr < (VFTA + VLAN_FILTER_TABLE_SIZE)*4) && !(daddr >= RAL && daddr < (RAL + RCV_ADDRESS_TABLE_SIZE)*4) && !(daddr >= MTA && daddr < (MTA + MULTICAST_TABLE_SIZE)*4)) pkt->set(0); else panic("Read request to unknown register number: %#x\n", daddr); }; pkt->result = Packet::Success; return pioDelay; } Tick IGbE::write(PacketPtr pkt) { int bar; Addr daddr; if (!getBAR(pkt->getAddr(), bar, daddr)) panic("Invalid PCI memory access to unmapped memory.\n"); // Only Memory register BAR is allowed assert(bar == 0); // Only 32bit accesses allowed assert(pkt->getSize() == sizeof(uint32_t)); //DPRINTF(Ethernet, "Wrote device register %#X value %#X\n", daddr, pkt->get()); /// /// Handle write of register here /// uint32_t val = pkt->get(); switch (daddr) { case CTRL: regs.ctrl.reg = val; break; case STATUS: regs.sts.reg = val; break; case EECD: int oldClk; oldClk = regs.eecd.sk; regs.eecd.reg = val; // See if this is a eeprom access and emulate accordingly if (!oldClk && regs.eecd.sk) { if (eeOpBits < 8) { eeOpcode = eeOpcode << 1 | regs.eecd.din; eeOpBits++; } else if (eeAddrBits < 8 && eeOpcode == EEPROM_READ_OPCODE_SPI) { eeAddr = eeAddr << 1 | regs.eecd.din; eeAddrBits++; } else if (eeDataBits < 16 && eeOpcode == EEPROM_READ_OPCODE_SPI) { assert(eeAddr>>1 < EEPROM_SIZE); DPRINTF(EthernetEEPROM, "EEPROM bit read: %d word: %#X\n", flash[eeAddr>>1] >> eeDataBits & 0x1, flash[eeAddr>>1]); regs.eecd.dout = (flash[eeAddr>>1] >> (15-eeDataBits)) & 0x1; eeDataBits++; } else if (eeDataBits < 8 && eeOpcode == EEPROM_RDSR_OPCODE_SPI) { regs.eecd.dout = 0; eeDataBits++; } else panic("What's going on with eeprom interface? opcode:" " %#x:%d addr: %#x:%d, data: %d\n", (uint32_t)eeOpcode, (uint32_t)eeOpBits, (uint32_t)eeAddr, (uint32_t)eeAddrBits, (uint32_t)eeDataBits); // Reset everything for the next command if ((eeDataBits == 16 && eeOpcode == EEPROM_READ_OPCODE_SPI) || (eeDataBits == 8 && eeOpcode == EEPROM_RDSR_OPCODE_SPI)) { eeOpBits = 0; eeAddrBits = 0; eeDataBits = 0; eeOpcode = 0; eeAddr = 0; } DPRINTF(EthernetEEPROM, "EEPROM: opcode: %#X:%d addr: %#X:%d\n", (uint32_t)eeOpcode, (uint32_t) eeOpBits, (uint32_t)eeAddr>>1, (uint32_t)eeAddrBits); if (eeOpBits == 8 && !(eeOpcode == EEPROM_READ_OPCODE_SPI || eeOpcode == EEPROM_RDSR_OPCODE_SPI )) panic("Unknown eeprom opcode: %#X:%d\n", (uint32_t)eeOpcode, (uint32_t)eeOpBits); } // If driver requests eeprom access, immediately give it to it regs.eecd.ee_gnt = regs.eecd.ee_req; break; case EERD: regs.eerd.reg = val; break; case ICR: regs.icd.reg = val; break; case IMC: regs.imc.reg = val; break; case RCTL: regs.rctl.reg = val; break; case TCTL: regs.tctl.reg = val; break; case PBA: regs.pba.rxa = val; regs.pba.txa = 64 - regs.pba.rxa; break; case WUC: case LEDCTL: ; // We don't care, so don't store anything break; case MANC: regs.manc.reg = val; break; default: if (!(daddr >= VFTA && daddr < (VFTA + VLAN_FILTER_TABLE_SIZE)*4) && !(daddr >= RAL && daddr < (RAL + RCV_ADDRESS_TABLE_SIZE)*4) && !(daddr >= MTA && daddr < (MTA + MULTICAST_TABLE_SIZE)*4)) panic("Write request to unknown register number: %#x\n", daddr); }; pkt->result = Packet::Success; return pioDelay; } bool IGbE::ethRxPkt(EthPacketPtr packet) { panic("Need to implemenet\n"); } void IGbE::ethTxDone() { panic("Need to implemenet\n"); } void IGbE::serialize(std::ostream &os) { panic("Need to implemenet\n"); } void IGbE::unserialize(Checkpoint *cp, const std::string §ion) { panic("Need to implemenet\n"); } BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) SimObjectParam peer; SimObjectParam device; END_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) BEGIN_INIT_SIM_OBJECT_PARAMS(IGbEInt) INIT_PARAM_DFLT(peer, "peer interface", NULL), INIT_PARAM(device, "Ethernet device of this interface") END_INIT_SIM_OBJECT_PARAMS(IGbEInt) CREATE_SIM_OBJECT(IGbEInt) { IGbEInt *dev_int = new IGbEInt(getInstanceName(), device); EtherInt *p = (EtherInt *)peer; if (p) { dev_int->setPeer(p); p->setPeer(dev_int); } return dev_int; } REGISTER_SIM_OBJECT("IGbEInt", IGbEInt) BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbE) SimObjectParam system; SimObjectParam platform; SimObjectParam configdata; Param pci_bus; Param pci_dev; Param pci_func; Param pio_latency; Param config_latency; END_DECLARE_SIM_OBJECT_PARAMS(IGbE) BEGIN_INIT_SIM_OBJECT_PARAMS(IGbE) INIT_PARAM(system, "System pointer"), INIT_PARAM(platform, "Platform pointer"), INIT_PARAM(configdata, "PCI Config data"), INIT_PARAM(pci_bus, "PCI bus ID"), INIT_PARAM(pci_dev, "PCI device number"), INIT_PARAM(pci_func, "PCI function code"), INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1), INIT_PARAM(config_latency, "Number of cycles for a config read or write") END_INIT_SIM_OBJECT_PARAMS(IGbE) CREATE_SIM_OBJECT(IGbE) { IGbE::Params *params = new IGbE::Params; params->name = getInstanceName(); params->platform = platform; params->system = system; params->configData = configdata; params->busNum = pci_bus; params->deviceNum = pci_dev; params->functionNum = pci_func; params->pio_delay = pio_latency; params->config_delay = config_latency; return new IGbE(params); } REGISTER_SIM_OBJECT("IGbE", IGbE)