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