/* * Copyright (C) 2006-2007 PA Semi, Inc * * Driver for the PA Semi PWRficient onchip 1G/10G Ethernet MACs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/init.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/dmaengine.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <asm/dma-mapping.h>#include <linux/in.h>#include <linux/skbuff.h> #include <linux/ip.h>#include <linux/tcp.h>#include <net/checksum.h> #include "pasemi_mac.h" /* TODO list * * - Get rid of pci_{read,write}_config(), map registers with ioremap * for performance * - PHY support * - Multicast support * - Large MTU support * - Other performance improvements */ /* Must be a power of two */#define RX_RING_SIZE 512#define TX_RING_SIZE 512 #define TX_DESC(mac, num) ((mac)->tx->desc[(num) & (TX_RING_SIZE-1)])#define TX_DESC_INFO(mac, num) ((mac)->tx->desc_info[(num) & (TX_RING_SIZE-1)])#define RX_DESC(mac, num) ((mac)->rx->desc[(num) & (RX_RING_SIZE-1)])#define RX_DESC_INFO(mac, num) ((mac)->rx->desc_info[(num) & (RX_RING_SIZE-1)])#define RX_BUFF(mac, num) ((mac)->rx->buffers[(num) & (RX_RING_SIZE-1)]) #define BUF_SIZE 1646 /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ /* XXXOJN these should come out of the device tree some day */#define PAS_DMA_CAP_BASE 0xe00d0040#define PAS_DMA_CAP_SIZE 0x100#define PAS_DMA_COM_BASE 0xe00d0100#define PAS_DMA_COM_SIZE 0x100 static struct pasdma_status *dma_status; static int pasemi_get_mac_addr(struct pasemi_mac *mac){ struct pci_dev *pdev = mac->pdev; struct device_node *dn = pci_device_to_OF_node(pdev); const u8 *maddr; u8 addr[6]; if (!dn) { dev_dbg(&pdev->dev, "No device node for mac, not configuring\n"); return -ENOENT; } maddr = get_property(dn, "mac-address", NULL); if (maddr == NULL) { dev_warn(&pdev->dev, "no mac address in device tree, not configuring\n"); return -ENOENT; } if (sscanf(maddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) != 6) { dev_warn(&pdev->dev, "can't parse mac address, not configuring\n"); return -EINVAL; } memcpy(mac->mac_addr, addr, sizeof(addr)); return 0;} static int pasemi_mac_setup_rx_resources(struct net_device *dev){ struct pasemi_mac_rxring *ring; struct pasemi_mac *mac = netdev_priv(dev); int chan_id = mac->dma_rxch; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out_ring; spin_lock_init(&ring->lock); ring->desc_info = kzalloc(sizeof(struct pasemi_mac_buffer) * RX_RING_SIZE, GFP_KERNEL); if (!ring->desc_info) goto out_desc_info; /* Allocate descriptors */ ring->desc = dma_alloc_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr), &ring->dma, GFP_KERNEL); if (!ring->desc) goto out_desc; memset(ring->desc, 0, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr)); ring->buffers = dma_alloc_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), &ring->buf_dma, GFP_KERNEL); if (!ring->buffers) goto out_buffers; memset(ring->buffers, 0, RX_RING_SIZE * sizeof(u64)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_BASEL(chan_id), PAS_DMA_RXCHAN_BASEL_BRBL(ring->dma)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_BASEU(chan_id), PAS_DMA_RXCHAN_BASEU_BRBH(ring->dma >> 32) | PAS_DMA_RXCHAN_BASEU_SIZ(RX_RING_SIZE >> 2)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_CFG(chan_id), PAS_DMA_RXCHAN_CFG_HBU(1)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXINT_BASEL(mac->dma_if), PAS_DMA_RXINT_BASEL_BRBL(__pa(ring->buffers))); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXINT_BASEU(mac->dma_if), PAS_DMA_RXINT_BASEU_BRBH(__pa(ring->buffers) >> 32) | PAS_DMA_RXINT_BASEU_SIZ(RX_RING_SIZE >> 3)); ring->next_to_fill = 0; ring->next_to_clean = 0; snprintf(ring->irq_name, sizeof(ring->irq_name), "%s rx", dev->name); mac->rx = ring; return 0; out_buffers: dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr), mac->rx->desc, mac->rx->dma);out_desc: kfree(ring->desc_info);out_desc_info: kfree(ring);out_ring: return -ENOMEM;} static int pasemi_mac_setup_tx_resources(struct net_device *dev){ struct pasemi_mac *mac = netdev_priv(dev); u32 val; int chan_id = mac->dma_txch; struct pasemi_mac_txring *ring; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out_ring; spin_lock_init(&ring->lock); ring->desc_info = kzalloc(sizeof(struct pasemi_mac_buffer) * TX_RING_SIZE, GFP_KERNEL); if (!ring->desc_info) goto out_desc_info; /* Allocate descriptors */ ring->desc = dma_alloc_coherent(&mac->dma_pdev->dev, TX_RING_SIZE * sizeof(struct pas_dma_xct_descr), &ring->dma, GFP_KERNEL); if (!ring->desc) goto out_desc; memset(ring->desc, 0, TX_RING_SIZE * sizeof(struct pas_dma_xct_descr)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_TXCHAN_BASEL(chan_id), PAS_DMA_TXCHAN_BASEL_BRBL(ring->dma)); val = PAS_DMA_TXCHAN_BASEU_BRBH(ring->dma >> 32); val |= PAS_DMA_TXCHAN_BASEU_SIZ(TX_RING_SIZE >> 2); pci_write_config_dword(mac->dma_pdev, PAS_DMA_TXCHAN_BASEU(chan_id), val); pci_write_config_dword(mac->dma_pdev, PAS_DMA_TXCHAN_CFG(chan_id), PAS_DMA_TXCHAN_CFG_TY_IFACE | PAS_DMA_TXCHAN_CFG_TATTR(mac->dma_if) | PAS_DMA_TXCHAN_CFG_UP | PAS_DMA_TXCHAN_CFG_WT(2)); ring->next_to_use = 0; ring->next_to_clean = 0; snprintf(ring->irq_name, sizeof(ring->irq_name), "%s tx", dev->name); mac->tx = ring; return 0; out_desc: kfree(ring->desc_info);out_desc_info: kfree(ring);out_ring: return -ENOMEM;} static void pasemi_mac_free_tx_resources(struct net_device *dev){ struct pasemi_mac *mac = netdev_priv(dev); unsigned int i; struct pasemi_mac_buffer *info; struct pas_dma_xct_descr *dp; for (i = 0; i < TX_RING_SIZE; i++) { info = &TX_DESC_INFO(mac, i); dp = &TX_DESC(mac, i); if (info->dma) { if (info->skb) { pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_any(info->skb); } info->dma = 0; info->skb = NULL; dp->mactx = 0; dp->ptr = 0; } } dma_free_coherent(&mac->dma_pdev->dev, TX_RING_SIZE * sizeof(struct pas_dma_xct_descr), mac->tx->desc, mac->tx->dma); kfree(mac->tx->desc_info); kfree(mac->tx); mac->tx = NULL;} static void pasemi_mac_free_rx_resources(struct net_device *dev){ struct pasemi_mac *mac = netdev_priv(dev); unsigned int i; struct pasemi_mac_buffer *info; struct pas_dma_xct_descr *dp; for (i = 0; i < RX_RING_SIZE; i++) { info = &RX_DESC_INFO(mac, i); dp = &RX_DESC(mac, i); if (info->dma) { if (info->skb) { pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_FROMDEVICE); dev_kfree_skb_any(info->skb); } info->dma = 0; info->skb = NULL; dp->macrx = 0; dp->ptr = 0; } } dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr), mac->rx->desc, mac->rx->dma); dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), mac->rx->buffers, mac->rx->buf_dma); kfree(mac->rx->desc_info); kfree(mac->rx); mac->rx = NULL;} static void pasemi_mac_replenish_rx_ring(struct net_device *dev){ struct pasemi_mac *mac = netdev_priv(dev); unsigned int i; int start = mac->rx->next_to_fill; unsigned int count; count = (mac->rx->next_to_clean + RX_RING_SIZE - mac->rx->next_to_fill) & (RX_RING_SIZE - 1); /* Check to see if we're doing first-time setup */ if (unlikely(mac->rx->next_to_clean == 0 && mac->rx->next_to_fill == 0)) count = RX_RING_SIZE; if (count <= 0) return; for (i = start; i < start + count; i++) { struct pasemi_mac_buffer *info = &RX_DESC_INFO(mac, i); u64 *buff = &RX_BUFF(mac, i); struct sk_buff *skb; dma_addr_t dma; skb = dev_alloc_skb(BUF_SIZE); if (!skb) { count = i - start; break; } skb->dev = dev; dma = pci_map_single(mac->dma_pdev, skb->data, skb->len, PCI_DMA_FROMDEVICE); if (dma_mapping_error(dma)) { dev_kfree_skb_irq(info->skb); count = i - start; break; } info->skb = skb; info->dma = dma; *buff = XCT_RXB_LEN(BUF_SIZE) | XCT_RXB_ADDR(dma); } wmb(); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_INCR(mac->dma_rxch), count); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXINT_INCR(mac->dma_if), count); mac->rx->next_to_fill += count;} static int pasemi_mac_clean_rx(struct pasemi_mac *mac, int limit){ unsigned int i; int start, count; spin_lock(&mac->rx->lock); start = mac->rx->next_to_clean; count = 0; for (i = start; i < (start + RX_RING_SIZE) && count < limit; i++) { struct pas_dma_xct_descr *dp; struct pasemi_mac_buffer *info; struct sk_buff *skb; unsigned int j, len; dma_addr_t dma; rmb(); dp = &RX_DESC(mac, i); if (!(dp->macrx & XCT_MACRX_O)) break; count++; info = NULL; /* We have to scan for our skb since there's no way * to back-map them from the descriptor, and if we * have several receive channels then they might not * show up in the same order as they were put on the * interface ring. */ dma = (dp->ptr & XCT_PTR_ADDR_M); for (j = start; j < (start + RX_RING_SIZE); j++) { info = &RX_DESC_INFO(mac, j); if (info->dma == dma) break; } BUG_ON(!info); BUG_ON(info->dma != dma); pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_FROMDEVICE); skb = info->skb; len = (dp->macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S; skb_put(skb, len); skb->protocol = eth_type_trans(skb, mac->netdev); if ((dp->macrx & XCT_MACRX_HTY_M) == XCT_MACRX_HTY_IPV4_OK) { skb->ip_summed = CHECKSUM_COMPLETE; skb->csum = (dp->macrx & XCT_MACRX_CSUM_M) >> XCT_MACRX_CSUM_S; } else skb->ip_summed = CHECKSUM_NONE; mac->stats.rx_bytes += len; mac->stats.rx_packets++; netif_receive_skb(skb); info->dma = 0; info->skb = NULL; dp->ptr = 0; dp->macrx = 0; } mac->rx->next_to_clean += count; pasemi_mac_replenish_rx_ring(mac->netdev); spin_unlock(&mac->rx->lock); return count;} static int pasemi_mac_clean_tx(struct pasemi_mac *mac){ int i; struct pasemi_mac_buffer *info; struct pas_dma_xct_descr *dp; int start, count; int flags; spin_lock_irqsave(&mac->tx->lock, flags); start = mac->tx->next_to_clean; count = 0; for (i = start; i < mac->tx->next_to_use; i++) { dp = &TX_DESC(mac, i); if (!dp || (dp->mactx & XCT_MACTX_O)) break; count++; info = &TX_DESC_INFO(mac, i); pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(info->skb); info->skb = NULL; info->dma = 0; dp->mactx = 0; dp->ptr = 0; } mac->tx->next_to_clean += count; spin_unlock_irqrestore(&mac->tx->lock, flags); return count;} static irqreturn_t pasemi_mac_rx_intr(int irq, void *data){ struct net_device *dev = data; struct pasemi_mac *mac = netdev_priv(dev); unsigned int reg; if (!(*mac->rx_status & PAS_STATUS_INT)) return IRQ_NONE; netif_rx_schedule(dev); pci_write_config_dword(mac->iob_pdev, PAS_IOB_DMA_COM_TIMEOUTCFG, PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(0)); reg = PAS_IOB_DMA_RXCH_RESET_PINTC | PAS_IOB_DMA_RXCH_RESET_SINTC | PAS_IOB_DMA_RXCH_RESET_DINTC; if (*mac->rx_status & PAS_STATUS_TIMER)Lines 1–500 of 1019. Load more
/* * Copyright (C) 2006-2007 PA Semi, Inc * * Driver for the PA Semi PWRficient onchip 1G/10G Ethernet MACs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/init.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/dmaengine.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <asm/dma-mapping.h> #include <linux/in.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/tcp.h> #include <net/checksum.h> #include "pasemi_mac.h" /* TODO list * * - Get rid of pci_{read,write}_config(), map registers with ioremap * for performance * - PHY support * - Multicast support * - Large MTU support * - Other performance improvements */ /* Must be a power of two */ #define RX_RING_SIZE 512 #define TX_RING_SIZE 512 #define TX_DESC(mac, num) ((mac)->tx->desc[(num) & (TX_RING_SIZE-1)]) #define TX_DESC_INFO(mac, num) ((mac)->tx->desc_info[(num) & (TX_RING_SIZE-1)]) #define RX_DESC(mac, num) ((mac)->rx->desc[(num) & (RX_RING_SIZE-1)]) #define RX_DESC_INFO(mac, num) ((mac)->rx->desc_info[(num) & (RX_RING_SIZE-1)]) #define RX_BUFF(mac, num) ((mac)->rx->buffers[(num) & (RX_RING_SIZE-1)]) #define BUF_SIZE 1646 /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */ /* XXXOJN these should come out of the device tree some day */ #define PAS_DMA_CAP_BASE 0xe00d0040 #define PAS_DMA_CAP_SIZE 0x100 #define PAS_DMA_COM_BASE 0xe00d0100 #define PAS_DMA_COM_SIZE 0x100 static struct pasdma_status *dma_status; static int pasemi_get_mac_addr(struct pasemi_mac *mac) { struct pci_dev *pdev = mac->pdev; struct device_node *dn = pci_device_to_OF_node(pdev); const u8 *maddr; u8 addr[6]; if (!dn) { dev_dbg(&pdev->dev, "No device node for mac, not configuring\n"); return -ENOENT; } maddr = get_property(dn, "mac-address", NULL); if (maddr == NULL) { dev_warn(&pdev->dev, "no mac address in device tree, not configuring\n"); return -ENOENT; } if (sscanf(maddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) != 6) { dev_warn(&pdev->dev, "can't parse mac address, not configuring\n"); return -EINVAL; } memcpy(mac->mac_addr, addr, sizeof(addr)); return 0; } static int pasemi_mac_setup_rx_resources(struct net_device *dev) { struct pasemi_mac_rxring *ring; struct pasemi_mac *mac = netdev_priv(dev); int chan_id = mac->dma_rxch; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out_ring; spin_lock_init(&ring->lock); ring->desc_info = kzalloc(sizeof(struct pasemi_mac_buffer) * RX_RING_SIZE, GFP_KERNEL); if (!ring->desc_info) goto out_desc_info; /* Allocate descriptors */ ring->desc = dma_alloc_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr), &ring->dma, GFP_KERNEL); if (!ring->desc) goto out_desc; memset(ring->desc, 0, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr)); ring->buffers = dma_alloc_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), &ring->buf_dma, GFP_KERNEL); if (!ring->buffers) goto out_buffers; memset(ring->buffers, 0, RX_RING_SIZE * sizeof(u64)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_BASEL(chan_id), PAS_DMA_RXCHAN_BASEL_BRBL(ring->dma)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_BASEU(chan_id), PAS_DMA_RXCHAN_BASEU_BRBH(ring->dma >> 32) | PAS_DMA_RXCHAN_BASEU_SIZ(RX_RING_SIZE >> 2)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_CFG(chan_id), PAS_DMA_RXCHAN_CFG_HBU(1)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXINT_BASEL(mac->dma_if), PAS_DMA_RXINT_BASEL_BRBL(__pa(ring->buffers))); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXINT_BASEU(mac->dma_if), PAS_DMA_RXINT_BASEU_BRBH(__pa(ring->buffers) >> 32) | PAS_DMA_RXINT_BASEU_SIZ(RX_RING_SIZE >> 3)); ring->next_to_fill = 0; ring->next_to_clean = 0; snprintf(ring->irq_name, sizeof(ring->irq_name), "%s rx", dev->name); mac->rx = ring; return 0; out_buffers: dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr), mac->rx->desc, mac->rx->dma); out_desc: kfree(ring->desc_info); out_desc_info: kfree(ring); out_ring: return -ENOMEM; } static int pasemi_mac_setup_tx_resources(struct net_device *dev) { struct pasemi_mac *mac = netdev_priv(dev); u32 val; int chan_id = mac->dma_txch; struct pasemi_mac_txring *ring; ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) goto out_ring; spin_lock_init(&ring->lock); ring->desc_info = kzalloc(sizeof(struct pasemi_mac_buffer) * TX_RING_SIZE, GFP_KERNEL); if (!ring->desc_info) goto out_desc_info; /* Allocate descriptors */ ring->desc = dma_alloc_coherent(&mac->dma_pdev->dev, TX_RING_SIZE * sizeof(struct pas_dma_xct_descr), &ring->dma, GFP_KERNEL); if (!ring->desc) goto out_desc; memset(ring->desc, 0, TX_RING_SIZE * sizeof(struct pas_dma_xct_descr)); pci_write_config_dword(mac->dma_pdev, PAS_DMA_TXCHAN_BASEL(chan_id), PAS_DMA_TXCHAN_BASEL_BRBL(ring->dma)); val = PAS_DMA_TXCHAN_BASEU_BRBH(ring->dma >> 32); val |= PAS_DMA_TXCHAN_BASEU_SIZ(TX_RING_SIZE >> 2); pci_write_config_dword(mac->dma_pdev, PAS_DMA_TXCHAN_BASEU(chan_id), val); pci_write_config_dword(mac->dma_pdev, PAS_DMA_TXCHAN_CFG(chan_id), PAS_DMA_TXCHAN_CFG_TY_IFACE | PAS_DMA_TXCHAN_CFG_TATTR(mac->dma_if) | PAS_DMA_TXCHAN_CFG_UP | PAS_DMA_TXCHAN_CFG_WT(2)); ring->next_to_use = 0; ring->next_to_clean = 0; snprintf(ring->irq_name, sizeof(ring->irq_name), "%s tx", dev->name); mac->tx = ring; return 0; out_desc: kfree(ring->desc_info); out_desc_info: kfree(ring); out_ring: return -ENOMEM; } static void pasemi_mac_free_tx_resources(struct net_device *dev) { struct pasemi_mac *mac = netdev_priv(dev); unsigned int i; struct pasemi_mac_buffer *info; struct pas_dma_xct_descr *dp; for (i = 0; i < TX_RING_SIZE; i++) { info = &TX_DESC_INFO(mac, i); dp = &TX_DESC(mac, i); if (info->dma) { if (info->skb) { pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_any(info->skb); } info->dma = 0; info->skb = NULL; dp->mactx = 0; dp->ptr = 0; } } dma_free_coherent(&mac->dma_pdev->dev, TX_RING_SIZE * sizeof(struct pas_dma_xct_descr), mac->tx->desc, mac->tx->dma); kfree(mac->tx->desc_info); kfree(mac->tx); mac->tx = NULL; } static void pasemi_mac_free_rx_resources(struct net_device *dev) { struct pasemi_mac *mac = netdev_priv(dev); unsigned int i; struct pasemi_mac_buffer *info; struct pas_dma_xct_descr *dp; for (i = 0; i < RX_RING_SIZE; i++) { info = &RX_DESC_INFO(mac, i); dp = &RX_DESC(mac, i); if (info->dma) { if (info->skb) { pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_FROMDEVICE); dev_kfree_skb_any(info->skb); } info->dma = 0; info->skb = NULL; dp->macrx = 0; dp->ptr = 0; } } dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(struct pas_dma_xct_descr), mac->rx->desc, mac->rx->dma); dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64), mac->rx->buffers, mac->rx->buf_dma); kfree(mac->rx->desc_info); kfree(mac->rx); mac->rx = NULL; } static void pasemi_mac_replenish_rx_ring(struct net_device *dev) { struct pasemi_mac *mac = netdev_priv(dev); unsigned int i; int start = mac->rx->next_to_fill; unsigned int count; count = (mac->rx->next_to_clean + RX_RING_SIZE - mac->rx->next_to_fill) & (RX_RING_SIZE - 1); /* Check to see if we're doing first-time setup */ if (unlikely(mac->rx->next_to_clean == 0 && mac->rx->next_to_fill == 0)) count = RX_RING_SIZE; if (count <= 0) return; for (i = start; i < start + count; i++) { struct pasemi_mac_buffer *info = &RX_DESC_INFO(mac, i); u64 *buff = &RX_BUFF(mac, i); struct sk_buff *skb; dma_addr_t dma; skb = dev_alloc_skb(BUF_SIZE); if (!skb) { count = i - start; break; } skb->dev = dev; dma = pci_map_single(mac->dma_pdev, skb->data, skb->len, PCI_DMA_FROMDEVICE); if (dma_mapping_error(dma)) { dev_kfree_skb_irq(info->skb); count = i - start; break; } info->skb = skb; info->dma = dma; *buff = XCT_RXB_LEN(BUF_SIZE) | XCT_RXB_ADDR(dma); } wmb(); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXCHAN_INCR(mac->dma_rxch), count); pci_write_config_dword(mac->dma_pdev, PAS_DMA_RXINT_INCR(mac->dma_if), count); mac->rx->next_to_fill += count; } static int pasemi_mac_clean_rx(struct pasemi_mac *mac, int limit) { unsigned int i; int start, count; spin_lock(&mac->rx->lock); start = mac->rx->next_to_clean; count = 0; for (i = start; i < (start + RX_RING_SIZE) && count < limit; i++) { struct pas_dma_xct_descr *dp; struct pasemi_mac_buffer *info; struct sk_buff *skb; unsigned int j, len; dma_addr_t dma; rmb(); dp = &RX_DESC(mac, i); if (!(dp->macrx & XCT_MACRX_O)) break; count++; info = NULL; /* We have to scan for our skb since there's no way * to back-map them from the descriptor, and if we * have several receive channels then they might not * show up in the same order as they were put on the * interface ring. */ dma = (dp->ptr & XCT_PTR_ADDR_M); for (j = start; j < (start + RX_RING_SIZE); j++) { info = &RX_DESC_INFO(mac, j); if (info->dma == dma) break; } BUG_ON(!info); BUG_ON(info->dma != dma); pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_FROMDEVICE); skb = info->skb; len = (dp->macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S; skb_put(skb, len); skb->protocol = eth_type_trans(skb, mac->netdev); if ((dp->macrx & XCT_MACRX_HTY_M) == XCT_MACRX_HTY_IPV4_OK) { skb->ip_summed = CHECKSUM_COMPLETE; skb->csum = (dp->macrx & XCT_MACRX_CSUM_M) >> XCT_MACRX_CSUM_S; } else skb->ip_summed = CHECKSUM_NONE; mac->stats.rx_bytes += len; mac->stats.rx_packets++; netif_receive_skb(skb); info->dma = 0; info->skb = NULL; dp->ptr = 0; dp->macrx = 0; } mac->rx->next_to_clean += count; pasemi_mac_replenish_rx_ring(mac->netdev); spin_unlock(&mac->rx->lock); return count; } static int pasemi_mac_clean_tx(struct pasemi_mac *mac) { int i; struct pasemi_mac_buffer *info; struct pas_dma_xct_descr *dp; int start, count; int flags; spin_lock_irqsave(&mac->tx->lock, flags); start = mac->tx->next_to_clean; count = 0; for (i = start; i < mac->tx->next_to_use; i++) { dp = &TX_DESC(mac, i); if (!dp || (dp->mactx & XCT_MACTX_O)) break; count++; info = &TX_DESC_INFO(mac, i); pci_unmap_single(mac->dma_pdev, info->dma, info->skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(info->skb); info->skb = NULL; info->dma = 0; dp->mactx = 0; dp->ptr = 0; } mac->tx->next_to_clean += count; spin_unlock_irqrestore(&mac->tx->lock, flags); return count; } static irqreturn_t pasemi_mac_rx_intr(int irq, void *data) { struct net_device *dev = data; struct pasemi_mac *mac = netdev_priv(dev); unsigned int reg; if (!(*mac->rx_status & PAS_STATUS_INT)) return IRQ_NONE; netif_rx_schedule(dev); pci_write_config_dword(mac->iob_pdev, PAS_IOB_DMA_COM_TIMEOUTCFG, PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(0)); reg = PAS_IOB_DMA_RXCH_RESET_PINTC | PAS_IOB_DMA_RXCH_RESET_SINTC | PAS_IOB_DMA_RXCH_RESET_DINTC; if (*mac->rx_status & PAS_STATUS_TIMER)