ethernet: Refactor PHY support to be part of ethernet component

Move generic PHY support into its own interface
This commit is contained in:
Angus Gratton 2017-04-19 12:02:53 +10:00 committed by Angus Gratton
parent 5d6e6f79b9
commit 453722ba54
15 changed files with 607 additions and 332 deletions

View file

@ -1,5 +1,5 @@
#
# Component Makefile
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
COMPONENT_SRCDIRS := . eth_phy

View file

@ -532,7 +532,7 @@ static void emac_set_macaddr_reg(void)
static void emac_check_phy_init(void)
{
emac_config.emac_phy_check_init();
if (emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) {
if (emac_config.emac_phy_get_duplex_mode() == ETH_MODE_FULLDUPLEX) {
REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX);
} else {
REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX);
@ -547,7 +547,7 @@ static void emac_check_phy_init(void)
emac_config.emac_flow_ctrl_partner_support = false;
#else
if (emac_config.emac_flow_ctrl_enable == true) {
if (emac_config.emac_phy_get_partner_pause_enable() == true && emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) {
if (emac_config.emac_phy_get_partner_pause_enable() == true && emac_config.emac_phy_get_duplex_mode() == ETH_MODE_FULLDUPLEX) {
emac_enable_flowctrl();
emac_config.emac_flow_ctrl_partner_support = true;
} else {
@ -954,7 +954,7 @@ esp_err_t esp_eth_init(eth_config_t *config)
goto _exit;
}
emac_config.emac_phy_power_enable(true);
emac_config.emac_phy_power_enable(true);
//before set emac reg must enable clk
emac_enable_clk(true);

View file

@ -0,0 +1,75 @@
// Copyright 2015-2016 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.
#include "eth_phy/phy.h"
#include "eth_phy/phy_reg.h"
#include "driver/gpio.h"
#include "esp_log.h"
static const char *TAG = "phy_common";
void phy_rmii_configure_data_interface_pins(void)
{
// CRS_DRV to GPIO27
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO27_U, FUNC_GPIO27_EMAC_RX_DV);
// TXD0 to GPIO19
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO19_U, FUNC_GPIO19_EMAC_TXD0);
// TX_EN to GPIO21
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO21_U, FUNC_GPIO21_EMAC_TX_EN);
// TXD1 to GPIO22
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO22_U, FUNC_GPIO22_EMAC_TXD1);
// RXD0 to GPIO25
gpio_set_direction(25, GPIO_MODE_INPUT);
// RXD1 to GPIO26
gpio_set_direction(26, GPIO_MODE_INPUT);
// RMII CLK to GPIO0
gpio_set_direction(0, GPIO_MODE_INPUT);
}
void phy_rmii_smi_configure_pins(uint8_t mdc_gpio, uint8_t mdio_gpio)
{
gpio_matrix_out(mdc_gpio, EMAC_MDC_O_IDX, 0, 0);
gpio_matrix_out(mdio_gpio, EMAC_MDO_O_IDX, 0, 0);
gpio_matrix_in(mdio_gpio, EMAC_MDI_I_IDX, 0);
}
void phy_mii_enable_flow_ctrl(void)
{
uint32_t data = esp_eth_smi_read(MII_AUTO_NEG_ADVERTISEMENT_REG);
data |= MII_ASM_DIR | MII_PAUSE;
esp_eth_smi_write(MII_AUTO_NEG_ADVERTISEMENT_REG, data);
}
bool phy_mii_check_link_status(void)
{
if ((esp_eth_smi_read(MII_BASIC_MODE_STATUS_REG) & MII_LINK_STATUS)) {
ESP_LOGD(TAG, "phy_mii_check_link_status(UP)");
return true;
} else {
ESP_LOGD(TAG, "phy_mii_check_link_status(DOWN)");
return false;
}
}
bool phy_mii_get_partner_pause_enable(void)
{
if((esp_eth_smi_read(MII_PHY_LINK_PARTNER_ABILITY_REG) & MII_PARTNER_PAUSE)) {
ESP_LOGD(TAG, "phy_mii_get_partner_pause_enable(TRUE)");
return true;
} else {
ESP_LOGD(TAG, "phy_mii_get_partner_pause_enable(FALSE)");
return false;
}
}

View file

