Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v2]  net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-14  7:08 UTC (permalink / raw)
  To: Lennert Buytenhek
  Cc: Philip Rakity, netdev@vger.kernel.org, Ashish Karkare,
	Prabhanjan Sarnaik, eric.y.miao@gmail.com, Mark Brown
In-Reply-To: <20100810123329.GR8876@mail.wantstofly.org>

On Tue, 2010-08-10 at 05:33 -0700, Lennert Buytenhek wrote:
> On Tue, Aug 10, 2010 at 02:00:04PM +0530, Sachin Sanap wrote:
> 
> > * Headroom in SKB for 802.11 not included in the patch since that
> >   varies based on 802.11 a/b/g/n.
> 
> I don't think this is true?
> 
> (The 11a/b/n on-the-air preambles are of different lengths (and are
> sent at different rates), but that isn't visible to software.)
> 
I might be wrong in the above reasoning, but if skb headroom of 64 is
enough then in 2.6.35 NET_SKB_PAD is already pulled up to 64 so still
not including the headroom patch. Also if wireless driver needs more
headroom than 64 then we might think of doing it the way David has
suggested.

> 
> > +#define ETH_HW_IP_ALIGN		2	/* hw aligns IP header */
> > +#define ETH_DATA_LEN		1500
> > +#define MAX_PKT_SIZE		1518
> 
> How about (stacked) VLANs?
> 
> Is the hardware entirely unable to receive larger packets than this, or
> is it capable of receiving such packets but e.g. with loss of hardware
> receive checksum offloading?
Changed the code to receive larger packet sizes with configurable MTU
upto 9500 bytes.
> 
> (I guess the hardware can't do RX checksum offload at all since I see
> no references to skb->ip_summed and CHECKSUM_*?)
Hardware can not do any checksum offloading.
> 
> 
> > +#define MAX_DESCS_PER_HIGH	(60)
> > +#define TX_DESC_COUNT_LOW	(10)
> 
> These don't seem used.
Removed.
> 
> 
> > +struct pxa168_eth_private {
> >
> > [...]
> >
> > +	/* Size of Tx Ring per queue */
> > +	int tx_ring_size;
> >
> > [...]
> >
> > +	/* Size of Rx Ring per queue */
> > +	int rx_ring_size;
> 
> If you're not going to let the tx/rx ring size be runtime configurable
> (like they are in mv643xx_eth), you might as well leave these as defines.
> 
Added the code for runtime configuration of tx/rx ring size.
> 
> > +static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
> > +{
> > +	u32 reg_data;
> > +
> > +	reg_data = rdl(pep, PHY_ADDRESS);
> > +	reg_data &= ~(0x1f);
> 
> No need for the parentheses.
Hardware does support 3ports so got that code back in.
> 
> 
> > +static inline u8 flip_8_bits(u8 x)
> > +{
> > +	return (((x) & 0x01) << 3) | (((x) & 0x002) << 1)
> > +	    | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3)
> 
> 0x02, 0x08
> 
> 
> > +	addr0 = (mac_addr[5] >> 2) & 0x03f;
> > +	addr1 = (mac_addr[5] & 0x003) | (((mac_addr[4] & 0x7f)) << 2);
> > +	addr2 = ((mac_addr[4] & 0x80) >> 7) | mac_addr[3] << 1;
> > +	addr3 = (mac_addr[2] & 0x0ff) | ((mac_addr[1] & 1) << 8);
> 
> 0x34, 0x03, 0xff
Changed.
> 
> 
> > +	if (i == HOP_NUMBER) {
> > +		if (!del) {
> > +			printk(KERN_INFO "%s: table section is full\n",
> > +			       __FILE__);
> > +			return -ENOSPC;
> > +		} else
> 
> What does it mean in practice if this happens?  (The error message
> could be a bit more descriptive.)
> 
might need the 8kB implementation of hash table. Added the comment.
> 
> > +static void pxa168_eth_set_rx_mode(struct net_device *dev)
> > +{
> > +	struct pxa168_eth_private *pep = netdev_priv(dev);
> > +	struct netdev_hw_addr *ha;
> > +	u32 val;
> > +
> > +	val = rdl(pep, PORT_CONFIG);
> > +	if (dev->flags & IFF_PROMISC)
> > +		val |= PCR_PM;
> > +	else
> > +		val &= ~PCR_PM;
> > +	wrl(pep, PORT_CONFIG, val);
> > +	netdev_for_each_mc_addr(ha, dev)
> > +	    update_hash_table_mac_address(pep, NULL, ha->addr);
> > +}
> 
> 1. Don't indent with spaces.
> 2. This will never remove old multicast MAC addresses?
Added code to flush old MAC addresses.
> 
> 
> > +	pep->work_todo &= ~(WORK_TX_DONE);
> 
> Doesn't need parentheses.
removed.
> 
> 
> > +static int rxq_process(struct net_device *dev, int budget)
> > +{
> > +	struct pxa168_eth_private *pep = netdev_priv(dev);
> > +	struct net_device_stats *stats = &dev->stats;
> > +	unsigned int received_packets = 0;
> > +	struct sk_buff *skb;
> > +
> > +	while (budget-- > 0) {
> > +
> > +		int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
> 
> No need for an empty line.
removed.
> 
> 
> > +static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
> > +				     struct net_device *dev)
> > +{
> > +	u32 icr;
> > +	int ret = 0;
> > +
> > +	icr = rdl(pep, INT_CAUSE);
> > +	if (0x00 == icr)
> 
> 	icr == 0
done.
> 
> 
> > +	wrl(pep, INT_CAUSE, icr ^ 0xffffffff);
> 
> ~icr ?
done.
> 
> 
> > +	/* Extended Port Configuration */
> > +	wrl(pep,
> > +	    PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte suffix aligns IP hdr */
> 
> Prefix?
changed.
> 
> 
> > +		dma_free_coherent(NULL, pep->tx_desc_area_size,
> > +				  pep->p_tx_desc_area, pep->tx_desc_dma);
> 
> BTW, you should pass in a struct device * to the DMA allocation
> functions.
done.
> 
> 
> > +	err = request_irq(dev->irq, pxa168_eth_int_handler,
> > +			  IRQF_DISABLED , dev->name, dev);
> 
> Superfluous space before the comma.
removed.
> 
> 
> > +static void eth_tx_submit_descs_for_skb(struct pxa168_eth_private *pep,
> > +					struct sk_buff *skb)
> > +{
> > +	int tx_index;
> > +	struct tx_desc *desc;
> > +	int length;
> > +
> > +	tx_index = eth_alloc_tx_desc_index(pep);
> > +	desc = &pep->p_tx_desc_area[tx_index];
> > +	length = skb->len;
> > +	pep->tx_skb[tx_index] = skb;
> > +	desc->byte_cnt = length;
> > +	desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
> > +	wmb();
> > +	desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
> > +			TX_ZERO_PADDING | TX_LAST_DESC;
> > +	if (unlikely(!(pep->tx_desc_count % (pep->tx_ring_size / 4))))
> > +		desc->cmd_sts |= TX_EN_INT;
> 
> Is this intended to only generate transmit completion interrupts for
> every N packets?
> 
> If so, you cannot delay kfree_skb()ing a transmitted skb indefinitely.
> If you want to batch TX completion interrupts, you at least have to put
> in a timeout.
Modified the code to generate interrupt per packet.
> 
> Also, the descriptor is in device-visible memory, and BUF_OWNED_BY_DMA
> becomes visible to the device as soon as you do the preceding store to
> desc->cmd_sts -- you cannot then go back and alter that field, as you
> could race with the device clearing BUF_OWNED_BY_DMA.
> 
> 
> > +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
> > +{
> > +	struct pxa168_eth_private *pep = netdev_priv(dev);
> > +	struct net_device_stats *stats = &dev->stats;
> > +
> > +	eth_tx_submit_descs_for_skb(pep, skb);
> 
> In mv643xx_eth, txq_submit_*() are much larger because they have to deal
> with scatter-gather transmit.  Since you don't support that, you might
> as well just inline this function here.
done.
> 
> 
> > +static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
> > +{
> > +	int val;
> > +	struct pxa168_eth_private *pep = bus->priv;
> > +	int i = 0;
> > +
> > +	/* wait for the SMI register to become available */
> > +	for (i = 0; (val = rdl(pep, SMI)) & SMI_BUSY; i++) {
> > +		if (i == PHY_WAIT_ITERATIONS) {
> > +			printk(KERN_ERR
> > +			       "pxa168 PHY timeout, val=0x%x\n", val);
> > +			return -ETIMEDOUT;
> > +		}
> > +		udelay(1);
> > +	}
> > +	wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
> > +	/* now wait for the data to be valid */
> > +	for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
> > +		if (i == PHY_WAIT_ITERATIONS) {
> > +			printk(KERN_ERR
> > +			       "pxa168 PHY RD timeout, val=0x%x\n", val);
> > +			return -ETIMEDOUT;
> > +		}
> > +		udelay(1);
> > +	}
> > +	return val & 0xffff;
> > +}
> 
> This can end up busy-waiting (i.e. hogging the CPU) for twice 500 us,
> i.e. 1 msec.  Isn't there a SMI completion interrupt you can use, or
> at least yield the cpu by sleeping for a bit?
> 
Added code to yield the CPU.
> 
> > +static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
> > +			    u16 value)
> > +{
> > +	struct pxa168_eth_private *pep = bus->priv;
> > +	int i;
> > +
> > +	/* wait for the SMI register to become available */
> > +	for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
> > +		if (i == PHY_WAIT_ITERATIONS) {
> > +			printk(KERN_ERR "pxa168 PHY busy timeout.\n");
> > +			return -ETIMEDOUT;
> > +		}
> > +		udelay(1);
> > +	}
> > +	wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
> > +	    SMI_OP_W | (value & 0xffff));
> 
> I would wait here for the write to complete, otherwise you can't report
> errors due to the slave address not responding.
> 
done.
> 
> > +	clk = clk_get(&pdev->dev, "MFUCLK");
> > +	if (IS_ERR(clk)) {
> > +		printk(KERN_ERR "fast Ethernet failed to get clock\n");
> 
> At least stick the name of the driver in here.
> 
> 
> > +	/* init callback is used for board specific initialization
> > +	 * e.g on Aspenite its used to initialize the PHY transceiver.
> > +	 */
> > +	int (*init)(void);
> 
> Is resetting the PHY not enough?
On Aspenite the PHY transceiver needs to be enabled by sending high
signals on two GPIO expander pins which are under I2C control.I could
not do this in the board specific init function since at that time the
I2C is not up. If we dont do this the phy is not detected so we cant
even reset it.



^ permalink raw reply

* Re: [PATCH v3]  net: add Fast Ethernet driver for PXA168.
From: Sachin Sanap @ 2010-08-14  7:22 UTC (permalink / raw)
  To: Lennert Buytenhek
  Cc: Philip Rakity, netdev@vger.kernel.org, Ashish Karkare,
	Prabhanjan Sarnaik, eric.y.miao@gmail.com, Mark Brown
In-Reply-To: <20100810123329.GR8876@mail.wantstofly.org>

>From df92c2959eb0e50ea63c90822fb9e1edef673ea6 Mon Sep 17 00:00:00 2001
From: Sachin Sanap <ssanap@marvell.com>
Date: Sat, 14 Aug 2010 17:11:05 +0530
Subject: [PATCH] net: add Fast Ethernet driver for PXA168.

Signed-off-by: Sachin Sanap <ssanap@marvell.com>
---
V3:
* TX_DONE interrupt is now generated for every packet.
* MTU can now be configured upto 9500 bytes.
* TX/RX ring sizes are now platform configurable.
* Remove the CPU hogging and yield the CPU while SMI operations take time.
* Cleanup the hash table for old MAC address entries in set_rx_mode function.

 drivers/net/Kconfig        |   10 +
 drivers/net/Makefile       |    1 +
 drivers/net/pxa168_eth.c   | 1666 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pxa168_eth.h |   30 +
 4 files changed, 1707 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/pxa168_eth.c
 create mode 100644 include/linux/pxa168_eth.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ce2fcdd..78cd7e8 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -927,6 +927,16 @@ config SMC91X
 	  The module will be called smc91x.  If you want to compile it as a
 	  module, say M here and read <file:Documentation/kbuild/modules.txt>.
 
+config PXA168_ETH
+	tristate "Marvell pxa168 ethernet support"
+	depends on CPU_PXA168
+	select PHYLIB
+	help
+	  This driver supports the pxa168 Ethernet ports.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called pxa168_eth.
+
 config NET_NETX
 	tristate "NetX Ethernet support"
 	select MII
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 0a0512a..a42d437 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
 obj-$(CONFIG_SMC91X) += smc91x.o
 obj-$(CONFIG_SMC911X) += smc911x.o
 obj-$(CONFIG_SMSC911X) += smsc911x.o
+obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
 obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
 obj-$(CONFIG_DM9000) += dm9000.o
 obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
new file mode 100644
index 0000000..ecc64d7
--- /dev/null
+++ b/drivers/net/pxa168_eth.c
@@ -0,0 +1,1666 @@
+/*
+ * PXA168 ethernet driver.
+ * Most of the code is derived from mv643xx ethernet driver.
+ *
+ * Copyright (C) 2010 Marvell International Ltd.
+ *		Sachin Sanap <ssanap@marvell.com>
+ *		Philip Rakity <prakity@marvell.com>
+ *		Mark Brown <markb@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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/dma-mapping.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/etherdevice.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <asm/cacheflush.h>
+#include <linux/pxa168_eth.h>
+
+#define DRIVER_NAME	"pxa168-eth"
+#define DRIVER_VERSION	"0.3"
+
+/*
+ * Registers
+ */
+
+#define PHY_ADDRESS		0x0000
+#define SMI			0x0010
+#define PORT_CONFIG		0x0400
+#define PORT_CONFIG_EXT		0x0408
+#define PORT_COMMAND		0x0410
+#define PORT_STATUS		0x0418
+#define HTPR			0x0428
+#define SDMA_CONFIG		0x0440
+#define SDMA_CMD		0x0448
+#define INT_CAUSE		0x0450
+#define INT_W_CLEAR		0x0454
+#define INT_MASK		0x0458
+#define ETH_F_RX_DESC_0		0x0480
+#define ETH_C_RX_DESC_0		0x04A0
+#define ETH_C_TX_DESC_1		0x04E4
+
+/* smi register */
+#define SMI_BUSY		(1 << 28)	/* 0 - Write, 1 - Read  */
+#define SMI_R_VALID		(1 << 27)	/* 0 - Write, 1 - Read  */
+#define SMI_OP_W		(0 << 26)	/* Write operation      */
+#define SMI_OP_R		(1 << 26)	/* Read operation */
+
+#define PHY_WAIT_ITERATIONS	10
+
+#define PXA168_ETH_PHY_ADDR_DEFAULT	0
+/* RX & TX descriptor command */
+#define BUF_OWNED_BY_DMA	(1 << 31)
+
+/* RX descriptor status */
+#define RX_EN_INT		(1 << 23)
+#define RX_FIRST_DESC		(1 << 17)
+#define RX_LAST_DESC		(1 << 16)
+#define RX_ERROR		(1 << 15)
+
+/* TX descriptor command */
+#define TX_EN_INT		(1 << 23)
+#define TX_GEN_CRC		(1 << 22)
+#define TX_ZERO_PADDING		(1 << 18)
+#define TX_FIRST_DESC		(1 << 17)
+#define TX_LAST_DESC		(1 << 16)
+#define TX_ERROR		(1 << 15)
+
+/* SDMA_CMD */
+#define SDMA_CMD_AT		(1 << 31)
+#define SDMA_CMD_TXDL		(1 << 24)
+#define SDMA_CMD_TXDH		(1 << 23)
+#define SDMA_CMD_AR		(1 << 15)
+#define SDMA_CMD_ERD		(1 << 7)
+
+/* Bit definitions of the Port Config Reg */
+#define PCR_HS			(1 << 12)
+#define PCR_EN			(1 << 7)
+#define PCR_PM			(1 << 0)
+
+/* Bit definitions of the Port Config Extend Reg */
+#define PCXR_2BSM		(1 << 28)
+#define PCXR_DSCP_EN		(1 << 21)
+#define PCXR_MFL_1518		(0 << 14)
+#define PCXR_MFL_1536		(1 << 14)
+#define PCXR_MFL_2048		(2 << 14)
+#define PCXR_MFL_64K		(3 << 14)
+#define PCXR_FLP		(1 << 11)
+#define PCXR_PRIO_TX_OFF	3
+#define PCXR_TX_HIGH_PRI	(7 << PCXR_PRIO_TX_OFF)
+
+/* Bit definitions of the SDMA Config Reg */
+#define SDCR_BSZ_OFF		12
+#define SDCR_BSZ8		(3 << SDCR_BSZ_OFF)
+#define SDCR_BSZ4		(2 << SDCR_BSZ_OFF)
+#define SDCR_BSZ2		(1 << SDCR_BSZ_OFF)
+#define SDCR_BSZ1		(0 << SDCR_BSZ_OFF)
+#define SDCR_BLMR		(1 << 6)
+#define SDCR_BLMT		(1 << 7)
+#define SDCR_RIFB		(1 << 9)
+#define SDCR_RC_OFF		2
+#define SDCR_RC_MAX_RETRANS	(0xf << SDCR_RC_OFF)
+
+/*
+ * Bit definitions of the Interrupt Cause Reg
+ * and Interrupt MASK Reg is the same
+ */
+#define ICR_RXBUF		(1 << 0)
+#define ICR_TXBUF_H		(1 << 2)
+#define ICR_TXBUF_L		(1 << 3)
+#define ICR_TXEND_H		(1 << 6)
+#define ICR_TXEND_L		(1 << 7)
+#define ICR_RXERR		(1 << 8)
+#define ICR_TXERR_H		(1 << 10)
+#define ICR_TXERR_L		(1 << 11)
+#define ICR_TX_UDR		(1 << 13)
+#define ICR_MII_CH		(1 << 28)
+
+#define ALL_INTS (ICR_TXBUF_H  | ICR_TXBUF_L  | ICR_TX_UDR |\
+				ICR_TXERR_H  | ICR_TXERR_L |\
+				ICR_TXEND_H  | ICR_TXEND_L |\
+				ICR_RXBUF | ICR_RXERR  | ICR_MII_CH)
+
+#define ETH_HW_IP_ALIGN		2	/* hw aligns IP header */
+
+#define NUM_RX_DESCS		64
+#define NUM_TX_DESCS		64
+
+#define HASH_ADD		0
+#define HASH_DELETE		1
+#define HASH_ADDR_TABLE_SIZE	0x4000	/* 16K (1/2K address - PCR_HS == 1) */
+#define HOP_NUMBER		12
+
+/* Bit definitions for Port status */
+#define PORT_SPEED_100		(1 << 0)
+#define FULL_DUPLEX		(1 << 1)
+#define FLOW_CONTROL_ENABLED	(1 << 2)
+#define LINK_UP			(1 << 3)
+
+/* Bit definitions for work to be done */
+#define WORK_LINK		(1 << 0)
+#define WORK_TX_DONE		(1 << 1)
+
+/*
+ * Misc definitions.
+ */
+#define SKB_DMA_REALIGN		((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
+
+struct rx_desc {
+	u32 cmd_sts;		/* Descriptor command status            */
+	u16 byte_cnt;		/* Descriptor buffer byte count         */
+	u16 buf_size;		/* Buffer size                          */
+	u32 buf_ptr;		/* Descriptor buffer pointer            */
+	u32 next_desc_ptr;	/* Next descriptor pointer              */
+};
+
+struct tx_desc {
+	u32 cmd_sts;		/* Command/status field                 */
+	u16 reserved;
+	u16 byte_cnt;		/* buffer byte count                    */
+	u32 buf_ptr;		/* pointer to buffer for this descriptor */
+	u32 next_desc_ptr;	/* Pointer to next descriptor           */
+};
+
+struct pxa168_eth_private {
+	int port_num;		/* User Ethernet port number    */
+
+	int rx_resource_err;	/* Rx ring resource error flag */
+
+	/* Next available and first returning Rx resource */
+	int rx_curr_desc_q, rx_used_desc_q;
+
+	/* Next available and first returning Tx resource */
+	int tx_curr_desc_q, tx_used_desc_q;
+
+	struct rx_desc *p_rx_desc_area;
+	dma_addr_t rx_desc_dma;
+	int rx_desc_area_size;
+	struct sk_buff **rx_skb;
+
+	struct tx_desc *p_tx_desc_area;
+	dma_addr_t tx_desc_dma;
+	int tx_desc_area_size;
+	struct sk_buff **tx_skb;
+
+	struct work_struct tx_timeout_task;
+
+	struct net_device *dev;
+	struct napi_struct napi;
+	u8 work_todo;
+	int skb_size;
+
+	struct net_device_stats stats;
+	/* Size of Tx Ring per queue */
+	int tx_ring_size;
+	/* Number of tx descriptors in use */
+	int tx_desc_count;
+	/* Size of Rx Ring per queue */
+	int rx_ring_size;
+	/* Number of rx descriptors in use */
+	int rx_desc_count;
+
+	/*
+	 * Used in case RX Ring is empty, which can occur when
+	 * system does not have resources (skb's)
+	 */
+	struct timer_list timeout;
+	struct mii_bus *smi_bus;
+	struct phy_device *phy;
+
+	/* clock */
+	struct clk *clk;
+	struct pxa168_eth_platform_data *pd;
+	/*
+	 * Ethernet controller base address.
+	 */
+	void __iomem *base;
+
+	/* Pointer to the hardware address filter table */
+	void *htpr;
+	dma_addr_t htpr_dma;
+};
+
+struct addr_table_entry {
+	__le32 lo;
+	__le32 hi;
+};
+
+/* Bit fields of a Hash Table Entry */
+enum hash_table_entry {
+	HASH_ENTRY_VALID = 1,
+	SKIP = 2,
+	HASH_ENTRY_RECEIVE_DISCARD = 4,
+	HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
+};
+
+static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
+static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd);
+static int pxa168_init_hw(struct pxa168_eth_private *pep);
+static void eth_port_reset(struct net_device *dev);
+static void eth_port_start(struct net_device *dev);
+static int pxa168_eth_open(struct net_device *dev);
+static int pxa168_eth_stop(struct net_device *dev);
+static int ethernet_phy_setup(struct net_device *dev);
+
+static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
+{
+	return readl(pep->base + offset);
+}
+
+static inline void wrl(struct pxa168_eth_private *pep, int offset, u32 data)
+{
+	writel(data, pep->base + offset);
+}
+
+static void abort_dma(struct pxa168_eth_private *pep)
+{
+	int delay;
+	int max_retries = 40;
+
+	do {
+		wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
+		udelay(100);
+
+		delay = 10;
+		while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
+		       && delay-- > 0) {
+			udelay(10);
+		}
+	} while (max_retries-- > 0 && delay <= 0);
+
+	if (max_retries <= 0)
+		printk(KERN_ERR "%s : DMA Stuck\n", __func__);
+}
+
+static int ethernet_phy_get(struct pxa168_eth_private *pep)
+{
+	unsigned int reg_data;
+
+	reg_data = rdl(pep, PHY_ADDRESS);
+
+	return (reg_data >> (5 * pep->port_num)) & 0x1f;
+}
+
+static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
+{
+	u32 reg_data;
+	int addr_shift = 5 * pep->port_num;
+
+	reg_data = rdl(pep, PHY_ADDRESS);
+	reg_data &= ~(0x1f << addr_shift);
+	reg_data |= (phy_addr & 0x1f) << addr_shift;
+	wrl(pep, PHY_ADDRESS, reg_data);
+}
+
+static void ethernet_phy_reset(struct pxa168_eth_private *pep)
+{
+	int data;
+
+	data = phy_read(pep->phy, MII_BMCR);
+	if (data < 0)
+		return;
+
+	data |= BMCR_RESET;
+	if (phy_write(pep->phy, MII_BMCR, data) < 0)
+		return;
+
+	do {
+		data = phy_read(pep->phy, MII_BMCR);
+	} while (data >= 0 && data & BMCR_RESET);
+}
+
+static void rxq_refill(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	struct sk_buff *skb;
+	struct rx_desc *p_used_rx_desc;
+	int used_rx_desc;
+
+	while (pep->rx_desc_count < pep->rx_ring_size) {
+		int size;
+
+		skb = dev_alloc_skb(pep->skb_size);
+		if (!skb)
+			break;
+		if (SKB_DMA_REALIGN)
+			skb_reserve(skb, SKB_DMA_REALIGN);
+		pep->rx_desc_count++;
+		/* Get 'used' Rx descriptor */
+		used_rx_desc = pep->rx_used_desc_q;
+		p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
+		size = skb->end - skb->data;
+		p_used_rx_desc->buf_ptr = dma_map_single(NULL,
+							 skb->data,
+							 size,
+							 DMA_FROM_DEVICE);
+		p_used_rx_desc->buf_size = size;
+		pep->rx_skb[used_rx_desc] = skb;
+
+		/* Return the descriptor to DMA ownership */
+		wmb();
+		p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
+		wmb();
+
+		/* Move the used descriptor pointer to the next descriptor */
+		pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
+
+		/* Any Rx return cancels the Rx resource error status */
+		pep->rx_resource_err = 0;
+
+		skb_reserve(skb, ETH_HW_IP_ALIGN);
+	}
+
+	/*
+	 * If RX ring is empty of SKB, set a timer to try allocating
+	 * again at a later time.
+	 */
+	if (pep->rx_desc_count == 0) {
+		pep->timeout.expires = jiffies + (HZ / 10);
+		add_timer(&pep->timeout);
+	}
+}
+
+static inline void rxq_refill_timer_wrapper(unsigned long data)
+{
+	struct pxa168_eth_private *pep = (void *)data;
+	napi_schedule(&pep->napi);
+}
+
+static inline u8 flip_8_bits(u8 x)
+{
+	return (((x) & 0x01) << 3) | (((x) & 0x02) << 1)
+	    | (((x) & 0x04) >> 1) | (((x) & 0x08) >> 3)
+	    | (((x) & 0x10) << 3) | (((x) & 0x20) << 1)
+	    | (((x) & 0x40) >> 1) | (((x) & 0x80) >> 3);
+}
+
+static void nibble_swap_every_byte(unsigned char *mac_addr)
+{
+	int i;
+	for (i = 0; i < ETH_ALEN; i++) {
+		mac_addr[i] = ((mac_addr[i] & 0x0f) << 4) |
+				((mac_addr[i] & 0xf0) >> 4);
+	}
+}
+
+static void inverse_every_nibble(unsigned char *mac_addr)
+{
+	int i;
+	for (i = 0; i < ETH_ALEN; i++)
+		mac_addr[i] = flip_8_bits(mac_addr[i]);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * This function will calculate the hash function of the address.
+ * Inputs
+ * mac_addr_orig    - MAC address.
+ * Outputs
+ * return the calculated entry.
+ */
+static u32 hash_function(unsigned char *mac_addr_orig)
+{
+	u32 hash_result;
+	u32 addr0;
+	u32 addr1;
+	u32 addr2;
+	u32 addr3;
+	unsigned char mac_addr[ETH_ALEN];
+
+	/* Make a copy of MAC address since we are going to performe bit
+	 * operations on it
+	 */
+	memcpy(mac_addr, mac_addr_orig, ETH_ALEN);
+
+	nibble_swap_every_byte(mac_addr);
+	inverse_every_nibble(mac_addr);
+
+	addr0 = (mac_addr[5] >> 2) & 0x3f;
+	addr1 = (mac_addr[5] & 0x03) | (((mac_addr[4] & 0x7f)) << 2);
+	addr2 = ((mac_addr[4] & 0x80) >> 7) | mac_addr[3] << 1;
+	addr3 = (mac_addr[2] & 0xff) | ((mac_addr[1] & 1) << 8);
+
+	hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
+	hash_result = hash_result & 0x07ff;
+	return hash_result;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * This function will add/del an entry to the address table.
+ * Inputs
+ * pep - ETHERNET .
+ * mac_addr - MAC address.
+ * skip - if 1, skip this address.Used in case of deleting an entry which is a
+ *	  part of chain in the hash table.We cant just delete the entry since
+ *	  that will break the chain.We need to defragment the tables time to
+ *	  time.
+ * rd   - 0 Discard packet upon match.
+ *	- 1 Receive packet upon match.
+ * Outputs
+ * address table entry is added/deleted.
+ * 0 if success.
+ * -ENOSPC if table full
+ */
+static int add_del_hash_entry(struct pxa168_eth_private *pep,
+			      unsigned char *mac_addr,
+			      u32 rd, u32 skip, int del)
+{
+	struct addr_table_entry *entry, *start;
+	u32 new_high;
+	u32 new_low;
+	u32 i;
+
+	new_low = (((mac_addr[1] >> 4) & 0xf) << 15)
+	    | (((mac_addr[1] >> 0) & 0xf) << 11)
+	    | (((mac_addr[0] >> 4) & 0xf) << 7)
+	    | (((mac_addr[0] >> 0) & 0xf) << 3)
+	    | (((mac_addr[3] >> 4) & 0x1) << 31)
+	    | (((mac_addr[3] >> 0) & 0xf) << 27)
+	    | (((mac_addr[2] >> 4) & 0xf) << 23)
+	    | (((mac_addr[2] >> 0) & 0xf) << 19)
+	    | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
+	    | HASH_ENTRY_VALID;
+
+	new_high = (((mac_addr[5] >> 4) & 0xf) << 15)
+	    | (((mac_addr[5] >> 0) & 0xf) << 11)
+	    | (((mac_addr[4] >> 4) & 0xf) << 7)
+	    | (((mac_addr[4] >> 0) & 0xf) << 3)
+	    | (((mac_addr[3] >> 5) & 0x7) << 0);
+
+	/*
+	 * Pick the appropriate table, start scanning for free/reusable
+	 * entries at the index obtained by hashing the specified MAC address
+	 */
+	start = (struct addr_table_entry *)(pep->htpr);
+	entry = start + hash_function(mac_addr);
+	for (i = 0; i < HOP_NUMBER; i++) {
+		if (!(le32_to_cpu(entry->lo) & HASH_ENTRY_VALID)) {
+			break;
+		} else {
+			/* if same address put in same position */
+			if (((le32_to_cpu(entry->lo) & 0xfffffff8) ==
+				(new_low & 0xfffffff8)) &&
+				(le32_to_cpu(entry->hi) == new_high)) {
+				break;
+			}
+		}
+		if (entry == start + 0x7ff)
+			entry = start;
+		else
+			entry++;
+	}
+
+	if (((le32_to_cpu(entry->lo) & 0xfffffff8) != (new_low & 0xfffffff8)) &&
+	    (le32_to_cpu(entry->hi) != new_high) && del)
+		return 0;
+
+	if (i == HOP_NUMBER) {
+		if (!del) {
+			printk(KERN_INFO "%s: table section is full, need to "
+					"move to 16kB implementation?\n",
+					 __FILE__);
+			return -ENOSPC;
+		} else
+			return 0;
+	}
+
+	/*
+	 * Update the selected entry
+	 */
+	if (del) {
+		entry->hi = 0;
+		entry->lo = 0;
+	} else {
+		entry->hi = cpu_to_le32(new_high);
+		entry->lo = cpu_to_le32(new_low);
+	}
+
+	return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ *  Create an addressTable entry from MAC address info
+ *  found in the specifed net_device struct
+ *
+ *  Input : pointer to ethernet interface network device structure
+ *  Output : N/A
+ */
+static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
+					  unsigned char *oaddr,
+					  unsigned char *addr)
+{
+	/* Delete old entry */
+	if (oaddr)
+		add_del_hash_entry(pep, oaddr, 1, 0, HASH_DELETE);
+	/* Add new entry */
+	add_del_hash_entry(pep, addr, 1, 0, HASH_ADD);
+}
+
+static int init_hash_table(struct pxa168_eth_private *pep)
+{
+	/*
+	 * Hardware expects CPU to build a hash table based on a predefined
+	 * hash function and populate it based on hardware address. The
+	 * location of the hash table is identified by 32-bit pointer stored
+	 * in HTPR internal register. Two possible sizes exists for the hash
+	 * table 8kB (256kB of DRAM required (4 x 64 kB banks)) and 1/2kB
+	 * (16kB of DRAM required (4 x 4 kB banks)).We currently only support
+	 * 1/2kB.
+	 */
+	/* TODO: Add support for 8kB hash table and alternative hash
+	 * function.Driver can dynamically switch to them if the 1/2kB hash
+	 * table is full.
+	 */
+	if (pep->htpr == NULL) {
+		pep->htpr = dma_alloc_coherent(pep->dev->dev.parent,
+					      HASH_ADDR_TABLE_SIZE,
+					      &pep->htpr_dma, GFP_KERNEL);
+		if (pep->htpr == NULL)
+			return -ENOMEM;
+	}
+	memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
+	wrl(pep, HTPR, pep->htpr_dma);
+	return 0;
+}
+
+static void pxa168_eth_set_rx_mode(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	struct netdev_hw_addr *ha;
+	u32 val;
+
+	val = rdl(pep, PORT_CONFIG);
+	if (dev->flags & IFF_PROMISC)
+		val |= PCR_PM;
+	else
+		val &= ~PCR_PM;
+	wrl(pep, PORT_CONFIG, val);
+
+	/*
+	 * Remove the old list of MAC address and add dev->addr
+	 * and multicast address.
+	 */
+	memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
+	update_hash_table_mac_address(pep, NULL, dev->dev_addr);
+
+	netdev_for_each_mc_addr(ha, dev)
+		update_hash_table_mac_address(pep, NULL, ha->addr);
+}
+
+static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	unsigned char oldMac[ETH_ALEN];
+
+	if (!is_valid_ether_addr(sa->sa_data))
+		return -EINVAL;
+	memcpy(oldMac, dev->dev_addr, ETH_ALEN);
+	memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+	netif_addr_lock_bh(dev);
+	update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
+	netif_addr_unlock_bh(dev);
+	return 0;
+}
+
+static void eth_port_start(struct net_device *dev)
+{
+	unsigned int val = 0;
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	int tx_curr_desc, rx_curr_desc;
+
+	/* Perform PHY reset, if there is a PHY. */
+	if (pep->phy != NULL) {
+		struct ethtool_cmd cmd;
+
+		pxa168_get_settings(pep->dev, &cmd);
+		ethernet_phy_reset(pep);
+		pxa168_set_settings(pep->dev, &cmd);
+	}
+
+	/* Assignment of Tx CTRP of given queue */
+	tx_curr_desc = pep->tx_curr_desc_q;
+	wrl(pep, ETH_C_TX_DESC_1,
+	    (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
+
+	/* Assignment of Rx CRDP of given queue */
+	rx_curr_desc = pep->rx_curr_desc_q;
+	wrl(pep, ETH_C_RX_DESC_0,
+	    (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
+
+	wrl(pep, ETH_F_RX_DESC_0,
+	    (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
+
+	/* Clear all interrupts */
+	wrl(pep, INT_CAUSE, 0);
+
+	/* Enable all interrupts for receive, transmit and error. */
+	wrl(pep, INT_MASK, ALL_INTS);
+
+	val = rdl(pep, PORT_CONFIG);
+	val |= PCR_EN;
+	wrl(pep, PORT_CONFIG, val);
+
+	/* Start RX DMA engine */
+	val = rdl(pep, SDMA_CMD);
+	val |= SDMA_CMD_ERD;
+	wrl(pep, SDMA_CMD, val);
+}
+
+static void eth_port_reset(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	unsigned int val = 0;
+
+	/* Stop all interrupts for receive, transmit and error. */
+	wrl(pep, INT_MASK, 0);
+
+	/* Clear all interrupts */
+	wrl(pep, INT_CAUSE, 0);
+
+	/* Stop RX DMA */
+	val = rdl(pep, SDMA_CMD);
+	val &= ~SDMA_CMD_ERD;	/* abort dma command */
+
+	/* Abort any transmit and receive operations and put DMA
+	 * in idle state.
+	 */
+	abort_dma(pep);
+
+	/* Disable port */
+	val = rdl(pep, PORT_CONFIG);
+	val &= ~PCR_EN;
+	wrl(pep, PORT_CONFIG, val);
+}
+
+/*
+ * txq_reclaim - Free the tx desc data for completed descriptors
+ * If force is non-zero, frees uncompleted descriptors as well
+ */
+static int txq_reclaim(struct net_device *dev, int force)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	struct tx_desc *desc;
+	u32 cmd_sts;
+	struct sk_buff *skb;
+	int tx_index;
+	dma_addr_t addr;
+	int count;
+	int released = 0;
+
+	netif_tx_lock(dev);
+
+	pep->work_todo &= ~WORK_TX_DONE;
+	while (pep->tx_desc_count > 0) {
+		tx_index = pep->tx_used_desc_q;
+		desc = &pep->p_tx_desc_area[tx_index];
+		cmd_sts = desc->cmd_sts;
+		if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
+			if (released > 0) {
+				goto txq_reclaim_end;
+			} else {
+				released = -1;
+				goto txq_reclaim_end;
+			}
+		}
+		pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
+		pep->tx_desc_count--;
+		addr = desc->buf_ptr;
+		count = desc->byte_cnt;
+		skb = pep->tx_skb[tx_index];
+		if (skb)
+			pep->tx_skb[tx_index] = NULL;
+
+		if (cmd_sts & TX_ERROR) {
+			if (net_ratelimit())
+				printk(KERN_ERR "%s: Error in TX\n", dev->name);
+			dev->stats.tx_errors++;
+		}
+		dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
+		if (skb)
+			dev_kfree_skb_irq(skb);
+		released++;
+	}
+txq_reclaim_end:
+	netif_tx_unlock(dev);
+	return released;
+}
+
+static void pxa168_eth_tx_timeout(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+
+	printk(KERN_INFO "%s: TX timeout  desc_count %d\n",
+	       dev->name, pep->tx_desc_count);
+
+	schedule_work(&pep->tx_timeout_task);
+}
+
+static void pxa168_eth_tx_timeout_task(struct work_struct *work)
+{
+	struct pxa168_eth_private *pep = container_of(work,
+						 struct pxa168_eth_private,
+						 tx_timeout_task);
+	struct net_device *dev = pep->dev;
+	pxa168_eth_stop(dev);
+	pxa168_eth_open(dev);
+}
+
+static int rxq_process(struct net_device *dev, int budget)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	struct net_device_stats *stats = &dev->stats;
+	unsigned int received_packets = 0;
+	struct sk_buff *skb;
+
+	while (budget-- > 0) {
+		int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
+		struct rx_desc *rx_desc;
+		unsigned int cmd_sts;
+
+		/* Do not process Rx ring in case of Rx ring resource error */
+		if (pep->rx_resource_err)
+			break;
+		rx_curr_desc = pep->rx_curr_desc_q;
+		rx_used_desc = pep->rx_used_desc_q;
+		rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
+		cmd_sts = rx_desc->cmd_sts;
+		rmb();
+		if (cmd_sts & (BUF_OWNED_BY_DMA))
+			break;
+		skb = pep->rx_skb[rx_curr_desc];
+		pep->rx_skb[rx_curr_desc] = NULL;
+
+		rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
+		pep->rx_curr_desc_q = rx_next_curr_desc;
+
+		/* Rx descriptors exhausted. */
+		/* Set the Rx ring resource error flag */
+		if (rx_next_curr_desc == rx_used_desc)
+			pep->rx_resource_err = 1;
+		pep->rx_desc_count--;
+		dma_unmap_single(NULL, rx_desc->buf_ptr,
+				 rx_desc->buf_size,
+				 DMA_FROM_DEVICE);
+		received_packets++;
+		/*
+		 * Update statistics.
+		 * Note byte count includes 4 byte CRC count
+		 */
+		stats->rx_packets++;
+		stats->rx_bytes += rx_desc->byte_cnt;
+		/*
+		 * In case received a packet without first / last bits on OR
+		 * the error summary bit is on, the packets needs to be droped.
+		 */
+		if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+		     (RX_FIRST_DESC | RX_LAST_DESC))
+		    || (cmd_sts & RX_ERROR)) {
+
+			stats->rx_dropped++;
+			if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+			    (RX_FIRST_DESC | RX_LAST_DESC)) {
+				if (net_ratelimit())
+					printk(KERN_ERR
+					       "%s: Rx pkt on multiple desc\n",
+					       dev->name);
+			}
+			if (cmd_sts & RX_ERROR)
+				stats->rx_errors++;
+			dev_kfree_skb_irq(skb);
+		} else {
+			/*
+			 * The -4 is for the CRC in the trailer of the
+			 * received packet
+			 */
+			skb_put(skb, rx_desc->byte_cnt - 4);
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_receive_skb(skb);
+		}
+		dev->last_rx = jiffies;
+	}
+	/* Fill RX ring with skb's */
+	rxq_refill(dev);
+	return received_packets;
+}
+
+static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
+				     struct net_device *dev)
+{
+	u32 icr;
+	int ret = 0;
+
+	icr = rdl(pep, INT_CAUSE);
+	if (icr == 0)
+		return IRQ_NONE;
+
+	wrl(pep, INT_CAUSE, ~icr);
+	if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
+		pep->work_todo |= WORK_TX_DONE;
+		ret = 1;
+	}
+	if (icr & ICR_RXBUF)
+		ret = 1;
+	if (icr & ICR_MII_CH) {
+		pep->work_todo |= WORK_LINK;
+		ret = 1;
+	}
+	return ret;
+}
+
+static void handle_link_event(struct pxa168_eth_private *pep)
+{
+	struct net_device *dev = pep->dev;
+	u32 port_status;
+	int speed;
+	int duplex;
+	int fc;
+
+	port_status = rdl(pep, PORT_STATUS);
+	if (!(port_status & LINK_UP)) {
+		if (netif_carrier_ok(dev)) {
+			printk(KERN_INFO "%s: link down\n", dev->name);
+			netif_carrier_off(dev);
+			txq_reclaim(dev, 1);
+		}
+		return;
+	}
+	if (port_status & PORT_SPEED_100)
+		speed = 100;
+	else
+		speed = 10;
+
+	duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
+	fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
+	printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
+	       "flow control %sabled\n", dev->name,
+	       speed, duplex ? "full" : "half", fc ? "en" : "dis");
+	if (!netif_carrier_ok(dev))
+		netif_carrier_on(dev);
+}
+
+static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+
+	if (unlikely(!pxa168_eth_collect_events(pep, dev)))
+		return IRQ_NONE;
+	/* Disable interrupts */
+	wrl(pep, INT_MASK, 0);
+	napi_schedule(&pep->napi);
+	return IRQ_HANDLED;
+}
+
+static void pxa168_eth_recalc_skb_size(struct pxa168_eth_private *pep)
+{
+	int skb_size;
+
+	/*
+	 * Reserve 2+14 bytes for an ethernet header (the hardware
+	 * automatically prepends 2 bytes of dummy data to each
+	 * received packet), 16 bytes for up to four VLAN tags, and
+	 * 4 bytes for the trailing FCS -- 36 bytes total.
+	 */
+	skb_size = pep->dev->mtu + 36;
+
+	/*
+	 * Make sure that the skb size is a multiple of 8 bytes, as
+	 * the lower three bits of the receive descriptor's buffer
+	 * size field are ignored by the hardware.
+	 */
+	pep->skb_size = (skb_size + 7) & ~7;
+
+	/*
+	 * If NET_SKB_PAD is smaller than a cache line,
+	 * netdev_alloc_skb() will cause skb->data to be misaligned
+	 * to a cache line boundary.  If this is the case, include
+	 * some extra space to allow re-aligning the data area.
+	 */
+	pep->skb_size += SKB_DMA_REALIGN;
+
+}
+
+static int set_port_config_ext(struct pxa168_eth_private *pep)
+{
+	int skb_size;
+
+	pxa168_eth_recalc_skb_size(pep);
+	if  (pep->skb_size <= 1518)
+		skb_size = PCXR_MFL_1518;
+	else if (pep->skb_size <= 1536)
+		skb_size = PCXR_MFL_1536;
+	else if (pep->skb_size <= 2048)
+		skb_size = PCXR_MFL_2048;
+	else
+		skb_size = PCXR_MFL_64K;
+
+	/* Extended Port Configuration */
+	wrl(pep,
+	    PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte prefix aligns IP hdr */
+	    PCXR_DSCP_EN |		 /* Enable DSCP in IP */
+	    skb_size | PCXR_FLP |	 /* do not force link pass */
+	    PCXR_TX_HIGH_PRI);		 /* Transmit - high priority queue */
+
+	return 0;
+}
+
+static int pxa168_init_hw(struct pxa168_eth_private *pep)
+{
+	int err = 0;
+
+	/* Disable interrupts */
+	wrl(pep, INT_MASK, 0);
+	wrl(pep, INT_CAUSE, 0);
+	/* Write to ICR to clear interrupts. */
+	wrl(pep, INT_W_CLEAR, 0);
+	/* Abort any transmit and receive operations and put DMA
+	 * in idle state.
+	 */
+	abort_dma(pep);
+	/* Initialize address hash table */
+	err = init_hash_table(pep);
+	if (err)
+		return err;
+	/* SDMA configuration */
+	wrl(pep, SDMA_CONFIG, SDCR_BSZ8 |	/* Burst size = 32 bytes */
+	    SDCR_RIFB |				/* Rx interrupt on frame */
+	    SDCR_BLMT |				/* Little endian transmit */
+	    SDCR_BLMR |				/* Little endian receive */
+	    SDCR_RC_MAX_RETRANS);		/* Max retransmit count */
+	/* Port Configuration */
+	wrl(pep, PORT_CONFIG, PCR_HS);		/* Hash size is 1/2kb */
+	set_port_config_ext(pep);
+
+	return err;
+}
+
+static int rxq_init(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	struct rx_desc *p_rx_desc;
+	int size = 0, i = 0;
+	int rx_desc_num = pep->rx_ring_size;
+
+	/* Allocate RX skb rings */
+	pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
+			     GFP_KERNEL);
+	if (!pep->rx_skb) {
+		printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
+		return -ENOMEM;
+	}
+	/* Allocate RX ring */
+	pep->rx_desc_count = 0;
+	size = pep->rx_ring_size * sizeof(struct rx_desc);
+	pep->rx_desc_area_size = size;
+	pep->p_rx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
+						&pep->rx_desc_dma, GFP_KERNEL);
+	if (!pep->p_rx_desc_area) {
+		printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
+		       dev->name, size);
+		goto out;
+	}
+	memset((void *)pep->p_rx_desc_area, 0, size);
+	/* initialize the next_desc_ptr links in the Rx descriptors ring */
+	p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
+	for (i = 0; i < rx_desc_num; i++) {
+		p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
+		    ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
+	}
+	/* Save Rx desc pointer to driver struct. */
+	pep->rx_curr_desc_q = 0;
+	pep->rx_used_desc_q = 0;
+	pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
+	return 0;
+out:
+	kfree(pep->rx_skb);
+	return -ENOMEM;
+}
+
+static void rxq_deinit(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	int curr;
+
+	/* Free preallocated skb's on RX rings */
+	for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size; curr++) {
+		if (pep->rx_skb[curr]) {
+			dev_kfree_skb(pep->rx_skb[curr]);
+			pep->rx_desc_count--;
+		}
+	}
+	if (pep->rx_desc_count)
+		printk(KERN_ERR
+		       "Error in freeing Rx Ring. %d skb's still\n",
+		       pep->rx_desc_count);
+	/* Free RX ring */
+	if (pep->p_rx_desc_area)
+		dma_free_coherent(pep->dev->dev.parent, pep->rx_desc_area_size,
+				  pep->p_rx_desc_area, pep->rx_desc_dma);
+	kfree(pep->rx_skb);
+}
+
+static int txq_init(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	struct tx_desc *p_tx_desc;
+	int size = 0, i = 0;
+	int tx_desc_num = pep->tx_ring_size;
+
+	pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
+			     GFP_KERNEL);
+	if (!pep->tx_skb) {
+		printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
+		return -ENOMEM;
+	}
+	/* Allocate TX ring */
+	pep->tx_desc_count = 0;
+	size = pep->tx_ring_size * sizeof(struct tx_desc);
+	pep->tx_desc_area_size = size;
+	pep->p_tx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
+						&pep->tx_desc_dma, GFP_KERNEL);
+	if (!pep->p_tx_desc_area) {
+		printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",
+		       dev->name, size);
+		goto out;
+	}
+	memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
+	/* Initialize the next_desc_ptr links in the Tx descriptors ring */
+	p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
+	for (i = 0; i < tx_desc_num; i++) {
+		p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
+		    ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
+	}
+	pep->tx_curr_desc_q = 0;
+	pep->tx_used_desc_q = 0;
+	pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
+	return 0;
+out:
+	kfree(pep->tx_skb);
+	return -ENOMEM;
+}
+
+static void txq_deinit(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+
+	/* Free outstanding skb's on TX ring */
+	txq_reclaim(dev, 1);
+	BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
+	/* Free TX ring */
+	if (pep->p_tx_desc_area)
+		dma_free_coherent(pep->dev->dev.parent, pep->tx_desc_area_size,
+				  pep->p_tx_desc_area, pep->tx_desc_dma);
+	kfree(pep->tx_skb);
+}
+
+static int pxa168_eth_open(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	int err;
+
+	err = request_irq(dev->irq, pxa168_eth_int_handler,
+			  IRQF_DISABLED, dev->name, dev);
+	if (err) {
+		dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
+		return -EAGAIN;
+	}
+	pep->rx_resource_err = 0;
+	err = rxq_init(dev);
+	if (err != 0)
+		goto out_free_irq;
+	err = txq_init(dev);
+	if (err != 0)
+		goto out_free_rx_skb;
+	pep->rx_used_desc_q = 0;
+	pep->rx_curr_desc_q = 0;
+
+	/* Fill RX ring with skb's */
+	rxq_refill(dev);
+	pep->rx_used_desc_q = 0;
+	pep->rx_curr_desc_q = 0;
+	netif_carrier_off(dev);
+	eth_port_start(dev);
+	napi_enable(&pep->napi);
+	return 0;
+out_free_rx_skb:
+	rxq_deinit(dev);
+out_free_irq:
+	free_irq(dev->irq, dev);
+	return err;
+}
+
+static int pxa168_eth_stop(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	eth_port_reset(dev);
+
+	/* Disable interrupts */
+	wrl(pep, INT_MASK, 0);
+	wrl(pep, INT_CAUSE, 0);
+	/* Write to ICR to clear interrupts. */
+	wrl(pep, INT_W_CLEAR, 0);
+	napi_disable(&pep->napi);
+	del_timer_sync(&pep->timeout);
+	netif_carrier_off(dev);
+	free_irq(dev->irq, dev);
+	rxq_deinit(dev);
+	txq_deinit(dev);
+
+	return 0;
+}
+
+static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
+{
+	int retval;
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+
+	if ((mtu > 9500) || (mtu < 68))
+		return -EINVAL;
+
+	dev->mtu = mtu;
+	retval = set_port_config_ext(pep);
+
+	if (!netif_running(dev))
+		return 0;
+
+	/*
+	 * Stop and then re-open the interface. This will allocate RX
+	 * skbs of the new MTU.
+	 * There is a possible danger that the open will not succeed,
+	 * due to memory being full.
+	 */
+	pxa168_eth_stop(dev);
+	if (pxa168_eth_open(dev)) {
+		dev_printk(KERN_ERR, &dev->dev,
+			   "fatal error on re-opening device after "
+			   "MTU change\n");
+	}
+
+	return 0;
+}
+
+static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
+{
+	int tx_desc_curr;
+
+	tx_desc_curr = pep->tx_curr_desc_q;
+	pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
+	BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
+	pep->tx_desc_count++;
+
+	return tx_desc_curr;
+}
+
+static int pxa168_rx_poll(struct napi_struct *napi, int budget)
+{
+	struct pxa168_eth_private *pep =
+	    container_of(napi, struct pxa168_eth_private, napi);
+	struct net_device *dev = pep->dev;
+	int work_done = 0;
+
+	if (unlikely(pep->work_todo & WORK_LINK)) {
+		pep->work_todo &= ~(WORK_LINK);
+		handle_link_event(pep);
+	}
+	/*
+	 * We call txq_reclaim every time since in NAPI interupts are disabled
+	 * and due to this we miss the TX_DONE interrupt,which is not updated in
+	 * interrupt status register.
+	 */
+	txq_reclaim(dev, 0);
+	if (netif_queue_stopped(dev)
+	    && pep->tx_ring_size - pep->tx_desc_count > 1) {
+		netif_wake_queue(dev);
+	}
+	work_done = rxq_process(dev, budget);
+	if (work_done < budget) {
+		napi_complete(napi);
+		wrl(pep, INT_MASK, ALL_INTS);
+	}
+
+	return work_done;
+}
+
+static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	struct net_device_stats *stats = &dev->stats;
+	struct tx_desc *desc;
+	int tx_index;
+	int length;
+
+	tx_index = eth_alloc_tx_desc_index(pep);
+	desc = &pep->p_tx_desc_area[tx_index];
+	length = skb->len;
+	pep->tx_skb[tx_index] = skb;
+	desc->byte_cnt = length;
+	desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
+	wmb();
+	desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
+			TX_ZERO_PADDING | TX_LAST_DESC | TX_EN_INT;
+	wmb();
+	wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
+
+	stats->tx_bytes += skb->len;
+	stats->tx_packets++;
+	dev->trans_start = jiffies;
+	if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
+		/* We handled the current skb, but now we are out of space.*/
+		netif_stop_queue(dev);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static int smi_wait_ready(struct pxa168_eth_private *pep)
+{
+	int i = 0;
+
+	/* wait for the SMI register to become available */
+	for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
+		if (i == PHY_WAIT_ITERATIONS)
+			return -ETIMEDOUT;
+		msleep(10);
+	}
+
+	return 0;
+}
+
+static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+	struct pxa168_eth_private *pep = bus->priv;
+	int i = 0;
+	int val;
+
+	if (smi_wait_ready(pep)) {
+		printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
+		return -ETIMEDOUT;
+	}
+	wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
+	/* now wait for the data to be valid */
+	for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
+		if (i == PHY_WAIT_ITERATIONS) {
+			printk(KERN_WARNING
+				"pxa168_eth: SMI bus read not valid\n");
+			return -ENODEV;
+		}
+		msleep(10);
+	}
+
+	return val & 0xffff;
+}
+
+static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
+			    u16 value)
+{
+	struct pxa168_eth_private *pep = bus->priv;
+
+	if (smi_wait_ready(pep)) {
+		printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
+	    SMI_OP_W | (value & 0xffff));
+
+	if (smi_wait_ready(pep)) {
+		printk(KERN_ERR "pxa168_eth: SMI bus busy timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
+			       int cmd)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	if (pep->phy != NULL)
+		return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
+
+	return -EOPNOTSUPP;
+}
+
+static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr)
+{
+	struct mii_bus *bus = pep->smi_bus;
+	struct phy_device *phydev;
+	int start;
+	int num;
+	int i;
+
+	if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
+		/* Scan entire range */
+		start = ethernet_phy_get(pep);
+		num = 32;
+	} else {
+		/* Use phy addr specific to platform */
+		start = phy_addr & 0x1f;
+		num = 1;
+	}
+	phydev = NULL;
+	for (i = 0; i < num; i++) {
+		int addr = (start + i) & 0x1f;
+		if (bus->phy_map[addr] == NULL)
+			mdiobus_scan(bus, addr);
+
+		if (phydev == NULL) {
+			phydev = bus->phy_map[addr];
+			if (phydev != NULL)
+				ethernet_phy_set_addr(pep, addr);
+		}
+	}
+
+	return phydev;
+}
+
+static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
+{
+	struct phy_device *phy = pep->phy;
+	ethernet_phy_reset(pep);
+
+	phy_attach(pep->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_MII);
+
+	if (speed == 0) {
+		phy->autoneg = AUTONEG_ENABLE;
+		phy->speed = 0;
+		phy->duplex = 0;
+		phy->supported &= PHY_BASIC_FEATURES;
+		phy->advertising = phy->supported | ADVERTISED_Autoneg;
+	} else {
+		phy->autoneg = AUTONEG_DISABLE;
+		phy->advertising = 0;
+		phy->speed = speed;
+		phy->duplex = duplex;
+	}
+	phy_start_aneg(phy);
+}
+
+static int ethernet_phy_setup(struct net_device *dev)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+
+	if (pep->pd != NULL) {
+		if (pep->pd->init)
+			pep->pd->init();
+	}
+	pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
+	if (pep->phy != NULL)
+		phy_init(pep, pep->pd->speed, pep->pd->duplex);
+	update_hash_table_mac_address(pep, NULL, dev->dev_addr);
+
+	return 0;
+}
+
+static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+	int err;
+
+	err = phy_read_status(pep->phy);
+	if (err == 0)
+		err = phy_ethtool_gset(pep->phy, cmd);
+
+	return err;
+}
+
+static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+
+	return phy_ethtool_sset(pep->phy, cmd);
+}
+
+static void pxa168_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strncpy(info->driver, DRIVER_NAME, 32);
+	strncpy(info->version, DRIVER_VERSION, 32);
+	strncpy(info->fw_version, "N/A", 32);
+	strncpy(info->bus_info, "N/A", 32);
+}
+
+static u32 pxa168_get_link(struct net_device *dev)
+{
+	return !!netif_carrier_ok(dev);
+}
+
+static const struct ethtool_ops pxa168_ethtool_ops = {
+	.get_settings = pxa168_get_settings,
+	.set_settings = pxa168_set_settings,
+	.get_drvinfo = pxa168_get_drvinfo,
+	.get_link = pxa168_get_link,
+};
+
+static const struct net_device_ops pxa168_eth_netdev_ops = {
+	.ndo_open = pxa168_eth_open,
+	.ndo_stop = pxa168_eth_stop,
+	.ndo_start_xmit = pxa168_eth_start_xmit,
+	.ndo_set_rx_mode = pxa168_eth_set_rx_mode,
+	.ndo_set_mac_address = pxa168_eth_set_mac_address,
+	.ndo_validate_addr = eth_validate_addr,
+	.ndo_do_ioctl = pxa168_eth_do_ioctl,
+	.ndo_change_mtu = pxa168_eth_change_mtu,
+	.ndo_tx_timeout = pxa168_eth_tx_timeout,
+};
+
+static int pxa168_eth_probe(struct platform_device *pdev)
+{
+	struct pxa168_eth_private *pep = NULL;
+	struct net_device *dev = NULL;
+	struct resource *res;
+	struct clk *clk;
+	int err;
+
+	printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
+
+	clk = clk_get(&pdev->dev, "MFUCLK");
+	if (IS_ERR(clk)) {
+		printk(KERN_ERR "%s: Fast Ethernet failed to get clock\n",
+			DRIVER_NAME);
+		return -ENODEV;
+	}
+	clk_enable(clk);
+
+	dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
+	if (!dev) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	platform_set_drvdata(pdev, dev);
+	pep = netdev_priv(dev);
+	pep->dev = dev;
+	pep->clk = clk;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		err = -ENODEV;
+		goto out;
+	}
+	pep->base = ioremap(res->start, res->end - res->start + 1);
+	if (pep->base == NULL) {
+		err = -ENOMEM;
+		goto out;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	BUG_ON(!res);
+	dev->irq = res->start;
+	dev->netdev_ops = &pxa168_eth_netdev_ops;
+	dev->watchdog_timeo = 2 * HZ;
+	dev->base_addr = 0;
+	SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
+
+	INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
+
+	printk(KERN_INFO "%s:Using random mac address\n", DRIVER_NAME);
+	random_ether_addr(dev->dev_addr);
+
+	pep->pd = pdev->dev.platform_data;
+	pep->rx_ring_size = NUM_RX_DESCS;
+	if (pep->pd->rx_queue_size)
+		pep->rx_ring_size = pep->pd->rx_queue_size;
+
+	pep->tx_ring_size = NUM_TX_DESCS;
+	if (pep->pd->tx_queue_size)
+		pep->tx_ring_size = pep->pd->tx_queue_size;
+
+	pep->port_num = pep->pd->port_number;
+	/* Hardware supports only 3 ports */
+	BUG_ON(pep->port_num > 2);
+	netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
+
+	memset(&pep->timeout, 0, sizeof(struct timer_list));
+	init_timer(&pep->timeout);
+	pep->timeout.function = rxq_refill_timer_wrapper;
+	pep->timeout.data = (unsigned long)pep;
+
+	pep->smi_bus = mdiobus_alloc();
+	if (pep->smi_bus == NULL) {
+		err = -ENOMEM;
+		goto out;
+	}
+	pep->smi_bus->priv = pep;
+	pep->smi_bus->name = "pxa168_eth smi";
+	pep->smi_bus->read = pxa168_smi_read;
+	pep->smi_bus->write = pxa168_smi_write;
+	snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
+	pep->smi_bus->parent = &pdev->dev;
+	pep->smi_bus->phy_mask = 0xffffffff;
+	if (mdiobus_register(pep->smi_bus) < 0) {
+		err = -ENOMEM;
+		goto out;
+	}
+	pxa168_init_hw(pep);
+	err = ethernet_phy_setup(dev);
+	if (err)
+		goto out;
+	SET_NETDEV_DEV(dev, &pdev->dev);
+	err = register_netdev(dev);
+	if (err)
+		goto out;
+	return 0;
+out:
+	if (pep->clk) {
+		clk_disable(pep->clk);
+		clk_put(pep->clk);
+		pep->clk = NULL;
+	}
+	if (pep->base) {
+		iounmap(pep->base);
+		pep->base = NULL;
+	}
+	if (dev)
+		free_netdev(dev);
+	return err;
+}
+
+static int pxa168_eth_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct pxa168_eth_private *pep = netdev_priv(dev);
+
+	if (pep->htpr) {
+		dma_free_coherent(pep->dev->dev.parent, HASH_ADDR_TABLE_SIZE,
+				  pep->htpr, pep->htpr_dma);
+		pep->htpr = NULL;
+	}
+	if (pep->clk) {
+		clk_disable(pep->clk);
+		clk_put(pep->clk);
+		pep->clk = NULL;
+	}
+	if (pep->phy != NULL)
+		phy_detach(pep->phy);
+
+	iounmap(pep->base);
+	pep->base = NULL;
+	unregister_netdev(dev);
+	flush_scheduled_work();
+	free_netdev(dev);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static void pxa168_eth_shutdown(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	eth_port_reset(dev);
+}
+
+#ifdef CONFIG_PM
+static int pxa168_eth_resume(struct platform_device *pdev)
+{
+	return -ENOSYS;
+}
+
+static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	return -ENOSYS;
+}
+
+#else
+#define pxa168_eth_resume NULL
+#define pxa168_eth_suspend NULL
+#endif
+
+static struct platform_driver pxa168_eth_driver = {
+	.probe = pxa168_eth_probe,
+	.remove = pxa168_eth_remove,
+	.shutdown = pxa168_eth_shutdown,
+	.resume = pxa168_eth_resume,
+	.suspend = pxa168_eth_suspend,
+	.driver = {
+		   .name = DRIVER_NAME,
+		   },
+};
+
+static int __init pxa168_init_module(void)
+{
+	return platform_driver_register(&pxa168_eth_driver);
+}
+
+static void __exit pxa168_cleanup_module(void)
+{
+	platform_driver_unregister(&pxa168_eth_driver);
+}
+
+module_init(pxa168_init_module);
+module_exit(pxa168_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
+MODULE_ALIAS("platform:pxa168_eth");
diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
new file mode 100644
index 0000000..18d75e7
--- /dev/null
+++ b/include/linux/pxa168_eth.h
@@ -0,0 +1,30 @@
+/*
+ *pxa168 ethernet platform device data definition file.
+ */
+#ifndef __LINUX_PXA168_ETH_H
+#define __LINUX_PXA168_ETH_H
+
+struct pxa168_eth_platform_data {
+	int	port_number;
+	int	phy_addr;
+
+	/*
+	 * If speed is 0, then speed and duplex are autonegotiated.
+	 */
+	int	speed;		/* 0, SPEED_10, SPEED_100 */
+	int	duplex;		/* DUPLEX_HALF or DUPLEX_FULL */
+
+	/*
+	 * Override default RX/TX queue sizes if nonzero.
+	 */
+	int	rx_queue_size;
+	int	tx_queue_size;
+
+	/*
+	 * init callback is used for board specific initialization
+	 * e.g on Aspenite its used to initialize the PHY transceiver.
+	 */
+	int (*init)(void);
+};
+
+#endif /* __LINUX_PXA168_ETH_H */
-- 
1.5.3.3




^ permalink raw reply related

* Re: [PATCH] orinoco: Fix walking past the end of the buffer
From: Dave Kilroy @ 2010-08-14  9:45 UTC (permalink / raw)
  To: Denis Kirjanov
  Cc: linville, proski, hermes, davem, linux-wireless, orinoco-devel,
	netdev
In-Reply-To: <20100811203216.GA25168@hera.kernel.org>

On Wed, Aug 11, 2010 at 9:32 PM, Denis Kirjanov <dkirjanov@kernel.org> wrote:
> diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
> index 077baa8..191bc03 100644
> --- a/drivers/net/wireless/orinoco/hw.c
> +++ b/drivers/net/wireless/orinoco/hw.c
> @@ -765,9 +765,12 @@ int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate)
>                        if (bitrate_table[i].intersil_txratectrl == val)
>                                break;
>
> -               if (i >= BITRATE_TABLE_SIZE)
> +               if (i >= BITRATE_TABLE_SIZE) {
>                        printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
>                               priv->ndev->name, val);
> +                       *bitrate = 100001; /* Mark as invalid */

