Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 208983f0 authored by Sasha Neftin's avatar Sasha Neftin Committed by Jeff Kirsher
Browse files

igc: Add watchdog



Code completion, remove obsolete code
Add watchdog methods

Signed-off-by: default avatarSasha Neftin <sasha.neftin@intel.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 4eb80801
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -33,6 +33,8 @@ extern char igc_driver_version[];
#define IGC_FLAG_HAS_MSI		BIT(0)
#define IGC_FLAG_HAS_MSI		BIT(0)
#define IGC_FLAG_QUEUE_PAIRS		BIT(4)
#define IGC_FLAG_QUEUE_PAIRS		BIT(4)
#define IGC_FLAG_NEED_LINK_UPDATE	BIT(9)
#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_HAS_MSIX		BIT(13)
#define IGC_FLAG_VLAN_PROMISC		BIT(15)
#define IGC_FLAG_VLAN_PROMISC		BIT(15)


@@ -290,6 +292,7 @@ struct igc_adapter {


	/* TX */
	/* TX */
	u16 tx_work_limit;
	u16 tx_work_limit;
	u32 tx_timeout_count;
	int num_tx_queues;
	int num_tx_queues;
	struct igc_ring *tx_ring[IGC_MAX_TX_QUEUES];
	struct igc_ring *tx_ring[IGC_MAX_TX_QUEUES];


@@ -348,6 +351,7 @@ struct igc_adapter {


	struct igc_mac_addr *mac_table;
	struct igc_mac_addr *mac_table;


	unsigned long link_check_timeout;
	struct igc_info ei;
	struct igc_info ei;
};
};


@@ -417,6 +421,14 @@ static inline unsigned int igc_rx_pg_order(struct igc_ring *ring)
	return 0;
	return 0;
}
}


static inline s32 igc_read_phy_reg(struct igc_hw *hw, u32 offset, u16 *data)
{
	if (hw->phy.ops.read_reg)
		return hw->phy.ops.read_reg(hw, offset, data);

	return 0;
}

#define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring))
#define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring))


#define IGC_TXD_DCMD	(IGC_ADVTXD_DCMD_EOP | IGC_ADVTXD_DCMD_RS)
#define IGC_TXD_DCMD	(IGC_ADVTXD_DCMD_EOP | IGC_ADVTXD_DCMD_RS)
+11 −0
Original line number Original line Diff line number Diff line
@@ -66,6 +66,8 @@
#define IGC_CTRL_RFCE		0x08000000  /* Receive Flow Control enable */
#define IGC_CTRL_RFCE		0x08000000  /* Receive Flow Control enable */
#define IGC_CTRL_TFCE		0x10000000  /* Transmit flow control enable */
#define IGC_CTRL_TFCE		0x10000000  /* Transmit flow control enable */


#define IGC_CONNSW_AUTOSENSE_EN	0x1

/* PBA constants */
/* PBA constants */
#define IGC_PBA_34K		0x0022
#define IGC_PBA_34K		0x0022


@@ -94,6 +96,10 @@
#define CR_1000T_HD_CAPS	0x0100 /* Advertise 1000T HD capability */
#define CR_1000T_HD_CAPS	0x0100 /* Advertise 1000T HD capability */
#define CR_1000T_FD_CAPS	0x0200 /* Advertise 1000T FD capability  */
#define CR_1000T_FD_CAPS	0x0200 /* Advertise 1000T FD capability  */


/* 1000BASE-T Status Register */
#define SR_1000T_REMOTE_RX_STATUS	0x1000 /* Remote receiver OK */
#define SR_1000T_LOCAL_RX_STATUS	0x2000 /* Local receiver OK */

/* PHY GPY 211 registers */
/* PHY GPY 211 registers */
#define STANDARD_AN_REG_MASK	0x0007 /* MMD */
#define STANDARD_AN_REG_MASK	0x0007 /* MMD */
#define ANEG_MULTIGBT_AN_CTRL	0x0020 /* MULTI GBT AN Control Register */
#define ANEG_MULTIGBT_AN_CTRL	0x0020 /* MULTI GBT AN Control Register */
@@ -210,6 +216,11 @@
#define IGC_QVECTOR_MASK	0x7FFC		/* Q-vector mask */
#define IGC_QVECTOR_MASK	0x7FFC		/* Q-vector mask */
#define IGC_ITR_VAL_MASK	0x04		/* ITR value mask */
#define IGC_ITR_VAL_MASK	0x04		/* ITR value mask */


/* 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_ICR_DOUTSYNC	0x10000000 /* NIC DMA out of sync */
#define IGC_EITR_CNT_IGNR	0x80000000 /* Don't reset counters on write */
#define IGC_EITR_CNT_IGNR	0x80000000 /* Don't reset counters on write */
#define IGC_IVAR_VALID		0x80
#define IGC_IVAR_VALID		0x80
+1 −0
Original line number Original line Diff line number Diff line
@@ -197,6 +197,7 @@ struct igc_dev_spec_base {
	bool clear_semaphore_once;
	bool clear_semaphore_once;
	bool module_plugged;
	bool module_plugged;
	u8 media_port;
	u8 media_port;
	bool mas_capable;
};
};


