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