X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fmv643xx_eth.c;h=a9c8c08044b1847ebe560964433ad3ee4a564322;hb=9301975ec251bab1ad7cfcb84a688b26187e4e4a;hp=e592fac87068985b013287095b29e0fd6d8b0cd0;hpb=befefe2177d4bca07a64a2410333388c063a6f7d;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index e592fac..a9c8c08 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,16 +49,15 @@ #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"; -#define MV643XX_ETH_TX_FAST_REFILL /* * Registers shared between all ports. @@ -103,7 +103,6 @@ static char mv643xx_eth_driver_version[] = "1.3"; #define TX_BW_MTU(p) (0x0458 + ((p) << 10)) #define TX_BW_BURST(p) (0x045c + ((p) << 10)) #define INT_CAUSE(p) (0x0460 + ((p) << 10)) -#define INT_TX_END_0 0x00080000 #define INT_TX_END 0x07f80000 #define INT_RX 0x000003fc #define INT_EXT 0x00000002 @@ -167,8 +166,8 @@ static char mv643xx_eth_driver_version[] = "1.3"; #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 /* @@ -250,9 +249,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 @@ -273,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 { @@ -338,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 { @@ -347,13 +355,24 @@ struct mv643xx_eth_private { struct net_device *dev; - 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. @@ -362,7 +381,6 @@ struct mv643xx_eth_private { unsigned long rx_desc_sram_addr; int rx_desc_sram_size; int rxq_count; - struct napi_struct napi; struct timer_list rx_oom; struct rx_queue rxq[8]; @@ -374,9 +392,6 @@ struct mv643xx_eth_private { int tx_desc_sram_size; int txq_count; struct tx_queue txq[8]; -#ifdef MV643XX_ETH_TX_FAST_REFILL - int tx_clean_threshold; -#endif }; @@ -446,87 +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 != 0); - - if (txq->tx_ring_size - txq->tx_desc_count >= MAX_SKB_FRAGS + 1) - netif_wake_queue(mp->dev); -} - - -/* rx ***********************************************************************/ -static void txq_reclaim(struct tx_queue *txq, int force); - -static int rxq_refill(struct rx_queue *rxq, int budget, int *oom) -{ - 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; - - skb = dev_alloc_skb(skb_size + dma_get_cache_alignment() - 1); - if (skb == NULL) { - *oom = 1; - break; - } - - 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, - 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 (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); } - - return refilled; } + +/* rx napi ******************************************************************/ static int rxq_process(struct rx_queue *rxq, int budget) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); @@ -538,6 +487,7 @@ static int rxq_process(struct rx_queue *rxq, int budget) struct rx_desc *rx_desc; unsigned int cmd_sts; struct sk_buff *skb; + u16 byte_cnt; rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc]; @@ -558,6 +508,10 @@ static int rxq_process(struct rx_queue *rxq, int budget) rxq->rx_desc_count--; rx++; + mp->work_rx_refill |= 1 << rxq->index; + + byte_cnt = rx_desc->byte_cnt; + /* * Update statistics. * @@ -567,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 @@ -596,13 +550,10 @@ static int rxq_process(struct rx_queue *rxq, int budget) * 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); netif_receive_skb(skb); } @@ -610,54 +561,66 @@ static int rxq_process(struct rx_queue *rxq, int budget) mp->dev->last_rx = jiffies; } + if (rx < budget) + mp->work_rx &= ~(1 << rxq->index); + return rx; } -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 work_done; - int oom; - 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 < mp->txq_count; i++) - txq_reclaim(mp->txq + i, 0); + skb = __skb_dequeue(&mp->rx_recycle); + if (skb == NULL) + skb = dev_alloc_skb(mp->skb_size + + dma_get_cache_alignment() - 1); - spin_lock_irq(&mp->lock); - __txq_maybe_wake(mp->txq); - spin_unlock_irq(&mp->lock); - } -#endif + if (skb == NULL) { + mp->work_rx_oom |= 1 << rxq->index; + goto oom; + } - work_done = 0; - oom = 0; - for (i = mp->rxq_count - 1; work_done < budget && i >= 0; i--) { - struct rx_queue *rxq = mp->rxq + i; + unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1); + if (unaligned) + skb_reserve(skb, dma_get_cache_alignment() - unaligned); - work_done += rxq_process(rxq, budget - work_done); - work_done += rxq_refill(rxq, budget - work_done, &oom); - } + refilled++; + rxq->rx_desc_count++; - if (work_done < budget) { - if (oom) - mod_timer(&mp->rx_oom, jiffies + (HZ / 10)); - netif_rx_complete(mp->dev, napi); - wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); - } + rx = rxq->rx_used_desc++; + if (rxq->rx_used_desc == rxq->rx_ring_size) + rxq->rx_used_desc = 0; - return work_done; -} + 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(); -static inline void oom_timer_wrapper(unsigned long data) -{ - struct mv643xx_eth_private *mp = (void *)data; + /* + * 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); + } - napi_schedule(&mp->napi); + if (refilled < budget) + mp->work_rx_refill &= ~(1 << rxq->index); + +oom: + return refilled; } @@ -712,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; @@ -732,144 +693,228 @@ 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; - - 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); - 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; - } - - desc->byte_cnt = length; - desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE); + l4i_chk = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { - int mac_hdr_len; + int tag_bytes; BUG_ON(skb->protocol != htons(ETH_P_IP) && skb->protocol != htons(ETH_P_8021Q)); - cmd_sts |= GEN_TCP_UDP_CHECKSUM | - GEN_IP_V4_CHECKSUM | - ip_hdr(skb)->ihl << TX_IHL_SHIFT; + 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; + } - mac_hdr_len = (void *)ip_hdr(skb) - (void *)skb->data; - switch (mac_hdr_len - ETH_HLEN) { - case 0: - break; - case 4: - cmd_sts |= MAC_HDR_EXTRA_4_BYTES; - break; - case 8: - cmd_sts |= MAC_HDR_EXTRA_8_BYTES; - break; - case 12: + if (tag_bytes & 4) cmd_sts |= MAC_HDR_EXTRA_4_BYTES; + if (tag_bytes & 8) cmd_sts |= MAC_HDR_EXTRA_8_BYTES; - break; - default: - if (net_ratelimit()) - dev_printk(KERN_ERR, &txq_to_mp(txq)->dev->dev, - "mac header length is %d?!\n", mac_hdr_len); - break; - } + + cmd_sts |= GEN_TCP_UDP_CHECKSUM | + GEN_IP_V4_CHECKSUM | + ip_hdr(skb)->ihl << TX_IHL_SHIFT; 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 interrupt status */ - wrl(mp, INT_CAUSE(mp->port_num), ~(INT_TX_END_0 << txq->index)); - rdl(mp, INT_CAUSE(mp->port_num)); + /* 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; - if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) { - spin_unlock_irqrestore(&mp->lock, flags); - if (txq->index == 0 && net_ratelimit()) - dev_printk(KERN_ERR, &dev->dev, - "primary tx queue full?!\n"); + 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 == 0) { + 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_SKB_FRAGS + 1) - netif_stop_queue(dev); + 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) @@ -893,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; } } @@ -932,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) @@ -951,23 +1006,30 @@ 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); + } } @@ -1011,68 +1073,80 @@ static int smi_wait_ready(struct mv643xx_eth_shared_private *msp) 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("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("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("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("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("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); @@ -1090,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); @@ -1122,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); } @@ -1187,7 +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; - 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. @@ -1241,7 +1328,7 @@ static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd * */ cmd->advertising &= ~ADVERTISED_1000baseT_Half; - return mii_ethtool_sset(&mp->mii, cmd); + return phy_ethtool_sset(mp->phy, cmd); } static int mv643xx_eth_set_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd) @@ -1263,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) @@ -1273,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, @@ -1304,6 +1384,7 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *dev, 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++) { @@ -1347,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, @@ -1624,18 +1705,11 @@ 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++) { @@ -1651,99 +1725,77 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) nexti * sizeof(struct tx_desc); } - return 0; - - -out_free: - if (index == 0 && 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); + skb_queue_head_init(&txq->tx_skb); -out: - return -ENOMEM; + return 0; } -static void txq_reclaim(struct tx_queue *txq, int force) +static void txq_deinit(struct tx_queue *txq) { 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; + txq_disable(txq); + txq_reclaim(txq, txq->tx_ring_size, 1); - if (cmd_sts & BUFFER_OWNED_BY_DMA) { - if (!force) - break; - desc->cmd_sts = cmd_sts & ~BUFFER_OWNED_BY_DMA; - } + BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); - txq->tx_used_desc = tx_index + 1; - if (txq->tx_used_desc == txq->tx_ring_size) - txq->tx_used_desc = 0; - txq->tx_desc_count--; + 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); +} - 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++; - } +/* netdev ops and related ***************************************************/ +static int mv643xx_eth_collect_events(struct mv643xx_eth_private *mp) +{ + u32 int_cause; + u32 int_cause_ext; - /* - * Drop mp->lock while we free the skb. - */ - spin_unlock_irqrestore(&mp->lock, flags); + int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & + (INT_TX_END | INT_RX | INT_EXT); + if (int_cause == 0) + return 0; - 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); + int_cause_ext = 0; + if (int_cause & INT_EXT) + int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num)); - if (skb) - dev_kfree_skb_irq(skb); + 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; + } - spin_lock_irqsave(&mp->lock, flags); + 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; } - spin_unlock_irqrestore(&mp->lock, flags); + + return 1; } -static void txq_deinit(struct tx_queue *txq) +static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) { - struct mv643xx_eth_private *mp = txq_to_mp(txq); - - txq_disable(txq); - txq_reclaim(txq, 1); + struct net_device *dev = (struct net_device *)dev_id; + struct mv643xx_eth_private *mp = netdev_priv(dev); - BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); + if (unlikely(!mv643xx_eth_collect_events(mp))) + return IRQ_NONE; - 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); + wrl(mp, INT_MASK(mp->port_num), 0); + napi_schedule(&mp->napi); - kfree(txq->tx_skb); + return IRQ_HANDLED; } - -/* netdev ops and related ***************************************************/ static void handle_link_event(struct mv643xx_eth_private *mp) { struct net_device *dev = mp->dev; @@ -1764,7 +1816,7 @@ static void handle_link_event(struct mv643xx_eth_private *mp) for (i = 0; i < mp->txq_count; i++) { struct tx_queue *txq = mp->txq + i; - txq_reclaim(txq, 1); + txq_reclaim(txq, txq->tx_ring_size, 1); txq_reset_hw_ptr(txq); } } @@ -1797,102 +1849,88 @@ static void handle_link_event(struct mv643xx_eth_private *mp) netif_carrier_on(dev); } -static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) +static int mv643xx_eth_poll(struct napi_struct *napi, int budget) { - struct net_device *dev = (struct net_device *)dev_id; - struct mv643xx_eth_private *mp = netdev_priv(dev); - u32 int_cause; - u32 int_cause_ext; + struct mv643xx_eth_private *mp; + int work_done; - int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & - (INT_TX_END | INT_RX | INT_EXT); - if (int_cause == 0) - return IRQ_NONE; + mp = container_of(napi, struct mv643xx_eth_private, napi); - int_cause_ext = 0; - if (int_cause & INT_EXT) { - int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num)) - & (INT_EXT_LINK_PHY | INT_EXT_TX); - wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext); - } + mp->work_rx_refill |= mp->work_rx_oom; + mp->work_rx_oom = 0; - if (int_cause_ext & INT_EXT_LINK_PHY) - handle_link_event(mp); + 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; + } - /* - * RxBuffer or RxError set for any of the 8 queues? - */ - if (int_cause & INT_RX) { - wrl(mp, INT_CAUSE(mp->port_num), ~(int_cause & INT_RX)); - wrl(mp, INT_MASK(mp->port_num), 0x00000000); - rdl(mp, INT_MASK(mp->port_num)); + 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; + } - napi_schedule(&mp->napi); + 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(); + } } - /* - * TxBuffer or TxError set for any of the 8 queues? - */ - if (int_cause_ext & INT_EXT_TX) { - int i; - - for (i = 0; i < mp->txq_count; i++) - txq_reclaim(mp->txq + i, 0); - - /* - * Enough space again in the primary TX queue for a - * full packet? - */ - spin_lock(&mp->lock); - __txq_maybe_wake(mp->txq); - spin_unlock(&mp->lock); + 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); } - /* - * Any TxEnd interrupts? - */ - if (int_cause & INT_TX_END) { - int i; - - wrl(mp, INT_CAUSE(mp->port_num), ~(int_cause & INT_TX_END)); - - spin_lock(&mp->lock); - for (i = 0; i < 8; i++) { - struct tx_queue *txq = mp->txq + i; - u32 hw_desc_ptr; - u32 expected_ptr; - - if ((int_cause & (INT_TX_END_0 << i)) == 0) - continue; - - hw_desc_ptr = - rdl(mp, TXQ_CURRENT_DESC_PTR(mp->port_num, i)); - expected_ptr = (u32)txq->tx_desc_dma + - txq->tx_curr_desc * sizeof(struct tx_desc); + return work_done; +} - if (hw_desc_ptr != expected_ptr) - txq_enable(txq); - } - spin_unlock(&mp->lock); - } +static inline void oom_timer_wrapper(unsigned long data) +{ + struct mv643xx_eth_private *mp = (void *)data; - return IRQ_HANDLED; + napi_schedule(&mp->napi); } 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); } @@ -1904,7 +1942,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); @@ -1921,7 +1959,7 @@ static void port_start(struct mv643xx_eth_private *mp) wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), 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); @@ -1946,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. @@ -2001,11 +2040,30 @@ 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); int err; - int oom; int i; wrl(mp, INT_CAUSE(mp->port_num), 0); @@ -2021,9 +2079,12 @@ static int mv643xx_eth_open(struct net_device *dev) init_mac_tables(mp); + mv643xx_eth_recalc_skb_size(mp); + napi_enable(&mp->napi); - oom = 0; + skb_queue_head_init(&mp->rx_recycle); + for (i = 0; i < mp->rxq_count; i++) { err = rxq_init(mp, i); if (err) { @@ -2032,10 +2093,10 @@ static int mv643xx_eth_open(struct net_device *dev) goto out; } - rxq_refill(mp->rxq + i, INT_MAX, &oom); + rxq_refill(mp->rxq + i, INT_MAX); } - if (oom) { + if (mp->work_rx_oom) { mp->rx_oom.expires = jiffies + (HZ / 10); add_timer(&mp->rx_oom); } @@ -2105,6 +2166,8 @@ static int mv643xx_eth_stop(struct net_device *dev) wrl(mp, INT_MASK(mp->port_num), 0x00000000); rdl(mp, INT_MASK(mp->port_num)); + del_timer_sync(&mp->mib_counters_timer); + napi_disable(&mp->napi); del_timer_sync(&mp->rx_oom); @@ -2114,8 +2177,11 @@ static int mv643xx_eth_stop(struct net_device *dev) free_irq(dev->irq, dev); port_reset(mp); + mv643xx_eth_get_stats(dev); mib_counters_update(mp); + 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++) @@ -2128,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; } @@ -2142,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)) @@ -2169,10 +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); - netif_wake_queue(mp->dev); + netif_tx_wake_all_queues(mp->dev); } } @@ -2199,18 +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); - 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 @@ -2262,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) @@ -2299,11 +2360,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); @@ -2339,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: @@ -2348,7 +2429,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_free(msp->smi_bus); + mdiobus_unregister(msp->smi_bus); + } if (msp->err_interrupt != NO_IRQ) free_irq(msp->err_interrupt, msp); iounmap(msp->base); @@ -2396,17 +2482,6 @@ 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; if (pd->rx_queue_size) mp->default_rx_ring_size = pd->rx_queue_size; @@ -2424,76 +2499,60 @@ static void set_params(struct mv643xx_eth_private *mp, 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, 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; } - - 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) @@ -2507,7 +2566,7 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) } 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; @@ -2546,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; @@ -2559,23 +2618,33 @@ static int mv643xx_eth_probe(struct platform_device *pdev) mp->dev = dev; 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); @@ -2587,6 +2656,7 @@ 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; @@ -2632,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);