📄 ns83820.c
字号:
do_intr = 1; dev->tx_intr_idx = (dev->tx_intr_idx + NR_TX_DESC/4) % NR_TX_DESC; } nr_free -= nr_frags; if (nr_free < MIN_TX_DESC_FREE) { dprintk("stop_queue - last entry(%p)\n", ndev); netif_stop_queue(ndev); stopped = 1; } frag = skb_shinfo(skb)->frags; if (!nr_frags) frag = NULL; extsts = 0; if (skb->ip_summed == CHECKSUM_HW) { extsts |= EXTSTS_IPPKT; if (IPPROTO_TCP == skb->nh.iph->protocol) extsts |= EXTSTS_TCPPKT; else if (IPPROTO_UDP == skb->nh.iph->protocol) extsts |= EXTSTS_UDPPKT; } len = skb->len; if (nr_frags) len -= skb->data_len; buf = pci_map_single(dev->pci_dev, skb->data, len, PCI_DMA_TODEVICE); first_desc = dev->tx_descs + (free_idx * DESC_SIZE); for (;;) { volatile u32 *desc = dev->tx_descs + (free_idx * DESC_SIZE); u32 residue = 0; dprintk("frag[%3u]: %4u @ 0x%08Lx\n", free_idx, len, (unsigned long long)buf); last_idx = free_idx; free_idx = (free_idx + 1) % NR_TX_DESC; desc[DESC_LINK] = cpu_to_le32(dev->tx_phy_descs + (free_idx * DESC_SIZE * 4)); desc_addr_set(desc + DESC_BUFPTR, buf); desc[DESC_EXTSTS] = cpu_to_le32(extsts); cmdsts = ((nr_frags|residue) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0); cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN; cmdsts |= len; desc[DESC_CMDSTS] = cpu_to_le32(cmdsts); if (residue) { buf += len; len = residue; continue; } if (!nr_frags) break; buf = pci_map_page(dev->pci_dev, frag->page, frag->page_offset, frag->size, PCI_DMA_TODEVICE); dprintk("frag: buf=%08Lx page=%08lx offset=%08lx\n", (long long)buf, (long) page_to_pfn(frag->page), frag->page_offset); len = frag->size; frag++; nr_frags--; } dprintk("done pkt\n"); spin_lock_irq(&dev->tx_lock); dev->tx_skbs[last_idx] = skb; first_desc[DESC_CMDSTS] |= cpu_to_le32(CMDSTS_OWN); dev->tx_free_idx = free_idx; atomic_inc(&dev->nr_tx_skbs); spin_unlock_irq(&dev->tx_lock); kick_tx(dev); /* Check again: we may have raced with a tx done irq */ if (stopped && (dev->tx_done_idx != tx_done_idx) && start_tx_okay(dev)) netif_start_queue(ndev); /* set the transmit start time to catch transmit timeouts */ ndev->trans_start = jiffies; return 0;}static void ns83820_update_stats(struct ns83820 *dev){ u8 *base = dev->base; /* the DP83820 will freeze counters, so we need to read all of them */ dev->stats.rx_errors += readl(base + 0x60) & 0xffff; dev->stats.rx_crc_errors += readl(base + 0x64) & 0xffff; dev->stats.rx_missed_errors += readl(base + 0x68) & 0xffff; dev->stats.rx_frame_errors += readl(base + 0x6c) & 0xffff; /*dev->stats.rx_symbol_errors +=*/ readl(base + 0x70); dev->stats.rx_length_errors += readl(base + 0x74) & 0xffff; dev->stats.rx_length_errors += readl(base + 0x78) & 0xffff; /*dev->stats.rx_badopcode_errors += */ readl(base + 0x7c); /*dev->stats.rx_pause_count += */ readl(base + 0x80); /*dev->stats.tx_pause_count += */ readl(base + 0x84); dev->stats.tx_carrier_errors += readl(base + 0x88) & 0xff;}static struct net_device_stats *ns83820_get_stats(struct net_device *ndev){ struct ns83820 *dev = PRIV(ndev); /* somewhat overkill */ spin_lock_irq(&dev->misc_lock); ns83820_update_stats(dev); spin_unlock_irq(&dev->misc_lock); return &dev->stats;}static int ns83820_ethtool_ioctl (struct ns83820 *dev, void __user *useraddr){ u32 ethcmd; if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd))) return -EFAULT; switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy(info.driver, "ns83820"); strcpy(info.version, VERSION); strcpy(info.bus_info, pci_name(dev->pci_dev)); if (copy_to_user(useraddr, &info, sizeof (info))) return -EFAULT; return 0; } /* get link status */ case ETHTOOL_GLINK: { struct ethtool_value edata = { ETHTOOL_GLINK }; u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; if (cfg & CFG_LNKSTS) edata.data = 1; else edata.data = 0; if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; } default: break; } return -EOPNOTSUPP;}static int ns83820_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd){ struct ns83820 *dev = PRIV(ndev); switch(cmd) { case SIOCETHTOOL: return ns83820_ethtool_ioctl(dev, rq->ifr_data); default: return -EOPNOTSUPP; }}static void ns83820_mib_isr(struct ns83820 *dev){ spin_lock(&dev->misc_lock); ns83820_update_stats(dev); spin_unlock(&dev->misc_lock);}static void ns83820_do_isr(struct net_device *ndev, u32 isr);static irqreturn_t ns83820_irq(int foo, void *data, struct pt_regs *regs){ struct net_device *ndev = data; struct ns83820 *dev = PRIV(ndev); u32 isr; dprintk("ns83820_irq(%p)\n", ndev); dev->ihr = 0; isr = readl(dev->base + ISR); dprintk("irq: %08x\n", isr); ns83820_do_isr(ndev, isr); return IRQ_HANDLED;}static void ns83820_do_isr(struct net_device *ndev, u32 isr){ struct ns83820 *dev = PRIV(ndev);#ifdef DEBUG if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC)) Dprintk("odd isr? 0x%08x\n", isr);#endif if (ISR_RXIDLE & isr) { dev->rx_info.idle = 1; Dprintk("oh dear, we are idle\n"); ns83820_rx_kick(ndev); } if ((ISR_RXDESC | ISR_RXOK) & isr) { prefetch(dev->rx_info.next_rx_desc); spin_lock_irq(&dev->misc_lock); dev->IMR_cache &= ~(ISR_RXDESC | ISR_RXOK); writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irq(&dev->misc_lock); tasklet_schedule(&dev->rx_tasklet); //rx_irq(ndev); //writel(4, dev->base + IHR); } if ((ISR_RXIDLE | ISR_RXORN | ISR_RXDESC | ISR_RXOK | ISR_RXERR) & isr) ns83820_rx_kick(ndev); if (unlikely(ISR_RXSOVR & isr)) { //printk("overrun: rxsovr\n"); dev->stats.rx_fifo_errors ++; } if (unlikely(ISR_RXORN & isr)) { //printk("overrun: rxorn\n"); dev->stats.rx_fifo_errors ++; } if ((ISR_RXRCMP & isr) && dev->rx_info.up) writel(CR_RXE, dev->base + CR); if (ISR_TXIDLE & isr) { u32 txdp; txdp = readl(dev->base + TXDP); dprintk("txdp: %08x\n", txdp); txdp -= dev->tx_phy_descs; dev->tx_idx = txdp / (DESC_SIZE * 4); if (dev->tx_idx >= NR_TX_DESC) { printk(KERN_ALERT "%s: BUG -- txdp out of range\n", ndev->name); dev->tx_idx = 0; } /* The may have been a race between a pci originated read * and the descriptor update from the cpu. Just in case, * kick the transmitter if the hardware thinks it is on a * different descriptor than we are. */ if (dev->tx_idx != dev->tx_free_idx) kick_tx(dev); } /* Defer tx ring processing until more than a minimum amount of * work has accumulated */ if ((ISR_TXDESC | ISR_TXIDLE | ISR_TXOK | ISR_TXERR) & isr) { do_tx_done(ndev); /* Disable TxOk if there are no outstanding tx packets. */ if ((dev->tx_done_idx == dev->tx_free_idx) && (dev->IMR_cache & ISR_TXOK)) { spin_lock_irq(&dev->misc_lock); dev->IMR_cache &= ~ISR_TXOK; writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irq(&dev->misc_lock); } } /* The TxIdle interrupt can come in before the transmit has * completed. Normally we reap packets off of the combination * of TxDesc and TxIdle and leave TxOk disabled (since it * occurs on every packet), but when no further irqs of this * nature are expected, we must enable TxOk. */ if ((ISR_TXIDLE & isr) && (dev->tx_done_idx != dev->tx_free_idx)) { spin_lock_irq(&dev->misc_lock); dev->IMR_cache |= ISR_TXOK; writel(dev->IMR_cache, dev->base + IMR); spin_unlock_irq(&dev->misc_lock); } /* MIB interrupt: one of the statistics counters is about to overflow */ if (unlikely(ISR_MIB & isr)) ns83820_mib_isr(dev); /* PHY: Link up/down/negotiation state change */ if (unlikely(ISR_PHY & isr)) phy_intr(ndev);#if 0 /* Still working on the interrupt mitigation strategy */ if (dev->ihr) writel(dev->ihr, dev->base + IHR);#endif}static void ns83820_do_reset(struct ns83820 *dev, u32 which){ Dprintk("resetting chip...\n"); writel(which, dev->base + CR); do { schedule(); } while (readl(dev->base + CR) & which); Dprintk("okay!\n");}static int ns83820_stop(struct net_device *ndev){ struct ns83820 *dev = PRIV(ndev); /* FIXME: protect against interrupt handler? */ del_timer_sync(&dev->tx_watchdog); /* disable interrupts */ writel(0, dev->base + IMR); writel(0, dev->base + IER); readl(dev->base + IER); dev->rx_info.up = 0; synchronize_irq(dev->pci_dev->irq); ns83820_do_reset(dev, CR_RST); synchronize_irq(dev->pci_dev->irq); spin_lock_irq(&dev->misc_lock); dev->IMR_cache &= ~(ISR_TXURN | ISR_TXIDLE | ISR_TXERR | ISR_TXDESC | ISR_TXOK); spin_unlock_irq(&dev->misc_lock); ns83820_cleanup_rx(dev); ns83820_cleanup_tx(dev); return 0;}static void ns83820_tx_timeout(struct net_device *ndev){ struct ns83820 *dev = PRIV(ndev); u32 tx_done_idx, *desc; unsigned long flags; local_irq_save(flags); tx_done_idx = dev->tx_done_idx; desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); printk(KERN_INFO "%s: tx_timeout: tx_done_idx=%d free_idx=%d cmdsts=%08x\n", ndev->name, tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS]));#if defined(DEBUG) { u32 isr; isr = readl(dev->base + ISR); printk("irq: %08x imr: %08x\n", isr, dev->IMR_cache); ns83820_do_isr(ndev, isr); }#endif do_tx_done(ndev); tx_done_idx = dev->tx_done_idx; desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); printk(KERN_INFO "%s: after: tx_done_idx=%d free_idx=%d cmdsts=%08x\n", ndev->name, tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); local_irq_restore(flags);}static void ns83820_tx_watch(unsigned long data){ struct net_device *ndev = (void *)data; struct ns83820 *dev = PRIV(ndev);#if defined(DEBUG) printk("ns83820_tx_watch: %u %u %d\n", dev->tx_done_idx, dev->tx_free_idx, atomic_read(&dev->nr_tx_skbs) );#endif if (time_after(jiffies, ndev->trans_start + 1*HZ) && dev->tx_done_idx != dev->tx_free_idx) { printk(KERN_DEBUG "%s: ns83820_tx_watch: %u %u %d\n", ndev->name, dev->tx_done_idx, dev->tx_free_idx, atomic_read(&dev->nr_tx_skbs)); ns83820_tx_timeout(ndev); } mod_timer(&dev->tx_watchdog, jiffies + 2*HZ);}static int ns83820_open(struct net_device *ndev){ struct ns83820 *dev = PRIV(ndev); unsigned i; u32 desc; int ret; dprintk("ns83820_open\n"); writel(0, dev->base + PQCR); ret = ns83820_setup_rx(ndev); if (ret) goto failed; memset(dev->tx_descs, 0, 4 * NR_TX_DESC * DESC_SIZE); for (i=0; i<NR_TX_DESC; i++) { dev->tx_descs[(i * DESC_SIZE) + DESC_LINK] = cpu_to_le32( dev->tx_phy_descs + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4); } dev->tx_idx = 0; dev->tx_done_idx = 0; desc = dev->tx_phy_descs; writel(0, dev->base + TXDP_HI); writel(desc, dev->base + TXDP); init_timer(&dev->tx_watchdog); dev->tx_watchdog.data = (unsigned long)ndev; dev->tx_watchdog.function = ns83820_tx_watch; mod_timer(&dev->tx_watchdog, jiffies + 2*HZ); netif_start_queue(ndev); /* FIXME: wait for phy to come up */ return 0;failed: ns83820_stop(ndev); return ret;}static void ns83820_getmac(struct ns83820 *dev, u8 *mac){ unsigned i; for (i=0; i<3; i++) { u32 data;#if 0 /* I've left this in as an example of how to use eeprom.h */ data = eeprom_readw(&dev->ee, 0xa + 2 - i);#else /* Read from the perfect match memory: this is loaded by * the chip from the EEPROM via the EELOAD self test. */ writel(i*2, dev->base + RFCR); data = readl(dev->base + RFDR);#endif *mac++ = data; *mac++ = data >> 8; }}static int ns83820_change_mtu(struct net_device *ndev, int new_mtu){ if (new_mtu > RX_BUF_SIZE) return -EINVAL; ndev->mtu = new_mtu; return 0;}static void ns83820_set_multicast(struct net_device *ndev){ struct ns83820 *dev = PRIV(ndev); u8 *rfcr = dev->base + RFCR; u32 and_mask = 0xffffffff; u32 or_mask = 0; u32 val; if (ndev->flags & IFF_PROMISC) or_mask |= RFCR_AAU | RFCR_AAM; else and_mask &= ~(RFCR_AAU | RFCR_AAM); if (ndev->flags & IFF_ALLMULTI) or_mask |= RFCR_AAM; else and_mask &= ~RFCR_AAM; spin_lock_irq(&dev->misc_lock); val = (readl(rfcr) & and_mask) | or_mask; /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */ writel(val & ~RFCR_RFEN, rfcr); writel(val, rfcr); spin_unlock_irq(&dev->misc_lock);}static void ns83820_run_bist(struct net_device *ndev, const char *name, u32 enable, u32 done, u32 fail){ struct ns83820 *dev = PRIV(ndev); int timed_out = 0; long start; u32 status; int loops = 0; dprintk("%s: start %s\n", ndev->name, name); start = jiffies; writel(enable, dev->base + PTSCR); for (;;) { loops++; status = readl(dev->base + PTSCR); if (!(status & enable)) break; if (status & done) break; if (status & fail) break; if ((jiffies - start) >= HZ) { timed_out = 1; break; } set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } if (status & fail) printk(KERN_INFO "%s: %s failed! (0x%08x & 0x%08x)\n", ndev->name, name, status, fail); else if (timed_out) printk(KERN_INFO "%s: run_bist %s timed out! (%08x)\n", ndev->name, name, status); dprintk("%s: done %s in %d loops\n", ndev->name, name, loops);}#ifdef PHY_CODE_IS_FINISHEDstatic void ns83820_mii_write_bit(struct ns83820 *dev, int bit){ /* drive MDC low */ dev->MEAR_cache &= ~MEAR_MDC; writel(dev->MEAR_cache, dev->base + MEAR); readl(dev->base + MEAR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -