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