diff options
Diffstat (limited to 'packages/linux/linux-2.6.28/stb225/ip3902.patch')
-rw-r--r-- | packages/linux/linux-2.6.28/stb225/ip3902.patch | 1549 |
1 files changed, 1549 insertions, 0 deletions
diff --git a/packages/linux/linux-2.6.28/stb225/ip3902.patch b/packages/linux/linux-2.6.28/stb225/ip3902.patch new file mode 100644 index 0000000000..3f911b661d --- /dev/null +++ b/packages/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 + |