IGbE: Add support for newer 8257x based Intel NICs
authorAli Saidi <saidi@eecs.umich.edu>
Fri, 5 Dec 2008 18:58:22 +0000 (13:58 -0500)
committerAli Saidi <saidi@eecs.umich.edu>
Fri, 5 Dec 2008 18:58:22 +0000 (13:58 -0500)
src/dev/Ethernet.py
src/dev/SConscript
src/dev/i8254xGBe.cc
src/dev/i8254xGBe.hh
src/dev/i8254xGBe_defs.hh

index 5821a3e96febb32c4b8060e39db512780a9cf479..670f48eac2a0427ea2f01d0bb9798892ce4936b2 100644 (file)
@@ -67,7 +67,9 @@ class EtherDevice(PciDevice):
     interface = Port("Ethernet Interrface")
 
 class IGbE(EtherDevice):
+    # Base class for two IGbE adapters listed above
     type = 'IGbE'
+    #abstract = True
     hardware_address = Param.EthernetAddr(NextEthernetAddr,
         "Ethernet Hardware Address")
     use_flow_control = Param.Bool(False,
@@ -80,7 +82,6 @@ class IGbE(EtherDevice):
         "Number of enteries in the rx descriptor cache")
     clock = Param.Clock('500MHz', "Clock speed of the device")
     VendorID = 0x8086
-    DeviceID = 0x1075
     SubsystemID = 0x1008
     SubsystemVendorID = 0x8086
     Status = 0x0000
@@ -104,7 +105,20 @@ class IGbE(EtherDevice):
     wb_comp_delay = Param.Latency('10ns', "delay after desc wb occurs")
     tx_read_delay = Param.Latency('0ns', "delay after tx dma read")
     rx_write_delay = Param.Latency('0ns', "delay after rx dma read")
+    is8257 = Param.Bool("Select between and 8254x and 8257x device")
+
+
+class IGbE_e1000(IGbE):
+    # Older Intel 8254x based gigabit ethernet adapter
+    # Uses Intel e1000 driver
+    DeviceID = 0x1075
+    is8257 = False
 
+class IGbE_igb(IGbE):
+    # Newer Intel 8257x based gigabit ethernet adapter
+    # Uses Intel igb driver and in theory supports packet splitting and LRO
+    DeviceID = 0x10C9
+    is8257 = True 
 
 class EtherDevBase(EtherDevice):
     type = 'EtherDevBase'
index 54148b68b8c2b6e6fdca4eb72435b29da4ed1181..2071600baae1dfec293abcc38cf5a83909597f73 100644 (file)
@@ -98,7 +98,7 @@ if env['FULL_SYSTEM']:
     CompoundFlag('DiskImageAll', [ 'DiskImageRead', 'DiskImageWrite' ])
     CompoundFlag('EthernetAll', [ 'Ethernet', 'EthernetPIO', 'EthernetDMA',
         'EthernetData' , 'EthernetDesc', 'EthernetIntr', 'EthernetSM',
-        'EthernetCksum' ])
+        'EthernetCksum', 'EthernetEEPROM' ])
     CompoundFlag('EthernetNoData', [ 'Ethernet', 'EthernetPIO', 'EthernetDesc',
         'EthernetIntr', 'EthernetSM', 'EthernetCksum' ])
     CompoundFlag('IdeAll', [ 'IdeCtrl', 'IdeDisk' ])
index 8493feb0d32f76372d27fd002af8da307ca04051..2a7ba0b3167b0ef6d348eef4e13e1410c6e01431 100644 (file)
@@ -85,6 +85,9 @@ IGbE::IGbE(const Params *p)
     regs.rxdctl.gran(1);
     regs.rxdctl.wthresh(1);
     regs.fcrth(1);
+    regs.tdwba = 0;
+    regs.rlpml = 0;
+    regs.sw_fw_sync = 0;
 
     regs.pba.rxa(0x30);
     regs.pba.txa(0x10);
@@ -197,6 +200,11 @@ IGbE::read(PacketPtr pkt)
             regs.imr &= ~regs.iam;
         chkInterrupt();
         break;
+      case REG_EICR:
+        // This is only useful for MSI, but the driver reads it every time
+        // Just don't do anything
+        pkt->set<uint32_t>(0);
+        break;
       case REG_ITR:
         pkt->set<uint32_t>(regs.itr());
         break;
@@ -231,6 +239,9 @@ IGbE::read(PacketPtr pkt)
       case REG_RDLEN:
         pkt->set<uint32_t>(regs.rdlen());
         break;
+      case REG_SRRCTL:
+        pkt->set<uint32_t>(regs.srrctl());
+        break;
       case REG_RDH:
         pkt->set<uint32_t>(regs.rdh());
         break;
@@ -246,6 +257,9 @@ IGbE::read(PacketPtr pkt)
             regs.rdtr.fpd(0);
         }
         break;
+      case REG_RXDCTL:
+        pkt->set<uint32_t>(regs.rxdctl());
+        break;
       case REG_RADV:
         pkt->set<uint32_t>(regs.radv());
         break;
@@ -261,6 +275,9 @@ IGbE::read(PacketPtr pkt)
       case REG_TDH:
         pkt->set<uint32_t>(regs.tdh());
         break;
+      case REG_TXDCA_CTL:
+        pkt->set<uint32_t>(regs.txdca_ctl());
+        break;
       case REG_TDT:
         pkt->set<uint32_t>(regs.tdt());
         break;
@@ -273,12 +290,34 @@ IGbE::read(PacketPtr pkt)
       case REG_TADV:
         pkt->set<uint32_t>(regs.tadv());
         break;
+      case REG_TDWBAL:
+        pkt->set<uint32_t>(regs.tdwba & mask(32));
+        break;
+      case REG_TDWBAH:
+        pkt->set<uint32_t>(regs.tdwba >> 32);
+        break;
       case REG_RXCSUM:
         pkt->set<uint32_t>(regs.rxcsum());
         break;