struct igc_hw {
struct igc_hw {
+232 −0
Original line number Original line Diff line number Diff line
@@ -1743,6 +1743,7 @@ static void igc_up(struct igc_adapter *adapter)


	/* start the watchdog. */
	/* start the watchdog. */
	hw->mac.get_link_status = 1;
	hw->mac.get_link_status = 1;
	schedule_work(&adapter->watchdog_task);
}
}


/**
/**
@@ -2297,6 +2298,55 @@ static void igc_free_q_vector(struct igc_adapter *adapter, int v_idx)
		kfree_rcu(q_vector, rcu);
		kfree_rcu(q_vector, rcu);
}
}


/* Need to wait a few seconds after link up to get diagnostic information from
 * the phy
 */
static void igc_update_phy_info(struct timer_list *t)
{
	struct igc_adapter *adapter = from_timer(adapter, t, phy_info_timer);

	igc_get_phy_info(&adapter->hw);
}

/**
 * igc_has_link - check shared code for link and determine up/down
 * @adapter: pointer to driver private info
 */
static bool igc_has_link(struct igc_adapter *adapter)
{
	struct igc_hw *hw = &adapter->hw;
	bool link_active = false;

	/* get_link_status is set on LSC (link status) interrupt or
	 * rx sequence error interrupt.  get_link_status will stay
	 * 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.type == igc_i225 &&
	    hw->phy.id == I225_I_PHY_ID) {
		if (!netif_carrier_ok(adapter->netdev)) {
			adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
		} else if (!(adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)) {
			adapter->flags |= IGC_FLAG_NEED_LINK_UPDATE;
			adapter->link_check_timeout = jiffies;
		}
	}

	return link_active;
}

/**
/**
 * igc_watchdog - Timer Call-back
 * igc_watchdog - Timer Call-back
 * @data: pointer to adapter cast into an unsigned long
 * @data: pointer to adapter cast into an unsigned long
@@ -2304,6 +2354,183 @@ static void igc_free_q_vector(struct igc_adapter *adapter, int v_idx)
static void igc_watchdog(struct timer_list *t)
static void igc_watchdog(struct timer_list *t)
{
{
	struct igc_adapter *adapter = from_timer(adapter, t, watchdog_timer);
	struct igc_adapter *adapter = from_timer(adapter, t, watchdog_timer);
	/* Do the rest outside of interrupt context */
	schedule_work(&adapter->watchdog_task);
}

