📄 via-velocity.c
字号:
{ /* * Map the linear network buffer into PCI space and * add it to the transmit ring. */ tdinfo->skb = skb; tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE); td_ptr->tdesc0.pktsize = pktlen; td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); td_ptr->td_buf[0].pa_high = 0; td_ptr->td_buf[0].bufsize = td_ptr->tdesc0.pktsize; tdinfo->nskb_dma = 1; td_ptr->tdesc1.CMDZ = 2; } if (vptr->flags & VELOCITY_FLAGS_TAGGING) { td_ptr->tdesc1.pqinf.VID = (vptr->options.vid & 0xfff); td_ptr->tdesc1.pqinf.priority = 0; td_ptr->tdesc1.pqinf.CFI = 0; td_ptr->tdesc1.TCR |= TCR0_VETAG; } /* * Handle hardware checksum */ if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM) && (skb->ip_summed == CHECKSUM_HW)) { struct iphdr *ip = skb->nh.iph; if (ip->protocol == IPPROTO_TCP) td_ptr->tdesc1.TCR |= TCR0_TCPCK; else if (ip->protocol == IPPROTO_UDP) td_ptr->tdesc1.TCR |= (TCR0_UDPCK); td_ptr->tdesc1.TCR |= TCR0_IPCK; } { int prev = index - 1; if (prev < 0) prev = vptr->options.numtx - 1; td_ptr->tdesc0.owner = OWNED_BY_NIC; vptr->td_used[qnum]++; vptr->td_curr[qnum] = (index + 1) % vptr->options.numtx; if (AVAIL_TD(vptr, qnum) < 1) netif_stop_queue(dev); td_ptr = &(vptr->td_rings[qnum][prev]); td_ptr->td_buf[0].queue = 1; mac_tx_queue_wake(vptr->mac_regs, qnum); } dev->trans_start = jiffies; spin_unlock_irqrestore(&vptr->lock, flags); return 0;}/** * velocity_intr - interrupt callback * @irq: interrupt number * @dev_instance: interrupting device * @pt_regs: CPU register state at interrupt * * Called whenever an interrupt is generated by the velocity * adapter IRQ line. We may not be the source of the interrupt * and need to identify initially if we are, and if not exit as * efficiently as possible. */ static int velocity_intr(int irq, void *dev_instance, struct pt_regs *regs){ struct net_device *dev = dev_instance; struct velocity_info *vptr = dev->priv; u32 isr_status; int max_count = 0; spin_lock(&vptr->lock); isr_status = mac_read_isr(vptr->mac_regs); /* Not us ? */ if (isr_status == 0) { spin_unlock(&vptr->lock); return IRQ_NONE; } mac_disable_int(vptr->mac_regs); /* * Keep processing the ISR until we have completed * processing and the isr_status becomes zero */ while (isr_status != 0) { mac_write_isr(vptr->mac_regs, isr_status); if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI))) velocity_error(vptr, isr_status); if (isr_status & (ISR_PRXI | ISR_PPRXI)) max_count += velocity_rx_srv(vptr, isr_status); if (isr_status & (ISR_PTXI | ISR_PPTXI)) max_count += velocity_tx_srv(vptr, isr_status); isr_status = mac_read_isr(vptr->mac_regs); if (max_count > vptr->options.int_works) { printk(KERN_WARNING "%s: excessive work at interrupt.\n", dev->name); max_count = 0; } } spin_unlock(&vptr->lock); mac_enable_int(vptr->mac_regs); return IRQ_HANDLED;}/** * velocity_set_multi - filter list change callback * @dev: network device * * Called by the network layer when the filter lists need to change * for a velocity adapter. Reload the CAMs with the new address * filter ruleset. */ static void velocity_set_multi(struct net_device *dev){ struct velocity_info *vptr = dev->priv; struct mac_regs * regs = vptr->mac_regs; u8 rx_mode; int i; struct dev_mc_list *mclist; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ /* Unconditionally log net taps. */ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); writel(0xffffffff, ®s->MARCAM[0]); writel(0xffffffff, ®s->MARCAM[4]); rx_mode = (RCR_AM | RCR_AB | RCR_PROM); } else if ((dev->mc_count > vptr->multicast_limit) || (dev->flags & IFF_ALLMULTI)) { writel(0xffffffff, ®s->MARCAM[0]); writel(0xffffffff, ®s->MARCAM[4]); rx_mode = (RCR_AM | RCR_AB); } else { int offset = MCAM_SIZE - vptr->multicast_limit; mac_get_cam_mask(regs, vptr->mCAMmask, VELOCITY_MULTICAST_CAM); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { mac_set_cam(regs, i + offset, mclist->dmi_addr, VELOCITY_MULTICAST_CAM); vptr->mCAMmask[(offset + i) / 8] |= 1 << ((offset + i) & 7); } mac_set_cam_mask(regs, vptr->mCAMmask, VELOCITY_MULTICAST_CAM); rx_mode = (RCR_AM | RCR_AB); } if (dev->mtu > 1500) rx_mode |= RCR_AL; BYTE_REG_BITS_ON(rx_mode, ®s->RCR);}/** * velocity_get_status - statistics callback * @dev: network device * * Callback from the network layer to allow driver statistics * to be resynchronized with hardware collected state. In the * case of the velocity we need to pull the MIB counters from * the hardware into the counters before letting the network * layer display them. */ static struct net_device_stats *velocity_get_stats(struct net_device *dev){ struct velocity_info *vptr = dev->priv; /* If the hardware is down, don't touch MII */ if(!netif_running(dev)) return &vptr->stats; spin_lock_irq(&vptr->lock); velocity_update_hw_mibs(vptr); spin_unlock_irq(&vptr->lock); vptr->stats.rx_packets = vptr->mib_counter[HW_MIB_ifRxAllPkts]; vptr->stats.rx_errors = vptr->mib_counter[HW_MIB_ifRxErrorPkts]; vptr->stats.rx_length_errors = vptr->mib_counter[HW_MIB_ifInRangeLengthErrors];// unsigned long rx_dropped; /* no space in linux buffers */ vptr->stats.collisions = vptr->mib_counter[HW_MIB_ifTxEtherCollisions]; /* detailed rx_errors: */// unsigned long rx_length_errors;// unsigned long rx_over_errors; /* receiver ring buff overflow */ vptr->stats.rx_crc_errors = vptr->mib_counter[HW_MIB_ifRxPktCRCE];// unsigned long rx_frame_errors; /* recv'd frame alignment error */// unsigned long rx_fifo_errors; /* recv'r fifo overrun */// unsigned long rx_missed_errors; /* receiver missed packet */ /* detailed tx_errors */// unsigned long tx_fifo_errors; return &vptr->stats;}/** * velocity_ioctl - ioctl entry point * @dev: network device * @rq: interface request ioctl * @cmd: command code * * Called when the user issues an ioctl request to the network * device in question. The velocity interface supports MII. */ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct velocity_info *vptr = dev->priv; int ret; /* If we are asked for information and the device is power saving then we need to bring the device back up to talk to it */ if(!netif_running(dev)) pci_set_power_state(vptr->pdev, 0); switch (cmd) { case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCGMIIREG: /* Read MII PHY register. */ case SIOCSMIIREG: /* Write to MII PHY register. */ ret = velocity_mii_ioctl(dev, rq, cmd); break; default: ret = -EOPNOTSUPP; } if(!netif_running(dev)) pci_set_power_state(vptr->pdev, 3); return ret;}/* * Definition for our device driver. The PCI layer interface * uses this to handle all our card discover and plugging */ static struct pci_driver velocity_driver = { .name = VELOCITY_NAME, .id_table = velocity_id_table, .probe = velocity_found1, .remove = __devexit_p(velocity_remove1),#ifdef CONFIG_PM .suspend = velocity_suspend, .resume = velocity_resume,#endif};/** * velocity_init_module - load time function * * Called when the velocity module is loaded. The PCI driver * is registered with the PCI layer, and in turn will call * the probe functions for each velocity adapter installed * in the system. */ static int __init velocity_init_module(void){ int ret; velocity_register_notifier(); ret = pci_module_init(&velocity_driver); if (ret < 0) velocity_unregister_notifier(); return ret;}/** * velocity_cleanup - module unload * * When the velocity hardware is unloaded this function is called. * It will clean up the notifiers and the unregister the PCI * driver interface for this hardware. This in turn cleans up * all discovered interfaces before returning from the function */ static void __exit velocity_cleanup_module(void){ velocity_unregister_notifier(); pci_unregister_driver(&velocity_driver);}module_init(velocity_init_module);module_exit(velocity_cleanup_module);/* * MII access , media link mode setting functions */ /** * mii_init - set up MII * @vptr: velocity adapter * @mii_status: links tatus * * Set up the PHY for the current link state. */ static void mii_init(struct velocity_info *vptr, u32 mii_status){ u16 BMCR; switch (PHYID_GET_PHY_ID(vptr->phy_id)) { case PHYID_CICADA_CS8201: /* * Reset to hardware default */ MII_REG_BITS_OFF((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, vptr->mac_regs); /* * Turn on ECHODIS bit in NWay-forced full mode and turn it * off it in NWay-forced half mode for NWay-forced v.s. * legacy-forced issue. */ if (vptr->mii_status & VELOCITY_DUPLEX_FULL) MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); else MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); /* * Turn on Link/Activity LED enable bit for CIS8201 */ MII_REG_BITS_ON(PLED_LALBE, MII_REG_PLED, vptr->mac_regs); break; case PHYID_VT3216_32BIT: case PHYID_VT3216_64BIT: /* * Reset to hardware default */ MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, vptr->mac_regs); /* * Turn on ECHODIS bit in NWay-forced full mode and turn it * off it in NWay-forced half mode for NWay-forced v.s. * legacy-forced issue */ if (vptr->mii_status & VELOCITY_DUPLEX_FULL) MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); else MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, vptr->mac_regs); break; case PHYID_MARVELL_1000: case PHYID_MARVELL_1000S: /* * Assert CRS on Transmit */ MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs); /* * Reset to hardware default */ MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, vptr->mac_regs); break; default: ; } velocity_mii_read(vptr->mac_regs, MII_REG_BMCR, &BMCR); if (BMCR & BMCR_ISO) { BMCR &= ~BMCR_ISO; velocity_mii_write(vptr->mac_regs, MII_REG_BMCR, BMCR); }}/** * safe_disable_mii_autopoll - autopoll off * @regs: velocity registers * * Turn off the autopoll and wait for it to disable on the chip */ static void safe_disable_mii_autopoll(struct mac_regs * regs){ u16 ww; /* turn off MAUTO */ writeb(0, ®s->MIICR); for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { udelay(1); if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) break; }}/** * enable_mii_autopoll - turn on autopolling * @regs: velocity registers * * Enable the MII link status autopoll feature on the Velocity * hardware. Wait for it to enable. */static void enable_mii_autopoll(struct mac_regs * regs){ int ii; writeb(0, &(regs->MIICR)); writeb(MIIADR_SWMPL, ®s->MIIADR); for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { udelay(1); if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) break; } writeb(MIICR_MAUTO, ®s->MIICR); for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { udelay(1); if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) break; }}/** * velocity_mii_read - read MII data * @regs: velocity registers * @index: MII register index * @data: buffer for received data * * Perform a single read of an MII 16bit register. Returns zero * on success or -ETIMEDOUT if the PHY did not respond. */ static int velocity_mii_read(struct mac_regs * regs, u8 index, u16 *data){ u16 ww; /* * Disable MIICR_MAUTO, so that mii addr can be set normally */ safe_disable_mii_autopoll(regs); writeb(index, ®s->MIIADR); BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { if (!(readb(®s->MIICR) & MIICR_RCMD)) break; } *data = readw(®s->MIIDATA); enable_mii_autopoll(regs); if (ww == W_MAX_TIMEOUT) return -ETIMEDOUT; return 0;}/** * velocity_mii_write - write MII data * @regs: velocity registers * @index: MII register index * @data: 16bit data for the MII register * * Perform a single write to an MII 16bit register. Returns zero * on success or -ETIMEDOUT if the PHY did not respond. */ static int velocity_mii_write(struct mac_regs * regs, u8 mii_addr, u16 data){ u16 ww; /* * Disable MIICR_MAUTO, so that mii addr can be set normally */ safe_disable_mii_autopoll(regs); /* MII reg offset */ writeb(mii_addr, ®s->MIIADR); /* set MII data */ writew(data, ®s->MIIDATA); /* turn on MIICR_WCMD */ BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); /* W_MAX_TIMEOUT is the timeout period */ for (w
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -