Home | History | Annotate | Download | only in MvPhyDxe
      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