We should propogate the failure by returning an error in the return
code rather than a cryptic bitrate value. The calling function(s)
should then propogate the error through wext/cfg80211 as appropriate.

> +                       break;
> +               }
>
>                *bitrate = bitrate_table[i].bitrate * 100000;
>                break;

We can also make the structure easier to understand by setting the
bitrate within the for loop. Something like the following (I only have
access to gmail ATM, so can't format a proper patch):

		for (i = 0; i < BITRATE_TABLE_SIZE; i++)
			if (bitrate_table[i].intersil_txratectrl == val) {
				*bitrate = bitrate_table[i].bitrate * 100000;
				break;
			}

		if (i >= BITRATE_TABLE_SIZE) {
			printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
			       priv->ndev->name, val);
			err = -EIO; /* maybe chose a better value... */
		}

		break;

Could you update the patch along those lines please?

Thanks,

Dave.

^ permalink raw reply

* Re: Using virtio as a physical (wire-level) transport
From: Alexander Graf @ 2010-08-14 11:34 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: Michael S. Tsirkin, netdev@vger.kernel.org, Zang Roy,
	virtualization@lists.linux-foundation.org
In-Reply-To: <20100806153427.GB12392@ovro.caltech.edu>


Am 06.08.2010 um 11:34 schrieb "Ira W. Snyder" <iws@ovro.caltech.edu>:

