162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2018 Intel Corporation */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/delay.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "igc_hw.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/** 962306a36Sopenharmony_ci * igc_acquire_nvm_i225 - Acquire exclusive access to EEPROM 1062306a36Sopenharmony_ci * @hw: pointer to the HW structure 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Acquire the necessary semaphores for exclusive access to the EEPROM. 1362306a36Sopenharmony_ci * Set the EEPROM access request bit and wait for EEPROM access grant bit. 1462306a36Sopenharmony_ci * Return successful if access grant bit set, else clear the request for 1562306a36Sopenharmony_ci * EEPROM access and return -IGC_ERR_NVM (-1). 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_cistatic s32 igc_acquire_nvm_i225(struct igc_hw *hw) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci return igc_acquire_swfw_sync_i225(hw, IGC_SWFW_EEP_SM); 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * igc_release_nvm_i225 - Release exclusive access to EEPROM 2462306a36Sopenharmony_ci * @hw: pointer to the HW structure 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Stop any current commands to the EEPROM and clear the EEPROM request bit, 2762306a36Sopenharmony_ci * then release the semaphores acquired. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic void igc_release_nvm_i225(struct igc_hw *hw) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci igc_release_swfw_sync_i225(hw, IGC_SWFW_EEP_SM); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/** 3562306a36Sopenharmony_ci * igc_get_hw_semaphore_i225 - Acquire hardware semaphore 3662306a36Sopenharmony_ci * @hw: pointer to the HW structure 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Acquire the HW semaphore to access the PHY or NVM 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic s32 igc_get_hw_semaphore_i225(struct igc_hw *hw) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci s32 timeout = hw->nvm.word_size + 1; 4362306a36Sopenharmony_ci s32 i = 0; 4462306a36Sopenharmony_ci u32 swsm; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Get the SW semaphore */ 4762306a36Sopenharmony_ci while (i < timeout) { 4862306a36Sopenharmony_ci swsm = rd32(IGC_SWSM); 4962306a36Sopenharmony_ci if (!(swsm & IGC_SWSM_SMBI)) 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci usleep_range(500, 600); 5362306a36Sopenharmony_ci i++; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (i == timeout) { 5762306a36Sopenharmony_ci /* In rare circumstances, the SW semaphore may already be held 5862306a36Sopenharmony_ci * unintentionally. Clear the semaphore once before giving up. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci if (hw->dev_spec._base.clear_semaphore_once) { 6162306a36Sopenharmony_ci hw->dev_spec._base.clear_semaphore_once = false; 6262306a36Sopenharmony_ci igc_put_hw_semaphore(hw); 6362306a36Sopenharmony_ci for (i = 0; i < timeout; i++) { 6462306a36Sopenharmony_ci swsm = rd32(IGC_SWSM); 6562306a36Sopenharmony_ci if (!(swsm & IGC_SWSM_SMBI)) 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci usleep_range(500, 600); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* If we do not have the semaphore here, we have to give up. */ 7362306a36Sopenharmony_ci if (i == timeout) { 7462306a36Sopenharmony_ci hw_dbg("Driver can't access device - SMBI bit is set.\n"); 7562306a36Sopenharmony_ci return -IGC_ERR_NVM; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Get the FW semaphore. */ 8062306a36Sopenharmony_ci for (i = 0; i < timeout; i++) { 8162306a36Sopenharmony_ci swsm = rd32(IGC_SWSM); 8262306a36Sopenharmony_ci wr32(IGC_SWSM, swsm | IGC_SWSM_SWESMBI); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Semaphore acquired if bit latched */ 8562306a36Sopenharmony_ci if (rd32(IGC_SWSM) & IGC_SWSM_SWESMBI) 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci usleep_range(500, 600); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (i == timeout) { 9262306a36Sopenharmony_ci /* Release semaphores */ 9362306a36Sopenharmony_ci igc_put_hw_semaphore(hw); 9462306a36Sopenharmony_ci hw_dbg("Driver can't access the NVM\n"); 9562306a36Sopenharmony_ci return -IGC_ERR_NVM; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/** 10262306a36Sopenharmony_ci * igc_acquire_swfw_sync_i225 - Acquire SW/FW semaphore 10362306a36Sopenharmony_ci * @hw: pointer to the HW structure 10462306a36Sopenharmony_ci * @mask: specifies which semaphore to acquire 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * Acquire the SW/FW semaphore to access the PHY or NVM. The mask 10762306a36Sopenharmony_ci * will also specify which port we're acquiring the lock for. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cis32 igc_acquire_swfw_sync_i225(struct igc_hw *hw, u16 mask) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci s32 i = 0, timeout = 200; 11262306a36Sopenharmony_ci u32 fwmask = mask << 16; 11362306a36Sopenharmony_ci u32 swmask = mask; 11462306a36Sopenharmony_ci s32 ret_val = 0; 11562306a36Sopenharmony_ci u32 swfw_sync; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci while (i < timeout) { 11862306a36Sopenharmony_ci if (igc_get_hw_semaphore_i225(hw)) { 11962306a36Sopenharmony_ci ret_val = -IGC_ERR_SWFW_SYNC; 12062306a36Sopenharmony_ci goto out; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci swfw_sync = rd32(IGC_SW_FW_SYNC); 12462306a36Sopenharmony_ci if (!(swfw_sync & (fwmask | swmask))) 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* Firmware currently using resource (fwmask) */ 12862306a36Sopenharmony_ci igc_put_hw_semaphore(hw); 12962306a36Sopenharmony_ci mdelay(5); 13062306a36Sopenharmony_ci i++; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (i == timeout) { 13462306a36Sopenharmony_ci hw_dbg("Driver can't access resource, SW_FW_SYNC timeout.\n"); 13562306a36Sopenharmony_ci ret_val = -IGC_ERR_SWFW_SYNC; 13662306a36Sopenharmony_ci goto out; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci swfw_sync |= swmask; 14062306a36Sopenharmony_ci wr32(IGC_SW_FW_SYNC, swfw_sync); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci igc_put_hw_semaphore(hw); 14362306a36Sopenharmony_ciout: 14462306a36Sopenharmony_ci return ret_val; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/** 14862306a36Sopenharmony_ci * igc_release_swfw_sync_i225 - Release SW/FW semaphore 14962306a36Sopenharmony_ci * @hw: pointer to the HW structure 15062306a36Sopenharmony_ci * @mask: specifies which semaphore to acquire 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * Release the SW/FW semaphore used to access the PHY or NVM. The mask 15362306a36Sopenharmony_ci * will also specify which port we're releasing the lock for. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_civoid igc_release_swfw_sync_i225(struct igc_hw *hw, u16 mask) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u32 swfw_sync; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* Releasing the resource requires first getting the HW semaphore. 16062306a36Sopenharmony_ci * If we fail to get the semaphore, there is nothing we can do, 16162306a36Sopenharmony_ci * except log an error and quit. We are not allowed to hang here 16262306a36Sopenharmony_ci * indefinitely, as it may cause denial of service or system crash. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci if (igc_get_hw_semaphore_i225(hw)) { 16562306a36Sopenharmony_ci hw_dbg("Failed to release SW_FW_SYNC.\n"); 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci swfw_sync = rd32(IGC_SW_FW_SYNC); 17062306a36Sopenharmony_ci swfw_sync &= ~mask; 17162306a36Sopenharmony_ci wr32(IGC_SW_FW_SYNC, swfw_sync); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci igc_put_hw_semaphore(hw); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/** 17762306a36Sopenharmony_ci * igc_read_nvm_srrd_i225 - Reads Shadow Ram using EERD register 17862306a36Sopenharmony_ci * @hw: pointer to the HW structure 17962306a36Sopenharmony_ci * @offset: offset of word in the Shadow Ram to read 18062306a36Sopenharmony_ci * @words: number of words to read 18162306a36Sopenharmony_ci * @data: word read from the Shadow Ram 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Reads a 16 bit word from the Shadow Ram using the EERD register. 18462306a36Sopenharmony_ci * Uses necessary synchronization semaphores. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic s32 igc_read_nvm_srrd_i225(struct igc_hw *hw, u16 offset, u16 words, 18762306a36Sopenharmony_ci u16 *data) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci s32 status = 0; 19062306a36Sopenharmony_ci u16 i, count; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* We cannot hold synchronization semaphores for too long, 19362306a36Sopenharmony_ci * because of forceful takeover procedure. However it is more efficient 19462306a36Sopenharmony_ci * to read in bursts than synchronizing access for each word. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci for (i = 0; i < words; i += IGC_EERD_EEWR_MAX_COUNT) { 19762306a36Sopenharmony_ci count = (words - i) / IGC_EERD_EEWR_MAX_COUNT > 0 ? 19862306a36Sopenharmony_ci IGC_EERD_EEWR_MAX_COUNT : (words - i); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci status = hw->nvm.ops.acquire(hw); 20162306a36Sopenharmony_ci if (status) 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci status = igc_read_nvm_eerd(hw, offset, count, data + i); 20562306a36Sopenharmony_ci hw->nvm.ops.release(hw); 20662306a36Sopenharmony_ci if (status) 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return status; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * igc_write_nvm_srwr - Write to Shadow Ram using EEWR 21562306a36Sopenharmony_ci * @hw: pointer to the HW structure 21662306a36Sopenharmony_ci * @offset: offset within the Shadow Ram to be written to 21762306a36Sopenharmony_ci * @words: number of words to write 21862306a36Sopenharmony_ci * @data: 16 bit word(s) to be written to the Shadow Ram 21962306a36Sopenharmony_ci * 22062306a36Sopenharmony_ci * Writes data to Shadow Ram at offset using EEWR register. 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * If igc_update_nvm_checksum is not called after this function , the 22362306a36Sopenharmony_ci * Shadow Ram will most likely contain an invalid checksum. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_cistatic s32 igc_write_nvm_srwr(struct igc_hw *hw, u16 offset, u16 words, 22662306a36Sopenharmony_ci u16 *data) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct igc_nvm_info *nvm = &hw->nvm; 22962306a36Sopenharmony_ci s32 ret_val = -IGC_ERR_NVM; 23062306a36Sopenharmony_ci u32 attempts = 100000; 23162306a36Sopenharmony_ci u32 i, k, eewr = 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* A check for invalid values: offset too large, too many words, 23462306a36Sopenharmony_ci * too many words for the offset, and not enough words. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci if (offset >= nvm->word_size || (words > (nvm->word_size - offset)) || 23762306a36Sopenharmony_ci words == 0) { 23862306a36Sopenharmony_ci hw_dbg("nvm parameter(s) out of bounds\n"); 23962306a36Sopenharmony_ci return ret_val; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci for (i = 0; i < words; i++) { 24362306a36Sopenharmony_ci ret_val = -IGC_ERR_NVM; 24462306a36Sopenharmony_ci eewr = ((offset + i) << IGC_NVM_RW_ADDR_SHIFT) | 24562306a36Sopenharmony_ci (data[i] << IGC_NVM_RW_REG_DATA) | 24662306a36Sopenharmony_ci IGC_NVM_RW_REG_START; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci wr32(IGC_SRWR, eewr); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci for (k = 0; k < attempts; k++) { 25162306a36Sopenharmony_ci if (IGC_NVM_RW_REG_DONE & 25262306a36Sopenharmony_ci rd32(IGC_SRWR)) { 25362306a36Sopenharmony_ci ret_val = 0; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci udelay(5); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (ret_val) { 26062306a36Sopenharmony_ci hw_dbg("Shadow RAM write EEWR timed out\n"); 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return ret_val; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/** 26962306a36Sopenharmony_ci * igc_write_nvm_srwr_i225 - Write to Shadow RAM using EEWR 27062306a36Sopenharmony_ci * @hw: pointer to the HW structure 27162306a36Sopenharmony_ci * @offset: offset within the Shadow RAM to be written to 27262306a36Sopenharmony_ci * @words: number of words to write 27362306a36Sopenharmony_ci * @data: 16 bit word(s) to be written to the Shadow RAM 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * Writes data to Shadow RAM at offset using EEWR register. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * If igc_update_nvm_checksum is not called after this function , the 27862306a36Sopenharmony_ci * data will not be committed to FLASH and also Shadow RAM will most likely 27962306a36Sopenharmony_ci * contain an invalid checksum. 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * If error code is returned, data and Shadow RAM may be inconsistent - buffer 28262306a36Sopenharmony_ci * partially written. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic s32 igc_write_nvm_srwr_i225(struct igc_hw *hw, u16 offset, u16 words, 28562306a36Sopenharmony_ci u16 *data) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci s32 status = 0; 28862306a36Sopenharmony_ci u16 i, count; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* We cannot hold synchronization semaphores for too long, 29162306a36Sopenharmony_ci * because of forceful takeover procedure. However it is more efficient 29262306a36Sopenharmony_ci * to write in bursts than synchronizing access for each word. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci for (i = 0; i < words; i += IGC_EERD_EEWR_MAX_COUNT) { 29562306a36Sopenharmony_ci count = (words - i) / IGC_EERD_EEWR_MAX_COUNT > 0 ? 29662306a36Sopenharmony_ci IGC_EERD_EEWR_MAX_COUNT : (words - i); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci status = hw->nvm.ops.acquire(hw); 29962306a36Sopenharmony_ci if (status) 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci status = igc_write_nvm_srwr(hw, offset, count, data + i); 30362306a36Sopenharmony_ci hw->nvm.ops.release(hw); 30462306a36Sopenharmony_ci if (status) 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return status; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/** 31262306a36Sopenharmony_ci * igc_validate_nvm_checksum_i225 - Validate EEPROM checksum 31362306a36Sopenharmony_ci * @hw: pointer to the HW structure 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * Calculates the EEPROM checksum by reading/adding each word of the EEPROM 31662306a36Sopenharmony_ci * and then verifies that the sum of the EEPROM is equal to 0xBABA. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_cistatic s32 igc_validate_nvm_checksum_i225(struct igc_hw *hw) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci s32 (*read_op_ptr)(struct igc_hw *hw, u16 offset, u16 count, 32162306a36Sopenharmony_ci u16 *data); 32262306a36Sopenharmony_ci s32 status = 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci status = hw->nvm.ops.acquire(hw); 32562306a36Sopenharmony_ci if (status) 32662306a36Sopenharmony_ci goto out; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Replace the read function with semaphore grabbing with 32962306a36Sopenharmony_ci * the one that skips this for a while. 33062306a36Sopenharmony_ci * We have semaphore taken already here. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci read_op_ptr = hw->nvm.ops.read; 33362306a36Sopenharmony_ci hw->nvm.ops.read = igc_read_nvm_eerd; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci status = igc_validate_nvm_checksum(hw); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Revert original read operation. */ 33862306a36Sopenharmony_ci hw->nvm.ops.read = read_op_ptr; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci hw->nvm.ops.release(hw); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciout: 34362306a36Sopenharmony_ci return status; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/** 34762306a36Sopenharmony_ci * igc_pool_flash_update_done_i225 - Pool FLUDONE status 34862306a36Sopenharmony_ci * @hw: pointer to the HW structure 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_cistatic s32 igc_pool_flash_update_done_i225(struct igc_hw *hw) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci s32 ret_val = -IGC_ERR_NVM; 35362306a36Sopenharmony_ci u32 i, reg; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci for (i = 0; i < IGC_FLUDONE_ATTEMPTS; i++) { 35662306a36Sopenharmony_ci reg = rd32(IGC_EECD); 35762306a36Sopenharmony_ci if (reg & IGC_EECD_FLUDONE_I225) { 35862306a36Sopenharmony_ci ret_val = 0; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci udelay(5); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return ret_val; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/** 36862306a36Sopenharmony_ci * igc_update_flash_i225 - Commit EEPROM to the flash 36962306a36Sopenharmony_ci * @hw: pointer to the HW structure 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_cistatic s32 igc_update_flash_i225(struct igc_hw *hw) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci s32 ret_val = 0; 37462306a36Sopenharmony_ci u32 flup; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci ret_val = igc_pool_flash_update_done_i225(hw); 37762306a36Sopenharmony_ci if (ret_val == -IGC_ERR_NVM) { 37862306a36Sopenharmony_ci hw_dbg("Flash update time out\n"); 37962306a36Sopenharmony_ci goto out; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci flup = rd32(IGC_EECD) | IGC_EECD_FLUPD_I225; 38362306a36Sopenharmony_ci wr32(IGC_EECD, flup); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ret_val = igc_pool_flash_update_done_i225(hw); 38662306a36Sopenharmony_ci if (ret_val) 38762306a36Sopenharmony_ci hw_dbg("Flash update time out\n"); 38862306a36Sopenharmony_ci else 38962306a36Sopenharmony_ci hw_dbg("Flash update complete\n"); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ciout: 39262306a36Sopenharmony_ci return ret_val; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/** 39662306a36Sopenharmony_ci * igc_update_nvm_checksum_i225 - Update EEPROM checksum 39762306a36Sopenharmony_ci * @hw: pointer to the HW structure 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Updates the EEPROM checksum by reading/adding each word of the EEPROM 40062306a36Sopenharmony_ci * up to the checksum. Then calculates the EEPROM checksum and writes the 40162306a36Sopenharmony_ci * value to the EEPROM. Next commit EEPROM data onto the Flash. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_cistatic s32 igc_update_nvm_checksum_i225(struct igc_hw *hw) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci u16 checksum = 0; 40662306a36Sopenharmony_ci s32 ret_val = 0; 40762306a36Sopenharmony_ci u16 i, nvm_data; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Read the first word from the EEPROM. If this times out or fails, do 41062306a36Sopenharmony_ci * not continue or we could be in for a very long wait while every 41162306a36Sopenharmony_ci * EEPROM read fails 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_ci ret_val = igc_read_nvm_eerd(hw, 0, 1, &nvm_data); 41462306a36Sopenharmony_ci if (ret_val) { 41562306a36Sopenharmony_ci hw_dbg("EEPROM read failed\n"); 41662306a36Sopenharmony_ci goto out; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret_val = hw->nvm.ops.acquire(hw); 42062306a36Sopenharmony_ci if (ret_val) 42162306a36Sopenharmony_ci goto out; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Do not use hw->nvm.ops.write, hw->nvm.ops.read 42462306a36Sopenharmony_ci * because we do not want to take the synchronization 42562306a36Sopenharmony_ci * semaphores twice here. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci for (i = 0; i < NVM_CHECKSUM_REG; i++) { 42962306a36Sopenharmony_ci ret_val = igc_read_nvm_eerd(hw, i, 1, &nvm_data); 43062306a36Sopenharmony_ci if (ret_val) { 43162306a36Sopenharmony_ci hw->nvm.ops.release(hw); 43262306a36Sopenharmony_ci hw_dbg("NVM Read Error while updating checksum.\n"); 43362306a36Sopenharmony_ci goto out; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci checksum += nvm_data; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci checksum = (u16)NVM_SUM - checksum; 43862306a36Sopenharmony_ci ret_val = igc_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1, 43962306a36Sopenharmony_ci &checksum); 44062306a36Sopenharmony_ci if (ret_val) { 44162306a36Sopenharmony_ci hw->nvm.ops.release(hw); 44262306a36Sopenharmony_ci hw_dbg("NVM Write Error while updating checksum.\n"); 44362306a36Sopenharmony_ci goto out; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci hw->nvm.ops.release(hw); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ret_val = igc_update_flash_i225(hw); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciout: 45162306a36Sopenharmony_ci return ret_val; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/** 45562306a36Sopenharmony_ci * igc_get_flash_presence_i225 - Check if flash device is detected 45662306a36Sopenharmony_ci * @hw: pointer to the HW structure 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cibool igc_get_flash_presence_i225(struct igc_hw *hw) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci bool ret_val = false; 46162306a36Sopenharmony_ci u32 eec = 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci eec = rd32(IGC_EECD); 46462306a36Sopenharmony_ci if (eec & IGC_EECD_FLASH_DETECTED_I225) 46562306a36Sopenharmony_ci ret_val = true; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return ret_val; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/** 47162306a36Sopenharmony_ci * igc_init_nvm_params_i225 - Init NVM func ptrs. 47262306a36Sopenharmony_ci * @hw: pointer to the HW structure 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_cis32 igc_init_nvm_params_i225(struct igc_hw *hw) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct igc_nvm_info *nvm = &hw->nvm; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci nvm->ops.acquire = igc_acquire_nvm_i225; 47962306a36Sopenharmony_ci nvm->ops.release = igc_release_nvm_i225; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* NVM Function Pointers */ 48262306a36Sopenharmony_ci if (igc_get_flash_presence_i225(hw)) { 48362306a36Sopenharmony_ci nvm->ops.read = igc_read_nvm_srrd_i225; 48462306a36Sopenharmony_ci nvm->ops.write = igc_write_nvm_srwr_i225; 48562306a36Sopenharmony_ci nvm->ops.validate = igc_validate_nvm_checksum_i225; 48662306a36Sopenharmony_ci nvm->ops.update = igc_update_nvm_checksum_i225; 48762306a36Sopenharmony_ci } else { 48862306a36Sopenharmony_ci nvm->ops.read = igc_read_nvm_eerd; 48962306a36Sopenharmony_ci nvm->ops.write = NULL; 49062306a36Sopenharmony_ci nvm->ops.validate = NULL; 49162306a36Sopenharmony_ci nvm->ops.update = NULL; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/** 49762306a36Sopenharmony_ci * igc_set_eee_i225 - Enable/disable EEE support 49862306a36Sopenharmony_ci * @hw: pointer to the HW structure 49962306a36Sopenharmony_ci * @adv2p5G: boolean flag enabling 2.5G EEE advertisement 50062306a36Sopenharmony_ci * @adv1G: boolean flag enabling 1G EEE advertisement 50162306a36Sopenharmony_ci * @adv100M: boolean flag enabling 100M EEE advertisement 50262306a36Sopenharmony_ci * 50362306a36Sopenharmony_ci * Enable/disable EEE based on setting in dev_spec structure. 50462306a36Sopenharmony_ci **/ 50562306a36Sopenharmony_cis32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G, 50662306a36Sopenharmony_ci bool adv100M) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci u32 ipcnfg, eeer; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ipcnfg = rd32(IGC_IPCNFG); 51162306a36Sopenharmony_ci eeer = rd32(IGC_EEER); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* enable or disable per user setting */ 51462306a36Sopenharmony_ci if (hw->dev_spec._base.eee_enable) { 51562306a36Sopenharmony_ci u32 eee_su = rd32(IGC_EEE_SU); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (adv100M) 51862306a36Sopenharmony_ci ipcnfg |= IGC_IPCNFG_EEE_100M_AN; 51962306a36Sopenharmony_ci else 52062306a36Sopenharmony_ci ipcnfg &= ~IGC_IPCNFG_EEE_100M_AN; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (adv1G) 52362306a36Sopenharmony_ci ipcnfg |= IGC_IPCNFG_EEE_1G_AN; 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci ipcnfg &= ~IGC_IPCNFG_EEE_1G_AN; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (adv2p5G) 52862306a36Sopenharmony_ci ipcnfg |= IGC_IPCNFG_EEE_2_5G_AN; 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci ipcnfg &= ~IGC_IPCNFG_EEE_2_5G_AN; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci eeer |= (IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN | 53362306a36Sopenharmony_ci IGC_EEER_LPI_FC); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* This bit should not be set in normal operation. */ 53662306a36Sopenharmony_ci if (eee_su & IGC_EEE_SU_LPI_CLK_STP) 53762306a36Sopenharmony_ci hw_dbg("LPI Clock Stop Bit should not be set!\n"); 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci ipcnfg &= ~(IGC_IPCNFG_EEE_2_5G_AN | IGC_IPCNFG_EEE_1G_AN | 54062306a36Sopenharmony_ci IGC_IPCNFG_EEE_100M_AN); 54162306a36Sopenharmony_ci eeer &= ~(IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN | 54262306a36Sopenharmony_ci IGC_EEER_LPI_FC); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci wr32(IGC_IPCNFG, ipcnfg); 54562306a36Sopenharmony_ci wr32(IGC_EEER, eeer); 54662306a36Sopenharmony_ci rd32(IGC_IPCNFG); 54762306a36Sopenharmony_ci rd32(IGC_EEER); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return IGC_SUCCESS; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci/* igc_set_ltr_i225 - Set Latency Tolerance Reporting thresholds 55362306a36Sopenharmony_ci * @hw: pointer to the HW structure 55462306a36Sopenharmony_ci * @link: bool indicating link status 55562306a36Sopenharmony_ci * 55662306a36Sopenharmony_ci * Set the LTR thresholds based on the link speed (Mbps), EEE, and DMAC 55762306a36Sopenharmony_ci * settings, otherwise specify that there is no LTR requirement. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_cis32 igc_set_ltr_i225(struct igc_hw *hw, bool link) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci u32 tw_system, ltrc, ltrv, ltr_min, ltr_max, scale_min, scale_max; 56262306a36Sopenharmony_ci u16 speed, duplex; 56362306a36Sopenharmony_ci s32 size; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* If we do not have link, LTR thresholds are zero. */ 56662306a36Sopenharmony_ci if (link) { 56762306a36Sopenharmony_ci hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Check if using copper interface with EEE enabled or if the 57062306a36Sopenharmony_ci * link speed is 10 Mbps. 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci if (hw->dev_spec._base.eee_enable && 57362306a36Sopenharmony_ci speed != SPEED_10) { 57462306a36Sopenharmony_ci /* EEE enabled, so send LTRMAX threshold. */ 57562306a36Sopenharmony_ci ltrc = rd32(IGC_LTRC) | 57662306a36Sopenharmony_ci IGC_LTRC_EEEMS_EN; 57762306a36Sopenharmony_ci wr32(IGC_LTRC, ltrc); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Calculate tw_system (nsec). */ 58062306a36Sopenharmony_ci if (speed == SPEED_100) { 58162306a36Sopenharmony_ci tw_system = ((rd32(IGC_EEE_SU) & 58262306a36Sopenharmony_ci IGC_TW_SYSTEM_100_MASK) >> 58362306a36Sopenharmony_ci IGC_TW_SYSTEM_100_SHIFT) * 500; 58462306a36Sopenharmony_ci } else { 58562306a36Sopenharmony_ci tw_system = (rd32(IGC_EEE_SU) & 58662306a36Sopenharmony_ci IGC_TW_SYSTEM_1000_MASK) * 500; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } else { 58962306a36Sopenharmony_ci tw_system = 0; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Get the Rx packet buffer size. */ 59362306a36Sopenharmony_ci size = rd32(IGC_RXPBS) & 59462306a36Sopenharmony_ci IGC_RXPBS_SIZE_I225_MASK; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Convert size to bytes, subtract the MTU, and then 59762306a36Sopenharmony_ci * convert the size to bits. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci size *= 1024; 60062306a36Sopenharmony_ci size *= 8; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (size < 0) { 60362306a36Sopenharmony_ci hw_dbg("Invalid effective Rx buffer size %d\n", 60462306a36Sopenharmony_ci size); 60562306a36Sopenharmony_ci return -IGC_ERR_CONFIG; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Calculate the thresholds. Since speed is in Mbps, simplify 60962306a36Sopenharmony_ci * the calculation by multiplying size/speed by 1000 for result 61062306a36Sopenharmony_ci * to be in nsec before dividing by the scale in nsec. Set the 61162306a36Sopenharmony_ci * scale such that the LTR threshold fits in the register. 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci ltr_min = (1000 * size) / speed; 61462306a36Sopenharmony_ci ltr_max = ltr_min + tw_system; 61562306a36Sopenharmony_ci scale_min = (ltr_min / 1024) < 1024 ? IGC_LTRMINV_SCALE_1024 : 61662306a36Sopenharmony_ci IGC_LTRMINV_SCALE_32768; 61762306a36Sopenharmony_ci scale_max = (ltr_max / 1024) < 1024 ? IGC_LTRMAXV_SCALE_1024 : 61862306a36Sopenharmony_ci IGC_LTRMAXV_SCALE_32768; 61962306a36Sopenharmony_ci ltr_min /= scale_min == IGC_LTRMINV_SCALE_1024 ? 1024 : 32768; 62062306a36Sopenharmony_ci ltr_min -= 1; 62162306a36Sopenharmony_ci ltr_max /= scale_max == IGC_LTRMAXV_SCALE_1024 ? 1024 : 32768; 62262306a36Sopenharmony_ci ltr_max -= 1; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Only write the LTR thresholds if they differ from before. */ 62562306a36Sopenharmony_ci ltrv = rd32(IGC_LTRMINV); 62662306a36Sopenharmony_ci if (ltr_min != (ltrv & IGC_LTRMINV_LTRV_MASK)) { 62762306a36Sopenharmony_ci ltrv = IGC_LTRMINV_LSNP_REQ | ltr_min | 62862306a36Sopenharmony_ci (scale_min << IGC_LTRMINV_SCALE_SHIFT); 62962306a36Sopenharmony_ci wr32(IGC_LTRMINV, ltrv); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ltrv = rd32(IGC_LTRMAXV); 63362306a36Sopenharmony_ci if (ltr_max != (ltrv & IGC_LTRMAXV_LTRV_MASK)) { 63462306a36Sopenharmony_ci ltrv = IGC_LTRMAXV_LSNP_REQ | ltr_max | 63562306a36Sopenharmony_ci (scale_max << IGC_LTRMAXV_SCALE_SHIFT); 63662306a36Sopenharmony_ci wr32(IGC_LTRMAXV, ltrv); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return IGC_SUCCESS; 64162306a36Sopenharmony_ci} 642