diff options
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc.h | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_defines.h | 39 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 95 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_hw.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_i225.c | 156 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_i225.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_mac.c | 16 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_main.c | 48 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 256 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/igc/igc_regs.h | 16 |
10 files changed, 414 insertions, 223 deletions
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index a2d260165df3..3070dfdb7eb4 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -117,6 +117,9 @@ struct igc_ring { struct igc_adapter { struct net_device *netdev; + struct ethtool_eee eee; + u16 eee_advert; + unsigned long state; unsigned int flags; unsigned int num_q_vectors; @@ -207,8 +210,6 @@ struct igc_adapter { struct sk_buff *ptp_tx_skb; struct hwtstamp_config tstamp_config; unsigned long ptp_tx_start; - unsigned long last_rx_ptp_check; - unsigned long last_rx_timestamp; unsigned int ptp_flags; /* System time value lock */ spinlock_t tmreg_lock; @@ -255,6 +256,7 @@ extern char igc_driver_name[]; #define IGC_FLAG_MEDIA_RESET BIT(10) #define IGC_FLAG_MAS_ENABLE BIT(12) #define IGC_FLAG_HAS_MSIX BIT(13) +#define IGC_FLAG_EEE BIT(14) #define IGC_FLAG_VLAN_PROMISC BIT(15) #define IGC_FLAG_RX_LEGACY BIT(16) #define IGC_FLAG_TSN_QBV_ENABLED BIT(17) @@ -545,7 +547,6 @@ void igc_ptp_init(struct igc_adapter *adapter); void igc_ptp_reset(struct igc_adapter *adapter); void igc_ptp_suspend(struct igc_adapter *adapter); void igc_ptp_stop(struct igc_adapter *adapter); -void igc_ptp_rx_rgtstamp(struct igc_q_vector *q_vector, struct sk_buff *skb); void igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, void *va, struct sk_buff *skb); int igc_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr); diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 186deb1d9375..f1f464967f87 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -323,7 +323,6 @@ /* Advanced Receive Descriptor bit definitions */ #define IGC_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */ -#define IGC_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */ #define IGC_RXDEXT_STATERR_CE 0x01000000 #define IGC_RXDEXT_STATERR_SE 0x02000000 @@ -384,7 +383,6 @@ #define IGC_FTQF_MASK_PROTO_BP 0x10000000 /* Time Sync Receive Control bit definitions */ -#define IGC_TSYNCRXCTL_VALID 0x00000001 /* Rx timestamp valid */ #define IGC_TSYNCRXCTL_TYPE_MASK 0x0000000E /* Rx type mask */ #define IGC_TSYNCRXCTL_TYPE_L2_V2 0x00 #define IGC_TSYNCRXCTL_TYPE_L4_V1 0x02 @@ -511,4 +509,41 @@ /* Maximum size of the MTA register table in all supported adapters */ #define MAX_MTA_REG 128 +/* EEE defines */ +#define IGC_IPCNFG_EEE_2_5G_AN 0x00000010 /* IPCNFG EEE Ena 2.5G AN */ +#define IGC_IPCNFG_EEE_1G_AN 0x00000008 /* IPCNFG EEE Ena 1G AN */ +#define IGC_IPCNFG_EEE_100M_AN 0x00000004 /* IPCNFG EEE Ena 100M AN */ +#define IGC_EEER_EEE_NEG 0x20000000 /* EEE capability nego */ +#define IGC_EEER_TX_LPI_EN 0x00010000 /* EEER Tx LPI Enable */ +#define IGC_EEER_RX_LPI_EN 0x00020000 /* EEER Rx LPI Enable */ +#define IGC_EEER_LPI_FC 0x00040000 /* EEER Ena on Flow Cntrl */ +#define IGC_EEE_SU_LPI_CLK_STP 0x00800000 /* EEE LPI Clock Stop */ + +/* LTR defines */ +#define IGC_LTRC_EEEMS_EN 0x00000020 /* Enable EEE LTR max send */ +#define IGC_RXPBS_SIZE_I225_MASK 0x0000003F /* Rx packet buffer size */ +#define IGC_TW_SYSTEM_1000_MASK 0x000000FF +/* Minimum time for 100BASE-T where no data will be transmit following move out + * of EEE LPI Tx state + */ +#define IGC_TW_SYSTEM_100_MASK 0x0000FF00 +#define IGC_TW_SYSTEM_100_SHIFT 8 +#define IGC_DMACR_DMAC_EN 0x80000000 /* Enable DMA Coalescing */ +#define IGC_DMACR_DMACTHR_MASK 0x00FF0000 +#define IGC_DMACR_DMACTHR_SHIFT 16 +/* Reg val to set scale to 1024 nsec */ +#define IGC_LTRMINV_SCALE_1024 2 +/* Reg val to set scale to 32768 nsec */ +#define IGC_LTRMINV_SCALE_32768 3 +/* Reg val to set scale to 1024 nsec */ +#define IGC_LTRMAXV_SCALE_1024 2 +/* Reg val to set scale to 32768 nsec */ +#define IGC_LTRMAXV_SCALE_32768 3 +#define IGC_LTRMINV_LTRV_MASK 0x000003FF /* LTR minimum value */ +#define IGC_LTRMAXV_LTRV_MASK 0x000003FF /* LTR maximum value */ +#define IGC_LTRMINV_LSNP_REQ 0x00008000 /* LTR Snoop Requirement */ +#define IGC_LTRMINV_SCALE_SHIFT 10 +#define IGC_LTRMAXV_LSNP_REQ 0x00008000 /* LTR Snoop Requirement */ +#define IGC_LTRMAXV_SCALE_SHIFT 10 + #endif /* _IGC_DEFINES_H_ */ diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 735f3fb47dca..ac331116ea08 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -4,6 +4,7 @@ /* ethtool support for igc */ #include <linux/if_vlan.h> #include <linux/pm_runtime.h> +#include <linux/mdio.h> #include "igc.h" #include "igc_diag.h" @@ -1548,6 +1549,98 @@ static int igc_ethtool_set_priv_flags(struct net_device *netdev, u32 priv_flags) return 0; } +static int igc_ethtool_get_eee(struct net_device *netdev, + struct ethtool_eee *edata) +{ + struct igc_adapter *adapter = netdev_priv(netdev); + struct igc_hw *hw = &adapter->hw; + u32 eeer; + + if (hw->dev_spec._base.eee_enable) + edata->advertised = + mmd_eee_adv_to_ethtool_adv_t(adapter->eee_advert); + + *edata = adapter->eee; + edata->supported = SUPPORTED_Autoneg; + + eeer = rd32(IGC_EEER); + + /* EEE status on negotiated link */ + if (eeer & IGC_EEER_EEE_NEG) + edata->eee_active = true; + + if (eeer & IGC_EEER_TX_LPI_EN) + edata->tx_lpi_enabled = true; + + edata->eee_enabled = hw->dev_spec._base.eee_enable; + + edata->advertised = SUPPORTED_Autoneg; + edata->lp_advertised = SUPPORTED_Autoneg; + + /* Report correct negotiated EEE status for devices that + * wrongly report EEE at half-duplex + */ + if (adapter->link_duplex == HALF_DUPLEX) { + edata->eee_enabled = false; + edata->eee_active = false; + edata->tx_lpi_enabled = false; + edata->advertised &= ~edata->advertised; + } + + return 0; +} + +static int igc_ethtool_set_eee(struct net_device *netdev, + struct ethtool_eee *edata) +{ + struct igc_adapter *adapter = netdev_priv(netdev); + struct igc_hw *hw = &adapter->hw; + struct ethtool_eee eee_curr; + s32 ret_val; + + memset(&eee_curr, 0, sizeof(struct ethtool_eee)); + + ret_val = igc_ethtool_get_eee(netdev, &eee_curr); + if (ret_val) { + netdev_err(netdev, + "Problem setting EEE advertisement options\n"); + return -EINVAL; + } + + if (eee_curr.eee_enabled) { + if (eee_curr.tx_lpi_enabled != edata->tx_lpi_enabled) { + netdev_err(netdev, + "Setting EEE tx-lpi is not supported\n"); + return -EINVAL; + } + + /* Tx LPI timer is not implemented currently */ + if (edata->tx_lpi_timer) { + netdev_err(netdev, + "Setting EEE Tx LPI timer is not supported\n"); + return -EINVAL; + } + } else if (!edata->eee_enabled) { + netdev_err(netdev, + "Setting EEE options are not supported with EEE disabled\n"); + return -EINVAL; + } + + adapter->eee_advert = ethtool_adv_to_mmd_eee_adv_t(edata->advertised); + if (hw->dev_spec._base.eee_enable != edata->eee_enabled) { + hw->dev_spec._base.eee_enable = edata->eee_enabled; + adapter->flags |= IGC_FLAG_EEE; + + /* reset link */ + if (netif_running(netdev)) + igc_reinit_locked(adapter); + else + igc_reset(adapter); + } + + return 0; +} + static int igc_ethtool_begin(struct net_device *netdev) { struct igc_adapter *adapter = netdev_priv(netdev); @@ -1829,6 +1922,8 @@ static const struct ethtool_ops igc_ethtool_ops = { .set_channels = igc_ethtool_set_channels, .get_priv_flags = igc_ethtool_get_priv_flags, .set_priv_flags = igc_ethtool_set_priv_flags, + .get_eee = igc_ethtool_get_eee, + .set_eee = igc_ethtool_set_eee, .begin = igc_ethtool_begin, .complete = igc_ethtool_complete, .get_link_ksettings = igc_ethtool_get_link_ksettings, diff --git a/drivers/net/ethernet/intel/igc/igc_hw.h b/drivers/net/ethernet/intel/igc/igc_hw.h index af34ae310327..2ab7d9fab6af 100644 --- a/drivers/net/ethernet/intel/igc/igc_hw.h +++ b/drivers/net/ethernet/intel/igc/igc_hw.h @@ -191,6 +191,7 @@ struct igc_fc_info { struct igc_dev_spec_base { bool clear_semaphore_once; + bool eee_enable; }; struct igc_hw { diff --git a/drivers/net/ethernet/intel/igc/igc_i225.c b/drivers/net/ethernet/intel/igc/igc_i225.c index c25f555aaf82..8b67d9b49a83 100644 --- a/drivers/net/ethernet/intel/igc/igc_i225.c +++ b/drivers/net/ethernet/intel/igc/igc_i225.c @@ -488,3 +488,159 @@ s32 igc_init_nvm_params_i225(struct igc_hw *hw) } return 0; } + +/** + * igc_set_eee_i225 - Enable/disable EEE support + * @hw: pointer to the HW structure + * @adv2p5G: boolean flag enabling 2.5G EEE advertisement + * @adv1G: boolean flag enabling 1G EEE advertisement + * @adv100M: boolean flag enabling 100M EEE advertisement + * + * Enable/disable EEE based on setting in dev_spec structure. + **/ +s32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G, + bool adv100M) +{ + u32 ipcnfg, eeer; + + ipcnfg = rd32(IGC_IPCNFG); + eeer = rd32(IGC_EEER); + + /* enable or disable per user setting */ + if (hw->dev_spec._base.eee_enable) { + u32 eee_su = rd32(IGC_EEE_SU); + + if (adv100M) + ipcnfg |= IGC_IPCNFG_EEE_100M_AN; + else + ipcnfg &= ~IGC_IPCNFG_EEE_100M_AN; + + if (adv1G) + ipcnfg |= IGC_IPCNFG_EEE_1G_AN; + else + ipcnfg &= ~IGC_IPCNFG_EEE_1G_AN; + + if (adv2p5G) + ipcnfg |= IGC_IPCNFG_EEE_2_5G_AN; + else + ipcnfg &= ~IGC_IPCNFG_EEE_2_5G_AN; + + eeer |= (IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN | + IGC_EEER_LPI_FC); + + /* This bit should not be set in normal operation. */ + if (eee_su & IGC_EEE_SU_LPI_CLK_STP) + hw_dbg("LPI Clock Stop Bit should not be set!\n"); + } else { + ipcnfg &= ~(IGC_IPCNFG_EEE_2_5G_AN | IGC_IPCNFG_EEE_1G_AN | + IGC_IPCNFG_EEE_100M_AN); + eeer &= ~(IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN | + IGC_EEER_LPI_FC); + } + wr32(IGC_IPCNFG, ipcnfg); + wr32(IGC_EEER, eeer); + rd32(IGC_IPCNFG); + rd32(IGC_EEER); + + return IGC_SUCCESS; +} + +/* igc_set_ltr_i225 - Set Latency Tolerance Reporting thresholds + * @hw: pointer to the HW structure + * @link: bool indicating link status + * + * Set the LTR thresholds based on the link speed (Mbps), EEE, and DMAC + * settings, otherwise specify that there is no LTR requirement. + */ +s32 igc_set_ltr_i225(struct igc_hw *hw, bool link) +{ + u32 tw_system, ltrc, ltrv, ltr_min, ltr_max, scale_min, scale_max; + u16 speed, duplex; + s32 size; + + /* If we do not have link, LTR thresholds are zero. */ + if (link) { + hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex); + + /* Check if using copper interface with EEE enabled or if the + * link speed is 10 Mbps. + */ + if (hw->dev_spec._base.eee_enable && + speed != SPEED_10) { + /* EEE enabled, so send LTRMAX threshold. */ + ltrc = rd32(IGC_LTRC) | + IGC_LTRC_EEEMS_EN; + wr32(IGC_LTRC, ltrc); + + /* Calculate tw_system (nsec). */ + if (speed == SPEED_100) { + tw_system = ((rd32(IGC_EEE_SU) & + IGC_TW_SYSTEM_100_MASK) >> + IGC_TW_SYSTEM_100_SHIFT) * 500; + } else { + tw_system = (rd32(IGC_EEE_SU) & + IGC_TW_SYSTEM_1000_MASK) * 500; + } + } else { + tw_system = 0; + } + + /* Get the Rx packet buffer size. */ + size = rd32(IGC_RXPBS) & + IGC_RXPBS_SIZE_I225_MASK; + + /* Calculations vary based on DMAC settings. */ + if (rd32(IGC_DMACR) & IGC_DMACR_DMAC_EN) { + size -= (rd32(IGC_DMACR) & + IGC_DMACR_DMACTHR_MASK) >> + IGC_DMACR_DMACTHR_SHIFT; + /* Convert size to bits. */ + size *= 1024 * 8; + } else { + /* Convert size to bytes, subtract the MTU, and then + * convert the size to bits. + */ + size *= 1024; + size *= 8; + } + + if (size < 0) { + hw_dbg("Invalid effective Rx buffer size %d\n", + size); + return -IGC_ERR_CONFIG; + } + + /* Calculate the thresholds. Since speed is in Mbps, simplify + * the calculation by multiplying size/speed by 1000 for result + * to be in nsec before dividing by the scale in nsec. Set the + * scale such that the LTR threshold fits in the register. + */ + ltr_min = (1000 * size) / speed; + ltr_max = ltr_min + tw_system; + scale_min = (ltr_min / 1024) < 1024 ? IGC_LTRMINV_SCALE_1024 : + IGC_LTRMINV_SCALE_32768; + scale_max = (ltr_max / 1024) < 1024 ? IGC_LTRMAXV_SCALE_1024 : + IGC_LTRMAXV_SCALE_32768; + ltr_min /= scale_min == IGC_LTRMINV_SCALE_1024 ? 1024 : 32768; + ltr_min -= 1; + ltr_max /= scale_max == IGC_LTRMAXV_SCALE_1024 ? 1024 : 32768; + ltr_max -= 1; + + /* Only write the LTR thresholds if they differ from before. */ + ltrv = rd32(IGC_LTRMINV); + if (ltr_min != (ltrv & IGC_LTRMINV_LTRV_MASK)) { + ltrv = IGC_LTRMINV_LSNP_REQ | ltr_min | + (scale_min << IGC_LTRMINV_SCALE_SHIFT); + wr32(IGC_LTRMINV, ltrv); + } + + ltrv = rd32(IGC_LTRMAXV); + if (ltr_max != (ltrv & IGC_LTRMAXV_LTRV_MASK)) { + ltrv = IGC_LTRMAXV_LSNP_REQ | ltr_max | + (scale_min << IGC_LTRMAXV_SCALE_SHIFT); + wr32(IGC_LTRMAXV, ltrv); + } + } + + return IGC_SUCCESS; +} diff --git a/drivers/net/ethernet/intel/igc/igc_i225.h b/drivers/net/ethernet/intel/igc/igc_i225.h index 7b66e1f9c0e6..dae47e4f16b0 100644 --- a/drivers/net/ethernet/intel/igc/igc_i225.h +++ b/drivers/net/ethernet/intel/igc/igc_i225.h @@ -9,5 +9,8 @@ void igc_release_swfw_sync_i225(struct igc_hw *hw, u16 mask); s32 igc_init_nvm_params_i225(struct igc_hw *hw); bool igc_get_flash_presence_i225(struct igc_hw *hw); +s32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G, + bool adv100M); +s32 igc_set_ltr_i225(struct igc_hw *hw, bool link); #endif diff --git a/drivers/net/ethernet/intel/igc/igc_mac.c b/drivers/net/ethernet/intel/igc/igc_mac.c index 410aeb01de5c..b47e7b0a6398 100644 --- a/drivers/net/ethernet/intel/igc/igc_mac.c +++ b/drivers/net/ethernet/intel/igc/igc_mac.c @@ -289,7 +289,6 @@ void igc_clear_hw_cntrs_base(struct igc_hw *hw) rd32(IGC_TNCRS); rd32(IGC_HTDPMC); rd32(IGC_TSCTC); - rd32(IGC_TSCTFC); rd32(IGC_MGTPRC); rd32(IGC_MGTPDC); @@ -307,6 +306,8 @@ void igc_clear_hw_cntrs_base(struct igc_hw *hw) rd32(IGC_ICRXDMTC); rd32(IGC_RPTHC); + rd32(IGC_TLPIC); + rd32(IGC_RLPIC); rd32(IGC_HGPTC); rd32(IGC_HGORCL); rd32(IGC_HGORCH); @@ -417,6 +418,11 @@ s32 igc_check_for_copper_link(struct igc_hw *hw) hw_dbg("Error configuring flow control\n"); out: + /* Now that we are aware of our link settings, we can set the LTR + * thresholds. + */ + ret_val = igc_set_ltr_i225(hw, link); + return ret_val; } @@ -462,10 +468,8 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw) * so we had to force link. In this case, we need to force the * configuration of the MAC to match the "fc" parameter. */ - if (mac->autoneg_failed) { - if (hw->phy.media_type == igc_media_type_copper) - ret_val = igc_force_mac_fc(hw); - } + if (mac->autoneg_failed) + ret_val = igc_force_mac_fc(hw); if (ret_val) { hw_dbg("Error forcing flow control settings\n"); @@ -477,7 +481,7 @@ s32 igc_config_fc_after_link_up(struct igc_hw *hw) * has completed, and if so, how the PHY and link partner has * flow control configured. */ - if (hw->phy.media_type == igc_media_type_copper && mac->autoneg) { + if (mac->autoneg) { /* Read the MII Status Register and check to see if AutoNeg * has completed. We read this twice because this reg has * some "sticky" (latched) bits. diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index c2f41a558fd6..e544f0599dcf 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -61,16 +61,6 @@ enum latency_range { latency_invalid = 255 }; -/** - * igc_power_down_link - Power down the phy/serdes link - * @adapter: address of board private structure - */ -static void igc_power_down_link(struct igc_adapter *adapter) -{ - if (adapter->hw.phy.media_type == igc_media_type_copper) - igc_power_down_phy_copper_base(&adapter->hw); -} - void igc_reset(struct igc_adapter *adapter) { struct net_device *dev = adapter->netdev; @@ -102,8 +92,11 @@ void igc_reset(struct igc_adapter *adapter) if (hw->mac.ops.init_hw(hw)) netdev_err(dev, "Error on hardware initialization\n"); + /* Re-establish EEE setting */ + igc_set_eee_i225(hw, true, true, true); + if (!netif_running(adapter->netdev)) - igc_power_down_link(adapter); + igc_power_down_phy_copper_base(&adapter->hw); /* Re-enable PTP, where applicable. */ igc_ptp_reset(adapter); @@ -122,8 +115,7 @@ static void igc_power_up_link(struct igc_adapter *adapter) { igc_reset_phy(&adapter->hw); - if (adapter->hw.phy.media_type == igc_media_type_copper) - igc_power_up_phy_copper(&adapter->hw); + igc_power_up_phy_copper(&adapter->hw); igc_setup_link(&adapter->hw); } @@ -1476,9 +1468,9 @@ static inline void igc_rx_hash(struct igc_ring *ring, * @rx_desc: pointer to the EOP Rx descriptor * @skb: pointer to current skb being populated * - * This function checks the ring, descriptor, and packet information in - * order to populate the hash, checksum, VLAN, timestamp, protocol, and - * other fields within the skb. + * This function checks the ring, descriptor, and packet information in order + * to populate the hash, checksum, VLAN, protocol, and other fields within the + * skb. */ static void igc_process_skb_fields(struct igc_ring *rx_ring, union igc_adv_rx_desc *rx_desc, @@ -1488,10 +1480,6 @@ static void igc_process_skb_fields(struct igc_ring *rx_ring, igc_rx_checksum(rx_ring, rx_desc, skb); - if (igc_test_staterr(rx_desc, IGC_RXDADV_STAT_TS) && - !igc_test_staterr(rx_desc, IGC_RXDADV_STAT_TSIP)) - igc_ptp_rx_rgtstamp(rx_ring->q_vector, skb); - skb_record_rx_queue(skb, rx_ring->queue_index); skb->protocol = eth_type_trans(skb, rx_ring->netdev); @@ -1972,7 +1960,7 @@ static int igc_clean_rx_irq(struct igc_q_vector *q_vector, const int budget) /* probably a little skewed due to removing CRC */ total_bytes += skb->len; - /* populate checksum, timestamp, VLAN, and protocol */ + /* populate checksum, VLAN, and protocol */ igc_process_skb_fields(rx_ring, rx_desc, skb); napi_gro_receive(&q_vector->napi, skb); @@ -3741,7 +3729,6 @@ void igc_update_stats(struct igc_adapter *adapter) adapter->stats.algnerrc += rd32(IGC_ALGNERRC); adapter->stats.tsctc += rd32(IGC_TSCTC); - adapter->stats.tsctfc += rd32(IGC_TSCTFC); adapter->stats.iac += rd32(IGC_IAC); adapter->stats.icrxoc += rd32(IGC_ICRXOC); @@ -4252,6 +4239,15 @@ static void igc_watchdog_task(struct work_struct *work) (ctrl & IGC_CTRL_RFCE) ? "RX" : (ctrl & IGC_CTRL_TFCE) ? "TX" : "None"); + /* disable EEE if enabled */ + if ((adapter->flags & IGC_FLAG_EEE) && + adapter->link_duplex == HALF_DUPLEX) { + netdev_info(netdev, + "EEE Disabled: unsupported at half duplex. Re-enable using ethtool when at full duplex\n"); + adapter->hw.dev_spec._base.eee_enable = false; + adapter->flags &= ~IGC_FLAG_EEE; + } + /* check if SmartSpeed worked */ igc_check_downshift(hw); if (phy->speed_downgraded) @@ -4608,7 +4604,7 @@ err_set_queues: igc_free_irq(adapter); err_req_irq: igc_release_hw_control(adapter); - igc_power_down_link(adapter); + igc_power_down_phy_copper_base(&adapter->hw); igc_free_all_rx_resources(adapter); err_setup_rx: igc_free_all_tx_resources(adapter); @@ -5182,6 +5178,10 @@ static int igc_probe(struct pci_dev *pdev, netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr); dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE); + /* Disable EEE for internal PHY devices */ + hw->dev_spec._base.eee_enable = false; + adapter->flags &= ~IGC_FLAG_EEE; + igc_set_eee_i225(hw, false, false, false); pm_runtime_put_noidle(&pdev->dev); @@ -5302,7 +5302,7 @@ static int __igc_shutdown(struct pci_dev *pdev, bool *enable_wake, wake = wufc || adapter->en_mng_pt; if (!wake) - igc_power_down_link(adapter); + igc_power_down_phy_copper_base(&adapter->hw); else igc_power_up_link(adapter); diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 0d746f8588c8..e67d4655b47e 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -205,78 +205,66 @@ void igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, void *va, ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, adjust); } -/** - * igc_ptp_rx_rgtstamp - retrieve Rx timestamp stored in register - * @q_vector: Pointer to interrupt specific structure - * @skb: Buffer containing timestamp and packet - * - * This function is meant to retrieve a timestamp from the internal registers - * of the adapter and store it in the skb. - */ -void igc_ptp_rx_rgtstamp(struct igc_q_vector *q_vector, - struct sk_buff *skb) +static void igc_ptp_disable_rx_timestamp(struct igc_adapter *adapter) { - struct igc_adapter *adapter = q_vector->adapter; struct igc_hw *hw = &adapter->hw; - u64 regval; - - /* If this bit is set, then the RX registers contain the time - * stamp. No other packet will be time stamped until we read - * these registers, so read the registers to make them - * available again. Because only one packet can be time - * stamped at a time, we know that the register values must - * belong to this one here and therefore we don't need to - * compare any of the additional attributes stored for it. - * - * If nothing went wrong, then it should have a shared - * tx_flags that we can turn into a skb_shared_hwtstamps. - */ - if (!(rd32(IGC_TSYNCRXCTL) & IGC_TSYNCRXCTL_VALID)) - return; + u32 val; + int i; - regval = rd32(IGC_RXSTMPL); - regval |= (u64)rd32(IGC_RXSTMPH) << 32; + wr32(IGC_TSYNCRXCTL, 0); - igc_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval); + for (i = 0; i < adapter->num_rx_queues; i++) { + val = rd32(IGC_SRRCTL(i)); + val &= ~IGC_SRRCTL_TIMESTAMP; + wr32(IGC_SRRCTL(i), val); + } - /* Update the last_rx_timestamp timer in order to enable watchdog check - * for error case of latched timestamp on a dropped packet. - */ - adapter->last_rx_timestamp = jiffies; + val = rd32(IGC_RXPBS); + val &= ~IGC_RXPBS_CFG_TS_EN; + wr32(IGC_RXPBS, val); } -/** - * igc_ptp_enable_tstamp_rxqueue - Enable RX timestamp for a queue - * @rx_ring: Pointer to RX queue - * @timer: Index for timer - * - * This function enables RX timestamping for a queue, and selects - * which 1588 timer will provide the timestamp. - */ -static void igc_ptp_enable_tstamp_rxqueue(struct igc_adapter *adapter, - struct igc_ring *rx_ring, u8 timer) +static void igc_ptp_enable_rx_timestamp(struct igc_adapter *adapter) { struct igc_hw *hw = &adapter->hw; - int reg_idx = rx_ring->reg_idx; - u32 srrctl = rd32(IGC_SRRCTL(reg_idx)); + u32 val; + int i; + + val = rd32(IGC_RXPBS); + val |= IGC_RXPBS_CFG_TS_EN; + wr32(IGC_RXPBS, val); - srrctl |= IGC_SRRCTL_TIMESTAMP; - srrctl |= IGC_SRRCTL_TIMER1SEL(timer); - srrctl |= IGC_SRRCTL_TIMER0SEL(timer); + for (i = 0; i < adapter->num_rx_queues; i++) { + val = rd32(IGC_SRRCTL(i)); + /* FIXME: For now, only support retrieving RX timestamps from + * timer 0. + */ + val |= IGC_SRRCTL_TIMER1SEL(0) | IGC_SRRCTL_TIMER0SEL(0) | + IGC_SRRCTL_TIMESTAMP; + wr32(IGC_SRRCTL(i), val); + } - wr32(IGC_SRRCTL(reg_idx), srrctl); + val = IGC_TSYNCRXCTL_ENABLED | IGC_TSYNCRXCTL_TYPE_ALL | + IGC_TSYNCRXCTL_RXSYNSIG; + wr32(IGC_TSYNCRXCTL, val); } -static void igc_ptp_enable_tstamp_all_rxqueues(struct igc_adapter *adapter, - u8 timer) +static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) { - int i; + struct igc_hw *hw = &adapter->hw; - for (i = 0; i < adapter->num_rx_queues; i++) { - struct igc_ring *ring = adapter->rx_ring[i]; + wr32(IGC_TSYNCTXCTL, 0); +} - igc_ptp_enable_tstamp_rxqueue(adapter, ring, timer); - } +static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + + wr32(IGC_TSYNCTXCTL, IGC_TSYNCTXCTL_ENABLED | IGC_TSYNCTXCTL_TXSYNSIG); + + /* Read TXSTMP registers to discard any timestamp previously stored. */ + rd32(IGC_TXSTMPL); + rd32(IGC_TXSTMPH); } /** @@ -284,37 +272,21 @@ static void igc_ptp_enable_tstamp_all_rxqueues(struct igc_adapter *adapter, * @adapter: networking device structure * @config: hwtstamp configuration * - * Outgoing time stamping can be enabled and disabled. Play nice and - * disable it when requested, although it shouldn't case any overhead - * when no packet needs it. At most one packet in the queue may be - * marked for time stamping, otherwise it would be impossible to tell - * for sure to which packet the hardware time stamp belongs. - * - * Incoming time stamping has to be configured via the hardware - * filters. Not all combinations are supported, in particular event - * type has to be specified. Matching the kind of event packet is - * not supported, with the exception of "all V2 events regardless of - * level 2 or 4". - * + * Return: 0 in case of success, negative errno code otherwise. */ static int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, struct hwtstamp_config *config) { - u32 tsync_tx_ctl = IGC_TSYNCTXCTL_ENABLED; - u32 tsync_rx_ctl = IGC_TSYNCRXCTL_ENABLED; - struct igc_hw *hw = &adapter->hw; - u32 tsync_rx_cfg = 0; - bool is_l4 = false; - u32 regval; - /* reserved for future extensions */ if (config->flags) return -EINVAL; switch (config->tx_type) { case HWTSTAMP_TX_OFF: - tsync_tx_ctl = 0; + igc_ptp_disable_tx_timestamp(adapter); + break; case HWTSTAMP_TX_ON: + igc_ptp_enable_tx_timestamp(adapter); break; default: return -ERANGE; @@ -322,18 +294,10 @@ static int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: - tsync_rx_ctl = 0; + igc_ptp_disable_rx_timestamp(adapter); break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: - tsync_rx_ctl |= IGC_TSYNCRXCTL_TYPE_L4_V1; - tsync_rx_cfg = IGC_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE; - is_l4 = true; - break; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: - tsync_rx_ctl |= IGC_TSYNCRXCTL_TYPE_L4_V1; - tsync_rx_cfg = IGC_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE; - is_l4 = true; - break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: @@ -343,99 +307,36 @@ static int igc_ptp_set_timestamp_mode(struct igc_adapter *adapter, case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - tsync_rx_ctl |= IGC_TSYNCRXCTL_TYPE_EVENT_V2; - config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; - is_l4 = true; - break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_NTP_ALL: case HWTSTAMP_FILTER_ALL: - tsync_rx_ctl |= IGC_TSYNCRXCTL_TYPE_ALL; + igc_ptp_enable_rx_timestamp(adapter); config->rx_filter = HWTSTAMP_FILTER_ALL; break; - /* fall through */ default: - config->rx_filter = HWTSTAMP_FILTER_NONE; return -ERANGE; } - /* Per-packet timestamping only works if all packets are - * timestamped, so enable timestamping in all packets as long - * as one Rx filter was configured. - */ - if (tsync_rx_ctl) { - tsync_rx_ctl = IGC_TSYNCRXCTL_ENABLED; - tsync_rx_ctl |= IGC_TSYNCRXCTL_TYPE_ALL; - tsync_rx_ctl |= IGC_TSYNCRXCTL_RXSYNSIG; - config->rx_filter = HWTSTAMP_FILTER_ALL; - is_l4 = true; - - if (hw->mac.type == igc_i225) { - regval = rd32(IGC_RXPBS); - regval |= IGC_RXPBS_CFG_TS_EN; - wr32(IGC_RXPBS, regval); - - /* FIXME: For now, only support retrieving RX - * timestamps from timer 0 - */ - igc_ptp_enable_tstamp_all_rxqueues(adapter, 0); - } - } - - if (tsync_tx_ctl) { - tsync_tx_ctl = IGC_TSYNCTXCTL_ENABLED; - tsync_tx_ctl |= IGC_TSYNCTXCTL_TXSYNSIG; - } - - /* enable/disable TX */ - regval = rd32(IGC_TSYNCTXCTL); - regval &= ~IGC_TSYNCTXCTL_ENABLED; - regval |= tsync_tx_ctl; - wr32(IGC_TSYNCTXCTL, regval); - - /* enable/disable RX */ - regval = rd32(IGC_TSYNCRXCTL); - regval &= ~(IGC_TSYNCRXCTL_ENABLED | IGC_TSYNCRXCTL_TYPE_MASK); - regval |= tsync_rx_ctl; - wr32(IGC_TSYNCRXCTL, regval); - - /* define which PTP packets are time stamped */ - wr32(IGC_TSYNCRXCFG, tsync_rx_cfg); - - /* L4 Queue Filter[3]: filter by destination port and protocol */ - if (is_l4) { - u32 ftqf = (IPPROTO_UDP /* UDP */ - | IGC_FTQF_VF_BP /* VF not compared */ - | IGC_FTQF_1588_TIME_STAMP /* Enable Timestamp */ - | IGC_FTQF_MASK); /* mask all inputs */ - ftqf &= ~IGC_FTQF_MASK_PROTO_BP; /* enable protocol check */ - - wr32(IGC_IMIR(3), htons(PTP_EV_PORT)); - wr32(IGC_IMIREXT(3), - (IGC_IMIREXT_SIZE_BP | IGC_IMIREXT_CTRL_BP)); - wr32(IGC_FTQF(3), ftqf); - } else { - wr32(IGC_FTQF(3), IGC_FTQF_MASK); - } - wrfl(); + return 0; +} - /* clear TX/RX time stamp registers, just to be sure */ - regval = rd32(IGC_TXSTMPL); - regval = rd32(IGC_TXSTMPH); - regval = rd32(IGC_RXSTMPL); - regval = rd32(IGC_RXSTMPH); +static void igc_ptp_tx_timeout(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; - return 0; + dev_kfree_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; + adapter->tx_hwtstamp_timeouts++; + clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state); + /* Clear the tx valid bit in TSYNCTXCTL register to enable interrupt. */ + rd32(IGC_TXSTMPH); + netdev_warn(adapter->netdev, "Tx timestamp timeout\n"); } void igc_ptp_tx_hang(struct igc_adapter *adapter) { bool timeout = time_is_before_jiffies(adapter->ptp_tx_start + IGC_PTP_TX_TIMEOUT); - struct igc_hw *hw = &adapter->hw; - - if (!adapter->ptp_tx_skb) - return; if (!test_bit(__IGC_PTP_TX_IN_PROGRESS, &adapter->state)) return; @@ -446,15 +347,7 @@ void igc_ptp_tx_hang(struct igc_adapter *adapter) */ if (timeout) { cancel_work_sync(&adapter->ptp_tx_work); - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; - clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state); - adapter->tx_hwtstamp_timeouts++; - /* Clear the Tx valid bit in TSYNCTXCTL register to enable - * interrupt - */ - rd32(IGC_TXSTMPH); - netdev_warn(adapter->netdev, "Clearing Tx timestamp hang\n"); + igc_ptp_tx_timeout(adapter); } } @@ -473,6 +366,9 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) struct igc_hw *hw = &adapter->hw; u64 regval; + if (WARN_ON_ONCE(!skb)) + return; + regval = rd32(IGC_TXSTMPL); regval |= (u64)rd32(IGC_TXSTMPH) << 32; igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval); @@ -504,20 +400,12 @@ static void igc_ptp_tx_work(struct work_struct *work) struct igc_hw *hw = &adapter->hw; u32 tsynctxctl; - if (!adapter->ptp_tx_skb) + if (!test_bit(__IGC_PTP_TX_IN_PROGRESS, &adapter->state)) return; if (time_is_before_jiffies(adapter->ptp_tx_start + IGC_PTP_TX_TIMEOUT)) { - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; - clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state); - adapter->tx_hwtstamp_timeouts++; - /* Clear the tx valid bit in TSYNCTXCTL register to enable - * interrupt - */ - rd32(IGC_TXSTMPH); - netdev_warn(adapter->netdev, "Clearing Tx timestamp hang\n"); + igc_ptp_tx_timeout(adapter); return; } @@ -634,11 +522,9 @@ void igc_ptp_suspend(struct igc_adapter *adapter) return; cancel_work_sync(&adapter->ptp_tx_work); - if (adapter->ptp_tx_skb) { - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; - clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state); - } + dev_kfree_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; + clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state); } /** diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index 232e82dec62e..1c46cec5a799 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -181,13 +181,14 @@ #define IGC_MPTC 0x040F0 /* Multicast Packets Tx Count - R/clr */ #define IGC_BPTC 0x040F4 /* Broadcast Packets Tx Count - R/clr */ #define IGC_TSCTC 0x040F8 /* TCP Segmentation Context Tx - R/clr */ -#define IGC_TSCTFC 0x040FC /* TCP Segmentation Context Tx Fail - R/clr */ #define IGC_IAC 0x04100 /* Interrupt Assertion Count */ #define IGC_ICTXPTC 0x0410C /* Interrupt Cause Tx Pkt Timer Expire Count */ #define IGC_ICTXATC 0x04110 /* Interrupt Cause Tx Abs Timer Expire Count */ #define IGC_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ #define IGC_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Min Thresh Count */ #define IGC_RPTHC 0x04104 /* Rx Packets To Host */ +#define IGC_TLPIC 0x04148 /* EEE Tx LPI Count */ +#define IGC_RLPIC 0x0414C /* EEE Rx LPI Count */ #define IGC_HGPTC 0x04118 /* Host Good Packets Tx Count */ #define IGC_RXDMTC 0x04120 /* Rx Descriptor Minimum Threshold Count */ #define IGC_HGORCL 0x04128 /* Host Good Octets Received Count Low */ @@ -228,8 +229,6 @@ #define IGC_SYSTIMR 0x0B6F8 /* System time register Residue */ #define IGC_TIMINCA 0x0B608 /* Increment attributes register - RW */ -#define IGC_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */ -#define IGC_RXSTMPH 0x0B628 /* Rx timestamp High - RO */ #define IGC_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */ #define IGC_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */ @@ -248,6 +247,17 @@ /* Wake Up packet memory */ #define IGC_WUPM_REG(_i) (0x05A00 + ((_i) * 4)) +/* Energy Efficient Ethernet "EEE" registers */ +#define IGC_EEER 0x0E30 /* Energy Efficient Ethernet "EEE"*/ +#define IGC_IPCNFG 0x0E38 /* Internal PHY Configuration */ +#define IGC_EEE_SU 0x0E34 /* EEE Setup */ + +/* LTR registers */ +#define IGC_LTRC 0x01A0 /* Latency Tolerance Reporting Control */ +#define IGC_DMACR 0x02508 /* DMA Coalescing Control Register */ +#define IGC_LTRMINV 0x5BB0 /* LTR Minimum Value */ +#define IGC_LTRMAXV 0x5BB4 /* LTR Maximum Value */ + /* forward declaration */ struct igc_hw; u32 igc_rd32(struct igc_hw *hw, u32 reg); |