Introduction
The Ethernet PHY driver is currently part of the Enet low-level driver (LLD), it's dedicated to Ethernet PHY management. It implements a state machine required to handle the lifecycle of PHYs, from initialization to link establishment.
The PHY submodule interacts with an underlying MDIO hardware through a simple MDIO driver abstraction (see EnetPhy_Mdio) in order to perform operations like detecting alive and/or linked PHYs, and for PHY register accesses. The relationship between PHY, MDIO and Enet LLD integration layer is shown below.
Currently, the PHY driver supports only Clause-22 devices. Clause-45 devices are not supported, but read and write helper functions are provided.
PHY Driver
The top-layer of PHY driver is located at source/networking/enet/core/src/phy/enetphy.c
. This layer implements the basic APIs that are needed to communicate the driver, such as EnetPhy_open()
, EnetPhy_tick()
, EnetPhy_close()
, etc.
The Enet LLD is capable of supporting multiple PHYs running simultaneously; each PHY has its own driver instance, its own state machine and hence will follow independent lifecycle from other PHYs.
The lifecycle of the PHYs is handled by the PHY driver via a state machine implementation. This state machine is composed of the following states:
- FINDING: The driver will remain in this state until the PHY is detected as alive and a device-specific PHY driver has been bound to it.
- FOUND: The driver will initiate (soft) reset if requested in PHY configuration.
- RESET_WAIT: The driver will remain in this state while waiting for reset to complete.
- ENABLE: The driver will put the PHY in normal mode, perform PHY device-specific extended configuration and get common capabilities supported by the MAC port and local PHY device. Depending on the requested mode (auto-negotiation or manual), the PHY will either restart auto-negotiation or manually configure speed and duplexity.
- LOOPBACK: Last state if PHY loopback is enabled.
- NWAY_START: The driver will remain in this state while waiting for auto-negotiation to start.
- NWAY_WAIT: The driver will remain in this state while waiting for auto-negotiation to complete.
- LINK_WAIT: The driver will remain in this state while waiting for link up.
- LINKED: The PHY will remain in this state until the link is down, i.e. cable disconnection.
The diagram in the figure below provides a simplified view of the state transitions of the PHY state machine.
Refer to Appendix A for a more detailed view of the PHY state machine.
Device-Specific Drivers
The PHY driver model has been designed to partition device-specific operations from device-agnostic common operations. The former are implemented by PHY device specific drivers, while the latter can be carried out directly by the main PHY driver.
This model facilitates the addition of new drivers for PHY devices not yet supported in the Enet LLD. The EnetPhy_Drv
structure is defined as the interface that device specific drivers must implement. Since this structure is not exposed to the application, its scope is internal to the Enet LLD.
The members of the EnetPhy_Drv
structure can be mandatory or optional in nature. Optional members can be set to NULL if the PHY doesn't provide an implementation for them. The list below provides a description of the purpose of each EnetPhy_Drv
member.
- name: Driver name.
- isPhyDevSupported(): Function pointer used by the main PHY driver to check if this device specific driver support a given PHY hardware identified by its version (OUI). This function is used during PHY device to driver binding.
- isMacModeSupported(): The main PHY driver will call this function to check if the device specific driver supports the requested MAC mode (MII, RMII, RGMII, SGMII, etc).
- config(): The main PHY driver will call this function to allow the driver to perform any device-specific extended configuration. This function is optional and will not be called if the driver sets the function pointer to
NULL
.
- reset(): Device specific reset. This function is optional and will not be called if the driver sets the function pointer to
NULL
.
- isResetComplete(): The main PHY driver will call this function to check if the reset operation triggered by
reset()
function has completed. If reset()
is provided, isResetComplete()
must be provided as well.
- readExtReg(): The main PHY driver will call this function to read extended registers.
- writeExtReg(): The main PHY driver will call this function to write extended registers.
- printRegs(): The main PHY driver will call this function when EnetPhy_printRegs() is called. This function is optional and will not be called if the driver sets the function pointer to
NULL
.
The current version of Enet LLD includes the following PHY drivers:
- Generic PHY driver.
- TI DP83867 RGMII PHY driver.
- TI DP83869 RGMII PHY driver.
- TI DP83822 RMII PHY driver.
- VSC8514 QSGMII PHY driver.
The generic PHY driver is a special case because its implementation is limited to IEEE-Standard MII registers. Reuse of PHY generic function by other device-specific drivers is possible when their EnetPhy_Drv
implementation doesn't deviate from standard. The diagram in figure below shows the reuse of extended register read/write functions by the DP83867 driver.
Device specific drivers can be found at source/networking/enet/core/src/phy/*
.
Custom Board Support
The MCU+SDK enet driver supports a set of boards for each SoC out of the box
- Refer MCU+SDK release notes for platforms/board supported for each SoC
The board specific portion of the enet code is auto generated in the file ti_board_config.c for supported boards
For porting enet based applications to custom board the following need to be done: Enable "Custom Board" syscfg option
- Enabling “Custom Board” will prevent auto generation of board specific code.
- A C file will have to be then written that is specific to the board.
Board config C file
The board specific file should contain the following
- const EnetPhy_DrvInfoTbl gEnetPhyDrvTbl: This is a table of ENET PHY drivers supported on the board. Refer Enet custom PHY integration guide for details on how to populate this table Ethernet PHY Integration Guide
- EnetBoard_setupPorts(): This function should setup any board level muxes and configure any SoC level RGMII internal delay/ RMII configuration for the specific port.
- Refer API documentation for mcu_plus_sdk/source/networking/enet/utils/include/enet_board.h for details of function and arguments
- EnetBoard_getPhyCfg(): This function should return the ETHPHY specific configuration for a given port including any extended phy configuration
- Refer API documentation for mcu_plus_sdk/source/networking/enet/utils/include/enet_board.h for details of function and arguments
- EnetBoard_getMacAddrList(): This function should populate any board specific MAC addresses that are available on board eeprom. If the board does not have any board specific macAddresses this function should set argument
*pAvailMacEntries = 0
- EnetBoard_getId(): This function should return the board id. This is not used anywhere outside this file so the board id returned will depend on the implementation of EnetBoard_setupPorts()/EnetBoard_getPhyCfg() for the custom board if it refers the boardId to determine PHY config/setup ports.
- Refer mcu_plus_sdk/examples/networking/enet_layer2_multi_channel
- enet_custom_board_config.c for example illustrating custom board integration
PHY to Driver Binding
The PHY-to-driver binding is the process of selecting the best driver for a PHY device based on the device's unique identifier. This process is done by the main PHY driver upon alive detection of a PHY device and takes place in the FINDING state.
The device unique identifier is read from PHYIDR1 and PHYIDR2 registers and populated into a structure of type EnetPhy_Version.
The main PHY driver has a gEnetPhyDrvs
array which contains all device specific drivers that are registered and that will participate in the search for the best driver for the PHY that had been recently discovered.
static EnetPhyDrv_Handle gEnetPhyDrvs[] =
{
&gEnetPhyDrvVsc8514,
&gEnetPhyDrvDp83822,
&gEnetPhyDrvDp83867,
&gEnetPhyDrvDp83869,
&gEnetPhyDrvGeneric,
};
In the search process, the main PHY driver will call the isPhyDevSupported()
function of each registered driver. The drivers will use the EnetPhy_Version which is passed as an argument in order to determine whether the device is supported or not. If it is, isPhyDevSupported()
must return true
and the search process ends. If not, the search continues with the next registered device.
The generic PHY (which must be the last one in the gEnetPhyDrvs
array) will be bound to the device if no other driver can support a given PHY, but the PHY full functionality can't be guaranteed.
Implementing a New PHY Driver
The following list of steps is provided as guideline when adding a new PHY driver for a device which is not supported by Enet LLD.
- Enable "Custom Board" (CPSW/ICSSG -> Board Config -> Custom Board) option in Sys-Cfg gui. This will enable the example specific enet_custom_board_config.c file. Please refer to Custom Board Support
- Create the public PHY specific header file at
source/networking/enet/core/include/phy
.
- This header file should have the device extended configuration structure definition (if applicable) as well as auxiliary structures or enumerations.
- Create new source file and private header file (if needed) for the new PHY driver at
source/networking/enet/core/src/phy
.
- Declare a global structure of type
EnetPhy_Drv
, but don't make it static. This variable will be later accessed as an extern symbol by the Ethernet PHY driver.
- Initialize
EnetPhy_Drv
structure with the function pointers of the device specific implementation.
- Look for reuse of PHY generic functions if a device specific implementation is not needed.
EnetPhy_Drv gEnetPhyDrvMyDrv =
{
.name = "My PHY driver",
.isPhyDevSupported = MyPhy_isPhyDevSupported,
.isMacModeSupported = MyPhy_isMacModeSupported,
.config = MyPhy_config,
.reset = MyPhy_reset,
.isResetComplete = MyPhy_isResetComplete,
.runComplianceTest = NULL,
.printRegs = MyPhy_printRegs,
};
- Add the PHY source file to "FILES_common" in
makefile.cpsw.am243x.r5f.ti-arm-clang
at source/networking/enet/
. Same is applicable for ICSSG makefiles.
- In enet_custom_board_config.c file, Declare the variable of type
EnetPhy_Drv
as extern. Also, add it to the gEnetPhyDrvs
array.
extern EnetPhy_Drv gEnetPhyDrvGeneric;
extern EnetPhy_Drv gEnetPhyDrvDp83822;
extern EnetPhy_Drv gEnetPhyDrvDp83867;
extern EnetPhy_Drv gEnetPhyDrvDp83869;
extern EnetPhy_Drv gEnetPhyDrvVsc8514;
extern EnetPhy_Drv gEnetPhyDrvMyDrv;
static EnetPhyDrv_Handle gEnetPhyDrvs[] =
{
&gEnetPhyDrvMyDrv,
&gEnetPhyDrvVsc8514,
&gEnetPhyDrvDp83822,
&gEnetPhyDrvDp83867,
&gEnetPhyDrvDp83869,
&gEnetPhyDrvGeneric,
};
- Please refer to the existing code in enet_custom_board_config.c and add your PHY configuration similar
gEnetCpbBoard_dp83869PhyCfg
.
- Update the EnetBoard_PortCfg structure as needed and pass the above PHY configuration as extendedCfg. You can also refer to ti_board_config.c in generated folder for any example for further details.
Appendix
Appendix A
Detailed view of the PHY state machine.