]>
Commit | Line | Data |
---|---|---|
7e19d03b SL |
1 | From e4d37a59973992e085fd0f2453b8845ce2f9a98d Mon Sep 17 00:00:00 2001 |
2 | From: Sasha Levin <sashal@kernel.org> | |
3 | Date: Thu, 4 Jan 2024 16:16:52 +0200 | |
4 | Subject: e1000e: Workaround for sporadic MDI error on Meteor Lake systems | |
5 | ||
6 | From: Vitaly Lifshits <vitaly.lifshits@intel.com> | |
7 | ||
8 | [ Upstream commit 6dbdd4de0362c37e54e8b049781402e5a409e7d0 ] | |
9 | ||
10 | On some Meteor Lake systems accessing the PHY via the MDIO interface may | |
11 | result in an MDI error. This issue happens sporadically and in most cases | |
12 | a second access to the PHY via the MDIO interface results in success. | |
13 | ||
14 | As a workaround, introduce a retry counter which is set to 3 on Meteor | |
15 | Lake systems. The driver will only return an error if 3 consecutive PHY | |
16 | access attempts fail. The retry mechanism is disabled in specific flows, | |
17 | where MDI errors are expected. | |
18 | ||
19 | Fixes: cc23f4f0b6b9 ("e1000e: Add support for Meteor Lake") | |
20 | Suggested-by: Nikolay Mushayev <nikolay.mushayev@intel.com> | |
21 | Co-developed-by: Nir Efrati <nir.efrati@intel.com> | |
22 | Signed-off-by: Nir Efrati <nir.efrati@intel.com> | |
23 | Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com> | |
24 | Tested-by: Naama Meir <naamax.meir@linux.intel.com> | |
25 | Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com> | |
26 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
27 | --- | |
28 | drivers/net/ethernet/intel/e1000e/hw.h | 2 + | |
29 | drivers/net/ethernet/intel/e1000e/ich8lan.c | 33 ++++ | |
30 | drivers/net/ethernet/intel/e1000e/phy.c | 182 ++++++++++++-------- | |
31 | drivers/net/ethernet/intel/e1000e/phy.h | 2 + | |
32 | 4 files changed, 150 insertions(+), 69 deletions(-) | |
33 | ||
34 | diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h | |
35 | index 1fef6bb5a5fbc..4b6e7536170ab 100644 | |
36 | --- a/drivers/net/ethernet/intel/e1000e/hw.h | |
37 | +++ b/drivers/net/ethernet/intel/e1000e/hw.h | |
38 | @@ -628,6 +628,7 @@ struct e1000_phy_info { | |
39 | u32 id; | |
40 | u32 reset_delay_us; /* in usec */ | |
41 | u32 revision; | |
42 | + u32 retry_count; | |
43 | ||
44 | enum e1000_media_type media_type; | |
45 | ||
46 | @@ -644,6 +645,7 @@ struct e1000_phy_info { | |
47 | bool polarity_correction; | |
48 | bool speed_downgraded; | |
49 | bool autoneg_wait_to_complete; | |
50 | + bool retry_enabled; | |
51 | }; | |
52 | ||
53 | struct e1000_nvm_info { | |
54 | diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c | |
55 | index a2788fd5f8bb8..717c52913e84b 100644 | |
56 | --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c | |
57 | +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c | |
58 | @@ -222,11 +222,18 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw) | |
59 | if (hw->mac.type >= e1000_pch_lpt) { | |
60 | /* Only unforce SMBus if ME is not active */ | |
61 | if (!(er32(FWSM) & E1000_ICH_FWSM_FW_VALID)) { | |
62 | + /* Switching PHY interface always returns MDI error | |
63 | + * so disable retry mechanism to avoid wasting time | |
64 | + */ | |
65 | + e1000e_disable_phy_retry(hw); | |
66 | + | |
67 | /* Unforce SMBus mode in PHY */ | |
68 | e1e_rphy_locked(hw, CV_SMB_CTRL, &phy_reg); | |
69 | phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS; | |
70 | e1e_wphy_locked(hw, CV_SMB_CTRL, phy_reg); | |
71 | ||
72 | + e1000e_enable_phy_retry(hw); | |
73 | + | |
74 | /* Unforce SMBus mode in MAC */ | |
75 | mac_reg = er32(CTRL_EXT); | |
76 | mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS; | |
77 | @@ -310,6 +317,11 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) | |
78 | goto out; | |
79 | } | |
80 | ||
81 | + /* There is no guarantee that the PHY is accessible at this time | |
82 | + * so disable retry mechanism to avoid wasting time | |
83 | + */ | |
84 | + e1000e_disable_phy_retry(hw); | |
85 | + | |
86 | /* The MAC-PHY interconnect may be in SMBus mode. If the PHY is | |
87 | * inaccessible and resetting the PHY is not blocked, toggle the | |
88 | * LANPHYPC Value bit to force the interconnect to PCIe mode. | |
89 | @@ -380,6 +392,8 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) | |
90 | break; | |
91 | } | |
92 | ||
93 | + e1000e_enable_phy_retry(hw); | |
94 | + | |
95 | hw->phy.ops.release(hw); | |
96 | if (!ret_val) { | |
97 | ||
98 | @@ -449,6 +463,11 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) | |
99 | ||
100 | phy->id = e1000_phy_unknown; | |
101 | ||
102 | + if (hw->mac.type == e1000_pch_mtp) { | |
103 | + phy->retry_count = 2; | |
104 | + e1000e_enable_phy_retry(hw); | |
105 | + } | |
106 | + | |
107 | ret_val = e1000_init_phy_workarounds_pchlan(hw); | |
108 | if (ret_val) | |
109 | return ret_val; | |
110 | @@ -1146,6 +1165,11 @@ s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx) | |
111 | if (ret_val) | |
112 | goto out; | |
113 | ||
114 | + /* Switching PHY interface always returns MDI error | |
115 | + * so disable retry mechanism to avoid wasting time | |
116 | + */ | |
117 | + e1000e_disable_phy_retry(hw); | |
118 | + | |
119 | /* Force SMBus mode in PHY */ | |
120 | ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg); | |
121 | if (ret_val) | |
122 | @@ -1153,6 +1177,8 @@ s32 e1000_enable_ulp_lpt_lp(struct e1000_hw *hw, bool to_sx) | |
123 | phy_reg |= CV_SMB_CTRL_FORCE_SMBUS; | |
124 | e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg); | |
125 | ||
126 | + e1000e_enable_phy_retry(hw); | |
127 | + | |
128 | /* Force SMBus mode in MAC */ | |
129 | mac_reg = er32(CTRL_EXT); | |
130 | mac_reg |= E1000_CTRL_EXT_FORCE_SMBUS; | |
131 | @@ -1313,6 +1339,11 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force) | |
132 | /* Toggle LANPHYPC Value bit */ | |
133 | e1000_toggle_lanphypc_pch_lpt(hw); | |
134 | ||
135 | + /* Switching PHY interface always returns MDI error | |
136 | + * so disable retry mechanism to avoid wasting time | |
137 | + */ | |
138 | + e1000e_disable_phy_retry(hw); | |
139 | + | |
140 | /* Unforce SMBus mode in PHY */ | |
141 | ret_val = e1000_read_phy_reg_hv_locked(hw, CV_SMB_CTRL, &phy_reg); | |
142 | if (ret_val) { | |
143 | @@ -1333,6 +1364,8 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force) | |
144 | phy_reg &= ~CV_SMB_CTRL_FORCE_SMBUS; | |
145 | e1000_write_phy_reg_hv_locked(hw, CV_SMB_CTRL, phy_reg); | |
146 | ||
147 | + e1000e_enable_phy_retry(hw); | |
148 | + | |
149 | /* Unforce SMBus mode in MAC */ | |
150 | mac_reg = er32(CTRL_EXT); | |
151 | mac_reg &= ~E1000_CTRL_EXT_FORCE_SMBUS; | |
152 | diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c | |
153 | index 96ff0ca561b6c..395746bcf8f7c 100644 | |
154 | --- a/drivers/net/ethernet/intel/e1000e/phy.c | |
155 | +++ b/drivers/net/ethernet/intel/e1000e/phy.c | |
156 | @@ -107,6 +107,16 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw) | |
157 | return e1e_wphy(hw, M88E1000_PHY_GEN_CONTROL, 0); | |
158 | } | |
159 | ||
160 | +void e1000e_disable_phy_retry(struct e1000_hw *hw) | |
161 | +{ | |
162 | + hw->phy.retry_enabled = false; | |
163 | +} | |
164 | + | |
165 | +void e1000e_enable_phy_retry(struct e1000_hw *hw) | |
166 | +{ | |
167 | + hw->phy.retry_enabled = true; | |
168 | +} | |
169 | + | |
170 | /** | |
171 | * e1000e_read_phy_reg_mdic - Read MDI control register | |
172 | * @hw: pointer to the HW structure | |
173 | @@ -118,55 +128,73 @@ s32 e1000e_phy_reset_dsp(struct e1000_hw *hw) | |
174 | **/ | |
175 | s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) | |
176 | { | |
177 | + u32 i, mdic = 0, retry_counter, retry_max; | |
178 | struct e1000_phy_info *phy = &hw->phy; | |
179 | - u32 i, mdic = 0; | |
180 | + bool success; | |
181 | ||
182 | if (offset > MAX_PHY_REG_ADDRESS) { | |
183 | e_dbg("PHY Address %d is out of range\n", offset); | |
184 | return -E1000_ERR_PARAM; | |
185 | } | |
186 | ||
187 | + retry_max = phy->retry_enabled ? phy->retry_count : 0; | |
188 | + | |
189 | /* Set up Op-code, Phy Address, and register offset in the MDI | |
190 | * Control register. The MAC will take care of interfacing with the | |
191 | * PHY to retrieve the desired data. | |
192 | */ | |
193 | - mdic = ((offset << E1000_MDIC_REG_SHIFT) | | |
194 | - (phy->addr << E1000_MDIC_PHY_SHIFT) | | |
195 | - (E1000_MDIC_OP_READ)); | |
196 | + for (retry_counter = 0; retry_counter <= retry_max; retry_counter++) { | |
197 | + success = true; | |
198 | ||
199 | - ew32(MDIC, mdic); | |
200 | + mdic = ((offset << E1000_MDIC_REG_SHIFT) | | |
201 | + (phy->addr << E1000_MDIC_PHY_SHIFT) | | |
202 | + (E1000_MDIC_OP_READ)); | |
203 | ||
204 | - /* Poll the ready bit to see if the MDI read completed | |
205 | - * Increasing the time out as testing showed failures with | |
206 | - * the lower time out | |
207 | - */ | |
208 | - for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { | |
209 | - udelay(50); | |
210 | - mdic = er32(MDIC); | |
211 | - if (mdic & E1000_MDIC_READY) | |
212 | - break; | |
213 | - } | |
214 | - if (!(mdic & E1000_MDIC_READY)) { | |
215 | - e_dbg("MDI Read PHY Reg Address %d did not complete\n", offset); | |
216 | - return -E1000_ERR_PHY; | |
217 | - } | |
218 | - if (mdic & E1000_MDIC_ERROR) { | |
219 | - e_dbg("MDI Read PHY Reg Address %d Error\n", offset); | |
220 | - return -E1000_ERR_PHY; | |
221 | - } | |
222 | - if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) { | |
223 | - e_dbg("MDI Read offset error - requested %d, returned %d\n", | |
224 | - offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic)); | |
225 | - return -E1000_ERR_PHY; | |
226 | + ew32(MDIC, mdic); | |
227 | + | |
228 | + /* Poll the ready bit to see if the MDI read completed | |
229 | + * Increasing the time out as testing showed failures with | |
230 | + * the lower time out | |
231 | + */ | |
232 | + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { | |
233 | + usleep_range(50, 60); | |
234 | + mdic = er32(MDIC); | |
235 | + if (mdic & E1000_MDIC_READY) | |
236 | + break; | |
237 | + } | |
238 | + if (!(mdic & E1000_MDIC_READY)) { | |
239 | + e_dbg("MDI Read PHY Reg Address %d did not complete\n", | |
240 | + offset); | |
241 | + success = false; | |
242 | + } | |
243 | + if (mdic & E1000_MDIC_ERROR) { | |
244 | + e_dbg("MDI Read PHY Reg Address %d Error\n", offset); | |
245 | + success = false; | |
246 | + } | |
247 | + if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) { | |
248 | + e_dbg("MDI Read offset error - requested %d, returned %d\n", | |
249 | + offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic)); | |
250 | + success = false; | |
251 | + } | |
252 | + | |
253 | + /* Allow some time after each MDIC transaction to avoid | |
254 | + * reading duplicate data in the next MDIC transaction. | |
255 | + */ | |
256 | + if (hw->mac.type == e1000_pch2lan) | |
257 | + usleep_range(100, 150); | |
258 | + | |
259 | + if (success) { | |
260 | + *data = (u16)mdic; | |
261 | + return 0; | |
262 | + } | |
263 | + | |
264 | + if (retry_counter != retry_max) { | |
265 | + e_dbg("Perform retry on PHY transaction...\n"); | |
266 | + mdelay(10); | |
267 | + } | |
268 | } | |
269 | - *data = (u16)mdic; | |
270 | ||
271 | - /* Allow some time after each MDIC transaction to avoid | |
272 | - * reading duplicate data in the next MDIC transaction. | |
273 | - */ | |
274 | - if (hw->mac.type == e1000_pch2lan) | |
275 | - udelay(100); | |
276 | - return 0; | |
277 | + return -E1000_ERR_PHY; | |
278 | } | |
279 | ||
280 | /** | |
281 | @@ -179,56 +207,72 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) | |
282 | **/ | |
283 | s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) | |
284 | { | |
285 | + u32 i, mdic = 0, retry_counter, retry_max; | |
286 | struct e1000_phy_info *phy = &hw->phy; | |
287 | - u32 i, mdic = 0; | |
288 | + bool success; | |
289 | ||
290 | if (offset > MAX_PHY_REG_ADDRESS) { | |
291 | e_dbg("PHY Address %d is out of range\n", offset); | |
292 | return -E1000_ERR_PARAM; | |
293 | } | |
294 | ||
295 | + retry_max = phy->retry_enabled ? phy->retry_count : 0; | |
296 | + | |
297 | /* Set up Op-code, Phy Address, and register offset in the MDI | |
298 | * Control register. The MAC will take care of interfacing with the | |
299 | * PHY to retrieve the desired data. | |
300 | */ | |
301 | - mdic = (((u32)data) | | |
302 | - (offset << E1000_MDIC_REG_SHIFT) | | |
303 | - (phy->addr << E1000_MDIC_PHY_SHIFT) | | |
304 | - (E1000_MDIC_OP_WRITE)); | |
305 | + for (retry_counter = 0; retry_counter <= retry_max; retry_counter++) { | |
306 | + success = true; | |
307 | ||
308 | - ew32(MDIC, mdic); | |
309 | + mdic = (((u32)data) | | |
310 | + (offset << E1000_MDIC_REG_SHIFT) | | |
311 | + (phy->addr << E1000_MDIC_PHY_SHIFT) | | |
312 | + (E1000_MDIC_OP_WRITE)); | |
313 | ||
314 | - /* Poll the ready bit to see if the MDI read completed | |
315 | - * Increasing the time out as testing showed failures with | |
316 | - * the lower time out | |
317 | - */ | |
318 | - for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { | |
319 | - udelay(50); | |
320 | - mdic = er32(MDIC); | |
321 | - if (mdic & E1000_MDIC_READY) | |
322 | - break; | |
323 | - } | |
324 | - if (!(mdic & E1000_MDIC_READY)) { | |
325 | - e_dbg("MDI Write PHY Reg Address %d did not complete\n", offset); | |
326 | - return -E1000_ERR_PHY; | |
327 | - } | |
328 | - if (mdic & E1000_MDIC_ERROR) { | |
329 | - e_dbg("MDI Write PHY Red Address %d Error\n", offset); | |
330 | - return -E1000_ERR_PHY; | |
331 | - } | |
332 | - if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) { | |
333 | - e_dbg("MDI Write offset error - requested %d, returned %d\n", | |
334 | - offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic)); | |
335 | - return -E1000_ERR_PHY; | |
336 | - } | |
337 | + ew32(MDIC, mdic); | |
338 | ||
339 | - /* Allow some time after each MDIC transaction to avoid | |
340 | - * reading duplicate data in the next MDIC transaction. | |
341 | - */ | |
342 | - if (hw->mac.type == e1000_pch2lan) | |
343 | - udelay(100); | |
344 | + /* Poll the ready bit to see if the MDI read completed | |
345 | + * Increasing the time out as testing showed failures with | |
346 | + * the lower time out | |
347 | + */ | |
348 | + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { | |
349 | + usleep_range(50, 60); | |
350 | + mdic = er32(MDIC); | |
351 | + if (mdic & E1000_MDIC_READY) | |
352 | + break; | |
353 | + } | |
354 | + if (!(mdic & E1000_MDIC_READY)) { | |
355 | + e_dbg("MDI Write PHY Reg Address %d did not complete\n", | |
356 | + offset); | |
357 | + success = false; | |
358 | + } | |
359 | + if (mdic & E1000_MDIC_ERROR) { | |
360 | + e_dbg("MDI Write PHY Reg Address %d Error\n", offset); | |
361 | + success = false; | |
362 | + } | |
363 | + if (FIELD_GET(E1000_MDIC_REG_MASK, mdic) != offset) { | |
364 | + e_dbg("MDI Write offset error - requested %d, returned %d\n", | |
365 | + offset, FIELD_GET(E1000_MDIC_REG_MASK, mdic)); | |
366 | + success = false; | |
367 | + } | |
368 | ||
369 | - return 0; | |
370 | + /* Allow some time after each MDIC transaction to avoid | |
371 | + * reading duplicate data in the next MDIC transaction. | |
372 | + */ | |
373 | + if (hw->mac.type == e1000_pch2lan) | |
374 | + usleep_range(100, 150); | |
375 | + | |
376 | + if (success) | |
377 | + return 0; | |
378 | + | |
379 | + if (retry_counter != retry_max) { | |
380 | + e_dbg("Perform retry on PHY transaction...\n"); | |
381 | + mdelay(10); | |
382 | + } | |
383 | + } | |
384 | + | |
385 | + return -E1000_ERR_PHY; | |
386 | } | |
387 | ||
388 | /** | |
389 | diff --git a/drivers/net/ethernet/intel/e1000e/phy.h b/drivers/net/ethernet/intel/e1000e/phy.h | |
390 | index c48777d095235..049bb325b4b14 100644 | |
391 | --- a/drivers/net/ethernet/intel/e1000e/phy.h | |
392 | +++ b/drivers/net/ethernet/intel/e1000e/phy.h | |
393 | @@ -51,6 +51,8 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data); | |
394 | s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data); | |
395 | void e1000_power_up_phy_copper(struct e1000_hw *hw); | |
396 | void e1000_power_down_phy_copper(struct e1000_hw *hw); | |
397 | +void e1000e_disable_phy_retry(struct e1000_hw *hw); | |
398 | +void e1000e_enable_phy_retry(struct e1000_hw *hw); | |
399 | s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); | |
400 | s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); | |
401 | s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data); | |
402 | -- | |
403 | 2.43.0 | |
404 |