1 /** @file
2
3 Copyright (c) 2020 Jared McNeill. All rights reserved.
4 Copyright (c) 2020 Andrey Warkentin <andrey.warkentin@gmail.com>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <Uefi.h>
11 #include <Library/DebugLib.h>
12 #include <Library/UefiBootServicesTableLib.h>
13
14 #include "GenericPhy.h"
15
16 #define PHY_RESET_TIMEOUT 500
17
18 /**
19 Perform a PHY register read.
20
21 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
22 @param PhyAddr[in] PHY address.
23 @param Reg[in] PHY register.
24 @param Data[out] Pointer to register data read.
25
26 @retval EFI_SUCCESS Data read successfully.
27 @retval EFI_DEVICE_ERROR Failed to read data.
28
29 **/
30 STATIC
31 EFI_STATUS
GenericPhyRead(IN GENERIC_PHY_PRIVATE_DATA * Phy,IN UINT8 PhyAddr,IN UINT8 Reg,OUT UINT16 * Data)32 GenericPhyRead (
33 IN GENERIC_PHY_PRIVATE_DATA *Phy,
34 IN UINT8 PhyAddr,
35 IN UINT8 Reg,
36 OUT UINT16 *Data
37 )
38 {
39 return Phy->Read (Phy->PrivateData, PhyAddr, Reg, Data);
40 }
41
42 /**
43 Perform a PHY register write.
44
45 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
46 @param PhyAddr[in] PHY address.
47 @param Reg[in] PHY register.
48 @param Data[in] Pointer to register data to write.
49
50 @retval EFI_SUCCESS Data written successfully.
51 @retval EFI_DEVICE_ERROR Failed to write data.
52
53 **/
54 STATIC
55 EFI_STATUS
GenericPhyWrite(IN GENERIC_PHY_PRIVATE_DATA * Phy,IN UINT8 PhyAddr,IN UINT8 Reg,IN UINT16 Data)56 GenericPhyWrite (
57 IN GENERIC_PHY_PRIVATE_DATA *Phy,
58 IN UINT8 PhyAddr,
59 IN UINT8 Reg,
60 IN UINT16 Data
61 )
62 {
63 return Phy->Write (Phy->PrivateData, PhyAddr, Reg, Data);
64 }
65
66 /**
67 Process a PHY link speed change (e.g. with MAC layer).
68
69 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
70 @param Speed[in] Speed setting.
71 @param Duplex[in] Duplex setting.
72
73 **/
74 STATIC
75 VOID
GenericPhyConfigure(IN GENERIC_PHY_PRIVATE_DATA * Phy,IN GENERIC_PHY_SPEED Speed,IN GENERIC_PHY_DUPLEX Duplex)76 GenericPhyConfigure (
77 IN GENERIC_PHY_PRIVATE_DATA *Phy,
78 IN GENERIC_PHY_SPEED Speed,
79 IN GENERIC_PHY_DUPLEX Duplex
80 )
81 {
82 Phy->Configure (Phy->PrivateData, Speed, Duplex);
83 }
84
85 /**
86 Detect address for the first PHY seen, probing all possible addresses.
87
88 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
89
90 @retval EFI_SUCCESS Found a PHY and programmed Phy->PhyAddr
91 @retval EFI_DEVICE_ERROR Error reading/writing a PHY register.
92 @retval EFI_NOT_FOUND No PHY detected.
93
94 **/
95 STATIC
96 EFI_STATUS
GenericPhyDetect(IN GENERIC_PHY_PRIVATE_DATA * Phy)97 GenericPhyDetect (
98 IN GENERIC_PHY_PRIVATE_DATA *Phy
99 )
100 {
101 EFI_STATUS Status;
102 UINT8 PhyAddr;
103 UINT16 Id1, Id2;
104
105 for (PhyAddr = 0; PhyAddr < 32; PhyAddr++) {
106 Status = GenericPhyRead (Phy, PhyAddr, GENERIC_PHY_PHYIDR1, &Id1);
107 if (EFI_ERROR (Status)) {
108 continue;
109 }
110 Status = GenericPhyRead (Phy, PhyAddr, GENERIC_PHY_PHYIDR2, &Id2);
111 if (EFI_ERROR (Status)) {
112 continue;
113 }
114 if (Id1 != 0xFFFF && Id2 != 0xFFFF) {
115 Phy->PhyAddr = PhyAddr;
116 DEBUG ((DEBUG_INFO,
117 "%a: PHY detected at address 0x%02X (PHYIDR1=0x%04X, PHYIDR2=0x%04X)\n",
118 __FUNCTION__, PhyAddr, Id1, Id2));
119 return EFI_SUCCESS;
120 }
121 }
122
123 return EFI_NOT_FOUND;
124 }
125
126 /**
127 Start link auto-negotiation on a PHY.
128
129 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
130
131 @retval EFI_SUCCESS Auto-netogiation started.
132 @retval EFI_DEVICE_ERROR PHY register read/write error.
133
134 **/
135 STATIC
136 EFI_STATUS
GenericPhyAutoNegotiate(IN GENERIC_PHY_PRIVATE_DATA * Phy)137 GenericPhyAutoNegotiate (
138 IN GENERIC_PHY_PRIVATE_DATA *Phy
139 )
140 {
141 EFI_STATUS Status;
142 UINT16 Anar, Gbcr, Bmcr;
143
144 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_ANAR, &Anar);
145 if (EFI_ERROR (Status)) {
146 return Status;
147 }
148 Anar |= GENERIC_PHY_ANAR_100BASETX_FDX |
149 GENERIC_PHY_ANAR_100BASETX |
150 GENERIC_PHY_ANAR_10BASET_FDX |
151 GENERIC_PHY_ANAR_10BASET;
152 Status = GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_ANAR, Anar);
153 if (EFI_ERROR (Status)) {
154 return Status;
155 }
156
157 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_GBCR, &Gbcr);
158 if (EFI_ERROR (Status)) {
159 return Status;
160 }
161 Gbcr |= GENERIC_PHY_GBCR_1000BASET_FDX |
162 GENERIC_PHY_GBCR_1000BASET;
163 Status = GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_GBCR, Gbcr);
164 if (EFI_ERROR (Status)) {
165 return Status;
166 }
167
168 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR, &Bmcr);
169 if (EFI_ERROR (Status)) {
170 return Status;
171 }
172 Bmcr |= GENERIC_PHY_BMCR_ANE |
173 GENERIC_PHY_BMCR_RESTART_AN;
174 return GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR, Bmcr);
175 }
176
177 /**
178 Initialize the first PHY detected, performing a reset and enabling
179 auto-negotiation.
180
181 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
182
183 @retval EFI_SUCCESS Auto-negotiation started.
184 @retval EFI_DEVICE_ERROR PHY register read/write error.
185 @retval EFI_TIMEOUT PHY reset time-out.
186 @retval EFI_NOT_FOUND No PHY detected.
187
188 **/
189 EFI_STATUS
190 EFIAPI
GenericPhyInit(IN GENERIC_PHY_PRIVATE_DATA * Phy)191 GenericPhyInit (
192 IN GENERIC_PHY_PRIVATE_DATA *Phy
193 )
194 {
195 EFI_STATUS Status;
196
197 ASSERT (Phy->Read != NULL);
198 ASSERT (Phy->Write != NULL);
199 ASSERT (Phy->Configure != NULL);
200
201 Status = GenericPhyDetect (Phy);
202 if (EFI_ERROR (Status)) {
203 return Status;
204 }
205
206 Status = GenericPhyReset (Phy);
207 if (EFI_ERROR (Status)) {
208 return Status;
209 }
210
211 return GenericPhyAutoNegotiate (Phy);
212 }
213
214 /**
215 Perform a PHY reset.
216
217 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
218
219 @retval EFI_SUCCESS Auto-negotiation started.
220 @retval EFI_DEVICE_ERROR PHY register read/write error.
221 @retval EFI_TIMEOUT PHY reset time-out.
222
223 **/
224 EFI_STATUS
225 EFIAPI
GenericPhyReset(IN GENERIC_PHY_PRIVATE_DATA * Phy)226 GenericPhyReset (
227 IN GENERIC_PHY_PRIVATE_DATA *Phy
228 )
229 {
230 EFI_STATUS Status;
231 UINTN Retry;
232 UINT16 Data;
233
234 // Start reset sequence
235 Status = GenericPhyWrite (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR,
236 GENERIC_PHY_BMCR_RESET);
237 if (EFI_ERROR (Status)) {
238 return Status;
239 }
240
241 // Wait up to 500ms for it to complete
242 for (Retry = PHY_RESET_TIMEOUT; Retry > 0; Retry--) {
243 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_BMCR, &Data);
244 if (EFI_ERROR (Status)) {
245 return Status;
246 }
247 if ((Data & GENERIC_PHY_BMCR_RESET) == 0) {
248 break;
249 }
250 gBS->Stall (1000);
251 }
252 if (Retry == 0) {
253 return EFI_TIMEOUT;
254 }
255
256 if (Phy->ResetAction != NULL) {
257 Phy->ResetAction (Phy->PrivateData);
258 }
259
260 return EFI_SUCCESS;
261 }
262
263 /**
264 Probe link status.
265
266 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
267
268 @retval EFI_SUCCESS Link is up and auto-negotiation is complete.
269 @retval EFI_DEVICE_ERROR PHY register read/write error,
270
271 **/
272 STATIC
273 EFI_STATUS
GenericPhyGetLinkStatus(IN GENERIC_PHY_PRIVATE_DATA * Phy)274 GenericPhyGetLinkStatus (
275 IN GENERIC_PHY_PRIVATE_DATA *Phy
276 )
277 {
278 EFI_STATUS Status;
279 UINT16 Bmsr;
280
281 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_BMSR, &Bmsr);
282 if (EFI_ERROR (Status)) {
283 return Status;
284 }
285
286 if ((Bmsr & GENERIC_PHY_BMSR_LINK_STATUS) == 0) {
287 return EFI_TIMEOUT;
288 }
289
290 if ((Bmsr & GENERIC_PHY_BMSR_ANEG_COMPLETE) == 0) {
291 return EFI_TIMEOUT;
292 }
293
294 return EFI_SUCCESS;
295 }
296
297 /**
298 Return PHY link configuration.
299
300 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
301 @param Speed[out] Pointer to store link speed.
302 @param Duplex[out] Pointer to store link duplex setting.
303
304 @retval EFI_SUCCESS Link configuration settings read.
305 @retval EFI_DEVICE_ERROR PHY register read/write error,
306
307 **/
308 STATIC
309 EFI_STATUS
GenericPhyGetConfig(IN GENERIC_PHY_PRIVATE_DATA * Phy,OUT GENERIC_PHY_SPEED * Speed,OUT GENERIC_PHY_DUPLEX * Duplex)310 GenericPhyGetConfig (
311 IN GENERIC_PHY_PRIVATE_DATA *Phy,
312 OUT GENERIC_PHY_SPEED *Speed,
313 OUT GENERIC_PHY_DUPLEX *Duplex
314 )
315 {
316 EFI_STATUS Status;
317 UINT16 Gbcr, Gbsr, Anlpar, Anar;
318 UINT16 Gb, An;
319
320 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_GBCR, &Gbcr);
321 if (EFI_ERROR (Status)) {
322 return Status;
323 }
324 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_GBSR, &Gbsr);
325 if (EFI_ERROR (Status)) {
326 return Status;
327 }
328 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_ANLPAR, &Anlpar);
329 if (EFI_ERROR (Status)) {
330 return Status;
331 }
332 Status = GenericPhyRead (Phy, Phy->PhyAddr, GENERIC_PHY_ANAR, &Anar);
333 if (EFI_ERROR (Status)) {
334 return Status;
335 }
336
337 Gb = (Gbsr >> 2) & Gbcr;
338 An = Anlpar & Anar;
339
340 if ((Gb & (GENERIC_PHY_GBCR_1000BASET_FDX |
341 GENERIC_PHY_GBCR_1000BASET)) != 0) {
342 *Speed = PHY_SPEED_1000;
343 *Duplex = (Gb & GENERIC_PHY_GBCR_1000BASET_FDX) ? PHY_DUPLEX_FULL
344 : PHY_DUPLEX_HALF;
345 } else if ((An & (GENERIC_PHY_ANAR_100BASETX_FDX |
346 GENERIC_PHY_ANAR_100BASETX)) != 0) {
347 *Speed = PHY_SPEED_100;
348 *Duplex = (An & GENERIC_PHY_ANAR_100BASETX_FDX) ? PHY_DUPLEX_FULL
349 : PHY_DUPLEX_HALF;
350 } else {
351 *Speed = PHY_SPEED_10;
352 *Duplex = (An & GENERIC_PHY_ANAR_10BASET_FDX) ? PHY_DUPLEX_FULL
353 : PHY_DUPLEX_HALF;
354 }
355
356 DEBUG ((DEBUG_INFO, "%a: Link speed %d Mbps, %a-duplex\n",
357 __FUNCTION__, *Speed, *Duplex == PHY_DUPLEX_FULL ? "full" : "half"));
358
359 return EFI_SUCCESS;
360 }
361
362 /**
363 Update link status, propagating PHY link state into the MAC layer.
364
365 @param Phy[in] Pointer to GENERIC_PHY_PRIVATE_DATA.
366
367 @retval EFI_SUCCESS Link is up.
368 @retval EFI_DEVICE_ERROR PHY register read/write error.
369 @retval EFI_NOT_READY Link is down.
370
371 **/
372 EFI_STATUS
373 EFIAPI
GenericPhyUpdateConfig(IN GENERIC_PHY_PRIVATE_DATA * Phy)374 GenericPhyUpdateConfig (
375 IN GENERIC_PHY_PRIVATE_DATA *Phy
376 )
377 {
378 EFI_STATUS Status;
379 GENERIC_PHY_SPEED Speed;
380 GENERIC_PHY_DUPLEX Duplex;
381 BOOLEAN LinkUp;
382
383 Status = GenericPhyGetLinkStatus (Phy);
384 LinkUp = EFI_ERROR (Status) ? FALSE : TRUE;
385
386 if (Phy->LinkUp != LinkUp) {
387 if (LinkUp) {
388 DEBUG ((DEBUG_VERBOSE, "%a: Link is up\n", __FUNCTION__));
389
390 Status = GenericPhyGetConfig (Phy, &Speed, &Duplex);
391 if (EFI_ERROR (Status)) {
392 return Status;
393 }
394
395 GenericPhyConfigure (Phy, Speed, Duplex);
396 } else {
397 DEBUG ((DEBUG_VERBOSE, "%a: Link is down\n", __FUNCTION__));
398 }
399 }
400
401 Phy->LinkUp = LinkUp;
402
403 return LinkUp ? EFI_SUCCESS : EFI_NOT_READY;
404 }
405