[v1,08/42] net/txgbe: add HW reset operation
diff mbox series

Message ID 20200901115113.1529675-8-jiawenwu@trustnetic.com
State Superseded
Delegated to: Ferruh Yigit
Headers show
Series
  • [v1,01/42] net/txgbe: add build and doc infrastructure
Related show

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Jiawen Wu Sept. 1, 2020, 11:50 a.m. UTC
Add hardware reset operation.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/txgbe/base/txgbe_hw.c   | 298 +++++++++++++++++++++++++++-
 drivers/net/txgbe/base/txgbe_hw.h   |   5 +
 drivers/net/txgbe/base/txgbe_type.h |  12 ++
 3 files changed, 304 insertions(+), 11 deletions(-)

Patch
diff mbox series

diff --git a/drivers/net/txgbe/base/txgbe_hw.c b/drivers/net/txgbe/base/txgbe_hw.c
index c644de864..9a77adc72 100644
--- a/drivers/net/txgbe/base/txgbe_hw.c
+++ b/drivers/net/txgbe/base/txgbe_hw.c
@@ -5,6 +5,7 @@ 
 #include "txgbe_type.h"
 #include "txgbe_vf.h"
 #include "txgbe_eeprom.h"
+#include "txgbe_mng.h"
 #include "txgbe_hw.h"
 
 /**
@@ -69,18 +70,131 @@  s32 txgbe_init_hw(struct txgbe_hw *hw)
 	return status;
 }
 
+/**
+ *  txgbe_validate_mac_addr - Validate MAC address
+ *  @mac_addr: pointer to MAC address.
+ *
+ *  Tests a MAC address to ensure it is a valid Individual Address.
+ **/
+s32 txgbe_validate_mac_addr(u8 *mac_addr)
+{
+	s32 status = 0;
+
+	DEBUGFUNC("txgbe_validate_mac_addr");
+
+	/* Make sure it is not a multicast address */
+	if (TXGBE_IS_MULTICAST(mac_addr)) {
+		status = TXGBE_ERR_INVALID_MAC_ADDR;
+	/* Not a broadcast address */
+	} else if (TXGBE_IS_BROADCAST(mac_addr)) {
+		status = TXGBE_ERR_INVALID_MAC_ADDR;
+	/* Reject the zero address */
+	} else if (mac_addr[0] == 0 && mac_addr[1] == 0 && mac_addr[2] == 0 &&
+		   mac_addr[3] == 0 && mac_addr[4] == 0 && mac_addr[5] == 0) {
+		status = TXGBE_ERR_INVALID_MAC_ADDR;
+	}
+	return status;
+}
+
 s32 txgbe_set_rar(struct txgbe_hw *hw, u32 index, u8 *addr, u32 vmdq,
 			  u32 enable_addr)
 {
-	RTE_SET_USED(hw);
-	RTE_SET_USED(index);
-	RTE_SET_USED(addr);
-	RTE_SET_USED(vmdq);
-	RTE_SET_USED(enable_addr);
+	u32 rar_low, rar_high;
+	u32 rar_entries = hw->mac.num_rar_entries;
 
-	return 0;
+	DEBUGFUNC("txgbe_set_rar");
+
+	/* Make sure we are using a valid rar index range */
+	if (index >= rar_entries) {
+		DEBUGOUT("RAR index %d is out of range.\n", index);
+		return TXGBE_ERR_INVALID_ARGUMENT;
+	}
+
+	/* setup VMDq pool selection before this RAR gets enabled */
+	hw->mac.set_vmdq(hw, index, vmdq);
+
+	/*
+	 * HW expects these in little endian so we reverse the byte
+	 * order from network order (big endian) to little endian
+	 */
+	rar_low = TXGBE_ETHADDRL_AD0(addr[5]) |
+		  TXGBE_ETHADDRL_AD1(addr[4]) |
+		  TXGBE_ETHADDRL_AD2(addr[3]) |
+		  TXGBE_ETHADDRL_AD3(addr[2]);
+	/*
+	 * Some parts put the VMDq setting in the extra RAH bits,
+	 * so save everything except the lower 16 bits that hold part
+	 * of the address and the address valid bit.
+	 */
+	rar_high = rd32(hw, TXGBE_ETHADDRH);
+	rar_high &= ~TXGBE_ETHADDRH_AD_MASK;
+	rar_high |= (TXGBE_ETHADDRH_AD4(addr[1]) |
+		     TXGBE_ETHADDRH_AD5(addr[0]));
+
+	rar_high &= ~TXGBE_ETHADDRH_VLD;
+	if (enable_addr != 0)
+		rar_high |= TXGBE_ETHADDRH_VLD;
+
+	wr32(hw, TXGBE_ETHADDRIDX, index);
+	wr32(hw, TXGBE_ETHADDRL, rar_low);
+	wr32(hw, TXGBE_ETHADDRH, rar_high);
+}
+
+/**
+ * txgbe_clear_tx_pending - Clear pending TX work from the PCIe fifo
+ * @hw: pointer to the hardware structure
+ *
+ * The MACs can experience issues if TX work is still pending
+ * when a reset occurs.  This function prevents this by flushing the PCIe
+ * buffers on the system.
+ **/
+void txgbe_clear_tx_pending(struct txgbe_hw *hw)
+{
+	u32 hlreg0, i, poll;
+
+	/*
+	 * If double reset is not requested then all transactions should
+	 * already be clear and as such there is no work to do
+	 */
+	if (!(hw->mac.flags & TXGBE_FLAGS_DOUBLE_RESET_REQUIRED))
+		return;
+
+	hlreg0 = rd32(hw, TXGBE_PSRCTL);
+	wr32(hw, TXGBE_PSRCTL, hlreg0 | TXGBE_PSRCTL_LBENA);
+
+	/* Wait for a last completion before clearing buffers */
+	txgbe_flush(hw);
+	msec_delay(3);
+
+	/*
+	 * Before proceeding, make sure that the PCIe block does not have
+	 * transactions pending.
+	 */
+	poll = (800 * 11) / 10;
+	for (i = 0; i < poll; i++) {
+		usec_delay(100);
+	}
+
+	/* Flush all writes and allow 20usec for all transactions to clear */
+	txgbe_flush(hw);
+	usec_delay(20);
+
+	/* restore previous register values */
+	wr32(hw, TXGBE_PSRCTL, hlreg0);
 }
 