@ -0,0 +1,157 @@
// Copyright 2015-2017 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.
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "eth_phy/phy_lan8720.h"
#include "eth_phy/phy_reg.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/* Value of MII_PHY_IDENTIFIER_REGs for Microchip LAN8720
* (Except for bottom 4 bits of ID2, used for model revision)
*/
#define LAN8720_PHY_ID1 0x0007
#define LAN8720_PHY_ID2 0xc0f0
/* LAN8720-specific registers */
#define SW_STRAP_CONTROL_REG (0x9)
#define SW_STRAP_CONFIG_DONE BIT(15)
#define AUTO_MDIX_ENABLE BIT(14)
#define AUTO_NEGOTIATION_ENABLE BIT(13)
#define AN_1 BIT(12)
#define AN_0 BIT(11)
#define LED_CFG BIT(10)
#define RMII_ENHANCED_MODE BIT(9)
#define DEFAULT_STRAP_CONFIG (AUTO_MDIX_ENABLE|AUTO_NEGOTIATION_ENABLE|AN_1|AN_0|LED_CFG)
#define PHY_SPECIAL_CONTROL_STATUS_REG (0x1f)
#define AUTO_NEGOTIATION_DONE BIT(12)
#define SPEED_DUPLEX_INDICATION_10T_HALF 0x04
#define SPEED_DUPLEX_INDICATION_10T_FULL 0x14
#define SPEED_DUPLEX_INDICATION_100T_HALF 0x08
#define SPEED_DUPLEX_INDICATION_100T_FULL 0x18
#define SPEED_INDICATION_100T BIT(3)
#define SPEED_INDICATION_10T BIT(2)
#define DUPLEX_INDICATION_FULL BIT(4)
static const char *TAG = "lan8720";
void phy_lan8720_check_phy_init(void)
{
phy_lan8720_dump_registers();
while((esp_eth_smi_read(MII_BASIC_MODE_STATUS_REG) & MII_AUTO_NEGOTIATION_COMPLETE ) == 0) {
vTaskDelay(1);
}
while((esp_eth_smi_read(PHY_SPECIAL_CONTROL_STATUS_REG) & AUTO_NEGOTIATION_DONE ) == 0) {
vTaskDelay(1);
}
}
eth_speed_mode_t phy_lan8720_get_speed_mode(void)
{
if(esp_eth_smi_read(PHY_SPECIAL_CONTROL_STATUS_REG) & SPEED_INDICATION_100T) {
ESP_LOGD(TAG, "phy_lan8720_get_speed_mode(100)");
return ETH_SPEED_MODE_100M;
} else {
ESP_LOGD(TAG, "phy_lan8720_get_speed_mode(10)");
return ETH_SPEED_MODE_10M;
}
}
eth_duplex_mode_t phy_lan8720_get_duplex_mode(void)
{
if(esp_eth_smi_read(PHY_SPECIAL_CONTROL_STATUS_REG) & DUPLEX_INDICATION_FULL) {
ESP_LOGD(TAG, "phy_lan8720_get_duplex_mode(FULL)");
return ETH_MODE_FULLDUPLEX;
} else {
ESP_LOGD(TAG, "phy_lan8720_get_duplex_mode(HALF)");
return ETH_MODE_HALFDUPLEX;
}
}
void phy_lan8720_power_enable(bool enable)
{
if (enable) {
esp_eth_smi_write(SW_STRAP_CONTROL_REG, DEFAULT_STRAP_CONFIG | SW_STRAP_CONFIG_DONE);
// TODO: only enable if config.flow_ctrl_enable == true
phy_mii_enable_flow_ctrl();
}
}
void phy_lan8720_init(void)
{
ESP_LOGD(TAG, "phy_lan8720_init()");
phy_lan8720_dump_registers();
esp_eth_smi_write(MII_BASIC_MODE_CONTROL_REG, MII_SOFTWARE_RESET);
unsigned phy_id1, phy_id2;
do {
vTaskDelay(1);
phy_id1 = esp_eth_smi_read(MII_PHY_IDENTIFIER_1_REG);
phy_id2 = esp_eth_smi_read(MII_PHY_IDENTIFIER_2_REG);
ESP_LOGD(TAG, "PHY ID 0x%04x 0x%04x", phy_id1, phy_id2);
phy_id2 &= 0xFFF0; // Remove model revision code
} while (phy_id1 != LAN8720_PHY_ID1 && phy_id2 != LAN8720_PHY_ID2);
esp_eth_smi_write(SW_STRAP_CONTROL_REG,
DEFAULT_STRAP_CONFIG | SW_STRAP_CONFIG_DONE);
ets_delay_us(300);
// TODO: only enable if config.flow_ctrl_enable == true
phy_mii_enable_flow_ctrl();
}
const eth_config_t phy_lan8720_default_ethernet_config = {
// By default, the PHY address is 0 or 1 based on PHYAD0
// pin. Can also be overriden in software. See datasheet
// for defaults.
.phy_addr = 0,
.mac_mode = ETH_MODE_RMII,
//Only FULLDUPLEX mode support flow ctrl now!
.flow_ctrl_enable = true,
.phy_init = phy_lan8720_init,
.phy_check_init = phy_lan8720_check_phy_init,
.phy_power_enable = phy_lan8720_power_enable,
.phy_check_link = phy_mii_check_link_status,
.phy_get_speed_mode = phy_lan8720_get_speed_mode,
.phy_get_duplex_mode = phy_lan8720_get_duplex_mode,
.phy_get_partner_pause_enable = phy_mii_get_partner_pause_enable,
};
void phy_lan8720_dump_registers()
{
ESP_LOGD(TAG, "LAN8720 Registers:");
ESP_LOGD(TAG, "BCR 0x%04x", esp_eth_smi_read(0x0));
ESP_LOGD(TAG, "BSR 0x%04x", esp_eth_smi_read(0x1));
ESP_LOGD(TAG, "PHY1 0x%04x", esp_eth_smi_read(0x2));
ESP_LOGD(TAG, "PHY2 0x%04x", esp_eth_smi_read(0x3));
ESP_LOGD(TAG, "ANAR 0x%04x", esp_eth_smi_read(0x4));
ESP_LOGD(TAG, "ANLPAR 0x%04x", esp_eth_smi_read(0x5));
ESP_LOGD(TAG, "ANER 0x%04x", esp_eth_smi_read(0x6));
ESP_LOGD(TAG, "MCSR 0x%04x", esp_eth_smi_read(0x17));
ESP_LOGD(TAG, "SM 0x%04x", esp_eth_smi_read(0x18));
ESP_LOGD(TAG, "SECR 0x%04x", esp_eth_smi_read(0x26));
ESP_LOGD(TAG, "CSIR 0x%04x", esp_eth_smi_read(0x27));
ESP_LOGD(TAG, "ISR 0x%04x", esp_eth_smi_read(0x29));
ESP_LOGD(TAG, "IMR 0x%04x", esp_eth_smi_read(0x30));
ESP_LOGD(TAG, "PSCSR 0x%04x", esp_eth_smi_read(0x31));
}

