summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/intel/igc/igc_nvm.c
blob: 58f81aba0144665ba09d2a0aaf08c02a77d95760 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c)  2018 Intel Corporation */

#include "igc_mac.h"
#include "igc_nvm.h"

/**
 * igc_poll_eerd_eewr_done - Poll for EEPROM read/write completion
 * @hw: pointer to the HW structure
 * @ee_reg: EEPROM flag for polling
 *
 * Polls the EEPROM status bit for either read or write completion based
 * upon the value of 'ee_reg'.
 */
static s32 igc_poll_eerd_eewr_done(struct igc_hw *hw, int ee_reg)
{
	s32 ret_val = -IGC_ERR_NVM;
	u32 attempts = 100000;
	u32 i, reg = 0;

	for (i = 0; i < attempts; i++) {
		if (ee_reg == IGC_NVM_POLL_READ)
			reg = rd32(IGC_EERD);
		else
			reg = rd32(IGC_EEWR);

		if (reg & IGC_NVM_RW_REG_DONE) {
			ret_val = 0;
			break;
		}

		udelay(5);
	}

	return ret_val;
}

/**
 * igc_acquire_nvm - Generic request for access to EEPROM
 * @hw: pointer to the HW structure
 *
 * Set the EEPROM access request bit and wait for EEPROM access grant bit.
 * Return successful if access grant bit set, else clear the request for
 * EEPROM access and return -IGC_ERR_NVM (-1).
 */
s32 igc_acquire_nvm(struct igc_hw *hw)
{
	s32 timeout = IGC_NVM_GRANT_ATTEMPTS;
	u32 eecd = rd32(IGC_EECD);
	s32 ret_val = 0;

	wr32(IGC_EECD, eecd | IGC_EECD_REQ);
	eecd = rd32(IGC_EECD);

	while (timeout) {
		if (eecd & IGC_EECD_GNT)
			break;
		udelay(5);
		eecd = rd32(IGC_EECD);
		timeout--;
	}

	if (!timeout) {
		eecd &= ~IGC_EECD_REQ;
		wr32(IGC_EECD, eecd);
		hw_dbg("Could not acquire NVM grant\n");
		ret_val = -IGC_ERR_NVM;
	}

	return ret_val;
}

/**
 * igc_release_nvm - Release exclusive access to EEPROM
 * @hw: pointer to the HW structure
 *
 * Stop any current commands to the EEPROM and clear the EEPROM request bit.
 */
void igc_release_nvm(struct igc_hw *hw)
{
	u32 eecd;

	eecd = rd32(IGC_EECD);
	eecd &= ~IGC_EECD_REQ;
	wr32(IGC_EECD, eecd);
}

/**
 * igc_read_nvm_eerd - Reads EEPROM using EERD register
 * @hw: pointer to the HW structure
 * @offset: offset of word in the EEPROM to read
 * @words: number of words to read
 * @data: word read from the EEPROM
 *
 * Reads a 16 bit word from the EEPROM using the EERD register.
 */
s32 igc_read_nvm_eerd(struct igc_hw *hw, u16 offset, u16 words, u16 *data)
{
	struct igc_nvm_info *nvm = &hw->nvm;
	u32 i, eerd = 0;
	s32 ret_val = 0;

	/* A check for invalid values:  offset too large, too many words,
	 * and not enough words.
	 */
	if (offset >= nvm->word_size || (words > (nvm->word_size - offset)) ||
	    words == 0) {
		hw_dbg("nvm parameter(s) out of bounds\n");
		ret_val = -IGC_ERR_NVM;
		goto out;
	}

	for (i = 0; i < words; i++) {
		eerd = ((offset + i) << IGC_NVM_RW_ADDR_SHIFT) +
			IGC_NVM_RW_REG_START;

		wr32(IGC_EERD, eerd);
		ret_val = igc_poll_eerd_eewr_done(hw, IGC_NVM_POLL_READ);
		if (ret_val)
			break;

		data[i] = (rd32(IGC_EERD) >> IGC_NVM_RW_REG_DATA);
	}

out:
	return ret_val;
}

/**
 * igc_read_mac_addr - Read device MAC address
 * @hw: pointer to the HW structure
 */
s32 igc_read_mac_addr(struct igc_hw *hw)
{
	u32 rar_high;
	u32 rar_low;
	u16 i;

	rar_high = rd32(IGC_RAH(0));
	rar_low = rd32(IGC_RAL(0));

	for (i = 0; i < IGC_RAL_MAC_ADDR_LEN; i++)
		hw->mac.perm_addr[i] = (u8)(rar_low >> (i * 8));

	for (i = 0; i < IGC_RAH_MAC_ADDR_LEN; i++)
		hw->mac.perm_addr[i + 4] = (u8)(rar_high >> (i * 8));

	for (i = 0; i < ETH_ALEN; i++)
		hw->mac.addr[i] = hw->mac.perm_addr[i];

	return 0;
}

/**
 * igc_validate_nvm_checksum - Validate EEPROM checksum
 * @hw: pointer to the HW structure
 *
 * Calculates the EEPROM checksum by reading/adding each word of the EEPROM
 * and then verifies that the sum of the EEPROM is equal to 0xBABA.
 */
s32 igc_validate_nvm_checksum(struct igc_hw *hw)
{
	u16 checksum = 0;
	u16 i, nvm_data;
	s32 ret_val = 0;

	for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
		ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
		if (ret_val) {
			hw_dbg("NVM Read Error\n");
			goto out;
		}
		checksum += nvm_data;
	}

	if (checksum != (u16)NVM_SUM) {
		hw_dbg("NVM Checksum Invalid\n");
		ret_val = -IGC_ERR_NVM;
		goto out;
	}

out:
	return ret_val;
}

/**
 * igc_update_nvm_checksum - Update EEPROM checksum
 * @hw: pointer to the HW structure
 *
 * Updates the EEPROM checksum by reading/adding each word of the EEPROM
 * up to the checksum.  Then calculates the EEPROM checksum and writes the
 * value to the EEPROM.
 */
s32 igc_update_nvm_checksum(struct igc_hw *hw)
{
	u16 checksum = 0;
	u16 i, nvm_data;
	s32  ret_val;

	for (i = 0; i < NVM_CHECKSUM_REG; i++) {
		ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
		if (ret_val) {
			hw_dbg("NVM Read Error while updating checksum.\n");
			goto out;
		}
		checksum += nvm_data;
	}
	checksum = (u16)NVM_SUM - checksum;
	ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum);
	if (ret_val)
		hw_dbg("NVM Write Error while updating checksum.\n");

out:
	return ret_val;
}