18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2018 Intel Corporation */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/delay.h> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include "igc_hw.h" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/** 98c2ecf20Sopenharmony_ci * igc_get_hw_semaphore_i225 - Acquire hardware semaphore 108c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Acquire the necessary semaphores for exclusive access to the EEPROM. 138c2ecf20Sopenharmony_ci * Set the EEPROM access request bit and wait for EEPROM access grant bit. 148c2ecf20Sopenharmony_ci * Return successful if access grant bit set, else clear the request for 158c2ecf20Sopenharmony_ci * EEPROM access and return -IGC_ERR_NVM (-1). 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_cistatic s32 igc_acquire_nvm_i225(struct igc_hw *hw) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci return igc_acquire_swfw_sync_i225(hw, IGC_SWFW_EEP_SM); 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/** 238c2ecf20Sopenharmony_ci * igc_release_nvm_i225 - Release exclusive access to EEPROM 248c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Stop any current commands to the EEPROM and clear the EEPROM request bit, 278c2ecf20Sopenharmony_ci * then release the semaphores acquired. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistatic void igc_release_nvm_i225(struct igc_hw *hw) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci igc_release_swfw_sync_i225(hw, IGC_SWFW_EEP_SM); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/** 358c2ecf20Sopenharmony_ci * igc_get_hw_semaphore_i225 - Acquire hardware semaphore 368c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Acquire the HW semaphore to access the PHY or NVM 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic s32 igc_get_hw_semaphore_i225(struct igc_hw *hw) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci s32 timeout = hw->nvm.word_size + 1; 438c2ecf20Sopenharmony_ci s32 i = 0; 448c2ecf20Sopenharmony_ci u32 swsm; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Get the SW semaphore */ 478c2ecf20Sopenharmony_ci while (i < timeout) { 488c2ecf20Sopenharmony_ci swsm = rd32(IGC_SWSM); 498c2ecf20Sopenharmony_ci if (!(swsm & IGC_SWSM_SMBI)) 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci usleep_range(500, 600); 538c2ecf20Sopenharmony_ci i++; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (i == timeout) { 578c2ecf20Sopenharmony_ci /* In rare circumstances, the SW semaphore may already be held 588c2ecf20Sopenharmony_ci * unintentionally. Clear the semaphore once before giving up. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci if (hw->dev_spec._base.clear_semaphore_once) { 618c2ecf20Sopenharmony_ci hw->dev_spec._base.clear_semaphore_once = false; 628c2ecf20Sopenharmony_ci igc_put_hw_semaphore(hw); 638c2ecf20Sopenharmony_ci for (i = 0; i < timeout; i++) { 648c2ecf20Sopenharmony_ci swsm = rd32(IGC_SWSM); 658c2ecf20Sopenharmony_ci if (!(swsm & IGC_SWSM_SMBI)) 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci usleep_range(500, 600); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* If we do not have the semaphore here, we have to give up. */ 738c2ecf20Sopenharmony_ci if (i == timeout) { 748c2ecf20Sopenharmony_ci hw_dbg("Driver can't access device - SMBI bit is set.\n"); 758c2ecf20Sopenharmony_ci return -IGC_ERR_NVM; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Get the FW semaphore. */ 808c2ecf20Sopenharmony_ci for (i = 0; i < timeout; i++) { 818c2ecf20Sopenharmony_ci swsm = rd32(IGC_SWSM); 828c2ecf20Sopenharmony_ci wr32(IGC_SWSM, swsm | IGC_SWSM_SWESMBI); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Semaphore acquired if bit latched */ 858c2ecf20Sopenharmony_ci if (rd32(IGC_SWSM) & IGC_SWSM_SWESMBI) 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci usleep_range(500, 600); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (i == timeout) { 928c2ecf20Sopenharmony_ci /* Release semaphores */ 938c2ecf20Sopenharmony_ci igc_put_hw_semaphore(hw); 948c2ecf20Sopenharmony_ci hw_dbg("Driver can't access the NVM\n"); 958c2ecf20Sopenharmony_ci return -IGC_ERR_NVM; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/** 1028c2ecf20Sopenharmony_ci * igc_acquire_swfw_sync_i225 - Acquire SW/FW semaphore 1038c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 1048c2ecf20Sopenharmony_ci * @mask: specifies which semaphore to acquire 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * Acquire the SW/FW semaphore to access the PHY or NVM. The mask 1078c2ecf20Sopenharmony_ci * will also specify which port we're acquiring the lock for. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cis32 igc_acquire_swfw_sync_i225(struct igc_hw *hw, u16 mask) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci s32 i = 0, timeout = 200; 1128c2ecf20Sopenharmony_ci u32 fwmask = mask << 16; 1138c2ecf20Sopenharmony_ci u32 swmask = mask; 1148c2ecf20Sopenharmony_ci s32 ret_val = 0; 1158c2ecf20Sopenharmony_ci u32 swfw_sync; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci while (i < timeout) { 1188c2ecf20Sopenharmony_ci if (igc_get_hw_semaphore_i225(hw)) { 1198c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_SWFW_SYNC; 1208c2ecf20Sopenharmony_ci goto out; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci swfw_sync = rd32(IGC_SW_FW_SYNC); 1248c2ecf20Sopenharmony_ci if (!(swfw_sync & (fwmask | swmask))) 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Firmware currently using resource (fwmask) */ 1288c2ecf20Sopenharmony_ci igc_put_hw_semaphore(hw); 1298c2ecf20Sopenharmony_ci mdelay(5); 1308c2ecf20Sopenharmony_ci i++; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (i == timeout) { 1348c2ecf20Sopenharmony_ci hw_dbg("Driver can't access resource, SW_FW_SYNC timeout.\n"); 1358c2ecf20Sopenharmony_ci ret_val = -IGC_ERR_SWFW_SYNC; 1368c2ecf20Sopenharmony_ci goto out; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci swfw_sync |= swmask; 1408c2ecf20Sopenharmony_ci wr32(IGC_SW_FW_SYNC, swfw_sync); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci igc_put_hw_semaphore(hw); 1438c2ecf20Sopenharmony_ciout: 1448c2ecf20Sopenharmony_ci return ret_val; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/** 1488c2ecf20Sopenharmony_ci * igc_release_swfw_sync_i225 - Release SW/FW semaphore 1498c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 1508c2ecf20Sopenharmony_ci * @mask: specifies which semaphore to acquire 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * Release the SW/FW semaphore used to access the PHY or NVM. The mask 1538c2ecf20Sopenharmony_ci * will also specify which port we're releasing the lock for. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_civoid igc_release_swfw_sync_i225(struct igc_hw *hw, u16 mask) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci u32 swfw_sync; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Releasing the resource requires first getting the HW semaphore. 1608c2ecf20Sopenharmony_ci * If we fail to get the semaphore, there is nothing we can do, 1618c2ecf20Sopenharmony_ci * except log an error and quit. We are not allowed to hang here 1628c2ecf20Sopenharmony_ci * indefinitely, as it may cause denial of service or system crash. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci if (igc_get_hw_semaphore_i225(hw)) { 1658c2ecf20Sopenharmony_ci hw_dbg("Failed to release SW_FW_SYNC.\n"); 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci swfw_sync = rd32(IGC_SW_FW_SYNC); 1708c2ecf20Sopenharmony_ci swfw_sync &= ~mask; 1718c2ecf20Sopenharmony_ci wr32(IGC_SW_FW_SYNC, swfw_sync); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci igc_put_hw_semaphore(hw); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/** 1778c2ecf20Sopenharmony_ci * igc_read_nvm_srrd_i225 - Reads Shadow Ram using EERD register 1788c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 1798c2ecf20Sopenharmony_ci * @offset: offset of word in the Shadow Ram to read 1808c2ecf20Sopenharmony_ci * @words: number of words to read 1818c2ecf20Sopenharmony_ci * @data: word read from the Shadow Ram 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Reads a 16 bit word from the Shadow Ram using the EERD register. 1848c2ecf20Sopenharmony_ci * Uses necessary synchronization semaphores. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_cistatic s32 igc_read_nvm_srrd_i225(struct igc_hw *hw, u16 offset, u16 words, 1878c2ecf20Sopenharmony_ci u16 *data) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci s32 status = 0; 1908c2ecf20Sopenharmony_ci u16 i, count; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* We cannot hold synchronization semaphores for too long, 1938c2ecf20Sopenharmony_ci * because of forceful takeover procedure. However it is more efficient 1948c2ecf20Sopenharmony_ci * to read in bursts than synchronizing access for each word. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci for (i = 0; i < words; i += IGC_EERD_EEWR_MAX_COUNT) { 1978c2ecf20Sopenharmony_ci count = (words - i) / IGC_EERD_EEWR_MAX_COUNT > 0 ? 1988c2ecf20Sopenharmony_ci IGC_EERD_EEWR_MAX_COUNT : (words - i); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci status = hw->nvm.ops.acquire(hw); 2018c2ecf20Sopenharmony_ci if (status) 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci status = igc_read_nvm_eerd(hw, offset, count, data + i); 2058c2ecf20Sopenharmony_ci hw->nvm.ops.release(hw); 2068c2ecf20Sopenharmony_ci if (status) 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return status; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/** 2148c2ecf20Sopenharmony_ci * igc_write_nvm_srwr - Write to Shadow Ram using EEWR 2158c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 2168c2ecf20Sopenharmony_ci * @offset: offset within the Shadow Ram to be written to 2178c2ecf20Sopenharmony_ci * @words: number of words to write 2188c2ecf20Sopenharmony_ci * @data: 16 bit word(s) to be written to the Shadow Ram 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * Writes data to Shadow Ram at offset using EEWR register. 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * If igc_update_nvm_checksum is not called after this function , the 2238c2ecf20Sopenharmony_ci * Shadow Ram will most likely contain an invalid checksum. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_cistatic s32 igc_write_nvm_srwr(struct igc_hw *hw, u16 offset, u16 words, 2268c2ecf20Sopenharmony_ci u16 *data) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct igc_nvm_info *nvm = &hw->nvm; 2298c2ecf20Sopenharmony_ci s32 ret_val = -IGC_ERR_NVM; 2308c2ecf20Sopenharmony_ci u32 attempts = 100000; 2318c2ecf20Sopenharmony_ci u32 i, k, eewr = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* A check for invalid values: offset too large, too many words, 2348c2ecf20Sopenharmony_ci * too many words for the offset, and not enough words. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci if (offset >= nvm->word_size || (words > (nvm->word_size - offset)) || 2378c2ecf20Sopenharmony_ci words == 0) { 2388c2ecf20Sopenharmony_ci hw_dbg("nvm parameter(s) out of bounds\n"); 2398c2ecf20Sopenharmony_ci goto out; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci for (i = 0; i < words; i++) { 2438c2ecf20Sopenharmony_ci eewr = ((offset + i) << IGC_NVM_RW_ADDR_SHIFT) | 2448c2ecf20Sopenharmony_ci (data[i] << IGC_NVM_RW_REG_DATA) | 2458c2ecf20Sopenharmony_ci IGC_NVM_RW_REG_START; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci wr32(IGC_SRWR, eewr); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for (k = 0; k < attempts; k++) { 2508c2ecf20Sopenharmony_ci if (IGC_NVM_RW_REG_DONE & 2518c2ecf20Sopenharmony_ci rd32(IGC_SRWR)) { 2528c2ecf20Sopenharmony_ci ret_val = 0; 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci udelay(5); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (ret_val) { 2598c2ecf20Sopenharmony_ci hw_dbg("Shadow RAM write EEWR timed out\n"); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciout: 2658c2ecf20Sopenharmony_ci return ret_val; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * igc_write_nvm_srwr_i225 - Write to Shadow RAM using EEWR 2708c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 2718c2ecf20Sopenharmony_ci * @offset: offset within the Shadow RAM to be written to 2728c2ecf20Sopenharmony_ci * @words: number of words to write 2738c2ecf20Sopenharmony_ci * @data: 16 bit word(s) to be written to the Shadow RAM 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Writes data to Shadow RAM at offset using EEWR register. 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * If igc_update_nvm_checksum is not called after this function , the 2788c2ecf20Sopenharmony_ci * data will not be committed to FLASH and also Shadow RAM will most likely 2798c2ecf20Sopenharmony_ci * contain an invalid checksum. 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * If error code is returned, data and Shadow RAM may be inconsistent - buffer 2828c2ecf20Sopenharmony_ci * partially written. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic s32 igc_write_nvm_srwr_i225(struct igc_hw *hw, u16 offset, u16 words, 2858c2ecf20Sopenharmony_ci u16 *data) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci s32 status = 0; 2888c2ecf20Sopenharmony_ci u16 i, count; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* We cannot hold synchronization semaphores for too long, 2918c2ecf20Sopenharmony_ci * because of forceful takeover procedure. However it is more efficient 2928c2ecf20Sopenharmony_ci * to write in bursts than synchronizing access for each word. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci for (i = 0; i < words; i += IGC_EERD_EEWR_MAX_COUNT) { 2958c2ecf20Sopenharmony_ci count = (words - i) / IGC_EERD_EEWR_MAX_COUNT > 0 ? 2968c2ecf20Sopenharmony_ci IGC_EERD_EEWR_MAX_COUNT : (words - i); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci status = hw->nvm.ops.acquire(hw); 2998c2ecf20Sopenharmony_ci if (status) 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci status = igc_write_nvm_srwr(hw, offset, count, data + i); 3038c2ecf20Sopenharmony_ci hw->nvm.ops.release(hw); 3048c2ecf20Sopenharmony_ci if (status) 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return status; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/** 3128c2ecf20Sopenharmony_ci * igc_validate_nvm_checksum_i225 - Validate EEPROM checksum 3138c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * Calculates the EEPROM checksum by reading/adding each word of the EEPROM 3168c2ecf20Sopenharmony_ci * and then verifies that the sum of the EEPROM is equal to 0xBABA. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_cistatic s32 igc_validate_nvm_checksum_i225(struct igc_hw *hw) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci s32 (*read_op_ptr)(struct igc_hw *hw, u16 offset, u16 count, 3218c2ecf20Sopenharmony_ci u16 *data); 3228c2ecf20Sopenharmony_ci s32 status = 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci status = hw->nvm.ops.acquire(hw); 3258c2ecf20Sopenharmony_ci if (status) 3268c2ecf20Sopenharmony_ci goto out; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* Replace the read function with semaphore grabbing with 3298c2ecf20Sopenharmony_ci * the one that skips this for a while. 3308c2ecf20Sopenharmony_ci * We have semaphore taken already here. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci read_op_ptr = hw->nvm.ops.read; 3338c2ecf20Sopenharmony_ci hw->nvm.ops.read = igc_read_nvm_eerd; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci status = igc_validate_nvm_checksum(hw); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Revert original read operation. */ 3388c2ecf20Sopenharmony_ci hw->nvm.ops.read = read_op_ptr; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci hw->nvm.ops.release(hw); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciout: 3438c2ecf20Sopenharmony_ci return status; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/** 3478c2ecf20Sopenharmony_ci * igc_pool_flash_update_done_i225 - Pool FLUDONE status 3488c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_cistatic s32 igc_pool_flash_update_done_i225(struct igc_hw *hw) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci s32 ret_val = -IGC_ERR_NVM; 3538c2ecf20Sopenharmony_ci u32 i, reg; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci for (i = 0; i < IGC_FLUDONE_ATTEMPTS; i++) { 3568c2ecf20Sopenharmony_ci reg = rd32(IGC_EECD); 3578c2ecf20Sopenharmony_ci if (reg & IGC_EECD_FLUDONE_I225) { 3588c2ecf20Sopenharmony_ci ret_val = 0; 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci udelay(5); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return ret_val; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/** 3688c2ecf20Sopenharmony_ci * igc_update_flash_i225 - Commit EEPROM to the flash 3698c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_cistatic s32 igc_update_flash_i225(struct igc_hw *hw) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci s32 ret_val = 0; 3748c2ecf20Sopenharmony_ci u32 flup; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ret_val = igc_pool_flash_update_done_i225(hw); 3778c2ecf20Sopenharmony_ci if (ret_val == -IGC_ERR_NVM) { 3788c2ecf20Sopenharmony_ci hw_dbg("Flash update time out\n"); 3798c2ecf20Sopenharmony_ci goto out; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci flup = rd32(IGC_EECD) | IGC_EECD_FLUPD_I225; 3838c2ecf20Sopenharmony_ci wr32(IGC_EECD, flup); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ret_val = igc_pool_flash_update_done_i225(hw); 3868c2ecf20Sopenharmony_ci if (ret_val) 3878c2ecf20Sopenharmony_ci hw_dbg("Flash update time out\n"); 3888c2ecf20Sopenharmony_ci else 3898c2ecf20Sopenharmony_ci hw_dbg("Flash update complete\n"); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ciout: 3928c2ecf20Sopenharmony_ci return ret_val; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/** 3968c2ecf20Sopenharmony_ci * igc_update_nvm_checksum_i225 - Update EEPROM checksum 3978c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Updates the EEPROM checksum by reading/adding each word of the EEPROM 4008c2ecf20Sopenharmony_ci * up to the checksum. Then calculates the EEPROM checksum and writes the 4018c2ecf20Sopenharmony_ci * value to the EEPROM. Next commit EEPROM data onto the Flash. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_cistatic s32 igc_update_nvm_checksum_i225(struct igc_hw *hw) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci u16 checksum = 0; 4068c2ecf20Sopenharmony_ci s32 ret_val = 0; 4078c2ecf20Sopenharmony_ci u16 i, nvm_data; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Read the first word from the EEPROM. If this times out or fails, do 4108c2ecf20Sopenharmony_ci * not continue or we could be in for a very long wait while every 4118c2ecf20Sopenharmony_ci * EEPROM read fails 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci ret_val = igc_read_nvm_eerd(hw, 0, 1, &nvm_data); 4148c2ecf20Sopenharmony_ci if (ret_val) { 4158c2ecf20Sopenharmony_ci hw_dbg("EEPROM read failed\n"); 4168c2ecf20Sopenharmony_ci goto out; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret_val = hw->nvm.ops.acquire(hw); 4208c2ecf20Sopenharmony_ci if (ret_val) 4218c2ecf20Sopenharmony_ci goto out; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Do not use hw->nvm.ops.write, hw->nvm.ops.read 4248c2ecf20Sopenharmony_ci * because we do not want to take the synchronization 4258c2ecf20Sopenharmony_ci * semaphores twice here. 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci for (i = 0; i < NVM_CHECKSUM_REG; i++) { 4298c2ecf20Sopenharmony_ci ret_val = igc_read_nvm_eerd(hw, i, 1, &nvm_data); 4308c2ecf20Sopenharmony_ci if (ret_val) { 4318c2ecf20Sopenharmony_ci hw->nvm.ops.release(hw); 4328c2ecf20Sopenharmony_ci hw_dbg("NVM Read Error while updating checksum.\n"); 4338c2ecf20Sopenharmony_ci goto out; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci checksum += nvm_data; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci checksum = (u16)NVM_SUM - checksum; 4388c2ecf20Sopenharmony_ci ret_val = igc_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1, 4398c2ecf20Sopenharmony_ci &checksum); 4408c2ecf20Sopenharmony_ci if (ret_val) { 4418c2ecf20Sopenharmony_ci hw->nvm.ops.release(hw); 4428c2ecf20Sopenharmony_ci hw_dbg("NVM Write Error while updating checksum.\n"); 4438c2ecf20Sopenharmony_ci goto out; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci hw->nvm.ops.release(hw); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret_val = igc_update_flash_i225(hw); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ciout: 4518c2ecf20Sopenharmony_ci return ret_val; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/** 4558c2ecf20Sopenharmony_ci * igc_get_flash_presence_i225 - Check if flash device is detected 4568c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_cibool igc_get_flash_presence_i225(struct igc_hw *hw) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci bool ret_val = false; 4618c2ecf20Sopenharmony_ci u32 eec = 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci eec = rd32(IGC_EECD); 4648c2ecf20Sopenharmony_ci if (eec & IGC_EECD_FLASH_DETECTED_I225) 4658c2ecf20Sopenharmony_ci ret_val = true; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return ret_val; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/** 4718c2ecf20Sopenharmony_ci * igc_init_nvm_params_i225 - Init NVM func ptrs. 4728c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_cis32 igc_init_nvm_params_i225(struct igc_hw *hw) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct igc_nvm_info *nvm = &hw->nvm; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci nvm->ops.acquire = igc_acquire_nvm_i225; 4798c2ecf20Sopenharmony_ci nvm->ops.release = igc_release_nvm_i225; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* NVM Function Pointers */ 4828c2ecf20Sopenharmony_ci if (igc_get_flash_presence_i225(hw)) { 4838c2ecf20Sopenharmony_ci hw->nvm.type = igc_nvm_flash_hw; 4848c2ecf20Sopenharmony_ci nvm->ops.read = igc_read_nvm_srrd_i225; 4858c2ecf20Sopenharmony_ci nvm->ops.write = igc_write_nvm_srwr_i225; 4868c2ecf20Sopenharmony_ci nvm->ops.validate = igc_validate_nvm_checksum_i225; 4878c2ecf20Sopenharmony_ci nvm->ops.update = igc_update_nvm_checksum_i225; 4888c2ecf20Sopenharmony_ci } else { 4898c2ecf20Sopenharmony_ci hw->nvm.type = igc_nvm_invm; 4908c2ecf20Sopenharmony_ci nvm->ops.read = igc_read_nvm_eerd; 4918c2ecf20Sopenharmony_ci nvm->ops.write = NULL; 4928c2ecf20Sopenharmony_ci nvm->ops.validate = NULL; 4938c2ecf20Sopenharmony_ci nvm->ops.update = NULL; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * igc_set_eee_i225 - Enable/disable EEE support 5008c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 5018c2ecf20Sopenharmony_ci * @adv2p5G: boolean flag enabling 2.5G EEE advertisement 5028c2ecf20Sopenharmony_ci * @adv1G: boolean flag enabling 1G EEE advertisement 5038c2ecf20Sopenharmony_ci * @adv100M: boolean flag enabling 100M EEE advertisement 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * Enable/disable EEE based on setting in dev_spec structure. 5068c2ecf20Sopenharmony_ci **/ 5078c2ecf20Sopenharmony_cis32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G, 5088c2ecf20Sopenharmony_ci bool adv100M) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci u32 ipcnfg, eeer; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ipcnfg = rd32(IGC_IPCNFG); 5138c2ecf20Sopenharmony_ci eeer = rd32(IGC_EEER); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* enable or disable per user setting */ 5168c2ecf20Sopenharmony_ci if (hw->dev_spec._base.eee_enable) { 5178c2ecf20Sopenharmony_ci u32 eee_su = rd32(IGC_EEE_SU); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (adv100M) 5208c2ecf20Sopenharmony_ci ipcnfg |= IGC_IPCNFG_EEE_100M_AN; 5218c2ecf20Sopenharmony_ci else 5228c2ecf20Sopenharmony_ci ipcnfg &= ~IGC_IPCNFG_EEE_100M_AN; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (adv1G) 5258c2ecf20Sopenharmony_ci ipcnfg |= IGC_IPCNFG_EEE_1G_AN; 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci ipcnfg &= ~IGC_IPCNFG_EEE_1G_AN; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (adv2p5G) 5308c2ecf20Sopenharmony_ci ipcnfg |= IGC_IPCNFG_EEE_2_5G_AN; 5318c2ecf20Sopenharmony_ci else 5328c2ecf20Sopenharmony_ci ipcnfg &= ~IGC_IPCNFG_EEE_2_5G_AN; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci eeer |= (IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN | 5358c2ecf20Sopenharmony_ci IGC_EEER_LPI_FC); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* This bit should not be set in normal operation. */ 5388c2ecf20Sopenharmony_ci if (eee_su & IGC_EEE_SU_LPI_CLK_STP) 5398c2ecf20Sopenharmony_ci hw_dbg("LPI Clock Stop Bit should not be set!\n"); 5408c2ecf20Sopenharmony_ci } else { 5418c2ecf20Sopenharmony_ci ipcnfg &= ~(IGC_IPCNFG_EEE_2_5G_AN | IGC_IPCNFG_EEE_1G_AN | 5428c2ecf20Sopenharmony_ci IGC_IPCNFG_EEE_100M_AN); 5438c2ecf20Sopenharmony_ci eeer &= ~(IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN | 5448c2ecf20Sopenharmony_ci IGC_EEER_LPI_FC); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci wr32(IGC_IPCNFG, ipcnfg); 5478c2ecf20Sopenharmony_ci wr32(IGC_EEER, eeer); 5488c2ecf20Sopenharmony_ci rd32(IGC_IPCNFG); 5498c2ecf20Sopenharmony_ci rd32(IGC_EEER); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return IGC_SUCCESS; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/* igc_set_ltr_i225 - Set Latency Tolerance Reporting thresholds 5558c2ecf20Sopenharmony_ci * @hw: pointer to the HW structure 5568c2ecf20Sopenharmony_ci * @link: bool indicating link status 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * Set the LTR thresholds based on the link speed (Mbps), EEE, and DMAC 5598c2ecf20Sopenharmony_ci * settings, otherwise specify that there is no LTR requirement. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_cis32 igc_set_ltr_i225(struct igc_hw *hw, bool link) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci u32 tw_system, ltrc, ltrv, ltr_min, ltr_max, scale_min, scale_max; 5648c2ecf20Sopenharmony_ci u16 speed, duplex; 5658c2ecf20Sopenharmony_ci s32 size; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* If we do not have link, LTR thresholds are zero. */ 5688c2ecf20Sopenharmony_ci if (link) { 5698c2ecf20Sopenharmony_ci hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Check if using copper interface with EEE enabled or if the 5728c2ecf20Sopenharmony_ci * link speed is 10 Mbps. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci if (hw->dev_spec._base.eee_enable && 5758c2ecf20Sopenharmony_ci speed != SPEED_10) { 5768c2ecf20Sopenharmony_ci /* EEE enabled, so send LTRMAX threshold. */ 5778c2ecf20Sopenharmony_ci ltrc = rd32(IGC_LTRC) | 5788c2ecf20Sopenharmony_ci IGC_LTRC_EEEMS_EN; 5798c2ecf20Sopenharmony_ci wr32(IGC_LTRC, ltrc); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Calculate tw_system (nsec). */ 5828c2ecf20Sopenharmony_ci if (speed == SPEED_100) { 5838c2ecf20Sopenharmony_ci tw_system = ((rd32(IGC_EEE_SU) & 5848c2ecf20Sopenharmony_ci IGC_TW_SYSTEM_100_MASK) >> 5858c2ecf20Sopenharmony_ci IGC_TW_SYSTEM_100_SHIFT) * 500; 5868c2ecf20Sopenharmony_ci } else { 5878c2ecf20Sopenharmony_ci tw_system = (rd32(IGC_EEE_SU) & 5888c2ecf20Sopenharmony_ci IGC_TW_SYSTEM_1000_MASK) * 500; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci } else { 5918c2ecf20Sopenharmony_ci tw_system = 0; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Get the Rx packet buffer size. */ 5958c2ecf20Sopenharmony_ci size = rd32(IGC_RXPBS) & 5968c2ecf20Sopenharmony_ci IGC_RXPBS_SIZE_I225_MASK; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* Calculations vary based on DMAC settings. */ 5998c2ecf20Sopenharmony_ci if (rd32(IGC_DMACR) & IGC_DMACR_DMAC_EN) { 6008c2ecf20Sopenharmony_ci size -= (rd32(IGC_DMACR) & 6018c2ecf20Sopenharmony_ci IGC_DMACR_DMACTHR_MASK) >> 6028c2ecf20Sopenharmony_ci IGC_DMACR_DMACTHR_SHIFT; 6038c2ecf20Sopenharmony_ci /* Convert size to bits. */ 6048c2ecf20Sopenharmony_ci size *= 1024 * 8; 6058c2ecf20Sopenharmony_ci } else { 6068c2ecf20Sopenharmony_ci /* Convert size to bytes, subtract the MTU, and then 6078c2ecf20Sopenharmony_ci * convert the size to bits. 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_ci size *= 1024; 6108c2ecf20Sopenharmony_ci size *= 8; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (size < 0) { 6148c2ecf20Sopenharmony_ci hw_dbg("Invalid effective Rx buffer size %d\n", 6158c2ecf20Sopenharmony_ci size); 6168c2ecf20Sopenharmony_ci return -IGC_ERR_CONFIG; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* Calculate the thresholds. Since speed is in Mbps, simplify 6208c2ecf20Sopenharmony_ci * the calculation by multiplying size/speed by 1000 for result 6218c2ecf20Sopenharmony_ci * to be in nsec before dividing by the scale in nsec. Set the 6228c2ecf20Sopenharmony_ci * scale such that the LTR threshold fits in the register. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci ltr_min = (1000 * size) / speed; 6258c2ecf20Sopenharmony_ci ltr_max = ltr_min + tw_system; 6268c2ecf20Sopenharmony_ci scale_min = (ltr_min / 1024) < 1024 ? IGC_LTRMINV_SCALE_1024 : 6278c2ecf20Sopenharmony_ci IGC_LTRMINV_SCALE_32768; 6288c2ecf20Sopenharmony_ci scale_max = (ltr_max / 1024) < 1024 ? IGC_LTRMAXV_SCALE_1024 : 6298c2ecf20Sopenharmony_ci IGC_LTRMAXV_SCALE_32768; 6308c2ecf20Sopenharmony_ci ltr_min /= scale_min == IGC_LTRMINV_SCALE_1024 ? 1024 : 32768; 6318c2ecf20Sopenharmony_ci ltr_min -= 1; 6328c2ecf20Sopenharmony_ci ltr_max /= scale_max == IGC_LTRMAXV_SCALE_1024 ? 1024 : 32768; 6338c2ecf20Sopenharmony_ci ltr_max -= 1; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* Only write the LTR thresholds if they differ from before. */ 6368c2ecf20Sopenharmony_ci ltrv = rd32(IGC_LTRMINV); 6378c2ecf20Sopenharmony_ci if (ltr_min != (ltrv & IGC_LTRMINV_LTRV_MASK)) { 6388c2ecf20Sopenharmony_ci ltrv = IGC_LTRMINV_LSNP_REQ | ltr_min | 6398c2ecf20Sopenharmony_ci (scale_min << IGC_LTRMINV_SCALE_SHIFT); 6408c2ecf20Sopenharmony_ci wr32(IGC_LTRMINV, ltrv); 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci ltrv = rd32(IGC_LTRMAXV); 6448c2ecf20Sopenharmony_ci if (ltr_max != (ltrv & IGC_LTRMAXV_LTRV_MASK)) { 6458c2ecf20Sopenharmony_ci ltrv = IGC_LTRMAXV_LSNP_REQ | ltr_max | 6468c2ecf20Sopenharmony_ci (scale_max << IGC_LTRMAXV_SCALE_SHIFT); 6478c2ecf20Sopenharmony_ci wr32(IGC_LTRMAXV, ltrv); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return IGC_SUCCESS; 6528c2ecf20Sopenharmony_ci} 653