+/**
+ *  txgbe_init_shared_code - Initialize the shared code
+ *  @hw: pointer to hardware structure
+ *
+ *  This will assign function pointers and assign the MAC type and PHY code.
+ *  Does not touch the hardware. This function must be called prior to any
+ *  other function in the shared code. The txgbe_hw structure should be
+ *  memset to 0 prior to calling this function.  The following fields in
+ *  hw structure should be filled in prior to calling this function:
+ *  hw_addr, back, device_id, vendor_id, subsystem_device_id,
+ *  subsystem_vendor_id, and revision_id
+ **/
 s32 txgbe_init_shared_code(struct txgbe_hw *hw)
 {
 	s32 status;
@@ -109,7 +223,6 @@  s32 txgbe_init_shared_code(struct txgbe_hw *hw)
 	hw->bus.set_lan_id(hw);
 
 	return status;
-
 }
 
 /**
@@ -172,6 +285,11 @@  s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
 	struct txgbe_mac_info *mac = &hw->mac;
 	struct txgbe_rom_info *rom = &hw->rom;
 
+	/* MAC */
+	mac->init_hw = txgbe_init_hw;
+	mac->start_hw = txgbe_start_hw_raptor;
+	mac->reset_hw = txgbe_reset_hw;
+
 	/* EEPROM */
 	rom->init_params = txgbe_init_eeprom_params;
 	rom->read16 = txgbe_ee_read16;
@@ -186,13 +304,171 @@  s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
 	rom->update_checksum = txgbe_update_eeprom_checksum;
 	rom->calc_checksum = txgbe_calc_eeprom_checksum;
 
-	/* MAC */
-	mac->init_hw = txgbe_init_hw;
-	mac->start_hw = txgbe_start_hw_raptor;
-
 	return 0;
 }
 
