| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (c) 2018 Intel Corporation */
- #include <linux/delay.h>
- #include "igc_hw.h"
- #include "igc_i225.h"
- #include "igc_mac.h"
- #include "igc_base.h"
- #include "igc.h"
- /**
- * igc_reset_hw_base - Reset hardware
- * @hw: pointer to the HW structure
- *
- * This resets the hardware into a known state. This is a
- * function pointer entry point called by the api module.
- */
- static s32 igc_reset_hw_base(struct igc_hw *hw)
- {
- s32 ret_val;
- u32 ctrl;
- /* Prevent the PCI-E bus from sticking if there is no TLP connection
- * on the last TLP read/write transaction when MAC is reset.
- */
- ret_val = igc_disable_pcie_master(hw);
- if (ret_val)
- hw_dbg("PCI-E Master disable polling has failed\n");
- hw_dbg("Masking off all interrupts\n");
- wr32(IGC_IMC, 0xffffffff);
- wr32(IGC_RCTL, 0);
- wr32(IGC_TCTL, IGC_TCTL_PSP);
- wrfl();
- usleep_range(10000, 20000);
- ctrl = rd32(IGC_CTRL);
- hw_dbg("Issuing a global reset to MAC\n");
- wr32(IGC_CTRL, ctrl | IGC_CTRL_RST);
- ret_val = igc_get_auto_rd_done(hw);
- if (ret_val) {
- /* When auto config read does not complete, do not
- * return with an error. This can happen in situations
- * where there is no eeprom and prevents getting link.
- */
- hw_dbg("Auto Read Done did not complete\n");
- }
- /* Clear any pending interrupt events. */
- wr32(IGC_IMC, 0xffffffff);
- rd32(IGC_ICR);
- return ret_val;
- }
- /**
- * igc_init_nvm_params_base - Init NVM func ptrs.
- * @hw: pointer to the HW structure
- */
- static s32 igc_init_nvm_params_base(struct igc_hw *hw)
- {
- struct igc_nvm_info *nvm = &hw->nvm;
- u32 eecd = rd32(IGC_EECD);
- u16 size;
- /* failed to read reg and got all F's */
- if (!(~eecd))
- return -ENXIO;
- size = FIELD_GET(IGC_EECD_SIZE_EX_MASK, eecd);
- /* Added to a constant, "size" becomes the left-shift value
- * for setting word_size.
- */
- size += NVM_WORD_SIZE_BASE_SHIFT;
- /* Just in case size is out of range, cap it to the largest
- * EEPROM size supported
- */
- if (size > 15)
- size = 15;
- nvm->type = igc_nvm_eeprom_spi;
- nvm->word_size = BIT(size);
- nvm->opcode_bits = 8;
- nvm->delay_usec = 1;
- nvm->page_size = eecd & IGC_EECD_ADDR_BITS ? 32 : 8;
- nvm->address_bits = eecd & IGC_EECD_ADDR_BITS ?
- 16 : 8;
- if (nvm->word_size == BIT(15))
- nvm->page_size = 128;
- return 0;
- }
- /**
- * igc_setup_copper_link_base - Configure copper link settings
- * @hw: pointer to the HW structure
- *
- * Configures the link for auto-neg or forced speed and duplex. Then we check
- * for link, once link is established calls to configure collision distance
- * and flow control are called.
- */
- static s32 igc_setup_copper_link_base(struct igc_hw *hw)
- {
- s32 ret_val = 0;
- u32 ctrl;
- ctrl = rd32(IGC_CTRL);
- ctrl |= IGC_CTRL_SLU;
- ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
- wr32(IGC_CTRL, ctrl);
- ret_val = igc_setup_copper_link(hw);
- return ret_val;
- }
- /**
- * igc_init_mac_params_base - Init MAC func ptrs.
- * @hw: pointer to the HW structure
- */
- static s32 igc_init_mac_params_base(struct igc_hw *hw)
- {
- struct igc_dev_spec_base *dev_spec = &hw->dev_spec._base;
- struct igc_mac_info *mac = &hw->mac;
- /* Set mta register count */
- mac->mta_reg_count = 128;
- mac->rar_entry_count = IGC_RAR_ENTRIES;
- /* reset */
- mac->ops.reset_hw = igc_reset_hw_base;
- mac->ops.acquire_swfw_sync = igc_acquire_swfw_sync_i225;
- mac->ops.release_swfw_sync = igc_release_swfw_sync_i225;
- /* Allow a single clear of the SW semaphore on I225 */
- if (mac->type == igc_i225)
- dev_spec->clear_semaphore_once = true;
- /* physical interface link setup */
- mac->ops.setup_physical_interface = igc_setup_copper_link_base;
- return 0;
- }
- /**
- * igc_init_phy_params_base - Init PHY func ptrs.
- * @hw: pointer to the HW structure
- */
- static s32 igc_init_phy_params_base(struct igc_hw *hw)
- {
- struct igc_phy_info *phy = &hw->phy;
- s32 ret_val = 0;
- phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT_2500;
- phy->reset_delay_us = 100;
- /* set lan id */
- hw->bus.func = FIELD_GET(IGC_STATUS_FUNC_MASK, rd32(IGC_STATUS));
- /* Make sure the PHY is in a good state. Several people have reported
- * firmware leaving the PHY's page select register set to something
- * other than the default of zero, which causes the PHY ID read to
- * access something other than the intended register.
- */
- ret_val = hw->phy.ops.reset(hw);
- if (ret_val) {
- hw_dbg("Error resetting the PHY\n");
- goto out;
- }
- ret_val = igc_get_phy_id(hw);
- if (ret_val)
- return ret_val;
- igc_check_for_copper_link(hw);
- out:
- return ret_val;
- }
- static s32 igc_get_invariants_base(struct igc_hw *hw)
- {
- struct igc_mac_info *mac = &hw->mac;
- s32 ret_val = 0;
- switch (hw->device_id) {
- case IGC_DEV_ID_I225_LM:
- case IGC_DEV_ID_I225_V:
- 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:
- return -IGC_ERR_MAC_INIT;
- }
- hw->phy.media_type = igc_media_type_copper;
- /* mac initialization and operations */
- ret_val = igc_init_mac_params_base(hw);
- if (ret_val)
- goto out;
- /* NVM initialization */
- ret_val = igc_init_nvm_params_base(hw);
- if (ret_val)
- goto out;
- switch (hw->mac.type) {
- case igc_i225:
- ret_val = igc_init_nvm_params_i225(hw);
- break;
- default:
- break;
- }
- /* setup PHY parameters */
- ret_val = igc_init_phy_params_base(hw);
- if (ret_val)
- goto out;
- out:
- return ret_val;
- }
- /**
- * igc_acquire_phy_base - Acquire rights to access PHY
- * @hw: pointer to the HW structure
- *
- * Acquire access rights to the correct PHY. This is a
- * function pointer entry point called by the api module.
- */
- static s32 igc_acquire_phy_base(struct igc_hw *hw)
- {
- u16 mask = IGC_SWFW_PHY0_SM;
- return hw->mac.ops.acquire_swfw_sync(hw, mask);
- }
- /**
- * igc_release_phy_base - Release rights to access PHY
- * @hw: pointer to the HW structure
- *
- * A wrapper to release access rights to the correct PHY. This is a
- * function pointer entry point called by the api module.
- */
- static void igc_release_phy_base(struct igc_hw *hw)
- {
- u16 mask = IGC_SWFW_PHY0_SM;
- hw->mac.ops.release_swfw_sync(hw, mask);
- }
- /**
- * igc_init_hw_base - Initialize hardware
- * @hw: pointer to the HW structure
- *
- * This inits the hardware readying it for operation.
- */
- static s32 igc_init_hw_base(struct igc_hw *hw)
- {
- struct igc_mac_info *mac = &hw->mac;
- u16 i, rar_count = mac->rar_entry_count;
- s32 ret_val = 0;
- /* Setup the receive address */
- igc_init_rx_addrs(hw, rar_count);
- /* Zero out the Multicast HASH table */
- hw_dbg("Zeroing the MTA\n");
- for (i = 0; i < mac->mta_reg_count; i++)
- array_wr32(IGC_MTA, i, 0);
- /* Zero out the Unicast HASH table */
- hw_dbg("Zeroing the UTA\n");
- for (i = 0; i < mac->uta_reg_count; i++)
- array_wr32(IGC_UTA, i, 0);
- /* Setup link and flow control */
- ret_val = igc_setup_link(hw);
- /* Clear all of the statistics registers (clear on read). It is
- * important that we do this after we have tried to establish link
- * because the symbol error count will increment wildly if there
- * is no link.
- */
- igc_clear_hw_cntrs_base(hw);
- return ret_val;
- }
- /**
- * igc_power_down_phy_copper_base - Remove link during PHY power down
- * @hw: pointer to the HW structure
- *
- * In the case of a PHY power down to save power, or to turn off link during a
- * driver unload, or wake on lan is not enabled, remove the link.
- */
- void igc_power_down_phy_copper_base(struct igc_hw *hw)
- {
- /* If the management interface is not enabled, then power down */
- if (!(igc_enable_mng_pass_thru(hw) || igc_check_reset_block(hw)))
- igc_power_down_phy_copper(hw);
- }
- /**
- * igc_rx_fifo_flush_base - Clean rx fifo after Rx enable
- * @hw: pointer to the HW structure
- *
- * After Rx enable, if manageability is enabled then there is likely some
- * bad data at the start of the fifo and possibly in the DMA fifo. This
- * function clears the fifos and flushes any packets that came in as rx was
- * being enabled.
- */
- void igc_rx_fifo_flush_base(struct igc_hw *hw)
- {
- u32 rctl, rlpml, rxdctl[4], rfctl, temp_rctl, rx_enabled;
- int i, ms_wait;
- /* disable IPv6 options as per hardware errata */
- rfctl = rd32(IGC_RFCTL);
- rfctl |= IGC_RFCTL_IPV6_EX_DIS;
- wr32(IGC_RFCTL, rfctl);
- if (!(rd32(IGC_MANC) & IGC_MANC_RCV_TCO_EN))
- return;
- /* Disable all Rx queues */
- for (i = 0; i < 4; i++) {
- rxdctl[i] = rd32(IGC_RXDCTL(i));
- wr32(IGC_RXDCTL(i),
- rxdctl[i] & ~IGC_RXDCTL_QUEUE_ENABLE);
- }
- /* Poll all queues to verify they have shut down */
- for (ms_wait = 0; ms_wait < 10; ms_wait++) {
- usleep_range(1000, 2000);
- rx_enabled = 0;
- for (i = 0; i < 4; i++)
- rx_enabled |= rd32(IGC_RXDCTL(i));
- if (!(rx_enabled & IGC_RXDCTL_QUEUE_ENABLE))
- break;
- }
- if (ms_wait == 10)
- 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
- * any packet that was coming in as RCTL.EN was set is flushed
- */
- wr32(IGC_RFCTL, rfctl & ~IGC_RFCTL_LEF);
- rlpml = rd32(IGC_RLPML);
- wr32(IGC_RLPML, 0);
- rctl = rd32(IGC_RCTL);
- temp_rctl = rctl & ~(IGC_RCTL_EN | IGC_RCTL_SBP);
- temp_rctl |= IGC_RCTL_LPE;
- wr32(IGC_RCTL, temp_rctl);
- wr32(IGC_RCTL, temp_rctl | IGC_RCTL_EN);
- wrfl();
- usleep_range(2000, 3000);
- /* Enable Rx queues that were previously enabled and restore our
- * previous state
- */
- for (i = 0; i < 4; i++)
- wr32(IGC_RXDCTL(i), rxdctl[i]);
- wr32(IGC_RCTL, rctl);
- wrfl();
- wr32(IGC_RLPML, rlpml);
- wr32(IGC_RFCTL, rfctl);
- /* Flush receive errors generated by workaround */
- rd32(IGC_ROC);
- rd32(IGC_RNBC);
- rd32(IGC_MPC);
- }
- bool igc_is_device_id_i225(struct igc_hw *hw)
- {
- switch (hw->device_id) {
- case IGC_DEV_ID_I225_LM:
- case IGC_DEV_ID_I225_V:
- case IGC_DEV_ID_I225_I:
- case IGC_DEV_ID_I225_K:
- case IGC_DEV_ID_I225_K2:
- case IGC_DEV_ID_I225_LMVP:
- case IGC_DEV_ID_I225_IT:
- return true;
- default:
- return false;
- }
- }
- bool igc_is_device_id_i226(struct igc_hw *hw)
- {
- switch (hw->device_id) {
- case IGC_DEV_ID_I226_LM:
- case IGC_DEV_ID_I226_V:
- case IGC_DEV_ID_I226_K:
- case IGC_DEV_ID_I226_IT:
- return true;
- default:
- return false;
- }
- }
- static struct igc_mac_operations igc_mac_ops_base = {
- .init_hw = igc_init_hw_base,
- .check_for_link = igc_check_for_copper_link,
- .rar_set = igc_rar_set,
- .read_mac_addr = igc_read_mac_addr,
- .get_speed_and_duplex = igc_get_speed_and_duplex_copper,
- };
- static const struct igc_phy_operations igc_phy_ops_base = {
- .acquire = igc_acquire_phy_base,
- .release = igc_release_phy_base,
- .reset = igc_phy_hw_reset,
- .read_reg = igc_read_phy_reg_gpy,
- .write_reg = igc_write_phy_reg_gpy,
- };
- const struct igc_info igc_base_info = {
- .get_invariants = igc_get_invariants_base,
- .mac_ops = &igc_mac_ops_base,
- .phy_ops = &igc_phy_ops_base,
- };
|