X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fmv643xx_eth.c;h=b62e61d4ca3e82d40500d73b6b762b12264d7600;hb=37c4fd8c7551c7419a5faf87a76ff6949c5adaf3;hp=d0ecc440aac2d3801d640c0ba09eef55311d2bc7;hpb=4ff3495a51c7226376d8013c5742d1d5e54876a7;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index d0ecc44..b62e61d 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -48,14 +49,16 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include +#include #include +#include static char mv643xx_eth_driver_name[] = "mv643xx_eth"; -static char mv643xx_eth_driver_version[] = "1.3"; +static char mv643xx_eth_driver_version[] = "1.4"; /* @@ -77,16 +80,34 @@ static char mv643xx_eth_driver_version[] = "1.3"; #define WINDOW_PROTECT(w) (0x0294 + ((w) << 4)) /* - * Per-port registers. + * Main per-port registers. These live at offset 0x0400 for + * port #0, 0x0800 for port #1, and 0x0c00 for port #2. */ -#define PORT_CONFIG(p) (0x0400 + ((p) << 10)) +#define PORT_CONFIG 0x0000 #define UNICAST_PROMISCUOUS_MODE 0x00000001 -#define PORT_CONFIG_EXT(p) (0x0404 + ((p) << 10)) -#define MAC_ADDR_LOW(p) (0x0414 + ((p) << 10)) -#define MAC_ADDR_HIGH(p) (0x0418 + ((p) << 10)) -#define SDMA_CONFIG(p) (0x041c + ((p) << 10)) -#define PORT_SERIAL_CONTROL(p) (0x043c + ((p) << 10)) -#define PORT_STATUS(p) (0x0444 + ((p) << 10)) +#define PORT_CONFIG_EXT 0x0004 +#define MAC_ADDR_LOW 0x0014 +#define MAC_ADDR_HIGH 0x0018 +#define SDMA_CONFIG 0x001c +#define TX_BURST_SIZE_16_64BIT 0x01000000 +#define TX_BURST_SIZE_4_64BIT 0x00800000 +#define BLM_TX_NO_SWAP 0x00000020 +#define BLM_RX_NO_SWAP 0x00000010 +#define RX_BURST_SIZE_16_64BIT 0x00000008 +#define RX_BURST_SIZE_4_64BIT 0x00000004 +#define PORT_SERIAL_CONTROL 0x003c +#define SET_MII_SPEED_TO_100 0x01000000 +#define SET_GMII_SPEED_TO_1000 0x00800000 +#define SET_FULL_DUPLEX_MODE 0x00200000 +#define MAX_RX_PACKET_9700BYTE 0x000a0000 +#define DISABLE_AUTO_NEG_SPEED_GMII 0x00002000 +#define DO_NOT_FORCE_LINK_FAIL 0x00000400 +#define SERIAL_PORT_CONTROL_RESERVED 0x00000200 +#define DISABLE_AUTO_NEG_FOR_FLOW_CTRL 0x00000008 +#define DISABLE_AUTO_NEG_FOR_DUPLEX 0x00000004 +#define FORCE_LINK_PASS 0x00000002 +#define SERIAL_PORT_ENABLE 0x00000001 +#define PORT_STATUS 0x0044 #define TX_FIFO_EMPTY 0x00000400 #define TX_IN_PROGRESS 0x00000080 #define PORT_SPEED_MASK 0x00000030 @@ -96,31 +117,37 @@ static char mv643xx_eth_driver_version[] = "1.3"; #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)) -#define TX_BW_MTU(p) (0x0458 + ((p) << 10)) -#define TX_BW_BURST(p) (0x045c + ((p) << 10)) -#define INT_CAUSE(p) (0x0460 + ((p) << 10)) +#define TXQ_COMMAND 0x0048 +#define TXQ_FIX_PRIO_CONF 0x004c +#define TX_BW_RATE 0x0050 +#define TX_BW_MTU 0x0058 +#define TX_BW_BURST 0x005c +#define INT_CAUSE 0x0060 #define INT_TX_END 0x07f80000 +#define INT_TX_END_0 0x00080000 #define INT_RX 0x000003fc +#define INT_RX_0 0x00000004 #define INT_EXT 0x00000002 -#define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10)) +#define INT_CAUSE_EXT 0x0064 #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)) -#define TXQ_FIX_PRIO_CONF_MOVED(p) (0x04dc + ((p) << 10)) -#define TX_BW_RATE_MOVED(p) (0x04e0 + ((p) << 10)) -#define TX_BW_MTU_MOVED(p) (0x04e8 + ((p) << 10)) -#define TX_BW_BURST_MOVED(p) (0x04ec + ((p) << 10)) -#define RXQ_CURRENT_DESC_PTR(p, q) (0x060c + ((p) << 10) + ((q) << 4)) -#define RXQ_COMMAND(p) (0x0680 + ((p) << 10)) -#define TXQ_CURRENT_DESC_PTR(p, q) (0x06c0 + ((p) << 10) + ((q) << 2)) -#define TXQ_BW_TOKENS(p, q) (0x0700 + ((p) << 10) + ((q) << 4)) -#define TXQ_BW_CONF(p, q) (0x0704 + ((p) << 10) + ((q) << 4)) -#define TXQ_BW_WRR_CONF(p, q) (0x0708 + ((p) << 10) + ((q) << 4)) +#define INT_MASK 0x0068 +#define INT_MASK_EXT 0x006c +#define TX_FIFO_URGENT_THRESHOLD 0x0074 +#define TXQ_FIX_PRIO_CONF_MOVED 0x00dc +#define TX_BW_RATE_MOVED 0x00e0 +#define TX_BW_MTU_MOVED 0x00e8 +#define TX_BW_BURST_MOVED 0x00ec +#define RXQ_CURRENT_DESC_PTR(q) (0x020c + ((q) << 4)) +#define RXQ_COMMAND 0x0280 +#define TXQ_CURRENT_DESC_PTR(q) (0x02c0 + ((q) << 2)) +#define TXQ_BW_TOKENS(q) (0x0300 + ((q) << 4)) +#define TXQ_BW_CONF(q) (0x0304 + ((q) << 4)) +#define TXQ_BW_WRR_CONF(q) (0x0308 + ((q) << 4)) + +/* + * Misc per-port registers. + */ #define MIB_COUNTERS(p) (0x1000 + ((p) << 7)) #define SPECIAL_MCAST_TABLE(p) (0x1400 + ((p) << 10)) #define OTHER_MCAST_TABLE(p) (0x1500 + ((p) << 10)) @@ -128,45 +155,29 @@ static char mv643xx_eth_driver_version[] = "1.3"; /* - * SDMA configuration register. + * SDMA configuration register default value. */ -#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_16_64BIT (4 << 22) - #if defined(__BIG_ENDIAN) #define PORT_SDMA_CONFIG_DEFAULT_VALUE \ - RX_BURST_SIZE_16_64BIT | \ - TX_BURST_SIZE_16_64BIT + (RX_BURST_SIZE_4_64BIT | \ + TX_BURST_SIZE_4_64BIT) #elif defined(__LITTLE_ENDIAN) #define PORT_SDMA_CONFIG_DEFAULT_VALUE \ - RX_BURST_SIZE_16_64BIT | \ - BLM_RX_NO_SWAP | \ - BLM_TX_NO_SWAP | \ - TX_BURST_SIZE_16_64BIT + (RX_BURST_SIZE_4_64BIT | \ + BLM_RX_NO_SWAP | \ + BLM_TX_NO_SWAP | \ + TX_BURST_SIZE_4_64BIT) #else #error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined #endif /* - * Port serial control register. + * Misc definitions. */ -#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_9700BYTE (5 << 17) -#define DISABLE_AUTO_NEG_SPEED_GMII (1 << 13) -#define DO_NOT_FORCE_LINK_FAIL (1 << 10) -#define SERIAL_PORT_CONTROL_RESERVED (1 << 9) -#define DISABLE_AUTO_NEG_FOR_FLOW_CTRL (1 << 3) -#define DISABLE_AUTO_NEG_FOR_DUPLEX (1 << 2) -#define FORCE_LINK_PASS (1 << 1) -#define SERIAL_PORT_ENABLE (1 << 0) - -#define DEFAULT_RX_QUEUE_SIZE 128 -#define DEFAULT_TX_QUEUE_SIZE 256 +#define DEFAULT_RX_QUEUE_SIZE 128 +#define DEFAULT_TX_QUEUE_SIZE 256 +#define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES) /* @@ -219,6 +230,12 @@ struct tx_desc { #define RX_ENABLE_INTERRUPT 0x20000000 #define RX_FIRST_DESC 0x08000000 #define RX_LAST_DESC 0x04000000 +#define RX_IP_HDR_OK 0x02000000 +#define RX_PKT_IS_IPV4 0x01000000 +#define RX_PKT_IS_ETHERNETV2 0x00800000 +#define RX_PKT_LAYER4_TYPE_MASK 0x00600000 +#define RX_PKT_LAYER4_TYPE_TCP_IPV4 0x00000000 +#define RX_PKT_IS_VLAN_TAGGED 0x00080000 /* TX descriptor command */ #define TX_ENABLE_INTERRUPT 0x00800000 @@ -248,9 +265,9 @@ struct mv643xx_eth_shared_private { struct mv643xx_eth_shared_private *smi; /* - * Protects access to SMI_REG, which is shared between ports. + * Provides access to local SMI interface. */ - struct mutex phy_lock; + struct mii_bus *smi_bus; /* * If we have access to the error interrupt pin (which is @@ -278,6 +295,9 @@ struct mv643xx_eth_shared_private { #define TX_BW_CONTROL_OLD_LAYOUT 1 #define TX_BW_CONTROL_NEW_LAYOUT 2 +static int mv643xx_eth_open(struct net_device *dev); +static int mv643xx_eth_stop(struct net_device *dev); + /* per-port *****************************************************************/ struct mib_counters { @@ -313,6 +333,12 @@ struct mib_counters { u32 late_collision; }; +struct lro_counters { + u32 lro_aggregated; + u32 lro_flushed; + u32 lro_no_desc; +}; + struct rx_queue { int index; @@ -326,6 +352,9 @@ struct rx_queue { dma_addr_t rx_desc_dma; int rx_desc_area_size; struct sk_buff **rx_skb; + + struct net_lro_mgr lro_mgr; + struct net_lro_desc lro_arr[8]; }; struct tx_queue { @@ -350,31 +379,37 @@ struct tx_queue { struct mv643xx_eth_private { struct mv643xx_eth_shared_private *shared; + void __iomem *base; int port_num; struct net_device *dev; - int phy_addr; + struct phy_device *phy; struct timer_list mib_counters_timer; spinlock_t mib_counters_lock; struct mib_counters mib_counters; + struct lro_counters lro_counters; + struct work_struct tx_timeout_task; - struct mii_if_info mii; struct napi_struct napi; + u32 int_mask; + u8 oom; 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. */ - int default_rx_ring_size; + int rx_ring_size; unsigned long rx_desc_sram_addr; int rx_desc_sram_size; int rxq_count; @@ -384,7 +419,7 @@ struct mv643xx_eth_private { /* * TX state. */ - int default_tx_ring_size; + int tx_ring_size; unsigned long tx_desc_sram_addr; int tx_desc_sram_size; int txq_count; @@ -398,11 +433,21 @@ static inline u32 rdl(struct mv643xx_eth_private *mp, int offset) return readl(mp->shared->base + offset); } +static inline u32 rdlp(struct mv643xx_eth_private *mp, int offset) +{ + return readl(mp->base + offset); +} + static inline void wrl(struct mv643xx_eth_private *mp, int offset, u32 data) { writel(data, mp->shared->base + offset); } +static inline void wrlp(struct mv643xx_eth_private *mp, int offset, u32 data) +{ + writel(data, mp->base + offset); +} + /* rxq/txq helper functions *************************************************/ static struct mv643xx_eth_private *rxq_to_mp(struct rx_queue *rxq) @@ -418,7 +463,7 @@ static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) static void rxq_enable(struct rx_queue *rxq) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); - wrl(mp, RXQ_COMMAND(mp->port_num), 1 << rxq->index); + wrlp(mp, RXQ_COMMAND, 1 << rxq->index); } static void rxq_disable(struct rx_queue *rxq) @@ -426,26 +471,25 @@ static void rxq_disable(struct rx_queue *rxq) struct mv643xx_eth_private *mp = rxq_to_mp(rxq); u8 mask = 1 << rxq->index; - wrl(mp, RXQ_COMMAND(mp->port_num), mask << 8); - while (rdl(mp, RXQ_COMMAND(mp->port_num)) & mask) + wrlp(mp, RXQ_COMMAND, mask << 8); + while (rdlp(mp, RXQ_COMMAND) & mask) 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); + wrlp(mp, TXQ_CURRENT_DESC_PTR(txq->index), addr); } static void txq_enable(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); - wrl(mp, TXQ_COMMAND(mp->port_num), 1 << txq->index); + wrlp(mp, TXQ_COMMAND, 1 << txq->index); } static void txq_disable(struct tx_queue *txq) @@ -453,8 +497,8 @@ static void txq_disable(struct tx_queue *txq) struct mv643xx_eth_private *mp = txq_to_mp(txq); u8 mask = 1 << txq->index; - wrl(mp, TXQ_COMMAND(mp->port_num), mask << 8); - while (rdl(mp, TXQ_COMMAND(mp->port_num)) & mask) + wrlp(mp, TXQ_COMMAND, mask << 8); + while (rdlp(mp, TXQ_COMMAND) & mask) udelay(10); } @@ -473,12 +517,40 @@ static void txq_maybe_wake(struct tx_queue *txq) /* rx napi ******************************************************************/ +static int +mv643xx_get_skb_header(struct sk_buff *skb, void **iphdr, void **tcph, + u64 *hdr_flags, void *priv) +{ + unsigned long cmd_sts = (unsigned long)priv; + + /* + * Make sure that this packet is Ethernet II, is not VLAN + * tagged, is IPv4, has a valid IP header, and is TCP. + */ + if ((cmd_sts & (RX_IP_HDR_OK | RX_PKT_IS_IPV4 | + RX_PKT_IS_ETHERNETV2 | RX_PKT_LAYER4_TYPE_MASK | + RX_PKT_IS_VLAN_TAGGED)) != + (RX_IP_HDR_OK | RX_PKT_IS_IPV4 | + RX_PKT_IS_ETHERNETV2 | RX_PKT_LAYER4_TYPE_TCP_IPV4)) + return -1; + + skb_reset_network_header(skb); + skb_set_transport_header(skb, ip_hdrlen(skb)); + *iphdr = ip_hdr(skb); + *tcph = tcp_hdr(skb); + *hdr_flags = LRO_IPV4 | LRO_TCP; + + return 0; +} + static int rxq_process(struct rx_queue *rxq, int budget) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); struct net_device_stats *stats = &mp->dev->stats; + int lro_flush_needed; int rx; + lro_flush_needed = 0; rx = 0; while (rx < budget && rxq->rx_desc_count) { struct rx_desc *rx_desc; @@ -500,7 +572,7 @@ static int rxq_process(struct rx_queue *rxq, int budget) if (rxq->rx_curr_desc == rxq->rx_ring_size) rxq->rx_curr_desc = 0; - dma_unmap_single(NULL, rx_desc->buf_ptr, + dma_unmap_single(mp->dev->dev.parent, rx_desc->buf_ptr, rx_desc->buf_size, DMA_FROM_DEVICE); rxq->rx_desc_count--; rx++; @@ -525,39 +597,49 @@ static int rxq_process(struct rx_queue *rxq, int budget) * on, or the error summary bit is set, the packet needs * to be dropped. */ - if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != - (RX_FIRST_DESC | RX_LAST_DESC)) - || (cmd_sts & ERROR_SUMMARY)) { - stats->rx_dropped++; - - if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != - (RX_FIRST_DESC | RX_LAST_DESC)) { - if (net_ratelimit()) - dev_printk(KERN_ERR, &mp->dev->dev, - "received packet spanning " - "multiple descriptors\n"); - } + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC | ERROR_SUMMARY)) + != (RX_FIRST_DESC | RX_LAST_DESC)) + goto err; + + /* + * The -4 is for the CRC in the trailer of the + * received packet + */ + skb_put(skb, byte_cnt - 2 - 4); - if (cmd_sts & ERROR_SUMMARY) - stats->rx_errors++; + if (cmd_sts & LAYER_4_CHECKSUM_OK) + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->protocol = eth_type_trans(skb, mp->dev); - dev_kfree_skb(skb); - } else { - /* - * The -4 is for the CRC in the trailer of the - * received packet - */ - skb_put(skb, byte_cnt - 2 - 4); - - if (cmd_sts & LAYER_4_CHECKSUM_OK) - skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->protocol = eth_type_trans(skb, mp->dev); + if (skb->dev->features & NETIF_F_LRO && + skb->ip_summed == CHECKSUM_UNNECESSARY) { + lro_receive_skb(&rxq->lro_mgr, skb, (void *)cmd_sts); + lro_flush_needed = 1; + } else netif_receive_skb(skb); + + continue; + +err: + stats->rx_dropped++; + + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != + (RX_FIRST_DESC | RX_LAST_DESC)) { + if (net_ratelimit()) + dev_printk(KERN_ERR, &mp->dev->dev, + "received packet spanning " + "multiple descriptors\n"); } - mp->dev->last_rx = jiffies; + if (cmd_sts & ERROR_SUMMARY) + stats->rx_errors++; + + dev_kfree_skb(skb); } + if (lro_flush_needed) + lro_flush_all(&rxq->lro_mgr); + if (rx < budget) mp->work_rx &= ~(1 << rxq->index); @@ -567,39 +649,25 @@ static int rxq_process(struct rx_queue *rxq, int budget) static int rxq_refill(struct rx_queue *rxq, int budget) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); - int skb_size; int refilled; - /* - * 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 = rxq_to_mp(rxq)->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. - */ - skb_size = (skb_size + 7) & ~7; - refilled = 0; while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) { struct sk_buff *skb; - int unaligned; int rx; + struct rx_desc *rx_desc; + + skb = __skb_dequeue(&mp->rx_recycle); + if (skb == NULL) + skb = dev_alloc_skb(mp->skb_size); - skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1); if (skb == NULL) { - mp->work_rx_oom |= 1 << rxq->index; + mp->oom = 1; goto oom; } - unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1); - if (unaligned) - skb_reserve(skb, dma_get_cache_alignment() - unaligned); + if (SKB_DMA_REALIGN) + skb_reserve(skb, SKB_DMA_REALIGN); refilled++; rxq->rx_desc_count++; @@ -608,13 +676,15 @@ static int rxq_refill(struct rx_queue *rxq, int budget) 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, - skb_size, DMA_FROM_DEVICE); - rxq->rx_desc_area[rx].buf_size = skb_size; + rx_desc = rxq->rx_desc_area + rx; + + rx_desc->buf_ptr = dma_map_single(mp->dev->dev.parent, + skb->data, mp->skb_size, + DMA_FROM_DEVICE); + rx_desc->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; + rx_desc->cmd_sts = BUFFER_OWNED_BY_DMA | RX_ENABLE_INTERRUPT; wmb(); /* @@ -647,23 +717,9 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb) return 0; } -static int txq_alloc_desc_index(struct tx_queue *txq) -{ - int tx_desc_curr; - - BUG_ON(txq->tx_desc_count >= 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); - - return tx_desc_curr; -} - static void txq_submit_frag_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 frag; @@ -673,7 +729,9 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) struct tx_desc *desc; this_frag = &skb_shinfo(skb)->frags[frag]; - tx_index = txq_alloc_desc_index(txq); + tx_index = txq->tx_curr_desc++; + if (txq->tx_curr_desc == txq->tx_ring_size) + txq->tx_curr_desc = 0; desc = &txq->tx_desc_area[tx_index]; /* @@ -690,10 +748,10 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) desc->l4i_chk = 0; desc->byte_cnt = this_frag->size; - desc->buf_ptr = dma_map_page(NULL, this_frag->page, - this_frag->page_offset, - this_frag->size, - DMA_TO_DEVICE); + desc->buf_ptr = dma_map_page(mp->dev->dev.parent, + this_frag->page, + this_frag->page_offset, + this_frag->size, DMA_TO_DEVICE); } } @@ -755,7 +813,9 @@ no_csum: cmd_sts |= 5 << TX_IHL_SHIFT; } - tx_index = txq_alloc_desc_index(txq); + tx_index = txq->tx_curr_desc++; + if (txq->tx_curr_desc == txq->tx_ring_size) + txq->tx_curr_desc = 0; desc = &txq->tx_desc_area[tx_index]; if (nr_frags) { @@ -768,7 +828,8 @@ no_csum: desc->l4i_chk = l4i_chk; desc->byte_cnt = length; - desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE); + desc->buf_ptr = dma_map_single(mp->dev->dev.parent, skb->data, + length, DMA_TO_DEVICE); __skb_queue_tail(&txq->tx_skb, skb); @@ -840,10 +901,10 @@ static void txq_kick(struct tx_queue *txq) __netif_tx_lock(nq, smp_processor_id()); - if (rdl(mp, TXQ_COMMAND(mp->port_num)) & (1 << txq->index)) + if (rdlp(mp, TXQ_COMMAND) & (1 << txq->index)) goto out; - hw_desc_ptr = rdl(mp, TXQ_CURRENT_DESC_PTR(mp->port_num, txq->index)); + hw_desc_ptr = rdlp(mp, TXQ_CURRENT_DESC_PTR(txq->index)); expected_ptr = (u32)txq->tx_desc_dma + txq->tx_curr_desc * sizeof(struct tx_desc); @@ -898,15 +959,21 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) } if (cmd_sts & TX_FIRST_DESC) { - dma_unmap_single(NULL, desc->buf_ptr, + dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr, desc->byte_cnt, DMA_TO_DEVICE); } else { - dma_unmap_page(NULL, desc->buf_ptr, + dma_unmap_page(mp->dev->dev.parent, desc->buf_ptr, desc->byte_cnt, DMA_TO_DEVICE); } - if (skb) - dev_kfree_skb(skb); + if (skb != NULL) { + if (skb_queue_len(&mp->rx_recycle) < + mp->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); @@ -943,14 +1010,14 @@ static void tx_set_rate(struct mv643xx_eth_private *mp, int rate, int burst) 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); + wrlp(mp, TX_BW_RATE, token_rate); + wrlp(mp, TX_BW_MTU, mtu); + wrlp(mp, TX_BW_BURST, 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); + wrlp(mp, TX_BW_RATE_MOVED, token_rate); + wrlp(mp, TX_BW_MTU_MOVED, mtu); + wrlp(mp, TX_BW_BURST_MOVED, bucket_size); break; } } @@ -969,9 +1036,8 @@ static void txq_set_rate(struct tx_queue *txq, int rate, int burst) if (bucket_size > 65535) bucket_size = 65535; - wrl(mp, TXQ_BW_TOKENS(mp->port_num, txq->index), token_rate << 14); - wrl(mp, TXQ_BW_CONF(mp->port_num, txq->index), - (bucket_size << 10) | token_rate); + wrlp(mp, TXQ_BW_TOKENS(txq->index), token_rate << 14); + wrlp(mp, TXQ_BW_CONF(txq->index), (bucket_size << 10) | token_rate); } static void txq_set_fixed_prio_mode(struct tx_queue *txq) @@ -986,52 +1052,17 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq) off = 0; switch (mp->shared->tx_bw_control) { case TX_BW_CONTROL_OLD_LAYOUT: - off = TXQ_FIX_PRIO_CONF(mp->port_num); + off = TXQ_FIX_PRIO_CONF; break; case TX_BW_CONTROL_NEW_LAYOUT: - off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num); + off = TXQ_FIX_PRIO_CONF_MOVED; break; } if (off) { - val = rdl(mp, off); + val = rdlp(mp, off); val |= 1 << txq->index; - wrl(mp, off, val); - } -} - -static void txq_set_wrr(struct tx_queue *txq, int weight) -{ - struct mv643xx_eth_private *mp = txq_to_mp(txq); - int off; - u32 val; - - /* - * Turn off fixed priority mode. - */ - 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; - } - - 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); - - val = rdl(mp, off); - val = (val & ~0xff) | (weight & 0xff); - wrl(mp, off, val); + wrlp(mp, off, val); } } @@ -1069,69 +1100,60 @@ static int smi_wait_ready(struct mv643xx_eth_shared_private *msp) return 0; } - if (!wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp), - msecs_to_jiffies(100))) - return -ETIMEDOUT; + if (!smi_is_done(msp)) { + wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp), + msecs_to_jiffies(100)); + if (!smi_is_done(msp)) + return -ETIMEDOUT; + } return 0; } -static int smi_reg_read(struct mv643xx_eth_private *mp, - unsigned int addr, unsigned int reg) +static int smi_bus_read(struct mii_bus *bus, int addr, int reg) { - struct mv643xx_eth_shared_private *msp = mp->shared->smi; + struct mv643xx_eth_shared_private *msp = bus->priv; void __iomem *smi_reg = msp->base + SMI_REG; int ret; - mutex_lock(&msp->phy_lock); - if (smi_wait_ready(msp)) { - printk("%s: SMI bus busy timeout\n", mp->dev->name); - ret = -ETIMEDOUT; - goto out; + printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); + return -ETIMEDOUT; } writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg); if (smi_wait_ready(msp)) { - printk("%s: SMI bus busy timeout\n", mp->dev->name); - ret = -ETIMEDOUT; - goto out; + printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); + return -ETIMEDOUT; } ret = readl(smi_reg); if (!(ret & SMI_READ_VALID)) { - printk("%s: SMI bus read not valid\n", mp->dev->name); - ret = -ENODEV; - goto out; + printk(KERN_WARNING "mv643xx_eth: SMI bus read not valid\n"); + return -ENODEV; } - ret &= 0xffff; - -out: - mutex_unlock(&msp->phy_lock); - - return ret; + return ret & 0xffff; } -static int 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) { - struct mv643xx_eth_shared_private *msp = mp->shared->smi; + struct mv643xx_eth_shared_private *msp = bus->priv; void __iomem *smi_reg = msp->base + SMI_REG; - mutex_lock(&msp->phy_lock); - if (smi_wait_ready(msp)) { - printk("%s: SMI bus busy timeout\n", mp->dev->name); - mutex_unlock(&msp->phy_lock); + printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); return -ETIMEDOUT; } writel(SMI_OPCODE_WRITE | (reg << 21) | - (addr << 16) | (value & 0xffff), smi_reg); + (addr << 16) | (val & 0xffff), smi_reg); - mutex_unlock(&msp->phy_lock); + if (smi_wait_ready(msp)) { + printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); + return -ETIMEDOUT; + } return 0; } @@ -1162,6 +1184,26 @@ static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev) return stats; } +static void mv643xx_eth_grab_lro_stats(struct mv643xx_eth_private *mp) +{ + u32 lro_aggregated = 0; + u32 lro_flushed = 0; + u32 lro_no_desc = 0; + int i; + + for (i = 0; i < mp->rxq_count; i++) { + struct rx_queue *rxq = mp->rxq + i; + + lro_aggregated += rxq->lro_mgr.stats.aggregated; + lro_flushed += rxq->lro_mgr.stats.flushed; + lro_no_desc += rxq->lro_mgr.stats.no_desc; + } + + mp->lro_counters.lro_aggregated = lro_aggregated; + mp->lro_counters.lro_flushed = lro_flushed; + mp->lro_counters.lro_no_desc = lro_no_desc; +} + static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset) { return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); @@ -1179,9 +1221,8 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) { struct mib_counters *p = &mp->mib_counters; - spin_lock(&mp->mib_counters_lock); + spin_lock_bh(&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); p->internal_mac_transmit_err += mib_read(mp, 0x0c); p->good_frames_received += mib_read(mp, 0x10); @@ -1195,7 +1236,6 @@ static void mib_counters_update(struct mv643xx_eth_private *mp) p->frames_512_to_1023_octets += mib_read(mp, 0x30); p->frames_1024_to_max_octets += mib_read(mp, 0x34); p->good_octets_sent += mib_read(mp, 0x38); - p->good_octets_sent += (u64)mib_read(mp, 0x3c) << 32; p->good_frames_sent += mib_read(mp, 0x40); p->excessive_collision += mib_read(mp, 0x44); p->multicast_frames_sent += mib_read(mp, 0x48); @@ -1212,7 +1252,7 @@ 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); + spin_unlock_bh(&mp->mib_counters_lock); mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ); } @@ -1225,6 +1265,85 @@ static void mib_counters_timer_wrapper(unsigned long _mp) } +/* interrupt coalescing *****************************************************/ +/* + * Hardware coalescing parameters are set in units of 64 t_clk + * cycles. I.e.: + * + * coal_delay_in_usec = 64000000 * register_value / t_clk_rate + * + * register_value = coal_delay_in_usec * t_clk_rate / 64000000 + * + * In the ->set*() methods, we round the computed register value + * to the nearest integer. + */ +static unsigned int get_rx_coal(struct mv643xx_eth_private *mp) +{ + u32 val = rdlp(mp, SDMA_CONFIG); + u64 temp; + + if (mp->shared->extended_rx_coal_limit) + temp = ((val & 0x02000000) >> 10) | ((val & 0x003fff80) >> 7); + else + temp = (val & 0x003fff00) >> 8; + + temp *= 64000000; + do_div(temp, mp->shared->t_clk); + + return (unsigned int)temp; +} + +static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int usec) +{ + u64 temp; + u32 val; + + temp = (u64)usec * mp->shared->t_clk; + temp += 31999999; + do_div(temp, 64000000); + + val = rdlp(mp, SDMA_CONFIG); + if (mp->shared->extended_rx_coal_limit) { + if (temp > 0xffff) + temp = 0xffff; + val &= ~0x023fff80; + val |= (temp & 0x8000) << 10; + val |= (temp & 0x7fff) << 7; + } else { + if (temp > 0x3fff) + temp = 0x3fff; + val &= ~0x003fff00; + val |= (temp & 0x3fff) << 8; + } + wrlp(mp, SDMA_CONFIG, val); +} + +static unsigned int get_tx_coal(struct mv643xx_eth_private *mp) +{ + u64 temp; + + temp = (rdlp(mp, TX_FIFO_URGENT_THRESHOLD) & 0x3fff0) >> 4; + temp *= 64000000; + do_div(temp, mp->shared->t_clk); + + return (unsigned int)temp; +} + +static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int usec) +{ + u64 temp; + + temp = (u64)usec * mp->shared->t_clk; + temp += 31999999; + do_div(temp, 64000000); + + if (temp > 0x3fff) + temp = 0x3fff; + + wrlp(mp, TX_FIFO_URGENT_THRESHOLD, temp << 4); +} + + /* ethtool ******************************************************************/ struct mv643xx_eth_stats { char stat_string[ETH_GSTRING_LEN]; @@ -1241,6 +1360,10 @@ struct mv643xx_eth_stats { { #m, FIELD_SIZEOF(struct mib_counters, m), \ -1, offsetof(struct mv643xx_eth_private, mib_counters.m) } +#define LROSTAT(m) \ + { #m, FIELD_SIZEOF(struct lro_counters, m), \ + -1, offsetof(struct mv643xx_eth_private, lro_counters.m) } + static const struct mv643xx_eth_stats mv643xx_eth_stats[] = { SSTAT(rx_packets), SSTAT(tx_packets), @@ -1280,14 +1403,20 @@ static const struct mv643xx_eth_stats mv643xx_eth_stats[] = { MIBSTAT(bad_crc_event), MIBSTAT(collision), MIBSTAT(late_collision), + LROSTAT(lro_aggregated), + LROSTAT(lro_flushed), + LROSTAT(lro_no_desc), }; -static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int +mv643xx_eth_get_settings_phy(struct mv643xx_eth_private *mp, + struct ethtool_cmd *cmd) { - struct mv643xx_eth_private *mp = netdev_priv(dev); int err; - err = mii_ethtool_gset(&mp->mii, cmd); + err = phy_read_status(mp->phy); + if (err == 0) + err = phy_ethtool_gset(mp->phy, cmd); /* * The MAC does not support 1000baseT_Half. @@ -1298,12 +1427,13 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd * return err; } -static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd) +static int +mv643xx_eth_get_settings_phyless(struct mv643xx_eth_private *mp, + struct ethtool_cmd *cmd) { - struct mv643xx_eth_private *mp = netdev_priv(dev); u32 port_status; - port_status = rdl(mp, PORT_STATUS(mp->port_num)); + port_status = rdlp(mp, PORT_STATUS); cmd->supported = SUPPORTED_MII; cmd->advertising = ADVERTISED_MII; @@ -1332,21 +1462,31 @@ static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethto return 0; } -static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int +mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct mv643xx_eth_private *mp = netdev_priv(dev); + + if (mp->phy != NULL) + return mv643xx_eth_get_settings_phy(mp, cmd); + else + return mv643xx_eth_get_settings_phyless(mp, cmd); +} + +static int +mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct mv643xx_eth_private *mp = netdev_priv(dev); + if (mp->phy == NULL) + return -EINVAL; + /* * The MAC does not support 1000baseT_Half. */ cmd->advertising &= ~ADVERTISED_1000baseT_Half; - return mii_ethtool_sset(&mp->mii, cmd); -} - -static int mv643xx_eth_set_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd) -{ - return -EINVAL; + return phy_ethtool_sset(mp->phy, cmd); } static void mv643xx_eth_get_drvinfo(struct net_device *dev, @@ -1363,24 +1503,95 @@ static int mv643xx_eth_nway_reset(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - return mii_nway_restart(&mp->mii); + if (mp->phy == NULL) + return -EINVAL; + + return genphy_restart_aneg(mp->phy); } -static int mv643xx_eth_nway_reset_phyless(struct net_device *dev) +static u32 mv643xx_eth_get_link(struct net_device *dev) { - return -EINVAL; + return !!netif_carrier_ok(dev); } -static u32 mv643xx_eth_get_link(struct net_device *dev) +static int +mv643xx_eth_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) { struct mv643xx_eth_private *mp = netdev_priv(dev); - return mii_link_ok(&mp->mii); + ec->rx_coalesce_usecs = get_rx_coal(mp); + ec->tx_coalesce_usecs = get_tx_coal(mp); + + return 0; } -static u32 mv643xx_eth_get_link_phyless(struct net_device *dev) +static int +mv643xx_eth_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) { - return 1; + struct mv643xx_eth_private *mp = netdev_priv(dev); + + set_rx_coal(mp, ec->rx_coalesce_usecs); + set_tx_coal(mp, ec->tx_coalesce_usecs); + + return 0; +} + +static void +mv643xx_eth_get_ringparam(struct net_device *dev, struct ethtool_ringparam *er) +{ + struct mv643xx_eth_private *mp = netdev_priv(dev); + + er->rx_max_pending = 4096; + er->tx_max_pending = 4096; + er->rx_mini_max_pending = 0; + er->rx_jumbo_max_pending = 0; + + er->rx_pending = mp->rx_ring_size; + er->tx_pending = mp->tx_ring_size; + er->rx_mini_pending = 0; + er->rx_jumbo_pending = 0; +} + +static int +mv643xx_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er) +{ + struct mv643xx_eth_private *mp = netdev_priv(dev); + + if (er->rx_mini_pending || er->rx_jumbo_pending) + return -EINVAL; + + mp->rx_ring_size = er->rx_pending < 4096 ? er->rx_pending : 4096; + mp->tx_ring_size = er->tx_pending < 4096 ? er->tx_pending : 4096; + + if (netif_running(dev)) { + mv643xx_eth_stop(dev); + if (mv643xx_eth_open(dev)) { + dev_printk(KERN_ERR, &dev->dev, + "fatal error on re-opening device after " + "ring param change\n"); + return -ENOMEM; + } + } + + return 0; +} + +static u32 +mv643xx_eth_get_rx_csum(struct net_device *dev) +{ + struct mv643xx_eth_private *mp = netdev_priv(dev); + + return !!(rdlp(mp, PORT_CONFIG) & 0x02000000); +} + +static int +mv643xx_eth_set_rx_csum(struct net_device *dev, u32 rx_csum) +{ + struct mv643xx_eth_private *mp = netdev_priv(dev); + + wrlp(mp, PORT_CONFIG, rx_csum ? 0x02000000 : 0x00000000); + + return 0; } static void mv643xx_eth_get_strings(struct net_device *dev, @@ -1406,6 +1617,7 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *dev, mv643xx_eth_get_stats(dev); mib_counters_update(mp); + mv643xx_eth_grab_lro_stats(mp); for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { const struct mv643xx_eth_stats *stat; @@ -1437,21 +1649,18 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops = { .get_drvinfo = mv643xx_eth_get_drvinfo, .nway_reset = mv643xx_eth_nway_reset, .get_link = mv643xx_eth_get_link, + .get_coalesce = mv643xx_eth_get_coalesce, + .set_coalesce = mv643xx_eth_set_coalesce, + .get_ringparam = mv643xx_eth_get_ringparam, + .set_ringparam = mv643xx_eth_set_ringparam, + .get_rx_csum = mv643xx_eth_get_rx_csum, + .set_rx_csum = mv643xx_eth_set_rx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, .set_sg = ethtool_op_set_sg, .get_strings = mv643xx_eth_get_strings, .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, - .get_sset_count = mv643xx_eth_get_sset_count, -}; - -static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = { - .get_settings = mv643xx_eth_get_settings_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, - .set_sg = ethtool_op_set_sg, - .get_strings = mv643xx_eth_get_strings, - .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, + .get_flags = ethtool_op_get_flags, + .set_flags = ethtool_op_set_flags, .get_sset_count = mv643xx_eth_get_sset_count, }; @@ -1459,11 +1668,8 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = { /* address handling *********************************************************/ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) { - unsigned int mac_h; - unsigned int mac_l; - - mac_h = rdl(mp, MAC_ADDR_HIGH(mp->port_num)); - mac_l = rdl(mp, MAC_ADDR_LOW(mp->port_num)); + unsigned int mac_h = rdlp(mp, MAC_ADDR_HIGH); + unsigned int mac_l = rdlp(mp, MAC_ADDR_LOW); addr[0] = (mac_h >> 24) & 0xff; addr[1] = (mac_h >> 16) & 0xff; @@ -1473,57 +1679,70 @@ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) addr[5] = mac_l & 0xff; } -static void init_mac_tables(struct mv643xx_eth_private *mp) +static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr) { - int i; - - for (i = 0; i < 0x100; i += 4) { - wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0); - wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0); - } - - for (i = 0; i < 0x10; i += 4) - wrl(mp, UNICAST_TABLE(mp->port_num) + i, 0); + wrlp(mp, MAC_ADDR_HIGH, + (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]); + wrlp(mp, MAC_ADDR_LOW, (addr[4] << 8) | addr[5]); } -static void set_filter_table_entry(struct mv643xx_eth_private *mp, - int table, unsigned char entry) +static u32 uc_addr_filter_mask(struct net_device *dev) { - unsigned int table_reg; + struct netdev_hw_addr *ha; + u32 nibbles; - /* Set "accepts frame bit" at specified table entry */ - table_reg = rdl(mp, table + (entry & 0xfc)); - table_reg |= 0x01 << (8 * (entry & 3)); - wrl(mp, table + (entry & 0xfc), table_reg); -} - -static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr) -{ - unsigned int mac_h; - unsigned int mac_l; - int table; + if (dev->flags & IFF_PROMISC) + return 0; - mac_l = (addr[4] << 8) | addr[5]; - mac_h = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; + nibbles = 1 << (dev->dev_addr[5] & 0x0f); + list_for_each_entry(ha, &dev->uc.list, list) { + if (memcmp(dev->dev_addr, ha->addr, 5)) + return 0; + if ((dev->dev_addr[5] ^ ha->addr[5]) & 0xf0) + return 0; - wrl(mp, MAC_ADDR_LOW(mp->port_num), mac_l); - wrl(mp, MAC_ADDR_HIGH(mp->port_num), mac_h); + nibbles |= 1 << (ha->addr[5] & 0x0f); + } - table = UNICAST_TABLE(mp->port_num); - set_filter_table_entry(mp, table, addr[5] & 0x0f); + return nibbles; } -static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) +static void mv643xx_eth_program_unicast_filter(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); + u32 port_config; + u32 nibbles; + int i; - /* +2 is for the offset of the HW addr type */ - memcpy(dev->dev_addr, addr + 2, 6); - - init_mac_tables(mp); uc_addr_set(mp, dev->dev_addr); - return 0; + port_config = rdlp(mp, PORT_CONFIG) & ~UNICAST_PROMISCUOUS_MODE; + + nibbles = uc_addr_filter_mask(dev); + if (!nibbles) { + port_config |= UNICAST_PROMISCUOUS_MODE; + nibbles = 0xffff; + } + + for (i = 0; i < 16; i += 4) { + int off = UNICAST_TABLE(mp->port_num) + i; + u32 v; + + v = 0; + if (nibbles & 1) + v |= 0x00000001; + if (nibbles & 2) + v |= 0x00000100; + if (nibbles & 4) + v |= 0x00010000; + if (nibbles & 8) + v |= 0x01000000; + nibbles >>= 4; + + wrl(mp, off, v); + } + + wrlp(mp, PORT_CONFIG, port_config); } static int addr_crc(unsigned char *addr) @@ -1544,24 +1763,21 @@ static int addr_crc(unsigned char *addr) return crc; } -static void mv643xx_eth_set_rx_mode(struct net_device *dev) +static void mv643xx_eth_program_multicast_filter(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - u32 port_config; + u32 *mc_spec; + u32 *mc_other; struct dev_addr_list *addr; int i; - port_config = rdl(mp, PORT_CONFIG(mp->port_num)); - if (dev->flags & IFF_PROMISC) - port_config |= UNICAST_PROMISCUOUS_MODE; - else - port_config &= ~UNICAST_PROMISCUOUS_MODE; - wrl(mp, PORT_CONFIG(mp->port_num), port_config); - if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { - int port_num = mp->port_num; - u32 accept = 0x01010101; + int port_num; + u32 accept; +oom: + port_num = mp->port_num; + accept = 0x01010101; for (i = 0; i < 0x100; i += 4) { wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept); wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept); @@ -1569,28 +1785,55 @@ static void mv643xx_eth_set_rx_mode(struct net_device *dev) return; } - for (i = 0; i < 0x100; i += 4) { - wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0); - wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0); - } + mc_spec = kmalloc(0x200, GFP_ATOMIC); + if (mc_spec == NULL) + goto oom; + mc_other = mc_spec + (0x100 >> 2); + + memset(mc_spec, 0, 0x100); + memset(mc_other, 0, 0x100); for (addr = dev->mc_list; addr != NULL; addr = addr->next) { u8 *a = addr->da_addr; - int table; - - if (addr->da_addrlen != 6) - continue; + u32 *table; + int entry; if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) { - table = SPECIAL_MCAST_TABLE(mp->port_num); - set_filter_table_entry(mp, table, a[5]); + table = mc_spec; + entry = a[5]; } else { - int crc = addr_crc(a); - - table = OTHER_MCAST_TABLE(mp->port_num); - set_filter_table_entry(mp, table, crc); + table = mc_other; + entry = addr_crc(a); } + + table[entry >> 2] |= 1 << (8 * (entry & 3)); } + + for (i = 0; i < 0x100; i += 4) { + wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, mc_spec[i >> 2]); + wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, mc_other[i >> 2]); + } + + kfree(mc_spec); +} + +static void mv643xx_eth_set_rx_mode(struct net_device *dev) +{ + mv643xx_eth_program_unicast_filter(dev); + mv643xx_eth_program_multicast_filter(dev); +} + +static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); + + netif_addr_lock_bh(dev); + mv643xx_eth_program_unicast_filter(dev); + netif_addr_unlock_bh(dev); + + return 0; } @@ -1604,7 +1847,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) rxq->index = index; - rxq->rx_ring_size = mp->default_rx_ring_size; + rxq->rx_ring_size = mp->rx_ring_size; rxq->rx_desc_count = 0; rxq->rx_curr_desc = 0; @@ -1617,9 +1860,9 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) mp->rx_desc_sram_size); rxq->rx_desc_dma = mp->rx_desc_sram_addr; } else { - rxq->rx_desc_area = dma_alloc_coherent(NULL, size, - &rxq->rx_desc_dma, - GFP_KERNEL); + rxq->rx_desc_area = dma_alloc_coherent(mp->dev->dev.parent, + size, &rxq->rx_desc_dma, + GFP_KERNEL); } if (rxq->rx_desc_area == NULL) { @@ -1650,6 +1893,19 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) nexti * sizeof(struct rx_desc); } + rxq->lro_mgr.dev = mp->dev; + memset(&rxq->lro_mgr.stats, 0, sizeof(rxq->lro_mgr.stats)); + rxq->lro_mgr.features = LRO_F_NAPI; + rxq->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY; + rxq->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY; + rxq->lro_mgr.max_desc = ARRAY_SIZE(rxq->lro_arr); + rxq->lro_mgr.max_aggr = 32; + rxq->lro_mgr.frag_align_pad = 0; + rxq->lro_mgr.lro_arr = rxq->lro_arr; + rxq->lro_mgr.get_skb_header = mv643xx_get_skb_header; + + memset(&rxq->lro_arr, 0, sizeof(rxq->lro_arr)); + return 0; @@ -1657,7 +1913,7 @@ out_free: if (index == 0 && size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else - dma_free_coherent(NULL, size, + dma_free_coherent(mp->dev->dev.parent, size, rxq->rx_desc_area, rxq->rx_desc_dma); @@ -1689,7 +1945,7 @@ static void rxq_deinit(struct rx_queue *rxq) rxq->rx_desc_area_size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else - dma_free_coherent(NULL, rxq->rx_desc_area_size, + dma_free_coherent(mp->dev->dev.parent, rxq->rx_desc_area_size, rxq->rx_desc_area, rxq->rx_desc_dma); kfree(rxq->rx_skb); @@ -1704,7 +1960,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) txq->index = index; - txq->tx_ring_size = mp->default_tx_ring_size; + txq->tx_ring_size = mp->tx_ring_size; txq->tx_desc_count = 0; txq->tx_curr_desc = 0; @@ -1717,9 +1973,9 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) mp->tx_desc_sram_size); txq->tx_desc_dma = mp->tx_desc_sram_addr; } else { - txq->tx_desc_area = dma_alloc_coherent(NULL, size, - &txq->tx_desc_dma, - GFP_KERNEL); + txq->tx_desc_area = dma_alloc_coherent(mp->dev->dev.parent, + size, &txq->tx_desc_dma, + GFP_KERNEL); } if (txq->tx_desc_area == NULL) { @@ -1763,7 +2019,7 @@ static void txq_deinit(struct tx_queue *txq) 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, + dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, txq->tx_desc_area, txq->tx_desc_dma); } @@ -1774,26 +2030,26 @@ static int mv643xx_eth_collect_events(struct mv643xx_eth_private *mp) u32 int_cause; u32 int_cause_ext; - int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & - (INT_TX_END | INT_RX | INT_EXT); + int_cause = rdlp(mp, INT_CAUSE) & mp->int_mask; if (int_cause == 0) return 0; int_cause_ext = 0; - if (int_cause & INT_EXT) - int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num)); + if (int_cause & INT_EXT) { + int_cause &= ~INT_EXT; + int_cause_ext = rdlp(mp, INT_CAUSE_EXT); + } - int_cause &= INT_TX_END | INT_RX; if (int_cause) { - wrl(mp, INT_CAUSE(mp->port_num), ~int_cause); + wrlp(mp, INT_CAUSE, ~int_cause); mp->work_tx_end |= ((int_cause & INT_TX_END) >> 19) & - ~(rdl(mp, TXQ_COMMAND(mp->port_num)) & 0xff); + ~(rdlp(mp, TXQ_COMMAND) & 0xff); mp->work_rx |= (int_cause & INT_RX) >> 2; } 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); + wrlp(mp, INT_CAUSE_EXT, ~int_cause_ext); if (int_cause_ext & INT_EXT_LINK_PHY) mp->work_link = 1; mp->work_tx |= int_cause_ext & INT_EXT_TX; @@ -1810,7 +2066,7 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) if (unlikely(!mv643xx_eth_collect_events(mp))) return IRQ_NONE; - wrl(mp, INT_MASK(mp->port_num), 0); + wrlp(mp, INT_MASK, 0); napi_schedule(&mp->napi); return IRQ_HANDLED; @@ -1824,7 +2080,7 @@ static void handle_link_event(struct mv643xx_eth_private *mp) int duplex; int fc; - port_status = rdl(mp, PORT_STATUS(mp->port_num)); + port_status = rdlp(mp, PORT_STATUS); if (!(port_status & LINK_UP)) { if (netif_carrier_ok(dev)) { int i; @@ -1876,8 +2132,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) mp = container_of(napi, struct mv643xx_eth_private, napi); - mp->work_rx_refill |= mp->work_rx_oom; - mp->work_rx_oom = 0; + if (unlikely(mp->oom)) { + mp->oom = 0; + del_timer(&mp->rx_oom); + } work_done = 0; while (work_done < budget) { @@ -1888,11 +2146,14 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) if (mp->work_link) { mp->work_link = 0; handle_link_event(mp); + work_done++; continue; } - queue_mask = mp->work_tx | mp->work_tx_end | - mp->work_rx | mp->work_rx_refill; + queue_mask = mp->work_tx | mp->work_tx_end | mp->work_rx; + if (likely(!mp->oom)) + queue_mask |= mp->work_rx_refill; + if (!queue_mask) { if (mv643xx_eth_collect_events(mp)) continue; @@ -1913,7 +2174,7 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) 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) { + } else if (!mp->oom && (mp->work_rx_refill & queue_mask)) { work_done += rxq_refill(mp->rxq + queue, work_tbd); } else { BUG(); @@ -1921,10 +2182,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) } if (work_done < budget) { - if (mp->work_rx_oom) + if (mp->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); + wrlp(mp, INT_MASK, mp->int_mask); } return work_done; @@ -1941,16 +2202,16 @@ static void phy_reset(struct mv643xx_eth_private *mp) { int data; - data = smi_reg_read(mp, mp->phy_addr, MII_BMCR); + data = phy_read(mp->phy, MII_BMCR); if (data < 0) return; data |= BMCR_RESET; - if (smi_reg_write(mp, mp->phy_addr, MII_BMCR, data) < 0) + if (phy_write(mp->phy, MII_BMCR, data) < 0) return; do { - data = smi_reg_read(mp, mp->phy_addr, MII_BMCR); + data = phy_read(mp->phy, MII_BMCR); } while (data >= 0 && data & BMCR_RESET); } @@ -1962,7 +2223,7 @@ static void port_start(struct mv643xx_eth_private *mp) /* * 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); @@ -1973,17 +2234,15 @@ static void port_start(struct mv643xx_eth_private *mp) /* * Configure basic link parameters. */ - pscr = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + pscr = rdlp(mp, PORT_SERIAL_CONTROL); pscr |= SERIAL_PORT_ENABLE; - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); + wrlp(mp, PORT_SERIAL_CONTROL, pscr); pscr |= DO_NOT_FORCE_LINK_FAIL; - if (mp->phy_addr == -1) + 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); + wrlp(mp, PORT_SERIAL_CONTROL, pscr); /* * Configure TX path and queues. @@ -1998,66 +2257,63 @@ static void port_start(struct mv643xx_eth_private *mp) } /* - * Add configured unicast address to address filter table. - */ - uc_addr_set(mp, mp->dev->dev_addr); - - /* * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast * frames to RX queue #0, and include the pseudo-header when * calculating receive checksums. */ - wrl(mp, PORT_CONFIG(mp->port_num), 0x02000000); + wrlp(mp, PORT_CONFIG, 0x02000000); /* * Treat BPDUs as normal multicasts, and disable partition mode. */ - wrl(mp, PORT_CONFIG_EXT(mp->port_num), 0x00000000); + wrlp(mp, PORT_CONFIG_EXT, 0x00000000); + + /* + * Add configured unicast addresses to address filter table. + */ + mv643xx_eth_program_unicast_filter(mp->dev); /* * Enable the receive queues. */ 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; addr = (u32)rxq->rx_desc_dma; addr += rxq->rx_curr_desc * sizeof(struct rx_desc); - wrl(mp, off, addr); + wrlp(mp, RXQ_CURRENT_DESC_PTR(i), addr); rxq_enable(rxq); } } -static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int delay) +static void mv643xx_eth_recalc_skb_size(struct mv643xx_eth_private *mp) { - unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64; - u32 val; + int skb_size; - val = rdl(mp, SDMA_CONFIG(mp->port_num)); - if (mp->shared->extended_rx_coal_limit) { - if (coal > 0xffff) - coal = 0xffff; - val &= ~0x023fff80; - val |= (coal & 0x8000) << 10; - val |= (coal & 0x7fff) << 7; - } else { - if (coal > 0x3fff) - coal = 0x3fff; - val &= ~0x003fff00; - val |= (coal & 0x3fff) << 8; - } - wrl(mp, SDMA_CONFIG(mp->port_num), val); -} + /* + * 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; -static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay) -{ - unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64; + /* + * 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; - if (coal > 0x3fff) - coal = 0x3fff; - wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), (coal & 0x3fff) << 4); + /* + * If NET_SKB_PAD is smaller than a cache line, + * netdev_alloc_skb() will cause skb->data to be misaligned + * to a cache line boundary. If this is the case, include + * some extra space to allow re-aligning the data area. + */ + mp->skb_size += SKB_DMA_REALIGN; } static int mv643xx_eth_open(struct net_device *dev) @@ -2066,9 +2322,9 @@ static int mv643xx_eth_open(struct net_device *dev) int err; int i; - wrl(mp, INT_CAUSE(mp->port_num), 0); - wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); - rdl(mp, INT_CAUSE_EXT(mp->port_num)); + wrlp(mp, INT_CAUSE, 0); + wrlp(mp, INT_CAUSE_EXT, 0); + rdlp(mp, INT_CAUSE_EXT); err = request_irq(dev->irq, mv643xx_eth_irq, IRQF_SHARED, dev->name, dev); @@ -2077,10 +2333,14 @@ static int mv643xx_eth_open(struct net_device *dev) return -EAGAIN; } - init_mac_tables(mp); + mv643xx_eth_recalc_skb_size(mp); napi_enable(&mp->napi); + skb_queue_head_init(&mp->rx_recycle); + + mp->int_mask = INT_EXT; + for (i = 0; i < mp->rxq_count; i++) { err = rxq_init(mp, i); if (err) { @@ -2090,9 +2350,10 @@ static int mv643xx_eth_open(struct net_device *dev) } rxq_refill(mp->rxq + i, INT_MAX); + mp->int_mask |= INT_RX_0 << i; } - if (mp->work_rx_oom) { + if (mp->oom) { mp->rx_oom.expires = jiffies + (HZ / 10); add_timer(&mp->rx_oom); } @@ -2104,17 +2365,13 @@ static int mv643xx_eth_open(struct net_device *dev) txq_deinit(mp->txq + i); goto out_free; } + mp->int_mask |= INT_TX_END_0 << i; } - 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_PHY | INT_EXT_TX); - wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); + wrlp(mp, INT_MASK_EXT, INT_EXT_LINK_PHY | INT_EXT_TX); + wrlp(mp, INT_MASK, mp->int_mask); return 0; @@ -2139,7 +2396,7 @@ static void port_reset(struct mv643xx_eth_private *mp) txq_disable(mp->txq + i); while (1) { - u32 ps = rdl(mp, PORT_STATUS(mp->port_num)); + u32 ps = rdlp(mp, PORT_STATUS); if ((ps & (TX_IN_PROGRESS | TX_FIFO_EMPTY)) == TX_FIFO_EMPTY) break; @@ -2147,11 +2404,11 @@ static void port_reset(struct mv643xx_eth_private *mp) } /* Reset the Enable bit in the Configuration Register */ - data = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + data = rdlp(mp, PORT_SERIAL_CONTROL); data &= ~(SERIAL_PORT_ENABLE | DO_NOT_FORCE_LINK_FAIL | FORCE_LINK_PASS); - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), data); + wrlp(mp, PORT_SERIAL_CONTROL, data); } static int mv643xx_eth_stop(struct net_device *dev) @@ -2159,10 +2416,9 @@ static int mv643xx_eth_stop(struct net_device *dev) struct mv643xx_eth_private *mp = netdev_priv(dev); int i; - wrl(mp, INT_MASK(mp->port_num), 0x00000000); - rdl(mp, INT_MASK(mp->port_num)); - - del_timer_sync(&mp->mib_counters_timer); + wrlp(mp, INT_MASK_EXT, 0x00000000); + wrlp(mp, INT_MASK, 0x00000000); + rdlp(mp, INT_MASK); napi_disable(&mp->napi); @@ -2175,6 +2431,9 @@ static int mv643xx_eth_stop(struct net_device *dev) port_reset(mp); mv643xx_eth_get_stats(dev); mib_counters_update(mp); + del_timer_sync(&mp->mib_counters_timer); + + skb_queue_purge(&mp->rx_recycle); for (i = 0; i < mp->rxq_count; i++) rxq_deinit(mp->rxq + i); @@ -2188,8 +2447,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; } @@ -2202,6 +2461,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)) @@ -2250,27 +2510,15 @@ static void mv643xx_eth_netpoll(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - wrl(mp, INT_MASK(mp->port_num), 0x00000000); - rdl(mp, INT_MASK(mp->port_num)); + wrlp(mp, INT_MASK, 0x00000000); + rdlp(mp, INT_MASK); mv643xx_eth_irq(dev->irq, dev); - wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); + wrlp(mp, INT_MASK, mp->int_mask); } #endif -static int mv643xx_eth_mdio_read(struct net_device *dev, int addr, int reg) -{ - struct mv643xx_eth_private *mp = netdev_priv(dev); - return smi_reg_read(mp, addr, reg); -} - -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 @@ -2315,8 +2563,8 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) * [21:8], or a 16-bit coal limit in bits [25,21:7] of the * SDMA config register. */ - writel(0x02000000, msp->base + SDMA_CONFIG(0)); - if (readl(msp->base + SDMA_CONFIG(0)) & 0x02000000) + writel(0x02000000, msp->base + 0x0400 + SDMA_CONFIG); + if (readl(msp->base + 0x0400 + SDMA_CONFIG) & 0x02000000) msp->extended_rx_coal_limit = 1; else msp->extended_rx_coal_limit = 0; @@ -2326,12 +2574,12 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) * 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) { + writel(1, msp->base + 0x0400 + TX_BW_MTU_MOVED); + if (readl(msp->base + 0x0400 + TX_BW_MTU_MOVED) & 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) + writel(7, msp->base + 0x0400 + TX_BW_RATE); + if (readl(msp->base + 0x0400 + TX_BW_RATE) & 7) msp->tx_bw_control = TX_BW_CONTROL_OLD_LAYOUT; else msp->tx_bw_control = TX_BW_CONTROL_ABSENT; @@ -2340,7 +2588,7 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) static int mv643xx_eth_shared_probe(struct platform_device *pdev) { - static int mv643xx_eth_version_printed = 0; + static int mv643xx_eth_version_printed; struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data; struct mv643xx_eth_shared_private *msp; struct resource *res; @@ -2365,11 +2613,27 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (msp->base == NULL) goto out_free; - msp->smi = msp; - if (pd != NULL && pd->shared_smi != NULL) + /* + * 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); - - mutex_init(&msp->phy_lock); + } msp->err_interrupt = NO_IRQ; init_waitqueue_head(&msp->smi_busy_wait); @@ -2405,6 +2669,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: @@ -2414,7 +2682,12 @@ 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_unregister(msp->smi_bus); + mdiobus_free(msp->smi_bus); + } if (msp->err_interrupt != NO_IRQ) free_irq(msp->err_interrupt, msp); iounmap(msp->base); @@ -2462,118 +2735,91 @@ static void set_params(struct mv643xx_eth_private *mp, else uc_addr_get(mp, dev->dev_addr); - if (pd->phy_addr == MV643XX_ETH_PHY_NONE) { - mp->phy_addr = -1; - } else { - if (pd->phy_addr != MV643XX_ETH_PHY_ADDR_DEFAULT) { - 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; + mp->rx_ring_size = DEFAULT_RX_QUEUE_SIZE; if (pd->rx_queue_size) - mp->default_rx_ring_size = pd->rx_queue_size; + mp->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; mp->rxq_count = pd->rx_queue_count ? : 1; - mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; + mp->tx_ring_size = DEFAULT_TX_QUEUE_SIZE; if (pd->tx_queue_size) - mp->default_tx_ring_size = pd->tx_queue_size; + mp->tx_ring_size = pd->tx_queue_size; mp->tx_desc_sram_addr = pd->tx_sram_addr; mp->tx_desc_sram_size = pd->tx_sram_size; 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) { - int data; - int data2; - - data = smi_reg_read(mp, mp->phy_addr, MII_BMCR); - if (data < 0) - return -ENODEV; + struct mii_bus *bus = mp->shared->smi->smi_bus; + struct phy_device *phydev; + int start; + int num; + int i; - if (smi_reg_write(mp, mp->phy_addr, MII_BMCR, data ^ BMCR_ANENABLE) < 0) - return -ENODEV; + if (phy_addr == MV643XX_ETH_PHY_ADDR_DEFAULT) { + start = phy_addr_get(mp) & 0x1f; + num = 32; + } else { + start = phy_addr & 0x1f; + num = 1; + } - data2 = smi_reg_read(mp, mp->phy_addr, MII_BMCR); - if (data2 < 0) - return -ENODEV; + phydev = NULL; + for (i = 0; i < num; i++) { + int addr = (start + i) & 0x1f; - if (((data ^ data2) & BMCR_ANENABLE) == 0) - return -ENODEV; + if (bus->phy_map[addr] == NULL) + mdiobus_scan(bus, addr); - smi_reg_write(mp, mp->phy_addr, MII_BMCR, 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, dev_name(&phy->dev), 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; } - - mv643xx_eth_set_settings(mp->dev, &cmd); - - return 0; + phy_start_aneg(phy); } static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) { u32 pscr; - pscr = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + pscr = rdlp(mp, PORT_SERIAL_CONTROL); if (pscr & SERIAL_PORT_ENABLE) { pscr &= ~SERIAL_PORT_ENABLE; - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); + wrlp(mp, PORT_SERIAL_CONTROL, pscr); } pscr = MAX_RX_PACKET_9700BYTE | SERIAL_PORT_CONTROL_RESERVED; - if (mp->phy_addr == -1) { + if (mp->phy == NULL) { pscr |= DISABLE_AUTO_NEG_SPEED_GMII; if (speed == SPEED_1000) pscr |= SET_GMII_SPEED_TO_1000; @@ -2587,16 +2833,30 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) pscr |= SET_FULL_DUPLEX_MODE; } - wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr); + wrlp(mp, PORT_SERIAL_CONTROL, pscr); } +static const struct net_device_ops mv643xx_eth_netdev_ops = { + .ndo_open = mv643xx_eth_open, + .ndo_stop = mv643xx_eth_stop, + .ndo_start_xmit = mv643xx_eth_xmit, + .ndo_set_rx_mode = mv643xx_eth_set_rx_mode, + .ndo_set_mac_address = mv643xx_eth_set_mac_address, + .ndo_do_ioctl = mv643xx_eth_ioctl, + .ndo_change_mtu = mv643xx_eth_change_mtu, + .ndo_tx_timeout = mv643xx_eth_tx_timeout, + .ndo_get_stats = mv643xx_eth_get_stats, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = mv643xx_eth_netpoll, +#endif +}; + static int mv643xx_eth_probe(struct platform_device *pdev) { struct mv643xx_eth_platform_data *pd; struct mv643xx_eth_private *mp; struct net_device *dev; struct resource *res; - DECLARE_MAC_BUF(mac); int err; pd = pdev->dev.platform_data; @@ -2620,6 +2880,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mp); mp->shared = platform_get_drvdata(pd->shared); + mp->base = mp->shared->base + 0x0400 + (pd->port_number << 10); mp->port_num = pd->port_number; mp->dev = dev; @@ -2627,18 +2888,14 @@ static int mv643xx_eth_probe(struct platform_device *pdev) set_params(mp, pd); dev->real_num_tx_queues = mp->txq_count; - mib_counters_clear(mp); - INIT_WORK(&mp->tx_timeout_task, tx_timeout_task); + if (pd->phy_addr != MV643XX_ETH_PHY_NONE) + mp->phy = phy_scan(mp, pd->phy_addr); - if (mp->phy_addr != -1) { - err = phy_init(mp, pd); - if (err) - goto out; + if (mp->phy != NULL) + phy_init(mp, pd->speed, pd->duplex); + + SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); - 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); @@ -2665,18 +2922,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev) 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; - dev->set_multicast_list = mv643xx_eth_set_rx_mode; - dev->set_mac_address = mv643xx_eth_set_mac_address; - dev->do_ioctl = mv643xx_eth_ioctl; - dev->change_mtu = mv643xx_eth_change_mtu; - dev->tx_timeout = mv643xx_eth_tx_timeout; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = mv643xx_eth_netpoll; -#endif + dev->netdev_ops = &mv643xx_eth_netdev_ops; + dev->watchdog_timeo = 2 * HZ; dev->base_addr = 0; @@ -2688,12 +2935,19 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (mp->shared->win_protect) wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect); + netif_carrier_off(dev); + + wrlp(mp, SDMA_CONFIG, PORT_SDMA_CONFIG_DEFAULT_VALUE); + + set_rx_coal(mp, 250); + set_tx_coal(mp, 0); + err = register_netdev(dev); if (err) goto out; - dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %s\n", - mp->port_num, print_mac(mac, dev->dev_addr)); + dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %pM\n", + mp->port_num, dev->dev_addr); if (mp->tx_desc_sram_size > 0) dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n"); @@ -2711,6 +2965,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); @@ -2724,8 +2980,8 @@ static void mv643xx_eth_shutdown(struct platform_device *pdev) struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); /* Mask all interrupts on ethernet port */ - wrl(mp, INT_MASK(mp->port_num), 0); - rdl(mp, INT_MASK(mp->port_num)); + wrlp(mp, INT_MASK, 0); + rdlp(mp, INT_MASK); if (netif_running(mp->dev)) port_reset(mp);