Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-pdl-cat1 / drivers / source / cy_ephy.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 7 KB Fix file modes.
/***************************************************************************//**
* \file cy_ephy.c
* \version 1.0
*
* Provides an API implementation of the Ethernet PHY driver
*
********************************************************************************
* \copyright
* Copyright 2020, Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

#include "cy_device.h"

#if defined (CY_IP_MXETH)

#include "cy_ephy.h"

#if defined(__cplusplus)
extern "C" {
#endif

#define DEFAULT_PHY_ADDRESS     0
#define CY_EPHY_INVALID_VALUE   0xFFFF

cy_en_ephy_status_t Cy_EPHY_Init( cy_stc_ephy_t *phy, phy_read_handle fnRead, phy_write_handle fnWrite )
{
    CY_ASSERT_L2(phy != NULL);
    CY_ASSERT_L2(fnRead != NULL);
    CY_ASSERT_L2(fnWrite != NULL);

    phy->fnPhyRead = fnRead;
    phy->fnPhyWrite = fnWrite;
    phy->phyId = CY_EPHY_INVALID_VALUE;
    phy->state = CY_EPHY_DOWN;
    phy->anar=0;
    phy->bmcr=0;

    return CY_EPHY_SUCCESS;
}

/* discover connected phy. updated phy-id in ephy structure */
cy_en_ephy_status_t Cy_EPHY_Discover( cy_stc_ephy_t *phy )
{
    uint32_t phyAddress = DEFAULT_PHY_ADDRESS;
    uint32_t ulLowerID = 0;
    uint32_t ulUpperID = 0;
    uint32_t ulPhyID = 0;

    CY_ASSERT_L2(phy->fnPhyRead != NULL);
    CY_ASSERT_L2(phy->fnPhyWrite != NULL);

    phy->fnPhyRead( phyAddress, PHYREG_03_PHYSID2, &ulLowerID );
    if ( CY_EPHY_INVALID_VALUE == ulLowerID )
        return CY_EPHY_ERROR;
    /* A valid PHY id can not be all zeros or all ones. */
    if( ( ulLowerID != ( uint16_t )~0UL ) && ( ulLowerID != ( uint16_t )0UL ) )
    {
        phy->fnPhyRead( phyAddress, PHYREG_02_PHYSID1, &ulUpperID );
        if ( CY_EPHY_INVALID_VALUE == ulUpperID )
            return CY_EPHY_ERROR;
        ulPhyID = ( _VAL2FLD( PHYID_ID1, ulUpperID ) | _VAL2FLD( PHYID_ID2, ulLowerID ) );
    }
    phy->phyId = ulPhyID;
    return CY_EPHY_SUCCESS;
}

cy_en_ephy_status_t Cy_EPHY_Reset(cy_stc_ephy_t *phy)
{
    uint32_t ulConfig;
    uint32_t phyAddress = DEFAULT_PHY_ADDRESS;
    uint32_t delay, max_delay=10;

    CY_ASSERT_L2(phy->fnPhyRead != NULL);
    CY_ASSERT_L2(phy->fnPhyWrite != NULL);

    /* Read Control register. */
    phy->fnPhyRead( phyAddress, PHYREG_00_BMCR, &ulConfig );
    phy->fnPhyWrite( phyAddress, PHYREG_00_BMCR, ( ulConfig | PHYBMCR_RESET_Msk ) );
    /* The reset should last less than a second. */
    for( delay=0; delay < max_delay; delay++ )
    {
        phy->fnPhyRead( phyAddress, PHYREG_00_BMCR, &ulConfig );
        if( _FLD2VAL(PHYBMCR_RESET, ulConfig) == 0 )
        {
            break;
        }
        Cy_SysLib_Delay(100);
    }

    /* Clear the reset bits. */
    phy->fnPhyRead( phyAddress, PHYREG_00_BMCR, &ulConfig );
    phy->fnPhyWrite( phyAddress, PHYREG_00_BMCR, ( ulConfig & ( ~PHYBMCR_RESET_Msk ) ) );
    Cy_SysLib_Delay(50);
    return CY_EPHY_SUCCESS;
}

cy_en_ephy_status_t Cy_EPHY_Configure( cy_stc_ephy_t *phy, cy_stc_ephy_config_t *config )
{
    uint32_t ulConfig, ulAdvertise = 0;
    uint32_t phyAddress = DEFAULT_PHY_ADDRESS;

    CY_ASSERT_L2(phy->fnPhyRead != NULL);
    CY_ASSERT_L2(phy->fnPhyWrite != NULL);

    if( ( config->speed == CY_EPHY_SPEED_AUTO ) && ( config->duplex == CY_EPHY_DUPLEX_AUTO ) )
    {
        ulAdvertise = PHYANAR_CSMA_Msk | PHYANAR_ALL_Msk;
    }
    else
    {
        if( config->speed == CY_EPHY_SPEED_AUTO )
        {
            if( config->duplex == CY_EPHY_DUPLEX_FULL )
            {
                ulAdvertise |= PHYANAR_10BASE_T_FD_Msk | PHYANAR_100BASE_TX_FD_Msk;
            }
            else
            {
                ulAdvertise |= PHYANAR_10BASE_T_Msk | PHYANAR_100BASE_TX_Msk;
            }
        }
        else if( config->duplex == CY_EPHY_DUPLEX_AUTO )
        {
            if( config->speed == CY_EPHY_SPEED_10 )
            {
                ulAdvertise |= PHYANAR_10BASE_T_Msk | PHYANAR_10BASE_T_FD_Msk;
            }
            else
            {
                ulAdvertise |= PHYANAR_100BASE_TX_Msk | PHYANAR_100BASE_TX_FD_Msk;
            }
        }
        else if( config->speed == CY_EPHY_SPEED_100 )
        {
            if( config->duplex == CY_EPHY_DUPLEX_FULL )
            {
                ulAdvertise |= PHYANAR_100BASE_TX_FD_Msk;
            }
            else
            {
                ulAdvertise |= PHYANAR_100BASE_TX_Msk;
            }
        }
        else
        {
            if( config->duplex == CY_EPHY_DUPLEX_FULL )
            {
                ulAdvertise |= PHYANAR_10BASE_T_FD_Msk;
            }
            else
            {
                ulAdvertise |= PHYANAR_10BASE_T_Msk;
            }
        }
    }

    /* Send a reset commando to a set of PHY-ports. */
    Cy_EPHY_Reset( phy );

    /* Write advertise register. */
    phy->fnPhyWrite( phyAddress, PHYREG_04_ADVERTISE, ulAdvertise );

//    Cy_SysLib_Delay(500);

    /* Read Control register. */
    phy->fnPhyRead( phyAddress, PHYREG_00_BMCR, &ulConfig );

    ulConfig &= ~( PHYBMCR_SPEED_100_Msk | PHYBMCR_FULL_DUPLEX_Msk );

    ulConfig |= PHYBMCR_AN_ENABLE_Msk;

    if( ( config->speed == CY_EPHY_SPEED_100 ) || ( config->speed == CY_EPHY_SPEED_AUTO ) )
    {
        ulConfig |= PHYBMCR_SPEED_100_Msk;
    }
    else if( config->speed == CY_EPHY_SPEED_10 )
    {
        ulConfig &= ~PHYBMCR_SPEED_100_Msk;
    }

    if( ( config->duplex == CY_EPHY_DUPLEX_FULL ) || ( config->duplex == CY_EPHY_DUPLEX_AUTO ) )
    {
        ulConfig |= PHYBMCR_FULL_DUPLEX_Msk;
    }
    else if( config->duplex == CY_EPHY_DUPLEX_HALF )
    {
        ulConfig &= ~PHYBMCR_FULL_DUPLEX_Msk;
    }

    /* Keep these values for later use. */
    phy->bmcr = ulConfig & ~PHYBMCR_ISOLATE_Msk;
    phy->anar = ulAdvertise;
    return CY_EPHY_SUCCESS;
}

cy_en_ephy_status_t Cy_EPHY_StartAutoNegotiation(cy_stc_ephy_t *phy)
{
    uint32_t phyAddress=DEFAULT_PHY_ADDRESS;
    uint32_t ulRegValue;
    uint32 delay = 0, max_try=30;

    CY_ASSERT_L2(phy->fnPhyRead != NULL);
    CY_ASSERT_L2(phy->fnPhyWrite != NULL);

    /* Enable Auto-Negotiation. */
    phy->fnPhyWrite( phyAddress, PHYREG_04_ADVERTISE, phy->anar );
    phy->fnPhyWrite( phyAddress, PHYREG_00_BMCR, ( phy->bmcr | PHYBMCR_AN_RESTART_Msk ) );

    /* Wait until the auto-negotiation will be completed */
    /* max wait = 3s */
    for( delay=0; delay<max_try; delay++ )
    {
        phy->fnPhyRead( phyAddress, PHYREG_01_BMSR, &ulRegValue );
        if( _FLD2VAL( PHYBMSR_AN_COMPLETE,ulRegValue) != 0 )
        {
            break;
        }
        Cy_SysLib_Delay(100);
    }

    if(delay < max_try)
        return CY_EPHY_SUCCESS;
    else
        return CY_EPHY_ERROR;
}

/*
 * we need to read the status register twice, keeping the second value.
 */

uint32_t Cy_EPHY_GetLinkStatus(cy_stc_ephy_t *phy)
{
    uint32_t phyAddress=DEFAULT_PHY_ADDRESS;
    uint32_t status;

    CY_ASSERT_L2(phy->fnPhyRead != NULL);
    CY_ASSERT_L2(phy->fnPhyWrite != NULL);

    /* read bmcr */
    phy->fnPhyRead( phyAddress, PHYREG_00_BMCR, &status );

    /* check for auto-neg-restart. Autoneg is being started, therefore disregard
     * BMSR value and report link as down.
     */
    if (status & PHYBMCR_AN_RESTART_Msk )
        return 0;

    /* Read link and autonegotiation status */
    status = 0;
    phy->fnPhyRead( phyAddress, PHYREG_01_BMSR, &status );

    if (CY_EPHY_INVALID_VALUE == status)
        return 0;
    else
        return _FLD2VAL( PHYBMSR_LINK_STATUS, status );
}



#if defined(__cplusplus)
}
#endif

#endif /* CY_IP_MXETH */

/* [] END OF FILE */