> On Fri, Aug 06, 2010 at 02:20:42AM +0300, Michael S. Tsirkin wrote:
>> On Thu, Aug 05, 2010 at 04:01:03PM -0700, Ira W. Snyder wrote:
>>> On Fri, Aug 06, 2010 at 12:30:50AM +0300, Michael S. Tsirkin wrote:
>>>> Hi Ira,
>>>> 
>>>>> Making my life harder since the last time I tried this, mainline commit
>>>>> 7c5e9ed0c (virtio_ring: remove a level of indirection) has removed the
>>>>> possibility of using an alternative virtqueue implementation. The commit
>>>>> message suggests that you might be willing to add this capability back.
>>>>> Would this be an option?
>>>> 
>>>> Sorry about that.
>>>> 
>>>> With respect to this commit, we only had one implementation upstream
>>>> and extra levels of indirection made extending the API
>>>> much harder for no apparent benefit.
>>>> 
>>>> When there's more than one ring implementation with very small amount of
>>>> common code, I think that it might make sense to readd the indirection
>>>> back, to separate the code cleanly.
>>>> 
>>>> OTOH if the two implementations share a lot of code, I think that it
>>>> might be better to just add a couple of if statements here and there.
>>>> This way compiler even might have a chance to compile the code out if
>>>> the feature is disabled in kernel config.
>>>> 
>>> 
>>> The virtqueue implementation I envision will be almost identical to the
>>> current virtio_ring virtqueue implementation, with the following
>>> exceptions:
>>> 
>>> * the "shared memory" will actually be remote, on the PCI BAR of a device
>>> * iowrite32(), ioread32() and friends will be needed to access the memory
>>> * there will only be a fixed number of virtqueues available, due to PCI
>>>  BAR size
>>> * cross-endian virtqueues must work
>>> * kick needs to be cross-machine (using PCI IRQ's)
>>> 
>>> I don't think it is feasible to add this to the existing implementation.
>>> I think the requirement of being cross-endian will be the hardest to
>>> overcome. Rusty did not envision the cross-endian use case when he
>>> designed this, and it shows, in virtio_ring, virtio_net and vhost. I
>>> have no idea what to do about this. Do you have any ideas?
>> 
>> My guess is sticking an if around each access in virtio would hurt,
>> if this is what you are asking about.
>> 
> 
> Yes, I think so too. I think using le32 byte order everywhere in virtio
> would be a good thing. In addition, it means that on all x86, things
> continue to work as-is. It would also have no overhead in the most
> common case: x86-on-x86.
> 
> This problem is not limited to my new use of virtio. Virtio is
> completely useless in a relatively common virtualization scenario:
> x86 host with qemu-ppc guest. Or any other big endian guest system.

This one actually works because we know that we're building for a BE guest. But I agree that it's a mess and clearly a very incorrect design decision.

>> Just a crazy idea: vhost already uses wrappers like get_user etc,
>> maybe when building kernel for your board you could
>> redefine these to also byteswap?
>> 
> 
> I think idea is clever, but also psychotic :) I'm sure it would work,
> but that only solves the problem of virtio ring descriptors. The
> virtio-net header contains several __u16 fields which would also need
> to be fixed-endianness.

