📄 e100.c
字号:
config->rx_discard_short_frames = 0x0; /* 1=discard, 0=save */ config->promiscuous_mode = 0x1; /* 1=on, 0=off */ } if(nic->flags & multicast_all) config->multicast_all = 0x1; /* 1=accept, 0=no */ /* disable WoL when up */ if(netif_running(nic->netdev) || !(nic->flags & wol_magic)) config->magic_packet_disable = 0x1; /* 1=off, 0=on */ if(nic->mac >= mac_82558_D101_A4) { config->fc_disable = 0x1; /* 1=Tx fc off, 0=Tx fc on */ config->mwi_enable = 0x1; /* 1=enable, 0=disable */ config->standard_tcb = 0x0; /* 1=standard, 0=extended */ config->rx_long_ok = 0x1; /* 1=VLANs ok, 0=standard */ if(nic->mac >= mac_82559_D101M) config->tno_intr = 0x1; /* TCO stats enable */ else config->standard_stat_counter = 0x0; } DPRINTK(HW, DEBUG, "[00-07]=%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); DPRINTK(HW, DEBUG, "[08-15]=%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15]); DPRINTK(HW, DEBUG, "[16-23]=%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23]);}static void e100_setup_iaaddr(struct nic *nic, struct cb *cb, struct sk_buff *skb){ cb->command = cpu_to_le16(cb_iaaddr); memcpy(cb->u.iaaddr, nic->netdev->dev_addr, ETH_ALEN);}static void e100_dump(struct nic *nic, struct cb *cb, struct sk_buff *skb){ cb->command = cpu_to_le16(cb_dump); cb->u.dump_buffer_addr = cpu_to_le32(nic->dma_addr + offsetof(struct mem, dump_buf));}#define NCONFIG_AUTO_SWITCH 0x0080#define MII_NSC_CONG MII_RESV1#define NSC_CONG_ENABLE 0x0100#define NSC_CONG_TXREADY 0x0400#define ADVERTISE_FC_SUPPORTED 0x0400static int e100_phy_init(struct nic *nic){ struct net_device *netdev = nic->netdev; u32 addr; u16 bmcr, stat, id_lo, id_hi, cong; /* Discover phy addr by searching addrs in order {1,0,2,..., 31} */ for(addr = 0; addr < 32; addr++) { nic->mii.phy_id = (addr == 0) ? 1 : (addr == 1) ? 0 : addr; bmcr = mdio_read(netdev, nic->mii.phy_id, MII_BMCR); stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR); stat = mdio_read(netdev, nic->mii.phy_id, MII_BMSR); if(!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0)))) break; } DPRINTK(HW, DEBUG, "phy_addr = %d\n", nic->mii.phy_id); if(addr == 32) return -EAGAIN; /* Selected the phy and isolate the rest */ for(addr = 0; addr < 32; addr++) { if(addr != nic->mii.phy_id) { mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE); } else { bmcr = mdio_read(netdev, addr, MII_BMCR); mdio_write(netdev, addr, MII_BMCR, bmcr & ~BMCR_ISOLATE); } } /* Get phy ID */ id_lo = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID1); id_hi = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID2); nic->phy = (u32)id_hi << 16 | (u32)id_lo; DPRINTK(HW, DEBUG, "phy ID = 0x%08X\n", nic->phy); /* Handle National tx phys */#define NCS_PHY_MODEL_MASK 0xFFF0FFFF if((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx) { /* Disable congestion control */ cong = mdio_read(netdev, nic->mii.phy_id, MII_NSC_CONG); cong |= NSC_CONG_TXREADY; cong &= ~NSC_CONG_ENABLE; mdio_write(netdev, nic->mii.phy_id, MII_NSC_CONG, cong); } if((nic->mac >= mac_82550_D102) || ((nic->flags & ich) && (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000))) { /* enable/disable MDI/MDI-X auto-switching. MDI/MDI-X auto-switching is disabled for 82551ER/QM chips */ if((nic->mac == mac_82551_E) || (nic->mac == mac_82551_F) || (nic->mac == mac_82551_10) || (nic->mii.force_media) || !(nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled)) mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, 0); else mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, NCONFIG_AUTO_SWITCH); } return 0;}static int e100_hw_init(struct nic *nic){ int err; e100_hw_reset(nic); DPRINTK(HW, ERR, "e100_hw_init\n"); if(!in_interrupt() && (err = e100_self_test(nic))) return err; if((err = e100_phy_init(nic))) return err; if((err = e100_exec_cmd(nic, cuc_load_base, 0))) return err; if((err = e100_exec_cmd(nic, ruc_load_base, 0))) return err; if ((err = e100_exec_cb_wait(nic, NULL, e100_setup_ucode))) return err; if((err = e100_exec_cb(nic, NULL, e100_configure))) return err; if((err = e100_exec_cb(nic, NULL, e100_setup_iaaddr))) return err; if((err = e100_exec_cmd(nic, cuc_dump_addr, nic->dma_addr + offsetof(struct mem, stats)))) return err; if((err = e100_exec_cmd(nic, cuc_dump_reset, 0))) return err; e100_disable_irq(nic); return 0;}static void e100_multi(struct nic *nic, struct cb *cb, struct sk_buff *skb){ struct net_device *netdev = nic->netdev; struct dev_mc_list *list = netdev->mc_list; u16 i, count = min(netdev->mc_count, E100_MAX_MULTICAST_ADDRS); cb->command = cpu_to_le16(cb_multi); cb->u.multi.count = cpu_to_le16(count * ETH_ALEN); for(i = 0; list && i < count; i++, list = list->next) memcpy(&cb->u.multi.addr[i*ETH_ALEN], &list->dmi_addr, ETH_ALEN);}static void e100_set_multicast_list(struct net_device *netdev){ struct nic *nic = netdev_priv(netdev); DPRINTK(HW, DEBUG, "mc_count=%d, flags=0x%04X\n", netdev->mc_count, netdev->flags); if(netdev->flags & IFF_PROMISC) nic->flags |= promiscuous; else nic->flags &= ~promiscuous; if(netdev->flags & IFF_ALLMULTI || netdev->mc_count > E100_MAX_MULTICAST_ADDRS) nic->flags |= multicast_all; else nic->flags &= ~multicast_all; e100_exec_cb(nic, NULL, e100_configure); e100_exec_cb(nic, NULL, e100_multi);}static void e100_update_stats(struct nic *nic){ struct net_device_stats *ns = &nic->net_stats; struct stats *s = &nic->mem->stats; u32 *complete = (nic->mac < mac_82558_D101_A4) ? &s->fc_xmt_pause : (nic->mac < mac_82559_D101M) ? (u32 *)&s->xmt_tco_frames : &s->complete; /* Device's stats reporting may take several microseconds to * complete, so where always waiting for results of the * previous command. */ if(*complete == le32_to_cpu(cuc_dump_reset_complete)) { *complete = 0; nic->tx_frames = le32_to_cpu(s->tx_good_frames); nic->tx_collisions = le32_to_cpu(s->tx_total_collisions); ns->tx_aborted_errors += le32_to_cpu(s->tx_max_collisions); ns->tx_window_errors += le32_to_cpu(s->tx_late_collisions); ns->tx_carrier_errors += le32_to_cpu(s->tx_lost_crs); ns->tx_fifo_errors += le32_to_cpu(s->tx_underruns); ns->collisions += nic->tx_collisions; ns->tx_errors += le32_to_cpu(s->tx_max_collisions) + le32_to_cpu(s->tx_lost_crs); ns->rx_length_errors += le32_to_cpu(s->rx_short_frame_errors) + nic->rx_over_length_errors; ns->rx_crc_errors += le32_to_cpu(s->rx_crc_errors); ns->rx_frame_errors += le32_to_cpu(s->rx_alignment_errors); ns->rx_over_errors += le32_to_cpu(s->rx_overrun_errors); ns->rx_fifo_errors += le32_to_cpu(s->rx_overrun_errors); ns->rx_missed_errors += le32_to_cpu(s->rx_resource_errors); ns->rx_errors += le32_to_cpu(s->rx_crc_errors) + le32_to_cpu(s->rx_alignment_errors) + le32_to_cpu(s->rx_short_frame_errors) + le32_to_cpu(s->rx_cdt_errors); nic->tx_deferred += le32_to_cpu(s->tx_deferred); nic->tx_single_collisions += le32_to_cpu(s->tx_single_collisions); nic->tx_multiple_collisions += le32_to_cpu(s->tx_multiple_collisions); if(nic->mac >= mac_82558_D101_A4) { nic->tx_fc_pause += le32_to_cpu(s->fc_xmt_pause); nic->rx_fc_pause += le32_to_cpu(s->fc_rcv_pause); nic->rx_fc_unsupported += le32_to_cpu(s->fc_rcv_unsupported); if(nic->mac >= mac_82559_D101M) { nic->tx_tco_frames += le16_to_cpu(s->xmt_tco_frames); nic->rx_tco_frames += le16_to_cpu(s->rcv_tco_frames); } } } if(e100_exec_cmd(nic, cuc_dump_reset, 0)) DPRINTK(TX_ERR, DEBUG, "exec cuc_dump_reset failed\n");}static void e100_adjust_adaptive_ifs(struct nic *nic, int speed, int duplex){ /* Adjust inter-frame-spacing (IFS) between two transmits if * we're getting collisions on a half-duplex connection. */ if(duplex == DUPLEX_HALF) { u32 prev = nic->adaptive_ifs; u32 min_frames = (speed == SPEED_100) ? 1000 : 100; if((nic->tx_frames / 32 < nic->tx_collisions) && (nic->tx_frames > min_frames)) { if(nic->adaptive_ifs < 60) nic->adaptive_ifs += 5; } else if (nic->tx_frames < min_frames) { if(nic->adaptive_ifs >= 5) nic->adaptive_ifs -= 5; } if(nic->adaptive_ifs != prev) e100_exec_cb(nic, NULL, e100_configure); }}static void e100_watchdog(unsigned long data){ struct nic *nic = (struct nic *)data; struct ethtool_cmd cmd; DPRINTK(TIMER, DEBUG, "right now = %ld\n", jiffies); /* mii library handles link maintenance tasks */ mii_ethtool_gset(&nic->mii, &cmd); if(mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->netdev)) { DPRINTK(LINK, INFO, "link up, %sMbps, %s-duplex\n", cmd.speed == SPEED_100 ? "100" : "10", cmd.duplex == DUPLEX_FULL ? "full" : "half"); } else if(!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->netdev)) { DPRINTK(LINK, INFO, "link down\n"); } mii_check_link(&nic->mii); /* Software generated interrupt to recover from (rare) Rx * allocation failure. * Unfortunately have to use a spinlock to not re-enable interrupts * accidentally, due to hardware that shares a register between the * interrupt mask bit and the SW Interrupt generation bit */ spin_lock_irq(&nic->cmd_lock); writeb(readb(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi); spin_unlock_irq(&nic->cmd_lock); e100_write_flush(nic); e100_update_stats(nic); e100_adjust_adaptive_ifs(nic, cmd.speed, cmd.duplex); if(nic->mac <= mac_82557_D100_C) /* Issue a multicast command to workaround a 557 lock up */ e100_set_multicast_list(nic->netdev); if(nic->flags & ich && cmd.speed==SPEED_10 && cmd.duplex==DUPLEX_HALF) /* Need SW workaround for ICH[x] 10Mbps/half duplex Tx hang. */ nic->flags |= ich_10h_workaround; else nic->flags &= ~ich_10h_workaround; mod_timer(&nic->watchdog, jiffies + E100_WATCHDOG_PERIOD);}static void e100_xmit_prepare(struct nic *nic, struct cb *cb, struct sk_buff *skb){ cb->command = nic->tx_command; /* interrupt every 16 packets regardless of delay */ if((nic->cbs_avail & ~15) == nic->cbs_avail) cb->command |= cpu_to_le16(cb_i); cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd); cb->u.tcb.tcb_byte_count = 0; cb->u.tcb.threshold = nic->tx_threshold; cb->u.tcb.tbd_count = 1; cb->u.tcb.tbd.buf_addr = cpu_to_le32(pci_map_single(nic->pdev, skb->data, skb->len, PCI_DMA_TODEVICE)); /* check for mapping failure? */ cb->u.tcb.tbd.size = cpu_to_le16(skb->len);}static int e100_xmit_frame(struct sk_buff *skb, struct net_device *netdev){ struct nic *nic = netdev_priv(netdev); int err; if(nic->flags & ich_10h_workaround) { /* SW workaround for ICH[x] 10Mbps/half duplex Tx hang. Issue a NOP command followed by a 1us delay before issuing the Tx command. */ if(e100_exec_cmd(nic, cuc_nop, 0)) DPRINTK(TX_ERR, DEBUG, "exec cuc_nop failed\n"); udelay(1); } err = e100_exec_cb(nic, skb, e100_xmit_prepare); switch(err) { case -ENOSPC: /* We queued the skb, but now we're out of space. */ DPRINTK(TX_ERR, DEBUG, "No space for CB\n"); netif_stop_queue(netdev); break; case -ENOMEM: /* This is a hard error - log it. */ DPRINTK(TX_ERR, DEBUG, "Out of Tx resources, returning skb\n"); netif_stop_queue(netdev); return 1; } netdev->trans_start = jiffies; return 0;}static int e100_tx_clean(struct nic *nic){ struct cb *cb; int tx_cleaned = 0; spin_lock(&nic->cb_lock); /* Clean CBs marked complete */ for(cb = nic->cb_to_clean; cb->status & cpu_to_le16(cb_complete); cb = nic->cb_to_clean = cb->next) { DPRINTK(TX_DONE, DEBUG, "cb[%d]->status = 0x%04X\n", (int)(((void*)cb - (void*)nic->cbs)/sizeof(struct cb)), cb->status); if(likely(cb->skb != NULL)) { nic->net_stats.tx_packets++; nic->net_stats.tx_bytes += cb->skb->len; pci_unmap_single(nic->pdev, le32_to_cpu(cb->u.tcb.tbd.buf_addr), le16_to_cpu(cb->u.tcb.tbd.size), PCI_DMA_TODEVICE); dev_kfree_skb_any(cb->skb); cb->skb = NULL; tx_cleaned = 1; } cb->status = 0; nic->cbs_avail++; } spin_unlock(&nic->cb_lock); /* Recover from running out of Tx resources in xmit_frame */ if(unlikely(tx_cleaned && netif_queue_stopped(nic->netdev))) netif_wake_queue(nic->netdev); return tx_cleaned;}static void e100_clean_cbs(struct nic *nic){ if(nic->cbs) { while(nic->cbs_avail != nic->params.cbs.count) { struct cb *cb = nic->cb_to_clean; if(cb->skb) { pci_unmap_single(nic->pdev, le32_to_cpu(cb->u.tcb.tbd.buf_addr), le16_to_cpu(cb->u.tcb.tbd.size), PCI_DMA_TODEVICE); dev_kfree_skb(cb->skb); } nic->cb_to_clean = nic->cb_to_clean->next; nic->cbs_avail++; } pci_free_consistent(nic->pdev, sizeof(struct cb) * nic->params.cbs.count, nic->cbs, nic->cbs_dma_addr); nic->cbs = NULL; nic->cbs_avail = 0; } nic->cuc_cmd = cuc_start; nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = nic->cbs;}static int e100_alloc_cbs(struct nic *nic){ struct cb *cb; unsigned int i, count = nic->params.cbs.count; nic->cuc_cmd = cuc_start; nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = NULL; nic->cbs_avail = 0; nic->cbs = pci_alloc_consistent(nic->pdev, sizeof(struct cb) * count, &nic->cbs_dma_addr); if(!nic->cbs) return -ENOMEM; for(cb = nic->cbs, i = 0; i < count; cb++, i++) { cb->next = (i + 1 < count) ? cb + 1 : nic->cbs;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -