/******************************************************************************** Copyright (C) 2016 Marvell International Ltd. SPDX-License-Identifier: BSD-2-Clause-Patent *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "MvPhyDxe.h" #define TIMEOUT 500 STATIC MARVELL_MDIO_PROTOCOL *Mdio; // // Table with available Mdio controllers // STATIC UINT8 * CONST MdioDeviceTable = PcdGetPtr (PcdMdioControllersEnabled); // // Table with PHY to Mdio controller mappings // STATIC UINT8 * CONST Phy2MdioController = PcdGetPtr (PcdPhy2MdioController); // // Table with PHYs' SMI addresses // STATIC UINT8 * CONST PhySmiAddresses = PcdGetPtr (PcdPhySmiAddresses); STATIC MV_PHY_DEVICE MvPhyDevices[] = { { MV_PHY_DEVICE_1512, MvPhyInit1512 }, { MV_PHY_DEVICE_1112, MvPhyInit1112 }, { 0, NULL } }; EFI_STATUS MvPhyStatus ( IN CONST MARVELL_PHY_PROTOCOL *This, IN PHY_DEVICE *PhyDev ); EFI_STATUS MvPhyReset ( IN PHY_DEVICE *PhyDev ) { UINT32 Reg = 0; INTN timeout = TIMEOUT; Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MII_BMCR, &Reg); Reg |= BMCR_RESET; Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MII_BMCR, Reg); while ((Reg & BMCR_RESET) && timeout--) { Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MII_BMCR, &Reg); gBS->Stall(1000); } if (Reg & BMCR_RESET) { DEBUG((DEBUG_ERROR, "PHY reset timed out\n")); return EFI_TIMEOUT; } return EFI_SUCCESS; } /* Marvell 88E1111S */ EFI_STATUS MvPhyM88e1111sConfig ( IN PHY_DEVICE *PhyDev ) { UINT32 Reg; if ((PhyDev->Connection == PHY_CONNECTION_RGMII) || (PhyDev->Connection == PHY_CONNECTION_RGMII_ID) || (PhyDev->Connection == PHY_CONNECTION_RGMII_RXID) || (PhyDev->Connection == PHY_CONNECTION_RGMII_TXID)) { Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_CR, &Reg); if ((PhyDev->Connection == PHY_CONNECTION_RGMII) || (PhyDev->Connection == PHY_CONNECTION_RGMII_ID)) { Reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY); } else if (PhyDev->Connection == PHY_CONNECTION_RGMII_RXID) { Reg &= ~MIIM_88E1111_TX_DELAY; Reg |= MIIM_88E1111_RX_DELAY; } else if (PhyDev->Connection == PHY_CONNECTION_RGMII_TXID) { Reg &= ~MIIM_88E1111_RX_DELAY; Reg |= MIIM_88E1111_TX_DELAY; } Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_CR, Reg); Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, &Reg); Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK); if (Reg & MIIM_88E1111_HWCFG_FIBER_COPPER_RES) Reg |= MIIM_88E1111_HWCFG_MODE_FIBER_RGMII; else Reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RGMII; Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, Reg); } if (PhyDev->Connection == PHY_CONNECTION_SGMII) { Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, &Reg); Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK); Reg |= MIIM_88E1111_HWCFG_MODE_SGMII_NO_CLK; Reg |= MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO; Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, Reg); } if (PhyDev->Connection == PHY_CONNECTION_RTBI) { Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_CR, &Reg); Reg |= (MIIM_88E1111_RX_DELAY | MIIM_88E1111_TX_DELAY); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_CR, Reg); Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, &Reg); Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK | MIIM_88E1111_HWCFG_FIBER_COPPER_RES); Reg |= 0x7 | MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO; Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, Reg); /* Soft reset */ MvPhyReset (PhyDev); Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, &Reg); Reg &= ~(MIIM_88E1111_HWCFG_MODE_MASK | MIIM_88E1111_HWCFG_FIBER_COPPER_RES); Reg |= MIIM_88E1111_HWCFG_MODE_COPPER_RTBI | MIIM_88E1111_HWCFG_FIBER_COPPER_AUTO; Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1111_PHY_EXT_SR, Reg); } Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MII_BMCR, &Reg); Reg |= (BMCR_ANENABLE | BMCR_ANRESTART); Reg &= ~BMCR_ISOLATE; Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MII_BMCR, Reg); /* Soft reset */ MvPhyReset (PhyDev); return EFI_SUCCESS; } EFI_STATUS MvPhyParseStatus ( IN PHY_DEVICE *PhyDev ) { UINT32 Data; UINT32 Speed; Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1xxx_PHY_STATUS, &Data); if ((Data & MIIM_88E1xxx_PHYSTAT_LINK) && !(Data & MIIM_88E1xxx_PHYSTAT_SPDDONE)) { INTN i = 0; DEBUG((DEBUG_ERROR,"MvPhyDxe: Waiting for PHY realtime link")); while (!(Data & MIIM_88E1xxx_PHYSTAT_SPDDONE)) { if (i > PHY_AUTONEGOTIATE_TIMEOUT) { DEBUG((DEBUG_ERROR," TIMEOUT !\n")); PhyDev->LinkUp = FALSE; break; } if ((i++ % 1000) == 0) DEBUG((DEBUG_ERROR, ".")); gBS->Stall(1000); Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MIIM_88E1xxx_PHY_STATUS, &Data); } DEBUG((DEBUG_ERROR," done\n")); gBS->Stall(500000); } else { if (Data & MIIM_88E1xxx_PHYSTAT_LINK) { DEBUG((DEBUG_ERROR, "MvPhyDxe: link up, ")); PhyDev->LinkUp = TRUE; } else { DEBUG((DEBUG_ERROR, "MvPhyDxe: link down, ")); PhyDev->LinkUp = FALSE; } } if (Data & MIIM_88E1xxx_PHYSTAT_DUPLEX) { DEBUG((DEBUG_ERROR, "full duplex, ")); PhyDev->FullDuplex = TRUE; } else { DEBUG((DEBUG_ERROR, "half duplex, ")); PhyDev->FullDuplex = FALSE; } Speed = Data & MIIM_88E1xxx_PHYSTAT_SPEED; switch (Speed) { case MIIM_88E1xxx_PHYSTAT_GBIT: DEBUG((DEBUG_ERROR, "speed 1000\n")); PhyDev->Speed = SPEED_1000; break; case MIIM_88E1xxx_PHYSTAT_100: DEBUG((DEBUG_ERROR, "speed 100\n")); PhyDev->Speed = SPEED_100; break; default: DEBUG((DEBUG_ERROR, "speed 10\n")); PhyDev->Speed = SPEED_10; break; } return EFI_SUCCESS; } /** Configure PHY device autonegotiation. @param[in out] *PhyDevice A pointer to configured PHY device structure. **/ STATIC EFI_STATUS MvPhyConfigureAutonegotiation ( IN OUT PHY_DEVICE *PhyDevice ) { UINT32 Data; INTN Index; /* Read BMSR register in order to check autoneg capabilities and status. */ Mdio->Read (Mdio, PhyDevice->Addr, PhyDevice->MdioIndex, MII_BMSR, &Data); if ((Data & BMSR_ANEGCAPABLE) && !(Data & BMSR_ANEGCOMPLETE)) { DEBUG ((DEBUG_INFO, "%a: Waiting for PHY auto negotiation...", __FUNCTION__)); /* Wait for autonegotiation to complete and read media status */ for (Index = 0; !(Data & BMSR_ANEGCOMPLETE); Index++) { if (Index > PHY_AUTONEGOTIATE_TIMEOUT) { DEBUG ((DEBUG_ERROR, "%a: Timeout\n", __FUNCTION__)); PhyDevice->LinkUp = FALSE; return EFI_TIMEOUT; } gBS->Stall (1000); /* 1 ms */ Mdio->Read (Mdio, PhyDevice->Addr, PhyDevice->MdioIndex, MII_BMSR, &Data); } PhyDevice->LinkUp = TRUE; DEBUG ((DEBUG_INFO, "%a: link up\n", __FUNCTION__)); } else { Mdio->Read (Mdio, PhyDevice->Addr, PhyDevice->MdioIndex, MII_BMSR, &Data); if (Data & BMSR_LSTATUS) { PhyDevice->LinkUp = TRUE; DEBUG ((DEBUG_INFO, "%a: link up\n", __FUNCTION__)); } else { PhyDevice->LinkUp = FALSE; DEBUG ((DEBUG_INFO, "%a: link down\n", __FUNCTION__)); } } return EFI_SUCCESS; } STATIC VOID MvPhy1512WriteBits ( IN PHY_DEVICE *PhyDev, IN UINT8 RegNum, IN UINT16 Offset, IN UINT16 Len, IN UINT16 Data) { UINT32 Reg, Mask; if ((Len + Offset) >= 16) Mask = 0 - (1 << Offset); else Mask = (1 << (Len + Offset)) - (1 << Offset); Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, RegNum, &Reg); Reg &= ~Mask; Reg |= Data << Offset; Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, RegNum, Reg); } STATIC EFI_STATUS MvPhyInit1512 ( IN CONST MARVELL_PHY_PROTOCOL *Snp, IN OUT PHY_DEVICE *PhyDev ) { EFI_STATUS Status; if (PhyDev->Connection == PHY_CONNECTION_SGMII) { /* Select page 0xff and update configuration registers according to * Marvell Release Notes - Alaska 88E1510/88E1518/88E1512 Rev A0, * Errata Section 3.1 - needed in SGMII mode. */ Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 22, 0x00ff); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 17, 0x214B); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 16, 0x2144); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 17, 0x0C28); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 16, 0x2146); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 17, 0xB233); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 16, 0x214D); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 17, 0xCC0C); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 16, 0x2159); /* Reset page selection and select page 0x12 */ Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 22, 0x0000); Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 22, 0x0012); /* Write HWCFG_MODE = SGMII to Copper */ MvPhy1512WriteBits(PhyDev, 20, 0, 3, 1); /* Phy reset - necessary after changing mode */ MvPhy1512WriteBits(PhyDev, 20, 15, 1, 1); /* Reset page selection */ Mdio->Write (Mdio, PhyDev->Addr, PhyDev->MdioIndex, 22, 0x0000); gBS->Stall(100); } MvPhyM88e1111sConfig (PhyDev); /* autonegotiation on startup is not always required */ if (!PcdGetBool (PcdPhyStartupAutoneg)) return EFI_SUCCESS; Status = MvPhyConfigureAutonegotiation (PhyDev); if (EFI_ERROR (Status)) { return Status; } MvPhyParseStatus (PhyDev); return EFI_SUCCESS; } /** Initialize Marvell 88E1112 PHY. @param[in] MvPhyProtocol Marvell PHY protocol instance. @param[in out] *PhyDevice PHY device structure. **/ STATIC EFI_STATUS MvPhyInit1112 ( IN CONST MARVELL_PHY_PROTOCOL *MvPhyProtocol, IN OUT PHY_DEVICE *PhyDevice ) { EFI_STATUS Status; MvPhyM88e1111sConfig (PhyDevice); if (PcdGetBool (PcdPhyStartupAutoneg)) { Status = MvPhyConfigureAutonegotiation (PhyDevice); if (EFI_ERROR (Status)) { return Status; } MvPhyParseStatus (PhyDevice); } return EFI_SUCCESS; } EFI_STATUS MvPhyInit ( IN CONST MARVELL_PHY_PROTOCOL *Snp, IN UINT32 PhyIndex, IN PHY_CONNECTION PhyConnection, IN OUT PHY_DEVICE **OutPhyDev ) { EFI_STATUS Status; PHY_DEVICE *PhyDev; UINT8 *DeviceIds; UINT8 MdioIndex; UINT8 PhyId; Status = gBS->LocateProtocol ( &gMarvellMdioProtocolGuid, NULL, (VOID **) &Mdio ); if (EFI_ERROR(Status)) return Status; MdioIndex = Phy2MdioController[PhyIndex]; /* Verify correctness of PHY <-> MDIO assignment */ if ((MdioDeviceTable[MdioIndex] == 0) || (MdioIndex >= Mdio->ControllerCount)) { DEBUG ((DEBUG_ERROR, "MvPhyDxe: Incorrect Mdio controller assignment for PHY#%d", PhyIndex)); return EFI_INVALID_PARAMETER; } DeviceIds = PcdGetPtr (PcdPhyDeviceIds); PhyId = DeviceIds[PhyIndex]; if (PhyId >= MV_PHY_DEVICE_ID_MAX) { DEBUG ((DEBUG_ERROR, "%a, Incorrect PHY ID (0x%x) for PHY#%d\n", __FUNCTION__, PhyId, PhyIndex)); return EFI_INVALID_PARAMETER; } /* perform setup common for all PHYs */ PhyDev = AllocateZeroPool (sizeof (PHY_DEVICE)); PhyDev->Addr = PhySmiAddresses[PhyIndex]; PhyDev->Connection = PhyConnection; PhyDev->MdioIndex = MdioIndex; DEBUG ((DEBUG_INFO, "MvPhyDxe: MdioIndex is %d, PhyAddr is %d, connection %d\n", PhyDev->MdioIndex, PhyDev->Addr, PhyConnection)); *OutPhyDev = PhyDev; return MvPhyDevices[PhyId].DevInit (Snp, PhyDev); } EFI_STATUS MvPhyStatus ( IN CONST MARVELL_PHY_PROTOCOL *This, IN PHY_DEVICE *PhyDev ) { UINT32 Data; Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MII_BMSR, &Data); Mdio->Read (Mdio, PhyDev->Addr, PhyDev->MdioIndex, MII_BMSR, &Data); if ((Data & BMSR_LSTATUS) == 0) { PhyDev->LinkUp = FALSE; } else { PhyDev->LinkUp = TRUE; } return EFI_SUCCESS; } EFI_STATUS EFIAPI MvPhyDxeInitialise ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { MARVELL_PHY_PROTOCOL *Phy; EFI_STATUS Status; EFI_HANDLE Handle = NULL; Phy = AllocateZeroPool (sizeof (MARVELL_PHY_PROTOCOL)); Phy->Status = MvPhyStatus; Phy->Init = MvPhyInit; Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gMarvellPhyProtocolGuid, Phy, NULL ); if (EFI_ERROR(Status)) { DEBUG((DEBUG_ERROR, "Failed to install interfaces\n")); return Status; } DEBUG((DEBUG_ERROR, "Succesfully installed protocol interfaces\n")); return EFI_SUCCESS; }