I'd vote for defining virtio v2 that makes everything LE. Maybe we could even have an LE capability with a grace period of phasing out non-LE capable hosts and guests.


Alex


^ permalink raw reply

* Re: QoS weirdness : HTB accuracy
From: Jussi Kivilinna @ 2010-08-14 17:27 UTC (permalink / raw)
  To: Andrew Beverley
  Cc: Jesper Dangaard Brouer, Julien Vehent, Philip A. Prindeville,
	Netdev, netfilter, hawk
In-Reply-To: <1281549546.1432.248.camel@andybev>

Quoting "Andrew Beverley" <andy@andybev.com>:

> On Wed, 2010-07-07 at 18:07 +0300, Jussi Kivilinna wrote:
>> >>>> I was, in fact, an error in my ruleset. I had put the  
>> 'linklayer atm' at
>> >>>> both the branch and leaf levels, so the overhead was computed twice,
>> >>>> creating those holes in the bandwidth.
>> >>>
>> >>> I am seeing similar behaviour with my setup. Am I making the same
>> >>> mistake? A subset of my rules is as follows:
>> >>>
>> >>> tc qdisc add dev ppp0 root handle 1: htb r2q 1
>> >>>
>> >>> tc class add dev ppp0 parent 1: classid 1:1 htb \
>> >>>    rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit \
>> >>>    overhead $overhead linklayer atm                   <------- Here
>> >>>
>> >>> tc class add dev ppp0 parent 1:1 classid 1:10 htb \
>> >>>    rate 612kbit ceil 612kbit prio 0 \
>> >>>    overhead $overhead linklayer atm                   <------- And here
>> >>>
>> >>> tc qdisc add dev ppp0 parent 1:10 handle 4210: \
>> >>>    sfq perturb 10 limit 50
>> >>>
>> >>> tc filter add dev ppp0 parent 1:0 protocol ip \
>> >>>    prio 10 handle 10 fw flowid 1:10
>> >>
>> >> I removed the overhead option on the first leaf, and the speeds change
>> >> to what I expect. However, the rules above are taken straight from the
>> >> ADSL Optimizer project, which was the source of the original overhead
>> >> patch for tc. So is the ADSL Optimizer project wrong?
>> >
>> > After looking at the HTB kernel code I believe that the ADSL
>> > Optimizer project is NOT wrong.  You should/must set the linklayer
>> > option on both the root class and leaf (else you would be charging
>> > the root/parent node too little).
>> >
>>
>> It's been while I looked at the linklayer/size-table code, but if I
>> remember right overhead is calculated with first linklayer packet sees
>> in qdisc. So when packet goes to leaf with 'linklayer atm', packet get
>> packet size with overhead for ATM and root linklayer is not used for
>> that packet. Because of this you can have leafs with different
>> overheads (pppoe, ipv6-tunnel, etc, with ) and all get right
>> overhead... ..
>>
>> ..
>>
>> ...Uh oh...
>>
>> HTB still has linklayer/overhead of its own, I was talking about the
>> generic linklayer code that can be used with all qdiscs. Never mind,
>> sorry.
>
> Ah, I hadn't come across the tc-stab functionality until now. So that
> does exactly the same for all qdiscs as the original ATM patch does for
> HTB? But they work slightly differently as you have alluded to above?
>

Yes, tc-stab works with all qdiscs. Packet size is calculated  
everytime when qdisc_enqueue() is called and qdisc has size-table set.  
When none of qdiscs has size-table, packet size ends up being same as  
skb->len. So if leaf has size-table, that takes preference over  
earlier parent size-tables.

-Jussi


^ permalink raw reply

* Re: [GIT] Networking
From: Linus Torvalds @ 2010-08-14 18:05 UTC (permalink / raw)
  To: David Miller; +Cc: akpm, netdev, linux-kernel
In-Reply-To: <20100812.161050.246523792.davem@davemloft.net>

David,
 I completely screwed up locking in my VM guard page patch, so when I
verified my fix for that I tried to enable all the lock debug crud I
possibly could.

And as a result, I got a locking error report, but it had nothing to
do with the VM guard page any more (so hopefully I finally fixed my
mindless code-drivel correctly. I'm a bit ashamed of myself).

Anyway, the lock warning I do get seems to be networking-related, and
is appended. Does this ring any bells? It could easily be something
old: I turn on lock debugging only when I look for bugs (or when
people point out bugs that I've created :^/ )

The only thing that seems to be related that google can find is pretty
recent too: a report from Valdis Kletnieks about this apparently
happening on e1000e too (Subject "mmotm 2010-08-11 - lockdep whinges
at e1000e driver ifconfig up"). So it does seem to be pretty recent.

Hmm? Everything obviously still works, but judging by the lockdep
report this might be a deadlock situation (lock taken in softirq _and_
outside softirq without disabling bhs)

                                    Linus

---
  r8169 0000:01:00.0: eth0: link up
  r8169 0000:01:00.0: eth0: link up

  =================================
  [ INFO: inconsistent lock state ]
  2.6.35-07956-g92fa5bd9-dirty #7
  ---------------------------------
  inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage.
  dbus-daemon/2432 [HC0[0]:SC1[2]:HE1:SE0] takes:
   (&(&lock->lock)->rlock){+.?...}, at: [<ffffffff814e9fd8>]
ip6t_do_table+0x7f/0x3e7
  {SOFTIRQ-ON-W} state was registered at:
    [<ffffffff8105eb80>] __lock_acquire+0x756/0x1712
    [<ffffffff8105fbbf>] lock_acquire+0x83/0x9d
    [<ffffffff81505c77>] _raw_spin_lock+0x31/0x64
    [<ffffffff814e943d>] get_counters+0xa4/0x139
    [<ffffffff814e9509>] alloc_counters+0x37/0x42
    [<ffffffff814eab47>] do_ip6t_get_ctl+0x107/0x35e
    [<ffffffff81463241>] nf_sockopt+0x55/0x81
    [<ffffffff81463280>] nf_getsockopt+0x13/0x15
    [<ffffffff814d0cce>] ipv6_getsockopt+0x7f/0xb5
    [<ffffffff814d7e29>] rawv6_getsockopt+0x3d/0x46
    [<ffffffff81434fae>] sock_common_getsockopt+0xf/0x11
    [<ffffffff814323ff>] sys_getsockopt+0x75/0x96
    [<ffffffff81001eab>] system_call_fastpath+0x16/0x1b
  irq event stamp: 21072
  hardirqs last  enabled at (21072): [<ffffffff8103b5cf>]
local_bh_enable+0xbd/0xc2
  hardirqs last disabled at (21071): [<ffffffff8103b564>]
local_bh_enable+0x52/0xc2
  softirqs last  enabled at (20000): [<ffffffff814bd355>]
unix_create1+0x164/0x17c
  softirqs last disabled at (21037): [<ffffffff81002dcc>] call_softirq+0x1c/0x28

  other info that might help us debug this:
  3 locks held by dbus-daemon/2432:
   #0:  (&idev->mc_ifc_timer){+.-...}, at: [<ffffffff810404f6>]
run_timer_softirq+0x14e/0x28c
   #1:  (rcu_read_lock){.+.+..}, at: [<ffffffff814dc9b2>] mld_sendpack+0x0/0x3bd
   #2:  (rcu_read_lock){.+.+..}, at: [<ffffffff81461ed5>] nf_hook_slow+0x0/0x10a

  stack backtrace:
  Pid: 2432, comm: dbus-daemon Not tainted 2.6.35-07956-g92fa5bd9-dirty #7
  Call Trace:
   <IRQ>  [<ffffffff8105bf16>] print_usage_bug+0x1a4/0x1b5
   [<ffffffff8100c7d3>] ? save_stack_trace+0x2a/0x47
   [<ffffffff8105cb6c>] ? check_usage_forwards+0x0/0xc6
   [<ffffffff8105c211>] mark_lock+0x2ea/0x552
   [<ffffffff8105eb07>] __lock_acquire+0x6dd/0x1712
   [<ffffffff8105246e>] ? local_clock+0x2b/0x3c
   [<ffffffff8105b2e8>] ? lock_release_holdtime+0x1c/0x123
   [<ffffffff8105fbbf>] lock_acquire+0x83/0x9d
   [<ffffffff814e9fd8>] ? ip6t_do_table+0x7f/0x3e7
   [<ffffffff81505c77>] _raw_spin_lock+0x31/0x64
   [<ffffffff814e9fd8>] ? ip6t_do_table+0x7f/0x3e7
   [<ffffffff814e9fd8>] ip6t_do_table+0x7f/0x3e7
   [<ffffffff814ebd87>] ip6table_filter_hook+0x17/0x1c
   [<ffffffff81461e92>] nf_iterate+0x41/0x84
   [<ffffffff814db3ba>] ? dst_output+0x0/0x58
   [<ffffffff81461f63>] nf_hook_slow+0x8e/0x10a
   [<ffffffff814db3ba>] ? dst_output+0x0/0x58
   [<ffffffff814dcc00>] mld_sendpack+0x24e/0x3bd
   [<ffffffff8105c4cb>] ? mark_held_locks+0x52/0x70
   [<ffffffff814dd478>] mld_ifc_timer_expire+0x24f/0x288
   [<ffffffff814dd229>] ? mld_ifc_timer_expire+0x0/0x288
   [<ffffffff81040584>] run_timer_softirq+0x1dc/0x28c
   [<ffffffff810404f6>] ? run_timer_softirq+0x14e/0x28c
   [<ffffffff8103b6dc>] ? __do_softirq+0x69/0x13d
   [<ffffffff8103b715>] __do_softirq+0xa2/0x13d
   [<ffffffff810590eb>] ? tick_program_event+0x25/0x27
   [<ffffffff81002dcc>] call_softirq+0x1c/0x28
   [<ffffffff81004834>] do_softirq+0x38/0x80
   [<ffffffff8103b2e0>] irq_exit+0x45/0x87
   [<ffffffff8101a278>] smp_apic_timer_interrupt+0x88/0x96
   [<ffffffff81002893>] apic_timer_interrupt+0x13/0x20
   <EOI>
  r8169: WARNING! Changing of MTU on this NIC may lead to frame
reception errors!

^ permalink raw reply

* Re: [PATCH v3]  net: add Fast Ethernet driver for PXA168.
From: Philip Rakity @ 2010-08-14 20:47 UTC (permalink / raw)
  To: Sachin Sanap
  Cc: Lennert Buytenhek, netdev@vger.kernel.org, Ashish Karkare,
	Prabhanjan Sarnaik, eric.y.miao@gmail.com, Mark Brown
In-Reply-To: <1281770569.26697.912.camel@pe-lt522.marvell.com>



I was wondering why you need to take a tx interrupt done per packet.  The only normal reason for the interrupt is to restart the higher layer when the tx queue is blocked (FULL) due to flow control or sending traffic too fast.  The original code tried to minimize tx interrupts.

The other point was the you should set the DMA ownership bit as the LAST operation after deciding if you need to set the TX interrupt bit in the descriptor and not before making the decision.

Philip


On Aug 14, 2010, at 12:22 AM, Sachin Sanap wrote:

> From df92c2959eb0e50ea63c90822fb9e1edef673ea6 Mon Sep 17 00:00:00 2001
> From: Sachin Sanap <ssanap@marvell.com>
> Date: Sat, 14 Aug 2010 17:11:05 +0530
> Subject: [PATCH] net: add Fast Ethernet driver for PXA168.
>
> Signed-off-by: Sachin Sanap <ssanap@marvell.com>
> ---
> V3:
> * TX_DONE interrupt is now generated for every packet.
> * MTU can now be configured upto 9500 bytes.
> * TX/RX ring sizes are now platform configurable.
> * Remove the CPU hogging and yield the CPU while SMI operations take time.
> * Cleanup the hash table for old MAC address entries in set_rx_mode function.
>
> drivers/net/Kconfig        |   10 +
> drivers/net/Makefile       |    1 +
> drivers/net/pxa168_eth.c   | 1666 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pxa168_eth.h |   30 +
> 4 files changed, 1707 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/pxa168_eth.c
> create mode 100644 include/linux/pxa168_eth.h
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index ce2fcdd..78cd7e8 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -927,6 +927,16 @@ config SMC91X
>          The module will be called smc91x.  If you want to compile it as a
>          module, say M here and read <file:Documentation/kbuild/modules.txt>.
>
> +config PXA168_ETH
> +       tristate "Marvell pxa168 ethernet support"
> +       depends on CPU_PXA168
> +       select PHYLIB
> +       help
> +         This driver supports the pxa168 Ethernet ports.
> +
> +         To compile this driver as a module, choose M here. The module
> +         will be called pxa168_eth.
> +
> config NET_NETX
>        tristate "NetX Ethernet support"
>        select MII
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 0a0512a..a42d437 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -244,6 +244,7 @@ obj-$(CONFIG_MYRI10GE) += myri10ge/
> obj-$(CONFIG_SMC91X) += smc91x.o
> obj-$(CONFIG_SMC911X) += smc911x.o
> obj-$(CONFIG_SMSC911X) += smsc911x.o
> +obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
> obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
> obj-$(CONFIG_DM9000) += dm9000.o
> obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
> diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c
> new file mode 100644
> index 0000000..ecc64d7
> --- /dev/null
> +++ b/drivers/net/pxa168_eth.c
> @@ -0,0 +1,1666 @@
> +/*
> + * PXA168 ethernet driver.
> + * Most of the code is derived from mv643xx ethernet driver.
> + *
> + * Copyright (C) 2010 Marvell International Ltd.
> + *             Sachin Sanap <ssanap@marvell.com>
> + *             Philip Rakity <prakity@marvell.com>
> + *             Mark Brown <markb@marvell.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * 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/dma-mapping.h>
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/tcp.h>
> +#include <linux/udp.h>
> +#include <linux/etherdevice.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/ethtool.h>
> +#include <linux/platform_device.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/workqueue.h>
> +#include <linux/clk.h>
> +#include <linux/phy.h>
> +#include <linux/io.h>
> +#include <linux/types.h>
> +#include <asm/pgtable.h>
> +#include <asm/system.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <asm/cacheflush.h>
> +#include <linux/pxa168_eth.h>
> +
> +#define DRIVER_NAME    "pxa168-eth"
> +#define DRIVER_VERSION "0.3"
> +
> +/*
> + * Registers
> + */
> +
> +#define PHY_ADDRESS            0x0000
> +#define SMI                    0x0010
> +#define PORT_CONFIG            0x0400
> +#define PORT_CONFIG_EXT                0x0408
> +#define PORT_COMMAND           0x0410
> +#define PORT_STATUS            0x0418
> +#define HTPR                   0x0428
> +#define SDMA_CONFIG            0x0440
> +#define SDMA_CMD               0x0448
> +#define INT_CAUSE              0x0450
> +#define INT_W_CLEAR            0x0454
> +#define INT_MASK               0x0458
> +#define ETH_F_RX_DESC_0                0x0480
> +#define ETH_C_RX_DESC_0                0x04A0
> +#define ETH_C_TX_DESC_1                0x04E4
> +
> +/* smi register */
> +#define SMI_BUSY               (1 << 28)       /* 0 - Write, 1 - Read  */
> +#define SMI_R_VALID            (1 << 27)       /* 0 - Write, 1 - Read  */
> +#define SMI_OP_W               (0 << 26)       /* Write operation      */
> +#define SMI_OP_R               (1 << 26)       /* Read operation */
> +
> +#define PHY_WAIT_ITERATIONS    10
> +
> +#define PXA168_ETH_PHY_ADDR_DEFAULT    0
> +/* RX & TX descriptor command */
> +#define BUF_OWNED_BY_DMA       (1 << 31)
> +
> +/* RX descriptor status */
> +#define RX_EN_INT              (1 << 23)
> +#define RX_FIRST_DESC          (1 << 17)
> +#define RX_LAST_DESC           (1 << 16)
> +#define RX_ERROR               (1 << 15)
> +
> +/* TX descriptor command */
> +#define TX_EN_INT              (1 << 23)
> +#define TX_GEN_CRC             (1 << 22)
> +#define TX_ZERO_PADDING                (1 << 18)
> +#define TX_FIRST_DESC          (1 << 17)
> +#define TX_LAST_DESC           (1 << 16)
> +#define TX_ERROR               (1 << 15)
> +
> +/* SDMA_CMD */
> +#define SDMA_CMD_AT            (1 << 31)
> +#define SDMA_CMD_TXDL          (1 << 24)
> +#define SDMA_CMD_TXDH          (1 << 23)
> +#define SDMA_CMD_AR            (1 << 15)
> +#define SDMA_CMD_ERD           (1 << 7)
> +
> +/* Bit definitions of the Port Config Reg */
> +#define PCR_HS                 (1 << 12)
> +#define PCR_EN                 (1 << 7)
> +#define PCR_PM                 (1 << 0)
> +
> +/* Bit definitions of the Port Config Extend Reg */
> +#define PCXR_2BSM              (1 << 28)
> +#define PCXR_DSCP_EN           (1 << 21)
> +#define PCXR_MFL_1518          (0 << 14)
> +#define PCXR_MFL_1536          (1 << 14)
> +#define PCXR_MFL_2048          (2 << 14)
> +#define PCXR_MFL_64K           (3 << 14)
> +#define PCXR_FLP               (1 << 11)
> +#define PCXR_PRIO_TX_OFF       3
> +#define PCXR_TX_HIGH_PRI       (7 << PCXR_PRIO_TX_OFF)
> +
> +/* Bit definitions of the SDMA Config Reg */
> +#define SDCR_BSZ_OFF           12
> +#define SDCR_BSZ8              (3 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ4              (2 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ2              (1 << SDCR_BSZ_OFF)
> +#define SDCR_BSZ1              (0 << SDCR_BSZ_OFF)
> +#define SDCR_BLMR              (1 << 6)
> +#define SDCR_BLMT              (1 << 7)
> +#define SDCR_RIFB              (1 << 9)
> +#define SDCR_RC_OFF            2
> +#define SDCR_RC_MAX_RETRANS    (0xf << SDCR_RC_OFF)
> +
> +/*
> + * Bit definitions of the Interrupt Cause Reg
> + * and Interrupt MASK Reg is the same
> + */
> +#define ICR_RXBUF              (1 << 0)
> +#define ICR_TXBUF_H            (1 << 2)
> +#define ICR_TXBUF_L            (1 << 3)
> +#define ICR_TXEND_H            (1 << 6)
> +#define ICR_TXEND_L            (1 << 7)
> +#define ICR_RXERR              (1 << 8)
> +#define ICR_TXERR_H            (1 << 10)
> +#define ICR_TXERR_L            (1 << 11)
> +#define ICR_TX_UDR             (1 << 13)
> +#define ICR_MII_CH             (1 << 28)
> +
> +#define ALL_INTS (ICR_TXBUF_H  | ICR_TXBUF_L  | ICR_TX_UDR |\
> +                               ICR_TXERR_H  | ICR_TXERR_L |\
> +                               ICR_TXEND_H  | ICR_TXEND_L |\
> +                               ICR_RXBUF | ICR_RXERR  | ICR_MII_CH)
> +
> +#define ETH_HW_IP_ALIGN                2       /* hw aligns IP header */
> +
> +#define NUM_RX_DESCS           64
> +#define NUM_TX_DESCS           64
> +
> +#define HASH_ADD               0
> +#define HASH_DELETE            1
> +#define HASH_ADDR_TABLE_SIZE   0x4000  /* 16K (1/2K address - PCR_HS == 1) */
> +#define HOP_NUMBER             12
> +
> +/* Bit definitions for Port status */
> +#define PORT_SPEED_100         (1 << 0)
> +#define FULL_DUPLEX            (1 << 1)
> +#define FLOW_CONTROL_ENABLED   (1 << 2)
> +#define LINK_UP                        (1 << 3)
> +
> +/* Bit definitions for work to be done */
> +#define WORK_LINK              (1 << 0)
> +#define WORK_TX_DONE           (1 << 1)
> +
> +/*
> + * Misc definitions.
> + */
> +#define SKB_DMA_REALIGN                ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
> +
> +struct rx_desc {
> +       u32 cmd_sts;            /* Descriptor command status            */
> +       u16 byte_cnt;           /* Descriptor buffer byte count         */
> +       u16 buf_size;           /* Buffer size                          */
> +       u32 buf_ptr;            /* Descriptor buffer pointer            */
> +       u32 next_desc_ptr;      /* Next descriptor pointer              */
> +};
> +
> +struct tx_desc {
> +       u32 cmd_sts;            /* Command/status field                 */
> +       u16 reserved;
> +       u16 byte_cnt;           /* buffer byte count                    */
> +       u32 buf_ptr;            /* pointer to buffer for this descriptor */
> +       u32 next_desc_ptr;      /* Pointer to next descriptor           */
> +};
> +
> +struct pxa168_eth_private {
> +       int port_num;           /* User Ethernet port number    */
> +
> +       int rx_resource_err;    /* Rx ring resource error flag */
> +
> +       /* Next available and first returning Rx resource */
> +       int rx_curr_desc_q, rx_used_desc_q;
> +
> +       /* Next available and first returning Tx resource */
> +       int tx_curr_desc_q, tx_used_desc_q;
> +
> +       struct rx_desc *p_rx_desc_area;
> +       dma_addr_t rx_desc_dma;
> +       int rx_desc_area_size;
> +       struct sk_buff **rx_skb;
> +
> +       struct tx_desc *p_tx_desc_area;
> +       dma_addr_t tx_desc_dma;
> +       int tx_desc_area_size;
> +       struct sk_buff **tx_skb;
> +
> +       struct work_struct tx_timeout_task;
> +
> +       struct net_device *dev;
> +       struct napi_struct napi;
> +       u8 work_todo;
> +       int skb_size;
> +
> +       struct net_device_stats stats;
> +       /* Size of Tx Ring per queue */
> +       int tx_ring_size;
> +       /* Number of tx descriptors in use */
> +       int tx_desc_count;
> +       /* Size of Rx Ring per queue */
> +       int rx_ring_size;
> +       /* Number of rx descriptors in use */
> +       int rx_desc_count;
> +
> +       /*
> +        * Used in case RX Ring is empty, which can occur when
> +        * system does not have resources (skb's)
> +        */
> +       struct timer_list timeout;
> +       struct mii_bus *smi_bus;
> +       struct phy_device *phy;
> +
> +       /* clock */
> +       struct clk *clk;
> +       struct pxa168_eth_platform_data *pd;
> +       /*
> +        * Ethernet controller base address.
> +        */
> +       void __iomem *base;
> +
> +       /* Pointer to the hardware address filter table */
> +       void *htpr;
> +       dma_addr_t htpr_dma;
> +};
> +
> +struct addr_table_entry {
> +       __le32 lo;
> +       __le32 hi;
> +};
> +
> +/* Bit fields of a Hash Table Entry */
> +enum hash_table_entry {
> +       HASH_ENTRY_VALID = 1,
> +       SKIP = 2,
> +       HASH_ENTRY_RECEIVE_DISCARD = 4,
> +       HASH_ENTRY_RECEIVE_DISCARD_BIT = 2
> +};
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd);
> +static int pxa168_init_hw(struct pxa168_eth_private *pep);
> +static void eth_port_reset(struct net_device *dev);
> +static void eth_port_start(struct net_device *dev);
> +static int pxa168_eth_open(struct net_device *dev);
> +static int pxa168_eth_stop(struct net_device *dev);
> +static int ethernet_phy_setup(struct net_device *dev);
> +
> +static inline u32 rdl(struct pxa168_eth_private *pep, int offset)
> +{
> +       return readl(pep->base + offset);
> +}
> +
> +static inline void wrl(struct pxa168_eth_private *pep, int offset, u32 data)
> +{
> +       writel(data, pep->base + offset);
> +}
> +
> +static void abort_dma(struct pxa168_eth_private *pep)
> +{
> +       int delay;
> +       int max_retries = 40;
> +
> +       do {
> +               wrl(pep, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT);
> +               udelay(100);
> +
> +               delay = 10;
> +               while ((rdl(pep, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT))
> +                      && delay-- > 0) {
> +                       udelay(10);
> +               }
> +       } while (max_retries-- > 0 && delay <= 0);
> +
> +       if (max_retries <= 0)
> +               printk(KERN_ERR "%s : DMA Stuck\n", __func__);
> +}
> +
> +static int ethernet_phy_get(struct pxa168_eth_private *pep)
> +{
> +       unsigned int reg_data;
> +
> +       reg_data = rdl(pep, PHY_ADDRESS);
> +
> +       return (reg_data >> (5 * pep->port_num)) & 0x1f;
> +}
> +
> +static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
> +{
> +       u32 reg_data;
> +       int addr_shift = 5 * pep->port_num;
> +
> +       reg_data = rdl(pep, PHY_ADDRESS);
> +       reg_data &= ~(0x1f << addr_shift);
> +       reg_data |= (phy_addr & 0x1f) << addr_shift;
> +       wrl(pep, PHY_ADDRESS, reg_data);
> +}
> +
> +static void ethernet_phy_reset(struct pxa168_eth_private *pep)
> +{
> +       int data;
> +
> +       data = phy_read(pep->phy, MII_BMCR);
> +       if (data < 0)
> +               return;
> +
> +       data |= BMCR_RESET;
> +       if (phy_write(pep->phy, MII_BMCR, data) < 0)
> +               return;
> +
> +       do {
> +               data = phy_read(pep->phy, MII_BMCR);
> +       } while (data >= 0 && data & BMCR_RESET);
> +}
> +
> +static void rxq_refill(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       struct sk_buff *skb;
> +       struct rx_desc *p_used_rx_desc;
> +       int used_rx_desc;
> +
> +       while (pep->rx_desc_count < pep->rx_ring_size) {
> +               int size;
> +
> +               skb = dev_alloc_skb(pep->skb_size);
> +               if (!skb)
> +                       break;
> +               if (SKB_DMA_REALIGN)
> +                       skb_reserve(skb, SKB_DMA_REALIGN);
> +               pep->rx_desc_count++;
> +               /* Get 'used' Rx descriptor */
> +               used_rx_desc = pep->rx_used_desc_q;
> +               p_used_rx_desc = &pep->p_rx_desc_area[used_rx_desc];
> +               size = skb->end - skb->data;
> +               p_used_rx_desc->buf_ptr = dma_map_single(NULL,
> +                                                        skb->data,
> +                                                        size,
> +                                                        DMA_FROM_DEVICE);
> +               p_used_rx_desc->buf_size = size;
> +               pep->rx_skb[used_rx_desc] = skb;
> +
> +               /* Return the descriptor to DMA ownership */
> +               wmb();
> +               p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
> +               wmb();
> +
> +               /* Move the used descriptor pointer to the next descriptor */
> +               pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size;
> +
> +               /* Any Rx return cancels the Rx resource error status */
> +               pep->rx_resource_err = 0;
> +
> +               skb_reserve(skb, ETH_HW_IP_ALIGN);
> +       }
> +
> +       /*
> +        * If RX ring is empty of SKB, set a timer to try allocating
> +        * again at a later time.
> +        */
> +       if (pep->rx_desc_count == 0) {
> +               pep->timeout.expires = jiffies + (HZ / 10);
> +               add_timer(&pep->timeout);
> +       }
> +}
> +
> +static inline void rxq_refill_timer_wrapper(unsigned long data)
> +{
> +       struct pxa168_eth_private *pep = (void *)data;
> +       napi_schedule(&pep->napi);
> +}
> +
> +static inline u8 flip_8_bits(u8 x)
> +{
> +       return (((x) & 0x01) << 3) | (((x) & 0x02) << 1)
> +           | (((x) & 0x04) >> 1) | (((x) & 0x08) >> 3)
> +           | (((x) & 0x10) << 3) | (((x) & 0x20) << 1)
> +           | (((x) & 0x40) >> 1) | (((x) & 0x80) >> 3);
> +}
> +
> +static void nibble_swap_every_byte(unsigned char *mac_addr)
> +{
> +       int i;
> +       for (i = 0; i < ETH_ALEN; i++) {
> +               mac_addr[i] = ((mac_addr[i] & 0x0f) << 4) |
> +                               ((mac_addr[i] & 0xf0) >> 4);
> +       }
> +}
> +
> +static void inverse_every_nibble(unsigned char *mac_addr)
> +{
> +       int i;
> +       for (i = 0; i < ETH_ALEN; i++)
> +               mac_addr[i] = flip_8_bits(mac_addr[i]);
> +}
> +
> +/*
> + * ----------------------------------------------------------------------------
> + * This function will calculate the hash function of the address.
> + * Inputs
> + * mac_addr_orig    - MAC address.
> + * Outputs
> + * return the calculated entry.
> + */
> +static u32 hash_function(unsigned char *mac_addr_orig)
> +{
> +       u32 hash_result;
> +       u32 addr0;
> +       u32 addr1;
> +       u32 addr2;
> +       u32 addr3;
> +       unsigned char mac_addr[ETH_ALEN];
> +
> +       /* Make a copy of MAC address since we are going to performe bit
> +        * operations on it
> +        */
> +       memcpy(mac_addr, mac_addr_orig, ETH_ALEN);
> +
> +       nibble_swap_every_byte(mac_addr);
> +       inverse_every_nibble(mac_addr);
> +
> +       addr0 = (mac_addr[5] >> 2) & 0x3f;
> +       addr1 = (mac_addr[5] & 0x03) | (((mac_addr[4] & 0x7f)) << 2);
> +       addr2 = ((mac_addr[4] & 0x80) >> 7) | mac_addr[3] << 1;
> +       addr3 = (mac_addr[2] & 0xff) | ((mac_addr[1] & 1) << 8);
> +
> +       hash_result = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
> +       hash_result = hash_result & 0x07ff;
> +       return hash_result;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------------
> + * This function will add/del an entry to the address table.
> + * Inputs
> + * pep - ETHERNET .
> + * mac_addr - MAC address.
> + * skip - if 1, skip this address.Used in case of deleting an entry which is a
> + *       part of chain in the hash table.We cant just delete the entry since
> + *       that will break the chain.We need to defragment the tables time to
> + *       time.
> + * rd   - 0 Discard packet upon match.
> + *     - 1 Receive packet upon match.
> + * Outputs
> + * address table entry is added/deleted.
> + * 0 if success.
> + * -ENOSPC if table full
> + */
> +static int add_del_hash_entry(struct pxa168_eth_private *pep,
> +                             unsigned char *mac_addr,
> +                             u32 rd, u32 skip, int del)
> +{
> +       struct addr_table_entry *entry, *start;
> +       u32 new_high;
> +       u32 new_low;
> +       u32 i;
> +
> +       new_low = (((mac_addr[1] >> 4) & 0xf) << 15)
> +           | (((mac_addr[1] >> 0) & 0xf) << 11)
> +           | (((mac_addr[0] >> 4) & 0xf) << 7)
> +           | (((mac_addr[0] >> 0) & 0xf) << 3)
> +           | (((mac_addr[3] >> 4) & 0x1) << 31)
> +           | (((mac_addr[3] >> 0) & 0xf) << 27)
> +           | (((mac_addr[2] >> 4) & 0xf) << 23)
> +           | (((mac_addr[2] >> 0) & 0xf) << 19)
> +           | (skip << SKIP) | (rd << HASH_ENTRY_RECEIVE_DISCARD_BIT)
> +           | HASH_ENTRY_VALID;
> +
> +       new_high = (((mac_addr[5] >> 4) & 0xf) << 15)
> +           | (((mac_addr[5] >> 0) & 0xf) << 11)
> +           | (((mac_addr[4] >> 4) & 0xf) << 7)
> +           | (((mac_addr[4] >> 0) & 0xf) << 3)
> +           | (((mac_addr[3] >> 5) & 0x7) << 0);
> +
> +       /*
> +        * Pick the appropriate table, start scanning for free/reusable
> +        * entries at the index obtained by hashing the specified MAC address
> +        */
> +       start = (struct addr_table_entry *)(pep->htpr);
> +       entry = start + hash_function(mac_addr);
> +       for (i = 0; i < HOP_NUMBER; i++) {
> +               if (!(le32_to_cpu(entry->lo) & HASH_ENTRY_VALID)) {
> +                       break;
> +               } else {
> +                       /* if same address put in same position */
> +                       if (((le32_to_cpu(entry->lo) & 0xfffffff8) ==
> +                               (new_low & 0xfffffff8)) &&
> +                               (le32_to_cpu(entry->hi) == new_high)) {
> +                               break;
> +                       }
> +               }
> +               if (entry == start + 0x7ff)
> +                       entry = start;
> +               else
> +                       entry++;
> +       }
> +
> +       if (((le32_to_cpu(entry->lo) & 0xfffffff8) != (new_low & 0xfffffff8)) &&
> +           (le32_to_cpu(entry->hi) != new_high) && del)
> +               return 0;
> +
> +       if (i == HOP_NUMBER) {
> +               if (!del) {
> +                       printk(KERN_INFO "%s: table section is full, need to "
> +                                       "move to 16kB implementation?\n",
> +                                        __FILE__);
> +                       return -ENOSPC;
> +               } else
> +                       return 0;
> +       }
> +
> +       /*
> +        * Update the selected entry
> +        */
> +       if (del) {
> +               entry->hi = 0;
> +               entry->lo = 0;
> +       } else {
> +               entry->hi = cpu_to_le32(new_high);
> +               entry->lo = cpu_to_le32(new_low);
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * ----------------------------------------------------------------------------
> + *  Create an addressTable entry from MAC address info
> + *  found in the specifed net_device struct
> + *
> + *  Input : pointer to ethernet interface network device structure
> + *  Output : N/A
> + */
> +static void update_hash_table_mac_address(struct pxa168_eth_private *pep,
> +                                         unsigned char *oaddr,
> +                                         unsigned char *addr)
> +{
> +       /* Delete old entry */
> +       if (oaddr)
> +               add_del_hash_entry(pep, oaddr, 1, 0, HASH_DELETE);
> +       /* Add new entry */
> +       add_del_hash_entry(pep, addr, 1, 0, HASH_ADD);
> +}
> +
> +static int init_hash_table(struct pxa168_eth_private *pep)
> +{
> +       /*
> +        * Hardware expects CPU to build a hash table based on a predefined
> +        * hash function and populate it based on hardware address. The
> +        * location of the hash table is identified by 32-bit pointer stored
> +        * in HTPR internal register. Two possible sizes exists for the hash
> +        * table 8kB (256kB of DRAM required (4 x 64 kB banks)) and 1/2kB
> +        * (16kB of DRAM required (4 x 4 kB banks)).We currently only support
> +        * 1/2kB.
> +        */
> +       /* TODO: Add support for 8kB hash table and alternative hash
> +        * function.Driver can dynamically switch to them if the 1/2kB hash
> +        * table is full.
> +        */
> +       if (pep->htpr == NULL) {
> +               pep->htpr = dma_alloc_coherent(pep->dev->dev.parent,
> +                                             HASH_ADDR_TABLE_SIZE,
> +                                             &pep->htpr_dma, GFP_KERNEL);
> +               if (pep->htpr == NULL)
> +                       return -ENOMEM;
> +       }
> +       memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> +       wrl(pep, HTPR, pep->htpr_dma);
> +       return 0;
> +}
> +
> +static void pxa168_eth_set_rx_mode(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       struct netdev_hw_addr *ha;
> +       u32 val;
> +
> +       val = rdl(pep, PORT_CONFIG);
> +       if (dev->flags & IFF_PROMISC)
> +               val |= PCR_PM;
> +       else
> +               val &= ~PCR_PM;
> +       wrl(pep, PORT_CONFIG, val);
> +
> +       /*
> +        * Remove the old list of MAC address and add dev->addr
> +        * and multicast address.
> +        */
> +       memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE);
> +       update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> +
> +       netdev_for_each_mc_addr(ha, dev)
> +               update_hash_table_mac_address(pep, NULL, ha->addr);
> +}
> +
> +static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr)
> +{
> +       struct sockaddr *sa = addr;
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       unsigned char oldMac[ETH_ALEN];
> +
> +       if (!is_valid_ether_addr(sa->sa_data))
> +               return -EINVAL;
> +       memcpy(oldMac, dev->dev_addr, ETH_ALEN);
> +       memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
> +       netif_addr_lock_bh(dev);
> +       update_hash_table_mac_address(pep, oldMac, dev->dev_addr);
> +       netif_addr_unlock_bh(dev);
> +       return 0;
> +}
> +
> +static void eth_port_start(struct net_device *dev)
> +{
> +       unsigned int val = 0;
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       int tx_curr_desc, rx_curr_desc;
> +
> +       /* Perform PHY reset, if there is a PHY. */
> +       if (pep->phy != NULL) {
> +               struct ethtool_cmd cmd;
> +
> +               pxa168_get_settings(pep->dev, &cmd);
> +               ethernet_phy_reset(pep);
> +               pxa168_set_settings(pep->dev, &cmd);
> +       }
> +
> +       /* Assignment of Tx CTRP of given queue */
> +       tx_curr_desc = pep->tx_curr_desc_q;
> +       wrl(pep, ETH_C_TX_DESC_1,
> +           (u32) ((struct tx_desc *)pep->tx_desc_dma + tx_curr_desc));
> +
> +       /* Assignment of Rx CRDP of given queue */
> +       rx_curr_desc = pep->rx_curr_desc_q;
> +       wrl(pep, ETH_C_RX_DESC_0,
> +           (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> +       wrl(pep, ETH_F_RX_DESC_0,
> +           (u32) ((struct rx_desc *)pep->rx_desc_dma + rx_curr_desc));
> +
> +       /* Clear all interrupts */
> +       wrl(pep, INT_CAUSE, 0);
> +
> +       /* Enable all interrupts for receive, transmit and error. */
> +       wrl(pep, INT_MASK, ALL_INTS);
> +
> +       val = rdl(pep, PORT_CONFIG);
> +       val |= PCR_EN;
> +       wrl(pep, PORT_CONFIG, val);
> +
> +       /* Start RX DMA engine */
> +       val = rdl(pep, SDMA_CMD);
> +       val |= SDMA_CMD_ERD;
> +       wrl(pep, SDMA_CMD, val);
> +}
> +
> +static void eth_port_reset(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       unsigned int val = 0;
> +
> +       /* Stop all interrupts for receive, transmit and error. */
> +       wrl(pep, INT_MASK, 0);
> +
> +       /* Clear all interrupts */
> +       wrl(pep, INT_CAUSE, 0);
> +
> +       /* Stop RX DMA */
> +       val = rdl(pep, SDMA_CMD);
> +       val &= ~SDMA_CMD_ERD;   /* abort dma command */
> +
> +       /* Abort any transmit and receive operations and put DMA
> +        * in idle state.
> +        */
> +       abort_dma(pep);
> +
> +       /* Disable port */
> +       val = rdl(pep, PORT_CONFIG);
> +       val &= ~PCR_EN;
> +       wrl(pep, PORT_CONFIG, val);
> +}
> +
> +/*
> + * txq_reclaim - Free the tx desc data for completed descriptors
> + * If force is non-zero, frees uncompleted descriptors as well
> + */
> +static int txq_reclaim(struct net_device *dev, int force)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       struct tx_desc *desc;
> +       u32 cmd_sts;
> +       struct sk_buff *skb;
> +       int tx_index;
> +       dma_addr_t addr;
> +       int count;
> +       int released = 0;
> +
> +       netif_tx_lock(dev);
> +
> +       pep->work_todo &= ~WORK_TX_DONE;
> +       while (pep->tx_desc_count > 0) {
> +               tx_index = pep->tx_used_desc_q;
> +               desc = &pep->p_tx_desc_area[tx_index];
> +               cmd_sts = desc->cmd_sts;
> +               if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) {
> +                       if (released > 0) {
> +                               goto txq_reclaim_end;
> +                       } else {
> +                               released = -1;
> +                               goto txq_reclaim_end;
> +                       }
> +               }
> +               pep->tx_used_desc_q = (tx_index + 1) % pep->tx_ring_size;
> +               pep->tx_desc_count--;
> +               addr = desc->buf_ptr;
> +               count = desc->byte_cnt;
> +               skb = pep->tx_skb[tx_index];
> +               if (skb)
> +                       pep->tx_skb[tx_index] = NULL;
> +
> +               if (cmd_sts & TX_ERROR) {
> +                       if (net_ratelimit())
> +                               printk(KERN_ERR "%s: Error in TX\n", dev->name);
> +                       dev->stats.tx_errors++;
> +               }
> +               dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
> +               if (skb)
> +                       dev_kfree_skb_irq(skb);
> +               released++;
> +       }
> +txq_reclaim_end:
> +       netif_tx_unlock(dev);
> +       return released;
> +}
> +
> +static void pxa168_eth_tx_timeout(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> +       printk(KERN_INFO "%s: TX timeout  desc_count %d\n",
> +              dev->name, pep->tx_desc_count);
> +
> +       schedule_work(&pep->tx_timeout_task);
> +}
> +
> +static void pxa168_eth_tx_timeout_task(struct work_struct *work)
> +{
> +       struct pxa168_eth_private *pep = container_of(work,
> +                                                struct pxa168_eth_private,
> +                                                tx_timeout_task);
> +       struct net_device *dev = pep->dev;
> +       pxa168_eth_stop(dev);
> +       pxa168_eth_open(dev);
> +}
> +
> +static int rxq_process(struct net_device *dev, int budget)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       struct net_device_stats *stats = &dev->stats;
> +       unsigned int received_packets = 0;
> +       struct sk_buff *skb;
> +
> +       while (budget-- > 0) {
> +               int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
> +               struct rx_desc *rx_desc;
> +               unsigned int cmd_sts;
> +
> +               /* Do not process Rx ring in case of Rx ring resource error */
> +               if (pep->rx_resource_err)
> +                       break;
> +               rx_curr_desc = pep->rx_curr_desc_q;
> +               rx_used_desc = pep->rx_used_desc_q;
> +               rx_desc = &pep->p_rx_desc_area[rx_curr_desc];
> +               cmd_sts = rx_desc->cmd_sts;
> +               rmb();
> +               if (cmd_sts & (BUF_OWNED_BY_DMA))
> +                       break;
> +               skb = pep->rx_skb[rx_curr_desc];
> +               pep->rx_skb[rx_curr_desc] = NULL;
> +
> +               rx_next_curr_desc = (rx_curr_desc + 1) % pep->rx_ring_size;
> +               pep->rx_curr_desc_q = rx_next_curr_desc;
> +
> +               /* Rx descriptors exhausted. */
> +               /* Set the Rx ring resource error flag */
> +               if (rx_next_curr_desc == rx_used_desc)
> +                       pep->rx_resource_err = 1;
> +               pep->rx_desc_count--;
> +               dma_unmap_single(NULL, rx_desc->buf_ptr,
> +                                rx_desc->buf_size,
> +                                DMA_FROM_DEVICE);
> +               received_packets++;
> +               /*
> +                * Update statistics.
> +                * Note byte count includes 4 byte CRC count
> +                */
> +               stats->rx_packets++;
> +               stats->rx_bytes += rx_desc->byte_cnt;
> +               /*
> +                * In case received a packet without first / last bits on OR
> +                * the error summary bit is on, the packets needs to be droped.
> +                */
> +               if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> +                    (RX_FIRST_DESC | RX_LAST_DESC))
> +                   || (cmd_sts & RX_ERROR)) {
> +
> +                       stats->rx_dropped++;
> +                       if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
> +                           (RX_FIRST_DESC | RX_LAST_DESC)) {
> +                               if (net_ratelimit())
> +                                       printk(KERN_ERR
> +                                              "%s: Rx pkt on multiple desc\n",
> +                                              dev->name);
> +                       }
> +                       if (cmd_sts & RX_ERROR)
> +                               stats->rx_errors++;
> +                       dev_kfree_skb_irq(skb);
> +               } else {
> +                       /*
> +                        * The -4 is for the CRC in the trailer of the
> +                        * received packet
> +                        */
> +                       skb_put(skb, rx_desc->byte_cnt - 4);
> +                       skb->protocol = eth_type_trans(skb, dev);
> +                       netif_receive_skb(skb);
> +               }
> +               dev->last_rx = jiffies;
> +       }
> +       /* Fill RX ring with skb's */
> +       rxq_refill(dev);
> +       return received_packets;
> +}
> +
> +static int pxa168_eth_collect_events(struct pxa168_eth_private *pep,
> +                                    struct net_device *dev)
> +{
> +       u32 icr;
> +       int ret = 0;
> +
> +       icr = rdl(pep, INT_CAUSE);
> +       if (icr == 0)
> +               return IRQ_NONE;
> +
> +       wrl(pep, INT_CAUSE, ~icr);
> +       if (icr & (ICR_TXBUF_H | ICR_TXBUF_L)) {
> +               pep->work_todo |= WORK_TX_DONE;
> +               ret = 1;
> +       }
> +       if (icr & ICR_RXBUF)
> +               ret = 1;
> +       if (icr & ICR_MII_CH) {
> +               pep->work_todo |= WORK_LINK;
> +               ret = 1;
> +       }
> +       return ret;
> +}
> +
> +static void handle_link_event(struct pxa168_eth_private *pep)
> +{
> +       struct net_device *dev = pep->dev;
> +       u32 port_status;
> +       int speed;
> +       int duplex;
> +       int fc;
> +
> +       port_status = rdl(pep, PORT_STATUS);
> +       if (!(port_status & LINK_UP)) {
> +               if (netif_carrier_ok(dev)) {
> +                       printk(KERN_INFO "%s: link down\n", dev->name);
> +                       netif_carrier_off(dev);
> +                       txq_reclaim(dev, 1);
> +               }
> +               return;
> +       }
> +       if (port_status & PORT_SPEED_100)
> +               speed = 100;
> +       else
> +               speed = 10;
> +
> +       duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> +       fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> +       printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
> +              "flow control %sabled\n", dev->name,
> +              speed, duplex ? "full" : "half", fc ? "en" : "dis");
> +       if (!netif_carrier_ok(dev))
> +               netif_carrier_on(dev);
> +}
> +
> +static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id)
> +{
> +       struct net_device *dev = (struct net_device *)dev_id;
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> +       if (unlikely(!pxa168_eth_collect_events(pep, dev)))
> +               return IRQ_NONE;
> +       /* Disable interrupts */
> +       wrl(pep, INT_MASK, 0);
> +       napi_schedule(&pep->napi);
> +       return IRQ_HANDLED;
> +}
> +
> +static void pxa168_eth_recalc_skb_size(struct pxa168_eth_private *pep)
> +{
> +       int skb_size;
> +
> +       /*
> +        * Reserve 2+14 bytes for an ethernet header (the hardware
> +        * automatically prepends 2 bytes of dummy data to each
> +        * received packet), 16 bytes for up to four VLAN tags, and
> +        * 4 bytes for the trailing FCS -- 36 bytes total.
> +        */
> +       skb_size = pep->dev->mtu + 36;
> +
> +       /*
> +        * Make sure that the skb size is a multiple of 8 bytes, as
> +        * the lower three bits of the receive descriptor's buffer
> +        * size field are ignored by the hardware.
> +        */
> +       pep->skb_size = (skb_size + 7) & ~7;
> +
> +       /*
> +        * If NET_SKB_PAD is smaller than a cache line,
> +        * netdev_alloc_skb() will cause skb->data to be misaligned
> +        * to a cache line boundary.  If this is the case, include
> +        * some extra space to allow re-aligning the data area.
> +        */
> +       pep->skb_size += SKB_DMA_REALIGN;
> +
> +}
> +
> +static int set_port_config_ext(struct pxa168_eth_private *pep)
> +{
> +       int skb_size;
> +
> +       pxa168_eth_recalc_skb_size(pep);
> +       if  (pep->skb_size <= 1518)
> +               skb_size = PCXR_MFL_1518;
> +       else if (pep->skb_size <= 1536)
> +               skb_size = PCXR_MFL_1536;
> +       else if (pep->skb_size <= 2048)
> +               skb_size = PCXR_MFL_2048;
> +       else
> +               skb_size = PCXR_MFL_64K;
> +
> +       /* Extended Port Configuration */
> +       wrl(pep,
> +           PORT_CONFIG_EXT, PCXR_2BSM | /* Two byte prefix aligns IP hdr */
> +           PCXR_DSCP_EN |               /* Enable DSCP in IP */
> +           skb_size | PCXR_FLP |        /* do not force link pass */
> +           PCXR_TX_HIGH_PRI);           /* Transmit - high priority queue */
> +
> +       return 0;
> +}
> +
> +static int pxa168_init_hw(struct pxa168_eth_private *pep)
> +{
> +       int err = 0;
> +
> +       /* Disable interrupts */
> +       wrl(pep, INT_MASK, 0);
> +       wrl(pep, INT_CAUSE, 0);
> +       /* Write to ICR to clear interrupts. */
> +       wrl(pep, INT_W_CLEAR, 0);
> +       /* Abort any transmit and receive operations and put DMA
> +        * in idle state.
> +        */
> +       abort_dma(pep);
> +       /* Initialize address hash table */
> +       err = init_hash_table(pep);
> +       if (err)
> +               return err;
> +       /* SDMA configuration */
> +       wrl(pep, SDMA_CONFIG, SDCR_BSZ8 |       /* Burst size = 32 bytes */
> +           SDCR_RIFB |                         /* Rx interrupt on frame */
> +           SDCR_BLMT |                         /* Little endian transmit */
> +           SDCR_BLMR |                         /* Little endian receive */
> +           SDCR_RC_MAX_RETRANS);               /* Max retransmit count */
> +       /* Port Configuration */
> +       wrl(pep, PORT_CONFIG, PCR_HS);          /* Hash size is 1/2kb */
> +       set_port_config_ext(pep);
> +
> +       return err;
> +}
> +
> +static int rxq_init(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       struct rx_desc *p_rx_desc;
> +       int size = 0, i = 0;
> +       int rx_desc_num = pep->rx_ring_size;
> +
> +       /* Allocate RX skb rings */
> +       pep->rx_skb = kmalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size,
> +                            GFP_KERNEL);
> +       if (!pep->rx_skb) {
> +               printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name);
> +               return -ENOMEM;
> +       }
> +       /* Allocate RX ring */
> +       pep->rx_desc_count = 0;
> +       size = pep->rx_ring_size * sizeof(struct rx_desc);
> +       pep->rx_desc_area_size = size;
> +       pep->p_rx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
> +                                               &pep->rx_desc_dma, GFP_KERNEL);
> +       if (!pep->p_rx_desc_area) {
> +               printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n",
> +                      dev->name, size);
> +               goto out;
> +       }
> +       memset((void *)pep->p_rx_desc_area, 0, size);
> +       /* initialize the next_desc_ptr links in the Rx descriptors ring */
> +       p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area;
> +       for (i = 0; i < rx_desc_num; i++) {
> +               p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma +
> +                   ((i + 1) % rx_desc_num) * sizeof(struct rx_desc);
> +       }
> +       /* Save Rx desc pointer to driver struct. */
> +       pep->rx_curr_desc_q = 0;
> +       pep->rx_used_desc_q = 0;
> +       pep->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc);
> +       return 0;
> +out:
> +       kfree(pep->rx_skb);
> +       return -ENOMEM;
> +}
> +
> +static void rxq_deinit(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       int curr;
> +
> +       /* Free preallocated skb's on RX rings */
> +       for (curr = 0; pep->rx_desc_count && curr < pep->rx_ring_size; curr++) {
> +               if (pep->rx_skb[curr]) {
> +                       dev_kfree_skb(pep->rx_skb[curr]);
> +                       pep->rx_desc_count--;
> +               }
> +       }
> +       if (pep->rx_desc_count)
> +               printk(KERN_ERR
> +                      "Error in freeing Rx Ring. %d skb's still\n",
> +                      pep->rx_desc_count);
> +       /* Free RX ring */
> +       if (pep->p_rx_desc_area)
> +               dma_free_coherent(pep->dev->dev.parent, pep->rx_desc_area_size,
> +                                 pep->p_rx_desc_area, pep->rx_desc_dma);
> +       kfree(pep->rx_skb);
> +}
> +
> +static int txq_init(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       struct tx_desc *p_tx_desc;
> +       int size = 0, i = 0;
> +       int tx_desc_num = pep->tx_ring_size;
> +
> +       pep->tx_skb = kmalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size,
> +                            GFP_KERNEL);
> +       if (!pep->tx_skb) {
> +               printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name);
> +               return -ENOMEM;
> +       }
> +       /* Allocate TX ring */
> +       pep->tx_desc_count = 0;
> +       size = pep->tx_ring_size * sizeof(struct tx_desc);
> +       pep->tx_desc_area_size = size;
> +       pep->p_tx_desc_area = dma_alloc_coherent(pep->dev->dev.parent, size,
> +                                               &pep->tx_desc_dma, GFP_KERNEL);
> +       if (!pep->p_tx_desc_area) {
> +               printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n",
> +                      dev->name, size);
> +               goto out;
> +       }
> +       memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size);
> +       /* Initialize the next_desc_ptr links in the Tx descriptors ring */
> +       p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area;
> +       for (i = 0; i < tx_desc_num; i++) {
> +               p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma +
> +                   ((i + 1) % tx_desc_num) * sizeof(struct tx_desc);
> +       }
> +       pep->tx_curr_desc_q = 0;
> +       pep->tx_used_desc_q = 0;
> +       pep->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc);
> +       return 0;
> +out:
> +       kfree(pep->tx_skb);
> +       return -ENOMEM;
> +}
> +
> +static void txq_deinit(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> +       /* Free outstanding skb's on TX ring */
> +       txq_reclaim(dev, 1);
> +       BUG_ON(pep->tx_used_desc_q != pep->tx_curr_desc_q);
> +       /* Free TX ring */
> +       if (pep->p_tx_desc_area)
> +               dma_free_coherent(pep->dev->dev.parent, pep->tx_desc_area_size,
> +                                 pep->p_tx_desc_area, pep->tx_desc_dma);
> +       kfree(pep->tx_skb);
> +}
> +
> +static int pxa168_eth_open(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       int err;
> +
> +       err = request_irq(dev->irq, pxa168_eth_int_handler,
> +                         IRQF_DISABLED, dev->name, dev);
> +       if (err) {
> +               dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n");
> +               return -EAGAIN;
> +       }
> +       pep->rx_resource_err = 0;
> +       err = rxq_init(dev);
> +       if (err != 0)
> +               goto out_free_irq;
> +       err = txq_init(dev);
> +       if (err != 0)
> +               goto out_free_rx_skb;
> +       pep->rx_used_desc_q = 0;
> +       pep->rx_curr_desc_q = 0;
> +
> +       /* Fill RX ring with skb's */
> +       rxq_refill(dev);
> +       pep->rx_used_desc_q = 0;
> +       pep->rx_curr_desc_q = 0;
> +       netif_carrier_off(dev);
> +       eth_port_start(dev);
> +       napi_enable(&pep->napi);
> +       return 0;
> +out_free_rx_skb:
> +       rxq_deinit(dev);
> +out_free_irq:
> +       free_irq(dev->irq, dev);
> +       return err;
> +}
> +
> +static int pxa168_eth_stop(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       eth_port_reset(dev);
> +
> +       /* Disable interrupts */
> +       wrl(pep, INT_MASK, 0);
> +       wrl(pep, INT_CAUSE, 0);
> +       /* Write to ICR to clear interrupts. */
> +       wrl(pep, INT_W_CLEAR, 0);
> +       napi_disable(&pep->napi);
> +       del_timer_sync(&pep->timeout);
> +       netif_carrier_off(dev);
> +       free_irq(dev->irq, dev);
> +       rxq_deinit(dev);
> +       txq_deinit(dev);
> +
> +       return 0;
> +}
> +
> +static int pxa168_eth_change_mtu(struct net_device *dev, int mtu)
> +{
> +       int retval;
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> +       if ((mtu > 9500) || (mtu < 68))
> +               return -EINVAL;
> +
> +       dev->mtu = mtu;
> +       retval = set_port_config_ext(pep);
> +
> +       if (!netif_running(dev))
> +               return 0;
> +
> +       /*
> +        * Stop and then re-open the interface. This will allocate RX
> +        * skbs of the new MTU.
> +        * There is a possible danger that the open will not succeed,
> +        * due to memory being full.
> +        */
> +       pxa168_eth_stop(dev);
> +       if (pxa168_eth_open(dev)) {
> +               dev_printk(KERN_ERR, &dev->dev,
> +                          "fatal error on re-opening device after "
> +                          "MTU change\n");
> +       }
> +
> +       return 0;
> +}
> +
> +static int eth_alloc_tx_desc_index(struct pxa168_eth_private *pep)
> +{
> +       int tx_desc_curr;
> +
> +       tx_desc_curr = pep->tx_curr_desc_q;
> +       pep->tx_curr_desc_q = (tx_desc_curr + 1) % pep->tx_ring_size;
> +       BUG_ON(pep->tx_curr_desc_q == pep->tx_used_desc_q);
> +       pep->tx_desc_count++;
> +
> +       return tx_desc_curr;
> +}
> +
> +static int pxa168_rx_poll(struct napi_struct *napi, int budget)
> +{
> +       struct pxa168_eth_private *pep =
> +           container_of(napi, struct pxa168_eth_private, napi);
> +       struct net_device *dev = pep->dev;
> +       int work_done = 0;
> +
> +       if (unlikely(pep->work_todo & WORK_LINK)) {
> +               pep->work_todo &= ~(WORK_LINK);
> +               handle_link_event(pep);
> +       }
> +       /*
> +        * We call txq_reclaim every time since in NAPI interupts are disabled
> +        * and due to this we miss the TX_DONE interrupt,which is not updated in
> +        * interrupt status register.
> +        */
> +       txq_reclaim(dev, 0);
> +       if (netif_queue_stopped(dev)
> +           && pep->tx_ring_size - pep->tx_desc_count > 1) {
> +               netif_wake_queue(dev);
> +       }
> +       work_done = rxq_process(dev, budget);
> +       if (work_done < budget) {
> +               napi_complete(napi);
> +               wrl(pep, INT_MASK, ALL_INTS);
> +       }
> +
> +       return work_done;
> +}
> +
> +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       struct net_device_stats *stats = &dev->stats;
> +       struct tx_desc *desc;
> +       int tx_index;
> +       int length;
> +
> +       tx_index = eth_alloc_tx_desc_index(pep);
> +       desc = &pep->p_tx_desc_area[tx_index];
> +       length = skb->len;
> +       pep->tx_skb[tx_index] = skb;
> +       desc->byte_cnt = length;
> +       desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
> +       wmb();
> +       desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC |
> +                       TX_ZERO_PADDING | TX_LAST_DESC | TX_EN_INT;
> +       wmb();
> +       wrl(pep, SDMA_CMD, SDMA_CMD_TXDH | SDMA_CMD_ERD);
> +
> +       stats->tx_bytes += skb->len;
> +       stats->tx_packets++;
> +       dev->trans_start = jiffies;
> +       if (pep->tx_ring_size - pep->tx_desc_count <= 1) {
> +               /* We handled the current skb, but now we are out of space.*/
> +               netif_stop_queue(dev);
> +       }
> +
> +       return NETDEV_TX_OK;
> +}
> +
> +static int smi_wait_ready(struct pxa168_eth_private *pep)
> +{
> +       int i = 0;
> +
> +       /* wait for the SMI register to become available */
> +       for (i = 0; rdl(pep, SMI) & SMI_BUSY; i++) {
> +               if (i == PHY_WAIT_ITERATIONS)
> +                       return -ETIMEDOUT;
> +               msleep(10);
> +       }
> +
> +       return 0;
> +}
> +
> +static int pxa168_smi_read(struct mii_bus *bus, int phy_addr, int regnum)
> +{
> +       struct pxa168_eth_private *pep = bus->priv;
> +       int i = 0;
> +       int val;
> +
> +       if (smi_wait_ready(pep)) {
> +               printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
> +               return -ETIMEDOUT;
> +       }
> +       wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) | SMI_OP_R);
> +       /* now wait for the data to be valid */
> +       for (i = 0; !((val = rdl(pep, SMI)) & SMI_R_VALID); i++) {
> +               if (i == PHY_WAIT_ITERATIONS) {
> +                       printk(KERN_WARNING
> +                               "pxa168_eth: SMI bus read not valid\n");
> +                       return -ENODEV;
> +               }
> +               msleep(10);
> +       }
> +
> +       return val & 0xffff;
> +}
> +
> +static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum,
> +                           u16 value)
> +{
> +       struct pxa168_eth_private *pep = bus->priv;
> +
> +       if (smi_wait_ready(pep)) {
> +               printk(KERN_WARNING "pxa168_eth: SMI bus busy timeout\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       wrl(pep, SMI, (phy_addr << 16) | (regnum << 21) |
> +           SMI_OP_W | (value & 0xffff));
> +
> +       if (smi_wait_ready(pep)) {
> +               printk(KERN_ERR "pxa168_eth: SMI bus busy timeout\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +
> +static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr,
> +                              int cmd)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       if (pep->phy != NULL)
> +               return phy_mii_ioctl(pep->phy, if_mii(ifr), cmd);
> +
> +       return -EOPNOTSUPP;
> +}
> +
> +static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr)
> +{
> +       struct mii_bus *bus = pep->smi_bus;
> +       struct phy_device *phydev;
> +       int start;
> +       int num;
> +       int i;
> +
> +       if (phy_addr == PXA168_ETH_PHY_ADDR_DEFAULT) {
> +               /* Scan entire range */
> +               start = ethernet_phy_get(pep);
> +               num = 32;
> +       } else {
> +               /* Use phy addr specific to platform */
> +               start = phy_addr & 0x1f;
> +               num = 1;
> +       }
> +       phydev = NULL;
> +       for (i = 0; i < num; i++) {
> +               int addr = (start + i) & 0x1f;
> +               if (bus->phy_map[addr] == NULL)
> +                       mdiobus_scan(bus, addr);
> +
> +               if (phydev == NULL) {
> +                       phydev = bus->phy_map[addr];
> +                       if (phydev != NULL)
> +                               ethernet_phy_set_addr(pep, addr);
> +               }
> +       }
> +
> +       return phydev;
> +}
> +
> +static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
> +{
> +       struct phy_device *phy = pep->phy;
> +       ethernet_phy_reset(pep);
> +
> +       phy_attach(pep->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_MII);
> +
> +       if (speed == 0) {
> +               phy->autoneg = AUTONEG_ENABLE;
> +               phy->speed = 0;
> +               phy->duplex = 0;
> +               phy->supported &= PHY_BASIC_FEATURES;
> +               phy->advertising = phy->supported | ADVERTISED_Autoneg;
> +       } else {
> +               phy->autoneg = AUTONEG_DISABLE;
> +               phy->advertising = 0;
> +               phy->speed = speed;
> +               phy->duplex = duplex;
> +       }
> +       phy_start_aneg(phy);
> +}
> +
> +static int ethernet_phy_setup(struct net_device *dev)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> +       if (pep->pd != NULL) {
> +               if (pep->pd->init)
> +                       pep->pd->init();
> +       }
> +       pep->phy = phy_scan(pep, pep->pd->phy_addr & 0x1f);
> +       if (pep->phy != NULL)
> +               phy_init(pep, pep->pd->speed, pep->pd->duplex);
> +       update_hash_table_mac_address(pep, NULL, dev->dev_addr);
> +
> +       return 0;
> +}
> +
> +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +       int err;
> +
> +       err = phy_read_status(pep->phy);
> +       if (err == 0)
> +               err = phy_ethtool_gset(pep->phy, cmd);
> +
> +       return err;
> +}
> +
> +static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> +       return phy_ethtool_sset(pep->phy, cmd);
> +}
> +
> +static void pxa168_get_drvinfo(struct net_device *dev,
> +                              struct ethtool_drvinfo *info)
> +{
> +       strncpy(info->driver, DRIVER_NAME, 32);
> +       strncpy(info->version, DRIVER_VERSION, 32);
> +       strncpy(info->fw_version, "N/A", 32);
> +       strncpy(info->bus_info, "N/A", 32);
> +}
> +
> +static u32 pxa168_get_link(struct net_device *dev)
> +{
> +       return !!netif_carrier_ok(dev);
> +}
> +
> +static const struct ethtool_ops pxa168_ethtool_ops = {
> +       .get_settings = pxa168_get_settings,
> +       .set_settings = pxa168_set_settings,
> +       .get_drvinfo = pxa168_get_drvinfo,
> +       .get_link = pxa168_get_link,
> +};
> +
> +static const struct net_device_ops pxa168_eth_netdev_ops = {
> +       .ndo_open = pxa168_eth_open,
> +       .ndo_stop = pxa168_eth_stop,
> +       .ndo_start_xmit = pxa168_eth_start_xmit,
> +       .ndo_set_rx_mode = pxa168_eth_set_rx_mode,
> +       .ndo_set_mac_address = pxa168_eth_set_mac_address,
> +       .ndo_validate_addr = eth_validate_addr,
> +       .ndo_do_ioctl = pxa168_eth_do_ioctl,
> +       .ndo_change_mtu = pxa168_eth_change_mtu,
> +       .ndo_tx_timeout = pxa168_eth_tx_timeout,
> +};
> +
> +static int pxa168_eth_probe(struct platform_device *pdev)
> +{
> +       struct pxa168_eth_private *pep = NULL;
> +       struct net_device *dev = NULL;
> +       struct resource *res;
> +       struct clk *clk;
> +       int err;
> +
> +       printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n");
> +
> +       clk = clk_get(&pdev->dev, "MFUCLK");
> +       if (IS_ERR(clk)) {
> +               printk(KERN_ERR "%s: Fast Ethernet failed to get clock\n",
> +                       DRIVER_NAME);
> +               return -ENODEV;
> +       }
> +       clk_enable(clk);
> +
> +       dev = alloc_etherdev(sizeof(struct pxa168_eth_private));
> +       if (!dev) {
> +               err = -ENOMEM;
> +               goto out;
> +       }
> +
> +       platform_set_drvdata(pdev, dev);
> +       pep = netdev_priv(dev);
> +       pep->dev = dev;
> +       pep->clk = clk;
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (res == NULL) {
> +               err = -ENODEV;
> +               goto out;
> +       }
> +       pep->base = ioremap(res->start, res->end - res->start + 1);
> +       if (pep->base == NULL) {
> +               err = -ENOMEM;
> +               goto out;
> +       }
> +       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +       BUG_ON(!res);
> +       dev->irq = res->start;
> +       dev->netdev_ops = &pxa168_eth_netdev_ops;
> +       dev->watchdog_timeo = 2 * HZ;
> +       dev->base_addr = 0;
> +       SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
> +
> +       INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
> +
> +       printk(KERN_INFO "%s:Using random mac address\n", DRIVER_NAME);
> +       random_ether_addr(dev->dev_addr);
> +
> +       pep->pd = pdev->dev.platform_data;
> +       pep->rx_ring_size = NUM_RX_DESCS;
> +       if (pep->pd->rx_queue_size)
> +               pep->rx_ring_size = pep->pd->rx_queue_size;
> +
> +       pep->tx_ring_size = NUM_TX_DESCS;
> +       if (pep->pd->tx_queue_size)
> +               pep->tx_ring_size = pep->pd->tx_queue_size;
> +
> +       pep->port_num = pep->pd->port_number;
> +       /* Hardware supports only 3 ports */
> +       BUG_ON(pep->port_num > 2);
> +       netif_napi_add(dev, &pep->napi, pxa168_rx_poll, pep->rx_ring_size);
> +
> +       memset(&pep->timeout, 0, sizeof(struct timer_list));
> +       init_timer(&pep->timeout);
> +       pep->timeout.function = rxq_refill_timer_wrapper;
> +       pep->timeout.data = (unsigned long)pep;
> +
> +       pep->smi_bus = mdiobus_alloc();
> +       if (pep->smi_bus == NULL) {
> +               err = -ENOMEM;
> +               goto out;
> +       }
> +       pep->smi_bus->priv = pep;
> +       pep->smi_bus->name = "pxa168_eth smi";
> +       pep->smi_bus->read = pxa168_smi_read;
> +       pep->smi_bus->write = pxa168_smi_write;
> +       snprintf(pep->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id);
> +       pep->smi_bus->parent = &pdev->dev;
> +       pep->smi_bus->phy_mask = 0xffffffff;
> +       if (mdiobus_register(pep->smi_bus) < 0) {
> +               err = -ENOMEM;
> +               goto out;
> +       }
> +       pxa168_init_hw(pep);
> +       err = ethernet_phy_setup(dev);
> +       if (err)
> +               goto out;
> +       SET_NETDEV_DEV(dev, &pdev->dev);
> +       err = register_netdev(dev);
> +       if (err)
> +               goto out;
> +       return 0;
> +out:
> +       if (pep->clk) {
> +               clk_disable(pep->clk);
> +               clk_put(pep->clk);
> +               pep->clk = NULL;
> +       }
> +       if (pep->base) {
> +               iounmap(pep->base);
> +               pep->base = NULL;
> +       }
> +       if (dev)
> +               free_netdev(dev);
> +       return err;
> +}
> +
> +static int pxa168_eth_remove(struct platform_device *pdev)
> +{
> +       struct net_device *dev = platform_get_drvdata(pdev);
> +       struct pxa168_eth_private *pep = netdev_priv(dev);
> +
> +       if (pep->htpr) {
> +               dma_free_coherent(pep->dev->dev.parent, HASH_ADDR_TABLE_SIZE,
> +                                 pep->htpr, pep->htpr_dma);
> +               pep->htpr = NULL;
> +       }
> +       if (pep->clk) {
> +               clk_disable(pep->clk);
> +               clk_put(pep->clk);
> +               pep->clk = NULL;
> +       }
> +       if (pep->phy != NULL)
> +               phy_detach(pep->phy);
> +
> +       iounmap(pep->base);
> +       pep->base = NULL;
> +       unregister_netdev(dev);
> +       flush_scheduled_work();
> +       free_netdev(dev);
> +       platform_set_drvdata(pdev, NULL);
> +       return 0;
> +}
> +
> +static void pxa168_eth_shutdown(struct platform_device *pdev)
> +{
> +       struct net_device *dev = platform_get_drvdata(pdev);
> +       eth_port_reset(dev);
> +}
> +
> +#ifdef CONFIG_PM
> +static int pxa168_eth_resume(struct platform_device *pdev)
> +{
> +       return -ENOSYS;
> +}
> +
> +static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +       return -ENOSYS;
> +}
> +
> +#else
> +#define pxa168_eth_resume NULL
> +#define pxa168_eth_suspend NULL
> +#endif
> +
> +static struct platform_driver pxa168_eth_driver = {
> +       .probe = pxa168_eth_probe,
> +       .remove = pxa168_eth_remove,
> +       .shutdown = pxa168_eth_shutdown,
> +       .resume = pxa168_eth_resume,
> +       .suspend = pxa168_eth_suspend,
> +       .driver = {
> +                  .name = DRIVER_NAME,
> +                  },
> +};
> +
> +static int __init pxa168_init_module(void)
> +{
> +       return platform_driver_register(&pxa168_eth_driver);
> +}
> +
> +static void __exit pxa168_cleanup_module(void)
> +{
> +       platform_driver_unregister(&pxa168_eth_driver);
> +}
> +
> +module_init(pxa168_init_module);
> +module_exit(pxa168_cleanup_module);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168");
> +MODULE_ALIAS("platform:pxa168_eth");
> diff --git a/include/linux/pxa168_eth.h b/include/linux/pxa168_eth.h
> new file mode 100644
> index 0000000..18d75e7
> --- /dev/null
> +++ b/include/linux/pxa168_eth.h
> @@ -0,0 +1,30 @@
> +/*
> + *pxa168 ethernet platform device data definition file.
> + */
> +#ifndef __LINUX_PXA168_ETH_H
> +#define __LINUX_PXA168_ETH_H
> +
> +struct pxa168_eth_platform_data {
> +       int     port_number;
> +       int     phy_addr;
> +
> +       /*
> +        * If speed is 0, then speed and duplex are autonegotiated.
> +        */
> +       int     speed;          /* 0, SPEED_10, SPEED_100 */
> +       int     duplex;         /* DUPLEX_HALF or DUPLEX_FULL */
> +
> +       /*
> +        * Override default RX/TX queue sizes if nonzero.
> +        */
> +       int     rx_queue_size;
> +       int     tx_queue_size;
> +
> +       /*
> +        * init callback is used for board specific initialization
> +        * e.g on Aspenite its used to initialize the PHY transceiver.
> +        */
> +       int (*init)(void);
> +};
> +
> +#endif /* __LINUX_PXA168_ETH_H */
> --
> 1.5.3.3
>
>
>


