• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /********************************************************************************
2 Copyright (C) 2016 Marvell International Ltd.
3 
4 Marvell BSD License Option
5 
6 If you received this File from Marvell, you may opt to use, redistribute and/or
7 modify this File under the following licensing terms.
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10 
11   * Redistributions of source code must retain the above copyright notice,
12     this list of conditions and the following disclaimer.
13 
14   * Redistributions in binary form must reproduce the above copyright
15     notice, this list of conditions and the following disclaimer in the
16     documentation and/or other materials provided with the distribution.
17 
18   * Neither the name of Marvell nor the names of its contributors may be
19     used to endorse or promote products derived from this software without
20     specific prior written permission.
21 
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 *******************************************************************************/
34 
35 #include <Protocol/DriverBinding.h>
36 #include <Protocol/Mdio.h>
37 #include <Protocol/MvPhy.h>
38 
39 #include <Library/BaseLib.h>
40 #include <Library/BaseMemoryLib.h>
41 #include <Library/DebugLib.h>
42 #include <Library/IoLib.h>
43 #include <Library/MemoryAllocationLib.h>
44 #include <Library/PcdLib.h>
45 #include <Library/UefiBootServicesTableLib.h>
46 #include <Library/UefiLib.h>
47 
48 #include "MvPhyDxe.h"
49 
50 #define TIMEOUT   500
51 
52 STATIC MARVELL_MDIO_PROTOCOL *Mdio;
53 
54 STATIC MV_PHY_DEVICE MvPhyDevices[] = {
55   { MV_PHY_DEVICE_1512, MvPhyInit1512 },
56   { 0, NULL }
57 };
58 
59 EFI_STATUS
60 MvPhyStatus (
61   IN CONST MARVELL_PHY_PROTOCOL *This,
62   IN PHY_DEVICE  *PhyDev
63   );
64 
65 EFI_STATUS
MvPhyReset(IN UINT32 PhyAddr)66 MvPhyReset (
67   IN UINT32 PhyAddr
68   )
69 {
70   UINT32 Reg = 0;
71   INTN timeout = TIMEOUT;
72 
73   Mdio->Read(Mdio, PhyAddr, MII_BMCR, &Reg);
74   Reg |= BMCR_RESET;
75   Mdio->Write(Mdio, PhyAddr, MII_BMCR, Reg);
76 
77   while ((Reg & BMCR_RESET) && timeout--) {
78     Mdio->Read(Mdio, PhyAddr, MII_BMCR, &Reg);
79     gBS->Stall(1000);
80   }
81 
82   if (Reg & BMCR_RESET) {
83     DEBUG((DEBUG_ERROR, "PHY reset timed out\n"));
84     return EFI_TIMEOUT;
85   }
86 
87   return EFI_SUCCESS;
88 }
89 
90 /* Marvell 88E1111S */
91 EFI_STATUS
MvPhyM88e1111sConfig(IN PHY_DEVICE * PhyDev)92 MvPhyM88e1111sConfig (
93   IN PHY_DEVICE *PhyDev
94   )
95 {
96   UINT32 Reg;
97 
98   if ((PhyDev->Connection == PHY_CONNECTION_RGMII) ||
99       (PhyDev->Connection == PHY_CONNECTION_RGMII_ID) ||
100       (PhyDev->Connection == PHY_CONNECTION_RGMII_RXID) ||
101       (PhyDev->Connection == PHY_CONNECTION_RGMII_TXID)) {
102     Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_CR, &Reg);
103 
104     if ((PhyDev->Connection == PHY_CONNECTION_RGMII) ||
105       (PhyDev->Connection == PHY_CONNECTION_RGMII_ID)) {
106       Reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY);
107     } else if (PhyDev->Connection == PHY_CONNECTION_RGMII_RXID) {
108       Reg &= ~MIIM_88E1111_TX_DELAY;
109       Reg |= MIIM_88E1111_RX_DELAY;
110     } else if (PhyDev->Connection == PHY_CONNECTION_RGMII_TXID) {
111       Reg &= ~MIIM_88E1111_RX_DELAY;
112       Reg |= MIIM_88E1111_TX_DELAY;
113     }
114 
115     Mdio->Write(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_CR, Reg);
116 
117     Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, &Reg);
118 
119     Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK);
120 
121     if (Reg & MIIM_88E1111_HWCFG_FIBER_COPPER_RES)
122       Reg |= MIIM_88E1111_HWCFG_MODE_FIBER_RGMII;
123     else
124       Reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RGMII;
125 
126     Mdio->Write(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, Reg);
127   }
128 
129   if (PhyDev->Connection == PHY_CONNECTION_SGMII) {
130     Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, &Reg);
131 
132     Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK);
133     Reg |= MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK;
134     Reg |= MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
135 
136     Mdio->Write(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, Reg);
137   }
138 
139   if (PhyDev->Connection == PHY_CONNECTION_RTBI) {
140     Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_CR, &Reg);
141     Reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY);
142     Mdio->Write(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_CR, Reg);
143 
144     Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, &Reg);
145     Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK |
146       MIIM_88E1111_HWCFG_FIBER_COPPER_RES);
147     Reg |= 0x7 | MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
148     Mdio->Write(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, Reg);
149 
150     /* Soft reset */
151     MvPhyReset(PhyDev->Addr);
152 
153     Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, &Reg);
154     Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK |
155       MIIM_88E1111_HWCFG_FIBER_COPPER_RES);
156     Reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RTBI |
157       MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO;
158     Mdio->Write(Mdio, PhyDev->Addr, MIIM_88E1111_PHY_EXT_SR, Reg);
159   }
160 
161   Mdio->Read(Mdio, PhyDev->Addr, MII_BMCR, &Reg);
162   Reg |= (BMCR_ANENABLE | BMCR_ANRESTART);
163   Reg &= ~BMCR_ISOLATE;
164   Mdio->Write(Mdio, PhyDev->Addr, MII_BMCR, Reg);
165 
166   /* Soft reset */
167   MvPhyReset(PhyDev->Addr);
168 
169   MvPhyReset(PhyDev->Addr);
170 
171   return EFI_SUCCESS;
172 }
173 
174 EFI_STATUS
MvPhyParseStatus(IN PHY_DEVICE * PhyDev)175 MvPhyParseStatus (
176   IN PHY_DEVICE *PhyDev
177   )
178 {
179   UINT32 Data;
180   UINT32 Speed;
181 
182   Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1xxx_PHY_STATUS, &Data);
183 
184   if ((Data & MIIM_88E1xxx_PHYSTAT_LINK) &&
185     !(Data & MIIM_88E1xxx_PHYSTAT_SPDDONE)) {
186     INTN i = 0;
187 
188     DEBUG((DEBUG_ERROR,"MvPhyDxe: Waiting for PHY realtime link"));
189     while (!(Data & MIIM_88E1xxx_PHYSTAT_SPDDONE)) {
190       if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
191         DEBUG((DEBUG_ERROR," TIMEOUT !\n"));
192         PhyDev->LinkUp = FALSE;
193         break;
194       }
195 
196       if ((i++ % 1000) == 0)
197         DEBUG((DEBUG_ERROR, "."));
198       gBS->Stall(1000);
199       Mdio->Read(Mdio, PhyDev->Addr, MIIM_88E1xxx_PHY_STATUS, &Data);
200     }
201     DEBUG((DEBUG_ERROR," done\n"));
202     gBS->Stall(500000);
203   } else {
204     if (Data & MIIM_88E1xxx_PHYSTAT_LINK) {
205       DEBUG((DEBUG_ERROR, "MvPhyDxe: link up, "));
206       PhyDev->LinkUp = TRUE;
207     } else {
208       DEBUG((DEBUG_ERROR, "MvPhyDxe: link down, "));
209       PhyDev->LinkUp = FALSE;
210     }
211   }
212 
213   if (Data & MIIM_88E1xxx_PHYSTAT_DUPLEX) {
214     DEBUG((DEBUG_ERROR, "full duplex, "));
215     PhyDev->FullDuplex = TRUE;
216   } else {
217     DEBUG((DEBUG_ERROR, "half duplex, "));
218     PhyDev->FullDuplex = FALSE;
219   }
220 
221   Speed = Data & MIIM_88E1xxx_PHYSTAT_SPEED;
222 
223   switch (Speed) {
224   case MIIM_88E1xxx_PHYSTAT_GBIT:
225     DEBUG((DEBUG_ERROR, "speed 1000\n"));
226     PhyDev->Speed = SPEED_1000;
227     break;
228   case MIIM_88E1xxx_PHYSTAT_100:
229     DEBUG((DEBUG_ERROR, "speed 100\n"));
230     PhyDev->Speed = SPEED_100;
231     break;
232   default:
233     DEBUG((DEBUG_ERROR, "speed 10\n"));
234     PhyDev->Speed = SPEED_10;
235     break;
236   }
237 
238   return EFI_SUCCESS;
239 }
240 
241 STATIC
242 VOID
MvPhy1512WriteBits(IN UINT32 PhyAddr,IN UINT8 RegNum,IN UINT16 Offset,IN UINT16 Len,IN UINT16 Data)243 MvPhy1512WriteBits (
244   IN UINT32 PhyAddr,
245   IN UINT8 RegNum,
246   IN UINT16 Offset,
247   IN UINT16 Len,
248   IN UINT16 Data)
249 {
250   UINT32 Reg, Mask;
251 
252   if ((Len + Offset) >= 16)
253     Mask = 0 - (1 << Offset);
254   else
255     Mask = (1 << (Len + Offset)) - (1 << Offset);
256 
257   Mdio->Read(Mdio, PhyAddr, RegNum, &Reg);
258 
259   Reg &= ~Mask;
260   Reg |= Data << Offset;
261 
262   Mdio->Write(Mdio, PhyAddr, RegNum, Reg);
263 }
264 
265 STATIC
266 EFI_STATUS
MvPhyInit1512(IN CONST MARVELL_PHY_PROTOCOL * Snp,IN UINT32 PhyAddr,IN OUT PHY_DEVICE * PhyDev)267 MvPhyInit1512 (
268     IN CONST MARVELL_PHY_PROTOCOL *Snp,
269     IN UINT32 PhyAddr,
270     IN OUT PHY_DEVICE *PhyDev
271     )
272 {
273   UINT32 Data;
274   INTN i;
275 
276   if (PhyDev->Connection == PHY_CONNECTION_SGMII) {
277     /* Select page 0xff and update configuration registers according to
278      * Marvell Release Notes - Alaska 88E1510/88E1518/88E1512 Rev A0,
279      * Errata Section 3.1 - needed in SGMII mode.
280      */
281     Mdio->Write(Mdio, PhyAddr, 22, 0x00ff);
282     Mdio->Write(Mdio, PhyAddr, 17, 0x214B);
283     Mdio->Write(Mdio, PhyAddr, 16, 0x2144);
284     Mdio->Write(Mdio, PhyAddr, 17, 0x0C28);
285     Mdio->Write(Mdio, PhyAddr, 16, 0x2146);
286     Mdio->Write(Mdio, PhyAddr, 17, 0xB233);
287     Mdio->Write(Mdio, PhyAddr, 16, 0x214D);
288     Mdio->Write(Mdio, PhyAddr, 17, 0xCC0C);
289     Mdio->Write(Mdio, PhyAddr, 16, 0x2159);
290 
291     /* Reset page selection and select page 0x12 */
292     Mdio->Write(Mdio, PhyAddr, 22, 0x0000);
293     Mdio->Write(Mdio, PhyAddr, 22, 0x0012);
294 
295     /* Write HWCFG_MODE = SGMII to Copper */
296     MvPhy1512WriteBits(PhyAddr, 20, 0, 3, 1);
297 
298     /* Phy reset - necessary after changing mode */
299     MvPhy1512WriteBits(PhyAddr, 20, 15, 1, 1);
300 
301     /* Reset page selection */
302     Mdio->Write(Mdio, PhyAddr, 22, 0x0000);
303     gBS->Stall(100);
304   }
305 
306   MvPhyM88e1111sConfig (PhyDev);
307 
308   /* autonegotiation on startup is not always required */
309   if (!PcdGetBool (PcdPhyStartupAutoneg))
310     return EFI_SUCCESS;
311 
312   Mdio->Read(Mdio, PhyAddr, MII_BMSR, &Data);
313 
314   if ((Data & BMSR_ANEGCAPABLE) && !(Data & BMSR_ANEGCOMPLETE)) {
315 
316     DEBUG((DEBUG_ERROR, "MvPhyDxe: Waiting for PHY auto negotiation... "));
317     for (i = 0; !(Data & BMSR_ANEGCOMPLETE); i++) {
318       if (i > PHY_ANEG_TIMEOUT) {
319         DEBUG((DEBUG_ERROR, "timeout\n"));
320         PhyDev->LinkUp = FALSE;
321         return EFI_TIMEOUT;
322       }
323 
324       gBS->Stall(1000);  /* 1 ms */
325       Mdio->Read(Mdio, PhyAddr, MII_BMSR, &Data);
326     }
327     PhyDev->LinkUp = TRUE;
328     DEBUG((DEBUG_INFO, "MvPhyDxe: link up\n"));
329   } else {
330     Mdio->Read(Mdio, PhyAddr, MII_BMSR, &Data);
331 
332     if (Data & BMSR_LSTATUS) {
333       PhyDev->LinkUp = TRUE;
334       DEBUG((DEBUG_INFO, "MvPhyDxe: link up\n"));
335     } else {
336       PhyDev->LinkUp = FALSE;
337       DEBUG((DEBUG_INFO, "MvPhyDxe: link down\n"));
338     }
339   }
340   MvPhyParseStatus (PhyDev);
341 
342   return EFI_SUCCESS;
343 }
344 
345 EFI_STATUS
MvPhyInit(IN CONST MARVELL_PHY_PROTOCOL * Snp,IN UINT32 PhyAddr,IN PHY_CONNECTION PhyConnection,IN OUT PHY_DEVICE ** OutPhyDev)346 MvPhyInit (
347   IN CONST MARVELL_PHY_PROTOCOL *Snp,
348   IN UINT32 PhyAddr,
349   IN PHY_CONNECTION PhyConnection,
350   IN OUT PHY_DEVICE **OutPhyDev
351   )
352 {
353   EFI_STATUS Status;
354   PHY_DEVICE *PhyDev;
355   UINT8 *DeviceIds;
356   INTN i;
357 
358   Status = gBS->LocateProtocol (
359       &gMarvellMdioProtocolGuid,
360       NULL,
361       (VOID **) &Mdio
362       );
363   if (EFI_ERROR(Status))
364     return Status;
365 
366   /* perform setup common for all PHYs */
367   PhyDev = AllocateZeroPool (sizeof (PHY_DEVICE));
368   PhyDev->Addr = PhyAddr;
369   PhyDev->Connection = PhyConnection;
370   DEBUG((DEBUG_INFO, "MvPhyDxe: PhyAddr is %d, connection %d\n",
371         PhyAddr, PhyConnection));
372   *OutPhyDev = PhyDev;
373 
374   DeviceIds = PcdGetPtr (PcdPhyDeviceIds);
375   for (i = 0; i < PcdGetSize (PcdPhyDeviceIds); i++) {
376     /* find MvPhyDevices fitting entry */
377     if (MvPhyDevices[i].DevId == DeviceIds[i]) {
378       ASSERT (MvPhyDevices[i].DevInit != NULL);
379       /* proceed with PHY-specific initialization */
380       return MvPhyDevices[i].DevInit(Snp, PhyAddr, PhyDev);
381     }
382   }
383 
384   /* if we are here, no matching DevId was found */
385   Status = EFI_INVALID_PARAMETER;
386   FreePool (PhyDev);
387   return Status;
388 }
389 
390 EFI_STATUS
MvPhyStatus(IN CONST MARVELL_PHY_PROTOCOL * This,IN PHY_DEVICE * PhyDev)391 MvPhyStatus (
392   IN CONST MARVELL_PHY_PROTOCOL *This,
393   IN PHY_DEVICE  *PhyDev
394   )
395 {
396   UINT32 Data;
397 
398   Mdio->Read(Mdio, PhyDev->Addr, MII_BMSR, &Data);
399   Mdio->Read(Mdio, PhyDev->Addr, MII_BMSR, &Data);
400 
401   if ((Data & BMSR_LSTATUS) == 0) {
402     PhyDev->LinkUp = FALSE;
403   } else {
404     PhyDev->LinkUp = TRUE;
405   }
406 
407   return EFI_SUCCESS;
408 }
409 
410 EFI_STATUS
411 EFIAPI
MvPhyDxeInitialise(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)412 MvPhyDxeInitialise (
413   IN EFI_HANDLE  ImageHandle,
414   IN EFI_SYSTEM_TABLE  *SystemTable
415   )
416 {
417   MARVELL_PHY_PROTOCOL *Phy;
418   EFI_STATUS Status;
419   EFI_HANDLE Handle = NULL;
420 
421   Phy = AllocateZeroPool (sizeof (MARVELL_PHY_PROTOCOL));
422   Phy->Status = MvPhyStatus;
423   Phy->Init = MvPhyInit;
424 
425   Status = gBS->InstallMultipleProtocolInterfaces (
426                   &Handle,
427                   &gMarvellPhyProtocolGuid, Phy,
428                   NULL
429                   );
430 
431   if (EFI_ERROR(Status)) {
432     DEBUG((DEBUG_ERROR, "Failed to install interfaces\n"));
433     return Status;
434   }
435   DEBUG((DEBUG_ERROR, "Succesfully installed protocol interfaces\n"));
436 
437   return EFI_SUCCESS;
438 }
439