+static int
+txgbe_check_flash_load(struct txgbe_hw *hw, u32 check_bit)
+{
+	u32 reg = 0;
+	u32 i;
+	int err = 0;
+	/* if there's flash existing */
+	if (!(rd32(hw, TXGBE_SPISTAT) & TXGBE_SPISTAT_BPFLASH)) {
+		/* wait hw load flash done */
+		for (i = 0; i < 10; i++) {
+			reg = rd32(hw, TXGBE_ILDRSTAT);
+			if (!(reg & check_bit)) {
+				/* done */
+				break;
+			}
+			msleep(100);
+		}
+		if (i == 10) {
+			err = TXGBE_ERR_FLASH_LOADING_FAILED;
+		}
+	}
+	return err;
+}
+
+static void
+txgbe_reset_misc(struct txgbe_hw *hw)
+{
+	RTE_SET_USED(hw);
+}
+
+/**
+ *  txgbe_reset_hw - Perform hardware reset
+ *  @hw: pointer to hardware structure
+ *
+ *  Resets the hardware by resetting the transmit and receive units, masks
+ *  and clears all interrupts, perform a PHY reset, and perform a link (MAC)
+ *  reset.
+ **/
+s32 txgbe_reset_hw(struct txgbe_hw *hw)
+{
+	s32 status;
+	u32 autoc;
+
+	DEBUGFUNC("txgbe_reset_hw");
+
+	/* Call adapter stop to disable tx/rx and clear interrupts */
+	status = hw->mac.stop_hw(hw);
+	if (status != 0)
+		return status;
+
+	/* flush pending Tx transactions */
+	txgbe_clear_tx_pending(hw);
+
+	/* Identify PHY and related function pointers */
+	status = hw->phy.init(hw);
+	if (status == TXGBE_ERR_SFP_NOT_SUPPORTED)
+		return status;
+
+	/* Setup SFP module if there is one present. */
+	if (hw->phy.sfp_setup_needed) {
+		status = hw->mac.setup_sfp(hw);
+		hw->phy.sfp_setup_needed = false;
+	}
+	if (status == TXGBE_ERR_SFP_NOT_SUPPORTED)
+		return status;
+
+	/* Reset PHY */
+	if (hw->phy.reset_disable == false)
+		hw->phy.reset(hw);
+
+	/* remember AUTOC from before we reset */
+	autoc = hw->mac.autoc_read(hw);
+
+mac_reset_top:
+	/*
+	 * Issue global reset to the MAC.  Needs to be SW reset if link is up.
+	 * If link reset is used when link is up, it might reset the PHY when
+	 * mng is using it.  If link is down or the flag to force full link
+	 * reset is set, then perform link reset.
+	 */
+	if (txgbe_mng_present(hw)) {
+		txgbe_hic_reset(hw);
+	} else {
+		wr32(hw, TXGBE_RST, TXGBE_RST_LAN(hw->bus.lan_id));
+		txgbe_flush(hw);
+	}
+	usec_delay(10);
+
+	txgbe_reset_misc(hw);
+
+	if (hw->bus.lan_id == 0) {
+		status = txgbe_check_flash_load(hw,
+				TXGBE_ILDRSTAT_SWRST_LAN0);
+	} else {
+		status = txgbe_check_flash_load(hw,
+				TXGBE_ILDRSTAT_SWRST_LAN1);
+	}
+	if (status != 0)
+		return status;
+
+	msec_delay(50);
+
+	/*
+	 * Double resets are required for recovery from certain error
+	 * conditions.  Between resets, it is necessary to stall to
+	 * allow time for any pending HW events to complete.
+	 */
+	if (hw->mac.flags & TXGBE_FLAGS_DOUBLE_RESET_REQUIRED) {
+		hw->mac.flags &= ~TXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
+		goto mac_reset_top;
+	}
+
+	/*
+	 * Store the original AUTOC/AUTOC2 values if they have not been
+	 * stored off yet.  Otherwise restore the stored original
+	 * values since the reset operation sets back to defaults.
+	 */
+	if (hw->mac.orig_link_settings_stored == false) {
+		hw->mac.orig_autoc = hw->mac.autoc_read(hw);
+		hw->mac.autoc_write(hw, hw->mac.orig_autoc);
+		hw->mac.orig_link_settings_stored = true;
+	} else {
+		hw->mac.orig_autoc = autoc;
+	}
+
+	/* Store the permanent mac address */
+	hw->mac.get_mac_addr(hw, hw->mac.perm_addr);
+
+	/*
+	 * Store MAC address from RAR0, clear receive address registers, and
+	 * clear the multicast table.  Also reset num_rar_entries to 128,
+	 * since we modify this value when programming the SAN MAC address.
+	 */
+	hw->mac.num_rar_entries = 128;
+	hw->mac.init_rx_addrs(hw);
+
+	/* Store the permanent SAN mac address */
+	hw->mac.get_san_mac_addr(hw, hw->mac.san_addr);
+
+	/* Add the SAN MAC address to the RAR only if it's a valid address */
+	if (txgbe_validate_mac_addr(hw->mac.san_addr) == 0) {
+		/* Save the SAN MAC RAR index */
+		hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
+
+		hw->mac.set_rar(hw, hw->mac.san_mac_rar_index,
+				    hw->mac.san_addr, 0, true);
+
+		/* clear VMDq pool/queue selection for this RAR */
+		hw->mac.clear_vmdq(hw, hw->mac.san_mac_rar_index,
+				       BIT_MASK32);
+
+		/* Reserve the last RAR for the SAN MAC address */
+		hw->mac.num_rar_entries--;
+	}
+
+	/* Store the alternative WWNN/WWPN prefix */
+	hw->mac.get_wwn_prefix(hw, &hw->mac.wwnn_prefix,
+				   &hw->mac.wwpn_prefix);
+
+	return status;
+}
+
 /**
  *  txgbe_start_hw_raptor - Prepare hardware for Tx/Rx
  *  @hw: pointer to hardware structure
diff --git a/drivers/net/txgbe/base/txgbe_hw.h b/drivers/net/txgbe/base/txgbe_hw.h
index 55b1b60de..a2816c40a 100644
--- a/drivers/net/txgbe/base/txgbe_hw.h
+++ b/drivers/net/txgbe/base/txgbe_hw.h
@@ -15,7 +15,12 @@  s32 txgbe_start_hw_raptor(struct txgbe_hw *hw);
 
 s32 txgbe_set_rar(struct txgbe_hw *hw, u32 index, u8 *addr, u32 vmdq,
 			  u32 enable_addr);
+
+s32 txgbe_validate_mac_addr(u8 *mac_addr);
+void txgbe_clear_tx_pending(struct txgbe_hw *hw);
 s32 txgbe_init_shared_code(struct txgbe_hw *hw);
 s32 txgbe_set_mac_type(struct txgbe_hw *hw);
 s32 txgbe_init_ops_pf(struct txgbe_hw *hw);
+s32 txgbe_reset_hw(struct txgbe_hw *hw);
+s32 txgbe_start_hw_raptor(struct txgbe_hw *hw);
 #endif /* _TXGBE_HW_H_ */