^ permalink raw reply

* Re: [PATCH 2/2] platform: Facilitate the creation of pseudo-platform buses
From: Greg KH @ 2010-08-14 21:04 UTC (permalink / raw)
  To: Patrick Pannuto
  Cc: linux-kernel, linux-arm-msm, magnus.damm, grant.likely,
	Kevin Hilman, Paul Mundt, Magnus Damm, Rafael J. Wysocki,
	Eric Miao, Dmitry Torokhov, netdev
In-Reply-To: <4C649C25.5090808@codeaurora.org>

On Thu, Aug 12, 2010 at 06:13:09PM -0700, Patrick Pannuto wrote:
> On 08/10/2010 04:49 PM, Patrick Pannuto wrote:
> 
> ^^^ small bug here, this line should be deleted; any other comments though?

Sorry, been busy with the -rc1 merge stuff, I'll have more time to look
at it next week.

Well, after you address Grant's issues first :)

thanks,

greg k-h

^ permalink raw reply

* [patch] net/sched: remove unneeded NULL check
From: Dan Carpenter @ 2010-08-14 21:09 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: David S. Miller, Patrick McHardy, Jarek Poplawski, Eric Dumazet,
	Stephen Hemminger, netdev, kernel-janitors

There is no need to check "s".  nla_data() doesn't return NULL.  Also we
already dereferenced "s" at this point so it would have oopsed ealier if
it were NULL.

