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:
Ondrej Kosta 2021-11-04 09:10:19 +01:00
parent 4a011f3183
commit d1f2a3dfcc
19 changed files with 1284 additions and 414 deletions

View File

@ -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.
*

View File

@ -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;

View File

@ -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
*

View File

@ -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, &eth_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(&eth_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, &eth_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, &eth_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, &eth_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");
}
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(&eth_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, &eth_driver->auto_nego_en), err, TAG, "phy negotiation enable failed");
} else {
ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_DIS, &eth_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(&eth_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_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");
} 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");
}
break;
case ETH_CMD_S_FLOW_CTRL:
if ((uint32_t)data >= SOC_DRAM_LOW) {
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");
} 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");
}
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(&eth_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_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");
} 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");
}
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown io command: %d", cmd);

View File

@ -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;
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;
/* 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 */
};
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;
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) {
if (bmsr.auto_nego_complete) {
break;
}
}
if ((to >= dm9051->autonego_timeout_ms / 100) && (dm9051->link_status == ETH_LINK_UP)) {
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
ESP_LOGW(TAG, "auto negotiation timeout");
}
break;
case ESP_ETH_PHY_AUTONEGO_DIS:
if (bmcr.en_auto_nego == 1) {
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_EN:
if (bmcr.en_auto_nego == 0) {
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dm9051->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_G_STAT:
/* do nothing autonego_en_stat is set at the function end */
break;
default:
return ESP_ERR_INVALID_ARG;
}
*autonego_en_stat = bmcr.en_auto_nego;
return ESP_OK;
err:
return ret;
@ -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:

View File

@ -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;
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;
/* 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 */
};
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;
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) {
if (bmsr.auto_nego_complete) {
break;
}
}
if ((to >= dp83848->autonego_timeout_ms / 100) && (dp83848->link_status == ETH_LINK_UP)) {
ESP_LOGW(TAG, "auto negotiation timeout");
}
break;
case ESP_ETH_PHY_AUTONEGO_DIS:
if (bmcr.en_auto_nego == 1) {
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_EN:
if (bmcr.en_auto_nego == 0) {
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_G_STAT:
/* do nothing autonego_en_stat is set at the function end */
break;
default:
return ESP_ERR_INVALID_ARG;
}
*autonego_en_stat = bmcr.en_auto_nego;
return ESP_OK;
err:
return ret;
@ -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:

View File

@ -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,20 +227,23 @@ 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;
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;
/* 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 */
};
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;
@ -263,6 +258,33 @@ static esp_err_t ip101_negotiate(esp_eth_phy_t *phy)
if ((to >= ip101->autonego_timeout_ms / 100) && (ip101->link_status == ETH_LINK_UP)) {
ESP_LOGW(TAG, "auto negotiation timeout");
}
break;
case ESP_ETH_PHY_AUTONEGO_DIS:
if (bmcr.en_auto_nego == 1) {
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_EN:
if (bmcr.en_auto_nego == 0) {
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_G_STAT:
/* do nothing autonego_en_stat is set at the function end */
break;
default:
return ESP_ERR_INVALID_ARG;
}
*autonego_en_stat = bmcr.en_auto_nego;
return ESP_OK;
err:
return ret;
@ -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);

View File

@ -3,7 +3,6 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
@ -234,20 +233,23 @@ 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;
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;
/* 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 */
};
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;
@ -262,6 +264,33 @@ static esp_err_t ksz80xx_negotiate(esp_eth_phy_t *phy)
if ((to >= ksz80xx->autonego_timeout_ms / 100) && (ksz80xx->link_status == ETH_LINK_UP)) {
ESP_LOGW(TAG, "auto negotiation timeout");
}
break;
case ESP_ETH_PHY_AUTONEGO_DIS:
if (bmcr.en_auto_nego == 1) {
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_EN:
if (bmcr.en_auto_nego == 0) {
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz80xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_G_STAT:
/* do nothing autonego_en_stat is set at the function end */
break;
default:
return ESP_ERR_INVALID_ARG;
}
*autonego_en_stat = bmcr.en_auto_nego;
return ESP_OK;
err:
return ret;
@ -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:

View File

@ -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,16 +152,21 @@ 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;
uint32_t control;
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
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;
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;
@ -193,9 +185,35 @@ static esp_err_t phy_ksz8851_negotiate(esp_eth_phy_t *phy)
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;
}
*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:

View File

@ -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;
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;
/* 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 */
};
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;
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) {
if (bmsr.auto_nego_complete) {
break;
}
}
/* 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)) {
if ((to >= lan87xx->autonego_timeout_ms / 100) && (lan87xx->link_status == ETH_LINK_UP)) {
ESP_LOGW(TAG, "auto negotiation timeout");
}
break;
case ESP_ETH_PHY_AUTONEGO_DIS:
if (bmcr.en_auto_nego == 1) {
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_EN:
if (bmcr.en_auto_nego == 0) {
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, lan87xx->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_G_STAT:
/* do nothing autonego_en_stat is set at the function end */
break;
default:
return ESP_ERR_INVALID_ARG;
}
*autonego_en_stat = bmcr.en_auto_nego;
return ESP_OK;
err:
return ret;
@ -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;

View File

@ -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,20 +180,23 @@ 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;
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;
/* 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 */
};
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;
@ -216,6 +211,33 @@ static esp_err_t rtl8201_negotiate(esp_eth_phy_t *phy)
if ((to >= rtl8201->autonego_timeout_ms / 100) && (rtl8201->link_status == ETH_LINK_UP)) {
ESP_LOGW(TAG, "auto negotiation timeout");
}
break;
case ESP_ETH_PHY_AUTONEGO_DIS:
if (bmcr.en_auto_nego == 1) {
bmcr.en_auto_nego = 0; /* Disable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 0, ESP_FAIL, err, TAG, "disable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_EN:
if (bmcr.en_auto_nego == 0) {
bmcr.en_auto_nego = 1; /* Enable Auto Negotiation */
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val), err, TAG, "write BMCR failed");
/* read configuration back */
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, rtl8201->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed");
ESP_GOTO_ON_FALSE(bmcr.en_auto_nego == 1, ESP_FAIL, err, TAG, "enable auto-negotiation failed");
}
break;
case ESP_ETH_PHY_AUTONEGO_G_STAT:
/* do nothing autonego_en_stat is set at the function end */
break;
default:
return ESP_ERR_INVALID_ARG;
}
*autonego_en_stat = bmcr.en_auto_nego;
return ESP_OK;
err:
return ret;
@ -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);

View File

@ -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
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:

View File

