Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / net / sky2.c
index ff8d262..57339da 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <linux/pci.h>
+#include <linux/interrupt.h>
 #include <linux/ip.h>
 #include <linux/slab.h>
 #include <net/ip.h>
@@ -49,7 +50,7 @@
 #include "sky2.h"
 
 #define DRV_NAME               "sky2"
-#define DRV_VERSION            "1.28"
+#define DRV_VERSION            "1.29"
 
 /*
  * The Yukon II chipset takes 64 bit command blocks (called list elements)
@@ -364,6 +365,17 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
                                gm_phy_write(hw, port, PHY_MARV_FE_SPEC_2, spec);
                        }
                } else {
+                       if (hw->chip_id >= CHIP_ID_YUKON_OPT) {
+                               u16 ctrl2 = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL_2);
+
+                               /* enable PHY Reverse Auto-Negotiation */
+                               ctrl2 |= 1u << 13;
+
+                               /* Write PHY changes (SW-reset must follow) */
+                               gm_phy_write(hw, port, PHY_MARV_EXT_CTRL_2, ctrl2);
+                       }
+
+
                        /* disable energy detect */
                        ctrl &= ~PHY_M_PC_EN_DET_MSK;
 
@@ -625,6 +637,63 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
                if (ledover)
                        gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
 
+       } else if (hw->chip_id == CHIP_ID_YUKON_PRM &&
+                  (sky2_read8(hw, B2_MAC_CFG) & 0xf) == 0x7) {
+               int i;
+               /* This a phy register setup workaround copied from vendor driver. */
+               static const struct {
+                       u16 reg, val;
+               } eee_afe[] = {
+                       { 0x156, 0x58ce },
+                       { 0x153, 0x99eb },
+                       { 0x141, 0x8064 },
+                       /* { 0x155, 0x130b },*/
+                       { 0x000, 0x0000 },
+                       { 0x151, 0x8433 },
+                       { 0x14b, 0x8c44 },
+                       { 0x14c, 0x0f90 },
+                       { 0x14f, 0x39aa },
+                       /* { 0x154, 0x2f39 },*/
+                       { 0x14d, 0xba33 },
+                       { 0x144, 0x0048 },
+                       { 0x152, 0x2010 },
+                       /* { 0x158, 0x1223 },*/
+                       { 0x140, 0x4444 },
+                       { 0x154, 0x2f3b },
+                       { 0x158, 0xb203 },
+                       { 0x157, 0x2029 },
+               };
+
+               /* Start Workaround for OptimaEEE Rev.Z0 */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00fb);
+
+               gm_phy_write(hw, port,  1, 0x4099);
+               gm_phy_write(hw, port,  3, 0x1120);
+               gm_phy_write(hw, port, 11, 0x113c);
+               gm_phy_write(hw, port, 14, 0x8100);
+               gm_phy_write(hw, port, 15, 0x112a);
+               gm_phy_write(hw, port, 17, 0x1008);
+
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00fc);
+               gm_phy_write(hw, port,  1, 0x20b0);
+
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff);
+
+               for (i = 0; i < ARRAY_SIZE(eee_afe); i++) {
+                       /* apply AFE settings */
+                       gm_phy_write(hw, port, 17, eee_afe[i].val);
+                       gm_phy_write(hw, port, 16, eee_afe[i].reg | 1u<<13);
+               }
+
+               /* End Workaround for OptimaEEE */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+
+               /* Enable 10Base-Te (EEE) */
+               if (hw->chip_id >= CHIP_ID_YUKON_PRM) {
+                       reg = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
+                       gm_phy_write(hw, port, PHY_MARV_EXT_CTRL,
+                                    reg | PHY_M_10B_TE_ENABLE);
+               }
        }
 
        /* Enable phy interrupt on auto-negotiation complete (or link up) */
@@ -713,6 +782,20 @@ static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port)
        sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
 }
 
+/* configure IPG according to used link speed */
+static void sky2_set_ipg(struct sky2_port *sky2)
+{
+       u16 reg;
+
+       reg = gma_read16(sky2->hw, sky2->port, GM_SERIAL_MODE);
+       reg &= ~GM_SMOD_IPG_MSK;
+       if (sky2->speed > SPEED_100)
+               reg |= IPG_DATA_VAL(IPG_DATA_DEF_1000);
+       else
+               reg |= IPG_DATA_VAL(IPG_DATA_DEF_10_100);
+       gma_write16(sky2->hw, sky2->port, GM_SERIAL_MODE, reg);
+}
+
 /* Enable Rx/Tx */
 static void sky2_enable_rx_tx(struct sky2_port *sky2)
 {
@@ -881,7 +964,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
 
        /* serial mode register */
        reg = DATA_BLIND_VAL(DATA_BLIND_DEF) |
-               GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+               GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF_1000);
 
        if (hw->dev[port]->mtu > ETH_DATA_LEN)
                reg |= GM_SMOD_JUMBO_ENA;
@@ -1198,12 +1281,12 @@ static void rx_set_checksum(struct sky2_port *sky2)
 
        sky2_write32(sky2->hw,
                     Q_ADDR(rxqaddr[sky2->port], Q_CSR),
-                    (sky2->flags & SKY2_FLAG_RX_CHECKSUM)
+                    (sky2->netdev->features & NETIF_F_RXCSUM)
                     ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
 }
 
 /* Enable/disable receive hash calculation (RSS) */
-static void rx_set_rss(struct net_device *dev)
+static void rx_set_rss(struct net_device *dev, u32 features)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
        struct sky2_hw *hw = sky2->hw;
@@ -1216,7 +1299,7 @@ static void rx_set_rss(struct net_device *dev)
        }
 
        /* Program RSS initial values */
-       if (dev->features & NETIF_F_RXHASH) {
+       if (features & NETIF_F_RXHASH) {
                u32 key[nkeys];
 
                get_random_bytes(key, nkeys * sizeof(u32));
@@ -1322,32 +1405,32 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return err;
 }
 
-#define NETIF_F_ALL_VLAN (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX)
+#define SKY2_VLAN_OFFLOADS (NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO)
 
-static void sky2_vlan_mode(struct net_device *dev)
+static void sky2_vlan_mode(struct net_device *dev, u32 features)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
        struct sky2_hw *hw = sky2->hw;
        u16 port = sky2->port;
 
-       if (dev->features & NETIF_F_HW_VLAN_RX)
+       if (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);
 
-       dev->vlan_features = dev->features &~ NETIF_F_ALL_VLAN;
-       if (dev->features & NETIF_F_HW_VLAN_TX)
+       if (features & NETIF_F_HW_VLAN_TX) {
                sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
                             TX_VLAN_TAG_ON);
-       else {
+
+               dev->vlan_features |= SKY2_VLAN_OFFLOADS;
+       } else {
                sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
                             TX_VLAN_TAG_OFF);
 
                /* Can't do transmit offload of vlan without hw vlan */
-               dev->vlan_features &= ~(NETIF_F_TSO | NETIF_F_SG
-                                       | NETIF_F_ALL_CSUM);
+               dev->vlan_features &= ~SKY2_VLAN_OFFLOADS;
        }
 }
 
@@ -1361,13 +1444,14 @@ static inline unsigned sky2_rx_pad(const struct sky2_hw *hw)
  * Allocate an skb for receiving. If the MTU is large enough
  * make the skb non-linear with a fragment list of pages.
  */
