aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-2.6.28/stb225/ip3902.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-2.6.28/stb225/ip3902.patch')
-rw-r--r--recipes/linux/linux-2.6.28/stb225/ip3902.patch1549
1 files changed, 1549 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.28/stb225/ip3902.patch b/recipes/linux/linux-2.6.28/stb225/ip3902.patch
new file mode 100644
index 0000000000..3f911b661d
--- /dev/null
+++ b/recipes/linux/linux-2.6.28/stb225/ip3902.patch
@@ -0,0 +1,1549 @@
+diff -urN --exclude=.svn linux-2.6.26-rc4.orig/drivers/net/ip3902.c linux-2.6.26-rc4/drivers/net/ip3902.c
+--- linux-2.6.26-rc4.orig/drivers/net/ip3902.c 1970-01-01
+01:00:00.000000000 +0100
++++ linux-2.6.26-rc4/drivers/net/ip3902.c 2008-06-06 11:29:24.000000000 +0100
+@@ -0,0 +1,1510 @@
++/*
++ * ip3902.c: NXP ip3902 embedded 10/100 Ethernet controller support
++ * Copyright 2008 NXP Semiconductors
++ * Chris Steel <chris.steel@nxp.com>
++ * Daniel Laird <daniel.j.laird@nxp.com>
++ *
++ * Based on ax88796.c, by Ben Dooks.
++ * Based on previous ip3902.c by Nikita V. Youshchenko
++ *
++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/isapnp.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/ethtool.h>
++#include <linux/mii.h>
++#include <linux/crc32.h>
++#include <linux/inet_lro.h>
++#include <asm/system.h>
++#include <linux/io.h>
++
++#define DRVNAME "ip3902-eth"
++#define DRVVERSION "1.00"
++
++/* "Strange hardware" support macros */
++
++/* These control endianness of descriptors and statuses.
++ * If none if LITTLE_ENDIAN_xxx and BIG_ENDIAN_xxx is defined, system endian
++ * is used for xxx */
++#define LITTLE_ENDIAN_DESCRIPTORS
++#undef BIG_ENDIAN_DESCRIPTORS
++#undef LITTLE_ENDIAN_STATUSES
++#define BIG_ENDIAN_STATUSES
++
++#define ETH_RX_SKB_SIZE 0x600 /* 1536 bytes, just over max mtu */
++#define TX_RING_SIZE 64
++#define RX_RING_SIZE 64
++#define IP3902_NAPI_WEIGHT 48
++#define MAX_LRO_DESCRIPTORS 6
++#define LRO_THRESHOLD 3
++
++#define BYTES_IN_ETHERNET_CRC 4
++#define MAX_DESCS_PER_SKB (MAX_SKB_FRAGS + 1)
++
++#define NEXT_TX(i) (((i) == TX_RING_SIZE-1) ? 0 : (i)+1)
++#define NEXT_RX(i) (((i) == RX_RING_SIZE-1) ? 0 : (i)+1)
++
++/* Access to IP3902 registers */
++
++/* Alcatel (Packet Engines) core registers */
++#define MAC1_REG 0x000 /* R/W: MAC configuration register 1 */
++#define MAC2_REG 0x004 /* R/W: MAC configuration register 2 */
++#define IPGT_REG 0x008 /* R/W: Back-to-Back Inter-Packet-Gap register */
++#define IPGR_REG 0x00c /* R/W: Non Back-to-Back Inter-Packet-Gap register */
++#define CLRT_REG 0x010 /* R/W: Collision window / Retry register */
++#define MAXF_REG 0x014 /* R/W: Maximum Frame register */
++#define SUPP_REG 0x018 /* R/W: PHY Support register */
++#define TEST_REG 0x01C /* R/W: Test register */
++#define MCFG_REG 0x020 /* R/W: MII Mgmt Con???guration register */
++#define MCMD_REG 0x024 /* R/W: MII Mgmt Command register */
++#define MADR_REG 0x028 /* R/W: MII Mgmt Address register */
++#define MWTD_REG 0x02C /* WO: MII Mgmt Write Data register */
++#define MRDD_REG 0x030 /* RO: MII Mgmt Read Data register */
++#define MIND_REG 0x034 /* RO: MII Mgmt Indicators register */
++#define SA0_REG 0x040 /* R/W: Station Address 0 register */
++#define SA1_REG 0x044 /* R/W: Station Address 1 register */
++#define SA2_REG 0x048 /* R/W: Station Address 2 register */
++
++/* Control registers */
++#define COMMAND_REG 0x100 /* R/W: Command register */
++#define STATUS_REG 0x104 /* RO: Status register */
++#define RX_DESC_REG 0x108 /* R/W: Receive descriptor base address register */
++#define RX_STATUS_REG 0x10C /* R/W: Receive status base address register */
++#define RX_DESC_NUMBER_REG 0x110 /* R/W: Receive number of descriptors register */
++#define RX_PRODUCE_INDEX_REG 0x114 /* RO: Receive produce index register */
++#define RX_CONSUME_INDEX_REG 0x118 /* R/W: Receive consume index register */
++#define TX_DESC_REG 0x11C /* R/W: Non real-time transmit descriptor base address register */
++#define TX_STATUS_REG 0x120 /* R/W: Non real-time transmit status base address register */
++#define TX_DESC_NUMBER_REG 0x124 /* R/W: Non real-time transmit number of descriptors register */
++#define TX_PRODUCE_INDEX_REG 0x128 /* R/W: Non real-time transmit produce index register */
++#define TX_CONSUME_INDEX_REG 0x12C /* RO: Non real-time transmit consume index register */
++#define TX_RT_DESC_REG 0x130 /* R/W: Real-time transmit descriptor base address register */
++#define TX_RT_STATUS_REG 0x134 /* R/W: Real-time transmit status base address register */
++#define TX_RT_DESC_NUMBER_REG 0x138 /* R/W: Real-time transmit number of descriptors register */
++#define TX_RT_PRODUCE_INDEX_REG 0x13C /* R/W: Real-time transmit produce index register */
++#define TX_RT_CONSUME_INDEX_REG 0x140 /* RO: Real-time transmit consume index register */
++#define QOS_TIMEOUT_REG 0x148 /* R/W: Transmit quality of service time-out register */
++#define TSV0_REG 0x158 /* RO: Transmit status vector 0 register */
++#define TSV1_REG 0x15C /* RO: Transmit status vector 1 register */
++#define RSV_REG 0x160 /* RO: Receive status vector register */
++#define FC_COUNTER_REG 0x170 /* R/W: Flow control counter register */
++#define FC_STATUS_REG 0x174 /* RO: Flow control status register */
++
++/* Rx filter registers */
++#define FILTER_CTRL_REG 0x200 /* R/W: Receive filter control register */
++#define FILTER_WOL_STATUS_REG 0x204 /* RO: Receive filter WoL status register */
++#define FILTER_WOL_CLEAR_REG 0x208 /* WO: Receive filter WoL clear register */
++#define HASH_FILTER_L_REG 0x210 /* R/W: Hash filter table LSBs register */
++#define HASH_FILTER_H_REG 0x214 /* R/W: Hash filter table MSBs register */
++
++/* DVP Standard registers */
++#define INT_STATUS_REG 0xFE0 /* RO: Interrupt status register */
++#define INT_ENABLE_REG 0xFE4 /* R/W: Interrupt enable register */
++#define INT_CLEAR_REG 0xFE8 /* WO: Interrupt clear register */
++#define INT_SET_REG 0xFEC /* WO: Interrupt set register */
++#define POWERDOWN_REG 0xFF4 /* R/W: Power-down register */
++#define MODULE_ID_REG 0xFFC /* RO: Module ID register */
++
++/* Bits for MAC1 register */
++#define MAC1_SOFT_RESET (1 << 15)
++#define MAC1_TX_FLOW_CONTROL (1 << 3)
++#define MAC1_RX_FLOW_CONTROL (1 << 2)
++#define MAC1_RECEIVE_PASS_ALL (1 << 1)
++#define MAC1_RECEIVE_ENABLE (1 << 0)
++
++/* Bits for MAC2 register */
++#define MAC2_AUTO_DETECT_PAD_ENABLE (1 << 7)
++#define MAC2_VLAN_PAD_ENABLE (1 << 6)
++#define MAC2_PAD_CRC_ENABLE (1 << 5)
++#define MAC2_CRC_ENABLE (1 << 4)
++#define MAC2_FULL_DUPLEX (1 << 0)
++
++#define INITIAL_MAC2 (MAC2_AUTO_DETECT_PAD_ENABLE | MAC2_VLAN_PAD_ENABLE | MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE)
++
++/* Recommended values for IPGT register (see sec. 3.3.2.3 0f datasheet */
++#define IPGT_FD_VALUE 0x15
++#define IPGT_HD_VALUE 0x12
++
++/* Bits for MCMD register */
++#define MCMD_READ (1 << 0)
++
++/* Bits for MIND register */
++#define MIND_NOT_VALID (1 << 2)
++#define MIND_BUSY (1 << 0)
++
++/* Bits for command register */
++#define COMMAND_ENABLE_QOS (1 << 11)
++#define COMMAND_FULL_DUPLEX (1 << 10)
++#define COMMAND_RMII_MODE (1 << 9)
++#define COMMAND_TX_FLOW_CONTROL (1 << 8)
++#define COMMAND_PROMISC (1 << 7)
++#define COMMAND_ALLOW_SHORT (1 << 6)
++#define COMMAND_RX_RESET (1 << 5)
++#define COMMAND_TX_RESET (1 << 4)
++#define COMMAND_RESET (1 << 3)
++#define COMMAND_TX_RT_ENABLE (1 << 2)
++#define COMMAND_TX_ENABLE (1 << 1)
++#define COMMAND_RX_ENABLE (1 << 0)
++
++/* Bits for receive filter control register */
++#define FILTER_ACCEPT_SELF (1 << 5)
++#define FILTER_ACCEPT_MCAST_HASH (1 << 4)
++#define FILTER_ACCEPT_UCAST_HASH (1 << 3)
++#define FILTER_ACCEPT_MCAST_ANY (1 << 2)
++#define FILTER_ACCEPT_BCAST_ANY (1 << 1)
++#define FILTER_ACCEPT_UCAST_ANY (1 << 0)
++
++/* Bits for interrupt registers */
++#define WAKEUP_INT (1 << 13)
++#define SOFT_INT (1 << 12)
++#define TX_RT_DONE_INT (1 << 11)
++#define TX_RT_FINISHED_INT (1 << 10)
++#define TX_RT_ERROR_INT (1 << 9)
++#define TX_RT_UNDERRUN_INT (1 << 8)
++#define TX_DONE_INT (1 << 7)
++#define TX_FINISHED_INT (1 << 6)
++#define TX_ERROR_INT (1 << 5)
++#define TX_UNDERRUN_INT (1 << 4)
++#define RX_DONE_INT (1 << 3)
++#define RX_FINISHED_INT (1 << 2)
++#define RX_ERROR_INT (1 << 1)
++#define RX_OVERRUN_INT (1 << 0)
++
++/* Bit for POWERDOWN register */
++#define POWERDOWN_VALUE (1 << 31)
++
++/* Bits for TX control */
++#define TX_CONTROL_INT (1 << 31)
++#define TX_CONTROL_LAST (1 << 30)
++#define TX_CONTROL_CRC (1 << 29)
++#define TX_CONTROL_PAD (1 << 28)
++#define TX_CONTROL_HUGE (1 << 27)
++#define TX_CONTROL_OVERRIDE (1 << 26)
++
++/* these flags used for non-last fragment of a frame */
++#define TX_CONTROL_ALL_NOTLAST (TX_CONTROL_CRC | TX_CONTROL_PAD | TX_CONTROL_OVERRIDE)
++/* these flags used for last fragment of a frame, and for single-fragment
++ * frames */
++#define TX_CONTROL_ALL_LAST (TX_CONTROL_ALL_NOTLAST | TX_CONTROL_LAST | TX_CONTROL_INT)
++
++/* Bits for TX status */
++#define TX_STATUS_ERROR (1 << 31)
++#define TX_STATUS_UNDERRUN (1 << 29)
++#define TX_STATUS_LATE_COLLISION (1 << 28)
++#define TX_STATUS_MANY_COLLISIONS (1 << 27)
++#define TX_STATUS_MANY_DEFER (1 << 26)
++#define TX_STATUS_COLLISIONS(s) ((s >> 21) & 15)
++
++/* Bits for RX control */
++#define RX_CONTROL_INT (1 << 31)
++
++/* Bits for RX status */
++#define RX_STATUS_ERROR (1 << 31)
++#define RX_STATUS_LAST_FRAG (1 << 30)
++#define RX_STATUS_OVERRUN (1 << 28)
++#define RX_STATUS_ALIGNMENT_ERROR (1 << 27)
++#define RX_STATUS_RANGE_ERROR (1 << 26)
++#define RX_STATUS_LENGTH_ERROR (1 << 25)
++#define RX_STATUS_SYMBOL_ERROR (1 << 24)
++#define RX_STATUS_CRC_ERROR (1 << 23)
++#define RX_STATUS_BROADCAST (1 << 22)
++#define RX_STATUS_MULTICAST (1 << 21)
++#define RX_STATUS_FAIL_FILTER (1 << 20)
++#define RX_STATUS_VLAN (1 << 19)
++#define RX_STATUS_CONTROL_FRAME (1 << 18)
++#define RX_STATUS_LENGTH(s) ((s & 0x7ff) + 1)
++
++/* Bits for RSV register */
++#define RSV_VLAN (1 << 30)
++#define RSV_CONTROL_FRAME (1 << 27)
++#define RSV_DRIBBLE_NIBBLE (1 << 26)
++#define RSV_BROADCAST (1 << 25)
++#define RSV_MULTICAST (1 << 24)
++#define RSV_LENGTH_OUT_OF_RANGE (1 << 22)
++#define RSV_LENGTH_CHECK_ERROR (1 << 21)
++#define RSV_CRC_ERROR (1 << 20)
++#define RSV_RECEIVE_CODE_VIOLATION (1 << 19)
++#define RSV_MASK 0xFFFF
++
++static char *mac_address;
++module_param(mac_address, charp, S_IRUGO);
++MODULE_PARM_DESC(mac_address, "MAC address of the device");
++
++
++/* device private data */
++struct ip3902_descriptor {
++ unsigned long address;
++ unsigned long control;
++};
++
++struct ip3902_rx_status {
++ unsigned long status;
++ unsigned long hash_crc;
++};
++
++struct ip3902_dma_struct {
++ struct ip3902_descriptor rx_desc[RX_RING_SIZE];
++ struct ip3902_rx_status rx_status[RX_RING_SIZE];
++ struct ip3902_descriptor tx_desc[TX_RING_SIZE];
++ unsigned long tx_status[TX_RING_SIZE];
++};
++
++struct ip3902_private {
++ spinlock_t mii_lock;
++ struct mii_if_info mii;
++ u32 msg_enable;
++
++ spinlock_t lock;
++ struct net_device *ndev;
++ struct platform_device *pdev;
++ struct resource *bus;
++ void __iomem *mem;
++ struct napi_struct napi;
++
++ struct ip3902_dma_struct *ds; /* descriptors and statuses */
++ dma_addr_t ds_dma;
++
++ struct sk_buff *rx_skb[RX_RING_SIZE]; /* where to recieve to */
++ struct sk_buff *tx_skb[TX_RING_SIZE]; /* where to send from */
++ bool tx_first_desc[TX_RING_SIZE]; /* true if this is the first desc of an skb */
++
++ int rx_next_allocate; /* index in rx ring where skb should be allocated */
++ int rx_next_consume; /* index in rx ring where data should be read when available */
++ int tx_next_produce; /* index in tx ring where new data should be put */
++ int tx_next_deallocate; /* index in tx ring of first not freed skb */
++
++#ifdef CONFIG_INET_LRO
++ bool use_lro;
++ int lro_count;
++ struct net_lro_mgr lro_mgr;
++ struct net_lro_desc lro_desc[MAX_LRO_DESCRIPTORS];
++ struct timer_list lro_timer;
++#endif
++
++ unsigned char running;
++ unsigned char resume_open;
++
++};
++
++static inline unsigned long ip3902_read_reg(struct net_device *ndev, int reg)
++{
++ unsigned long value = readl((void * __iomem)(ndev->base_addr + reg));
++ return value;
++}
++
++static inline void ip3902_write_reg(struct net_device *ndev, int reg,
++ unsigned long val)
++{
++ writel(val, (void * __iomem)(ndev->base_addr + reg));
++}
++
++static inline void ip3902_write_tx_desc(struct ip3902_private *ip3902_priv, int pos, unsigned long address, unsigned long control)
++{
++#if defined(BIG_ENDIAN_DESCRIPTORS)
++ ip3902_priv->ds->tx_desc[pos].address = cpu_to_be32(address);
++ ip3902_priv->ds->tx_desc[pos].control = cpu_to_be32(control);
++#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
++ ip3902_priv->ds->tx_desc[pos].address = cpu_to_le32(address);
++ ip3902_priv->ds->tx_desc[pos].control = cpu_to_le32(control);
++#else
++ ip3902_priv->ds->tx_desc[pos].address = address;
++ ip3902_priv->ds->tx_desc[pos].control = control;
++#endif
++ wmb();
++}
++
++static inline void ip3902_read_tx_desc(struct ip3902_private *ip3902_priv, int pos, dma_addr_t *address, int *length)
++{
++#if defined(BIG_ENDIAN_DESCRIPTORS)
++ *address = (dma_addr_t)be32_to_cpu(ip3902_priv->ds->tx_desc[pos].address);
++ *length = (int)be32_to_cpu(ip3902_priv->ds->tx_desc[pos].control) & 0xffff;
++#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
++ *address = (dma_addr_t)le32_to_cpu(ip3902_priv->ds->tx_desc[pos].address);
++ *length = (int)le32_to_cpu(ip3902_priv->ds->tx_desc[pos].control) & 0xffff;
++#else
++ *address = (dma_addr_t)ip3902_priv->ds->tx_desc[pos].address;
++ *length = (int)ip3902_priv->ds->tx_desc[pos].control & 0xffff;
++#endif
++}
++
++static inline unsigned long ip3902_read_tx_status(struct ip3902_private *ip3902_priv, int pos)
++{
++#if defined(BIG_ENDIAN_STATUSES)
++ return be32_to_cpu(ip3902_priv->ds->tx_status[pos]);
++#elif defined(LITTLE_ENDIAN_STATUSES)
++ return le32_to_cpu(ip3902_priv->ds->tx_status[pos]);
++#else
++ return ip3902_priv->ds->tx_status[pos];
++#endif
++}
++
++static inline void ip3902_write_rx_desc(struct ip3902_private *ip3902_priv, int pos, unsigned long address, unsigned long control)
++{
++#if defined(BIG_ENDIAN_DESCRIPTORS)
++ ip3902_priv->ds->rx_desc[pos].address = cpu_to_be32(address);
++ ip3902_priv->ds->rx_desc[pos].control = cpu_to_be32(control);
++#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
++ ip3902_priv->ds->rx_desc[pos].address = cpu_to_le32(address);
++ ip3902_priv->ds->rx_desc[pos].control = cpu_to_le32(control);
++#else
++ ip3902_priv->ds->rx_desc[pos].address = address;
++ ip3902_priv->ds->rx_desc[pos].control = control;
++#endif
++ wmb();
++}
++
++static inline void ip3902_read_rx_desc(struct ip3902_private *ip3902_priv, int pos, dma_addr_t *address, int *length)
++{
++#if defined(BIG_ENDIAN_DESCRIPTORS)
++ *address = (dma_addr_t)be32_to_cpu(ip3902_priv->ds->rx_desc[pos].address);
++ *length = (int)be32_to_cpu(ip3902_priv->ds->rx_desc[pos].control) & 0xffff;
++#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
++ *address = (dma_addr_t)le32_to_cpu(ip3902_priv->ds->rx_desc[pos].address);
++ *length = (int)le32_to_cpu(ip3902_priv->ds->rx_desc[pos].control) & 0xffff;
++#else
++ *address = (dma_addr_t)ip3902_priv->ds->rx_desc[pos].address;
++ *length = (int)ip3902_priv->ds->rx_desc[pos].control & 0xffff;
++#endif
++}
++
++static inline unsigned long ip3902_read_rx_status(struct net_device *ndev, struct ip3902_private *ip3902_priv, int pos)
++{
++#if defined(BIG_ENDIAN_STATUSES)
++ return be32_to_cpu(ip3902_priv->ds->rx_status[pos].status);
++#elif defined(LITTLE_ENDIAN_STATUSES)
++ return le32_to_cpu(ip3902_priv->ds->rx_status[pos].status);
++#else
++ return ip3902_priv->ds->rx_status[pos].status;
++#endif
++}
++
++static inline void ip3902_write_madr_reg(struct net_device *ndev, int phy_id, int location)
++{
++ /* assume ranges of phy_id and location are correct - we set masks in
++ * struct mii_if_info for that */
++
++ unsigned long val = (phy_id << 8) | location;
++ ip3902_write_reg(ndev, MADR_REG, val);
++}
++
++static inline int ip3902_wait_mdio_op_complete(struct net_device *ndev, unsigned long mask)
++{
++ int timeout = 10000; /* to avoid hangup in case of unexpected badness ... */
++
++ while (--timeout > 0) {
++ if ((ip3902_read_reg(ndev, MIND_REG) & mask) == 0)
++ return 0;
++ udelay(1);
++ }
++
++ return -EIO;
++}
++
++static int ip3902_mdio_read(struct net_device *ndev, int phy_id, int location)
++{
++ ip3902_write_madr_reg(ndev, phy_id, location);
++ ip3902_write_reg(ndev, MCMD_REG, 0);
++ ip3902_write_reg(ndev, MCMD_REG, MCMD_READ);
++ if (ip3902_wait_mdio_op_complete(ndev, MIND_NOT_VALID | MIND_BUSY) < 0)
++ return 0;
++ else
++ return ip3902_read_reg(ndev, MRDD_REG) & 0xffff;
++}
++
++static void ip3902_mdio_write(struct net_device *ndev, int phy_id, int location, int val)
++{
++ ip3902_write_madr_reg(ndev, phy_id, location);
++ ip3902_write_reg(ndev, MWTD_REG, val & 0xffff);
++ ip3902_wait_mdio_op_complete(ndev, MIND_BUSY);
++}
++
++static inline int ip3902_nr_free_descs(int head, int tail, int size)
++{
++ int free;
++
++ if (head >= tail)
++ free = (tail + size) - head;
++ else
++ free = tail - head;
++
++ return free;
++}
++
++static void ip3902_eth_rx_refill_descs(struct net_device *ndev, struct ip3902_private *ip3902_priv)
++{
++ do {
++ int rx_index = ip3902_priv->rx_next_allocate;
++ struct sk_buff *skb = netdev_alloc_skb(ndev, ETH_RX_SKB_SIZE + dma_get_cache_alignment());
++
++ if (skb) {
++ int unaligned = (((u32)skb->data) + ETH_HLEN) & (dma_get_cache_alignment() - 1);
++ unsigned long desc_address;
++
++ if (unaligned)
++ skb_reserve(skb, (dma_get_cache_alignment() - unaligned));
++
++ desc_address = dma_map_single(NULL, skb->data, ETH_RX_SKB_SIZE, DMA_FROM_DEVICE);
++ ip3902_write_rx_desc(ip3902_priv, rx_index, desc_address, (ETH_RX_SKB_SIZE - 1) | RX_CONTROL_INT);
++
++ ip3902_priv->rx_skb[rx_index] = skb;
++ ip3902_priv->rx_next_allocate = NEXT_RX(rx_index);
++ } else {
++ ip3902_write_reg(ndev, RX_CONSUME_INDEX_REG, ip3902_priv->rx_next_allocate);
++ return;
++ }
++ } while (ip3902_priv->rx_next_allocate != ip3902_priv->rx_next_consume);
++
++ ip3902_write_reg(ndev, RX_CONSUME_INDEX_REG, ip3902_priv->rx_next_allocate);
++}
++
++static int ip3902_eth_receive_queue(struct net_device *ndev, struct ip3902_private *ip3902_priv, int budget)
++{
++ int rx_index = ip3902_priv->rx_next_consume;
++ int write_index = ip3902_read_reg(ndev, RX_PRODUCE_INDEX_REG);
++ int received = 0;
++ int limit;
++
++ do {
++ limit = write_index;
++ spin_lock(&ip3902_priv->lock);
++ while (rx_index != limit) {
++ unsigned long status = ip3902_read_rx_status(ndev, ip3902_priv, rx_index);
++
++ if (!(status & RX_STATUS_LAST_FRAG)) {
++ printk(DRVNAME ": broken RX status: %08lx\n", status);
++ continue;
++ }
++
++ if (status & RX_STATUS_FAIL_FILTER)
++ continue;
++
++ /* Looks like hardware returns RANGE_ERROR for each frame */
++ if (status & (RX_STATUS_OVERRUN | RX_STATUS_ALIGNMENT_ERROR | RX_STATUS_LENGTH_ERROR | RX_STATUS_CRC_ERROR)) {
++ ndev->stats.rx_errors++;
++
++ if (status & RX_STATUS_OVERRUN)
++ ndev->stats.rx_fifo_errors++;
++
++ if (status & RX_STATUS_ALIGNMENT_ERROR)
++ ndev->stats.rx_frame_errors++;
++
++ if (status & (RX_STATUS_RANGE_ERROR | RX_STATUS_LENGTH_ERROR))
++ ndev->stats.rx_length_errors++;
++
++ if (status & RX_STATUS_CRC_ERROR)
++ ndev->stats.rx_crc_errors++;
++
++ } else {
++ if (--budget < 0) {
++ /* we got packets, but no quota */
++ /* store current ring pointer state */
++ ip3902_priv->rx_next_consume = rx_index;
++ return received;
++ } else {
++ struct sk_buff *skb = ip3902_priv->rx_skb[rx_index];
++ int length = RX_STATUS_LENGTH(status);
++ dma_addr_t data_addr;
++ int data_length;
++
++ ndev->stats.rx_packets++;
++ ndev->stats.rx_bytes += length;
++ if (status & RX_STATUS_MULTICAST)
++ ndev->stats.multicast++;
++
++ skb_put(skb, length - BYTES_IN_ETHERNET_CRC);
++ skb->protocol = eth_type_trans(skb, ndev);
++
++#ifdef CONFIG_INET_LRO
++ if (ip3902_priv->use_lro)
++ lro_receive_skb(&ip3902_priv->lro_mgr, skb, ip3902_priv);
++ else
++ netif_receive_skb(skb);
++
++ ip3902_priv->lro_count++;
++#else
++ netif_receive_skb(skb);
++#endif
++
++ ip3902_read_rx_desc(ip3902_priv, rx_index, &data_addr, &data_length);
++ dma_unmap_single(NULL, data_addr, ETH_RX_SKB_SIZE, DMA_FROM_DEVICE);
++
++ ip3902_priv->rx_skb[rx_index] = NULL;
++ ndev->last_rx = jiffies;
++ received++;
++ }
++ }
++ rx_index = NEXT_RX(rx_index);
++ }
++
++ spin_unlock(&ip3902_priv->lock);
++ ip3902_priv->rx_next_consume = rx_index;
++ ip3902_eth_rx_refill_descs(ndev, ip3902_priv);
++ write_index = ip3902_read_reg(ndev, RX_PRODUCE_INDEX_REG);
++ } while (limit != write_index);
++
++#ifdef CONFIG_INET_LRO
++ if (ip3902_priv->use_lro) {
++ if (timer_pending(&ip3902_priv->lro_timer)) {
++ mod_timer(&ip3902_priv->lro_timer, jiffies + 2);
++ } else {
++ ip3902_priv->lro_timer.expires = jiffies + 2;
++ add_timer(&ip3902_priv->lro_timer);
++ }
++ }
++#endif
++
++ return received;
++}
++
++static int ip3902_poll(struct napi_struct *napi, int budget)
++{
++ struct ip3902_private *ip3902_priv = container_of(napi, struct ip3902_private, napi);
++ struct net_device *ndev = ip3902_priv->ndev;
++ int work_done;
++
++ work_done = ip3902_eth_receive_queue(ndev, ip3902_priv, budget);
++
++ if (work_done < budget) {
++ ip3902_write_reg(ndev, INT_CLEAR_REG, RX_DONE_INT);
++ ip3902_write_reg(ndev, INT_CLEAR_REG, 0);
++ netif_rx_complete(ndev, napi);
++ ip3902_write_reg(ndev, INT_ENABLE_REG, (TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT));
++ }
++
++ return work_done;
++}
++
++#ifdef CONFIG_INET_LRO
++static void ip3902_lro_timeout(unsigned long data)
++{
++ struct ip3902_private *ip3902_priv = (struct ip3902_private *)data;
++
++ spin_lock(&ip3902_priv->lock);
++ if (ip3902_priv->lro_count <= LRO_THRESHOLD) {
++ ip3902_priv->use_lro = false;
++ ip3902_priv->lro_count = 0;
++ }
++ lro_flush_all(&ip3902_priv->lro_mgr);
++ spin_unlock(&ip3902_priv->lock);
++}
++#endif
++
++#define ip3902_eth_free_completed_tx_descs(ndev, priv) ip3902_eth_free_tx_descs(ndev, priv, 0)
++#define ip3902_eth_free_all_tx_descs(ndev, priv) ip3902_eth_free_tx_descs(ndev, priv, 1)
++
++static void ip3902_eth_free_tx_descs(struct net_device *ndev, struct ip3902_private *ip3902_priv, int force)
++{
++ int limit;
++
++ if (force)
++ limit = ip3902_priv->tx_next_produce;
++ else
++ limit = ip3902_read_reg(ndev, TX_CONSUME_INDEX_REG);
++
++ while (ip3902_priv->tx_next_deallocate != limit) {
++ int length;
++ int tx_index;
++ unsigned long status;
++ dma_addr_t addr;
++ struct sk_buff *skb;
++
++ tx_index = ip3902_priv->tx_next_deallocate;
++
++ ip3902_priv->tx_next_deallocate = NEXT_TX(tx_index);
++ ip3902_read_tx_desc(ip3902_priv, tx_index, &addr, &length);
++ skb = ip3902_priv->tx_skb[tx_index];
++
++ status = ip3902_read_tx_status(ip3902_priv, tx_index);
++ if (status & TX_STATUS_ERROR) {
++ ndev->stats.tx_errors++;
++ if (status & TX_STATUS_LATE_COLLISION)
++ ndev->stats.tx_aborted_errors++;
++
++ if (status & (TX_STATUS_MANY_COLLISIONS | TX_STATUS_MANY_DEFER))
++ ndev->stats.tx_window_errors++;
++
++ } else {
++ ndev->stats.tx_packets++;
++ ndev->stats.tx_bytes += skb->len;
++ ndev->stats.collisions += TX_STATUS_COLLISIONS(status);
++ }
++
++ if (skb)
++ ip3902_priv->tx_skb[tx_index] = NULL;
++
++ if (ip3902_priv->tx_first_desc[tx_index] == true)
++ dma_unmap_single(NULL, addr, length, DMA_TO_DEVICE);
++ else
++ dma_unmap_page(NULL, addr, length, DMA_TO_DEVICE);
++
++ if (skb)
++ dev_kfree_skb_irq(skb);
++ }
++}
++
++static void ip3902_reset_tx(struct net_device *ndev, struct ip3902_private *ip3902_priv, int initial)
++{
++ unsigned long val;
++
++ /* Reset Tx hardware */
++ val = ip3902_read_reg(ndev, COMMAND_REG);
++ val &= ~(COMMAND_TX_RT_ENABLE | COMMAND_TX_ENABLE);
++ val |= COMMAND_TX_RESET;
++ ip3902_write_reg(ndev, COMMAND_REG, val);
++
++ if (!initial)
++ ip3902_eth_free_all_tx_descs(ndev, ip3902_priv);
++
++ ip3902_priv->tx_next_produce = 0;
++ ip3902_priv->tx_next_deallocate = 0;
++
++ /* Configure Tx registers */
++ ip3902_write_reg(ndev, TX_DESC_REG, ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, tx_desc));
++ ip3902_write_reg(ndev, TX_STATUS_REG, ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, tx_status));
++ ip3902_write_reg(ndev, TX_DESC_NUMBER_REG, TX_RING_SIZE - 1);
++ ip3902_write_reg(ndev, TX_PRODUCE_INDEX_REG, ip3902_priv->tx_next_produce);
++ ip3902_write_reg(ndev, TX_CONSUME_INDEX_REG, ip3902_priv->tx_next_deallocate);
++}
++
++static void ip3902_reset_rx(struct net_device *ndev, struct ip3902_private *ip3902_priv, int init)
++{
++ unsigned long val;
++
++ /* Reset Rx hardware */
++ val = ip3902_read_reg(ndev, COMMAND_REG);
++ val &= ~COMMAND_RX_ENABLE;
++ val |= COMMAND_RX_RESET;
++ ip3902_write_reg(ndev, COMMAND_REG, val);
++
++ /* Set maximum frame size register */
++ ip3902_write_reg(ndev, MAXF_REG, ETH_RX_SKB_SIZE);
++
++ if (init) {
++ ip3902_priv->rx_next_allocate = 0;
++ ip3902_priv->rx_next_consume = 0;
++ ip3902_eth_rx_refill_descs(ndev, ip3902_priv);
++ }
++
++ /* Prepare skb's for Rx (any skb's already prepared will be reused)
++ * and configure Rx registers */
++ ip3902_write_reg(ndev, RX_DESC_REG, ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, rx_desc));
++ ip3902_write_reg(ndev, RX_STATUS_REG, ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, rx_status));
++ ip3902_write_reg(ndev, RX_DESC_NUMBER_REG, RX_RING_SIZE - 1);
++ ip3902_write_reg(ndev, RX_PRODUCE_INDEX_REG, ip3902_priv->rx_next_consume);
++}
++
++static inline void ip3902_start_tx(struct net_device *ndev)
++{
++ unsigned long val;
++
++ val = ip3902_read_reg(ndev, COMMAND_REG);
++ val |= COMMAND_TX_ENABLE;
++ ip3902_write_reg(ndev, COMMAND_REG, val);
++}
++
++static inline void ip3902_start_rx(struct net_device *ndev)
++{
++ unsigned long val;
++
++ /* First on high-level ... */
++ val = ip3902_read_reg(ndev, COMMAND_REG);
++ val |= (COMMAND_RX_ENABLE | COMMAND_ALLOW_SHORT);
++ ip3902_write_reg(ndev, COMMAND_REG, val);
++
++ /* ... and then on low-level (after high level is ready to receive) */
++ val = ip3902_read_reg(ndev, MAC1_REG);
++ val |= MAC1_RECEIVE_ENABLE; /* flow control frames won't be passed to driver */
++ ip3902_write_reg(ndev, MAC1_REG, val);
++}
++
++/* Interrupt handler body - split out to use both in interrupt handler
++ * and in net poll controller.
++ *
++ * Internal routine, called with lock held. */
++static void ip3902_do_handle_interrupt(struct net_device *ndev, struct ip3902_private *ip3902_priv, unsigned long status)
++{
++ ip3902_write_reg(ndev, INT_CLEAR_REG, status);
++ ip3902_write_reg(ndev, INT_CLEAR_REG, 0);
++
++ if (status & TX_UNDERRUN_INT) {
++ printk(KERN_ERR DRVNAME ": %s: fatal Tx underrun, resetting Tx\n", ndev->name);
++ ip3902_reset_tx(ndev, ip3902_priv, 0);
++ ip3902_start_tx(ndev);
++ }
++
++ if (status & RX_OVERRUN_INT) {
++ printk(KERN_ERR DRVNAME ": %s: fatal Rx overrun, resetting Rx\n", ndev->name);
++ ip3902_reset_rx(ndev, ip3902_priv, 0);
++ ip3902_start_rx(ndev);
++ } else if (status & RX_DONE_INT) {
++ /* Disable the Rx interrupt */
++ ip3902_write_reg(ndev, INT_ENABLE_REG, (RX_OVERRUN_INT | TX_UNDERRUN_INT));
++ netif_rx_schedule(ndev, &ip3902_priv->napi);
++ }
++}
++
++static irqreturn_t ip3902_interrupt(int irq, void *dev_instance)
++{
++ struct net_device *ndev = (struct net_device *) dev_instance;
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ unsigned long status;
++
++ status = ip3902_read_reg(ndev, INT_STATUS_REG) & (TX_DONE_INT | TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT);
++ do {
++ if (!status) {
++ return IRQ_NONE;
++ } else {
++ ip3902_do_handle_interrupt(ndev, ip3902_priv, status);
++ status = ip3902_read_reg(ndev, INT_STATUS_REG) & (TX_DONE_INT | TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT);
++ }
++ } while (status);
++
++ return IRQ_HANDLED;
++}
++
++#ifdef CONFIG_NET_POLL_CONTROLLER
++static void ip3902_net_poll(struct net_device *ndev)
++{
++ disable_irq_lockdep(ndev->irq);
++ ip3902_interrupt(ndev->irq, ndev);
++ enable_irq_lockdep(ndev->irq);
++}
++#endif
++
++static int eth_alloc_tx_desc_index(struct ip3902_private *ip3902_priv)
++{
++ int tx_desc_curr;
++
++ tx_desc_curr = ip3902_priv->tx_next_produce;
++ ip3902_priv->tx_next_produce = NEXT_TX(tx_desc_curr);
++
++ return tx_desc_curr;
++}
++
++static void eth_tx_fill_frag_descs(struct ip3902_private *ip3902_priv, struct sk_buff *skb)
++{
++ int frag = 0;
++ int tx_index;
++
++ do {
++ skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
++ unsigned long desc_address;
++ unsigned long desc_control;
++
++ tx_index = eth_alloc_tx_desc_index(ip3902_priv);
++
++ if (frag == (skb_shinfo(skb)->nr_frags - 1)) {
++ desc_control = (this_frag->size - 1) | TX_CONTROL_ALL_NOTLAST;
++ ip3902_priv->tx_skb[tx_index] = skb;
++ } else {
++ desc_control = (this_frag->size - 1) | TX_CONTROL_ALL_LAST;
++ ip3902_priv->tx_skb[tx_index] = NULL;
++ }
++
++ ip3902_priv->tx_first_desc[tx_index] = false;
++ desc_address = dma_map_page(NULL, this_frag->page,
++ this_frag->page_offset,
++ this_frag->size,
++ DMA_TO_DEVICE);
++ ip3902_write_tx_desc(ip3902_priv, tx_index, desc_address, desc_control);
++ } while (++frag < skb_shinfo(skb)->nr_frags);
++}
++
++static int ip3902_submit_skb_for_tx(struct net_device *ndev, struct sk_buff *skb)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ int nr_frags;
++ int free_desc;
++ int ret = 0;
++
++#ifdef CONFIG_INET_LRO
++ if (ip3902_priv->lro_count > LRO_THRESHOLD)
++ ip3902_priv->use_lro = true;
++
++ ip3902_priv->lro_count = 0;
++#endif
++
++ free_desc = ip3902_nr_free_descs(ip3902_priv->tx_next_produce, ip3902_priv->tx_next_deallocate, TX_RING_SIZE);
++ nr_frags = skb_shinfo(skb)->nr_frags;
++
++ if (free_desc <= nr_frags) {
++ ip3902_eth_free_completed_tx_descs(ndev, ip3902_priv);
++ free_desc = ip3902_nr_free_descs(ip3902_priv->tx_next_produce, ip3902_priv->tx_next_deallocate, TX_RING_SIZE);
++ }
++
++ if (free_desc > nr_frags) {
++ unsigned long desc_address;
++ unsigned long desc_control;
++ int tx_index;
++ int length;
++
++ tx_index = eth_alloc_tx_desc_index(ip3902_priv);
++
++ if (nr_frags) {
++ eth_tx_fill_frag_descs(ip3902_priv, skb);
++ length = skb_headlen(skb);
++ desc_control = (length - 1) | TX_CONTROL_ALL_NOTLAST;
++ ip3902_priv->tx_skb[tx_index] = NULL;
++ } else {
++ length = skb->len;
++ desc_control = (length - 1) | TX_CONTROL_ALL_LAST;
++ ip3902_priv->tx_skb[tx_index] = skb;
++ }
++
++ ip3902_priv->tx_first_desc[tx_index] = true;
++ desc_address = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
++
++ ip3902_write_tx_desc(ip3902_priv, tx_index, desc_address, desc_control);
++ ip3902_write_reg(ndev, TX_PRODUCE_INDEX_REG, ip3902_priv->tx_next_produce);
++ ip3902_eth_free_completed_tx_descs(ndev, ip3902_priv);
++ } else {
++ ret = -ENOMEM;
++ }
++
++ return ret;
++}
++
++static int ip3902_start_xmit(struct sk_buff *skb, struct net_device *ndev)
++{
++ int ret;
++
++ BUG_ON(netif_queue_stopped(ndev));
++ BUG_ON(skb == NULL);
++ ret = ip3902_submit_skb_for_tx(ndev, skb);
++
++ if (ret) {
++ printk(KERN_ERR "%s: transmit with queue full\n", ndev->name);
++ netif_stop_queue(ndev);
++ } else {
++ ndev->stats.tx_bytes += skb->len;
++ ndev->stats.tx_packets++;
++ ndev->trans_start = jiffies;
++ }
++
++ return ret; /* success */
++}
++
++static void ip3902_do_set_rx_filter(struct net_device *ndev, struct ip3902_private *ip3902_priv)
++{
++ unsigned long creg, freg;
++
++ creg = ip3902_read_reg(ndev, COMMAND_REG);
++ if (ndev->flags & IFF_PROMISC) {
++ /* If interface is in promiscuous mode, just disable filter */
++ ip3902_write_reg(ndev, COMMAND_REG, creg | COMMAND_PROMISC);
++ return;
++ }
++ /* Enable filter */
++ ip3902_write_reg(ndev, COMMAND_REG, creg & ~COMMAND_PROMISC);
++
++ /* Frames for self address and broadcast frames are always accepted */
++ freg = FILTER_ACCEPT_SELF | FILTER_ACCEPT_BCAST_ANY;
++
++ if (ndev->flags & IFF_ALLMULTI) {
++ /* Accept all multicast frames */
++ freg |= FILTER_ACCEPT_MCAST_ANY;
++ } else if (ndev->mc_count > 0) {
++ /* Accept some multicast frames */
++ u64 hash_mask = 0;
++ struct dev_mc_list *mc;
++
++ freg |= FILTER_ACCEPT_MCAST_HASH;
++ for (mc = ndev->mc_list; mc; mc = mc->next) {
++ int b = (ether_crc(ETH_ALEN, mc->dmi_addr) >> 23) & 0x3f;
++ hash_mask |= (1 << b);
++ }
++ ip3902_write_reg(ndev, HASH_FILTER_L_REG, hash_mask & 0xffffffff);
++ ip3902_write_reg(ndev, HASH_FILTER_H_REG, hash_mask >> 32);
++ }
++
++ ip3902_write_reg(ndev, FILTER_CTRL_REG, freg);
++}
++
++static void ip3902_set_rx_filter(struct net_device *ndev)
++{
++ struct ip3902_private *ip3902_priv = (struct ip3902_private *)netdev_priv(ndev);
++
++ ip3902_do_set_rx_filter(ndev, ip3902_priv);
++}
++
++static void set_duplex_mode(struct net_device *ndev, int duplex)
++{
++ unsigned long val;
++
++ if (duplex) {
++ /* Full Duplex mode */
++
++ val = ip3902_read_reg(ndev, MAC2_REG);
++ val |= MAC2_FULL_DUPLEX;
++ ip3902_write_reg(ndev, MAC2_REG, val);
++
++ ip3902_write_reg(ndev, IPGT_REG, IPGT_FD_VALUE);
++
++ val = ip3902_read_reg(ndev, COMMAND_REG);
++ val |= COMMAND_FULL_DUPLEX;
++ ip3902_write_reg(ndev, COMMAND_REG, val);
++ } else {
++ /* Half Duplex mode */
++
++ val = ip3902_read_reg(ndev, MAC2_REG);
++ val &= ~MAC2_FULL_DUPLEX;
++ ip3902_write_reg(ndev, MAC2_REG, val);
++
++ ip3902_write_reg(ndev, IPGT_REG, IPGT_HD_VALUE);
++
++ val = ip3902_read_reg(ndev, COMMAND_REG);
++ val &= ~COMMAND_FULL_DUPLEX;
++ ip3902_write_reg(ndev, COMMAND_REG, val);
++ }
++}
++
++static int ip3902_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ unsigned int duplex_changed;
++ unsigned long flags;
++ int rc;
++
++ if (!netif_running(ndev))
++ return -EINVAL;
++
++ spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
++ rc = generic_mii_ioctl(&ip3902_priv->mii, if_mii(req), cmd, &duplex_changed);
++ spin_unlock_irqrestore(&ip3902_priv->mii_lock, flags);
++ if (duplex_changed)
++ set_duplex_mode(ndev, ip3902_priv->mii.full_duplex);
++
++ return rc;
++}
++
++/* ethtool ops */
++
++static void ip3902_get_drvinfo(struct net_device *ndev,
++ struct ethtool_drvinfo *info)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++
++ strcpy(info->driver, DRVNAME);
++ strcpy(info->version, DRVVERSION);
++ strcpy(info->bus_info, ip3902_priv->ndev->name);
++}
++
++static int ip3902_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ unsigned long flags;
++
++ spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
++ mii_ethtool_gset(&ip3902_priv->mii, cmd);
++ spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
++
++ return 0;
++}
++
++static int ip3902_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ unsigned long flags;
++ int rc;
++
++ spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
++ rc = mii_ethtool_sset(&ip3902_priv->mii, cmd);
++ spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
++
++ return rc;
++}
++
++static int ip3902_nway_reset(struct net_device *ndev)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ return mii_nway_restart(&ip3902_priv->mii);
++}
++
++static u32 ip3902_get_link(struct net_device *ndev)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ return mii_link_ok(&ip3902_priv->mii);
++}
++
++static const struct ethtool_ops ip3902_ethtool_ops = {
++ .get_drvinfo = ip3902_get_drvinfo,
++ .get_settings = ip3902_get_settings,
++ .set_settings = ip3902_set_settings,
++ .nway_reset = ip3902_nway_reset,
++ .get_link = ip3902_get_link,
++ .get_sg = ethtool_op_get_sg,
++ .set_sg = ethtool_op_set_sg,
++};
++
++/* setup code */
++
++static void ip3902_eth_update_mac_address(struct net_device *ndev)
++{
++ ip3902_write_reg(ndev, SA0_REG, (ndev->dev_addr[5] << 8) | ndev->dev_addr[4]);
++ ip3902_write_reg(ndev, SA1_REG, (ndev->dev_addr[3] << 8) | ndev->dev_addr[2]);
++ ip3902_write_reg(ndev, SA2_REG, (ndev->dev_addr[1] << 8) | ndev->dev_addr[0]);
++}
++
++static int ip3902_eth_set_mac_address(struct net_device *ndev, void *addr)
++{
++ int i;
++
++ for (i = 0; i < 6; i++)
++ /* +2 is for the offset of the HW addr type */
++ ndev->dev_addr[i] = ((unsigned char *)addr)[i + 2];
++
++ ip3902_eth_update_mac_address(ndev);
++ return 0;
++}
++
++static void ip3902_hw_deinit(struct net_device *ndev)
++{
++ unsigned long val;
++
++ /* Stop Rx and Tx hardware and disable interrupts */
++ val = ip3902_read_reg(ndev, COMMAND_REG);
++ val &= ~(COMMAND_TX_ENABLE | COMMAND_RX_ENABLE);
++ ip3902_write_reg(ndev, COMMAND_REG, val);
++ ip3902_write_reg(ndev, INT_ENABLE_REG, 0);
++
++ /* Put low-level hardware into reset, and high-level into poweroff */
++ ip3902_write_reg(ndev, MAC1_REG, MAC1_SOFT_RESET);
++ ip3902_write_reg(ndev, POWERDOWN_REG, POWERDOWN_VALUE);
++}
++
++static int ethernet_phy_get(struct net_device *ndev)
++{
++ int addr;
++
++ for (addr = 1; addr < 32; addr++) {
++ int stat;
++ stat = ip3902_mdio_read(ndev, addr, MII_BMSR);
++ if ((stat != 0) && (stat != 0xffff))
++ return addr;
++ }
++ printk(KERN_ERR DRVNAME ": could not locate PHY\n");
++ return -EIO;
++}
++
++static int ip3902_hw_init(struct net_device *ndev, struct ip3902_private *ip3902_priv)
++{
++ int ret = 0;
++
++ /* Poweron hardware */
++ ip3902_write_reg(ndev, POWERDOWN_REG, 0);
++
++ /* Move low level out of reset (also initialize the registers)*/
++ ip3902_write_reg(ndev, MAC1_REG, 0);
++ ip3902_write_reg(ndev, MAC2_REG, INITIAL_MAC2);
++
++ ip3902_priv->mii.phy_id = ethernet_phy_get(ndev);
++
++ if (ip3902_priv->mii.phy_id < 0) {
++ ret = ip3902_priv->mii.phy_id;
++ } else {
++ ip3902_eth_update_mac_address(ndev);
++
++ /* "Initialize" command register (before resets - those routines
++ * use read-modify-write operations on that register */
++ ip3902_write_reg(ndev, COMMAND_REG, COMMAND_ALLOW_SHORT);
++
++ /* Reset and configure Rx and Tx */
++ ip3902_reset_tx(ndev, ip3902_priv, 1);
++ ip3902_reset_rx(ndev, ip3902_priv, 1);
++
++ /* Initialize Rx filtering */
++ ip3902_do_set_rx_filter(ndev, ip3902_priv);
++
++ /* Clear all interrupts, and enable interesting interrupts */
++ ip3902_write_reg(ndev, INT_CLEAR_REG, 0xffffffff);
++ ip3902_write_reg(ndev, INT_CLEAR_REG, 0);
++ ip3902_write_reg(ndev, INT_ENABLE_REG, (TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT));
++
++ /* Start Tx and Rx hardware */
++ ip3902_start_tx(ndev);
++ ip3902_start_rx(ndev);
++ }
++ return 0;
++}
++
++static int ip3902_open(struct net_device *ndev)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++ int ret;
++
++ dev_dbg(&ip3902_priv->ndev->dev, "%s: open\n", ndev->name);
++
++ ret = request_irq(ndev->irq, ip3902_interrupt, 0, ndev->name, ndev);
++ if (ret)
++ return ret;
++
++ ret = ip3902_hw_init(ndev, ip3902_priv);
++
++ if (ret)
++ return ret;
++
++ mii_check_media(&ip3902_priv->mii, netif_msg_link(ip3902_priv), 1);
++ set_duplex_mode(ndev, ip3902_priv->mii.full_duplex);
++
++#ifdef CONFIG_INET_LRO
++ init_timer(&ip3902_priv->lro_timer);
++ ip3902_priv->lro_timer.data = (unsigned long) ip3902_priv;
++ ip3902_priv->lro_timer.function = ip3902_lro_timeout;
++#endif
++
++ netif_start_queue(ndev);
++ napi_enable(&ip3902_priv->napi);
++
++ ip3902_priv->running = 1;
++
++ return 0;
++}
++
++static int ip3902_close(struct net_device *ndev)
++{
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++
++ dev_dbg(&ip3902_priv->ndev->dev, "%s: close\n", ndev->name);
++
++ ip3902_priv->running = 0;
++ wmb();
++
++#ifdef CONFIG_INET_LRO
++ del_timer(&ip3902_priv->lro_timer);
++#endif
++
++ napi_disable(&ip3902_priv->napi);
++
++ ip3902_hw_deinit(ndev);
++
++ netif_stop_queue(ndev);
++
++ ip3902_eth_free_all_tx_descs(ndev, ip3902_priv);
++
++ free_irq(ndev->irq, ndev);
++ return 0;
++}
++
++static int parse_mac_address(struct net_device *ndev)
++{
++ int n = sscanf(mac_address, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
++ &ndev->dev_addr[0], &ndev->dev_addr[1],
++ &ndev->dev_addr[2], &ndev->dev_addr[3],
++ &ndev->dev_addr[4], &ndev->dev_addr[5]);
++
++ if (n == 6)
++ return 0;
++
++ printk(KERN_WARNING DRVNAME": failed to parse mac address string \"%s\"\n", mac_address);
++ return -EINVAL;
++}
++
++static void ip3902_hw_shutdown(struct net_device *ndev, struct ip3902_private *ip3902_priv)
++{
++ dma_free_coherent(NULL, sizeof(*(ip3902_priv->ds)), ip3902_priv->ds, ip3902_priv->ds_dma);
++}
++
++static int ip3902_hw_startup(struct net_device *ndev, struct ip3902_private *ip3902_priv)
++{
++ ip3902_priv->ds = dma_alloc_coherent(NULL, sizeof(*(ip3902_priv->ds)), &ip3902_priv->ds_dma, GFP_KERNEL);
++ if (!ip3902_priv->ds) {
++ printk(KERN_ERR DRVNAME ": can't allocate DMA structure\n");
++ ip3902_hw_shutdown(ndev, ip3902_priv);
++ return -ENOMEM;
++ }
++
++ /* Poweron hardware */
++ ip3902_write_reg(ndev, POWERDOWN_REG, 0);
++
++ /* set mii clock */
++ ip3902_write_reg(ndev, MCFG_REG, 0x1c);
++
++ /* Move low level out of reset (also initialize the registers)*/
++ ip3902_write_reg(ndev, MAC1_REG, 0);
++ ip3902_write_reg(ndev, MAC2_REG, INITIAL_MAC2);
++
++ if (!mac_address || parse_mac_address(ndev) < 0) {
++ unsigned long val;
++
++ val = ip3902_read_reg(ndev, SA0_REG);
++ ndev->dev_addr[5] = (val >> 8) & 255;
++ ndev->dev_addr[4] = val & 255;
++ val = ip3902_read_reg(ndev, SA1_REG);
++ ndev->dev_addr[3] = (val >> 8) & 255;
++ ndev->dev_addr[2] = val & 255;
++ val = ip3902_read_reg(ndev, SA2_REG);
++ ndev->dev_addr[1] = (val >> 8) & 255;
++ ndev->dev_addr[0] = val & 255;
++ }
++
++ /* Put low-level hardware into reset, and high-level into poweroff */
++ ip3902_write_reg(ndev, MAC1_REG, MAC1_SOFT_RESET);
++ ip3902_write_reg(ndev, POWERDOWN_REG, POWERDOWN_VALUE);
++
++ return 0;
++}
++
++static int ip3902_init_dev(struct net_device *ndev, struct ip3902_private *ip3902_priv)
++{
++ int ret;
++
++ ret = ip3902_hw_startup(ndev, ip3902_priv);
++
++ if (!ret) {
++ ndev->hard_start_xmit = ip3902_start_xmit;
++ ndev->set_mac_address = ip3902_eth_set_mac_address;
++ ndev->set_multicast_list = ip3902_set_rx_filter;
++ ndev->open = ip3902_open;
++ ndev->stop = ip3902_close;
++ ndev->do_ioctl = ip3902_ioctl;
++ ndev->features = 0;
++#ifdef CONFIG_NET_POLL_CONTROLLER
++ ndev->poll_controller = ip3902_net_poll;
++#endif
++ SET_ETHTOOL_OPS(ndev, &ip3902_ethtool_ops);
++
++ ip3902_priv->msg_enable = NETIF_MSG_LINK;
++ ip3902_priv->mii.phy_id_mask = 0x1f;
++ ip3902_priv->mii.reg_num_mask = 0x1f;
++ ip3902_priv->mii.mdio_read = ip3902_mdio_read;
++ ip3902_priv->mii.mdio_write = ip3902_mdio_write;
++ ip3902_priv->mii.dev = ndev;
++
++ spin_lock_init(&ip3902_priv->lock);
++
++ ret = register_netdev(ndev);
++
++ if (ret)
++ ip3902_hw_shutdown(ndev, ip3902_priv);
++ }
++
++ return ret;
++}
++
++#ifdef CONFIG_INET_LRO
++static int ip3902_get_skb_hdr(struct sk_buff *skb, void **iphdr, void **tcph, u64 *hdr_flags, void *priv)
++{
++ unsigned int ip_len;
++ struct iphdr *iph;
++
++ /* non tcp packet */
++ skb_reset_network_header(skb);
++ iph = ip_hdr(skb);
++ if (iph->protocol != IPPROTO_TCP)
++ return -1;
++
++ ip_len = ip_hdrlen(skb);
++ skb_set_transport_header(skb, ip_len);
++ *tcph = tcp_hdr(skb);
++
++ /* check if ip header and tcp header are complete */
++ if (iph->tot_len < ip_len + tcp_hdrlen(skb))
++ return -1;
++
++ *hdr_flags = LRO_IPV4 | LRO_TCP;
++ *iphdr = iph;
++
++ return 0;
++}
++#endif
++
++static int ip3902_remove(struct platform_device *pdev)
++{
++ struct net_device *ndev = platform_get_drvdata(pdev);
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++
++ platform_set_drvdata(pdev, NULL);
++
++ unregister_netdev(ndev);
++
++ iounmap(ip3902_priv->mem);
++ release_resource(ip3902_priv->bus);
++ kfree(ip3902_priv->bus);
++
++ free_netdev(ndev);
++
++ return 0;
++}
++
++/* ip3902_probe
++ *
++ * This is the entry point when the platform device system uses to
++ * notify us of a new device to attach to. Allocate memory, find
++ * the resources and information passed, and map the necessary registers.
++*/
++
++static int ip3902_probe(struct platform_device *pdev)
++{
++ struct net_device *ndev;
++ struct ip3902_private *ip3902_priv;
++ struct resource *res;
++ size_t size;
++ int ret;
++
++ ndev = alloc_etherdev(sizeof(struct ip3902_private));
++
++ if (ndev == NULL)
++ return -ENOMEM;
++
++ ip3902_priv = netdev_priv(ndev);
++
++ memset(ip3902_priv, 0, sizeof(struct ip3902_private));
++
++ spin_lock_init(&ip3902_priv->mii_lock);
++
++ ip3902_priv->ndev = ndev;
++ ip3902_priv->pdev = pdev;
++
++ netif_napi_add(ndev, &ip3902_priv->napi, ip3902_poll, IP3902_NAPI_WEIGHT);
++
++#ifdef CONFIG_INET_LRO
++ ip3902_priv->use_lro = false;
++ ip3902_priv->lro_count = 0;
++ ip3902_priv->lro_mgr.max_aggr = IP3902_NAPI_WEIGHT;
++ ip3902_priv->lro_mgr.max_desc = MAX_LRO_DESCRIPTORS;
++ ip3902_priv->lro_mgr.lro_arr = ip3902_priv->lro_desc;
++ ip3902_priv->lro_mgr.get_skb_header = ip3902_get_skb_hdr;
++ ip3902_priv->lro_mgr.features = LRO_F_NAPI;
++ ip3902_priv->lro_mgr.dev = ndev;
++ ip3902_priv->lro_mgr.ip_summed = 0;
++ ip3902_priv->lro_mgr.ip_summed_aggr = 0;
++#endif
++
++ platform_set_drvdata(pdev, ndev);
++
++ /* find the platform resources */
++ ndev->irq = platform_get_irq(pdev, 0);
++ if (ndev->irq < 0) {
++ dev_err(&pdev->dev, "no IRQ specified\n");
++ ret = -ENXIO;
++ goto exit_mem;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (res == NULL) {
++ dev_err(&pdev->dev, "no MEM specified\n");
++ ret = -ENXIO;
++ goto exit_mem;
++ }
++ size = (res->end - res->start) + 1;
++
++ ip3902_priv->bus = request_mem_region(res->start & 0x1fffffff, size, pdev->name);
++ if (ip3902_priv->bus == NULL) {
++ dev_err(&pdev->dev, "cannot reserve registers\n");
++ ret = -ENXIO;
++ goto exit_mem;
++ }
++
++ ip3902_priv->mem = ioremap(res->start & 0x1fffffff, size);
++ ndev->base_addr = (unsigned long)ip3902_priv->mem;
++
++ if (ip3902_priv->mem == NULL) {
++ dev_err(&pdev->dev, "Cannot ioremap area (%08llx,%08llx)\n",
++ (unsigned long long)res->start,
++ (unsigned long long)res->end);
++
++ ret = -ENXIO;
++ goto exit_req;
++ }
++
++ SET_NETDEV_DEV(ndev, &pdev->dev);
++
++ /* got resources, now initialise and register device */
++ ret = ip3902_init_dev(ndev, ip3902_priv);
++ if (!ret) {
++ printk(KERN_INFO "NXP ip3902 10/100 Ethernet platform driver irq %d base %08lx\n", ndev->irq, ndev->base_addr);
++ return 0;
++ }
++
++exit_req:
++ release_resource(ip3902_priv->bus);
++ kfree(ip3902_priv->bus);
++
++exit_mem:
++ free_netdev(ndev);
++
++ return ret;
++}
++
++/* suspend and resume */
++
++#ifdef CONFIG_PM
++static int ip3902_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct net_device *ndev = platform_get_drvdata(pdev);
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++
++ ip3902_priv->resume_open = ip3902_priv->running;
++
++ netif_device_detach(ndev);
++ ip3902_close(ndev);
++
++ return 0;
++}
++
++static int ip3902_resume(struct platform_device *pdev)
++{
++ struct net_device *ndev = platform_get_drvdata(pdev);
++ struct ip3902_private *ip3902_priv = netdev_priv(ndev);
++
++ netif_device_attach(ndev);
++
++ if (ip3902_priv->resume_open)
++ ip3902_open(ndev);
++
++ return 0;
++}
++
++#else
++ #define ip3902_suspend NULL
++ #define ip3902_resume NULL
++#endif
++
++static struct platform_driver ip3902drv = {
++ .driver = {
++ .name = "ip3902-eth",
++ .owner = THIS_MODULE,
++ },
++ .probe = ip3902_probe,
++ .remove = ip3902_remove,
++ .suspend = ip3902_suspend,
++ .resume = ip3902_resume,
++};
++
++static int __init ip3902drv_init(void)
++{
++ return platform_driver_register(&ip3902drv);
++}
++
++static void __exit ip3902drv_exit(void)
++{
++ platform_driver_unregister(&ip3902drv);
++}
++
++module_init(ip3902drv_init);
++module_exit(ip3902drv_exit);
++
++MODULE_DESCRIPTION("NXP IP3902 10/100 Ethernet platform driver");
++MODULE_AUTHOR("Chris Steel, <chris.steel@nxp.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
+index 9fe8cb7..d140375 100644
+--- a/drivers/net/Kconfig
++++ b/drivers/net/Kconfig
+@@ -1882,6 +1882,16 @@ config ATL2
+ To compile this driver as a module, choose M here. The module
+ will be called atl2.
+
++config IP3902
++ tristate "NXP IP3902 ethernet hardware support"
++ depends on SOC_PNX8335 && NET_ETHERNET
++ select MII
++ select CRC32
++ help
++ This is a driver for NXP IP3902 ethernet hardware found
++ in PNX8335 and probably other SOCs.
++
++
+ source "drivers/net/fs_enet/Kconfig"
+
+ endif # NET_ETHERNET
+diff -urN --exclude=.svn linux-2.6.26-rc4.orig/drivers/net/Makefile linux-2.6.26-rc4/drivers/net/Makefile
+--- linux-2.6.26-rc4.orig/drivers/net/Makefile 2008-06-03
+10:56:55.000000000 +0100
++++ linux-2.6.26-rc4/drivers/net/Makefile 2008-06-03 17:17:11.000000000 +0100
+@@ -122,6 +122,7 @@
+ obj-$(CONFIG_FORCEDETH) += forcedeth.o
+ obj-$(CONFIG_NE_H8300) += ne-h8300.o
+ obj-$(CONFIG_AX88796) += ax88796.o
++obj-$(CONFIG_IP3902) += ip3902.o
+
+ obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
+ obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
+