162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2018 Intel Corporation */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "igc_mac.h" 562306a36Sopenharmony_ci#include "igc_nvm.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/** 862306a36Sopenharmony_ci * igc_poll_eerd_eewr_done - Poll for EEPROM read/write completion 962306a36Sopenharmony_ci * @hw: pointer to the HW structure 1062306a36Sopenharmony_ci * @ee_reg: EEPROM flag for polling 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Polls the EEPROM status bit for either read or write completion based 1362306a36Sopenharmony_ci * upon the value of 'ee_reg'. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_cistatic s32 igc_poll_eerd_eewr_done(struct igc_hw *hw, int ee_reg) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci s32 ret_val = -IGC_ERR_NVM; 1862306a36Sopenharmony_ci u32 attempts = 100000; 1962306a36Sopenharmony_ci u32 i, reg = 0; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci for (i = 0; i < attempts; i++) { 2262306a36Sopenharmony_ci if (ee_reg == IGC_NVM_POLL_READ) 2362306a36Sopenharmony_ci reg = rd32(IGC_EERD); 2462306a36Sopenharmony_ci else 2562306a36Sopenharmony_ci reg = rd32(IGC_EEWR); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (reg & IGC_NVM_RW_REG_DONE) { 2862306a36Sopenharmony_ci ret_val = 0; 2962306a36Sopenharmony_ci break; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci udelay(5); 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return ret_val; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/** 3962306a36Sopenharmony_ci * igc_acquire_nvm - Generic request for access to EEPROM 4062306a36Sopenharmony_ci * @hw: pointer to the HW structure 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Set the EEPROM access request bit and wait for EEPROM access grant bit. 4362306a36Sopenharmony_ci * Return successful if access grant bit set, else clear the request for 4462306a36Sopenharmony_ci * EEPROM access and return -IGC_ERR_NVM (-1). 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cis32 igc_acquire_nvm(struct igc_hw *hw) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci s32 timeout = IGC_NVM_GRANT_ATTEMPTS; 4962306a36Sopenharmony_ci u32 eecd = rd32(IGC_EECD); 5062306a36Sopenharmony_ci s32 ret_val = 0; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci wr32(IGC_EECD, eecd | IGC_EECD_REQ); 5362306a36Sopenharmony_ci eecd = rd32(IGC_EECD); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci while (timeout) { 5662306a36Sopenharmony_ci if (eecd & IGC_EECD_GNT) 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci udelay(5); 5962306a36Sopenharmony_ci eecd = rd32(IGC_EECD); 6062306a36Sopenharmony_ci timeout--; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (!timeout) { 6462306a36Sopenharmony_ci eecd &= ~IGC_EECD_REQ; 6562306a36Sopenharmony_ci wr32(IGC_EECD, eecd); 6662306a36Sopenharmony_ci hw_dbg("Could not acquire NVM grant\n"); 6762306a36Sopenharmony_ci ret_val = -IGC_ERR_NVM; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return ret_val; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/** 7462306a36Sopenharmony_ci * igc_release_nvm - Release exclusive access to EEPROM 7562306a36Sopenharmony_ci * @hw: pointer to the HW structure 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * Stop any current commands to the EEPROM and clear the EEPROM request bit. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_civoid igc_release_nvm(struct igc_hw *hw) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32 eecd; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci eecd = rd32(IGC_EECD); 8462306a36Sopenharmony_ci eecd &= ~IGC_EECD_REQ; 8562306a36Sopenharmony_ci wr32(IGC_EECD, eecd); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * igc_read_nvm_eerd - Reads EEPROM using EERD register 9062306a36Sopenharmony_ci * @hw: pointer to the HW structure 9162306a36Sopenharmony_ci * @offset: offset of word in the EEPROM to read 9262306a36Sopenharmony_ci * @words: number of words to read 9362306a36Sopenharmony_ci * @data: word read from the EEPROM 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Reads a 16 bit word from the EEPROM using the EERD register. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cis32 igc_read_nvm_eerd(struct igc_hw *hw, u16 offset, u16 words, u16 *data) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct igc_nvm_info *nvm = &hw->nvm; 10062306a36Sopenharmony_ci u32 i, eerd = 0; 10162306a36Sopenharmony_ci s32 ret_val = 0; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* A check for invalid values: offset too large, too many words, 10462306a36Sopenharmony_ci * and not enough words. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci if (offset >= nvm->word_size || (words > (nvm->word_size - offset)) || 10762306a36Sopenharmony_ci words == 0) { 10862306a36Sopenharmony_ci hw_dbg("nvm parameter(s) out of bounds\n"); 10962306a36Sopenharmony_ci ret_val = -IGC_ERR_NVM; 11062306a36Sopenharmony_ci goto out; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (i = 0; i < words; i++) { 11462306a36Sopenharmony_ci eerd = ((offset + i) << IGC_NVM_RW_ADDR_SHIFT) + 11562306a36Sopenharmony_ci IGC_NVM_RW_REG_START; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci wr32(IGC_EERD, eerd); 11862306a36Sopenharmony_ci ret_val = igc_poll_eerd_eewr_done(hw, IGC_NVM_POLL_READ); 11962306a36Sopenharmony_ci if (ret_val) 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci data[i] = (rd32(IGC_EERD) >> IGC_NVM_RW_REG_DATA); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciout: 12662306a36Sopenharmony_ci return ret_val; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * igc_read_mac_addr - Read device MAC address 13162306a36Sopenharmony_ci * @hw: pointer to the HW structure 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cis32 igc_read_mac_addr(struct igc_hw *hw) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci u32 rar_high; 13662306a36Sopenharmony_ci u32 rar_low; 13762306a36Sopenharmony_ci u16 i; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci rar_high = rd32(IGC_RAH(0)); 14062306a36Sopenharmony_ci rar_low = rd32(IGC_RAL(0)); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci for (i = 0; i < IGC_RAL_MAC_ADDR_LEN; i++) 14362306a36Sopenharmony_ci hw->mac.perm_addr[i] = (u8)(rar_low >> (i * 8)); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci for (i = 0; i < IGC_RAH_MAC_ADDR_LEN; i++) 14662306a36Sopenharmony_ci hw->mac.perm_addr[i + 4] = (u8)(rar_high >> (i * 8)); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 14962306a36Sopenharmony_ci hw->mac.addr[i] = hw->mac.perm_addr[i]; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * igc_validate_nvm_checksum - Validate EEPROM checksum 15662306a36Sopenharmony_ci * @hw: pointer to the HW structure 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Calculates the EEPROM checksum by reading/adding each word of the EEPROM 15962306a36Sopenharmony_ci * and then verifies that the sum of the EEPROM is equal to 0xBABA. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cis32 igc_validate_nvm_checksum(struct igc_hw *hw) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci u16 checksum = 0; 16462306a36Sopenharmony_ci u16 i, nvm_data; 16562306a36Sopenharmony_ci s32 ret_val = 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) { 16862306a36Sopenharmony_ci ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); 16962306a36Sopenharmony_ci if (ret_val) { 17062306a36Sopenharmony_ci hw_dbg("NVM Read Error\n"); 17162306a36Sopenharmony_ci goto out; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci checksum += nvm_data; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (checksum != (u16)NVM_SUM) { 17762306a36Sopenharmony_ci hw_dbg("NVM Checksum Invalid\n"); 17862306a36Sopenharmony_ci ret_val = -IGC_ERR_NVM; 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciout: 18362306a36Sopenharmony_ci return ret_val; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/** 18762306a36Sopenharmony_ci * igc_update_nvm_checksum - Update EEPROM checksum 18862306a36Sopenharmony_ci * @hw: pointer to the HW structure 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * Updates the EEPROM checksum by reading/adding each word of the EEPROM 19162306a36Sopenharmony_ci * up to the checksum. Then calculates the EEPROM checksum and writes the 19262306a36Sopenharmony_ci * value to the EEPROM. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_cis32 igc_update_nvm_checksum(struct igc_hw *hw) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci u16 checksum = 0; 19762306a36Sopenharmony_ci u16 i, nvm_data; 19862306a36Sopenharmony_ci s32 ret_val; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci for (i = 0; i < NVM_CHECKSUM_REG; i++) { 20162306a36Sopenharmony_ci ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); 20262306a36Sopenharmony_ci if (ret_val) { 20362306a36Sopenharmony_ci hw_dbg("NVM Read Error while updating checksum.\n"); 20462306a36Sopenharmony_ci goto out; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci checksum += nvm_data; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci checksum = (u16)NVM_SUM - checksum; 20962306a36Sopenharmony_ci ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum); 21062306a36Sopenharmony_ci if (ret_val) 21162306a36Sopenharmony_ci hw_dbg("NVM Write Error while updating checksum.\n"); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciout: 21462306a36Sopenharmony_ci return ret_val; 21562306a36Sopenharmony_ci} 216