Subject: [PATCH] [v3] PA Semi PWRficient Ethernet driver

From: [email-masked] (Olof Johansson)
Date: Tue, 30 Jan 2007 23:44:57 -0600

Driver for the PA Semi PWRficient on-chip Ethernet (1/10G)

Basic enablement, will be complemented with performance enhancements
over time. PHY support will be added as well.

Signed-off-by: Olof Johansson <[email-masked]>

---

Further improvements based on comments:

* Added remove function
* Checking interrupt status in handler
* Misc cleanups w.r.t. ring handing (INFO/DESCR/BUFF are gone)
* Added locking of the rings
* Using PCI DMA for all buffers

Misc other changes while I was touching the code:

* Setting the interrupt descriptor field to include interface number
* Added PCI vendor ID, it's been submitted to sf.net as well.
* Moved Kconfig entry to the 10GbE section

Index: merge/drivers/net/Kconfig
===================================================================
--- merge.orig/drivers/net/Kconfig
+++ merge/drivers/net/Kconfig
@@ -2488,6 +2488,13 @@ config NETXEN_NIC
 	help
 	  This enables the support for NetXen's Gigabit Ethernet card.
 
+config PASEMI_MAC
+	tristate "PA Semi 1/10Gbit MAC"
+	depends on PPC64 && PCI
+	help
+	  This driver supports the on-chip 1/10Gbit Ethernet controller on
+	  PA Semi's PWRficient line of chips.
+
 endmenu
 
 source "drivers/net/tokenring/Kconfig"
Index: merge/drivers/net/Makefile
===================================================================
--- merge.orig/drivers/net/Makefile
+++ merge/drivers/net/Makefile
@@ -196,6 +196,7 @@ obj-$(CONFIG_SMC91X) += smc91x.o
 obj-$(CONFIG_SMC911X) += smc911x.o
 obj-$(CONFIG_DM9000) += dm9000.o
 obj-$(CONFIG_FEC_8XX) += fec_8xx/
+obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o
 
 obj-$(CONFIG_MACB) += macb.o
 
