1019 lines · 24.8 KB · C
RawDownload
/* * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t