+      case REG_RLPML:
+        pkt->set<uint32_t>(regs.rlpml);
+        break;
+      case REG_RFCTL:
+        pkt->set<uint32_t>(regs.rfctl());
+        break;
       case REG_MANC:
         pkt->set<uint32_t>(regs.manc());
         break;
+      case REG_SWSM:
+        pkt->set<uint32_t>(regs.swsm());
+        regs.swsm.smbi(1);
+        break;
+      case REG_FWSM:
+        pkt->set<uint32_t>(regs.fwsm());
+        break;
+      case REG_SWFWSYNC:
+        pkt->set<uint32_t>(regs.sw_fw_sync);
+        break;
       default:
         if (!(daddr >= REG_VFTA && daddr < (REG_VFTA + VLAN_FILTER_TABLE_SIZE*4)) &&
             !(daddr >= REG_RAL && daddr < (REG_RAL + RCV_ADDRESS_TABLE_SIZE*8)) &&
@@ -385,6 +424,14 @@ IGbE::write(PacketPtr pkt)
         break;
       case REG_EERD:
         regs.eerd = val;
+        if (regs.eerd.start()) {
+            regs.eerd.done(1);
+            assert(regs.eerd.addr() < EEPROM_SIZE);
+            regs.eerd.data(flash[regs.eerd.addr()]);
+            regs.eerd.start(0);
+            DPRINTF(EthernetEEPROM, "EEPROM: read addr: %#X data %#x\n",
+                    regs.eerd.addr(), regs.eerd.data());
+        }
         break;
       case REG_MDIC:
         regs.mdic = val;
@@ -399,10 +446,16 @@ IGbE::write(PacketPtr pkt)
                 regs.mdic.data(0x796D); // link up
                 break;
             case PHY_PID:
-                regs.mdic.data(0x02A8);
+                if (params()->is8257)
+                    regs.mdic.data(0x0141);
+                else
+                    regs.mdic.data(0x02A8);
                 break;
             case PHY_EPID:
-                regs.mdic.data(0x0380);
+                if (params()->is8257)
+                    regs.mdic.data(0x0CC0);
+                else
+                    regs.mdic.data(0x0380);
                 break;
             case PHY_GSTATUS:
                 regs.mdic.data(0x7C00);
@@ -485,6 +538,9 @@ IGbE::write(PacketPtr pkt)
       case REG_TIPG:
         ; // We don't care, so don't store anything
         break;
+      case REG_IVAR0:
+        warn("Writing to IVAR0, ignoring...\n");
+        break;
       case REG_FCRTL:
         regs.fcrtl = val;
         break;
@@ -503,6 +559,9 @@ IGbE::write(PacketPtr pkt)
         regs.rdlen = val & ~mask(7);
         rxDescCache.areaChanged();
         break;
+      case REG_SRRCTL:
+        regs.srrctl = val;
+        break;
       case REG_RDH:
         regs.rdh = val;
         rxDescCache.areaChanged();
@@ -523,6 +582,9 @@ IGbE::write(PacketPtr pkt)
       case REG_RADV:
         regs.radv = val;
         break;
+      case REG_RXDCTL:
+        regs.rxdctl = val;
+        break;
       case REG_TDBAL:
         regs.tdba.tdbal( val & ~mask(4));
         txDescCache.areaChanged();
@@ -539,6 +601,11 @@ IGbE::write(PacketPtr pkt)
         regs.tdh = val;
         txDescCache.areaChanged();
         break;
+      case REG_TXDCA_CTL:
+        regs.txdca_ctl = val;
+        if (regs.txdca_ctl.enabled())
+            panic("No support for DCA\n");
+        break;
       case REG_TDT:
         regs.tdt = val;
         DPRINTF(EthernetSM, "TXS: TX Tail pointer updated\n");
@@ -558,12 +625,38 @@ IGbE::write(PacketPtr pkt)
       case REG_TADV:
         regs.tadv = val;
         break;
+      case REG_TDWBAL:
+        regs.tdwba &= ~mask(32);
+        regs.tdwba |= val;
+        txDescCache.completionWriteback(regs.tdwba & ~mask(1), regs.tdwba & mask(1));
+        break;
+      case REG_TDWBAH:
+        regs.tdwba &= mask(32);
+        regs.tdwba |= (uint64_t)val << 32;
+        txDescCache.completionWriteback(regs.tdwba & ~mask(1), regs.tdwba & mask(1));
+        break;
       case REG_RXCSUM:
         regs.rxcsum = val;
         break;
+      case REG_RLPML:
+        regs.rlpml = val;
+        break;
+      case REG_RFCTL:
+        regs.rfctl = val;
+        if (regs.rfctl.exsten())
+            panic("Extended RX descriptors not implemented\n");
+        break;
       case REG_MANC:
         regs.manc = val;
         break;
+      case REG_SWSM:
+        regs.swsm = val;
+        if (regs.fwsm.eep_fw_semaphore())
+            regs.swsm.swesmbi(0);
+        break;
+      case REG_SWFWSYNC:
+        regs.sw_fw_sync = val;
+        break;
       default:
        if (!(daddr >= REG_VFTA && daddr < (REG_VFTA + VLAN_FILTER_TABLE_SIZE*4)) &&
            !(daddr >= REG_RAL && daddr < (REG_RAL + RCV_ADDRESS_TABLE_SIZE*8)) &&
@@ -710,19 +803,38 @@ IGbE::RxDescCache::RxDescCache(IGbE *i, const std::string n, int s)
 void
 IGbE::RxDescCache::writePacket(EthPacketPtr packet)
 {
-    // We shouldn't have to deal with any of these yet
-    DPRINTF(EthernetDesc, "Packet Length: %d Desc Size: %d\n",
-            packet->length, igbe->regs.rctl.descSize());
-    assert(packet->length < igbe->regs.rctl.descSize());
-
-    assert(unusedCache.size());
+        assert(unusedCache.size());
     //if (!unusedCache.size())
     //    return false;
 
     pktPtr = packet;
     pktDone = false;
-    igbe->dmaWrite(igbe->platform->pciToDma(unusedCache.front()->buf),
-            packet->length, &pktEvent, packet->data, igbe->rxWriteDelay);
+
+    Addr buf;
+    RxDesc *desc = unusedCache.front();
+    switch (igbe->regs.srrctl.desctype()) {
+      case RXDT_LEGACY:
+        buf = desc->legacy.buf;
+        DPRINTF(EthernetDesc, "Packet Length: %d Desc Size: %d\n",
+            packet->length, igbe->regs.rctl.descSize());
+        assert(packet->length < igbe->regs.rctl.descSize());
+        break;
+      case RXDT_ADV_ONEBUF:
+        int buf_len;
+        buf_len = igbe->regs.rctl.lpe() ? igbe->regs.srrctl.bufLen() :
+                                              igbe->regs.rctl.descSize();
+        DPRINTF(EthernetDesc, "Packet Length: %d srrctl: %#x Desc Size: %d\n",
+            packet->length, igbe->regs.srrctl(), buf_len);
+        assert(packet->length < buf_len);
+        buf = desc->adv_read.pkt;
+        break;
+      default:
+        panic("Unimplemnted RX receive buffer type: %d\n",
+                igbe->regs.srrctl.desctype());
+    }
+
+    igbe->dmaWrite(igbe->platform->pciToDma(buf), packet->length, &pktEvent,
+                packet->data, igbe->rxWriteDelay);
 }
 
 void
@@ -733,7 +845,6 @@ IGbE::RxDescCache::pktComplete()
     desc = unusedCache.front();
 
     uint16_t crcfixup = igbe->regs.rctl.secrc() ? 0 : 4 ;
-    desc->len = htole((uint16_t)(pktPtr->length + crcfixup));
     DPRINTF(EthernetDesc, "pktPtr->length: %d stripcrc offset: %d value written: %d %d\n",
             pktPtr->length, crcfixup,
             htole((uint16_t)(pktPtr->length + crcfixup)),
@@ -744,21 +855,28 @@ IGbE::RxDescCache::pktComplete()
 
     DPRINTF(EthernetDesc, "Packet written to memory updating Descriptor\n");
 
-    uint8_t status = RXDS_DD | RXDS_EOP;
+    uint16_t status = RXDS_DD | RXDS_EOP;
     uint8_t err = 0;
+    uint16_t ext_err = 0;
+    uint16_t csum = 0;
+    uint16_t ptype = 0;
+    uint16_t ip_id = 0;
 
     IpPtr ip(pktPtr);
 
     if (ip) {
         DPRINTF(EthernetDesc, "Proccesing Ip packet with Id=%d\n", ip->id());
+        ptype |= RXDP_IPV4;
+        ip_id = ip->id();
 
         if (igbe->regs.rxcsum.ipofld()) {
             DPRINTF(EthernetDesc, "Checking IP checksum\n");
             status |= RXDS_IPCS;
-            desc->csum = htole(cksum(ip));
+            csum = htole(cksum(ip));
             igbe->rxIpChecksums++;
             if (cksum(ip) != 0) {
                 err |= RXDE_IPE;
+                ext_err |= RXDEE_IPE;
                 DPRINTF(EthernetDesc, "Checksum is bad!!\n");
             }
         }
@@ -766,11 +884,13 @@ IGbE::RxDescCache::pktComplete()
         if (tcp && igbe->regs.rxcsum.tuofld()) {
             DPRINTF(EthernetDesc, "Checking TCP checksum\n");
             status |= RXDS_TCPCS;
-            desc->csum = htole(cksum(tcp));
+            ptype |= RXDP_TCP;
+            csum = htole(cksum(tcp));
             igbe->rxTcpChecksums++;
             if (cksum(tcp) != 0) {
                 DPRINTF(EthernetDesc, "Checksum is bad!!\n");
                 err |= RXDE_TCPE;
+                ext_err |= RXDEE_TCPE;
             }
         }
 
@@ -778,10 +898,12 @@ IGbE::RxDescCache::pktComplete()
         if (udp && igbe->regs.rxcsum.tuofld()) {
             DPRINTF(EthernetDesc, "Checking UDP checksum\n");
             status |= RXDS_UDPCS;
-            desc->csum = htole(cksum(udp));
+            ptype |= RXDP_UDP;
+            csum = htole(cksum(udp));
             igbe->rxUdpChecksums++;
             if (cksum(udp) != 0) {
                 DPRINTF(EthernetDesc, "Checksum is bad!!\n");
+                ext_err |= RXDEE_TCPE;
                 err |= RXDE_TCPE;
             }
         }
@@ -789,12 +911,38 @@ IGbE::RxDescCache::pktComplete()
         DPRINTF(EthernetSM, "Proccesing Non-Ip packet\n");
     }
 
+    switch (igbe->regs.srrctl.desctype()) {
+      case RXDT_LEGACY:
+        desc->legacy.len = htole((uint16_t)(pktPtr->length + crcfixup));
+        desc->legacy.status = htole(status);
+        desc->legacy.errors = htole(err);
+        // No vlan support at this point... just set it to 0
+        desc->legacy.vlan = 0;
+        break;
+      case RXDT_ADV_ONEBUF:
+        desc->adv_wb.pkt_len = htole((uint16_t)(pktPtr->length + crcfixup));
+        desc->adv_wb.rss_type = htole(0);
+        desc->adv_wb.pkt_type = htole(ptype);
+        // no header splititng support yet
+        desc->adv_wb.header_len = htole(0);
+        desc->adv_wb.sph = htole(0);
+        if (igbe->regs.rxcsum.pcsd()) {
+            // no rss support right now
+            desc->adv_wb.rss_hash = htole(0);
+        } else {
+            desc->adv_wb.id = htole(ip_id);
+            desc->adv_wb.csum = htole(csum);
+        }
+        desc->adv_wb.status = htole(status);
+        desc->adv_wb.errors = htole(ext_err);
+        // no vlan support
+        desc->adv_wb.vlan_tag = htole(0); 
+        break;
+      default:
+        panic("Unimplemnted RX receive buffer type %d\n",
+                igbe->regs.srrctl.desctype());
+    }
 
-    desc->status = htole(status);
-    desc->errors = htole(err);
-
-    // No vlan support at this point... just set it to 0
-    desc->vlan = 0;
 
     // Deal with the rx timer interrupts
     if (igbe->regs.rdtr.delay()) {
@@ -883,7 +1031,7 @@ IGbE::RxDescCache::unserialize(Checkpoint *cp, const std::string &section)
 
 IGbE::TxDescCache::TxDescCache(IGbE *i, const std::string n, int s)
     : DescCache<TxDesc>(i,n, s), pktDone(false), isTcp(false), pktWaiting(false),
-       useTso(false), pktEvent(this), headerEvent(this)
+       useTso(false), pktEvent(this), headerEvent(this), nullEvent(this)
 
 {
 }
@@ -907,13 +1055,15 @@ IGbE::TxDescCache::processContextDesc()
         // is this going to be a tcp or udp packet?
         isTcp = TxdOp::tcp(desc) ? true : false;
 
-        if (TxdOp::tse(desc)) {
+        // setup all the TSO variables, they'll be ignored if we don't use 
+        // tso for this connection
+        tsoHeaderLen = TxdOp::hdrlen(desc);
+        tsoMss  = TxdOp::mss(desc);
+
+        if (TxdOp::isType(desc, TxdOp::TXD_CNXT) && TxdOp::tse(desc)) {
             DPRINTF(EthernetDesc, "TCP offload enabled for packet hdrlen: %d mss: %d paylen %d\n", 
                     TxdOp::hdrlen(desc), TxdOp::mss(desc), TxdOp::getLen(desc));
-            // setup all the TSO variables
             useTso = true;
-            tsoHeaderLen = TxdOp::hdrlen(desc);
-            tsoMss  = TxdOp::mss(desc);
             tsoTotalLen = TxdOp::getLen(desc);
             tsoLoadedHeader = false;
             tsoDescBytesUsed = 0;
@@ -921,6 +1071,7 @@ IGbE::TxDescCache::processContextDesc()
             tsoPrevSeq = 0;
             tsoPktHasHeader = false;
             tsoPkts = 0;
+
         }
 
         TxdOp::setDd(desc);
@@ -931,10 +1082,23 @@ IGbE::TxDescCache::processContextDesc()
     if (!unusedCache.size())
         return;
 
+    desc = unusedCache.front();
+    if (!useTso && TxdOp::isType(desc, TxdOp::TXD_ADVDATA) && TxdOp::tse(desc)) {
+        DPRINTF(EthernetDesc, "TCP offload(adv) enabled for packet hdrlen: %d mss: %d paylen %d\n", 
+                tsoHeaderLen, tsoMss, TxdOp::getTsoLen(desc));
+        useTso = true;
+        tsoTotalLen = TxdOp::getTsoLen(desc);
+        tsoLoadedHeader = false;
+        tsoDescBytesUsed = 0;
+        tsoUsedLen = 0;
+        tsoPrevSeq = 0;
+        tsoPktHasHeader = false;
+        tsoPkts = 0;
+    }
+
     if (useTso && !tsoLoadedHeader) {
         // we need to fetch a header
         DPRINTF(EthernetDesc, "Starting DMA of TSO header\n");
-        desc = unusedCache.front();
         assert(TxdOp::isData(desc) && TxdOp::getLen(desc) >= tsoHeaderLen);
         pktWaiting = true;
         assert(tsoHeaderLen <= 256);
@@ -1015,6 +1179,7 @@ IGbE::TxDescCache::getPacketData(EthPacketPtr p)
     TxDesc *desc;
     desc = unusedCache.front();
 
+    DPRINTF(EthernetDesc, "getPacketData(): TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2);
     assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && TxdOp::getLen(desc));
 
     pktPtr = p;
@@ -1216,14 +1381,34 @@ IGbE::TxDescCache::pktComplete()
     if (igbe->regs.txdctl.wthresh() == 0) {
         DPRINTF(EthernetDesc, "WTHRESH == 0, writing back descriptor\n");
         writeback(0);
+    } else if (igbe->regs.txdctl.gran() && igbe->regs.txdctl.wthresh() >=
+            descInBlock(usedCache.size())) {
+        DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n");
+        writeback((igbe->cacheBlockSize()-1)>>4);
     } else if (igbe->regs.txdctl.wthresh() >= usedCache.size()) {
         DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n");
         writeback((igbe->cacheBlockSize()-1)>>4);
     }
+
     enableSm();
     igbe->checkDrain();
 }
 
+void
+IGbE::TxDescCache::actionAfterWb() 
+{
+    DPRINTF(EthernetDesc, "actionAfterWb() completionEnabled: %d\n",
+            completionEnabled);
+    igbe->postInterrupt(iGbReg::IT_TXDW);
+    if (completionEnabled) { 
+        descEnd = igbe->regs.tdh();
+        DPRINTF(EthernetDesc, "Completion writing back value: %d to addr: %#x\n", descEnd,
+                completionAddress);
+        igbe->dmaWrite(igbe->platform->pciToDma(mbits(completionAddress, 63, 2)),
+                    sizeof(descEnd), &nullEvent, (uint8_t*)&descEnd, 0);
+    }
+}
+
 void
 IGbE::TxDescCache::serialize(std::ostream &os)
 {
@@ -1247,7 +1432,9 @@ IGbE::TxDescCache::serialize(std::ostream &os)
     SERIALIZE_SCALAR(tsoCopyBytes);
     SERIALIZE_SCALAR(tsoPkts);
 
-
+    SERIALIZE_SCALAR(completionAddress);
+    SERIALIZE_SCALAR(completionEnabled);
+    SERIALIZE_SCALAR(descEnd);
 }
 
 void
@@ -1272,6 +1459,10 @@ IGbE::TxDescCache::unserialize(Checkpoint *cp, const std::string &section)
     UNSERIALIZE_SCALAR(tsoDescBytesUsed);
     UNSERIALIZE_SCALAR(tsoCopyBytes);
     UNSERIALIZE_SCALAR(tsoPkts);
+
+    UNSERIALIZE_SCALAR(completionAddress);
+    UNSERIALIZE_SCALAR(completionEnabled);
+    UNSERIALIZE_SCALAR(descEnd);
 }
 
 bool
@@ -1391,7 +1582,6 @@ IGbE::txStateMachine()
     }
 
     // Only support descriptor granularity
-    assert(regs.txdctl.gran());
     if (regs.txdctl.lwthresh() && txDescCache.descLeft() < (regs.txdctl.lwthresh() * 8)) {
         DPRINTF(EthernetSM, "TXS: LWTHRESH caused posting of TXDLOW\n");
         postInterrupt(IT_TXDLOW);
index 473174bcbb1381c0f1ea0e784af7d87d115a2969..5e5ab1d213de90274ec8602f26777ffb589055b8 100644 (file)
@@ -183,7 +183,7 @@ class IGbE : public EtherDevice
         virtual long descLen() const = 0;
         virtual void updateHead(long h) = 0;
         virtual void enableSm() = 0;
-        virtual void intAfterWb() const {}
+        virtual void actionAfterWb() {}
         virtual void fetchAfterWb() = 0;
 
         std::deque<T*> usedCache;
@@ -440,7 +440,7 @@ class IGbE : public EtherDevice
                     oldHead, curHead);
 
             // If we still have more to wb, call wb now
-            intAfterWb();
+            actionAfterWb();
             if (moreToWb) {
                 moreToWb = false;
                 DPRINTF(EthernetDesc, "Writeback has more todo\n");
@@ -625,16 +625,22 @@ class IGbE : public EtherDevice
         virtual long descLen() const { return igbe->regs.tdlen() >> 4; }
         virtual void updateHead(long h) { igbe->regs.tdh(h); }
         virtual void enableSm();
-        virtual void intAfterWb() const { igbe->postInterrupt(iGbReg::IT_TXDW); }
+        virtual void actionAfterWb();
         virtual void fetchAfterWb() {
             if (!igbe->txTick && igbe->getState() == SimObject::Running)
                 fetchDescriptors();
         }
+        
+
 
         bool pktDone;
         bool isTcp;
         bool pktWaiting;
         bool pktMultiDesc;
+        Addr completionAddress;
+        bool completionEnabled;
+        uint32_t descEnd;
+        
 
         // tso variables
         bool useTso;
@@ -662,6 +668,11 @@ class IGbE : public EtherDevice
         void getPacketData(EthPacketPtr p);
         void processContextDesc();
 
+        /** Return the number of dsecriptors in a cache block for threshold
+         * operations.
+         */
+        int descInBlock(int num_desc) { return num_desc / 
+            igbe->cacheBlockSize() / sizeof(iGbReg::TxDesc); }
         /** Ask if the packet has been transfered so the state machine can give
          * it to the fifo.
          * @return packet available in descriptor cache
@@ -689,8 +700,19 @@ class IGbE : public EtherDevice
         void headerComplete();
         EventWrapper<TxDescCache, &TxDescCache::headerComplete> headerEvent;
 
+
+        void completionWriteback(Addr a, bool enabled) {
+            DPRINTF(EthernetDesc, "Completion writeback Addr: %#x enabled: %d\n", 
+                    a, enabled);
+            completionAddress = a;
+            completionEnabled = enabled;
+        }
+
         virtual bool hasOutstandingEvents();
 
+        void nullCallback() { DPRINTF(EthernetDesc, "Completion writeback complete\n"); }
+        EventWrapper<TxDescCache, &TxDescCache::nullCallback> nullEvent;
+
         virtual void serialize(std::ostream &os);
         virtual void unserialize(Checkpoint *cp, const std::string &section);
 
index 8d9996ef0344cdf434e1ed8b5482c42b1e4d077b..78492d47e8ea70b5fee6cfd02a4f66063f919e5c 100644 (file)
@@ -59,11 +59,14 @@ const uint32_t REG_FCTTV    = 0x00170;
 const uint32_t REG_TIPG     = 0x00410;
 const uint32_t REG_AIFS     = 0x00458;
 const uint32_t REG_LEDCTL   = 0x00e00;
+const uint32_t REG_EICR     = 0x01580;
+const uint32_t REG_IVAR0    = 0x01700;
 const uint32_t REG_FCRTL    = 0x02160;
 const uint32_t REG_FCRTH    = 0x02168;
 const uint32_t REG_RDBAL    = 0x02800;
 const uint32_t REG_RDBAH    = 0x02804;
 const uint32_t REG_RDLEN    = 0x02808;
+const uint32_t REG_SRRCTL   = 0x0280C;
 const uint32_t REG_RDH      = 0x02810;
 const uint32_t REG_RDT      = 0x02818;
 const uint32_t REG_RDTR     = 0x02820;
@@ -74,12 +77,17 @@ const uint32_t REG_TDBAL    = 0x03800;
 const uint32_t REG_TDBAH    = 0x03804;
 const uint32_t REG_TDLEN    = 0x03808;
 const uint32_t REG_TDH      = 0x03810;
+const uint32_t REG_TXDCA_CTL = 0x03814;
 const uint32_t REG_TDT      = 0x03818;
 const uint32_t REG_TIDV     = 0x03820;
 const uint32_t REG_TXDCTL   = 0x03828;
 const uint32_t REG_TADV     = 0x0382C;
+const uint32_t REG_TDWBAL   = 0x03838;
+const uint32_t REG_TDWBAH   = 0x0383C;
 const uint32_t REG_CRCERRS  = 0x04000;
 const uint32_t REG_RXCSUM   = 0x05000;
+const uint32_t REG_RLPML    = 0x05004;
+const uint32_t REG_RFCTL    = 0x05008;
 const uint32_t REG_MTA      = 0x05200;
 const uint32_t REG_RAL      = 0x05400;
 const uint32_t REG_RAH      = 0x05404;
@@ -87,6 +95,9 @@ const uint32_t REG_VFTA     = 0x05600;
 
 const uint32_t REG_WUC      = 0x05800;
 const uint32_t REG_MANC     = 0x05820;
+const uint32_t REG_SWSM     = 0x05B50;
+const uint32_t REG_FWSM     = 0x05B54;
+const uint32_t REG_SWFWSYNC = 0x05B5C;
 
 const uint8_t EEPROM_READ_OPCODE_SPI    = 0x03;
 const uint8_t EEPROM_RDSR_OPCODE_SPI    = 0x05;
@@ -94,9 +105,9 @@ const uint8_t EEPROM_SIZE               = 64;
 const uint16_t EEPROM_CSUM              = 0xBABA;
 
 const uint8_t VLAN_FILTER_TABLE_SIZE    = 128;
-const uint8_t RCV_ADDRESS_TABLE_SIZE    = 16;
+const uint8_t RCV_ADDRESS_TABLE_SIZE    = 24;
 const uint8_t MULTICAST_TABLE_SIZE      = 128;
-const uint32_t STATS_REGS_SIZE           = 0x124;
+const uint32_t STATS_REGS_SIZE           = 0x228;
 
 
 // Registers in that are accessed in the PHY
@@ -108,14 +119,17 @@ const uint8_t PHY_EPSTATUS      = 15;
 const uint8_t PHY_AGC           = 18;
 
 // Receive Descriptor Status Flags
-const uint8_t RXDS_PIF         = 0x80;
-const uint8_t RXDS_IPCS        = 0x40;
-const uint8_t RXDS_TCPCS       = 0x20;
-const uint8_t RXDS_UDPCS       = 0x10;
-const uint8_t RXDS_VP          = 0x08;
-const uint8_t RXDS_IXSM        = 0x04;
-const uint8_t RXDS_EOP         = 0x02;
-const uint8_t RXDS_DD          = 0x01;
+const uint16_t RXDS_DYNINT      = 0x800;
+const uint16_t RXDS_UDPV        = 0x400;
+const uint16_t RXDS_CRCV        = 0x100;
+const uint16_t RXDS_PIF         = 0x080;
+const uint16_t RXDS_IPCS        = 0x040;
+const uint16_t RXDS_TCPCS       = 0x020;
+const uint16_t RXDS_UDPCS       = 0x010;
+const uint16_t RXDS_VP          = 0x008;
+const uint16_t RXDS_IXSM        = 0x004;
+const uint16_t RXDS_EOP         = 0x002;
+const uint16_t RXDS_DD          = 0x001;
 
 // Receive Descriptor Error Flags
 const uint8_t RXDE_RXE         = 0x80;
@@ -125,6 +139,32 @@ const uint8_t RXDE_SEQ         = 0x04;
 const uint8_t RXDE_SE          = 0x02;
 const uint8_t RXDE_CE          = 0x01;
 
+// Receive Descriptor Extended Error Flags
+const uint16_t RXDEE_HBO       = 0x008;
+const uint16_t RXDEE_CE        = 0x010;
+const uint16_t RXDEE_LE        = 0x020;
+const uint16_t RXDEE_PE        = 0x080;
+const uint16_t RXDEE_OSE       = 0x100;
+const uint16_t RXDEE_USE       = 0x200;
+const uint16_t RXDEE_TCPE      = 0x400;
+const uint16_t RXDEE_IPE       = 0x800;
+
+
+// Receive Descriptor Types
+const uint8_t RXDT_LEGACY      = 0x00;
+const uint8_t RXDT_ADV_ONEBUF  = 0x01;
+const uint8_t RXDT_ADV_SPLIT_A = 0x05;
+
+// Receive Descriptor Packet Types
+const uint16_t RXDP_IPV4       = 0x001;
+const uint16_t RXDP_IPV4E      = 0x002;
+const uint16_t RXDP_IPV6       = 0x004;
+const uint16_t RXDP_IPV6E      = 0x008;
+const uint16_t RXDP_TCP        = 0x010;
+const uint16_t RXDP_UDP        = 0x020;
+const uint16_t RXDP_SCTP       = 0x040;
+const uint16_t RXDP_NFS        = 0x080;
+
 // Interrupt types
 enum IntTypes
 {
@@ -147,12 +187,38 @@ enum IntTypes
 
 // Receive Descriptor struct
 struct RxDesc {
-    Addr buf;
-    uint16_t len;
-    uint16_t csum;
-    uint8_t status;
-    uint8_t errors;
-    uint16_t vlan;
+    union { 
+        struct {
+            Addr buf;
+            uint16_t len;
+            uint16_t csum;
+            uint8_t status;
+            uint8_t errors;
+            uint16_t vlan;
+        } legacy;
+        struct {
+            Addr pkt;
+            Addr hdr;
+        } adv_read;
+        struct {
+            uint16_t rss_type:4;
+            uint16_t pkt_type:12;
+            uint16_t __reserved1:5;
+            uint16_t header_len:10;
+            uint16_t sph:1;
+            union {
+                struct {
+                    uint16_t id;
+                    uint16_t csum;
+                };
+                uint32_t rss_hash;
+            };
+            uint32_t status:20;
+            uint32_t errors:12;
+            uint16_t pkt_len;
+            uint16_t vlan_tag;
+        } adv_wb ;
+    };
 };
 
 struct TxDesc {
@@ -163,24 +229,33 @@ struct TxDesc {
 namespace TxdOp {
 const uint8_t TXD_CNXT = 0x0;
 const uint8_t TXD_DATA = 0x1;
+const uint8_t TXD_ADVCNXT = 0x2;
+const uint8_t TXD_ADVDATA = 0x3;
 
 bool isLegacy(TxDesc *d) { return !bits(d->d2,29,29); }
 uint8_t getType(TxDesc *d) { return bits(d->d2, 23,20); }
-bool isContext(TxDesc *d) { return !isLegacy(d) && getType(d) == TXD_CNXT; }
-bool isData(TxDesc *d) { return !isLegacy(d) && getType(d) == TXD_DATA; }
+bool isType(TxDesc *d, uint8_t type) { return getType(d) == type; }
+bool isTypes(TxDesc *d, uint8_t t1, uint8_t t2) { return isType(d, t1) || isType(d, t2); }
+bool isAdvDesc(TxDesc *d) { return !isLegacy(d) && isTypes(d, TXD_ADVDATA,TXD_ADVCNXT);  }
+bool isContext(TxDesc *d) { return !isLegacy(d) && isTypes(d,TXD_CNXT, TXD_ADVCNXT); }
+bool isData(TxDesc *d) { return !isLegacy(d) && isTypes(d, TXD_DATA, TXD_ADVDATA); }
 
 Addr getBuf(TxDesc *d) { assert(isLegacy(d) || isData(d)); return d->d1; }
 Addr getLen(TxDesc *d) { if (isLegacy(d)) return bits(d->d2,15,0); else return bits(d->d2, 19,0); }
-void setDd(TxDesc *d)
-{
-    replaceBits(d->d2, 35, 32, ULL(1));
-}
+void setDd(TxDesc *d) { replaceBits(d->d2, 35, 32, ULL(1)); }
 
-bool ide(TxDesc *d)  { return bits(d->d2, 31,31); }
+bool ide(TxDesc *d)  { return bits(d->d2, 31,31) && (getType(d) == TXD_DATA || isLegacy(d)); }
 bool vle(TxDesc *d)  { assert(isLegacy(d) || isData(d)); return bits(d->d2, 30,30); }
 bool rs(TxDesc *d)   { return bits(d->d2, 27,27); }
 bool ic(TxDesc *d)   { assert(isLegacy(d) || isData(d)); return isLegacy(d) && bits(d->d2, 26,26); }
-bool tse(TxDesc *d)  { return (isData(d) || isContext(d)) && bits(d->d2, 26,26); }
+bool tse(TxDesc *d)  { 
+    if (isTypes(d, TXD_CNXT, TXD_DATA))
+        return bits(d->d2, 26,26); 
+    if (isType(d, TXD_ADVDATA))
+        return bits(d->d2, 31, 31);
+    return false;
+}
+
 bool ifcs(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 25,25); }
 bool eop(TxDesc *d)  { assert(isLegacy(d) || isData(d)); return bits(d->d2, 24,24); }
 bool ip(TxDesc *d)   { assert(isContext(d)); return bits(d->d2, 25,25); }
@@ -199,7 +274,14 @@ int ipcse(TxDesc *d) { assert(isContext(d)); return bits(d->d1,31,16); }
 int ipcso(TxDesc *d) { assert(isContext(d)); return bits(d->d1,15,8); }
 int ipcss(TxDesc *d) { assert(isContext(d)); return bits(d->d1,7,0); }
 int mss(TxDesc *d) { assert(isContext(d)); return bits(d->d2,63,48); }
-int hdrlen(TxDesc *d) { assert(isContext(d)); return bits(d->d2,47,40); }
+int hdrlen(TxDesc *d) { 
+    assert(isContext(d)); 
+    if (!isAdvDesc(d))
+        return bits(d->d2,47,40);
+    return bits(d->d2, 47,40) + bits(d->d1, 8,0) + bits(d->d1, 15, 9); 
+}
+
+int getTsoLen(TxDesc *d) { assert(isType(d, TXD_ADVDATA)); return bits(d->d2, 63,46); }
 int utcmd(TxDesc *d) { assert(isContext(d)); return bits(d->d2,24,31); }
 } // namespace TxdOp
 
@@ -304,8 +386,8 @@ struct Regs {
     struct EERD : public Reg<uint32_t> { // 0x0014 EERD Register
         using Reg<uint32_t>::operator=;
         ADD_FIELD32(start,0,1);  // start read
-        ADD_FIELD32(done,4,1);   // done read
-        ADD_FIELD32(addr,8,8);   // address
+        ADD_FIELD32(done,1,1);   // done read
+        ADD_FIELD32(addr,2,14);   // address
         ADD_FIELD32(data,16,16); // data
     };
     EERD eerd;
@@ -471,6 +553,17 @@ struct Regs {
     };
     RDLEN rdlen;
 
+    struct SRRCTL : public Reg<uint32_t> { // 0x280C SRRCTL Register
+        using Reg<uint32_t>::operator=;
+        ADD_FIELD32(pktlen, 0, 7);
+        ADD_FIELD32(hdrlen, 16, 7); // guess based on header, not documented
+        ADD_FIELD32(desctype, 25,3); // type of descriptor 000 legacy, 001 adv,
+                                     //101 hdr split
+        int bufLen() { return pktlen() << 10; }
+        int hdrLen() { return hdrlen() << 6; }
+    };
+    SRRCTL srrctl;
+
     struct RDH : public Reg<uint32_t> { // 0x2810 RDH Register
         using Reg<uint32_t>::operator=;
         ADD_FIELD32(rdh,0,16); // head of the descriptor ring
@@ -532,6 +625,14 @@ struct Regs {
     };
     TDH tdh;
 
+    struct TXDCA_CTL : public Reg<uint32_t> { // 0x3814 TXDCA_CTL Register
+        using Reg<uint32_t>::operator=;
+        ADD_FIELD32(cpu_mask, 0, 5);
+        ADD_FIELD32(enabled, 5,1);
+        ADD_FIELD32(relax_ordering, 6, 1);
+    };
+    TXDCA_CTL txdca_ctl;
+
     struct TDT : public Reg<uint32_t> { // 0x3818 TDT Register
         using Reg<uint32_t>::operator=;
         ADD_FIELD32(tdt,0,16); // tail of the descriptor ring
@@ -564,15 +665,42 @@ struct Regs {
         ADD_FIELD32(idv,0,16); // absolute interrupt delay
     };
     TADV tadv;
+/*
+    struct TDWBA : public Reg<uint64_t> { // 0x3838 TDWBA Register
+        using Reg<uint64_t>::operator=;
+        ADD_FIELD64(en,0,1); // enable  transmit description ring address writeback
+        ADD_FIELD64(tdwbal,2,32); // base address of transmit descriptor ring address writeback
+        ADD_FIELD64(tdwbah,32,32); // base address of transmit descriptor ring
+    };
+    TDWBA tdwba;*/
+    uint64_t tdwba;
 
     struct RXCSUM : public Reg<uint32_t> { // 0x5000 RXCSUM Register
         using Reg<uint32_t>::operator=;
         ADD_FIELD32(pcss,0,8);
         ADD_FIELD32(ipofld,8,1);
         ADD_FIELD32(tuofld,9,1);
+        ADD_FIELD32(pcsd, 13,1);
     };
     RXCSUM rxcsum;
 
+    uint32_t rlpml; // 0x5004 RLPML probably maximum accepted packet size
+
+    struct RFCTL : public Reg<uint32_t> { // 0x5008 RFCTL Register
+        using Reg<uint32_t>::operator=;
+        ADD_FIELD32(iscsi_dis,0,1);
+        ADD_FIELD32(iscsi_dwc,1,5);
+        ADD_FIELD32(nfsw_dis,6,1);
+        ADD_FIELD32(nfsr_dis,7,1);
+        ADD_FIELD32(nfs_ver,8,2);
+        ADD_FIELD32(ipv6_dis,10,1);
+        ADD_FIELD32(ipv6xsum_dis,11,1);
+        ADD_FIELD32(ackdis,13,1);
+        ADD_FIELD32(ipfrsp_dis,14,1);
+        ADD_FIELD32(exsten,15,1);
+    };
+    RFCTL rfctl;
+
     struct MANC : public Reg<uint32_t> { // 0x5820 MANC Register
         using Reg<uint32_t>::operator=;
         ADD_FIELD32(smbus,0,1);    // SMBus enabled #####
@@ -605,6 +733,32 @@ struct Regs {
     };
     MANC manc;
 
+    struct SWSM : public Reg<uint32_t> { // 0x5B50 SWSM register
+        using Reg<uint32_t>::operator=;
+        ADD_FIELD32(smbi,0,1); // Semaphone bit
+        ADD_FIELD32(swesmbi, 1,1); // Software eeporm semaphore
+        ADD_FIELD32(wmng, 2,1); // Wake MNG clock
+        ADD_FIELD32(reserved, 3, 29);
+    };
+    SWSM swsm;
+
+    struct FWSM : public Reg<uint32_t> { // 0x5B54 FWSM register
+        using Reg<uint32_t>::operator=;
+        ADD_FIELD32(eep_fw_semaphore,0,1); 
+        ADD_FIELD32(fw_mode, 1,3); 
+        ADD_FIELD32(ide, 4,1); 
+        ADD_FIELD32(sol, 5,1);
+        ADD_FIELD32(eep_roload, 6,1);
+        ADD_FIELD32(reserved, 7,8);
+        ADD_FIELD32(fw_val_bit, 15, 1);
+        ADD_FIELD32(reset_cnt, 16, 3);
+        ADD_FIELD32(ext_err_ind, 19, 6);
+        ADD_FIELD32(reserved2, 25, 7);
+    };
+    FWSM fwsm;
+
+    uint32_t sw_fw_sync;
+
     void serialize(std::ostream &os)
     {
         paramOut(os, "ctrl", ctrl._data);
@@ -625,6 +779,7 @@ struct Regs {
         paramOut(os, "fcrth", fcrth._data);
         paramOut(os, "rdba", rdba._data);
         paramOut(os, "rdlen", rdlen._data);
+        paramOut(os, "srrctl", srrctl._data);
         paramOut(os, "rdh", rdh._data);
         paramOut(os, "rdt", rdt._data);
         paramOut(os, "rdtr", rdtr._data);
@@ -634,12 +789,20 @@ struct Regs {
         paramOut(os, "tdba", tdba._data);
         paramOut(os, "tdlen", tdlen._data);
         paramOut(os, "tdh", tdh._data);
+        paramOut(os, "txdca_ctl", txdca_ctl._data);
         paramOut(os, "tdt", tdt._data);
         paramOut(os, "tidv", tidv._data);
         paramOut(os, "txdctl", txdctl._data);
         paramOut(os, "tadv", tadv._data);
+        //paramOut(os, "tdwba", tdwba._data);
+        SERIALIZE_SCALAR(tdwba);
         paramOut(os, "rxcsum", rxcsum._data);
+        SERIALIZE_SCALAR(rlpml);
+        paramOut(os, "rfctl", rfctl._data);
         paramOut(os, "manc", manc._data);
+        paramOut(os, "swsm", swsm._data);
+        paramOut(os, "fwsm", fwsm._data);
+        SERIALIZE_SCALAR(sw_fw_sync);
     }
 
     void unserialize(Checkpoint *cp, const std::string &section)
@@ -662,6 +825,7 @@ struct Regs {
         paramIn(cp, section, "fcrth", fcrth._data);
         paramIn(cp, section, "rdba", rdba._data);
         paramIn(cp, section, "rdlen", rdlen._data);
+        paramIn(cp, section, "srrctl", srrctl._data);
         paramIn(cp, section, "rdh", rdh._data);
         paramIn(cp, section, "rdt", rdt._data);
         paramIn(cp, section, "rdtr", rdtr._data);
@@ -671,12 +835,20 @@ struct Regs {
         paramIn(cp, section, "tdba", tdba._data);
         paramIn(cp, section, "tdlen", tdlen._data);
         paramIn(cp, section, "tdh", tdh._data);
+        paramIn(cp, section, "txdca_ctl", txdca_ctl._data);
         paramIn(cp, section, "tdt", tdt._data);
         paramIn(cp, section, "tidv", tidv._data);
         paramIn(cp, section, "txdctl", txdctl._data);
         paramIn(cp, section, "tadv", tadv._data);
+        UNSERIALIZE_SCALAR(tdwba);
+        //paramIn(cp, section, "tdwba", tdwba._data);
         paramIn(cp, section, "rxcsum", rxcsum._data);
+        UNSERIALIZE_SCALAR(rlpml);
+        paramIn(cp, section, "rfctl", rfctl._data);
         paramIn(cp, section, "manc", manc._data);
+        paramIn(cp, section, "swsm", swsm._data);
+        paramIn(cp, section, "fwsm", fwsm._data);
+        UNSERIALIZE_SCALAR(sw_fw_sync);
     }
 };
 } // iGbReg namespace