@ -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, &eth_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(&eth_config, &eth_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;

View File

@ -428,7 +428,16 @@ Ethernet on MCU usually has a limitation in the number of frames it can handle d
Pause frame is a special Ethernet frame used to carry the pause command, whose EtherType field is 0x8808, with the Control opcode set to 0x0001. Only stations configured for full-duplex operation may send pause frames. When a station wishes to pause the other end of a link, it sends a pause frame to the 48-bit reserved multicast address of 01-80-C2-00-00-01. The pause frame also includes the period of pause time being requested, in the form of a two-byte integer, ranging from 0 to 65535.
After Ethernet driver installation, the flow control feature is disabled by default. You can enable it by invoking `esp_eth_ioctl(eth_handle, ETH_CMD_S_FLOW_CTRL, true);`. One thing should be kept in mind, is that the pause frame ability will be advertised to peer end by PHY during auto negotiation. Ethernet driver sends pause frame only when both sides of the link support it.
After Ethernet driver installation, the flow control feature is disabled by default. You can enable it by:
.. highlight:: c
::
bool flow_ctrl_enable = true;
esp_eth_ioctl(eth_handle, ETH_CMD_S_FLOW_CTRL, &flow_ctrl_enable);
One thing should be kept in mind, is that the pause frame ability will be advertised to peer end by PHY during auto negotiation. Ethernet driver sends pause frame only when both sides of the link support it.
.. -------------------------------- Examples -----------------------------------

View File

@ -14,26 +14,26 @@ If you have a more complicated application to go (for example, connect to some I
### Hardware Required
To run this example, you need to prepare following hardwares:
* [ESP32 board](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html) (e.g. ESP32-PICO, ESP32 DevKitC, etc)
* ENC28J60 module (the latest revision should be 6)
* [ESP32 dev board](https://www.espressif.com/en/products/devkits?id=ESP32) (e.g. ESP32-PICO, ESP32 DevKitC, etc)
* ENC28J60 Ethernet module (the latest revision should be 6)
* **!! IMPORTANT !!** Proper input power source since ENC28J60 is quite power consuming device (it consumes more than 200 mA in peaks when transmitting). If improper power source is used, input voltage may drop and ENC28J60 may either provide nonsense response to host controller via SPI (fail to read registers properly) or it may enter to some strange state in the worst case. There are several options how to resolve it:
* Power ESP32 board from `USB 3.0`, if board is used as source of power to ENC board.
* Power ESP32 board from external 5V power supply with current limit at least 1 A, if board is used as source of power to ENC board.
* Power ENC28J60 from external 3.3V power supply with common GND to ESP32 board. Note that there might be some ENC28J60 boards with integrated voltage regulator on market and so powered by 5 V. Please consult documentation of your board for details.
* Power ESP32 dev board from `USB 3.0`, if the dev board is used as source of power to the ENC28J60 module.
* Power ESP32 dev board from external 5 V power supply with current limit at least 1 A, if the dev board is used as source of power to the ENC28J60 module.
* Power ENC28J60 from external 3.3 V power supply with common GND to ESP32 dev board. Note that there might be some ENC28J60 modules with integrated voltage regulator on market and so powered by 5 V. Please consult documentation of your board for details.
If a ESP32 board is used as source of power to ENC board, ensure that that particular board is assembled with voltage regulator capable to deliver current up to 1 A. This is a case of ESP32 DevKitC or ESP-WROVER-KIT, for example. Such setup was tested and works as expected. Other boards may use different voltage regulators and may perform differently.
**WARNING:** Always consult documentation/schematics associated with particular ENC28J60 and ESP32 boards used in your use-case first.
If an ESP32 dev board is used as the source of power to the ENC28J60 module, ensure that that the particular dev board is assembled with a voltage regulator capable to deliver current of 1 A. This is a case of ESP32-DevKitC or ESP-WROVER-KIT, for example. Such setup was tested and works as expected. Other dev boards may use different voltage regulators and may perform differently.
**WARNING:** Always consult documentation/schematics associated with particular ENC28J60 and ESP32 dev boards used in your use-case first.
#### Pin Assignment
* ENC28J60 Ethernet module consumes one SPI interface plus an interrupt GPIO. By default they're connected as follows:
* ENC28J60 Ethernet module consumes one SPI interface plus an interrupt GPIO. By default they're connected as follows, in case of ESP32 dev boards:
| GPIO | ENC28J60 |
| ------ | ----------- |
| GPIO19 | SPI_CLK |
| GPIO23 | SPI_MOSI |
| GPIO25 | SPI_MISO |
| GPIO22 | SPI_CS |
| ESP32 GPIO | ENC28J60 |
| ---------- | ----------- |
| GPIO14 | SPI_CLK |
| GPIO13 | SPI_MOSI |
| GPIO12 | SPI_MISO |
| GPIO15 | SPI_CS |
| GPIO4 | Interrupt |
### Configure the project
@ -92,7 +92,7 @@ Now you can ping your ESP32 in the terminal by entering `ping 192.168.2.34` (it
sudo ethtool -s YOUR_INTERFACE_NAME speed 10 duplex full autoneg off
```
* On Windows, go to `Network Connections` -> `Change adapter options` -> open `Properties` of selected network card -> `Configure` -> `Advanced` -> `Link Speed & Duplex` -> select `10 Mbps Full Duplex in dropdown menu`.
3. Ensure that your wiring between ESP32 board and the ENC28J60 board is realized by short wires with the same length and no wire crossings.
3. Ensure that your wiring between ESP32 dev board and the ENC28J60 module is realized by short wires with the same length and no wire crossings.
4. CS Hold Time needs to be configured to be at least 210 ns to properly read MAC and MII registers as defined by ENC28J60 Data Sheet. This is automatically configured in the example based on selected SPI clock frequency by computing amount of SPI bit-cycles the CS should stay active after the transmission. However, if your PCB design/wiring requires different value, please update `cs_ena_posttrans` member of `devcfg` structure per your actual needs.

View File

@ -1,16 +1,8 @@
// Copyright 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: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@ -98,19 +90,6 @@ esp_eth_mac_t *esp_eth_mac_new_enc28j60(const eth_enc28j60_config_t *enc28j60_co
*/
esp_eth_phy_t *esp_eth_phy_new_enc28j60(const eth_phy_config_t *config);
// todo: the below functions should be accessed through ioctl in the future
/**
* @brief Set ENC28J60 Duplex mode. It sets Duplex mode first to the PHY and then
* MAC is set based on what PHY indicates.
*
* @param phy ENC28J60 PHY Handle
* @param duplex Duplex mode
*
* @return esp_err_t
* - ESP_OK when PHY registers were correctly written.
*/
esp_err_t enc28j60_set_phy_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex);
/**
* @brief Get ENC28J60 silicon revision ID
*

View File

@ -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>
@ -190,7 +182,7 @@ static esp_err_t enc28j60_reset_hw(esp_eth_phy_t *phy)
return ESP_OK;
}
static esp_err_t enc28j60_negotiate(esp_eth_phy_t *phy)
static esp_err_t enc28j60_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autoneg_en_stat)
{
/**
* ENC28J60 does not support automatic duplex negotiation.
@ -199,20 +191,46 @@ static esp_err_t enc28j60_negotiate(esp_eth_phy_t *phy)
* To communicate in Full-Duplex mode, ENC28J60 and the remote node
* must be manually configured for full-duplex operation.
*/
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
/* Updata information about link, speed, duplex */
PHY_CHECK(enc28j60_update_link_duplex_speed(enc28j60) == ESP_OK, "update link duplex speed failed", err);
switch (cmd)
{
case ESP_ETH_PHY_AUTONEGO_RESTART:
/* Fallthrough */
case ESP_ETH_PHY_AUTONEGO_EN:
return ESP_ERR_NOT_SUPPORTED;
case ESP_ETH_PHY_AUTONEGO_DIS:
/* Fallthrough */
case ESP_ETH_PHY_AUTONEGO_G_STAT:
*autoneg_en_stat = false;
break;
default:
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t enc28j60_set_phy_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
esp_err_t enc28j60_set_speed(esp_eth_phy_t *phy, eth_speed_t speed)
{
/* ENC28J60 supports only 10Mbps */
if (speed == ETH_SPEED_10M) {
return ESP_OK;
}
return ESP_ERR_NOT_SUPPORTED;
}
esp_err_t enc28j60_set_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
{
phy_enc28j60_t *enc28j60 = __containerof(phy, phy_enc28j60_t, parent);
esp_eth_mediator_t *eth = enc28j60->eth;
phcon1_reg_t phcon1;
if (enc28j60->link_status == ETH_LINK_UP) {
/* Since the link is going to be reconfigured, consider it down for a while */
enc28j60->link_status = ETH_LINK_DOWN;
/* Indicate to upper stream apps the link is cosidered down */
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)enc28j60->link_status), "change link failed", err);
}
PHY_CHECK(eth->phy_reg_read(eth, enc28j60->addr, 0, &phcon1.val) == ESP_OK,
"read PHCON1 failed", err);
switch (duplex) {
@ -230,7 +248,6 @@ esp_err_t enc28j60_set_phy_duplex(esp_eth_phy_t *phy, eth_duplex_t duplex)
PHY_CHECK(eth->phy_reg_write(eth, enc28j60->addr, 0, phcon1.val) == ESP_OK,
"write PHCON1 failed", err);
PHY_CHECK(enc28j60_update_link_duplex_speed(enc28j60) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
@ -340,11 +357,13 @@ esp_eth_phy_t *esp_eth_phy_new_enc28j60(const eth_phy_config_t *config)
enc28j60->parent.init = enc28j60_init;
enc28j60->parent.deinit = enc28j60_deinit;
enc28j60->parent.set_mediator = enc28j60_set_mediator;
enc28j60->parent.negotiate = enc28j60_negotiate;
enc28j60->parent.autonego_ctrl = enc28j60_autonego_ctrl;
enc28j60->parent.get_link = enc28j60_get_link;
enc28j60->parent.pwrctl = enc28j60_pwrctl;
enc28j60->parent.get_addr = enc28j60_get_addr;
enc28j60->parent.set_addr = enc28j60_set_addr;
enc28j60->parent.set_speed = enc28j60_set_speed;
enc28j60->parent.set_duplex = enc28j60_set_duplex;
enc28j60->parent.del = enc28j60_del;
return &(enc28j60->parent);
err:

View File

@ -132,12 +132,13 @@ void app_main(void)
// Register user defined event handers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
/* start Ethernet driver state machine */
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
/* It is recommended to use ENC28J60 in Full Duplex mode since multiple errata exist to the Half Duplex mode */
#if CONFIG_EXAMPLE_ENC28J60_DUPLEX_FULL
/* Set duplex needs to be called after esp_eth_start since the driver is started with auto-negotiation by default */
enc28j60_set_phy_duplex(phy, ETH_DUPLEX_FULL);
eth_duplex_t duplex = ETH_DUPLEX_FULL;
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
#endif
/* start Ethernet driver state machine */
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
}

View File

@ -584,23 +584,14 @@ components/esp_common/include/esp_err.h
components/esp_common/include/esp_types.h
components/esp_common/src/esp_err_to_name.c
components/esp_common/test/test_attr.c
components/esp_eth/include/esp_eth.h
components/esp_eth/include/esp_eth_com.h
components/esp_eth/include/esp_eth_mac.h
components/esp_eth/include/esp_eth_netif_glue.h
components/esp_eth/include/eth_phy_regs_struct.h
components/esp_eth/src/dm9051.h
components/esp_eth/src/esp_eth.c
components/esp_eth/src/esp_eth_mac_openeth.c
components/esp_eth/src/esp_eth_mac_w5500.c
components/esp_eth/src/esp_eth_netif_glue.c
components/esp_eth/src/esp_eth_phy.c
components/esp_eth/src/esp_eth_phy_dm9051.c
components/esp_eth/src/esp_eth_phy_dp83848.c
components/esp_eth/src/esp_eth_phy_ip101.c
components/esp_eth/src/esp_eth_phy_ksz8851snl.c
components/esp_eth/src/esp_eth_phy_rtl8201.c
components/esp_eth/src/esp_eth_phy_w5500.c
components/esp_eth/src/ksz8851.h
components/esp_eth/src/openeth.h
components/esp_eth/src/w5500.h
@ -663,6 +654,9 @@ components/esp_https_ota/src/esp_https_ota.c
components/esp_hw_support/include/esp_clk.h
components/esp_hw_support/include/soc/esp_himem.h
components/esp_hw_support/include/soc/esp_spiram.h
components/esp_ipc/include/esp_ipc.h
components/esp_ipc/test/test_ipc.c
components/esp_ipc/test/test_ipc_isr.c
components/esp_lcd/test/test_i2c_lcd_panel.c
components/esp_lcd/test/test_i80_board.h
components/esp_lcd/test/test_i80_lcd_panel.c
@ -3058,8 +3052,6 @@ examples/cxx/rtti/example_test.py
examples/cxx/rtti/main/rtti_example_main.cpp
examples/ethernet/basic/main/ethernet_example_main.c
examples/ethernet/enc28j60/components/eth_enc28j60/enc28j60.h
examples/ethernet/enc28j60/components/eth_enc28j60/esp_eth_enc28j60.h
examples/ethernet/enc28j60/components/eth_enc28j60/esp_eth_phy_enc28j60.c
examples/ethernet/enc28j60/main/enc28j60_example_main.c
examples/ethernet/eth2ap/main/ethernet_example_main.c
examples/ethernet/iperf/iperf_test.py