-static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2)
+static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2, gfp_t gfp)
 {
        struct sk_buff *skb;
        int i;
 
-       skb = netdev_alloc_skb(sky2->netdev,
-                              sky2->rx_data_size + sky2_rx_pad(sky2->hw));
+       skb = __netdev_alloc_skb(sky2->netdev,
+                                sky2->rx_data_size + sky2_rx_pad(sky2->hw),
+                                gfp);
        if (!skb)
                goto nomem;
 
@@ -1385,7 +1469,7 @@ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2)
                skb_reserve(skb, NET_IP_ALIGN);
 
        for (i = 0; i < sky2->rx_nfrags; i++) {
-               struct page *page = alloc_page(GFP_ATOMIC);
+               struct page *page = alloc_page(gfp);
 
                if (!page)
                        goto free_partial;
@@ -1415,7 +1499,7 @@ static int sky2_alloc_rx_skbs(struct sky2_port *sky2)
        for (i = 0; i < sky2->rx_pending; i++) {
                struct rx_ring_info *re = sky2->rx_ring + i;
 
-               re->skb = sky2_rx_alloc(sky2);
+               re->skb = sky2_rx_alloc(sky2, GFP_KERNEL);
                if (!re->skb)
                        return -ENOMEM;
 
@@ -1448,7 +1532,7 @@ static void sky2_rx_start(struct sky2_port *sky2)
        sky2_qset(hw, rxq);
 
        /* On PCI express lowering the watermark gives better performance */
-       if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP))
+       if (pci_is_pcie(hw->pdev))
                sky2_write32(hw, Q_ADDR(rxq, Q_WM), BMU_WM_PEX);
 
        /* These chips have no ram buffer?
@@ -1463,7 +1547,7 @@ static void sky2_rx_start(struct sky2_port *sky2)
                rx_set_checksum(sky2);
 
        if (!(hw->flags & SKY2_HW_RSS_BROKEN))
-               rx_set_rss(sky2->netdev);
+               rx_set_rss(sky2->netdev, sky2->netdev->features);
 
        /* submit Rx ring */
        for (i = 0; i < sky2->rx_pending; i++) {
@@ -1626,7 +1710,8 @@ static void sky2_hw_up(struct sky2_port *sky2)
        sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
                           sky2->tx_ring_size - 1);
 
-       sky2_vlan_mode(sky2->netdev);
+       sky2_vlan_mode(sky2->netdev, sky2->netdev->features);
+       netdev_update_features(sky2->netdev);
 
        sky2_rx_start(sky2);
 }
@@ -2050,6 +2135,8 @@ static void sky2_link_up(struct sky2_port *sky2)
                [FC_BOTH]       = "both",
        };
 
+       sky2_set_ipg(sky2);
+
        sky2_enable_rx_tx(sky2);
 
        gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
@@ -2261,12 +2348,9 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
             hw->chip_id == CHIP_ID_YUKON_FE_P))
                return -EINVAL;
 
-       /* TSO, etc on Yukon Ultra and MTU > 1500 not supported */
-       if (new_mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U)
-               dev->features &= ~(NETIF_F_TSO|NETIF_F_SG|NETIF_F_ALL_CSUM);
-
        if (!netif_running(dev)) {
                dev->mtu = new_mtu;
+               netdev_update_features(dev);
                return 0;
        }
 
@@ -2288,9 +2372,13 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
        sky2_rx_clean(sky2);
 
        dev->mtu = new_mtu;
+       netdev_update_features(dev);
 
-       mode = DATA_BLIND_VAL(DATA_BLIND_DEF) |
-               GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+       mode = DATA_BLIND_VAL(DATA_BLIND_DEF) | GM_SMOD_VLAN_ENA;
+       if (sky2->speed > SPEED_100)
+               mode |= IPG_DATA_VAL(IPG_DATA_DEF_1000);
+       else
+               mode |= IPG_DATA_VAL(IPG_DATA_DEF_10_100);
 
        if (dev->mtu > ETH_DATA_LEN)
                mode |= GM_SMOD_JUMBO_ENA;
@@ -2384,7 +2472,7 @@ static struct sk_buff *receive_new(struct sky2_port *sky2,
        struct rx_ring_info nre;
        unsigned hdr_space = sky2->rx_data_size;
 
-       nre.skb = sky2_rx_alloc(sky2);
+       nre.skb = sky2_rx_alloc(sky2, GFP_ATOMIC);
        if (unlikely(!nre.skb))
                goto nobuf;
 
@@ -2535,8 +2623,11 @@ static void sky2_rx_checksum(struct sky2_port *sky2, u32 status)
                           "%s: receive checksum problem (status = %#x)\n",
                           sky2->netdev->name, status);
 
-               /* Disable checksum offload */
-               sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
+               /* Disable checksum offload
+                * It will be reenabled on next ndo_set_features, but if it's
+                * really broken, will get disabled again
+                */
+               sky2->netdev->features &= ~NETIF_F_RXCSUM;
                sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
                             BMU_DIS_RX_CHKSUM);
        }
