📄 tc35815.c
字号:
#ifdef DEBUG if (lp->rfd_cur != next_rfd) printk("rfd_cur = %p, next_rfd %p\n", lp->rfd_cur, next_rfd);#endif } /* re-enable BL/FDA Exhaust interrupts. */ if (fd_free_count) { struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; u32 en, en_old = tc_readl(&tr->Int_En); en = en_old | Int_FDAExEn; if (buf_free_count) en |= Int_BLExEn; if (en != en_old) tc_writel(en, &tr->Int_En); }#ifdef TC35815_NAPI return received;#endif}#ifdef TC35815_NAPIstatic int tc35815_poll(struct napi_struct *napi, int budget){ struct tc35815_local *lp = container_of(napi, struct tc35815_local, napi); struct net_device *dev = lp->dev; struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; int received = 0, handled; u32 status; spin_lock(&lp->lock); status = tc_readl(&tr->Int_Src); do { tc_writel(status, &tr->Int_Src); /* write to clear */ handled = tc35815_do_interrupt(dev, status, limit); if (handled >= 0) { received += handled; if (received >= budget) break; } status = tc_readl(&tr->Int_Src); } while (status); spin_unlock(&lp->lock); if (received < budget) { netif_rx_complete(dev, napi); /* enable interrupts */ tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl); } return received;}#endif#ifdef NO_CHECK_CARRIER#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_LateColl|Tx_TxPar|Tx_SQErr)#else#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_NCarr|Tx_LateColl|Tx_TxPar|Tx_SQErr)#endifstatic voidtc35815_check_tx_stat(struct net_device *dev, int status){ struct tc35815_local *lp = dev->priv; const char *msg = NULL; /* count collisions */ if (status & Tx_ExColl) lp->stats.collisions += 16; if (status & Tx_TxColl_MASK) lp->stats.collisions += status & Tx_TxColl_MASK;#ifndef NO_CHECK_CARRIER /* TX4939 does not have NCarr */ if (lp->boardtype == TC35815_TX4939) status &= ~Tx_NCarr;#ifdef WORKAROUND_LOSTCAR /* WORKAROUND: ignore LostCrS in full duplex operation */ if ((lp->timer_state != asleep && lp->timer_state != lcheck) || lp->fullduplex) status &= ~Tx_NCarr;#endif#endif if (!(status & TX_STA_ERR)) { /* no error. */ lp->stats.tx_packets++; return; } lp->stats.tx_errors++; if (status & Tx_ExColl) { lp->stats.tx_aborted_errors++; msg = "Excessive Collision."; } if (status & Tx_Under) { lp->stats.tx_fifo_errors++; msg = "Tx FIFO Underrun."; if (lp->lstats.tx_underrun < TX_THRESHOLD_KEEP_LIMIT) { lp->lstats.tx_underrun++; if (lp->lstats.tx_underrun >= TX_THRESHOLD_KEEP_LIMIT) { struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; tc_writel(TX_THRESHOLD_MAX, &tr->TxThrsh); msg = "Tx FIFO Underrun.Change Tx threshold to max."; } } } if (status & Tx_Defer) { lp->stats.tx_fifo_errors++; msg = "Excessive Deferral."; }#ifndef NO_CHECK_CARRIER if (status & Tx_NCarr) { lp->stats.tx_carrier_errors++; msg = "Lost Carrier Sense."; }#endif if (status & Tx_LateColl) { lp->stats.tx_aborted_errors++; msg = "Late Collision."; } if (status & Tx_TxPar) { lp->stats.tx_fifo_errors++; msg = "Transmit Parity Error."; } if (status & Tx_SQErr) { lp->stats.tx_heartbeat_errors++; msg = "Signal Quality Error."; } if (msg && netif_msg_tx_err(lp)) printk(KERN_WARNING "%s: %s (%#x)\n", dev->name, msg, status);}/* This handles TX complete events posted by the device * via interrupts. */static voidtc35815_txdone(struct net_device *dev){ struct tc35815_local *lp = dev->priv; struct TxFD *txfd; unsigned int fdctl; txfd = &lp->tfd_base[lp->tfd_end]; while (lp->tfd_start != lp->tfd_end && !((fdctl = le32_to_cpu(txfd->fd.FDCtl)) & FD_CownsFD)) { int status = le32_to_cpu(txfd->fd.FDStat); struct sk_buff *skb; unsigned long fdnext = le32_to_cpu(txfd->fd.FDNext); u32 fdsystem = le32_to_cpu(txfd->fd.FDSystem); if (netif_msg_tx_done(lp)) { printk("%s: complete TxFD.\n", dev->name); dump_txfd(txfd); } tc35815_check_tx_stat(dev, status); skb = fdsystem != 0xffffffff ? lp->tx_skbs[fdsystem].skb : NULL;#ifdef DEBUG if (lp->tx_skbs[lp->tfd_end].skb != skb) { printk("%s: tx_skbs mismatch.\n", dev->name); panic_queues(dev); }#else BUG_ON(lp->tx_skbs[lp->tfd_end].skb != skb);#endif if (skb) { lp->stats.tx_bytes += skb->len; pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE); lp->tx_skbs[lp->tfd_end].skb = NULL; lp->tx_skbs[lp->tfd_end].skb_dma = 0;#ifdef TC35815_NAPI dev_kfree_skb_any(skb);#else dev_kfree_skb_irq(skb);#endif } txfd->fd.FDSystem = cpu_to_le32(0xffffffff); lp->tfd_end = (lp->tfd_end + 1) % TX_FD_NUM; txfd = &lp->tfd_base[lp->tfd_end];#ifdef DEBUG if ((fdnext & ~FD_Next_EOL) != fd_virt_to_bus(lp, txfd)) { printk("%s: TxFD FDNext invalid.\n", dev->name); panic_queues(dev); }#endif if (fdnext & FD_Next_EOL) { /* DMA Transmitter has been stopping... */ if (lp->tfd_end != lp->tfd_start) { struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; int head = (lp->tfd_start + TX_FD_NUM - 1) % TX_FD_NUM; struct TxFD* txhead = &lp->tfd_base[head]; int qlen = (lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM;#ifdef DEBUG if (!(le32_to_cpu(txfd->fd.FDCtl) & FD_CownsFD)) { printk("%s: TxFD FDCtl invalid.\n", dev->name); panic_queues(dev); }#endif /* log max queue length */ if (lp->lstats.max_tx_qlen < qlen) lp->lstats.max_tx_qlen = qlen; /* start DMA Transmitter again */ txhead->fd.FDNext |= cpu_to_le32(FD_Next_EOL);#ifdef GATHER_TXINT txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx);#endif if (netif_msg_tx_queued(lp)) { printk("%s: start TxFD on queue.\n", dev->name); dump_txfd(txfd); } tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); } break; } } /* If we had stopped the queue due to a "tx full" * condition, and space has now been made available, * wake up the queue. */ if (netif_queue_stopped(dev) && ! tc35815_tx_full(dev)) netif_wake_queue(dev);}/* The inverse routine to tc35815_open(). */static inttc35815_close(struct net_device *dev){ struct tc35815_local *lp = dev->priv; netif_stop_queue(dev);#ifdef TC35815_NAPI napi_disable(&lp->napi);#endif /* Flush the Tx and disable Rx here. */ del_timer(&lp->timer); /* Kill if running */ tc35815_chip_reset(dev); free_irq(dev->irq, dev); tc35815_free_queues(dev); return 0;}/* * Get the current statistics. * This may be called with the card open or closed. */static struct net_device_stats *tc35815_get_stats(struct net_device *dev){ struct tc35815_local *lp = dev->priv; struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; if (netif_running(dev)) { /* Update the statistics from the device registers. */ lp->stats.rx_missed_errors = tc_readl(&tr->Miss_Cnt); } return &lp->stats;}static void tc35815_set_cam_entry(struct net_device *dev, int index, unsigned char *addr){ struct tc35815_local *lp = dev->priv; struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; int cam_index = index * 6; u32 cam_data; u32 saved_addr; saved_addr = tc_readl(&tr->CAM_Adr); if (netif_msg_hw(lp)) { int i; printk(KERN_DEBUG "%s: CAM %d:", dev->name, index); for (i = 0; i < 6; i++) printk(" %02x", addr[i]); printk("\n"); } if (index & 1) { /* read modify write */ tc_writel(cam_index - 2, &tr->CAM_Adr); cam_data = tc_readl(&tr->CAM_Data) & 0xffff0000; cam_data |= addr[0] << 8 | addr[1]; tc_writel(cam_data, &tr->CAM_Data); /* write whole word */ tc_writel(cam_index + 2, &tr->CAM_Adr); cam_data = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; tc_writel(cam_data, &tr->CAM_Data); } else { /* write whole word */ tc_writel(cam_index, &tr->CAM_Adr); cam_data = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; tc_writel(cam_data, &tr->CAM_Data); /* read modify write */ tc_writel(cam_index + 4, &tr->CAM_Adr); cam_data = tc_readl(&tr->CAM_Data) & 0x0000ffff; cam_data |= addr[4] << 24 | (addr[5] << 16); tc_writel(cam_data, &tr->CAM_Data); } tc_writel(saved_addr, &tr->CAM_Adr);}/* * Set or clear the multicast filter for this adaptor. * num_addrs == -1 Promiscuous mode, receive all packets * num_addrs == 0 Normal mode, clear multicast list * num_addrs > 0 Multicast mode, receive normal and MC packets, * and do best-effort filtering. */static voidtc35815_set_multicast_list(struct net_device *dev){ struct tc35815_regs __iomem *tr = (struct tc35815_regs __iomem *)dev->base_addr; if (dev->flags&IFF_PROMISC) {#ifdef WORKAROUND_100HALF_PROMISC /* With some (all?) 100MHalf HUB, controller will hang * if we enabled promiscuous mode before linkup... */ struct tc35815_local *lp = dev->priv; int pid = lp->phy_addr; if (!(tc_mdio_read(dev, pid, MII_BMSR) & BMSR_LSTATUS)) return;#endif /* Enable promiscuous mode */ tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl); } else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > CAM_ENTRY_MAX - 3) { /* CAM 0, 1, 20 are reserved. */ /* Disable promiscuous mode, use normal mode. */ tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc, &tr->CAM_Ctl); } else if(dev->mc_count) { struct dev_mc_list* cur_addr = dev->mc_list; int i; int ena_bits = CAM_Ena_Bit(CAM_ENTRY_SOURCE); tc_writel(0, &tr->CAM_Ctl); /* Walk the address list, and load the filter */ for (i = 0; i < dev->mc_count; i++, cur_addr = cur_addr->next) { if (!cur_addr) break; /* entry 0,1 is reserved. */ tc35815_set_cam_entry(dev, i + 2, cur_addr->dmi_addr); ena_bits |= CAM_Ena_Bit(i + 2); } tc_writel(ena_bits, &tr->CAM_Ena); tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); } else { tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena); tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); }}static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ struct tc35815_local *lp = dev->priv; strcpy(info->driver, MODNAME); strcpy(info->version, DRV_VERSION); strcpy(info->bus_info, pci_name(lp->pci_dev));}static int tc35815_get_settings(struct net_device *dev, struct ethtool_cmd *cmd){ struct tc35815_local *lp = dev->priv; spin_lock_irq(&lp->lock); mii_ethtool_gset(&lp->mii, cmd); spin_unlock_irq(&lp->lock); return 0;}static int tc35815_set_settings(struct net_device *dev, struct ethtool_cmd *cmd){ struct tc35815_local *lp = dev->priv; int rc;#if 1 /* use our negotiation method... */ /* Verify the settings we care about. */ if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE) return -EINVAL; if (cmd->autoneg == AUTONEG_DISABLE && ((cmd->speed != SPEED_100 && cmd->speed != SPEED_10) || (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL))) return -EINVAL; /* Ok, do it to it. */ spin_lock_irq(&lp->lock); del_timer(&lp->timer); tc35815_start_auto_negotiation(dev, cmd); spin_unlock_irq(&lp->lock); rc = 0;#else spin_lock_irq(&lp->lock); rc = mii_ethtool_sset(&lp->mii, cmd); spin_unlock_irq(&lp->lock);#endif return rc;}static int tc35815_nway_reset(struct net_device *dev){ struct tc35815_local *lp = dev->priv; int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -