📄 pasemi_mac.c
字号:
mac->tx->irq_name, dev); if (ret) { dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n", base_irq + mac->dma_txch, ret); goto out_tx_int; } ret = request_irq(mac->rx_irq, &pasemi_mac_rx_intr, IRQF_DISABLED, mac->rx->irq_name, dev); if (ret) { dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n", base_irq + 20 + mac->dma_rxch, ret); goto out_rx_int; } if (mac->phydev) phy_start(mac->phydev); return 0;out_rx_int: free_irq(mac->tx_irq, dev);out_tx_int: napi_disable(&mac->napi); netif_stop_queue(dev); pasemi_mac_free_tx_resources(dev);out_tx_resources: pasemi_mac_free_rx_resources(dev);out_rx_resources: return ret;}#define MAX_RETRIES 5000static int pasemi_mac_close(struct net_device *dev){ struct pasemi_mac *mac = netdev_priv(dev); unsigned int sta; int retries; if (mac->phydev) { phy_stop(mac->phydev); phy_disconnect(mac->phydev); } netif_stop_queue(dev); napi_disable(&mac->napi); sta = read_dma_reg(mac, PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); if (sta & (PAS_DMA_RXINT_RCMDSTA_BP | PAS_DMA_RXINT_RCMDSTA_OO | PAS_DMA_RXINT_RCMDSTA_BT)) printk(KERN_DEBUG "pasemi_mac: rcmdsta error: 0x%08x\n", sta); sta = read_dma_reg(mac, PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch)); if (sta & (PAS_DMA_RXCHAN_CCMDSTA_DU | PAS_DMA_RXCHAN_CCMDSTA_OD | PAS_DMA_RXCHAN_CCMDSTA_FD | PAS_DMA_RXCHAN_CCMDSTA_DT)) printk(KERN_DEBUG "pasemi_mac: ccmdsta error: 0x%08x\n", sta); sta = read_dma_reg(mac, PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch)); if (sta & (PAS_DMA_TXCHAN_TCMDSTA_SZ | PAS_DMA_TXCHAN_TCMDSTA_DB | PAS_DMA_TXCHAN_TCMDSTA_DE | PAS_DMA_TXCHAN_TCMDSTA_DA)) printk(KERN_DEBUG "pasemi_mac: tcmdsta error: 0x%08x\n", sta); /* Clean out any pending buffers */ pasemi_mac_clean_tx(mac); pasemi_mac_clean_rx(mac, RX_RING_SIZE); /* Disable interface */ write_dma_reg(mac, PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch), PAS_DMA_TXCHAN_TCMDSTA_ST); write_dma_reg(mac, PAS_DMA_RXINT_RCMDSTA(mac->dma_if), PAS_DMA_RXINT_RCMDSTA_ST); write_dma_reg(mac, PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch), PAS_DMA_RXCHAN_CCMDSTA_ST); for (retries = 0; retries < MAX_RETRIES; retries++) { sta = read_dma_reg(mac, PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch)); if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) break; cond_resched(); } if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT) dev_err(&mac->dma_pdev->dev, "Failed to stop tx channel\n"); for (retries = 0; retries < MAX_RETRIES; retries++) { sta = read_dma_reg(mac, PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch)); if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) break; cond_resched(); } if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT) dev_err(&mac->dma_pdev->dev, "Failed to stop rx channel\n"); for (retries = 0; retries < MAX_RETRIES; retries++) { sta = read_dma_reg(mac, PAS_DMA_RXINT_RCMDSTA(mac->dma_if)); if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT)) break; cond_resched(); } if (sta & PAS_DMA_RXINT_RCMDSTA_ACT) dev_err(&mac->dma_pdev->dev, "Failed to stop rx interface\n"); /* Then, disable the channel. This must be done separately from * stopping, since you can't disable when active. */ write_dma_reg(mac, PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch), 0); write_dma_reg(mac, PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch), 0); write_dma_reg(mac, PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0); free_irq(mac->tx_irq, dev); free_irq(mac->rx_irq, dev); /* Free resources */ pasemi_mac_free_rx_resources(dev); pasemi_mac_free_tx_resources(dev); return 0;}static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev){ struct pasemi_mac *mac = netdev_priv(dev); struct pasemi_mac_txring *txring; u64 dflags, mactx; dma_addr_t map[MAX_SKB_FRAGS+1]; unsigned int map_size[MAX_SKB_FRAGS+1]; unsigned long flags; int i, nfrags; dflags = XCT_MACTX_O | XCT_MACTX_ST | XCT_MACTX_CRC_PAD; if (skb->ip_summed == CHECKSUM_PARTIAL) { const unsigned char *nh = skb_network_header(skb); switch (ip_hdr(skb)->protocol) { case IPPROTO_TCP: dflags |= XCT_MACTX_CSUM_TCP; dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2); dflags |= XCT_MACTX_IPO(nh - skb->data); break; case IPPROTO_UDP: dflags |= XCT_MACTX_CSUM_UDP; dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2); dflags |= XCT_MACTX_IPO(nh - skb->data); break; } } nfrags = skb_shinfo(skb)->nr_frags; map[0] = pci_map_single(mac->dma_pdev, skb->data, skb_headlen(skb), PCI_DMA_TODEVICE); map_size[0] = skb_headlen(skb); if (dma_mapping_error(map[0])) goto out_err_nolock; for (i = 0; i < nfrags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; map[i+1] = pci_map_page(mac->dma_pdev, frag->page, frag->page_offset, frag->size, PCI_DMA_TODEVICE); map_size[i+1] = frag->size; if (dma_mapping_error(map[i+1])) { nfrags = i; goto out_err_nolock; } } mactx = dflags | XCT_MACTX_LLEN(skb->len); txring = mac->tx; spin_lock_irqsave(&txring->lock, flags); /* Avoid stepping on the same cache line that the DMA controller * is currently about to send, so leave at least 8 words available. * Total free space needed is mactx + fragments + 8 */ if (RING_AVAIL(txring) < nfrags + 10) { /* no room -- stop the queue and wait for tx intr */ netif_stop_queue(dev); goto out_err; } TX_RING(mac, txring->next_to_fill) = mactx; txring->next_to_fill++; TX_RING_INFO(mac, txring->next_to_fill).skb = skb; for (i = 0; i <= nfrags; i++) { TX_RING(mac, txring->next_to_fill+i) = XCT_PTR_LEN(map_size[i]) | XCT_PTR_ADDR(map[i]); TX_RING_INFO(mac, txring->next_to_fill+i).dma = map[i]; } /* We have to add an even number of 8-byte entries to the ring * even if the last one is unused. That means always an odd number * of pointers + one mactx descriptor. */ if (nfrags & 1) nfrags++; txring->next_to_fill = (txring->next_to_fill + nfrags + 1) & (TX_RING_SIZE-1); dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; spin_unlock_irqrestore(&txring->lock, flags); write_dma_reg(mac, PAS_DMA_TXCHAN_INCR(mac->dma_txch), (nfrags+2) >> 1); return NETDEV_TX_OK;out_err: spin_unlock_irqrestore(&txring->lock, flags);out_err_nolock: while (nfrags--) pci_unmap_single(mac->dma_pdev, map[nfrags], map_size[nfrags], PCI_DMA_TODEVICE); return NETDEV_TX_BUSY;}static void pasemi_mac_set_rx_mode(struct net_device *dev){ struct pasemi_mac *mac = netdev_priv(dev); unsigned int flags; flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG); /* Set promiscuous */ if (dev->flags & IFF_PROMISC) flags |= PAS_MAC_CFG_PCFG_PR; else flags &= ~PAS_MAC_CFG_PCFG_PR; write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);}static int pasemi_mac_poll(struct napi_struct *napi, int budget){ struct pasemi_mac *mac = container_of(napi, struct pasemi_mac, napi); struct net_device *dev = mac->netdev; int pkts; pasemi_mac_clean_tx(mac); pkts = pasemi_mac_clean_rx(mac, budget); if (pkts < budget) { /* all done, no more packets present */ netif_rx_complete(dev, napi); pasemi_mac_restart_rx_intr(mac); } return pkts;}static void __iomem * __devinit map_onedev(struct pci_dev *p, int index){ struct device_node *dn; void __iomem *ret; dn = pci_device_to_OF_node(p); if (!dn) goto fallback; ret = of_iomap(dn, index); if (!ret) goto fallback; return ret;fallback: /* This is hardcoded and ugly, but we have some firmware versions * that don't provide the register space in the device tree. Luckily * they are at well-known locations so we can just do the math here. */ return ioremap(0xe0000000 + (p->devfn << 12), 0x2000);}static int __devinit pasemi_mac_map_regs(struct pasemi_mac *mac){ struct resource res; struct device_node *dn; int err; mac->dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL); if (!mac->dma_pdev) { dev_err(&mac->pdev->dev, "Can't find DMA Controller\n"); return -ENODEV; } mac->iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL); if (!mac->iob_pdev) { dev_err(&mac->pdev->dev, "Can't find I/O Bridge\n"); return -ENODEV; } mac->regs = map_onedev(mac->pdev, 0); mac->dma_regs = map_onedev(mac->dma_pdev, 0); mac->iob_regs = map_onedev(mac->iob_pdev, 0); if (!mac->regs || !mac->dma_regs || !mac->iob_regs) { dev_err(&mac->pdev->dev, "Can't map registers\n"); return -ENODEV; } /* The dma status structure is located in the I/O bridge, and * is cache coherent. */ if (!dma_status) { dn = pci_device_to_OF_node(mac->iob_pdev); if (dn) err = of_address_to_resource(dn, 1, &res); if (!dn || err) { /* Fallback for old firmware */ res.start = 0xfd800000; res.end = res.start + 0x1000; } dma_status = __ioremap(res.start, res.end-res.start, 0); } return 0;}static int __devinitpasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent){ static int index = 0; struct net_device *dev; struct pasemi_mac *mac; int err; DECLARE_MAC_BUF(mac_buf); err = pci_enable_device(pdev); if (err) return err; dev = alloc_etherdev(sizeof(struct pasemi_mac)); if (dev == NULL) { dev_err(&pdev->dev, "pasemi_mac: Could not allocate ethernet device.\n"); err = -ENOMEM; goto out_disable_device; } pci_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); mac = netdev_priv(dev); mac->pdev = pdev; mac->netdev = dev; netif_napi_add(dev, &mac->napi, pasemi_mac_poll, 64); dev->features = NETIF_F_IP_CSUM | NETIF_F_LLTX | NETIF_F_SG; /* These should come out of the device tree eventually */ mac->dma_txch = index; mac->dma_rxch = index; /* We probe GMAC before XAUI, but the DMA interfaces are * in XAUI, GMAC order. */ if (index < 4) mac->dma_if = index + 2; else mac->dma_if = index - 4; index++; switch (pdev->device) { case 0xa005: mac->type = MAC_TYPE_GMAC; break; case 0xa006: mac->type = MAC_TYPE_XAUI; break; default: err = -ENODEV; goto out; } /* get mac addr from device tree */ if (pasemi_get_mac_addr(mac) || !is_valid_ether_addr(mac->mac_addr)) { err = -ENODEV; goto out; } memcpy(dev->dev_addr, mac->mac_addr, sizeof(mac->mac_addr)); dev->open = pasemi_mac_open; dev->stop = pasemi_mac_close; dev->hard_start_xmit = pasemi_mac_start_tx; dev->set_multicast_list = pasemi_mac_set_rx_mode; err = pasemi_mac_map_regs(mac); if (err) goto out; mac->rx_status = &dma_status->rx_sta[mac->dma_rxch]; mac->tx_status = &dma_status->tx_sta[mac->dma_txch]; mac->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); /* Enable most messages by default */ mac->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; err = register_netdev(dev); if (err) { dev_err(&mac->pdev->dev, "register_netdev failed with error %d\n", err); goto out; } else if netif_msg_probe(mac) printk(KERN_INFO "%s: PA Semi %s: intf %d, txch %d, rxch %d, " "hw addr %s\n", dev->name, mac->type == MAC_TYPE_GMAC ? "GMAC" : "XAUI", mac->dma_if, mac->dma_txch, mac->dma_rxch, print_mac(mac_buf, dev->dev_addr)); return err;out: if (mac->iob_pdev) pci_dev_put(mac->iob_pdev); if (mac->dma_pdev) pci_dev_put(mac->dma_pdev); if (mac->dma_regs) iounmap(mac->dma_regs); if (mac->iob_regs) iounmap(mac->iob_regs); if (mac->regs) iounmap(mac->regs); free_netdev(dev);out_disable_device: pci_disable_device(pdev); return err;}static void __devexit pasemi_mac_remove(struct pci_dev *pdev){ struct net_device *netdev = pci_get_drvdata(pdev); struct pasemi_mac *mac; if (!netdev) return; mac = netdev_priv(netdev); unregister_netdev(netdev); pci_disable_device(pdev); pci_dev_put(mac->dma_pdev); pci_dev_put(mac->iob_pdev); iounmap(mac->regs); iounmap(mac->dma_regs); iounmap(mac->iob_regs); pci_set_drvdata(pdev, NULL); free_netdev(netdev);}static struct pci_device_id pasemi_mac_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa005) }, { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa006) }, { },};MODULE_DEVICE_TABLE(pci, pasemi_mac_pci_tbl);static struct pci_driver pasemi_mac_driver = { .name = "pasemi_mac", .id_table = pasemi_mac_pci_tbl, .probe = pasemi_mac_probe, .remove = __devexit_p(pasemi_mac_remove),};static void __exit pasemi_mac_cleanup_module(void){ pci_unregister_driver(&pasemi_mac_driver); __iounmap(dma_status); dma_status = NULL;}int pasemi_mac_init_module(void){ return pci_register_driver(&pasemi_mac_driver);}module_init(pasemi_mac_init_module);module_exit(pasemi_mac_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -