mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp_eth: IEEE 802.3 PHY MII Management Interface functionality grouped to one common file
This commit is contained in:
parent
26ca7cbaed
commit
43f3904304
@ -9,7 +9,7 @@ set(priv_requires driver log esp_timer)
|
|||||||
|
|
||||||
# If Ethernet disabled in Kconfig, this is a config-only component
|
# If Ethernet disabled in Kconfig, this is a config-only component
|
||||||
if(CONFIG_ETH_ENABLED)
|
if(CONFIG_ETH_ENABLED)
|
||||||
set(srcs "src/esp_eth.c" "src/esp_eth_phy.c")
|
set(srcs "src/esp_eth.c" "src/esp_eth_phy_802_3.c")
|
||||||
set(include "include")
|
set(include "include")
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
|
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||||
|
@ -113,19 +113,6 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
ESP_EVENT_DECLARE_BASE(ETH_EVENT);
|
ESP_EVENT_DECLARE_BASE(ETH_EVENT);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Detect PHY address
|
|
||||||
*
|
|
||||||
* @param[in] eth: mediator of Ethernet driver
|
|
||||||
* @param[out] detected_addr: a valid address after detection
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: detect phy address successfully
|
|
||||||
* - ESP_ERR_INVALID_ARG: invalid parameter
|
|
||||||
* - ESP_ERR_NOT_FOUND: can't detect any PHY device
|
|
||||||
* - ESP_FAIL: detect phy address failed because some error occurred
|
|
||||||
*/
|
|
||||||
esp_err_t esp_eth_detect_phy_addr(esp_eth_mediator_t *eth, int *detected_addr);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
134
components/esp_eth/include/esp_eth_phy_802_3.h
Normal file
134
components/esp_eth/include/esp_eth_phy_802_3.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_eth.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "eth_phy_802_3_regs.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IEEE 802.3 PHY object infostructure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
esp_eth_phy_t parent; /*!< Parent Ethernet PHY instance */
|
||||||
|
esp_eth_mediator_t *eth; /*!< Mediator of Ethernet driver */
|
||||||
|
int addr; /*!< PHY address */
|
||||||
|
uint32_t reset_timeout_ms; /*!< Reset timeout value (Unit: ms) */
|
||||||
|
uint32_t autonego_timeout_ms; /*!< Auto-negotiation timeout value (Unit: ms) */
|
||||||
|
eth_link_t link_status; /*!< Current Link status */
|
||||||
|
int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */
|
||||||
|
} phy_802_3_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs hardware reset with specific reset pin assertion time
|
||||||
|
*
|
||||||
|
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||||
|
* @param reset_assert_us Hardware reset pin assertion time
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: reset Ethernet PHY successfully
|
||||||
|
*/
|
||||||
|
esp_err_t esp_eth_phy_802_3_reset_hw(phy_802_3_t *phy_802_3, uint32_t reset_assert_us);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Detect PHY address
|
||||||
|
*
|
||||||
|
* @param eth Mediator of Ethernet driver
|
||||||
|
* @param[out] detected_addr: a valid address after detection
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: detect phy address successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: invalid parameter
|
||||||
|
* - ESP_ERR_NOT_FOUND: can't detect any PHY device
|
||||||
|
* - ESP_FAIL: detect phy address failed because some error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t esp_eth_phy_802_3_detect_phy_addr(esp_eth_mediator_t *eth, int *detected_addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs basic PHY chip initialization
|
||||||
|
*
|
||||||
|
* @note It should be called as the first function in PHY specific driver instance
|
||||||
|
*
|
||||||
|
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: initialized Ethernet PHY successfully
|
||||||
|
* - ESP_FAIL: initialization of Ethernet PHY failed because some error occurred
|
||||||
|
* - ESP_ERR_INVALID_ARG: invalid argument
|
||||||
|
* - ESP_ERR_NOT_FOUND: PHY device not detected
|
||||||
|
* - ESP_ERR_TIMEOUT: MII Management read/write operation timeout
|
||||||
|
* - ESP_ERR_INVALID_STATE: PHY is in invalid state to perform requested operation
|
||||||
|
*/
|
||||||
|
esp_err_t esp_eth_phy_802_3_basic_phy_init(phy_802_3_t *phy_802_3);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs basic PHY chip de-initialization
|
||||||
|
*
|
||||||
|
* @note It should be called as the last function in PHY specific driver instance
|
||||||
|
*
|
||||||
|
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: de-initialized Ethernet PHY successfully
|
||||||
|
* - ESP_FAIL: de-initialization of Ethernet PHY failed because some error occurred
|
||||||
|
* - ESP_ERR_TIMEOUT: MII Management read/write operation timeout
|
||||||
|
* - ESP_ERR_INVALID_STATE: PHY is in invalid state to perform requested operation
|
||||||
|
*/
|
||||||
|
esp_err_t esp_eth_phy_802_3_basic_phy_deinit(phy_802_3_t *phy_802_3);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads raw content of OUI field
|
||||||
|
*
|
||||||
|
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||||
|
* @param[out] oui OUI value
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: OUI field read successfully
|
||||||
|
* - ESP_FAIL: OUI field read failed because some error occurred
|
||||||
|
* - ESP_ERR_INVALID_ARG: invalid @c oui argument
|
||||||
|
* - ESP_ERR_TIMEOUT: MII Management read/write operation timeout
|
||||||
|
* - ESP_ERR_INVALID_STATE: PHY is in invalid state to perform requested operation
|
||||||
|
*/
|
||||||
|
esp_err_t esp_eth_phy_802_3_read_oui(phy_802_3_t *phy_802_3, uint32_t *oui);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads manufacturer’s model and revision number
|
||||||
|
*
|
||||||
|
* @param phy_802_3 IEEE 802.3 PHY object infostructure
|
||||||
|
* @param[out] model Manufacturer’s model number (can be NULL when not required)
|
||||||
|
* @param[out] rev Manufacturer’s revision number (can be NULL when not required)
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Manufacturer’s info read successfully
|
||||||
|
* - ESP_FAIL: Manufacturer’s info read failed because some error occurred
|
||||||
|
* - ESP_ERR_TIMEOUT: MII Management read/write operation timeout
|
||||||
|
* - ESP_ERR_INVALID_STATE: PHY is in invalid state to perform requested operation
|
||||||
|
*/
|
||||||
|
esp_err_t esp_eth_phy_802_3_read_manufac_info(phy_802_3_t *phy_802_3, uint8_t *model, uint8_t *rev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns address to parent IEEE 802.3 PHY object infostructure
|
||||||
|
*
|
||||||
|
* @param phy Ethernet PHY instance
|
||||||
|
* @return phy_802_3_t*
|
||||||
|
* - address to parent IEEE 802.3 PHY object infostructure
|
||||||
|
*/
|
||||||
|
phy_802_3_t *esp_eth_phy_into_phy_802_3(esp_eth_phy_t *phy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes configuration of parent IEEE 802.3 PHY object infostructure
|
||||||
|
*
|
||||||
|
* @param phy_802_3 Address to IEEE 802.3 PHY object infostructure
|
||||||
|
* @param config Configuration of the IEEE 802.3 PHY object
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: configuration initialized successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: invalid @c config argument
|
||||||
|
*/
|
||||||
|
esp_err_t esp_eth_phy_802_3_obj_config_init(phy_802_3_t *phy_802_3, const eth_phy_config_t *config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -1,16 +1,8 @@
|
|||||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// 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.
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -19,7 +11,11 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/******************Basic PHY Registers*******************/
|
/**
|
||||||
|
*
|
||||||
|
* This file defines basic PHY registers in compliance to IEEE 802.3, 22.2.4 Management functions section.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief BMCR(Basic Mode Control Register)
|
* @brief BMCR(Basic Mode Control Register)
|
430
components/esp_eth/src/esp_eth_phy_802_3.c
Normal file
430
components/esp_eth/src/esp_eth_phy_802_3.c
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_eth.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_rom_gpio.h"
|
||||||
|
#include "esp_rom_sys.h"
|
||||||
|
#include "esp_eth_phy_802_3.h"
|
||||||
|
|
||||||
|
static const char *TAG = "eth_phy_802_3";
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "mediator can't be null");
|
||||||
|
phy_802_3->eth = eth;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_reset(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
phy_802_3->link_status = ETH_LINK_DOWN;
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
bmcr_reg_t bmcr = {.reset = 1};
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
/* wait for reset complete */
|
||||||
|
uint32_t to = 0;
|
||||||
|
for (to = 0; to < phy_802_3->reset_timeout_ms / 10; to++) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
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");
|
||||||
|
if (!bmcr.reset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_GOTO_ON_FALSE(to < phy_802_3->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "reset timeout");
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY hardware reset with default assert time
|
||||||
|
*
|
||||||
|
* @note Default reset assertion time is selected to be 100us as it is most commonly used value among PHY chips.
|
||||||
|
* If your PHY chip requires different value, redefine the `reset_hw` function in derived PHY specific driver structure.
|
||||||
|
*
|
||||||
|
* @param phy Ethernet PHY instance
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
*/
|
||||||
|
static esp_err_t eth_phy_802_3_reset_hw_default(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
return esp_eth_phy_802_3_reset_hw(phy_802_3, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_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 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
||||||
|
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
||||||
|
/* in case any link status has changed, let's assume we're in link down status */
|
||||||
|
phy_802_3->link_status = ETH_LINK_DOWN;
|
||||||
|
|
||||||
|
bmcr.restart_auto_nego = 1; /* Restart Auto Negotiation */
|
||||||
|
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
/* Wait for auto negotiation complete */
|
||||||
|
bmsr_reg_t bmsr;
|
||||||
|
uint32_t to = 0;
|
||||||
|
for (to = 0; to < phy_802_3->autonego_timeout_ms / 100; to++) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
||||||
|
if (bmsr.auto_nego_complete) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((to >= phy_802_3->autonego_timeout_ms / 100) && (phy_802_3->link_status == ETH_LINK_UP)) {
|
||||||
|
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESP_ETH_PHY_AUTONEGO_DIS:
|
||||||
|
if (bmcr.en_auto_nego == 1) {
|
||||||
|
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
/* read configuration back */
|
||||||
|
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_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESP_ETH_PHY_AUTONEGO_EN:
|
||||||
|
if (bmcr.en_auto_nego == 0) {
|
||||||
|
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
/* read configuration back */
|
||||||
|
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_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
||||||
|
/* do nothing autonego_en_stat is set at the function end */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
*autonego_en_stat = bmcr.en_auto_nego;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_pwrctl(esp_eth_phy_t *phy, bool enable)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
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");
|
||||||
|
if (!enable) {
|
||||||
|
/* Enable IEEE Power Down Mode */
|
||||||
|
bmcr.power_down = 1;
|
||||||
|
} else {
|
||||||
|
/* Disable IEEE Power Down Mode */
|
||||||
|
bmcr.power_down = 0;
|
||||||
|
}
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
if (!enable) {
|
||||||
|
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.power_down == 1, ESP_FAIL, err, TAG, "power down failed");
|
||||||
|
} else {
|
||||||
|
/* wait for power up complete */
|
||||||
|
uint32_t to = 0;
|
||||||
|
for (to = 0; to < phy_802_3->reset_timeout_ms / 10; to++) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
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");
|
||||||
|
if (bmcr.power_down == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_GOTO_ON_FALSE(to < phy_802_3->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "power up timeout");
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
||||||
|
{
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
phy_802_3->addr = addr;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
||||||
|
*addr = phy_802_3->addr;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
/* Set PAUSE function ability */
|
||||||
|
anar_reg_t anar;
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed");
|
||||||
|
if (ability) {
|
||||||
|
anar.asymmetric_pause = 1;
|
||||||
|
anar.symmetric_pause = 1;
|
||||||
|
} else {
|
||||||
|
anar.asymmetric_pause = 0;
|
||||||
|
anar.symmetric_pause = 0;
|
||||||
|
}
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_ANAR_REG_ADDR, anar.val), err, TAG, "write ANAR failed");
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_loopback(esp_eth_phy_t *phy, bool enable)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
/* Set Loopback function */
|
||||||
|
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");
|
||||||
|
if (enable) {
|
||||||
|
bmcr.en_loopback = 1;
|
||||||
|
} else {
|
||||||
|
bmcr.en_loopback = 0;
|
||||||
|
}
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
|
||||||
|
if (phy_802_3->link_status == ETH_LINK_UP) {
|
||||||
|
/* Since the link is going to be reconfigured, consider it down for a while */
|
||||||
|
phy_802_3->link_status = ETH_LINK_DOWN;
|
||||||
|
/* Indicate to upper stream apps the link is cosidered down */
|
||||||
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)phy_802_3->link_status), err, TAG, "change link failed");
|
||||||
|
}
|
||||||
|
/* Set speed */
|
||||||
|
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");
|
||||||
|
bmcr.speed_select = speed == ETH_SPEED_100M ? 1 : 0;
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
|
||||||
|
if (phy_802_3->link_status == ETH_LINK_UP) {
|
||||||
|
/* Since the link is going to be reconfigured, consider it down for a while */
|
||||||
|
phy_802_3->link_status = ETH_LINK_DOWN;
|
||||||
|
/* Indicate to upper stream apps the link is cosidered down */
|
||||||
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)phy_802_3->link_status), err, TAG, "change link failed");
|
||||||
|
}
|
||||||
|
/* Set duplex mode */
|
||||||
|
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");
|
||||||
|
bmcr.duplex_mode = duplex;
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_init(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
return esp_eth_phy_802_3_basic_phy_init(phy_802_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_deinit(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
phy_802_3_t *phy_802_3 = __containerof(phy, phy_802_3_t, parent);
|
||||||
|
return esp_eth_phy_802_3_basic_phy_deinit(phy_802_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t eth_phy_802_3_del(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
free(phy);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_eth_phy_802_3_reset_hw(phy_802_3_t *phy_802_3, uint32_t reset_assert_us)
|
||||||
|
{
|
||||||
|
if (phy_802_3->reset_gpio_num >= 0) {
|
||||||
|
esp_rom_gpio_pad_select_gpio(phy_802_3->reset_gpio_num);
|
||||||
|
gpio_set_direction(phy_802_3->reset_gpio_num, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_level(phy_802_3->reset_gpio_num, 0);
|
||||||
|
esp_rom_delay_us(reset_assert_us);
|
||||||
|
gpio_set_level(phy_802_3->reset_gpio_num, 1);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_eth_phy_802_3_detect_phy_addr(esp_eth_mediator_t *eth, int *detected_addr)
|
||||||
|
{
|
||||||
|
if (!eth || !detected_addr) {
|
||||||
|
ESP_LOGE(TAG, "eth and detected_addr can't be null");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
int addr_try = 0;
|
||||||
|
uint32_t reg_value = 0;
|
||||||
|
for (; addr_try < 16; addr_try++) {
|
||||||
|
eth->phy_reg_read(eth, addr_try, ETH_PHY_IDR1_REG_ADDR, ®_value);
|
||||||
|
if (reg_value != 0xFFFF && reg_value != 0x00) {
|
||||||
|
*detected_addr = addr_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addr_try < 16) {
|
||||||
|
ESP_LOGD(TAG, "Found PHY address: %d", addr_try);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
ESP_LOGE(TAG, "No PHY device detected");
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_eth_phy_802_3_basic_phy_init(phy_802_3_t *phy_802_3)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
// Detect PHY address
|
||||||
|
if (phy_802_3->addr == ESP_ETH_PHY_ADDR_AUTO) {
|
||||||
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_detect_phy_addr(phy_802_3->eth, &phy_802_3->addr), err, TAG, "Detect PHY address failed");
|
||||||
|
}
|
||||||
|
/* Power on Ethernet PHY */
|
||||||
|
ESP_GOTO_ON_ERROR(eth_phy_802_3_pwrctl(&phy_802_3->parent, true), err, TAG, "power control failed");
|
||||||
|
/* Reset Ethernet PHY */
|
||||||
|
ESP_GOTO_ON_ERROR(eth_phy_802_3_reset(&phy_802_3->parent), err, TAG, "reset failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_eth_phy_802_3_basic_phy_deinit(phy_802_3_t *phy_802_3)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
/* Power off Ethernet PHY */
|
||||||
|
ESP_GOTO_ON_ERROR(eth_phy_802_3_pwrctl(&phy_802_3->parent, false), err, TAG, "power control failed");
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_eth_phy_802_3_read_oui(phy_802_3_t *phy_802_3, uint32_t *oui)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
phyidr1_reg_t id1;
|
||||||
|
phyidr2_reg_t id2;
|
||||||
|
|
||||||
|
ESP_GOTO_ON_FALSE(oui != NULL, ESP_ERR_INVALID_ARG, err, TAG, "oui can't be null");
|
||||||
|
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed");
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed");
|
||||||
|
|
||||||
|
*oui = (id1.oui_msb << 6) | id2.oui_lsb;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_eth_phy_802_3_read_manufac_info(phy_802_3_t *phy_802_3, uint8_t *model, uint8_t *rev)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
esp_eth_mediator_t *eth = phy_802_3->eth;
|
||||||
|
|
||||||
|
phyidr2_reg_t id2;
|
||||||
|
|
||||||
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed");
|
||||||
|
|
||||||
|
if (model != NULL) {
|
||||||
|
*model = id2.vendor_model;
|
||||||
|
}
|
||||||
|
if (rev != NULL) {
|
||||||
|
*rev = id2.model_revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
phy_802_3_t *esp_eth_phy_into_phy_802_3(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
return __containerof(phy, phy_802_3_t, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_eth_phy_802_3_obj_config_init(phy_802_3_t *phy_802_3, const eth_phy_config_t *config)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
ESP_GOTO_ON_FALSE(config, ESP_ERR_INVALID_ARG, err, TAG, "can't set phy config to null");
|
||||||
|
|
||||||
|
phy_802_3->link_status = ETH_LINK_DOWN;
|
||||||
|
phy_802_3->addr = config->phy_addr;
|
||||||
|
phy_802_3->reset_timeout_ms = config->reset_timeout_ms;
|
||||||
|
phy_802_3->reset_gpio_num = config->reset_gpio_num;
|
||||||
|
phy_802_3->autonego_timeout_ms = config->autonego_timeout_ms;
|
||||||
|
|
||||||
|
phy_802_3->parent.reset = eth_phy_802_3_reset;
|
||||||
|
phy_802_3->parent.reset_hw = eth_phy_802_3_reset_hw_default;
|
||||||
|
phy_802_3->parent.init = eth_phy_802_3_init;
|
||||||
|
phy_802_3->parent.deinit = eth_phy_802_3_deinit;
|
||||||
|
phy_802_3->parent.set_mediator = eth_phy_802_3_set_mediator;
|
||||||
|
phy_802_3->parent.autonego_ctrl = eth_phy_802_3_autonego_ctrl;
|
||||||
|
phy_802_3->parent.pwrctl = eth_phy_802_3_pwrctl;
|
||||||
|
phy_802_3->parent.get_addr = eth_phy_802_3_get_addr;
|
||||||
|
phy_802_3->parent.set_addr = eth_phy_802_3_set_addr;
|
||||||
|
phy_802_3->parent.advertise_pause_ability = eth_phy_802_3_advertise_pause_ability;
|
||||||
|
phy_802_3->parent.loopback = eth_phy_802_3_loopback;
|
||||||
|
phy_802_3->parent.set_speed = eth_phy_802_3_set_speed;
|
||||||
|
phy_802_3->parent.set_duplex = eth_phy_802_3_set_duplex;
|
||||||
|
phy_802_3->parent.del = eth_phy_802_3_del;
|
||||||
|
phy_802_3->parent.get_link = NULL;
|
||||||
|
phy_802_3->parent.custom_ioctl = NULL;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -8,13 +8,9 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_eth_driver.h"
|
#include "esp_eth_phy_802_3.h"
|
||||||
#include "eth_phy_regs_struct.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_rom_gpio.h"
|
|
||||||
#include "esp_rom_sys.h"
|
|
||||||
|
|
||||||
static const char *TAG = "dm9051.phy";
|
static const char *TAG = "dm9051.phy";
|
||||||
|
|
||||||
@ -64,19 +60,14 @@ typedef union {
|
|||||||
#define ETH_PHY_DSCSR_REG_ADDR (0x11)
|
#define ETH_PHY_DSCSR_REG_ADDR (0x11)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
esp_eth_phy_t parent;
|
phy_802_3_t phy_802_3;
|
||||||
esp_eth_mediator_t *eth;
|
|
||||||
int addr;
|
|
||||||
uint32_t reset_timeout_ms;
|
|
||||||
uint32_t autonego_timeout_ms;
|
|
||||||
eth_link_t link_status;
|
|
||||||
int reset_gpio_num;
|
|
||||||
} phy_dm9051_t;
|
} phy_dm9051_t;
|
||||||
|
|
||||||
static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
|
static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
esp_eth_mediator_t *eth = dm9051->phy_802_3.eth;
|
||||||
|
uint32_t addr = dm9051->phy_802_3.addr;
|
||||||
eth_speed_t speed = ETH_SPEED_10M;
|
eth_speed_t speed = ETH_SPEED_10M;
|
||||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||||
uint32_t peer_pause_ability = false;
|
uint32_t peer_pause_ability = false;
|
||||||
@ -86,15 +77,15 @@ static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
|
|||||||
// BMSR is a latch low register
|
// BMSR is a latch low register
|
||||||
// after power up, the first latched value must be 0, which means down
|
// after power up, the first latched value must be 0, which means down
|
||||||
// to speed up power up link speed, double read this register as a workaround
|
// to speed up power up link speed, double read this register as a workaround
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->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_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->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_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
||||||
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
/* check if link status changed */
|
/* check if link status changed */
|
||||||
if (dm9051->link_status != link) {
|
if (dm9051->phy_802_3.link_status != link) {
|
||||||
/* when link up, read negotiation result */
|
/* when link up, read negotiation result */
|
||||||
if (link == ETH_LINK_UP) {
|
if (link == ETH_LINK_UP) {
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCSR_REG_ADDR, &(dscsr.val)), err, TAG, "read DSCSR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_DSCSR_REG_ADDR, &(dscsr.val)), err, TAG, "read DSCSR failed");
|
||||||
if (dscsr.fdx100 || dscsr.hdx100) {
|
if (dscsr.fdx100 || dscsr.hdx100) {
|
||||||
speed = ETH_SPEED_100M;
|
speed = ETH_SPEED_100M;
|
||||||
} else {
|
} else {
|
||||||
@ -116,28 +107,17 @@ static esp_err_t dm9051_update_link_duplex_speed(phy_dm9051_t *dm9051)
|
|||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
||||||
dm9051->link_status = link;
|
dm9051->phy_802_3.link_status = link;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t dm9051_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
dm9051->eth = eth;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_get_link(esp_eth_phy_t *phy)
|
static esp_err_t dm9051_get_link(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
phy_dm9051_t *dm9051 = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_dm9051_t, phy_802_3);
|
||||||
/* Update information about link, speed, duplex */
|
/* Update information about link, speed, duplex */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_update_link_duplex_speed(dm9051), err, TAG, "update link duplex speed failed");
|
ESP_GOTO_ON_ERROR(dm9051_update_link_duplex_speed(dm9051), err, TAG, "update link duplex speed failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -148,253 +128,27 @@ err:
|
|||||||
static esp_err_t dm9051_reset(esp_eth_phy_t *phy)
|
static esp_err_t dm9051_reset(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
phy_dm9051_t *dm9051 = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_dm9051_t, phy_802_3);
|
||||||
dm9051->link_status = ETH_LINK_DOWN;
|
uint32_t addr = dm9051->phy_802_3.addr;
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
dm9051->phy_802_3.link_status = ETH_LINK_DOWN;
|
||||||
|
esp_eth_mediator_t *eth = dm9051->phy_802_3.eth;
|
||||||
dscr_reg_t dscr;
|
dscr_reg_t dscr;
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)), err, TAG, "read DSCR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)), err, TAG, "read DSCR failed");
|
||||||
dscr.smrst = 1;
|
dscr.smrst = 1;
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, dscr.val), err, TAG, "write DSCR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, addr, ETH_PHY_DSCR_REG_ADDR, dscr.val), err, TAG, "write DSCR failed");
|
||||||
bmcr_reg_t bmcr = {.reset = 1};
|
bmcr_reg_t bmcr = {.reset = 1};
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
||||||
/* Wait for reset complete */
|
/* Wait for reset complete */
|
||||||
uint32_t to = 0;
|
uint32_t to = 0;
|
||||||
for (to = 0; to < dm9051->reset_timeout_ms / 10; to++) {
|
for (to = 0; to < dm9051->phy_802_3.reset_timeout_ms / 10; to++) {
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)), err, TAG, "read DSCR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_DSCR_REG_ADDR, &(dscr.val)), err, TAG, "read DSCR failed");
|
||||||
if (!bmcr.reset && !dscr.smrst) {
|
if (!bmcr.reset && !dscr.smrst) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_FALSE(to < dm9051->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "PHY reset timeout");
|
ESP_GOTO_ON_FALSE(to < dm9051->phy_802_3.reset_timeout_ms / 10, ESP_FAIL, err, TAG, "PHY reset timeout");
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_reset_hw(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
// set reset_gpio_num minus zero can skip hardware reset phy chip
|
|
||||||
if (dm9051->reset_gpio_num >= 0) {
|
|
||||||
esp_rom_gpio_pad_select_gpio(dm9051->reset_gpio_num);
|
|
||||||
gpio_set_direction(dm9051->reset_gpio_num, GPIO_MODE_OUTPUT);
|
|
||||||
gpio_set_level(dm9051->reset_gpio_num, 0);
|
|
||||||
esp_rom_delay_us(100); // insert min input assert time
|
|
||||||
gpio_set_level(dm9051->reset_gpio_num, 1);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note This function is responsible for restarting a new auto-negotiation,
|
|
||||||
* the result of negotiation won't be relected to uppler layers.
|
|
||||||
* Instead, the negotiation result is fetched by linker timer, see `dm9051_get_link()`
|
|
||||||
*/
|
|
||||||
static esp_err_t dm9051_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
|
||||||
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
|
||||||
/* in case any link status has changed, let's assume we're in link down status */
|
|
||||||
dm9051->link_status = ETH_LINK_DOWN;
|
|
||||||
|
|
||||||
bmcr.restart_auto_nego = 1; /* Restart Auto Negotiation */
|
|
||||||
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for auto negotiation complete */
|
|
||||||
bmsr_reg_t bmsr;
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < dm9051->autonego_timeout_ms / 100; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
|
||||||
if (bmsr.auto_nego_complete) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((to >= dm9051->autonego_timeout_ms / 100) && (dm9051->link_status == ETH_LINK_UP)) {
|
|
||||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
|
||||||
if (bmcr.en_auto_nego == 1) {
|
|
||||||
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
|
||||||
if (bmcr.en_auto_nego == 0) {
|
|
||||||
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
|
||||||
/* do nothing autonego_en_stat is set at the function end */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
*autonego_en_stat = bmcr.en_auto_nego;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_pwrctl(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
/* Enable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 1;
|
|
||||||
} else {
|
|
||||||
/* Disable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.power_down == 1, ESP_FAIL, err, TAG, "power down failed");
|
|
||||||
} else {
|
|
||||||
/* wait for power up complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < dm9051->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (bmcr.power_down == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < dm9051->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "power up timeout");
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
|
||||||
{
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
dm9051->addr = addr;
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
*addr = dm9051->addr;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_del(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
free(dm9051);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
|
||||||
/* Set PAUSE function ability */
|
|
||||||
anar_reg_t anar;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed");
|
|
||||||
if (ability) {
|
|
||||||
anar.asymmetric_pause = 1;
|
|
||||||
anar.symmetric_pause = 1;
|
|
||||||
} else {
|
|
||||||
anar.asymmetric_pause = 0;
|
|
||||||
anar.symmetric_pause = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_ANAR_REG_ADDR, anar.val), err, TAG, "write ANAR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_loopback(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
|
||||||
/* Set Loopback function */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (enable) {
|
|
||||||
bmcr.en_loopback = 1;
|
|
||||||
} else {
|
|
||||||
bmcr.en_loopback = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
|
||||||
if (dm9051->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
dm9051->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)dm9051->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set speed */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.speed_select = speed;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
|
||||||
|
|
||||||
if (dm9051->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
dm9051->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)dm9051->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set duplex mode */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.duplex_mode = duplex;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -403,32 +157,18 @@ err:
|
|||||||
static esp_err_t dm9051_init(esp_eth_phy_t *phy)
|
static esp_err_t dm9051_init(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||||
esp_eth_mediator_t *eth = dm9051->eth;
|
|
||||||
// Detect PHY address
|
/* Basic PHY init */
|
||||||
if (dm9051->addr == ESP_ETH_PHY_ADDR_AUTO) {
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY");
|
||||||
ESP_GOTO_ON_ERROR(esp_eth_detect_phy_addr(eth, &dm9051->addr), err, TAG, "Detect PHY address failed");
|
|
||||||
}
|
/* Check PHY ID */
|
||||||
/* Power on Ethernet PHY */
|
uint32_t oui;
|
||||||
ESP_GOTO_ON_ERROR(dm9051_pwrctl(phy, true), err, TAG, "power control failed");
|
uint8_t model;
|
||||||
/* Reset Ethernet PHY */
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed");
|
||||||
ESP_GOTO_ON_ERROR(dm9051_reset(phy), err, TAG, "reset 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");
|
||||||
/* Check PHY ID */
|
ESP_GOTO_ON_FALSE(oui == 0x606E && model == 0x0A, ESP_FAIL, err, TAG, "wrong chip ID");
|
||||||
phyidr1_reg_t id1;
|
|
||||||
phyidr2_reg_t id2;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed");
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed");
|
|
||||||
ESP_GOTO_ON_FALSE(id1.oui_msb == 0x0181 && id2.oui_lsb == 0x2E && id2.vendor_model == 0x0A, ESP_FAIL, err, TAG, "wrong chip ID");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dm9051_deinit(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
/* Power off Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(dm9051_pwrctl(phy, false), err, TAG, "power control failed");
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -437,30 +177,20 @@ err:
|
|||||||
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config)
|
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config)
|
||||||
{
|
{
|
||||||
esp_eth_phy_t *ret = NULL;
|
esp_eth_phy_t *ret = NULL;
|
||||||
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "can't set phy config to null");
|
|
||||||
phy_dm9051_t *dm9051 = calloc(1, sizeof(phy_dm9051_t));
|
phy_dm9051_t *dm9051 = calloc(1, sizeof(phy_dm9051_t));
|
||||||
ESP_GOTO_ON_FALSE(dm9051, NULL, err, TAG, "calloc dm9051 failed");
|
ESP_GOTO_ON_FALSE(dm9051, NULL, err, TAG, "calloc dm9051 failed");
|
||||||
dm9051->addr = config->phy_addr;
|
ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&dm9051->phy_802_3, config) == ESP_OK,
|
||||||
dm9051->reset_timeout_ms = config->reset_timeout_ms;
|
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||||
dm9051->reset_gpio_num = config->reset_gpio_num;
|
|
||||||
dm9051->link_status = ETH_LINK_DOWN;
|
// redefine functions which need to be customized for sake of dm9051
|
||||||
dm9051->autonego_timeout_ms = config->autonego_timeout_ms;
|
dm9051->phy_802_3.parent.init = dm9051_init;
|
||||||
dm9051->parent.reset = dm9051_reset;
|
dm9051->phy_802_3.parent.reset = dm9051_reset;
|
||||||
dm9051->parent.reset_hw = dm9051_reset_hw;
|
dm9051->phy_802_3.parent.get_link = dm9051_get_link;
|
||||||
dm9051->parent.init = dm9051_init;
|
|
||||||
dm9051->parent.deinit = dm9051_deinit;
|
return &dm9051->phy_802_3.parent;
|
||||||
dm9051->parent.set_mediator = dm9051_set_mediator;
|
|
||||||
dm9051->parent.autonego_ctrl = dm9051_autonego_ctrl;
|
|
||||||
dm9051->parent.get_link = dm9051_get_link;
|
|
||||||
dm9051->parent.pwrctl = dm9051_pwrctl;
|
|
||||||
dm9051->parent.get_addr = dm9051_get_addr;
|
|
||||||
dm9051->parent.set_addr = dm9051_set_addr;
|
|
||||||
dm9051->parent.advertise_pause_ability = dm9051_advertise_pause_ability;
|
|
||||||
dm9051->parent.loopback = dm9051_loopback;
|
|
||||||
dm9051->parent.set_speed = dm9051_set_speed;
|
|
||||||
dm9051->parent.set_duplex = dm9051_set_duplex;
|
|
||||||
dm9051->parent.del = dm9051_del;
|
|
||||||
return &(dm9051->parent);
|
|
||||||
err:
|
err:
|
||||||
|
if (dm9051 != NULL) {
|
||||||
|
free(dm9051);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -8,13 +8,7 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_eth_driver.h"
|
#include "esp_eth_phy_802_3.h"
|
||||||
#include "eth_phy_regs_struct.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_rom_gpio.h"
|
|
||||||
#include "esp_rom_sys.h"
|
|
||||||
|
|
||||||
static const char *TAG = "dp83848";
|
static const char *TAG = "dp83848";
|
||||||
|
|
||||||
@ -70,29 +64,24 @@ typedef union {
|
|||||||
#define ETH_PHY_CR_REG_ADDR (0x19)
|
#define ETH_PHY_CR_REG_ADDR (0x19)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
esp_eth_phy_t parent;
|
phy_802_3_t phy_802_3;
|
||||||
esp_eth_mediator_t *eth;
|
|
||||||
int addr;
|
|
||||||
uint32_t reset_timeout_ms;
|
|
||||||
uint32_t autonego_timeout_ms;
|
|
||||||
eth_link_t link_status;
|
|
||||||
int reset_gpio_num;
|
|
||||||
} phy_dp83848_t;
|
} phy_dp83848_t;
|
||||||
|
|
||||||
static esp_err_t dp83848_update_link_duplex_speed(phy_dp83848_t *dp83848)
|
static esp_err_t dp83848_update_link_duplex_speed(phy_dp83848_t *dp83848)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
esp_eth_mediator_t *eth = dp83848->phy_802_3.eth;
|
||||||
|
uint32_t addr = dp83848->phy_802_3.addr;
|
||||||
eth_speed_t speed = ETH_SPEED_10M;
|
eth_speed_t speed = ETH_SPEED_10M;
|
||||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||||
uint32_t peer_pause_ability = false;
|
uint32_t peer_pause_ability = false;
|
||||||
anlpar_reg_t anlpar;
|
anlpar_reg_t anlpar;
|
||||||
physts_reg_t physts;
|
physts_reg_t physts;
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)), err, TAG, "read PHYSTS failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_STS_REG_ADDR, &(physts.val)), err, TAG, "read PHYSTS failed");
|
||||||
eth_link_t link = physts.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
eth_link_t link = physts.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
/* check if link status changed */
|
/* check if link status changed */
|
||||||
if (dp83848->link_status != link) {
|
if (dp83848->phy_802_3.link_status != link) {
|
||||||
/* when link up, read negotiation result */
|
/* when link up, read negotiation result */
|
||||||
if (link == ETH_LINK_UP) {
|
if (link == ETH_LINK_UP) {
|
||||||
if (physts.speed_status) {
|
if (physts.speed_status) {
|
||||||
@ -116,28 +105,17 @@ static esp_err_t dp83848_update_link_duplex_speed(phy_dp83848_t *dp83848)
|
|||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
||||||
dp83848->link_status = link;
|
dp83848->phy_802_3.link_status = link;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t dp83848_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
dp83848->eth = eth;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_get_link(esp_eth_phy_t *phy)
|
static esp_err_t dp83848_get_link(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
phy_dp83848_t *dp83848 = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_dp83848_t, phy_802_3);
|
||||||
/* Update information about link, speed, duplex */
|
/* Update information about link, speed, duplex */
|
||||||
ESP_GOTO_ON_ERROR(dp83848_update_link_duplex_speed(dp83848), err, TAG, "update link duplex speed failed");
|
ESP_GOTO_ON_ERROR(dp83848_update_link_duplex_speed(dp83848), err, TAG, "update link duplex speed failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -145,284 +123,21 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t dp83848_reset(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
dp83848->link_status = ETH_LINK_DOWN;
|
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
bmcr_reg_t bmcr = {.reset = 1};
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for reset complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < dp83848->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!bmcr.reset) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < dp83848->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "reset timeout");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_reset_hw(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
if (dp83848->reset_gpio_num >= 0) {
|
|
||||||
esp_rom_gpio_pad_select_gpio(dp83848->reset_gpio_num);
|
|
||||||
gpio_set_direction(dp83848->reset_gpio_num, GPIO_MODE_OUTPUT);
|
|
||||||
gpio_set_level(dp83848->reset_gpio_num, 0);
|
|
||||||
esp_rom_delay_us(100); // insert min input assert time
|
|
||||||
gpio_set_level(dp83848->reset_gpio_num, 1);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note This function is responsible for restarting a new auto-negotiation,
|
|
||||||
* the result of negotiation won't be relected to uppler layers.
|
|
||||||
* Instead, the negotiation result is fetched by linker timer, see `dp83848_get_link()`
|
|
||||||
*/
|
|
||||||
static esp_err_t dp83848_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
|
||||||
/* in case any link status has changed, let's assume we're in link down status */
|
|
||||||
dp83848->link_status = ETH_LINK_DOWN;
|
|
||||||
|
|
||||||
bmcr.restart_auto_nego = 1; /* Restart Auto Negotiation */
|
|
||||||
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for auto negotiation complete */
|
|
||||||
bmsr_reg_t bmsr;
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < dp83848->autonego_timeout_ms / 100; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
|
||||||
if (bmsr.auto_nego_complete) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((to >= dp83848->autonego_timeout_ms / 100) && (dp83848->link_status == ETH_LINK_UP)) {
|
|
||||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
|
||||||
if (bmcr.en_auto_nego == 1) {
|
|
||||||
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
|
||||||
if (bmcr.en_auto_nego == 0) {
|
|
||||||
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
|
||||||
/* do nothing autonego_en_stat is set at the function end */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
*autonego_en_stat = bmcr.en_auto_nego;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_pwrctl(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
/* Enable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 1;
|
|
||||||
} else {
|
|
||||||
/* Disable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.power_down == 1, ESP_FAIL, err, TAG, "power down failed");
|
|
||||||
} else {
|
|
||||||
/* wait for power up complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < dp83848->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (bmcr.power_down == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < dp83848->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "power up timeout");
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
|
||||||
{
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
dp83848->addr = addr;
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
*addr = dp83848->addr;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_del(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
free(dp83848);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
/* Set PAUSE function ability */
|
|
||||||
anar_reg_t anar;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed");
|
|
||||||
if (ability) {
|
|
||||||
anar.asymmetric_pause = 1;
|
|
||||||
anar.symmetric_pause = 1;
|
|
||||||
} else {
|
|
||||||
anar.asymmetric_pause = 0;
|
|
||||||
anar.symmetric_pause = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_ANAR_REG_ADDR, anar.val), err, TAG, "write ANAR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_loopback(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
/* Set Loopback function */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (enable) {
|
|
||||||
bmcr.en_loopback = 1;
|
|
||||||
} else {
|
|
||||||
bmcr.en_loopback = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
if (dp83848->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
dp83848->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)dp83848->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set speed */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.speed_select = speed;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
|
|
||||||
if (dp83848->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
dp83848->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)dp83848->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set duplex mode */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.duplex_mode = duplex;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_init(esp_eth_phy_t *phy)
|
static esp_err_t dp83848_init(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||||
esp_eth_mediator_t *eth = dp83848->eth;
|
|
||||||
// Detect PHY address
|
/* Basic PHY init */
|
||||||
if (dp83848->addr == ESP_ETH_PHY_ADDR_AUTO) {
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY");
|
||||||
ESP_GOTO_ON_ERROR(esp_eth_detect_phy_addr(eth, &dp83848->addr), err, TAG, "Detect PHY address failed");
|
|
||||||
}
|
/* Check PHY ID */
|
||||||
/* Power on Ethernet PHY */
|
uint32_t oui;
|
||||||
ESP_GOTO_ON_ERROR(dp83848_pwrctl(phy, true), err, TAG, "power control failed");
|
uint8_t model;
|
||||||
/* Reset Ethernet PHY */
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed");
|
||||||
ESP_GOTO_ON_ERROR(dp83848_reset(phy), err, TAG, "reset 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");
|
||||||
/* Check PHY ID */
|
ESP_GOTO_ON_FALSE(oui == 0x80017 && model == 0x09, ESP_FAIL, err, TAG, "wrong chip ID");
|
||||||
phyidr1_reg_t id1;
|
|
||||||
phyidr2_reg_t id2;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed");
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed");
|
|
||||||
ESP_GOTO_ON_FALSE(id1.oui_msb == 0x2000 && id2.oui_lsb == 0x17 && id2.vendor_model == 0x09, ESP_FAIL, err, TAG, "wrong chip ID");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t dp83848_deinit(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
/* Power off Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(dp83848_pwrctl(phy, false), err, TAG, "power control failed");
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -431,30 +146,19 @@ err:
|
|||||||
esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config)
|
esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config)
|
||||||
{
|
{
|
||||||
esp_eth_phy_t *ret = NULL;
|
esp_eth_phy_t *ret = NULL;
|
||||||
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "can't set phy config to null");
|
|
||||||
phy_dp83848_t *dp83848 = calloc(1, sizeof(phy_dp83848_t));
|
phy_dp83848_t *dp83848 = calloc(1, sizeof(phy_dp83848_t));
|
||||||
ESP_GOTO_ON_FALSE(dp83848, NULL, err, TAG, "calloc dp83848 failed");
|
ESP_GOTO_ON_FALSE(dp83848, NULL, err, TAG, "calloc dp83848 failed");
|
||||||
dp83848->addr = config->phy_addr;
|
ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&dp83848->phy_802_3, config) == ESP_OK,
|
||||||
dp83848->reset_timeout_ms = config->reset_timeout_ms;
|
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||||
dp83848->link_status = ETH_LINK_DOWN;
|
|
||||||
dp83848->reset_gpio_num = config->reset_gpio_num;
|
// redefine functions which need to be customized for sake of dp83848
|
||||||
dp83848->autonego_timeout_ms = config->autonego_timeout_ms;
|
dp83848->phy_802_3.parent.init = dp83848_init;
|
||||||
dp83848->parent.reset = dp83848_reset;
|
dp83848->phy_802_3.parent.get_link = dp83848_get_link;
|
||||||
dp83848->parent.reset_hw = dp83848_reset_hw;
|
|
||||||
dp83848->parent.init = dp83848_init;
|
return &dp83848->phy_802_3.parent;
|
||||||
dp83848->parent.deinit = dp83848_deinit;
|
|
||||||
dp83848->parent.set_mediator = dp83848_set_mediator;
|
|
||||||
dp83848->parent.autonego_ctrl = dp83848_autonego_ctrl;
|
|
||||||
dp83848->parent.get_link = dp83848_get_link;
|
|
||||||
dp83848->parent.pwrctl = dp83848_pwrctl;
|
|
||||||
dp83848->parent.get_addr = dp83848_get_addr;
|
|
||||||
dp83848->parent.set_addr = dp83848_set_addr;
|
|
||||||
dp83848->parent.advertise_pause_ability = dp83848_advertise_pause_ability;
|
|
||||||
dp83848->parent.loopback = dp83848_loopback;
|
|
||||||
dp83848->parent.set_speed = dp83848_set_speed;
|
|
||||||
dp83848->parent.set_duplex = dp83848_set_duplex;
|
|
||||||
dp83848->parent.del = dp83848_del;
|
|
||||||
return &(dp83848->parent);
|
|
||||||
err:
|
err:
|
||||||
|
if (dp83848 != NULL) {
|
||||||
|
free(dp83848);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -8,13 +8,7 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_eth_driver.h"
|
#include "esp_eth_phy_802_3.h"
|
||||||
#include "eth_phy_regs_struct.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_rom_gpio.h"
|
|
||||||
#include "esp_rom_sys.h"
|
|
||||||
|
|
||||||
static const char *TAG = "ip101";
|
static const char *TAG = "ip101";
|
||||||
|
|
||||||
@ -87,23 +81,17 @@ typedef union {
|
|||||||
#define ETH_PHY_PSCR_REG_ADDR (0x11)
|
#define ETH_PHY_PSCR_REG_ADDR (0x11)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
esp_eth_phy_t parent;
|
phy_802_3_t phy_802_3;
|
||||||
esp_eth_mediator_t *eth;
|
|
||||||
int addr;
|
|
||||||
uint32_t reset_timeout_ms;
|
|
||||||
uint32_t autonego_timeout_ms;
|
|
||||||
eth_link_t link_status;
|
|
||||||
int reset_gpio_num;
|
|
||||||
} phy_ip101_t;
|
} phy_ip101_t;
|
||||||
|
|
||||||
static esp_err_t ip101_page_select(phy_ip101_t *ip101, uint32_t page)
|
static esp_err_t ip101_page_select(phy_ip101_t *ip101, uint32_t page)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
esp_eth_mediator_t *eth = ip101->phy_802_3.eth;
|
||||||
pcr_reg_t pcr = {
|
pcr_reg_t pcr = {
|
||||||
.register_page_select = page
|
.register_page_select = page
|
||||||
};
|
};
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_PCR_REG_ADDR, pcr.val), err, TAG, "write PCR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->phy_802_3.addr, ETH_PHY_PCR_REG_ADDR, pcr.val), err, TAG, "write PCR failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -112,18 +100,20 @@ err:
|
|||||||
static esp_err_t ip101_update_link_duplex_speed(phy_ip101_t *ip101)
|
static esp_err_t ip101_update_link_duplex_speed(phy_ip101_t *ip101)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
esp_eth_mediator_t *eth = ip101->phy_802_3.eth;
|
||||||
|
uint32_t addr = ip101->phy_802_3.addr;
|
||||||
eth_speed_t speed = ETH_SPEED_10M;
|
eth_speed_t speed = ETH_SPEED_10M;
|
||||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||||
uint32_t peer_pause_ability = false;
|
uint32_t peer_pause_ability = false;
|
||||||
cssr_reg_t cssr;
|
cssr_reg_t cssr;
|
||||||
anlpar_reg_t anlpar;
|
anlpar_reg_t anlpar;
|
||||||
|
|
||||||
ESP_GOTO_ON_ERROR(ip101_page_select(ip101, 16), err, TAG, "select page 16 failed");
|
ESP_GOTO_ON_ERROR(ip101_page_select(ip101, 16), err, TAG, "select page 16 failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_CSSR_REG_ADDR, &(cssr.val)), err, TAG, "read CSSR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_CSSR_REG_ADDR, &(cssr.val)), err, TAG, "read CSSR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
||||||
eth_link_t link = cssr.link_up ? ETH_LINK_UP : ETH_LINK_DOWN;
|
eth_link_t link = cssr.link_up ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
/* check if link status changed */
|
/* check if link status changed */
|
||||||
if (ip101->link_status != link) {
|
if (ip101->phy_802_3.link_status != link) {
|
||||||
/* when link up, read negotiation result */
|
/* when link up, read negotiation result */
|
||||||
if (link == ETH_LINK_UP) {
|
if (link == ETH_LINK_UP) {
|
||||||
switch (cssr.op_mode) {
|
switch (cssr.op_mode) {
|
||||||
@ -157,28 +147,18 @@ static esp_err_t ip101_update_link_duplex_speed(phy_ip101_t *ip101)
|
|||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
||||||
ip101->link_status = link;
|
ip101->phy_802_3.link_status = link;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t ip101_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
ip101->eth = eth;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_get_link(esp_eth_phy_t *phy)
|
static esp_err_t ip101_get_link(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
phy_ip101_t *ip101 = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_ip101_t, phy_802_3);
|
||||||
|
|
||||||
/* Update information about link, speed, duplex */
|
/* Update information about link, speed, duplex */
|
||||||
ESP_GOTO_ON_ERROR(ip101_update_link_duplex_speed(ip101), err, TAG, "update link duplex speed failed");
|
ESP_GOTO_ON_ERROR(ip101_update_link_duplex_speed(ip101), err, TAG, "update link duplex speed failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -186,285 +166,21 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t ip101_reset(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
ip101->link_status = ETH_LINK_DOWN;
|
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
bmcr_reg_t bmcr = {.reset = 1};
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* wait for reset complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < ip101->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!bmcr.reset) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < ip101->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "reset timeout");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_reset_hw(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
if (ip101->reset_gpio_num >= 0) {
|
|
||||||
esp_rom_gpio_pad_select_gpio(ip101->reset_gpio_num);
|
|
||||||
gpio_set_direction(ip101->reset_gpio_num, GPIO_MODE_OUTPUT);
|
|
||||||
gpio_set_level(ip101->reset_gpio_num, 0);
|
|
||||||
esp_rom_delay_us(100); // insert min input assert time
|
|
||||||
gpio_set_level(ip101->reset_gpio_num, 1);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note This function is responsible for restarting a new auto-negotiation,
|
|
||||||
* the result of negotiation won't be relected to uppler layers.
|
|
||||||
* Instead, the negotiation result is fetched by linker timer, see `ip101_get_link()`
|
|
||||||
*/
|
|
||||||
static esp_err_t ip101_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
|
||||||
/* in case any link status has changed, let's assume we're in link down status */
|
|
||||||
ip101->link_status = ETH_LINK_DOWN;
|
|
||||||
|
|
||||||
bmcr.restart_auto_nego = 1; /* Restart Auto Negotiation */
|
|
||||||
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for auto negotiation complete */
|
|
||||||
bmsr_reg_t bmsr;
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < ip101->autonego_timeout_ms / 100; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
|
||||||
if (bmsr.auto_nego_complete) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((to >= ip101->autonego_timeout_ms / 100) && (ip101->link_status == ETH_LINK_UP)) {
|
|
||||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
|
||||||
if (bmcr.en_auto_nego == 1) {
|
|
||||||
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
|
||||||
if (bmcr.en_auto_nego == 0) {
|
|
||||||
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
|
||||||
/* do nothing autonego_en_stat is set at the function end */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
*autonego_en_stat = bmcr.en_auto_nego;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_pwrctl(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
/* Enable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 1;
|
|
||||||
} else {
|
|
||||||
/* Disable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.power_down == 1, ESP_FAIL, err, TAG, "power down failed");
|
|
||||||
} else {
|
|
||||||
/* wait for power up complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < ip101->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (bmcr.power_down == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < ip101->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "power up timeout");
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
|
||||||
{
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
ip101->addr = addr;
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
*addr = ip101->addr;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_del(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
free(ip101);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
/* Set PAUSE function ability */
|
|
||||||
anar_reg_t anar;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed");
|
|
||||||
if (ability) {
|
|
||||||
anar.asymmetric_pause = 1;
|
|
||||||
anar.symmetric_pause = 1;
|
|
||||||
} else {
|
|
||||||
anar.asymmetric_pause = 0;
|
|
||||||
anar.symmetric_pause = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_ANAR_REG_ADDR, anar.val), err, TAG, "write ANAR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_loopback(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
/* Set Loopback function */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (enable) {
|
|
||||||
bmcr.en_loopback = 1;
|
|
||||||
} else {
|
|
||||||
bmcr.en_loopback = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
|
|
||||||
if (ip101->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
ip101->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)ip101->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set speed */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.speed_select = speed;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
|
|
||||||
if (ip101->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
ip101->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)ip101->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set duplex mode */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.duplex_mode = duplex;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_init(esp_eth_phy_t *phy)
|
static esp_err_t ip101_init(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||||
esp_eth_mediator_t *eth = ip101->eth;
|
|
||||||
// Detect PHY address
|
/* Basic PHY init */
|
||||||
if (ip101->addr == ESP_ETH_PHY_ADDR_AUTO) {
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY");
|
||||||
ESP_GOTO_ON_ERROR(esp_eth_detect_phy_addr(eth, &ip101->addr), err, TAG, "Detect PHY address failed");
|
|
||||||
}
|
/* Check PHY ID */
|
||||||
/* Power on Ethernet PHY */
|
uint32_t oui;
|
||||||
ESP_GOTO_ON_ERROR(ip101_pwrctl(phy, true), err, TAG, "power control failed");
|
uint8_t model;
|
||||||
/* Reset Ethernet PHY */
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed");
|
||||||
ESP_GOTO_ON_ERROR(ip101_reset(phy), err, TAG, "reset 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");
|
||||||
/* Check PHY ID */
|
ESP_GOTO_ON_FALSE(oui == 0x90C3 && model == 0x5, ESP_FAIL, err, TAG, "wrong chip ID");
|
||||||
phyidr1_reg_t id1;
|
|
||||||
phyidr2_reg_t id2;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed");
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed");
|
|
||||||
ESP_GOTO_ON_FALSE(id1.oui_msb == 0x243 && id2.oui_lsb == 0x3 && id2.vendor_model == 0x5, ESP_FAIL, err, TAG, "wrong chip ID");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ip101_deinit(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
/* Power off Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(ip101_pwrctl(phy, false), err, TAG, "power control failed");
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -473,31 +189,19 @@ err:
|
|||||||
esp_eth_phy_t *esp_eth_phy_new_ip101(const eth_phy_config_t *config)
|
esp_eth_phy_t *esp_eth_phy_new_ip101(const eth_phy_config_t *config)
|
||||||
{
|
{
|
||||||
esp_eth_phy_t *ret = NULL;
|
esp_eth_phy_t *ret = NULL;
|
||||||
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "can't set phy config to null");
|
|
||||||
phy_ip101_t *ip101 = calloc(1, sizeof(phy_ip101_t));
|
phy_ip101_t *ip101 = calloc(1, sizeof(phy_ip101_t));
|
||||||
ESP_GOTO_ON_FALSE(ip101, NULL, err, TAG, "calloc ip101 failed");
|
ESP_GOTO_ON_FALSE(ip101, NULL, err, TAG, "calloc ip101 failed");
|
||||||
ip101->addr = config->phy_addr;
|
ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&ip101->phy_802_3, config) == ESP_OK,
|
||||||
ip101->reset_timeout_ms = config->reset_timeout_ms;
|
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||||
ip101->reset_gpio_num = config->reset_gpio_num;
|
|
||||||
ip101->link_status = ETH_LINK_DOWN;
|
|
||||||
ip101->autonego_timeout_ms = config->autonego_timeout_ms;
|
|
||||||
ip101->parent.reset = ip101_reset;
|
|
||||||
ip101->parent.reset_hw = ip101_reset_hw;
|
|
||||||
ip101->parent.init = ip101_init;
|
|
||||||
ip101->parent.deinit = ip101_deinit;
|
|
||||||
ip101->parent.set_mediator = ip101_set_mediator;
|
|
||||||
ip101->parent.autonego_ctrl = ip101_autonego_ctrl;
|
|
||||||
ip101->parent.get_link = ip101_get_link;
|
|
||||||
ip101->parent.pwrctl = ip101_pwrctl;
|
|
||||||
ip101->parent.get_addr = ip101_get_addr;
|
|
||||||
ip101->parent.set_addr = ip101_set_addr;
|
|
||||||
ip101->parent.advertise_pause_ability = ip101_advertise_pause_ability;
|
|
||||||
ip101->parent.loopback = ip101_loopback;
|
|
||||||
ip101->parent.set_speed = ip101_set_speed;
|
|
||||||
ip101->parent.set_duplex = ip101_set_duplex;
|
|
||||||
ip101->parent.del = ip101_del;
|
|
||||||
|
|
||||||
return &(ip101->parent);
|
// redefine functions which need to be customized for sake of IP101
|
||||||
|
ip101->phy_802_3.parent.init = ip101_init;
|
||||||
|
ip101->phy_802_3.parent.get_link = ip101_get_link;
|
||||||
|
|
||||||
|
return &ip101->phy_802_3.parent;
|
||||||
err:
|
err:
|
||||||
|
if (ip101 != NULL) {
|
||||||
|
free(ip101);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -8,16 +8,11 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_eth_driver.h"
|
#include "esp_eth_phy_802_3.h"
|
||||||
#include "eth_phy_regs_struct.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_rom_gpio.h"
|
|
||||||
#include "esp_rom_sys.h"
|
|
||||||
|
|
||||||
#define KSZ80XX_PHY_ID_MSB (0x22)
|
#define KSZ80XX_PHY_ID_MSB (0x22)
|
||||||
#define KSZ80XX_PHY_ID_LSB (0x05)
|
#define KSZ80XX_PHY_ID_LSB (0x05)
|
||||||
|
#define KSZ80XX_PHY_OUI (KSZ80XX_PHY_ID_MSB << 6 | KSZ80XX_PHY_ID_LSB)
|
||||||
|
|
||||||
#define KSZ80XX_PC1R_REG_ADDR (0x1E)
|
#define KSZ80XX_PC1R_REG_ADDR (0x1E)
|
||||||
#define KSZ80XX_PC2R_REG_ADDR (0x1F)
|
#define KSZ80XX_PC2R_REG_ADDR (0x1F)
|
||||||
@ -33,13 +28,7 @@ typedef enum
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
esp_eth_phy_t parent;
|
phy_802_3_t phy_802_3;
|
||||||
esp_eth_mediator_t *eth;
|
|
||||||
int addr;
|
|
||||||
uint32_t reset_timeout_ms;
|
|
||||||
uint32_t autonego_timeout_ms;
|
|
||||||
eth_link_t link_status;
|
|
||||||
int reset_gpio_num;
|
|
||||||
uint8_t model_number;
|
uint8_t model_number;
|
||||||
uint32_t op_mode_reg;
|
uint32_t op_mode_reg;
|
||||||
uint32_t op_mode_offset;
|
uint32_t op_mode_offset;
|
||||||
@ -67,21 +56,22 @@ static const char *TAG = "ksz80xx";
|
|||||||
static esp_err_t ksz80xx_update_link_duplex_speed(phy_ksz80xx_t * ksz80xx)
|
static esp_err_t ksz80xx_update_link_duplex_speed(phy_ksz80xx_t * ksz80xx)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
esp_eth_mediator_t *eth = ksz80xx->phy_802_3.eth;
|
||||||
|
uint32_t addr = ksz80xx->phy_802_3.addr;
|
||||||
eth_speed_t speed = ETH_SPEED_10M;
|
eth_speed_t speed = ETH_SPEED_10M;
|
||||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||||
uint32_t peer_pause_ability = false;
|
uint32_t peer_pause_ability = false;
|
||||||
anlpar_reg_t anlpar;
|
anlpar_reg_t anlpar;
|
||||||
bmsr_reg_t bmsr;
|
bmsr_reg_t bmsr;
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->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_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
||||||
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
/* check if link status changed */
|
/* check if link status changed */
|
||||||
if (ksz80xx->link_status != link) {
|
if (ksz80xx->phy_802_3.link_status != link) {
|
||||||
/* when link up, read negotiation result */
|
/* when link up, read negotiation result */
|
||||||
if (link == ETH_LINK_UP) {
|
if (link == ETH_LINK_UP) {
|
||||||
uint32_t reg_value = 0;
|
uint32_t reg_value = 0;
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ksz80xx->op_mode_reg, ®_value), err, TAG, "read %#04x failed", ksz80xx->op_mode_reg);
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ksz80xx->op_mode_reg, ®_value), err, TAG, "read %#04x failed", ksz80xx->op_mode_reg);
|
||||||
uint8_t op_mode = (reg_value >> ksz80xx->op_mode_offset) & 0x07;
|
uint8_t op_mode = (reg_value >> ksz80xx->op_mode_offset) & 0x07;
|
||||||
switch (op_mode) {
|
switch (op_mode) {
|
||||||
case 1: //10Base-T half-duplex
|
case 1: //10Base-T half-duplex
|
||||||
@ -114,28 +104,17 @@ static esp_err_t ksz80xx_update_link_duplex_speed(phy_ksz80xx_t * ksz80xx)
|
|||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
||||||
ksz80xx->link_status = link;
|
ksz80xx->phy_802_3.link_status = link;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t ksz80xx_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
ksz80xx->eth = eth;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_get_link(esp_eth_phy_t *phy)
|
static esp_err_t ksz80xx_get_link(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
phy_ksz80xx_t *ksz80xx = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_ksz80xx_t, phy_802_3);
|
||||||
/* Update information about link, speed, duplex */
|
/* Update information about link, speed, duplex */
|
||||||
ESP_GOTO_ON_ERROR(ksz80xx_update_link_duplex_speed(ksz80xx), err, TAG, "update link duplex speed failed");
|
ESP_GOTO_ON_ERROR(ksz80xx_update_link_duplex_speed(ksz80xx), err, TAG, "update link duplex speed failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -143,255 +122,6 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t ksz80xx_reset(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
ksz80xx->link_status = ETH_LINK_DOWN;
|
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
|
||||||
bmcr_reg_t bmcr = {.reset = 1};
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* wait for reset complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < ksz80xx->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!bmcr.reset) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < ksz80xx->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "reset timeout");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_reset_hw(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
if (ksz80xx->reset_gpio_num >= 0) {
|
|
||||||
esp_rom_gpio_pad_select_gpio(ksz80xx->reset_gpio_num);
|
|
||||||
gpio_set_direction(ksz80xx->reset_gpio_num, GPIO_MODE_OUTPUT);
|
|
||||||
gpio_set_level(ksz80xx->reset_gpio_num, 0);
|
|
||||||
esp_rom_delay_us(100); // insert min input assert time
|
|
||||||
gpio_set_level(ksz80xx->reset_gpio_num, 1);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note This function is responsible for restarting a new auto-negotiation,
|
|
||||||
* the result of negotiation won't be relected to uppler layers.
|
|
||||||
* Instead, the negotiation result is fetched by linker timer, see `ksz80xx_get_link()`
|
|
||||||
*/
|
|
||||||
static esp_err_t ksz80xx_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
|
||||||
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
|
||||||
/* in case any link status has changed, let's assume we're in link down status */
|
|
||||||
ksz80xx->link_status = ETH_LINK_DOWN;
|
|
||||||
|
|
||||||
bmcr.restart_auto_nego = 1; /* Restart Auto Negotiation */
|
|
||||||
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for auto negotiation complete */
|
|
||||||
bmsr_reg_t bmsr;
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < ksz80xx->autonego_timeout_ms / 100; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
|
||||||
if (bmsr.auto_nego_complete) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((to >= ksz80xx->autonego_timeout_ms / 100) && (ksz80xx->link_status == ETH_LINK_UP)) {
|
|
||||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
|
||||||
if (bmcr.en_auto_nego == 1) {
|
|
||||||
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
|
||||||
if (bmcr.en_auto_nego == 0) {
|
|
||||||
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
|
||||||
/* do nothing autonego_en_stat is set at the function end */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
*autonego_en_stat = bmcr.en_auto_nego;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_pwrctl(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
/* General Power Down Mode */
|
|
||||||
bmcr.power_down = 1;
|
|
||||||
} else {
|
|
||||||
/* Normal operation Mode */
|
|
||||||
bmcr.power_down = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.power_down == 1, ESP_FAIL, err, TAG, "power down failed");
|
|
||||||
} else {
|
|
||||||
/* wait for power up complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < ksz80xx->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (bmcr.power_down == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < ksz80xx->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "power up timeout");
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
|
||||||
{
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
ksz80xx->addr = addr;
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
*addr = ksz80xx->addr;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_del(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
free(ksz80xx);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
|
||||||
/* Set PAUSE function ability */
|
|
||||||
anar_reg_t anar;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed");
|
|
||||||
if (ability) {
|
|
||||||
anar.asymmetric_pause = 1;
|
|
||||||
anar.symmetric_pause = 1;
|
|
||||||
} else {
|
|
||||||
anar.asymmetric_pause = 0;
|
|
||||||
anar.symmetric_pause = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_ANAR_REG_ADDR, anar.val), err, TAG, "write ANAR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_loopback(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
|
||||||
/* Set Loopback function */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (enable) {
|
|
||||||
bmcr.en_loopback = 1;
|
|
||||||
} else {
|
|
||||||
bmcr.en_loopback = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
|
||||||
if (ksz80xx->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
ksz80xx->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)ksz80xx->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set speed */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.speed_select = speed;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t ksz80xx_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
|
||||||
|
|
||||||
if (ksz80xx->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
ksz80xx->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)ksz80xx->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set duplex mode */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.duplex_mode = duplex;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ksz80xx_init_model(phy_ksz80xx_t *ksz80xx)
|
static bool ksz80xx_init_model(phy_ksz80xx_t *ksz80xx)
|
||||||
{
|
{
|
||||||
// set variables for op_mode access
|
// set variables for op_mode access
|
||||||
@ -418,20 +148,18 @@ static bool ksz80xx_init_model(phy_ksz80xx_t *ksz80xx)
|
|||||||
static esp_err_t ksz80xx_init(esp_eth_phy_t *phy)
|
static esp_err_t ksz80xx_init(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_ksz80xx_t *ksz80xx = __containerof(phy, phy_ksz80xx_t, parent);
|
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||||
esp_eth_mediator_t *eth = ksz80xx->eth;
|
phy_ksz80xx_t *ksz80xx = __containerof(phy_802_3, phy_ksz80xx_t, phy_802_3);
|
||||||
/* Power on Ethernet PHY */
|
/* Basic PHY init */
|
||||||
ESP_GOTO_ON_ERROR(ksz80xx_pwrctl(phy, true), err, TAG, "power control failed");
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY");
|
||||||
/* Reset Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(ksz80xx_reset(phy), err, TAG, "reset failed");
|
|
||||||
/* Check PHY ID */
|
/* Check PHY ID */
|
||||||
phyidr1_reg_t id1;
|
uint32_t oui;
|
||||||
phyidr2_reg_t id2;
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed");
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_manufac_info(phy_802_3, &ksz80xx->model_number, NULL), err, TAG, "read manufacturer's info failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed");
|
ESP_GOTO_ON_FALSE(oui == KSZ80XX_PHY_OUI, ESP_FAIL, err, TAG, "wrong chip ID");
|
||||||
ESP_GOTO_ON_FALSE(id1.oui_msb == KSZ80XX_PHY_ID_MSB && id2.oui_lsb == KSZ80XX_PHY_ID_LSB, ESP_FAIL, err, TAG, "wrong chip ID");
|
|
||||||
const char* supported_model_name = NULL;
|
const char* supported_model_name = NULL;
|
||||||
ksz80xx->model_number = id2.vendor_model;
|
|
||||||
for (size_t i = 0; i < sizeof(supported_model_numbers); i++) {
|
for (size_t i = 0; i < sizeof(supported_model_numbers); i++) {
|
||||||
if (ksz80xx->model_number == supported_model_numbers[i]) {
|
if (ksz80xx->model_number == supported_model_numbers[i]) {
|
||||||
supported_model_name = model_names[i];
|
supported_model_name = model_names[i];
|
||||||
@ -445,43 +173,22 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t ksz80xx_deinit(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
/* Power off Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(ksz80xx_pwrctl(phy, false), err, TAG, "power control failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_eth_phy_t *esp_eth_phy_new_ksz80xx(const eth_phy_config_t *config)
|
esp_eth_phy_t *esp_eth_phy_new_ksz80xx(const eth_phy_config_t *config)
|
||||||
{
|
{
|
||||||
esp_eth_phy_t *ret = NULL;
|
esp_eth_phy_t *ret = NULL;
|
||||||
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "can't set phy config to null");
|
|
||||||
phy_ksz80xx_t *ksz80xx = calloc(1, sizeof(phy_ksz80xx_t));
|
phy_ksz80xx_t *ksz80xx = calloc(1, sizeof(phy_ksz80xx_t));
|
||||||
ESP_GOTO_ON_FALSE(ksz80xx, NULL, err, TAG, "calloc ksz80xx failed");
|
ESP_GOTO_ON_FALSE(ksz80xx, NULL, err, TAG, "calloc ksz80xx failed");
|
||||||
ksz80xx->addr = config->phy_addr;
|
ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&ksz80xx->phy_802_3, config) == ESP_OK,
|
||||||
ksz80xx->reset_gpio_num = config->reset_gpio_num;
|
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||||
ksz80xx->reset_timeout_ms = config->reset_timeout_ms;
|
|
||||||
ksz80xx->link_status = ETH_LINK_DOWN;
|
// redefine functions which need to be customized for sake of ksz80xx
|
||||||
ksz80xx->autonego_timeout_ms = config->autonego_timeout_ms;
|
ksz80xx->phy_802_3.parent.init = ksz80xx_init;
|
||||||
ksz80xx->parent.reset = ksz80xx_reset;
|
ksz80xx->phy_802_3.parent.get_link = ksz80xx_get_link;
|
||||||
ksz80xx->parent.reset_hw = ksz80xx_reset_hw;
|
|
||||||
ksz80xx->parent.init = ksz80xx_init;
|
return &ksz80xx->phy_802_3.parent;
|
||||||
ksz80xx->parent.deinit = ksz80xx_deinit;
|
|
||||||
ksz80xx->parent.set_mediator = ksz80xx_set_mediator;
|
|
||||||
ksz80xx->parent.autonego_ctrl = ksz80xx_autonego_ctrl;
|
|
||||||
ksz80xx->parent.get_link = ksz80xx_get_link;
|
|
||||||
ksz80xx->parent.pwrctl = ksz80xx_pwrctl;
|
|
||||||
ksz80xx->parent.get_addr = ksz80xx_get_addr;
|
|
||||||
ksz80xx->parent.set_addr = ksz80xx_set_addr;
|
|
||||||
ksz80xx->parent.advertise_pause_ability = ksz80xx_advertise_pause_ability;
|
|
||||||
ksz80xx->parent.loopback = ksz80xx_loopback;
|
|
||||||
ksz80xx->parent.set_speed = ksz80xx_set_speed;
|
|
||||||
ksz80xx->parent.set_duplex = ksz80xx_set_duplex;
|
|
||||||
ksz80xx->parent.del = ksz80xx_del;
|
|
||||||
return &(ksz80xx->parent);
|
|
||||||
err:
|
err:
|
||||||
|
if (ksz80xx != NULL) {
|
||||||
|
free(ksz80xx);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -8,13 +8,7 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_eth_driver.h"
|
#include "esp_eth_phy_802_3.h"
|
||||||
#include "eth_phy_regs_struct.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_rom_gpio.h"
|
|
||||||
#include "esp_rom_sys.h"
|
|
||||||
|
|
||||||
static const char *TAG = "lan87xx";
|
static const char *TAG = "lan87xx";
|
||||||
|
|
||||||
@ -212,33 +206,28 @@ typedef union {
|
|||||||
#define ETH_PHY_PSCSR_REG_ADDR (0x1F)
|
#define ETH_PHY_PSCSR_REG_ADDR (0x1F)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
esp_eth_phy_t parent;
|
phy_802_3_t phy_802_3;
|
||||||
esp_eth_mediator_t *eth;
|
|
||||||
int addr;
|
|
||||||
uint32_t reset_timeout_ms;
|
|
||||||
uint32_t autonego_timeout_ms;
|
|
||||||
eth_link_t link_status;
|
|
||||||
int reset_gpio_num;
|
|
||||||
} phy_lan87xx_t;
|
} phy_lan87xx_t;
|
||||||
|
|
||||||
static esp_err_t lan87xx_update_link_duplex_speed(phy_lan87xx_t *lan87xx)
|
static esp_err_t lan87xx_update_link_duplex_speed(phy_lan87xx_t *lan87xx)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
esp_eth_mediator_t *eth = lan87xx->phy_802_3.eth;
|
||||||
|
uint32_t addr = lan87xx->phy_802_3.addr;
|
||||||
eth_speed_t speed = ETH_SPEED_10M;
|
eth_speed_t speed = ETH_SPEED_10M;
|
||||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||||
bmsr_reg_t bmsr;
|
bmsr_reg_t bmsr;
|
||||||
pscsr_reg_t pscsr;
|
pscsr_reg_t pscsr;
|
||||||
uint32_t peer_pause_ability = false;
|
uint32_t peer_pause_ability = false;
|
||||||
anlpar_reg_t anlpar;
|
anlpar_reg_t anlpar;
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->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_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
||||||
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
/* check if link status changed */
|
/* check if link status changed */
|
||||||
if (lan87xx->link_status != link) {
|
if (lan87xx->phy_802_3.link_status != link) {
|
||||||
/* when link up, read negotiation result */
|
/* when link up, read negotiation result */
|
||||||
if (link == ETH_LINK_UP) {
|
if (link == ETH_LINK_UP) {
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)), err, TAG, "read PSCSR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)), err, TAG, "read PSCSR failed");
|
||||||
switch (pscsr.speed_indication) {
|
switch (pscsr.speed_indication) {
|
||||||
case 1: //10Base-T half-duplex
|
case 1: //10Base-T half-duplex
|
||||||
speed = ETH_SPEED_10M;
|
speed = ETH_SPEED_10M;
|
||||||
@ -270,28 +259,17 @@ static esp_err_t lan87xx_update_link_duplex_speed(phy_lan87xx_t *lan87xx)
|
|||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
||||||
lan87xx->link_status = link;
|
lan87xx->phy_802_3.link_status = link;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lan87xx_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
lan87xx->eth = eth;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_get_link(esp_eth_phy_t *phy)
|
static esp_err_t lan87xx_get_link(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
phy_lan87xx_t *lan87xx = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_lan87xx_t, phy_802_3);
|
||||||
/* Updata information about link, speed, duplex */
|
/* Updata information about link, speed, duplex */
|
||||||
ESP_GOTO_ON_ERROR(lan87xx_update_link_duplex_speed(lan87xx), err, TAG, "update link duplex speed failed");
|
ESP_GOTO_ON_ERROR(lan87xx_update_link_duplex_speed(lan87xx), err, TAG, "update link duplex speed failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -299,278 +277,30 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lan87xx_reset(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
lan87xx->link_status = ETH_LINK_DOWN;
|
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
bmcr_reg_t bmcr = {.reset = 1};
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* wait for reset complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < lan87xx->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!bmcr.reset) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < lan87xx->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "reset timeout");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_reset_hw(esp_eth_phy_t *phy)
|
static esp_err_t lan87xx_reset_hw(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
/* It was observed that assert nRST signal on LAN87xx needs to be a little longer than the minimum specified in datasheet */
|
||||||
if (lan87xx->reset_gpio_num >= 0) {
|
return esp_eth_phy_802_3_reset_hw(esp_eth_phy_into_phy_802_3(phy), 150);
|
||||||
esp_rom_gpio_pad_select_gpio(lan87xx->reset_gpio_num);
|
|
||||||
gpio_set_direction(lan87xx->reset_gpio_num, GPIO_MODE_OUTPUT);
|
|
||||||
gpio_set_level(lan87xx->reset_gpio_num, 0);
|
|
||||||
/* assert nRST signal on LAN87xx a little longer than the minimum specified in datasheet */
|
|
||||||
esp_rom_delay_us(150);
|
|
||||||
gpio_set_level(lan87xx->reset_gpio_num, 1);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note This function is responsible for restarting a new auto-negotiation,
|
|
||||||
* the result of negotiation won't be relected to uppler layers.
|
|
||||||
* Instead, the negotiation result is fetched by linker timer, see `lan87xx_get_link()`
|
|
||||||
*/
|
|
||||||
static esp_err_t lan87xx_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
|
||||||
/* in case any link status has changed, let's assume we're in link down status */
|
|
||||||
lan87xx->link_status = ETH_LINK_DOWN;
|
|
||||||
|
|
||||||
bmcr.restart_auto_nego = 1; /* Restart Auto Negotiation */
|
|
||||||
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for auto negotiation complete */
|
|
||||||
bmsr_reg_t bmsr;
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < lan87xx->autonego_timeout_ms / 100; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
|
||||||
if (bmsr.auto_nego_complete) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((to >= lan87xx->autonego_timeout_ms / 100) && (lan87xx->link_status == ETH_LINK_UP)) {
|
|
||||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
|
||||||
if (bmcr.en_auto_nego == 1) {
|
|
||||||
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
|
||||||
if (bmcr.en_auto_nego == 0) {
|
|
||||||
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
|
||||||
/* do nothing autonego_en_stat is set at the function end */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
*autonego_en_stat = bmcr.en_auto_nego;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_pwrctl(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
/* General Power Down Mode */
|
|
||||||
bmcr.power_down = 1;
|
|
||||||
} else {
|
|
||||||
/* Normal operation Mode */
|
|
||||||
bmcr.power_down = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.power_down == 1, ESP_FAIL, err, TAG, "power down failed");
|
|
||||||
} else {
|
|
||||||
/* wait for power up complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < lan87xx->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (bmcr.power_down == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < lan87xx->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "power up timeout");
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
|
||||||
{
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
lan87xx->addr = addr;
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
*addr = lan87xx->addr;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_del(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
free(lan87xx);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
/* Set PAUSE function ability */
|
|
||||||
anar_reg_t anar;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed");
|
|
||||||
if (ability) {
|
|
||||||
anar.asymmetric_pause = 1;
|
|
||||||
anar.symmetric_pause = 1;
|
|
||||||
} else {
|
|
||||||
anar.asymmetric_pause = 0;
|
|
||||||
anar.symmetric_pause = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_ANAR_REG_ADDR, anar.val), err, TAG, "write ANAR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_loopback(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
/* Set Loopback function */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (enable) {
|
|
||||||
bmcr.en_loopback = 1;
|
|
||||||
} else {
|
|
||||||
bmcr.en_loopback = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
if (lan87xx->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
lan87xx->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)lan87xx->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set speed */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.speed_select = speed;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lan87xx_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
|
|
||||||
if (lan87xx->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
lan87xx->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)lan87xx->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set duplex mode */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.duplex_mode = duplex;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lan87xx_init(esp_eth_phy_t *phy)
|
static esp_err_t lan87xx_init(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_lan87xx_t *lan87xx = __containerof(phy, phy_lan87xx_t, parent);
|
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||||
esp_eth_mediator_t *eth = lan87xx->eth;
|
|
||||||
// Detect PHY address
|
/* Basic PHY init */
|
||||||
if (lan87xx->addr == ESP_ETH_PHY_ADDR_AUTO) {
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY");
|
||||||
ESP_GOTO_ON_ERROR(esp_eth_detect_phy_addr(eth, &lan87xx->addr), err, TAG, "Detect PHY address failed");
|
|
||||||
}
|
|
||||||
/* Power on Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(lan87xx_pwrctl(phy, true), err, TAG, "power control failed");
|
|
||||||
/* Reset Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(lan87xx_reset(phy), err, TAG, "reset failed");
|
|
||||||
/* Check PHY ID */
|
/* Check PHY ID */
|
||||||
phyidr1_reg_t id1;
|
uint32_t oui;
|
||||||
phyidr2_reg_t id2;
|
uint8_t model;
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed");
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 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(id1.oui_msb == 0x7 && id2.oui_lsb == 0x30, ESP_FAIL, err, TAG, "wrong chip ID");
|
ESP_GOTO_ON_FALSE(oui == 0x1F0, ESP_FAIL, err, TAG, "wrong chip OUI");
|
||||||
|
|
||||||
bool supported_model = false;
|
bool supported_model = false;
|
||||||
for (unsigned int i = 0; i < sizeof(supported_models); i++) {
|
for (unsigned int i = 0; i < sizeof(supported_models); i++) {
|
||||||
if (id2.vendor_model == supported_models[i]) {
|
if (model == supported_models[i]) {
|
||||||
supported_model = true;
|
supported_model = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -581,44 +311,23 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lan87xx_deinit(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
/* Power off Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(lan87xx_pwrctl(phy, false), err, TAG, "power control failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_eth_phy_t *esp_eth_phy_new_lan87xx(const eth_phy_config_t *config)
|
esp_eth_phy_t *esp_eth_phy_new_lan87xx(const eth_phy_config_t *config)
|
||||||
{
|
{
|
||||||
esp_eth_phy_t *ret = NULL;
|
esp_eth_phy_t *ret = NULL;
|
||||||
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "can't set phy config to null");
|
|
||||||
phy_lan87xx_t *lan87xx = calloc(1, sizeof(phy_lan87xx_t));
|
phy_lan87xx_t *lan87xx = calloc(1, sizeof(phy_lan87xx_t));
|
||||||
ESP_GOTO_ON_FALSE(lan87xx, NULL, err, TAG, "calloc lan87xx failed");
|
ESP_GOTO_ON_FALSE(lan87xx, NULL, err, TAG, "calloc lan87xx failed");
|
||||||
lan87xx->addr = config->phy_addr;
|
ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&lan87xx->phy_802_3, config) == ESP_OK,
|
||||||
lan87xx->reset_gpio_num = config->reset_gpio_num;
|
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||||
lan87xx->reset_timeout_ms = config->reset_timeout_ms;
|
|
||||||
lan87xx->link_status = ETH_LINK_DOWN;
|
|
||||||
lan87xx->autonego_timeout_ms = config->autonego_timeout_ms;
|
|
||||||
lan87xx->parent.reset = lan87xx_reset;
|
|
||||||
lan87xx->parent.reset_hw = lan87xx_reset_hw;
|
|
||||||
lan87xx->parent.init = lan87xx_init;
|
|
||||||
lan87xx->parent.deinit = lan87xx_deinit;
|
|
||||||
lan87xx->parent.set_mediator = lan87xx_set_mediator;
|
|
||||||
lan87xx->parent.autonego_ctrl = lan87xx_autonego_ctrl;
|
|
||||||
lan87xx->parent.get_link = lan87xx_get_link;
|
|
||||||
lan87xx->parent.pwrctl = lan87xx_pwrctl;
|
|
||||||
lan87xx->parent.get_addr = lan87xx_get_addr;
|
|
||||||
lan87xx->parent.set_addr = lan87xx_set_addr;
|
|
||||||
lan87xx->parent.loopback = lan87xx_loopback;
|
|
||||||
lan87xx->parent.set_speed = lan87xx_set_speed;
|
|
||||||
lan87xx->parent.set_duplex = lan87xx_set_duplex;
|
|
||||||
lan87xx->parent.advertise_pause_ability = lan87xx_advertise_pause_ability;
|
|
||||||
lan87xx->parent.del = lan87xx_del;
|
|
||||||
|
|
||||||
return &(lan87xx->parent);
|
// redefine functions which need to be customized for sake of LAN87xx
|
||||||
|
lan87xx->phy_802_3.parent.reset_hw = lan87xx_reset_hw;
|
||||||
|
lan87xx->phy_802_3.parent.init = lan87xx_init;
|
||||||
|
lan87xx->phy_802_3.parent.get_link = lan87xx_get_link;
|
||||||
|
|
||||||
|
return &lan87xx->phy_802_3.parent;
|
||||||
err:
|
err:
|
||||||
|
if (lan87xx != NULL) {
|
||||||
|
free(lan87xx);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -9,13 +9,7 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_eth_driver.h"
|
#include "esp_eth_phy_802_3.h"
|
||||||
#include "eth_phy_regs_struct.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_rom_gpio.h"
|
|
||||||
#include "esp_rom_sys.h"
|
|
||||||
|
|
||||||
static const char *TAG = "rtl8201";
|
static const char *TAG = "rtl8201";
|
||||||
|
|
||||||
@ -48,23 +42,17 @@ typedef union {
|
|||||||
#define ETH_PHY_PSR_REG_ADDR (0x1F)
|
#define ETH_PHY_PSR_REG_ADDR (0x1F)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
esp_eth_phy_t parent;
|
phy_802_3_t phy_802_3;
|
||||||
esp_eth_mediator_t *eth;
|
|
||||||
int addr;
|
|
||||||
uint32_t reset_timeout_ms;
|
|
||||||
uint32_t autonego_timeout_ms;
|
|
||||||
eth_link_t link_status;
|
|
||||||
int reset_gpio_num;
|
|
||||||
} phy_rtl8201_t;
|
} phy_rtl8201_t;
|
||||||
|
|
||||||
static esp_err_t rtl8201_page_select(phy_rtl8201_t *rtl8201, uint32_t page)
|
static esp_err_t rtl8201_page_select(phy_rtl8201_t *rtl8201, uint32_t page)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
esp_eth_mediator_t *eth = rtl8201->phy_802_3.eth;
|
||||||
psr_reg_t psr = {
|
psr_reg_t psr = {
|
||||||
.page_select = page
|
.page_select = page
|
||||||
};
|
};
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_PSR_REG_ADDR, psr.val), err, TAG, "write PSR failed");
|
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");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -73,7 +61,8 @@ err:
|
|||||||
static esp_err_t rtl8201_update_link_duplex_speed(phy_rtl8201_t *rtl8201)
|
static esp_err_t rtl8201_update_link_duplex_speed(phy_rtl8201_t *rtl8201)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
esp_eth_mediator_t *eth = rtl8201->phy_802_3.eth;
|
||||||
|
uint32_t addr = rtl8201->phy_802_3.addr;
|
||||||
eth_speed_t speed = ETH_SPEED_10M;
|
eth_speed_t speed = ETH_SPEED_10M;
|
||||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||||
bmcr_reg_t bmcr;
|
bmcr_reg_t bmcr;
|
||||||
@ -81,14 +70,14 @@ static esp_err_t rtl8201_update_link_duplex_speed(phy_rtl8201_t *rtl8201)
|
|||||||
uint32_t peer_pause_ability = false;
|
uint32_t peer_pause_ability = false;
|
||||||
anlpar_reg_t anlpar;
|
anlpar_reg_t anlpar;
|
||||||
ESP_GOTO_ON_ERROR(rtl8201_page_select(rtl8201, 0), err, TAG, "select page 0 failed");
|
ESP_GOTO_ON_ERROR(rtl8201_page_select(rtl8201, 0), err, TAG, "select page 0 failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->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_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed");
|
||||||
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
/* check if link status changed */
|
/* check if link status changed */
|
||||||
if (rtl8201->link_status != link) {
|
if (rtl8201->phy_802_3.link_status != link) {
|
||||||
/* when link up, read negotiation result */
|
/* when link up, read negotiation result */
|
||||||
if (link == ETH_LINK_UP) {
|
if (link == ETH_LINK_UP) {
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
||||||
if (bmcr.speed_select) {
|
if (bmcr.speed_select) {
|
||||||
speed = ETH_SPEED_100M;
|
speed = ETH_SPEED_100M;
|
||||||
} else {
|
} else {
|
||||||
@ -110,28 +99,17 @@ static esp_err_t rtl8201_update_link_duplex_speed(phy_rtl8201_t *rtl8201)
|
|||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed");
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
|
||||||
rtl8201->link_status = link;
|
rtl8201->phy_802_3.link_status = link;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t rtl8201_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
rtl8201->eth = eth;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_get_link(esp_eth_phy_t *phy)
|
static esp_err_t rtl8201_get_link(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
phy_rtl8201_t *rtl8201 = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_rtl8201_t, phy_802_3);
|
||||||
/* Updata information about link, speed, duplex */
|
/* Updata information about link, speed, duplex */
|
||||||
ESP_GOTO_ON_ERROR(rtl8201_update_link_duplex_speed(rtl8201), err, TAG, "update link duplex speed failed");
|
ESP_GOTO_ON_ERROR(rtl8201_update_link_duplex_speed(rtl8201), err, TAG, "update link duplex speed failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -139,284 +117,21 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t rtl8201_reset(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
rtl8201->link_status = ETH_LINK_DOWN;
|
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
bmcr_reg_t bmcr = {.reset = 1};
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for reset complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < rtl8201->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!bmcr.reset) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < rtl8201->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "reset timeout");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_reset_hw(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
if (rtl8201->reset_gpio_num >= 0) {
|
|
||||||
esp_rom_gpio_pad_select_gpio(rtl8201->reset_gpio_num);
|
|
||||||
gpio_set_direction(rtl8201->reset_gpio_num, GPIO_MODE_OUTPUT);
|
|
||||||
gpio_set_level(rtl8201->reset_gpio_num, 0);
|
|
||||||
esp_rom_delay_us(100); // insert min input assert time
|
|
||||||
gpio_set_level(rtl8201->reset_gpio_num, 1);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note This function is responsible for restarting a new auto-negotiation,
|
|
||||||
* the result of negotiation won't be relected to uppler layers.
|
|
||||||
* Instead, the negotiation result is fetched by linker timer, see `rtl8201_get_link()`
|
|
||||||
*/
|
|
||||||
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_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
|
||||||
/* in case any link status has changed, let's assume we're in link down status */
|
|
||||||
rtl8201->link_status = ETH_LINK_DOWN;
|
|
||||||
|
|
||||||
bmcr.restart_auto_nego = 1; /* Restart Auto Negotiation */
|
|
||||||
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* Wait for auto negotiation complete */
|
|
||||||
bmsr_reg_t bmsr;
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < rtl8201->autonego_timeout_ms / 100; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(100));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed");
|
|
||||||
if (bmsr.auto_nego_complete) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((to >= rtl8201->autonego_timeout_ms / 100) && (rtl8201->link_status == ETH_LINK_UP)) {
|
|
||||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
|
||||||
if (bmcr.en_auto_nego == 1) {
|
|
||||||
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
|
||||||
if (bmcr.en_auto_nego == 0) {
|
|
||||||
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
/* read configuration back */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
|
||||||
/* do nothing autonego_en_stat is set at the function end */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
*autonego_en_stat = bmcr.en_auto_nego;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_pwrctl(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
/* Enable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 1;
|
|
||||||
} else {
|
|
||||||
/* Disable IEEE Power Down Mode */
|
|
||||||
bmcr.power_down = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
if (!enable) {
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
ESP_GOTO_ON_FALSE(bmcr.power_down == 1, ESP_FAIL, err, TAG, "power down failed");
|
|
||||||
} else {
|
|
||||||
/* wait for power up complete */
|
|
||||||
uint32_t to = 0;
|
|
||||||
for (to = 0; to < rtl8201->reset_timeout_ms / 10; to++) {
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (bmcr.power_down == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_FALSE(to < rtl8201->reset_timeout_ms / 10, ESP_FAIL, err, TAG, "power up timeout");
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
|
||||||
{
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
rtl8201->addr = addr;
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can't be null");
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
*addr = rtl8201->addr;
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_del(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
free(rtl8201);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
/* Set PAUSE function ability */
|
|
||||||
anar_reg_t anar;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)), err, TAG, "read ANAR failed");
|
|
||||||
if (ability) {
|
|
||||||
anar.asymmetric_pause = 1;
|
|
||||||
anar.symmetric_pause = 1;
|
|
||||||
} else {
|
|
||||||
anar.asymmetric_pause = 0;
|
|
||||||
anar.symmetric_pause = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_ANAR_REG_ADDR, anar.val), err, TAG, "write ANAR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_loopback(esp_eth_phy_t *phy, bool enable)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
/* Set Loopback function */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
if (enable) {
|
|
||||||
bmcr.en_loopback = 1;
|
|
||||||
} else {
|
|
||||||
bmcr.en_loopback = 0;
|
|
||||||
}
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
if (rtl8201->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
rtl8201->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)rtl8201->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set speed */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.speed_select = speed;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
|
|
||||||
if (rtl8201->link_status == ETH_LINK_UP) {
|
|
||||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
|
||||||
rtl8201->link_status = ETH_LINK_DOWN;
|
|
||||||
/* Indicate to upper stream apps the link is cosidered down */
|
|
||||||
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)rtl8201->link_status), err, TAG, "change link failed");
|
|
||||||
}
|
|
||||||
/* Set duplex mode */
|
|
||||||
bmcr_reg_t bmcr;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
|
|
||||||
bmcr.duplex_mode = duplex;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_init(esp_eth_phy_t *phy)
|
static esp_err_t rtl8201_init(esp_eth_phy_t *phy)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent);
|
phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy);
|
||||||
esp_eth_mediator_t *eth = rtl8201->eth;
|
|
||||||
// Detect PHY address
|
/* Basic PHY init */
|
||||||
if (rtl8201->addr == ESP_ETH_PHY_ADDR_AUTO) {
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY");
|
||||||
ESP_GOTO_ON_ERROR(esp_eth_detect_phy_addr(eth, &rtl8201->addr), err, TAG, "Detect PHY address failed");
|
|
||||||
}
|
/* Check PHY ID */
|
||||||
/* Power on Ethernet PHY */
|
uint32_t oui;
|
||||||
ESP_GOTO_ON_ERROR(rtl8201_pwrctl(phy, true), err, TAG, "power control failed");
|
uint8_t model;
|
||||||
/* Reset Ethernet PHY */
|
ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed");
|
||||||
ESP_GOTO_ON_ERROR(rtl8201_reset(phy), err, TAG, "reset 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");
|
||||||
/* Check PHY ID */
|
ESP_GOTO_ON_FALSE(oui == 0x732 && model == 0x1, ESP_FAIL, err, TAG, "wrong chip ID");
|
||||||
phyidr1_reg_t id1;
|
|
||||||
phyidr2_reg_t id2;
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)), err, TAG, "read ID1 failed");
|
|
||||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)), err, TAG, "read ID2 failed");
|
|
||||||
ESP_GOTO_ON_FALSE(id1.oui_msb == 0x1C && id2.oui_lsb == 0x32 && id2.vendor_model == 0x1, ESP_FAIL, err, TAG, "wrong chip ID");
|
|
||||||
return ESP_OK;
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t rtl8201_deinit(esp_eth_phy_t *phy)
|
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
/* Power off Ethernet PHY */
|
|
||||||
ESP_GOTO_ON_ERROR(rtl8201_pwrctl(phy, false), err, TAG, "power control failed");
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -425,31 +140,19 @@ err:
|
|||||||
esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config)
|
esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config)
|
||||||
{
|
{
|
||||||
esp_eth_phy_t *ret = NULL;
|
esp_eth_phy_t *ret = NULL;
|
||||||
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "can't set phy config to null");
|
|
||||||
phy_rtl8201_t *rtl8201 = calloc(1, sizeof(phy_rtl8201_t));
|
phy_rtl8201_t *rtl8201 = calloc(1, sizeof(phy_rtl8201_t));
|
||||||
ESP_GOTO_ON_FALSE(rtl8201, NULL, err, TAG, "calloc rtl8201 failed");
|
ESP_GOTO_ON_FALSE(rtl8201, NULL, err, TAG, "calloc rtl8201 failed");
|
||||||
rtl8201->addr = config->phy_addr;
|
ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&rtl8201->phy_802_3, config) == ESP_OK,
|
||||||
rtl8201->reset_gpio_num = config->reset_gpio_num;
|
NULL, err, TAG, "configuration initialization of PHY 802.3 failed");
|
||||||
rtl8201->reset_timeout_ms = config->reset_timeout_ms;
|
|
||||||
rtl8201->link_status = ETH_LINK_DOWN;
|
|
||||||
rtl8201->autonego_timeout_ms = config->autonego_timeout_ms;
|
|
||||||
rtl8201->parent.reset = rtl8201_reset;
|
|
||||||
rtl8201->parent.reset_hw = rtl8201_reset_hw;
|
|
||||||
rtl8201->parent.init = rtl8201_init;
|
|
||||||
rtl8201->parent.deinit = rtl8201_deinit;
|
|
||||||
rtl8201->parent.set_mediator = rtl8201_set_mediator;
|
|
||||||
rtl8201->parent.autonego_ctrl = rtl8201_autonego_ctrl;
|
|
||||||
rtl8201->parent.get_link = rtl8201_get_link;
|
|
||||||
rtl8201->parent.pwrctl = rtl8201_pwrctl;
|
|
||||||
rtl8201->parent.get_addr = rtl8201_get_addr;
|
|
||||||
rtl8201->parent.set_addr = rtl8201_set_addr;
|
|
||||||
rtl8201->parent.advertise_pause_ability = rtl8201_advertise_pause_ability;
|
|
||||||
rtl8201->parent.loopback = rtl8201_loopback;
|
|
||||||
rtl8201->parent.set_speed = rtl8201_set_speed;
|
|
||||||
rtl8201->parent.set_duplex = rtl8201_set_duplex;
|
|
||||||
rtl8201->parent.del = rtl8201_del;
|
|
||||||
|
|
||||||
return &(rtl8201->parent);
|
// 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;
|
||||||
|
|
||||||
|
return &rtl8201->phy_802_3.parent;
|
||||||
err:
|
err:
|
||||||
|
if (rtl8201 != NULL) {
|
||||||
|
free(rtl8201);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ err:
|
|||||||
static esp_err_t w5500_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
static esp_err_t w5500_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "can't set mediator to null");
|
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "mediator can't be null");
|
||||||
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
|
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
|
||||||
w5500->eth = eth;
|
w5500->eth = eth;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
@ -88,10 +88,12 @@ INPUT = \
|
|||||||
$(PROJECT_PATH)/components/esp_common/include/esp_err.h \
|
$(PROJECT_PATH)/components/esp_common/include/esp_err.h \
|
||||||
$(PROJECT_PATH)/components/esp_common/include/esp_idf_version.h \
|
$(PROJECT_PATH)/components/esp_common/include/esp_idf_version.h \
|
||||||
$(PROJECT_PATH)/components/esp_eth/include/esp_eth.h \
|
$(PROJECT_PATH)/components/esp_eth/include/esp_eth.h \
|
||||||
|
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_driver.h \
|
||||||
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_com.h \
|
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_com.h \
|
||||||
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_mac.h \
|
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_mac.h \
|
||||||
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_netif_glue.h \
|
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_netif_glue.h \
|
||||||
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_phy.h \
|
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_phy.h \
|
||||||
|
$(PROJECT_PATH)/components/esp_eth/include/esp_eth_phy_802_3.h \
|
||||||
$(PROJECT_PATH)/components/esp_event/include/esp_event.h \
|
$(PROJECT_PATH)/components/esp_event/include/esp_event.h \
|
||||||
$(PROJECT_PATH)/components/esp_event/include/esp_event_base.h \
|
$(PROJECT_PATH)/components/esp_event/include/esp_event_base.h \
|
||||||
$(PROJECT_PATH)/components/esp_http_client/include/esp_http_client.h \
|
$(PROJECT_PATH)/components/esp_http_client/include/esp_http_client.h \
|
||||||
|
@ -441,21 +441,59 @@ One thing should be kept in mind, is that the pause frame ability will be advert
|
|||||||
|
|
||||||
.. -------------------------------- Examples -----------------------------------
|
.. -------------------------------- Examples -----------------------------------
|
||||||
|
|
||||||
Application Example
|
Application Examples
|
||||||
-------------------
|
--------------------
|
||||||
|
|
||||||
* Ethernet basic example: :example:`ethernet/basic`.
|
* Ethernet basic example: :example:`ethernet/basic`.
|
||||||
* Ethernet iperf example: :example:`ethernet/iperf`.
|
* Ethernet iperf example: :example:`ethernet/iperf`.
|
||||||
* Ethernet to Wi-Fi AP "router": :example:`ethernet/eth2ap`.
|
* Ethernet to Wi-Fi AP "router": :example:`ethernet/eth2ap`.
|
||||||
* Most of protocol examples should also work for Ethernet: :example:`protocols`.
|
* Most of protocol examples should also work for Ethernet: :example:`protocols`.
|
||||||
|
|
||||||
|
.. ------------------------------ Advanced Topics -------------------------------
|
||||||
|
|
||||||
|
.. _advanced-topics:
|
||||||
|
|
||||||
|
Advanced Topics
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Custom PHY Driver
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
There are multiple PHY manufactures with their wide portfolios of chips available. The ESP-IDF already supports several PHY chips however one can easily get to a point where none of them satisfies user's actual needs due to either price, features, stock availability etc.
|
||||||
|
|
||||||
|
Luckily, a management interface between EMAC and PHY is standardized by IEEE 802.3 in 22.2.4 Management functions section. It defines provisions of so called “MII Management Interface” for the purposes of controlling the PHY and gathering status from the PHY. A set of management registers is defined to control chip behavior, link properties, auto-negotiation configuration etc. This basic management functionality is addressed by :component_file:`esp_eth/src/esp_eth_phy_802_3.c` in ESP-IDF and so it makes a creation of new custom PHY chip driver quite a simple task.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Always consult with PHY datasheet since some PHY chips may not comply with IEEE 802.3, Section 22.2.4. It does not mean you are not able to create a custom PHY driver, it will just require more effort. You will have to define all PHY management functions.
|
||||||
|
|
||||||
|
Majority of PHY management functionality required by the ESP-IDF Ethernet driver is covered by the :component_file:`esp_eth/src/esp_eth_phy_802_3.c` however, the following may require developing chip specific management functions:
|
||||||
|
|
||||||
|
* link status which is almost always chip specific,
|
||||||
|
* chip initialization, even though it is not strictly required, should be customized to at least ensure that expected chip is used and
|
||||||
|
* chip specific features configuration.
|
||||||
|
|
||||||
|
**Steps to create custom PHY driver:**
|
||||||
|
|
||||||
|
1. Define vendor specific registry layout based on PHY datasheet. See :component_file:`esp_eth/src/esp_eth_phy_ip101.c` as an example.
|
||||||
|
2. Prepare derived PHY management object infostructure which
|
||||||
|
|
||||||
|
* must contain at least parent IEEE 802.3 :cpp:class:`phy_802_3_t` object and
|
||||||
|
* optionally contain additional variables needed to support non-IEEE 802.3 or customized functionality. See :component_file:`esp_eth/src/esp_eth_phy_ksz80xx.c` as an example.
|
||||||
|
|
||||||
|
3. Define chip specific management call-back functions.
|
||||||
|
4. Initialize parent IEEE 802.3 object and re-assign chip specific management call-back functions.
|
||||||
|
|
||||||
|
Once you finish the new custom PHY driver implementation, consider sharing it among with other users via `IDF Component Registry <https://components.espressif.com/>`_.
|
||||||
|
|
||||||
.. ---------------------------- API Reference ----------------------------------
|
.. ---------------------------- API Reference ----------------------------------
|
||||||
|
|
||||||
API Reference
|
API Reference
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
.. include-build-file:: inc/esp_eth.inc
|
.. include-build-file:: inc/esp_eth.inc
|
||||||
|
.. include-build-file:: inc/esp_eth_driver.inc
|
||||||
.. include-build-file:: inc/esp_eth_com.inc
|
.. include-build-file:: inc/esp_eth_com.inc
|
||||||
.. include-build-file:: inc/esp_eth_mac.inc
|
.. include-build-file:: inc/esp_eth_mac.inc
|
||||||
.. include-build-file:: inc/esp_eth_phy.inc
|
.. include-build-file:: inc/esp_eth_phy.inc
|
||||||
|
.. include-build-file:: inc/esp_eth_phy_802_3.inc
|
||||||
.. include-build-file:: inc/esp_eth_netif_glue.inc
|
.. include-build-file:: inc/esp_eth_netif_glue.inc
|
||||||
|
@ -47,3 +47,6 @@ ESP NETIF Glue Event Handlers
|
|||||||
-----------------------------
|
-----------------------------
|
||||||
``esp_eth_set_default_handlers()`` and ``esp_eth_clear_default_handlers()`` functions were removed. Registration of the default IP layer handlers for Ethernet is now handled automatically. If users have already followed the recommendation to fully initialize the Ethernet driver and network interface prior to registering their Ethernet/IP event handlers, then no action is required (except for deleting the affected functions). Otherwise, users should ensure that they register the user event handlers as the last thing prior to starting the Ethernet driver.
|
``esp_eth_set_default_handlers()`` and ``esp_eth_clear_default_handlers()`` functions were removed. Registration of the default IP layer handlers for Ethernet is now handled automatically. If users have already followed the recommendation to fully initialize the Ethernet driver and network interface prior to registering their Ethernet/IP event handlers, then no action is required (except for deleting the affected functions). Otherwise, users should ensure that they register the user event handlers as the last thing prior to starting the Ethernet driver.
|
||||||
|
|
||||||
|
PHY Address Auto-detect
|
||||||
|
-----------------------
|
||||||
|
Ethernet PHY address auto-detect function ``esp_eth_detect_phy_addr`` was renamed to :cpp:func:`esp_eth_phy_802_3_detect_phy_addr` and its header declaration was moved to :component_file:`esp_eth/include/esp_eth_phy_802_3.h`.
|
@ -32,7 +32,7 @@
|
|||||||
以太网 PHY 公共寄存器
|
以太网 PHY 公共寄存器
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
* :component_file:`esp_eth/include/eth_phy_regs_struct.h`
|
* :component_file:`esp_eth/include/eth_phy_802_3_regs.h`
|
||||||
|
|
||||||
API 参考 -- 驱动程序模型
|
API 参考 -- 驱动程序模型
|
||||||
----------------------------
|
----------------------------
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_eth.h"
|
#include "esp_eth.h"
|
||||||
#include "eth_phy_regs_struct.h"
|
#include "eth_phy_802_3_regs.h"
|
||||||
#include "esp_eth_enc28j60.h"
|
#include "esp_eth_enc28j60.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
@ -417,7 +417,6 @@ components/esp32/include/rom/spi_flash.h
|
|||||||
components/esp32/include/rom/tbconsole.h
|
components/esp32/include/rom/tbconsole.h
|
||||||
components/esp32/include/rom/tjpgd.h
|
components/esp32/include/rom/tjpgd.h
|
||||||
components/esp32/include/rom/uart.h
|
components/esp32/include/rom/uart.h
|
||||||
components/esp_eth/include/eth_phy_regs_struct.h
|
|
||||||
components/esp_eth/src/dm9051.h
|
components/esp_eth/src/dm9051.h
|
||||||
components/esp_eth/src/ksz8851.h
|
components/esp_eth/src/ksz8851.h
|
||||||
components/esp_eth/src/openeth.h
|
components/esp_eth/src/openeth.h
|
||||||
|
Loading…
x
Reference in New Issue
Block a user