diff options
Diffstat (limited to 'drivers/net/mv643xx_eth.c')
-rw-r--r-- | drivers/net/mv643xx_eth.c | 1630 |
1 files changed, 908 insertions, 722 deletions
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 8a97a00..a9c8c08 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -38,6 +38,7 @@ #include <linux/init.h> #include <linux/dma-mapping.h> #include <linux/in.h> +#include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/etherdevice.h> @@ -48,30 +49,28 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/workqueue.h> -#include <linux/mii.h> +#include <linux/phy.h> #include <linux/mv643xx_eth.h> #include <asm/io.h> #include <asm/types.h> #include <asm/system.h> static char mv643xx_eth_driver_name[] = "mv643xx_eth"; -static char mv643xx_eth_driver_version[] = "1.1"; +static char mv643xx_eth_driver_version[] = "1.4"; -#define MV643XX_ETH_CHECKSUM_OFFLOAD_TX -#define MV643XX_ETH_NAPI -#define MV643XX_ETH_TX_FAST_REFILL - -#ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX -#define MAX_DESCS_PER_SKB (MAX_SKB_FRAGS + 1) -#else -#define MAX_DESCS_PER_SKB 1 -#endif /* * Registers shared between all ports. */ #define PHY_ADDR 0x0000 #define SMI_REG 0x0004 +#define SMI_BUSY 0x10000000 +#define SMI_READ_VALID 0x08000000 +#define SMI_OPCODE_READ 0x04000000 +#define SMI_OPCODE_WRITE 0x00000000 +#define ERR_INT_CAUSE 0x0080 +#define ERR_INT_SMI_DONE 0x00000010 +#define ERR_INT_MASK 0x0084 #define WINDOW_BASE(w) (0x0200 + ((w) << 3)) #define WINDOW_SIZE(w) (0x0204 + ((w) << 3)) #define WINDOW_REMAP_HIGH(w) (0x0280 + ((w) << 2)) @@ -90,6 +89,14 @@ static char mv643xx_eth_driver_version[] = "1.1"; #define PORT_SERIAL_CONTROL(p) (0x043c + ((p) << 10)) #define PORT_STATUS(p) (0x0444 + ((p) << 10)) #define TX_FIFO_EMPTY 0x00000400 +#define TX_IN_PROGRESS 0x00000080 +#define PORT_SPEED_MASK 0x00000030 +#define PORT_SPEED_1000 0x00000010 +#define PORT_SPEED_100 0x00000020 +#define PORT_SPEED_10 0x00000000 +#define FLOW_CONTROL_ENABLED 0x00000008 +#define FULL_DUPLEX 0x00000004 +#define LINK_UP 0x00000002 #define TXQ_COMMAND(p) (0x0448 + ((p) << 10)) #define TXQ_FIX_PRIO_CONF(p) (0x044c + ((p) << 10)) #define TX_BW_RATE(p) (0x0450 + ((p) << 10)) @@ -97,14 +104,11 @@ static char mv643xx_eth_driver_version[] = "1.1"; #define TX_BW_BURST(p) (0x045c + ((p) << 10)) #define INT_CAUSE(p) (0x0460 + ((p) << 10)) #define INT_TX_END 0x07f80000 -#define INT_RX 0x0007fbfc +#define INT_RX 0x000003fc #define INT_EXT 0x00000002 #define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10)) -#define INT_EXT_LINK 0x00100000 -#define INT_EXT_PHY 0x00010000 -#define INT_EXT_TX_ERROR_0 0x00000100 -#define INT_EXT_TX_0 0x00000001 -#define INT_EXT_TX 0x0000ffff +#define INT_EXT_LINK_PHY 0x00110000 +#define INT_EXT_TX 0x000000ff #define INT_MASK(p) (0x0468 + ((p) << 10)) #define INT_MASK_EXT(p) (0x046c + ((p) << 10)) #define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10)) @@ -127,21 +131,21 @@ static char mv643xx_eth_driver_version[] = "1.1"; /* * SDMA configuration register. */ -#define RX_BURST_SIZE_4_64BIT (2 << 1) +#define RX_BURST_SIZE_16_64BIT (4 << 1) #define BLM_RX_NO_SWAP (1 << 4) #define BLM_TX_NO_SWAP (1 << 5) -#define TX_BURST_SIZE_4_64BIT (2 << 22) +#define TX_BURST_SIZE_16_64BIT (4 << 22) #if defined(__BIG_ENDIAN) #define PORT_SDMA_CONFIG_DEFAULT_VALUE \ - RX_BURST_SIZE_4_64BIT | \ - TX_BURST_SIZE_4_64BIT + RX_BURST_SIZE_16_64BIT | \ + TX_BURST_SIZE_16_64BIT #elif defined(__LITTLE_ENDIAN) #define PORT_SDMA_CONFIG_DEFAULT_VALUE \ - RX_BURST_SIZE_4_64BIT | \ + RX_BURST_SIZE_16_64BIT | \ BLM_RX_NO_SWAP | \ BLM_TX_NO_SWAP | \ - TX_BURST_SIZE_4_64BIT + TX_BURST_SIZE_16_64BIT #else #error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined #endif @@ -153,9 +157,7 @@ static char mv643xx_eth_driver_version[] = "1.1"; #define SET_MII_SPEED_TO_100 (1 << 24) #define SET_GMII_SPEED_TO_1000 (1 << 23) #define SET_FULL_DUPLEX_MODE (1 << 21) -#define MAX_RX_PACKET_1522BYTE (1 << 17) #define MAX_RX_PACKET_9700BYTE (5 << 17) -#define MAX_RX_PACKET_MASK (7 << 17) #define DISABLE_AUTO_NEG_SPEED_GMII (1 << 13) #define DO_NOT_FORCE_LINK_FAIL (1 << 10) #define SERIAL_PORT_CONTROL_RESERVED (1 << 9) @@ -164,8 +166,8 @@ static char mv643xx_eth_driver_version[] = "1.1"; #define FORCE_LINK_PASS (1 << 1) #define SERIAL_PORT_ENABLE (1 << 0) -#define DEFAULT_RX_QUEUE_SIZE 400 -#define DEFAULT_TX_QUEUE_SIZE 800 +#define DEFAULT_RX_QUEUE_SIZE 128 +#define DEFAULT_TX_QUEUE_SIZE 256 /* @@ -228,6 +230,8 @@ struct tx_desc { #define GEN_IP_V4_CHECKSUM 0x00040000 #define GEN_TCP_UDP_CHECKSUM 0x00020000 #define UDP_FRAME 0x00010000 +#define MAC_HDR_EXTRA_4_BYTES 0x00008000 +#define MAC_HDR_EXTRA_8_BYTES 0x00000200 #define TX_IHL_SHIFT 11 @@ -240,9 +244,23 @@ struct mv643xx_eth_shared_private { void __iomem *base; /* - * Protects access to SMI_REG, which is shared between ports. + * Points at the right SMI instance to use. + */ + struct mv643xx_eth_shared_private *smi; + + /* + * Provides access to local SMI interface. */ - spinlock_t phy_lock; + struct mii_bus *smi_bus; + + /* + * If we have access to the error interrupt pin (which is + * somewhat misnamed as it not only reflects internal errors + * but also reflects SMI completion), use that to wait for + * SMI access completion instead of polling the SMI busy bit. + */ + int err_interrupt; + wait_queue_head_t smi_busy_wait; /* * Per-port MBUS window access register value. @@ -254,9 +272,13 @@ struct mv643xx_eth_shared_private { */ unsigned int t_clk; int extended_rx_coal_limit; - int tx_bw_control_moved; + int tx_bw_control; }; +#define TX_BW_CONTROL_ABSENT 0 +#define TX_BW_CONTROL_OLD_LAYOUT 1 +#define TX_BW_CONTROL_NEW_LAYOUT 2 + /* per-port *****************************************************************/ struct mib_counters { @@ -305,8 +327,6 @@ struct rx_queue { dma_addr_t rx_desc_dma; int rx_desc_area_size; struct sk_buff **rx_skb; - - struct timer_list rx_oom; }; struct tx_queue { @@ -321,7 +341,12 @@ struct tx_queue { struct tx_desc *tx_desc_area; dma_addr_t tx_desc_dma; int tx_desc_area_size; - struct sk_buff **tx_skb; + + struct sk_buff_head tx_skb; + + unsigned long tx_packets; + unsigned long tx_bytes; + unsigned long tx_dropped; }; struct mv643xx_eth_private { @@ -330,14 +355,24 @@ struct mv643xx_eth_private { struct net_device *dev; - struct mv643xx_eth_shared_private *shared_smi; - int phy_addr; - - spinlock_t lock; + struct phy_device *phy; + struct timer_list mib_counters_timer; + spinlock_t mib_counters_lock; struct mib_counters mib_counters; + struct work_struct tx_timeout_task; - struct mii_if_info mii; + + struct napi_struct napi; + u8 work_link; + u8 work_tx; + u8 work_tx_end; + u8 work_rx; + u8 work_rx_refill; + u8 work_rx_oom; + + int skb_size; + struct sk_buff_head rx_recycle; /* * RX state. @@ -345,9 +380,8 @@ struct mv643xx_eth_private { int default_rx_ring_size; unsigned long rx_desc_sram_addr; int rx_desc_sram_size; - u8 rxq_mask; - int rxq_primary; - struct napi_struct napi; + int rxq_count; + struct timer_list rx_oom; struct rx_queue rxq[8]; /* @@ -356,12 +390,8 @@ struct mv643xx_eth_private { int default_tx_ring_size; unsigned long tx_desc_sram_addr; int tx_desc_sram_size; - u8 txq_mask; - int txq_primary; + int txq_count; struct tx_queue txq[8]; -#ifdef MV643XX_ETH_TX_FAST_REFILL - int tx_clean_threshold; -#endif }; @@ -404,6 +434,17 @@ static void rxq_disable(struct rx_queue *rxq) udelay(10); } +static void txq_reset_hw_ptr(struct tx_queue *txq) +{ + struct mv643xx_eth_private *mp = txq_to_mp(txq); + int off = TXQ_CURRENT_DESC_PTR(mp->port_num, txq->index); + u32 addr; + + addr = (u32)txq->tx_desc_dma; + addr += txq->tx_curr_desc * sizeof(struct tx_desc); + wrl(mp, off, addr); +} + static void txq_enable(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); @@ -420,88 +461,21 @@ static void txq_disable(struct tx_queue *txq) udelay(10); } -static void __txq_maybe_wake(struct tx_queue *txq) +static void txq_maybe_wake(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); + struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index); - /* - * netif_{stop,wake}_queue() flow control only applies to - * the primary queue. - */ - BUG_ON(txq->index != mp->txq_primary); - - if (txq->tx_ring_size - txq->tx_desc_count >= MAX_DESCS_PER_SKB) - netif_wake_queue(mp->dev); -} - - -/* rx ***********************************************************************/ -static void txq_reclaim(struct tx_queue *txq, int force); - -static void rxq_refill(struct rx_queue *rxq) -{ - struct mv643xx_eth_private *mp = rxq_to_mp(rxq); - unsigned long flags; - - spin_lock_irqsave(&mp->lock, flags); - - while (rxq->rx_desc_count < rxq->rx_ring_size) { - int skb_size; - struct sk_buff *skb; - int unaligned; - int rx; - - /* - * Reserve 2+14 bytes for an ethernet header (the - * hardware automatically prepends 2 bytes of dummy - * data to each received packet), 4 bytes for a VLAN - * header, and 4 bytes for the trailing FCS -- 24 - * bytes total. - */ - skb_size = mp->dev->mtu + 24; - - skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1); - if (skb == NULL) - break; - - unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1); - if (unaligned) - skb_reserve(skb, dma_get_cache_alignment() - unaligned); - - rxq->rx_desc_count++; - rx = rxq->rx_used_desc; - rxq->rx_used_desc = (rx + 1) % rxq->rx_ring_size; - - rxq->rx_desc_area[rx].buf_ptr = dma_map_single(NULL, skb->data, - skb_size, DMA_FROM_DEVICE); - rxq->rx_desc_area[rx].buf_size = skb_size; - rxq->rx_skb[rx] = skb; - wmb(); - rxq->rx_desc_area[rx].cmd_sts = BUFFER_OWNED_BY_DMA | - RX_ENABLE_INTERRUPT; - wmb(); - - /* - * The hardware automatically prepends 2 bytes of - * dummy data to each received packet, so that the - * IP header ends up 16-byte aligned. - */ - skb_reserve(skb, 2); - } - - if (rxq->rx_desc_count != rxq->rx_ring_size) { - rxq->rx_oom.expires = jiffies + (HZ / 10); - add_timer(&rxq->rx_oom); + if (netif_tx_queue_stopped(nq)) { + __netif_tx_lock(nq, smp_processor_id()); + if (txq->tx_ring_size - txq->tx_desc_count >= MAX_SKB_FRAGS + 1) + netif_tx_wake_queue(nq); + __netif_tx_unlock(nq); } - - spin_unlock_irqrestore(&mp->lock, flags); } -static inline void rxq_refill_timer_wrapper(unsigned long data) -{ - rxq_refill((struct rx_queue *)data); -} +/* rx napi ******************************************************************/ static int rxq_process(struct rx_queue *rxq, int budget) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); @@ -509,35 +483,35 @@ static int rxq_process(struct rx_queue *rxq, int budget) int rx; rx = 0; - while (rx < budget) { + while (rx < budget && rxq->rx_desc_count) { struct rx_desc *rx_desc; unsigned int cmd_sts; struct sk_buff *skb; - unsigned long flags; - - spin_lock_irqsave(&mp->lock, flags); + u16 byte_cnt; rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc]; cmd_sts = rx_desc->cmd_sts; - if (cmd_sts & BUFFER_OWNED_BY_DMA) { - spin_unlock_irqrestore(&mp->lock, flags); + if (cmd_sts & BUFFER_OWNED_BY_DMA) break; - } rmb(); skb = rxq->rx_skb[rxq->rx_curr_desc]; rxq->rx_skb[rxq->rx_curr_desc] = NULL; - rxq->rx_curr_desc = (rxq->rx_curr_desc + 1) % rxq->rx_ring_size; - - spin_unlock_irqrestore(&mp->lock, flags); + rxq->rx_curr_desc++; + if (rxq->rx_curr_desc == rxq->rx_ring_size) + rxq->rx_curr_desc = 0; - dma_unmap_single(NULL, rx_desc->buf_ptr + 2, - mp->dev->mtu + 24, DMA_FROM_DEVICE); + dma_unmap_single(NULL, rx_desc->buf_ptr, + rx_desc->buf_size, DMA_FROM_DEVICE); rxq->rx_desc_count--; rx++; + mp->work_rx_refill |= 1 << rxq->index; + + byte_cnt = rx_desc->byte_cnt; + /* * Update statistics. * @@ -547,7 +521,7 @@ static int rxq_process(struct rx_queue *rxq, int budget) * byte CRC at the end of the packet (which we do count). */ stats->rx_packets++; - stats->rx_bytes += rx_desc->byte_cnt - 2; + stats->rx_bytes += byte_cnt - 2; /* * In case we received a packet without first / last bits @@ -570,68 +544,84 @@ static int rxq_process(struct rx_queue *rxq, int budget) if (cmd_sts & ERROR_SUMMARY) stats->rx_errors++; - dev_kfree_skb_irq(skb); + dev_kfree_skb(skb); } else { /* * The -4 is for the CRC in the trailer of the * received packet */ - skb_put(skb, rx_desc->byte_cnt - 2 - 4); + skb_put(skb, byte_cnt - 2 - 4); - if (cmd_sts & LAYER_4_CHECKSUM_OK) { + if (cmd_sts & LAYER_4_CHECKSUM_OK) skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->csum = htons( - (cmd_sts & 0x0007fff8) >> 3); - } skb->protocol = eth_type_trans(skb, mp->dev); -#ifdef MV643XX_ETH_NAPI netif_receive_skb(skb); -#else - netif_rx(skb); -#endif } mp->dev->last_rx = jiffies; } - rxq_refill(rxq); + if (rx < budget) + mp->work_rx &= ~(1 << rxq->index); return rx; } -#ifdef MV643XX_ETH_NAPI -static int mv643xx_eth_poll(struct napi_struct *napi, int budget) +static int rxq_refill(struct rx_queue *rxq, int budget) { - struct mv643xx_eth_private *mp; - int rx; - int i; + struct mv643xx_eth_private *mp = rxq_to_mp(rxq); + int refilled; - mp = container_of(napi, struct mv643xx_eth_private, napi); + refilled = 0; + while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) { + struct sk_buff *skb; + int unaligned; + int rx; -#ifdef MV643XX_ETH_TX_FAST_REFILL - if (++mp->tx_clean_threshold > 5) { - mp->tx_clean_threshold = 0; - for (i = 0; i < 8; i++) - if (mp->txq_mask & (1 << i)) - txq_reclaim(mp->txq + i, 0); - } -#endif + skb = __skb_dequeue(&mp->rx_recycle); + if (skb == NULL) + skb = dev_alloc_skb(mp->skb_size + + dma_get_cache_alignment() - 1); - rx = 0; - for (i = 7; rx < budget && i >= 0; i--) - if (mp->rxq_mask & (1 << i)) - rx += rxq_process(mp->rxq + i, budget - rx); - - if (rx < budget) { - netif_rx_complete(mp->dev, napi); - wrl(mp, INT_CAUSE(mp->port_num), 0); - wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); - wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); + if (skb == NULL) { + mp->work_rx_oom |= 1 << rxq->index; + goto oom; + } + + unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1); + if (unaligned) + skb_reserve(skb, dma_get_cache_alignment() - unaligned); + + refilled++; + rxq->rx_desc_count++; + + rx = rxq->rx_used_desc++; + if (rxq->rx_used_desc == rxq->rx_ring_size) + rxq->rx_used_desc = 0; + + rxq->rx_desc_area[rx].buf_ptr = dma_map_single(NULL, skb->data, + mp->skb_size, DMA_FROM_DEVICE); + rxq->rx_desc_area[rx].buf_size = mp->skb_size; + rxq->rx_skb[rx] = skb; + wmb(); + rxq->rx_desc_area[rx].cmd_sts = BUFFER_OWNED_BY_DMA | + RX_ENABLE_INTERRUPT; + wmb(); + + /* + * The hardware automatically prepends 2 bytes of + * dummy data to each received packet, so that the + * IP header ends up 16-byte aligned. + */ + skb_reserve(skb, 2); } - return rx; + if (refilled < budget) + mp->work_rx_refill &= ~(1 << rxq->index); + +oom: + return refilled; } -#endif /* tx ***********************************************************************/ @@ -654,8 +644,9 @@ static int txq_alloc_desc_index(struct tx_queue *txq) BUG_ON(txq->tx_desc_count >= txq->tx_ring_size); - tx_desc_curr = txq->tx_curr_desc; - txq->tx_curr_desc = (tx_desc_curr + 1) % txq->tx_ring_size; + tx_desc_curr = txq->tx_curr_desc++; + if (txq->tx_curr_desc == txq->tx_ring_size) + txq->tx_curr_desc = 0; BUG_ON(txq->tx_curr_desc == txq->tx_used_desc); @@ -684,10 +675,8 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) desc->cmd_sts = BUFFER_OWNED_BY_DMA | ZERO_PADDING | TX_LAST_DESC | TX_ENABLE_INTERRUPT; - txq->tx_skb[tx_index] = skb; } else { desc->cmd_sts = BUFFER_OWNED_BY_DMA; - txq->tx_skb[tx_index] = NULL; } desc->l4i_chk = 0; @@ -704,35 +693,37 @@ static inline __be16 sum16_as_be(__sum16 sum) return (__force __be16)sum; } -static void txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) +static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) { + struct mv643xx_eth_private *mp = txq_to_mp(txq); int nr_frags = skb_shinfo(skb)->nr_frags; int tx_index; struct tx_desc *desc; u32 cmd_sts; + u16 l4i_chk; int length; cmd_sts = TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA; + l4i_chk = 0; - tx_index = txq_alloc_desc_index(txq); - desc = &txq->tx_desc_area[tx_index]; - - if (nr_frags) { - txq_submit_frag_skb(txq, skb); + if (skb->ip_summed == CHECKSUM_PARTIAL) { + int tag_bytes; - length = skb_headlen(skb); - txq->tx_skb[tx_index] = NULL; - } else { - cmd_sts |= ZERO_PADDING | TX_LAST_DESC | TX_ENABLE_INTERRUPT; - length = skb->len; - txq->tx_skb[tx_index] = skb; - } + BUG_ON(skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_8021Q)); - desc->byte_cnt = length; - desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE); + tag_bytes = (void *)ip_hdr(skb) - (void *)skb->data - ETH_HLEN; + if (unlikely(tag_bytes & ~12)) { + if (skb_checksum_help(skb) == 0) + goto no_csum; + kfree_skb(skb); + return 1; + } - if (skb->ip_summed == CHECKSUM_PARTIAL) { - BUG_ON(skb->protocol != htons(ETH_P_IP)); + if (tag_bytes & 4) + cmd_sts |= MAC_HDR_EXTRA_4_BYTES; + if (tag_bytes & 8) + cmd_sts |= MAC_HDR_EXTRA_8_BYTES; cmd_sts |= GEN_TCP_UDP_CHECKSUM | GEN_IP_V4_CHECKSUM | @@ -741,78 +732,189 @@ static void txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) switch (ip_hdr(skb)->protocol) { case IPPROTO_UDP: cmd_sts |= UDP_FRAME; - desc->l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check)); + l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check)); break; case IPPROTO_TCP: - desc->l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check)); + l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check)); break; default: BUG(); } } else { +no_csum: /* Errata BTS #50, IHL must be 5 if no HW checksum */ cmd_sts |= 5 << TX_IHL_SHIFT; - desc->l4i_chk = 0; } + tx_index = txq_alloc_desc_index(txq); + desc = &txq->tx_desc_area[tx_index]; + + if (nr_frags) { + txq_submit_frag_skb(txq, skb); + length = skb_headlen(skb); + } else { + cmd_sts |= ZERO_PADDING | TX_LAST_DESC | TX_ENABLE_INTERRUPT; + length = skb->len; + } + + desc->l4i_chk = l4i_chk; + desc->byte_cnt = length; + desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE); + + __skb_queue_tail(&txq->tx_skb, skb); + /* ensure all other descriptors are written before first cmd_sts */ wmb(); desc->cmd_sts = cmd_sts; + /* clear TX_END status */ + mp->work_tx_end &= ~(1 << txq->index); + /* ensure all descriptors are written before poking hardware */ wmb(); txq_enable(txq); txq->tx_desc_count += nr_frags + 1; + + return 0; } static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; + int queue; struct tx_queue *txq; - unsigned long flags; + struct netdev_queue *nq; + + queue = skb_get_queue_mapping(skb); + txq = mp->txq + queue; + nq = netdev_get_tx_queue(dev, queue); if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { - stats->tx_dropped++; + txq->tx_dropped++; dev_printk(KERN_DEBUG, &dev->dev, "failed to linearize skb with tiny " "unaligned fragment\n"); return NETDEV_TX_BUSY; } - spin_lock_irqsave(&mp->lock, flags); - - txq = mp->txq + mp->txq_primary; - - if (txq->tx_ring_size - txq->tx_desc_count < MAX_DESCS_PER_SKB) { - spin_unlock_irqrestore(&mp->lock, flags); - if (txq->index == mp->txq_primary && net_ratelimit()) - dev_printk(KERN_ERR, &dev->dev, - "primary tx queue full?!\n"); + if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) { + if (net_ratelimit()) + dev_printk(KERN_ERR, &dev->dev, "tx queue full?!\n"); kfree_skb(skb); return NETDEV_TX_OK; } - txq_submit_skb(txq, skb); - stats->tx_bytes += skb->len; - stats->tx_packets++; - dev->trans_start = jiffies; - - if (txq->index == mp->txq_primary) { + if (!txq_submit_skb(txq, skb)) { int entries_left; + txq->tx_bytes += skb->len; + txq->tx_packets++; + dev->trans_start = jiffies; + entries_left = txq->tx_ring_size - txq->tx_desc_count; - if (entries_left < MAX_DESCS_PER_SKB) - netif_stop_queue(dev); + if (entries_left < MAX_SKB_FRAGS + 1) + netif_tx_stop_queue(nq); } - spin_unlock_irqrestore(&mp->lock, flags); - return NETDEV_TX_OK; } +/* tx napi ******************************************************************/ +static void txq_kick(struct tx_queue *txq) +{ + struct mv643xx_eth_private *mp = txq_to_mp(txq); + struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index); + u32 hw_desc_ptr; + u32 expected_ptr; + + __netif_tx_lock(nq, smp_processor_id()); + + if (rdl(mp, TXQ_COMMAND(mp->port_num)) & (1 << txq->index)) + goto out; + + hw_desc_ptr = rdl(mp, TXQ_CURRENT_DESC_PTR(mp->port_num, txq->index)); + expected_ptr = (u32)txq->tx_desc_dma + + txq->tx_curr_desc * sizeof(struct tx_desc); + + if (hw_desc_ptr != expected_ptr) + txq_enable(txq); + +out: + __netif_tx_unlock(nq); + + mp->work_tx_end &= ~(1 << txq->index); +} + +static int txq_reclaim(struct tx_queue *txq, int budget, int force) +{ + struct mv643xx_eth_private *mp = txq_to_mp(txq); + struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index); + int reclaimed; + + __netif_tx_lock(nq, smp_processor_id()); + + reclaimed = 0; + while (reclaimed < budget && txq->tx_desc_count > 0) { + int tx_index; + struct tx_desc *desc; + u32 cmd_sts; + struct sk_buff *skb; + + tx_index = txq->tx_used_desc; + desc = &txq->tx_desc_area[tx_index]; + cmd_sts = desc->cmd_sts; + + if (cmd_sts & BUFFER_OWNED_BY_DMA) { + if (!force) + break; + desc->cmd_sts = cmd_sts & ~BUFFER_OWNED_BY_DMA; + } + + txq->tx_used_desc = tx_index + 1; + if (txq->tx_used_desc == txq->tx_ring_size) + txq->tx_used_desc = 0; + + reclaimed++; + txq->tx_desc_count--; + + skb = NULL; + if (cmd_sts & TX_LAST_DESC) + skb = __skb_dequeue(&txq->tx_skb); + + if (cmd_sts & ERROR_SUMMARY) { + dev_printk(KERN_INFO, &mp->dev->dev, "tx error\n"); + mp->dev->stats.tx_errors++; + } + + if (cmd_sts & TX_FIRST_DESC) { + dma_unmap_single(NULL, desc->buf_ptr, + desc->byte_cnt, DMA_TO_DEVICE); + } else { + dma_unmap_page(NULL, desc->buf_ptr, + desc->byte_cnt, DMA_TO_DEVICE); + } + + if (skb != NULL) { + if (skb_queue_len(&mp->rx_recycle) < + mp->default_rx_ring_size && + skb_recycle_check(skb, mp->skb_size)) + __skb_queue_head(&mp->rx_recycle, skb); + else + dev_kfree_skb(skb); + } + } + + __netif_tx_unlock(nq); + + if (reclaimed < budget) + mp->work_tx &= ~(1 << txq->index); + + return reclaimed; +} + + /* tx rate control **********************************************************/ /* * Set total maximum TX rate (shared by all TX queues for this port) @@ -836,14 +938,17 @@ static void tx_set_rate(struct mv643xx_eth_private *mp, int rate, int burst) if (bucket_size > 65535) bucket_size = 65535; - if (mp->shared->tx_bw_control_moved) { - wrl(mp, TX_BW_RATE_MOVED(mp->port_num), token_rate); - wrl(mp, TX_BW_MTU_MOVED(mp->port_num), mtu); - wrl(mp, TX_BW_BURST_MOVED(mp->port_num), bucket_size); - } else { + switch (mp->shared->tx_bw_control) { + case TX_BW_CONTROL_OLD_LAYOUT: wrl(mp, TX_BW_RATE(mp->port_num), token_rate); wrl(mp, TX_BW_MTU(mp->port_num), mtu); wrl(mp, TX_BW_BURST(mp->port_num), bucket_size); + break; + case TX_BW_CONTROL_NEW_LAYOUT: + wrl(mp, TX_BW_RATE_MOVED(mp->port_num), token_rate); + wrl(mp, TX_BW_MTU_MOVED(mp->port_num), mtu); + wrl(mp, TX_BW_BURST_MOVED(mp->port_num), bucket_size); + break; } } @@ -875,14 +980,21 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq) /* * Turn on fixed priority mode. */ - if (mp->shared->tx_bw_control_moved) - off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num); - else + off = 0; + switch (mp->shared->tx_bw_control) { + case TX_BW_CONTROL_OLD_LAYOUT: off = TXQ_FIX_PRIO_CONF(mp->port_num); + break; + case TX_BW_CONTROL_NEW_LAYOUT: + off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num); + break; + } - val = rdl(mp, off); - val |= 1 << txq->index; - wrl(mp, off, val); + if (off) { + val = rdl(mp, off); + val |= 1 << txq->index; + wrl(mp, off, val); + } } static void txq_set_wrr(struct tx_queue *txq, int weight) @@ -894,95 +1006,147 @@ static void txq_set_wrr(struct tx_queue *txq, int weight) /* * Turn off fixed priority mode. */ - if (mp->shared->tx_bw_control_moved) - off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num); - else + off = 0; + switch (mp->shared->tx_bw_control) { + case TX_BW_CONTROL_OLD_LAYOUT: off = TXQ_FIX_PRIO_CONF(mp->port_num); + break; + case TX_BW_CONTROL_NEW_LAYOUT: + off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num); + break; + } - val = rdl(mp, off); - val &= ~(1 << txq->index); - wrl(mp, off, val); + if (off) { + val = rdl(mp, off); + val &= ~(1 << txq->index); + wrl(mp, off, val); - /* - * Configure WRR weight for this queue. - */ - off = TXQ_BW_WRR_CONF(mp->port_num, txq->index); + /* + * Configure WRR weight for this queue. + */ + off = TXQ_BW_WRR_CONF(mp->port_num, txq->index); - val = rdl(mp, off); - val = (val & ~0xff) | (weight & 0xff); - wrl(mp, off, val); + val = rdl(mp, off); + val = (val & ~0xff) | (weight & 0xff); + wrl(mp, off, val); + } } /* mii management interface *************************************************/ -#define SMI_BUSY 0x10000000 -#define SMI_READ_VALID 0x08000000 -#define SMI_OPCODE_READ 0x04000000 -#define SMI_OPCODE_WRITE 0x00000000 +static irqreturn_t mv643xx_eth_err_irq(int irq, void *dev_id) +{ + struct mv643xx_eth_shared_private *msp = dev_id; + + if (readl(msp->base + ERR_INT_CAUSE) & ERR_INT_SMI_DONE) { + writel(~ERR_INT_SMI_DONE, msp->base + ERR_INT_CAUSE); + wake_up(&msp->smi_busy_wait); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} -static void smi_reg_read(struct mv643xx_eth_private *mp, unsigned int addr, - unsigned int reg, unsigned int *value) +static int smi_is_done(struct mv643xx_eth_shared_private *msp) { - void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; - unsigned long flags; - int i; + return !(readl(msp->base + SMI_REG) & SMI_BUSY); +} - /* the SMI register is a shared resource */ - spin_lock_irqsave(&mp->shared_smi->phy_lock, flags); +static int smi_wait_ready(struct mv643xx_eth_shared_private *msp) +{ + if (msp->err_interrupt == NO_IRQ) { + int i; - /* wait for the SMI register to become available */ - for (i = 0; readl(smi_reg) & SMI_BUSY; i++) { - if (i == 1000) { - printk("%s: PHY busy timeout\n", mp->dev->name); - goto out; + for (i = 0; !smi_is_done(msp); i++) { + if (i == 10) + return -ETIMEDOUT; + msleep(10); } - udelay(10); + + return 0; + } + + if (!wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp), + msecs_to_jiffies(100))) + return -ETIMEDOUT; + + return 0; +} + +static int smi_bus_read(struct mii_bus *bus, int addr, int reg) +{ + struct mv643xx_eth_shared_private *msp = bus->priv; + void __iomem *smi_reg = msp->base + SMI_REG; + int ret; + + if (smi_wait_ready(msp)) { + printk("mv643xx_eth: SMI bus busy timeout\n"); + return -ETIMEDOUT; } writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg); - /* now wait for the data to be valid */ - for (i = 0; !(readl(smi_reg) & SMI_READ_VALID); i++) { - if (i == 1000) { - printk("%s: PHY read timeout\n", mp->dev->name); - goto out; - } - udelay(10); + if (smi_wait_ready(msp)) { + printk("mv643xx_eth: SMI bus busy timeout\n"); + return -ETIMEDOUT; } - *value = readl(smi_reg) & 0xffff; -out: - spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags); + ret = readl(smi_reg); + if (!(ret & SMI_READ_VALID)) { + printk("mv643xx_eth: SMI bus read not valid\n"); + return -ENODEV; + } + + return ret & 0xffff; } -static void smi_reg_write(struct mv643xx_eth_private *mp, - unsigned int addr, - unsigned int reg, unsigned int value) +static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val) { - void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; - unsigned long flags; - int i; - - /* the SMI register is a shared resource */ - spin_lock_irqsave(&mp->shared_smi->phy_lock, flags); + struct mv643xx_eth_shared_private *msp = bus->priv; + void __iomem *smi_reg = msp->base + SMI_REG; - /* wait for the SMI register to become available */ - for (i = 0; readl(smi_reg) & SMI_BUSY; i++) { - if (i == 1000) { - printk("%s: PHY busy timeout\n", mp->dev->name); - goto out; - } - udelay(10); + if (smi_wait_ready(msp)) { + printk("mv643xx_eth: SMI bus busy timeout\n"); + return -ETIMEDOUT; } writel(SMI_OPCODE_WRITE | (reg << 21) | - (addr << 16) | (value & 0xffff), smi_reg); -out: - spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags); + (addr << 16) | (val & 0xffff), smi_reg); + + if (smi_wait_ready(msp)) { + printk("mv643xx_eth: SMI bus busy timeout\n"); + return -ETIMEDOUT; + } + + return 0; } -/* mib counters *************************************************************/ +/* statistics ***************************************************************/ +static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev) +{ + struct mv643xx_eth_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + unsigned long tx_packets = 0; + unsigned long tx_bytes = 0; + unsigned long tx_dropped = 0; + int i; + + for (i = 0; i < mp->txq_count; i++) { + struct tx_queue *txq = mp->txq + i; + + tx_packets += txq->tx_packets; + tx_bytes += txq->tx_bytes; + tx_dropped += txq->tx_dropped; + } + + stats->tx_packets = tx_packets; + stats->tx_bytes = tx_bytes; + stats->tx_dropped = tx_dropped; + + return stats; +} + static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset) { return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); @@ -1000,6 +1164,7 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) { struct mib_counters *p = &mp->mib_counters; + spin_lock(&mp->mib_counters_lock); p->good_octets_received += mib_read(mp, 0x00); p->good_octets_received += (u64)mib_read(mp, 0x04) << 32; p->bad_octets_received += mib_read(mp, 0x08); @@ -1032,6 +1197,16 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) p->bad_crc_event += mib_read(mp, 0x74); p->collision += mib_read(mp, 0x78); p->late_collision += mib_read(mp, 0x7c); + spin_unlock(&mp->mib_counters_lock); + + mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ); +} + +static void mib_counters_timer_wrapper(unsigned long _mp) +{ + struct mv643xx_eth_private *mp = (void *)_mp; + + mib_counters_update(mp); } @@ -1097,9 +1272,9 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd * struct mv643xx_eth_private *mp = netdev_priv(dev); int err; - spin_lock_irq(&mp->lock); - err = mii_ethtool_gset(&mp->mii, cmd); - spin_unlock_irq(&mp->lock); + err = phy_read_status(mp->phy); + if (err == 0) + err = phy_ethtool_gset(mp->phy, cmd); /* * The MAC does not support 1000baseT_Half. @@ -1112,10 +1287,28 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd * static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd) { + struct mv643xx_eth_private *mp = netdev_priv(dev); + u32 port_status; + + port_status = rdl(mp, PORT_STATUS(mp->port_num)); + cmd->supported = SUPPORTED_MII; cmd->advertising = ADVERTISED_MII; - cmd->speed = SPEED_1000; - cmd->duplex = DUPLEX_FULL; + switch (port_status & PORT_SPEED_MASK) { + case PORT_SPEED_10: + cmd->speed = SPEED_10; + break; + case PORT_SPEED_100: + cmd->speed = SPEED_100; + break; + case PORT_SPEED_1000: + cmd->speed = SPEED_1000; + break; + default: + cmd->speed = -1; + break; + } + cmd->duplex = (port_status & FULL_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; cmd->port = PORT_MII; cmd->phy_address = 0; cmd->transceiver = XCVR_INTERNAL; @@ -1129,18 +1322,13 @@ static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethto static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct mv643xx_eth_private *mp = netdev_priv(dev); - int err; /* * The MAC does not support 1000baseT_Half. */ cmd->advertising &= ~ADVERTISED_1000baseT_Half; - spin_lock_irq(&mp->lock); - err = mii_ethtool_sset(&mp->mii, cmd); - spin_unlock_irq(&mp->lock); - - return err; + return phy_ethtool_sset(mp->phy, cmd); } static int mv643xx_eth_set_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd) @@ -1162,7 +1350,7 @@ static int mv643xx_eth_nway_reset(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - return mii_nway_restart(&mp->mii); + return genphy_restart_aneg(mp->phy); } static int mv643xx_eth_nway_reset_phyless(struct net_device *dev) @@ -1172,14 +1360,7 @@ static int mv643xx_eth_nway_reset_phyless(struct net_device *dev) static u32 mv643xx_eth_get_link(struct net_device *dev) { - struct mv643xx_eth_private *mp = netdev_priv(dev); - - return mii_link_ok(&mp->mii); -} - -static u32 mv643xx_eth_get_link_phyless(struct net_device *dev) -{ - return 1; + return !!netif_carrier_ok(dev); } static void mv643xx_eth_get_strings(struct net_device *dev, @@ -1200,9 +1381,10 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, uint64_t *data) { - struct mv643xx_eth_private *mp = dev->priv; + struct mv643xx_eth_private *mp = netdev_priv(dev); int i; + mv643xx_eth_get_stats(dev); mib_counters_update(mp); for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { @@ -1246,7 +1428,7 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = { .set_settings = mv643xx_eth_set_settings_phyless, .get_drvinfo = mv643xx_eth_get_drvinfo, .nway_reset = mv643xx_eth_nway_reset_phyless, - .get_link = mv643xx_eth_get_link_phyless, + .get_link = mv643xx_eth_get_link, .set_sg = ethtool_op_set_sg, .get_strings = mv643xx_eth_get_strings, .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, @@ -1410,7 +1592,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) size = rxq->rx_ring_size * sizeof(struct rx_desc); - if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) { + if (index == 0 && size <= mp->rx_desc_sram_size) { rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr, mp->rx_desc_sram_size); rxq->rx_desc_dma = mp->rx_desc_sram_addr; @@ -1438,20 +1620,21 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) rx_desc = (struct rx_desc *)rxq->rx_desc_area; for (i = 0; i < rxq->rx_ring_size; i++) { - int nexti = (i + 1) % rxq->rx_ring_size; + int nexti; + + nexti = i + 1; + if (nexti == rxq->rx_ring_size) + nexti = 0; + rx_desc[i].next_desc_ptr = rxq->rx_desc_dma + nexti * sizeof(struct rx_desc); } - init_timer(&rxq->rx_oom); - rxq->rx_oom.data = (unsigned long)rxq; - rxq->rx_oom.function = rxq_refill_timer_wrapper; - return 0; out_free: - if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) + if (index == 0 && size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else dma_free_coherent(NULL, size, @@ -1469,8 +1652,6 @@ static void rxq_deinit(struct rx_queue *rxq) rxq_disable(rxq); - del_timer_sync(&rxq->rx_oom); - for (i = 0; i < rxq->rx_ring_size; i++) { if (rxq->rx_skb[i]) { dev_kfree_skb(rxq->rx_skb[i]); @@ -1484,7 +1665,7 @@ static void rxq_deinit(struct rx_queue *rxq) rxq->rx_desc_count); } - if (rxq->index == mp->rxq_primary && + if (rxq->index == 0 && rxq->rx_desc_area_size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else @@ -1511,7 +1692,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) size = txq->tx_ring_size * sizeof(struct tx_desc); - if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) { + if (index == 0 && size <= mp->tx_desc_sram_size) { txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr, mp->tx_desc_sram_size); txq->tx_desc_dma = mp->tx_desc_sram_addr; @@ -1524,91 +1705,29 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) if (txq->tx_desc_area == NULL) { dev_printk(KERN_ERR, &mp->dev->dev, "can't allocate tx ring (%d bytes)\n", size); - goto out; + return -ENOMEM; } memset(txq->tx_desc_area, 0, size); txq->tx_desc_area_size = size; - txq->tx_skb = kmalloc(txq->tx_ring_size * sizeof(*txq->tx_skb), - GFP_KERNEL); - if (txq->tx_skb == NULL) { - dev_printk(KERN_ERR, &mp->dev->dev, - "can't allocate tx skb ring\n"); - goto out_free; - } tx_desc = (struct tx_desc *)txq->tx_desc_area; for (i = 0; i < txq->tx_ring_size; i++) { - int nexti = (i + 1) % txq->tx_ring_size; - tx_desc[i].next_desc_ptr = txq->tx_desc_dma + - nexti * sizeof(struct tx_desc); - } - - return 0; + struct tx_desc *txd = tx_desc + i; + int nexti; + nexti = i + 1; + if (nexti == txq->tx_ring_size) + nexti = 0; -out_free: - if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) - iounmap(txq->tx_desc_area); - else - dma_free_coherent(NULL, size, - txq->tx_desc_area, - txq->tx_desc_dma); - -out: - return -ENOMEM; -} - -static void txq_reclaim(struct tx_queue *txq, int force) -{ - struct mv643xx_eth_private *mp = txq_to_mp(txq); - unsigned long flags; - - spin_lock_irqsave(&mp->lock, flags); - while (txq->tx_desc_count > 0) { - int tx_index; - struct tx_desc *desc; - u32 cmd_sts; - struct sk_buff *skb; - dma_addr_t addr; - int count; - - tx_index = txq->tx_used_desc; - desc = &txq->tx_desc_area[tx_index]; - cmd_sts = desc->cmd_sts; - - if (!force && (cmd_sts & BUFFER_OWNED_BY_DMA)) - break; - - txq->tx_used_desc = (tx_index + 1) % txq->tx_ring_size; - txq->tx_desc_count--; - - addr = desc->buf_ptr; - count = desc->byte_cnt; - skb = txq->tx_skb[tx_index]; - txq->tx_skb[tx_index] = NULL; - - if (cmd_sts & ERROR_SUMMARY) { - dev_printk(KERN_INFO, &mp->dev->dev, "tx error\n"); - mp->dev->stats.tx_errors++; - } - - /* - * Drop mp->lock while we free the skb. - */ - spin_unlock_irqrestore(&mp->lock, flags); - - if (cmd_sts & TX_FIRST_DESC) - dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE); - else - dma_unmap_page(NULL, addr, count, DMA_TO_DEVICE); + txd->cmd_sts = 0; + txd->next_desc_ptr = txq->tx_desc_dma + + nexti * sizeof(struct tx_desc); + } - if (skb) - dev_kfree_skb_irq(skb); + skb_queue_head_init(&txq->tx_skb); - spin_lock_irqsave(&mp->lock, flags); - } - spin_unlock_irqrestore(&mp->lock, flags); + return 0; } static void txq_deinit(struct tx_queue *txq) @@ -1616,183 +1735,203 @@ static void txq_deinit(struct tx_queue *txq) struct mv643xx_eth_private *mp = txq_to_mp(txq); txq_disable(txq); - txq_reclaim(txq, 1); + txq_reclaim(txq, txq->tx_ring_size, 1); BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); - if (txq->index == mp->txq_primary && + if (txq->index == 0 && txq->tx_desc_area_size <= mp->tx_desc_sram_size) iounmap(txq->tx_desc_area); else dma_free_coherent(NULL, txq->tx_desc_area_size, txq->tx_desc_area, txq->tx_desc_dma); - - kfree(txq->tx_skb); } /* netdev ops and related ***************************************************/ -static void update_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) +static int mv643xx_eth_collect_events(struct mv643xx_eth_private *mp) { - u32 pscr_o; - u32 pscr_n; - - pscr_o = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + u32 int_cause; + u32 int_cause_ext; - /* clear speed, duplex and rx buffer size fields */ - pscr_n = pscr_o & ~(SET_MII_SPEED_TO_100 | - SET_GMII_SPEED_TO_1000 | - SET_FULL_DUPLEX_MODE | - MAX_RX_PACKET_MASK); + int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & + (INT_TX_END | INT_RX | INT_EXT); + if (int_cause == 0) + return 0; - if (speed == SPEED_1000) { - pscr_n |= SET_GMII_SPEED_TO_1000 | MAX_RX_PACKET_9700BYTE; - } else { - if (speed == SPEED_100) - pscr_n |= SET_MII_SPEED_TO_100; - pscr_n |= MAX_RX_PACKET_1522BYTE; + int_cause_ext = 0; + if (int_cause & INT_EXT) + int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num)); + + int_cause &= INT_TX_END | INT_RX; + if (int_cause) { + wrl(mp, INT_CAUSE(mp->port_num), ~int_cause); + mp->work_tx_end |= ((int_cause & INT_TX_END) >> 19) & + ~(rdl(mp, TXQ_COMMAND(mp->port_num)) & 0xff); + mp->work_rx |= (int_cause & INT_RX) >> 2; } - if (duplex == DUPLEX_FULL) - pscr_n |= SET_FULL_DUPLEX_MODE; - - if (pscr_n != pscr_o) { - if ((pscr_o & SERIAL_PORT_ENABLE) == 0) - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); - else { - int i; - - for (i = 0; i < 8; i++) - if (mp->txq_mask & (1 << i)) - txq_disable(mp->txq + i); - - pscr_o &= ~SERIAL_PORT_ENABLE; - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_o); - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); - - for (i = 0; i < 8; i++) - if (mp->txq_mask & (1 << i)) - txq_enable(mp->txq + i); - } + int_cause_ext &= INT_EXT_LINK_PHY | INT_EXT_TX; + if (int_cause_ext) { + wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext); + if (int_cause_ext & INT_EXT_LINK_PHY) + mp->work_link = 1; + mp->work_tx |= int_cause_ext & INT_EXT_TX; } + + return 1; } static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct mv643xx_eth_private *mp = netdev_priv(dev); - u32 int_cause; - u32 int_cause_ext; - u32 txq_active; - int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & - (INT_TX_END | INT_RX | INT_EXT); - if (int_cause == 0) + if (unlikely(!mv643xx_eth_collect_events(mp))) return IRQ_NONE; - int_cause_ext = 0; - if (int_cause & INT_EXT) { - int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num)) - & (INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); - wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext); - } + wrl(mp, INT_MASK(mp->port_num), 0); + napi_schedule(&mp->napi); - if (int_cause_ext & (INT_EXT_PHY | INT_EXT_LINK)) { - if (mp->phy_addr == -1 || mii_link_ok(&mp->mii)) { + return IRQ_HANDLED; +} + +static void handle_link_event(struct mv643xx_eth_private *mp) +{ + struct net_device *dev = mp->dev; + u32 port_status; + int speed; + int duplex; + int fc; + + port_status = rdl(mp, PORT_STATUS(mp->port_num)); + if (!(port_status & LINK_UP)) { + if (netif_carrier_ok(dev)) { int i; - if (mp->phy_addr != -1) { - struct ethtool_cmd cmd; + printk(KERN_INFO "%s: link down\n", dev->name); - mii_ethtool_gset(&mp->mii, &cmd); - update_pscr(mp, cmd.speed, cmd.duplex); - } + netif_carrier_off(dev); - for (i = 0; i < 8; i++) - if (mp->txq_mask & (1 << i)) - txq_enable(mp->txq + i); + for (i = 0; i < mp->txq_count; i++) { + struct tx_queue *txq = mp->txq + i; - if (!netif_carrier_ok(dev)) { - netif_carrier_on(dev); - __txq_maybe_wake(mp->txq + mp->txq_primary); + txq_reclaim(txq, txq->tx_ring_size, 1); + txq_reset_hw_ptr(txq); } - } else if (netif_carrier_ok(dev)) { - netif_stop_queue(dev); - netif_carrier_off(dev); } + return; } - /* - * RxBuffer or RxError set for any of the 8 queues? - */ -#ifdef MV643XX_ETH_NAPI - if (int_cause & INT_RX) { - wrl(mp, INT_MASK(mp->port_num), 0x00000000); - rdl(mp, INT_MASK(mp->port_num)); - - netif_rx_schedule(dev, &mp->napi); + switch (port_status & PORT_SPEED_MASK) { + case PORT_SPEED_10: + speed = 10; + break; + case PORT_SPEED_100: + speed = 100; + break; + case PORT_SPEED_1000: + speed = 1000; + break; + default: + speed = -1; + break; } -#else - if (int_cause & INT_RX) { - int i; + duplex = (port_status & FULL_DUPLEX) ? 1 : 0; + fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0; - for (i = 7; i >= 0; i--) - if (mp->rxq_mask & (1 << i)) - rxq_process(mp->rxq + i, INT_MAX); - } -#endif + printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, " + "flow control %sabled\n", dev->name, + speed, duplex ? "full" : "half", + fc ? "en" : "dis"); - txq_active = rdl(mp, TXQ_COMMAND(mp->port_num)); + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); +} - /* - * TxBuffer or TxError set for any of the 8 queues? - */ - if (int_cause_ext & INT_EXT_TX) { - int i; +static int mv643xx_eth_poll(struct napi_struct *napi, int budget) +{ + struct mv643xx_eth_private *mp; + int work_done; - for (i = 0; i < 8; i++) - if (mp->txq_mask & (1 << i)) - txq_reclaim(mp->txq + i, 0); - } + mp = container_of(napi, struct mv643xx_eth_private, napi); - /* - * Any TxEnd interrupts? - */ - if (int_cause & INT_TX_END) { - int i; + mp->work_rx_refill |= mp->work_rx_oom; + mp->work_rx_oom = 0; + + work_done = 0; + while (work_done < budget) { + u8 queue_mask; + int queue; + int work_tbd; + + if (mp->work_link) { + mp->work_link = 0; + handle_link_event(mp); + continue; + } - wrl(mp, INT_CAUSE(mp->port_num), ~(int_cause & INT_TX_END)); - for (i = 0; i < 8; i++) { - struct tx_queue *txq = mp->txq + i; - if (txq->tx_desc_count && !((txq_active >> i) & 1)) - txq_enable(txq); + queue_mask = mp->work_tx | mp->work_tx_end | + mp->work_rx | mp->work_rx_refill; + if (!queue_mask) { + if (mv643xx_eth_collect_events(mp)) + continue; + break; + } + + queue = fls(queue_mask) - 1; + queue_mask = 1 << queue; + + work_tbd = budget - work_done; + if (work_tbd > 16) + work_tbd = 16; + + if (mp->work_tx_end & queue_mask) { + txq_kick(mp->txq + queue); + } else if (mp->work_tx & queue_mask) { + work_done += txq_reclaim(mp->txq + queue, work_tbd, 0); + txq_maybe_wake(mp->txq + queue); + } else if (mp->work_rx & queue_mask) { + work_done += rxq_process(mp->rxq + queue, work_tbd); + } else if (mp->work_rx_refill & queue_mask) { + work_done += rxq_refill(mp->rxq + queue, work_tbd); + } else { + BUG(); } } - /* - * Enough space again in the primary TX queue for a full packet? - */ - if (int_cause_ext & INT_EXT_TX) { - struct tx_queue *txq = mp->txq + mp->txq_primary; - __txq_maybe_wake(txq); + if (work_done < budget) { + if (mp->work_rx_oom) + mod_timer(&mp->rx_oom, jiffies + (HZ / 10)); + napi_complete(napi); + wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); } - return IRQ_HANDLED; + return work_done; +} + +static inline void oom_timer_wrapper(unsigned long data) +{ + struct mv643xx_eth_private *mp = (void *)data; + + napi_schedule(&mp->napi); } static void phy_reset(struct mv643xx_eth_private *mp) { - unsigned int data; + int data; - smi_reg_read(mp, mp->phy_addr, 0, &data); - data |= 0x8000; - smi_reg_write(mp, mp->phy_addr, 0, data); + data = phy_read(mp->phy, MII_BMCR); + if (data < 0) + return; + + data |= BMCR_RESET; + if (phy_write(mp->phy, MII_BMCR, data) < 0) + return; do { - udelay(1); - smi_reg_read(mp, mp->phy_addr, 0, &data); - } while (data & 0x8000); + data = phy_read(mp->phy, MII_BMCR); + } while (data >= 0 && data & BMCR_RESET); } static void port_start(struct mv643xx_eth_private *mp) @@ -1801,26 +1940,9 @@ static void port_start(struct mv643xx_eth_private *mp) int i; /* - * Configure basic link parameters. - */ - pscr = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); - pscr &= ~(SERIAL_PORT_ENABLE | FORCE_LINK_PASS); - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); - pscr |= DISABLE_AUTO_NEG_FOR_FLOW_CTRL | - DISABLE_AUTO_NEG_SPEED_GMII | - DISABLE_AUTO_NEG_FOR_DUPLEX | - DO_NOT_FORCE_LINK_FAIL | - SERIAL_PORT_CONTROL_RESERVED; - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); - pscr |= SERIAL_PORT_ENABLE; - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); - - wrl(mp, SDMA_CONFIG(mp->port_num), PORT_SDMA_CONFIG_DEFAULT_VALUE); - - /* * Perform PHY reset, if there is a PHY. */ - if (mp->phy_addr != -1) { + if (mp->phy != NULL) { struct ethtool_cmd cmd; mv643xx_eth_get_settings(mp->dev, &cmd); @@ -1829,21 +1951,28 @@ static void port_start(struct mv643xx_eth_private *mp) } /* + * Configure basic link parameters. + */ + pscr = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + + pscr |= SERIAL_PORT_ENABLE; + wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); + + pscr |= DO_NOT_FORCE_LINK_FAIL; + if (mp->phy == NULL) + pscr |= FORCE_LINK_PASS; + wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); + + wrl(mp, SDMA_CONFIG(mp->port_num), PORT_SDMA_CONFIG_DEFAULT_VALUE); + + /* * Configure TX path and queues. */ tx_set_rate(mp, 1000000000, 16777216); - for (i = 0; i < 8; i++) { + for (i = 0; i < mp->txq_count; i++) { struct tx_queue *txq = mp->txq + i; - int off = TXQ_CURRENT_DESC_PTR(mp->port_num, i); - u32 addr; - - if ((mp->txq_mask & (1 << i)) == 0) - continue; - - addr = (u32)txq->tx_desc_dma; - addr += txq->tx_curr_desc * sizeof(struct tx_desc); - wrl(mp, off, addr); + txq_reset_hw_ptr(txq); txq_set_rate(txq, 1000000000, 16777216); txq_set_fixed_prio_mode(txq); } @@ -1855,9 +1984,10 @@ static void port_start(struct mv643xx_eth_private *mp) /* * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast - * frames to RX queue #0. + * frames to RX queue #0, and include the pseudo-header when + * calculating receive checksums. */ - wrl(mp, PORT_CONFIG(mp->port_num), 0x00000000); + wrl(mp, PORT_CONFIG(mp->port_num), 0x02000000); /* * Treat BPDUs as normal multicasts, and disable partition mode. @@ -1867,14 +1997,11 @@ static void port_start(struct mv643xx_eth_private *mp) /* * Enable the receive queues. */ - for (i = 0; i < 8; i++) { + for (i = 0; i < mp->rxq_count; i++) { struct rx_queue *rxq = mp->rxq + i; int off = RXQ_CURRENT_DESC_PTR(mp->port_num, i); u32 addr; - if ((mp->rxq_mask & (1 << i)) == 0) - continue; - addr = (u32)rxq->rx_desc_dma; addr += rxq->rx_curr_desc * sizeof(struct rx_desc); wrl(mp, off, addr); @@ -1913,6 +2040,26 @@ static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay) wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), (coal & 0x3fff) << 4); } +static void mv643xx_eth_recalc_skb_size(struct mv643xx_eth_private *mp) +{ + int skb_size; + + /* + * Reserve 2+14 bytes for an ethernet header (the hardware + * automatically prepends 2 bytes of dummy data to each + * received packet), 16 bytes for up to four VLAN tags, and + * 4 bytes for the trailing FCS -- 36 bytes total. + */ + skb_size = mp->dev->mtu + 36; + + /* + * Make sure that the skb size is a multiple of 8 bytes, as + * the lower three bits of the receive descriptor's buffer + * size field are ignored by the hardware. + */ + mp->skb_size = (skb_size + 7) & ~7; +} + static int mv643xx_eth_open(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); @@ -1924,8 +2071,7 @@ static int mv643xx_eth_open(struct net_device *dev) rdl(mp, INT_CAUSE_EXT(mp->port_num)); err = request_irq(dev->irq, mv643xx_eth_irq, - IRQF_SHARED | IRQF_SAMPLE_RANDOM, - dev->name, dev); + IRQF_SHARED, dev->name, dev); if (err) { dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n"); return -EAGAIN; @@ -1933,55 +2079,53 @@ static int mv643xx_eth_open(struct net_device *dev) init_mac_tables(mp); - for (i = 0; i < 8; i++) { - if ((mp->rxq_mask & (1 << i)) == 0) - continue; + mv643xx_eth_recalc_skb_size(mp); + + napi_enable(&mp->napi); + + skb_queue_head_init(&mp->rx_recycle); + for (i = 0; i < mp->rxq_count; i++) { err = rxq_init(mp, i); if (err) { while (--i >= 0) - if (mp->rxq_mask & (1 << i)) - rxq_deinit(mp->rxq + i); + rxq_deinit(mp->rxq + i); goto out; } - rxq_refill(mp->rxq + i); + rxq_refill(mp->rxq + i, INT_MAX); } - for (i = 0; i < 8; i++) { - if ((mp->txq_mask & (1 << i)) == 0) - continue; + if (mp->work_rx_oom) { + mp->rx_oom.expires = jiffies + (HZ / 10); + add_timer(&mp->rx_oom); + } + for (i = 0; i < mp->txq_count; i++) { err = txq_init(mp, i); if (err) { while (--i >= 0) - if (mp->txq_mask & (1 << i)) - txq_deinit(mp->txq + i); + txq_deinit(mp->txq + i); goto out_free; } } -#ifdef MV643XX_ETH_NAPI - napi_enable(&mp->napi); -#endif + netif_carrier_off(dev); port_start(mp); set_rx_coal(mp, 0); set_tx_coal(mp, 0); - wrl(mp, INT_MASK_EXT(mp->port_num), - INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); - + wrl(mp, INT_MASK_EXT(mp->port_num), INT_EXT_LINK_PHY | INT_EXT_TX); wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); return 0; out_free: - for (i = 0; i < 8; i++) - if (mp->rxq_mask & (1 << i)) - rxq_deinit(mp->rxq + i); + for (i = 0; i < mp->rxq_count; i++) + rxq_deinit(mp->rxq + i); out: free_irq(dev->irq, dev); @@ -1993,14 +2137,18 @@ static void port_reset(struct mv643xx_eth_private *mp) unsigned int data; int i; - for (i = 0; i < 8; i++) { - if (mp->rxq_mask & (1 << i)) - rxq_disable(mp->rxq + i); - if (mp->txq_mask & (1 << i)) - txq_disable(mp->txq + i); - } - while (!(rdl(mp, PORT_STATUS(mp->port_num)) & TX_FIFO_EMPTY)) + for (i = 0; i < mp->rxq_count; i++) + rxq_disable(mp->rxq + i); + for (i = 0; i < mp->txq_count; i++) + txq_disable(mp->txq + i); + + while (1) { + u32 ps = rdl(mp, PORT_STATUS(mp->port_num)); + + if ((ps & (TX_IN_PROGRESS | TX_FIFO_EMPTY)) == TX_FIFO_EMPTY) + break; udelay(10); + } /* Reset the Enable bit in the Configuration Register */ data = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); @@ -2018,23 +2166,26 @@ static int mv643xx_eth_stop(struct net_device *dev) wrl(mp, INT_MASK(mp->port_num), 0x00000000); rdl(mp, INT_MASK(mp->port_num)); -#ifdef MV643XX_ETH_NAPI + del_timer_sync(&mp->mib_counters_timer); + napi_disable(&mp->napi); -#endif + + del_timer_sync(&mp->rx_oom); + netif_carrier_off(dev); - netif_stop_queue(dev); free_irq(dev->irq, dev); port_reset(mp); + mv643xx_eth_get_stats(dev); mib_counters_update(mp); - for (i = 0; i < 8; i++) { - if (mp->rxq_mask & (1 << i)) - rxq_deinit(mp->rxq + i); - if (mp->txq_mask & (1 << i)) - txq_deinit(mp->txq + i); - } + skb_queue_purge(&mp->rx_recycle); + + for (i = 0; i < mp->rxq_count; i++) + rxq_deinit(mp->rxq + i); + for (i = 0; i < mp->txq_count; i++) + txq_deinit(mp->txq + i); return 0; } @@ -2043,8 +2194,8 @@ static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mv643xx_eth_private *mp = netdev_priv(dev); - if (mp->phy_addr != -1) - return generic_mii_ioctl(&mp->mii, if_mii(ifr), cmd, NULL); + if (mp->phy != NULL) + return phy_mii_ioctl(mp->phy, if_mii(ifr), cmd); return -EOPNOTSUPP; } @@ -2057,6 +2208,7 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) return -EINVAL; dev->mtu = new_mtu; + mv643xx_eth_recalc_skb_size(mp); tx_set_rate(mp, 1000000000, 16777216); if (!netif_running(dev)) @@ -2084,12 +2236,10 @@ static void tx_timeout_task(struct work_struct *ugly) mp = container_of(ugly, struct mv643xx_eth_private, tx_timeout_task); if (netif_running(mp->dev)) { - netif_stop_queue(mp->dev); - + netif_tx_stop_all_queues(mp->dev); port_reset(mp); port_start(mp); - - __txq_maybe_wake(mp->txq + mp->txq_primary); + netif_tx_wake_all_queues(mp->dev); } } @@ -2116,22 +2266,6 @@ static void mv643xx_eth_netpoll(struct net_device *dev) } #endif -static int mv643xx_eth_mdio_read(struct net_device *dev, int addr, int reg) -{ - struct mv643xx_eth_private *mp = netdev_priv(dev); - int val; - - smi_reg_read(mp, addr, reg, &val); - - return val; -} - -static void mv643xx_eth_mdio_write(struct net_device *dev, int addr, int reg, int val) -{ - struct mv643xx_eth_private *mp = netdev_priv(dev); - smi_reg_write(mp, addr, reg, val); -} - /* platform glue ************************************************************/ static void @@ -2183,14 +2317,20 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) msp->extended_rx_coal_limit = 0; /* - * Check whether the TX rate control registers are in the - * old or the new place. + * Check whether the MAC supports TX rate control, and if + * yes, whether its associated registers are in the old or + * the new place. */ writel(1, msp->base + TX_BW_MTU_MOVED(0)); - if (readl(msp->base + TX_BW_MTU_MOVED(0)) & 1) - msp->tx_bw_control_moved = 1; - else - msp->tx_bw_control_moved = 0; + if (readl(msp->base + TX_BW_MTU_MOVED(0)) & 1) { + msp->tx_bw_control = TX_BW_CONTROL_NEW_LAYOUT; + } else { + writel(7, msp->base + TX_BW_RATE(0)); + if (readl(msp->base + TX_BW_RATE(0)) & 7) + msp->tx_bw_control = TX_BW_CONTROL_OLD_LAYOUT; + else + msp->tx_bw_control = TX_BW_CONTROL_ABSENT; + } } static int mv643xx_eth_shared_probe(struct platform_device *pdev) @@ -2202,7 +2342,8 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) int ret; if (!mv643xx_eth_version_printed++) - printk(KERN_NOTICE "MV-643xx 10/100/1000 Ethernet Driver\n"); + printk(KERN_NOTICE "MV-643xx 10/100/1000 ethernet " + "driver version %s\n", mv643xx_eth_driver_version); ret = -EINVAL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -2219,7 +2360,45 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (msp->base == NULL) goto out_free; - spin_lock_init(&msp->phy_lock); + /* + * Set up and register SMI bus. + */ + if (pd == NULL || pd->shared_smi == NULL) { + msp->smi_bus = mdiobus_alloc(); + if (msp->smi_bus == NULL) + goto out_unmap; + + msp->smi_bus->priv = msp; + msp->smi_bus->name = "mv643xx_eth smi"; + msp->smi_bus->read = smi_bus_read; + msp->smi_bus->write = smi_bus_write, + snprintf(msp->smi_bus->id, MII_BUS_ID_SIZE, "%d", pdev->id); + msp->smi_bus->parent = &pdev->dev; + msp->smi_bus->phy_mask = 0xffffffff; + if (mdiobus_register(msp->smi_bus) < 0) + goto out_free_mii_bus; + msp->smi = msp; + } else { + msp->smi = platform_get_drvdata(pd->shared_smi); + } + + msp->err_interrupt = NO_IRQ; + init_waitqueue_head(&msp->smi_busy_wait); + + /* + * Check whether the error interrupt is hooked up. + */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res != NULL) { + int err; + + err = request_irq(res->start, mv643xx_eth_err_irq, + IRQF_SHARED, "mv643xx_eth", msp); + if (!err) { + writel(ERR_INT_SMI_DONE, msp->base + ERR_INT_MASK); + msp->err_interrupt = res->start; + } + } /* * (Re-)program MBUS remapping windows if we are asked to. @@ -2237,6 +2416,10 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) return 0; +out_free_mii_bus: + mdiobus_free(msp->smi_bus); +out_unmap: + iounmap(msp->base); out_free: kfree(msp); out: @@ -2246,7 +2429,14 @@ out: static int mv643xx_eth_shared_remove(struct platform_device *pdev) { struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); + struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data; + if (pd == NULL || pd->shared_smi == NULL) { + mdiobus_free(msp->smi_bus); + mdiobus_unregister(msp->smi_bus); + } + if (msp->err_interrupt != NO_IRQ) + free_irq(msp->err_interrupt, msp); iounmap(msp->base); kfree(msp); @@ -2292,33 +2482,13 @@ static void set_params(struct mv643xx_eth_private *mp, else uc_addr_get(mp, dev->dev_addr); - if (pd->phy_addr == -1) { - mp->shared_smi = NULL; - mp->phy_addr = -1; - } else { - mp->shared_smi = mp->shared; - if (pd->shared_smi != NULL) - mp->shared_smi = platform_get_drvdata(pd->shared_smi); - - if (pd->force_phy_addr || pd->phy_addr) { - mp->phy_addr = pd->phy_addr & 0x3f; - phy_addr_set(mp, mp->phy_addr); - } else { - mp->phy_addr = phy_addr_get(mp); - } - } - mp->default_rx_ring_size = DEFAULT_RX_QUEUE_SIZE; if (pd->rx_queue_size) mp->default_rx_ring_size = pd->rx_queue_size; mp->rx_desc_sram_addr = pd->rx_sram_addr; mp->rx_desc_sram_size = pd->rx_sram_size; - if (pd->rx_queue_mask) - mp->rxq_mask = pd->rx_queue_mask; - else - mp->rxq_mask = 0x01; - mp->rxq_primary = fls(mp->rxq_mask) - 1; + mp->rxq_count = pd->rx_queue_count ? : 1; mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; if (pd->tx_queue_size) @@ -2326,77 +2496,91 @@ static void set_params(struct mv643xx_eth_private *mp, mp->tx_desc_sram_addr = pd->tx_sram_addr; mp->tx_desc_sram_size = pd->tx_sram_size; - if (pd->tx_queue_mask) - mp->txq_mask = pd->tx_queue_mask; - else - mp->txq_mask = 0x01; - mp->txq_primary = fls(mp->txq_mask) - 1; + mp->txq_count = pd->tx_queue_count ? : 1; } -static int phy_detect(struct mv643xx_eth_private *mp) +static struct phy_device *phy_scan(struct mv643xx_eth_private *mp, + int phy_addr) { - unsigned int data; - unsigned int data2; + struct mii_bus *bus = mp->shared->smi->smi_bus; + struct phy_device *phydev; + int start; + int num; + int i; + + if (phy_addr == MV643XX_ETH_PHY_ADDR_DEFAULT) { + start = phy_addr_get(mp) & 0x1f; + num = 32; + } else { + start = phy_addr & 0x1f; + num = 1; + } - smi_reg_read(mp, mp->phy_addr, 0, &data); - smi_reg_write(mp, mp->phy_addr, 0, data ^ 0x1000); + phydev = NULL; + for (i = 0; i < num; i++) { + int addr = (start + i) & 0x1f; - smi_reg_read(mp, mp->phy_addr, 0, &data2); - if (((data ^ data2) & 0x1000) == 0) - return -ENODEV; + if (bus->phy_map[addr] == NULL) + mdiobus_scan(bus, addr); - smi_reg_write(mp, mp->phy_addr, 0, data); + if (phydev == NULL) { + phydev = bus->phy_map[addr]; + if (phydev != NULL) + phy_addr_set(mp, addr); + } + } - return 0; + return phydev; } -static int phy_init(struct mv643xx_eth_private *mp, - struct mv643xx_eth_platform_data *pd) +static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) { - struct ethtool_cmd cmd; - int err; + struct phy_device *phy = mp->phy; - err = phy_detect(mp); - if (err) { - dev_printk(KERN_INFO, &mp->dev->dev, - "no PHY detected at addr %d\n", mp->phy_addr); - return err; - } phy_reset(mp); - mp->mii.phy_id = mp->phy_addr; - mp->mii.phy_id_mask = 0x3f; - mp->mii.reg_num_mask = 0x1f; - mp->mii.dev = mp->dev; - mp->mii.mdio_read = mv643xx_eth_mdio_read; - mp->mii.mdio_write = mv643xx_eth_mdio_write; - - mp->mii.supports_gmii = mii_check_gmii_support(&mp->mii); - - memset(&cmd, 0, sizeof(cmd)); - - cmd.port = PORT_MII; - cmd.transceiver = XCVR_INTERNAL; - cmd.phy_address = mp->phy_addr; - if (pd->speed == 0) { - cmd.autoneg = AUTONEG_ENABLE; - cmd.speed = SPEED_100; - cmd.advertising = ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full; - if (mp->mii.supports_gmii) - cmd.advertising |= ADVERTISED_1000baseT_Full; + phy_attach(mp->dev, phy->dev.bus_id, 0, PHY_INTERFACE_MODE_GMII); + + if (speed == 0) { + phy->autoneg = AUTONEG_ENABLE; + phy->speed = 0; + phy->duplex = 0; + phy->advertising = phy->supported | ADVERTISED_Autoneg; } else { - cmd.autoneg = AUTONEG_DISABLE; - cmd.speed = pd->speed; - cmd.duplex = pd->duplex; + phy->autoneg = AUTONEG_DISABLE; + phy->advertising = 0; + phy->speed = speed; + phy->duplex = duplex; } + phy_start_aneg(phy); +} - update_pscr(mp, cmd.speed, cmd.duplex); - mv643xx_eth_set_settings(mp->dev, &cmd); +static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) +{ + u32 pscr; - return 0; + pscr = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + if (pscr & SERIAL_PORT_ENABLE) { + pscr &= ~SERIAL_PORT_ENABLE; + wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); + } + + pscr = MAX_RX_PACKET_9700BYTE | SERIAL_PORT_CONTROL_RESERVED; + if (mp->phy == NULL) { + pscr |= DISABLE_AUTO_NEG_SPEED_GMII; + if (speed == SPEED_1000) + pscr |= SET_GMII_SPEED_TO_1000; + else if (speed == SPEED_100) + pscr |= SET_MII_SPEED_TO_100; + + pscr |= DISABLE_AUTO_NEG_FOR_FLOW_CTRL; + + pscr |= DISABLE_AUTO_NEG_FOR_DUPLEX; + if (duplex == DUPLEX_FULL) + pscr |= SET_FULL_DUPLEX_MODE; + } + + wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); } static int mv643xx_eth_probe(struct platform_device *pdev) @@ -2421,7 +2605,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) return -ENODEV; } - dev = alloc_etherdev(sizeof(struct mv643xx_eth_private)); + dev = alloc_etherdev_mq(sizeof(struct mv643xx_eth_private), 8); if (!dev) return -ENOMEM; @@ -2432,32 +2616,47 @@ static int mv643xx_eth_probe(struct platform_device *pdev) mp->port_num = pd->port_number; mp->dev = dev; -#ifdef MV643XX_ETH_NAPI - netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 64); -#endif set_params(mp, pd); + dev->real_num_tx_queues = mp->txq_count; - spin_lock_init(&mp->lock); - - mib_counters_clear(mp); - INIT_WORK(&mp->tx_timeout_task, tx_timeout_task); - - if (mp->phy_addr != -1) { - err = phy_init(mp, pd); - if (err) - goto out; + if (pd->phy_addr != MV643XX_ETH_PHY_NONE) + mp->phy = phy_scan(mp, pd->phy_addr); + if (mp->phy != NULL) { + phy_init(mp, pd->speed, pd->duplex); SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); } else { SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops_phyless); } + init_pscr(mp, pd->speed, pd->duplex); + + + mib_counters_clear(mp); + + init_timer(&mp->mib_counters_timer); + mp->mib_counters_timer.data = (unsigned long)mp; + mp->mib_counters_timer.function = mib_counters_timer_wrapper; + mp->mib_counters_timer.expires = jiffies + 30 * HZ; + add_timer(&mp->mib_counters_timer); + + spin_lock_init(&mp->mib_counters_lock); + + INIT_WORK(&mp->tx_timeout_task, tx_timeout_task); + + netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 128); + + init_timer(&mp->rx_oom); + mp->rx_oom.data = (unsigned long)mp; + mp->rx_oom.function = oom_timer_wrapper; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); BUG_ON(!res); dev->irq = res->start; + dev->get_stats = mv643xx_eth_get_stats; dev->hard_start_xmit = mv643xx_eth_xmit; dev->open = mv643xx_eth_open; dev->stop = mv643xx_eth_stop; @@ -2472,13 +2671,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev) dev->watchdog_timeo = 2 * HZ; dev->base_addr = 0; -#ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX - /* - * Zero copy can only work if we use Discovery II memory. Else, we will - * have to map the buffers to ISA memory which is only 16 MB - */ dev->features = NETIF_F_SG | NETIF_F_IP_CSUM; -#endif + dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM; SET_NETDEV_DEV(dev, &pdev->dev); @@ -2492,16 +2686,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev) dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %s\n", mp->port_num, print_mac(mac, dev->dev_addr)); - if (dev->features & NETIF_F_SG) - dev_printk(KERN_NOTICE, &dev->dev, "scatter/gather enabled\n"); - - if (dev->features & NETIF_F_IP_CSUM) - dev_printk(KERN_NOTICE, &dev->dev, "tx checksum offload\n"); - -#ifdef MV643XX_ETH_NAPI - dev_printk(KERN_NOTICE, &dev->dev, "napi enabled\n"); -#endif - if (mp->tx_desc_sram_size > 0) dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n"); @@ -2518,6 +2702,8 @@ static int mv643xx_eth_remove(struct platform_device *pdev) struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); unregister_netdev(mp->dev); + if (mp->phy != NULL) + phy_detach(mp->phy); flush_scheduled_work(); free_netdev(mp->dev); |