📄 gmac.c
字号:
#endif /* CONFIG_PMAC_PBOOK *//* * Handle a transmit timeout */static voidgmac_tx_timeout(struct net_device *dev){ struct gmac *gm = (struct gmac *) dev->priv; int i, timeout; unsigned long flags; printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name); spin_lock_irqsave(&gm->lock, flags); /* Stop chip */ gmac_stop_dma(gm); /* Empty Tx ring of any remaining gremlins */ gmac_tx_cleanup(dev, 1); /* Empty Rx ring of any remaining gremlins */ for (i = 0; i < NRX; ++i) { if (gm->rx_buff[i] != 0) { dev_kfree_skb_irq(gm->rx_buff[i]); gm->rx_buff[i] = 0; } } /* Perform a software reset */ GM_OUT(GM_RESET, GM_RESET_TX | GM_RESET_RX); for (timeout = 100; timeout > 0; --timeout) { mdelay(10); if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) { /* Mask out all chips interrupts */ GM_OUT(GM_IRQ_MASK, 0xffffffff); break; } } if (!timeout) printk(KERN_ERR "%s reset chip failed !\n", dev->name); /* Create fresh rings */ gmac_init_rings(gm, 1); /* re-initialize the MAC */ gmac_mac_init(gm, dev->dev_addr); /* re-initialize the multicast tables & promisc mode if any */ gmac_set_multicast(dev); /* Restart PHY auto-poll */ mii_interrupt(gm); /* Restart chip */ gmac_start_dma(gm); spin_unlock_irqrestore(&gm->lock, flags); netif_wake_queue(dev);}/* * Add a packet to the transmit ring */static intgmac_xmit_start(struct sk_buff *skb, struct net_device *dev){ struct gmac *gm = (struct gmac *) dev->priv; volatile struct gmac_dma_desc *dp; unsigned long flags; int i; spin_lock_irqsave(&gm->lock, flags); i = gm->next_tx; if (gm->tx_buff[i] != 0) { /* * Buffer is full, can't send this packet at the moment * * Can this ever happen in 2.4 ? */ netif_stop_queue(dev); spin_unlock_irqrestore(&gm->lock, flags); return 1; } gm->next_tx = (i + 1) & (NTX - 1); gm->tx_buff[i] = skb; dp = &gm->txring[i]; /* FIXME: Interrupt on all packet for now, change this to every N packet, * with N to be adjusted */ dp->flags = TX_FL_INTERRUPT; dp->hi_addr = 0; st_le32(&dp->lo_addr, virt_to_bus(skb->data)); mb(); st_le32(&dp->size, TX_SZ_SOP | TX_SZ_EOP | skb->len); mb(); GM_OUT(GM_TX_KICK, gm->next_tx); if (gm->tx_buff[gm->next_tx] != 0) netif_stop_queue(dev); spin_unlock_irqrestore(&gm->lock, flags); dev->trans_start = jiffies; return 0;}/* * Handle servicing of the transmit ring by deallocating used * Tx packets and restoring flow control when necessary */static voidgmac_tx_cleanup(struct net_device *dev, int force_cleanup){ struct gmac *gm = (struct gmac *) dev->priv; volatile struct gmac_dma_desc *dp; struct sk_buff *skb; int gone, i; i = gm->tx_gone; /* Note: If i==gone, we empty the entire ring. This works because * if the ring was empty, we wouldn't have received the interrupt */ do { gone = GM_IN(GM_TX_COMP); skb = gm->tx_buff[i]; if (skb == NULL) break; dp = &gm->txring[i]; if (force_cleanup) ++gm->stats.tx_errors; else { ++gm->stats.tx_packets; gm->stats.tx_bytes += skb->len; } gm->tx_buff[i] = NULL; dev_kfree_skb_irq(skb); if (++i >= NTX) i = 0; } while (force_cleanup || i != gone); gm->tx_gone = i; if (!force_cleanup && netif_queue_stopped(dev) && (gm->tx_buff[gm->next_tx] == 0)) netif_wake_queue(dev);}/* * Handle servicing of receive ring */static voidgmac_receive(struct net_device *dev){ struct gmac *gm = (struct gmac *) dev->priv; int i = gm->next_rx; volatile struct gmac_dma_desc *dp; struct sk_buff *skb, *new_skb; int len, flags, drop, last; unsigned char *data; u16 csum; last = -1; for (;;) { dp = &gm->rxring[i]; /* Buffer not yet filled, no more Rx buffers to handle */ if (ld_le32(&dp->size) & RX_SZ_OWN) break; /* Get packet length, flags, etc... */ len = (ld_le32(&dp->size) >> 16) & 0x7fff; flags = ld_le32(&dp->flags); skb = gm->rx_buff[i]; drop = 0; new_skb = NULL; csum = ld_le32(&dp->size) & RX_SZ_CKSUM_MASK; /* Handle errors */ if ((len < ETH_ZLEN)||(flags & RX_FL_CRC_ERROR)||(!skb)) { ++gm->stats.rx_errors; if (len < ETH_ZLEN) ++gm->stats.rx_length_errors; if (flags & RX_FL_CRC_ERROR) ++gm->stats.rx_crc_errors; if (!skb) { ++gm->stats.rx_dropped; skb = gmac_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); if (skb) { gm->rx_buff[i] = skb; skb->dev = dev; skb_put(skb, ETH_FRAME_LEN + RX_OFFSET); skb_reserve(skb, RX_OFFSET); } } drop = 1; } else { /* Large packet, alloc a new skb for the ring */ if (len > RX_COPY_THRESHOLD) { new_skb = gmac_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); if(!new_skb) { printk(KERN_INFO "%s: Out of SKBs in Rx, packet dropped !\n", dev->name); drop = 1; ++gm->stats.rx_dropped; goto finish; } gm->rx_buff[i] = new_skb; new_skb->dev = dev; skb_put(new_skb, ETH_FRAME_LEN + RX_OFFSET); skb_reserve(new_skb, RX_OFFSET); skb_trim(skb, len); } else { /* Small packet, copy it to a new small skb */ struct sk_buff *copy_skb = dev_alloc_skb(len + RX_OFFSET); if(!copy_skb) { printk(KERN_INFO "%s: Out of SKBs in Rx, packet dropped !\n", dev->name); drop = 1; ++gm->stats.rx_dropped; goto finish; } copy_skb->dev = dev; skb_reserve(copy_skb, RX_OFFSET); skb_put(copy_skb, len); memcpy(copy_skb->data, skb->data, len); new_skb = skb; skb = copy_skb; } } finish: /* Need to drop packet ? */ if (drop) { new_skb = skb; skb = NULL; } /* Put back ring entry */ data = new_skb ? (new_skb->data - RX_OFFSET) : dummy_buf; dp->hi_addr = 0; st_le32(&dp->lo_addr, virt_to_bus(data)); mb(); st_le32(&dp->size, RX_SZ_OWN | ((RX_BUF_ALLOC_SIZE-RX_OFFSET) << RX_SZ_SHIFT)); /* Got Rx packet ? */ if (skb) { /* Yes, baby, keep that hot ;) */ if(!(csum ^ 0xffff)) skb->ip_summed = CHECKSUM_UNNECESSARY; else skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); gm->stats.rx_bytes += skb->len; ++gm->stats.rx_packets; } last = i; if (++i >= NRX) i = 0; } gm->next_rx = i; if (last >= 0) { mb(); GM_OUT(GM_RX_KICK, last & 0xfffffffc); }}/* * Service chip interrupts */static voidgmac_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = (struct net_device *) dev_id; struct gmac *gm = (struct gmac *) dev->priv; unsigned int status; status = GM_IN(GM_IRQ_STATUS); if (status & (GM_IRQ_BUS_ERROR | GM_IRQ_MIF)) GM_OUT(GM_IRQ_ACK, status & (GM_IRQ_BUS_ERROR | GM_IRQ_MIF)); if (status & (GM_IRQ_RX_TAG_ERR | GM_IRQ_BUS_ERROR)) { printk(KERN_ERR "%s: IRQ Error status: 0x%08x\n", dev->name, status); } if (status & GM_IRQ_MIF) { spin_lock(&gm->lock); mii_interrupt(gm); spin_unlock(&gm->lock); } if (status & GM_IRQ_RX_DONE) { spin_lock(&gm->lock); gmac_receive(dev); spin_unlock(&gm->lock); } if (status & (GM_IRQ_TX_INT_ME | GM_IRQ_TX_ALL)) { spin_lock(&gm->lock); gmac_tx_cleanup(dev, 0); spin_unlock(&gm->lock); }}/* * Retreive some error stats from chip and return them * to above layer */static struct net_device_stats *gmac_stats(struct net_device *dev){ struct gmac *gm = (struct gmac *) dev->priv; struct net_device_stats *stats = &gm->stats; if (gm && gm->opened) { stats->rx_crc_errors += GM_IN(GM_MAC_RX_CRC_ERR_CTR); GM_OUT(GM_MAC_RX_CRC_ERR_CTR, 0); stats->rx_frame_errors += GM_IN(GM_MAC_RX_ALIGN_ERR_CTR); GM_OUT(GM_MAC_RX_ALIGN_ERR_CTR, 0); stats->rx_length_errors += GM_IN(GM_MAC_RX_LEN_ERR_CTR); GM_OUT(GM_MAC_RX_LEN_ERR_CTR, 0); stats->tx_aborted_errors += GM_IN(GM_MAC_EXCS_COLLISION_CTR); stats->collisions += (GM_IN(GM_MAC_EXCS_COLLISION_CTR) + GM_IN(GM_MAC_LATE_COLLISION_CTR)); GM_OUT(GM_MAC_EXCS_COLLISION_CTR, 0); GM_OUT(GM_MAC_LATE_COLLISION_CTR, 0); } return stats;}static int __initgmac_probe(void){ struct device_node *gmac; /* We bump use count during probe since get_free_page can sleep * which can be a race condition if module is unloaded at this * point. */ MOD_INC_USE_COUNT; /* * We don't use PCI scanning on pmac since the GMAC cell is disabled * by default, and thus absent from kernel original PCI probing. */ for (gmac = find_compatible_devices("network", "gmac"); gmac != 0; gmac = gmac->next) gmac_probe1(gmac); MOD_DEC_USE_COUNT; return 0;}static voidgmac_probe1(struct device_node *gmac){ struct gmac *gm; unsigned long tx_descpage, rx_descpage; unsigned char *addr; struct net_device *dev; int i; if (gmac->n_addrs < 1 || gmac->n_intrs < 1) { printk(KERN_ERR "can't use GMAC %s: %d addrs and %d intrs\n", gmac->full_name, gmac->n_addrs, gmac->n_intrs); return; } addr = get_property(gmac, "local-mac-address", NULL); if (addr == NULL) { printk(KERN_ERR "Can't get mac-address for GMAC %s\n", gmac->full_name); return; } tx_descpage = get_free_page(GFP_KERNEL); if (tx_descpage == 0) { printk(KERN_ERR "GMAC: can't get a page for tx descriptors\n"); return; } rx_descpage = get_free_page(GFP_KERNEL); if (rx_descpage == 0) { printk(KERN_ERR "GMAC: can't get a page for rx descriptors\n"); free_page(tx_descpage); return; } dev = init_etherdev(NULL, sizeof(struct gmac)); if (!dev) { printk(KERN_ERR "GMAC: init_etherdev failed, out of memory\n"); free_page(tx_descpage); free_page(rx_descpage); return; } SET_MODULE_OWNER(dev); gm = dev->priv; dev->base_addr = gmac->addrs[0].address; gm->regs = (volatile unsigned int *) ioremap(gmac->addrs[0].address, 0x10000); dev->irq = gmac->intrs[0].line; gm->dev = dev; gm->of_node = gmac; spin_lock_init(&gm->lock); if (pci_device_from_OF_node(gmac, &gm->pci_bus, &gm->pci_devfn)) { gm->pci_bus = gm->pci_devfn = 0xff; printk(KERN_ERR "Can't locate GMAC PCI entry\n"); } printk(KERN_INFO "%s: GMAC at", dev->name); for (i = 0; i < 6; ++i) { dev->dev_addr[i] = addr[i]; printk("%c%.2x", (i? ':': ' '), addr[i]); } printk(", driver " GMAC_VERSION "\n"); gm->tx_desc_page = tx_descpage; gm->rx_desc_page = rx_descpage; gm->rxring = (volatile struct gmac_dma_desc *) rx_descpage; gm->txring = (volatile struct gmac_dma_desc *) tx_descpage; gm->phy_addr = 0; gm->opened = 0; dev->open = gmac_open; dev->stop = gmac_close; dev->hard_start_xmit = gmac_xmit_start; dev->get_stats = gmac_stats; dev->set_multicast_list = &gmac_set_multicast; dev->tx_timeout = &gmac_tx_timeout; dev->watchdog_timeo = 5*HZ; ether_setup(dev); gm->next_gmac = gmacs; gmacs = dev;#ifdef CONFIG_PMAC_PBOOK pmu_register_sleep_notifier(&gmac_sleep_notifier);#endif}MODULE_AUTHOR("Paul Mackerras/Ben Herrenschmidt");MODULE_DESCRIPTION("PowerMac GMAC driver.");static void __exit gmac_cleanup_module(void){ struct gmac *gm; struct net_device *dev; while ((dev = gmacs) != NULL) { gm = (struct gmac *) dev->priv; unregister_netdev(dev); free_page(gm->tx_desc_page); free_page(gm->rx_desc_page); gmacs = gm->next_gmac; kfree(dev); }}module_init(gmac_probe);module_exit(gmac_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -