mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
esp_eth: added ioctl options to set Ethernet speed and duplex mode
esp_eth_ioctl third argument always acts as untyped pointer to memory now
This commit is contained in:
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_eth_com.h"
|
||||
@@ -50,6 +42,12 @@ typedef struct {
|
||||
*/
|
||||
uint32_t check_link_period_ms;
|
||||
|
||||
/**
|
||||
* @brief Configuration status of PHY autonegotiation feature
|
||||
*
|
||||
*/
|
||||
bool auto_nego_en;
|
||||
|
||||
/**
|
||||
* @brief Input frame buffer to user's stack
|
||||
*
|
||||
@@ -271,15 +269,23 @@ esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length)
|
||||
* - ESP_OK: process io command successfully
|
||||
* - ESP_ERR_INVALID_ARG: process io command failed because of some invalid argument
|
||||
* - ESP_FAIL: process io command failed because some other error occurred
|
||||
* - ESP_ERR_NOT_SUPPORTED: requested feature is not supported
|
||||
*
|
||||
* The following IO control commands are supported:
|
||||
* @li @c ETH_CMD_S_MAC_ADDR sets Ethernet interface MAC address. @c data argument is pointer to MAC address buffer with expected size of 6 bytes.
|
||||
* @li @c ETH_CMD_G_MAC_ADDR gets Ethernet interface MAC address. @c data argument is pointer to a buffer to which MAC address is to be copied. The buffer size must be at least 6 bytes.
|
||||
* @li @c ETH_CMD_S_PHY_ADDR sets PHY address in range of <0-31>. @c data argument is pointer to memory of uint32_t datatype from where the configuration option is read.
|
||||
* @li @c ETH_CMD_G_PHY_ADDR gets PHY address. @c data argument is pointer to memory of uint32_t datatype to which the PHY address is to be stored.
|
||||
* @li @c ETH_CMD_S_AUTONEGO enables or disables Ethernet link speed and duplex mode autonegotiation. @c data argument is pointer to memory of bool datatype from which the configuration option is read.
|
||||
* Preconditions: Ethernet driver needs to be stopped.
|
||||
* @li @c ETH_CMD_G_AUTONEGO gets current configuration of the Ethernet link speed and duplex mode autonegotiation. @c data argument is pointer to memory of bool datatype to which the current configuration is to be stored.
|
||||
* @li @c ETH_CMD_S_SPEED sets the Ethernet link speed. @c data argument is pointer to memory of eth_speed_t datatype from which the configuration option is read.
|
||||
* Preconditions: Ethernet driver needs to be stopped and auto-negotiation disabled.
|
||||
* @li @c ETH_CMD_G_SPEED gets current Ethernet link speed. @c data argument is pointer to memory of eth_speed_t datatype to which the speed is to be stored.
|
||||
* @li @c ETH_CMD_S_PROMISCUOUS sets/resets Ethernet interface promiscuous mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read.
|
||||
* @li @c ETH_CMD_S_FLOW_CTRL sets/resets Ethernet interface flow control. @c data argument is pointer to memory of bool datatype from which the configuration option is read.
|
||||
* @li @c ETH_CMD_S_DUPLEX_MODE sets the Ethernet duplex mode. @c data argument is pointer to memory of eth_duplex_t datatype from which the configuration option is read.
|
||||
* Preconditions: Ethernet driver needs to be stopped and auto-negotiation disabled.
|
||||
* @li @c ETH_CMD_G_DUPLEX_MODE gets current Ethernet link duplex mode. @c data argument is pointer to memory of eth_duplex_t datatype to which the duplex mode is to be stored.
|
||||
* @li @c ETH_CMD_S_PHY_LOOPBACK sets/resets PHY to/from loopback mode. @c data argument is pointer to memory of bool datatype from which the configuration option is read.
|
||||
*
|
||||
|
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
@@ -85,10 +77,14 @@ typedef enum {
|
||||
ETH_CMD_S_MAC_ADDR, /*!< Set MAC address */
|
||||
ETH_CMD_G_PHY_ADDR, /*!< Get PHY address */
|
||||
ETH_CMD_S_PHY_ADDR, /*!< Set PHY address */
|
||||
ETH_CMD_G_AUTONEGO, /*!< Get PHY Auto Negotiation */
|
||||
ETH_CMD_S_AUTONEGO, /*!< Set PHY Auto Negotiation */
|
||||
ETH_CMD_G_SPEED, /*!< Get Speed */
|
||||
ETH_CMD_S_SPEED, /*!< Set Speed */
|
||||
ETH_CMD_S_PROMISCUOUS, /*!< Set promiscuous mode */
|
||||
ETH_CMD_S_FLOW_CTRL, /*!< Set flow control */
|
||||
ETH_CMD_G_DUPLEX_MODE, /*!< Get Duplex mode */
|
||||
ETH_CMD_S_DUPLEX_MODE, /*!< Set Duplex mode */
|
||||
ETH_CMD_S_PHY_LOOPBACK,/*!< Set PHY loopback */
|
||||
} esp_eth_io_cmd_t;
|
||||
|
||||
|
@@ -3,7 +3,6 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -16,6 +15,17 @@ extern "C" {
|
||||
|
||||
#define ESP_ETH_PHY_ADDR_AUTO (-1)
|
||||
|
||||
/**
|
||||
* @brief Auto-negotiation controll commands
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_ETH_PHY_AUTONEGO_RESTART,
|
||||
ESP_ETH_PHY_AUTONEGO_EN,
|
||||
ESP_ETH_PHY_AUTONEGO_DIS,
|
||||
ESP_ETH_PHY_AUTONEGO_G_STAT,
|
||||
} eth_phy_autoneg_cmd_t;
|
||||
|
||||
/**
|
||||
* @brief Ethernet PHY
|
||||
*
|
||||
@@ -81,7 +91,7 @@ struct esp_eth_phy_s {
|
||||
/**
|
||||
* @brief Deinitialize Ethernet PHY
|
||||
*
|
||||
* @param[in] phyL Ethernet PHY instance
|
||||
* @param[in] phy: Ethernet PHY instance
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: deinitialize Ethernet PHY successfully
|
||||
@@ -91,16 +101,20 @@ struct esp_eth_phy_s {
|
||||
esp_err_t (*deinit)(esp_eth_phy_t *phy);
|
||||
|
||||
/**
|
||||
* @brief Start auto negotiation
|
||||
* @brief Configure auto negotiation
|
||||
*
|
||||
* @param[in] phy: Ethernet PHY instance
|
||||
* @param[in] cmd: Configuration command, it is possible to Enable (restart), Disable or get current status
|
||||
* of PHY auto negotiation
|
||||
* @param[out] autonego_en_stat: Address where to store current status of auto negotiation configuration
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: restart auto negotiation successfully
|
||||
* - ESP_FAIL: restart auto negotiation failed because some error occurred
|
||||
* - ESP_ERR_INVALID_ARG: invalid command
|
||||
*
|
||||
*/
|
||||
esp_err_t (*negotiate)(esp_eth_phy_t *phy);
|
||||
esp_err_t (*autonego_ctrl)(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat);
|
||||
|
||||
/**
|
||||
* @brief Get Ethernet PHY link status
|
||||
@@ -167,18 +181,50 @@ struct esp_eth_phy_s {
|
||||
esp_err_t (*advertise_pause_ability)(esp_eth_phy_t *phy, uint32_t ability);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @brief Sets the PHY to loopback mode
|
||||
*
|
||||
* @param[in] phy: Ethernet PHY instance
|
||||
* @param[in] enable: enables or disables PHY loopback
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: configures PHY instance loopback function successfully
|
||||
* - ESP_OK: PHY instance loopback mode has been configured successfully
|
||||
* - ESP_FAIL: PHY instance loopback configuration failed because some error occurred
|
||||
*
|
||||
*/
|
||||
esp_err_t (*loopback)(esp_eth_phy_t *phy, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Sets PHY speed mode
|
||||
*
|
||||
* @note Autonegotiation feature needs to be disabled prior to calling this function for the new
|
||||
* setting to be applied
|
||||
*
|
||||
* @param[in] phy: Ethernet PHY instance
|
||||
* @param[in] speed: Speed mode to be set
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: PHY instance speed mode has been configured successfully
|
||||
* - ESP_FAIL: PHY instance speed mode configuration failed because some error occurred
|
||||
*
|
||||
*/
|
||||
esp_err_t (*set_speed)(esp_eth_phy_t *phy, eth_speed_t speed);
|
||||
|
||||
/**
|
||||
* @brief Sets PHY duplex mode
|
||||
*
|
||||
* @note Autonegotiation feature needs to be disabled prior to calling this function for the new
|
||||
* setting to be applied
|
||||
*
|
||||
* @param[in] phy: Ethernet PHY instance
|
||||
* @param[in] duplex: Duplex mode to be set
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: PHY instance duplex mode has been configured successfully
|
||||
* - ESP_FAIL: PHY instance duplex mode configuration failed because some error occurred
|
||||
*
|
||||
*/
|
||||
esp_err_t (*set_duplex)(esp_eth_phy_t *phy, eth_duplex_t duplex);
|
||||
|
||||
/**
|
||||
* @brief Free memory of Ethernet PHY instance
|
||||
*
|
||||
|
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdatomic.h>
|
||||
@@ -20,7 +12,6 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_timer.h"
|
||||
#include "soc/soc.h" // TODO: for esp_eth_ioctl API compatibility reasons, will be removed with next major release
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
@@ -49,6 +40,7 @@ typedef struct {
|
||||
esp_eth_mac_t *mac;
|
||||
esp_timer_handle_t check_link_timer;
|
||||
uint32_t check_link_period_ms;
|
||||
bool auto_nego_en;
|
||||
eth_speed_t speed;
|
||||
eth_duplex_t duplex;
|
||||
eth_link_t link;
|
||||
@@ -221,6 +213,9 @@ esp_err_t esp_eth_driver_install(const esp_eth_config_t *config, esp_eth_handle_
|
||||
// init MAC first, so that MAC can generate the correct SMI signals
|
||||
ESP_GOTO_ON_ERROR(mac->init(mac), err, TAG, "init mac failed");
|
||||
ESP_GOTO_ON_ERROR(phy->init(phy), err, TAG, "init phy failed");
|
||||
// get default status of PHY autonegotiation (ultimately may also indicate if it is supported)
|
||||
ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, ð_driver->auto_nego_en),
|
||||
err, TAG, "get autonegotiation status failed");
|
||||
ESP_LOGD(TAG, "new ethernet driver @%p", eth_driver);
|
||||
*out_hdl = eth_driver;
|
||||
|
||||
@@ -275,7 +270,10 @@ esp_err_t esp_eth_start(esp_eth_handle_t hdl)
|
||||
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
|
||||
ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(ð_driver->fsm, &expected_fsm, ESP_ETH_FSM_START),
|
||||
ESP_ERR_INVALID_STATE, err, TAG, "driver started already");
|
||||
ESP_GOTO_ON_ERROR(phy->negotiate(phy), err, TAG, "phy negotiation failed");
|
||||
// Autonegotiate link speed and duplex mode when enabled
|
||||
if (eth_driver->auto_nego_en == true) {
|
||||
ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_RESTART, ð_driver->auto_nego_en), err, TAG, "phy negotiation failed");
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "start mac failed");
|
||||
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, ð_driver, sizeof(esp_eth_driver_t *), 0),
|
||||
err, TAG, "send ETHERNET_EVENT_START event failed");
|
||||
@@ -298,6 +296,7 @@ esp_err_t esp_eth_stop(esp_eth_handle_t hdl)
|
||||
ESP_ERR_INVALID_STATE, err, TAG, "driver not started yet");
|
||||
ESP_GOTO_ON_ERROR(esp_timer_stop(eth_driver->check_link_timer), err, TAG, "stop link timer failed");
|
||||
ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "stop mac failed");
|
||||
|
||||
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_STOP, ð_driver, sizeof(esp_eth_driver_t *), 0),
|
||||
err, TAG, "send ETHERNET_EVENT_STOP event failed");
|
||||
err:
|
||||
@@ -361,46 +360,61 @@ esp_err_t esp_eth_ioctl(esp_eth_handle_t hdl, esp_eth_io_cmd_t cmd, void *data)
|
||||
ESP_GOTO_ON_ERROR(mac->get_addr(mac, (uint8_t *)data), err, TAG, "get mac address failed");
|
||||
break;
|
||||
case ETH_CMD_S_PHY_ADDR:
|
||||
if ((uint32_t)data >= SOC_DRAM_LOW) {
|
||||
ESP_GOTO_ON_ERROR(phy->set_addr(phy, *(uint32_t *)data), err, TAG, "set phy address failed");
|
||||
} else { // TODO: for API compatibility reasons, will be removed with next major release
|
||||
ESP_GOTO_ON_ERROR(phy->set_addr(phy, (uint32_t)data), err, TAG, "set phy address failed");
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(phy->set_addr(phy, *(uint32_t *)data), err, TAG, "set phy address failed");
|
||||
break;
|
||||
case ETH_CMD_G_PHY_ADDR:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store phy addr");
|
||||
ESP_GOTO_ON_ERROR(phy->get_addr(phy, (uint32_t *)data), err, TAG, "get phy address failed");
|
||||
break;
|
||||
case ETH_CMD_S_AUTONEGO:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set autonegotiation to null");
|
||||
// check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
|
||||
ESP_GOTO_ON_FALSE(atomic_load(ð_driver->fsm) == ESP_ETH_FSM_STOP, ESP_ERR_INVALID_STATE, err, TAG, "link configuration is only allowed when driver is stopped");
|
||||
if (*(bool *)data == true) {
|
||||
ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_EN, ð_driver->auto_nego_en), err, TAG, "phy negotiation enable failed");
|
||||
} else {
|
||||
ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_DIS, ð_driver->auto_nego_en), err, TAG, "phy negotiation disable failed");
|
||||
}
|
||||
break;
|
||||
case ETH_CMD_G_AUTONEGO:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store autonegotiation configuration");
|
||||
*(bool *)data = eth_driver->auto_nego_en;
|
||||
break;
|
||||
case ETH_CMD_S_SPEED:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set speed to null");
|
||||
// check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
|
||||
ESP_GOTO_ON_FALSE(atomic_load(ð_driver->fsm) == ESP_ETH_FSM_STOP, ESP_ERR_INVALID_STATE, err, TAG, "link configuration is only allowed when driver is stopped");
|
||||
ESP_GOTO_ON_FALSE(eth_driver->auto_nego_en == false, ESP_ERR_INVALID_STATE, err, TAG, "autonegotiation needs to be disabled to change this parameter");
|
||||
ESP_GOTO_ON_ERROR(phy->set_speed(phy, *(eth_speed_t *)data), err, TAG, "set speed mode failed");
|
||||
break;
|
||||
case ETH_CMD_G_SPEED:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store speed value");
|
||||
*(eth_speed_t *)data = eth_driver->speed;
|
||||
break;
|
||||
case ETH_CMD_S_PROMISCUOUS:
|
||||
if ((uint32_t)data >= SOC_DRAM_LOW) {
|
||||
ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed");
|
||||
} else { // TODO: for API compatibility reasons, will be removed with next major release
|
||||
ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, (bool)data), err, TAG, "set promiscuous mode failed");
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set promiscuous to null");
|
||||
ESP_GOTO_ON_ERROR(mac->set_promiscuous(mac, *(bool *)data), err, TAG, "set promiscuous mode failed");
|
||||
break;
|
||||
case ETH_CMD_S_FLOW_CTRL:
|
||||
if ((uint32_t)data >= SOC_DRAM_LOW) {
|
||||
ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed");
|
||||
ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, *(uint32_t *)data), err, TAG, "phy advertise pause ability failed");
|
||||
} else { // TODO: for API compatibility reasons, will be removed with next major release
|
||||
ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, (bool)data), err, TAG, "enable mac flow control failed");
|
||||
ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, (uint32_t)data), err, TAG, "phy advertise pause ability failed");
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set flow ctrl to null");
|
||||
ESP_GOTO_ON_ERROR(mac->enable_flow_ctrl(mac, *(bool *)data), err, TAG, "enable mac flow control failed");
|
||||
ESP_GOTO_ON_ERROR(phy->advertise_pause_ability(phy, *(uint32_t *)data), err, TAG, "phy advertise pause ability failed");
|
||||
break;
|
||||
case ETH_CMD_S_DUPLEX_MODE:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set duplex to null");
|
||||
ESP_GOTO_ON_FALSE(eth_driver->auto_nego_en == false, ESP_ERR_INVALID_STATE, err, TAG, "autonegotiation needs to be disabled to change this parameter");
|
||||
// check if driver is stopped; configuration should be changed only when transmitting/receiving is not active
|
||||
ESP_GOTO_ON_FALSE(atomic_load(ð_driver->fsm) == ESP_ETH_FSM_STOP, ESP_ERR_INVALID_STATE, err, TAG, "link configuration is only allowed when driver is stopped");
|
||||
ESP_GOTO_ON_ERROR(phy->set_duplex(phy, *(eth_duplex_t *)data), err, TAG, "set duplex mode failed");
|
||||
break;
|
||||
case ETH_CMD_G_DUPLEX_MODE:
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "no mem to store duplex value");
|
||||
*(eth_duplex_t *)data = eth_driver->duplex;
|
||||
break;
|
||||
case ETH_CMD_S_PHY_LOOPBACK:
|
||||
if ((uint32_t)data >= SOC_DRAM_LOW) {
|
||||
ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed");
|
||||
} else { // TODO: for API compatibility reasons, will be removed with next major release
|
||||
ESP_GOTO_ON_ERROR(phy->loopback(phy, (bool)data), err, TAG, "configuration of phy loopback mode failed");
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(data, ESP_ERR_INVALID_ARG, err, TAG, "can't set loopback to null");
|
||||
ESP_GOTO_ON_ERROR(phy->loopback(phy, *(bool *)data), err, TAG, "configuration of phy loopback mode failed");
|
||||
|
||||
break;
|
||||
default:
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);
|
||||
|
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
@@ -146,7 +138,7 @@ static esp_err_t dm9051_get_link(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent);
|
||||
/* Updata 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");
|
||||
return ESP_OK;
|
||||
err:
|
||||
@@ -200,36 +192,64 @@ static esp_err_t dm9051_reset_hw(esp_eth_phy_t *phy)
|
||||
* 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_negotiate(esp_eth_phy_t *phy)
|
||||
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;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
dm9051->link_status = ETH_LINK_DOWN;
|
||||
/* Start auto negotiation */
|
||||
bmcr_reg_t bmcr = {
|
||||
.speed_select = 1, /* 100Mbps */
|
||||
.duplex_mode = 1, /* Full Duplex */
|
||||
.en_auto_nego = 1, /* Auto Negotiation */
|
||||
.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;
|
||||
dscsr_reg_t dscsr;
|
||||
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");
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_DSCSR_REG_ADDR, &(dscsr.val)), err, TAG, "read DSCSR failed");
|
||||
if (bmsr.auto_nego_complete && dscsr.anmb & 0x08) {
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
if ((to >= dm9051->autonego_timeout_ms / 100) && (dm9051->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
|
||||
}
|
||||
|
||||
*autonego_en_stat = bmcr.en_auto_nego;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -335,6 +355,51 @@ 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;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t dm9051_init(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -385,13 +450,15 @@ esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config)
|
||||
dm9051->parent.init = dm9051_init;
|
||||
dm9051->parent.deinit = dm9051_deinit;
|
||||
dm9051->parent.set_mediator = dm9051_set_mediator;
|
||||
dm9051->parent.negotiate = dm9051_negotiate;
|
||||
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:
|
||||
|
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
@@ -146,7 +138,7 @@ static esp_err_t dp83848_get_link(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent);
|
||||
/* Updata 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");
|
||||
return ESP_OK;
|
||||
err:
|
||||
@@ -194,36 +186,64 @@ static esp_err_t dp83848_reset_hw(esp_eth_phy_t *phy)
|
||||
* 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_negotiate(esp_eth_phy_t *phy)
|
||||
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;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
dp83848->link_status = ETH_LINK_DOWN;
|
||||
/* Start auto negotiation */
|
||||
bmcr_reg_t bmcr = {
|
||||
.speed_select = 1, /* 100Mbps */
|
||||
.duplex_mode = 1, /* Full Duplex */
|
||||
.en_auto_nego = 1, /* Auto Negotiation */
|
||||
.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;
|
||||
physts_reg_t physts;
|
||||
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");
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)), err, TAG, "read PHYSTS failed");
|
||||
if (bmsr.auto_nego_complete && physts.auto_nego_complete) {
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
if ((to >= dp83848->autonego_timeout_ms / 100) && (dp83848->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||
}
|
||||
|
||||
*autonego_en_stat = bmcr.en_auto_nego;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -329,6 +349,51 @@ 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)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -379,13 +444,15 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config)
|
||||
dp83848->parent.init = dp83848_init;
|
||||
dp83848->parent.deinit = dp83848_deinit;
|
||||
dp83848->parent.set_mediator = dp83848_set_mediator;
|
||||
dp83848->parent.negotiate = dp83848_negotiate;
|
||||
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:
|
||||
|
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
@@ -164,7 +156,7 @@ 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_LINK, (void *)link), err, TAG, "chagne 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;
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -187,7 +179,7 @@ static esp_err_t ip101_get_link(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent);
|
||||
/* Updata 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");
|
||||
return ESP_OK;
|
||||
err:
|
||||
@@ -235,34 +227,64 @@ static esp_err_t ip101_reset_hw(esp_eth_phy_t *phy)
|
||||
* 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_negotiate(esp_eth_phy_t *phy)
|
||||
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;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
ip101->link_status = ETH_LINK_DOWN;
|
||||
/* Restart auto negotiation */
|
||||
bmcr_reg_t bmcr = {
|
||||
.speed_select = 1, /* 100Mbps */
|
||||
.duplex_mode = 1, /* Full Duplex */
|
||||
.en_auto_nego = 1, /* Auto Negotiation */
|
||||
.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;
|
||||
|
||||
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;
|
||||
}
|
||||
if ((to >= ip101->autonego_timeout_ms / 100) && (ip101->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||
}
|
||||
|
||||
*autonego_en_stat = bmcr.en_auto_nego;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -368,6 +390,52 @@ 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)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -418,13 +486,15 @@ esp_eth_phy_t *esp_eth_phy_new_ip101(const eth_phy_config_t *config)
|
||||
ip101->parent.init = ip101_init;
|
||||
ip101->parent.deinit = ip101_deinit;
|
||||
ip101->parent.set_mediator = ip101_set_mediator;
|
||||
ip101->parent.negotiate = ip101_negotiate;
|
||||
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);
|
||||
|
@@ -3,7 +3,6 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
@@ -234,34 +233,64 @@ static esp_err_t ksz80xx_reset_hw(esp_eth_phy_t *phy)
|
||||
* 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_negotiate(esp_eth_phy_t *phy)
|
||||
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;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
ksz80xx->link_status = ETH_LINK_DOWN;
|
||||
/* Restart auto negotiation */
|
||||
bmcr_reg_t bmcr = {
|
||||
.speed_select = 1, /* 100Mbps */
|
||||
.duplex_mode = 1, /* Full Duplex */
|
||||
.en_auto_nego = 1, /* Auto Negotiation */
|
||||
.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;
|
||||
|
||||
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;
|
||||
}
|
||||
if ((to >= ksz80xx->autonego_timeout_ms / 100) && (ksz80xx->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||
}
|
||||
|
||||
*autonego_en_stat = bmcr.en_auto_nego;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -367,6 +396,51 @@ 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 esp_err_t ksz80xx_init(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -414,13 +488,15 @@ esp_eth_phy_t *esp_eth_phy_new_ksz8041(const eth_phy_config_t *config)
|
||||
ksz8041->parent.init = ksz80xx_init;
|
||||
ksz8041->parent.deinit = ksz80xx_deinit;
|
||||
ksz8041->parent.set_mediator = ksz80xx_set_mediator;
|
||||
ksz8041->parent.negotiate = ksz80xx_negotiate;
|
||||
ksz8041->parent.autonego_ctrl = ksz80xx_autonego_ctrl;
|
||||
ksz8041->parent.get_link = ksz80xx_get_link;
|
||||
ksz8041->parent.pwrctl = ksz80xx_pwrctl;
|
||||
ksz8041->parent.get_addr = ksz80xx_get_addr;
|
||||
ksz8041->parent.set_addr = ksz80xx_set_addr;
|
||||
ksz8041->parent.advertise_pause_ability = ksz80xx_advertise_pause_ability;
|
||||
ksz8041->parent.loopback = ksz80xx_loopback;
|
||||
ksz8041->parent.set_speed = ksz80xx_set_speed;
|
||||
ksz8041->parent.set_duplex = ksz80xx_set_duplex;
|
||||
ksz8041->parent.del = ksz80xx_del;
|
||||
return &(ksz8041->parent);
|
||||
err:
|
||||
@@ -444,12 +520,15 @@ esp_eth_phy_t *esp_eth_phy_new_ksz8081(const eth_phy_config_t *config)
|
||||
ksz8081->parent.init = ksz80xx_init;
|
||||
ksz8081->parent.deinit = ksz80xx_deinit;
|
||||
ksz8081->parent.set_mediator = ksz80xx_set_mediator;
|
||||
ksz8081->parent.negotiate = ksz80xx_negotiate;
|
||||
ksz8081->parent.autonego_ctrl = ksz80xx_autonego_ctrl;
|
||||
ksz8081->parent.get_link = ksz80xx_get_link;
|
||||
ksz8081->parent.pwrctl = ksz80xx_pwrctl;
|
||||
ksz8081->parent.get_addr = ksz80xx_get_addr;
|
||||
ksz8081->parent.set_addr = ksz80xx_set_addr;
|
||||
ksz8081->parent.advertise_pause_ability = ksz80xx_advertise_pause_ability;
|
||||
ksz8081->parent.loopback = ksz80xx_loopback;
|
||||
ksz8081->parent.set_speed = ksz80xx_set_speed;
|
||||
ksz8081->parent.set_duplex = ksz80xx_set_duplex;
|
||||
ksz8081->parent.del = ksz80xx_del;
|
||||
return &(ksz8081->parent);
|
||||
err:
|
||||
|
@@ -1,23 +1,10 @@
|
||||
// Copyright (c) 2021 Vladimir Chistyakov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Vladimir Chistyakov
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
@@ -165,37 +152,68 @@ err:
|
||||
* the result of negotiation won't be relected to uppler layers.
|
||||
* Instead, the negotiation result is fetched by linker timer, see `phy_ksz8851_get_link()`
|
||||
*/
|
||||
static esp_err_t phy_ksz8851_negotiate(esp_eth_phy_t *phy)
|
||||
static esp_err_t phy_ksz8851_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
|
||||
esp_eth_mediator_t *eth = ksz8851->eth;
|
||||
ESP_LOGD(TAG, "restart negotiation");
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
ksz8851->link_status = ETH_LINK_DOWN;
|
||||
|
||||
uint32_t control;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control | P1CR_RESTART_AN), err, TAG, "P1CR write failed");
|
||||
|
||||
uint32_t status;
|
||||
unsigned to;
|
||||
for (to = 0; to < ksz8851->autonego_timeout_ms / 100; to++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1SR, &status), err, TAG, "P1SR read failed");
|
||||
if (status & P1SR_AN_DONE) {
|
||||
break;
|
||||
switch (cmd) {
|
||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
||||
ESP_GOTO_ON_FALSE(control & P1CR_AUTO_NEGOTIATION_ENABLE, ESP_ERR_INVALID_STATE, err, TAG, "auto negotiation is disabled");
|
||||
ESP_LOGD(TAG, "restart negotiation");
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
ksz8851->link_status = ETH_LINK_DOWN;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control | P1CR_RESTART_AN), err, TAG, "P1CR write failed");
|
||||
|
||||
uint32_t status;
|
||||
unsigned to;
|
||||
for (to = 0; to < ksz8851->autonego_timeout_ms / 100; to++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1SR, &status), err, TAG, "P1SR read failed");
|
||||
if (status & P1SR_AN_DONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((to >= ksz8851->autonego_timeout_ms / 100) && (ksz8851->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||
if ((to >= ksz8851->autonego_timeout_ms / 100) && (ksz8851->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||
}
|
||||
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control), err, TAG, "P1CR write failed");
|
||||
|
||||
ESP_LOGD(TAG, "negotiation succeeded");
|
||||
break;
|
||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
||||
if (control & P1CR_AUTO_NEGOTIATION_ENABLE) {
|
||||
control &= ~P1CR_AUTO_NEGOTIATION_ENABLE; /* Disable Auto Negotiation */
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control), err, TAG, "P1CR write failed");
|
||||
/* read configuration back */
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
|
||||
ESP_GOTO_ON_FALSE((control & P1CR_AUTO_NEGOTIATION_ENABLE) == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
|
||||
}
|
||||
break;
|
||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
||||
if (!(control & P1CR_AUTO_NEGOTIATION_ENABLE)) {
|
||||
control |= P1CR_AUTO_NEGOTIATION_ENABLE; /* Enable Auto Negotiation */
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control), err, TAG, "P1CR write failed");
|
||||
/* read configuration back */
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
|
||||
ESP_GOTO_ON_FALSE(control & P1CR_AUTO_NEGOTIATION_ENABLE, ESP_FAIL, err, TAG, "disable 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;
|
||||
}
|
||||
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control), err, TAG, "P1CR write failed");
|
||||
|
||||
ESP_LOGD(TAG, "negotiation succeeded");
|
||||
*autonego_en_stat = (control & P1CR_AUTO_NEGOTIATION_ENABLE) != 0;
|
||||
return ESP_OK;
|
||||
err:
|
||||
ESP_LOGD(TAG, "negotiation failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -264,6 +282,60 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t phy_ksz8851_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
|
||||
esp_eth_mediator_t *eth = ksz8851->eth;
|
||||
|
||||
if (ksz8851->link_status == ETH_LINK_UP) {
|
||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
||||
ksz8851->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 *)ksz8851->link_status), err, TAG, "change link failed");
|
||||
}
|
||||
/* Set speed */
|
||||
uint32_t control;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
|
||||
if (speed == ETH_SPEED_100M) {
|
||||
control |= P1CR_FORCE_SPEED;
|
||||
} else {
|
||||
control &= ~P1CR_FORCE_SPEED;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control), err, TAG, "P1CR write failed");
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t phy_ksz8851_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
|
||||
esp_eth_mediator_t *eth = ksz8851->eth;
|
||||
|
||||
if (ksz8851->link_status == ETH_LINK_UP) {
|
||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
||||
ksz8851->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 *)ksz8851->link_status), err, TAG, "change link failed");
|
||||
}
|
||||
/* Set duplex mode */
|
||||
uint32_t control;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
|
||||
if (duplex == ETH_DUPLEX_FULL) {
|
||||
control |= P1CR_FORCE_DUPLEX;
|
||||
} else {
|
||||
control &= ~P1CR_FORCE_DUPLEX;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control), err, TAG, "P1CR write failed");
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t phy_ksz8851_del(esp_eth_phy_t *phy)
|
||||
{
|
||||
ESP_LOGD(TAG, "deleting PHY");
|
||||
@@ -288,13 +360,15 @@ esp_eth_phy_t *esp_eth_phy_new_ksz8851snl(const eth_phy_config_t *config)
|
||||
ksz8851->parent.reset_hw = phy_ksz8851_reset_hw;
|
||||
ksz8851->parent.init = phy_ksz8851_init;
|
||||
ksz8851->parent.deinit = phy_ksz8851_deinit;
|
||||
ksz8851->parent.negotiate = phy_ksz8851_negotiate;
|
||||
ksz8851->parent.autonego_ctrl = phy_ksz8851_autonego_ctrl;
|
||||
ksz8851->parent.get_link = phy_ksz8851_get_link;
|
||||
ksz8851->parent.pwrctl = phy_ksz8851_pwrctl;
|
||||
ksz8851->parent.set_addr = phy_ksz8851_set_addr;
|
||||
ksz8851->parent.get_addr = phy_ksz8851_get_addr;
|
||||
ksz8851->parent.advertise_pause_ability = phy_ksz8851_advertise_pause_ability;
|
||||
ksz8851->parent.loopback = phy_ksz8851_loopback;
|
||||
ksz8851->parent.set_speed = phy_ksz8851_set_speed;
|
||||
ksz8851->parent.set_duplex = phy_ksz8851_set_duplex;
|
||||
ksz8851->parent.del = phy_ksz8851_del;
|
||||
return &(ksz8851->parent);
|
||||
err:
|
||||
|
@@ -3,7 +3,6 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
@@ -342,37 +341,64 @@ static esp_err_t lan87xx_reset_hw(esp_eth_phy_t *phy)
|
||||
* 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_negotiate(esp_eth_phy_t *phy)
|
||||
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;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
lan87xx->link_status = ETH_LINK_DOWN;
|
||||
/* Restart auto negotiation */
|
||||
bmcr_reg_t bmcr = {
|
||||
.speed_select = 1, /* 100Mbps */
|
||||
.duplex_mode = 1, /* Full Duplex */
|
||||
.en_auto_nego = 1, /* Auto Negotiation */
|
||||
.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;
|
||||
pscsr_reg_t pscsr;
|
||||
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");
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_PSCSR_REG_ADDR, &(pscsr.val)), err, TAG, "read PSCSR failed");
|
||||
if (bmsr.auto_nego_complete && pscsr.auto_nego_done) {
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
/* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
|
||||
if (to >= lan87xx->autonego_timeout_ms / 100 && (lan87xx->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||
}
|
||||
|
||||
*autonego_en_stat = bmcr.en_auto_nego;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -478,6 +504,51 @@ 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)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -536,12 +607,14 @@ esp_eth_phy_t *esp_eth_phy_new_lan87xx(const eth_phy_config_t *config)
|
||||
lan87xx->parent.init = lan87xx_init;
|
||||
lan87xx->parent.deinit = lan87xx_deinit;
|
||||
lan87xx->parent.set_mediator = lan87xx_set_mediator;
|
||||
lan87xx->parent.negotiate = lan87xx_negotiate;
|
||||
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;
|
||||
|
||||
|
@@ -1,16 +1,8 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -188,34 +180,64 @@ static esp_err_t rtl8201_reset_hw(esp_eth_phy_t *phy)
|
||||
* 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_negotiate(esp_eth_phy_t *phy)
|
||||
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;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
rtl8201->link_status = ETH_LINK_DOWN;
|
||||
/* Restart auto negotiation */
|
||||
bmcr_reg_t bmcr = {
|
||||
.speed_select = 1, /* 100Mbps */
|
||||
.duplex_mode = 1, /* Full Duplex */
|
||||
.en_auto_nego = 1, /* Auto Negotiation */
|
||||
.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;
|
||||
|
||||
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;
|
||||
}
|
||||
if ((to >= rtl8201->autonego_timeout_ms / 100) && (rtl8201->link_status == ETH_LINK_UP)) {
|
||||
ESP_LOGW(TAG, "auto negotiation timeout");
|
||||
}
|
||||
|
||||
*autonego_en_stat = bmcr.en_auto_nego;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -321,6 +343,51 @@ 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)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -371,13 +438,15 @@ esp_eth_phy_t *esp_eth_phy_new_rtl8201(const eth_phy_config_t *config)
|
||||
rtl8201->parent.init = rtl8201_init;
|
||||
rtl8201->parent.deinit = rtl8201_deinit;
|
||||
rtl8201->parent.set_mediator = rtl8201_set_mediator;
|
||||
rtl8201->parent.negotiate = rtl8201_negotiate;
|
||||
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);
|
||||
|
@@ -1,16 +1,8 @@
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
@@ -24,6 +16,8 @@
|
||||
#include "esp_rom_sys.h"
|
||||
#include "w5500.h"
|
||||
|
||||
#define W5500_WAIT_FOR_RESET_MS (10) // wait for W5500 internal PLL to be Locked after reset assert
|
||||
|
||||
static const char *TAG = "w5500.phy";
|
||||
|
||||
/***************Vendor Specific Register***************/
|
||||
@@ -43,6 +37,16 @@ typedef union {
|
||||
uint8_t val;
|
||||
} phycfg_reg_t;
|
||||
|
||||
typedef enum {
|
||||
W5500_OP_MODE_10BT_HALF_AUTO_DIS,
|
||||
W5500_OP_MODE_10BT_FULL_AUTO_DIS,
|
||||
W5500_OP_MODE_100BT_HALF_AUTO_DIS,
|
||||
W5500_OP_MODE_100BT_FULL_AUTO_DIS,
|
||||
W5500_OP_MODE_100BT_HALF_AUTO_EN,
|
||||
W5500_OP_MODE_NOT_USED,
|
||||
W5500_OP_MODE_PWR_DOWN,
|
||||
W5500_OP_MODE_ALL_CAPABLE,
|
||||
} phy_w5500_op_mode_e;
|
||||
|
||||
typedef struct {
|
||||
esp_eth_phy_t parent;
|
||||
@@ -121,7 +125,7 @@ static esp_err_t w5500_reset(esp_eth_phy_t *phy)
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG, "read PHYCFG failed");
|
||||
phycfg.reset = 0; // set to '0' will reset internal PHY
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
vTaskDelay(pdMS_TO_TICKS(W5500_WAIT_FOR_RESET_MS));
|
||||
phycfg.reset = 1; // set to '1' after reset
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
return ESP_OK;
|
||||
@@ -143,18 +147,64 @@ static esp_err_t w5500_reset_hw(esp_eth_phy_t *phy)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t w5500_negotiate(esp_eth_phy_t *phy)
|
||||
static esp_err_t w5500_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
|
||||
esp_eth_mediator_t *eth = w5500->eth;
|
||||
/* in case any link status has changed, let's assume we're in link down status */
|
||||
w5500->link_status = ETH_LINK_DOWN;
|
||||
|
||||
phycfg_reg_t phycfg;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG, "read PHYCFG failed");
|
||||
phycfg.opsel = 1; // PHY working mode configured by register
|
||||
phycfg.opmode = 7; // all capable, auto-negotiation enabled
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
|
||||
switch (cmd) {
|
||||
case ESP_ETH_PHY_AUTONEGO_RESTART:
|
||||
ESP_GOTO_ON_FALSE(phycfg.opmode == W5500_OP_MODE_ALL_CAPABLE || phycfg.opmode == W5500_OP_MODE_100BT_HALF_AUTO_EN,
|
||||
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 */
|
||||
w5500->link_status = ETH_LINK_DOWN;
|
||||
|
||||
phycfg.opsel = 1; // Configure PHY Operation Mode based on registry setting
|
||||
phycfg.reset = 0; // PHY needs to be reset after configuring opsel and opmode
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(W5500_WAIT_FOR_RESET_MS));
|
||||
phycfg.reset = 1; // reset flag needs to be put back to 1
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
|
||||
*autonego_en_stat = phycfg.opmode == W5500_OP_MODE_ALL_CAPABLE || phycfg.opmode == W5500_OP_MODE_100BT_HALF_AUTO_EN;
|
||||
break;
|
||||
case ESP_ETH_PHY_AUTONEGO_DIS:
|
||||
/* W5500 autonegotiation cannot be separately disabled, only specific speed/duplex mode needs to be configured. Hence set the
|
||||
last used configuration */
|
||||
if (phycfg.duplex) { // Full duplex
|
||||
if (phycfg.speed) { // 100 Mbps speed
|
||||
phycfg.opmode = W5500_OP_MODE_100BT_FULL_AUTO_DIS;
|
||||
} else {
|
||||
phycfg.opmode = W5500_OP_MODE_10BT_FULL_AUTO_DIS;
|
||||
}
|
||||
} else {
|
||||
if (phycfg.speed) { // 100 Mbps speed
|
||||
phycfg.opmode = W5500_OP_MODE_100BT_HALF_AUTO_DIS;
|
||||
} else {
|
||||
phycfg.opmode = W5500_OP_MODE_10BT_HALF_AUTO_DIS;
|
||||
}
|
||||
}
|
||||
phycfg.opsel = 1;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
*autonego_en_stat = false;
|
||||
break;
|
||||
case ESP_ETH_PHY_AUTONEGO_EN:
|
||||
phycfg.opsel = 1; // PHY working mode configured by register
|
||||
phycfg.opmode = W5500_OP_MODE_ALL_CAPABLE; // all capable, auto-negotiation enabled
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
*autonego_en_stat = true;
|
||||
break;
|
||||
case ESP_ETH_PHY_AUTONEGO_G_STAT:
|
||||
*autonego_en_stat = phycfg.opmode == W5500_OP_MODE_ALL_CAPABLE || phycfg.opmode == W5500_OP_MODE_100BT_HALF_AUTO_EN;
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
@@ -203,6 +253,83 @@ static esp_err_t w5500_loopback(esp_eth_phy_t *phy, bool enable)
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static esp_err_t w5500_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
|
||||
esp_eth_mediator_t *eth = w5500->eth;
|
||||
|
||||
if (w5500->link_status == ETH_LINK_UP) {
|
||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
||||
w5500->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 *)w5500->link_status), err, TAG, "change link failed");
|
||||
}
|
||||
|
||||
phycfg_reg_t phycfg;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG, "read PHYCFG failed");
|
||||
if (phycfg.duplex) { // Full duplex
|
||||
if (speed == ETH_SPEED_100M) {
|
||||
phycfg.opmode = W5500_OP_MODE_100BT_FULL_AUTO_DIS;
|
||||
} else {
|
||||
phycfg.opmode = W5500_OP_MODE_10BT_FULL_AUTO_DIS;
|
||||
}
|
||||
} else {
|
||||
if (speed == ETH_SPEED_100M) {
|
||||
phycfg.opmode = W5500_OP_MODE_100BT_HALF_AUTO_DIS;
|
||||
} else {
|
||||
phycfg.opmode = W5500_OP_MODE_10BT_HALF_AUTO_DIS;
|
||||
}
|
||||
}
|
||||
phycfg.opsel = 1; // PHY working mode configured by register
|
||||
phycfg.reset = 0; // PHY needs to be reset after configuring opsel and opmode
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(W5500_WAIT_FOR_RESET_MS));
|
||||
phycfg.reset = 1; // reset flag needs to be put back to 1
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t w5500_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
|
||||
esp_eth_mediator_t *eth = w5500->eth;
|
||||
|
||||
if (w5500->link_status == ETH_LINK_UP) {
|
||||
/* Since the link is going to be reconfigured, consider it down for a while */
|
||||
w5500->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 *)w5500->link_status), err, TAG, "change link failed");
|
||||
}
|
||||
|
||||
phycfg_reg_t phycfg;
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)), err, TAG, "read PHYCFG failed");
|
||||
if (phycfg.speed) { // 100Mbps
|
||||
if (duplex == ETH_DUPLEX_FULL) {
|
||||
phycfg.opmode = W5500_OP_MODE_100BT_FULL_AUTO_DIS;
|
||||
} else {
|
||||
phycfg.opmode = W5500_OP_MODE_100BT_HALF_AUTO_DIS;
|
||||
}
|
||||
} else {
|
||||
if (duplex == ETH_DUPLEX_FULL) {
|
||||
phycfg.opmode = W5500_OP_MODE_10BT_FULL_AUTO_DIS;
|
||||
} else {
|
||||
phycfg.opmode = W5500_OP_MODE_10BT_HALF_AUTO_DIS;
|
||||
}
|
||||
}
|
||||
phycfg.opsel = 1; // PHY working mode configured by register
|
||||
phycfg.reset = 0; // PHY needs to be reset after configuring opsel and opmode
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
vTaskDelay(pdMS_TO_TICKS(W5500_WAIT_FOR_RESET_MS));
|
||||
phycfg.reset = 1; // reset flag needs to be put back to 1
|
||||
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val), err, TAG, "write PHYCFG failed");
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t w5500_init(esp_eth_phy_t *phy)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -241,13 +368,15 @@ esp_eth_phy_t *esp_eth_phy_new_w5500(const eth_phy_config_t *config)
|
||||
w5500->parent.init = w5500_init;
|
||||
w5500->parent.deinit = w5500_deinit;
|
||||
w5500->parent.set_mediator = w5500_set_mediator;
|
||||
w5500->parent.negotiate = w5500_negotiate;
|
||||
w5500->parent.autonego_ctrl = w5500_autonego_ctrl;
|
||||
w5500->parent.get_link = w5500_get_link;
|
||||
w5500->parent.pwrctl = w5500_pwrctl;
|
||||
w5500->parent.get_addr = w5500_get_addr;
|
||||
w5500->parent.set_addr = w5500_set_addr;
|
||||
w5500->parent.advertise_pause_ability = w5500_advertise_pause_ability;
|
||||
w5500->parent.loopback = w5500_loopback;
|
||||
w5500->parent.set_speed = w5500_set_speed;
|
||||
w5500->parent.set_duplex = w5500_set_duplex;
|
||||
w5500->parent.del = w5500_del;
|
||||
return &(w5500->parent);
|
||||
err:
|
||||
|
@@ -123,6 +123,186 @@ TEST_CASE("esp32 ethernet io test", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
TEST_ESP_OK(mac->del(mac));
|
||||
}
|
||||
|
||||
TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
EventGroupHandle_t eth_event_group = xEventGroupCreate();
|
||||
TEST_ASSERT(eth_event_group != NULL);
|
||||
TEST_ESP_OK(esp_event_loop_create_default());
|
||||
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group));
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
// auto detect PHY address
|
||||
phy_config.phy_addr = ESP_ETH_PHY_ADDR_AUTO;
|
||||
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
|
||||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
|
||||
esp_eth_handle_t eth_handle = NULL;
|
||||
TEST_ESP_OK(esp_eth_driver_install(ð_config, ð_handle));
|
||||
|
||||
// Set PHY to loopback mode so we do not have to take care about link configuration of the other node.
|
||||
// The reason behind is improbable, however, if the other node was configured to e.g. 100 Mbps and we
|
||||
// tried to change the speed at ESP node to 10 Mbps, we could get into trouble to establish a link.
|
||||
bool loopback_en = true;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
|
||||
|
||||
// this test only test layer2, so don't need to register input callback (i.e. esp_eth_update_input_path)
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
// wait for connection start
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
|
||||
// wait for connection establish
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
|
||||
eth_duplex_t exp_duplex;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex);
|
||||
|
||||
eth_speed_t exp_speed;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed);
|
||||
// verify autonegotiation result (expecting the best link configuration)
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
bool exp_autoneg_en;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(true, exp_autoneg_en);
|
||||
|
||||
ESP_LOGI(TAG, "try to change autonegotiation when driver is started...");
|
||||
bool auto_nego_en = false;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(true, exp_autoneg_en);
|
||||
|
||||
ESP_LOGI(TAG, "stop the Ethernet driver and...");
|
||||
esp_eth_stop(eth_handle);
|
||||
|
||||
ESP_LOGI(TAG, "try to change speed/duplex prior disabling the autonegotiation...");
|
||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
eth_speed_t speed = ETH_SPEED_10M;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// Disable autonegotiation and change speed to 10 Mbps and duplex to half
|
||||
ESP_LOGI(TAG, "disable the autonegotiation and change the speed/duplex...");
|
||||
auto_nego_en = false;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(false, exp_autoneg_en);
|
||||
|
||||
// set new duplex mode
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
// set new speed
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_HALF, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_10M, exp_speed);
|
||||
|
||||
// Change speed back to 100 Mbps
|
||||
esp_eth_stop(eth_handle);
|
||||
ESP_LOGI(TAG, "change speed again...");
|
||||
speed = ETH_SPEED_100M;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_HALF, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
// Change duplex back to full
|
||||
esp_eth_stop(eth_handle);
|
||||
ESP_LOGI(TAG, "change duplex again...");
|
||||
duplex = ETH_DUPLEX_FULL;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
ESP_LOGI(TAG, "try to change speed/duplex when driver is started and autonegotiation disabled...");
|
||||
speed = ETH_SPEED_10M;
|
||||
duplex = ETH_DUPLEX_HALF;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
ESP_LOGI(TAG, "change the speed/duplex to 10 Mbps/half and then enable autonegotiation...");
|
||||
esp_eth_stop(eth_handle);
|
||||
speed = ETH_SPEED_10M;
|
||||
duplex = ETH_DUPLEX_HALF;
|
||||
|
||||
// set new duplex mode
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
// set new speed
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_HALF, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_10M, exp_speed);
|
||||
|
||||
esp_eth_stop(eth_handle);
|
||||
auto_nego_en = true;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en);
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(true, exp_autoneg_en);
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
// verify autonegotiation result (expecting the best link configuration)
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
// stop Ethernet driver
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
/* wait for connection stop */
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
|
||||
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
|
||||
TEST_ESP_OK(phy->del(phy));
|
||||
TEST_ESP_OK(mac->del(mac));
|
||||
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
|
||||
TEST_ESP_OK(esp_event_loop_delete_default());
|
||||
vEventGroupDelete(eth_event_group);
|
||||
}
|
||||
|
||||
TEST_CASE("esp32 ethernet event test", "[ethernet][test_env=UT_T2_Ethernet]")
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
|
Reference in New Issue
Block a user