162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2007 - 2018 Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* e1000_i210 562306a36Sopenharmony_ci * e1000_i211 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/if_ether.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "e1000_hw.h" 1262306a36Sopenharmony_ci#include "e1000_i210.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic s32 igb_update_flash_i210(struct e1000_hw *hw); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/** 1762306a36Sopenharmony_ci * igb_get_hw_semaphore_i210 - Acquire hardware semaphore 1862306a36Sopenharmony_ci * @hw: pointer to the HW structure 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Acquire the HW semaphore to access the PHY or NVM 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic s32 igb_get_hw_semaphore_i210(struct e1000_hw *hw) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci u32 swsm; 2562306a36Sopenharmony_ci s32 timeout = hw->nvm.word_size + 1; 2662306a36Sopenharmony_ci s32 i = 0; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci /* Get the SW semaphore */ 2962306a36Sopenharmony_ci while (i < timeout) { 3062306a36Sopenharmony_ci swsm = rd32(E1000_SWSM); 3162306a36Sopenharmony_ci if (!(swsm & E1000_SWSM_SMBI)) 3262306a36Sopenharmony_ci break; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci udelay(50); 3562306a36Sopenharmony_ci i++; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (i == timeout) { 3962306a36Sopenharmony_ci /* In rare circumstances, the SW semaphore may already be held 4062306a36Sopenharmony_ci * unintentionally. Clear the semaphore once before giving up. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci if (hw->dev_spec._82575.clear_semaphore_once) { 4362306a36Sopenharmony_ci hw->dev_spec._82575.clear_semaphore_once = false; 4462306a36Sopenharmony_ci igb_put_hw_semaphore(hw); 4562306a36Sopenharmony_ci for (i = 0; i < timeout; i++) { 4662306a36Sopenharmony_ci swsm = rd32(E1000_SWSM); 4762306a36Sopenharmony_ci if (!(swsm & E1000_SWSM_SMBI)) 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci udelay(50); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* If we do not have the semaphore here, we have to give up. */ 5562306a36Sopenharmony_ci if (i == timeout) { 5662306a36Sopenharmony_ci hw_dbg("Driver can't access device - SMBI bit is set.\n"); 5762306a36Sopenharmony_ci return -E1000_ERR_NVM; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Get the FW semaphore. */ 6262306a36Sopenharmony_ci for (i = 0; i < timeout; i++) { 6362306a36Sopenharmony_ci swsm = rd32(E1000_SWSM); 6462306a36Sopenharmony_ci wr32(E1000_SWSM, swsm | E1000_SWSM_SWESMBI); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Semaphore acquired if bit latched */ 6762306a36Sopenharmony_ci if (rd32(E1000_SWSM) & E1000_SWSM_SWESMBI) 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci udelay(50); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (i == timeout) { 7462306a36Sopenharmony_ci /* Release semaphores */ 7562306a36Sopenharmony_ci igb_put_hw_semaphore(hw); 7662306a36Sopenharmony_ci hw_dbg("Driver can't access the NVM\n"); 7762306a36Sopenharmony_ci return -E1000_ERR_NVM; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * igb_acquire_nvm_i210 - Request for access to EEPROM 8562306a36Sopenharmony_ci * @hw: pointer to the HW structure 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Acquire the necessary semaphores for exclusive access to the EEPROM. 8862306a36Sopenharmony_ci * Set the EEPROM access request bit and wait for EEPROM access grant bit. 8962306a36Sopenharmony_ci * Return successful if access grant bit set, else clear the request for 9062306a36Sopenharmony_ci * EEPROM access and return -E1000_ERR_NVM (-1). 9162306a36Sopenharmony_ci **/ 9262306a36Sopenharmony_cistatic s32 igb_acquire_nvm_i210(struct e1000_hw *hw) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci return igb_acquire_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * igb_release_nvm_i210 - Release exclusive access to EEPROM 9962306a36Sopenharmony_ci * @hw: pointer to the HW structure 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Stop any current commands to the EEPROM and clear the EEPROM request bit, 10262306a36Sopenharmony_ci * then release the semaphores acquired. 10362306a36Sopenharmony_ci **/ 10462306a36Sopenharmony_cistatic void igb_release_nvm_i210(struct e1000_hw *hw) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci igb_release_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/** 11062306a36Sopenharmony_ci * igb_acquire_swfw_sync_i210 - Acquire SW/FW semaphore 11162306a36Sopenharmony_ci * @hw: pointer to the HW structure 11262306a36Sopenharmony_ci * @mask: specifies which semaphore to acquire 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Acquire the SW/FW semaphore to access the PHY or NVM. The mask 11562306a36Sopenharmony_ci * will also specify which port we're acquiring the lock for. 11662306a36Sopenharmony_ci **/ 11762306a36Sopenharmony_cis32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci u32 swfw_sync; 12062306a36Sopenharmony_ci u32 swmask = mask; 12162306a36Sopenharmony_ci u32 fwmask = mask << 16; 12262306a36Sopenharmony_ci s32 ret_val = 0; 12362306a36Sopenharmony_ci s32 i = 0, timeout = 200; /* FIXME: find real value to use here */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci while (i < timeout) { 12662306a36Sopenharmony_ci if (igb_get_hw_semaphore_i210(hw)) { 12762306a36Sopenharmony_ci ret_val = -E1000_ERR_SWFW_SYNC; 12862306a36Sopenharmony_ci goto out; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci swfw_sync = rd32(E1000_SW_FW_SYNC); 13262306a36Sopenharmony_ci if (!(swfw_sync & (fwmask | swmask))) 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Firmware currently using resource (fwmask) */ 13662306a36Sopenharmony_ci igb_put_hw_semaphore(hw); 13762306a36Sopenharmony_ci mdelay(5); 13862306a36Sopenharmony_ci i++; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (i == timeout) { 14262306a36Sopenharmony_ci hw_dbg("Driver can't access resource, SW_FW_SYNC timeout.\n"); 14362306a36Sopenharmony_ci ret_val = -E1000_ERR_SWFW_SYNC; 14462306a36Sopenharmony_ci goto out; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci swfw_sync |= swmask; 14862306a36Sopenharmony_ci wr32(E1000_SW_FW_SYNC, swfw_sync); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci igb_put_hw_semaphore(hw); 15162306a36Sopenharmony_ciout: 15262306a36Sopenharmony_ci return ret_val; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/** 15662306a36Sopenharmony_ci * igb_release_swfw_sync_i210 - Release SW/FW semaphore 15762306a36Sopenharmony_ci * @hw: pointer to the HW structure 15862306a36Sopenharmony_ci * @mask: specifies which semaphore to acquire 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * Release the SW/FW semaphore used to access the PHY or NVM. The mask 16162306a36Sopenharmony_ci * will also specify which port we're releasing the lock for. 16262306a36Sopenharmony_ci **/ 16362306a36Sopenharmony_civoid igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci u32 swfw_sync; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci while (igb_get_hw_semaphore_i210(hw)) 16862306a36Sopenharmony_ci ; /* Empty */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci swfw_sync = rd32(E1000_SW_FW_SYNC); 17162306a36Sopenharmony_ci swfw_sync &= ~mask; 17262306a36Sopenharmony_ci wr32(E1000_SW_FW_SYNC, swfw_sync); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci igb_put_hw_semaphore(hw); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * igb_read_nvm_srrd_i210 - Reads Shadow Ram using EERD register 17962306a36Sopenharmony_ci * @hw: pointer to the HW structure 18062306a36Sopenharmony_ci * @offset: offset of word in the Shadow Ram to read 18162306a36Sopenharmony_ci * @words: number of words to read 18262306a36Sopenharmony_ci * @data: word read from the Shadow Ram 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Reads a 16 bit word from the Shadow Ram using the EERD register. 18562306a36Sopenharmony_ci * Uses necessary synchronization semaphores. 18662306a36Sopenharmony_ci **/ 18762306a36Sopenharmony_cistatic s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words, 18862306a36Sopenharmony_ci u16 *data) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci s32 status = 0; 19162306a36Sopenharmony_ci u16 i, count; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* We cannot hold synchronization semaphores for too long, 19462306a36Sopenharmony_ci * because of forceful takeover procedure. However it is more efficient 19562306a36Sopenharmony_ci * to read in bursts than synchronizing access for each word. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { 19862306a36Sopenharmony_ci count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? 19962306a36Sopenharmony_ci E1000_EERD_EEWR_MAX_COUNT : (words - i); 20062306a36Sopenharmony_ci if (!(hw->nvm.ops.acquire(hw))) { 20162306a36Sopenharmony_ci status = igb_read_nvm_eerd(hw, offset, count, 20262306a36Sopenharmony_ci data + i); 20362306a36Sopenharmony_ci hw->nvm.ops.release(hw); 20462306a36Sopenharmony_ci } else { 20562306a36Sopenharmony_ci status = E1000_ERR_SWFW_SYNC; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (status) 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return status; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/** 21662306a36Sopenharmony_ci * igb_write_nvm_srwr - Write to Shadow Ram using EEWR 21762306a36Sopenharmony_ci * @hw: pointer to the HW structure 21862306a36Sopenharmony_ci * @offset: offset within the Shadow Ram to be written to 21962306a36Sopenharmony_ci * @words: number of words to write 22062306a36Sopenharmony_ci * @data: 16 bit word(s) to be written to the Shadow Ram 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * Writes data to Shadow Ram at offset using EEWR register. 22362306a36Sopenharmony_ci * 22462306a36Sopenharmony_ci * If igb_update_nvm_checksum is not called after this function , the 22562306a36Sopenharmony_ci * Shadow Ram will most likely contain an invalid checksum. 22662306a36Sopenharmony_ci **/ 22762306a36Sopenharmony_cistatic s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, 22862306a36Sopenharmony_ci u16 *data) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct e1000_nvm_info *nvm = &hw->nvm; 23162306a36Sopenharmony_ci u32 i, k, eewr = 0; 23262306a36Sopenharmony_ci u32 attempts = 100000; 23362306a36Sopenharmony_ci s32 ret_val = 0; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* A check for invalid values: offset too large, too many words, 23662306a36Sopenharmony_ci * too many words for the offset, and not enough words. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || 23962306a36Sopenharmony_ci (words == 0)) { 24062306a36Sopenharmony_ci hw_dbg("nvm parameter(s) out of bounds\n"); 24162306a36Sopenharmony_ci ret_val = -E1000_ERR_NVM; 24262306a36Sopenharmony_ci goto out; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (i = 0; i < words; i++) { 24662306a36Sopenharmony_ci eewr = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) | 24762306a36Sopenharmony_ci (data[i] << E1000_NVM_RW_REG_DATA) | 24862306a36Sopenharmony_ci E1000_NVM_RW_REG_START; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci wr32(E1000_SRWR, eewr); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for (k = 0; k < attempts; k++) { 25362306a36Sopenharmony_ci if (E1000_NVM_RW_REG_DONE & 25462306a36Sopenharmony_ci rd32(E1000_SRWR)) { 25562306a36Sopenharmony_ci ret_val = 0; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci udelay(5); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (ret_val) { 26262306a36Sopenharmony_ci hw_dbg("Shadow RAM write EEWR timed out\n"); 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciout: 26862306a36Sopenharmony_ci return ret_val; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/** 27262306a36Sopenharmony_ci * igb_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR 27362306a36Sopenharmony_ci * @hw: pointer to the HW structure 27462306a36Sopenharmony_ci * @offset: offset within the Shadow RAM to be written to 27562306a36Sopenharmony_ci * @words: number of words to write 27662306a36Sopenharmony_ci * @data: 16 bit word(s) to be written to the Shadow RAM 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * Writes data to Shadow RAM at offset using EEWR register. 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * If e1000_update_nvm_checksum is not called after this function , the 28162306a36Sopenharmony_ci * data will not be committed to FLASH and also Shadow RAM will most likely 28262306a36Sopenharmony_ci * contain an invalid checksum. 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * If error code is returned, data and Shadow RAM may be inconsistent - buffer 28562306a36Sopenharmony_ci * partially written. 28662306a36Sopenharmony_ci **/ 28762306a36Sopenharmony_cistatic s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, 28862306a36Sopenharmony_ci u16 *data) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci s32 status = 0; 29162306a36Sopenharmony_ci u16 i, count; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* We cannot hold synchronization semaphores for too long, 29462306a36Sopenharmony_ci * because of forceful takeover procedure. However it is more efficient 29562306a36Sopenharmony_ci * to write in bursts than synchronizing access for each word. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { 29862306a36Sopenharmony_ci count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? 29962306a36Sopenharmony_ci E1000_EERD_EEWR_MAX_COUNT : (words - i); 30062306a36Sopenharmony_ci if (!(hw->nvm.ops.acquire(hw))) { 30162306a36Sopenharmony_ci status = igb_write_nvm_srwr(hw, offset, count, 30262306a36Sopenharmony_ci data + i); 30362306a36Sopenharmony_ci hw->nvm.ops.release(hw); 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci status = E1000_ERR_SWFW_SYNC; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (status) 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return status; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/** 31662306a36Sopenharmony_ci * igb_read_invm_word_i210 - Reads OTP 31762306a36Sopenharmony_ci * @hw: pointer to the HW structure 31862306a36Sopenharmony_ci * @address: the word address (aka eeprom offset) to read 31962306a36Sopenharmony_ci * @data: pointer to the data read 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * Reads 16-bit words from the OTP. Return error when the word is not 32262306a36Sopenharmony_ci * stored in OTP. 32362306a36Sopenharmony_ci **/ 32462306a36Sopenharmony_cistatic s32 igb_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; 32762306a36Sopenharmony_ci u32 invm_dword; 32862306a36Sopenharmony_ci u16 i; 32962306a36Sopenharmony_ci u8 record_type, word_address; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci for (i = 0; i < E1000_INVM_SIZE; i++) { 33262306a36Sopenharmony_ci invm_dword = rd32(E1000_INVM_DATA_REG(i)); 33362306a36Sopenharmony_ci /* Get record type */ 33462306a36Sopenharmony_ci record_type = INVM_DWORD_TO_RECORD_TYPE(invm_dword); 33562306a36Sopenharmony_ci if (record_type == E1000_INVM_UNINITIALIZED_STRUCTURE) 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci if (record_type == E1000_INVM_CSR_AUTOLOAD_STRUCTURE) 33862306a36Sopenharmony_ci i += E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS; 33962306a36Sopenharmony_ci if (record_type == E1000_INVM_RSA_KEY_SHA256_STRUCTURE) 34062306a36Sopenharmony_ci i += E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS; 34162306a36Sopenharmony_ci if (record_type == E1000_INVM_WORD_AUTOLOAD_STRUCTURE) { 34262306a36Sopenharmony_ci word_address = INVM_DWORD_TO_WORD_ADDRESS(invm_dword); 34362306a36Sopenharmony_ci if (word_address == address) { 34462306a36Sopenharmony_ci *data = INVM_DWORD_TO_WORD_DATA(invm_dword); 34562306a36Sopenharmony_ci hw_dbg("Read INVM Word 0x%02x = %x\n", 34662306a36Sopenharmony_ci address, *data); 34762306a36Sopenharmony_ci status = 0; 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci if (status) 35362306a36Sopenharmony_ci hw_dbg("Requested word 0x%02x not found in OTP\n", address); 35462306a36Sopenharmony_ci return status; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/** 35862306a36Sopenharmony_ci * igb_read_invm_i210 - Read invm wrapper function for I210/I211 35962306a36Sopenharmony_ci * @hw: pointer to the HW structure 36062306a36Sopenharmony_ci * @offset: offset to read from 36162306a36Sopenharmony_ci * @words: number of words to read (unused) 36262306a36Sopenharmony_ci * @data: pointer to the data read 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * Wrapper function to return data formerly found in the NVM. 36562306a36Sopenharmony_ci **/ 36662306a36Sopenharmony_cistatic s32 igb_read_invm_i210(struct e1000_hw *hw, u16 offset, 36762306a36Sopenharmony_ci u16 __always_unused words, u16 *data) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci s32 ret_val = 0; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Only the MAC addr is required to be present in the iNVM */ 37262306a36Sopenharmony_ci switch (offset) { 37362306a36Sopenharmony_ci case NVM_MAC_ADDR: 37462306a36Sopenharmony_ci ret_val = igb_read_invm_word_i210(hw, (u8)offset, &data[0]); 37562306a36Sopenharmony_ci ret_val |= igb_read_invm_word_i210(hw, (u8)offset+1, 37662306a36Sopenharmony_ci &data[1]); 37762306a36Sopenharmony_ci ret_val |= igb_read_invm_word_i210(hw, (u8)offset+2, 37862306a36Sopenharmony_ci &data[2]); 37962306a36Sopenharmony_ci if (ret_val) 38062306a36Sopenharmony_ci hw_dbg("MAC Addr not found in iNVM\n"); 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case NVM_INIT_CTRL_2: 38362306a36Sopenharmony_ci ret_val = igb_read_invm_word_i210(hw, (u8)offset, data); 38462306a36Sopenharmony_ci if (ret_val) { 38562306a36Sopenharmony_ci *data = NVM_INIT_CTRL_2_DEFAULT_I211; 38662306a36Sopenharmony_ci ret_val = 0; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case NVM_INIT_CTRL_4: 39062306a36Sopenharmony_ci ret_val = igb_read_invm_word_i210(hw, (u8)offset, data); 39162306a36Sopenharmony_ci if (ret_val) { 39262306a36Sopenharmony_ci *data = NVM_INIT_CTRL_4_DEFAULT_I211; 39362306a36Sopenharmony_ci ret_val = 0; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case NVM_LED_1_CFG: 39762306a36Sopenharmony_ci ret_val = igb_read_invm_word_i210(hw, (u8)offset, data); 39862306a36Sopenharmony_ci if (ret_val) { 39962306a36Sopenharmony_ci *data = NVM_LED_1_CFG_DEFAULT_I211; 40062306a36Sopenharmony_ci ret_val = 0; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci case NVM_LED_0_2_CFG: 40462306a36Sopenharmony_ci ret_val = igb_read_invm_word_i210(hw, (u8)offset, data); 40562306a36Sopenharmony_ci if (ret_val) { 40662306a36Sopenharmony_ci *data = NVM_LED_0_2_CFG_DEFAULT_I211; 40762306a36Sopenharmony_ci ret_val = 0; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case NVM_ID_LED_SETTINGS: 41162306a36Sopenharmony_ci ret_val = igb_read_invm_word_i210(hw, (u8)offset, data); 41262306a36Sopenharmony_ci if (ret_val) { 41362306a36Sopenharmony_ci *data = ID_LED_RESERVED_FFFF; 41462306a36Sopenharmony_ci ret_val = 0; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci case NVM_SUB_DEV_ID: 41862306a36Sopenharmony_ci *data = hw->subsystem_device_id; 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case NVM_SUB_VEN_ID: 42162306a36Sopenharmony_ci *data = hw->subsystem_vendor_id; 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci case NVM_DEV_ID: 42462306a36Sopenharmony_ci *data = hw->device_id; 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci case NVM_VEN_ID: 42762306a36Sopenharmony_ci *data = hw->vendor_id; 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci default: 43062306a36Sopenharmony_ci hw_dbg("NVM word 0x%02x is not mapped.\n", offset); 43162306a36Sopenharmony_ci *data = NVM_RESERVED_WORD; 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci return ret_val; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * igb_read_invm_version - Reads iNVM version and image type 43962306a36Sopenharmony_ci * @hw: pointer to the HW structure 44062306a36Sopenharmony_ci * @invm_ver: version structure for the version read 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Reads iNVM version and image type. 44362306a36Sopenharmony_ci **/ 44462306a36Sopenharmony_cis32 igb_read_invm_version(struct e1000_hw *hw, 44562306a36Sopenharmony_ci struct e1000_fw_version *invm_ver) { 44662306a36Sopenharmony_ci u32 *record = NULL; 44762306a36Sopenharmony_ci u32 *next_record = NULL; 44862306a36Sopenharmony_ci u32 i = 0; 44962306a36Sopenharmony_ci u32 invm_dword = 0; 45062306a36Sopenharmony_ci u32 invm_blocks = E1000_INVM_SIZE - (E1000_INVM_ULT_BYTES_SIZE / 45162306a36Sopenharmony_ci E1000_INVM_RECORD_SIZE_IN_BYTES); 45262306a36Sopenharmony_ci u32 buffer[E1000_INVM_SIZE]; 45362306a36Sopenharmony_ci s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; 45462306a36Sopenharmony_ci u16 version = 0; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Read iNVM memory */ 45762306a36Sopenharmony_ci for (i = 0; i < E1000_INVM_SIZE; i++) { 45862306a36Sopenharmony_ci invm_dword = rd32(E1000_INVM_DATA_REG(i)); 45962306a36Sopenharmony_ci buffer[i] = invm_dword; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Read version number */ 46362306a36Sopenharmony_ci for (i = 1; i < invm_blocks; i++) { 46462306a36Sopenharmony_ci record = &buffer[invm_blocks - i]; 46562306a36Sopenharmony_ci next_record = &buffer[invm_blocks - i + 1]; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Check if we have first version location used */ 46862306a36Sopenharmony_ci if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) { 46962306a36Sopenharmony_ci version = 0; 47062306a36Sopenharmony_ci status = 0; 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci /* Check if we have second version location used */ 47462306a36Sopenharmony_ci else if ((i == 1) && 47562306a36Sopenharmony_ci ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) { 47662306a36Sopenharmony_ci version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; 47762306a36Sopenharmony_ci status = 0; 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci /* Check if we have odd version location 48162306a36Sopenharmony_ci * used and it is the last one used 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci else if ((((*record & E1000_INVM_VER_FIELD_ONE) == 0) && 48462306a36Sopenharmony_ci ((*record & 0x3) == 0)) || (((*record & 0x3) != 0) && 48562306a36Sopenharmony_ci (i != 1))) { 48662306a36Sopenharmony_ci version = (*next_record & E1000_INVM_VER_FIELD_TWO) 48762306a36Sopenharmony_ci >> 13; 48862306a36Sopenharmony_ci status = 0; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci /* Check if we have even version location 49262306a36Sopenharmony_ci * used and it is the last one used 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) && 49562306a36Sopenharmony_ci ((*record & 0x3) == 0)) { 49662306a36Sopenharmony_ci version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; 49762306a36Sopenharmony_ci status = 0; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!status) { 50362306a36Sopenharmony_ci invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK) 50462306a36Sopenharmony_ci >> E1000_INVM_MAJOR_SHIFT; 50562306a36Sopenharmony_ci invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci /* Read Image Type */ 50862306a36Sopenharmony_ci for (i = 1; i < invm_blocks; i++) { 50962306a36Sopenharmony_ci record = &buffer[invm_blocks - i]; 51062306a36Sopenharmony_ci next_record = &buffer[invm_blocks - i + 1]; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Check if we have image type in first location used */ 51362306a36Sopenharmony_ci if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) { 51462306a36Sopenharmony_ci invm_ver->invm_img_type = 0; 51562306a36Sopenharmony_ci status = 0; 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci /* Check if we have image type in first location used */ 51962306a36Sopenharmony_ci else if ((((*record & 0x3) == 0) && 52062306a36Sopenharmony_ci ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) || 52162306a36Sopenharmony_ci ((((*record & 0x3) != 0) && (i != 1)))) { 52262306a36Sopenharmony_ci invm_ver->invm_img_type = 52362306a36Sopenharmony_ci (*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23; 52462306a36Sopenharmony_ci status = 0; 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci return status; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/** 53262306a36Sopenharmony_ci * igb_validate_nvm_checksum_i210 - Validate EEPROM checksum 53362306a36Sopenharmony_ci * @hw: pointer to the HW structure 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * Calculates the EEPROM checksum by reading/adding each word of the EEPROM 53662306a36Sopenharmony_ci * and then verifies that the sum of the EEPROM is equal to 0xBABA. 53762306a36Sopenharmony_ci **/ 53862306a36Sopenharmony_cistatic s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci s32 status = 0; 54162306a36Sopenharmony_ci s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (!(hw->nvm.ops.acquire(hw))) { 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* Replace the read function with semaphore grabbing with 54662306a36Sopenharmony_ci * the one that skips this for a while. 54762306a36Sopenharmony_ci * We have semaphore taken already here. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci read_op_ptr = hw->nvm.ops.read; 55062306a36Sopenharmony_ci hw->nvm.ops.read = igb_read_nvm_eerd; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci status = igb_validate_nvm_checksum(hw); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Revert original read operation. */ 55562306a36Sopenharmony_ci hw->nvm.ops.read = read_op_ptr; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci hw->nvm.ops.release(hw); 55862306a36Sopenharmony_ci } else { 55962306a36Sopenharmony_ci status = E1000_ERR_SWFW_SYNC; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return status; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/** 56662306a36Sopenharmony_ci * igb_update_nvm_checksum_i210 - Update EEPROM checksum 56762306a36Sopenharmony_ci * @hw: pointer to the HW structure 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * Updates the EEPROM checksum by reading/adding each word of the EEPROM 57062306a36Sopenharmony_ci * up to the checksum. Then calculates the EEPROM checksum and writes the 57162306a36Sopenharmony_ci * value to the EEPROM. Next commit EEPROM data onto the Flash. 57262306a36Sopenharmony_ci **/ 57362306a36Sopenharmony_cistatic s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci s32 ret_val = 0; 57662306a36Sopenharmony_ci u16 checksum = 0; 57762306a36Sopenharmony_ci u16 i, nvm_data; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Read the first word from the EEPROM. If this times out or fails, do 58062306a36Sopenharmony_ci * not continue or we could be in for a very long wait while every 58162306a36Sopenharmony_ci * EEPROM read fails 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci ret_val = igb_read_nvm_eerd(hw, 0, 1, &nvm_data); 58462306a36Sopenharmony_ci if (ret_val) { 58562306a36Sopenharmony_ci hw_dbg("EEPROM read failed\n"); 58662306a36Sopenharmony_ci goto out; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!(hw->nvm.ops.acquire(hw))) { 59062306a36Sopenharmony_ci /* Do not use hw->nvm.ops.write, hw->nvm.ops.read 59162306a36Sopenharmony_ci * because we do not want to take the synchronization 59262306a36Sopenharmony_ci * semaphores twice here. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci for (i = 0; i < NVM_CHECKSUM_REG; i++) { 59662306a36Sopenharmony_ci ret_val = igb_read_nvm_eerd(hw, i, 1, &nvm_data); 59762306a36Sopenharmony_ci if (ret_val) { 59862306a36Sopenharmony_ci hw->nvm.ops.release(hw); 59962306a36Sopenharmony_ci hw_dbg("NVM Read Error while updating checksum.\n"); 60062306a36Sopenharmony_ci goto out; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci checksum += nvm_data; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci checksum = (u16) NVM_SUM - checksum; 60562306a36Sopenharmony_ci ret_val = igb_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1, 60662306a36Sopenharmony_ci &checksum); 60762306a36Sopenharmony_ci if (ret_val) { 60862306a36Sopenharmony_ci hw->nvm.ops.release(hw); 60962306a36Sopenharmony_ci hw_dbg("NVM Write Error while updating checksum.\n"); 61062306a36Sopenharmony_ci goto out; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci hw->nvm.ops.release(hw); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ret_val = igb_update_flash_i210(hw); 61662306a36Sopenharmony_ci } else { 61762306a36Sopenharmony_ci ret_val = -E1000_ERR_SWFW_SYNC; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ciout: 62062306a36Sopenharmony_ci return ret_val; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/** 62462306a36Sopenharmony_ci * igb_pool_flash_update_done_i210 - Pool FLUDONE status. 62562306a36Sopenharmony_ci * @hw: pointer to the HW structure 62662306a36Sopenharmony_ci * 62762306a36Sopenharmony_ci **/ 62862306a36Sopenharmony_cistatic s32 igb_pool_flash_update_done_i210(struct e1000_hw *hw) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci s32 ret_val = -E1000_ERR_NVM; 63162306a36Sopenharmony_ci u32 i, reg; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) { 63462306a36Sopenharmony_ci reg = rd32(E1000_EECD); 63562306a36Sopenharmony_ci if (reg & E1000_EECD_FLUDONE_I210) { 63662306a36Sopenharmony_ci ret_val = 0; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci udelay(5); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return ret_val; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/** 64662306a36Sopenharmony_ci * igb_get_flash_presence_i210 - Check if flash device is detected. 64762306a36Sopenharmony_ci * @hw: pointer to the HW structure 64862306a36Sopenharmony_ci * 64962306a36Sopenharmony_ci **/ 65062306a36Sopenharmony_cibool igb_get_flash_presence_i210(struct e1000_hw *hw) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci u32 eec = 0; 65362306a36Sopenharmony_ci bool ret_val = false; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci eec = rd32(E1000_EECD); 65662306a36Sopenharmony_ci if (eec & E1000_EECD_FLASH_DETECTED_I210) 65762306a36Sopenharmony_ci ret_val = true; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return ret_val; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci/** 66362306a36Sopenharmony_ci * igb_update_flash_i210 - Commit EEPROM to the flash 66462306a36Sopenharmony_ci * @hw: pointer to the HW structure 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci **/ 66762306a36Sopenharmony_cistatic s32 igb_update_flash_i210(struct e1000_hw *hw) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci s32 ret_val = 0; 67062306a36Sopenharmony_ci u32 flup; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ret_val = igb_pool_flash_update_done_i210(hw); 67362306a36Sopenharmony_ci if (ret_val == -E1000_ERR_NVM) { 67462306a36Sopenharmony_ci hw_dbg("Flash update time out\n"); 67562306a36Sopenharmony_ci goto out; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci flup = rd32(E1000_EECD) | E1000_EECD_FLUPD_I210; 67962306a36Sopenharmony_ci wr32(E1000_EECD, flup); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci ret_val = igb_pool_flash_update_done_i210(hw); 68262306a36Sopenharmony_ci if (ret_val) 68362306a36Sopenharmony_ci hw_dbg("Flash update time out\n"); 68462306a36Sopenharmony_ci else 68562306a36Sopenharmony_ci hw_dbg("Flash update complete\n"); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciout: 68862306a36Sopenharmony_ci return ret_val; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci/** 69262306a36Sopenharmony_ci * igb_valid_led_default_i210 - Verify a valid default LED config 69362306a36Sopenharmony_ci * @hw: pointer to the HW structure 69462306a36Sopenharmony_ci * @data: pointer to the NVM (EEPROM) 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci * Read the EEPROM for the current default LED configuration. If the 69762306a36Sopenharmony_ci * LED configuration is not valid, set to a valid LED configuration. 69862306a36Sopenharmony_ci **/ 69962306a36Sopenharmony_cis32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci s32 ret_val; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); 70462306a36Sopenharmony_ci if (ret_val) { 70562306a36Sopenharmony_ci hw_dbg("NVM Read Error\n"); 70662306a36Sopenharmony_ci goto out; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) { 71062306a36Sopenharmony_ci switch (hw->phy.media_type) { 71162306a36Sopenharmony_ci case e1000_media_type_internal_serdes: 71262306a36Sopenharmony_ci *data = ID_LED_DEFAULT_I210_SERDES; 71362306a36Sopenharmony_ci break; 71462306a36Sopenharmony_ci case e1000_media_type_copper: 71562306a36Sopenharmony_ci default: 71662306a36Sopenharmony_ci *data = ID_LED_DEFAULT_I210; 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ciout: 72162306a36Sopenharmony_ci return ret_val; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/** 72562306a36Sopenharmony_ci * __igb_access_xmdio_reg - Read/write XMDIO register 72662306a36Sopenharmony_ci * @hw: pointer to the HW structure 72762306a36Sopenharmony_ci * @address: XMDIO address to program 72862306a36Sopenharmony_ci * @dev_addr: device address to program 72962306a36Sopenharmony_ci * @data: pointer to value to read/write from/to the XMDIO address 73062306a36Sopenharmony_ci * @read: boolean flag to indicate read or write 73162306a36Sopenharmony_ci **/ 73262306a36Sopenharmony_cistatic s32 __igb_access_xmdio_reg(struct e1000_hw *hw, u16 address, 73362306a36Sopenharmony_ci u8 dev_addr, u16 *data, bool read) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci s32 ret_val = 0; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, dev_addr); 73862306a36Sopenharmony_ci if (ret_val) 73962306a36Sopenharmony_ci return ret_val; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, address); 74262306a36Sopenharmony_ci if (ret_val) 74362306a36Sopenharmony_ci return ret_val; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, E1000_MMDAC_FUNC_DATA | 74662306a36Sopenharmony_ci dev_addr); 74762306a36Sopenharmony_ci if (ret_val) 74862306a36Sopenharmony_ci return ret_val; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (read) 75162306a36Sopenharmony_ci ret_val = hw->phy.ops.read_reg(hw, E1000_MMDAAD, data); 75262306a36Sopenharmony_ci else 75362306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, *data); 75462306a36Sopenharmony_ci if (ret_val) 75562306a36Sopenharmony_ci return ret_val; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* Recalibrate the device back to 0 */ 75862306a36Sopenharmony_ci ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, 0); 75962306a36Sopenharmony_ci if (ret_val) 76062306a36Sopenharmony_ci return ret_val; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return ret_val; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/** 76662306a36Sopenharmony_ci * igb_read_xmdio_reg - Read XMDIO register 76762306a36Sopenharmony_ci * @hw: pointer to the HW structure 76862306a36Sopenharmony_ci * @addr: XMDIO address to program 76962306a36Sopenharmony_ci * @dev_addr: device address to program 77062306a36Sopenharmony_ci * @data: value to be read from the EMI address 77162306a36Sopenharmony_ci **/ 77262306a36Sopenharmony_cis32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci return __igb_access_xmdio_reg(hw, addr, dev_addr, data, true); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/** 77862306a36Sopenharmony_ci * igb_write_xmdio_reg - Write XMDIO register 77962306a36Sopenharmony_ci * @hw: pointer to the HW structure 78062306a36Sopenharmony_ci * @addr: XMDIO address to program 78162306a36Sopenharmony_ci * @dev_addr: device address to program 78262306a36Sopenharmony_ci * @data: value to be written to the XMDIO address 78362306a36Sopenharmony_ci **/ 78462306a36Sopenharmony_cis32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci return __igb_access_xmdio_reg(hw, addr, dev_addr, &data, false); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci/** 79062306a36Sopenharmony_ci * igb_init_nvm_params_i210 - Init NVM func ptrs. 79162306a36Sopenharmony_ci * @hw: pointer to the HW structure 79262306a36Sopenharmony_ci **/ 79362306a36Sopenharmony_cis32 igb_init_nvm_params_i210(struct e1000_hw *hw) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct e1000_nvm_info *nvm = &hw->nvm; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci nvm->ops.acquire = igb_acquire_nvm_i210; 79862306a36Sopenharmony_ci nvm->ops.release = igb_release_nvm_i210; 79962306a36Sopenharmony_ci nvm->ops.valid_led_default = igb_valid_led_default_i210; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* NVM Function Pointers */ 80262306a36Sopenharmony_ci if (igb_get_flash_presence_i210(hw)) { 80362306a36Sopenharmony_ci hw->nvm.type = e1000_nvm_flash_hw; 80462306a36Sopenharmony_ci nvm->ops.read = igb_read_nvm_srrd_i210; 80562306a36Sopenharmony_ci nvm->ops.write = igb_write_nvm_srwr_i210; 80662306a36Sopenharmony_ci nvm->ops.validate = igb_validate_nvm_checksum_i210; 80762306a36Sopenharmony_ci nvm->ops.update = igb_update_nvm_checksum_i210; 80862306a36Sopenharmony_ci } else { 80962306a36Sopenharmony_ci hw->nvm.type = e1000_nvm_invm; 81062306a36Sopenharmony_ci nvm->ops.read = igb_read_invm_i210; 81162306a36Sopenharmony_ci nvm->ops.write = NULL; 81262306a36Sopenharmony_ci nvm->ops.validate = NULL; 81362306a36Sopenharmony_ci nvm->ops.update = NULL; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci/** 81962306a36Sopenharmony_ci * igb_pll_workaround_i210 82062306a36Sopenharmony_ci * @hw: pointer to the HW structure 82162306a36Sopenharmony_ci * 82262306a36Sopenharmony_ci * Works around an errata in the PLL circuit where it occasionally 82362306a36Sopenharmony_ci * provides the wrong clock frequency after power up. 82462306a36Sopenharmony_ci **/ 82562306a36Sopenharmony_cis32 igb_pll_workaround_i210(struct e1000_hw *hw) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci s32 ret_val; 82862306a36Sopenharmony_ci u32 wuc, mdicnfg, ctrl, ctrl_ext, reg_val; 82962306a36Sopenharmony_ci u16 nvm_word, phy_word, pci_word, tmp_nvm; 83062306a36Sopenharmony_ci int i; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Get and set needed register values */ 83362306a36Sopenharmony_ci wuc = rd32(E1000_WUC); 83462306a36Sopenharmony_ci mdicnfg = rd32(E1000_MDICNFG); 83562306a36Sopenharmony_ci reg_val = mdicnfg & ~E1000_MDICNFG_EXT_MDIO; 83662306a36Sopenharmony_ci wr32(E1000_MDICNFG, reg_val); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Get data from NVM, or set default */ 83962306a36Sopenharmony_ci ret_val = igb_read_invm_word_i210(hw, E1000_INVM_AUTOLOAD, 84062306a36Sopenharmony_ci &nvm_word); 84162306a36Sopenharmony_ci if (ret_val) 84262306a36Sopenharmony_ci nvm_word = E1000_INVM_DEFAULT_AL; 84362306a36Sopenharmony_ci tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL; 84462306a36Sopenharmony_ci igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, E1000_PHY_PLL_FREQ_PAGE); 84562306a36Sopenharmony_ci phy_word = E1000_PHY_PLL_UNCONF; 84662306a36Sopenharmony_ci for (i = 0; i < E1000_MAX_PLL_TRIES; i++) { 84762306a36Sopenharmony_ci /* check current state directly from internal PHY */ 84862306a36Sopenharmony_ci igb_read_phy_reg_82580(hw, E1000_PHY_PLL_FREQ_REG, &phy_word); 84962306a36Sopenharmony_ci if ((phy_word & E1000_PHY_PLL_UNCONF) 85062306a36Sopenharmony_ci != E1000_PHY_PLL_UNCONF) { 85162306a36Sopenharmony_ci ret_val = 0; 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci } else { 85462306a36Sopenharmony_ci ret_val = -E1000_ERR_PHY; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci /* directly reset the internal PHY */ 85762306a36Sopenharmony_ci ctrl = rd32(E1000_CTRL); 85862306a36Sopenharmony_ci wr32(E1000_CTRL, ctrl|E1000_CTRL_PHY_RST); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci ctrl_ext = rd32(E1000_CTRL_EXT); 86162306a36Sopenharmony_ci ctrl_ext |= (E1000_CTRL_EXT_PHYPDEN | E1000_CTRL_EXT_SDLPE); 86262306a36Sopenharmony_ci wr32(E1000_CTRL_EXT, ctrl_ext); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci wr32(E1000_WUC, 0); 86562306a36Sopenharmony_ci reg_val = (E1000_INVM_AUTOLOAD << 4) | (tmp_nvm << 16); 86662306a36Sopenharmony_ci wr32(E1000_EEARBC_I210, reg_val); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci igb_read_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); 86962306a36Sopenharmony_ci pci_word |= E1000_PCI_PMCSR_D3; 87062306a36Sopenharmony_ci igb_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); 87162306a36Sopenharmony_ci usleep_range(1000, 2000); 87262306a36Sopenharmony_ci pci_word &= ~E1000_PCI_PMCSR_D3; 87362306a36Sopenharmony_ci igb_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); 87462306a36Sopenharmony_ci reg_val = (E1000_INVM_AUTOLOAD << 4) | (nvm_word << 16); 87562306a36Sopenharmony_ci wr32(E1000_EEARBC_I210, reg_val); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* restore WUC register */ 87862306a36Sopenharmony_ci wr32(E1000_WUC, wuc); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, 0); 88162306a36Sopenharmony_ci /* restore MDICNFG setting */ 88262306a36Sopenharmony_ci wr32(E1000_MDICNFG, mdicnfg); 88362306a36Sopenharmony_ci return ret_val; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci/** 88762306a36Sopenharmony_ci * igb_get_cfg_done_i210 - Read config done bit 88862306a36Sopenharmony_ci * @hw: pointer to the HW structure 88962306a36Sopenharmony_ci * 89062306a36Sopenharmony_ci * Read the management control register for the config done bit for 89162306a36Sopenharmony_ci * completion status. NOTE: silicon which is EEPROM-less will fail trying 89262306a36Sopenharmony_ci * to read the config done bit, so an error is *ONLY* logged and returns 89362306a36Sopenharmony_ci * 0. If we were to return with error, EEPROM-less silicon 89462306a36Sopenharmony_ci * would not be able to be reset or change link. 89562306a36Sopenharmony_ci **/ 89662306a36Sopenharmony_cis32 igb_get_cfg_done_i210(struct e1000_hw *hw) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci s32 timeout = PHY_CFG_TIMEOUT; 89962306a36Sopenharmony_ci u32 mask = E1000_NVM_CFG_DONE_PORT_0; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci while (timeout) { 90262306a36Sopenharmony_ci if (rd32(E1000_EEMNGCTL_I210) & mask) 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci usleep_range(1000, 2000); 90562306a36Sopenharmony_ci timeout--; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci if (!timeout) 90862306a36Sopenharmony_ci hw_dbg("MNG configuration cycle has not completed.\n"); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci} 912