View file

@ -1,26 +1,70 @@
// Copyright 2015-2017 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.
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "tlk110_phy.h"
#include "eth_phy/phy_tlk110.h"
#include "eth_phy/phy_reg.h"
#define DEFAULT_PHY_CONFIG (AUTO_MDIX_ENABLE|AUTO_NEGOTIATION_ENABLE|AN_1|AN_0|LED_CFG)
void phy_dump_tlk110_registers();
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/* Value of MII_PHY_IDENTIFIER_REG for TI TLK110,
Excluding bottom 4 bytes of ID2, used for model revision
*/
#define TLK110_PHY_ID1 0x2000
#define TLK110_PHY_ID2 0xa210
/* TLK110-specific registers */
#define SW_STRAP_CONTROL_REG (0x9)
#define SW_STRAP_CONFIG_DONE BIT(15)
#define AUTO_MDIX_ENABLE BIT(14)
#define AUTO_NEGOTIATION_ENABLE BIT(13)
#define AN_1 BIT(12)
#define AN_0 BIT(11)
#define LED_CFG BIT(10)
#define RMII_ENHANCED_MODE BIT(9)
#define DEFAULT_STRAP_CONFIG (AUTO_MDIX_ENABLE|AUTO_NEGOTIATION_ENABLE|AN_1|AN_0|LED_CFG)
#define PHY_STATUS_REG (0x10)
#define AUTO_NEGOTIATION_STATUS BIT(4)
#define DUPLEX_STATUS BIT(2)
#define SPEED_STATUS BIT(1)
#define CABLE_DIAGNOSTIC_CONTROL_REG (0x1e)
#define DIAGNOSTIC_DONE BIT(1)
#define PHY_RESET_CONTROL_REG (0x1f)
#define SOFTWARE_RESET BIT(15)
static const char *TAG = "tlk110";
void phy_tlk110_check_phy_init(void)
{
phy_dump_tlk110_registers();
phy_tlk110_dump_registers();
while((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & AUTO_NEGOTIATION_COMPLETE ) != AUTO_NEGOTIATION_COMPLETE)
{};
while((esp_eth_smi_read(PHY_STATUS_REG) & AUTO_NEGTIATION_STATUS ) != AUTO_NEGTIATION_STATUS)
{};
while((esp_eth_smi_read(CABLE_DIAGNOSTIC_CONTROL_REG) & DIAGNOSTIC_DONE ) != DIAGNOSTIC_DONE)
{};
while((esp_eth_smi_read(MII_BASIC_MODE_STATUS_REG) & MII_AUTO_NEGOTIATION_COMPLETE ) == 0) {
vTaskDelay(1);
}
while((esp_eth_smi_read(PHY_STATUS_REG) & AUTO_NEGOTIATION_STATUS ) == 0) {
vTaskDelay(1);
}
while((esp_eth_smi_read(CABLE_DIAGNOSTIC_CONTROL_REG) & DIAGNOSTIC_DONE ) == 0) {
vTaskDelay(1);
}
}
eth_speed_mode_t phy_tlk110_get_speed_mode(void)
@ -38,74 +82,65 @@ eth_duplex_mode_t phy_tlk110_get_duplex_mode(void)
{
if((esp_eth_smi_read(PHY_STATUS_REG) & DUPLEX_STATUS ) == DUPLEX_STATUS) {
ESP_LOGD(TAG, "phy_tlk110_get_duplex_mode(FULL)");
return ETH_MDOE_FULLDUPLEX;
return ETH_MODE_FULLDUPLEX;
} else {
ESP_LOGD(TAG, "phy_tlk110_get_duplex_mode(HALF)");
return ETH_MODE_HALFDUPLEX;
}
}
bool phy_tlk110_check_phy_link_status(void)
void phy_tlk110_power_enable(bool enable)
{
if ((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & LINK_STATUS) == LINK_STATUS) {
ESP_LOGD(TAG, "phy_tlk110_check_phy_link_status(UP)");
return true;
} else {
ESP_LOGD(TAG, "phy_tlk110_check_phy_link_status(DOWN)");
return false;
}
}
if (enable) {
esp_eth_smi_write(SW_STRAP_CONTROL_REG, DEFAULT_STRAP_CONFIG | SW_STRAP_CONFIG_DONE);
bool phy_tlk110_get_partner_pause_enable(void)
{
if((esp_eth_smi_read(PHY_LINK_PARTNER_ABILITY_REG) & PARTNER_PAUSE) == PARTNER_PAUSE) {
ESP_LOGD(TAG, "phy_tlk110_get_partner_pause_enable(TRUE)");
return true;
} else {
ESP_LOGD(TAG, "phy_tlk110_get_partner_pause_enable(FALSE)");
return false;
// TODO: only do this if config.flow_ctrl_enable == true
phy_mii_enable_flow_ctrl();
}
}
void phy_enable_flow_ctrl(void)
{
uint32_t data = 0;
data = esp_eth_smi_read(AUTO_NEG_ADVERTISEMENT_REG);
esp_eth_smi_write(AUTO_NEG_ADVERTISEMENT_REG,data|ASM_DIR|PAUSE);
}
void phy_tlk110_init(void)
{
ESP_LOGD(TAG, "phy_tlk110_init()");
phy_dump_tlk110_registers();
phy_tlk110_dump_registers();
esp_eth_smi_write(PHY_RESET_CONTROL_REG, SOFTWARE_RESET);
while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) {
}
unsigned phy_id1, phy_id2;
do {
vTaskDelay(1);
phy_id1 = esp_eth_smi_read(MII_PHY_IDENTIFIER_1_REG);
phy_id2 = esp_eth_smi_read(MII_PHY_IDENTIFIER_2_REG);
ESP_LOGD(TAG, "PHY ID 0x%04x 0x%04x", phy_id1, phy_id2);
phy_id2 &= 0xFFF0; // Remove model revision code
} while (phy_id1 != TLK110_PHY_ID1 && phy_id2 != TLK110_PHY_ID2);
esp_eth_smi_write(SOFTWARE_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE);
esp_eth_smi_write(SW_STRAP_CONTROL_REG,
DEFAULT_STRAP_CONFIG | SW_STRAP_CONFIG_DONE);
ets_delay_us(300);
//if config.flow_ctrl_enable == true ,enable this
phy_enable_flow_ctrl();
// TODO: only do this if config.flow_ctrl_enable == true
phy_mii_enable_flow_ctrl();
}
const eth_config_t tlk110_default_ethernet_phy_config = {
.phy_addr = CONFIG_PHY_ID,
const eth_config_t phy_tlk110_default_ethernet_config = {
// PHY address configured by PHYADx pins. Default value of 0x1
// is used if all pins are unconnected.
.phy_addr = 0x1,
.mac_mode = ETH_MODE_RMII,
//Only FULLDUPLEX mode support flow ctrl now!
.flow_ctrl_enable = true,
.phy_init = phy_tlk110_init,
.phy_check_init = phy_tlk110_check_phy_init,
.phy_check_link = phy_tlk110_check_phy_link_status,
.phy_check_link = phy_mii_check_link_status,
.phy_get_speed_mode = phy_tlk110_get_speed_mode,
.phy_get_duplex_mode = phy_tlk110_get_duplex_mode,
.phy_get_partner_pause_enable = phy_tlk110_get_partner_pause_enable
.phy_get_partner_pause_enable = phy_mii_get_partner_pause_enable,
.phy_power_enable = phy_tlk110_power_enable,
};
void phy_dump_tlk110_registers()
void phy_tlk110_dump_registers()
{
ESP_LOGD(TAG, "TLK110 Registers:");
ESP_LOGD(TAG, "BMCR 0x%04x", esp_eth_smi_read(0x0));

View file

@ -15,6 +15,7 @@
#ifndef __ESP_ETH_H__
#define __ESP_ETH_H__
#include <stdbool.h>
#include <stdint.h>
#include "esp_err.h"
@ -24,7 +25,7 @@ extern "C" {
typedef enum {
ETH_MODE_RMII = 0,
ETH_MDOE_MII,
ETH_MODE_MII,
} eth_mode_t;
typedef enum {
@ -34,7 +35,7 @@ typedef enum {
typedef enum {
ETH_MODE_HALFDUPLEX = 0,
ETH_MDOE_FULLDUPLEX,
ETH_MODE_FULLDUPLEX,
} eth_duplex_mode_t;
typedef enum {
@ -99,9 +100,10 @@ typedef struct {
bool flow_ctrl_enable; /*!< flag of flow ctrl enable */
eth_phy_get_partner_pause_enable_func phy_get_partner_pause_enable; /*!< get partner pause enable */
eth_phy_power_enable_func phy_power_enable; /*!< enable or disable phy power */
} eth_config_t;
/**
* @brief Init ethernet mac
*

View file

@ -0,0 +1,59 @@
// Copyright 2015-2016 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_eth.h"
/* Common PHY-management functions.
These are not enough to drive any particular Ethernet PHY, but they provide a common configuration structure and
management functions.
*/
/* Configure fixed pins for RMII data interface.
This configures GPIOs 0, 19, 22, 25, 26, 27 for use with RMII
data interface. These pins cannot be changed, and must be wired to
ethernet functions.
This is not sufficient to fully configure the Ethernet PHY,
MDIO configuration interface pins (such as SMI MDC, MDO, MDI)
must also be configured correctly in the GPIO matrix.
*/
void phy_rmii_configure_data_interface_pins(void);
/* Configure variable pins for SMI (MDIO) ethernet functions.
Calling this function along with mii_configure_default_pins() will
fully configure the GPIOs for the ethernet PHY.
*/
void phy_rmii_smi_configure_pins(uint8_t mdc_gpio, uint8_t mdio_gpio);
/* Enable flow control in standard PHY MII register.
*/
void phy_mii_enable_flow_ctrl(void);
bool phy_mii_check_link_status(void);
bool phy_mii_get_partner_pause_enable(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,67 @@
// Copyright 2015-2017 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "phy.h"
/* @brief Dump all LAN8720 PHY SMI configuration registers
*
* @note These registers are dumped at 'debug' level, so output
* may not be visible depending on default log levels.
*/
void phy_lan8720_dump_registers();
/* @brief Default LAN8720 phy_check_init function.
*/
void phy_lan8720_check_phy_init(void);
/* @brief Default LAN8720 phy_get_speed_mode function.
*/
eth_speed_mode_t phy_lan8720_get_speed_mode(void);
/* @brief Default LAN8720 phy_get_duplex_mode function.
*/
eth_duplex_mode_t phy_lan8720_get_duplex_mode(void);
/* @brief Default LAN8720 phy_power_enable function.
*
* @note This function may need to be replaced with a custom function
* if the PHY has a GPIO to enable power or start a clock.
*
* Consult the ethernet example to see how this is done.
*/
void phy_lan8720_power_enable(bool);
/* @brief Default LAN8720 phy_init function.
*/
void phy_lan8720_init(void);
/* @brief Default LAN8720 PHY configuration
*
* This configuration is not suitable for use as-is, it will need
* to be modified for your particular PHY hardware setup.
*
* Consult the Ethernet example to see how this is done.
*/
extern const eth_config_t phy_lan8720_default_ethernet_config;
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,37 @@
// Copyright 2015-2016 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.
#pragma once
/* This header contains register/bit masks for the standard
PHY MII registers that should be supported by all PHY models.
*/
#define MII_BASIC_MODE_CONTROL_REG (0x0)
#define MII_SOFTWARE_RESET BIT(15)
#define MII_BASIC_MODE_STATUS_REG (0x1)
#define MII_AUTO_NEGOTIATION_COMPLETE BIT(5)
#define MII_LINK_STATUS BIT(2)
#define MII_PHY_IDENTIFIER_1_REG (0x2)
#define MII_PHY_IDENTIFIER_2_REG (0x3)
#define MII_AUTO_NEG_ADVERTISEMENT_REG (0x4)
#define MII_ASM_DIR BIT(11)
#define MII_PAUSE BIT(10)
#define MII_PHY_LINK_PARTNER_ABILITY_REG (0x5)
#define MII_PARTNER_ASM_DIR BIT(11)
#define MII_PARTNER_PAUSE BIT(10)

View file

@ -0,0 +1,66 @@
// Copyright 2015-2017 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "phy.h"
/* @brief Dump all TLK110 PHY SMI configuration registers
*
* @note These registers are dumped at 'debug' level, so output
* may not be visible depending on default log levels.
*/
void phy_tlk110_dump_registers();
/* @brief Default TLK110 phy_check_init function.
*/
void phy_tlk110_check_phy_init(void);
/* @brief Default TLK110 phy_get_speed_mode function.
*/
eth_speed_mode_t phy_tlk110_get_speed_mode(void);
/* @brief Default TLK110 phy_get_duplex_mode function.
*/
eth_duplex_mode_t phy_tlk110_get_duplex_mode(void);
/* @brief Default TLK110 phy_power_enable function.
*
* @note This function may need to be replaced with a custom function
* if the PHY has a GPIO to enable power or start a clock.
*
* Consult the ethernet example to see how this is done.
*/
void phy_tlk110_power_enable(bool);
/* @brief Default TLK110 phy_init function.
*/
void phy_tlk110_init(void);
/* @brief Default TLK110 PHY configuration
*
* This configuration is not suitable for use as-is, it will need
* to be modified for your particular PHY hardware setup.
*
* Consult the Ethernet example to see how this is done.
*/
extern const eth_config_t phy_tlk110_default_ethernet_config;
#ifdef __cplusplus
}
#endif

View file

@ -13,10 +13,17 @@ Header Files
^^^^^^^^^^^^
* :component_file:`ethernet/include/esp_eth.h`
* :component_file:`ethernet/include/phy/phy.h`
Macros
^^^^^^
PHY Interfaces
^^^^^^^^^^^^^^
The configured PHY model(s) are set in software by configuring the eth_config_t structure for the given PHY.
Headers include a default configuration structure. These default configurations will need some members overriden or re-set before they can be used for a particular PHY hardware configuration. Consult the Ethernet example to see how this is done.
* :component_file:`ethernet/include/phy/phy_tlk110.h`
* :component_file:`ethernet/include/phy/phy_lan8720.h`
Type Definitions
^^^^^^^^^^^^^^^^
@ -56,3 +63,10 @@ Functions
.. doxygenfunction:: esp_eth_smi_write
.. doxygenfunction:: esp_eth_smi_read
.. doxygenfunction:: esp_eth_free_rx_buf
PHY Configuration Constants
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. doxygenvariable:: phy_tlk110_default_ethernet_config
.. doxygenvariable:: phy_lan8720_default_ethernet_config

View file

@ -34,10 +34,12 @@
#include "driver/gpio.h"
#ifdef CONFIG_PHY_LAN8720
#include "lan8720_phy.h"
#include "eth_phy/phy_lan8720.h"
#define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config
#endif
#ifdef CONFIG_PHY_TLK110
#include "tlk110_phy.h"
#include "eth_phy/phy_tlk110.h"
#define DEFAULT_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config
#endif
static const char *TAG = "eth_example";
@ -46,57 +48,21 @@ static const char *TAG = "eth_example";
#define PIN_SMI_MDC 23
#define PIN_SMI_MDIO 18
void phy_tlk110_check_phy_init(void)
{
while((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & AUTO_NEGOTIATION_COMPLETE ) != AUTO_NEGOTIATION_COMPLETE)
{};
while((esp_eth_smi_read(PHY_STATUS_REG) & AUTO_NEGOTIATION_STATUS ) != AUTO_NEGOTIATION_STATUS)
{};
while((esp_eth_smi_read(CABLE_DIAGNOSTIC_CONTROL_REG) & DIAGNOSTIC_DONE ) != DIAGNOSTIC_DONE)
{};
}
/* This replaces the default PHY power on/off function with one that
also uses a GPIO for power on/off.
eth_speed_mode_t phy_tlk110_get_speed_mode(void)
If this GPIO is not connected on your device (and PHY is always powered), you can use the default PHY-specific power
on/off function rather than overriding with this one.
*/
static void phy_device_power_enable_via_gpio(bool enable)
{
if((esp_eth_smi_read(PHY_STATUS_REG) & SPEED_STATUS ) != SPEED_STATUS) {
return ETH_SPEED_MODE_100M;
} else {
return ETH_SPEED_MODE_10M;
assert(DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable);
if (!enable) {
/* Do the PHY-specific power_enable(false) function before powering down */
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(false);
}
}
eth_duplex_mode_t phy_tlk110_get_duplex_mode(void)
{
if((esp_eth_smi_read(PHY_STATUS_REG) & DUPLEX_STATUS ) == DUPLEX_STATUS) {
return ETH_MDOE_FULLDUPLEX;
} else {
return ETH_MODE_HALFDUPLEX;
}
}
bool phy_tlk110_check_phy_link_status(void)
{
return ((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & LINK_STATUS) == LINK_STATUS );
}
bool phy_tlk110_get_partner_pause_enable(void)
{
if((esp_eth_smi_read(PHY_LINK_PARTNER_ABILITY_REG) & PARTNER_PAUSE) == PARTNER_PAUSE) {
return true;
} else {
return false;
}
}
void phy_enable_flow_ctrl(void)
{
uint32_t data = 0;
data = esp_eth_smi_read(AUTO_NEG_ADVERTISEMENT_REG);
esp_eth_smi_write(AUTO_NEG_ADVERTISEMENT_REG,data|ASM_DIR|PAUSE);
}
void phy_device_power_enable(bool enable)
{
gpio_pad_select_gpio(PIN_PHY_POWER);
gpio_set_direction(PIN_PHY_POWER,GPIO_MODE_OUTPUT);
if(enable == true) {
@ -107,37 +73,27 @@ void phy_device_power_enable(bool enable)
ESP_LOGD(TAG, "power_enable(FALSE)");
}
esp_eth_smi_write(SW_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG | SW_STRAP_CONFIG_DONE);
// Allow the power up/down to take effect, min 300us
vTaskDelay(1);
ets_delay_us(300);
//if config.flow_ctrl_enable == true ,enable this
phy_enable_flow_ctrl();
if (enable) {
/* Run the PHY-specific power on operations now the PHY has power */
DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true);
}
}
void eth_gpio_config_rmii(void)
static void eth_gpio_config_rmii(void)
{
//crs_dv to gpio27 ,can not change (default so not needed but physical signal must be connected)
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO27_U, FUNC_GPIO27_EMAC_RX_DV);
//txd0 to gpio19 ,can not change
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO19_U, FUNC_GPIO19_EMAC_TXD0);
//tx_en to gpio21 ,can not change
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO21_U, FUNC_GPIO21_EMAC_TX_EN);
//txd1 to gpio22 , can not change
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO22_U, FUNC_GPIO22_EMAC_TXD1);
//rxd0 to gpio25 , can not change
gpio_set_direction(25, GPIO_MODE_INPUT);
//rxd1 to gpio26 ,can not change
gpio_set_direction(26, GPIO_MODE_INPUT);
//rmii clk ,can not change
gpio_set_direction(0, GPIO_MODE_INPUT);
//mdc to gpio23
gpio_matrix_out(PIN_SMI_MDC, EMAC_MDC_O_IDX, 0, 0);
//mdio to gpio18
gpio_matrix_out(PIN_SMI_MDIO, EMAC_MDO_O_IDX, 0, 0);
gpio_matrix_in(PIN_SMI_MDIO, EMAC_MDI_I_IDX, 0);
// RMII data pins are fixed:
// TXD0 = GPIO19
// TXD1 = GPIO22
// TX_EN = GPIO21
// RXD0 = GPIO25
// RXD1 = GPIO26
// CLK == GPIO0
phy_rmii_configure_data_interface_pins();
// MDC is GPIO 23, MDIO is GPIO 18
phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO);
}
void eth_task(void *pvParameter)
@ -166,15 +122,15 @@ void app_main()
tcpip_adapter_init();
esp_event_loop_init(NULL, NULL);
#ifdef CONFIG_PHY_LAN8720
eth_config_t config = lan8720_default_ethernet_phy_config;
#endif
#ifdef CONFIG_PHY_TLK110
eth_config_t config = tlk110_default_ethernet_phy_config;
#endif
eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG;
/* Set the PHY address in the example configuration */
config.phy_addr = CONFIG_PHY_ID;
config.gpio_config = eth_gpio_config_rmii;
config.tcpip_input = tcpip_adapter_eth_input;
config.phy_power_enable = phy_device_power_enable;
/* Replace the default 'power enable' function with an example-specific
one that toggles a power GPIO. */
config.phy_power_enable = phy_device_power_enable_via_gpio;
ret = esp_eth_init(&config);

View file

@ -1,120 +0,0 @@
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "lan8720_phy.h"
void phy_dump_lan8720_registers();
static const char *TAG = "lan8720";
void phy_lan8720_check_phy_init(void)
{
phy_dump_lan8720_registers();
while((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & AUTO_NEGOTIATION_COMPLETE ) != AUTO_NEGOTIATION_COMPLETE)
{};
while((esp_eth_smi_read(PHY_SPECIAL_CONTROL_STATUS_REG) & AUTO_NEGOTIATION_DONE ) != AUTO_NEGOTIATION_DONE)
{};
}
eth_speed_mode_t phy_lan8720_get_speed_mode(void)
{
if(esp_eth_smi_read(PHY_SPECIAL_CONTROL_STATUS_REG) & SPEED_INDICATION_100T ) {
ESP_LOGD(TAG, "phy_lan8720_get_speed_mode(100)");
return ETH_SPEED_MODE_100M;
} else {
ESP_LOGD(TAG, "phy_lan8720_get_speed_mode(10)");
return ETH_SPEED_MODE_10M;
}
}
eth_duplex_mode_t phy_lan8720_get_duplex_mode(void)
{
if(esp_eth_smi_read(PHY_SPECIAL_CONTROL_STATUS_REG) & DUPLEX_INDICATION_FULL ) {
ESP_LOGD(TAG, "phy_lan8720_get_duplex_mode(FULL)");
return ETH_MDOE_FULLDUPLEX;
} else {
ESP_LOGD(TAG, "phy_lan8720_get_duplex_mode(HALF)");
return ETH_MODE_HALFDUPLEX;
}
}
bool phy_lan8720_check_phy_link_status(void)
{
if ((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & LINK_STATUS) == LINK_STATUS) {
ESP_LOGD(TAG, "phy_lan8720_check_phy_link_status(UP)");
return true;
} else {
ESP_LOGD(TAG, "phy_lan8720_check_phy_link_status(DOWN)");
return false;
}
}
bool phy_lan8720_get_partner_pause_enable(void)
{
if((esp_eth_smi_read(PHY_LINK_PARTNER_ABILITY_REG) & PARTNER_PAUSE) == PARTNER_PAUSE) {
ESP_LOGD(TAG, "phy_lan8720_get_partner_pause_enable(TRUE)");
return true;
} else {
ESP_LOGD(TAG, "phy_lan8720_get_partner_pause_enable(FALSE)");
return false;
}
}
void phy_enable_flow_ctrl(void)
{
uint32_t data = 0;
data = esp_eth_smi_read(AUTO_NEG_ADVERTISEMENT_REG);
esp_eth_smi_write(AUTO_NEG_ADVERTISEMENT_REG,data|ASM_DIR|PAUSE);
}
void phy_lan8720_init(void)
{
ESP_LOGD(TAG, "phy_lan8720_init()");
phy_dump_lan8720_registers();
esp_eth_smi_write(BASIC_MODE_CONTROL_REG, SOFTWARE_RESET);
while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) {
}
ets_delay_us(300);
//if config.flow_ctrl_enable == true ,enable this
phy_enable_flow_ctrl();
}
const eth_config_t lan8720_default_ethernet_phy_config = {
.phy_addr = CONFIG_PHY_ID,
.mac_mode = ETH_MODE_RMII,
//Only FULLDUPLEX mode support flow ctrl now!
.flow_ctrl_enable = true,
.phy_init = phy_lan8720_init,
.phy_check_init = phy_lan8720_check_phy_init,
.phy_check_link = phy_lan8720_check_phy_link_status,
.phy_get_speed_mode = phy_lan8720_get_speed_mode,
.phy_get_duplex_mode = phy_lan8720_get_duplex_mode,
.phy_get_partner_pause_enable = phy_lan8720_get_partner_pause_enable
};
void phy_dump_lan8720_registers()
{
ESP_LOGD(TAG, "LAN8720 Registers:");
ESP_LOGD(TAG, "BCR 0x%04x", esp_eth_smi_read(0x0));
ESP_LOGD(TAG, "BSR 0x%04x", esp_eth_smi_read(0x1));
ESP_LOGD(TAG, "PHY1 0x%04x", esp_eth_smi_read(0x2));
ESP_LOGD(TAG, "PHY2 0x%04x", esp_eth_smi_read(0x3));
ESP_LOGD(TAG, "ANAR 0x%04x", esp_eth_smi_read(0x4));
ESP_LOGD(TAG, "ANLPAR 0x%04x", esp_eth_smi_read(0x5));
ESP_LOGD(TAG, "ANER 0x%04x", esp_eth_smi_read(0x6));
ESP_LOGD(TAG, "MCSR 0x%04x", esp_eth_smi_read(0x17));
ESP_LOGD(TAG, "SM 0x%04x", esp_eth_smi_read(0x18));
ESP_LOGD(TAG, "SECR 0x%04x", esp_eth_smi_read(0x26));
ESP_LOGD(TAG, "CSIR 0x%04x", esp_eth_smi_read(0x27));
ESP_LOGD(TAG, "ISR 0x%04x", esp_eth_smi_read(0x29));
ESP_LOGD(TAG, "IMR 0x%04x", esp_eth_smi_read(0x30));
ESP_LOGD(TAG, "PSCSR 0x%04x", esp_eth_smi_read(0x31));
}

View file

@ -1,37 +0,0 @@
#define BASIC_MODE_CONTROL_REG (0x0)
#define SOFTWARE_RESET BIT(15)
#define BASIC_MODE_STATUS_REG (0x1)
#define AUTO_NEGOTIATION_COMPLETE BIT(5)
#define LINK_STATUS BIT(2)
#define PHY_IDENTIFIER_REG (0x2)
#define OUI_MSB_21TO6_DEF 0x0007
#define AUTO_NEG_ADVERTISEMENT_REG (0x4)
#define ASM_DIR BIT(11)
#define PAUSE BIT(10)
#define PHY_LINK_PARTNER_ABILITY_REG (0x5)
#define PARTNER_PAUSE BIT(10)
#define SOFTWARE_STRAP_CONTROL_REG (0x9)
#define SW_STRAP_CONFIG_DONE BIT(15)
#define AUTO_MDIX_ENABLE BIT(14)
#define AUTO_NEGOTIATION_ENABLE BIT(13)
#define AN_1 BIT(12)
#define AN_0 BIT(11)
#define LED_CFG BIT(10)
#define RMII_ENHANCED_MODE BIT(9)
#define PHY_SPECIAL_CONTROL_STATUS_REG (0x1f)
#define AUTO_NEGOTIATION_DONE BIT(12)
#define SPEED_DUPLEX_INDICATION_10T_HALF 0x04
#define SPEED_DUPLEX_INDICATION_10T_FULL 0x14
#define SPEED_DUPLEX_INDICATION_100T_HALF 0x08
#define SPEED_DUPLEX_INDICATION_100T_FULL 0x18
#define SPEED_INDICATION_100T BIT(3)
#define SPEED_INDICATION_10T BIT(2)
#define DUPLEX_INDICATION_FULL BIT(4)
extern const eth_config_t lan8720_default_ethernet_phy_config;

View file

@ -1,36 +0,0 @@
#define BASIC_MODE_STATUS_REG (0x1)
#define AUTO_NEGOTIATION_COMPLETE BIT(5)
#define LINK_STATUS BIT(2)
#define PHY_IDENTIFIER_REG (0x2)
#define OUI_MSB_21TO6_DEF 0x2000
#define AUTO_NEG_ADVERTISEMENT_REG (0x4)
#define ASM_DIR BIT(11)
#define PAUSE BIT(10)
#define PHY_LINK_PARTNER_ABILITY_REG (0x5)
#define PARTNER_ASM_DIR BIT(11)
#define PARTNER_PAUSE BIT(10)
#define SW_STRAP_CONTROL_REG (0x9)
#define SW_STRAP_CONFIG_DONE BIT(15)
#define AUTO_MDIX_ENABLE BIT(14)
#define AUTO_NEGOTIATION_ENABLE BIT(13)
#define AN_1 BIT(12)
#define AN_0 BIT(11)
#define LED_CFG BIT(10)
#define RMII_ENHANCED_MODE BIT(9)
#define PHY_STATUS_REG (0x10)
#define AUTO_NEGOTIATION_STATUS BIT(4)
#define DUPLEX_STATUS BIT(2)
#define SPEED_STATUS BIT(1)
#define CABLE_DIAGNOSTIC_CONTROL_REG (0x1e)
#define DIAGNOSTIC_DONE BIT(1)
#define PHY_RESET_CONTROL_REG (0x1f)
#define SOFTWARE_RESET BIT(15)
extern const eth_config_t tlk110_default_ethernet_phy_config;