diff options
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r-- | drivers/net/sky2.c | 300 |
1 files changed, 162 insertions, 138 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index d6577084ce70..7d85a38377a1 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -46,10 +46,6 @@ #include <asm/irq.h> -#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) -#define SKY2_VLAN_TAG_USED 1 -#endif - #include "sky2.h" #define DRV_NAME "sky2" @@ -1326,39 +1322,34 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return err; } -#ifdef SKY2_VLAN_TAG_USED -static void sky2_set_vlan_mode(struct sky2_hw *hw, u16 port, bool onoff) -{ - if (onoff) { - sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), - RX_VLAN_STRIP_ON); - sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), - TX_VLAN_TAG_ON); - } else { - sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), - RX_VLAN_STRIP_OFF); - sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), - TX_VLAN_TAG_OFF); - } -} +#define NETIF_F_ALL_VLAN (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX) -static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +static void sky2_vlan_mode(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; u16 port = sky2->port; - netif_tx_lock_bh(dev); - napi_disable(&hw->napi); + if (dev->features & NETIF_F_HW_VLAN_RX) + sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), + RX_VLAN_STRIP_ON); + else + sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), + RX_VLAN_STRIP_OFF); - sky2->vlgrp = grp; - sky2_set_vlan_mode(hw, port, grp != NULL); + dev->vlan_features = dev->features &~ NETIF_F_ALL_VLAN; + if (dev->features & NETIF_F_HW_VLAN_TX) + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), + TX_VLAN_TAG_ON); + else { + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), + TX_VLAN_TAG_OFF); - sky2_read32(hw, B0_Y2_SP_LISR); - napi_enable(&hw->napi); - netif_tx_unlock_bh(dev); + /* Can't do transmit offload of vlan without hw vlan */ + dev->vlan_features &= ~(NETIF_F_TSO | NETIF_F_SG + | NETIF_F_ALL_CSUM); + } } -#endif /* Amount of required worst case padding in rx buffer */ static inline unsigned sky2_rx_pad(const struct sky2_hw *hw) @@ -1635,9 +1626,7 @@ static void sky2_hw_up(struct sky2_port *sky2) sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, sky2->tx_ring_size - 1); -#ifdef SKY2_VLAN_TAG_USED - sky2_set_vlan_mode(hw, port, sky2->vlgrp != NULL); -#endif + sky2_vlan_mode(sky2->netdev); sky2_rx_start(sky2); } @@ -1780,7 +1769,7 @@ static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb, } ctrl = 0; -#ifdef SKY2_VLAN_TAG_USED + /* Add VLAN tag, can piggyback on LRGLEN or ADDR64 */ if (vlan_tx_tag_present(skb)) { if (!le) { @@ -1792,7 +1781,6 @@ static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb, le->length = cpu_to_be16(vlan_tx_tag_get(skb)); ctrl |= INS_VLAN; } -#endif /* Handle TCP checksum offload */ if (skb->ip_summed == CHECKSUM_PARTIAL) { @@ -1917,8 +1905,10 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) netif_printk(sky2, tx_done, KERN_DEBUG, dev, "tx done %u\n", idx); - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; + u64_stats_update_begin(&sky2->tx_stats.syncp); + ++sky2->tx_stats.packets; + sky2->tx_stats.bytes += skb->len; + u64_stats_update_end(&sky2->tx_stats.syncp); re->skb = NULL; dev_kfree_skb_any(skb); @@ -2430,11 +2420,8 @@ static struct sk_buff *sky2_receive(struct net_device *dev, struct sk_buff *skb = NULL; u16 count = (status & GMR_FS_LEN) >> 16; -#ifdef SKY2_VLAN_TAG_USED - /* Account for vlan tag */ - if (sky2->vlgrp && (status & GMR_FS_VLAN)) - count -= VLAN_HLEN; -#endif + if (status & GMR_FS_VLAN) + count -= VLAN_HLEN; /* Account for vlan tag */ netif_printk(sky2, rx_status, KERN_DEBUG, dev, "rx slot %u status 0x%x len %d\n", @@ -2460,7 +2447,7 @@ static struct sk_buff *sky2_receive(struct net_device *dev, /* if length reported by DMA does not match PHY, packet was truncated */ if (length != count) - goto len_error; + goto error; okay: if (length < copybreak) @@ -2475,34 +2462,13 @@ resubmit: return skb; -len_error: - /* Truncation of overlength packets - causes PHY length to not match MAC length */ - ++dev->stats.rx_length_errors; - if (net_ratelimit()) - netif_info(sky2, rx_err, dev, - "rx length error: status %#x length %d\n", - status, length); - goto resubmit; - error: ++dev->stats.rx_errors; - if (status & GMR_FS_RX_FF_OV) { - dev->stats.rx_over_errors++; - goto resubmit; - } if (net_ratelimit()) netif_info(sky2, rx_err, dev, "rx error, status 0x%x length %d\n", status, length); - if (status & (GMR_FS_LONG_ERR | GMR_FS_UN_SIZE)) - dev->stats.rx_length_errors++; - if (status & GMR_FS_FRAGMENT) - dev->stats.rx_frame_errors++; - if (status & GMR_FS_CRC_ERR) - dev->stats.rx_crc_errors++; - goto resubmit; } @@ -2523,17 +2489,9 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last) static inline void sky2_skb_rx(const struct sky2_port *sky2, u32 status, struct sk_buff *skb) { -#ifdef SKY2_VLAN_TAG_USED - u16 vlan_tag = be16_to_cpu(sky2->rx_tag); - if (sky2->vlgrp && (status & GMR_FS_VLAN)) { - if (skb->ip_summed == CHECKSUM_NONE) - vlan_hwaccel_receive_skb(skb, sky2->vlgrp, vlan_tag); - else - vlan_gro_receive(&sky2->hw->napi, sky2->vlgrp, - vlan_tag, skb); - return; - } -#endif + if (status & GMR_FS_VLAN) + __vlan_hwaccel_put_tag(skb, be16_to_cpu(sky2->rx_tag)); + if (skb->ip_summed == CHECKSUM_NONE) netif_receive_skb(skb); else @@ -2543,14 +2501,19 @@ static inline void sky2_skb_rx(const struct sky2_port *sky2, static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port, unsigned packets, unsigned bytes) { - if (packets) { - struct net_device *dev = hw->dev[port]; + struct net_device *dev = hw->dev[port]; + struct sky2_port *sky2 = netdev_priv(dev); - dev->stats.rx_packets += packets; - dev->stats.rx_bytes += bytes; - dev->last_rx = jiffies; - sky2_rx_update(netdev_priv(dev), rxqaddr[port]); - } + if (packets == 0) + return; + + u64_stats_update_begin(&sky2->rx_stats.syncp); + sky2->rx_stats.packets += packets; + sky2->rx_stats.bytes += bytes; + u64_stats_update_end(&sky2->rx_stats.syncp); + + dev->last_rx = jiffies; + sky2_rx_update(netdev_priv(dev), rxqaddr[port]); } static void sky2_rx_checksum(struct sky2_port *sky2, u32 status) @@ -2645,7 +2608,6 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) goto exit_loop; break; -#ifdef SKY2_VLAN_TAG_USED case OP_RXVLAN: sky2->rx_tag = length; break; @@ -2653,7 +2615,6 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx) case OP_RXCHKSVLAN: sky2->rx_tag = length; /* fall through */ -#endif case OP_RXCHKS: if (likely(sky2->flags & SKY2_FLAG_RX_CHECKSUM)) sky2_rx_checksum(sky2, status); @@ -3056,6 +3017,10 @@ static int __devinit sky2_init(struct sky2_hw *hw) | SKY2_HW_NEW_LE | SKY2_HW_AUTO_TX_SUM | SKY2_HW_ADV_POWER_CTL; + + /* The workaround for status conflicts VLAN tag detection. */ + if (hw->chip_rev == CHIP_REV_YU_FE2_A0) + hw->flags |= SKY2_HW_VLAN_BROKEN; break; case CHIP_ID_YUKON_SUPR: @@ -3398,12 +3363,24 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; + bool enable_wakeup = false; + int i; if ((wol->wolopts & ~sky2_wol_supported(sky2->hw)) || !device_can_wakeup(&hw->pdev->dev)) return -EOPNOTSUPP; sky2->wol = wol->wolopts; + + for (i = 0; i < hw->ports; i++) { + struct net_device *dev = hw->dev[i]; + struct sky2_port *sky2 = netdev_priv(dev); + + if (sky2->wol) + enable_wakeup = true; + } + device_set_wakeup_enable(&hw->pdev->dev, enable_wakeup); + return 0; } @@ -3413,18 +3390,15 @@ static u32 sky2_supported_modes(const struct sky2_hw *hw) u32 modes = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half - | SUPPORTED_100baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_TP; + | SUPPORTED_100baseT_Full; if (hw->flags & SKY2_HW_GIGABIT) modes |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; return modes; } else - return SUPPORTED_1000baseT_Half - | SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg - | SUPPORTED_FIBRE; + return SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full; } static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) @@ -3438,9 +3412,11 @@ static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) if (sky2_is_copper(hw)) { ecmd->port = PORT_TP; ecmd->speed = sky2->speed; + ecmd->supported |= SUPPORTED_Autoneg | SUPPORTED_TP; } else { ecmd->speed = SPEED_1000; ecmd->port = PORT_FIBRE; + ecmd->supported |= SUPPORTED_Autoneg | SUPPORTED_FIBRE; } ecmd->advertising = sky2->advertising; @@ -3457,8 +3433,19 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) u32 supported = sky2_supported_modes(hw); if (ecmd->autoneg == AUTONEG_ENABLE) { + if (ecmd->advertising & ~supported) + return -EINVAL; + + if (sky2_is_copper(hw)) + sky2->advertising = ecmd->advertising | + ADVERTISED_TP | + ADVERTISED_Autoneg; + else + sky2->advertising = ecmd->advertising | + ADVERTISED_FIBRE | + ADVERTISED_Autoneg; + sky2->flags |= SKY2_FLAG_AUTO_SPEED; - ecmd->advertising = supported; sky2->duplex = -1; sky2->speed = -1; } else { @@ -3502,8 +3489,6 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) sky2->flags &= ~SKY2_FLAG_AUTO_SPEED; } - sky2->advertising = ecmd->advertising; - if (netif_running(dev)) { sky2_phy_reinit(sky2); sky2_set_multicast(dev); @@ -3614,13 +3599,11 @@ static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count) unsigned port = sky2->port; int i; - data[0] = (u64) gma_read32(hw, port, GM_TXO_OK_HI) << 32 - | (u64) gma_read32(hw, port, GM_TXO_OK_LO); - data[1] = (u64) gma_read32(hw, port, GM_RXO_OK_HI) << 32 - | (u64) gma_read32(hw, port, GM_RXO_OK_LO); + data[0] = get_stats64(hw, port, GM_TXO_OK_LO); + data[1] = get_stats64(hw, port, GM_RXO_OK_LO); for (i = 2; i < count; i++) - data[i] = (u64) gma_read32(hw, port, sky2_stats[i].offset); + data[i] = get_stats32(hw, port, sky2_stats[i].offset); } static void sky2_set_msglevel(struct net_device *netdev, u32 value) @@ -3738,6 +3721,51 @@ static void sky2_set_multicast(struct net_device *dev) gma_write16(hw, port, GM_RX_CTRL, reg); } +static struct rtnl_link_stats64 *sky2_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct sky2_port *sky2 = netdev_priv(dev); + struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; + unsigned int start; + u64 _bytes, _packets; + + do { + start = u64_stats_fetch_begin_bh(&sky2->rx_stats.syncp); + _bytes = sky2->rx_stats.bytes; + _packets = sky2->rx_stats.packets; + } while (u64_stats_fetch_retry_bh(&sky2->rx_stats.syncp, start)); + + stats->rx_packets = _packets; + stats->rx_bytes = _bytes; + + do { + start = u64_stats_fetch_begin_bh(&sky2->tx_stats.syncp); + _bytes = sky2->tx_stats.bytes; + _packets = sky2->tx_stats.packets; + } while (u64_stats_fetch_retry_bh(&sky2->tx_stats.syncp, start)); + + stats->tx_packets = _packets; + stats->tx_bytes = _bytes; + + stats->multicast = get_stats32(hw, port, GM_RXF_MC_OK) + + get_stats32(hw, port, GM_RXF_BC_OK); + + stats->collisions = get_stats32(hw, port, GM_TXF_COL); + + stats->rx_length_errors = get_stats32(hw, port, GM_RXF_LNG_ERR); + stats->rx_crc_errors = get_stats32(hw, port, GM_RXF_FCS_ERR); + stats->rx_frame_errors = get_stats32(hw, port, GM_RXF_SHT) + + get_stats32(hw, port, GM_RXE_FRAG); + stats->rx_over_errors = get_stats32(hw, port, GM_RXE_FIFO_OV); + + stats->rx_dropped = dev->stats.rx_dropped; + stats->rx_fifo_errors = dev->stats.rx_fifo_errors; + stats->tx_fifo_errors = dev->stats.tx_fifo_errors; + + return stats; +} + /* Can have one global because blinking is controlled by * ethtool and that is always under RTNL mutex */ @@ -4188,15 +4216,28 @@ static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom static int sky2_set_flags(struct net_device *dev, u32 data) { struct sky2_port *sky2 = netdev_priv(dev); - u32 supported = - (sky2->hw->flags & SKY2_HW_RSS_BROKEN) ? 0 : ETH_FLAG_RXHASH; + unsigned long old_feat = dev->features; + u32 supported = 0; int rc; + if (!(sky2->hw->flags & SKY2_HW_RSS_BROKEN)) + supported |= ETH_FLAG_RXHASH; + + if (!(sky2->hw->flags & SKY2_HW_VLAN_BROKEN)) + supported |= ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN; + + printk(KERN_DEBUG "sky2 set_flags: supported %x data %x\n", + supported, data); + rc = ethtool_op_set_flags(dev, data, supported); if (rc) return rc; - rx_set_rss(dev); + if ((old_feat ^ dev->features) & NETIF_F_RXHASH) + rx_set_rss(dev); + + if ((old_feat ^ dev->features) & NETIF_F_ALL_VLAN) + sky2_vlan_mode(dev); return 0; } @@ -4232,6 +4273,7 @@ static const struct ethtool_ops sky2_ethtool_ops = { .get_sset_count = sky2_get_sset_count, .get_ethtool_stats = sky2_get_ethtool_stats, .set_flags = sky2_set_flags, + .get_flags = ethtool_op_get_flags, }; #ifdef CONFIG_SKY2_DEBUG @@ -4512,9 +4554,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = { .ndo_set_multicast_list = sky2_set_multicast, .ndo_change_mtu = sky2_change_mtu, .ndo_tx_timeout = sky2_tx_timeout, -#ifdef SKY2_VLAN_TAG_USED - .ndo_vlan_rx_register = sky2_vlan_rx_register, -#endif + .ndo_get_stats64 = sky2_get_stats, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = sky2_netpoll, #endif @@ -4529,9 +4569,7 @@ static const struct net_device_ops sky2_netdev_ops[2] = { .ndo_set_multicast_list = sky2_set_multicast, .ndo_change_mtu = sky2_change_mtu, .ndo_tx_timeout = sky2_tx_timeout, -#ifdef SKY2_VLAN_TAG_USED - .ndo_vlan_rx_register = sky2_vlan_rx_register, -#endif + .ndo_get_stats64 = sky2_get_stats, }, }; @@ -4582,7 +4620,8 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, sky2->port = port; dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG - | NETIF_F_TSO | NETIF_F_GRO; + | NETIF_F_TSO | NETIF_F_GRO; + if (highmem) dev->features |= NETIF_F_HIGHDMA; @@ -4590,13 +4629,8 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, if (!(hw->flags & SKY2_HW_RSS_BROKEN)) dev->features |= NETIF_F_RXHASH; -#ifdef SKY2_VLAN_TAG_USED - /* The workaround for FE+ status conflicts with VLAN tag detection. */ - if (!(sky2->hw->chip_id == CHIP_ID_YUKON_FE_P && - sky2->hw->chip_rev == CHIP_REV_YU_FE2_A0)) { + if (!(hw->flags & SKY2_HW_VLAN_BROKEN)) dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; - } -#endif /* read the mac address */ memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); @@ -4920,10 +4954,11 @@ static void __devexit sky2_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } -static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) +static int sky2_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct sky2_hw *hw = pci_get_drvdata(pdev); - int i, wol = 0; + int i; if (!hw) return 0; @@ -4940,41 +4975,24 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state) if (sky2->wol) sky2_wol_init(sky2); - - wol |= sky2->wol; } - device_set_wakeup_enable(&pdev->dev, wol != 0); - sky2_power_aux(hw); rtnl_unlock(); - pci_save_state(pdev); - pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; } #ifdef CONFIG_PM -static int sky2_resume(struct pci_dev *pdev) +static int sky2_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct sky2_hw *hw = pci_get_drvdata(pdev); int err; if (!hw) return 0; - err = pci_set_power_state(pdev, PCI_D0); - if (err) - goto out; - - err = pci_restore_state(pdev); - if (err) - goto out; - - pci_enable_wake(pdev, PCI_D0, 0); - /* Re-enable all clocks */ err = pci_write_config_dword(pdev, PCI_DEV_REG3, 0); if (err) { @@ -4994,11 +5012,20 @@ out: pci_disable_device(pdev); return err; } + +static SIMPLE_DEV_PM_OPS(sky2_pm_ops, sky2_suspend, sky2_resume); +#define SKY2_PM_OPS (&sky2_pm_ops) + +#else + +#define SKY2_PM_OPS NULL #endif static void sky2_shutdown(struct pci_dev *pdev) { - sky2_suspend(pdev, PMSG_SUSPEND); + sky2_suspend(&pdev->dev); + pci_wake_from_d3(pdev, device_may_wakeup(&pdev->dev)); + pci_set_power_state(pdev, PCI_D3hot); } static struct pci_driver sky2_driver = { @@ -5006,11 +5033,8 @@ static struct pci_driver sky2_driver = { .id_table = sky2_id_table, .probe = sky2_probe, .remove = __devexit_p(sky2_remove), -#ifdef CONFIG_PM - .suspend = sky2_suspend, - .resume = sky2_resume, -#endif .shutdown = sky2_shutdown, + .driver.pm = SKY2_PM_OPS, }; static int __init sky2_init_module(void) |