📄 gianfar.c
字号:
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring * until the budget/quota has been reached. Returns the number * of frames handled */int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit){ struct rxbd8 *bdp; struct sk_buff *skb; u16 pkt_len; int howmany = 0; struct gfar_private *priv = netdev_priv(dev); /* Get the first full descriptor */ bdp = priv->cur_rx; while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { rmb(); skb = priv->rx_skbuff[priv->skb_currx]; if (!(bdp->status & (RXBD_LARGE | RXBD_SHORT | RXBD_NONOCTET | RXBD_CRCERR | RXBD_OVERRUN | RXBD_TRUNCATED))) { /* Increment the number of packets */ dev->stats.rx_packets++; howmany++; /* Remove the FCS from the packet length */ pkt_len = bdp->length - 4; gfar_process_frame(dev, skb, pkt_len); dev->stats.rx_bytes += pkt_len; } else { count_errors(bdp->status, dev); if (skb) dev_kfree_skb_any(skb); priv->rx_skbuff[priv->skb_currx] = NULL; } dev->last_rx = jiffies; /* Clear the status flags for this buffer */ bdp->status &= ~RXBD_STATS; /* Add another skb for the future */ skb = gfar_new_skb(dev, bdp); priv->rx_skbuff[priv->skb_currx] = skb; /* Update to the next pointer */ if (bdp->status & RXBD_WRAP) bdp = priv->rx_bd_base; else bdp++; /* update to point at the next skb */ priv->skb_currx = (priv->skb_currx + 1) & RX_RING_MOD_MASK(priv->rx_ring_size); } /* Update the current rxbd pointer to be the next one */ priv->cur_rx = bdp; return howmany;}#ifdef CONFIG_GFAR_NAPIstatic int gfar_poll(struct napi_struct *napi, int budget){ struct gfar_private *priv = container_of(napi, struct gfar_private, napi); struct net_device *dev = priv->dev; int howmany; howmany = gfar_clean_rx_ring(dev, budget); if (howmany < budget) { netif_rx_complete(dev, napi); /* Clear the halt bit in RSTAT */ gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT); gfar_write(&priv->regs->imask, IMASK_DEFAULT); /* If we are coalescing interrupts, update the timer */ /* Otherwise, clear it */ if (priv->rxcoalescing) gfar_write(&priv->regs->rxic, mk_ic_value(priv->rxcount, priv->rxtime)); else gfar_write(&priv->regs->rxic, 0); } return howmany;}#endif#ifdef CONFIG_NET_POLL_CONTROLLER/* * Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing. */static void gfar_netpoll(struct net_device *dev){ struct gfar_private *priv = netdev_priv(dev); /* If the device has multiple interrupts, run tx/rx */ if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { disable_irq(priv->interruptTransmit); disable_irq(priv->interruptReceive); disable_irq(priv->interruptError); gfar_interrupt(priv->interruptTransmit, dev); enable_irq(priv->interruptError); enable_irq(priv->interruptReceive); enable_irq(priv->interruptTransmit); } else { disable_irq(priv->interruptTransmit); gfar_interrupt(priv->interruptTransmit, dev); enable_irq(priv->interruptTransmit); }}#endif/* The interrupt handler for devices with one interrupt */static irqreturn_t gfar_interrupt(int irq, void *dev_id){ struct net_device *dev = dev_id; struct gfar_private *priv = netdev_priv(dev); /* Save ievent for future reference */ u32 events = gfar_read(&priv->regs->ievent); /* Check for reception */ if (events & IEVENT_RX_MASK) gfar_receive(irq, dev_id); /* Check for transmit completion */ if (events & IEVENT_TX_MASK) gfar_transmit(irq, dev_id); /* Check for errors */ if (events & IEVENT_ERR_MASK) gfar_error(irq, dev_id); return IRQ_HANDLED;}/* Called every time the controller might need to be made * aware of new link state. The PHY code conveys this * information through variables in the phydev structure, and this * function converts those variables into the appropriate * register values, and can bring down the device if needed. */static void adjust_link(struct net_device *dev){ struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = priv->regs; unsigned long flags; struct phy_device *phydev = priv->phydev; int new_state = 0; spin_lock_irqsave(&priv->txlock, flags); if (phydev->link) { u32 tempval = gfar_read(®s->maccfg2); u32 ecntrl = gfar_read(®s->ecntrl); /* Now we make sure that we can be in full duplex mode. * If not, we operate in half-duplex mode. */ if (phydev->duplex != priv->oldduplex) { new_state = 1; if (!(phydev->duplex)) tempval &= ~(MACCFG2_FULL_DUPLEX); else tempval |= MACCFG2_FULL_DUPLEX; priv->oldduplex = phydev->duplex; } if (phydev->speed != priv->oldspeed) { new_state = 1; switch (phydev->speed) { case 1000: tempval = ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII); break; case 100: case 10: tempval = ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII); /* Reduced mode distinguishes * between 10 and 100 */ if (phydev->speed == SPEED_100) ecntrl |= ECNTRL_R100; else ecntrl &= ~(ECNTRL_R100); break; default: if (netif_msg_link(priv)) printk(KERN_WARNING "%s: Ack! Speed (%d) is not 10/100/1000!\n", dev->name, phydev->speed); break; } priv->oldspeed = phydev->speed; } gfar_write(®s->maccfg2, tempval); gfar_write(®s->ecntrl, ecntrl); if (!priv->oldlink) { new_state = 1; priv->oldlink = 1; netif_schedule(dev); } } else if (priv->oldlink) { new_state = 1; priv->oldlink = 0; priv->oldspeed = 0; priv->oldduplex = -1; } if (new_state && netif_msg_link(priv)) phy_print_status(phydev); spin_unlock_irqrestore(&priv->txlock, flags);}/* Update the hash table based on the current list of multicast * addresses we subscribe to. Also, change the promiscuity of * the device based on the flags (this function is called * whenever dev->flags is changed */static void gfar_set_multi(struct net_device *dev){ struct dev_mc_list *mc_ptr; struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = priv->regs; u32 tempval; if(dev->flags & IFF_PROMISC) { /* Set RCTRL to PROM */ tempval = gfar_read(®s->rctrl); tempval |= RCTRL_PROM; gfar_write(®s->rctrl, tempval); } else { /* Set RCTRL to not PROM */ tempval = gfar_read(®s->rctrl); tempval &= ~(RCTRL_PROM); gfar_write(®s->rctrl, tempval); } if(dev->flags & IFF_ALLMULTI) { /* Set the hash to rx all multicast frames */ gfar_write(®s->igaddr0, 0xffffffff); gfar_write(®s->igaddr1, 0xffffffff); gfar_write(®s->igaddr2, 0xffffffff); gfar_write(®s->igaddr3, 0xffffffff); gfar_write(®s->igaddr4, 0xffffffff); gfar_write(®s->igaddr5, 0xffffffff); gfar_write(®s->igaddr6, 0xffffffff); gfar_write(®s->igaddr7, 0xffffffff); gfar_write(®s->gaddr0, 0xffffffff); gfar_write(®s->gaddr1, 0xffffffff); gfar_write(®s->gaddr2, 0xffffffff); gfar_write(®s->gaddr3, 0xffffffff); gfar_write(®s->gaddr4, 0xffffffff); gfar_write(®s->gaddr5, 0xffffffff); gfar_write(®s->gaddr6, 0xffffffff); gfar_write(®s->gaddr7, 0xffffffff); } else { int em_num; int idx; /* zero out the hash */ gfar_write(®s->igaddr0, 0x0); gfar_write(®s->igaddr1, 0x0); gfar_write(®s->igaddr2, 0x0); gfar_write(®s->igaddr3, 0x0); gfar_write(®s->igaddr4, 0x0); gfar_write(®s->igaddr5, 0x0); gfar_write(®s->igaddr6, 0x0); gfar_write(®s->igaddr7, 0x0); gfar_write(®s->gaddr0, 0x0); gfar_write(®s->gaddr1, 0x0); gfar_write(®s->gaddr2, 0x0); gfar_write(®s->gaddr3, 0x0); gfar_write(®s->gaddr4, 0x0); gfar_write(®s->gaddr5, 0x0); gfar_write(®s->gaddr6, 0x0); gfar_write(®s->gaddr7, 0x0); /* If we have extended hash tables, we need to * clear the exact match registers to prepare for * setting them */ if (priv->extended_hash) { em_num = GFAR_EM_NUM + 1; gfar_clear_exact_match(dev); idx = 1; } else { idx = 0; em_num = 0; } if(dev->mc_count == 0) return; /* Parse the list, and set the appropriate bits */ for(mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { if (idx < em_num) { gfar_set_mac_for_addr(dev, idx, mc_ptr->dmi_addr); idx++; } else gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr); } } return;}/* Clears each of the exact match registers to zero, so they * don't interfere with normal reception */static void gfar_clear_exact_match(struct net_device *dev){ int idx; u8 zero_arr[MAC_ADDR_LEN] = {0,0,0,0,0,0}; for(idx = 1;idx < GFAR_EM_NUM + 1;idx++) gfar_set_mac_for_addr(dev, idx, (u8 *)zero_arr);}/* Set the appropriate hash bit for the given addr *//* The algorithm works like so: * 1) Take the Destination Address (ie the multicast address), and * do a CRC on it (little endian), and reverse the bits of the * result. * 2) Use the 8 most significant bits as a hash into a 256-entry * table. The table is controlled through 8 32-bit registers: * gaddr0-7. gaddr0's MSB is entry 0, and gaddr7's LSB is * gaddr7. This means that the 3 most significant bits in the * hash index which gaddr register to use, and the 5 other bits * indicate which bit (assuming an IBM numbering scheme, which * for PowerPC (tm) is usually the case) in the register holds * the entry. */static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr){ u32 tempval; struct gfar_private *priv = netdev_priv(dev); u32 result = ether_crc(MAC_ADDR_LEN, addr); int width = priv->hash_width; u8 whichbit = (result >> (32 - width)) & 0x1f; u8 whichreg = result >> (32 - width + 5); u32 value = (1 << (31-whichbit)); tempval = gfar_read(priv->hash_regs[whichreg]); tempval |= value; gfar_write(priv->hash_regs[whichreg], tempval); return;}/* There are multiple MAC Address register pairs on some controllers * This function sets the numth pair to a given address */static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr){ struct gfar_private *priv = netdev_priv(dev); int idx; char tmpbuf[MAC_ADDR_LEN]; u32 tempval; u32 __iomem *macptr = &priv->regs->macstnaddr1; macptr += num*2; /* Now copy it into the mac registers backwards, cuz */ /* little endian is silly */ for (idx = 0; idx < MAC_ADDR_LEN; idx++) tmpbuf[MAC_ADDR_LEN - 1 - idx] = addr[idx]; gfar_write(macptr, *((u32 *) (tmpbuf))); tempval = *((u32 *) (tmpbuf + 4)); gfar_write(macptr+1, tempval);}/* GFAR error interrupt handler */static irqreturn_t gfar_error(int irq, void *dev_id){ struct net_device *dev = dev_id; struct gfar_private *priv = netdev_priv(dev); /* Save ievent for future reference */ u32 events = gfar_read(&priv->regs->ievent); /* Clear IEVENT */ gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK); /* Hmm... */ if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n", dev->name, events, gfar_read(&priv->regs->imask)); /* Update the error counters */ if (events & IEVENT_TXE) { dev->stats.tx_errors++; if (events & IEVENT_LC) dev->stats.tx_window_errors++; if (events & IEVENT_CRL) dev->stats.tx_aborted_errors++; if (events & IEVENT_XFUN) { if (netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: TX FIFO underrun, " "packet dropped.\n", dev->name); dev->stats.tx_dropped++; priv->extra_stats.tx_underrun++; /* Reactivate the Tx Queues */ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); } if (netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: Transmit Error\n", dev->name); } if (events & IEVENT_BSY) { dev->stats.rx_errors++; priv->extra_stats.rx_bsy++; gfar_receive(irq, dev_id);#ifndef CONFIG_GFAR_NAPI /* Clear the halt bit in RSTAT */ gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);#endif if (netif_msg_rx_err(priv)) printk(KERN_DEBUG "%s: busy error (rstat: %x)\n", dev->name, gfar_read(&priv->regs->rstat)); } if (events & IEVENT_BABR) { dev->stats.rx_errors++; priv->extra_stats.rx_babr++; if (netif_msg_rx_err(priv)) printk(KERN_DEBUG "%s: babbling RX error\n", dev->name); } if (events & IEVENT_EBERR) { priv->extra_stats.eberr++; if (netif_msg_rx_err(priv)) printk(KERN_DEBUG "%s: bus error\n", dev->name); } if ((events & IEVENT_RXC) && netif_msg_rx_status(priv)) printk(KERN_DEBUG "%s: control frame\n", dev->name); if (events & IEVENT_BABT) { priv->extra_stats.tx_babt++; if (netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: babbling TX error\n", dev->name); } return IRQ_HANDLED;}/* Structure for a device driver */static struct platform_driver gfar_driver = { .probe = gfar_probe, .remove = gfar_remove, .driver = { .name = "fsl-gianfar", },};static int __init gfar_init(void){ int err = gfar_mdio_init(); if (err) return err; err = platform_driver_register(&gfar_driver); if (err) gfar_mdio_exit(); return err;}static void __exit gfar_exit(void){ platform_driver_unregister(&gfar_driver); gfar_mdio_exit();}module_init(gfar_init);module_exit(gfar_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -