diff options
Diffstat (limited to 'drivers/net/ethernet/intel/igc')
| -rw-r--r-- | drivers/net/ethernet/intel/igc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc.h | 12 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_defines.h | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_dump.c | 323 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ethtool.c | 83 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_main.c | 144 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_ptp.c | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/igc/igc_regs.h | 5 | 
8 files changed, 552 insertions, 25 deletions
diff --git a/drivers/net/ethernet/intel/igc/Makefile b/drivers/net/ethernet/intel/igc/Makefile index 49fb1e1965cd..e3c164c12e10 100644 --- a/drivers/net/ethernet/intel/igc/Makefile +++ b/drivers/net/ethernet/intel/igc/Makefile @@ -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_ptp.o +igc_ethtool.o igc_ptp.o igc_dump.o diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 52066bdbbad0..a1f845a2aa80 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -42,6 +42,10 @@ 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[]; @@ -53,10 +57,13 @@ extern char igc_driver_version[];  /* Interrupt defines */  #define IGC_START_ITR			648 /* ~6000 ints/sec */ + +/* 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) @@ -108,7 +115,7 @@ extern char igc_driver_version[];  #define IGC_RX_HDR_LEN			IGC_RXBUFFER_256  /* Transmit and receive latency (for PTP timestamps) */ -/* FIXME: These values were estimated using the ones that i210 has as +/* FIXME: These values were estimated using the ones that i225 has as   * basis, they seem to provide good numbers with ptp4l/phc2sys, but we   * need to confirm them.   */ @@ -319,7 +326,7 @@ struct igc_q_vector {  	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) @@ -552,6 +559,7 @@ int igc_erase_filter(struct igc_adapter *adapter,  void igc_ptp_init(struct igc_adapter *adapter);  void igc_ptp_reset(struct igc_adapter *adapter); +void igc_ptp_suspend(struct igc_adapter *adapter);  void igc_ptp_stop(struct igc_adapter *adapter);  void igc_ptp_rx_rgtstamp(struct igc_q_vector *q_vector, struct sk_buff *skb);  void igc_ptp_rx_pktstamp(struct igc_q_vector *q_vector, void *va, diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h index 58efa7a02c68..4ddccccf42cc 100644 --- a/drivers/net/ethernet/intel/igc/igc_defines.h +++ b/drivers/net/ethernet/intel/igc/igc_defines.h @@ -16,7 +16,10 @@  /* 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 */ @@ -259,6 +262,9 @@  #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 */ diff --git a/drivers/net/ethernet/intel/igc/igc_dump.c b/drivers/net/ethernet/intel/igc/igc_dump.c new file mode 100644 index 000000000000..657ab50ae296 --- /dev/null +++ b/drivers/net/ethernet/intel/igc/igc_dump.c @@ -0,0 +1,323 @@ +// 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"}, +	{IGC_TDFH, "TDFH"}, +	{IGC_TDFT, "TDFT"}, +	{IGC_TDFHS, "TDFHS"}, +	{IGC_TDFPC, "TDFPC"}, + +	/* List Terminator */ +	{} +}; + +/* igc_regdump - register printout routine */ +static void igc_regdump(struct igc_hw *hw, struct igc_reg_info *reginfo) +{ +	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: +		pr_info("%-15s %08x\n", reginfo->name, rd32(reginfo->ofs)); +		return; +	} + +	snprintf(rname, 16, "%s%s", reginfo->name, "[0-3]"); +	pr_info("%-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; + +	/* Print netdevice Info */ +	if (netdev) { +		dev_info(&adapter->pdev->dev, "Net device Info\n"); +		pr_info("Device Name     state            trans_start\n"); +		pr_info("%-15s %016lX %016lX\n", netdev->name, +			netdev->state, dev_trans_start(netdev)); +	} + +	/* Print TX Ring Summary */ +	if (!netdev || !netif_running(netdev)) +		goto exit; + +	dev_info(&adapter->pdev->dev, "TX Rings Summary\n"); +	pr_info("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]; + +		pr_info(" %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; + +	dev_info(&adapter->pdev->dev, "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]; +		pr_info("------------------------------------\n"); +		pr_info("TX QUEUE INDEX = %d\n", tx_ring->queue_index); +		pr_info("------------------------------------\n"); +		pr_info("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 = ""; + +			pr_info("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: +	dev_info(&adapter->pdev->dev, "RX Rings Summary\n"); +	pr_info("Queue [NTU] [NTC]\n"); +	for (n = 0; n < adapter->num_rx_queues; n++) { +		rx_ring = adapter->rx_ring[n]; +		pr_info(" %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; + +	dev_info(&adapter->pdev->dev, "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]; +		pr_info("------------------------------------\n"); +		pr_info("RX QUEUE INDEX = %d\n", rx_ring->queue_index); +		pr_info("------------------------------------\n"); +		pr_info("R  [desc]      [ PktBuf     A0] [  HeadBuf   DD] [bi->dma       ] [bi->skb] <-- Adv Rx Read format\n"); +		pr_info("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 */ +				pr_info("%s[0x%03X]     %016llX %016llX ---------------- %s\n", +					"RWB", i, +					le64_to_cpu(u0->a), +					le64_to_cpu(u0->b), +					next_desc); +			} else { +				pr_info("%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 */ +	dev_info(&adapter->pdev->dev, "Register Dump\n"); +	pr_info(" Register Name   Value\n"); +	for (reginfo = (struct igc_reg_info *)igc_reg_info_tbl; +	     reginfo->name; reginfo++) { +		igc_regdump(hw, reginfo); +	} +} diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index ee07011e13e9..f530fc29b074 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -308,6 +308,65 @@ static void igc_get_regs(struct net_device *netdev,  		regs_buff[168 + i] = rd32(IGC_TXDCTL(i));  } +static void igc_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_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)  {  	struct igc_adapter *adapter = netdev_priv(netdev); @@ -802,27 +861,6 @@ static int igc_set_coalesce(struct net_device *netdev,  	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) || @@ -1856,9 +1894,12 @@ static int igc_set_link_ksettings(struct net_device *netdev,  }  static const struct ethtool_ops igc_ethtool_ops = { +	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,  	.get_drvinfo		= igc_get_drvinfo,  	.get_regs_len		= igc_get_regs_len,  	.get_regs		= igc_get_regs, +	.get_wol		= igc_get_wol, +	.set_wol		= igc_set_wol,  	.get_msglevel		= igc_get_msglevel,  	.set_msglevel		= igc_set_msglevel,  	.nway_reset		= igc_nway_reset, diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index d9d5425fe8d9..69fa1ce1f927 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -3546,6 +3546,8 @@ static void igc_reset_task(struct work_struct *work)  	adapter = container_of(work, struct igc_adapter, reset_task); +	igc_rings_dump(adapter); +	igc_regs_dump(adapter);  	netdev_err(adapter->netdev, "Reset adapter\n");  	igc_reinit_locked(adapter);  } @@ -4029,6 +4031,9 @@ static void igc_watchdog_task(struct work_struct *work)  		}  	}  	if (link) { +		/* Cancel scheduled suspend requests. */ +		pm_runtime_resume(netdev->dev.parent); +  		if (!netif_carrier_ok(netdev)) {  			u32 ctrl; @@ -4114,6 +4119,8 @@ no_wait:  					return;  				}  			} +			pm_schedule_suspend(netdev->dev.parent, +					    MSEC_PER_SEC * 5);  		/* also check for alternate media here */  		} else if (!netif_carrier_ok(netdev) && @@ -4337,6 +4344,7 @@ request_done:  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; @@ -4348,6 +4356,9 @@ static int __igc_open(struct net_device *netdev, bool resuming)  		return -EBUSY;  	} +	if (!resuming) +		pm_runtime_get_sync(&pdev->dev); +  	netif_carrier_off(netdev);  	/* allocate transmit descriptors */ @@ -4386,6 +4397,9 @@ static int __igc_open(struct net_device *netdev, bool resuming)  	rd32(IGC_ICR);  	igc_irq_enable(adapter); +	if (!resuming) +		pm_runtime_put(&pdev->dev); +  	netif_tx_start_all_queues(netdev);  	/* start the watchdog. */ @@ -4404,6 +4418,8 @@ err_setup_rx:  	igc_free_all_tx_resources(adapter);  err_setup_tx:  	igc_reset(adapter); +	if (!resuming) +		pm_runtime_put(&pdev->dev);  	return err;  } @@ -4428,9 +4444,13 @@ static int igc_open(struct net_device *netdev)  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); @@ -4440,6 +4460,9 @@ static int __igc_close(struct net_device *netdev, bool suspending)  	igc_free_all_tx_resources(adapter);  	igc_free_all_rx_resources(adapter); +	if (!suspending) +		pm_runtime_put_sync(&pdev->dev); +  	return 0;  } @@ -4766,6 +4789,16 @@ static int igc_probe(struct pci_dev *pdev,  	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); +  	/* reset the hardware with the new settings */  	igc_reset(adapter); @@ -4792,6 +4825,10 @@ static int igc_probe(struct pci_dev *pdev,  	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); + +	pm_runtime_put_noidle(&pdev->dev); +  	return 0;  err_register: @@ -4826,6 +4863,8 @@ static void igc_remove(struct pci_dev *pdev)  	struct net_device *netdev = pci_get_drvdata(pdev);  	struct igc_adapter *adapter = netdev_priv(netdev); +	pm_runtime_get_noresume(&pdev->dev); +  	igc_ptp_stop(adapter);  	set_bit(__IGC_DOWN, &adapter->state); @@ -4870,6 +4909,8 @@ static int __igc_shutdown(struct pci_dev *pdev, bool *enable_wake,  	if (netif_running(netdev))  		__igc_close(netdev, true); +	igc_ptp_suspend(adapter); +  	igc_clear_interrupt_scheme(adapter);  	rtnl_unlock(); @@ -5045,6 +5086,108 @@ static void igc_shutdown(struct pci_dev *pdev)  	}  } +/** + *  igc_io_error_detected - called when PCI error is detected + *  @pdev: Pointer to PCI device + *  @state: The current PCI connection state + * + *  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 = pci_get_drvdata(pdev); +	struct igc_adapter *adapter = netdev_priv(netdev); + +	netif_device_detach(netdev); + +	if (state == pci_channel_io_perm_failure) +		return PCI_ERS_RESULT_DISCONNECT; + +	if (netif_running(netdev)) +		igc_down(adapter); +	pci_disable_device(pdev); + +	/* Request a slot reset. */ +	return PCI_ERS_RESULT_NEED_RESET; +} + +/** + *  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; + +	if (pci_enable_device_mem(pdev)) { +		dev_err(&pdev->dev, +			"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); + +		pci_enable_wake(pdev, PCI_D3hot, 0); +		pci_enable_wake(pdev, PCI_D3cold, 0); + +		/* In case of PCI error, adapter loses its HW address +		 * so we should re-assign it here. +		 */ +		hw->hw_addr = adapter->io_addr; + +		igc_reset(adapter); +		wr32(IGC_WUS, ~0); +		result = PCI_ERS_RESULT_RECOVERED; +	} + +	return result; +} + +/** + *  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); + +	rtnl_lock(); +	if (netif_running(netdev)) { +		if (igc_open(netdev)) { +			dev_err(&pdev->dev, "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) @@ -5062,6 +5205,7 @@ static struct pci_driver igc_driver = {  	.driver.pm = &igc_pm_ops,  #endif  	.shutdown = igc_shutdown, +	.err_handler = &igc_err_handler,  };  /** diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 693506587198..f99c514ad0f4 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -509,7 +509,7 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)   * This work function polls the TSYNCTXCTL valid bit to determine when a   * timestamp has been taken for the current stored skb.   */ -void igc_ptp_tx_work(struct work_struct *work) +static void igc_ptp_tx_work(struct work_struct *work)  {  	struct igc_adapter *adapter = container_of(work, struct igc_adapter,  						   ptp_tx_work); diff --git a/drivers/net/ethernet/intel/igc/igc_regs.h b/drivers/net/ethernet/intel/igc/igc_regs.h index c9029b549b90..d4af53a80f11 100644 --- a/drivers/net/ethernet/intel/igc/igc_regs.h +++ b/drivers/net/ethernet/intel/igc/igc_regs.h @@ -17,6 +17,11 @@  /* Internal Packet Buffer Size Registers */  #define IGC_RXPBS		0x02404  /* Rx Packet Buffer Size - RW */  #define IGC_TXPBS		0x03404  /* Tx Packet Buffer Size - RW */ +#define IGC_TDFH		0x03410  /* Tx Data FIFO Head - RW */ +#define IGC_TDFT		0x03418  /* Tx Data FIFO Tail - RW */ +#define IGC_TDFHS		0x03420  /* Tx Data FIFO Head Saved - RW */ +#define IGC_TDFTS		0x03428  /* Tx Data FIFO Tail Saved - RW */ +#define IGC_TDFPC		0x03430  /* Tx Data FIFO Packet Count - RW */  /* NVM  Register Descriptions */  #define IGC_EERD		0x12014  /* EEprom mode read - RW */  |