diff options
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r-- | drivers/net/sky2.c | 358 |
1 files changed, 214 insertions, 144 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index ff8d262dc27..57339da7632 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -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); |