static void igc_watchdog_task(struct work_struct *work)
{
	struct igc_adapter *adapter = container_of(work,
						   struct igc_adapter,
						   watchdog_task);
	struct net_device *netdev = adapter->netdev;
	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;

	link = igc_has_link(adapter);

	if (adapter->flags & IGC_FLAG_NEED_LINK_UPDATE) {
		if (time_after(jiffies, (adapter->link_check_timeout + HZ)))
			adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
		else
			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) {
		if (!netif_carrier_ok(netdev)) {
			u32 ctrl;

			hw->mac.ops.get_speed_and_duplex(hw,
							 &adapter->link_speed,
							 &adapter->link_duplex);

			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,
				    adapter->link_speed,
				    adapter->link_duplex == FULL_DUPLEX ?
				    "Full" : "Half",
				    (ctrl & IGC_CTRL_TFCE) &&
				    (ctrl & IGC_CTRL_RFCE) ? "RX/TX" :
				    (ctrl & IGC_CTRL_RFCE) ?  "RX" :
				    (ctrl & IGC_CTRL_TFCE) ?  "TX" : "None");

			/* check if SmartSpeed worked */
			igc_check_downshift(hw);
			if (phy->speed_downgraded)
				netdev_warn(netdev, "Link Speed was downgraded by SmartSpeed\n");

			/* adjust timeout factor according to speed/duplex */
			adapter->tx_timeout_factor = 1;
			switch (adapter->link_speed) {
			case SPEED_10:
				adapter->tx_timeout_factor = 14;
				break;
			case SPEED_100:
				/* maybe add some timeout factor ? */
				break;
			}

			if (adapter->link_speed != SPEED_1000)
				goto no_wait;

			/* wait for Remote receiver status OK */
retry_read_status:
			if (!igc_read_phy_reg(hw, PHY_1000T_STATUS,
					      &phy_data)) {
				if (!(phy_data & SR_1000T_REMOTE_RX_STATUS) &&
				    retry_count) {
					msleep(100);
					retry_count--;
					goto retry_read_status;
				} else if (!retry_count) {
					dev_err(&adapter->pdev->dev, "exceed max 2 second\n");
				}
			} else {
				dev_err(&adapter->pdev->dev, "read 1000Base-T Status Reg\n");
			}
no_wait:
			netif_carrier_on(netdev);

			/* link state has changed, schedule phy info update */
			if (!test_bit(__IGC_DOWN, &adapter->state))
				mod_timer(&adapter->phy_info_timer,
					  round_jiffies(jiffies + 2 * HZ));
		}
	} else {
		if (netif_carrier_ok(netdev)) {
			adapter->link_speed = 0;
			adapter->link_duplex = 0;

			/* Links status message must follow this format */
			netdev_info(netdev, "igc: %s NIC Link is Down\n",
				    netdev->name);
			netif_carrier_off(netdev);

			/* link state has changed, schedule phy info update */
			if (!test_bit(__IGC_DOWN, &adapter->state))
				mod_timer(&adapter->phy_info_timer,
					  round_jiffies(jiffies + 2 * HZ));

			/* link is down, time to check for alternate media */
			if (adapter->flags & IGC_FLAG_MAS_ENABLE) {
				if (adapter->flags & IGC_FLAG_MEDIA_RESET) {
					schedule_work(&adapter->reset_task);
					/* return immediately */
					return;
				}
			}

		/* also check for alternate media here */
		} else if (!netif_carrier_ok(netdev) &&
			   (adapter->flags & IGC_FLAG_MAS_ENABLE)) {
			if (adapter->flags & IGC_FLAG_MEDIA_RESET) {
				schedule_work(&adapter->reset_task);
				/* return immediately */
				return;
			}
		}
	}

	spin_lock(&adapter->stats64_lock);
	igc_update_stats(adapter);
	spin_unlock(&adapter->stats64_lock);

	for (i = 0; i < adapter->num_tx_queues; i++) {
		struct igc_ring *tx_ring = adapter->tx_ring[i];

		if (!netif_carrier_ok(netdev)) {
			/* We've lost link, so the controller stops DMA,
			 * but we've got queued Tx work that's never going
			 * to get done, so reset controller to flush Tx.
			 * (Do the reset outside of interrupt context).
			 */
			if (igc_desc_unused(tx_ring) + 1 < tx_ring->count) {
				adapter->tx_timeout_count++;
				schedule_work(&adapter->reset_task);
				/* return immediately since reset is imminent */
				return;
			}
		}

		/* Force detection of hung controller every watchdog period */
		set_bit(IGC_RING_FLAG_TX_DETECT_HANG, &tx_ring->flags);
	}

	/* Cause software interrupt to ensure Rx ring is cleaned */
	if (adapter->flags & IGC_FLAG_HAS_MSIX) {
		u32 eics = 0;

		for (i = 0; i < adapter->num_q_vectors; i++)
			eics |= adapter->q_vector[i]->eims_value;
		wr32(IGC_EICS, eics);
	} else {
		wr32(IGC_ICS, IGC_ICS_RXDMT0);
	}

	/* Reset the timer */
	if (!test_bit(__IGC_DOWN, &adapter->state)) {
		if (adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)
			mod_timer(&adapter->watchdog_timer,
				  round_jiffies(jiffies +  HZ));
		else
			mod_timer(&adapter->watchdog_timer,
				  round_jiffies(jiffies + 2 * HZ));
	}
}
}


/**
/**
@@ -3152,6 +3379,7 @@ static int __igc_open(struct net_device *netdev, bool resuming)


	/* start the watchdog. */
	/* start the watchdog. */
	hw->mac.get_link_status = 1;
	hw->mac.get_link_status = 1;
	schedule_work(&adapter->watchdog_task);


	return IGC_SUCCESS;
	return IGC_SUCCESS;


@@ -3427,8 +3655,10 @@ static int igc_probe(struct pci_dev *pdev,
	wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
	wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);


	timer_setup(&adapter->watchdog_timer, igc_watchdog, 0);
	timer_setup(&adapter->watchdog_timer, igc_watchdog, 0);
	timer_setup(&adapter->phy_info_timer, igc_update_phy_info, 0);


	INIT_WORK(&adapter->reset_task, igc_reset_task);
	INIT_WORK(&adapter->reset_task, igc_reset_task);
	INIT_WORK(&adapter->watchdog_task, igc_watchdog_task);


	/* Initialize link properties that are user-changeable */
	/* Initialize link properties that are user-changeable */
	adapter->fc_autoneg = true;
	adapter->fc_autoneg = true;
@@ -3499,8 +3729,10 @@ static void igc_remove(struct pci_dev *pdev)
	set_bit(__IGC_DOWN, &adapter->state);
	set_bit(__IGC_DOWN, &adapter->state);


	del_timer_sync(&adapter->watchdog_timer);
	del_timer_sync(&adapter->watchdog_timer);
	del_timer_sync(&adapter->phy_info_timer);


	cancel_work_sync(&adapter->reset_task);
	cancel_work_sync(&adapter->reset_task);
	cancel_work_sync(&adapter->watchdog_task);


	/* Release control of h/w to f/w.  If f/w is AMT enabled, this
	/* Release control of h/w to f/w.  If f/w is AMT enabled, this
	 * would have already happened in close and is redundant.
	 * would have already happened in close and is redundant.