diff --git a/drivers/net/txgbe/base/txgbe_type.h b/drivers/net/txgbe/base/txgbe_type.h
index 92068b6f7..f8ac41fe9 100644
--- a/drivers/net/txgbe/base/txgbe_type.h
+++ b/drivers/net/txgbe/base/txgbe_type.h
@@ -207,6 +207,7 @@  struct txgbe_flash_info {
 	u16 address_bits;
 };
 
+#define TXGBE_FLAGS_DOUBLE_RESET_REQUIRED	0x01
 struct txgbe_mac_info {
 	s32 (*init_hw)(struct txgbe_hw *);
 	s32 (*reset_hw)(struct txgbe_hw *);
@@ -316,9 +317,18 @@  struct txgbe_mac_info {
 	u8 addr[ETH_ADDR_LEN];
 	u8 perm_addr[ETH_ADDR_LEN];
 	u8 san_addr[ETH_ADDR_LEN];
+	/* prefix for World Wide Node Name (WWNN) */
+	u16 wwnn_prefix;
+	/* prefix for World Wide Port Name (WWPN) */
+	u16 wwpn_prefix;
 
 	u32 num_rar_entries;
+
+	u8  san_mac_rar_index;
+	u64 orig_autoc;  /* cached value of AUTOC */
+	bool orig_link_settings_stored;
 	bool autotry_restart;
+	u8 flags;
 	u32  max_link_up_time;
 };
 
@@ -354,6 +364,8 @@  struct txgbe_phy_info {
 
 	enum txgbe_phy_type type;
 	enum txgbe_sfp_type sfp_type;
+	bool sfp_setup_needed;
+	bool reset_disable;
 };
 
 struct txgbe_mbx_info {