Signed-off-by: Dan Carpenter <error27@gmail.com>

diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 408eea7..6fb3d41 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -360,7 +360,7 @@ static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt)
 		tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16);
 	}
 
-	if (!s || tsize != s->tsize || (!tab && tsize > 0))
+	if (tsize != s->tsize || (!tab && tsize > 0))
 		return ERR_PTR(-EINVAL);
 
 	spin_lock(&qdisc_stab_lock);

^ permalink raw reply related

* GTSM and TCP accept problem
From: Stephen Hemminger @ 2010-08-15  1:46 UTC (permalink / raw)
  To: Eric Dumazet, Herbert Xu; +Cc: netdev

The implementation of Generalized TTL Security has a problem
on the TCP accept side. Since there can be different number of
hops for each peer, the listener ends up doing

   socket
   bind
   set TTL to 255
   listen
   while (nfd = accept(peer)) {
     info = lookup(peer)
     set MINTTL to 255 - info->ttl_hops


The problem is that a rogue peer can still do three way
handshake causing the accept to succeed. But the rogue will
leave a stuck connection that will then have to timeout.

The only ways I have come up to deal with this are:
   * have short timeout on initial data (recycle faster)
   * push peer;MINTTL table down into kernel (per socket)
   * have BGP do this through iptables which is a non-starter
     for a general application that needs to run on BSD, and
     would mess up existing firewall rules.

Ideas welcome..

^ permalink raw reply

* Re: [GIT] Networking
From: David Miller @ 2010-08-15  2:28 UTC (permalink / raw)
  To: torvalds; +Cc: akpm, netdev, linux-kernel
In-Reply-To: <AANLkTinGy+-ELbVx_2WJ1wOJCsz4Rez26Q2Baisws9+C@mail.gmail.com>

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Sat, 14 Aug 2010 11:05:54 -0700

> Anyway, the lock warning I do get seems to be networking-related, and
> is appended. Does this ring any bells? It could easily be something
> old: I turn on lock debugging only when I look for bugs (or when
> people point out bugs that I've created :^/ )
> 
> The only thing that seems to be related that google can find is pretty
> recent too: a report from Valdis Kletnieks about this apparently
> happening on e1000e too (Subject "mmotm 2010-08-11 - lockdep whinges
> at e1000e driver ifconfig up"). So it does seem to be pretty recent.
> 
> Hmm? Everything obviously still works, but judging by the lockdep
> report this might be a deadlock situation (lock taken in softirq _and_
> outside softirq without disabling bhs)

I'll take a look.

^ permalink raw reply

* Re: [GIT] Networking
From: David Miller @ 2010-08-15  5:09 UTC (permalink / raw)
  To: torvalds; +Cc: akpm, netdev, linux-kernel
In-Reply-To: <AANLkTinGy+-ELbVx_2WJ1wOJCsz4Rez26Q2Baisws9+C@mail.gmail.com>

From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Sat, 14 Aug 2010 11:05:54 -0700

> Anyway, the lock warning I do get seems to be networking-related, and
> is appended. Does this ring any bells? It could easily be something
> old: I turn on lock debugging only when I look for bugs (or when
> people point out bugs that I've created :^/ )

This is a false positive but I have no idea how we can annotate
this to not trigger in lockdep.

These are per-cpu locks for counter management.

The get_counters() code knows that the locks for other cpu's counters
can only be taken in software interrupt context of that other cpu.  So
it is legal to turn software interrupts back on when grabbing their
locks in base context.

CC:'ing Eric Dumazet since he put the code the way it is now :-)
Via commit 24b36f0193467fa727b85b4c004016a8dae999b9
("netfilter: {ip,ip6,arp}_tables: dont block bottom half more than necessary")

^ permalink raw reply

* Re: [GIT] Networking
From: David Miller @ 2010-08-15  5:10 UTC (permalink / raw)
  To: torvalds; +Cc: akpm, netdev, linux-kernel, eric.dumazet
In-Reply-To: <20100814.220945.232761341.davem@davemloft.net>

From: David Miller <davem@davemloft.net>
Date: Sat, 14 Aug 2010 22:09:45 -0700 (PDT)

Umm, didn't actually CC: Eric, doing that now :-)

> From: Linus Torvalds <torvalds@linux-foundation.org>
> Date: Sat, 14 Aug 2010 11:05:54 -0700
> 
>> Anyway, the lock warning I do get seems to be networking-related, and
>> is appended. Does this ring any bells? It could easily be something
>> old: I turn on lock debugging only when I look for bugs (or when
>> people point out bugs that I've created :^/ )
> 
> This is a false positive but I have no idea how we can annotate
> this to not trigger in lockdep.
> 
> These are per-cpu locks for counter management.
> 
> The get_counters() code knows that the locks for other cpu's counters
> can only be taken in software interrupt context of that other cpu.  So
> it is legal to turn software interrupts back on when grabbing their
> locks in base context.
> 
> CC:'ing Eric Dumazet since he put the code the way it is now :-)
> Via commit 24b36f0193467fa727b85b4c004016a8dae999b9
> ("netfilter: {ip,ip6,arp}_tables: dont block bottom half more than necessary")
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* Re: GTSM and TCP accept problem
From: David Miller @ 2010-08-15  5:12 UTC (permalink / raw)
  To: shemminger; +Cc: dada1, herbert, netdev
In-Reply-To: <20100814214634.24a87715@s6510>

From: Stephen Hemminger <shemminger@vyatta.com>
Date: Sat, 14 Aug 2010 21:46:34 -0400

> The implementation of Generalized TTL Security has a problem
> on the TCP accept side. Since there can be different number of
> hops for each peer, the listener ends up doing
> 
>    socket
>    bind
>    set TTL to 255
>    listen
>    while (nfd = accept(peer)) {
>      info = lookup(peer)
>      set MINTTL to 255 - info->ttl_hops

Surely when creating the initial listening socket we should use the
minimum TTL over the set of peers right?

That surely takes most of the fun out of this problem for the
attacker.

^ permalink raw reply

* Re: tproxy related crash in inet_hashtables
From: David Miller @ 2010-08-15  5:16 UTC (permalink / raw)
  To: stephen.buck; +Cc: eric.dumazet, netdev, netfilter-devel, kaber, hidden
In-Reply-To: <4C661D09.8030709@exinda.com>

From: Stephen Buck <stephen.buck@exinda.com>
Date: Sat, 14 Aug 2010 14:35:21 +1000

> +		}
> +		if (!node)
> +			tb = inet_bind_bucket_create(table->bind_bucket_cachep,
> +						     sock_net(sk), head, port);
> +	}
>  	sk_add_bind_node(child, &tb->owners);

inet_bind_bucket_create() can fail due to memory allocation failure,
you're therefore going to have to find a way to handle that.

And in doing so you're going to find out that there is no easy
way out of this code path in such an error, this code was
absolutely not designed to be able to handle cases like this.

^ permalink raw reply

* Re: PROBLEM: raw sockets rewriting IP ID in rare cases.
From: David Miller @ 2010-08-15  5:35 UTC (permalink / raw)
  To: morgon.j.kanter; +Cc: netdev
In-Reply-To: <201008131307.38381.morgon.j.kanter@dartmouth.edu>

From: "Morgon J. Kanter" <morgon.j.kanter@dartmouth.edu>
Date: Fri, 13 Aug 2010 13:07:38 -0400

> I have stumbled across what I think is a rare bug in the raw socket mechanism 
> of the kernel. When attempting to send a certain packet with an IP ID of zero 
> from a raw socket created with the call "socket(AF_INET, SOCK_RAW, 
> IPPROTO_RAW)", the kernel will modify the IP ID field and update the checksum. 
> It only seems to do it with this specific packet for some reason, similar 
> packets with an IP ID of zero do not get so mangled.

Even when using hdrinclude mode of RAW ipv4 sockets, if you specify
a zero ID field, the kernel will fill it in with the usual value.

It is expected behavior.

If you want the kernel to leave your ID field alone, provide a
non-zero one.

^ permalink raw reply

* Re: [PATCH net-next-2.6] netlink: netlink_recvmsg() fix
From: David Miller @ 2010-08-15  5:37 UTC (permalink / raw)
  To: johannes; +Cc: eric.dumazet, netdev
In-Reply-To: <1281712433.2116.15.camel@jlt3.sipsolutions.net>

From: Johannes Berg <johannes@sipsolutions.net>
Date: Fri, 13 Aug 2010 17:13:53 +0200

> On Fri, 2010-08-13 at 16:48 +0200, Eric Dumazet wrote:
> 
>> I assume reverting 1235f504aaba removes these errors ?
> 
> I haven't tried yet, but it only happened very recently and I didn't
> find any other candidate -- the error always points to
> wireless_send_event too.

Please test with the commit reverted and let us know if it helps.

The current situation is worse than what we were trying to fix
in that commit, so if a revert fixes your problem then as Eric
said we should do that first.

^ permalink raw reply

* Re: [xfrm_user] BUG: sleeping function called from invalid context
From: David Miller @ 2010-08-15  5:38 UTC (permalink / raw)
  To: herbert; +Cc: kronos.it, netdev, linux-kernel
In-Reply-To: <20100813153658.GA27982@gondor.apana.org.au>

From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Fri, 13 Aug 2010 11:36:58 -0400

> Luca Tettamanti <kronos.it@gmail.com> wrote:
>>
>> xfrm_user_policy takes read_lock(&xfrm_km_lock) before calling
>> xfrm_compile_policy (via km->compile_policy), which in turn calls
>> xfrm_policy_alloc with GFP_KERNEL.
> 
> Thanks for discovering this bug, it only took 8 years :)

We stumble over one of these every so often don't we? :)

> 
> xfrm: Use GFP_ATOMIC in xfrm_compile_policy
> 
> As xfrm_compile_policy runs within a read_lock, we cannot use
> GFP_KERNEL for memory allocations.
> 
> Reported-by: Luca Tettamanti <kronos.it@gmail.com>
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Applied, thanks Herbert.

^ permalink raw reply

* Re: [PATCH] ipv6: remove sysctl jiffies conversion on gc_elasticity and min_adv_ms
From: David Miller @ 2010-08-15  5:43 UTC (permalink / raw)
  To: mzhang; +Cc: netdev
In-Reply-To: <alpine.LFD.2.00.1008131249060.27872@mvista.com>

From: Min Zhang <mzhang@mvista.com>
Date: Fri, 13 Aug 2010 12:50:37 -0700 (PDT)

> ipv6: remove sysctl jiffies conversion on gc_elasticity and
> min_adv_mss
> 
> sysctl output ipv6 gc_elasticity and min_adv_mss as values divided by
> HZ. However, they are not in unit of jiffies, since ip6_rt_min_advmss
> refers to packet size and ip6_rt_fc_elasticity is used as scaler as in
> expire>>ip6_rt_gc_elasticity, so replace the jiffies conversion
> handler will regular handler for them.
> 
> This has impact on scripts that are currently working assuming the
> divide by HZ, will yield different results with this patch in place.
> 
> Signed-off-by: Min Zhang <mzhang@mvista.com>

Applied, thank you.

^ permalink raw reply

* Re: [PATCH] netpoll: use non-BH variant of RCU
From: David Miller @ 2010-08-15  6:50 UTC (permalink / raw)
  To: herbert; +Cc: paulmck, linville, netdev, mingo, linux-kernel
In-Reply-To: <20100813175157.GA29586@gondor.apana.org.au>

From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Fri, 13 Aug 2010 13:51:57 -0400

> On Fri, Aug 13, 2010 at 09:29:12AM -0700, Paul E. McKenney wrote:
>> But all in all, mightn't it be easier to remove the checks from
>> _local_bh_enable(), and then just use rcu_read_lock_bh()?  Have those
>> checks really been that helpful in finding bugs?  ;-)
> 
> You are right.  It would be much simpler to simply have it not
> warn.

For now I'm going to assume that this is how the issue will be
addressed.

^ permalink raw reply

* Re: [GIT] Networking
From: Eric Dumazet @ 2010-08-15 14:47 UTC (permalink / raw)
  To: David Miller; +Cc: torvalds, akpm, netdev, linux-kernel, Patrick McHardy
In-Reply-To: <1281869722.2942.20.camel@edumazet-laptop>

Le dimanche 15 août 2010 à 12:55 +0200, Eric Dumazet a écrit :

> We have one lock per cpu, and only one cpu can possibly lock its
> associated lock under softirq. So the usual lockdep check, warning a
> lock is taken with BH enabled, while same lock was taken inside softirq
> handler is triggering a false positive here.
> 
> I believe no existing lockdep annotation can instruct lockdep this use
> is OK, I guess we have following choice :
> 
> 1) Mask BH again, using xt_info_wrlock_lockdep(cpu) instead of
> xt_info_wrlock(cpu).
> 
> xt_info_wrlock_lockdep() being a variant, that disables BH in case
> CONFIG_PROVE_LOCKING=y
> 
> 2) temporally switch off lockdep in get_counters(), using a
> lockdep_off()/lockdep_on() pair, and a comment why this is necessary.
> 

