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