2021-11-04 04:10:19 -04:00
/*
2023-08-02 06:28:18 -04:00
* SPDX - FileCopyrightText : 2019 - 2023 Espressif Systems ( Shanghai ) CO LTD
2021-11-04 04:10:19 -04:00
*
* SPDX - License - Identifier : Apache - 2.0
*/
2019-04-10 04:24:50 -04:00
# include <string.h>
# include <stdlib.h>
# include <sys/cdefs.h>
# include "esp_log.h"
2021-04-01 08:00:54 -04:00
# include "esp_check.h"
2023-09-25 05:29:51 -04:00
# include "freertos/FreeRTOS.h"
# include "freertos/task.h"
2022-05-13 06:03:56 -04:00
# include "esp_eth_phy_802_3.h"
2019-04-10 04:24:50 -04:00
2023-09-25 05:29:51 -04:00
# define RTL8201_PHY_RESET_ASSERTION_TIME_US 10000
# define RTL8201_PHY_POST_RESET_INIT_TIME_MS 150
2019-04-10 04:24:50 -04:00
static const char * TAG = " rtl8201 " ;
/***************Vendor Specific Register***************/
/**
* @ brief PSMR ( Power Saving Mode Register )
*
*/
typedef union {
struct {
uint16_t reserved : 15 ; /* Reserved */
uint16_t en_pwr_save : 1 ; /* Enable power saving mode */
} ;
uint16_t val ;
} psmr_reg_t ;
# define ETH_PHY_PSMR_REG_ADDR (0x18)
/**
* @ brief PSR ( Page Select Register )
*
*/
typedef union {
struct {
uint16_t page_select : 8 ; /* Select register page, default is 0 */
uint16_t reserved : 8 ; /* Reserved */
} ;
uint16_t val ;
} psr_reg_t ;
# define ETH_PHY_PSR_REG_ADDR (0x1F)
typedef struct {
2022-05-13 06:03:56 -04:00
phy_802_3_t phy_802_3 ;
2019-04-10 04:24:50 -04:00
} phy_rtl8201_t ;
static esp_err_t rtl8201_page_select ( phy_rtl8201_t * rtl8201 , uint32_t page )
{
2021-04-01 08:00:54 -04:00
esp_err_t ret = ESP_OK ;
2022-05-13 06:03:56 -04:00
esp_eth_mediator_t * eth = rtl8201 - > phy_802_3 . eth ;
2019-04-10 04:24:50 -04:00
psr_reg_t psr = {
. page_select = page
} ;
2022-05-13 06:03:56 -04:00
ESP_GOTO_ON_ERROR ( eth - > phy_reg_write ( eth , rtl8201 - > phy_802_3 . addr , ETH_PHY_PSR_REG_ADDR , psr . val ) , err , TAG , " write PSR failed " ) ;
2019-04-10 04:24:50 -04:00
return ESP_OK ;
err :
2021-04-01 08:00:54 -04:00
return ret ;
2019-04-10 04:24:50 -04:00
}
2019-09-18 23:27:42 -04:00
static esp_err_t rtl8201_update_link_duplex_speed ( phy_rtl8201_t * rtl8201 )
{
2021-04-01 08:00:54 -04:00
esp_err_t ret = ESP_OK ;
2022-05-13 06:03:56 -04:00
esp_eth_mediator_t * eth = rtl8201 - > phy_802_3 . eth ;
uint32_t addr = rtl8201 - > phy_802_3 . addr ;
2019-09-18 23:27:42 -04:00
eth_speed_t speed = ETH_SPEED_10M ;
eth_duplex_t duplex = ETH_DUPLEX_HALF ;
bmcr_reg_t bmcr ;
bmsr_reg_t bmsr ;
2020-07-20 08:42:52 -04:00
uint32_t peer_pause_ability = false ;
anlpar_reg_t anlpar ;
2021-04-01 08:00:54 -04:00
ESP_GOTO_ON_ERROR ( rtl8201_page_select ( rtl8201 , 0 ) , err , TAG , " select page 0 failed " ) ;
2022-05-13 06:03:56 -04:00
ESP_GOTO_ON_ERROR ( eth - > phy_reg_read ( eth , addr , ETH_PHY_BMSR_REG_ADDR , & ( bmsr . val ) ) , err , TAG , " read BMSR failed " ) ;
ESP_GOTO_ON_ERROR ( eth - > phy_reg_read ( eth , addr , ETH_PHY_ANLPAR_REG_ADDR , & ( anlpar . val ) ) , err , TAG , " read ANLPAR failed " ) ;
2019-09-18 23:27:42 -04:00
eth_link_t link = bmsr . link_status ? ETH_LINK_UP : ETH_LINK_DOWN ;
/* check if link status changed */
2022-05-13 06:03:56 -04:00
if ( rtl8201 - > phy_802_3 . link_status ! = link ) {
2019-09-18 23:27:42 -04:00
/* when link up, read negotiation result */
if ( link = = ETH_LINK_UP ) {
2022-05-13 06:03:56 -04:00
ESP_GOTO_ON_ERROR ( eth - > phy_reg_read ( eth , addr , ETH_PHY_BMCR_REG_ADDR , & ( bmcr . val ) ) , err , TAG , " read BMCR failed " ) ;
2019-09-18 23:27:42 -04:00
if ( bmcr . speed_select ) {
speed = ETH_SPEED_100M ;
} else {
speed = ETH_SPEED_10M ;
}
if ( bmcr . duplex_mode ) {
duplex = ETH_DUPLEX_FULL ;
} else {
duplex = ETH_DUPLEX_HALF ;
}
2021-04-01 08:00:54 -04:00
ESP_GOTO_ON_ERROR ( eth - > on_state_changed ( eth , ETH_STATE_SPEED , ( void * ) speed ) , err , TAG , " change speed failed " ) ;
ESP_GOTO_ON_ERROR ( eth - > on_state_changed ( eth , ETH_STATE_DUPLEX , ( void * ) duplex ) , err , TAG , " change duplex failed " ) ;
2020-07-20 08:42:52 -04:00
/* if we're in duplex mode, and peer has the flow control ability */
if ( duplex = = ETH_DUPLEX_FULL & & anlpar . symmetric_pause ) {
peer_pause_ability = 1 ;
} else {
peer_pause_ability = 0 ;
}
2021-04-01 08:00:54 -04:00
ESP_GOTO_ON_ERROR ( eth - > on_state_changed ( eth , ETH_STATE_PAUSE , ( void * ) peer_pause_ability ) , err , TAG , " change pause ability failed " ) ;
2019-09-18 23:27:42 -04:00
}
2021-04-01 08:00:54 -04:00
ESP_GOTO_ON_ERROR ( eth - > on_state_changed ( eth , ETH_STATE_LINK , ( void * ) link ) , err , TAG , " change link failed " ) ;
2022-05-13 06:03:56 -04:00
rtl8201 - > phy_802_3 . link_status = link ;
2019-09-18 23:27:42 -04:00
}
return ESP_OK ;
err :
2021-04-01 08:00:54 -04:00
return ret ;
2019-09-18 23:27:42 -04:00
}
2019-04-10 04:24:50 -04:00
static esp_err_t rtl8201_get_link ( esp_eth_phy_t * phy )
{
2021-04-01 08:00:54 -04:00
esp_err_t ret = ESP_OK ;
2022-05-13 06:03:56 -04:00
phy_rtl8201_t * rtl8201 = __containerof ( esp_eth_phy_into_phy_802_3 ( phy ) , phy_rtl8201_t , phy_802_3 ) ;
2019-09-18 23:27:42 -04:00
/* Updata information about link, speed, duplex */
2021-04-01 08:00:54 -04:00
ESP_GOTO_ON_ERROR ( rtl8201_update_link_duplex_speed ( rtl8201 ) , err , TAG , " update link duplex speed failed " ) ;
2019-04-10 04:24:50 -04:00
return ESP_OK ;
err :
2021-04-01 08:00:54 -04:00
return ret ;
2019-04-10 04:24:50 -04:00
}
2023-08-02 06:28:18 -04:00
static esp_err_t rtl8201_autonego_ctrl ( esp_eth_phy_t * phy , eth_phy_autoneg_cmd_t cmd , bool * autonego_en_stat )
{
esp_err_t ret = ESP_OK ;
phy_802_3_t * phy_802_3 = esp_eth_phy_into_phy_802_3 ( phy ) ;
esp_eth_mediator_t * eth = phy_802_3 - > eth ;
if ( cmd = = ESP_ETH_PHY_AUTONEGO_EN ) {
bmcr_reg_t bmcr ;
ESP_GOTO_ON_ERROR ( eth - > phy_reg_read ( eth , phy_802_3 - > addr , ETH_PHY_BMCR_REG_ADDR , & ( bmcr . val ) ) , err , TAG , " read BMCR failed " ) ;
ESP_GOTO_ON_FALSE ( bmcr . en_loopback = = 0 , ESP_ERR_INVALID_STATE , err , TAG , " Autonegotiation can't be enabled while in loopback operation " ) ;
}
return esp_eth_phy_802_3_autonego_ctrl ( phy_802_3 , cmd , autonego_en_stat ) ;
err :
return ret ;
}
static esp_err_t rtl8201_loopback ( esp_eth_phy_t * phy , bool enable )
{
esp_err_t ret = ESP_OK ;
phy_802_3_t * phy_802_3 = esp_eth_phy_into_phy_802_3 ( phy ) ;
bool auto_nego_en ;
ESP_GOTO_ON_ERROR ( rtl8201_autonego_ctrl ( phy , ESP_ETH_PHY_AUTONEGO_G_STAT , & auto_nego_en ) , err , TAG , " get status of autonegotiation failed " ) ;
ESP_GOTO_ON_FALSE ( ! ( auto_nego_en & & enable ) , ESP_ERR_INVALID_STATE , err , TAG , " Unable to set loopback while autonegotiation is enabled. Disable it to use loopback " ) ;
return esp_eth_phy_802_3_loopback ( phy_802_3 , enable ) ;
err :
return ret ;
}
2023-09-25 05:29:51 -04:00
static esp_err_t rtl8201_reset_hw ( esp_eth_phy_t * phy )
{
phy_802_3_t * phy_802_3 = esp_eth_phy_into_phy_802_3 ( phy ) ;
esp_err_t ret = esp_eth_phy_802_3_reset_hw ( phy_802_3 , RTL8201_PHY_RESET_ASSERTION_TIME_US ) ;
vTaskDelay ( pdMS_TO_TICKS ( RTL8201_PHY_POST_RESET_INIT_TIME_MS ) ) ;
return ret ;
}
2022-05-13 06:03:56 -04:00
static esp_err_t rtl8201_init ( esp_eth_phy_t * phy )
2021-11-04 04:10:19 -04:00
{
esp_err_t ret = ESP_OK ;
2022-05-13 06:03:56 -04:00
phy_802_3_t * phy_802_3 = esp_eth_phy_into_phy_802_3 ( phy ) ;
2021-11-04 04:10:19 -04:00
2022-05-13 06:03:56 -04:00
/* Basic PHY init */
ESP_GOTO_ON_ERROR ( esp_eth_phy_802_3_basic_phy_init ( phy_802_3 ) , err , TAG , " failed to init PHY " ) ;
2021-11-04 04:10:19 -04:00
2019-04-10 04:24:50 -04:00
/* Check PHY ID */
2022-05-13 06:03:56 -04:00
uint32_t oui ;
uint8_t model ;
ESP_GOTO_ON_ERROR ( esp_eth_phy_802_3_read_oui ( phy_802_3 , & oui ) , err , TAG , " read OUI failed " ) ;
ESP_GOTO_ON_ERROR ( esp_eth_phy_802_3_read_manufac_info ( phy_802_3 , & model , NULL ) , err , TAG , " read manufacturer's info failed " ) ;
ESP_GOTO_ON_FALSE ( oui = = 0x732 & & model = = 0x1 , ESP_FAIL , err , TAG , " wrong chip ID " ) ;
2019-04-10 04:24:50 -04:00
return ESP_OK ;
err :
2021-04-01 08:00:54 -04:00
return ret ;
2019-04-10 04:24:50 -04:00
}
esp_eth_phy_t * esp_eth_phy_new_rtl8201 ( const eth_phy_config_t * config )
{
2021-04-01 08:00:54 -04:00
esp_eth_phy_t * ret = NULL ;
2019-04-10 04:24:50 -04:00
phy_rtl8201_t * rtl8201 = calloc ( 1 , sizeof ( phy_rtl8201_t ) ) ;
2021-04-01 08:00:54 -04:00
ESP_GOTO_ON_FALSE ( rtl8201 , NULL , err , TAG , " calloc rtl8201 failed " ) ;
2022-05-13 06:03:56 -04:00
ESP_GOTO_ON_FALSE ( esp_eth_phy_802_3_obj_config_init ( & rtl8201 - > phy_802_3 , config ) = = ESP_OK ,
NULL , err , TAG , " configuration initialization of PHY 802.3 failed " ) ;
// redefine functions which need to be customized for sake of RTL8201
rtl8201 - > phy_802_3 . parent . init = rtl8201_init ;
rtl8201 - > phy_802_3 . parent . get_link = rtl8201_get_link ;
2023-08-02 06:28:18 -04:00
rtl8201 - > phy_802_3 . parent . autonego_ctrl = rtl8201_autonego_ctrl ;
rtl8201 - > phy_802_3 . parent . loopback = rtl8201_loopback ;
2023-09-25 05:29:51 -04:00
rtl8201 - > phy_802_3 . parent . reset_hw = rtl8201_reset_hw ;
2019-04-10 04:24:50 -04:00
2022-05-13 06:03:56 -04:00
return & rtl8201 - > phy_802_3 . parent ;
2019-04-10 04:24:50 -04:00
err :
2022-05-13 06:03:56 -04:00
if ( rtl8201 ! = NULL ) {
free ( rtl8201 ) ;
}
2021-04-01 08:00:54 -04:00
return ret ;
2019-04-10 04:24:50 -04:00
}