@@ -2591,7 +2682,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
 
                        /* This chip reports checksum status differently */
                        if (hw->flags & SKY2_HW_NEW_LE) {
-                               if ((sky2->flags & SKY2_FLAG_RX_CHECKSUM) &&
+                               if ((dev->features & NETIF_F_RXCSUM) &&
                                    (le->css & (CSS_ISIPV4 | CSS_ISIPV6)) &&
                                    (le->css & CSS_TCPUDPCSOK))
                                        skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -2616,7 +2707,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
                        sky2->rx_tag = length;
                        /* fall through */
                case OP_RXCHKS:
-                       if (likely(sky2->flags & SKY2_FLAG_RX_CHECKSUM))
+                       if (likely(dev->features & NETIF_F_RXCSUM))
                                sky2_rx_checksum(sky2, status);
                        break;
 
@@ -2936,6 +3027,8 @@ static u32 sky2_mhz(const struct sky2_hw *hw)
        case CHIP_ID_YUKON_SUPR:
        case CHIP_ID_YUKON_UL_2:
        case CHIP_ID_YUKON_OPT:
+       case CHIP_ID_YUKON_PRM:
+       case CHIP_ID_YUKON_OP_2:
                return 125;
 
        case CHIP_ID_YUKON_FE:
@@ -2992,7 +3085,8 @@ static int __devinit sky2_init(struct sky2_hw *hw)
                hw->flags = SKY2_HW_GIGABIT
                        | SKY2_HW_NEWER_PHY
                        | SKY2_HW_NEW_LE
-                       | SKY2_HW_ADV_POWER_CTL;
+                       | SKY2_HW_ADV_POWER_CTL
+                       | SKY2_HW_RSS_CHKSUM;
 
                /* New transmit checksum */
                if (hw->chip_rev != CHIP_REV_YU_EX_B0)
@@ -3020,7 +3114,7 @@ static int __devinit sky2_init(struct sky2_hw *hw)
 
                /* The workaround for status conflicts VLAN tag detection. */
                if (hw->chip_rev == CHIP_REV_YU_FE2_A0)
-                       hw->flags |= SKY2_HW_VLAN_BROKEN;
+                       hw->flags |= SKY2_HW_VLAN_BROKEN | SKY2_HW_RSS_CHKSUM;
                break;
 
        case CHIP_ID_YUKON_SUPR:
@@ -3029,6 +3123,9 @@ static int __devinit sky2_init(struct sky2_hw *hw)
                        | SKY2_HW_NEW_LE
                        | SKY2_HW_AUTO_TX_SUM
                        | SKY2_HW_ADV_POWER_CTL;
+
+               if (hw->chip_rev == CHIP_REV_YU_SU_A0)
+                       hw->flags |= SKY2_HW_RSS_CHKSUM;
                break;
 
        case CHIP_ID_YUKON_UL_2:
@@ -3037,6 +3134,8 @@ static int __devinit sky2_init(struct sky2_hw *hw)
                break;
 
        case CHIP_ID_YUKON_OPT:
+       case CHIP_ID_YUKON_PRM:
+       case CHIP_ID_YUKON_OP_2:
                hw->flags = SKY2_HW_GIGABIT
                        | SKY2_HW_NEW_LE
                        | SKY2_HW_ADV_POWER_CTL;
@@ -3069,7 +3168,7 @@ static void sky2_reset(struct sky2_hw *hw)
 {
        struct pci_dev *pdev = hw->pdev;
        u16 status;
-       int i, cap;
+       int i;
        u32 hwe_mask = Y2_HWE_ALL_MASK;
 
        /* disable ASF */
@@ -3105,8 +3204,7 @@ static void sky2_reset(struct sky2_hw *hw)
 
        sky2_write8(hw, B0_CTST, CS_MRST_CLR);
 
-       cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
-       if (cap) {
+       if (pci_is_pcie(pdev)) {
                sky2_write32(hw, Y2_CFG_AER + PCI_ERR_UNCOR_STATUS,
                             0xfffffffful);
 
@@ -3137,30 +3235,33 @@ static void sky2_reset(struct sky2_hw *hw)
                sky2_pci_write32(hw, PCI_DEV_REG3, P_CLK_MACSEC_DIS);
        }
 
-       if (hw->chip_id == CHIP_ID_YUKON_OPT) {
+       if (hw->chip_id == CHIP_ID_YUKON_OPT ||
+           hw->chip_id == CHIP_ID_YUKON_PRM ||
+           hw->chip_id == CHIP_ID_YUKON_OP_2) {
                u16 reg;
                u32 msk;
 
-               if (hw->chip_rev == 0) {
+               if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) {
                        /* disable PCI-E PHY power down (set PHY reg 0x80, bit 7 */
                        sky2_write32(hw, Y2_PEX_PHY_DATA, (0x80UL << 16) | (1 << 7));
 
                        /* set PHY Link Detect Timer to 1.1 second (11x 100ms) */
                        reg = 10;
+
+                       /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
+                       sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
                } else {
                        /* set PHY Link Detect Timer to 0.4 second (4x 100ms) */
                        reg = 3;
                }
 
                reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE;
+               reg |= PSM_CONFIG_REG4_RST_PHY_LINK_DETECT;
 
                /* reset PHY Link Detect */
                sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
-               sky2_pci_write16(hw, PSM_CONFIG_REG4,
-                                reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT);
                sky2_pci_write16(hw, PSM_CONFIG_REG4, reg);
 
-
                /* enable PHY Quick Link */
                msk = sky2_read32(hw, B0_IMSK);
                msk |= Y2_IS_PHY_QLNK;
@@ -3168,11 +3269,11 @@ static void sky2_reset(struct sky2_hw *hw)
 
                /* check if PSMv2 was running before */
                reg = sky2_pci_read16(hw, PSM_CONFIG_REG3);
-               if (reg & PCI_EXP_LNKCTL_ASPMC) {
-                       cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+               if (reg & PCI_EXP_LNKCTL_ASPMC)
                        /* restore the PCIe Link Control register */
-                       sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg);
-               }
+                       sky2_pci_write16(hw, pdev->pcie_cap + PCI_EXP_LNKCTL,
+                                        reg);
+
                sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
 
                /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
@@ -3411,10 +3512,10 @@ static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
        ecmd->phy_address = PHY_ADDR_MARV;
        if (sky2_is_copper(hw)) {
                ecmd->port = PORT_TP;
-               ecmd->speed = sky2->speed;
+               ethtool_cmd_speed_set(ecmd, sky2->speed);
                ecmd->supported |=  SUPPORTED_Autoneg | SUPPORTED_TP;
        } else {
-               ecmd->speed = SPEED_1000;
+               ethtool_cmd_speed_set(ecmd, SPEED_1000);
                ecmd->port = PORT_FIBRE;
                ecmd->supported |=  SUPPORTED_Autoneg | SUPPORTED_FIBRE;
        }
@@ -3450,8 +3551,9 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
                sky2->speed = -1;
        } else {
                u32 setting;
+               u32 speed = ethtool_cmd_speed(ecmd);
 
-               switch (ecmd->speed) {
+               switch (speed) {
                case SPEED_1000:
                        if (ecmd->duplex == DUPLEX_FULL)
                                setting = SUPPORTED_1000baseT_Full;
@@ -3484,7 +3586,7 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
                if ((setting & supported) == 0)
                        return -EINVAL;
 
-               sky2->speed = ecmd->speed;
+               sky2->speed = speed;
                sky2->duplex = ecmd->duplex;
                sky2->flags &= ~SKY2_FLAG_AUTO_SPEED;
        }
@@ -3552,28 +3654,6 @@ static const struct sky2_stat {
        { "tx_fifo_underrun", GM_TXE_FIFO_UR },
 };
 
-static u32 sky2_get_rx_csum(struct net_device *dev)
-{
-       struct sky2_port *sky2 = netdev_priv(dev);
-
-       return !!(sky2->flags & SKY2_FLAG_RX_CHECKSUM);
-}
-
-static int sky2_set_rx_csum(struct net_device *dev, u32 data)
-{
-       struct sky2_port *sky2 = netdev_priv(dev);
-
-       if (data)
-               sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
-       else
-               sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
-
-       sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
-                    data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
-
-       return 0;
-}
-
 static u32 sky2_get_msglevel(struct net_device *netdev)
 {
        struct sky2_port *sky2 = netdev_priv(netdev);
@@ -3826,23 +3906,24 @@ static void sky2_led(struct sky2_port *sky2, enum led_mode mode)
 }
 
 /* blink LED's for finding board */
-static int sky2_phys_id(struct net_device *dev, u32 data)
+static int sky2_set_phys_id(struct net_device *dev,
+                           enum ethtool_phys_id_state state)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
-       unsigned int i;
-
-       if (data == 0)
-               data = UINT_MAX;
 
-       for (i = 0; i < data; i++) {
+       switch (state) {
+       case ETHTOOL_ID_ACTIVE:
+               return 1;       /* cycle on/off once per second */
+       case ETHTOOL_ID_INACTIVE:
+               sky2_led(sky2, MO_LED_NORM);
+               break;
+       case ETHTOOL_ID_ON:
                sky2_led(sky2, MO_LED_ON);
-               if (msleep_interruptible(500))
-                       break;
+               break;
+       case ETHTOOL_ID_OFF:
                sky2_led(sky2, MO_LED_OFF);
-               if (msleep_interruptible(500))
-                       break;
+               break;
        }
-       sky2_led(sky2, MO_LED_NORM);
 
        return 0;
 }
@@ -4083,34 +4164,6 @@ static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs,
        }
 }
 
-/* In order to do Jumbo packets on these chips, need to turn off the
- * transmit store/forward. Therefore checksum offload won't work.
- */
-static int no_tx_offload(struct net_device *dev)
-{
-       const struct sky2_port *sky2 = netdev_priv(dev);
-       const struct sky2_hw *hw = sky2->hw;
-
-       return dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U;
-}
-
-static int sky2_set_tx_csum(struct net_device *dev, u32 data)
-{
-       if (data && no_tx_offload(dev))
-               return -EINVAL;
-
-       return ethtool_op_set_tx_csum(dev, data);
-}
-
-
-static int sky2_set_tso(struct net_device *dev, u32 data)
-{
-       if (data && no_tx_offload(dev))
-               return -EINVAL;
-
-       return ethtool_op_set_tso(dev, data);
-}
-
 static int sky2_get_eeprom_len(struct net_device *dev)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
@@ -4213,31 +4266,46 @@ static int sky2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom
        return sky2_vpd_write(sky2->hw, cap, data, eeprom->offset, eeprom->len);
 }
 
-static int sky2_set_flags(struct net_device *dev, u32 data)
+static u32 sky2_fix_features(struct net_device *dev, u32 features)
 {
-       struct sky2_port *sky2 = netdev_priv(dev);
-       unsigned long old_feat = dev->features;
-       u32 supported = 0;
-       int rc;
+       const struct sky2_port *sky2 = netdev_priv(dev);
+       const struct sky2_hw *hw = sky2->hw;
 
-       if (!(sky2->hw->flags & SKY2_HW_RSS_BROKEN))
-               supported |= ETH_FLAG_RXHASH;
+       /* In order to do Jumbo packets on these chips, need to turn off the
+        * transmit store/forward. Therefore checksum offload won't work.
+        */
+       if (dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U) {
+               netdev_info(dev, "checksum offload not possible with jumbo frames\n");
+               features &= ~(NETIF_F_TSO|NETIF_F_SG|NETIF_F_ALL_CSUM);
+       }
 
-       if (!(sky2->hw->flags & SKY2_HW_VLAN_BROKEN))
-               supported |= ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN;
+       /* Some hardware requires receive checksum for RSS to work. */
+       if ( (features & NETIF_F_RXHASH) &&
+            !(features & NETIF_F_RXCSUM) &&
+            (sky2->hw->flags & SKY2_HW_RSS_CHKSUM)) {
+               netdev_info(dev, "receive hashing forces receive checksum\n");
+               features |= NETIF_F_RXCSUM;
+       }
 
-       printk(KERN_DEBUG "sky2 set_flags: supported %x data %x\n",
-              supported, data);
+       return features;
+}
+
+static int sky2_set_features(struct net_device *dev, u32 features)
+{
+       struct sky2_port *sky2 = netdev_priv(dev);
+       u32 changed = dev->features ^ features;
 
-       rc = ethtool_op_set_flags(dev, data, supported);
-       if (rc)
-               return rc;
+       if (changed & NETIF_F_RXCSUM) {
+               u32 on = features & NETIF_F_RXCSUM;
+               sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+                            on ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
+       }
 
-       if ((old_feat ^ dev->features) & NETIF_F_RXHASH)
-               rx_set_rss(dev);
+       if (changed & NETIF_F_RXHASH)
+               rx_set_rss(dev, features);
 
-       if ((old_feat ^ dev->features) & NETIF_F_ALL_VLAN)
-               sky2_vlan_mode(dev);
+       if (changed & (NETIF_F_HW_VLAN_TX|NETIF_F_HW_VLAN_RX))
+               sky2_vlan_mode(dev, features);
 
        return 0;
 }
@@ -4257,11 +4325,6 @@ static const struct ethtool_ops sky2_ethtool_ops = {
        .get_eeprom_len = sky2_get_eeprom_len,
        .get_eeprom     = sky2_get_eeprom,
        .set_eeprom     = sky2_set_eeprom,
-       .set_sg         = ethtool_op_set_sg,
-       .set_tx_csum    = sky2_set_tx_csum,
-       .set_tso        = sky2_set_tso,
-       .get_rx_csum    = sky2_get_rx_csum,
-       .set_rx_csum    = sky2_set_rx_csum,
        .get_strings    = sky2_get_strings,
        .get_coalesce   = sky2_get_coalesce,
        .set_coalesce   = sky2_set_coalesce,
@@ -4269,11 +4332,9 @@ static const struct ethtool_ops sky2_ethtool_ops = {
        .set_ringparam  = sky2_set_ringparam,
        .get_pauseparam = sky2_get_pauseparam,
        .set_pauseparam = sky2_set_pauseparam,
-       .phys_id        = sky2_phys_id,
+       .set_phys_id    = sky2_set_phys_id,
        .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
@@ -4553,6 +4614,8 @@ static const struct net_device_ops sky2_netdev_ops[2] = {
        .ndo_set_mac_address    = sky2_set_mac_address,
        .ndo_set_multicast_list = sky2_set_multicast,
        .ndo_change_mtu         = sky2_change_mtu,
+       .ndo_fix_features       = sky2_fix_features,
+       .ndo_set_features       = sky2_set_features,
        .ndo_tx_timeout         = sky2_tx_timeout,
        .ndo_get_stats64        = sky2_get_stats,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -4568,6 +4631,8 @@ static const struct net_device_ops sky2_netdev_ops[2] = {
        .ndo_set_mac_address    = sky2_set_mac_address,
        .ndo_set_multicast_list = sky2_set_multicast,
        .ndo_change_mtu         = sky2_change_mtu,
+       .ndo_fix_features       = sky2_fix_features,
+       .ndo_set_features       = sky2_set_features,
        .ndo_tx_timeout         = sky2_tx_timeout,
        .ndo_get_stats64        = sky2_get_stats,
   },
@@ -4600,7 +4665,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
        /* Auto speed and flow control */
        sky2->flags = SKY2_FLAG_AUTO_SPEED | SKY2_FLAG_AUTO_PAUSE;
        if (hw->chip_id != CHIP_ID_YUKON_XL)
-               sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
+               dev->hw_features |= NETIF_F_RXCSUM;
 
        sky2->flow_mode = FC_BOTH;
 
@@ -4619,18 +4684,21 @@ 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;
+       dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO;
 
        if (highmem)
                dev->features |= NETIF_F_HIGHDMA;
 
        /* Enable receive hashing unless hardware is known broken */
        if (!(hw->flags & SKY2_HW_RSS_BROKEN))
-               dev->features |= NETIF_F_RXHASH;
+               dev->hw_features |= NETIF_F_RXHASH;
+
+       if (!(hw->flags & SKY2_HW_VLAN_BROKEN)) {
+               dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               dev->vlan_features |= SKY2_VLAN_OFFLOADS;
+       }
 
-       if (!(hw->flags & SKY2_HW_VLAN_BROKEN))
-               dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+       dev->features |= dev->hw_features;
 
        /* read the mac address */
        memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN);
@@ -4717,9 +4785,11 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
                "UL 2",         /* 0xba */
                "Unknown",      /* 0xbb */
                "Optima",       /* 0xbc */
+               "Optima Prime", /* 0xbd */
+               "Optima 2",     /* 0xbe */
        };
 
-       if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_OPT)
+       if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_OP_2)
                strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
        else
                snprintf(buf, sz, "(chip %#x)", chipid);