From: Jiawen Wu Date: Mon, 19 Oct 2020 08:53:25 +0000 (+0800) Subject: net/txgbe: add HW init and reset operation X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=f58ae2fcfea623afde2edb125f8a687d7a04fc2e;p=dpdk.git net/txgbe: add HW init and reset operation Add hardware init function and reset operation in mac layer. Signed-off-by: Jiawen Wu Reviewed-by: Ferruh Yigit --- diff --git a/drivers/net/txgbe/base/txgbe_hw.c b/drivers/net/txgbe/base/txgbe_hw.c index e942c5631d..396410342d 100644 --- a/drivers/net/txgbe/base/txgbe_hw.c +++ b/drivers/net/txgbe/base/txgbe_hw.c @@ -4,10 +4,41 @@ #include "txgbe_type.h" #include "txgbe_eeprom.h" +#include "txgbe_mng.h" #include "txgbe_hw.h" #define TXGBE_RAPTOR_RAR_ENTRIES 128 +/** + * txgbe_init_hw - Generic hardware initialization + * @hw: pointer to hardware structure + * + * Initialize the hardware by resetting the hardware, filling the bus info + * structure and media type, clears all on chip counters, initializes receive + * address registers, multicast table, VLAN filter table, calls routine to set + * up link and flow control settings, and leaves transmit and receive units + * disabled and uninitialized + **/ +s32 txgbe_init_hw(struct txgbe_hw *hw) +{ + s32 status; + + DEBUGFUNC("txgbe_init_hw"); + + /* Reset the hardware */ + status = hw->mac.reset_hw(hw); + if (status == 0 || status == TXGBE_ERR_SFP_NOT_PRESENT) { + /* Start the HW */ + status = hw->mac.start_hw(hw); + } + + if (status != 0) + DEBUGOUT("Failed to initialize HW, STATUS = %d\n", status); + + return status; +} + + /** * txgbe_set_lan_id_multi_port - Set LAN id for PCIe multiple port devices * @hw: pointer to the HW structure @@ -33,6 +64,74 @@ void txgbe_set_lan_id_multi_port(struct txgbe_hw *hw) bus->func = bus->lan_id; } +/** + * 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; +} + +/** + * 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 @@ -144,6 +243,8 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw) bus->set_lan_id = txgbe_set_lan_id_multi_port; /* MAC */ + mac->init_hw = txgbe_init_hw; + mac->reset_hw = txgbe_reset_hw; mac->num_rar_entries = TXGBE_RAPTOR_RAR_ENTRIES; /* EEPROM */ @@ -162,3 +263,157 @@ s32 txgbe_init_ops_pf(struct txgbe_hw *hw) 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; +} + +/** + * 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) + 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); + + 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) { + 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; +} + diff --git a/drivers/net/txgbe/base/txgbe_hw.h b/drivers/net/txgbe/base/txgbe_hw.h index f9cf2a8f02..8a9515524a 100644 --- a/drivers/net/txgbe/base/txgbe_hw.h +++ b/drivers/net/txgbe/base/txgbe_hw.h @@ -7,10 +7,14 @@ #include "txgbe_type.h" +s32 txgbe_init_hw(struct txgbe_hw *hw); + void txgbe_set_lan_id_multi_port(struct txgbe_hw *hw); +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); #endif /* _TXGBE_HW_H_ */ diff --git a/drivers/net/txgbe/base/txgbe_type.h b/drivers/net/txgbe/base/txgbe_type.h index c43c39e99b..ee6198603f 100644 --- a/drivers/net/txgbe/base/txgbe_type.h +++ b/drivers/net/txgbe/base/txgbe_type.h @@ -201,6 +201,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 *hw); s32 (*reset_hw)(struct txgbe_hw *hw); @@ -295,7 +296,19 @@ struct txgbe_mac_info { enum txgbe_mac_type type; 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; }; @@ -338,6 +351,8 @@ struct txgbe_phy_info { enum txgbe_phy_type type; enum txgbe_sfp_type sfp_type; + bool sfp_setup_needed; + bool reset_disable; u32 media_type; }; diff --git a/drivers/net/txgbe/txgbe_ethdev.c b/drivers/net/txgbe/txgbe_ethdev.c index 7bafe4123a..7a4c723a25 100644 --- a/drivers/net/txgbe/txgbe_ethdev.c +++ b/drivers/net/txgbe/txgbe_ethdev.c @@ -95,6 +95,41 @@ eth_txgbe_dev_init(struct rte_eth_dev *eth_dev, void *init_params __rte_unused) return -EIO; } + err = hw->mac.init_hw(hw); + + /* + * Devices with copper phys will fail to initialise if txgbe_init_hw() + * is called too soon after the kernel driver unbinding/binding occurs. + * The failure occurs in txgbe_identify_phy() for all devices, + * but for non-copper devies, txgbe_identify_sfp_module() is + * also called. See txgbe_identify_phy(). The reason for the + * failure is not known, and only occuts when virtualisation features + * are disabled in the bios. A delay of 200ms was found to be enough by + * trial-and-error, and is doubled to be safe. + */ + if (err && hw->phy.media_type == txgbe_media_type_copper) { + rte_delay_ms(200); + err = hw->mac.init_hw(hw); + } + + if (err == TXGBE_ERR_SFP_NOT_PRESENT) + err = 0; + + if (err == TXGBE_ERR_EEPROM_VERSION) { + PMD_INIT_LOG(ERR, "This device is a pre-production adapter/" + "LOM. Please be aware there may be issues associated " + "with your hardware."); + PMD_INIT_LOG(ERR, "If you are experiencing problems " + "please contact your hardware representative " + "who provided you with this hardware."); + } else if (err == TXGBE_ERR_SFP_NOT_SUPPORTED) { + PMD_INIT_LOG(ERR, "Unsupported SFP+ Module"); + } + if (err) { + PMD_INIT_LOG(ERR, "Hardware Initialization Failure: %d", err); + return -EIO; + } + /* Allocate memory for storing MAC addresses */ eth_dev->data->mac_addrs = rte_zmalloc("txgbe", RTE_ETHER_ADDR_LEN * hw->mac.num_rar_entries, 0);