Index: merge/drivers/net/pasemi_mac.c
===================================================================
--- /dev/null
+++ merge/drivers/net/pasemi_mac.c
@@ -0,0 +1,963 @@
+/*
+ * 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"
+
+#define INITIAL_RX_RING_SIZE 512
+#define INITIAL_TX_RING_SIZE 512
+
+#define BUF_SIZE 1646 /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
+
+#define PAS_DMA_MAX_IF     40
+#define PAS_DMA_MAX_RXCH   8
+#define PAS_DMA_MAX_TXCH   8
+
+/* 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_set_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->count = INITIAL_RX_RING_SIZE;
+
+	ring->desc_info = kzalloc(sizeof(struct pasemi_mac_buffer)*ring->count,
+				  GFP_KERNEL);
+
+	if (!ring->desc_info)
+		goto out_desc_info;
+
+	/* Allocate descriptors */
+	ring->desc = dma_alloc_coherent(&mac->dma_pdev->dev,
+					ring->count *
+					sizeof(struct pas_dma_xct_descr),
+					&ring->dma, GFP_KERNEL);
+
+	if (!ring->desc)
+		goto out_desc;
+
+	memset(ring->desc, 0, ring->count * sizeof(struct pas_dma_xct_descr));
+
+	ring->buffers = dma_alloc_coherent(&mac->dma_pdev->dev,
+					   ring->count * sizeof(u64),
+					   &ring->buf_dma, GFP_KERNEL);
+	if (!ring->buffers)
+		goto out_buffers;
+
+	memset(ring->buffers, 0, ring->count * 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(INITIAL_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(INITIAL_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,
+			  mac->rx->count * 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->count = INITIAL_TX_RING_SIZE;
+
+	ring->desc_info = kzalloc(sizeof(struct pasemi_mac_buffer)*ring->count,
+				  GFP_KERNEL);
+	if (!ring->desc_info)
+		goto out_desc_info;
+
+	/* Allocate descriptors */
+	ring->desc = dma_alloc_coherent(&mac->dma_pdev->dev,
+					ring->count *
+					sizeof(struct pas_dma_xct_descr),
+					&ring->dma, GFP_KERNEL);
+	if (!ring->desc)
+		goto out_desc;
+
+	memset(ring->desc, 0, ring->count * 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(INITIAL_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 noinline 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 < mac->tx->count; i++) {
+		info = &mac->tx->desc_info[i % mac->tx->count];
+		dp = &mac->tx->desc[i % mac->tx->count];
+		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 = 0;
+			dp->mactx = 0;
+			dp->ptr = 0;
+		}
+	}
+
+	dma_free_coherent(&mac->dma_pdev->dev,
+			  mac->tx->count * sizeof(struct pas_dma_xct_descr),
+			  mac->tx->desc, mac->tx->dma);
+
+	kfree(mac->tx->desc_info);
+	kfree(mac->tx);
+	mac->tx = NULL;
+}
+
+static noinline 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 < mac->rx->count; i++) {
+		info = &mac->rx->desc_info[i % mac->rx->count];
+		dp = &mac->rx->desc[i % mac->rx->count];
+		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 = 0;
+			dp->macrx = 0;
+			dp->ptr = 0;
+		}
+	}
+
+	dma_free_coherent(&mac->dma_pdev->dev,
+			  mac->rx->count * sizeof(struct pas_dma_xct_descr),
+			  mac->rx->desc, mac->rx->dma);
+
+	dma_free_coherent(&mac->dma_pdev->dev, mac->rx->count * 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;
+	int count;
+
+	count = ((mac->rx->next_to_clean & ~7) + mac->rx->count -
+		 mac->rx->next_to_fill) % mac->rx->count;
+
+	if (unlikely(mac->rx->next_to_clean == 0 && mac->rx->next_to_fill == 0))
+		count = mac->rx->count - 8;
+
+	/* Limit so we don't go into the last cache line */
+	count -= 8;
+
+	if (count <= 0)
+		return;
+
+	for (i = start; i < start + count; i++) {
+		struct pasemi_mac_buffer *info = &mac->rx->desc_info[i % mac->rx->count];
+		u64 *buff = &mac->rx->buffers[i % mac->rx->count];
+		struct sk_buff *skb;
+		dma_addr_t dma;
+
+		skb = dev_alloc_skb(BUF_SIZE);
+
+		if (!skb)
+			return;
+
+		skb->dev = dev;
+
+		dma = pci_map_single(mac->dma_pdev, skb->data, skb->len,
+				     PCI_DMA_FROMDEVICE);
+		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 + mac->rx->count) && 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 = &mac->rx->desc[i % mac->rx->count];
+
+		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 + mac->rx->count); j++) {
+			info = &mac->rx->desc_info[j % mac->rx->count];
+			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);
+
+		dp->ptr = 0;
+		dp->macrx = 0;
+		info->dma = 0;
+		info->skb = 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;
+
+	if (!spin_trylock_irqsave(&mac->tx->lock, flags))
+		return 0;
+
+	start = mac->tx->next_to_clean;
+	count = 0;
+
+	for (i = start; i < mac->tx->next_to_use; i++) {
+		dp = &mac->tx->desc[i % mac->tx->count];
+		if (!dp || (dp->mactx & XCT_MACTX_O))
+			break;
+
+		count++;
+
+		info = &mac->tx->desc_info[i % mac->tx->count];
+
+		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)
+		reg |= PAS_IOB_DMA_RXCH_RESET_TINTC;
+
+	pci_write_config_dword(mac->iob_pdev,
+			       PAS_IOB_DMA_RXCH_RESET(mac->dma_rxch), reg);
+
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pasemi_mac_tx_intr(int irq, void *data)
+{
+	struct net_device *dev = data;
+	struct pasemi_mac *mac = netdev_priv(dev);
+	unsigned int reg;
+	int was_full;
+
+	was_full = mac->tx->next_to_clean + mac->tx->count == mac->tx->next_to_use;
+
+	if (!(*mac->tx_status & PAS_STATUS_INT))
+		return IRQ_NONE;
+
+	pasemi_mac_clean_tx(mac);
+
+	reg = PAS_IOB_DMA_TXCH_RESET_PINTC | PAS_IOB_DMA_TXCH_RESET_SINTC;
+	if (*mac->tx_status & PAS_STATUS_TIMER)
+		reg |= PAS_IOB_DMA_TXCH_RESET_TINTC;
+
+	pci_write_config_dword(mac->iob_pdev, PAS_IOB_DMA_TXCH_RESET(mac->dma_txch),
+			       reg);
+
+	if (was_full)
+		netif_wake_queue(dev);
+
+	return IRQ_HANDLED;
+}
+
+static int pasemi_mac_open(struct net_device *dev)
+{
+	struct pasemi_mac *mac = netdev_priv(dev);
+	unsigned int flags;
+	int ret;
+
+	/* enable rx section */
+	pci_write_config_dword(mac->dma_pdev, PAS_DMA_COM_RXCMD,
+			       PAS_DMA_COM_RXCMD_EN);
+
+	/* enable tx section */
+	pci_write_config_dword(mac->dma_pdev, PAS_DMA_COM_TXCMD,
+			       PAS_DMA_COM_TXCMD_EN);
+
+	flags = PAS_MAC_CFG_TXP_FCE | PAS_MAC_CFG_TXP_FPC(3) |
+		PAS_MAC_CFG_TXP_SL(3) | PAS_MAC_CFG_TXP_COB(0xf) |
+		PAS_MAC_CFG_TXP_TIFT(8) | PAS_MAC_CFG_TXP_TIFG(12);
+
+	pci_write_config_dword(mac->pdev, PAS_MAC_CFG_TXP, flags);
+
+	flags = PAS_MAC_CFG_PCFG_S1 | PAS_MAC_CFG_PCFG_PE |
+		PAS_MAC_CFG_PCFG_PR | PAS_MAC_CFG_PCFG_CE;
+
+	flags |= PAS_MAC_CFG_PCFG_TSR_1G | PAS_MAC_CFG_PCFG_SPD_1G;
+
+	pci_write_config_dword(mac->iob_pdev, PAS_IOB_DMA_RXCH_CFG(mac->dma_rxch),
+			       PAS_IOB_DMA_RXCH_CFG_CNTTH(30));
+
+	pci_write_config_dword(mac->iob_pdev, PAS_IOB_DMA_COM_TIMEOUTCFG,
+			       PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(1000000));
+
+	pci_write_config_dword(mac->pdev, PAS_MAC_CFG_PCFG, flags);
+
+	ret = pasemi_mac_setup_rx_resources(dev);
+	if (ret)
+		goto out_rx_resources;
+
+	ret = pasemi_mac_setup_tx_resources(dev);
+	if (ret)
+		goto out_tx_resources;
+
+	pci_write_config_dword(mac->pdev, PAS_MAC_IPC_CHNL,
+			       PAS_MAC_IPC_CHNL_DCHNO(mac->dma_rxch) |
+			       PAS_MAC_IPC_CHNL_BCH(mac->dma_rxch));
+
+	/* enable rx if */
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
+			       PAS_DMA_RXINT_RCMDSTA_EN);
+
+	/* enable rx channel */
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch),
+			       PAS_DMA_RXCHAN_CCMDSTA_EN |
+			       PAS_DMA_RXCHAN_CCMDSTA_DU);
+
+	/* enable tx channel */
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch),
+			       PAS_DMA_TXCHAN_TCMDSTA_EN);
+
+	pasemi_mac_replenish_rx_ring(dev);
+
+	netif_start_queue(dev);
+	netif_poll_enable(dev);
+
+	ret = request_irq(mac->dma_pdev->irq + mac->dma_txch,
+			  &pasemi_mac_tx_intr, IRQF_DISABLED,
+			  mac->tx->irq_name, dev);
+	if (ret) {
+		dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n",
+		       mac->dma_pdev->irq + mac->dma_txch, ret);
+		goto out_tx_int;
+	}
+
+	ret = request_irq(mac->dma_pdev->irq + 20 + mac->dma_rxch,
+			  &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",
+		       mac->dma_pdev->irq + 20 + mac->dma_rxch, ret);
+		goto out_rx_int;
+	}
+
+	return 0;
+
+out_rx_int:
+	free_irq(mac->dma_pdev->irq + mac->dma_txch, dev);
+out_tx_int:
+	netif_poll_disable(dev);
+	netif_stop_queue(dev);
+	pasemi_mac_free_tx_resources(dev);
+out_tx_resources:
+	pasemi_mac_free_rx_resources(dev);
+out_rx_resources:
+
+	return ret;
+}
+
+static int pasemi_mac_close(struct net_device *dev)
+{
+	struct pasemi_mac *mac = netdev_priv(dev);
+	unsigned int stat;
+
+	netif_stop_queue(dev);
+
+	/* Clean out any pending buffers */
+	pasemi_mac_clean_tx(mac);
+	pasemi_mac_clean_rx(mac, mac->rx->count);
+
+	/* Disable interface */
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch),
+			       PAS_DMA_TXCHAN_TCMDSTA_ST);
+	pci_write_config_dword(mac->dma_pdev,
+		      PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
+		      PAS_DMA_RXINT_RCMDSTA_ST);
+	pci_write_config_dword(mac->dma_pdev,
+		      PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch),
+		      PAS_DMA_RXCHAN_CCMDSTA_ST);
+
+	do {
+		pci_read_config_dword(mac->dma_pdev,
+				      PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch),
+				      &stat);
+	} while (stat & PAS_DMA_TXCHAN_TCMDSTA_ACT);
+
+	do {
+		pci_read_config_dword(mac->dma_pdev,
+				      PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch),
+				      &stat);
+	} while (stat & PAS_DMA_RXCHAN_CCMDSTA_ACT);
+
+	do {
+		pci_read_config_dword(mac->dma_pdev,
+				      PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
+				      &stat);
+	} while (stat & PAS_DMA_RXINT_RCMDSTA_ACT);
+
+	/* Then, disable the channel. This must be done separately from
+	 * stopping, since you can't disable when active.
+	 */
+
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_TXCHAN_TCMDSTA(mac->dma_txch), 0);
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_RXCHAN_CCMDSTA(mac->dma_rxch), 0);
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0);
+
+	synchronize_irq(mac->dma_pdev->irq + mac->dma_txch);
+	synchronize_irq(mac->dma_pdev->irq + 20 + mac->dma_rxch);
+
+	free_irq(mac->dma_pdev->irq + mac->dma_txch, dev);
+	free_irq(mac->dma_pdev->irq + 20 + mac->dma_rxch, 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;
+	struct pasemi_mac_buffer *info;
+	struct pas_dma_xct_descr *dp;
+	u64 flags;
+	dma_addr_t map;
+
+	txring = mac->tx;
+
+	if (txring->next_to_clean + txring->count == txring->next_to_use) {
+		pasemi_mac_clean_tx(mac);
+
+		if (txring->next_to_clean + txring->count == txring->next_to_use) {
+			/* Still no room -- stop the queue and wait for tx
+			 * intr when there's room.
+			 */
+			netif_stop_queue(dev);
+			return NETDEV_TX_BUSY;
+		}
+	}
+
+	mac->stats.tx_packets++;
+	mac->stats.tx_bytes += skb->len;
+
+	flags = XCT_MACTX_O | XCT_MACTX_ST |
+		XCT_MACTX_SS | XCT_MACTX_CRC_PAD;
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		switch (skb->nh.iph->protocol) {
+		case IPPROTO_TCP:
+			flags |= XCT_MACTX_CSUM_TCP;
+			flags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2);
+			flags |= XCT_MACTX_IPO(skb->nh.raw - skb->data);
+			break;
+		case IPPROTO_UDP:
+			flags |= XCT_MACTX_CSUM_UDP;
+			flags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2);
+			flags |= XCT_MACTX_IPO(skb->nh.raw - skb->data);
+			break;
+		}
+	}
+
+	map = pci_map_single(mac->dma_pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+
+	dp = &txring->desc[txring->next_to_use % txring->count];
+	info = &txring->desc_info[txring->next_to_use % txring->count];
+
+	dp->mactx = flags | XCT_MACTX_LLEN(skb->len);
+	dp->ptr = XCT_PTR_LEN(skb->len) | XCT_PTR_ADDR(map);
+	info->dma = map;
+	info->skb = skb;
+	/* XXXOJN Deal with fragmented packets when larger MTU is supported */
+
+	txring->next_to_use++;
+
+	pci_write_config_dword(mac->dma_pdev,
+			       PAS_DMA_TXCHAN_INCR(mac->dma_txch), 1);
+
+	return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *pasemi_mac_get_stats(struct net_device *dev)
+{
+	struct pasemi_mac *mac = netdev_priv(dev);
+
+	return &mac->stats;
+}
+
+static void pasemi_mac_set_rx_mode(struct net_device *dev)
+{
+	struct pasemi_mac *mac = netdev_priv(dev);
+	unsigned int flags;
+
+	pci_read_config_dword(mac->pdev, PAS_MAC_CFG_PCFG, &flags);
+
+	/* Set promiscuous */
+	if (dev->flags & IFF_PROMISC)
+		flags |= PAS_MAC_CFG_PCFG_PR;
+	else
+		flags &= ~PAS_MAC_CFG_PCFG_PR;
+
+	pci_write_config_dword(mac->pdev, PAS_MAC_CFG_PCFG, flags);
+}
+
+
+static int pasemi_mac_poll(struct net_device *dev, int *budget)
+{
+	int pkts, limit = min(*budget, dev->quota);
+	struct pasemi_mac *mac = netdev_priv(dev);
+
+	pkts = pasemi_mac_clean_rx(mac, limit);
+
+	if (pkts < limit) {
+		/* all done, no more packets present */
+		netif_rx_complete(dev);
+
+		/* re-enable receive interrupts */
+		pci_write_config_dword(mac->iob_pdev, PAS_IOB_DMA_COM_TIMEOUTCFG,
+				       PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(1000000));
+		return 0;
+	} else {
+		/* used up our quantum, so reschedule */
+		dev->quota -= pkts;
+		*budget -= pkts;
+		return 1;
+	}
+}
+
+static int __devinit
+pasemi_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;
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "pasemi_mac: Could not enable device.\n");
+		return err;
+	}
+	dev = alloc_etherdev(sizeof(struct pasemi_mac));
+	if (dev == NULL) {
+		dev_err(&pdev->dev,
+			"pasemi_mac: Could not allocate ethernet device.\n");
+		return -ENOMEM;
+	}
+
+	SET_MODULE_OWNER(dev);
+	pci_set_drvdata(pdev, dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	mac = netdev_priv(dev);
+
+	mac->pdev = pdev;
+	mac->netdev = dev;
+	mac->dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL);
+
+	if (!mac->dma_pdev) {
+		dev_err(&pdev->dev, "Can't find DMA Controller\n");
+		free_netdev(dev);
+		return -ENODEV;
+	}
+
+	mac->iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL);
+
+	if (!mac->iob_pdev) {
+		dev_err(&pdev->dev, "Can't find I/O Bridge\n");
+		free_netdev(dev);
+		return -ENODEV;
+	}
+
+	/* 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_set_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->get_stats = pasemi_mac_get_stats;
+	dev->set_multicast_list = pasemi_mac_set_rx_mode;
+	dev->weight = 64;
+	dev->poll = pasemi_mac_poll;
+	dev->features = NETIF_F_HW_CSUM;
+
+	/* The dma status structure is located in the I/O bridge, and
+	 * is cache coherent.
+	 */
+	if (!dma_status)
+		/* XXXOJN This should come from the device tree */
+		dma_status = __ioremap(0xfd800000, 0x1000, 0);
+
+	mac->rx_status = &dma_status->rx_sta[mac->dma_rxch];
+	mac->tx_status = &dma_status->tx_sta[mac->dma_txch];
+
+	err = register_netdev(dev);
+
+	if (err) {
+		dev_err(&mac->pdev->dev, "register_netdev failed with error %d\n",
+			err);
+		goto out;
+	} else
+		printk(KERN_INFO "%s: PA Semi %s: intf %d, txch %d, rxch %d, "
+		       "hw addr %02x:%02x:%02x:%02x:%02x:%02x\n",
+		       dev->name, mac->type == MAC_TYPE_GMAC ? "GMAC" : "XAUI",
+		       mac->dma_if, mac->dma_txch, mac->dma_rxch,
+		       dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+		       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+	return err;
+
+out:
+	dev_err(&mac->pdev->dev, "pasemi_mac: init failed\n");
+
+	pci_disable_device(pdev);
+	pci_dev_put(mac->dma_pdev);
+	pci_dev_put(mac->iob_pdev);
+	free_netdev(dev);
+	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);
+
+	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) },
+	{ 0 }
+};
+
+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_LICENSE("GPL");
+MODULE_AUTHOR ("Olof Johansson <[email-masked]>");
+MODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver");
+
+module_init(pasemi_mac_init_module);
+module_exit(pasemi_mac_cleanup_module);
Index: merge/drivers/net/pasemi_mac.h
===================================================================
--- /dev/null
+++ merge/drivers/net/pasemi_mac.h
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2006 PA Semi, Inc
+ *
+ * Driver for the PA6T-1682M onchip 1G/10G Ethernet MACs, soft state and
+ * hardware register layouts.
+ *
+ * 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
+ */
+
+#ifndef PASEMI_MAC_H
+#define PASEMI_MAC_H
+
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+
+struct pasemi_mac_txring {
+	spinlock_t	 lock;
+	struct pas_dma_xct_descr	*desc;
+	dma_addr_t	 dma;
+	unsigned int	 size;
+	unsigned int	 count;
+	unsigned int	 next_to_use;
+	unsigned int	 next_to_clean;
+	unsigned short	 last_count;
+	struct pasemi_mac_buffer *desc_info;
+	char		 irq_name[10];  /* "eth%d tx" */
+};
+
+struct pasemi_mac_rxring {
+	spinlock_t	 lock;
+	struct pas_dma_xct_descr	*desc;	/* RX channel descriptor ring */
+	dma_addr_t	 dma;
+	u64		*buffers;	/* RX interface buffer ring */
+	dma_addr_t	 buf_dma;
+	unsigned int	 size;
+	unsigned int	 count;
+	unsigned int	 next_to_fill;
+	unsigned int	 next_to_clean;
+	unsigned short	 last_count;
+	struct pasemi_mac_buffer *desc_info;
+	char		 irq_name[10];  /* "eth%d rx" */
+};
+
+/* Number of unused descriptors, considering ring wraparounds */
+#define PASEMI_MAC_DESC_UNUSED(ring) ((((ring)->next_to_clean >		\
+					(ring)->next_to_use) ?		\
+					  0 :				\
+					  (ring)->count) +		\
+					  (ring)->next_to_clean -	\
+					  (ring)->next_to_use - 1)
+
+struct pasemi_mac {
+	struct net_device *netdev;
+	struct pci_dev *pdev;
+	struct pci_dev *dma_pdev;
+	struct pci_dev *iob_pdev;
+	struct net_device_stats stats;
+
+	/* Pointer to the cacheable per-channel status registers */
+	u64	*rx_status;
+	u64	*tx_status;
+
+	u8		type;
+#define MAC_TYPE_GMAC	1
+#define MAC_TYPE_XAUI	2
+	u32	dma_txch;
+	u32	dma_if;
+	u32	dma_rxch;
+
+	u8		mac_addr[6];
+
+	struct timer_list	rxtimer;
+
+	struct pasemi_mac_txring *tx;
+	struct pasemi_mac_rxring *rx;
+};
+
+struct pasemi_mac_buffer {
+	struct sk_buff *skb;
+	dma_addr_t	dma;
+};
+
+
+
+#define PAS_MAC_CFG_PCFG		0x80
+#define    PAS_MAC_CFG_PCFG_PE		0x80000000
+#define    PAS_MAC_CFG_PCFG_CE		0x40000000
+#define    PAS_MAC_CFG_PCFG_BU		0x20000000
+#define    PAS_MAC_CFG_PCFG_TT		0x10000000
+#define    PAS_MAC_CFG_PCFG_TSR_M	0x0c000000
+#define    PAS_MAC_CFG_PCFG_TSR_10M	0x00000000
+#define    PAS_MAC_CFG_PCFG_TSR_100M	0x04000000
+#define    PAS_MAC_CFG_PCFG_TSR_1G	0x08000000
+#define    PAS_MAC_CFG_PCFG_TSR_10G	0x0c000000
+#define    PAS_MAC_CFG_PCFG_T24		0x02000000
+#define    PAS_MAC_CFG_PCFG_PR		0x01000000
+#define    PAS_MAC_CFG_PCFG_CRO_M	0x00ff0000
+#define    PAS_MAC_CFG_PCFG_CRO_S	16
+#define    PAS_MAC_CFG_PCFG_IPO_M	0x0000ff00
+#define    PAS_MAC_CFG_PCFG_IPO_S	8
+#define    PAS_MAC_CFG_PCFG_S1		0x00000080
+#define    PAS_MAC_CFG_PCFG_IO_M	0x00000060
+#define    PAS_MAC_CFG_PCFG_IO_MAC	0x00000000
+#define    PAS_MAC_CFG_PCFG_IO_OFF	0x00000020
+#define    PAS_MAC_CFG_PCFG_IO_IND_ETH	0x00000040
+#define    PAS_MAC_CFG_PCFG_IO_IND_IP	0x00000060
+#define    PAS_MAC_CFG_PCFG_LP		0x00000010
+#define    PAS_MAC_CFG_PCFG_TS		0x00000008
+#define    PAS_MAC_CFG_PCFG_HD		0x00000004
+#define    PAS_MAC_CFG_PCFG_SPD_M	0x00000003
+#define    PAS_MAC_CFG_PCFG_SPD_10M	0x00000000
+#define    PAS_MAC_CFG_PCFG_SPD_100M	0x00000001
+#define    PAS_MAC_CFG_PCFG_SPD_1G	0x00000002
+#define    PAS_MAC_CFG_PCFG_SPD_10G	0x00000003
+#define PAS_MAC_CFG_TXP			0x98
+#define    PAS_MAC_CFG_TXP_FCF		0x01000000
+#define    PAS_MAC_CFG_TXP_FCE		0x00800000
+#define    PAS_MAC_CFG_TXP_FC		0x00400000
+#define    PAS_MAC_CFG_TXP_FPC_M	0x00300000
+#define    PAS_MAC_CFG_TXP_FPC_S	20
+#define    PAS_MAC_CFG_TXP_FPC(x)	(((x) << PAS_MAC_CFG_TXP_FPC_S) & PAS_MAC_CFG_TXP_FPC_M)
+#define    PAS_MAC_CFG_TXP_RT		0x00080000
+#define    PAS_MAC_CFG_TXP_BL		0x00040000
+#define    PAS_MAC_CFG_TXP_SL_M		0x00030000
+#define    PAS_MAC_CFG_TXP_SL_S		16
+#define    PAS_MAC_CFG_TXP_SL(x)	(((x) << PAS_MAC_CFG_TXP_SL_S) & PAS_MAC_CFG_TXP_SL_M)
+#define    PAS_MAC_CFG_TXP_COB_M	0x0000f000
+#define    PAS_MAC_CFG_TXP_COB_S	12
+#define    PAS_MAC_CFG_TXP_COB(x)	(((x) << PAS_MAC_CFG_TXP_COB_S) & PAS_MAC_CFG_TXP_COB_M)
+#define    PAS_MAC_CFG_TXP_TIFT_M	0x00000f00
+#define    PAS_MAC_CFG_TXP_TIFT_S	8
+#define    PAS_MAC_CFG_TXP_TIFT(x)	(((x) << PAS_MAC_CFG_TXP_TIFT_S) & PAS_MAC_CFG_TXP_TIFT_M)
+#define    PAS_MAC_CFG_TXP_TIFG_M	0x000000ff
+#define    PAS_MAC_CFG_TXP_TIFG_S	0
+#define    PAS_MAC_CFG_TXP_TIFG(x)	(((x) << PAS_MAC_CFG_TXP_TIFG_S) & PAS_MAC_CFG_TXP_TIFG_M)
+
+#define PAS_MAC_IPC_CHNL		0x208
+#define    PAS_MAC_IPC_CHNL_DCHNO_M	0x003f0000
+#define    PAS_MAC_IPC_CHNL_DCHNO_S	16
+#define    PAS_MAC_IPC_CHNL_DCHNO(x)	(((x) << PAS_MAC_IPC_CHNL_DCHNO_S) & \
+					 PAS_MAC_IPC_CHNL_DCHNO_M)
+#define    PAS_MAC_IPC_CHNL_BCH_M	0x0000003f
+#define    PAS_MAC_IPC_CHNL_BCH_S	0
+#define    PAS_MAC_IPC_CHNL_BCH(x)	(((x) << PAS_MAC_IPC_CHNL_BCH_S) & \
+					 PAS_MAC_IPC_CHNL_BCH_M)
+
+/* All these registers live in the PCI configuration space for the DMA PCI
+ * device. Use the normal PCI config access functions for them.
+ */
+
+#define PAS_DMA_COM_TXCMD	0x100	/* Transmit Command Register  */
+#define    PAS_DMA_COM_TXCMD_EN		0x00000001 /* enable */
+#define PAS_DMA_COM_TXSTA	0x104	/* Transmit Status Register   */
+#define    PAS_DMA_COM_TXSTA_ACT	0x00000001 /* active */
+#define PAS_DMA_COM_RXCMD	0x108	/* Receive Command Register   */
+#define    PAS_DMA_COM_RXCMD_EN		0x00000001 /* enable */
+#define PAS_DMA_COM_RXSTA	0x10c	/* Receive Status Register    */
+#define    PAS_DMA_COM_RXSTA_ACT	0x00000001 /* active */
+
+
+#define _PAS_DMA_RXINT_STRIDE		0x20
+#define PAS_DMA_RXINT_RCMDSTA(i)	(0x200+(i)*_PAS_DMA_RXINT_STRIDE)
+#define    PAS_DMA_RXINT_RCMDSTA_EN	0x00000001
+#define    PAS_DMA_RXINT_RCMDSTA_ST	0x00000002
+#define    PAS_DMA_RXINT_RCMDSTA_OO	0x00000100
+#define    PAS_DMA_RXINT_RCMDSTA_BP	0x00000200
+#define    PAS_DMA_RXINT_RCMDSTA_DR	0x00000400
+#define    PAS_DMA_RXINT_RCMDSTA_BT	0x00000800
+#define    PAS_DMA_RXINT_RCMDSTA_TB	0x00001000
+#define    PAS_DMA_RXINT_RCMDSTA_ACT	0x00010000
+#define    PAS_DMA_RXINT_RCMDSTA_DROPS_M	0xfffe0000
+#define    PAS_DMA_RXINT_RCMDSTA_DROPS_S	17
+#define PAS_DMA_RXINT_INCR(i)		(0x210+(i)*_PAS_DMA_RXINT_STRIDE)
+#define    PAS_DMA_RXINT_INCR_INCR_M	0x0000ffff
+#define    PAS_DMA_RXINT_INCR_INCR_S	0
+#define    PAS_DMA_RXINT_INCR_INCR(x)	((x) & 0x0000ffff)
+#define PAS_DMA_RXINT_BASEL(i)		(0x218+(i)*_PAS_DMA_RXINT_STRIDE)
+#define    PAS_DMA_RXINT_BASEL_BRBL(x)	((x) & ~0x3f)
+#define PAS_DMA_RXINT_BASEU(i)		(0x21c+(i)*_PAS_DMA_RXINT_STRIDE)
+#define    PAS_DMA_RXINT_BASEU_BRBH(x)	((x) & 0xfff)
+#define    PAS_DMA_RXINT_BASEU_SIZ_M	0x3fff0000	/* # of cache lines worth of buffer ring */
+#define    PAS_DMA_RXINT_BASEU_SIZ_S	16		/* 0 = 16K */
+#define    PAS_DMA_RXINT_BASEU_SIZ(x)	(((x) << PAS_DMA_RXINT_BASEU_SIZ_S) & \
+					 PAS_DMA_RXINT_BASEU_SIZ_M)
+
+
+#define _PAS_DMA_TXCHAN_STRIDE	0x20    /* Size per channel		*/
+#define _PAS_DMA_TXCHAN_TCMDSTA	0x300	/* Command / Status		*/
+#define _PAS_DMA_TXCHAN_CFG	0x304	/* Configuration		*/
+#define _PAS_DMA_TXCHAN_DSCRBU	0x308	/* Descriptor BU Allocation	*/
+#define _PAS_DMA_TXCHAN_INCR	0x310	/* Descriptor increment		*/
+#define _PAS_DMA_TXCHAN_CNT	0x314	/* Descriptor count/offset	*/
+#define _PAS_DMA_TXCHAN_BASEL	0x318	/* Descriptor ring base (low)	*/
+#define _PAS_DMA_TXCHAN_BASEU	0x31c	/*			(high)	*/
+#define PAS_DMA_TXCHAN_TCMDSTA(c) (0x300+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define    PAS_DMA_TXCHAN_TCMDSTA_EN	0x00000001	/* Enabled */
+#define    PAS_DMA_TXCHAN_TCMDSTA_ST	0x00000002	/* Stop interface */
+#define    PAS_DMA_TXCHAN_TCMDSTA_ACT	0x00010000	/* Active */
+#define PAS_DMA_TXCHAN_CFG(c)     (0x304+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define    PAS_DMA_TXCHAN_CFG_TY_IFACE	0x00000000	/* Type = interface */
+#define    PAS_DMA_TXCHAN_CFG_TATTR_M	0x0000003c
+#define    PAS_DMA_TXCHAN_CFG_TATTR_S	2
+#define    PAS_DMA_TXCHAN_CFG_TATTR(x)	(((x) << PAS_DMA_TXCHAN_CFG_TATTR_S) & \
+					 PAS_DMA_TXCHAN_CFG_TATTR_M)
+#define    PAS_DMA_TXCHAN_CFG_WT_M	0x000001c0
+#define    PAS_DMA_TXCHAN_CFG_WT_S	6
+#define    PAS_DMA_TXCHAN_CFG_WT(x)	(((x) << PAS_DMA_TXCHAN_CFG_WT_S) & \
+					 PAS_DMA_TXCHAN_CFG_WT_M)
+#define    PAS_DMA_TXCHAN_CFG_CF	0x00001000	/* Clean first line */
+#define    PAS_DMA_TXCHAN_CFG_CL	0x00002000	/* Clean last line */
+#define    PAS_DMA_TXCHAN_CFG_UP	0x00004000	/* update tx descr when sent */
+#define PAS_DMA_TXCHAN_INCR(c)    (0x310+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define PAS_DMA_TXCHAN_BASEL(c)   (0x318+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define    PAS_DMA_TXCHAN_BASEL_BRBL_M	0xffffffc0
+#define    PAS_DMA_TXCHAN_BASEL_BRBL_S	0
+#define    PAS_DMA_TXCHAN_BASEL_BRBL(x)	(((x) << PAS_DMA_TXCHAN_BASEL_BRBL_S) & \
+					 PAS_DMA_TXCHAN_BASEL_BRBL_M)
+#define PAS_DMA_TXCHAN_BASEU(c)   (0x31c+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define    PAS_DMA_TXCHAN_BASEU_BRBH_M	0x00000fff
+#define    PAS_DMA_TXCHAN_BASEU_BRBH_S	0
+#define    PAS_DMA_TXCHAN_BASEU_BRBH(x)	(((x) << PAS_DMA_TXCHAN_BASEU_BRBH_S) & \
+					 PAS_DMA_TXCHAN_BASEU_BRBH_M)
+/* # of cache lines worth of buffer ring */
+#define    PAS_DMA_TXCHAN_BASEU_SIZ_M	0x3fff0000
+#define    PAS_DMA_TXCHAN_BASEU_SIZ_S	16		/* 0 = 16K */
+#define    PAS_DMA_TXCHAN_BASEU_SIZ(x)	(((x) << PAS_DMA_TXCHAN_BASEU_SIZ_S) & \
+					 PAS_DMA_TXCHAN_BASEU_SIZ_M)
+
+#define _PAS_DMA_RXCHAN_STRIDE	0x20    /* Size per channel		*/
+#define _PAS_DMA_RXCHAN_CCMDSTA	0x800	/* Command / Status		*/
+#define _PAS_DMA_RXCHAN_CFG	0x804	/* Configuration		*/
+#define _PAS_DMA_RXCHAN_INCR	0x810	/* Descriptor increment		*/
+#define _PAS_DMA_RXCHAN_CNT	0x814	/* Descriptor count/offset	*/
+#define _PAS_DMA_RXCHAN_BASEL	0x818	/* Descriptor ring base (low)	*/
+#define _PAS_DMA_RXCHAN_BASEU	0x81c	/*			(high)	*/
+#define PAS_DMA_RXCHAN_CCMDSTA(c) (0x800+(c)*_PAS_DMA_RXCHAN_STRIDE)
+#define    PAS_DMA_RXCHAN_CCMDSTA_EN	0x00000001	/* Enabled */
+#define    PAS_DMA_RXCHAN_CCMDSTA_ST	0x00000002	/* Stop interface */
+#define    PAS_DMA_RXCHAN_CCMDSTA_ACT	0x00010000	/* Active */
+#define    PAS_DMA_RXCHAN_CCMDSTA_DU	0x00020000
+#define PAS_DMA_RXCHAN_CFG(c)     (0x804+(c)*_PAS_DMA_RXCHAN_STRIDE)
+#define    PAS_DMA_RXCHAN_CFG_HBU_M	0x00000380
+#define    PAS_DMA_RXCHAN_CFG_HBU_S	7
+#define    PAS_DMA_RXCHAN_CFG_HBU(x)	(((x) << PAS_DMA_RXCHAN_CFG_HBU_S) & \
+					 PAS_DMA_RXCHAN_CFG_HBU_M)
+#define PAS_DMA_RXCHAN_INCR(c)    (0x810+(c)*_PAS_DMA_RXCHAN_STRIDE)
+#define PAS_DMA_RXCHAN_BASEL(c)   (0x818+(c)*_PAS_DMA_RXCHAN_STRIDE)
+#define    PAS_DMA_RXCHAN_BASEL_BRBL_M	0xffffffc0
+#define    PAS_DMA_RXCHAN_BASEL_BRBL_S	0
+#define    PAS_DMA_RXCHAN_BASEL_BRBL(x)	(((x) << PAS_DMA_RXCHAN_BASEL_BRBL_S) & \
+					 PAS_DMA_RXCHAN_BASEL_BRBL_M)
+#define PAS_DMA_RXCHAN_BASEU(c)   (0x81c+(c)*_PAS_DMA_RXCHAN_STRIDE)
+#define    PAS_DMA_RXCHAN_BASEU_BRBH_M	0x00000fff
+#define    PAS_DMA_RXCHAN_BASEU_BRBH_S	0
+#define    PAS_DMA_RXCHAN_BASEU_BRBH(x)	(((x) << PAS_DMA_RXCHAN_BASEU_BRBH_S) & \
+					 PAS_DMA_RXCHAN_BASEU_BRBH_M)
+/* # of cache lines worth of buffer ring */
+#define    PAS_DMA_RXCHAN_BASEU_SIZ_M	0x3fff0000
+#define    PAS_DMA_RXCHAN_BASEU_SIZ_S	16		/* 0 = 16K */
+#define    PAS_DMA_RXCHAN_BASEU_SIZ(x)	(((x) << PAS_DMA_RXCHAN_BASEU_SIZ_S) & \
+					 PAS_DMA_RXCHAN_BASEU_SIZ_M)
+
+/* status register layout in IOB region, at 0xfb800000 */
+struct pasdma_status {
+	u64 rx_sta[64];
+	u64 tx_sta[20];
+};
+
+#define    PAS_STATUS_PCNT_M		0x000000000000ffff
+#define    PAS_STATUS_PCNT_S		0
+#define    PAS_STATUS_DCNT_M		0x00000000ffff0000
+#define    PAS_STATUS_DCNT_S		16
+#define    PAS_STATUS_BPCNT_M		0x0000ffff00000000
+#define    PAS_STATUS_BPCNT_S		32
+#define    PAS_STATUS_TIMER		0x1000000000000000
+#define    PAS_STATUS_ERROR		0x2000000000000000
+#define    PAS_STATUS_SOFT		0x4000000000000000
+#define    PAS_STATUS_INT		0x8000000000000000
+
+#define PAS_IOB_DMA_RXCH_CFG(i)		(0x1100 + (i)*4)
+#define    PAS_IOB_DMA_RXCH_CFG_CNTTH_M		0x00000fff
+#define    PAS_IOB_DMA_RXCH_CFG_CNTTH_S		0
+#define    PAS_IOB_DMA_RXCH_CFG_CNTTH(x)	(((x) << PAS_IOB_DMA_RXCH_CFG_CNTTH_S) & \
+						 PAS_IOB_DMA_RXCH_CFG_CNTTH_M)
+#define PAS_IOB_DMA_TXCH_CFG(i)		(0x1200 + (i)*4)
+#define    PAS_IOB_DMA_TXCH_CFG_CNTTH_M		0x00000fff
+#define    PAS_IOB_DMA_TXCH_CFG_CNTTH_S		0
+#define    PAS_IOB_DMA_TXCH_CFG_CNTTH(x)	(((x) << PAS_IOB_DMA_TXCH_CFG_CNTTH_S) & \
+						 PAS_IOB_DMA_TXCH_CFG_CNTTH_M)
+#define PAS_IOB_DMA_RXCH_STAT(i)	(0x1300 + (i)*4)
+#define    PAS_IOB_DMA_RXCH_STAT_INTGEN	0x00001000
+#define    PAS_IOB_DMA_RXCH_STAT_CNTDEL_M	0x00000fff
+#define    PAS_IOB_DMA_RXCH_STAT_CNTDEL_S	0
+#define    PAS_IOB_DMA_RXCH_STAT_CNTDEL(x)	(((x) << PAS_IOB_DMA_RXCH_STAT_CNTDEL_S) &\
+						 PAS_IOB_DMA_RXCH_STAT_CNTDEL_M)
+#define PAS_IOB_DMA_TXCH_STAT(i)	(0x1400 + (i)*4)
+#define    PAS_IOB_DMA_TXCH_STAT_INTGEN	0x00001000
+#define    PAS_IOB_DMA_TXCH_STAT_CNTDEL_M	0x00000fff
+#define    PAS_IOB_DMA_TXCH_STAT_CNTDEL_S	0
+#define    PAS_IOB_DMA_TXCH_STAT_CNTDEL(x)	(((x) << PAS_IOB_DMA_TXCH_STAT_CNTDEL_S) &\
+						 PAS_IOB_DMA_TXCH_STAT_CNTDEL_M)
+#define PAS_IOB_DMA_RXCH_RESET(i)	(0x1500 + (i)*4)
+#define    PAS_IOB_DMA_RXCH_RESET_PCNT_M	0xffff0000
+#define    PAS_IOB_DMA_RXCH_RESET_PCNT_S	0
+#define    PAS_IOB_DMA_RXCH_RESET_PCNT(x)	(((x) << PAS_IOB_DMA_RXCH_RESET_PCNT_S) & \
+						 PAS_IOB_DMA_RXCH_RESET_PCNT_M)
+#define    PAS_IOB_DMA_RXCH_RESET_PCNTRST	0x00000020
+#define    PAS_IOB_DMA_RXCH_RESET_DCNTRST	0x00000010
+#define    PAS_IOB_DMA_RXCH_RESET_TINTC		0x00000008
+#define    PAS_IOB_DMA_RXCH_RESET_DINTC		0x00000004
+#define    PAS_IOB_DMA_RXCH_RESET_SINTC		0x00000002
+#define    PAS_IOB_DMA_RXCH_RESET_PINTC		0x00000001
+#define PAS_IOB_DMA_TXCH_RESET(i)	(0x1600 + (i)*4)
+#define    PAS_IOB_DMA_TXCH_RESET_PCNT_M	0xffff0000
+#define    PAS_IOB_DMA_TXCH_RESET_PCNT_S	0
+#define    PAS_IOB_DMA_TXCH_RESET_PCNT(x)	(((x) << PAS_IOB_DMA_TXCH_RESET_PCNT_S) & \
+						 PAS_IOB_DMA_TXCH_RESET_PCNT_M)
+#define    PAS_IOB_DMA_TXCH_RESET_PCNTRST	0x00000020
+#define    PAS_IOB_DMA_TXCH_RESET_DCNTRST	0x00000010
+#define    PAS_IOB_DMA_TXCH_RESET_TINTC		0x00000008
+#define    PAS_IOB_DMA_TXCH_RESET_DINTC		0x00000004
+#define    PAS_IOB_DMA_TXCH_RESET_SINTC		0x00000002
+#define    PAS_IOB_DMA_TXCH_RESET_PINTC		0x00000001
+
+#define PAS_IOB_DMA_COM_TIMEOUTCFG		0x1700
+#define    PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_M	0x00ffffff
+#define    PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_S	0
+#define    PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(x)	(((x) << PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_S) & \
+						 PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_M)
+
+struct pas_dma_xct_descr {
+	union {
+ 		u64	mactx;
+#define	XCT_MACTX_T		0x8000000000000000
+#define	XCT_MACTX_ST		0x4000000000000000
+#define XCT_MACTX_NORES		0x0000000000000000
+#define XCT_MACTX_8BRES		0x1000000000000000
+#define XCT_MACTX_24BRES	0x2000000000000000
+#define XCT_MACTX_40BRES	0x3000000000000000
+#define XCT_MACTX_I		0x0800000000000000
+#define XCT_MACTX_O		0x0400000000000000
+#define XCT_MACTX_E		0x0200000000000000
+#define XCT_MACTX_VLAN_M	0x0180000000000000
+#define XCT_MACTX_VLAN_NOP	0x0000000000000000
+#define XCT_MACTX_VLAN_REMOVE	0x0080000000000000
+#define XCT_MACTX_VLAN_INSERT   0x0100000000000000
+#define XCT_MACTX_VLAN_REPLACE  0x0180000000000000
+#define XCT_MACTX_CRC_M		0x0060000000000000
+#define XCT_MACTX_CRC_NOP	0x0000000000000000
+#define XCT_MACTX_CRC_INSERT	0x0020000000000000
+#define XCT_MACTX_CRC_PAD	0x0040000000000000
+#define XCT_MACTX_CRC_REPLACE	0x0060000000000000
+#define XCT_MACTX_SS		0x0010000000000000
+#define XCT_MACTX_LLEN_M	0x00007fff00000000
+#define XCT_MACTX_LLEN_S	32ull
+#define XCT_MACTX_LLEN(x)	((((long)(x)) << XCT_MACTX_LLEN_S) & XCT_MACTX_LLEN_M)
+#define XCT_MACTX_IPH_M		0x00000000f8000000
+#define XCT_MACTX_IPH_S		27ull
+#define XCT_MACTX_IPH(x)	((((long)(x)) << XCT_MACTX_IPH_S) & XCT_MACTX_IPH_M)
+#define XCT_MACTX_IPO_M		0x0000000007c00000
+#define XCT_MACTX_IPO_S		22ull
+#define XCT_MACTX_IPO(x)	((((long)(x)) << XCT_MACTX_IPO_S) & XCT_MACTX_IPO_M)
+#define XCT_MACTX_CSUM_M	0x0000000000000060
+#define XCT_MACTX_CSUM_NOP	0x0000000000000000
+#define XCT_MACTX_CSUM_TCP	0x0000000000000040
+#define XCT_MACTX_CSUM_UDP	0x0000000000000060
+#define XCT_MACTX_V6		0x0000000000000010
+#define XCT_MACTX_C		0x0000000000000004
+#define XCT_MACTX_AL2		0x0000000000000002
+		u64	macrx;
+#define	XCT_MACRX_T		0x8000000000000000
+#define	XCT_MACRX_ST		0x4000000000000000
+#define XCT_MACRX_NORES		0x0000000000000000
+#define XCT_MACRX_8BRES		0x1000000000000000
+#define XCT_MACRX_24BRES	0x2000000000000000
+#define XCT_MACRX_40BRES	0x3000000000000000
+#define XCT_MACRX_O		0x0400000000000000
+#define XCT_MACRX_E		0x0200000000000000
+#define XCT_MACRX_FF		0x0100000000000000
+#define XCT_MACRX_PF		0x0080000000000000
+#define XCT_MACRX_OB		0x0040000000000000
+#define XCT_MACRX_OD		0x0020000000000000
+#define XCT_MACRX_FS		0x0010000000000000
+#define XCT_MACRX_NB_M		0x000fc00000000000
+#define XCT_MACRX_NB_S		46ULL
+#define XCT_MACRX_NB(x)		((((long)(x)) << XCT_MACRX_NB_S) & XCT_MACRX_NB_M)
+#define XCT_MACRX_LLEN_M	0x00003fff00000000
+#define XCT_MACRX_LLEN_S	32ULL
+#define XCT_MACRX_LLEN(x)	((((long)(x)) << XCT_MACRX_LLEN_S) & XCT_MACRX_LLEN_M)
+#define XCT_MACRX_CRC		0x0000000080000000
+#define XCT_MACRX_LEN_M		0x0000000060000000
+#define XCT_MACRX_LEN_TOOSHORT	0x0000000020000000
+#define XCT_MACRX_LEN_BELOWMIN	0x0000000040000000
+#define XCT_MACRX_LEN_TRUNC	0x0000000060000000
+#define XCT_MACRX_CAST_M	0x0000000018000000
+#define XCT_MACRX_CAST_UNI	0x0000000000000000
+#define XCT_MACRX_CAST_MULTI	0x0000000008000000
+#define XCT_MACRX_CAST_BROAD	0x0000000010000000
+#define XCT_MACRX_CAST_PAUSE	0x0000000018000000
+#define XCT_MACRX_VLC_M		0x0000000006000000
+#define XCT_MACRX_FM		0x0000000001000000
+#define XCT_MACRX_HTY_M		0x0000000000c00000
+#define XCT_MACRX_HTY_IPV4_OK	0x0000000000000000
+#define XCT_MACRX_HTY_IPV6 	0x0000000000400000
+#define XCT_MACRX_HTY_IPV4_BAD	0x0000000000800000
+#define XCT_MACRX_HTY_NONIP	0x0000000000c00000
+#define XCT_MACRX_IPP_M		0x00000000003f0000
+#define XCT_MACRX_IPP_S		16
+#define XCT_MACRX_CSUM_M	0x000000000000ffff
+#define XCT_MACRX_CSUM_S	0
+	};
+	union {
+		u64	ptr;
+#define XCT_PTR_T		0x8000000000000000
+#define XCT_PTR_LEN_M		0x7ffff00000000000
+#define XCT_PTR_LEN_S		44
+#define XCT_PTR_LEN(x)		((((long)(x)) << XCT_PTR_LEN_S) & XCT_PTR_LEN_M)
+#define XCT_PTR_ADDR_M		0x00000fffffffffff
+#define XCT_PTR_ADDR_S		0
+#define XCT_PTR_ADDR(x)		((((long)(x)) << XCT_PTR_ADDR_S) & XCT_PTR_ADDR_M)
+		u64	rxb;
+#define XCT_RXB_LEN_M		0x0ffff00000000000
+#define XCT_RXB_LEN_S		44
+#define XCT_RXB_LEN(x)		((((long)(x)) << XCT_PTR_LEN_S) & XCT_PTR_LEN_M)
+#define XCT_RXB_ADDR_M		0x00000fffffffffff
+#define XCT_RXB_ADDR_S		0
+#define XCT_RXB_ADDR(x)		((((long)(x)) << XCT_PTR_ADDR_S) & XCT_PTR_ADDR_M)
+	};
+};
+
+#endif /* PASEMI_MAC_H */
Index: merge/MAINTAINERS
===================================================================
--- merge.orig/MAINTAINERS
+++ merge/MAINTAINERS
@@ -2484,6 +2484,12 @@ L:	[email-masked]
 W:	http://www.nongnu.org/orinoco/
 S:	Maintained
 
+PA SEMI ETHERNET DRIVER
+P:	Olof Johansson
+M:	[email-masked]
+L:	[email-masked]
+S:	Maintained
+
 PARALLEL PORT SUPPORT
 P:	Phil Blundell
 M:	[email-masked]
Index: merge/include/linux/pci_ids.h
===================================================================
--- merge.orig/include/linux/pci_ids.h
+++ merge/include/linux/pci_ids.h
@@ -2064,6 +2064,8 @@
 #define PCI_VENDOR_ID_TDI               0x192E
 #define PCI_DEVICE_ID_TDI_EHCI          0x0101
 
+#define PCI_VENDOR_ID_PASEMI		0x1959
+
 #define PCI_VENDOR_ID_JMICRON		0x197B
 #define PCI_DEVICE_ID_JMICRON_JMB360	0x2360
 #define PCI_DEVICE_ID_JMICRON_JMB361	0x2361