In any case, here is patch implementing the later

CC Patrick, our netfilter maintainer...

Maybe lockdep rules could be improved to take care of this later ?

Thanks

[PATCH] netfilter: {ip,ip6,arp}_tables: avoid lockdep false positive

After commit 24b36f019 (netfilter: {ip,ip6,arp}_tables: dont block
bottom half more than necessary), lockdep can raise a warning
because we attempt to lock a spinlock with BH enabled, while
the same lock is usually locked by another cpu in a softirq context.

In this use case, the lockdep splat is a false positive, because
the BH disabling only matters for one cpu for a given lock
(we use one lock per cpu).

Use lockdep_off()/lockdep_on() around the problematic section to
avoid the splat.

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Diagnosed-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Patrick McHardy <kaber@trash.net>
---
 net/ipv4/netfilter/arp_tables.c |    3 +++
 net/ipv4/netfilter/ip_tables.c  |    3 +++
 net/ipv6/netfilter/ip6_tables.c |    3 +++
 3 files changed, 9 insertions(+)

diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 6bccba3..b4f7ebf 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -729,8 +729,10 @@ static void get_counters(const struct xt_table_info *t,
 	local_bh_enable();
 	/* Processing counters from other cpus, we can let bottom half enabled,
 	 * (preemption is disabled)
+	 * We must turn off lockdep to avoid a false positive.
 	 */
 
+	lockdep_off();
 	for_each_possible_cpu(cpu) {
 		if (cpu == curcpu)
 			continue;
@@ -743,6 +745,7 @@ static void get_counters(const struct xt_table_info *t,
 		}
 		xt_info_wrunlock(cpu);
 	}
+	lockdep_on();
 	put_cpu();
 }
 
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index c439721..dc5b2fd 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -903,8 +903,10 @@ get_counters(const struct xt_table_info *t,
 	local_bh_enable();
 	/* Processing counters from other cpus, we can let bottom half enabled,
 	 * (preemption is disabled)
+	 * We must turn off lockdep to avoid a false positive.
 	 */
 
+	lockdep_off();
 	for_each_possible_cpu(cpu) {
 		if (cpu == curcpu)
 			continue;
@@ -917,6 +919,7 @@ get_counters(const struct xt_table_info *t,
 		}
 		xt_info_wrunlock(cpu);
 	}
+	lockdep_on();
 	put_cpu();
 }
 
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 5359ef4..fb55443 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -916,8 +916,10 @@ get_counters(const struct xt_table_info *t,
 	local_bh_enable();
 	/* Processing counters from other cpus, we can let bottom half enabled,
 	 * (preemption is disabled)
+	 * We must turn off lockdep to avoid a false positive.
 	 */
 
+	lockdep_off();
 	for_each_possible_cpu(cpu) {
 		if (cpu == curcpu)
 			continue;
@@ -930,6 +932,7 @@ get_counters(const struct xt_table_info *t,
 		}
 		xt_info_wrunlock(cpu);
 	}
+	lockdep_on();
 	put_cpu();
 }
 

^ permalink raw reply related

* Re: [GIT] Networking
From: Eric Dumazet @ 2010-08-15 10:55 UTC (permalink / raw)
  To: David Miller; +Cc: torvalds, akpm, netdev, linux-kernel
In-Reply-To: <20100814.220945.232761341.davem@davemloft.net>

Le samedi 14 août 2010 à 22:09 -0700, David Miller a écrit :
> From: Linus Torvalds <torvalds@linux-foundation.org>
> Date: Sat, 14 Aug 2010 11:05:54 -0700
> 
> > Anyway, the lock warning I do get seems to be networking-related, and
> > is appended. Does this ring any bells? It could easily be something
> > old: I turn on lock debugging only when I look for bugs (or when
> > people point out bugs that I've created :^/ )
> 
> This is a false positive but I have no idea how we can annotate
> this to not trigger in lockdep.
> 
> These are per-cpu locks for counter management.
> 
> The get_counters() code knows that the locks for other cpu's counters
> can only be taken in software interrupt context of that other cpu.  So
> it is legal to turn software interrupts back on when grabbing their
> locks in base context.
> 
> CC:'ing Eric Dumazet since he put the code the way it is now :-)
> Via commit 24b36f0193467fa727b85b4c004016a8dae999b9
> ("netfilter: {ip,ip6,arp}_tables: dont block bottom half more than necessary")

Hmm... thats right.

We have one lock per cpu, and only one cpu can possibly lock its
associated lock under softirq. So the usual lockdep check, warning a
lock is taken with BH enabled, while same lock was taken inside softirq
handler is triggering a false positive here.

I believe no existing lockdep annotation can instruct lockdep this use
is OK, I guess we have following choice :

1) Mask BH again, using xt_info_wrlock_lockdep(cpu) instead of
xt_info_wrlock(cpu).

xt_info_wrlock_lockdep() being a variant, that disables BH in case
CONFIG_PROVE_LOCKING=y

2) temporally switch off lockdep in get_counters(), using a
lockdep_off()/lockdep_on() pair, and a comment why this is necessary.

I'll provide a patch with either way, just tell me which one you
prefer !

Thanks



^ permalink raw reply

* Re: GTSM and TCP accept problem
From: Stephen Hemminger @ 2010-08-15 16:38 UTC (permalink / raw)
  To: David Miller; +Cc: dada1, herbert, netdev
In-Reply-To: <20100814.221258.35029372.davem@davemloft.net>

On Sat, 14 Aug 2010 22:12:58 -0700 (PDT)
David Miller <davem@davemloft.net> wrote:

> From: Stephen Hemminger <shemminger@vyatta.com>
> Date: Sat, 14 Aug 2010 21:46:34 -0400
> 
> > The implementation of Generalized TTL Security has a problem
> > on the TCP accept side. Since there can be different number of
> > hops for each peer, the listener ends up doing
> > 
> >    socket
> >    bind
> >    set TTL to 255
> >    listen
> >    while (nfd = accept(peer)) {
> >      info = lookup(peer)
> >      set MINTTL to 255 - info->ttl_hops
> 
> Surely when creating the initial listening socket we should use the
> minimum TTL over the set of peers right?
> 
> That surely takes most of the fun out of this problem for the
> attacker.

That only works if all peers are external and using ttl-security.
If any peer is not using ttl-security, then that peer will be
sending with low TTL and the SYN would get dropped.

The real problem goes back to the GTSM RFC assumptions that TTL
security will be done by firewall, not TCP itself.

^ permalink raw reply

* Re: PROBLEM: raw sockets rewriting IP ID in rare cases.
From: Morgon.J.Kanter @ 2010-08-15 17:57 UTC (permalink / raw)
  To: David Miller; +Cc: netdev@vger.kernel.org
In-Reply-To: <20100814.223532.242143460.davem@davemloft.net>

Quoting David Miller <davem@davemloft.net>:
>> I have stumbled across what I think is a rare bug in the raw socket  
>>  mechanism
>> of the kernel. When attempting to send a certain packet with an IP   
>> ID of zero
>> from a raw socket created with the call "socket(AF_INET, SOCK_RAW,
>> IPPROTO_RAW)", the kernel will modify the IP ID field and update   
>> the checksum.
>> It only seems to do it with this specific packet for some reason, similar
>> packets with an IP ID of zero do not get so mangled.
>
> Even when using hdrinclude mode of RAW ipv4 sockets, if you specify
> a zero ID field, the kernel will fill it in with the usual value.
>
> It is expected behavior.
>
> If you want the kernel to leave your ID field alone, provide a
> non-zero one.

Is there a way to turn this behavior off, then, with raw sockets? Zero  
is the desired ID value.

Thanks,
-- Morgon

^ permalink raw reply

* Patch-o-match
From: Philip Prindeville @ 2010-08-15 18:14 UTC (permalink / raw)
  To: Netdev

  Hi.

I'd like to build our astlinux images (it's an automated build) with the geoip and TARPIT patches, but can't seem to figure out how.

The links from the Netfilter extensions HowTo seemed to be broken... and since I'm statically building images, I figure that just figuring out the appropriate patches and downloading them by once is probably easier than getting patch-o-matic installed and scripted...  How do I go about doing that?

Oh, maybe also the ROUTE target.  Seemed that it and --tee would be useful for implementing an FBI Jack for lawful intercept in our PBX.

I know how to intercept a particular SIP session, but not the associated SDP (RTP) session that goes with it.  Is there an easy way to do that as well?

Thanks.

-Philip


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox