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 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 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 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 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 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 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 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 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