mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-04-16 14:23:38 +00:00
9354 lines
263 KiB
Diff
9354 lines
263 KiB
Diff
--- a/drivers/net/ethernet/intel/igc/igc_base.c 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_base.c 2022-03-10 14:51:05.326870558 +0800
|
|
@@ -26,7 +26,7 @@
|
|
*/
|
|
ret_val = igc_disable_pcie_master(hw);
|
|
if (ret_val)
|
|
- hw_dbg("PCI-E Master disable polling has failed.\n");
|
|
+ hw_dbg("PCI-E Master disable polling has failed\n");
|
|
|
|
hw_dbg("Masking off all interrupts\n");
|
|
wr32(IGC_IMC, 0xffffffff);
|
|
@@ -40,7 +40,7 @@
|
|
ctrl = rd32(IGC_CTRL);
|
|
|
|
hw_dbg("Issuing a global reset to MAC\n");
|
|
- wr32(IGC_CTRL, ctrl | IGC_CTRL_DEV_RST);
|
|
+ wr32(IGC_CTRL, ctrl | IGC_CTRL_RST);
|
|
|
|
ret_val = igc_get_auto_rd_done(hw);
|
|
if (ret_val) {
|
|
@@ -158,11 +158,6 @@
|
|
struct igc_phy_info *phy = &hw->phy;
|
|
s32 ret_val = 0;
|
|
|
|
- if (hw->phy.media_type != igc_media_type_copper) {
|
|
- phy->type = igc_phy_none;
|
|
- goto out;
|
|
- }
|
|
-
|
|
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT_2500;
|
|
phy->reset_delay_us = 100;
|
|
|
|
@@ -177,7 +172,7 @@
|
|
*/
|
|
ret_val = hw->phy.ops.reset(hw);
|
|
if (ret_val) {
|
|
- hw_dbg("Error resetting the PHY.\n");
|
|
+ hw_dbg("Error resetting the PHY\n");
|
|
goto out;
|
|
}
|
|
|
|
@@ -187,15 +182,7 @@
|
|
|
|
igc_check_for_copper_link(hw);
|
|
|
|
- /* Verify phy id and set remaining function pointers */
|
|
- switch (phy->id) {
|
|
- case I225_I_PHY_ID:
|
|
- phy->type = igc_phy_i225;
|
|
- break;
|
|
- default:
|
|
- ret_val = -IGC_ERR_PHY;
|
|
- goto out;
|
|
- }
|
|
+ phy->type = igc_phy_i225;
|
|
|
|
out:
|
|
return ret_val;
|
|
@@ -212,6 +199,17 @@
|
|
case IGC_DEV_ID_I225_I:
|
|
case IGC_DEV_ID_I220_V:
|
|
case IGC_DEV_ID_I225_K:
|
|
+ case IGC_DEV_ID_I225_K2:
|
|
+ case IGC_DEV_ID_I226_K:
|
|
+ case IGC_DEV_ID_I225_LMVP:
|
|
+ case IGC_DEV_ID_I226_LMVP:
|
|
+ case IGC_DEV_ID_I225_IT:
|
|
+ case IGC_DEV_ID_I226_LM:
|
|
+ case IGC_DEV_ID_I226_V:
|
|
+ case IGC_DEV_ID_I226_IT:
|
|
+ case IGC_DEV_ID_I221_V:
|
|
+ case IGC_DEV_ID_I226_BLANK_NVM:
|
|
+ case IGC_DEV_ID_I225_BLANK_NVM:
|
|
mac->type = igc_i225;
|
|
break;
|
|
default:
|
|
@@ -363,7 +361,7 @@
|
|
}
|
|
|
|
if (ms_wait == 10)
|
|
- pr_debug("Queue disable timed out after 10ms\n");
|
|
+ hw_dbg("Queue disable timed out after 10ms\n");
|
|
|
|
/* Clear RLPML, RCTL.SBP, RFCTL.LEF, and set RCTL.LPE so that all
|
|
* incoming packets are rejected. Set enable and wait 2ms so that
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_defines.h 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_defines.h 2022-03-10 14:48:43.638939361 +0800
|
|
@@ -10,13 +10,43 @@
|
|
|
|
#define IGC_CTRL_EXT_DRV_LOAD 0x10000000 /* Drv loaded bit for FW */
|
|
|
|
-/* Physical Func Reset Done Indication */
|
|
-#define IGC_CTRL_EXT_LINK_MODE_MASK 0x00C00000
|
|
+/* Definitions for power management and wakeup registers */
|
|
+/* Wake Up Control */
|
|
+#define IGC_WUC_PME_EN 0x00000002 /* PME Enable */
|
|
+
|
|
+/* Wake Up Filter Control */
|
|
+#define IGC_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
|
|
+#define IGC_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
|
|
+#define IGC_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
|
|
+#define IGC_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */
|
|
+#define IGC_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
|
|
+
|
|
+#define IGC_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */
|
|
+
|
|
+/* Wake Up Status */
|
|
+#define IGC_WUS_EX 0x00000004 /* Directed Exact */
|
|
+#define IGC_WUS_ARPD 0x00000020 /* Directed ARP Request */
|
|
+#define IGC_WUS_IPV4 0x00000040 /* Directed IPv4 */
|
|
+#define IGC_WUS_IPV6 0x00000080 /* Directed IPv6 */
|
|
+#define IGC_WUS_NSD 0x00000400 /* Directed IPv6 Neighbor Solicitation */
|
|
+
|
|
+/* Packet types that are enabled for wake packet delivery */
|
|
+#define WAKE_PKT_WUS ( \
|
|
+ IGC_WUS_EX | \
|
|
+ IGC_WUS_ARPD | \
|
|
+ IGC_WUS_IPV4 | \
|
|
+ IGC_WUS_IPV6 | \
|
|
+ IGC_WUS_NSD)
|
|
+
|
|
+/* Wake Up Packet Length */
|
|
+#define IGC_WUPL_MASK 0x00000FFF
|
|
+
|
|
+/* Wake Up Packet Memory stores the first 128 bytes of the wake up packet */
|
|
+#define IGC_WUPM_BYTES 128
|
|
|
|
/* Loop limit on how long we wait for auto-negotiation to complete */
|
|
#define COPPER_LINK_UP_LIMIT 10
|
|
#define PHY_AUTO_NEG_LIMIT 45
|
|
-#define PHY_FORCE_LIMIT 20
|
|
|
|
/* Number of 100 microseconds we wait for PCI Express master disable */
|
|
#define MASTER_DISABLE_TIMEOUT 800
|
|
@@ -32,8 +62,14 @@
|
|
* (RAR[15]) for our directed address used by controllers with
|
|
* manageability enabled, allowing us room for 15 multicast addresses.
|
|
*/
|
|
+#define IGC_RAH_RAH_MASK 0x0000FFFF
|
|
+#define IGC_RAH_ASEL_MASK 0x00030000
|
|
+#define IGC_RAH_ASEL_SRC_ADDR BIT(16)
|
|
+#define IGC_RAH_QSEL_MASK 0x000C0000
|
|
+#define IGC_RAH_QSEL_SHIFT 18
|
|
+#define IGC_RAH_QSEL_ENABLE BIT(28)
|
|
#define IGC_RAH_AV 0x80000000 /* Receive descriptor valid */
|
|
-#define IGC_RAH_POOL_1 0x00040000
|
|
+
|
|
#define IGC_RAL_MAC_ADDR_LEN 4
|
|
#define IGC_RAH_MAC_ADDR_LEN 2
|
|
|
|
@@ -50,7 +86,7 @@
|
|
#define IGC_ERR_SWFW_SYNC 13
|
|
|
|
/* Device Control */
|
|
-#define IGC_CTRL_DEV_RST 0x20000000 /* Device reset */
|
|
+#define IGC_CTRL_RST 0x04000000 /* Global reset */
|
|
|
|
#define IGC_CTRL_PHY_RST 0x80000000 /* PHY Reset */
|
|
#define IGC_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
|
|
@@ -60,8 +96,6 @@
|
|
#define IGC_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
|
|
#define IGC_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
|
|
|
|
-#define IGC_CONNSW_AUTOSENSE_EN 0x1
|
|
-
|
|
/* As per the EAS the maximum supported size is 9.5KB (9728 bytes) */
|
|
#define MAX_JUMBO_FRAME_SIZE 0x2600
|
|
|
|
@@ -132,11 +166,6 @@
|
|
|
|
/* For checksumming, the sum of all words in the NVM should equal 0xBABA. */
|
|
#define NVM_SUM 0xBABA
|
|
-
|
|
-#define NVM_PBA_OFFSET_0 8
|
|
-#define NVM_PBA_OFFSET_1 9
|
|
-#define NVM_RESERVED_WORD 0xFFFF
|
|
-#define NVM_PBA_PTR_GUARD 0xFAFA
|
|
#define NVM_WORD_SIZE_BASE_SHIFT 6
|
|
|
|
/* Collision related configuration parameters */
|
|
@@ -187,6 +216,7 @@
|
|
#define IGC_ICR_RXDMT0 BIT(4) /* Rx desc min. threshold (0) */
|
|
#define IGC_ICR_RXO BIT(6) /* Rx overrun */
|
|
#define IGC_ICR_RXT0 BIT(7) /* Rx timer intr (ring 0) */
|
|
+#define IGC_ICR_TS BIT(19) /* Time Sync Interrupt */
|
|
#define IGC_ICR_DRSTA BIT(30) /* Device Reset Asserted */
|
|
|
|
/* If this bit asserted, the driver should claim the interrupt */
|
|
@@ -209,6 +239,7 @@
|
|
#define IGC_IMS_DRSTA IGC_ICR_DRSTA /* Device Reset Asserted */
|
|
#define IGC_IMS_RXT0 IGC_ICR_RXT0 /* Rx timer intr */
|
|
#define IGC_IMS_RXDMT0 IGC_ICR_RXDMT0 /* Rx desc min. threshold */
|
|
+#define IGC_IMS_TS IGC_ICR_TS /* Time Sync Interrupt */
|
|
|
|
#define IGC_QVECTOR_MASK 0x7FFC /* Q-vector mask */
|
|
#define IGC_ITR_VAL_MASK 0x04 /* ITR value mask */
|
|
@@ -216,7 +247,6 @@
|
|
/* Interrupt Cause Set */
|
|
#define IGC_ICS_LSC IGC_ICR_LSC /* Link Status Change */
|
|
#define IGC_ICS_RXDMT0 IGC_ICR_RXDMT0 /* rx desc min. threshold */
|
|
-#define IGC_ICS_DRSTA IGC_ICR_DRSTA /* Device Reset Aserted */
|
|
|
|
#define IGC_ICR_DOUTSYNC 0x10000000 /* NIC DMA out of sync */
|
|
#define IGC_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */
|
|
@@ -226,29 +256,28 @@
|
|
#define IGC_GPIE_EIAME 0x40000000
|
|
#define IGC_GPIE_PBA 0x80000000
|
|
|
|
+/* Receive Descriptor bit definitions */
|
|
+#define IGC_RXD_STAT_DD 0x01 /* Descriptor Done */
|
|
+
|
|
/* Transmit Descriptor bit definitions */
|
|
#define IGC_TXD_DTYP_D 0x00100000 /* Data Descriptor */
|
|
#define IGC_TXD_DTYP_C 0x00000000 /* Context Descriptor */
|
|
#define IGC_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
|
|
#define IGC_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
|
|
#define IGC_TXD_CMD_EOP 0x01000000 /* End of Packet */
|
|
-#define IGC_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
|
|
#define IGC_TXD_CMD_IC 0x04000000 /* Insert Checksum */
|
|
-#define IGC_TXD_CMD_RS 0x08000000 /* Report Status */
|
|
-#define IGC_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */
|
|
#define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */
|
|
#define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */
|
|
-#define IGC_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */
|
|
#define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */
|
|
-#define IGC_TXD_STAT_EC 0x00000002 /* Excess Collisions */
|
|
-#define IGC_TXD_STAT_LC 0x00000004 /* Late Collisions */
|
|
-#define IGC_TXD_STAT_TU 0x00000008 /* Transmit underrun */
|
|
#define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */
|
|
#define IGC_TXD_CMD_IP 0x02000000 /* IP packet */
|
|
#define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */
|
|
-#define IGC_TXD_STAT_TC 0x00000004 /* Tx Underrun */
|
|
#define IGC_TXD_EXTCMD_TSTAMP 0x00000010 /* IEEE1588 Timestamp packet */
|
|
|
|
+/* IPSec Encrypt Enable */
|
|
+#define IGC_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */
|
|
+#define IGC_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */
|
|
+
|
|
/* Transmit Control */
|
|
#define IGC_TCTL_EN 0x00000002 /* enable Tx */
|
|
#define IGC_TCTL_PSP 0x00000008 /* pad short packets */
|
|
@@ -281,25 +310,24 @@
|
|
#define IGC_RCTL_RDMTS_HALF 0x00000000 /* Rx desc min thresh size */
|
|
#define IGC_RCTL_BAM 0x00008000 /* broadcast enable */
|
|
|
|
+/* Split Replication Receive Control */
|
|
+#define IGC_SRRCTL_TIMESTAMP 0x40000000
|
|
+#define IGC_SRRCTL_TIMER1SEL(timer) (((timer) & 0x3) << 14)
|
|
+#define IGC_SRRCTL_TIMER0SEL(timer) (((timer) & 0x3) << 17)
|
|
+
|
|
/* Receive Descriptor bit definitions */
|
|
-#define IGC_RXD_STAT_EOP 0x02 /* End of Packet */
|
|
+#define IGC_RXD_STAT_EOP 0x02 /* End of Packet */
|
|
+#define IGC_RXD_STAT_IXSM 0x04 /* Ignore checksum */
|
|
+#define IGC_RXD_STAT_UDPCS 0x10 /* UDP xsum calculated */
|
|
+#define IGC_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */
|
|
|
|
-#define IGC_RXDEXT_STATERR_CE 0x01000000
|
|
-#define IGC_RXDEXT_STATERR_SE 0x02000000
|
|
-#define IGC_RXDEXT_STATERR_SEQ 0x04000000
|
|
-#define IGC_RXDEXT_STATERR_CXE 0x10000000
|
|
-#define IGC_RXDEXT_STATERR_TCPE 0x20000000
|
|
+/* Advanced Receive Descriptor bit definitions */
|
|
+#define IGC_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */
|
|
+
|
|
+#define IGC_RXDEXT_STATERR_L4E 0x20000000
|
|
#define IGC_RXDEXT_STATERR_IPE 0x40000000
|
|
#define IGC_RXDEXT_STATERR_RXE 0x80000000
|
|
|
|
-/* Same mask, but for extended and packet split descriptors */
|
|
-#define IGC_RXDEXT_ERR_FRAME_ERR_MASK ( \
|
|
- IGC_RXDEXT_STATERR_CE | \
|
|
- IGC_RXDEXT_STATERR_SE | \
|
|
- IGC_RXDEXT_STATERR_SEQ | \
|
|
- IGC_RXDEXT_STATERR_CXE | \
|
|
- IGC_RXDEXT_STATERR_RXE)
|
|
-
|
|
#define IGC_MRQC_RSS_FIELD_IPV4_TCP 0x00010000
|
|
#define IGC_MRQC_RSS_FIELD_IPV4 0x00020000
|
|
#define IGC_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000
|
|
@@ -320,6 +348,70 @@
|
|
|
|
#define I225_RXPBSIZE_DEFAULT 0x000000A2 /* RXPBSIZE default */
|
|
#define I225_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */
|
|
+#define IGC_RXPBS_CFG_TS_EN 0x80000000 /* Timestamp in Rx buffer */
|
|
+
|
|
+#define IGC_TXPBSIZE_TSN 0x04145145 /* 5k bytes buffer for each queue */
|
|
+
|
|
+#define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */
|
|
+#define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */
|
|
+
|
|
+/* Time Sync Interrupt Causes */
|
|
+#define IGC_TSICR_SYS_WRAP BIT(0) /* SYSTIM Wrap around. */
|
|
+#define IGC_TSICR_TXTS BIT(1) /* Transmit Timestamp. */
|
|
+#define IGC_TSICR_TT0 BIT(3) /* Target Time 0 Trigger. */
|
|
+#define IGC_TSICR_TT1 BIT(4) /* Target Time 1 Trigger. */
|
|
+#define IGC_TSICR_AUTT0 BIT(5) /* Auxiliary Timestamp 0 Taken. */
|
|
+#define IGC_TSICR_AUTT1 BIT(6) /* Auxiliary Timestamp 1 Taken. */
|
|
+
|
|
+#define IGC_TSICR_INTERRUPTS IGC_TSICR_TXTS
|
|
+
|
|
+#define IGC_FTQF_VF_BP 0x00008000
|
|
+#define IGC_FTQF_1588_TIME_STAMP 0x08000000
|
|
+#define IGC_FTQF_MASK 0xF0000000
|
|
+#define IGC_FTQF_MASK_PROTO_BP 0x10000000
|
|
+
|
|
+/* Time Sync Receive Control bit definitions */
|
|
+#define IGC_TSYNCRXCTL_TYPE_MASK 0x0000000E /* Rx type mask */
|
|
+#define IGC_TSYNCRXCTL_TYPE_L2_V2 0x00
|
|
+#define IGC_TSYNCRXCTL_TYPE_L4_V1 0x02
|
|
+#define IGC_TSYNCRXCTL_TYPE_L2_L4_V2 0x04
|
|
+#define IGC_TSYNCRXCTL_TYPE_ALL 0x08
|
|
+#define IGC_TSYNCRXCTL_TYPE_EVENT_V2 0x0A
|
|
+#define IGC_TSYNCRXCTL_ENABLED 0x00000010 /* enable Rx timestamping */
|
|
+#define IGC_TSYNCRXCTL_SYSCFI 0x00000020 /* Sys clock frequency */
|
|
+#define IGC_TSYNCRXCTL_RXSYNSIG 0x00000400 /* Sample RX tstamp in PHY sop */
|
|
+
|
|
+/* Time Sync Receive Configuration */
|
|
+#define IGC_TSYNCRXCFG_PTP_V1_CTRLT_MASK 0x000000FF
|
|
+#define IGC_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE 0x00
|
|
+#define IGC_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE 0x01
|
|
+
|
|
+/* Immediate Interrupt Receive */
|
|
+#define IGC_IMIR_CLEAR_MASK 0xF001FFFF /* IMIR Reg Clear Mask */
|
|
+#define IGC_IMIR_PORT_BYPASS 0x20000 /* IMIR Port Bypass Bit */
|
|
+#define IGC_IMIR_PRIORITY_SHIFT 29 /* IMIR Priority Shift */
|
|
+#define IGC_IMIREXT_CLEAR_MASK 0x7FFFF /* IMIREXT Reg Clear Mask */
|
|
+
|
|
+/* Immediate Interrupt Receive Extended */
|
|
+#define IGC_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of ctrl bits */
|
|
+#define IGC_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */
|
|
+
|
|
+/* Time Sync Transmit Control bit definitions */
|
|
+#define IGC_TSYNCTXCTL_TXTT_0 0x00000001 /* Tx timestamp reg 0 valid */
|
|
+#define IGC_TSYNCTXCTL_ENABLED 0x00000010 /* enable Tx timestamping */
|
|
+#define IGC_TSYNCTXCTL_MAX_ALLOWED_DLY_MASK 0x0000F000 /* max delay */
|
|
+#define IGC_TSYNCTXCTL_SYNC_COMP_ERR 0x20000000 /* sync err */
|
|
+#define IGC_TSYNCTXCTL_SYNC_COMP 0x40000000 /* sync complete */
|
|
+#define IGC_TSYNCTXCTL_START_SYNC 0x80000000 /* initiate sync */
|
|
+#define IGC_TSYNCTXCTL_TXSYNSIG 0x00000020 /* Sample TX tstamp in PHY sop */
|
|
+
|
|
+/* Transmit Scheduling */
|
|
+#define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001
|
|
+#define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008
|
|
+
|
|
+#define IGC_TXQCTL_QUEUE_MODE_LAUNCHT 0x00000001
|
|
+#define IGC_TXQCTL_STRICT_CYCLE 0x00000002
|
|
+#define IGC_TXQCTL_STRICT_END 0x00000004
|
|
|
|
/* Receive Checksum Control */
|
|
#define IGC_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */
|
|
@@ -347,7 +439,6 @@
|
|
#define IGC_GEN_POLL_TIMEOUT 1920
|
|
|
|
/* PHY Control Register */
|
|
-#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
|
|
#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
|
|
#define MII_CR_POWER_DOWN 0x0800 /* Power down */
|
|
#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
|
|
@@ -360,6 +451,7 @@
|
|
/* PHY Status Register */
|
|
#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */
|
|
#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */
|
|
+#define IGC_PHY_RST_COMP 0x0100 /* Internal PHY reset completion */
|
|
|
|
/* PHY 1000 MII Register/Bit Definitions */
|
|
/* PHY Registers defined by IEEE */
|
|
@@ -372,9 +464,6 @@
|
|
#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */
|
|
#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */
|
|
|
|
-/* Bit definitions for valid PHY IDs. I = Integrated E = External */
|
|
-#define I225_I_PHY_ID 0x67C9DC00
|
|
-
|
|
/* MDI Control */
|
|
#define IGC_MDIC_DATA_MASK 0x0000FFFF
|
|
#define IGC_MDIC_REG_MASK 0x001F0000
|
|
@@ -386,20 +475,59 @@
|
|
#define IGC_MDIC_READY 0x10000000
|
|
#define IGC_MDIC_INT_EN 0x20000000
|
|
#define IGC_MDIC_ERROR 0x40000000
|
|
-#define IGC_MDIC_DEST 0x80000000
|
|
|
|
#define IGC_N0_QUEUE -1
|
|
|
|
#define IGC_MAX_MAC_HDR_LEN 127
|
|
#define IGC_MAX_NETWORK_HDR_LEN 511
|
|
|
|
-#define IGC_VLAPQF_QUEUE_SEL(_n, q_idx) ((q_idx) << ((_n) * 4))
|
|
-#define IGC_VLAPQF_P_VALID(_n) (0x1 << (3 + (_n) * 4))
|
|
-#define IGC_VLAPQF_QUEUE_MASK 0x03
|
|
+#define IGC_VLANPQF_QSEL(_n, q_idx) ((q_idx) << ((_n) * 4))
|
|
+#define IGC_VLANPQF_VALID(_n) (0x1 << (3 + (_n) * 4))
|
|
+#define IGC_VLANPQF_QUEUE_MASK 0x03
|
|
|
|
#define IGC_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */
|
|
#define IGC_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type:1=IPv4 */
|
|
#define IGC_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet Type of TCP */
|
|
#define IGC_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 packet TYPE of SCTP */
|
|
|
|
+/* 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_ */
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_diag.c 1970-01-01 08:00:00.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_diag.c 2022-03-10 14:33:17.223213194 +0800
|
|
@@ -0,0 +1,186 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/* Copyright (c) 2020 Intel Corporation */
|
|
+
|
|
+#include "igc.h"
|
|
+#include "igc_diag.h"
|
|
+
|
|
+static struct igc_reg_test reg_test[] = {
|
|
+ { IGC_FCAL, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
|
|
+ { IGC_FCAH, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF },
|
|
+ { IGC_FCT, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF },
|
|
+ { IGC_RDBAH(0), 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
|
|
+ { IGC_RDBAL(0), 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFF80 },
|
|
+ { IGC_RDLEN(0), 4, PATTERN_TEST, 0x000FFF80, 0x000FFFFF },
|
|
+ { IGC_RDT(0), 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
|
|
+ { IGC_FCRTH, 1, PATTERN_TEST, 0x0003FFF0, 0x0003FFF0 },
|
|
+ { IGC_FCTTV, 1, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
|
|
+ { IGC_TIPG, 1, PATTERN_TEST, 0x3FFFFFFF, 0x3FFFFFFF },
|
|
+ { IGC_TDBAH(0), 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
|
|
+ { IGC_TDBAL(0), 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFF80 },
|
|
+ { IGC_TDLEN(0), 4, PATTERN_TEST, 0x000FFF80, 0x000FFFFF },
|
|
+ { IGC_TDT(0), 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
|
|
+ { IGC_RCTL, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
|
|
+ { IGC_RCTL, 1, SET_READ_TEST, 0x04CFB2FE, 0x003FFFFB },
|
|
+ { IGC_RCTL, 1, SET_READ_TEST, 0x04CFB2FE, 0xFFFFFFFF },
|
|
+ { IGC_TCTL, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
|
|
+ { IGC_RA, 16, TABLE64_TEST_LO,
|
|
+ 0xFFFFFFFF, 0xFFFFFFFF },
|
|
+ { IGC_RA, 16, TABLE64_TEST_HI,
|
|
+ 0x900FFFFF, 0xFFFFFFFF },
|
|
+ { IGC_MTA, 128, TABLE32_TEST,
|
|
+ 0xFFFFFFFF, 0xFFFFFFFF },
|
|
+ { 0, 0, 0, 0}
|
|
+};
|
|
+
|
|
+static bool reg_pattern_test(struct igc_adapter *adapter, u64 *data, int reg,
|
|
+ u32 mask, u32 write)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 pat, val, before;
|
|
+ static const u32 test_pattern[] = {
|
|
+ 0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF
|
|
+ };
|
|
+
|
|
+ for (pat = 0; pat < ARRAY_SIZE(test_pattern); pat++) {
|
|
+ before = rd32(reg);
|
|
+ wr32(reg, test_pattern[pat] & write);
|
|
+ val = rd32(reg);
|
|
+ if (val != (test_pattern[pat] & write & mask)) {
|
|
+ netdev_err(adapter->netdev,
|
|
+ "pattern test reg %04X failed: got 0x%08X expected 0x%08X",
|
|
+ reg, val, test_pattern[pat] & write & mask);
|
|
+ *data = reg;
|
|
+ wr32(reg, before);
|
|
+ return false;
|
|
+ }
|
|
+ wr32(reg, before);
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool reg_set_and_check(struct igc_adapter *adapter, u64 *data, int reg,
|
|
+ u32 mask, u32 write)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 val, before;
|
|
+
|
|
+ before = rd32(reg);
|
|
+ wr32(reg, write & mask);
|
|
+ val = rd32(reg);
|
|
+ if ((write & mask) != (val & mask)) {
|
|
+ netdev_err(adapter->netdev,
|
|
+ "set/check reg %04X test failed: got 0x%08X expected 0x%08X",
|
|
+ reg, (val & mask), (write & mask));
|
|
+ *data = reg;
|
|
+ wr32(reg, before);
|
|
+ return false;
|
|
+ }
|
|
+ wr32(reg, before);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool igc_reg_test(struct igc_adapter *adapter, u64 *data)
|
|
+{
|
|
+ struct igc_reg_test *test = reg_test;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 value, before, after;
|
|
+ u32 i, toggle, b = false;
|
|
+
|
|
+ /* Because the status register is such a special case,
|
|
+ * we handle it separately from the rest of the register
|
|
+ * tests. Some bits are read-only, some toggle, and some
|
|
+ * are writeable.
|
|
+ */
|
|
+ toggle = 0x6800D3;
|
|
+ before = rd32(IGC_STATUS);
|
|
+ value = before & toggle;
|
|
+ wr32(IGC_STATUS, toggle);
|
|
+ after = rd32(IGC_STATUS) & toggle;
|
|
+ if (value != after) {
|
|
+ netdev_err(adapter->netdev,
|
|
+ "failed STATUS register test got: 0x%08X expected: 0x%08X",
|
|
+ after, value);
|
|
+ *data = 1;
|
|
+ return false;
|
|
+ }
|
|
+ /* restore previous status */
|
|
+ wr32(IGC_STATUS, before);
|
|
+
|
|
+ /* Perform the remainder of the register test, looping through
|
|
+ * the test table until we either fail or reach the null entry.
|
|
+ */
|
|
+ while (test->reg) {
|
|
+ for (i = 0; i < test->array_len; i++) {
|
|
+ switch (test->test_type) {
|
|
+ case PATTERN_TEST:
|
|
+ b = reg_pattern_test(adapter, data,
|
|
+ test->reg + (i * 0x40),
|
|
+ test->mask,
|
|
+ test->write);
|
|
+ break;
|
|
+ case SET_READ_TEST:
|
|
+ b = reg_set_and_check(adapter, data,
|
|
+ test->reg + (i * 0x40),
|
|
+ test->mask,
|
|
+ test->write);
|
|
+ break;
|
|
+ case TABLE64_TEST_LO:
|
|
+ b = reg_pattern_test(adapter, data,
|
|
+ test->reg + (i * 8),
|
|
+ test->mask,
|
|
+ test->write);
|
|
+ break;
|
|
+ case TABLE64_TEST_HI:
|
|
+ b = reg_pattern_test(adapter, data,
|
|
+ test->reg + 4 + (i * 8),
|
|
+ test->mask,
|
|
+ test->write);
|
|
+ break;
|
|
+ case TABLE32_TEST:
|
|
+ b = reg_pattern_test(adapter, data,
|
|
+ test->reg + (i * 4),
|
|
+ test->mask,
|
|
+ test->write);
|
|
+ break;
|
|
+ }
|
|
+ if (!b)
|
|
+ return false;
|
|
+ }
|
|
+ test++;
|
|
+ }
|
|
+ *data = 0;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool igc_eeprom_test(struct igc_adapter *adapter, u64 *data)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ *data = 0;
|
|
+
|
|
+ if (hw->nvm.ops.validate(hw) != IGC_SUCCESS) {
|
|
+ *data = 1;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool igc_link_test(struct igc_adapter *adapter, u64 *data)
|
|
+{
|
|
+ bool link_up;
|
|
+
|
|
+ *data = 0;
|
|
+
|
|
+ /* add delay to give enough time for autonegotioation to finish */
|
|
+ if (adapter->hw.mac.autoneg)
|
|
+ ssleep(5);
|
|
+
|
|
+ link_up = igc_has_link(adapter);
|
|
+ if (!link_up) {
|
|
+ *data = 1;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_diag.h 1970-01-01 08:00:00.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_diag.h 2022-03-10 14:33:17.223213194 +0800
|
|
@@ -0,0 +1,30 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+/* Copyright (c) 2020 Intel Corporation */
|
|
+
|
|
+bool igc_reg_test(struct igc_adapter *adapter, u64 *data);
|
|
+bool igc_eeprom_test(struct igc_adapter *adapter, u64 *data);
|
|
+bool igc_link_test(struct igc_adapter *adapter, u64 *data);
|
|
+
|
|
+struct igc_reg_test {
|
|
+ u16 reg;
|
|
+ u8 array_len;
|
|
+ u8 test_type;
|
|
+ u32 mask;
|
|
+ u32 write;
|
|
+};
|
|
+
|
|
+/* In the hardware, registers are laid out either singly, in arrays
|
|
+ * spaced 0x40 bytes apart, or in contiguous tables. We assume
|
|
+ * most tests take place on arrays or single registers (handled
|
|
+ * as a single-element array) and special-case the tables.
|
|
+ * Table tests are always pattern tests.
|
|
+ *
|
|
+ * We also make provision for some required setup steps by specifying
|
|
+ * registers to be written without any read-back testing.
|
|
+ */
|
|
+
|
|
+#define PATTERN_TEST 1
|
|
+#define SET_READ_TEST 2
|
|
+#define TABLE32_TEST 3
|
|
+#define TABLE64_TEST_LO 4
|
|
+#define TABLE64_TEST_HI 5
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_dump.c 1970-01-01 08:00:00.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_dump.c 2022-03-10 14:33:17.223213194 +0800
|
|
@@ -0,0 +1,318 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/* Copyright (c) 2018 Intel Corporation */
|
|
+
|
|
+#include "igc.h"
|
|
+
|
|
+struct igc_reg_info {
|
|
+ u32 ofs;
|
|
+ char *name;
|
|
+};
|
|
+
|
|
+static const struct igc_reg_info igc_reg_info_tbl[] = {
|
|
+ /* General Registers */
|
|
+ {IGC_CTRL, "CTRL"},
|
|
+ {IGC_STATUS, "STATUS"},
|
|
+ {IGC_CTRL_EXT, "CTRL_EXT"},
|
|
+ {IGC_MDIC, "MDIC"},
|
|
+
|
|
+ /* Interrupt Registers */
|
|
+ {IGC_ICR, "ICR"},
|
|
+
|
|
+ /* RX Registers */
|
|
+ {IGC_RCTL, "RCTL"},
|
|
+ {IGC_RDLEN(0), "RDLEN"},
|
|
+ {IGC_RDH(0), "RDH"},
|
|
+ {IGC_RDT(0), "RDT"},
|
|
+ {IGC_RXDCTL(0), "RXDCTL"},
|
|
+ {IGC_RDBAL(0), "RDBAL"},
|
|
+ {IGC_RDBAH(0), "RDBAH"},
|
|
+
|
|
+ /* TX Registers */
|
|
+ {IGC_TCTL, "TCTL"},
|
|
+ {IGC_TDBAL(0), "TDBAL"},
|
|
+ {IGC_TDBAH(0), "TDBAH"},
|
|
+ {IGC_TDLEN(0), "TDLEN"},
|
|
+ {IGC_TDH(0), "TDH"},
|
|
+ {IGC_TDT(0), "TDT"},
|
|
+ {IGC_TXDCTL(0), "TXDCTL"},
|
|
+
|
|
+ /* List Terminator */
|
|
+ {}
|
|
+};
|
|
+
|
|
+/* igc_regdump - register printout routine */
|
|
+static void igc_regdump(struct igc_hw *hw, struct igc_reg_info *reginfo)
|
|
+{
|
|
+ struct net_device *dev = igc_get_hw_dev(hw);
|
|
+ int n = 0;
|
|
+ char rname[16];
|
|
+ u32 regs[8];
|
|
+
|
|
+ switch (reginfo->ofs) {
|
|
+ case IGC_RDLEN(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_RDLEN(n));
|
|
+ break;
|
|
+ case IGC_RDH(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_RDH(n));
|
|
+ break;
|
|
+ case IGC_RDT(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_RDT(n));
|
|
+ break;
|
|
+ case IGC_RXDCTL(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_RXDCTL(n));
|
|
+ break;
|
|
+ case IGC_RDBAL(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_RDBAL(n));
|
|
+ break;
|
|
+ case IGC_RDBAH(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_RDBAH(n));
|
|
+ break;
|
|
+ case IGC_TDBAL(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_RDBAL(n));
|
|
+ break;
|
|
+ case IGC_TDBAH(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_TDBAH(n));
|
|
+ break;
|
|
+ case IGC_TDLEN(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_TDLEN(n));
|
|
+ break;
|
|
+ case IGC_TDH(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_TDH(n));
|
|
+ break;
|
|
+ case IGC_TDT(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_TDT(n));
|
|
+ break;
|
|
+ case IGC_TXDCTL(0):
|
|
+ for (n = 0; n < 4; n++)
|
|
+ regs[n] = rd32(IGC_TXDCTL(n));
|
|
+ break;
|
|
+ default:
|
|
+ netdev_info(dev, "%-15s %08x\n", reginfo->name,
|
|
+ rd32(reginfo->ofs));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ snprintf(rname, 16, "%s%s", reginfo->name, "[0-3]");
|
|
+ netdev_info(dev, "%-15s %08x %08x %08x %08x\n", rname, regs[0], regs[1],
|
|
+ regs[2], regs[3]);
|
|
+}
|
|
+
|
|
+/* igc_rings_dump - Tx-rings and Rx-rings */
|
|
+void igc_rings_dump(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct net_device *netdev = adapter->netdev;
|
|
+ struct my_u0 { u64 a; u64 b; } *u0;
|
|
+ union igc_adv_tx_desc *tx_desc;
|
|
+ union igc_adv_rx_desc *rx_desc;
|
|
+ struct igc_ring *tx_ring;
|
|
+ struct igc_ring *rx_ring;
|
|
+ u32 staterr;
|
|
+ u16 i, n;
|
|
+
|
|
+ if (!netif_msg_hw(adapter))
|
|
+ return;
|
|
+
|
|
+ netdev_info(netdev, "Device info: state %016lX trans_start %016lX\n",
|
|
+ netdev->state, dev_trans_start(netdev));
|
|
+
|
|
+ /* Print TX Ring Summary */
|
|
+ if (!netif_running(netdev))
|
|
+ goto exit;
|
|
+
|
|
+ netdev_info(netdev, "TX Rings Summary\n");
|
|
+ netdev_info(netdev, "Queue [NTU] [NTC] [bi(ntc)->dma ] leng ntw timestamp\n");
|
|
+ for (n = 0; n < adapter->num_tx_queues; n++) {
|
|
+ struct igc_tx_buffer *buffer_info;
|
|
+
|
|
+ tx_ring = adapter->tx_ring[n];
|
|
+ buffer_info = &tx_ring->tx_buffer_info[tx_ring->next_to_clean];
|
|
+
|
|
+ netdev_info(netdev, "%5d %5X %5X %016llX %04X %p %016llX\n",
|
|
+ n, tx_ring->next_to_use, tx_ring->next_to_clean,
|
|
+ (u64)dma_unmap_addr(buffer_info, dma),
|
|
+ dma_unmap_len(buffer_info, len),
|
|
+ buffer_info->next_to_watch,
|
|
+ (u64)buffer_info->time_stamp);
|
|
+ }
|
|
+
|
|
+ /* Print TX Rings */
|
|
+ if (!netif_msg_tx_done(adapter))
|
|
+ goto rx_ring_summary;
|
|
+
|
|
+ netdev_info(netdev, "TX Rings Dump\n");
|
|
+
|
|
+ /* Transmit Descriptor Formats
|
|
+ *
|
|
+ * Advanced Transmit Descriptor
|
|
+ * +--------------------------------------------------------------+
|
|
+ * 0 | Buffer Address [63:0] |
|
|
+ * +--------------------------------------------------------------+
|
|
+ * 8 | PAYLEN | PORTS |CC|IDX | STA | DCMD |DTYP|MAC|RSV| DTALEN |
|
|
+ * +--------------------------------------------------------------+
|
|
+ * 63 46 45 40 39 38 36 35 32 31 24 15 0
|
|
+ */
|
|
+
|
|
+ for (n = 0; n < adapter->num_tx_queues; n++) {
|
|
+ tx_ring = adapter->tx_ring[n];
|
|
+ netdev_info(netdev, "------------------------------------\n");
|
|
+ netdev_info(netdev, "TX QUEUE INDEX = %d\n",
|
|
+ tx_ring->queue_index);
|
|
+ netdev_info(netdev, "------------------------------------\n");
|
|
+ netdev_info(netdev, "T [desc] [address 63:0 ] [PlPOCIStDDM Ln] [bi->dma ] leng ntw timestamp bi->skb\n");
|
|
+
|
|
+ for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) {
|
|
+ const char *next_desc;
|
|
+ struct igc_tx_buffer *buffer_info;
|
|
+
|
|
+ tx_desc = IGC_TX_DESC(tx_ring, i);
|
|
+ buffer_info = &tx_ring->tx_buffer_info[i];
|
|
+ u0 = (struct my_u0 *)tx_desc;
|
|
+ if (i == tx_ring->next_to_use &&
|
|
+ i == tx_ring->next_to_clean)
|
|
+ next_desc = " NTC/U";
|
|
+ else if (i == tx_ring->next_to_use)
|
|
+ next_desc = " NTU";
|
|
+ else if (i == tx_ring->next_to_clean)
|
|
+ next_desc = " NTC";
|
|
+ else
|
|
+ next_desc = "";
|
|
+
|
|
+ netdev_info(netdev, "T [0x%03X] %016llX %016llX %016llX %04X %p %016llX %p%s\n",
|
|
+ i, le64_to_cpu(u0->a),
|
|
+ le64_to_cpu(u0->b),
|
|
+ (u64)dma_unmap_addr(buffer_info, dma),
|
|
+ dma_unmap_len(buffer_info, len),
|
|
+ buffer_info->next_to_watch,
|
|
+ (u64)buffer_info->time_stamp,
|
|
+ buffer_info->skb, next_desc);
|
|
+
|
|
+ if (netif_msg_pktdata(adapter) && buffer_info->skb)
|
|
+ print_hex_dump(KERN_INFO, "",
|
|
+ DUMP_PREFIX_ADDRESS,
|
|
+ 16, 1, buffer_info->skb->data,
|
|
+ dma_unmap_len(buffer_info, len),
|
|
+ true);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Print RX Rings Summary */
|
|
+rx_ring_summary:
|
|
+ netdev_info(netdev, "RX Rings Summary\n");
|
|
+ netdev_info(netdev, "Queue [NTU] [NTC]\n");
|
|
+ for (n = 0; n < adapter->num_rx_queues; n++) {
|
|
+ rx_ring = adapter->rx_ring[n];
|
|
+ netdev_info(netdev, "%5d %5X %5X\n", n, rx_ring->next_to_use,
|
|
+ rx_ring->next_to_clean);
|
|
+ }
|
|
+
|
|
+ /* Print RX Rings */
|
|
+ if (!netif_msg_rx_status(adapter))
|
|
+ goto exit;
|
|
+
|
|
+ netdev_info(netdev, "RX Rings Dump\n");
|
|
+
|
|
+ /* Advanced Receive Descriptor (Read) Format
|
|
+ * 63 1 0
|
|
+ * +-----------------------------------------------------+
|
|
+ * 0 | Packet Buffer Address [63:1] |A0/NSE|
|
|
+ * +----------------------------------------------+------+
|
|
+ * 8 | Header Buffer Address [63:1] | DD |
|
|
+ * +-----------------------------------------------------+
|
|
+ *
|
|
+ *
|
|
+ * Advanced Receive Descriptor (Write-Back) Format
|
|
+ *
|
|
+ * 63 48 47 32 31 30 21 20 17 16 4 3 0
|
|
+ * +------------------------------------------------------+
|
|
+ * 0 | Packet IP |SPH| HDR_LEN | RSV|Packet| RSS |
|
|
+ * | Checksum Ident | | | | Type | Type |
|
|
+ * +------------------------------------------------------+
|
|
+ * 8 | VLAN Tag | Length | Extended Error | Extended Status |
|
|
+ * +------------------------------------------------------+
|
|
+ * 63 48 47 32 31 20 19 0
|
|
+ */
|
|
+
|
|
+ for (n = 0; n < adapter->num_rx_queues; n++) {
|
|
+ rx_ring = adapter->rx_ring[n];
|
|
+ netdev_info(netdev, "------------------------------------\n");
|
|
+ netdev_info(netdev, "RX QUEUE INDEX = %d\n",
|
|
+ rx_ring->queue_index);
|
|
+ netdev_info(netdev, "------------------------------------\n");
|
|
+ netdev_info(netdev, "R [desc] [ PktBuf A0] [ HeadBuf DD] [bi->dma ] [bi->skb] <-- Adv Rx Read format\n");
|
|
+ netdev_info(netdev, "RWB[desc] [PcsmIpSHl PtRs] [vl er S cks ln] ---------------- [bi->skb] <-- Adv Rx Write-Back format\n");
|
|
+
|
|
+ for (i = 0; i < rx_ring->count; i++) {
|
|
+ const char *next_desc;
|
|
+ struct igc_rx_buffer *buffer_info;
|
|
+
|
|
+ buffer_info = &rx_ring->rx_buffer_info[i];
|
|
+ rx_desc = IGC_RX_DESC(rx_ring, i);
|
|
+ u0 = (struct my_u0 *)rx_desc;
|
|
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
|
|
+
|
|
+ if (i == rx_ring->next_to_use)
|
|
+ next_desc = " NTU";
|
|
+ else if (i == rx_ring->next_to_clean)
|
|
+ next_desc = " NTC";
|
|
+ else
|
|
+ next_desc = "";
|
|
+
|
|
+ if (staterr & IGC_RXD_STAT_DD) {
|
|
+ /* Descriptor Done */
|
|
+ netdev_info(netdev, "%s[0x%03X] %016llX %016llX ---------------- %s\n",
|
|
+ "RWB", i,
|
|
+ le64_to_cpu(u0->a),
|
|
+ le64_to_cpu(u0->b),
|
|
+ next_desc);
|
|
+ } else {
|
|
+ netdev_info(netdev, "%s[0x%03X] %016llX %016llX %016llX %s\n",
|
|
+ "R ", i,
|
|
+ le64_to_cpu(u0->a),
|
|
+ le64_to_cpu(u0->b),
|
|
+ (u64)buffer_info->dma,
|
|
+ next_desc);
|
|
+
|
|
+ if (netif_msg_pktdata(adapter) &&
|
|
+ buffer_info->dma && buffer_info->page) {
|
|
+ print_hex_dump(KERN_INFO, "",
|
|
+ DUMP_PREFIX_ADDRESS,
|
|
+ 16, 1,
|
|
+ page_address
|
|
+ (buffer_info->page) +
|
|
+ buffer_info->page_offset,
|
|
+ igc_rx_bufsz(rx_ring),
|
|
+ true);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+exit:
|
|
+ return;
|
|
+}
|
|
+
|
|
+/* igc_regs_dump - registers dump */
|
|
+void igc_regs_dump(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ struct igc_reg_info *reginfo;
|
|
+
|
|
+ /* Print Registers */
|
|
+ netdev_info(adapter->netdev, "Register Dump\n");
|
|
+ netdev_info(adapter->netdev, "Register Name Value\n");
|
|
+ for (reginfo = (struct igc_reg_info *)igc_reg_info_tbl;
|
|
+ reginfo->name; reginfo++) {
|
|
+ igc_regdump(hw, reginfo);
|
|
+ }
|
|
+}
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c 2022-03-10 14:33:17.223213194 +0800
|
|
@@ -4,8 +4,10 @@
|
|
/* 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"
|
|
|
|
/* forward declaration */
|
|
struct igc_stats {
|
|
@@ -16,7 +18,7 @@
|
|
|
|
#define IGC_STAT(_name, _stat) { \
|
|
.stat_string = _name, \
|
|
- .sizeof_stat = FIELD_SIZEOF(struct igc_adapter, _stat), \
|
|
+ .sizeof_stat = sizeof_field(struct igc_adapter, _stat), \
|
|
.stat_offset = offsetof(struct igc_adapter, _stat) \
|
|
}
|
|
|
|
@@ -67,7 +69,7 @@
|
|
|
|
#define IGC_NETDEV_STAT(_net_stat) { \
|
|
.stat_string = __stringify(_net_stat), \
|
|
- .sizeof_stat = FIELD_SIZEOF(struct rtnl_link_stats64, _net_stat), \
|
|
+ .sizeof_stat = sizeof_field(struct rtnl_link_stats64, _net_stat), \
|
|
.stat_offset = offsetof(struct rtnl_link_stats64, _net_stat) \
|
|
}
|
|
|
|
@@ -123,13 +125,12 @@
|
|
|
|
#define IGC_PRIV_FLAGS_STR_LEN ARRAY_SIZE(igc_priv_flags_strings)
|
|
|
|
-static void igc_get_drvinfo(struct net_device *netdev,
|
|
- struct ethtool_drvinfo *drvinfo)
|
|
+static void igc_ethtool_get_drvinfo(struct net_device *netdev,
|
|
+ struct ethtool_drvinfo *drvinfo)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
strlcpy(drvinfo->driver, igc_driver_name, sizeof(drvinfo->driver));
|
|
- strlcpy(drvinfo->version, igc_driver_version, sizeof(drvinfo->version));
|
|
|
|
/* add fw_version here */
|
|
strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
|
|
@@ -138,13 +139,13 @@
|
|
drvinfo->n_priv_flags = IGC_PRIV_FLAGS_STR_LEN;
|
|
}
|
|
|
|
-static int igc_get_regs_len(struct net_device *netdev)
|
|
+static int igc_ethtool_get_regs_len(struct net_device *netdev)
|
|
{
|
|
return IGC_REGS_LEN * sizeof(u32);
|
|
}
|
|
|
|
-static void igc_get_regs(struct net_device *netdev,
|
|
- struct ethtool_regs *regs, void *p)
|
|
+static void igc_ethtool_get_regs(struct net_device *netdev,
|
|
+ struct ethtool_regs *regs, void *p)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_hw *hw = &adapter->hw;
|
|
@@ -153,7 +154,7 @@
|
|
|
|
memset(p, 0, IGC_REGS_LEN * sizeof(u32));
|
|
|
|
- regs->version = (1u << 24) | (hw->revision_id << 16) | hw->device_id;
|
|
+ regs->version = (2u << 24) | (hw->revision_id << 16) | hw->device_id;
|
|
|
|
/* General Registers */
|
|
regs_buff[0] = rd32(IGC_CTRL);
|
|
@@ -306,23 +307,101 @@
|
|
regs_buff[164 + i] = rd32(IGC_TDT(i));
|
|
for (i = 0; i < 4; i++)
|
|
regs_buff[168 + i] = rd32(IGC_TXDCTL(i));
|
|
+
|
|
+ /* XXX: Due to a bug few lines above, RAL and RAH registers are
|
|
+ * overwritten. To preserve the ABI, we write these registers again in
|
|
+ * regs_buff.
|
|
+ */
|
|
+ for (i = 0; i < 16; i++)
|
|
+ regs_buff[172 + i] = rd32(IGC_RAL(i));
|
|
+ for (i = 0; i < 16; i++)
|
|
+ regs_buff[188 + i] = rd32(IGC_RAH(i));
|
|
+
|
|
+ regs_buff[204] = rd32(IGC_VLANPQF);
|
|
+
|
|
+ for (i = 0; i < 8; i++)
|
|
+ regs_buff[205 + i] = rd32(IGC_ETQF(i));
|
|
+
|
|
+ regs_buff[213] = adapter->stats.tlpic;
|
|
+ regs_buff[214] = adapter->stats.rlpic;
|
|
+}
|
|
+
|
|
+static void igc_ethtool_get_wol(struct net_device *netdev,
|
|
+ struct ethtool_wolinfo *wol)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+
|
|
+ wol->wolopts = 0;
|
|
+
|
|
+ if (!(adapter->flags & IGC_FLAG_WOL_SUPPORTED))
|
|
+ return;
|
|
+
|
|
+ wol->supported = WAKE_UCAST | WAKE_MCAST |
|
|
+ WAKE_BCAST | WAKE_MAGIC |
|
|
+ WAKE_PHY;
|
|
+
|
|
+ /* apply any specific unsupported masks here */
|
|
+ switch (adapter->hw.device_id) {
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (adapter->wol & IGC_WUFC_EX)
|
|
+ wol->wolopts |= WAKE_UCAST;
|
|
+ if (adapter->wol & IGC_WUFC_MC)
|
|
+ wol->wolopts |= WAKE_MCAST;
|
|
+ if (adapter->wol & IGC_WUFC_BC)
|
|
+ wol->wolopts |= WAKE_BCAST;
|
|
+ if (adapter->wol & IGC_WUFC_MAG)
|
|
+ wol->wolopts |= WAKE_MAGIC;
|
|
+ if (adapter->wol & IGC_WUFC_LNKC)
|
|
+ wol->wolopts |= WAKE_PHY;
|
|
+}
|
|
+
|
|
+static int igc_ethtool_set_wol(struct net_device *netdev,
|
|
+ struct ethtool_wolinfo *wol)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+
|
|
+ if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_FILTER))
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ if (!(adapter->flags & IGC_FLAG_WOL_SUPPORTED))
|
|
+ return wol->wolopts ? -EOPNOTSUPP : 0;
|
|
+
|
|
+ /* these settings will always override what we currently have */
|
|
+ adapter->wol = 0;
|
|
+
|
|
+ if (wol->wolopts & WAKE_UCAST)
|
|
+ adapter->wol |= IGC_WUFC_EX;
|
|
+ if (wol->wolopts & WAKE_MCAST)
|
|
+ adapter->wol |= IGC_WUFC_MC;
|
|
+ if (wol->wolopts & WAKE_BCAST)
|
|
+ adapter->wol |= IGC_WUFC_BC;
|
|
+ if (wol->wolopts & WAKE_MAGIC)
|
|
+ adapter->wol |= IGC_WUFC_MAG;
|
|
+ if (wol->wolopts & WAKE_PHY)
|
|
+ adapter->wol |= IGC_WUFC_LNKC;
|
|
+ device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
-static u32 igc_get_msglevel(struct net_device *netdev)
|
|
+static u32 igc_ethtool_get_msglevel(struct net_device *netdev)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
return adapter->msg_enable;
|
|
}
|
|
|
|
-static void igc_set_msglevel(struct net_device *netdev, u32 data)
|
|
+static void igc_ethtool_set_msglevel(struct net_device *netdev, u32 data)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
adapter->msg_enable = data;
|
|
}
|
|
|
|
-static int igc_nway_reset(struct net_device *netdev)
|
|
+static int igc_ethtool_nway_reset(struct net_device *netdev)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
@@ -331,7 +410,7 @@
|
|
return 0;
|
|
}
|
|
|
|
-static u32 igc_get_link(struct net_device *netdev)
|
|
+static u32 igc_ethtool_get_link(struct net_device *netdev)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_mac_info *mac = &adapter->hw.mac;
|
|
@@ -348,15 +427,15 @@
|
|
return igc_has_link(adapter);
|
|
}
|
|
|
|
-static int igc_get_eeprom_len(struct net_device *netdev)
|
|
+static int igc_ethtool_get_eeprom_len(struct net_device *netdev)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
return adapter->hw.nvm.word_size * 2;
|
|
}
|
|
|
|
-static int igc_get_eeprom(struct net_device *netdev,
|
|
- struct ethtool_eeprom *eeprom, u8 *bytes)
|
|
+static int igc_ethtool_get_eeprom(struct net_device *netdev,
|
|
+ struct ethtool_eeprom *eeprom, u8 *bytes)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_hw *hw = &adapter->hw;
|
|
@@ -402,8 +481,8 @@
|
|
return ret_val;
|
|
}
|
|
|
|
-static int igc_set_eeprom(struct net_device *netdev,
|
|
- struct ethtool_eeprom *eeprom, u8 *bytes)
|
|
+static int igc_ethtool_set_eeprom(struct net_device *netdev,
|
|
+ struct ethtool_eeprom *eeprom, u8 *bytes)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_hw *hw = &adapter->hw;
|
|
@@ -470,8 +549,8 @@
|
|
return ret_val;
|
|
}
|
|
|
|
-static void igc_get_ringparam(struct net_device *netdev,
|
|
- struct ethtool_ringparam *ring)
|
|
+static void igc_ethtool_get_ringparam(struct net_device *netdev,
|
|
+ struct ethtool_ringparam *ring)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
@@ -481,8 +560,8 @@
|
|
ring->tx_pending = adapter->tx_ring_count;
|
|
}
|
|
|
|
-static int igc_set_ringparam(struct net_device *netdev,
|
|
- struct ethtool_ringparam *ring)
|
|
+static int igc_ethtool_set_ringparam(struct net_device *netdev,
|
|
+ struct ethtool_ringparam *ring)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_ring *temp_ring;
|
|
@@ -596,8 +675,8 @@
|
|
return err;
|
|
}
|
|
|
|
-static void igc_get_pauseparam(struct net_device *netdev,
|
|
- struct ethtool_pauseparam *pause)
|
|
+static void igc_ethtool_get_pauseparam(struct net_device *netdev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_hw *hw = &adapter->hw;
|
|
@@ -615,8 +694,8 @@
|
|
}
|
|
}
|
|
|
|
-static int igc_set_pauseparam(struct net_device *netdev,
|
|
- struct ethtool_pauseparam *pause)
|
|
+static int igc_ethtool_set_pauseparam(struct net_device *netdev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_hw *hw = &adapter->hw;
|
|
@@ -655,7 +734,8 @@
|
|
return retval;
|
|
}
|
|
|
|
-static void igc_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
|
|
+static void igc_ethtool_get_strings(struct net_device *netdev, u32 stringset,
|
|
+ u8 *data)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
u8 *p = data;
|
|
@@ -706,7 +786,7 @@
|
|
}
|
|
}
|
|
|
|
-static int igc_get_sset_count(struct net_device *netdev, int sset)
|
|
+static int igc_ethtool_get_sset_count(struct net_device *netdev, int sset)
|
|
{
|
|
switch (sset) {
|
|
case ETH_SS_STATS:
|
|
@@ -720,7 +800,7 @@
|
|
}
|
|
}
|
|
|
|
-static void igc_get_ethtool_stats(struct net_device *netdev,
|
|
+static void igc_ethtool_get_stats(struct net_device *netdev,
|
|
struct ethtool_stats *stats, u64 *data)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
@@ -776,8 +856,8 @@
|
|
spin_unlock(&adapter->stats64_lock);
|
|
}
|
|
|
|
-static int igc_get_coalesce(struct net_device *netdev,
|
|
- struct ethtool_coalesce *ec)
|
|
+static int igc_ethtool_get_coalesce(struct net_device *netdev,
|
|
+ struct ethtool_coalesce *ec)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
@@ -796,33 +876,12 @@
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_set_coalesce(struct net_device *netdev,
|
|
- struct ethtool_coalesce *ec)
|
|
+static int igc_ethtool_set_coalesce(struct net_device *netdev,
|
|
+ struct ethtool_coalesce *ec)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
int i;
|
|
|
|
- if (ec->rx_max_coalesced_frames ||
|
|
- ec->rx_coalesce_usecs_irq ||
|
|
- ec->rx_max_coalesced_frames_irq ||
|
|
- ec->tx_max_coalesced_frames ||
|
|
- ec->tx_coalesce_usecs_irq ||
|
|
- ec->stats_block_coalesce_usecs ||
|
|
- ec->use_adaptive_rx_coalesce ||
|
|
- ec->use_adaptive_tx_coalesce ||
|
|
- ec->pkt_rate_low ||
|
|
- ec->rx_coalesce_usecs_low ||
|
|
- ec->rx_max_coalesced_frames_low ||
|
|
- ec->tx_coalesce_usecs_low ||
|
|
- ec->tx_max_coalesced_frames_low ||
|
|
- ec->pkt_rate_high ||
|
|
- ec->rx_coalesce_usecs_high ||
|
|
- ec->rx_max_coalesced_frames_high ||
|
|
- ec->tx_coalesce_usecs_high ||
|
|
- ec->tx_max_coalesced_frames_high ||
|
|
- ec->rate_sample_interval)
|
|
- return -ENOTSUPP;
|
|
-
|
|
if (ec->rx_coalesce_usecs > IGC_MAX_ITR_USECS ||
|
|
(ec->rx_coalesce_usecs > 3 &&
|
|
ec->rx_coalesce_usecs < IGC_MIN_ITR_USECS) ||
|
|
@@ -875,81 +934,83 @@
|
|
}
|
|
|
|
#define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
|
|
-static int igc_get_ethtool_nfc_entry(struct igc_adapter *adapter,
|
|
- struct ethtool_rxnfc *cmd)
|
|
+static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
|
|
+ struct ethtool_rxnfc *cmd)
|
|
{
|
|
struct ethtool_rx_flow_spec *fsp = &cmd->fs;
|
|
- struct igc_nfc_filter *rule = NULL;
|
|
+ struct igc_nfc_rule *rule = NULL;
|
|
|
|
- /* report total rule count */
|
|
- cmd->data = IGC_MAX_RXNFC_FILTERS;
|
|
+ cmd->data = IGC_MAX_RXNFC_RULES;
|
|
|
|
- hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
|
|
- if (fsp->location <= rule->sw_idx)
|
|
- break;
|
|
+ mutex_lock(&adapter->nfc_rule_lock);
|
|
+
|
|
+ rule = igc_get_nfc_rule(adapter, fsp->location);
|
|
+ if (!rule)
|
|
+ goto out;
|
|
+
|
|
+ fsp->flow_type = ETHER_FLOW;
|
|
+ fsp->ring_cookie = rule->action;
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {
|
|
+ fsp->h_u.ether_spec.h_proto = htons(rule->filter.etype);
|
|
+ fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK;
|
|
}
|
|
|
|
- if (!rule || fsp->location != rule->sw_idx)
|
|
- return -EINVAL;
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
|
|
+ fsp->flow_type |= FLOW_EXT;
|
|
+ fsp->h_ext.vlan_tci = htons(rule->filter.vlan_tci);
|
|
+ fsp->m_ext.vlan_tci = htons(VLAN_PRIO_MASK);
|
|
+ }
|
|
|
|
- if (rule->filter.match_flags) {
|
|
- fsp->flow_type = ETHER_FLOW;
|
|
- fsp->ring_cookie = rule->action;
|
|
- if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {
|
|
- fsp->h_u.ether_spec.h_proto = rule->filter.etype;
|
|
- fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK;
|
|
- }
|
|
- if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
|
|
- fsp->flow_type |= FLOW_EXT;
|
|
- fsp->h_ext.vlan_tci = rule->filter.vlan_tci;
|
|
- fsp->m_ext.vlan_tci = htons(VLAN_PRIO_MASK);
|
|
- }
|
|
- if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
|
|
- ether_addr_copy(fsp->h_u.ether_spec.h_dest,
|
|
- rule->filter.dst_addr);
|
|
- /* As we only support matching by the full
|
|
- * mask, return the mask to userspace
|
|
- */
|
|
- eth_broadcast_addr(fsp->m_u.ether_spec.h_dest);
|
|
- }
|
|
- if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
|
|
- ether_addr_copy(fsp->h_u.ether_spec.h_source,
|
|
- rule->filter.src_addr);
|
|
- /* As we only support matching by the full
|
|
- * mask, return the mask to userspace
|
|
- */
|
|
- eth_broadcast_addr(fsp->m_u.ether_spec.h_source);
|
|
- }
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
|
|
+ ether_addr_copy(fsp->h_u.ether_spec.h_dest,
|
|
+ rule->filter.dst_addr);
|
|
+ eth_broadcast_addr(fsp->m_u.ether_spec.h_dest);
|
|
+ }
|
|
|
|
- return 0;
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
|
|
+ ether_addr_copy(fsp->h_u.ether_spec.h_source,
|
|
+ rule->filter.src_addr);
|
|
+ eth_broadcast_addr(fsp->m_u.ether_spec.h_source);
|
|
}
|
|
+
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
+ return 0;
|
|
+
|
|
+out:
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
-static int igc_get_ethtool_nfc_all(struct igc_adapter *adapter,
|
|
- struct ethtool_rxnfc *cmd,
|
|
- u32 *rule_locs)
|
|
+static int igc_ethtool_get_nfc_rules(struct igc_adapter *adapter,
|
|
+ struct ethtool_rxnfc *cmd,
|
|
+ u32 *rule_locs)
|
|
{
|
|
- struct igc_nfc_filter *rule;
|
|
+ struct igc_nfc_rule *rule;
|
|
int cnt = 0;
|
|
|
|
- /* report total rule count */
|
|
- cmd->data = IGC_MAX_RXNFC_FILTERS;
|
|
+ cmd->data = IGC_MAX_RXNFC_RULES;
|
|
+
|
|
+ mutex_lock(&adapter->nfc_rule_lock);
|
|
|
|
- hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
|
|
- if (cnt == cmd->rule_cnt)
|
|
+ list_for_each_entry(rule, &adapter->nfc_rule_list, list) {
|
|
+ if (cnt == cmd->rule_cnt) {
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
return -EMSGSIZE;
|
|
- rule_locs[cnt] = rule->sw_idx;
|
|
+ }
|
|
+ rule_locs[cnt] = rule->location;
|
|
cnt++;
|
|
}
|
|
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
+
|
|
cmd->rule_cnt = cnt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_get_rss_hash_opts(struct igc_adapter *adapter,
|
|
- struct ethtool_rxnfc *cmd)
|
|
+static int igc_ethtool_get_rss_hash_opts(struct igc_adapter *adapter,
|
|
+ struct ethtool_rxnfc *cmd)
|
|
{
|
|
cmd->data = 0;
|
|
|
|
@@ -957,37 +1018,29 @@
|
|
switch (cmd->flow_type) {
|
|
case TCP_V4_FLOW:
|
|
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
- /* Fall through */
|
|
+ fallthrough;
|
|
case UDP_V4_FLOW:
|
|
if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV4_UDP)
|
|
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
- /* Fall through */
|
|
+ fallthrough;
|
|
case SCTP_V4_FLOW:
|
|
- /* Fall through */
|
|
case AH_ESP_V4_FLOW:
|
|
- /* Fall through */
|
|
case AH_V4_FLOW:
|
|
- /* Fall through */
|
|
case ESP_V4_FLOW:
|
|
- /* Fall through */
|
|
case IPV4_FLOW:
|
|
cmd->data |= RXH_IP_SRC | RXH_IP_DST;
|
|
break;
|
|
case TCP_V6_FLOW:
|
|
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
- /* Fall through */
|
|
+ fallthrough;
|
|
case UDP_V6_FLOW:
|
|
if (adapter->flags & IGC_FLAG_RSS_FIELD_IPV6_UDP)
|
|
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
- /* Fall through */
|
|
+ fallthrough;
|
|
case SCTP_V6_FLOW:
|
|
- /* Fall through */
|
|
case AH_ESP_V6_FLOW:
|
|
- /* Fall through */
|
|
case AH_V6_FLOW:
|
|
- /* Fall through */
|
|
case ESP_V6_FLOW:
|
|
- /* Fall through */
|
|
case IPV6_FLOW:
|
|
cmd->data |= RXH_IP_SRC | RXH_IP_DST;
|
|
break;
|
|
@@ -998,41 +1051,33 @@
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
|
|
- u32 *rule_locs)
|
|
+static int igc_ethtool_get_rxnfc(struct net_device *dev,
|
|
+ struct ethtool_rxnfc *cmd, u32 *rule_locs)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(dev);
|
|
- int ret = -EOPNOTSUPP;
|
|
|
|
switch (cmd->cmd) {
|
|
case ETHTOOL_GRXRINGS:
|
|
cmd->data = adapter->num_rx_queues;
|
|
- ret = 0;
|
|
- break;
|
|
+ return 0;
|
|
case ETHTOOL_GRXCLSRLCNT:
|
|
- cmd->rule_cnt = adapter->nfc_filter_count;
|
|
- ret = 0;
|
|
- break;
|
|
+ cmd->rule_cnt = adapter->nfc_rule_count;
|
|
+ return 0;
|
|
case ETHTOOL_GRXCLSRULE:
|
|
- ret = igc_get_ethtool_nfc_entry(adapter, cmd);
|
|
- break;
|
|
+ return igc_ethtool_get_nfc_rule(adapter, cmd);
|
|
case ETHTOOL_GRXCLSRLALL:
|
|
- ret = igc_get_ethtool_nfc_all(adapter, cmd, rule_locs);
|
|
- break;
|
|
+ return igc_ethtool_get_nfc_rules(adapter, cmd, rule_locs);
|
|
case ETHTOOL_GRXFH:
|
|
- ret = igc_get_rss_hash_opts(adapter, cmd);
|
|
- break;
|
|
+ return igc_ethtool_get_rss_hash_opts(adapter, cmd);
|
|
default:
|
|
- break;
|
|
+ return -EOPNOTSUPP;
|
|
}
|
|
-
|
|
- return ret;
|
|
}
|
|
|
|
#define UDP_RSS_FLAGS (IGC_FLAG_RSS_FIELD_IPV4_UDP | \
|
|
IGC_FLAG_RSS_FIELD_IPV6_UDP)
|
|
-static int igc_set_rss_hash_opt(struct igc_adapter *adapter,
|
|
- struct ethtool_rxnfc *nfc)
|
|
+static int igc_ethtool_set_rss_hash_opt(struct igc_adapter *adapter,
|
|
+ struct ethtool_rxnfc *nfc)
|
|
{
|
|
u32 flags = adapter->flags;
|
|
|
|
@@ -1107,8 +1152,8 @@
|
|
|
|
if ((flags & UDP_RSS_FLAGS) &&
|
|
!(adapter->flags & UDP_RSS_FLAGS))
|
|
- dev_err(&adapter->pdev->dev,
|
|
- "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n");
|
|
+ netdev_err(adapter->netdev,
|
|
+ "Enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n");
|
|
|
|
adapter->flags = flags;
|
|
|
|
@@ -1133,344 +1178,184 @@
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_rxnfc_write_etype_filter(struct igc_adapter *adapter,
|
|
- struct igc_nfc_filter *input)
|
|
+static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,
|
|
+ const struct ethtool_rx_flow_spec *fsp)
|
|
{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- u8 i;
|
|
- u32 etqf;
|
|
- u16 etype;
|
|
-
|
|
- /* find an empty etype filter register */
|
|
- for (i = 0; i < MAX_ETYPE_FILTER; ++i) {
|
|
- if (!adapter->etype_bitmap[i])
|
|
- break;
|
|
- }
|
|
- if (i == MAX_ETYPE_FILTER) {
|
|
- dev_err(&adapter->pdev->dev, "ethtool -N: etype filters are all used.\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- adapter->etype_bitmap[i] = true;
|
|
+ INIT_LIST_HEAD(&rule->list);
|
|
|
|
- etqf = rd32(IGC_ETQF(i));
|
|
- etype = ntohs(input->filter.etype & ETHER_TYPE_FULL_MASK);
|
|
+ rule->action = fsp->ring_cookie;
|
|
+ rule->location = fsp->location;
|
|
|
|
- etqf |= IGC_ETQF_FILTER_ENABLE;
|
|
- etqf &= ~IGC_ETQF_ETYPE_MASK;
|
|
- etqf |= (etype & IGC_ETQF_ETYPE_MASK);
|
|
-
|
|
- etqf &= ~IGC_ETQF_QUEUE_MASK;
|
|
- etqf |= ((input->action << IGC_ETQF_QUEUE_SHIFT)
|
|
- & IGC_ETQF_QUEUE_MASK);
|
|
- etqf |= IGC_ETQF_QUEUE_ENABLE;
|
|
-
|
|
- wr32(IGC_ETQF(i), etqf);
|
|
-
|
|
- input->etype_reg_index = i;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int igc_rxnfc_write_vlan_prio_filter(struct igc_adapter *adapter,
|
|
- struct igc_nfc_filter *input)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- u8 vlan_priority;
|
|
- u16 queue_index;
|
|
- u32 vlapqf;
|
|
-
|
|
- vlapqf = rd32(IGC_VLAPQF);
|
|
- vlan_priority = (ntohs(input->filter.vlan_tci) & VLAN_PRIO_MASK)
|
|
- >> VLAN_PRIO_SHIFT;
|
|
- queue_index = (vlapqf >> (vlan_priority * 4)) & IGC_VLAPQF_QUEUE_MASK;
|
|
-
|
|
- /* check whether this vlan prio is already set */
|
|
- if (vlapqf & IGC_VLAPQF_P_VALID(vlan_priority) &&
|
|
- queue_index != input->action) {
|
|
- dev_err(&adapter->pdev->dev, "ethtool rxnfc set vlan prio filter failed.\n");
|
|
- return -EEXIST;
|
|
+ if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) {
|
|
+ rule->filter.vlan_tci = ntohs(fsp->h_ext.vlan_tci);
|
|
+ rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI;
|
|
}
|
|
|
|
- vlapqf |= IGC_VLAPQF_P_VALID(vlan_priority);
|
|
- vlapqf |= IGC_VLAPQF_QUEUE_SEL(vlan_priority, input->action);
|
|
-
|
|
- wr32(IGC_VLAPQF, vlapqf);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int igc_add_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- int err = -EINVAL;
|
|
-
|
|
- if (hw->mac.type == igc_i225 &&
|
|
- !(input->filter.match_flags & ~IGC_FILTER_FLAG_SRC_MAC_ADDR)) {
|
|
- dev_err(&adapter->pdev->dev,
|
|
- "i225 doesn't support flow classification rules specifying only source addresses.\n");
|
|
- return -EOPNOTSUPP;
|
|
+ if (fsp->m_u.ether_spec.h_proto == ETHER_TYPE_FULL_MASK) {
|
|
+ rule->filter.etype = ntohs(fsp->h_u.ether_spec.h_proto);
|
|
+ rule->filter.match_flags = IGC_FILTER_FLAG_ETHER_TYPE;
|
|
}
|
|
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {
|
|
- err = igc_rxnfc_write_etype_filter(adapter, input);
|
|
- if (err)
|
|
- return err;
|
|
- }
|
|
-
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
|
|
- err = igc_add_mac_steering_filter(adapter,
|
|
- input->filter.dst_addr,
|
|
- input->action, 0);
|
|
- err = min_t(int, err, 0);
|
|
- if (err)
|
|
- return err;
|
|
- }
|
|
-
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
|
|
- err = igc_add_mac_steering_filter(adapter,
|
|
- input->filter.src_addr,
|
|
- input->action,
|
|
- IGC_MAC_STATE_SRC_ADDR);
|
|
- err = min_t(int, err, 0);
|
|
- if (err)
|
|
- return err;
|
|
+ /* Both source and destination address filters only support the full
|
|
+ * mask.
|
|
+ */
|
|
+ if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) {
|
|
+ rule->filter.match_flags |= IGC_FILTER_FLAG_SRC_MAC_ADDR;
|
|
+ ether_addr_copy(rule->filter.src_addr,
|
|
+ fsp->h_u.ether_spec.h_source);
|
|
}
|
|
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI)
|
|
- err = igc_rxnfc_write_vlan_prio_filter(adapter, input);
|
|
-
|
|
- return err;
|
|
-}
|
|
-
|
|
-static void igc_clear_etype_filter_regs(struct igc_adapter *adapter,
|
|
- u16 reg_index)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- u32 etqf = rd32(IGC_ETQF(reg_index));
|
|
-
|
|
- etqf &= ~IGC_ETQF_QUEUE_ENABLE;
|
|
- etqf &= ~IGC_ETQF_QUEUE_MASK;
|
|
- etqf &= ~IGC_ETQF_FILTER_ENABLE;
|
|
-
|
|
- wr32(IGC_ETQF(reg_index), etqf);
|
|
-
|
|
- adapter->etype_bitmap[reg_index] = false;
|
|
-}
|
|
-
|
|
-static void igc_clear_vlan_prio_filter(struct igc_adapter *adapter,
|
|
- u16 vlan_tci)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- u8 vlan_priority;
|
|
- u32 vlapqf;
|
|
-
|
|
- vlan_priority = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
|
|
-
|
|
- vlapqf = rd32(IGC_VLAPQF);
|
|
- vlapqf &= ~IGC_VLAPQF_P_VALID(vlan_priority);
|
|
- vlapqf &= ~IGC_VLAPQF_QUEUE_SEL(vlan_priority,
|
|
- IGC_VLAPQF_QUEUE_MASK);
|
|
-
|
|
- wr32(IGC_VLAPQF, vlapqf);
|
|
+ if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) {
|
|
+ rule->filter.match_flags |= IGC_FILTER_FLAG_DST_MAC_ADDR;
|
|
+ ether_addr_copy(rule->filter.dst_addr,
|
|
+ fsp->h_u.ether_spec.h_dest);
|
|
+ }
|
|
}
|
|
|
|
-int igc_erase_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input)
|
|
-{
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE)
|
|
- igc_clear_etype_filter_regs(adapter,
|
|
- input->etype_reg_index);
|
|
-
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI)
|
|
- igc_clear_vlan_prio_filter(adapter,
|
|
- ntohs(input->filter.vlan_tci));
|
|
+/**
|
|
+ * igc_ethtool_check_nfc_rule() - Check if NFC rule is valid
|
|
+ * @adapter: Pointer to adapter
|
|
+ * @rule: Rule under evaluation
|
|
+ *
|
|
+ * The driver doesn't support rules with multiple matches so if more than
|
|
+ * one bit in filter flags is set, @rule is considered invalid.
|
|
+ *
|
|
+ * Also, if there is already another rule with the same filter in a different
|
|
+ * location, @rule is considered invalid.
|
|
+ *
|
|
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
|
|
+ *
|
|
+ * Return: 0 in case of success, negative errno code otherwise.
|
|
+ */
|
|
+static int igc_ethtool_check_nfc_rule(struct igc_adapter *adapter,
|
|
+ struct igc_nfc_rule *rule)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ u8 flags = rule->filter.match_flags;
|
|
+ struct igc_nfc_rule *tmp;
|
|
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR)
|
|
- igc_del_mac_steering_filter(adapter, input->filter.src_addr,
|
|
- input->action,
|
|
- IGC_MAC_STATE_SRC_ADDR);
|
|
-
|
|
- if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR)
|
|
- igc_del_mac_steering_filter(adapter, input->filter.dst_addr,
|
|
- input->action, 0);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int igc_update_ethtool_nfc_entry(struct igc_adapter *adapter,
|
|
- struct igc_nfc_filter *input,
|
|
- u16 sw_idx)
|
|
-{
|
|
- struct igc_nfc_filter *rule, *parent;
|
|
- int err = -EINVAL;
|
|
-
|
|
- parent = NULL;
|
|
- rule = NULL;
|
|
-
|
|
- hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
|
|
- /* hash found, or no matching entry */
|
|
- if (rule->sw_idx >= sw_idx)
|
|
- break;
|
|
- parent = rule;
|
|
+ if (!flags) {
|
|
+ netdev_dbg(dev, "Rule with no match\n");
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
- /* if there is an old rule occupying our place remove it */
|
|
- if (rule && rule->sw_idx == sw_idx) {
|
|
- if (!input)
|
|
- err = igc_erase_filter(adapter, rule);
|
|
-
|
|
- hlist_del(&rule->nfc_node);
|
|
- kfree(rule);
|
|
- adapter->nfc_filter_count--;
|
|
+ if (flags & (flags - 1)) {
|
|
+ netdev_dbg(dev, "Rule with multiple matches not supported\n");
|
|
+ return -EOPNOTSUPP;
|
|
}
|
|
|
|
- /* If no input this was a delete, err should be 0 if a rule was
|
|
- * successfully found and removed from the list else -EINVAL
|
|
- */
|
|
- if (!input)
|
|
- return err;
|
|
-
|
|
- /* initialize node */
|
|
- INIT_HLIST_NODE(&input->nfc_node);
|
|
-
|
|
- /* add filter to the list */
|
|
- if (parent)
|
|
- hlist_add_behind(&input->nfc_node, &parent->nfc_node);
|
|
- else
|
|
- hlist_add_head(&input->nfc_node, &adapter->nfc_filter_list);
|
|
-
|
|
- /* update counts */
|
|
- adapter->nfc_filter_count++;
|
|
+ list_for_each_entry(tmp, &adapter->nfc_rule_list, list) {
|
|
+ if (!memcmp(&rule->filter, &tmp->filter,
|
|
+ sizeof(rule->filter)) &&
|
|
+ tmp->location != rule->location) {
|
|
+ netdev_dbg(dev, "Rule already exists\n");
|
|
+ return -EEXIST;
|
|
+ }
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_add_ethtool_nfc_entry(struct igc_adapter *adapter,
|
|
- struct ethtool_rxnfc *cmd)
|
|
+static int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter,
|
|
+ struct ethtool_rxnfc *cmd)
|
|
{
|
|
struct net_device *netdev = adapter->netdev;
|
|
struct ethtool_rx_flow_spec *fsp =
|
|
(struct ethtool_rx_flow_spec *)&cmd->fs;
|
|
- struct igc_nfc_filter *input, *rule;
|
|
- int err = 0;
|
|
+ struct igc_nfc_rule *rule, *old_rule;
|
|
+ int err;
|
|
|
|
- if (!(netdev->hw_features & NETIF_F_NTUPLE))
|
|
+ if (!(netdev->hw_features & NETIF_F_NTUPLE)) {
|
|
+ netdev_dbg(netdev, "N-tuple filters disabled\n");
|
|
return -EOPNOTSUPP;
|
|
+ }
|
|
|
|
- /* Don't allow programming if the action is a queue greater than
|
|
- * the number of online Rx queues.
|
|
- */
|
|
- if (fsp->ring_cookie == RX_CLS_FLOW_DISC ||
|
|
- fsp->ring_cookie >= adapter->num_rx_queues) {
|
|
- dev_err(&adapter->pdev->dev, "ethtool -N: The specified action is invalid\n");
|
|
- return -EINVAL;
|
|
+ if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW) {
|
|
+ netdev_dbg(netdev, "Only ethernet flow type is supported\n");
|
|
+ return -EOPNOTSUPP;
|
|
}
|
|
|
|
- /* Don't allow indexes to exist outside of available space */
|
|
- if (fsp->location >= IGC_MAX_RXNFC_FILTERS) {
|
|
- dev_err(&adapter->pdev->dev, "Location out of range\n");
|
|
- return -EINVAL;
|
|
+ if ((fsp->flow_type & FLOW_EXT) &&
|
|
+ fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK)) {
|
|
+ netdev_dbg(netdev, "VLAN mask not supported\n");
|
|
+ return -EOPNOTSUPP;
|
|
}
|
|
|
|
- if ((fsp->flow_type & ~FLOW_EXT) != ETHER_FLOW)
|
|
+ if (fsp->ring_cookie >= adapter->num_rx_queues) {
|
|
+ netdev_dbg(netdev, "Invalid action\n");
|
|
return -EINVAL;
|
|
-
|
|
- input = kzalloc(sizeof(*input), GFP_KERNEL);
|
|
- if (!input)
|
|
- return -ENOMEM;
|
|
-
|
|
- if (fsp->m_u.ether_spec.h_proto == ETHER_TYPE_FULL_MASK) {
|
|
- input->filter.etype = fsp->h_u.ether_spec.h_proto;
|
|
- input->filter.match_flags = IGC_FILTER_FLAG_ETHER_TYPE;
|
|
}
|
|
|
|
- /* Only support matching addresses by the full mask */
|
|
- if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_source)) {
|
|
- input->filter.match_flags |= IGC_FILTER_FLAG_SRC_MAC_ADDR;
|
|
- ether_addr_copy(input->filter.src_addr,
|
|
- fsp->h_u.ether_spec.h_source);
|
|
+ if (fsp->location >= IGC_MAX_RXNFC_RULES) {
|
|
+ netdev_dbg(netdev, "Invalid location\n");
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
- /* Only support matching addresses by the full mask */
|
|
- if (is_broadcast_ether_addr(fsp->m_u.ether_spec.h_dest)) {
|
|
- input->filter.match_flags |= IGC_FILTER_FLAG_DST_MAC_ADDR;
|
|
- ether_addr_copy(input->filter.dst_addr,
|
|
- fsp->h_u.ether_spec.h_dest);
|
|
- }
|
|
+ rule = kzalloc(sizeof(*rule), GFP_KERNEL);
|
|
+ if (!rule)
|
|
+ return -ENOMEM;
|
|
|
|
- if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) {
|
|
- if (fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK)) {
|
|
- err = -EINVAL;
|
|
- goto err_out;
|
|
- }
|
|
- input->filter.vlan_tci = fsp->h_ext.vlan_tci;
|
|
- input->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI;
|
|
- }
|
|
+ igc_ethtool_init_nfc_rule(rule, fsp);
|
|
|
|
- input->action = fsp->ring_cookie;
|
|
- input->sw_idx = fsp->location;
|
|
+ mutex_lock(&adapter->nfc_rule_lock);
|
|
|
|
- spin_lock(&adapter->nfc_lock);
|
|
+ err = igc_ethtool_check_nfc_rule(adapter, rule);
|
|
+ if (err)
|
|
+ goto err;
|
|
|
|
- hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node) {
|
|
- if (!memcmp(&input->filter, &rule->filter,
|
|
- sizeof(input->filter))) {
|
|
- err = -EEXIST;
|
|
- dev_err(&adapter->pdev->dev,
|
|
- "ethtool: this filter is already set\n");
|
|
- goto err_out_w_lock;
|
|
- }
|
|
- }
|
|
+ old_rule = igc_get_nfc_rule(adapter, fsp->location);
|
|
+ if (old_rule)
|
|
+ igc_del_nfc_rule(adapter, old_rule);
|
|
|
|
- err = igc_add_filter(adapter, input);
|
|
+ err = igc_add_nfc_rule(adapter, rule);
|
|
if (err)
|
|
- goto err_out_w_lock;
|
|
-
|
|
- igc_update_ethtool_nfc_entry(adapter, input, input->sw_idx);
|
|
+ goto err;
|
|
|
|
- spin_unlock(&adapter->nfc_lock);
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
return 0;
|
|
|
|
-err_out_w_lock:
|
|
- spin_unlock(&adapter->nfc_lock);
|
|
-err_out:
|
|
- kfree(input);
|
|
+err:
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
+ kfree(rule);
|
|
return err;
|
|
}
|
|
|
|
-static int igc_del_ethtool_nfc_entry(struct igc_adapter *adapter,
|
|
- struct ethtool_rxnfc *cmd)
|
|
+static int igc_ethtool_del_nfc_rule(struct igc_adapter *adapter,
|
|
+ struct ethtool_rxnfc *cmd)
|
|
{
|
|
struct ethtool_rx_flow_spec *fsp =
|
|
(struct ethtool_rx_flow_spec *)&cmd->fs;
|
|
- int err;
|
|
+ struct igc_nfc_rule *rule;
|
|
|
|
- spin_lock(&adapter->nfc_lock);
|
|
- err = igc_update_ethtool_nfc_entry(adapter, NULL, fsp->location);
|
|
- spin_unlock(&adapter->nfc_lock);
|
|
+ mutex_lock(&adapter->nfc_rule_lock);
|
|
|
|
- return err;
|
|
+ rule = igc_get_nfc_rule(adapter, fsp->location);
|
|
+ if (!rule) {
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ igc_del_nfc_rule(adapter, rule);
|
|
+
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
+ return 0;
|
|
}
|
|
|
|
-static int igc_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
|
|
+static int igc_ethtool_set_rxnfc(struct net_device *dev,
|
|
+ struct ethtool_rxnfc *cmd)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(dev);
|
|
- int ret = -EOPNOTSUPP;
|
|
|
|
switch (cmd->cmd) {
|
|
case ETHTOOL_SRXFH:
|
|
- ret = igc_set_rss_hash_opt(adapter, cmd);
|
|
- break;
|
|
+ return igc_ethtool_set_rss_hash_opt(adapter, cmd);
|
|
case ETHTOOL_SRXCLSRLINS:
|
|
- ret = igc_add_ethtool_nfc_entry(adapter, cmd);
|
|
- break;
|
|
+ return igc_ethtool_add_nfc_rule(adapter, cmd);
|
|
case ETHTOOL_SRXCLSRLDEL:
|
|
- ret = igc_del_ethtool_nfc_entry(adapter, cmd);
|
|
+ return igc_ethtool_del_nfc_rule(adapter, cmd);
|
|
default:
|
|
- break;
|
|
+ return -EOPNOTSUPP;
|
|
}
|
|
-
|
|
- return ret;
|
|
}
|
|
|
|
void igc_write_rss_indir_tbl(struct igc_adapter *adapter)
|
|
@@ -1495,13 +1380,13 @@
|
|
}
|
|
}
|
|
|
|
-static u32 igc_get_rxfh_indir_size(struct net_device *netdev)
|
|
+static u32 igc_ethtool_get_rxfh_indir_size(struct net_device *netdev)
|
|
{
|
|
return IGC_RETA_SIZE;
|
|
}
|
|
|
|
-static int igc_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
|
|
- u8 *hfunc)
|
|
+static int igc_ethtool_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
|
|
+ u8 *hfunc)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
int i;
|
|
@@ -1516,8 +1401,8 @@
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_set_rxfh(struct net_device *netdev, const u32 *indir,
|
|
- const u8 *key, const u8 hfunc)
|
|
+static int igc_ethtool_set_rxfh(struct net_device *netdev, const u32 *indir,
|
|
+ const u8 *key, const u8 hfunc)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
u32 num_queues;
|
|
@@ -1545,18 +1430,13 @@
|
|
return 0;
|
|
}
|
|
|
|
-static unsigned int igc_max_channels(struct igc_adapter *adapter)
|
|
-{
|
|
- return igc_get_max_rss_queues(adapter);
|
|
-}
|
|
-
|
|
-static void igc_get_channels(struct net_device *netdev,
|
|
- struct ethtool_channels *ch)
|
|
+static void igc_ethtool_get_channels(struct net_device *netdev,
|
|
+ struct ethtool_channels *ch)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
/* Report maximum channels */
|
|
- ch->max_combined = igc_max_channels(adapter);
|
|
+ ch->max_combined = igc_get_max_rss_queues(adapter);
|
|
|
|
/* Report info for other vector */
|
|
if (adapter->flags & IGC_FLAG_HAS_MSIX) {
|
|
@@ -1567,8 +1447,8 @@
|
|
ch->combined_count = adapter->rss_queues;
|
|
}
|
|
|
|
-static int igc_set_channels(struct net_device *netdev,
|
|
- struct ethtool_channels *ch)
|
|
+static int igc_ethtool_set_channels(struct net_device *netdev,
|
|
+ struct ethtool_channels *ch)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
unsigned int count = ch->combined_count;
|
|
@@ -1583,7 +1463,7 @@
|
|
return -EINVAL;
|
|
|
|
/* Verify the number of channels doesn't exceed hw limits */
|
|
- max_combined = igc_max_channels(adapter);
|
|
+ max_combined = igc_get_max_rss_queues(adapter);
|
|
if (count > max_combined)
|
|
return -EINVAL;
|
|
|
|
@@ -1600,7 +1480,40 @@
|
|
return 0;
|
|
}
|
|
|
|
-static u32 igc_get_priv_flags(struct net_device *netdev)
|
|
+static int igc_ethtool_get_ts_info(struct net_device *dev,
|
|
+ struct ethtool_ts_info *info)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(dev);
|
|
+
|
|
+ if (adapter->ptp_clock)
|
|
+ info->phc_index = ptp_clock_index(adapter->ptp_clock);
|
|
+ else
|
|
+ info->phc_index = -1;
|
|
+
|
|
+ switch (adapter->hw.mac.type) {
|
|
+ case igc_i225:
|
|
+ info->so_timestamping =
|
|
+ SOF_TIMESTAMPING_TX_SOFTWARE |
|
|
+ SOF_TIMESTAMPING_RX_SOFTWARE |
|
|
+ SOF_TIMESTAMPING_SOFTWARE |
|
|
+ SOF_TIMESTAMPING_TX_HARDWARE |
|
|
+ SOF_TIMESTAMPING_RX_HARDWARE |
|
|
+ SOF_TIMESTAMPING_RAW_HARDWARE;
|
|
+
|
|
+ info->tx_types =
|
|
+ BIT(HWTSTAMP_TX_OFF) |
|
|
+ BIT(HWTSTAMP_TX_ON);
|
|
+
|
|
+ info->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
|
|
+ info->rx_filters |= BIT(HWTSTAMP_FILTER_ALL);
|
|
+
|
|
+ return 0;
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+}
|
|
+
|
|
+static u32 igc_ethtool_get_priv_flags(struct net_device *netdev)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
u32 priv_flags = 0;
|
|
@@ -1611,7 +1524,7 @@
|
|
return priv_flags;
|
|
}
|
|
|
|
-static int igc_set_priv_flags(struct net_device *netdev, u32 priv_flags)
|
|
+static int igc_ethtool_set_priv_flags(struct net_device *netdev, u32 priv_flags)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
unsigned int flags = adapter->flags;
|
|
@@ -1631,6 +1544,98 @@
|
|
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);
|
|
@@ -1646,8 +1651,8 @@
|
|
pm_runtime_put(&adapter->pdev->dev);
|
|
}
|
|
|
|
-static int igc_get_link_ksettings(struct net_device *netdev,
|
|
- struct ethtool_link_ksettings *cmd)
|
|
+static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
|
|
+ struct ethtool_link_ksettings *cmd)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
struct igc_hw *hw = &adapter->hw;
|
|
@@ -1761,10 +1766,12 @@
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_set_link_ksettings(struct net_device *netdev,
|
|
- const struct ethtool_link_ksettings *cmd)
|
|
+static int
|
|
+igc_ethtool_set_link_ksettings(struct net_device *netdev,
|
|
+ const struct ethtool_link_ksettings *cmd)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct net_device *dev = adapter->netdev;
|
|
struct igc_hw *hw = &adapter->hw;
|
|
u32 advertising;
|
|
|
|
@@ -1772,8 +1779,7 @@
|
|
* cannot be changed
|
|
*/
|
|
if (igc_check_reset_block(hw)) {
|
|
- dev_err(&adapter->pdev->dev,
|
|
- "Cannot change link characteristics when reset is active.\n");
|
|
+ netdev_err(dev, "Cannot change link characteristics when reset is active\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -1784,7 +1790,7 @@
|
|
if (cmd->base.eth_tp_mdix_ctrl) {
|
|
if (cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO &&
|
|
cmd->base.autoneg != AUTONEG_ENABLE) {
|
|
- dev_err(&adapter->pdev->dev, "forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n");
|
|
+ netdev_err(dev, "Forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
@@ -1807,9 +1813,7 @@
|
|
if (adapter->fc_autoneg)
|
|
hw->fc.requested_mode = igc_fc_default;
|
|
} else {
|
|
- /* calling this overrides forced MDI setting */
|
|
- dev_info(&adapter->pdev->dev,
|
|
- "Force mode currently not supported\n");
|
|
+ netdev_info(dev, "Force mode currently not supported\n");
|
|
}
|
|
|
|
/* MDI-X => 2; MDI => 1; Auto => 3 */
|
|
@@ -1836,42 +1840,106 @@
|
|
return 0;
|
|
}
|
|
|
|
+static void igc_ethtool_diag_test(struct net_device *netdev,
|
|
+ struct ethtool_test *eth_test, u64 *data)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ bool if_running = netif_running(netdev);
|
|
+
|
|
+ if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
|
|
+ netdev_info(adapter->netdev, "Offline testing starting");
|
|
+ set_bit(__IGC_TESTING, &adapter->state);
|
|
+
|
|
+ /* Link test performed before hardware reset so autoneg doesn't
|
|
+ * interfere with test result
|
|
+ */
|
|
+ if (!igc_link_test(adapter, &data[TEST_LINK]))
|
|
+ eth_test->flags |= ETH_TEST_FL_FAILED;
|
|
+
|
|
+ if (if_running)
|
|
+ igc_close(netdev);
|
|
+ else
|
|
+ igc_reset(adapter);
|
|
+
|
|
+ netdev_info(adapter->netdev, "Register testing starting");
|
|
+ if (!igc_reg_test(adapter, &data[TEST_REG]))
|
|
+ eth_test->flags |= ETH_TEST_FL_FAILED;
|
|
+
|
|
+ igc_reset(adapter);
|
|
+
|
|
+ netdev_info(adapter->netdev, "EEPROM testing starting");
|
|
+ if (!igc_eeprom_test(adapter, &data[TEST_EEP]))
|
|
+ eth_test->flags |= ETH_TEST_FL_FAILED;
|
|
+
|
|
+ igc_reset(adapter);
|
|
+
|
|
+ /* loopback and interrupt tests
|
|
+ * will be implemented in the future
|
|
+ */
|
|
+ data[TEST_LOOP] = 0;
|
|
+ data[TEST_IRQ] = 0;
|
|
+
|
|
+ clear_bit(__IGC_TESTING, &adapter->state);
|
|
+ if (if_running)
|
|
+ igc_open(netdev);
|
|
+ } else {
|
|
+ netdev_info(adapter->netdev, "Online testing starting");
|
|
+
|
|
+ /* register, eeprom, intr and loopback tests not run online */
|
|
+ data[TEST_REG] = 0;
|
|
+ data[TEST_EEP] = 0;
|
|
+ data[TEST_IRQ] = 0;
|
|
+ data[TEST_LOOP] = 0;
|
|
+
|
|
+ if (!igc_link_test(adapter, &data[TEST_LINK]))
|
|
+ eth_test->flags |= ETH_TEST_FL_FAILED;
|
|
+ }
|
|
+
|
|
+ msleep_interruptible(4 * 1000);
|
|
+}
|
|
+
|
|
static const struct ethtool_ops igc_ethtool_ops = {
|
|
- .get_drvinfo = igc_get_drvinfo,
|
|
- .get_regs_len = igc_get_regs_len,
|
|
- .get_regs = igc_get_regs,
|
|
- .get_msglevel = igc_get_msglevel,
|
|
- .set_msglevel = igc_set_msglevel,
|
|
- .nway_reset = igc_nway_reset,
|
|
- .get_link = igc_get_link,
|
|
- .get_eeprom_len = igc_get_eeprom_len,
|
|
- .get_eeprom = igc_get_eeprom,
|
|
- .set_eeprom = igc_set_eeprom,
|
|
- .get_ringparam = igc_get_ringparam,
|
|
- .set_ringparam = igc_set_ringparam,
|
|
- .get_pauseparam = igc_get_pauseparam,
|
|
- .set_pauseparam = igc_set_pauseparam,
|
|
- .get_strings = igc_get_strings,
|
|
- .get_sset_count = igc_get_sset_count,
|
|
- .get_ethtool_stats = igc_get_ethtool_stats,
|
|
- .get_coalesce = igc_get_coalesce,
|
|
- .set_coalesce = igc_set_coalesce,
|
|
- .get_rxnfc = igc_get_rxnfc,
|
|
- .set_rxnfc = igc_set_rxnfc,
|
|
- .get_rxfh_indir_size = igc_get_rxfh_indir_size,
|
|
- .get_rxfh = igc_get_rxfh,
|
|
- .set_rxfh = igc_set_rxfh,
|
|
- .get_channels = igc_get_channels,
|
|
- .set_channels = igc_set_channels,
|
|
- .get_priv_flags = igc_get_priv_flags,
|
|
- .set_priv_flags = igc_set_priv_flags,
|
|
+ .get_drvinfo = igc_ethtool_get_drvinfo,
|
|
+ .get_regs_len = igc_ethtool_get_regs_len,
|
|
+ .get_regs = igc_ethtool_get_regs,
|
|
+ .get_wol = igc_ethtool_get_wol,
|
|
+ .set_wol = igc_ethtool_set_wol,
|
|
+ .get_msglevel = igc_ethtool_get_msglevel,
|
|
+ .set_msglevel = igc_ethtool_set_msglevel,
|
|
+ .nway_reset = igc_ethtool_nway_reset,
|
|
+ .get_link = igc_ethtool_get_link,
|
|
+ .get_eeprom_len = igc_ethtool_get_eeprom_len,
|
|
+ .get_eeprom = igc_ethtool_get_eeprom,
|
|
+ .set_eeprom = igc_ethtool_set_eeprom,
|
|
+ .get_ringparam = igc_ethtool_get_ringparam,
|
|
+ .set_ringparam = igc_ethtool_set_ringparam,
|
|
+ .get_pauseparam = igc_ethtool_get_pauseparam,
|
|
+ .set_pauseparam = igc_ethtool_set_pauseparam,
|
|
+ .get_strings = igc_ethtool_get_strings,
|
|
+ .get_sset_count = igc_ethtool_get_sset_count,
|
|
+ .get_ethtool_stats = igc_ethtool_get_stats,
|
|
+ .get_coalesce = igc_ethtool_get_coalesce,
|
|
+ .set_coalesce = igc_ethtool_set_coalesce,
|
|
+ .get_rxnfc = igc_ethtool_get_rxnfc,
|
|
+ .set_rxnfc = igc_ethtool_set_rxnfc,
|
|
+ .get_rxfh_indir_size = igc_ethtool_get_rxfh_indir_size,
|
|
+ .get_rxfh = igc_ethtool_get_rxfh,
|
|
+ .set_rxfh = igc_ethtool_set_rxfh,
|
|
+ .get_ts_info = igc_ethtool_get_ts_info,
|
|
+ .get_channels = igc_ethtool_get_channels,
|
|
+ .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_get_link_ksettings,
|
|
- .set_link_ksettings = igc_set_link_ksettings,
|
|
+ .get_link_ksettings = igc_ethtool_get_link_ksettings,
|
|
+ .set_link_ksettings = igc_ethtool_set_link_ksettings,
|
|
+ .self_test = igc_ethtool_diag_test,
|
|
};
|
|
|
|
-void igc_set_ethtool_ops(struct net_device *netdev)
|
|
+void igc_ethtool_set_ops(struct net_device *netdev)
|
|
{
|
|
netdev->ethtool_ops = &igc_ethtool_ops;
|
|
}
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc.h 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc.h 2022-03-10 14:33:17.223213194 +0800
|
|
@@ -10,17 +10,219 @@
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/sctp.h>
|
|
+#include <linux/ptp_clock_kernel.h>
|
|
+#include <linux/timecounter.h>
|
|
+#include <linux/net_tstamp.h>
|
|
|
|
#include "igc_hw.h"
|
|
|
|
-/* forward declaration */
|
|
-void igc_set_ethtool_ops(struct net_device *);
|
|
+void igc_ethtool_set_ops(struct net_device *);
|
|
|
|
-struct igc_adapter;
|
|
-struct igc_ring;
|
|
+/* Transmit and receive queues */
|
|
+#define IGC_MAX_RX_QUEUES 4
|
|
+#define IGC_MAX_TX_QUEUES 4
|
|
+
|
|
+#define MAX_Q_VECTORS 8
|
|
+#define MAX_STD_JUMBO_FRAME_SIZE 9216
|
|
+
|
|
+#define MAX_ETYPE_FILTER 8
|
|
+#define IGC_RETA_SIZE 128
|
|
+
|
|
+enum igc_mac_filter_type {
|
|
+ IGC_MAC_FILTER_TYPE_DST = 0,
|
|
+ IGC_MAC_FILTER_TYPE_SRC
|
|
+};
|
|
+
|
|
+struct igc_tx_queue_stats {
|
|
+ u64 packets;
|
|
+ u64 bytes;
|
|
+ u64 restart_queue;
|
|
+ u64 restart_queue2;
|
|
+};
|
|
+
|
|
+struct igc_rx_queue_stats {
|
|
+ u64 packets;
|
|
+ u64 bytes;
|
|
+ u64 drops;
|
|
+ u64 csum_err;
|
|
+ u64 alloc_failed;
|
|
+};
|
|
+
|
|
+struct igc_rx_packet_stats {
|
|
+ u64 ipv4_packets; /* IPv4 headers processed */
|
|
+ u64 ipv4e_packets; /* IPv4E headers with extensions processed */
|
|
+ u64 ipv6_packets; /* IPv6 headers processed */
|
|
+ u64 ipv6e_packets; /* IPv6E headers with extensions processed */
|
|
+ u64 tcp_packets; /* TCP headers processed */
|
|
+ u64 udp_packets; /* UDP headers processed */
|
|
+ u64 sctp_packets; /* SCTP headers processed */
|
|
+ u64 nfs_packets; /* NFS headers processe */
|
|
+ u64 other_packets;
|
|
+};
|
|
+
|
|
+struct igc_ring_container {
|
|
+ struct igc_ring *ring; /* pointer to linked list of rings */
|
|
+ unsigned int total_bytes; /* total bytes processed this int */
|
|
+ unsigned int total_packets; /* total packets processed this int */
|
|
+ u16 work_limit; /* total work allowed per interrupt */
|
|
+ u8 count; /* total number of rings in vector */
|
|
+ u8 itr; /* current ITR setting for ring */
|
|
+};
|
|
+
|
|
+struct igc_ring {
|
|
+ struct igc_q_vector *q_vector; /* backlink to q_vector */
|
|
+ struct net_device *netdev; /* back pointer to net_device */
|
|
+ struct device *dev; /* device for dma mapping */
|
|
+ union { /* array of buffer info structs */
|
|
+ struct igc_tx_buffer *tx_buffer_info;
|
|
+ struct igc_rx_buffer *rx_buffer_info;
|
|
+ };
|
|
+ void *desc; /* descriptor ring memory */
|
|
+ unsigned long flags; /* ring specific flags */
|
|
+ void __iomem *tail; /* pointer to ring tail register */
|
|
+ dma_addr_t dma; /* phys address of the ring */
|
|
+ unsigned int size; /* length of desc. ring in bytes */
|
|
+
|
|
+ u16 count; /* number of desc. in the ring */
|
|
+ u8 queue_index; /* logical index of the ring*/
|
|
+ u8 reg_idx; /* physical index of the ring */
|
|
+ bool launchtime_enable; /* true if LaunchTime is enabled */
|
|
+
|
|
+ u32 start_time;
|
|
+ u32 end_time;
|
|
+
|
|
+ /* everything past this point are written often */
|
|
+ u16 next_to_clean;
|
|
+ u16 next_to_use;
|
|
+ u16 next_to_alloc;
|
|
+
|
|
+ union {
|
|
+ /* TX */
|
|
+ struct {
|
|
+ struct igc_tx_queue_stats tx_stats;
|
|
+ struct u64_stats_sync tx_syncp;
|
|
+ struct u64_stats_sync tx_syncp2;
|
|
+ };
|
|
+ /* RX */
|
|
+ struct {
|
|
+ struct igc_rx_queue_stats rx_stats;
|
|
+ struct igc_rx_packet_stats pkt_stats;
|
|
+ struct u64_stats_sync rx_syncp;
|
|
+ struct sk_buff *skb;
|
|
+ };
|
|
+ };
|
|
+} ____cacheline_internodealigned_in_smp;
|
|
+
|
|
+/* Board specific private data structure */
|
|
+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;
|
|
+
|
|
+ struct msix_entry *msix_entries;
|
|
+
|
|
+ /* TX */
|
|
+ u16 tx_work_limit;
|
|
+ u32 tx_timeout_count;
|
|
+ int num_tx_queues;
|
|
+ struct igc_ring *tx_ring[IGC_MAX_TX_QUEUES];
|
|
+
|
|
+ /* RX */
|
|
+ int num_rx_queues;
|
|
+ struct igc_ring *rx_ring[IGC_MAX_RX_QUEUES];
|
|
+
|
|
+ struct timer_list watchdog_timer;
|
|
+ struct timer_list dma_err_timer;
|
|
+ struct timer_list phy_info_timer;
|
|
+
|
|
+ u32 wol;
|
|
+ u32 en_mng_pt;
|
|
+ u16 link_speed;
|
|
+ u16 link_duplex;
|
|
+
|
|
+ u8 port_num;
|
|
+
|
|
+ u8 __iomem *io_addr;
|
|
+ /* Interrupt Throttle Rate */
|
|
+ u32 rx_itr_setting;
|
|
+ u32 tx_itr_setting;
|
|
+
|
|
+ struct work_struct reset_task;
|
|
+ struct work_struct watchdog_task;
|
|
+ struct work_struct dma_err_task;
|
|
+ bool fc_autoneg;
|
|
+
|
|
+ u8 tx_timeout_factor;
|
|
+
|
|
+ int msg_enable;
|
|
+ u32 max_frame_size;
|
|
+ u32 min_frame_size;
|
|
+
|
|
+ ktime_t base_time;
|
|
+ ktime_t cycle_time;
|
|
+
|
|
+ /* OS defined structs */
|
|
+ struct pci_dev *pdev;
|
|
+ /* lock for statistics */
|
|
+ spinlock_t stats64_lock;
|
|
+ struct rtnl_link_stats64 stats64;
|
|
+
|
|
+ /* structs defined in igc_hw.h */
|
|
+ struct igc_hw hw;
|
|
+ struct igc_hw_stats stats;
|
|
+
|
|
+ struct igc_q_vector *q_vector[MAX_Q_VECTORS];
|
|
+ u32 eims_enable_mask;
|
|
+ u32 eims_other;
|
|
+
|
|
+ u16 tx_ring_count;
|
|
+ u16 rx_ring_count;
|
|
+
|
|
+ u32 tx_hwtstamp_timeouts;
|
|
+ u32 tx_hwtstamp_skipped;
|
|
+ u32 rx_hwtstamp_cleared;
|
|
+
|
|
+ u32 rss_queues;
|
|
+ u32 rss_indir_tbl_init;
|
|
+
|
|
+ /* Any access to elements in nfc_rule_list is protected by the
|
|
+ * nfc_rule_lock.
|
|
+ */
|
|
+ struct mutex nfc_rule_lock;
|
|
+ struct list_head nfc_rule_list;
|
|
+ unsigned int nfc_rule_count;
|
|
+
|
|
+ u8 rss_indir_tbl[IGC_RETA_SIZE];
|
|
+
|
|
+ unsigned long link_check_timeout;
|
|
+ struct igc_info ei;
|
|
+
|
|
+ u32 test_icr;
|
|
+
|
|
+ struct ptp_clock *ptp_clock;
|
|
+ struct ptp_clock_info ptp_caps;
|
|
+ struct work_struct ptp_tx_work;
|
|
+ struct sk_buff *ptp_tx_skb;
|
|
+ struct hwtstamp_config tstamp_config;
|
|
+ unsigned long ptp_tx_start;
|
|
+ unsigned int ptp_flags;
|
|
+ /* System time value lock */
|
|
+ spinlock_t tmreg_lock;
|
|
+ struct cyclecounter cc;
|
|
+ struct timecounter tc;
|
|
+ struct timespec64 prev_ptp_time; /* Pre-reset PTP clock */
|
|
+ ktime_t ptp_reset_start; /* Reset time in clock mono */
|
|
+};
|
|
|
|
void igc_up(struct igc_adapter *adapter);
|
|
void igc_down(struct igc_adapter *adapter);
|
|
+int igc_open(struct net_device *netdev);
|
|
+int igc_close(struct net_device *netdev);
|
|
int igc_setup_tx_resources(struct igc_ring *ring);
|
|
int igc_setup_rx_resources(struct igc_ring *ring);
|
|
void igc_free_tx_resources(struct igc_ring *ring);
|
|
@@ -33,29 +235,33 @@
|
|
bool igc_has_link(struct igc_adapter *adapter);
|
|
void igc_reset(struct igc_adapter *adapter);
|
|
int igc_set_spd_dplx(struct igc_adapter *adapter, u32 spd, u8 dplx);
|
|
-int igc_add_mac_steering_filter(struct igc_adapter *adapter,
|
|
- const u8 *addr, u8 queue, u8 flags);
|
|
-int igc_del_mac_steering_filter(struct igc_adapter *adapter,
|
|
- const u8 *addr, u8 queue, u8 flags);
|
|
void igc_update_stats(struct igc_adapter *adapter);
|
|
|
|
+/* igc_dump declarations */
|
|
+void igc_rings_dump(struct igc_adapter *adapter);
|
|
+void igc_regs_dump(struct igc_adapter *adapter);
|
|
+
|
|
extern char igc_driver_name[];
|
|
-extern char igc_driver_version[];
|
|
|
|
#define IGC_REGS_LEN 740
|
|
-#define IGC_RETA_SIZE 128
|
|
|
|
-/* Interrupt defines */
|
|
-#define IGC_START_ITR 648 /* ~6000 ints/sec */
|
|
+/* flags controlling PTP/1588 function */
|
|
+#define IGC_PTP_ENABLED BIT(0)
|
|
+
|
|
+/* Flags definitions */
|
|
#define IGC_FLAG_HAS_MSI BIT(0)
|
|
#define IGC_FLAG_QUEUE_PAIRS BIT(3)
|
|
#define IGC_FLAG_DMAC BIT(4)
|
|
+#define IGC_FLAG_PTP BIT(8)
|
|
+#define IGC_FLAG_WOL_SUPPORTED BIT(8)
|
|
#define IGC_FLAG_NEED_LINK_UPDATE BIT(9)
|
|
#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)
|
|
|
|
#define IGC_FLAG_RSS_FIELD_IPV4_UDP BIT(6)
|
|
#define IGC_FLAG_RSS_FIELD_IPV6_UDP BIT(7)
|
|
@@ -64,6 +270,7 @@
|
|
#define IGC_MRQC_RSS_FIELD_IPV4_UDP 0x00400000
|
|
#define IGC_MRQC_RSS_FIELD_IPV6_UDP 0x00800000
|
|
|
|
+/* Interrupt defines */
|
|
#define IGC_START_ITR 648 /* ~6000 ints/sec */
|
|
#define IGC_4K_ITR 980
|
|
#define IGC_20K_ITR 196
|
|
@@ -85,13 +292,6 @@
|
|
#define IGC_MIN_RXD 80
|
|
#define IGC_MAX_RXD 4096
|
|
|
|
-/* Transmit and receive queues */
|
|
-#define IGC_MAX_RX_QUEUES 4
|
|
-#define IGC_MAX_TX_QUEUES 4
|
|
-
|
|
-#define MAX_Q_VECTORS 8
|
|
-#define MAX_STD_JUMBO_FRAME_SIZE 9216
|
|
-
|
|
/* Supported Rx Buffer Sizes */
|
|
#define IGC_RXBUFFER_256 256
|
|
#define IGC_RXBUFFER_2048 2048
|
|
@@ -100,6 +300,16 @@
|
|
#define AUTO_ALL_MODES 0
|
|
#define IGC_RX_HDR_LEN IGC_RXBUFFER_256
|
|
|
|
+/* Transmit and receive latency (for PTP timestamps) */
|
|
+#define IGC_I225_TX_LATENCY_10 240
|
|
+#define IGC_I225_TX_LATENCY_100 58
|
|
+#define IGC_I225_TX_LATENCY_1000 80
|
|
+#define IGC_I225_TX_LATENCY_2500 1325
|
|
+#define IGC_I225_RX_LATENCY_10 6450
|
|
+#define IGC_I225_RX_LATENCY_100 185
|
|
+#define IGC_I225_RX_LATENCY_1000 300
|
|
+#define IGC_I225_RX_LATENCY_2500 1485
|
|
+
|
|
/* RX and TX descriptor control thresholds.
|
|
* PTHRESH - MAC will consider prefetch if it has fewer than this number of
|
|
* descriptors available in its onboard memory.
|
|
@@ -204,83 +414,6 @@
|
|
__u16 pagecnt_bias;
|
|
};
|
|
|
|
-struct igc_tx_queue_stats {
|
|
- u64 packets;
|
|
- u64 bytes;
|
|
- u64 restart_queue;
|
|
- u64 restart_queue2;
|
|
-};
|
|
-
|
|
-struct igc_rx_queue_stats {
|
|
- u64 packets;
|
|
- u64 bytes;
|
|
- u64 drops;
|
|
- u64 csum_err;
|
|
- u64 alloc_failed;
|
|
-};
|
|
-
|
|
-struct igc_rx_packet_stats {
|
|
- u64 ipv4_packets; /* IPv4 headers processed */
|
|
- u64 ipv4e_packets; /* IPv4E headers with extensions processed */
|
|
- u64 ipv6_packets; /* IPv6 headers processed */
|
|
- u64 ipv6e_packets; /* IPv6E headers with extensions processed */
|
|
- u64 tcp_packets; /* TCP headers processed */
|
|
- u64 udp_packets; /* UDP headers processed */
|
|
- u64 sctp_packets; /* SCTP headers processed */
|
|
- u64 nfs_packets; /* NFS headers processe */
|
|
- u64 other_packets;
|
|
-};
|
|
-
|
|
-struct igc_ring_container {
|
|
- struct igc_ring *ring; /* pointer to linked list of rings */
|
|
- unsigned int total_bytes; /* total bytes processed this int */
|
|
- unsigned int total_packets; /* total packets processed this int */
|
|
- u16 work_limit; /* total work allowed per interrupt */
|
|
- u8 count; /* total number of rings in vector */
|
|
- u8 itr; /* current ITR setting for ring */
|
|
-};
|
|
-
|
|
-struct igc_ring {
|
|
- struct igc_q_vector *q_vector; /* backlink to q_vector */
|
|
- struct net_device *netdev; /* back pointer to net_device */
|
|
- struct device *dev; /* device for dma mapping */
|
|
- union { /* array of buffer info structs */
|
|
- struct igc_tx_buffer *tx_buffer_info;
|
|
- struct igc_rx_buffer *rx_buffer_info;
|
|
- };
|
|
- void *desc; /* descriptor ring memory */
|
|
- unsigned long flags; /* ring specific flags */
|
|
- void __iomem *tail; /* pointer to ring tail register */
|
|
- dma_addr_t dma; /* phys address of the ring */
|
|
- unsigned int size; /* length of desc. ring in bytes */
|
|
-
|
|
- u16 count; /* number of desc. in the ring */
|
|
- u8 queue_index; /* logical index of the ring*/
|
|
- u8 reg_idx; /* physical index of the ring */
|
|
- bool launchtime_enable; /* true if LaunchTime is enabled */
|
|
-
|
|
- /* everything past this point are written often */
|
|
- u16 next_to_clean;
|
|
- u16 next_to_use;
|
|
- u16 next_to_alloc;
|
|
-
|
|
- union {
|
|
- /* TX */
|
|
- struct {
|
|
- struct igc_tx_queue_stats tx_stats;
|
|
- struct u64_stats_sync tx_syncp;
|
|
- struct u64_stats_sync tx_syncp2;
|
|
- };
|
|
- /* RX */
|
|
- struct {
|
|
- struct igc_rx_queue_stats rx_stats;
|
|
- struct igc_rx_packet_stats pkt_stats;
|
|
- struct u64_stats_sync rx_syncp;
|
|
- struct sk_buff *skb;
|
|
- };
|
|
- };
|
|
-} ____cacheline_internodealigned_in_smp;
|
|
-
|
|
struct igc_q_vector {
|
|
struct igc_adapter *adapter; /* backlink */
|
|
void __iomem *itr_register;
|
|
@@ -298,11 +431,9 @@
|
|
struct net_device poll_dev;
|
|
|
|
/* for dynamic allocation of rings associated with this q_vector */
|
|
- struct igc_ring ring[0] ____cacheline_internodealigned_in_smp;
|
|
+ struct igc_ring ring[] ____cacheline_internodealigned_in_smp;
|
|
};
|
|
|
|
-#define MAX_ETYPE_FILTER (4 - 1)
|
|
-
|
|
enum igc_filter_match_flags {
|
|
IGC_FILTER_FLAG_ETHER_TYPE = 0x1,
|
|
IGC_FILTER_FLAG_VLAN_TCI = 0x2,
|
|
@@ -310,128 +441,25 @@
|
|
IGC_FILTER_FLAG_DST_MAC_ADDR = 0x8,
|
|
};
|
|
|
|
-/* RX network flow classification data structure */
|
|
-struct igc_nfc_input {
|
|
- /* Byte layout in order, all values with MSB first:
|
|
- * match_flags - 1 byte
|
|
- * etype - 2 bytes
|
|
- * vlan_tci - 2 bytes
|
|
- */
|
|
+struct igc_nfc_filter {
|
|
u8 match_flags;
|
|
- __be16 etype;
|
|
- __be16 vlan_tci;
|
|
+ u16 etype;
|
|
+ u16 vlan_tci;
|
|
u8 src_addr[ETH_ALEN];
|
|
u8 dst_addr[ETH_ALEN];
|
|
};
|
|
|
|
-struct igc_nfc_filter {
|
|
- struct hlist_node nfc_node;
|
|
- struct igc_nfc_input filter;
|
|
- unsigned long cookie;
|
|
- u16 etype_reg_index;
|
|
- u16 sw_idx;
|
|
+struct igc_nfc_rule {
|
|
+ struct list_head list;
|
|
+ struct igc_nfc_filter filter;
|
|
+ u32 location;
|
|
u16 action;
|
|
};
|
|
|
|
-struct igc_mac_addr {
|
|
- u8 addr[ETH_ALEN];
|
|
- u8 queue;
|
|
- u8 state; /* bitmask */
|
|
-};
|
|
-
|
|
-#define IGC_MAC_STATE_DEFAULT 0x1
|
|
-#define IGC_MAC_STATE_IN_USE 0x2
|
|
-#define IGC_MAC_STATE_SRC_ADDR 0x4
|
|
-#define IGC_MAC_STATE_QUEUE_STEERING 0x8
|
|
-
|
|
-#define IGC_MAX_RXNFC_FILTERS 16
|
|
-
|
|
-/* Board specific private data structure */
|
|
-struct igc_adapter {
|
|
- struct net_device *netdev;
|
|
-
|
|
- unsigned long state;
|
|
- unsigned int flags;
|
|
- unsigned int num_q_vectors;
|
|
-
|
|
- struct msix_entry *msix_entries;
|
|
-
|
|
- /* TX */
|
|
- u16 tx_work_limit;
|
|
- u32 tx_timeout_count;
|
|
- int num_tx_queues;
|
|
- struct igc_ring *tx_ring[IGC_MAX_TX_QUEUES];
|
|
-
|
|
- /* RX */
|
|
- int num_rx_queues;
|
|
- struct igc_ring *rx_ring[IGC_MAX_RX_QUEUES];
|
|
-
|
|
- struct timer_list watchdog_timer;
|
|
- struct timer_list dma_err_timer;
|
|
- struct timer_list phy_info_timer;
|
|
-
|
|
- u16 link_speed;
|
|
- u16 link_duplex;
|
|
-
|
|
- u8 port_num;
|
|
-
|
|
- u8 __iomem *io_addr;
|
|
- /* Interrupt Throttle Rate */
|
|
- u32 rx_itr_setting;
|
|
- u32 tx_itr_setting;
|
|
-
|
|
- struct work_struct reset_task;
|
|
- struct work_struct watchdog_task;
|
|
- struct work_struct dma_err_task;
|
|
- bool fc_autoneg;
|
|
-
|
|
- u8 tx_timeout_factor;
|
|
-
|
|
- int msg_enable;
|
|
- u32 max_frame_size;
|
|
- u32 min_frame_size;
|
|
-
|
|
- /* OS defined structs */
|
|
- struct pci_dev *pdev;
|
|
- /* lock for statistics */
|
|
- spinlock_t stats64_lock;
|
|
- struct rtnl_link_stats64 stats64;
|
|
-
|
|
- /* structs defined in igc_hw.h */
|
|
- struct igc_hw hw;
|
|
- struct igc_hw_stats stats;
|
|
-
|
|
- struct igc_q_vector *q_vector[MAX_Q_VECTORS];
|
|
- u32 eims_enable_mask;
|
|
- u32 eims_other;
|
|
-
|
|
- u16 tx_ring_count;
|
|
- u16 rx_ring_count;
|
|
-
|
|
- u32 tx_hwtstamp_timeouts;
|
|
- u32 tx_hwtstamp_skipped;
|
|
- u32 rx_hwtstamp_cleared;
|
|
- u32 *shadow_vfta;
|
|
-
|
|
- u32 rss_queues;
|
|
- u32 rss_indir_tbl_init;
|
|
-
|
|
- /* RX network flow classification support */
|
|
- struct hlist_head nfc_filter_list;
|
|
- struct hlist_head cls_flower_list;
|
|
- unsigned int nfc_filter_count;
|
|
-
|
|
- /* lock for RX network flow classification filter */
|
|
- spinlock_t nfc_lock;
|
|
- bool etype_bitmap[MAX_ETYPE_FILTER];
|
|
-
|
|
- struct igc_mac_addr *mac_table;
|
|
-
|
|
- u8 rss_indir_tbl[IGC_RETA_SIZE];
|
|
-
|
|
- unsigned long link_check_timeout;
|
|
- struct igc_info ei;
|
|
-};
|
|
+/* IGC supports a total of 32 NFC rules: 16 MAC address based,, 8 VLAN priority
|
|
+ * based, and 8 ethertype based.
|
|
+ */
|
|
+#define IGC_MAX_RXNFC_RULES 32
|
|
|
|
/* igc_desc_unused - calculate if we have unused descriptors */
|
|
static inline u16 igc_desc_unused(const struct igc_ring *ring)
|
|
@@ -507,12 +535,22 @@
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
-/* forward declaration */
|
|
void igc_reinit_locked(struct igc_adapter *);
|
|
-int igc_add_filter(struct igc_adapter *adapter,
|
|
- struct igc_nfc_filter *input);
|
|
-int igc_erase_filter(struct igc_adapter *adapter,
|
|
- struct igc_nfc_filter *input);
|
|
+struct igc_nfc_rule *igc_get_nfc_rule(struct igc_adapter *adapter,
|
|
+ u32 location);
|
|
+int igc_add_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule);
|
|
+void igc_del_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule);
|
|
+
|
|
+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_pktstamp(struct igc_q_vector *q_vector, __le32 *va,
|
|
+ struct sk_buff *skb);
|
|
+int igc_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
|
|
+int igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
|
|
+void igc_ptp_tx_hang(struct igc_adapter *adapter);
|
|
+void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts);
|
|
|
|
#define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring))
|
|
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_hw.h 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_hw.h 2022-03-10 14:51:34.344921696 +0800
|
|
@@ -21,8 +21,17 @@
|
|
#define IGC_DEV_ID_I225_I 0x15F8
|
|
#define IGC_DEV_ID_I220_V 0x15F7
|
|
#define IGC_DEV_ID_I225_K 0x3100
|
|
-
|
|
-#define IGC_FUNC_0 0
|
|
+#define IGC_DEV_ID_I225_K2 0x3101
|
|
+#define IGC_DEV_ID_I226_K 0x3102
|
|
+#define IGC_DEV_ID_I225_LMVP 0x5502
|
|
+#define IGC_DEV_ID_I226_LMVP 0x5503
|
|
+#define IGC_DEV_ID_I225_IT 0x0D9F
|
|
+#define IGC_DEV_ID_I226_LM 0x125B
|
|
+#define IGC_DEV_ID_I226_V 0x125C
|
|
+#define IGC_DEV_ID_I226_IT 0x125D
|
|
+#define IGC_DEV_ID_I221_V 0x125E
|
|
+#define IGC_DEV_ID_I226_BLANK_NVM 0x125F
|
|
+#define IGC_DEV_ID_I225_BLANK_NVM 0x15FD
|
|
|
|
/* Function pointers for the MAC. */
|
|
struct igc_mac_operations {
|
|
@@ -46,7 +55,6 @@
|
|
|
|
enum igc_phy_type {
|
|
igc_phy_unknown = 0,
|
|
- igc_phy_none,
|
|
igc_phy_i225,
|
|
};
|
|
|
|
@@ -59,8 +67,6 @@
|
|
enum igc_nvm_type {
|
|
igc_nvm_unknown = 0,
|
|
igc_nvm_eeprom_spi,
|
|
- igc_nvm_flash_hw,
|
|
- igc_nvm_invm,
|
|
};
|
|
|
|
struct igc_info {
|
|
@@ -80,23 +86,16 @@
|
|
|
|
enum igc_mac_type type;
|
|
|
|
- u32 collision_delta;
|
|
- u32 ledctl_default;
|
|
- u32 ledctl_mode1;
|
|
- u32 ledctl_mode2;
|
|
u32 mc_filter_type;
|
|
- u32 tx_packet_delta;
|
|
- u32 txcw;
|
|
|
|
u16 mta_reg_count;
|
|
u16 uta_reg_count;
|
|
|
|
+ u32 mta_shadow[MAX_MTA_REG];
|
|
u16 rar_entry_count;
|
|
|
|
u8 forced_speed_duplex;
|
|
|
|
- bool adaptive_ifs;
|
|
- bool has_fwsm;
|
|
bool asf_firmware_present;
|
|
bool arc_subsystem_valid;
|
|
|
|
@@ -130,9 +129,6 @@
|
|
struct igc_nvm_operations ops;
|
|
enum igc_nvm_type type;
|
|
|
|
- u32 flash_bank_size;
|
|
- u32 flash_base_addr;
|
|
-
|
|
u16 word_size;
|
|
u16 delay_usec;
|
|
u16 address_bits;
|
|
@@ -158,7 +154,6 @@
|
|
u8 mdix;
|
|
|
|
bool is_mdix;
|
|
- bool reset_disable;
|
|
bool speed_downgraded;
|
|
bool autoneg_wait_to_complete;
|
|
};
|
|
@@ -188,6 +183,7 @@
|
|
|
|
struct igc_dev_spec_base {
|
|
bool clear_semaphore_once;
|
|
+ bool eee_enable;
|
|
};
|
|
|
|
struct igc_hw {
|
|
@@ -243,6 +239,8 @@
|
|
u64 prc511;
|
|
u64 prc1023;
|
|
u64 prc1522;
|
|
+ u64 tlpic;
|
|
+ u64 rlpic;
|
|
u64 gprc;
|
|
u64 bprc;
|
|
u64 mprc;
|
|
@@ -272,21 +270,9 @@
|
|
u64 tsctc;
|
|
u64 tsctfc;
|
|
u64 iac;
|
|
- u64 icrxptc;
|
|
- u64 icrxatc;
|
|
- u64 ictxptc;
|
|
- u64 ictxatc;
|
|
- u64 ictxqec;
|
|
- u64 ictxqmtc;
|
|
- u64 icrxdmtc;
|
|
- u64 icrxoc;
|
|
- u64 cbtmpc;
|
|
u64 htdpmc;
|
|
- u64 cbrdpc;
|
|
- u64 cbrmpc;
|
|
u64 rpthc;
|
|
u64 hgptc;
|
|
- u64 htcbdpc;
|
|
u64 hgorc;
|
|
u64 hgotc;
|
|
u64 lenerrs;
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_i225.c 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_i225.c 2022-03-10 14:46:02.453700756 +0800
|
|
@@ -473,13 +473,11 @@
|
|
|
|
/* NVM Function Pointers */
|
|
if (igc_get_flash_presence_i225(hw)) {
|
|
- hw->nvm.type = igc_nvm_flash_hw;
|
|
nvm->ops.read = igc_read_nvm_srrd_i225;
|
|
nvm->ops.write = igc_write_nvm_srwr_i225;
|
|
nvm->ops.validate = igc_validate_nvm_checksum_i225;
|
|
nvm->ops.update = igc_update_nvm_checksum_i225;
|
|
} else {
|
|
- hw->nvm.type = igc_nvm_invm;
|
|
nvm->ops.read = igc_read_nvm_eerd;
|
|
nvm->ops.write = NULL;
|
|
nvm->ops.validate = NULL;
|
|
@@ -487,3 +485,159 @@
|
|
}
|
|
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_max << IGC_LTRMAXV_SCALE_SHIFT);
|
|
+ wr32(IGC_LTRMAXV, ltrv);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return IGC_SUCCESS;
|
|
+}
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_i225.h 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_i225.h 2022-03-10 14:33:17.223213194 +0800
|
|
@@ -9,5 +9,8 @@
|
|
|
|
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
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_mac.c 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_mac.c 2022-03-10 14:33:17.227212778 +0800
|
|
@@ -235,15 +235,14 @@
|
|
void igc_clear_hw_cntrs_base(struct igc_hw *hw)
|
|
{
|
|
rd32(IGC_CRCERRS);
|
|
- rd32(IGC_SYMERRS);
|
|
rd32(IGC_MPC);
|
|
rd32(IGC_SCC);
|
|
rd32(IGC_ECOL);
|
|
rd32(IGC_MCC);
|
|
rd32(IGC_LATECOL);
|
|
rd32(IGC_COLC);
|
|
+ rd32(IGC_RERC);
|
|
rd32(IGC_DC);
|
|
- rd32(IGC_SEC);
|
|
rd32(IGC_RLEC);
|
|
rd32(IGC_XONRXC);
|
|
rd32(IGC_XONTXC);
|
|
@@ -288,31 +287,20 @@
|
|
rd32(IGC_ALGNERRC);
|
|
rd32(IGC_RXERRC);
|
|
rd32(IGC_TNCRS);
|
|
- rd32(IGC_CEXTERR);
|
|
+ rd32(IGC_HTDPMC);
|
|
rd32(IGC_TSCTC);
|
|
- rd32(IGC_TSCTFC);
|
|
|
|
rd32(IGC_MGTPRC);
|
|
rd32(IGC_MGTPDC);
|
|
rd32(IGC_MGTPTC);
|
|
|
|
rd32(IGC_IAC);
|
|
- rd32(IGC_ICRXOC);
|
|
-
|
|
- rd32(IGC_ICRXPTC);
|
|
- rd32(IGC_ICRXATC);
|
|
- rd32(IGC_ICTXPTC);
|
|
- rd32(IGC_ICTXATC);
|
|
- rd32(IGC_ICTXQEC);
|
|
- rd32(IGC_ICTXQMTC);
|
|
- rd32(IGC_ICRXDMTC);
|
|
|
|
- rd32(IGC_CBTMPC);
|
|
- rd32(IGC_HTDPMC);
|
|
- rd32(IGC_CBRMPC);
|
|
rd32(IGC_RPTHC);
|
|
+ rd32(IGC_TLPIC);
|
|
+ rd32(IGC_RLPIC);
|
|
rd32(IGC_HGPTC);
|
|
- rd32(IGC_HTCBDPC);
|
|
+ rd32(IGC_RXDMTC);
|
|
rd32(IGC_HGORCL);
|
|
rd32(IGC_HGORCH);
|
|
rd32(IGC_HGOTCL);
|
|
@@ -367,8 +355,8 @@
|
|
s32 igc_check_for_copper_link(struct igc_hw *hw)
|
|
{
|
|
struct igc_mac_info *mac = &hw->mac;
|
|
+ bool link = false;
|
|
s32 ret_val;
|
|
- bool link;
|
|
|
|
/* We only want to go out to the PHY registers to see if Auto-Neg
|
|
* has completed and/or if our link status has changed. The
|
|
@@ -422,6 +410,11 @@
|
|
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;
|
|
}
|
|
|
|
@@ -467,10 +460,8 @@
|
|
* 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");
|
|
@@ -482,7 +473,7 @@
|
|
* 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.
|
|
@@ -784,3 +775,107 @@
|
|
out:
|
|
return ret_val;
|
|
}
|
|
+
|
|
+/**
|
|
+ * igc_hash_mc_addr - Generate a multicast hash value
|
|
+ * @hw: pointer to the HW structure
|
|
+ * @mc_addr: pointer to a multicast address
|
|
+ *
|
|
+ * Generates a multicast address hash value which is used to determine
|
|
+ * the multicast filter table array address and new table value. See
|
|
+ * igc_mta_set()
|
|
+ **/
|
|
+static u32 igc_hash_mc_addr(struct igc_hw *hw, u8 *mc_addr)
|
|
+{
|
|
+ u32 hash_value, hash_mask;
|
|
+ u8 bit_shift = 0;
|
|
+
|
|
+ /* Register count multiplied by bits per register */
|
|
+ hash_mask = (hw->mac.mta_reg_count * 32) - 1;
|
|
+
|
|
+ /* For a mc_filter_type of 0, bit_shift is the number of left-shifts
|
|
+ * where 0xFF would still fall within the hash mask.
|
|
+ */
|
|
+ while (hash_mask >> bit_shift != 0xFF)
|
|
+ bit_shift++;
|
|
+
|
|
+ /* The portion of the address that is used for the hash table
|
|
+ * is determined by the mc_filter_type setting.
|
|
+ * The algorithm is such that there is a total of 8 bits of shifting.
|
|
+ * The bit_shift for a mc_filter_type of 0 represents the number of
|
|
+ * left-shifts where the MSB of mc_addr[5] would still fall within
|
|
+ * the hash_mask. Case 0 does this exactly. Since there are a total
|
|
+ * of 8 bits of shifting, then mc_addr[4] will shift right the
|
|
+ * remaining number of bits. Thus 8 - bit_shift. The rest of the
|
|
+ * cases are a variation of this algorithm...essentially raising the
|
|
+ * number of bits to shift mc_addr[5] left, while still keeping the
|
|
+ * 8-bit shifting total.
|
|
+ *
|
|
+ * For example, given the following Destination MAC Address and an
|
|
+ * MTA register count of 128 (thus a 4096-bit vector and 0xFFF mask),
|
|
+ * we can see that the bit_shift for case 0 is 4. These are the hash
|
|
+ * values resulting from each mc_filter_type...
|
|
+ * [0] [1] [2] [3] [4] [5]
|
|
+ * 01 AA 00 12 34 56
|
|
+ * LSB MSB
|
|
+ *
|
|
+ * case 0: hash_value = ((0x34 >> 4) | (0x56 << 4)) & 0xFFF = 0x563
|
|
+ * case 1: hash_value = ((0x34 >> 3) | (0x56 << 5)) & 0xFFF = 0xAC6
|
|
+ * case 2: hash_value = ((0x34 >> 2) | (0x56 << 6)) & 0xFFF = 0x163
|
|
+ * case 3: hash_value = ((0x34 >> 0) | (0x56 << 8)) & 0xFFF = 0x634
|
|
+ */
|
|
+ switch (hw->mac.mc_filter_type) {
|
|
+ default:
|
|
+ case 0:
|
|
+ break;
|
|
+ case 1:
|
|
+ bit_shift += 1;
|
|
+ break;
|
|
+ case 2:
|
|
+ bit_shift += 2;
|
|
+ break;
|
|
+ case 3:
|
|
+ bit_shift += 4;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ hash_value = hash_mask & (((mc_addr[4] >> (8 - bit_shift)) |
|
|
+ (((u16)mc_addr[5]) << bit_shift)));
|
|
+
|
|
+ return hash_value;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_update_mc_addr_list - Update Multicast addresses
|
|
+ * @hw: pointer to the HW structure
|
|
+ * @mc_addr_list: array of multicast addresses to program
|
|
+ * @mc_addr_count: number of multicast addresses to program
|
|
+ *
|
|
+ * Updates entire Multicast Table Array.
|
|
+ * The caller must have a packed mc_addr_list of multicast addresses.
|
|
+ **/
|
|
+void igc_update_mc_addr_list(struct igc_hw *hw,
|
|
+ u8 *mc_addr_list, u32 mc_addr_count)
|
|
+{
|
|
+ u32 hash_value, hash_bit, hash_reg;
|
|
+ int i;
|
|
+
|
|
+ /* clear mta_shadow */
|
|
+ memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow));
|
|
+
|
|
+ /* update mta_shadow from mc_addr_list */
|
|
+ for (i = 0; (u32)i < mc_addr_count; i++) {
|
|
+ hash_value = igc_hash_mc_addr(hw, mc_addr_list);
|
|
+
|
|
+ hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1);
|
|
+ hash_bit = hash_value & 0x1F;
|
|
+
|
|
+ hw->mac.mta_shadow[hash_reg] |= BIT(hash_bit);
|
|
+ mc_addr_list += ETH_ALEN;
|
|
+ }
|
|
+
|
|
+ /* replace the entire MTA table */
|
|
+ for (i = hw->mac.mta_reg_count - 1; i >= 0; i--)
|
|
+ array_wr32(IGC_MTA, i, hw->mac.mta_shadow[i]);
|
|
+ wrfl();
|
|
+}
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_mac.h 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_mac.h 2022-03-10 14:33:17.227212778 +0800
|
|
@@ -8,10 +8,6 @@
|
|
#include "igc_phy.h"
|
|
#include "igc_defines.h"
|
|
|
|
-#ifndef IGC_REMOVED
|
|
-#define IGC_REMOVED(a) (0)
|
|
-#endif /* IGC_REMOVED */
|
|
-
|
|
/* forward declaration */
|
|
s32 igc_disable_pcie_master(struct igc_hw *hw);
|
|
s32 igc_check_for_copper_link(struct igc_hw *hw);
|
|
@@ -29,6 +25,8 @@
|
|
u16 *duplex);
|
|
|
|
bool igc_enable_mng_pass_thru(struct igc_hw *hw);
|
|
+void igc_update_mc_addr_list(struct igc_hw *hw,
|
|
+ u8 *mc_addr_list, u32 mc_addr_count);
|
|
|
|
enum igc_mng_mode {
|
|
igc_mng_mode_none = 0,
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_main.c 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_main.c 2022-03-10 14:56:18.814132021 +0800
|
|
@@ -8,13 +8,15 @@
|
|
#include <linux/tcp.h>
|
|
#include <linux/udp.h>
|
|
#include <linux/ip.h>
|
|
+#include <linux/pm_runtime.h>
|
|
+#include <net/pkt_sched.h>
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
#include "igc.h"
|
|
#include "igc_hw.h"
|
|
+#include "igc_tsn.h"
|
|
|
|
-#define DRV_VERSION "0.0.1-k"
|
|
#define DRV_SUMMARY "Intel(R) 2.5G Ethernet Linux Driver"
|
|
|
|
#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
|
|
@@ -24,12 +26,10 @@
|
|
MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
|
|
MODULE_DESCRIPTION(DRV_SUMMARY);
|
|
MODULE_LICENSE("GPL v2");
|
|
-MODULE_VERSION(DRV_VERSION);
|
|
module_param(debug, int, 0);
|
|
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
|
|
|
|
char igc_driver_name[] = "igc";
|
|
-char igc_driver_version[] = DRV_VERSION;
|
|
static const char igc_driver_string[] = DRV_SUMMARY;
|
|
static const char igc_copyright[] =
|
|
"Copyright(c) 2018 Intel Corporation.";
|
|
@@ -44,31 +44,23 @@
|
|
{ PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_I), board_base },
|
|
{ PCI_VDEVICE(INTEL, IGC_DEV_ID_I220_V), board_base },
|
|
{ PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K2), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_K), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LMVP), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LMVP), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_IT), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_LM), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_V), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_IT), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I221_V), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I226_BLANK_NVM), board_base },
|
|
+ { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_BLANK_NVM), board_base },
|
|
/* required last entry */
|
|
{0, }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, igc_pci_tbl);
|
|
|
|
-/* forward declaration */
|
|
-static void igc_clean_tx_ring(struct igc_ring *tx_ring);
|
|
-static int igc_sw_init(struct igc_adapter *);
|
|
-static void igc_configure(struct igc_adapter *adapter);
|
|
-static void igc_power_down_link(struct igc_adapter *adapter);
|
|
-static void igc_set_default_mac_filter(struct igc_adapter *adapter);
|
|
-static void igc_set_rx_mode(struct net_device *netdev);
|
|
-static void igc_write_itr(struct igc_q_vector *q_vector);
|
|
-static void igc_assign_vector(struct igc_q_vector *q_vector, int msix_vector);
|
|
-static void igc_free_q_vector(struct igc_adapter *adapter, int v_idx);
|
|
-static void igc_set_interrupt_capability(struct igc_adapter *adapter,
|
|
- bool msix);
|
|
-static void igc_free_q_vectors(struct igc_adapter *adapter);
|
|
-static void igc_irq_disable(struct igc_adapter *adapter);
|
|
-static void igc_irq_enable(struct igc_adapter *adapter);
|
|
-static void igc_configure_msix(struct igc_adapter *adapter);
|
|
-static bool igc_alloc_mapped_page(struct igc_ring *rx_ring,
|
|
- struct igc_rx_buffer *bi);
|
|
-
|
|
enum latency_range {
|
|
lowest_latency = 0,
|
|
low_latency = 1,
|
|
@@ -78,7 +70,7 @@
|
|
|
|
void igc_reset(struct igc_adapter *adapter)
|
|
{
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
+ struct net_device *dev = adapter->netdev;
|
|
struct igc_hw *hw = &adapter->hw;
|
|
struct igc_fc_info *fc = &hw->fc;
|
|
u32 pba, hwm;
|
|
@@ -105,39 +97,37 @@
|
|
hw->mac.ops.reset_hw(hw);
|
|
|
|
if (hw->mac.ops.init_hw(hw))
|
|
- dev_err(&pdev->dev, "Hardware Error\n");
|
|
+ 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);
|
|
+
|
|
+ /* Re-enable TSN offloading, where applicable. */
|
|
+ igc_tsn_offload_apply(adapter);
|
|
|
|
igc_get_phy_info(hw);
|
|
}
|
|
|
|
/**
|
|
- * igc_power_up_link - Power up the phy/serdes link
|
|
+ * igc_power_up_link - Power up the phy link
|
|
* @adapter: address of board private structure
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
- * 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);
|
|
-}
|
|
-
|
|
-/**
|
|
* igc_release_hw_control - release control of the h/w to f/w
|
|
* @adapter: address of board private structure
|
|
*
|
|
@@ -150,6 +140,9 @@
|
|
struct igc_hw *hw = &adapter->hw;
|
|
u32 ctrl_ext;
|
|
|
|
+ if (!pci_device_is_present(adapter->pdev))
|
|
+ return;
|
|
+
|
|
/* Let firmware take over control of h/w */
|
|
ctrl_ext = rd32(IGC_CTRL_EXT);
|
|
wr32(IGC_CTRL_EXT,
|
|
@@ -176,43 +169,6 @@
|
|
}
|
|
|
|
/**
|
|
- * igc_free_tx_resources - Free Tx Resources per Queue
|
|
- * @tx_ring: Tx descriptor ring for a specific queue
|
|
- *
|
|
- * Free all transmit software resources
|
|
- */
|
|
-void igc_free_tx_resources(struct igc_ring *tx_ring)
|
|
-{
|
|
- igc_clean_tx_ring(tx_ring);
|
|
-
|
|
- vfree(tx_ring->tx_buffer_info);
|
|
- tx_ring->tx_buffer_info = NULL;
|
|
-
|
|
- /* if not set, then don't free */
|
|
- if (!tx_ring->desc)
|
|
- return;
|
|
-
|
|
- dma_free_coherent(tx_ring->dev, tx_ring->size,
|
|
- tx_ring->desc, tx_ring->dma);
|
|
-
|
|
- tx_ring->desc = NULL;
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_free_all_tx_resources - Free Tx Resources for All Queues
|
|
- * @adapter: board private structure
|
|
- *
|
|
- * Free all transmit software resources
|
|
- */
|
|
-static void igc_free_all_tx_resources(struct igc_adapter *adapter)
|
|
-{
|
|
- int i;
|
|
-
|
|
- for (i = 0; i < adapter->num_tx_queues; i++)
|
|
- igc_free_tx_resources(adapter->tx_ring[i]);
|
|
-}
|
|
-
|
|
-/**
|
|
* igc_clean_tx_ring - Free Tx Buffers
|
|
* @tx_ring: ring to be cleaned
|
|
*/
|
|
@@ -276,6 +232,43 @@
|
|
}
|
|
|
|
/**
|
|
+ * igc_free_tx_resources - Free Tx Resources per Queue
|
|
+ * @tx_ring: Tx descriptor ring for a specific queue
|
|
+ *
|
|
+ * Free all transmit software resources
|
|
+ */
|
|
+void igc_free_tx_resources(struct igc_ring *tx_ring)
|
|
+{
|
|
+ igc_clean_tx_ring(tx_ring);
|
|
+
|
|
+ vfree(tx_ring->tx_buffer_info);
|
|
+ tx_ring->tx_buffer_info = NULL;
|
|
+
|
|
+ /* if not set, then don't free */
|
|
+ if (!tx_ring->desc)
|
|
+ return;
|
|
+
|
|
+ dma_free_coherent(tx_ring->dev, tx_ring->size,
|
|
+ tx_ring->desc, tx_ring->dma);
|
|
+
|
|
+ tx_ring->desc = NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_free_all_tx_resources - Free Tx Resources for All Queues
|
|
+ * @adapter: board private structure
|
|
+ *
|
|
+ * Free all transmit software resources
|
|
+ */
|
|
+static void igc_free_all_tx_resources(struct igc_adapter *adapter)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < adapter->num_tx_queues; i++)
|
|
+ igc_free_tx_resources(adapter->tx_ring[i]);
|
|
+}
|
|
+
|
|
+/**
|
|
* igc_clean_all_tx_rings - Free Tx Buffers for all queues
|
|
* @adapter: board private structure
|
|
*/
|
|
@@ -296,6 +289,7 @@
|
|
*/
|
|
int igc_setup_tx_resources(struct igc_ring *tx_ring)
|
|
{
|
|
+ struct net_device *ndev = tx_ring->netdev;
|
|
struct device *dev = tx_ring->dev;
|
|
int size = 0;
|
|
|
|
@@ -321,8 +315,7 @@
|
|
|
|
err:
|
|
vfree(tx_ring->tx_buffer_info);
|
|
- dev_err(dev,
|
|
- "Unable to allocate memory for the transmit descriptor ring\n");
|
|
+ netdev_err(ndev, "Unable to allocate memory for Tx descriptor ring\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
@@ -334,14 +327,13 @@
|
|
*/
|
|
static int igc_setup_all_tx_resources(struct igc_adapter *adapter)
|
|
{
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
+ struct net_device *dev = adapter->netdev;
|
|
int i, err = 0;
|
|
|
|
for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
err = igc_setup_tx_resources(adapter->tx_ring[i]);
|
|
if (err) {
|
|
- dev_err(&pdev->dev,
|
|
- "Allocation for Tx Queue %u failed\n", i);
|
|
+ netdev_err(dev, "Error on Tx queue %u setup\n", i);
|
|
for (i--; i >= 0; i--)
|
|
igc_free_tx_resources(adapter->tx_ring[i]);
|
|
break;
|
|
@@ -452,6 +444,7 @@
|
|
*/
|
|
int igc_setup_rx_resources(struct igc_ring *rx_ring)
|
|
{
|
|
+ struct net_device *ndev = rx_ring->netdev;
|
|
struct device *dev = rx_ring->dev;
|
|
int size, desc_len;
|
|
|
|
@@ -481,8 +474,7 @@
|
|
err:
|
|
vfree(rx_ring->rx_buffer_info);
|
|
rx_ring->rx_buffer_info = NULL;
|
|
- dev_err(dev,
|
|
- "Unable to allocate memory for the receive descriptor ring\n");
|
|
+ netdev_err(ndev, "Unable to allocate memory for Rx descriptor ring\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
@@ -495,14 +487,13 @@
|
|
*/
|
|
static int igc_setup_all_rx_resources(struct igc_adapter *adapter)
|
|
{
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
+ struct net_device *dev = adapter->netdev;
|
|
int i, err = 0;
|
|
|
|
for (i = 0; i < adapter->num_rx_queues; i++) {
|
|
err = igc_setup_rx_resources(adapter->rx_ring[i]);
|
|
if (err) {
|
|
- dev_err(&pdev->dev,
|
|
- "Allocation for Rx Queue %u failed\n", i);
|
|
+ netdev_err(dev, "Error on Rx queue %u setup\n", i);
|
|
for (i--; i >= 0; i--)
|
|
igc_free_rx_resources(adapter->rx_ring[i]);
|
|
break;
|
|
@@ -773,6 +764,79 @@
|
|
}
|
|
|
|
/**
|
|
+ * igc_set_mac_filter_hw() - Set MAC address filter in hardware
|
|
+ * @adapter: Pointer to adapter where the filter should be set
|
|
+ * @index: Filter index
|
|
+ * @type: MAC address filter type (source or destination)
|
|
+ * @addr: MAC address
|
|
+ * @queue: If non-negative, queue assignment feature is enabled and frames
|
|
+ * matching the filter are enqueued onto 'queue'. Otherwise, queue
|
|
+ * assignment is disabled.
|
|
+ */
|
|
+static void igc_set_mac_filter_hw(struct igc_adapter *adapter, int index,
|
|
+ enum igc_mac_filter_type type,
|
|
+ const u8 *addr, int queue)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 ral, rah;
|
|
+
|
|
+ if (WARN_ON(index >= hw->mac.rar_entry_count))
|
|
+ return;
|
|
+
|
|
+ ral = le32_to_cpup((__le32 *)(addr));
|
|
+ rah = le16_to_cpup((__le16 *)(addr + 4));
|
|
+
|
|
+ if (type == IGC_MAC_FILTER_TYPE_SRC) {
|
|
+ rah &= ~IGC_RAH_ASEL_MASK;
|
|
+ rah |= IGC_RAH_ASEL_SRC_ADDR;
|
|
+ }
|
|
+
|
|
+ if (queue >= 0) {
|
|
+ rah &= ~IGC_RAH_QSEL_MASK;
|
|
+ rah |= (queue << IGC_RAH_QSEL_SHIFT);
|
|
+ rah |= IGC_RAH_QSEL_ENABLE;
|
|
+ }
|
|
+
|
|
+ rah |= IGC_RAH_AV;
|
|
+
|
|
+ wr32(IGC_RAL(index), ral);
|
|
+ wr32(IGC_RAH(index), rah);
|
|
+
|
|
+ netdev_dbg(dev, "MAC address filter set in HW: index %d", index);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_clear_mac_filter_hw() - Clear MAC address filter in hardware
|
|
+ * @adapter: Pointer to adapter where the filter should be cleared
|
|
+ * @index: Filter index
|
|
+ */
|
|
+static void igc_clear_mac_filter_hw(struct igc_adapter *adapter, int index)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ if (WARN_ON(index >= hw->mac.rar_entry_count))
|
|
+ return;
|
|
+
|
|
+ wr32(IGC_RAL(index), 0);
|
|
+ wr32(IGC_RAH(index), 0);
|
|
+
|
|
+ netdev_dbg(dev, "MAC address filter cleared in HW: index %d", index);
|
|
+}
|
|
+
|
|
+/* Set default MAC address for the PF in the first RAR entry */
|
|
+static void igc_set_default_mac_filter(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ u8 *addr = adapter->hw.mac.addr;
|
|
+
|
|
+ netdev_dbg(dev, "Set default MAC address filter: address %pM", addr);
|
|
+
|
|
+ igc_set_mac_filter_hw(adapter, 0, IGC_MAC_FILTER_TYPE_DST, addr, -1);
|
|
+}
|
|
+
|
|
+/**
|
|
* igc_set_mac - Change the Ethernet Address of the NIC
|
|
* @netdev: network interface device structure
|
|
* @p: pointer to an address structure
|
|
@@ -797,6 +861,61 @@
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * igc_write_mc_addr_list - write multicast addresses to MTA
|
|
+ * @netdev: network interface device structure
|
|
+ *
|
|
+ * Writes multicast address list to the MTA hash table.
|
|
+ * Returns: -ENOMEM on failure
|
|
+ * 0 on no addresses written
|
|
+ * X on writing X addresses to MTA
|
|
+ **/
|
|
+static int igc_write_mc_addr_list(struct net_device *netdev)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ struct netdev_hw_addr *ha;
|
|
+ u8 *mta_list;
|
|
+ int i;
|
|
+
|
|
+ if (netdev_mc_empty(netdev)) {
|
|
+ /* nothing to program, so clear mc list */
|
|
+ igc_update_mc_addr_list(hw, NULL, 0);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ mta_list = kcalloc(netdev_mc_count(netdev), 6, GFP_ATOMIC);
|
|
+ if (!mta_list)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* The shared function expects a packed array of only addresses. */
|
|
+ i = 0;
|
|
+ netdev_for_each_mc_addr(ha, netdev)
|
|
+ memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN);
|
|
+
|
|
+ igc_update_mc_addr_list(hw, mta_list, i);
|
|
+ kfree(mta_list);
|
|
+
|
|
+ return netdev_mc_count(netdev);
|
|
+}
|
|
+
|
|
+static __le32 igc_tx_launchtime(struct igc_adapter *adapter, ktime_t txtime)
|
|
+{
|
|
+ ktime_t cycle_time = adapter->cycle_time;
|
|
+ ktime_t base_time = adapter->base_time;
|
|
+ u32 launchtime;
|
|
+
|
|
+ /* FIXME: when using ETF together with taprio, we may have a
|
|
+ * case where 'delta' is larger than the cycle_time, this may
|
|
+ * cause problems if we don't read the current value of
|
|
+ * IGC_BASET, as the value writen into the launchtime
|
|
+ * descriptor field may be misinterpreted.
|
|
+ */
|
|
+ div_s64_rem(ktime_sub_ns(txtime, base_time), cycle_time, &launchtime);
|
|
+
|
|
+ return cpu_to_le32(launchtime);
|
|
+}
|
|
+
|
|
static void igc_tx_ctxtdesc(struct igc_ring *tx_ring,
|
|
struct igc_tx_buffer *first,
|
|
u32 vlan_macip_lens, u32 type_tucmd,
|
|
@@ -804,7 +923,6 @@
|
|
{
|
|
struct igc_adv_tx_context_desc *context_desc;
|
|
u16 i = tx_ring->next_to_use;
|
|
- struct timespec64 ts;
|
|
|
|
context_desc = IGC_TX_CTXTDESC(tx_ring, i);
|
|
|
|
@@ -814,7 +932,7 @@
|
|
/* set bits to identify this as an advanced context descriptor */
|
|
type_tucmd |= IGC_TXD_CMD_DEXT | IGC_ADVTXD_DTYP_CTXT;
|
|
|
|
- /* For 82575, context index must be unique per ring. */
|
|
+ /* For i225, context index must be unique per ring. */
|
|
if (test_bit(IGC_RING_FLAG_TX_CTX_IDX, &tx_ring->flags))
|
|
mss_l4len_idx |= tx_ring->reg_idx << 4;
|
|
|
|
@@ -826,9 +944,12 @@
|
|
* should have been handled by the upper layers.
|
|
*/
|
|
if (tx_ring->launchtime_enable) {
|
|
- ts = ktime_to_timespec64(first->skb->tstamp);
|
|
+ struct igc_adapter *adapter = netdev_priv(tx_ring->netdev);
|
|
+ ktime_t txtime = first->skb->tstamp;
|
|
+
|
|
first->skb->tstamp = ktime_set(0, 0);
|
|
- context_desc->launch_time = cpu_to_le32(ts.tv_nsec / 32);
|
|
+ context_desc->launch_time = igc_tx_launchtime(adapter,
|
|
+ txtime);
|
|
} else {
|
|
context_desc->launch_time = 0;
|
|
}
|
|
@@ -860,7 +981,7 @@
|
|
switch (skb->csum_offset) {
|
|
case offsetof(struct tcphdr, check):
|
|
type_tucmd = IGC_ADVTXD_TUCMD_L4T_TCP;
|
|
- /* fall through */
|
|
+ fallthrough;
|
|
case offsetof(struct udphdr, check):
|
|
break;
|
|
case offsetof(struct sctphdr, checksum):
|
|
@@ -872,7 +993,7 @@
|
|
type_tucmd = IGC_ADVTXD_TUCMD_L4T_SCTP;
|
|
break;
|
|
}
|
|
- /* fall through */
|
|
+ fallthrough;
|
|
default:
|
|
skb_checksum_help(skb);
|
|
goto csum_failed;
|
|
@@ -921,6 +1042,11 @@
|
|
return __igc_maybe_stop_tx(tx_ring, size);
|
|
}
|
|
|
|
+#define IGC_SET_FLAG(_input, _flag, _result) \
|
|
+ (((_flag) <= (_result)) ? \
|
|
+ ((u32)((_input) & (_flag)) * ((_result) / (_flag))) : \
|
|
+ ((u32)((_input) & (_flag)) / ((_flag) / (_result))))
|
|
+
|
|
static u32 igc_tx_cmd_type(struct sk_buff *skb, u32 tx_flags)
|
|
{
|
|
/* set type for advanced descriptor with frame checksum insertion */
|
|
@@ -928,6 +1054,14 @@
|
|
IGC_ADVTXD_DCMD_DEXT |
|
|
IGC_ADVTXD_DCMD_IFCS;
|
|
|
|
+ /* set segmentation bits for TSO */
|
|
+ cmd_type |= IGC_SET_FLAG(tx_flags, IGC_TX_FLAGS_TSO,
|
|
+ (IGC_ADVTXD_DCMD_TSE));
|
|
+
|
|
+ /* set timestamp bit if present */
|
|
+ cmd_type |= IGC_SET_FLAG(tx_flags, IGC_TX_FLAGS_TSTAMP,
|
|
+ (IGC_ADVTXD_MAC_TSTAMP));
|
|
+
|
|
return cmd_type;
|
|
}
|
|
|
|
@@ -1063,7 +1197,7 @@
|
|
|
|
return 0;
|
|
dma_error:
|
|
- dev_err(tx_ring->dev, "TX DMA map failed\n");
|
|
+ netdev_err(tx_ring->netdev, "TX DMA map failed\n");
|
|
tx_buffer = &tx_ring->tx_buffer_info[i];
|
|
|
|
/* clear dma mappings for failed tx_buffer_info map */
|
|
@@ -1095,6 +1229,100 @@
|
|
return -1;
|
|
}
|
|
|
|
+static int igc_tso(struct igc_ring *tx_ring,
|
|
+ struct igc_tx_buffer *first,
|
|
+ u8 *hdr_len)
|
|
+{
|
|
+ u32 vlan_macip_lens, type_tucmd, mss_l4len_idx;
|
|
+ struct sk_buff *skb = first->skb;
|
|
+ union {
|
|
+ struct iphdr *v4;
|
|
+ struct ipv6hdr *v6;
|
|
+ unsigned char *hdr;
|
|
+ } ip;
|
|
+ union {
|
|
+ struct tcphdr *tcp;
|
|
+ struct udphdr *udp;
|
|
+ unsigned char *hdr;
|
|
+ } l4;
|
|
+ u32 paylen, l4_offset;
|
|
+ int err;
|
|
+
|
|
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
|
|
+ return 0;
|
|
+
|
|
+ if (!skb_is_gso(skb))
|
|
+ return 0;
|
|
+
|
|
+ err = skb_cow_head(skb, 0);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+
|
|
+ ip.hdr = skb_network_header(skb);
|
|
+ l4.hdr = skb_checksum_start(skb);
|
|
+
|
|
+ /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
|
|
+ type_tucmd = IGC_ADVTXD_TUCMD_L4T_TCP;
|
|
+
|
|
+ /* initialize outer IP header fields */
|
|
+ if (ip.v4->version == 4) {
|
|
+ unsigned char *csum_start = skb_checksum_start(skb);
|
|
+ unsigned char *trans_start = ip.hdr + (ip.v4->ihl * 4);
|
|
+
|
|
+ /* IP header will have to cancel out any data that
|
|
+ * is not a part of the outer IP header
|
|
+ */
|
|
+ ip.v4->check = csum_fold(csum_partial(trans_start,
|
|
+ csum_start - trans_start,
|
|
+ 0));
|
|
+ type_tucmd |= IGC_ADVTXD_TUCMD_IPV4;
|
|
+
|
|
+ ip.v4->tot_len = 0;
|
|
+ first->tx_flags |= IGC_TX_FLAGS_TSO |
|
|
+ IGC_TX_FLAGS_CSUM |
|
|
+ IGC_TX_FLAGS_IPV4;
|
|
+ } else {
|
|
+ ip.v6->payload_len = 0;
|
|
+ first->tx_flags |= IGC_TX_FLAGS_TSO |
|
|
+ IGC_TX_FLAGS_CSUM;
|
|
+ }
|
|
+
|
|
+ /* determine offset of inner transport header */
|
|
+ l4_offset = l4.hdr - skb->data;
|
|
+
|
|
+ /* remove payload length from inner checksum */
|
|
+ paylen = skb->len - l4_offset;
|
|
+ if (type_tucmd & IGC_ADVTXD_TUCMD_L4T_TCP) {
|
|
+ /* compute length of segmentation header */
|
|
+ *hdr_len = (l4.tcp->doff * 4) + l4_offset;
|
|
+ csum_replace_by_diff(&l4.tcp->check,
|
|
+ (__force __wsum)htonl(paylen));
|
|
+ } else {
|
|
+ /* compute length of segmentation header */
|
|
+ *hdr_len = sizeof(*l4.udp) + l4_offset;
|
|
+ csum_replace_by_diff(&l4.udp->check,
|
|
+ (__force __wsum)htonl(paylen));
|
|
+ }
|
|
+
|
|
+ /* update gso size and bytecount with header size */
|
|
+ first->gso_segs = skb_shinfo(skb)->gso_segs;
|
|
+ first->bytecount += (first->gso_segs - 1) * *hdr_len;
|
|
+
|
|
+ /* MSS L4LEN IDX */
|
|
+ mss_l4len_idx = (*hdr_len - l4_offset) << IGC_ADVTXD_L4LEN_SHIFT;
|
|
+ mss_l4len_idx |= skb_shinfo(skb)->gso_size << IGC_ADVTXD_MSS_SHIFT;
|
|
+
|
|
+ /* VLAN MACLEN IPLEN */
|
|
+ vlan_macip_lens = l4.hdr - ip.hdr;
|
|
+ vlan_macip_lens |= (ip.hdr - skb->data) << IGC_ADVTXD_MACLEN_SHIFT;
|
|
+ vlan_macip_lens |= first->tx_flags & IGC_TX_FLAGS_VLAN_MASK;
|
|
+
|
|
+ igc_tx_ctxtdesc(tx_ring, first, vlan_macip_lens,
|
|
+ type_tucmd, mss_l4len_idx);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb,
|
|
struct igc_ring *tx_ring)
|
|
{
|
|
@@ -1104,6 +1332,7 @@
|
|
u32 tx_flags = 0;
|
|
unsigned short f;
|
|
u8 hdr_len = 0;
|
|
+ int tso = 0;
|
|
|
|
/* need: 1 descriptor per page * PAGE_SIZE/IGC_MAX_DATA_PER_TXD,
|
|
* + 1 desc for skb_headlen/IGC_MAX_DATA_PER_TXD,
|
|
@@ -1126,15 +1355,45 @@
|
|
first->bytecount = skb->len;
|
|
first->gso_segs = 1;
|
|
|
|
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
|
|
+ struct igc_adapter *adapter = netdev_priv(tx_ring->netdev);
|
|
+
|
|
+ /* FIXME: add support for retrieving timestamps from
|
|
+ * the other timer registers before skipping the
|
|
+ * timestamping request.
|
|
+ */
|
|
+ if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
|
|
+ !test_and_set_bit_lock(__IGC_PTP_TX_IN_PROGRESS,
|
|
+ &adapter->state)) {
|
|
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
|
+ tx_flags |= IGC_TX_FLAGS_TSTAMP;
|
|
+
|
|
+ adapter->ptp_tx_skb = skb_get(skb);
|
|
+ adapter->ptp_tx_start = jiffies;
|
|
+ } else {
|
|
+ adapter->tx_hwtstamp_skipped++;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* record initial flags and protocol */
|
|
first->tx_flags = tx_flags;
|
|
first->protocol = protocol;
|
|
|
|
- igc_tx_csum(tx_ring, first);
|
|
+ tso = igc_tso(tx_ring, first, &hdr_len);
|
|
+ if (tso < 0)
|
|
+ goto out_drop;
|
|
+ else if (!tso)
|
|
+ igc_tx_csum(tx_ring, first);
|
|
|
|
igc_tx_map(tx_ring, first, hdr_len);
|
|
|
|
return NETDEV_TX_OK;
|
|
+
|
|
+out_drop:
|
|
+ dev_kfree_skb_any(first->skb);
|
|
+ first->skb = NULL;
|
|
+
|
|
+ return NETDEV_TX_OK;
|
|
}
|
|
|
|
static inline struct igc_ring *igc_tx_queue_mapping(struct igc_adapter *adapter,
|
|
@@ -1165,6 +1424,46 @@
|
|
return igc_xmit_frame_ring(skb, igc_tx_queue_mapping(adapter, skb));
|
|
}
|
|
|
|
+static void igc_rx_checksum(struct igc_ring *ring,
|
|
+ union igc_adv_rx_desc *rx_desc,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ skb_checksum_none_assert(skb);
|
|
+
|
|
+ /* Ignore Checksum bit is set */
|
|
+ if (igc_test_staterr(rx_desc, IGC_RXD_STAT_IXSM))
|
|
+ return;
|
|
+
|
|
+ /* Rx checksum disabled via ethtool */
|
|
+ if (!(ring->netdev->features & NETIF_F_RXCSUM))
|
|
+ return;
|
|
+
|
|
+ /* TCP/UDP checksum error bit is set */
|
|
+ if (igc_test_staterr(rx_desc,
|
|
+ IGC_RXDEXT_STATERR_L4E |
|
|
+ IGC_RXDEXT_STATERR_IPE)) {
|
|
+ /* work around errata with sctp packets where the TCPE aka
|
|
+ * L4E bit is set incorrectly on 64 byte (60 byte w/o crc)
|
|
+ * packets (aka let the stack check the crc32c)
|
|
+ */
|
|
+ if (!(skb->len == 60 &&
|
|
+ test_bit(IGC_RING_FLAG_RX_SCTP_CSUM, &ring->flags))) {
|
|
+ u64_stats_update_begin(&ring->rx_syncp);
|
|
+ ring->rx_stats.csum_err++;
|
|
+ u64_stats_update_end(&ring->rx_syncp);
|
|
+ }
|
|
+ /* let the stack verify checksum errors */
|
|
+ return;
|
|
+ }
|
|
+ /* It must be a TCP or UDP packet with a valid checksum */
|
|
+ if (igc_test_staterr(rx_desc, IGC_RXD_STAT_TCPCS |
|
|
+ IGC_RXD_STAT_UDPCS))
|
|
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
+
|
|
+ netdev_dbg(ring->netdev, "cksum success: bits %08X\n",
|
|
+ le32_to_cpu(rx_desc->wb.upper.status_error));
|
|
+}
|
|
+
|
|
static inline void igc_rx_hash(struct igc_ring *ring,
|
|
union igc_adv_rx_desc *rx_desc,
|
|
struct sk_buff *skb)
|
|
@@ -1181,9 +1480,9 @@
|
|
* @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,
|
|
@@ -1191,6 +1490,8 @@
|
|
{
|
|
igc_rx_hash(rx_ring, rx_desc, skb);
|
|
|
|
+ igc_rx_checksum(rx_ring, rx_desc, skb);
|
|
+
|
|
skb_record_rx_queue(skb, rx_ring->queue_index);
|
|
|
|
skb->protocol = eth_type_trans(skb, rx_ring->netdev);
|
|
@@ -1310,6 +1611,12 @@
|
|
if (unlikely(!skb))
|
|
return NULL;
|
|
|
|
+ if (unlikely(igc_test_staterr(rx_desc, IGC_RXDADV_STAT_TSIP))) {
|
|
+ igc_ptp_rx_pktstamp(rx_ring->q_vector, va, skb);
|
|
+ va += IGC_TS_HDR_LEN;
|
|
+ size -= IGC_TS_HDR_LEN;
|
|
+ }
|
|
+
|
|
/* Determine available headroom for copy */
|
|
headlen = size;
|
|
if (headlen > IGC_RX_HDR_LEN)
|
|
@@ -1407,7 +1714,6 @@
|
|
* igc_is_non_eop - process handling of non-EOP buffers
|
|
* @rx_ring: Rx ring being processed
|
|
* @rx_desc: Rx descriptor for current buffer
|
|
- * @skb: current socket buffer containing buffer in progress
|
|
*
|
|
* This function updates next to clean. If the buffer is an EOP buffer
|
|
* this function exits returning false, otherwise it will place the
|
|
@@ -1449,8 +1755,7 @@
|
|
union igc_adv_rx_desc *rx_desc,
|
|
struct sk_buff *skb)
|
|
{
|
|
- if (unlikely((igc_test_staterr(rx_desc,
|
|
- IGC_RXDEXT_ERR_FRAME_ERR_MASK)))) {
|
|
+ if (unlikely(igc_test_staterr(rx_desc, IGC_RXDEXT_STATERR_RXE))) {
|
|
struct net_device *netdev = rx_ring->netdev;
|
|
|
|
if (!(netdev->features & NETIF_F_RXALL)) {
|
|
@@ -1487,9 +1792,56 @@
|
|
rx_buffer->page = NULL;
|
|
}
|
|
|
|
+static inline unsigned int igc_rx_offset(struct igc_ring *rx_ring)
|
|
+{
|
|
+ return ring_uses_build_skb(rx_ring) ? IGC_SKB_PAD : 0;
|
|
+}
|
|
+
|
|
+static bool igc_alloc_mapped_page(struct igc_ring *rx_ring,
|
|
+ struct igc_rx_buffer *bi)
|
|
+{
|
|
+ struct page *page = bi->page;
|
|
+ dma_addr_t dma;
|
|
+
|
|
+ /* since we are recycling buffers we should seldom need to alloc */
|
|
+ if (likely(page))
|
|
+ return true;
|
|
+
|
|
+ /* alloc new page for storage */
|
|
+ page = dev_alloc_pages(igc_rx_pg_order(rx_ring));
|
|
+ if (unlikely(!page)) {
|
|
+ rx_ring->rx_stats.alloc_failed++;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ /* map page for use */
|
|
+ dma = dma_map_page_attrs(rx_ring->dev, page, 0,
|
|
+ igc_rx_pg_size(rx_ring),
|
|
+ DMA_FROM_DEVICE,
|
|
+ IGC_RX_DMA_ATTR);
|
|
+
|
|
+ /* if mapping failed free memory back to system since
|
|
+ * there isn't much point in holding memory we can't use
|
|
+ */
|
|
+ if (dma_mapping_error(rx_ring->dev, dma)) {
|
|
+ __free_page(page);
|
|
+
|
|
+ rx_ring->rx_stats.alloc_failed++;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ bi->dma = dma;
|
|
+ bi->page = page;
|
|
+ bi->page_offset = igc_rx_offset(rx_ring);
|
|
+ bi->pagecnt_bias = 1;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
/**
|
|
* igc_alloc_rx_buffers - Replace used receive buffers; packet split
|
|
- * @adapter: address of board private structure
|
|
+ * @rx_ring: rx descriptor ring
|
|
+ * @cleaned_count: number of buffers to clean
|
|
*/
|
|
static void igc_alloc_rx_buffers(struct igc_ring *rx_ring, u16 cleaned_count)
|
|
{
|
|
@@ -1619,7 +1971,7 @@
|
|
/* 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);
|
|
@@ -1647,52 +1999,6 @@
|
|
return total_packets;
|
|
}
|
|
|
|
-static inline unsigned int igc_rx_offset(struct igc_ring *rx_ring)
|
|
-{
|
|
- return ring_uses_build_skb(rx_ring) ? IGC_SKB_PAD : 0;
|
|
-}
|
|
-
|
|
-static bool igc_alloc_mapped_page(struct igc_ring *rx_ring,
|
|
- struct igc_rx_buffer *bi)
|
|
-{
|
|
- struct page *page = bi->page;
|
|
- dma_addr_t dma;
|
|
-
|
|
- /* since we are recycling buffers we should seldom need to alloc */
|
|
- if (likely(page))
|
|
- return true;
|
|
-
|
|
- /* alloc new page for storage */
|
|
- page = dev_alloc_pages(igc_rx_pg_order(rx_ring));
|
|
- if (unlikely(!page)) {
|
|
- rx_ring->rx_stats.alloc_failed++;
|
|
- return false;
|
|
- }
|
|
-
|
|
- /* map page for use */
|
|
- dma = dma_map_page_attrs(rx_ring->dev, page, 0,
|
|
- igc_rx_pg_size(rx_ring),
|
|
- DMA_FROM_DEVICE,
|
|
- IGC_RX_DMA_ATTR);
|
|
-
|
|
- /* if mapping failed free memory back to system since
|
|
- * there isn't much point in holding memory we can't use
|
|
- */
|
|
- if (dma_mapping_error(rx_ring->dev, dma)) {
|
|
- __free_page(page);
|
|
-
|
|
- rx_ring->rx_stats.alloc_failed++;
|
|
- return false;
|
|
- }
|
|
-
|
|
- bi->dma = dma;
|
|
- bi->page = page;
|
|
- bi->page_offset = igc_rx_offset(rx_ring);
|
|
- bi->pagecnt_bias = 1;
|
|
-
|
|
- return true;
|
|
-}
|
|
-
|
|
/**
|
|
* igc_clean_tx_irq - Reclaim resources after transmit completes
|
|
* @q_vector: pointer to q_vector containing needed info
|
|
@@ -1812,27 +2118,27 @@
|
|
(adapter->tx_timeout_factor * HZ)) &&
|
|
!(rd32(IGC_STATUS) & IGC_STATUS_TXOFF)) {
|
|
/* detected Tx unit hang */
|
|
- dev_err(tx_ring->dev,
|
|
- "Detected Tx Unit Hang\n"
|
|
- " Tx Queue <%d>\n"
|
|
- " TDH <%x>\n"
|
|
- " TDT <%x>\n"
|
|
- " next_to_use <%x>\n"
|
|
- " next_to_clean <%x>\n"
|
|
- "buffer_info[next_to_clean]\n"
|
|
- " time_stamp <%lx>\n"
|
|
- " next_to_watch <%p>\n"
|
|
- " jiffies <%lx>\n"
|
|
- " desc.status <%x>\n",
|
|
- tx_ring->queue_index,
|
|
- rd32(IGC_TDH(tx_ring->reg_idx)),
|
|
- readl(tx_ring->tail),
|
|
- tx_ring->next_to_use,
|
|
- tx_ring->next_to_clean,
|
|
- tx_buffer->time_stamp,
|
|
- tx_buffer->next_to_watch,
|
|
- jiffies,
|
|
- tx_buffer->next_to_watch->wb.status);
|
|
+ netdev_err(tx_ring->netdev,
|
|
+ "Detected Tx Unit Hang\n"
|
|
+ " Tx Queue <%d>\n"
|
|
+ " TDH <%x>\n"
|
|
+ " TDT <%x>\n"
|
|
+ " next_to_use <%x>\n"
|
|
+ " next_to_clean <%x>\n"
|
|
+ "buffer_info[next_to_clean]\n"
|
|
+ " time_stamp <%lx>\n"
|
|
+ " next_to_watch <%p>\n"
|
|
+ " jiffies <%lx>\n"
|
|
+ " desc.status <%x>\n",
|
|
+ tx_ring->queue_index,
|
|
+ rd32(IGC_TDH(tx_ring->reg_idx)),
|
|
+ readl(tx_ring->tail),
|
|
+ tx_ring->next_to_use,
|
|
+ tx_ring->next_to_clean,
|
|
+ tx_buffer->time_stamp,
|
|
+ tx_buffer->next_to_watch,
|
|
+ jiffies,
|
|
+ tx_buffer->next_to_watch->wb.status);
|
|
netif_stop_subqueue(tx_ring->netdev,
|
|
tx_ring->queue_index);
|
|
|
|
@@ -1864,6 +2170,1416 @@
|
|
return !!budget;
|
|
}
|
|
|
|
+static int igc_find_mac_filter(struct igc_adapter *adapter,
|
|
+ enum igc_mac_filter_type type, const u8 *addr)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int max_entries = hw->mac.rar_entry_count;
|
|
+ u32 ral, rah;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < max_entries; i++) {
|
|
+ ral = rd32(IGC_RAL(i));
|
|
+ rah = rd32(IGC_RAH(i));
|
|
+
|
|
+ if (!(rah & IGC_RAH_AV))
|
|
+ continue;
|
|
+ if (!!(rah & IGC_RAH_ASEL_SRC_ADDR) != type)
|
|
+ continue;
|
|
+ if ((rah & IGC_RAH_RAH_MASK) !=
|
|
+ le16_to_cpup((__le16 *)(addr + 4)))
|
|
+ continue;
|
|
+ if (ral != le32_to_cpup((__le32 *)(addr)))
|
|
+ continue;
|
|
+
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int igc_get_avail_mac_filter_slot(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int max_entries = hw->mac.rar_entry_count;
|
|
+ u32 rah;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < max_entries; i++) {
|
|
+ rah = rd32(IGC_RAH(i));
|
|
+
|
|
+ if (!(rah & IGC_RAH_AV))
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_add_mac_filter() - Add MAC address filter
|
|
+ * @adapter: Pointer to adapter where the filter should be added
|
|
+ * @type: MAC address filter type (source or destination)
|
|
+ * @addr: MAC address
|
|
+ * @queue: If non-negative, queue assignment feature is enabled and frames
|
|
+ * matching the filter are enqueued onto 'queue'. Otherwise, queue
|
|
+ * assignment is disabled.
|
|
+ *
|
|
+ * Return: 0 in case of success, negative errno code otherwise.
|
|
+ */
|
|
+static int igc_add_mac_filter(struct igc_adapter *adapter,
|
|
+ enum igc_mac_filter_type type, const u8 *addr,
|
|
+ int queue)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ int index;
|
|
+
|
|
+ index = igc_find_mac_filter(adapter, type, addr);
|
|
+ if (index >= 0)
|
|
+ goto update_filter;
|
|
+
|
|
+ index = igc_get_avail_mac_filter_slot(adapter);
|
|
+ if (index < 0)
|
|
+ return -ENOSPC;
|
|
+
|
|
+ netdev_dbg(dev, "Add MAC address filter: index %d type %s address %pM queue %d\n",
|
|
+ index, type == IGC_MAC_FILTER_TYPE_DST ? "dst" : "src",
|
|
+ addr, queue);
|
|
+
|
|
+update_filter:
|
|
+ igc_set_mac_filter_hw(adapter, index, type, addr, queue);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_del_mac_filter() - Delete MAC address filter
|
|
+ * @adapter: Pointer to adapter where the filter should be deleted from
|
|
+ * @type: MAC address filter type (source or destination)
|
|
+ * @addr: MAC address
|
|
+ */
|
|
+static void igc_del_mac_filter(struct igc_adapter *adapter,
|
|
+ enum igc_mac_filter_type type, const u8 *addr)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ int index;
|
|
+
|
|
+ index = igc_find_mac_filter(adapter, type, addr);
|
|
+ if (index < 0)
|
|
+ return;
|
|
+
|
|
+ if (index == 0) {
|
|
+ /* If this is the default filter, we don't actually delete it.
|
|
+ * We just reset to its default value i.e. disable queue
|
|
+ * assignment.
|
|
+ */
|
|
+ netdev_dbg(dev, "Disable default MAC filter queue assignment");
|
|
+
|
|
+ igc_set_mac_filter_hw(adapter, 0, type, addr, -1);
|
|
+ } else {
|
|
+ netdev_dbg(dev, "Delete MAC address filter: index %d type %s address %pM\n",
|
|
+ index,
|
|
+ type == IGC_MAC_FILTER_TYPE_DST ? "dst" : "src",
|
|
+ addr);
|
|
+
|
|
+ igc_clear_mac_filter_hw(adapter, index);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_add_vlan_prio_filter() - Add VLAN priority filter
|
|
+ * @adapter: Pointer to adapter where the filter should be added
|
|
+ * @prio: VLAN priority value
|
|
+ * @queue: Queue number which matching frames are assigned to
|
|
+ *
|
|
+ * Return: 0 in case of success, negative errno code otherwise.
|
|
+ */
|
|
+static int igc_add_vlan_prio_filter(struct igc_adapter *adapter, int prio,
|
|
+ int queue)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 vlanpqf;
|
|
+
|
|
+ vlanpqf = rd32(IGC_VLANPQF);
|
|
+
|
|
+ if (vlanpqf & IGC_VLANPQF_VALID(prio)) {
|
|
+ netdev_dbg(dev, "VLAN priority filter already in use\n");
|
|
+ return -EEXIST;
|
|
+ }
|
|
+
|
|
+ vlanpqf |= IGC_VLANPQF_QSEL(prio, queue);
|
|
+ vlanpqf |= IGC_VLANPQF_VALID(prio);
|
|
+
|
|
+ wr32(IGC_VLANPQF, vlanpqf);
|
|
+
|
|
+ netdev_dbg(dev, "Add VLAN priority filter: prio %d queue %d\n",
|
|
+ prio, queue);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_del_vlan_prio_filter() - Delete VLAN priority filter
|
|
+ * @adapter: Pointer to adapter where the filter should be deleted from
|
|
+ * @prio: VLAN priority value
|
|
+ */
|
|
+static void igc_del_vlan_prio_filter(struct igc_adapter *adapter, int prio)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 vlanpqf;
|
|
+
|
|
+ vlanpqf = rd32(IGC_VLANPQF);
|
|
+
|
|
+ vlanpqf &= ~IGC_VLANPQF_VALID(prio);
|
|
+ vlanpqf &= ~IGC_VLANPQF_QSEL(prio, IGC_VLANPQF_QUEUE_MASK);
|
|
+
|
|
+ wr32(IGC_VLANPQF, vlanpqf);
|
|
+
|
|
+ netdev_dbg(adapter->netdev, "Delete VLAN priority filter: prio %d\n",
|
|
+ prio);
|
|
+}
|
|
+
|
|
+static int igc_get_avail_etype_filter_slot(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < MAX_ETYPE_FILTER; i++) {
|
|
+ u32 etqf = rd32(IGC_ETQF(i));
|
|
+
|
|
+ if (!(etqf & IGC_ETQF_FILTER_ENABLE))
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_add_etype_filter() - Add ethertype filter
|
|
+ * @adapter: Pointer to adapter where the filter should be added
|
|
+ * @etype: Ethertype value
|
|
+ * @queue: If non-negative, queue assignment feature is enabled and frames
|
|
+ * matching the filter are enqueued onto 'queue'. Otherwise, queue
|
|
+ * assignment is disabled.
|
|
+ *
|
|
+ * Return: 0 in case of success, negative errno code otherwise.
|
|
+ */
|
|
+static int igc_add_etype_filter(struct igc_adapter *adapter, u16 etype,
|
|
+ int queue)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int index;
|
|
+ u32 etqf;
|
|
+
|
|
+ index = igc_get_avail_etype_filter_slot(adapter);
|
|
+ if (index < 0)
|
|
+ return -ENOSPC;
|
|
+
|
|
+ etqf = rd32(IGC_ETQF(index));
|
|
+
|
|
+ etqf &= ~IGC_ETQF_ETYPE_MASK;
|
|
+ etqf |= etype;
|
|
+
|
|
+ if (queue >= 0) {
|
|
+ etqf &= ~IGC_ETQF_QUEUE_MASK;
|
|
+ etqf |= (queue << IGC_ETQF_QUEUE_SHIFT);
|
|
+ etqf |= IGC_ETQF_QUEUE_ENABLE;
|
|
+ }
|
|
+
|
|
+ etqf |= IGC_ETQF_FILTER_ENABLE;
|
|
+
|
|
+ wr32(IGC_ETQF(index), etqf);
|
|
+
|
|
+ netdev_dbg(adapter->netdev, "Add ethertype filter: etype %04x queue %d\n",
|
|
+ etype, queue);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int igc_find_etype_filter(struct igc_adapter *adapter, u16 etype)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < MAX_ETYPE_FILTER; i++) {
|
|
+ u32 etqf = rd32(IGC_ETQF(i));
|
|
+
|
|
+ if ((etqf & IGC_ETQF_ETYPE_MASK) == etype)
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_del_etype_filter() - Delete ethertype filter
|
|
+ * @adapter: Pointer to adapter where the filter should be deleted from
|
|
+ * @etype: Ethertype value
|
|
+ */
|
|
+static void igc_del_etype_filter(struct igc_adapter *adapter, u16 etype)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int index;
|
|
+
|
|
+ index = igc_find_etype_filter(adapter, etype);
|
|
+ if (index < 0)
|
|
+ return;
|
|
+
|
|
+ wr32(IGC_ETQF(index), 0);
|
|
+
|
|
+ netdev_dbg(adapter->netdev, "Delete ethertype filter: etype %04x\n",
|
|
+ etype);
|
|
+}
|
|
+
|
|
+static int igc_enable_nfc_rule(struct igc_adapter *adapter,
|
|
+ const struct igc_nfc_rule *rule)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE) {
|
|
+ err = igc_add_etype_filter(adapter, rule->filter.etype,
|
|
+ rule->action);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
|
|
+ err = igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_SRC,
|
|
+ rule->filter.src_addr, rule->action);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
|
|
+ err = igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST,
|
|
+ rule->filter.dst_addr, rule->action);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
|
|
+ int prio = (rule->filter.vlan_tci & VLAN_PRIO_MASK) >>
|
|
+ VLAN_PRIO_SHIFT;
|
|
+
|
|
+ err = igc_add_vlan_prio_filter(adapter, prio, rule->action);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void igc_disable_nfc_rule(struct igc_adapter *adapter,
|
|
+ const struct igc_nfc_rule *rule)
|
|
+{
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_ETHER_TYPE)
|
|
+ igc_del_etype_filter(adapter, rule->filter.etype);
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
|
|
+ int prio = (rule->filter.vlan_tci & VLAN_PRIO_MASK) >>
|
|
+ VLAN_PRIO_SHIFT;
|
|
+
|
|
+ igc_del_vlan_prio_filter(adapter, prio);
|
|
+ }
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR)
|
|
+ igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_SRC,
|
|
+ rule->filter.src_addr);
|
|
+
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR)
|
|
+ igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST,
|
|
+ rule->filter.dst_addr);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_get_nfc_rule() - Get NFC rule
|
|
+ * @adapter: Pointer to adapter
|
|
+ * @location: Rule location
|
|
+ *
|
|
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
|
|
+ *
|
|
+ * Return: Pointer to NFC rule at @location. If not found, NULL.
|
|
+ */
|
|
+struct igc_nfc_rule *igc_get_nfc_rule(struct igc_adapter *adapter,
|
|
+ u32 location)
|
|
+{
|
|
+ struct igc_nfc_rule *rule;
|
|
+
|
|
+ list_for_each_entry(rule, &adapter->nfc_rule_list, list) {
|
|
+ if (rule->location == location)
|
|
+ return rule;
|
|
+ if (rule->location > location)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_del_nfc_rule() - Delete NFC rule
|
|
+ * @adapter: Pointer to adapter
|
|
+ * @rule: Pointer to rule to be deleted
|
|
+ *
|
|
+ * Disable NFC rule in hardware and delete it from adapter.
|
|
+ *
|
|
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
|
|
+ */
|
|
+void igc_del_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule)
|
|
+{
|
|
+ igc_disable_nfc_rule(adapter, rule);
|
|
+
|
|
+ list_del(&rule->list);
|
|
+ adapter->nfc_rule_count--;
|
|
+
|
|
+ kfree(rule);
|
|
+}
|
|
+
|
|
+static void igc_flush_nfc_rules(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_nfc_rule *rule, *tmp;
|
|
+
|
|
+ mutex_lock(&adapter->nfc_rule_lock);
|
|
+
|
|
+ list_for_each_entry_safe(rule, tmp, &adapter->nfc_rule_list, list)
|
|
+ igc_del_nfc_rule(adapter, rule);
|
|
+
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_add_nfc_rule() - Add NFC rule
|
|
+ * @adapter: Pointer to adapter
|
|
+ * @rule: Pointer to rule to be added
|
|
+ *
|
|
+ * Enable NFC rule in hardware and add it to adapter.
|
|
+ *
|
|
+ * Context: Expects adapter->nfc_rule_lock to be held by caller.
|
|
+ *
|
|
+ * Return: 0 on success, negative errno on failure.
|
|
+ */
|
|
+int igc_add_nfc_rule(struct igc_adapter *adapter, struct igc_nfc_rule *rule)
|
|
+{
|
|
+ struct igc_nfc_rule *pred, *cur;
|
|
+ int err;
|
|
+
|
|
+ err = igc_enable_nfc_rule(adapter, rule);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ pred = NULL;
|
|
+ list_for_each_entry(cur, &adapter->nfc_rule_list, list) {
|
|
+ if (cur->location >= rule->location)
|
|
+ break;
|
|
+ pred = cur;
|
|
+ }
|
|
+
|
|
+ list_add(&rule->list, pred ? &pred->list : &adapter->nfc_rule_list);
|
|
+ adapter->nfc_rule_count++;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void igc_restore_nfc_rules(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_nfc_rule *rule;
|
|
+
|
|
+ mutex_lock(&adapter->nfc_rule_lock);
|
|
+
|
|
+ list_for_each_entry_reverse(rule, &adapter->nfc_rule_list, list)
|
|
+ igc_enable_nfc_rule(adapter, rule);
|
|
+
|
|
+ mutex_unlock(&adapter->nfc_rule_lock);
|
|
+}
|
|
+
|
|
+static int igc_uc_sync(struct net_device *netdev, const unsigned char *addr)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+
|
|
+ return igc_add_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST, addr, -1);
|
|
+}
|
|
+
|
|
+static int igc_uc_unsync(struct net_device *netdev, const unsigned char *addr)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+
|
|
+ igc_del_mac_filter(adapter, IGC_MAC_FILTER_TYPE_DST, addr);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
|
|
+ * @netdev: network interface device structure
|
|
+ *
|
|
+ * The set_rx_mode entry point is called whenever the unicast or multicast
|
|
+ * address lists or the network interface flags are updated. This routine is
|
|
+ * responsible for configuring the hardware for proper unicast, multicast,
|
|
+ * promiscuous mode, and all-multi behavior.
|
|
+ */
|
|
+static void igc_set_rx_mode(struct net_device *netdev)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 rctl = 0, rlpml = MAX_JUMBO_FRAME_SIZE;
|
|
+ int count;
|
|
+
|
|
+ /* Check for Promiscuous and All Multicast modes */
|
|
+ if (netdev->flags & IFF_PROMISC) {
|
|
+ rctl |= IGC_RCTL_UPE | IGC_RCTL_MPE;
|
|
+ } else {
|
|
+ if (netdev->flags & IFF_ALLMULTI) {
|
|
+ rctl |= IGC_RCTL_MPE;
|
|
+ } else {
|
|
+ /* Write addresses to the MTA, if the attempt fails
|
|
+ * then we should just turn on promiscuous mode so
|
|
+ * that we can at least receive multicast traffic
|
|
+ */
|
|
+ count = igc_write_mc_addr_list(netdev);
|
|
+ if (count < 0)
|
|
+ rctl |= IGC_RCTL_MPE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Write addresses to available RAR registers, if there is not
|
|
+ * sufficient space to store all the addresses then enable
|
|
+ * unicast promiscuous mode
|
|
+ */
|
|
+ if (__dev_uc_sync(netdev, igc_uc_sync, igc_uc_unsync))
|
|
+ rctl |= IGC_RCTL_UPE;
|
|
+
|
|
+ /* update state of unicast and multicast */
|
|
+ rctl |= rd32(IGC_RCTL) & ~(IGC_RCTL_UPE | IGC_RCTL_MPE);
|
|
+ wr32(IGC_RCTL, rctl);
|
|
+
|
|
+#if (PAGE_SIZE < 8192)
|
|
+ if (adapter->max_frame_size <= IGC_MAX_FRAME_BUILD_SKB)
|
|
+ rlpml = IGC_MAX_FRAME_BUILD_SKB;
|
|
+#endif
|
|
+ wr32(IGC_RLPML, rlpml);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_configure - configure the hardware for RX and TX
|
|
+ * @adapter: private board structure
|
|
+ */
|
|
+static void igc_configure(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct net_device *netdev = adapter->netdev;
|
|
+ int i = 0;
|
|
+
|
|
+ igc_get_hw_control(adapter);
|
|
+ igc_set_rx_mode(netdev);
|
|
+
|
|
+ igc_setup_tctl(adapter);
|
|
+ igc_setup_mrqc(adapter);
|
|
+ igc_setup_rctl(adapter);
|
|
+
|
|
+ igc_set_default_mac_filter(adapter);
|
|
+ igc_restore_nfc_rules(adapter);
|
|
+
|
|
+ igc_configure_tx(adapter);
|
|
+ igc_configure_rx(adapter);
|
|
+
|
|
+ igc_rx_fifo_flush_base(&adapter->hw);
|
|
+
|
|
+ /* call igc_desc_unused which always leaves
|
|
+ * at least 1 descriptor unused to make sure
|
|
+ * next_to_use != next_to_clean
|
|
+ */
|
|
+ for (i = 0; i < adapter->num_rx_queues; i++) {
|
|
+ struct igc_ring *ring = adapter->rx_ring[i];
|
|
+
|
|
+ igc_alloc_rx_buffers(ring, igc_desc_unused(ring));
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_write_ivar - configure ivar for given MSI-X vector
|
|
+ * @hw: pointer to the HW structure
|
|
+ * @msix_vector: vector number we are allocating to a given ring
|
|
+ * @index: row index of IVAR register to write within IVAR table
|
|
+ * @offset: column offset of in IVAR, should be multiple of 8
|
|
+ *
|
|
+ * The IVAR table consists of 2 columns,
|
|
+ * each containing an cause allocation for an Rx and Tx ring, and a
|
|
+ * variable number of rows depending on the number of queues supported.
|
|
+ */
|
|
+static void igc_write_ivar(struct igc_hw *hw, int msix_vector,
|
|
+ int index, int offset)
|
|
+{
|
|
+ u32 ivar = array_rd32(IGC_IVAR0, index);
|
|
+
|
|
+ /* clear any bits that are currently set */
|
|
+ ivar &= ~((u32)0xFF << offset);
|
|
+
|
|
+ /* write vector and valid bit */
|
|
+ ivar |= (msix_vector | IGC_IVAR_VALID) << offset;
|
|
+
|
|
+ array_wr32(IGC_IVAR0, index, ivar);
|
|
+}
|
|
+
|
|
+static void igc_assign_vector(struct igc_q_vector *q_vector, int msix_vector)
|
|
+{
|
|
+ struct igc_adapter *adapter = q_vector->adapter;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int rx_queue = IGC_N0_QUEUE;
|
|
+ int tx_queue = IGC_N0_QUEUE;
|
|
+
|
|
+ if (q_vector->rx.ring)
|
|
+ rx_queue = q_vector->rx.ring->reg_idx;
|
|
+ if (q_vector->tx.ring)
|
|
+ tx_queue = q_vector->tx.ring->reg_idx;
|
|
+
|
|
+ switch (hw->mac.type) {
|
|
+ case igc_i225:
|
|
+ if (rx_queue > IGC_N0_QUEUE)
|
|
+ igc_write_ivar(hw, msix_vector,
|
|
+ rx_queue >> 1,
|
|
+ (rx_queue & 0x1) << 4);
|
|
+ if (tx_queue > IGC_N0_QUEUE)
|
|
+ igc_write_ivar(hw, msix_vector,
|
|
+ tx_queue >> 1,
|
|
+ ((tx_queue & 0x1) << 4) + 8);
|
|
+ q_vector->eims_value = BIT(msix_vector);
|
|
+ break;
|
|
+ default:
|
|
+ WARN_ONCE(hw->mac.type != igc_i225, "Wrong MAC type\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* add q_vector eims value to global eims_enable_mask */
|
|
+ adapter->eims_enable_mask |= q_vector->eims_value;
|
|
+
|
|
+ /* configure q_vector to set itr on first interrupt */
|
|
+ q_vector->set_itr = 1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_configure_msix - Configure MSI-X hardware
|
|
+ * @adapter: Pointer to adapter structure
|
|
+ *
|
|
+ * igc_configure_msix sets up the hardware to properly
|
|
+ * generate MSI-X interrupts.
|
|
+ */
|
|
+static void igc_configure_msix(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int i, vector = 0;
|
|
+ u32 tmp;
|
|
+
|
|
+ adapter->eims_enable_mask = 0;
|
|
+
|
|
+ /* set vector for other causes, i.e. link changes */
|
|
+ switch (hw->mac.type) {
|
|
+ case igc_i225:
|
|
+ /* Turn on MSI-X capability first, or our settings
|
|
+ * won't stick. And it will take days to debug.
|
|
+ */
|
|
+ wr32(IGC_GPIE, IGC_GPIE_MSIX_MODE |
|
|
+ IGC_GPIE_PBA | IGC_GPIE_EIAME |
|
|
+ IGC_GPIE_NSICR);
|
|
+
|
|
+ /* enable msix_other interrupt */
|
|
+ adapter->eims_other = BIT(vector);
|
|
+ tmp = (vector++ | IGC_IVAR_VALID) << 8;
|
|
+
|
|
+ wr32(IGC_IVAR_MISC, tmp);
|
|
+ break;
|
|
+ default:
|
|
+ /* do nothing, since nothing else supports MSI-X */
|
|
+ break;
|
|
+ } /* switch (hw->mac.type) */
|
|
+
|
|
+ adapter->eims_enable_mask |= adapter->eims_other;
|
|
+
|
|
+ for (i = 0; i < adapter->num_q_vectors; i++)
|
|
+ igc_assign_vector(adapter->q_vector[i], vector++);
|
|
+
|
|
+ wrfl();
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_irq_enable - Enable default interrupt generation settings
|
|
+ * @adapter: board private structure
|
|
+ */
|
|
+static void igc_irq_enable(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ if (adapter->msix_entries) {
|
|
+ u32 ims = IGC_IMS_LSC | IGC_IMS_DOUTSYNC | IGC_IMS_DRSTA;
|
|
+ u32 regval = rd32(IGC_EIAC);
|
|
+
|
|
+ wr32(IGC_EIAC, regval | adapter->eims_enable_mask);
|
|
+ regval = rd32(IGC_EIAM);
|
|
+ wr32(IGC_EIAM, regval | adapter->eims_enable_mask);
|
|
+ wr32(IGC_EIMS, adapter->eims_enable_mask);
|
|
+ wr32(IGC_IMS, ims);
|
|
+ } else {
|
|
+ wr32(IGC_IMS, IMS_ENABLE_MASK | IGC_IMS_DRSTA);
|
|
+ wr32(IGC_IAM, IMS_ENABLE_MASK | IGC_IMS_DRSTA);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_irq_disable - Mask off interrupt generation on the NIC
|
|
+ * @adapter: board private structure
|
|
+ */
|
|
+static void igc_irq_disable(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ if (adapter->msix_entries) {
|
|
+ u32 regval = rd32(IGC_EIAM);
|
|
+
|
|
+ wr32(IGC_EIAM, regval & ~adapter->eims_enable_mask);
|
|
+ wr32(IGC_EIMC, adapter->eims_enable_mask);
|
|
+ regval = rd32(IGC_EIAC);
|
|
+ wr32(IGC_EIAC, regval & ~adapter->eims_enable_mask);
|
|
+ }
|
|
+
|
|
+ wr32(IGC_IAM, 0);
|
|
+ wr32(IGC_IMC, ~0);
|
|
+ wrfl();
|
|
+
|
|
+ if (adapter->msix_entries) {
|
|
+ int vector = 0, i;
|
|
+
|
|
+ synchronize_irq(adapter->msix_entries[vector++].vector);
|
|
+
|
|
+ for (i = 0; i < adapter->num_q_vectors; i++)
|
|
+ synchronize_irq(adapter->msix_entries[vector++].vector);
|
|
+ } else {
|
|
+ synchronize_irq(adapter->pdev->irq);
|
|
+ }
|
|
+}
|
|
+
|
|
+void igc_set_flag_queue_pairs(struct igc_adapter *adapter,
|
|
+ const u32 max_rss_queues)
|
|
+{
|
|
+ /* Determine if we need to pair queues. */
|
|
+ /* If rss_queues > half of max_rss_queues, pair the queues in
|
|
+ * order to conserve interrupts due to limited supply.
|
|
+ */
|
|
+ if (adapter->rss_queues > (max_rss_queues / 2))
|
|
+ adapter->flags |= IGC_FLAG_QUEUE_PAIRS;
|
|
+ else
|
|
+ adapter->flags &= ~IGC_FLAG_QUEUE_PAIRS;
|
|
+}
|
|
+
|
|
+unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter)
|
|
+{
|
|
+ return IGC_MAX_RX_QUEUES;
|
|
+}
|
|
+
|
|
+static void igc_init_queue_configuration(struct igc_adapter *adapter)
|
|
+{
|
|
+ u32 max_rss_queues;
|
|
+
|
|
+ max_rss_queues = igc_get_max_rss_queues(adapter);
|
|
+ adapter->rss_queues = min_t(u32, max_rss_queues, num_online_cpus());
|
|
+
|
|
+ igc_set_flag_queue_pairs(adapter, max_rss_queues);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_reset_q_vector - Reset config for interrupt vector
|
|
+ * @adapter: board private structure to initialize
|
|
+ * @v_idx: Index of vector to be reset
|
|
+ *
|
|
+ * If NAPI is enabled it will delete any references to the
|
|
+ * NAPI struct. This is preparation for igc_free_q_vector.
|
|
+ */
|
|
+static void igc_reset_q_vector(struct igc_adapter *adapter, int v_idx)
|
|
+{
|
|
+ struct igc_q_vector *q_vector = adapter->q_vector[v_idx];
|
|
+
|
|
+ /* if we're coming from igc_set_interrupt_capability, the vectors are
|
|
+ * not yet allocated
|
|
+ */
|
|
+ if (!q_vector)
|
|
+ return;
|
|
+
|
|
+ if (q_vector->tx.ring)
|
|
+ adapter->tx_ring[q_vector->tx.ring->queue_index] = NULL;
|
|
+
|
|
+ if (q_vector->rx.ring)
|
|
+ adapter->rx_ring[q_vector->rx.ring->queue_index] = NULL;
|
|
+
|
|
+ netif_napi_del(&q_vector->napi);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_free_q_vector - Free memory allocated for specific interrupt vector
|
|
+ * @adapter: board private structure to initialize
|
|
+ * @v_idx: Index of vector to be freed
|
|
+ *
|
|
+ * This function frees the memory allocated to the q_vector.
|
|
+ */
|
|
+static void igc_free_q_vector(struct igc_adapter *adapter, int v_idx)
|
|
+{
|
|
+ struct igc_q_vector *q_vector = adapter->q_vector[v_idx];
|
|
+
|
|
+ adapter->q_vector[v_idx] = NULL;
|
|
+
|
|
+ /* igc_get_stats64() might access the rings on this vector,
|
|
+ * we must wait a grace period before freeing it.
|
|
+ */
|
|
+ if (q_vector)
|
|
+ kfree_rcu(q_vector, rcu);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_free_q_vectors - Free memory allocated for interrupt vectors
|
|
+ * @adapter: board private structure to initialize
|
|
+ *
|
|
+ * This function frees the memory allocated to the q_vectors. In addition if
|
|
+ * NAPI is enabled it will delete any references to the NAPI struct prior
|
|
+ * to freeing the q_vector.
|
|
+ */
|
|
+static void igc_free_q_vectors(struct igc_adapter *adapter)
|
|
+{
|
|
+ int v_idx = adapter->num_q_vectors;
|
|
+
|
|
+ adapter->num_tx_queues = 0;
|
|
+ adapter->num_rx_queues = 0;
|
|
+ adapter->num_q_vectors = 0;
|
|
+
|
|
+ while (v_idx--) {
|
|
+ igc_reset_q_vector(adapter, v_idx);
|
|
+ igc_free_q_vector(adapter, v_idx);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_update_itr - update the dynamic ITR value based on statistics
|
|
+ * @q_vector: pointer to q_vector
|
|
+ * @ring_container: ring info to update the itr for
|
|
+ *
|
|
+ * Stores a new ITR value based on packets and byte
|
|
+ * counts during the last interrupt. The advantage of per interrupt
|
|
+ * computation is faster updates and more accurate ITR for the current
|
|
+ * traffic pattern. Constants in this function were computed
|
|
+ * based on theoretical maximum wire speed and thresholds were set based
|
|
+ * on testing data as well as attempting to minimize response time
|
|
+ * while increasing bulk throughput.
|
|
+ * NOTE: These calculations are only valid when operating in a single-
|
|
+ * queue environment.
|
|
+ */
|
|
+static void igc_update_itr(struct igc_q_vector *q_vector,
|
|
+ struct igc_ring_container *ring_container)
|
|
+{
|
|
+ unsigned int packets = ring_container->total_packets;
|
|
+ unsigned int bytes = ring_container->total_bytes;
|
|
+ u8 itrval = ring_container->itr;
|
|
+
|
|
+ /* no packets, exit with status unchanged */
|
|
+ if (packets == 0)
|
|
+ return;
|
|
+
|
|
+ switch (itrval) {
|
|
+ case lowest_latency:
|
|
+ /* handle TSO and jumbo frames */
|
|
+ if (bytes / packets > 8000)
|
|
+ itrval = bulk_latency;
|
|
+ else if ((packets < 5) && (bytes > 512))
|
|
+ itrval = low_latency;
|
|
+ break;
|
|
+ case low_latency: /* 50 usec aka 20000 ints/s */
|
|
+ if (bytes > 10000) {
|
|
+ /* this if handles the TSO accounting */
|
|
+ if (bytes / packets > 8000)
|
|
+ itrval = bulk_latency;
|
|
+ else if ((packets < 10) || ((bytes / packets) > 1200))
|
|
+ itrval = bulk_latency;
|
|
+ else if ((packets > 35))
|
|
+ itrval = lowest_latency;
|
|
+ } else if (bytes / packets > 2000) {
|
|
+ itrval = bulk_latency;
|
|
+ } else if (packets <= 2 && bytes < 512) {
|
|
+ itrval = lowest_latency;
|
|
+ }
|
|
+ break;
|
|
+ case bulk_latency: /* 250 usec aka 4000 ints/s */
|
|
+ if (bytes > 25000) {
|
|
+ if (packets > 35)
|
|
+ itrval = low_latency;
|
|
+ } else if (bytes < 1500) {
|
|
+ itrval = low_latency;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* clear work counters since we have the values we need */
|
|
+ ring_container->total_bytes = 0;
|
|
+ ring_container->total_packets = 0;
|
|
+
|
|
+ /* write updated itr to ring container */
|
|
+ ring_container->itr = itrval;
|
|
+}
|
|
+
|
|
+static void igc_set_itr(struct igc_q_vector *q_vector)
|
|
+{
|
|
+ struct igc_adapter *adapter = q_vector->adapter;
|
|
+ u32 new_itr = q_vector->itr_val;
|
|
+ u8 current_itr = 0;
|
|
+
|
|
+ /* for non-gigabit speeds, just fix the interrupt rate at 4000 */
|
|
+ switch (adapter->link_speed) {
|
|
+ case SPEED_10:
|
|
+ case SPEED_100:
|
|
+ current_itr = 0;
|
|
+ new_itr = IGC_4K_ITR;
|
|
+ goto set_itr_now;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ igc_update_itr(q_vector, &q_vector->tx);
|
|
+ igc_update_itr(q_vector, &q_vector->rx);
|
|
+
|
|
+ current_itr = max(q_vector->rx.itr, q_vector->tx.itr);
|
|
+
|
|
+ /* conservative mode (itr 3) eliminates the lowest_latency setting */
|
|
+ if (current_itr == lowest_latency &&
|
|
+ ((q_vector->rx.ring && adapter->rx_itr_setting == 3) ||
|
|
+ (!q_vector->rx.ring && adapter->tx_itr_setting == 3)))
|
|
+ current_itr = low_latency;
|
|
+
|
|
+ switch (current_itr) {
|
|
+ /* counts and packets in update_itr are dependent on these numbers */
|
|
+ case lowest_latency:
|
|
+ new_itr = IGC_70K_ITR; /* 70,000 ints/sec */
|
|
+ break;
|
|
+ case low_latency:
|
|
+ new_itr = IGC_20K_ITR; /* 20,000 ints/sec */
|
|
+ break;
|
|
+ case bulk_latency:
|
|
+ new_itr = IGC_4K_ITR; /* 4,000 ints/sec */
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+set_itr_now:
|
|
+ if (new_itr != q_vector->itr_val) {
|
|
+ /* this attempts to bias the interrupt rate towards Bulk
|
|
+ * by adding intermediate steps when interrupt rate is
|
|
+ * increasing
|
|
+ */
|
|
+ new_itr = new_itr > q_vector->itr_val ?
|
|
+ max((new_itr * q_vector->itr_val) /
|
|
+ (new_itr + (q_vector->itr_val >> 2)),
|
|
+ new_itr) : new_itr;
|
|
+ /* Don't write the value here; it resets the adapter's
|
|
+ * internal timer, and causes us to delay far longer than
|
|
+ * we should between interrupts. Instead, we write the ITR
|
|
+ * value at the beginning of the next interrupt so the timing
|
|
+ * ends up being correct.
|
|
+ */
|
|
+ q_vector->itr_val = new_itr;
|
|
+ q_vector->set_itr = 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void igc_reset_interrupt_capability(struct igc_adapter *adapter)
|
|
+{
|
|
+ int v_idx = adapter->num_q_vectors;
|
|
+
|
|
+ if (adapter->msix_entries) {
|
|
+ pci_disable_msix(adapter->pdev);
|
|
+ kfree(adapter->msix_entries);
|
|
+ adapter->msix_entries = NULL;
|
|
+ } else if (adapter->flags & IGC_FLAG_HAS_MSI) {
|
|
+ pci_disable_msi(adapter->pdev);
|
|
+ }
|
|
+
|
|
+ while (v_idx--)
|
|
+ igc_reset_q_vector(adapter, v_idx);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_set_interrupt_capability - set MSI or MSI-X if supported
|
|
+ * @adapter: Pointer to adapter structure
|
|
+ * @msix: boolean value for MSI-X capability
|
|
+ *
|
|
+ * Attempt to configure interrupts using the best available
|
|
+ * capabilities of the hardware and kernel.
|
|
+ */
|
|
+static void igc_set_interrupt_capability(struct igc_adapter *adapter,
|
|
+ bool msix)
|
|
+{
|
|
+ int numvecs, i;
|
|
+ int err;
|
|
+
|
|
+ if (!msix)
|
|
+ goto msi_only;
|
|
+ adapter->flags |= IGC_FLAG_HAS_MSIX;
|
|
+
|
|
+ /* Number of supported queues. */
|
|
+ adapter->num_rx_queues = adapter->rss_queues;
|
|
+
|
|
+ adapter->num_tx_queues = adapter->rss_queues;
|
|
+
|
|
+ /* start with one vector for every Rx queue */
|
|
+ numvecs = adapter->num_rx_queues;
|
|
+
|
|
+ /* if Tx handler is separate add 1 for every Tx queue */
|
|
+ if (!(adapter->flags & IGC_FLAG_QUEUE_PAIRS))
|
|
+ numvecs += adapter->num_tx_queues;
|
|
+
|
|
+ /* store the number of vectors reserved for queues */
|
|
+ adapter->num_q_vectors = numvecs;
|
|
+
|
|
+ /* add 1 vector for link status interrupts */
|
|
+ numvecs++;
|
|
+
|
|
+ adapter->msix_entries = kcalloc(numvecs, sizeof(struct msix_entry),
|
|
+ GFP_KERNEL);
|
|
+
|
|
+ if (!adapter->msix_entries)
|
|
+ return;
|
|
+
|
|
+ /* populate entry values */
|
|
+ for (i = 0; i < numvecs; i++)
|
|
+ adapter->msix_entries[i].entry = i;
|
|
+
|
|
+ err = pci_enable_msix_range(adapter->pdev,
|
|
+ adapter->msix_entries,
|
|
+ numvecs,
|
|
+ numvecs);
|
|
+ if (err > 0)
|
|
+ return;
|
|
+
|
|
+ kfree(adapter->msix_entries);
|
|
+ adapter->msix_entries = NULL;
|
|
+
|
|
+ igc_reset_interrupt_capability(adapter);
|
|
+
|
|
+msi_only:
|
|
+ adapter->flags &= ~IGC_FLAG_HAS_MSIX;
|
|
+
|
|
+ adapter->rss_queues = 1;
|
|
+ adapter->flags |= IGC_FLAG_QUEUE_PAIRS;
|
|
+ adapter->num_rx_queues = 1;
|
|
+ adapter->num_tx_queues = 1;
|
|
+ adapter->num_q_vectors = 1;
|
|
+ if (!pci_enable_msi(adapter->pdev))
|
|
+ adapter->flags |= IGC_FLAG_HAS_MSI;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_update_ring_itr - update the dynamic ITR value based on packet size
|
|
+ * @q_vector: pointer to q_vector
|
|
+ *
|
|
+ * Stores a new ITR value based on strictly on packet size. This
|
|
+ * algorithm is less sophisticated than that used in igc_update_itr,
|
|
+ * due to the difficulty of synchronizing statistics across multiple
|
|
+ * receive rings. The divisors and thresholds used by this function
|
|
+ * were determined based on theoretical maximum wire speed and testing
|
|
+ * data, in order to minimize response time while increasing bulk
|
|
+ * throughput.
|
|
+ * NOTE: This function is called only when operating in a multiqueue
|
|
+ * receive environment.
|
|
+ */
|
|
+static void igc_update_ring_itr(struct igc_q_vector *q_vector)
|
|
+{
|
|
+ struct igc_adapter *adapter = q_vector->adapter;
|
|
+ int new_val = q_vector->itr_val;
|
|
+ int avg_wire_size = 0;
|
|
+ unsigned int packets;
|
|
+
|
|
+ /* For non-gigabit speeds, just fix the interrupt rate at 4000
|
|
+ * ints/sec - ITR timer value of 120 ticks.
|
|
+ */
|
|
+ switch (adapter->link_speed) {
|
|
+ case SPEED_10:
|
|
+ case SPEED_100:
|
|
+ new_val = IGC_4K_ITR;
|
|
+ goto set_itr_val;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ packets = q_vector->rx.total_packets;
|
|
+ if (packets)
|
|
+ avg_wire_size = q_vector->rx.total_bytes / packets;
|
|
+
|
|
+ packets = q_vector->tx.total_packets;
|
|
+ if (packets)
|
|
+ avg_wire_size = max_t(u32, avg_wire_size,
|
|
+ q_vector->tx.total_bytes / packets);
|
|
+
|
|
+ /* if avg_wire_size isn't set no work was done */
|
|
+ if (!avg_wire_size)
|
|
+ goto clear_counts;
|
|
+
|
|
+ /* Add 24 bytes to size to account for CRC, preamble, and gap */
|
|
+ avg_wire_size += 24;
|
|
+
|
|
+ /* Don't starve jumbo frames */
|
|
+ avg_wire_size = min(avg_wire_size, 3000);
|
|
+
|
|
+ /* Give a little boost to mid-size frames */
|
|
+ if (avg_wire_size > 300 && avg_wire_size < 1200)
|
|
+ new_val = avg_wire_size / 3;
|
|
+ else
|
|
+ new_val = avg_wire_size / 2;
|
|
+
|
|
+ /* conservative mode (itr 3) eliminates the lowest_latency setting */
|
|
+ if (new_val < IGC_20K_ITR &&
|
|
+ ((q_vector->rx.ring && adapter->rx_itr_setting == 3) ||
|
|
+ (!q_vector->rx.ring && adapter->tx_itr_setting == 3)))
|
|
+ new_val = IGC_20K_ITR;
|
|
+
|
|
+set_itr_val:
|
|
+ if (new_val != q_vector->itr_val) {
|
|
+ q_vector->itr_val = new_val;
|
|
+ q_vector->set_itr = 1;
|
|
+ }
|
|
+clear_counts:
|
|
+ q_vector->rx.total_bytes = 0;
|
|
+ q_vector->rx.total_packets = 0;
|
|
+ q_vector->tx.total_bytes = 0;
|
|
+ q_vector->tx.total_packets = 0;
|
|
+}
|
|
+
|
|
+static void igc_ring_irq_enable(struct igc_q_vector *q_vector)
|
|
+{
|
|
+ struct igc_adapter *adapter = q_vector->adapter;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ if ((q_vector->rx.ring && (adapter->rx_itr_setting & 3)) ||
|
|
+ (!q_vector->rx.ring && (adapter->tx_itr_setting & 3))) {
|
|
+ if (adapter->num_q_vectors == 1)
|
|
+ igc_set_itr(q_vector);
|
|
+ else
|
|
+ igc_update_ring_itr(q_vector);
|
|
+ }
|
|
+
|
|
+ if (!test_bit(__IGC_DOWN, &adapter->state)) {
|
|
+ if (adapter->msix_entries)
|
|
+ wr32(IGC_EIMS, q_vector->eims_value);
|
|
+ else
|
|
+ igc_irq_enable(adapter);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void igc_add_ring(struct igc_ring *ring,
|
|
+ struct igc_ring_container *head)
|
|
+{
|
|
+ head->ring = ring;
|
|
+ head->count++;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_cache_ring_register - Descriptor ring to register mapping
|
|
+ * @adapter: board private structure to initialize
|
|
+ *
|
|
+ * Once we know the feature-set enabled for the device, we'll cache
|
|
+ * the register offset the descriptor ring is assigned to.
|
|
+ */
|
|
+static void igc_cache_ring_register(struct igc_adapter *adapter)
|
|
+{
|
|
+ int i = 0, j = 0;
|
|
+
|
|
+ switch (adapter->hw.mac.type) {
|
|
+ case igc_i225:
|
|
+ default:
|
|
+ for (; i < adapter->num_rx_queues; i++)
|
|
+ adapter->rx_ring[i]->reg_idx = i;
|
|
+ for (; j < adapter->num_tx_queues; j++)
|
|
+ adapter->tx_ring[j]->reg_idx = j;
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_poll - NAPI Rx polling callback
|
|
+ * @napi: napi polling structure
|
|
+ * @budget: count of how many packets we should handle
|
|
+ */
|
|
+static int igc_poll(struct napi_struct *napi, int budget)
|
|
+{
|
|
+ struct igc_q_vector *q_vector = container_of(napi,
|
|
+ struct igc_q_vector,
|
|
+ napi);
|
|
+ bool clean_complete = true;
|
|
+ int work_done = 0;
|
|
+
|
|
+ if (q_vector->tx.ring)
|
|
+ clean_complete = igc_clean_tx_irq(q_vector, budget);
|
|
+
|
|
+ if (q_vector->rx.ring) {
|
|
+ int cleaned = igc_clean_rx_irq(q_vector, budget);
|
|
+
|
|
+ work_done += cleaned;
|
|
+ if (cleaned >= budget)
|
|
+ clean_complete = false;
|
|
+ }
|
|
+
|
|
+ /* If all work not completed, return budget and keep polling */
|
|
+ if (!clean_complete)
|
|
+ return budget;
|
|
+
|
|
+ /* Exit the polling mode, but don't re-enable interrupts if stack might
|
|
+ * poll us due to busy-polling
|
|
+ */
|
|
+ if (likely(napi_complete_done(napi, work_done)))
|
|
+ igc_ring_irq_enable(q_vector);
|
|
+
|
|
+ return min(work_done, budget - 1);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_alloc_q_vector - Allocate memory for a single interrupt vector
|
|
+ * @adapter: board private structure to initialize
|
|
+ * @v_count: q_vectors allocated on adapter, used for ring interleaving
|
|
+ * @v_idx: index of vector in adapter struct
|
|
+ * @txr_count: total number of Tx rings to allocate
|
|
+ * @txr_idx: index of first Tx ring to allocate
|
|
+ * @rxr_count: total number of Rx rings to allocate
|
|
+ * @rxr_idx: index of first Rx ring to allocate
|
|
+ *
|
|
+ * We allocate one q_vector. If allocation fails we return -ENOMEM.
|
|
+ */
|
|
+static int igc_alloc_q_vector(struct igc_adapter *adapter,
|
|
+ unsigned int v_count, unsigned int v_idx,
|
|
+ unsigned int txr_count, unsigned int txr_idx,
|
|
+ unsigned int rxr_count, unsigned int rxr_idx)
|
|
+{
|
|
+ struct igc_q_vector *q_vector;
|
|
+ struct igc_ring *ring;
|
|
+ int ring_count;
|
|
+
|
|
+ /* igc only supports 1 Tx and/or 1 Rx queue per vector */
|
|
+ if (txr_count > 1 || rxr_count > 1)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ring_count = txr_count + rxr_count;
|
|
+
|
|
+ /* allocate q_vector and rings */
|
|
+ q_vector = adapter->q_vector[v_idx];
|
|
+ if (!q_vector)
|
|
+ q_vector = kzalloc(struct_size(q_vector, ring, ring_count),
|
|
+ GFP_KERNEL);
|
|
+ else
|
|
+ memset(q_vector, 0, struct_size(q_vector, ring, ring_count));
|
|
+ if (!q_vector)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* initialize NAPI */
|
|
+ netif_napi_add(adapter->netdev, &q_vector->napi,
|
|
+ igc_poll, 64);
|
|
+
|
|
+ /* tie q_vector and adapter together */
|
|
+ adapter->q_vector[v_idx] = q_vector;
|
|
+ q_vector->adapter = adapter;
|
|
+
|
|
+ /* initialize work limits */
|
|
+ q_vector->tx.work_limit = adapter->tx_work_limit;
|
|
+
|
|
+ /* initialize ITR configuration */
|
|
+ q_vector->itr_register = adapter->io_addr + IGC_EITR(0);
|
|
+ q_vector->itr_val = IGC_START_ITR;
|
|
+
|
|
+ /* initialize pointer to rings */
|
|
+ ring = q_vector->ring;
|
|
+
|
|
+ /* initialize ITR */
|
|
+ if (rxr_count) {
|
|
+ /* rx or rx/tx vector */
|
|
+ if (!adapter->rx_itr_setting || adapter->rx_itr_setting > 3)
|
|
+ q_vector->itr_val = adapter->rx_itr_setting;
|
|
+ } else {
|
|
+ /* tx only vector */
|
|
+ if (!adapter->tx_itr_setting || adapter->tx_itr_setting > 3)
|
|
+ q_vector->itr_val = adapter->tx_itr_setting;
|
|
+ }
|
|
+
|
|
+ if (txr_count) {
|
|
+ /* assign generic ring traits */
|
|
+ ring->dev = &adapter->pdev->dev;
|
|
+ ring->netdev = adapter->netdev;
|
|
+
|
|
+ /* configure backlink on ring */
|
|
+ ring->q_vector = q_vector;
|
|
+
|
|
+ /* update q_vector Tx values */
|
|
+ igc_add_ring(ring, &q_vector->tx);
|
|
+
|
|
+ /* apply Tx specific ring traits */
|
|
+ ring->count = adapter->tx_ring_count;
|
|
+ ring->queue_index = txr_idx;
|
|
+
|
|
+ /* assign ring to adapter */
|
|
+ adapter->tx_ring[txr_idx] = ring;
|
|
+
|
|
+ /* push pointer to next ring */
|
|
+ ring++;
|
|
+ }
|
|
+
|
|
+ if (rxr_count) {
|
|
+ /* assign generic ring traits */
|
|
+ ring->dev = &adapter->pdev->dev;
|
|
+ ring->netdev = adapter->netdev;
|
|
+
|
|
+ /* configure backlink on ring */
|
|
+ ring->q_vector = q_vector;
|
|
+
|
|
+ /* update q_vector Rx values */
|
|
+ igc_add_ring(ring, &q_vector->rx);
|
|
+
|
|
+ /* apply Rx specific ring traits */
|
|
+ ring->count = adapter->rx_ring_count;
|
|
+ ring->queue_index = rxr_idx;
|
|
+
|
|
+ /* assign ring to adapter */
|
|
+ adapter->rx_ring[rxr_idx] = ring;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_alloc_q_vectors - Allocate memory for interrupt vectors
|
|
+ * @adapter: board private structure to initialize
|
|
+ *
|
|
+ * We allocate one q_vector per queue interrupt. If allocation fails we
|
|
+ * return -ENOMEM.
|
|
+ */
|
|
+static int igc_alloc_q_vectors(struct igc_adapter *adapter)
|
|
+{
|
|
+ int rxr_remaining = adapter->num_rx_queues;
|
|
+ int txr_remaining = adapter->num_tx_queues;
|
|
+ int rxr_idx = 0, txr_idx = 0, v_idx = 0;
|
|
+ int q_vectors = adapter->num_q_vectors;
|
|
+ int err;
|
|
+
|
|
+ if (q_vectors >= (rxr_remaining + txr_remaining)) {
|
|
+ for (; rxr_remaining; v_idx++) {
|
|
+ err = igc_alloc_q_vector(adapter, q_vectors, v_idx,
|
|
+ 0, 0, 1, rxr_idx);
|
|
+
|
|
+ if (err)
|
|
+ goto err_out;
|
|
+
|
|
+ /* update counts and index */
|
|
+ rxr_remaining--;
|
|
+ rxr_idx++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (; v_idx < q_vectors; v_idx++) {
|
|
+ int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_idx);
|
|
+ int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_idx);
|
|
+
|
|
+ err = igc_alloc_q_vector(adapter, q_vectors, v_idx,
|
|
+ tqpv, txr_idx, rqpv, rxr_idx);
|
|
+
|
|
+ if (err)
|
|
+ goto err_out;
|
|
+
|
|
+ /* update counts and index */
|
|
+ rxr_remaining -= rqpv;
|
|
+ txr_remaining -= tqpv;
|
|
+ rxr_idx++;
|
|
+ txr_idx++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_out:
|
|
+ adapter->num_tx_queues = 0;
|
|
+ adapter->num_rx_queues = 0;
|
|
+ adapter->num_q_vectors = 0;
|
|
+
|
|
+ while (v_idx--)
|
|
+ igc_free_q_vector(adapter, v_idx);
|
|
+
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_init_interrupt_scheme - initialize interrupts, allocate queues/vectors
|
|
+ * @adapter: Pointer to adapter structure
|
|
+ * @msix: boolean for MSI-X capability
|
|
+ *
|
|
+ * This function initializes the interrupts and allocates all of the queues.
|
|
+ */
|
|
+static int igc_init_interrupt_scheme(struct igc_adapter *adapter, bool msix)
|
|
+{
|
|
+ struct net_device *dev = adapter->netdev;
|
|
+ int err = 0;
|
|
+
|
|
+ igc_set_interrupt_capability(adapter, msix);
|
|
+
|
|
+ err = igc_alloc_q_vectors(adapter);
|
|
+ if (err) {
|
|
+ netdev_err(dev, "Unable to allocate memory for vectors\n");
|
|
+ goto err_alloc_q_vectors;
|
|
+ }
|
|
+
|
|
+ igc_cache_ring_register(adapter);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_alloc_q_vectors:
|
|
+ igc_reset_interrupt_capability(adapter);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_sw_init - Initialize general software structures (struct igc_adapter)
|
|
+ * @adapter: board private structure to initialize
|
|
+ *
|
|
+ * igc_sw_init initializes the Adapter private data structure.
|
|
+ * Fields are initialized based on PCI device information and
|
|
+ * OS network device settings (MTU size).
|
|
+ */
|
|
+static int igc_sw_init(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct net_device *netdev = adapter->netdev;
|
|
+ struct pci_dev *pdev = adapter->pdev;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word);
|
|
+
|
|
+ /* set default ring sizes */
|
|
+ adapter->tx_ring_count = IGC_DEFAULT_TXD;
|
|
+ adapter->rx_ring_count = IGC_DEFAULT_RXD;
|
|
+
|
|
+ /* set default ITR values */
|
|
+ adapter->rx_itr_setting = IGC_DEFAULT_ITR;
|
|
+ adapter->tx_itr_setting = IGC_DEFAULT_ITR;
|
|
+
|
|
+ /* set default work limits */
|
|
+ adapter->tx_work_limit = IGC_DEFAULT_TX_WORK;
|
|
+
|
|
+ /* adjust max frame to be at least the size of a standard frame */
|
|
+ adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN +
|
|
+ VLAN_HLEN;
|
|
+ adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
|
|
+
|
|
+ mutex_init(&adapter->nfc_rule_lock);
|
|
+ INIT_LIST_HEAD(&adapter->nfc_rule_list);
|
|
+ adapter->nfc_rule_count = 0;
|
|
+
|
|
+ spin_lock_init(&adapter->stats64_lock);
|
|
+ /* Assume MSI-X interrupts, will be checked during IRQ allocation */
|
|
+ adapter->flags |= IGC_FLAG_HAS_MSIX;
|
|
+
|
|
+ igc_init_queue_configuration(adapter);
|
|
+
|
|
+ /* This call may decrease the number of queues */
|
|
+ if (igc_init_interrupt_scheme(adapter, true)) {
|
|
+ netdev_err(netdev, "Unable to allocate memory for queues\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* Explicitly disable IRQ since the NIC can be in any state. */
|
|
+ igc_irq_disable(adapter);
|
|
+
|
|
+ set_bit(__IGC_DOWN, &adapter->state);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/**
|
|
* igc_up - Open the interface and prepare it to handle traffic
|
|
* @adapter: board private structure
|
|
@@ -1980,8 +3696,8 @@
|
|
adapter->stats.prc511 += rd32(IGC_PRC511);
|
|
adapter->stats.prc1023 += rd32(IGC_PRC1023);
|
|
adapter->stats.prc1522 += rd32(IGC_PRC1522);
|
|
- adapter->stats.symerrs += rd32(IGC_SYMERRS);
|
|
- adapter->stats.sec += rd32(IGC_SEC);
|
|
+ adapter->stats.tlpic += rd32(IGC_TLPIC);
|
|
+ adapter->stats.rlpic += rd32(IGC_RLPIC);
|
|
|
|
mpc = rd32(IGC_MPC);
|
|
adapter->stats.mpc += mpc;
|
|
@@ -2020,21 +3736,13 @@
|
|
|
|
adapter->stats.tpt += rd32(IGC_TPT);
|
|
adapter->stats.colc += rd32(IGC_COLC);
|
|
+ adapter->stats.colc += rd32(IGC_RERC);
|
|
|
|
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);
|
|
- adapter->stats.icrxptc += rd32(IGC_ICRXPTC);
|
|
- adapter->stats.icrxatc += rd32(IGC_ICRXATC);
|
|
- adapter->stats.ictxptc += rd32(IGC_ICTXPTC);
|
|
- adapter->stats.ictxatc += rd32(IGC_ICTXATC);
|
|
- adapter->stats.ictxqec += rd32(IGC_ICTXQEC);
|
|
- adapter->stats.ictxqmtc += rd32(IGC_ICTXQMTC);
|
|
- adapter->stats.icrxdmtc += rd32(IGC_ICRXDMTC);
|
|
|
|
/* Fill out the OS statistics structure */
|
|
net_stats->multicast = adapter->stats.mprc;
|
|
@@ -2070,33 +3778,6 @@
|
|
adapter->stats.mgpdc += rd32(IGC_MGTPDC);
|
|
}
|
|
|
|
-static void igc_nfc_filter_exit(struct igc_adapter *adapter)
|
|
-{
|
|
- struct igc_nfc_filter *rule;
|
|
-
|
|
- spin_lock(&adapter->nfc_lock);
|
|
-
|
|
- hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
|
|
- igc_erase_filter(adapter, rule);
|
|
-
|
|
- hlist_for_each_entry(rule, &adapter->cls_flower_list, nfc_node)
|
|
- igc_erase_filter(adapter, rule);
|
|
-
|
|
- spin_unlock(&adapter->nfc_lock);
|
|
-}
|
|
-
|
|
-static void igc_nfc_filter_restore(struct igc_adapter *adapter)
|
|
-{
|
|
- struct igc_nfc_filter *rule;
|
|
-
|
|
- spin_lock(&adapter->nfc_lock);
|
|
-
|
|
- hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
|
|
- igc_add_filter(adapter, rule);
|
|
-
|
|
- spin_unlock(&adapter->nfc_lock);
|
|
-}
|
|
-
|
|
/**
|
|
* igc_down - Close the interface
|
|
* @adapter: board private structure
|
|
@@ -2110,28 +3791,31 @@
|
|
|
|
set_bit(__IGC_DOWN, &adapter->state);
|
|
|
|
- /* disable receives in the hardware */
|
|
- rctl = rd32(IGC_RCTL);
|
|
- wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN);
|
|
- /* flush and sleep below */
|
|
-
|
|
- igc_nfc_filter_exit(adapter);
|
|
+ igc_ptp_suspend(adapter);
|
|
|
|
+ if (pci_device_is_present(adapter->pdev)) {
|
|
+ /* disable receives in the hardware */
|
|
+ rctl = rd32(IGC_RCTL);
|
|
+ wr32(IGC_RCTL, rctl & ~IGC_RCTL_EN);
|
|
+ /* flush and sleep below */
|
|
+ }
|
|
/* set trans_start so we don't get spurious watchdogs during reset */
|
|
netif_trans_update(netdev);
|
|
|
|
netif_carrier_off(netdev);
|
|
netif_tx_stop_all_queues(netdev);
|
|
|
|
- /* disable transmits in the hardware */
|
|
- tctl = rd32(IGC_TCTL);
|
|
- tctl &= ~IGC_TCTL_EN;
|
|
- wr32(IGC_TCTL, tctl);
|
|
- /* flush both disables and wait for them to finish */
|
|
- wrfl();
|
|
- usleep_range(10000, 20000);
|
|
+ if (pci_device_is_present(adapter->pdev)) {
|
|
+ /* disable transmits in the hardware */
|
|
+ tctl = rd32(IGC_TCTL);
|
|
+ tctl &= ~IGC_TCTL_EN;
|
|
+ wr32(IGC_TCTL, tctl);
|
|
+ /* flush both disables and wait for them to finish */
|
|
+ wrfl();
|
|
+ usleep_range(10000, 20000);
|
|
|
|
- igc_irq_disable(adapter);
|
|
+ igc_irq_disable(adapter);
|
|
+ }
|
|
|
|
adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
|
|
|
|
@@ -2165,7 +3849,6 @@
|
|
|
|
void igc_reinit_locked(struct igc_adapter *adapter)
|
|
{
|
|
- WARN_ON(in_interrupt());
|
|
while (test_and_set_bit(__IGC_RESETTING, &adapter->state))
|
|
usleep_range(1000, 2000);
|
|
igc_down(adapter);
|
|
@@ -2179,8 +3862,19 @@
|
|
|
|
adapter = container_of(work, struct igc_adapter, reset_task);
|
|
|
|
+ rtnl_lock();
|
|
+ /* If we're already down or resetting, just bail */
|
|
+ if (test_bit(__IGC_DOWN, &adapter->state) ||
|
|
+ test_bit(__IGC_RESETTING, &adapter->state)) {
|
|
+ rtnl_unlock();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ igc_rings_dump(adapter);
|
|
+ igc_regs_dump(adapter);
|
|
netdev_err(adapter->netdev, "Reset adapter\n");
|
|
igc_reinit_locked(adapter);
|
|
+ rtnl_unlock();
|
|
}
|
|
|
|
/**
|
|
@@ -2194,7 +3888,6 @@
|
|
{
|
|
int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
|
|
/* adjust max frame to be at least the size of a standard frame */
|
|
if (max_frame < (ETH_FRAME_LEN + ETH_FCS_LEN))
|
|
@@ -2209,8 +3902,7 @@
|
|
if (netif_running(netdev))
|
|
igc_down(adapter);
|
|
|
|
- dev_info(&pdev->dev, "changing MTU from %d to %d\n",
|
|
- netdev->mtu, new_mtu);
|
|
+ netdev_dbg(netdev, "changing MTU from %d to %d\n", netdev->mtu, new_mtu);
|
|
netdev->mtu = new_mtu;
|
|
|
|
if (netif_running(netdev))
|
|
@@ -2267,20 +3959,8 @@
|
|
if (!(changed & (NETIF_F_RXALL | NETIF_F_NTUPLE)))
|
|
return 0;
|
|
|
|
- if (!(features & NETIF_F_NTUPLE)) {
|
|
- struct hlist_node *node2;
|
|
- struct igc_nfc_filter *rule;
|
|
-
|
|
- spin_lock(&adapter->nfc_lock);
|
|
- hlist_for_each_entry_safe(rule, node2,
|
|
- &adapter->nfc_filter_list, nfc_node) {
|
|
- igc_erase_filter(adapter, rule);
|
|
- hlist_del(&rule->nfc_node);
|
|
- kfree(rule);
|
|
- }
|
|
- spin_unlock(&adapter->nfc_lock);
|
|
- adapter->nfc_filter_count = 0;
|
|
- }
|
|
+ if (!(features & NETIF_F_NTUPLE))
|
|
+ igc_flush_nfc_rules(adapter);
|
|
|
|
netdev->features = features;
|
|
|
|
@@ -2323,216 +4003,20 @@
|
|
return features;
|
|
}
|
|
|
|
-/**
|
|
- * igc_configure - configure the hardware for RX and TX
|
|
- * @adapter: private board structure
|
|
- */
|
|
-static void igc_configure(struct igc_adapter *adapter)
|
|
+static void igc_tsync_interrupt(struct igc_adapter *adapter)
|
|
{
|
|
- struct net_device *netdev = adapter->netdev;
|
|
- int i = 0;
|
|
-
|
|
- igc_get_hw_control(adapter);
|
|
- igc_set_rx_mode(netdev);
|
|
-
|
|
- igc_setup_tctl(adapter);
|
|
- igc_setup_mrqc(adapter);
|
|
- igc_setup_rctl(adapter);
|
|
-
|
|
- igc_nfc_filter_restore(adapter);
|
|
- igc_configure_tx(adapter);
|
|
- igc_configure_rx(adapter);
|
|
-
|
|
- igc_rx_fifo_flush_base(&adapter->hw);
|
|
-
|
|
- /* call igc_desc_unused which always leaves
|
|
- * at least 1 descriptor unused to make sure
|
|
- * next_to_use != next_to_clean
|
|
- */
|
|
- for (i = 0; i < adapter->num_rx_queues; i++) {
|
|
- struct igc_ring *ring = adapter->rx_ring[i];
|
|
-
|
|
- igc_alloc_rx_buffers(ring, igc_desc_unused(ring));
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_rar_set_index - Sync RAL[index] and RAH[index] registers with MAC table
|
|
- * @adapter: address of board private structure
|
|
- * @index: Index of the RAR entry which need to be synced with MAC table
|
|
- */
|
|
-static void igc_rar_set_index(struct igc_adapter *adapter, u32 index)
|
|
-{
|
|
- u8 *addr = adapter->mac_table[index].addr;
|
|
struct igc_hw *hw = &adapter->hw;
|
|
- u32 rar_low, rar_high;
|
|
+ u32 tsicr = rd32(IGC_TSICR);
|
|
+ u32 ack = 0;
|
|
|
|
- /* HW expects these to be in network order when they are plugged
|
|
- * into the registers which are little endian. In order to guarantee
|
|
- * that ordering we need to do an leXX_to_cpup here in order to be
|
|
- * ready for the byteswap that occurs with writel
|
|
- */
|
|
- rar_low = le32_to_cpup((__le32 *)(addr));
|
|
- rar_high = le16_to_cpup((__le16 *)(addr + 4));
|
|
-
|
|
- /* Indicate to hardware the Address is Valid. */
|
|
- if (adapter->mac_table[index].state & IGC_MAC_STATE_IN_USE) {
|
|
- if (is_valid_ether_addr(addr))
|
|
- rar_high |= IGC_RAH_AV;
|
|
-
|
|
- rar_high |= IGC_RAH_POOL_1 <<
|
|
- adapter->mac_table[index].queue;
|
|
+ if (tsicr & IGC_TSICR_TXTS) {
|
|
+ /* retrieve hardware timestamp */
|
|
+ schedule_work(&adapter->ptp_tx_work);
|
|
+ ack |= IGC_TSICR_TXTS;
|
|
}
|
|
|
|
- wr32(IGC_RAL(index), rar_low);
|
|
- wrfl();
|
|
- wr32(IGC_RAH(index), rar_high);
|
|
- wrfl();
|
|
-}
|
|
-
|
|
-/* Set default MAC address for the PF in the first RAR entry */
|
|
-static void igc_set_default_mac_filter(struct igc_adapter *adapter)
|
|
-{
|
|
- struct igc_mac_addr *mac_table = &adapter->mac_table[0];
|
|
-
|
|
- ether_addr_copy(mac_table->addr, adapter->hw.mac.addr);
|
|
- mac_table->state = IGC_MAC_STATE_DEFAULT | IGC_MAC_STATE_IN_USE;
|
|
-
|
|
- igc_rar_set_index(adapter, 0);
|
|
-}
|
|
-
|
|
-/* If the filter to be added and an already existing filter express
|
|
- * the same address and address type, it should be possible to only
|
|
- * override the other configurations, for example the queue to steer
|
|
- * traffic.
|
|
- */
|
|
-static bool igc_mac_entry_can_be_used(const struct igc_mac_addr *entry,
|
|
- const u8 *addr, const u8 flags)
|
|
-{
|
|
- if (!(entry->state & IGC_MAC_STATE_IN_USE))
|
|
- return true;
|
|
-
|
|
- if ((entry->state & IGC_MAC_STATE_SRC_ADDR) !=
|
|
- (flags & IGC_MAC_STATE_SRC_ADDR))
|
|
- return false;
|
|
-
|
|
- if (!ether_addr_equal(addr, entry->addr))
|
|
- return false;
|
|
-
|
|
- return true;
|
|
-}
|
|
-
|
|
-/* Add a MAC filter for 'addr' directing matching traffic to 'queue',
|
|
- * 'flags' is used to indicate what kind of match is made, match is by
|
|
- * default for the destination address, if matching by source address
|
|
- * is desired the flag IGC_MAC_STATE_SRC_ADDR can be used.
|
|
- */
|
|
-static int igc_add_mac_filter_flags(struct igc_adapter *adapter,
|
|
- const u8 *addr, const u8 queue,
|
|
- const u8 flags)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- int rar_entries = hw->mac.rar_entry_count;
|
|
- int i;
|
|
-
|
|
- if (is_zero_ether_addr(addr))
|
|
- return -EINVAL;
|
|
-
|
|
- /* Search for the first empty entry in the MAC table.
|
|
- * Do not touch entries at the end of the table reserved for the VF MAC
|
|
- * addresses.
|
|
- */
|
|
- for (i = 0; i < rar_entries; i++) {
|
|
- if (!igc_mac_entry_can_be_used(&adapter->mac_table[i],
|
|
- addr, flags))
|
|
- continue;
|
|
-
|
|
- ether_addr_copy(adapter->mac_table[i].addr, addr);
|
|
- adapter->mac_table[i].queue = queue;
|
|
- adapter->mac_table[i].state |= IGC_MAC_STATE_IN_USE | flags;
|
|
-
|
|
- igc_rar_set_index(adapter, i);
|
|
- return i;
|
|
- }
|
|
-
|
|
- return -ENOSPC;
|
|
-}
|
|
-
|
|
-int igc_add_mac_steering_filter(struct igc_adapter *adapter,
|
|
- const u8 *addr, u8 queue, u8 flags)
|
|
-{
|
|
- return igc_add_mac_filter_flags(adapter, addr, queue,
|
|
- IGC_MAC_STATE_QUEUE_STEERING | flags);
|
|
-}
|
|
-
|
|
-/* Remove a MAC filter for 'addr' directing matching traffic to
|
|
- * 'queue', 'flags' is used to indicate what kind of match need to be
|
|
- * removed, match is by default for the destination address, if
|
|
- * matching by source address is to be removed the flag
|
|
- * IGC_MAC_STATE_SRC_ADDR can be used.
|
|
- */
|
|
-static int igc_del_mac_filter_flags(struct igc_adapter *adapter,
|
|
- const u8 *addr, const u8 queue,
|
|
- const u8 flags)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- int rar_entries = hw->mac.rar_entry_count;
|
|
- int i;
|
|
-
|
|
- if (is_zero_ether_addr(addr))
|
|
- return -EINVAL;
|
|
-
|
|
- /* Search for matching entry in the MAC table based on given address
|
|
- * and queue. Do not touch entries at the end of the table reserved
|
|
- * for the VF MAC addresses.
|
|
- */
|
|
- for (i = 0; i < rar_entries; i++) {
|
|
- if (!(adapter->mac_table[i].state & IGC_MAC_STATE_IN_USE))
|
|
- continue;
|
|
- if ((adapter->mac_table[i].state & flags) != flags)
|
|
- continue;
|
|
- if (adapter->mac_table[i].queue != queue)
|
|
- continue;
|
|
- if (!ether_addr_equal(adapter->mac_table[i].addr, addr))
|
|
- continue;
|
|
-
|
|
- /* When a filter for the default address is "deleted",
|
|
- * we return it to its initial configuration
|
|
- */
|
|
- if (adapter->mac_table[i].state & IGC_MAC_STATE_DEFAULT) {
|
|
- adapter->mac_table[i].state =
|
|
- IGC_MAC_STATE_DEFAULT | IGC_MAC_STATE_IN_USE;
|
|
- } else {
|
|
- adapter->mac_table[i].state = 0;
|
|
- adapter->mac_table[i].queue = 0;
|
|
- memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
|
|
- }
|
|
-
|
|
- igc_rar_set_index(adapter, i);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return -ENOENT;
|
|
-}
|
|
-
|
|
-int igc_del_mac_steering_filter(struct igc_adapter *adapter,
|
|
- const u8 *addr, u8 queue, u8 flags)
|
|
-{
|
|
- return igc_del_mac_filter_flags(adapter, addr, queue,
|
|
- IGC_MAC_STATE_QUEUE_STEERING | flags);
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
|
|
- * @netdev: network interface device structure
|
|
- *
|
|
- * The set_rx_mode entry point is called whenever the unicast or multicast
|
|
- * address lists or the network interface flags are updated. This routine is
|
|
- * responsible for configuring the hardware for proper unicast, multicast,
|
|
- * promiscuous mode, and all-multi behavior.
|
|
- */
|
|
-static void igc_set_rx_mode(struct net_device *netdev)
|
|
-{
|
|
+ /* acknowledge the interrupts */
|
|
+ wr32(IGC_TSICR, ack);
|
|
}
|
|
|
|
/**
|
|
@@ -2562,114 +4046,28 @@
|
|
mod_timer(&adapter->watchdog_timer, jiffies + 1);
|
|
}
|
|
|
|
+ if (icr & IGC_ICR_TS)
|
|
+ igc_tsync_interrupt(adapter);
|
|
+
|
|
wr32(IGC_EIMS, adapter->eims_other);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
-/**
|
|
- * igc_write_ivar - configure ivar for given MSI-X vector
|
|
- * @hw: pointer to the HW structure
|
|
- * @msix_vector: vector number we are allocating to a given ring
|
|
- * @index: row index of IVAR register to write within IVAR table
|
|
- * @offset: column offset of in IVAR, should be multiple of 8
|
|
- *
|
|
- * The IVAR table consists of 2 columns,
|
|
- * each containing an cause allocation for an Rx and Tx ring, and a
|
|
- * variable number of rows depending on the number of queues supported.
|
|
- */
|
|
-static void igc_write_ivar(struct igc_hw *hw, int msix_vector,
|
|
- int index, int offset)
|
|
-{
|
|
- u32 ivar = array_rd32(IGC_IVAR0, index);
|
|
-
|
|
- /* clear any bits that are currently set */
|
|
- ivar &= ~((u32)0xFF << offset);
|
|
-
|
|
- /* write vector and valid bit */
|
|
- ivar |= (msix_vector | IGC_IVAR_VALID) << offset;
|
|
-
|
|
- array_wr32(IGC_IVAR0, index, ivar);
|
|
-}
|
|
-
|
|
-static void igc_assign_vector(struct igc_q_vector *q_vector, int msix_vector)
|
|
-{
|
|
- struct igc_adapter *adapter = q_vector->adapter;
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- int rx_queue = IGC_N0_QUEUE;
|
|
- int tx_queue = IGC_N0_QUEUE;
|
|
-
|
|
- if (q_vector->rx.ring)
|
|
- rx_queue = q_vector->rx.ring->reg_idx;
|
|
- if (q_vector->tx.ring)
|
|
- tx_queue = q_vector->tx.ring->reg_idx;
|
|
-
|
|
- switch (hw->mac.type) {
|
|
- case igc_i225:
|
|
- if (rx_queue > IGC_N0_QUEUE)
|
|
- igc_write_ivar(hw, msix_vector,
|
|
- rx_queue >> 1,
|
|
- (rx_queue & 0x1) << 4);
|
|
- if (tx_queue > IGC_N0_QUEUE)
|
|
- igc_write_ivar(hw, msix_vector,
|
|
- tx_queue >> 1,
|
|
- ((tx_queue & 0x1) << 4) + 8);
|
|
- q_vector->eims_value = BIT(msix_vector);
|
|
- break;
|
|
- default:
|
|
- WARN_ONCE(hw->mac.type != igc_i225, "Wrong MAC type\n");
|
|
- break;
|
|
- }
|
|
-
|
|
- /* add q_vector eims value to global eims_enable_mask */
|
|
- adapter->eims_enable_mask |= q_vector->eims_value;
|
|
-
|
|
- /* configure q_vector to set itr on first interrupt */
|
|
- q_vector->set_itr = 1;
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_configure_msix - Configure MSI-X hardware
|
|
- * @adapter: Pointer to adapter structure
|
|
- *
|
|
- * igc_configure_msix sets up the hardware to properly
|
|
- * generate MSI-X interrupts.
|
|
- */
|
|
-static void igc_configure_msix(struct igc_adapter *adapter)
|
|
+static void igc_write_itr(struct igc_q_vector *q_vector)
|
|
{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
- int i, vector = 0;
|
|
- u32 tmp;
|
|
-
|
|
- adapter->eims_enable_mask = 0;
|
|
-
|
|
- /* set vector for other causes, i.e. link changes */
|
|
- switch (hw->mac.type) {
|
|
- case igc_i225:
|
|
- /* Turn on MSI-X capability first, or our settings
|
|
- * won't stick. And it will take days to debug.
|
|
- */
|
|
- wr32(IGC_GPIE, IGC_GPIE_MSIX_MODE |
|
|
- IGC_GPIE_PBA | IGC_GPIE_EIAME |
|
|
- IGC_GPIE_NSICR);
|
|
-
|
|
- /* enable msix_other interrupt */
|
|
- adapter->eims_other = BIT(vector);
|
|
- tmp = (vector++ | IGC_IVAR_VALID) << 8;
|
|
+ u32 itr_val = q_vector->itr_val & IGC_QVECTOR_MASK;
|
|
|
|
- wr32(IGC_IVAR_MISC, tmp);
|
|
- break;
|
|
- default:
|
|
- /* do nothing, since nothing else supports MSI-X */
|
|
- break;
|
|
- } /* switch (hw->mac.type) */
|
|
+ if (!q_vector->set_itr)
|
|
+ return;
|
|
|
|
- adapter->eims_enable_mask |= adapter->eims_other;
|
|
+ if (!itr_val)
|
|
+ itr_val = IGC_ITR_VAL_MASK;
|
|
|
|
- for (i = 0; i < adapter->num_q_vectors; i++)
|
|
- igc_assign_vector(adapter->q_vector[i], vector++);
|
|
+ itr_val |= IGC_EITR_CNT_IGNR;
|
|
|
|
- wrfl();
|
|
+ writel(itr_val, q_vector->itr_register);
|
|
+ q_vector->set_itr = 0;
|
|
}
|
|
|
|
static irqreturn_t igc_msix_ring(int irq, void *data)
|
|
@@ -2751,49 +4149,6 @@
|
|
}
|
|
|
|
/**
|
|
- * igc_reset_q_vector - Reset config for interrupt vector
|
|
- * @adapter: board private structure to initialize
|
|
- * @v_idx: Index of vector to be reset
|
|
- *
|
|
- * If NAPI is enabled it will delete any references to the
|
|
- * NAPI struct. This is preparation for igc_free_q_vector.
|
|
- */
|
|
-static void igc_reset_q_vector(struct igc_adapter *adapter, int v_idx)
|
|
-{
|
|
- struct igc_q_vector *q_vector = adapter->q_vector[v_idx];
|
|
-
|
|
- /* if we're coming from igc_set_interrupt_capability, the vectors are
|
|
- * not yet allocated
|
|
- */
|
|
- if (!q_vector)
|
|
- return;
|
|
-
|
|
- if (q_vector->tx.ring)
|
|
- adapter->tx_ring[q_vector->tx.ring->queue_index] = NULL;
|
|
-
|
|
- if (q_vector->rx.ring)
|
|
- adapter->rx_ring[q_vector->rx.ring->queue_index] = NULL;
|
|
-
|
|
- netif_napi_del(&q_vector->napi);
|
|
-}
|
|
-
|
|
-static void igc_reset_interrupt_capability(struct igc_adapter *adapter)
|
|
-{
|
|
- int v_idx = adapter->num_q_vectors;
|
|
-
|
|
- if (adapter->msix_entries) {
|
|
- pci_disable_msix(adapter->pdev);
|
|
- kfree(adapter->msix_entries);
|
|
- adapter->msix_entries = NULL;
|
|
- } else if (adapter->flags & IGC_FLAG_HAS_MSI) {
|
|
- pci_disable_msi(adapter->pdev);
|
|
- }
|
|
-
|
|
- while (v_idx--)
|
|
- igc_reset_q_vector(adapter, v_idx);
|
|
-}
|
|
-
|
|
-/**
|
|
* igc_clear_interrupt_scheme - reset the device to a state of no interrupts
|
|
* @adapter: Pointer to adapter structure
|
|
*
|
|
@@ -2806,48 +4161,6 @@
|
|
igc_reset_interrupt_capability(adapter);
|
|
}
|
|
|
|
-/**
|
|
- * igc_free_q_vectors - Free memory allocated for interrupt vectors
|
|
- * @adapter: board private structure to initialize
|
|
- *
|
|
- * This function frees the memory allocated to the q_vectors. In addition if
|
|
- * NAPI is enabled it will delete any references to the NAPI struct prior
|
|
- * to freeing the q_vector.
|
|
- */
|
|
-static void igc_free_q_vectors(struct igc_adapter *adapter)
|
|
-{
|
|
- int v_idx = adapter->num_q_vectors;
|
|
-
|
|
- adapter->num_tx_queues = 0;
|
|
- adapter->num_rx_queues = 0;
|
|
- adapter->num_q_vectors = 0;
|
|
-
|
|
- while (v_idx--) {
|
|
- igc_reset_q_vector(adapter, v_idx);
|
|
- igc_free_q_vector(adapter, v_idx);
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_free_q_vector - Free memory allocated for specific interrupt vector
|
|
- * @adapter: board private structure to initialize
|
|
- * @v_idx: Index of vector to be freed
|
|
- *
|
|
- * This function frees the memory allocated to the q_vector.
|
|
- */
|
|
-static void igc_free_q_vector(struct igc_adapter *adapter, int v_idx)
|
|
-{
|
|
- struct igc_q_vector *q_vector = adapter->q_vector[v_idx];
|
|
-
|
|
- adapter->q_vector[v_idx] = NULL;
|
|
-
|
|
- /* igc_get_stats64() might access the rings on this vector,
|
|
- * we must wait a grace period before freeing it.
|
|
- */
|
|
- if (q_vector)
|
|
- kfree_rcu(q_vector, rcu);
|
|
-}
|
|
-
|
|
/* Need to wait a few seconds after link up to get diagnostic information from
|
|
* the phy
|
|
*/
|
|
@@ -2872,20 +4185,12 @@
|
|
* false until the igc_check_for_link establishes link
|
|
* for copper adapters ONLY
|
|
*/
|
|
- switch (hw->phy.media_type) {
|
|
- case igc_media_type_copper:
|
|
- if (!hw->mac.get_link_status)
|
|
- return true;
|
|
- hw->mac.ops.check_for_link(hw);
|
|
- link_active = !hw->mac.get_link_status;
|
|
- break;
|
|
- default:
|
|
- case igc_media_type_unknown:
|
|
- break;
|
|
- }
|
|
+ if (!hw->mac.get_link_status)
|
|
+ return true;
|
|
+ hw->mac.ops.check_for_link(hw);
|
|
+ link_active = !hw->mac.get_link_status;
|
|
|
|
- if (hw->mac.type == igc_i225 &&
|
|
- hw->phy.id == I225_I_PHY_ID) {
|
|
+ if (hw->mac.type == igc_i225) {
|
|
if (!netif_carrier_ok(adapter->netdev)) {
|
|
adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
|
|
} else if (!(adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)) {
|
|
@@ -2899,7 +4204,7 @@
|
|
|
|
/**
|
|
* igc_watchdog - Timer Call-back
|
|
- * @data: pointer to adapter cast into an unsigned long
|
|
+ * @t: timer for the watchdog
|
|
*/
|
|
static void igc_watchdog(struct timer_list *t)
|
|
{
|
|
@@ -2917,7 +4222,6 @@
|
|
struct igc_hw *hw = &adapter->hw;
|
|
struct igc_phy_info *phy = &hw->phy;
|
|
u16 phy_data, retry_count = 20;
|
|
- u32 connsw;
|
|
u32 link;
|
|
int i;
|
|
|
|
@@ -2930,15 +4234,10 @@
|
|
link = false;
|
|
}
|
|
|
|
- /* Force link down if we have fiber to swap to */
|
|
- if (adapter->flags & IGC_FLAG_MAS_ENABLE) {
|
|
- if (hw->phy.media_type == igc_media_type_copper) {
|
|
- connsw = rd32(IGC_CONNSW);
|
|
- if (!(connsw & IGC_CONNSW_AUTOSENSE_EN))
|
|
- link = 0;
|
|
- }
|
|
- }
|
|
if (link) {
|
|
+ /* Cancel scheduled suspend requests. */
|
|
+ pm_runtime_resume(netdev->dev.parent);
|
|
+
|
|
if (!netif_carrier_ok(netdev)) {
|
|
u32 ctrl;
|
|
|
|
@@ -2949,8 +4248,7 @@
|
|
ctrl = rd32(IGC_CTRL);
|
|
/* Link status message must follow this format */
|
|
netdev_info(netdev,
|
|
- "igc: %s NIC Link is Up %d Mbps %s Duplex, Flow Control: %s\n",
|
|
- netdev->name,
|
|
+ "NIC Link is Up %d Mbps %s Duplex, Flow Control: %s\n",
|
|
adapter->link_speed,
|
|
adapter->link_duplex == FULL_DUPLEX ?
|
|
"Full" : "Half",
|
|
@@ -2959,6 +4257,15 @@
|
|
(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)
|
|
@@ -2971,7 +4278,9 @@
|
|
adapter->tx_timeout_factor = 14;
|
|
break;
|
|
case SPEED_100:
|
|
- /* maybe add some timeout factor ? */
|
|
+ case SPEED_1000:
|
|
+ case SPEED_2500:
|
|
+ adapter->tx_timeout_factor = 7;
|
|
break;
|
|
}
|
|
|
|
@@ -2988,10 +4297,10 @@
|
|
retry_count--;
|
|
goto retry_read_status;
|
|
} else if (!retry_count) {
|
|
- dev_err(&adapter->pdev->dev, "exceed max 2 second\n");
|
|
+ netdev_err(netdev, "exceed max 2 second\n");
|
|
}
|
|
} else {
|
|
- dev_err(&adapter->pdev->dev, "read 1000Base-T Status Reg\n");
|
|
+ netdev_err(netdev, "read 1000Base-T Status Reg\n");
|
|
}
|
|
no_wait:
|
|
netif_carrier_on(netdev);
|
|
@@ -3007,8 +4316,7 @@
|
|
adapter->link_duplex = 0;
|
|
|
|
/* Links status message must follow this format */
|
|
- netdev_info(netdev, "igc: %s NIC Link is Down\n",
|
|
- netdev->name);
|
|
+ netdev_info(netdev, "NIC Link is Down\n");
|
|
netif_carrier_off(netdev);
|
|
|
|
/* link state has changed, schedule phy info update */
|
|
@@ -3024,6 +4332,8 @@
|
|
return;
|
|
}
|
|
}
|
|
+ pm_schedule_suspend(netdev->dev.parent,
|
|
+ MSEC_PER_SEC * 5);
|
|
|
|
/* also check for alternate media here */
|
|
} else if (!netif_carrier_ok(netdev) &&
|
|
@@ -3072,6 +4382,8 @@
|
|
wr32(IGC_ICS, IGC_ICS_RXDMT0);
|
|
}
|
|
|
|
+ igc_ptp_tx_hang(adapter);
|
|
+
|
|
/* Reset the timer */
|
|
if (!test_bit(__IGC_DOWN, &adapter->state)) {
|
|
if (adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)
|
|
@@ -3084,149 +4396,6 @@
|
|
}
|
|
|
|
/**
|
|
- * igc_update_ring_itr - update the dynamic ITR value based on packet size
|
|
- * @q_vector: pointer to q_vector
|
|
- *
|
|
- * Stores a new ITR value based on strictly on packet size. This
|
|
- * algorithm is less sophisticated than that used in igc_update_itr,
|
|
- * due to the difficulty of synchronizing statistics across multiple
|
|
- * receive rings. The divisors and thresholds used by this function
|
|
- * were determined based on theoretical maximum wire speed and testing
|
|
- * data, in order to minimize response time while increasing bulk
|
|
- * throughput.
|
|
- * NOTE: This function is called only when operating in a multiqueue
|
|
- * receive environment.
|
|
- */
|
|
-static void igc_update_ring_itr(struct igc_q_vector *q_vector)
|
|
-{
|
|
- struct igc_adapter *adapter = q_vector->adapter;
|
|
- int new_val = q_vector->itr_val;
|
|
- int avg_wire_size = 0;
|
|
- unsigned int packets;
|
|
-
|
|
- /* For non-gigabit speeds, just fix the interrupt rate at 4000
|
|
- * ints/sec - ITR timer value of 120 ticks.
|
|
- */
|
|
- switch (adapter->link_speed) {
|
|
- case SPEED_10:
|
|
- case SPEED_100:
|
|
- new_val = IGC_4K_ITR;
|
|
- goto set_itr_val;
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- packets = q_vector->rx.total_packets;
|
|
- if (packets)
|
|
- avg_wire_size = q_vector->rx.total_bytes / packets;
|
|
-
|
|
- packets = q_vector->tx.total_packets;
|
|
- if (packets)
|
|
- avg_wire_size = max_t(u32, avg_wire_size,
|
|
- q_vector->tx.total_bytes / packets);
|
|
-
|
|
- /* if avg_wire_size isn't set no work was done */
|
|
- if (!avg_wire_size)
|
|
- goto clear_counts;
|
|
-
|
|
- /* Add 24 bytes to size to account for CRC, preamble, and gap */
|
|
- avg_wire_size += 24;
|
|
-
|
|
- /* Don't starve jumbo frames */
|
|
- avg_wire_size = min(avg_wire_size, 3000);
|
|
-
|
|
- /* Give a little boost to mid-size frames */
|
|
- if (avg_wire_size > 300 && avg_wire_size < 1200)
|
|
- new_val = avg_wire_size / 3;
|
|
- else
|
|
- new_val = avg_wire_size / 2;
|
|
-
|
|
- /* conservative mode (itr 3) eliminates the lowest_latency setting */
|
|
- if (new_val < IGC_20K_ITR &&
|
|
- ((q_vector->rx.ring && adapter->rx_itr_setting == 3) ||
|
|
- (!q_vector->rx.ring && adapter->tx_itr_setting == 3)))
|
|
- new_val = IGC_20K_ITR;
|
|
-
|
|
-set_itr_val:
|
|
- if (new_val != q_vector->itr_val) {
|
|
- q_vector->itr_val = new_val;
|
|
- q_vector->set_itr = 1;
|
|
- }
|
|
-clear_counts:
|
|
- q_vector->rx.total_bytes = 0;
|
|
- q_vector->rx.total_packets = 0;
|
|
- q_vector->tx.total_bytes = 0;
|
|
- q_vector->tx.total_packets = 0;
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_update_itr - update the dynamic ITR value based on statistics
|
|
- * @q_vector: pointer to q_vector
|
|
- * @ring_container: ring info to update the itr for
|
|
- *
|
|
- * Stores a new ITR value based on packets and byte
|
|
- * counts during the last interrupt. The advantage of per interrupt
|
|
- * computation is faster updates and more accurate ITR for the current
|
|
- * traffic pattern. Constants in this function were computed
|
|
- * based on theoretical maximum wire speed and thresholds were set based
|
|
- * on testing data as well as attempting to minimize response time
|
|
- * while increasing bulk throughput.
|
|
- * NOTE: These calculations are only valid when operating in a single-
|
|
- * queue environment.
|
|
- */
|
|
-static void igc_update_itr(struct igc_q_vector *q_vector,
|
|
- struct igc_ring_container *ring_container)
|
|
-{
|
|
- unsigned int packets = ring_container->total_packets;
|
|
- unsigned int bytes = ring_container->total_bytes;
|
|
- u8 itrval = ring_container->itr;
|
|
-
|
|
- /* no packets, exit with status unchanged */
|
|
- if (packets == 0)
|
|
- return;
|
|
-
|
|
- switch (itrval) {
|
|
- case lowest_latency:
|
|
- /* handle TSO and jumbo frames */
|
|
- if (bytes / packets > 8000)
|
|
- itrval = bulk_latency;
|
|
- else if ((packets < 5) && (bytes > 512))
|
|
- itrval = low_latency;
|
|
- break;
|
|
- case low_latency: /* 50 usec aka 20000 ints/s */
|
|
- if (bytes > 10000) {
|
|
- /* this if handles the TSO accounting */
|
|
- if (bytes / packets > 8000)
|
|
- itrval = bulk_latency;
|
|
- else if ((packets < 10) || ((bytes / packets) > 1200))
|
|
- itrval = bulk_latency;
|
|
- else if ((packets > 35))
|
|
- itrval = lowest_latency;
|
|
- } else if (bytes / packets > 2000) {
|
|
- itrval = bulk_latency;
|
|
- } else if (packets <= 2 && bytes < 512) {
|
|
- itrval = lowest_latency;
|
|
- }
|
|
- break;
|
|
- case bulk_latency: /* 250 usec aka 4000 ints/s */
|
|
- if (bytes > 25000) {
|
|
- if (packets > 35)
|
|
- itrval = low_latency;
|
|
- } else if (bytes < 1500) {
|
|
- itrval = low_latency;
|
|
- }
|
|
- break;
|
|
- }
|
|
-
|
|
- /* clear work counters since we have the values we need */
|
|
- ring_container->total_bytes = 0;
|
|
- ring_container->total_packets = 0;
|
|
-
|
|
- /* write updated itr to ring container */
|
|
- ring_container->itr = itrval;
|
|
-}
|
|
-
|
|
-/**
|
|
* igc_intr_msi - Interrupt Handler
|
|
* @irq: interrupt number
|
|
* @data: pointer to a network interface device structure
|
|
@@ -3255,6 +4424,9 @@
|
|
mod_timer(&adapter->watchdog_timer, jiffies + 1);
|
|
}
|
|
|
|
+ if (icr & IGC_ICR_TS)
|
|
+ igc_tsync_interrupt(adapter);
|
|
+
|
|
napi_schedule(&q_vector->napi);
|
|
|
|
return IRQ_HANDLED;
|
|
@@ -3298,429 +4470,14 @@
|
|
mod_timer(&adapter->watchdog_timer, jiffies + 1);
|
|
}
|
|
|
|
+ if (icr & IGC_ICR_TS)
|
|
+ igc_tsync_interrupt(adapter);
|
|
+
|
|
napi_schedule(&q_vector->napi);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
-static void igc_set_itr(struct igc_q_vector *q_vector)
|
|
-{
|
|
- struct igc_adapter *adapter = q_vector->adapter;
|
|
- u32 new_itr = q_vector->itr_val;
|
|
- u8 current_itr = 0;
|
|
-
|
|
- /* for non-gigabit speeds, just fix the interrupt rate at 4000 */
|
|
- switch (adapter->link_speed) {
|
|
- case SPEED_10:
|
|
- case SPEED_100:
|
|
- current_itr = 0;
|
|
- new_itr = IGC_4K_ITR;
|
|
- goto set_itr_now;
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- igc_update_itr(q_vector, &q_vector->tx);
|
|
- igc_update_itr(q_vector, &q_vector->rx);
|
|
-
|
|
- current_itr = max(q_vector->rx.itr, q_vector->tx.itr);
|
|
-
|
|
- /* conservative mode (itr 3) eliminates the lowest_latency setting */
|
|
- if (current_itr == lowest_latency &&
|
|
- ((q_vector->rx.ring && adapter->rx_itr_setting == 3) ||
|
|
- (!q_vector->rx.ring && adapter->tx_itr_setting == 3)))
|
|
- current_itr = low_latency;
|
|
-
|
|
- switch (current_itr) {
|
|
- /* counts and packets in update_itr are dependent on these numbers */
|
|
- case lowest_latency:
|
|
- new_itr = IGC_70K_ITR; /* 70,000 ints/sec */
|
|
- break;
|
|
- case low_latency:
|
|
- new_itr = IGC_20K_ITR; /* 20,000 ints/sec */
|
|
- break;
|
|
- case bulk_latency:
|
|
- new_itr = IGC_4K_ITR; /* 4,000 ints/sec */
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
-set_itr_now:
|
|
- if (new_itr != q_vector->itr_val) {
|
|
- /* this attempts to bias the interrupt rate towards Bulk
|
|
- * by adding intermediate steps when interrupt rate is
|
|
- * increasing
|
|
- */
|
|
- new_itr = new_itr > q_vector->itr_val ?
|
|
- max((new_itr * q_vector->itr_val) /
|
|
- (new_itr + (q_vector->itr_val >> 2)),
|
|
- new_itr) : new_itr;
|
|
- /* Don't write the value here; it resets the adapter's
|
|
- * internal timer, and causes us to delay far longer than
|
|
- * we should between interrupts. Instead, we write the ITR
|
|
- * value at the beginning of the next interrupt so the timing
|
|
- * ends up being correct.
|
|
- */
|
|
- q_vector->itr_val = new_itr;
|
|
- q_vector->set_itr = 1;
|
|
- }
|
|
-}
|
|
-
|
|
-static void igc_ring_irq_enable(struct igc_q_vector *q_vector)
|
|
-{
|
|
- struct igc_adapter *adapter = q_vector->adapter;
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
-
|
|
- if ((q_vector->rx.ring && (adapter->rx_itr_setting & 3)) ||
|
|
- (!q_vector->rx.ring && (adapter->tx_itr_setting & 3))) {
|
|
- if (adapter->num_q_vectors == 1)
|
|
- igc_set_itr(q_vector);
|
|
- else
|
|
- igc_update_ring_itr(q_vector);
|
|
- }
|
|
-
|
|
- if (!test_bit(__IGC_DOWN, &adapter->state)) {
|
|
- if (adapter->msix_entries)
|
|
- wr32(IGC_EIMS, q_vector->eims_value);
|
|
- else
|
|
- igc_irq_enable(adapter);
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_poll - NAPI Rx polling callback
|
|
- * @napi: napi polling structure
|
|
- * @budget: count of how many packets we should handle
|
|
- */
|
|
-static int igc_poll(struct napi_struct *napi, int budget)
|
|
-{
|
|
- struct igc_q_vector *q_vector = container_of(napi,
|
|
- struct igc_q_vector,
|
|
- napi);
|
|
- bool clean_complete = true;
|
|
- int work_done = 0;
|
|
-
|
|
- if (q_vector->tx.ring)
|
|
- clean_complete = igc_clean_tx_irq(q_vector, budget);
|
|
-
|
|
- if (q_vector->rx.ring) {
|
|
- int cleaned = igc_clean_rx_irq(q_vector, budget);
|
|
-
|
|
- work_done += cleaned;
|
|
- if (cleaned >= budget)
|
|
- clean_complete = false;
|
|
- }
|
|
-
|
|
- /* If all work not completed, return budget and keep polling */
|
|
- if (!clean_complete)
|
|
- return budget;
|
|
-
|
|
- /* Exit the polling mode, but don't re-enable interrupts if stack might
|
|
- * poll us due to busy-polling
|
|
- */
|
|
- if (likely(napi_complete_done(napi, work_done)))
|
|
- igc_ring_irq_enable(q_vector);
|
|
-
|
|
- return min(work_done, budget - 1);
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_set_interrupt_capability - set MSI or MSI-X if supported
|
|
- * @adapter: Pointer to adapter structure
|
|
- *
|
|
- * Attempt to configure interrupts using the best available
|
|
- * capabilities of the hardware and kernel.
|
|
- */
|
|
-static void igc_set_interrupt_capability(struct igc_adapter *adapter,
|
|
- bool msix)
|
|
-{
|
|
- int numvecs, i;
|
|
- int err;
|
|
-
|
|
- if (!msix)
|
|
- goto msi_only;
|
|
- adapter->flags |= IGC_FLAG_HAS_MSIX;
|
|
-
|
|
- /* Number of supported queues. */
|
|
- adapter->num_rx_queues = adapter->rss_queues;
|
|
-
|
|
- adapter->num_tx_queues = adapter->rss_queues;
|
|
-
|
|
- /* start with one vector for every Rx queue */
|
|
- numvecs = adapter->num_rx_queues;
|
|
-
|
|
- /* if Tx handler is separate add 1 for every Tx queue */
|
|
- if (!(adapter->flags & IGC_FLAG_QUEUE_PAIRS))
|
|
- numvecs += adapter->num_tx_queues;
|
|
-
|
|
- /* store the number of vectors reserved for queues */
|
|
- adapter->num_q_vectors = numvecs;
|
|
-
|
|
- /* add 1 vector for link status interrupts */
|
|
- numvecs++;
|
|
-
|
|
- adapter->msix_entries = kcalloc(numvecs, sizeof(struct msix_entry),
|
|
- GFP_KERNEL);
|
|
-
|
|
- if (!adapter->msix_entries)
|
|
- return;
|
|
-
|
|
- /* populate entry values */
|
|
- for (i = 0; i < numvecs; i++)
|
|
- adapter->msix_entries[i].entry = i;
|
|
-
|
|
- err = pci_enable_msix_range(adapter->pdev,
|
|
- adapter->msix_entries,
|
|
- numvecs,
|
|
- numvecs);
|
|
- if (err > 0)
|
|
- return;
|
|
-
|
|
- kfree(adapter->msix_entries);
|
|
- adapter->msix_entries = NULL;
|
|
-
|
|
- igc_reset_interrupt_capability(adapter);
|
|
-
|
|
-msi_only:
|
|
- adapter->flags &= ~IGC_FLAG_HAS_MSIX;
|
|
-
|
|
- adapter->rss_queues = 1;
|
|
- adapter->flags |= IGC_FLAG_QUEUE_PAIRS;
|
|
- adapter->num_rx_queues = 1;
|
|
- adapter->num_tx_queues = 1;
|
|
- adapter->num_q_vectors = 1;
|
|
- if (!pci_enable_msi(adapter->pdev))
|
|
- adapter->flags |= IGC_FLAG_HAS_MSI;
|
|
-}
|
|
-
|
|
-static void igc_add_ring(struct igc_ring *ring,
|
|
- struct igc_ring_container *head)
|
|
-{
|
|
- head->ring = ring;
|
|
- head->count++;
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_alloc_q_vector - Allocate memory for a single interrupt vector
|
|
- * @adapter: board private structure to initialize
|
|
- * @v_count: q_vectors allocated on adapter, used for ring interleaving
|
|
- * @v_idx: index of vector in adapter struct
|
|
- * @txr_count: total number of Tx rings to allocate
|
|
- * @txr_idx: index of first Tx ring to allocate
|
|
- * @rxr_count: total number of Rx rings to allocate
|
|
- * @rxr_idx: index of first Rx ring to allocate
|
|
- *
|
|
- * We allocate one q_vector. If allocation fails we return -ENOMEM.
|
|
- */
|
|
-static int igc_alloc_q_vector(struct igc_adapter *adapter,
|
|
- unsigned int v_count, unsigned int v_idx,
|
|
- unsigned int txr_count, unsigned int txr_idx,
|
|
- unsigned int rxr_count, unsigned int rxr_idx)
|
|
-{
|
|
- struct igc_q_vector *q_vector;
|
|
- struct igc_ring *ring;
|
|
- int ring_count;
|
|
-
|
|
- /* igc only supports 1 Tx and/or 1 Rx queue per vector */
|
|
- if (txr_count > 1 || rxr_count > 1)
|
|
- return -ENOMEM;
|
|
-
|
|
- ring_count = txr_count + rxr_count;
|
|
-
|
|
- /* allocate q_vector and rings */
|
|
- q_vector = adapter->q_vector[v_idx];
|
|
- if (!q_vector)
|
|
- q_vector = kzalloc(struct_size(q_vector, ring, ring_count),
|
|
- GFP_KERNEL);
|
|
- else
|
|
- memset(q_vector, 0, struct_size(q_vector, ring, ring_count));
|
|
- if (!q_vector)
|
|
- return -ENOMEM;
|
|
-
|
|
- /* initialize NAPI */
|
|
- netif_napi_add(adapter->netdev, &q_vector->napi,
|
|
- igc_poll, 64);
|
|
-
|
|
- /* tie q_vector and adapter together */
|
|
- adapter->q_vector[v_idx] = q_vector;
|
|
- q_vector->adapter = adapter;
|
|
-
|
|
- /* initialize work limits */
|
|
- q_vector->tx.work_limit = adapter->tx_work_limit;
|
|
-
|
|
- /* initialize ITR configuration */
|
|
- q_vector->itr_register = adapter->io_addr + IGC_EITR(0);
|
|
- q_vector->itr_val = IGC_START_ITR;
|
|
-
|
|
- /* initialize pointer to rings */
|
|
- ring = q_vector->ring;
|
|
-
|
|
- /* initialize ITR */
|
|
- if (rxr_count) {
|
|
- /* rx or rx/tx vector */
|
|
- if (!adapter->rx_itr_setting || adapter->rx_itr_setting > 3)
|
|
- q_vector->itr_val = adapter->rx_itr_setting;
|
|
- } else {
|
|
- /* tx only vector */
|
|
- if (!adapter->tx_itr_setting || adapter->tx_itr_setting > 3)
|
|
- q_vector->itr_val = adapter->tx_itr_setting;
|
|
- }
|
|
-
|
|
- if (txr_count) {
|
|
- /* assign generic ring traits */
|
|
- ring->dev = &adapter->pdev->dev;
|
|
- ring->netdev = adapter->netdev;
|
|
-
|
|
- /* configure backlink on ring */
|
|
- ring->q_vector = q_vector;
|
|
-
|
|
- /* update q_vector Tx values */
|
|
- igc_add_ring(ring, &q_vector->tx);
|
|
-
|
|
- /* apply Tx specific ring traits */
|
|
- ring->count = adapter->tx_ring_count;
|
|
- ring->queue_index = txr_idx;
|
|
-
|
|
- /* assign ring to adapter */
|
|
- adapter->tx_ring[txr_idx] = ring;
|
|
-
|
|
- /* push pointer to next ring */
|
|
- ring++;
|
|
- }
|
|
-
|
|
- if (rxr_count) {
|
|
- /* assign generic ring traits */
|
|
- ring->dev = &adapter->pdev->dev;
|
|
- ring->netdev = adapter->netdev;
|
|
-
|
|
- /* configure backlink on ring */
|
|
- ring->q_vector = q_vector;
|
|
-
|
|
- /* update q_vector Rx values */
|
|
- igc_add_ring(ring, &q_vector->rx);
|
|
-
|
|
- /* apply Rx specific ring traits */
|
|
- ring->count = adapter->rx_ring_count;
|
|
- ring->queue_index = rxr_idx;
|
|
-
|
|
- /* assign ring to adapter */
|
|
- adapter->rx_ring[rxr_idx] = ring;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_alloc_q_vectors - Allocate memory for interrupt vectors
|
|
- * @adapter: board private structure to initialize
|
|
- *
|
|
- * We allocate one q_vector per queue interrupt. If allocation fails we
|
|
- * return -ENOMEM.
|
|
- */
|
|
-static int igc_alloc_q_vectors(struct igc_adapter *adapter)
|
|
-{
|
|
- int rxr_remaining = adapter->num_rx_queues;
|
|
- int txr_remaining = adapter->num_tx_queues;
|
|
- int rxr_idx = 0, txr_idx = 0, v_idx = 0;
|
|
- int q_vectors = adapter->num_q_vectors;
|
|
- int err;
|
|
-
|
|
- if (q_vectors >= (rxr_remaining + txr_remaining)) {
|
|
- for (; rxr_remaining; v_idx++) {
|
|
- err = igc_alloc_q_vector(adapter, q_vectors, v_idx,
|
|
- 0, 0, 1, rxr_idx);
|
|
-
|
|
- if (err)
|
|
- goto err_out;
|
|
-
|
|
- /* update counts and index */
|
|
- rxr_remaining--;
|
|
- rxr_idx++;
|
|
- }
|
|
- }
|
|
-
|
|
- for (; v_idx < q_vectors; v_idx++) {
|
|
- int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_idx);
|
|
- int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_idx);
|
|
-
|
|
- err = igc_alloc_q_vector(adapter, q_vectors, v_idx,
|
|
- tqpv, txr_idx, rqpv, rxr_idx);
|
|
-
|
|
- if (err)
|
|
- goto err_out;
|
|
-
|
|
- /* update counts and index */
|
|
- rxr_remaining -= rqpv;
|
|
- txr_remaining -= tqpv;
|
|
- rxr_idx++;
|
|
- txr_idx++;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-
|
|
-err_out:
|
|
- adapter->num_tx_queues = 0;
|
|
- adapter->num_rx_queues = 0;
|
|
- adapter->num_q_vectors = 0;
|
|
-
|
|
- while (v_idx--)
|
|
- igc_free_q_vector(adapter, v_idx);
|
|
-
|
|
- return -ENOMEM;
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_cache_ring_register - Descriptor ring to register mapping
|
|
- * @adapter: board private structure to initialize
|
|
- *
|
|
- * Once we know the feature-set enabled for the device, we'll cache
|
|
- * the register offset the descriptor ring is assigned to.
|
|
- */
|
|
-static void igc_cache_ring_register(struct igc_adapter *adapter)
|
|
-{
|
|
- int i = 0, j = 0;
|
|
-
|
|
- switch (adapter->hw.mac.type) {
|
|
- case igc_i225:
|
|
- /* Fall through */
|
|
- default:
|
|
- for (; i < adapter->num_rx_queues; i++)
|
|
- adapter->rx_ring[i]->reg_idx = i;
|
|
- for (; j < adapter->num_tx_queues; j++)
|
|
- adapter->tx_ring[j]->reg_idx = j;
|
|
- break;
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_init_interrupt_scheme - initialize interrupts, allocate queues/vectors
|
|
- * @adapter: Pointer to adapter structure
|
|
- *
|
|
- * This function initializes the interrupts and allocates all of the queues.
|
|
- */
|
|
-static int igc_init_interrupt_scheme(struct igc_adapter *adapter, bool msix)
|
|
-{
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
- int err = 0;
|
|
-
|
|
- igc_set_interrupt_capability(adapter, msix);
|
|
-
|
|
- err = igc_alloc_q_vectors(adapter);
|
|
- if (err) {
|
|
- dev_err(&pdev->dev, "Unable to allocate memory for vectors\n");
|
|
- goto err_alloc_q_vectors;
|
|
- }
|
|
-
|
|
- igc_cache_ring_register(adapter);
|
|
-
|
|
- return 0;
|
|
-
|
|
-err_alloc_q_vectors:
|
|
- igc_reset_interrupt_capability(adapter);
|
|
- return err;
|
|
-}
|
|
-
|
|
static void igc_free_irq(struct igc_adapter *adapter)
|
|
{
|
|
if (adapter->msix_entries) {
|
|
@@ -3737,62 +4494,6 @@
|
|
}
|
|
|
|
/**
|
|
- * igc_irq_disable - Mask off interrupt generation on the NIC
|
|
- * @adapter: board private structure
|
|
- */
|
|
-static void igc_irq_disable(struct igc_adapter *adapter)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
-
|
|
- if (adapter->msix_entries) {
|
|
- u32 regval = rd32(IGC_EIAM);
|
|
-
|
|
- wr32(IGC_EIAM, regval & ~adapter->eims_enable_mask);
|
|
- wr32(IGC_EIMC, adapter->eims_enable_mask);
|
|
- regval = rd32(IGC_EIAC);
|
|
- wr32(IGC_EIAC, regval & ~adapter->eims_enable_mask);
|
|
- }
|
|
-
|
|
- wr32(IGC_IAM, 0);
|
|
- wr32(IGC_IMC, ~0);
|
|
- wrfl();
|
|
-
|
|
- if (adapter->msix_entries) {
|
|
- int vector = 0, i;
|
|
-
|
|
- synchronize_irq(adapter->msix_entries[vector++].vector);
|
|
-
|
|
- for (i = 0; i < adapter->num_q_vectors; i++)
|
|
- synchronize_irq(adapter->msix_entries[vector++].vector);
|
|
- } else {
|
|
- synchronize_irq(adapter->pdev->irq);
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
- * igc_irq_enable - Enable default interrupt generation settings
|
|
- * @adapter: board private structure
|
|
- */
|
|
-static void igc_irq_enable(struct igc_adapter *adapter)
|
|
-{
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
-
|
|
- if (adapter->msix_entries) {
|
|
- u32 ims = IGC_IMS_LSC | IGC_IMS_DOUTSYNC | IGC_IMS_DRSTA;
|
|
- u32 regval = rd32(IGC_EIAC);
|
|
-
|
|
- wr32(IGC_EIAC, regval | adapter->eims_enable_mask);
|
|
- regval = rd32(IGC_EIAM);
|
|
- wr32(IGC_EIAM, regval | adapter->eims_enable_mask);
|
|
- wr32(IGC_EIMS, adapter->eims_enable_mask);
|
|
- wr32(IGC_IMS, ims);
|
|
- } else {
|
|
- wr32(IGC_IMS, IMS_ENABLE_MASK | IGC_IMS_DRSTA);
|
|
- wr32(IGC_IAM, IMS_ENABLE_MASK | IGC_IMS_DRSTA);
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
* igc_request_irq - initialize interrupts
|
|
* @adapter: Pointer to adapter structure
|
|
*
|
|
@@ -3839,32 +4540,16 @@
|
|
netdev->name, adapter);
|
|
|
|
if (err)
|
|
- dev_err(&pdev->dev, "Error %d getting interrupt\n",
|
|
- err);
|
|
+ netdev_err(netdev, "Error %d getting interrupt\n", err);
|
|
|
|
request_done:
|
|
return err;
|
|
}
|
|
|
|
-static void igc_write_itr(struct igc_q_vector *q_vector)
|
|
-{
|
|
- u32 itr_val = q_vector->itr_val & IGC_QVECTOR_MASK;
|
|
-
|
|
- if (!q_vector->set_itr)
|
|
- return;
|
|
-
|
|
- if (!itr_val)
|
|
- itr_val = IGC_ITR_VAL_MASK;
|
|
-
|
|
- itr_val |= IGC_EITR_CNT_IGNR;
|
|
-
|
|
- writel(itr_val, q_vector->itr_register);
|
|
- q_vector->set_itr = 0;
|
|
-}
|
|
-
|
|
/**
|
|
- * igc_open - Called when a network interface is made active
|
|
+ * __igc_open - Called when a network interface is made active
|
|
* @netdev: network interface device structure
|
|
+ * @resuming: boolean indicating if the device is resuming
|
|
*
|
|
* Returns 0 on success, negative value on failure
|
|
*
|
|
@@ -3877,6 +4562,7 @@
|
|
static int __igc_open(struct net_device *netdev, bool resuming)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct pci_dev *pdev = adapter->pdev;
|
|
struct igc_hw *hw = &adapter->hw;
|
|
int err = 0;
|
|
int i = 0;
|
|
@@ -3888,6 +4574,9 @@
|
|
return -EBUSY;
|
|
}
|
|
|
|
+ if (!resuming)
|
|
+ pm_runtime_get_sync(&pdev->dev);
|
|
+
|
|
netif_carrier_off(netdev);
|
|
|
|
/* allocate transmit descriptors */
|
|
@@ -3926,6 +4615,9 @@
|
|
rd32(IGC_ICR);
|
|
igc_irq_enable(adapter);
|
|
|
|
+ if (!resuming)
|
|
+ pm_runtime_put(&pdev->dev);
|
|
+
|
|
netif_tx_start_all_queues(netdev);
|
|
|
|
/* start the watchdog. */
|
|
@@ -3938,24 +4630,27 @@
|
|
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);
|
|
err_setup_tx:
|
|
igc_reset(adapter);
|
|
+ if (!resuming)
|
|
+ pm_runtime_put(&pdev->dev);
|
|
|
|
return err;
|
|
}
|
|
|
|
-static int igc_open(struct net_device *netdev)
|
|
+int igc_open(struct net_device *netdev)
|
|
{
|
|
return __igc_open(netdev, false);
|
|
}
|
|
|
|
/**
|
|
- * igc_close - Disables a network interface
|
|
+ * __igc_close - Disables a network interface
|
|
* @netdev: network interface device structure
|
|
+ * @suspending: boolean indicating the device is suspending
|
|
*
|
|
* Returns 0, this is not allowed to fail
|
|
*
|
|
@@ -3967,9 +4662,13 @@
|
|
static int __igc_close(struct net_device *netdev, bool suspending)
|
|
{
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct pci_dev *pdev = adapter->pdev;
|
|
|
|
WARN_ON(test_bit(__IGC_RESETTING, &adapter->state));
|
|
|
|
+ if (!suspending)
|
|
+ pm_runtime_get_sync(&pdev->dev);
|
|
+
|
|
igc_down(adapter);
|
|
|
|
igc_release_hw_control(adapter);
|
|
@@ -3979,26 +4678,223 @@
|
|
igc_free_all_tx_resources(adapter);
|
|
igc_free_all_rx_resources(adapter);
|
|
|
|
+ if (!suspending)
|
|
+ pm_runtime_put_sync(&pdev->dev);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
-static int igc_close(struct net_device *netdev)
|
|
+int igc_close(struct net_device *netdev)
|
|
{
|
|
if (netif_device_present(netdev) || netdev->dismantle)
|
|
return __igc_close(netdev, false);
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * igc_ioctl - Access the hwtstamp interface
|
|
+ * @netdev: network interface device structure
|
|
+ * @ifr: interface request data
|
|
+ * @cmd: ioctl command
|
|
+ **/
|
|
+static int igc_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
|
|
+{
|
|
+ switch (cmd) {
|
|
+ case SIOCGHWTSTAMP:
|
|
+ return igc_ptp_get_ts_config(netdev, ifr);
|
|
+ case SIOCSHWTSTAMP:
|
|
+ return igc_ptp_set_ts_config(netdev, ifr);
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int igc_save_launchtime_params(struct igc_adapter *adapter, int queue,
|
|
+ bool enable)
|
|
+{
|
|
+ struct igc_ring *ring;
|
|
+ int i;
|
|
+
|
|
+ if (queue < 0 || queue >= adapter->num_tx_queues)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ring = adapter->tx_ring[queue];
|
|
+ ring->launchtime_enable = enable;
|
|
+
|
|
+ if (adapter->base_time)
|
|
+ return 0;
|
|
+
|
|
+ adapter->cycle_time = NSEC_PER_SEC;
|
|
+
|
|
+ for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
+ ring = adapter->tx_ring[i];
|
|
+ ring->start_time = 0;
|
|
+ ring->end_time = NSEC_PER_SEC;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static bool is_base_time_past(ktime_t base_time, const struct timespec64 *now)
|
|
+{
|
|
+ struct timespec64 b;
|
|
+
|
|
+ b = ktime_to_timespec64(base_time);
|
|
+
|
|
+ return timespec64_compare(now, &b) > 0;
|
|
+}
|
|
+
|
|
+static bool validate_schedule(struct igc_adapter *adapter,
|
|
+ const struct tc_taprio_qopt_offload *qopt)
|
|
+{
|
|
+ int queue_uses[IGC_MAX_TX_QUEUES] = { };
|
|
+ struct timespec64 now;
|
|
+ size_t n;
|
|
+
|
|
+ if (qopt->cycle_time_extension)
|
|
+ return false;
|
|
+
|
|
+ igc_ptp_read(adapter, &now);
|
|
+
|
|
+ /* If we program the controller's BASET registers with a time
|
|
+ * in the future, it will hold all the packets until that
|
|
+ * time, causing a lot of TX Hangs, so to avoid that, we
|
|
+ * reject schedules that would start in the future.
|
|
+ */
|
|
+ if (!is_base_time_past(qopt->base_time, &now))
|
|
+ return false;
|
|
+
|
|
+ for (n = 0; n < qopt->num_entries; n++) {
|
|
+ const struct tc_taprio_sched_entry *e;
|
|
+ int i;
|
|
+
|
|
+ e = &qopt->entries[n];
|
|
+
|
|
+ /* i225 only supports "global" frame preemption
|
|
+ * settings.
|
|
+ */
|
|
+ if (e->command != TC_TAPRIO_CMD_SET_GATES)
|
|
+ return false;
|
|
+
|
|
+ for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
+ if (e->gate_mask & BIT(i))
|
|
+ queue_uses[i]++;
|
|
+
|
|
+ if (queue_uses[i] > 1)
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static int igc_tsn_enable_launchtime(struct igc_adapter *adapter,
|
|
+ struct tc_etf_qopt_offload *qopt)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int err;
|
|
+
|
|
+ if (hw->mac.type != igc_i225)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ err = igc_save_launchtime_params(adapter, qopt->queue, qopt->enable);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return igc_tsn_offload_apply(adapter);
|
|
+}
|
|
+
|
|
+static int igc_save_qbv_schedule(struct igc_adapter *adapter,
|
|
+ struct tc_taprio_qopt_offload *qopt)
|
|
+{
|
|
+ u32 start_time = 0, end_time = 0;
|
|
+ size_t n;
|
|
+
|
|
+ if (!qopt->enable) {
|
|
+ adapter->base_time = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (adapter->base_time)
|
|
+ return -EALREADY;
|
|
+
|
|
+ if (!validate_schedule(adapter, qopt))
|
|
+ return -EINVAL;
|
|
+
|
|
+ adapter->cycle_time = qopt->cycle_time;
|
|
+ adapter->base_time = qopt->base_time;
|
|
+
|
|
+ /* FIXME: be a little smarter about cases when the gate for a
|
|
+ * queue stays open for more than one entry.
|
|
+ */
|
|
+ for (n = 0; n < qopt->num_entries; n++) {
|
|
+ struct tc_taprio_sched_entry *e = &qopt->entries[n];
|
|
+ int i;
|
|
+
|
|
+ end_time += e->interval;
|
|
+
|
|
+ for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
+ struct igc_ring *ring = adapter->tx_ring[i];
|
|
+
|
|
+ if (!(e->gate_mask & BIT(i)))
|
|
+ continue;
|
|
+
|
|
+ ring->start_time = start_time;
|
|
+ ring->end_time = end_time;
|
|
+ }
|
|
+
|
|
+ start_time += e->interval;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,
|
|
+ struct tc_taprio_qopt_offload *qopt)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int err;
|
|
+
|
|
+ if (hw->mac.type != igc_i225)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ err = igc_save_qbv_schedule(adapter, qopt);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return igc_tsn_offload_apply(adapter);
|
|
+}
|
|
+
|
|
+static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
|
+ void *type_data)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(dev);
|
|
+
|
|
+ switch (type) {
|
|
+ case TC_SETUP_QDISC_TAPRIO:
|
|
+ return igc_tsn_enable_qbv_scheduling(adapter, type_data);
|
|
+
|
|
+ case TC_SETUP_QDISC_ETF:
|
|
+ return igc_tsn_enable_launchtime(adapter, type_data);
|
|
+
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+}
|
|
+
|
|
static const struct net_device_ops igc_netdev_ops = {
|
|
.ndo_open = igc_open,
|
|
.ndo_stop = igc_close,
|
|
.ndo_start_xmit = igc_xmit_frame,
|
|
+ .ndo_set_rx_mode = igc_set_rx_mode,
|
|
.ndo_set_mac_address = igc_set_mac,
|
|
.ndo_change_mtu = igc_change_mtu,
|
|
.ndo_get_stats64 = igc_get_stats64,
|
|
.ndo_fix_features = igc_fix_features,
|
|
.ndo_set_features = igc_set_features,
|
|
.ndo_features_check = igc_features_check,
|
|
+ .ndo_do_ioctl = igc_ioctl,
|
|
+ .ndo_setup_tc = igc_setup_tc,
|
|
};
|
|
|
|
/* PCIe configuration access */
|
|
@@ -4046,9 +4942,6 @@
|
|
u8 __iomem *hw_addr = READ_ONCE(hw->hw_addr);
|
|
u32 value = 0;
|
|
|
|
- if (IGC_REMOVED(hw_addr))
|
|
- return ~value;
|
|
-
|
|
value = readl(&hw_addr[reg]);
|
|
|
|
/* reads should not return all F's */
|
|
@@ -4067,7 +4960,6 @@
|
|
|
|
int igc_set_spd_dplx(struct igc_adapter *adapter, u32 spd, u8 dplx)
|
|
{
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
struct igc_mac_info *mac = &adapter->hw.mac;
|
|
|
|
mac->autoneg = 0;
|
|
@@ -4112,7 +5004,7 @@
|
|
return 0;
|
|
|
|
err_inval:
|
|
- dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n");
|
|
+ netdev_err(adapter->netdev, "Unsupported Speed/Duplex configuration\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
@@ -4134,32 +5026,26 @@
|
|
struct net_device *netdev;
|
|
struct igc_hw *hw;
|
|
const struct igc_info *ei = igc_info_tbl[ent->driver_data];
|
|
- int err;
|
|
+ int err, pci_using_dac;
|
|
|
|
err = pci_enable_device_mem(pdev);
|
|
if (err)
|
|
return err;
|
|
|
|
- err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
|
|
+ pci_using_dac = 0;
|
|
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
|
if (!err) {
|
|
- err = dma_set_coherent_mask(&pdev->dev,
|
|
- DMA_BIT_MASK(64));
|
|
+ pci_using_dac = 1;
|
|
} else {
|
|
- err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
|
|
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
|
if (err) {
|
|
- err = dma_set_coherent_mask(&pdev->dev,
|
|
- DMA_BIT_MASK(32));
|
|
- if (err) {
|
|
- dev_err(&pdev->dev, "igc: Wrong DMA config\n");
|
|
- goto err_dma;
|
|
- }
|
|
+ dev_err(&pdev->dev,
|
|
+ "No usable DMA configuration, aborting\n");
|
|
+ goto err_dma;
|
|
}
|
|
}
|
|
|
|
- err = pci_request_selected_regions(pdev,
|
|
- pci_select_bars(pdev,
|
|
- IORESOURCE_MEM),
|
|
- igc_driver_name);
|
|
+ err = pci_request_mem_regions(pdev, igc_driver_name);
|
|
if (err)
|
|
goto err_pci_reg;
|
|
|
|
@@ -4199,7 +5085,7 @@
|
|
hw->hw_addr = adapter->io_addr;
|
|
|
|
netdev->netdev_ops = &igc_netdev_ops;
|
|
- igc_set_ethtool_ops(netdev);
|
|
+ igc_ethtool_set_ops(netdev);
|
|
netdev->watchdog_timeo = 5 * HZ;
|
|
|
|
netdev->mem_start = pci_resource_start(pdev, 0);
|
|
@@ -4222,7 +5108,24 @@
|
|
goto err_sw_init;
|
|
|
|
/* Add supported features to the features list*/
|
|
+ netdev->features |= NETIF_F_SG;
|
|
+ netdev->features |= NETIF_F_TSO;
|
|
+ netdev->features |= NETIF_F_TSO6;
|
|
+ netdev->features |= NETIF_F_TSO_ECN;
|
|
+ netdev->features |= NETIF_F_RXCSUM;
|
|
netdev->features |= NETIF_F_HW_CSUM;
|
|
+ netdev->features |= NETIF_F_SCTP_CRC;
|
|
+ netdev->features |= NETIF_F_HW_TC;
|
|
+
|
|
+#define IGC_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
|
|
+ NETIF_F_GSO_GRE_CSUM | \
|
|
+ NETIF_F_GSO_IPXIP4 | \
|
|
+ NETIF_F_GSO_IPXIP6 | \
|
|
+ NETIF_F_GSO_UDP_TUNNEL | \
|
|
+ NETIF_F_GSO_UDP_TUNNEL_CSUM)
|
|
+
|
|
+ netdev->gso_partial_features = IGC_GSO_PARTIAL_FEATURES;
|
|
+ netdev->features |= NETIF_F_GSO_PARTIAL | IGC_GSO_PARTIAL_FEATURES;
|
|
|
|
/* setup the private structure */
|
|
err = igc_sw_init(adapter);
|
|
@@ -4233,6 +5136,9 @@
|
|
netdev->hw_features |= NETIF_F_NTUPLE;
|
|
netdev->hw_features |= netdev->features;
|
|
|
|
+ if (pci_using_dac)
|
|
+ netdev->features |= NETIF_F_HIGHDMA;
|
|
+
|
|
/* MTU range: 68 - 9216 */
|
|
netdev->min_mtu = ETH_MIN_MTU;
|
|
netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE;
|
|
@@ -4244,8 +5150,7 @@
|
|
|
|
if (igc_get_flash_presence_i225(hw)) {
|
|
if (hw->nvm.ops.validate(hw) < 0) {
|
|
- dev_err(&pdev->dev,
|
|
- "The NVM Checksum Is Not Valid\n");
|
|
+ dev_err(&pdev->dev, "The NVM Checksum Is Not Valid\n");
|
|
err = -EIO;
|
|
goto err_eeprom;
|
|
}
|
|
@@ -4283,6 +5188,18 @@
|
|
hw->fc.requested_mode = igc_fc_default;
|
|
hw->fc.current_mode = igc_fc_default;
|
|
|
|
+ /* By default, support wake on port A */
|
|
+ adapter->flags |= IGC_FLAG_WOL_SUPPORTED;
|
|
+
|
|
+ /* initialize the wol settings based on the eeprom settings */
|
|
+ if (adapter->flags & IGC_FLAG_WOL_SUPPORTED)
|
|
+ adapter->wol |= IGC_WUFC_MAG;
|
|
+
|
|
+ device_set_wakeup_enable(&adapter->pdev->dev,
|
|
+ adapter->flags & IGC_FLAG_WOL_SUPPORTED);
|
|
+
|
|
+ igc_ptp_init(adapter);
|
|
+
|
|
/* reset the hardware with the new settings */
|
|
igc_reset(adapter);
|
|
|
|
@@ -4306,6 +5223,14 @@
|
|
pcie_print_link_status(pdev);
|
|
netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr);
|
|
|
|
+ dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
|
|
+ /* 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);
|
|
+
|
|
return 0;
|
|
|
|
err_register:
|
|
@@ -4341,6 +5266,12 @@
|
|
struct net_device *netdev = pci_get_drvdata(pdev);
|
|
struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
+ pm_runtime_get_noresume(&pdev->dev);
|
|
+
|
|
+ igc_flush_nfc_rules(adapter);
|
|
+
|
|
+ igc_ptp_stop(adapter);
|
|
+
|
|
set_bit(__IGC_DOWN, &adapter->state);
|
|
|
|
del_timer_sync(&adapter->watchdog_timer);
|
|
@@ -4359,8 +5290,6 @@
|
|
pci_iounmap(pdev, adapter->io_addr);
|
|
pci_release_mem_regions(pdev);
|
|
|
|
- kfree(adapter->mac_table);
|
|
- kfree(adapter->shadow_vfta);
|
|
free_netdev(netdev);
|
|
|
|
pci_disable_pcie_error_reporting(pdev);
|
|
@@ -4368,105 +5297,319 @@
|
|
pci_disable_device(pdev);
|
|
}
|
|
|
|
-static struct pci_driver igc_driver = {
|
|
- .name = igc_driver_name,
|
|
- .id_table = igc_pci_tbl,
|
|
- .probe = igc_probe,
|
|
- .remove = igc_remove,
|
|
-};
|
|
+static int __igc_shutdown(struct pci_dev *pdev, bool *enable_wake,
|
|
+ bool runtime)
|
|
+{
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ u32 wufc = runtime ? IGC_WUFC_LNKC : adapter->wol;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 ctrl, rctl, status;
|
|
+ bool wake;
|
|
|
|
-void igc_set_flag_queue_pairs(struct igc_adapter *adapter,
|
|
- const u32 max_rss_queues)
|
|
+ rtnl_lock();
|
|
+ netif_device_detach(netdev);
|
|
+
|
|
+ if (netif_running(netdev))
|
|
+ __igc_close(netdev, true);
|
|
+
|
|
+ igc_ptp_suspend(adapter);
|
|
+
|
|
+ igc_clear_interrupt_scheme(adapter);
|
|
+ rtnl_unlock();
|
|
+
|
|
+ status = rd32(IGC_STATUS);
|
|
+ if (status & IGC_STATUS_LU)
|
|
+ wufc &= ~IGC_WUFC_LNKC;
|
|
+
|
|
+ if (wufc) {
|
|
+ igc_setup_rctl(adapter);
|
|
+ igc_set_rx_mode(netdev);
|
|
+
|
|
+ /* turn on all-multi mode if wake on multicast is enabled */
|
|
+ if (wufc & IGC_WUFC_MC) {
|
|
+ rctl = rd32(IGC_RCTL);
|
|
+ rctl |= IGC_RCTL_MPE;
|
|
+ wr32(IGC_RCTL, rctl);
|
|
+ }
|
|
+
|
|
+ ctrl = rd32(IGC_CTRL);
|
|
+ ctrl |= IGC_CTRL_ADVD3WUC;
|
|
+ wr32(IGC_CTRL, ctrl);
|
|
+
|
|
+ /* Allow time for pending master requests to run */
|
|
+ igc_disable_pcie_master(hw);
|
|
+
|
|
+ wr32(IGC_WUC, IGC_WUC_PME_EN);
|
|
+ wr32(IGC_WUFC, wufc);
|
|
+ } else {
|
|
+ wr32(IGC_WUC, 0);
|
|
+ wr32(IGC_WUFC, 0);
|
|
+ }
|
|
+
|
|
+ wake = wufc || adapter->en_mng_pt;
|
|
+ if (!wake)
|
|
+ igc_power_down_phy_copper_base(&adapter->hw);
|
|
+ else
|
|
+ igc_power_up_link(adapter);
|
|
+
|
|
+ if (enable_wake)
|
|
+ *enable_wake = wake;
|
|
+
|
|
+ /* Release control of h/w to f/w. If f/w is AMT enabled, this
|
|
+ * would have already happened in close and is redundant.
|
|
+ */
|
|
+ igc_release_hw_control(adapter);
|
|
+
|
|
+ pci_disable_device(pdev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static int __maybe_unused igc_runtime_suspend(struct device *dev)
|
|
{
|
|
- /* Determine if we need to pair queues. */
|
|
- /* If rss_queues > half of max_rss_queues, pair the queues in
|
|
- * order to conserve interrupts due to limited supply.
|
|
+ return __igc_shutdown(to_pci_dev(dev), NULL, 1);
|
|
+}
|
|
+
|
|
+static void igc_deliver_wake_packet(struct net_device *netdev)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ struct sk_buff *skb;
|
|
+ u32 wupl;
|
|
+
|
|
+ wupl = rd32(IGC_WUPL) & IGC_WUPL_MASK;
|
|
+
|
|
+ /* WUPM stores only the first 128 bytes of the wake packet.
|
|
+ * Read the packet only if we have the whole thing.
|
|
*/
|
|
- if (adapter->rss_queues > (max_rss_queues / 2))
|
|
- adapter->flags |= IGC_FLAG_QUEUE_PAIRS;
|
|
- else
|
|
- adapter->flags &= ~IGC_FLAG_QUEUE_PAIRS;
|
|
+ if (wupl == 0 || wupl > IGC_WUPM_BYTES)
|
|
+ return;
|
|
+
|
|
+ skb = netdev_alloc_skb_ip_align(netdev, IGC_WUPM_BYTES);
|
|
+ if (!skb)
|
|
+ return;
|
|
+
|
|
+ skb_put(skb, wupl);
|
|
+
|
|
+ /* Ensure reads are 32-bit aligned */
|
|
+ wupl = roundup(wupl, 4);
|
|
+
|
|
+ memcpy_fromio(skb->data, hw->hw_addr + IGC_WUPM_REG(0), wupl);
|
|
+
|
|
+ skb->protocol = eth_type_trans(skb, netdev);
|
|
+ netif_rx(skb);
|
|
}
|
|
|
|
-unsigned int igc_get_max_rss_queues(struct igc_adapter *adapter)
|
|
+static int __maybe_unused igc_resume(struct device *dev)
|
|
{
|
|
- unsigned int max_rss_queues;
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 err, val;
|
|
+
|
|
+ pci_set_power_state(pdev, PCI_D0);
|
|
+ pci_restore_state(pdev);
|
|
+ pci_save_state(pdev);
|
|
+
|
|
+ if (!pci_device_is_present(pdev))
|
|
+ return -ENODEV;
|
|
+ err = pci_enable_device_mem(pdev);
|
|
+ if (err) {
|
|
+ netdev_err(netdev, "Cannot enable PCI device from suspend\n");
|
|
+ return err;
|
|
+ }
|
|
+ pci_set_master(pdev);
|
|
+
|
|
+ pci_enable_wake(pdev, PCI_D3hot, 0);
|
|
+ pci_enable_wake(pdev, PCI_D3cold, 0);
|
|
+
|
|
+ if (igc_init_interrupt_scheme(adapter, true)) {
|
|
+ netdev_err(netdev, "Unable to allocate memory for queues\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ igc_reset(adapter);
|
|
|
|
- /* Determine the maximum number of RSS queues supported. */
|
|
- max_rss_queues = IGC_MAX_RX_QUEUES;
|
|
+ /* let the f/w know that the h/w is now under the control of the
|
|
+ * driver.
|
|
+ */
|
|
+ igc_get_hw_control(adapter);
|
|
+
|
|
+ val = rd32(IGC_WUS);
|
|
+ if (val & WAKE_PKT_WUS)
|
|
+ igc_deliver_wake_packet(netdev);
|
|
+
|
|
+ wr32(IGC_WUS, ~0);
|
|
+
|
|
+ rtnl_lock();
|
|
+ if (!err && netif_running(netdev))
|
|
+ err = __igc_open(netdev, true);
|
|
+
|
|
+ if (!err)
|
|
+ netif_device_attach(netdev);
|
|
+ rtnl_unlock();
|
|
|
|
- return max_rss_queues;
|
|
+ return err;
|
|
}
|
|
|
|
-static void igc_init_queue_configuration(struct igc_adapter *adapter)
|
|
+static int __maybe_unused igc_runtime_resume(struct device *dev)
|
|
{
|
|
- u32 max_rss_queues;
|
|
+ return igc_resume(dev);
|
|
+}
|
|
|
|
- max_rss_queues = igc_get_max_rss_queues(adapter);
|
|
- adapter->rss_queues = min_t(u32, max_rss_queues, num_online_cpus());
|
|
+static int __maybe_unused igc_suspend(struct device *dev)
|
|
+{
|
|
+ return __igc_shutdown(to_pci_dev(dev), NULL, 0);
|
|
+}
|
|
|
|
- igc_set_flag_queue_pairs(adapter, max_rss_queues);
|
|
+static int __maybe_unused igc_runtime_idle(struct device *dev)
|
|
+{
|
|
+ struct net_device *netdev = dev_get_drvdata(dev);
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+
|
|
+ if (!igc_has_link(adapter))
|
|
+ pm_schedule_suspend(dev, MSEC_PER_SEC * 5);
|
|
+
|
|
+ return -EBUSY;
|
|
+}
|
|
+#endif /* CONFIG_PM */
|
|
+
|
|
+static void igc_shutdown(struct pci_dev *pdev)
|
|
+{
|
|
+ bool wake;
|
|
+
|
|
+ __igc_shutdown(pdev, &wake, 0);
|
|
+
|
|
+ if (system_state == SYSTEM_POWER_OFF) {
|
|
+ pci_wake_from_d3(pdev, wake);
|
|
+ pci_set_power_state(pdev, PCI_D3hot);
|
|
+ }
|
|
}
|
|
|
|
/**
|
|
- * igc_sw_init - Initialize general software structures (struct igc_adapter)
|
|
- * @adapter: board private structure to initialize
|
|
+ * igc_io_error_detected - called when PCI error is detected
|
|
+ * @pdev: Pointer to PCI device
|
|
+ * @state: The current PCI connection state
|
|
*
|
|
- * igc_sw_init initializes the Adapter private data structure.
|
|
- * Fields are initialized based on PCI device information and
|
|
- * OS network device settings (MTU size).
|
|
- */
|
|
-static int igc_sw_init(struct igc_adapter *adapter)
|
|
+ * This function is called after a PCI bus error affecting
|
|
+ * this device has been detected.
|
|
+ **/
|
|
+static pci_ers_result_t igc_io_error_detected(struct pci_dev *pdev,
|
|
+ pci_channel_state_t state)
|
|
{
|
|
- struct net_device *netdev = adapter->netdev;
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
- struct igc_hw *hw = &adapter->hw;
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
- int size = sizeof(struct igc_mac_addr) * hw->mac.rar_entry_count;
|
|
+ netif_device_detach(netdev);
|
|
|
|
- pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word);
|
|
+ if (state == pci_channel_io_perm_failure)
|
|
+ return PCI_ERS_RESULT_DISCONNECT;
|
|
|
|
- /* set default ring sizes */
|
|
- adapter->tx_ring_count = IGC_DEFAULT_TXD;
|
|
- adapter->rx_ring_count = IGC_DEFAULT_RXD;
|
|
+ if (netif_running(netdev))
|
|
+ igc_down(adapter);
|
|
+ pci_disable_device(pdev);
|
|
|
|
- /* set default ITR values */
|
|
- adapter->rx_itr_setting = IGC_DEFAULT_ITR;
|
|
- adapter->tx_itr_setting = IGC_DEFAULT_ITR;
|
|
+ /* Request a slot reset. */
|
|
+ return PCI_ERS_RESULT_NEED_RESET;
|
|
+}
|
|
|
|
- /* set default work limits */
|
|
- adapter->tx_work_limit = IGC_DEFAULT_TX_WORK;
|
|
+/**
|
|
+ * igc_io_slot_reset - called after the PCI bus has been reset.
|
|
+ * @pdev: Pointer to PCI device
|
|
+ *
|
|
+ * Restart the card from scratch, as if from a cold-boot. Implementation
|
|
+ * resembles the first-half of the igc_resume routine.
|
|
+ **/
|
|
+static pci_ers_result_t igc_io_slot_reset(struct pci_dev *pdev)
|
|
+{
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ pci_ers_result_t result;
|
|
|
|
- /* adjust max frame to be at least the size of a standard frame */
|
|
- adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN +
|
|
- VLAN_HLEN;
|
|
- adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
|
|
+ if (pci_enable_device_mem(pdev)) {
|
|
+ netdev_err(netdev, "Could not re-enable PCI device after reset\n");
|
|
+ result = PCI_ERS_RESULT_DISCONNECT;
|
|
+ } else {
|
|
+ pci_set_master(pdev);
|
|
+ pci_restore_state(pdev);
|
|
+ pci_save_state(pdev);
|
|
|
|
- spin_lock_init(&adapter->nfc_lock);
|
|
- spin_lock_init(&adapter->stats64_lock);
|
|
- /* Assume MSI-X interrupts, will be checked during IRQ allocation */
|
|
- adapter->flags |= IGC_FLAG_HAS_MSIX;
|
|
+ pci_enable_wake(pdev, PCI_D3hot, 0);
|
|
+ pci_enable_wake(pdev, PCI_D3cold, 0);
|
|
|
|
- adapter->mac_table = kzalloc(size, GFP_ATOMIC);
|
|
- if (!adapter->mac_table)
|
|
- return -ENOMEM;
|
|
-
|
|
- igc_init_queue_configuration(adapter);
|
|
+ /* In case of PCI error, adapter loses its HW address
|
|
+ * so we should re-assign it here.
|
|
+ */
|
|
+ hw->hw_addr = adapter->io_addr;
|
|
|
|
- /* This call may decrease the number of queues */
|
|
- if (igc_init_interrupt_scheme(adapter, true)) {
|
|
- dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
|
|
- return -ENOMEM;
|
|
+ igc_reset(adapter);
|
|
+ wr32(IGC_WUS, ~0);
|
|
+ result = PCI_ERS_RESULT_RECOVERED;
|
|
}
|
|
|
|
- /* Explicitly disable IRQ since the NIC can be in any state. */
|
|
- igc_irq_disable(adapter);
|
|
+ return result;
|
|
+}
|
|
|
|
- set_bit(__IGC_DOWN, &adapter->state);
|
|
+/**
|
|
+ * igc_io_resume - called when traffic can start to flow again.
|
|
+ * @pdev: Pointer to PCI device
|
|
+ *
|
|
+ * This callback is called when the error recovery driver tells us that
|
|
+ * its OK to resume normal operation. Implementation resembles the
|
|
+ * second-half of the igc_resume routine.
|
|
+ */
|
|
+static void igc_io_resume(struct pci_dev *pdev)
|
|
+{
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
|
|
- return 0;
|
|
+ rtnl_lock();
|
|
+ if (netif_running(netdev)) {
|
|
+ if (igc_open(netdev)) {
|
|
+ netdev_err(netdev, "igc_open failed after reset\n");
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ netif_device_attach(netdev);
|
|
+
|
|
+ /* let the f/w know that the h/w is now under the control of the
|
|
+ * driver.
|
|
+ */
|
|
+ igc_get_hw_control(adapter);
|
|
+ rtnl_unlock();
|
|
}
|
|
|
|
+static const struct pci_error_handlers igc_err_handler = {
|
|
+ .error_detected = igc_io_error_detected,
|
|
+ .slot_reset = igc_io_slot_reset,
|
|
+ .resume = igc_io_resume,
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_PM
|
|
+static const struct dev_pm_ops igc_pm_ops = {
|
|
+ SET_SYSTEM_SLEEP_PM_OPS(igc_suspend, igc_resume)
|
|
+ SET_RUNTIME_PM_OPS(igc_runtime_suspend, igc_runtime_resume,
|
|
+ igc_runtime_idle)
|
|
+};
|
|
+#endif
|
|
+
|
|
+static struct pci_driver igc_driver = {
|
|
+ .name = igc_driver_name,
|
|
+ .id_table = igc_pci_tbl,
|
|
+ .probe = igc_probe,
|
|
+ .remove = igc_remove,
|
|
+#ifdef CONFIG_PM
|
|
+ .driver.pm = &igc_pm_ops,
|
|
+#endif
|
|
+ .shutdown = igc_shutdown,
|
|
+ .err_handler = &igc_err_handler,
|
|
+};
|
|
+
|
|
/**
|
|
* igc_reinit_queues - return error
|
|
* @adapter: pointer to adapter structure
|
|
@@ -4474,7 +5617,6 @@
|
|
int igc_reinit_queues(struct igc_adapter *adapter)
|
|
{
|
|
struct net_device *netdev = adapter->netdev;
|
|
- struct pci_dev *pdev = adapter->pdev;
|
|
int err = 0;
|
|
|
|
if (netif_running(netdev))
|
|
@@ -4483,7 +5625,7 @@
|
|
igc_reset_interrupt_capability(adapter);
|
|
|
|
if (igc_init_interrupt_scheme(adapter, true)) {
|
|
- dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
|
|
+ netdev_err(netdev, "Unable to allocate memory for queues\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
@@ -4516,9 +5658,7 @@
|
|
{
|
|
int ret;
|
|
|
|
- pr_info("%s - version %s\n",
|
|
- igc_driver_string, igc_driver_version);
|
|
-
|
|
+ pr_info("%s\n", igc_driver_string);
|
|
pr_info("%s\n", igc_copyright);
|
|
|
|
ret = pci_register_driver(&igc_driver);
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_phy.c 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_phy.c 2022-03-10 14:39:17.877440146 +0800
|
|
@@ -173,6 +173,7 @@
|
|
s32 igc_phy_hw_reset(struct igc_hw *hw)
|
|
{
|
|
struct igc_phy_info *phy = &hw->phy;
|
|
+ u32 phpm = 0, timeout = 10000;
|
|
s32 ret_val;
|
|
u32 ctrl;
|
|
|
|
@@ -186,6 +187,8 @@
|
|
if (ret_val)
|
|
goto out;
|
|
|
|
+ phpm = rd32(IGC_I225_PHPM);
|
|
+
|
|
ctrl = rd32(IGC_CTRL);
|
|
wr32(IGC_CTRL, ctrl | IGC_CTRL_PHY_RST);
|
|
wrfl();
|
|
@@ -195,7 +198,18 @@
|
|
wr32(IGC_CTRL, ctrl);
|
|
wrfl();
|
|
|
|
- usleep_range(1500, 2000);
|
|
+ /* SW should guarantee 100us for the completion of the PHY reset */
|
|
+ usleep_range(100, 150);
|
|
+ do {
|
|
+ phpm = rd32(IGC_I225_PHPM);
|
|
+ timeout--;
|
|
+ udelay(1);
|
|
+ } while (!(phpm & IGC_PHY_RST_COMP) && timeout);
|
|
+
|
|
+ if (!timeout)
|
|
+ hw_dbg("Timeout is expired after a phy reset\n");
|
|
+
|
|
+ usleep_range(100, 150);
|
|
|
|
phy->ops.release(hw);
|
|
|
|
@@ -235,8 +249,7 @@
|
|
return ret_val;
|
|
}
|
|
|
|
- if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
|
|
- hw->phy.id == I225_I_PHY_ID) {
|
|
+ if (phy->autoneg_mask & ADVERTISE_2500_FULL) {
|
|
/* Read the MULTI GBT AN Control Register - reg 7.32 */
|
|
ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK <<
|
|
MMD_DEVADDR_SHIFT) |
|
|
@@ -376,8 +389,7 @@
|
|
ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL,
|
|
mii_1000t_ctrl_reg);
|
|
|
|
- if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
|
|
- hw->phy.id == I225_I_PHY_ID)
|
|
+ if (phy->autoneg_mask & ADVERTISE_2500_FULL)
|
|
ret_val = phy->ops.write_reg(hw,
|
|
(STANDARD_AN_REG_MASK <<
|
|
MMD_DEVADDR_SHIFT) |
|
|
@@ -734,8 +746,6 @@
|
|
if (ret_val)
|
|
return ret_val;
|
|
ret_val = igc_write_phy_reg_mdic(hw, offset, data);
|
|
- if (ret_val)
|
|
- return ret_val;
|
|
hw->phy.ops.release(hw);
|
|
} else {
|
|
ret_val = igc_write_xmdio_reg(hw, (u16)offset, dev_addr,
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_ptp.c 1970-01-01 08:00:00.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c 2022-03-10 14:33:17.227212778 +0800
|
|
@@ -0,0 +1,620 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/* Copyright (c) 2019 Intel Corporation */
|
|
+
|
|
+#include "igc.h"
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/ptp_classify.h>
|
|
+#include <linux/clocksource.h>
|
|
+#include <linux/ktime.h>
|
|
+
|
|
+#define INCVALUE_MASK 0x7fffffff
|
|
+#define ISGN 0x80000000
|
|
+
|
|
+#define IGC_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 9)
|
|
+#define IGC_PTP_TX_TIMEOUT (HZ * 15)
|
|
+
|
|
+/* SYSTIM read access for I225 */
|
|
+void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 sec, nsec;
|
|
+
|
|
+ /* The timestamp is latched when SYSTIML is read. */
|
|
+ nsec = rd32(IGC_SYSTIML);
|
|
+ sec = rd32(IGC_SYSTIMH);
|
|
+
|
|
+ ts->tv_sec = sec;
|
|
+ ts->tv_nsec = nsec;
|
|
+}
|
|
+
|
|
+static void igc_ptp_write_i225(struct igc_adapter *adapter,
|
|
+ const struct timespec64 *ts)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ wr32(IGC_SYSTIML, ts->tv_nsec);
|
|
+ wr32(IGC_SYSTIMH, ts->tv_sec);
|
|
+}
|
|
+
|
|
+static int igc_ptp_adjfine_i225(struct ptp_clock_info *ptp, long scaled_ppm)
|
|
+{
|
|
+ struct igc_adapter *igc = container_of(ptp, struct igc_adapter,
|
|
+ ptp_caps);
|
|
+ struct igc_hw *hw = &igc->hw;
|
|
+ int neg_adj = 0;
|
|
+ u64 rate;
|
|
+ u32 inca;
|
|
+
|
|
+ if (scaled_ppm < 0) {
|
|
+ neg_adj = 1;
|
|
+ scaled_ppm = -scaled_ppm;
|
|
+ }
|
|
+ rate = scaled_ppm;
|
|
+ rate <<= 14;
|
|
+ rate = div_u64(rate, 78125);
|
|
+
|
|
+ inca = rate & INCVALUE_MASK;
|
|
+ if (neg_adj)
|
|
+ inca |= ISGN;
|
|
+
|
|
+ wr32(IGC_TIMINCA, inca);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int igc_ptp_adjtime_i225(struct ptp_clock_info *ptp, s64 delta)
|
|
+{
|
|
+ struct igc_adapter *igc = container_of(ptp, struct igc_adapter,
|
|
+ ptp_caps);
|
|
+ struct timespec64 now, then = ns_to_timespec64(delta);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&igc->tmreg_lock, flags);
|
|
+
|
|
+ igc_ptp_read(igc, &now);
|
|
+ now = timespec64_add(now, then);
|
|
+ igc_ptp_write_i225(igc, (const struct timespec64 *)&now);
|
|
+
|
|
+ spin_unlock_irqrestore(&igc->tmreg_lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int igc_ptp_gettimex64_i225(struct ptp_clock_info *ptp,
|
|
+ struct timespec64 *ts,
|
|
+ struct ptp_system_timestamp *sts)
|
|
+{
|
|
+ struct igc_adapter *igc = container_of(ptp, struct igc_adapter,
|
|
+ ptp_caps);
|
|
+ struct igc_hw *hw = &igc->hw;
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&igc->tmreg_lock, flags);
|
|
+
|
|
+ ptp_read_system_prets(sts);
|
|
+ ts->tv_nsec = rd32(IGC_SYSTIML);
|
|
+ ts->tv_sec = rd32(IGC_SYSTIMH);
|
|
+ ptp_read_system_postts(sts);
|
|
+
|
|
+ spin_unlock_irqrestore(&igc->tmreg_lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int igc_ptp_settime_i225(struct ptp_clock_info *ptp,
|
|
+ const struct timespec64 *ts)
|
|
+{
|
|
+ struct igc_adapter *igc = container_of(ptp, struct igc_adapter,
|
|
+ ptp_caps);
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&igc->tmreg_lock, flags);
|
|
+
|
|
+ igc_ptp_write_i225(igc, ts);
|
|
+
|
|
+ spin_unlock_irqrestore(&igc->tmreg_lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,
|
|
+ struct ptp_clock_request *rq, int on)
|
|
+{
|
|
+ return -EOPNOTSUPP;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_systim_to_hwtstamp - convert system time value to HW timestamp
|
|
+ * @adapter: board private structure
|
|
+ * @hwtstamps: timestamp structure to update
|
|
+ * @systim: unsigned 64bit system time value
|
|
+ *
|
|
+ * We need to convert the system time value stored in the RX/TXSTMP registers
|
|
+ * into a hwtstamp which can be used by the upper level timestamping functions.
|
|
+ **/
|
|
+static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter,
|
|
+ struct skb_shared_hwtstamps *hwtstamps,
|
|
+ u64 systim)
|
|
+{
|
|
+ switch (adapter->hw.mac.type) {
|
|
+ case igc_i225:
|
|
+ memset(hwtstamps, 0, sizeof(*hwtstamps));
|
|
+ /* Upper 32 bits contain s, lower 32 bits contain ns. */
|
|
+ hwtstamps->hwtstamp = ktime_set(systim >> 32,
|
|
+ systim & 0xFFFFFFFF);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer
|
|
+ * @q_vector: Pointer to interrupt specific structure
|
|
+ * @va: Pointer to address containing Rx buffer
|
|
+ * @skb: Buffer containing timestamp and packet
|
|
+ *
|
|
+ * This function retrieves the timestamp saved in the beginning of packet
|
|
+ * buffer. While two timestamps are available, one in timer0 reference and the
|
|
+ * other in timer1 reference, this function considers only the timestamp in
|
|
+ * timer0 reference.
|
|
+ */
|
|
+void igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, __le32 *va,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ struct igc_adapter *adapter = q_vector->adapter;
|
|
+ u64 regval;
|
|
+ int adjust;
|
|
+
|
|
+ /* Timestamps are saved in little endian at the beginning of the packet
|
|
+ * buffer following the layout:
|
|
+ *
|
|
+ * DWORD: | 0 | 1 | 2 | 3 |
|
|
+ * Field: | Timer1 SYSTIML | Timer1 SYSTIMH | Timer0 SYSTIML | Timer0 SYSTIMH |
|
|
+ *
|
|
+ * SYSTIML holds the nanoseconds part while SYSTIMH holds the seconds
|
|
+ * part of the timestamp.
|
|
+ */
|
|
+ regval = le32_to_cpu(va[2]);
|
|
+ regval |= (u64)le32_to_cpu(va[3]) << 32;
|
|
+ igc_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
|
|
+
|
|
+ /* Adjust timestamp for the RX latency based on link speed */
|
|
+ switch (adapter->link_speed) {
|
|
+ case SPEED_10:
|
|
+ adjust = IGC_I225_RX_LATENCY_10;
|
|
+ break;
|
|
+ case SPEED_100:
|
|
+ adjust = IGC_I225_RX_LATENCY_100;
|
|
+ break;
|
|
+ case SPEED_1000:
|
|
+ adjust = IGC_I225_RX_LATENCY_1000;
|
|
+ break;
|
|
+ case SPEED_2500:
|
|
+ adjust = IGC_I225_RX_LATENCY_2500;
|
|
+ break;
|
|
+ default:
|
|
+ adjust = 0;
|
|
+ netdev_warn_once(adapter->netdev, "Imprecise timestamp\n");
|
|
+ break;
|
|
+ }
|
|
+ skb_hwtstamps(skb)->hwtstamp =
|
|
+ ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, adjust);
|
|
+}
|
|
+
|
|
+static void igc_ptp_disable_rx_timestamp(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 val;
|
|
+ int i;
|
|
+
|
|
+ wr32(IGC_TSYNCRXCTL, 0);
|
|
+
|
|
+ for (i = 0; i < adapter->num_rx_queues; i++) {
|
|
+ val = rd32(IGC_SRRCTL(i));
|
|
+ val &= ~IGC_SRRCTL_TIMESTAMP;
|
|
+ wr32(IGC_SRRCTL(i), val);
|
|
+ }
|
|
+
|
|
+ val = rd32(IGC_RXPBS);
|
|
+ val &= ~IGC_RXPBS_CFG_TS_EN;
|
|
+ wr32(IGC_RXPBS, val);
|
|
+}
|
|
+
|
|
+static void igc_ptp_enable_rx_timestamp(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 val;
|
|
+ int i;
|
|
+
|
|
+ val = rd32(IGC_RXPBS);
|
|
+ val |= IGC_RXPBS_CFG_TS_EN;
|
|
+ wr32(IGC_RXPBS, val);
|
|
+
|
|
+ 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);
|
|
+ }
|
|
+
|
|
+ val = IGC_TSYNCRXCTL_ENABLED | IGC_TSYNCRXCTL_TYPE_ALL |
|
|
+ IGC_TSYNCRXCTL_RXSYNSIG;
|
|
+ wr32(IGC_TSYNCRXCTL, val);
|
|
+}
|
|
+
|
|
+static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ wr32(IGC_TSYNCTXCTL, 0);
|
|
+}
|
|
+
|
|
+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);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_set_timestamp_mode - setup hardware for timestamping
|
|
+ * @adapter: networking device structure
|
|
+ * @config: hwtstamp configuration
|
|
+ *
|
|
+ * 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)
|
|
+{
|
|
+ /* reserved for future extensions */
|
|
+ if (config->flags)
|
|
+ return -EINVAL;
|
|
+
|
|
+ switch (config->tx_type) {
|
|
+ case HWTSTAMP_TX_OFF:
|
|
+ igc_ptp_disable_tx_timestamp(adapter);
|
|
+ break;
|
|
+ case HWTSTAMP_TX_ON:
|
|
+ igc_ptp_enable_tx_timestamp(adapter);
|
|
+ break;
|
|
+ default:
|
|
+ return -ERANGE;
|
|
+ }
|
|
+
|
|
+ switch (config->rx_filter) {
|
|
+ case HWTSTAMP_FILTER_NONE:
|
|
+ igc_ptp_disable_rx_timestamp(adapter);
|
|
+ break;
|
|
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
|
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
|
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
|
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
|
+ case HWTSTAMP_FILTER_NTP_ALL:
|
|
+ case HWTSTAMP_FILTER_ALL:
|
|
+ igc_ptp_enable_rx_timestamp(adapter);
|
|
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
|
|
+ break;
|
|
+ default:
|
|
+ return -ERANGE;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void igc_ptp_tx_timeout(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ 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);
|
|
+
|
|
+ if (!test_bit(__IGC_PTP_TX_IN_PROGRESS, &adapter->state))
|
|
+ return;
|
|
+
|
|
+ /* If we haven't received a timestamp within the timeout, it is
|
|
+ * reasonable to assume that it will never occur, so we can unlock the
|
|
+ * timestamp bit when this occurs.
|
|
+ */
|
|
+ if (timeout) {
|
|
+ cancel_work_sync(&adapter->ptp_tx_work);
|
|
+ igc_ptp_tx_timeout(adapter);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_tx_hwtstamp - utility function which checks for TX time stamp
|
|
+ * @adapter: Board private structure
|
|
+ *
|
|
+ * If we were asked to do hardware stamping and such a time stamp is
|
|
+ * available, then it must have been for this skb here because we only
|
|
+ * allow only one such packet into the queue.
|
|
+ */
|
|
+static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct sk_buff *skb = adapter->ptp_tx_skb;
|
|
+ struct skb_shared_hwtstamps shhwtstamps;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ int adjust = 0;
|
|
+ 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);
|
|
+
|
|
+ switch (adapter->link_speed) {
|
|
+ case SPEED_10:
|
|
+ adjust = IGC_I225_TX_LATENCY_10;
|
|
+ break;
|
|
+ case SPEED_100:
|
|
+ adjust = IGC_I225_TX_LATENCY_100;
|
|
+ break;
|
|
+ case SPEED_1000:
|
|
+ adjust = IGC_I225_TX_LATENCY_1000;
|
|
+ break;
|
|
+ case SPEED_2500:
|
|
+ adjust = IGC_I225_TX_LATENCY_2500;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ shhwtstamps.hwtstamp =
|
|
+ ktime_add_ns(shhwtstamps.hwtstamp, adjust);
|
|
+
|
|
+ /* Clear the lock early before calling skb_tstamp_tx so that
|
|
+ * applications are not woken up before the lock bit is clear. We use
|
|
+ * a copy of the skb pointer to ensure other threads can't change it
|
|
+ * while we're notifying the stack.
|
|
+ */
|
|
+ adapter->ptp_tx_skb = NULL;
|
|
+ clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state);
|
|
+
|
|
+ /* Notify the stack and free the skb after we've unlocked */
|
|
+ skb_tstamp_tx(skb, &shhwtstamps);
|
|
+ dev_kfree_skb_any(skb);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_tx_work
|
|
+ * @work: pointer to work struct
|
|
+ *
|
|
+ * This work function polls the TSYNCTXCTL valid bit to determine when a
|
|
+ * timestamp has been taken for the current stored skb.
|
|
+ */
|
|
+static void igc_ptp_tx_work(struct work_struct *work)
|
|
+{
|
|
+ struct igc_adapter *adapter = container_of(work, struct igc_adapter,
|
|
+ ptp_tx_work);
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 tsynctxctl;
|
|
+
|
|
+ if (!test_bit(__IGC_PTP_TX_IN_PROGRESS, &adapter->state))
|
|
+ return;
|
|
+
|
|
+ tsynctxctl = rd32(IGC_TSYNCTXCTL);
|
|
+ if (WARN_ON_ONCE(!(tsynctxctl & IGC_TSYNCTXCTL_TXTT_0)))
|
|
+ return;
|
|
+
|
|
+ igc_ptp_tx_hwtstamp(adapter);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_set_ts_config - set hardware time stamping config
|
|
+ * @netdev: network interface device structure
|
|
+ * @ifr: interface request data
|
|
+ *
|
|
+ **/
|
|
+int igc_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct hwtstamp_config config;
|
|
+ int err;
|
|
+
|
|
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ err = igc_ptp_set_timestamp_mode(adapter, &config);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ /* save these settings for future reference */
|
|
+ memcpy(&adapter->tstamp_config, &config,
|
|
+ sizeof(adapter->tstamp_config));
|
|
+
|
|
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
|
|
+ -EFAULT : 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_get_ts_config - get hardware time stamping config
|
|
+ * @netdev: network interface device structure
|
|
+ * @ifr: interface request data
|
|
+ *
|
|
+ * Get the hwtstamp_config settings to return to the user. Rather than attempt
|
|
+ * to deconstruct the settings from the registers, just return a shadow copy
|
|
+ * of the last known settings.
|
|
+ **/
|
|
+int igc_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
|
|
+{
|
|
+ struct igc_adapter *adapter = netdev_priv(netdev);
|
|
+ struct hwtstamp_config *config = &adapter->tstamp_config;
|
|
+
|
|
+ return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
|
|
+ -EFAULT : 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_init - Initialize PTP functionality
|
|
+ * @adapter: Board private structure
|
|
+ *
|
|
+ * This function is called at device probe to initialize the PTP
|
|
+ * functionality.
|
|
+ */
|
|
+void igc_ptp_init(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct net_device *netdev = adapter->netdev;
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+
|
|
+ switch (hw->mac.type) {
|
|
+ case igc_i225:
|
|
+ snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr);
|
|
+ adapter->ptp_caps.owner = THIS_MODULE;
|
|
+ adapter->ptp_caps.max_adj = 62499999;
|
|
+ adapter->ptp_caps.adjfine = igc_ptp_adjfine_i225;
|
|
+ adapter->ptp_caps.adjtime = igc_ptp_adjtime_i225;
|
|
+ adapter->ptp_caps.gettimex64 = igc_ptp_gettimex64_i225;
|
|
+ adapter->ptp_caps.settime64 = igc_ptp_settime_i225;
|
|
+ adapter->ptp_caps.enable = igc_ptp_feature_enable_i225;
|
|
+ break;
|
|
+ default:
|
|
+ adapter->ptp_clock = NULL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ spin_lock_init(&adapter->tmreg_lock);
|
|
+ INIT_WORK(&adapter->ptp_tx_work, igc_ptp_tx_work);
|
|
+
|
|
+ adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
|
|
+ adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
|
|
+
|
|
+ adapter->prev_ptp_time = ktime_to_timespec64(ktime_get_real());
|
|
+ adapter->ptp_reset_start = ktime_get();
|
|
+
|
|
+ adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
|
|
+ &adapter->pdev->dev);
|
|
+ if (IS_ERR(adapter->ptp_clock)) {
|
|
+ adapter->ptp_clock = NULL;
|
|
+ netdev_err(netdev, "ptp_clock_register failed\n");
|
|
+ } else if (adapter->ptp_clock) {
|
|
+ netdev_info(netdev, "PHC added\n");
|
|
+ adapter->ptp_flags |= IGC_PTP_ENABLED;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void igc_ptp_time_save(struct igc_adapter *adapter)
|
|
+{
|
|
+ igc_ptp_read(adapter, &adapter->prev_ptp_time);
|
|
+ adapter->ptp_reset_start = ktime_get();
|
|
+}
|
|
+
|
|
+static void igc_ptp_time_restore(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct timespec64 ts = adapter->prev_ptp_time;
|
|
+ ktime_t delta;
|
|
+
|
|
+ delta = ktime_sub(ktime_get(), adapter->ptp_reset_start);
|
|
+
|
|
+ timespec64_add_ns(&ts, ktime_to_ns(delta));
|
|
+
|
|
+ igc_ptp_write_i225(adapter, &ts);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_suspend - Disable PTP work items and prepare for suspend
|
|
+ * @adapter: Board private structure
|
|
+ *
|
|
+ * This function stops the overflow check work and PTP Tx timestamp work, and
|
|
+ * will prepare the device for OS suspend.
|
|
+ */
|
|
+void igc_ptp_suspend(struct igc_adapter *adapter)
|
|
+{
|
|
+ if (!(adapter->ptp_flags & IGC_PTP_ENABLED))
|
|
+ return;
|
|
+
|
|
+ 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);
|
|
+
|
|
+ if (pci_device_is_present(adapter->pdev))
|
|
+ igc_ptp_time_save(adapter);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_stop - Disable PTP device and stop the overflow check.
|
|
+ * @adapter: Board private structure.
|
|
+ *
|
|
+ * This function stops the PTP support and cancels the delayed work.
|
|
+ **/
|
|
+void igc_ptp_stop(struct igc_adapter *adapter)
|
|
+{
|
|
+ igc_ptp_suspend(adapter);
|
|
+
|
|
+ if (adapter->ptp_clock) {
|
|
+ ptp_clock_unregister(adapter->ptp_clock);
|
|
+ netdev_info(adapter->netdev, "PHC removed\n");
|
|
+ adapter->ptp_flags &= ~IGC_PTP_ENABLED;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * igc_ptp_reset - Re-enable the adapter for PTP following a reset.
|
|
+ * @adapter: Board private structure.
|
|
+ *
|
|
+ * This function handles the reset work required to re-enable the PTP device.
|
|
+ **/
|
|
+void igc_ptp_reset(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ unsigned long flags;
|
|
+
|
|
+ /* reset the tstamp_config */
|
|
+ igc_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
|
|
+
|
|
+ spin_lock_irqsave(&adapter->tmreg_lock, flags);
|
|
+
|
|
+ switch (adapter->hw.mac.type) {
|
|
+ case igc_i225:
|
|
+ wr32(IGC_TSAUXC, 0x0);
|
|
+ wr32(IGC_TSSDP, 0x0);
|
|
+ wr32(IGC_TSIM, IGC_TSICR_INTERRUPTS);
|
|
+ wr32(IGC_IMS, IGC_IMS_TS);
|
|
+ break;
|
|
+ default:
|
|
+ /* No work to do. */
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Re-initialize the timer. */
|
|
+ if (hw->mac.type == igc_i225) {
|
|
+ igc_ptp_time_restore(adapter);
|
|
+ } else {
|
|
+ timecounter_init(&adapter->tc, &adapter->cc,
|
|
+ ktime_to_ns(ktime_get_real()));
|
|
+ }
|
|
+out:
|
|
+ spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
|
|
+
|
|
+ wrfl();
|
|
+}
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_regs.h 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_regs.h 2022-03-10 14:33:17.227212778 +0800
|
|
@@ -12,6 +12,7 @@
|
|
#define IGC_MDIC 0x00020 /* MDI Control - RW */
|
|
#define IGC_MDICNFG 0x00E04 /* MDC/MDIO Configuration - RW */
|
|
#define IGC_CONNSW 0x00034 /* Copper/Fiber switch control - RW */
|
|
+#define IGC_I225_PHPM 0x00E14 /* I225 PHY Power Management */
|
|
|
|
/* Internal Packet Buffer Size Registers */
|
|
#define IGC_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */
|
|
@@ -29,10 +30,6 @@
|
|
#define IGC_FCRTL 0x02160 /* FC Receive Threshold Low - RW */
|
|
#define IGC_FCRTH 0x02168 /* FC Receive Threshold High - RW */
|
|
#define IGC_FCRTV 0x02460 /* FC Refresh Timer Value - RW */
|
|
-#define IGC_FCSTS 0x02464 /* FC Status - RO */
|
|
-
|
|
-/* PCIe Register Description */
|
|
-#define IGC_GCR 0x05B00 /* PCIe control- RW */
|
|
|
|
/* Semaphore registers */
|
|
#define IGC_SW_FW_SYNC 0x05B5C /* SW-FW Synchronization - RW */
|
|
@@ -43,6 +40,7 @@
|
|
#define IGC_FACTPS 0x05B30
|
|
|
|
/* Interrupt Register Description */
|
|
+#define IGC_EICR 0x01580 /* Ext. Interrupt Cause read - W0 */
|
|
#define IGC_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */
|
|
#define IGC_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */
|
|
#define IGC_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */
|
|
@@ -60,23 +58,6 @@
|
|
#define IGC_IVAR_MISC 0x01740 /* IVAR for "other" causes - RW */
|
|
#define IGC_GPIE 0x01514 /* General Purpose Intr Enable - RW */
|
|
|
|
-/* Interrupt Cause */
|
|
-#define IGC_ICRXPTC 0x04104 /* Rx Packet Timer Expire Count */
|
|
-#define IGC_ICRXATC 0x04108 /* Rx Absolute Timer Expire Count */
|
|
-#define IGC_ICTXPTC 0x0410C /* Tx Packet Timer Expire Count */
|
|
-#define IGC_ICTXATC 0x04110 /* Tx Absolute Timer Expire Count */
|
|
-#define IGC_ICTXQEC 0x04118 /* Tx Queue Empty Count */
|
|
-#define IGC_ICTXQMTC 0x0411C /* Tx Queue Min Threshold Count */
|
|
-#define IGC_ICRXDMTC 0x04120 /* Rx Descriptor Min Threshold Count */
|
|
-#define IGC_ICRXOC 0x04124 /* Receiver Overrun Count */
|
|
-
|
|
-#define IGC_CBTMPC 0x0402C /* Circuit Breaker TX Packet Count */
|
|
-#define IGC_HTDPMC 0x0403C /* Host Transmit Discarded Packets */
|
|
-#define IGC_CBRMPC 0x040FC /* Circuit Breaker RX Packet Count */
|
|
-#define IGC_RPTHC 0x04104 /* Rx Packets To Host */
|
|
-#define IGC_HGPTC 0x04118 /* Host Good Packets TX Count */
|
|
-#define IGC_HTCBDPC 0x04124 /* Host TX Circ.Breaker Drop Count */
|
|
-
|
|
/* MSI-X Table Register Descriptions */
|
|
#define IGC_PBACL 0x05B68 /* MSIx PBA Clear - R/W 1 to clear */
|
|
|
|
@@ -113,10 +94,11 @@
|
|
#define IGC_RLPML 0x05004 /* Rx Long Packet Max Length */
|
|
#define IGC_RFCTL 0x05008 /* Receive Filter Control*/
|
|
#define IGC_MTA 0x05200 /* Multicast Table Array - RW Array */
|
|
+#define IGC_RA 0x05400 /* Receive Address - RW Array */
|
|
#define IGC_UTA 0x0A000 /* Unicast Table Array - RW */
|
|
#define IGC_RAL(_n) (0x05400 + ((_n) * 0x08))
|
|
#define IGC_RAH(_n) (0x05404 + ((_n) * 0x08))
|
|
-#define IGC_VLAPQF 0x055B0 /* VLAN Priority Queue Filter VLAPQF */
|
|
+#define IGC_VLANPQF 0x055B0 /* VLAN Priority Queue Filter - RW */
|
|
|
|
/* Transmit Register Descriptions */
|
|
#define IGC_TCTL 0x00400 /* Tx Control - RW */
|
|
@@ -132,13 +114,9 @@
|
|
#define IGC_MMDAC 13 /* MMD Access Control */
|
|
#define IGC_MMDAAD 14 /* MMD Access Address/Data */
|
|
|
|
-/* Good transmitted packets counter registers */
|
|
-#define IGC_PQGPTC(_n) (0x010014 + (0x100 * (_n)))
|
|
-
|
|
/* Statistics Register Descriptions */
|
|
#define IGC_CRCERRS 0x04000 /* CRC Error Count - R/clr */
|
|
#define IGC_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */
|
|
-#define IGC_SYMERRS 0x04008 /* Symbol Error Count - R/clr */
|
|
#define IGC_RXERRC 0x0400C /* Receive Error Count - R/clr */
|
|
#define IGC_MPC 0x04010 /* Missed Packet Count - R/clr */
|
|
#define IGC_SCC 0x04014 /* Single Collision Count - R/clr */
|
|
@@ -146,10 +124,10 @@
|
|
#define IGC_MCC 0x0401C /* Multiple Collision Count - R/clr */
|
|
#define IGC_LATECOL 0x04020 /* Late Collision Count - R/clr */
|
|
#define IGC_COLC 0x04028 /* Collision Count - R/clr */
|
|
+#define IGC_RERC 0x0402C /* Receive Error Count - R/clr */
|
|
#define IGC_DC 0x04030 /* Defer Count - R/clr */
|
|
#define IGC_TNCRS 0x04034 /* Tx-No CRS - R/clr */
|
|
-#define IGC_SEC 0x04038 /* Sequence Error Count - R/clr */
|
|
-#define IGC_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */
|
|
+#define IGC_HTDPMC 0x0403C /* Host Transmit Discarded by MAC - R/clr */
|
|
#define IGC_RLEC 0x04040 /* Receive Length Error Count - R/clr */
|
|
#define IGC_XONRXC 0x04048 /* XON Rx Count - R/clr */
|
|
#define IGC_XONTXC 0x0404C /* XON Tx Count - R/clr */
|
|
@@ -193,13 +171,10 @@
|
|
#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 */
|
|
@@ -207,7 +182,41 @@
|
|
#define IGC_HGOTCL 0x04130 /* Host Good Octets Transmit Count Low */
|
|
#define IGC_HGOTCH 0x04134 /* Host Good Octets Transmit Count High */
|
|
#define IGC_LENERRS 0x04138 /* Length Errors Count */
|
|
-#define IGC_HRMPC 0x0A018 /* Header Redirection Missed Packet Count */
|
|
+
|
|
+/* Time sync registers */
|
|
+#define IGC_TSICR 0x0B66C /* Time Sync Interrupt Cause */
|
|
+#define IGC_TSIM 0x0B674 /* Time Sync Interrupt Mask Register */
|
|
+#define IGC_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */
|
|
+#define IGC_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */
|
|
+#define IGC_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */
|
|
+#define IGC_TSYNCRXCFG 0x05F50 /* Time Sync Rx Configuration - RW */
|
|
+#define IGC_TSSDP 0x0003C /* Time Sync SDP Configuration Register - RW */
|
|
+
|
|
+#define IGC_IMIR(_i) (0x05A80 + ((_i) * 4)) /* Immediate Interrupt */
|
|
+#define IGC_IMIREXT(_i) (0x05AA0 + ((_i) * 4)) /* Immediate INTR Ext*/
|
|
+
|
|
+#define IGC_FTQF(_n) (0x059E0 + (4 * (_n))) /* 5-tuple Queue Fltr */
|
|
+
|
|
+/* Transmit Scheduling Registers */
|
|
+#define IGC_TQAVCTRL 0x3570
|
|
+#define IGC_TXQCTL(_n) (0x3344 + 0x4 * (_n))
|
|
+#define IGC_BASET_L 0x3314
|
|
+#define IGC_BASET_H 0x3318
|
|
+#define IGC_QBVCYCLET 0x331C
|
|
+#define IGC_QBVCYCLET_S 0x3320
|
|
+
|
|
+#define IGC_STQT(_n) (0x3324 + 0x4 * (_n))
|
|
+#define IGC_ENDQT(_n) (0x3334 + 0x4 * (_n))
|
|
+#define IGC_DTXMXPKTSZ 0x355C
|
|
+
|
|
+/* System Time Registers */
|
|
+#define IGC_SYSTIML 0x0B600 /* System time register Low - RO */
|
|
+#define IGC_SYSTIMH 0x0B604 /* System time register High - RO */
|
|
+#define IGC_SYSTIMR 0x0B6F8 /* System time register Residue */
|
|
+#define IGC_TIMINCA 0x0B608 /* Increment attributes register - RW */
|
|
+
|
|
+#define IGC_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */
|
|
+#define IGC_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */
|
|
|
|
/* Management registers */
|
|
#define IGC_MANC 0x05820 /* Management Control - RW */
|
|
@@ -215,6 +224,26 @@
|
|
/* Shadow Ram Write Register - RW */
|
|
#define IGC_SRWR 0x12018
|
|
|
|
+/* Wake Up registers */
|
|
+#define IGC_WUC 0x05800 /* Wakeup Control - RW */
|
|
+#define IGC_WUFC 0x05808 /* Wakeup Filter Control - RW */
|
|
+#define IGC_WUS 0x05810 /* Wakeup Status - R/W1C */
|
|
+#define IGC_WUPL 0x05900 /* Wakeup Packet Length - RW */
|
|
+
|
|
+/* 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);
|
|
@@ -223,8 +252,7 @@
|
|
#define wr32(reg, val) \
|
|
do { \
|
|
u8 __iomem *hw_addr = READ_ONCE((hw)->hw_addr); \
|
|
- if (!IGC_REMOVED(hw_addr)) \
|
|
- writel((val), &hw_addr[(reg)]); \
|
|
+ writel((val), &hw_addr[(reg)]); \
|
|
} while (0)
|
|
|
|
#define rd32(reg) (igc_rd32(hw, reg))
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_tsn.c 1970-01-01 08:00:00.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_tsn.c 2022-03-10 14:55:29.084835415 +0800
|
|
@@ -0,0 +1,157 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/* Copyright (c) 2019 Intel Corporation */
|
|
+
|
|
+#include "igc.h"
|
|
+#include "igc_tsn.h"
|
|
+
|
|
+static bool is_any_launchtime(struct igc_adapter *adapter)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
+ struct igc_ring *ring = adapter->tx_ring[i];
|
|
+
|
|
+ if (ring->launchtime_enable)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/* Returns the TSN specific registers to their default values after
|
|
+ * TSN offloading is disabled.
|
|
+ */
|
|
+static int igc_tsn_disable_offload(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 tqavctrl;
|
|
+ int i;
|
|
+
|
|
+ if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED))
|
|
+ return 0;
|
|
+
|
|
+ adapter->cycle_time = 0;
|
|
+
|
|
+ wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
|
|
+ wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT);
|
|
+
|
|
+ tqavctrl = rd32(IGC_TQAVCTRL);
|
|
+ tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
|
|
+ IGC_TQAVCTRL_ENHANCED_QAV);
|
|
+ wr32(IGC_TQAVCTRL, tqavctrl);
|
|
+
|
|
+ for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
+ struct igc_ring *ring = adapter->tx_ring[i];
|
|
+
|
|
+ ring->start_time = 0;
|
|
+ ring->end_time = 0;
|
|
+ ring->launchtime_enable = false;
|
|
+
|
|
+ wr32(IGC_TXQCTL(i), 0);
|
|
+ wr32(IGC_STQT(i), 0);
|
|
+ wr32(IGC_ENDQT(i), NSEC_PER_SEC);
|
|
+ }
|
|
+
|
|
+ wr32(IGC_QBVCYCLET_S, 0);
|
|
+ wr32(IGC_QBVCYCLET, NSEC_PER_SEC);
|
|
+
|
|
+ adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int igc_tsn_enable_offload(struct igc_adapter *adapter)
|
|
+{
|
|
+ struct igc_hw *hw = &adapter->hw;
|
|
+ u32 tqavctrl, baset_l, baset_h;
|
|
+ u32 sec, nsec, cycle;
|
|
+ ktime_t base_time, systim;
|
|
+ int i;
|
|
+
|
|
+ if (adapter->flags & IGC_FLAG_TSN_QBV_ENABLED)
|
|
+ return 0;
|
|
+
|
|
+ cycle = adapter->cycle_time;
|
|
+ base_time = adapter->base_time;
|
|
+
|
|
+ wr32(IGC_TSAUXC, 0);
|
|
+ wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);
|
|
+ wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN);
|
|
+
|
|
+ tqavctrl = rd32(IGC_TQAVCTRL);
|
|
+ tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
|
|
+ wr32(IGC_TQAVCTRL, tqavctrl);
|
|
+
|
|
+ wr32(IGC_QBVCYCLET_S, cycle);
|
|
+ wr32(IGC_QBVCYCLET, cycle);
|
|
+
|
|
+ for (i = 0; i < adapter->num_tx_queues; i++) {
|
|
+ struct igc_ring *ring = adapter->tx_ring[i];
|
|
+ u32 txqctl = 0;
|
|
+
|
|
+ wr32(IGC_STQT(i), ring->start_time);
|
|
+ wr32(IGC_ENDQT(i), ring->end_time);
|
|
+
|
|
+ if (adapter->base_time) {
|
|
+ /* If we have a base_time we are in "taprio"
|
|
+ * mode and we need to be strict about the
|
|
+ * cycles: only transmit a packet if it can be
|
|
+ * completed during that cycle.
|
|
+ */
|
|
+ txqctl |= IGC_TXQCTL_STRICT_CYCLE |
|
|
+ IGC_TXQCTL_STRICT_END;
|
|
+ }
|
|
+
|
|
+ if (ring->launchtime_enable)
|
|
+ txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT;
|
|
+
|
|
+ wr32(IGC_TXQCTL(i), txqctl);
|
|
+ }
|
|
+
|
|
+ nsec = rd32(IGC_SYSTIML);
|
|
+ sec = rd32(IGC_SYSTIMH);
|
|
+
|
|
+ systim = ktime_set(sec, nsec);
|
|
+
|
|
+ if (ktime_compare(systim, base_time) > 0) {
|
|
+ s64 n;
|
|
+
|
|
+ n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
|
|
+ base_time = ktime_add_ns(base_time, (n + 1) * cycle);
|
|
+ }
|
|
+
|
|
+ baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);
|
|
+
|
|
+ wr32(IGC_BASET_H, baset_h);
|
|
+ wr32(IGC_BASET_L, baset_l);
|
|
+
|
|
+ adapter->flags |= IGC_FLAG_TSN_QBV_ENABLED;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int igc_tsn_offload_apply(struct igc_adapter *adapter)
|
|
+{
|
|
+ bool is_any_enabled = adapter->base_time || is_any_launchtime(adapter);
|
|
+
|
|
+ if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) && !is_any_enabled)
|
|
+ return 0;
|
|
+
|
|
+ if (!is_any_enabled) {
|
|
+ int err = igc_tsn_disable_offload(adapter);
|
|
+
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+
|
|
+ /* The BASET registers aren't cleared when writing
|
|
+ * into them, force a reset if the interface is
|
|
+ * running.
|
|
+ */
|
|
+ if (netif_running(adapter->netdev))
|
|
+ schedule_work(&adapter->reset_task);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return igc_tsn_enable_offload(adapter);
|
|
+}
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/igc_tsn.h 1970-01-01 08:00:00.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_tsn.h 2022-03-10 14:33:17.227212778 +0800
|
|
@@ -0,0 +1,9 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+/* Copyright (c) 2020 Intel Corporation */
|
|
+
|
|
+#ifndef _IGC_TSN_H_
|
|
+#define _IGC_TSN_H_
|
|
+
|
|
+int igc_tsn_offload_apply(struct igc_adapter *adapter);
|
|
+
|
|
+#endif /* _IGC_BASE_H */
|
|
|
|
--- a/drivers/net/ethernet/intel/igc/Makefile 2022-03-02 18:41:18.000000000 +0800
|
|
+++ b/drivers/net/ethernet/intel/igc/Makefile 2022-03-10 14:33:17.227212778 +0800
|
|
@@ -8,4 +8,4 @@
|
|
obj-$(CONFIG_IGC) += igc.o
|
|
|
|
igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \
|
|
-igc_ethtool.o
|
|
+igc_diag.o igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o
|