From 5d6e6f79b954719484f589d9396acdcdaf4754b1 Mon Sep 17 00:00:00 2001 From: Robin Cutshaw Date: Sun, 26 Feb 2017 13:27:11 -0500 Subject: [PATCH 1/4] examples/ethernet: Add LAN8720 phy support Merges #383 https://github.com/espressif/esp-idf/pull/383 --- .../ethernet/ethernet/main/Kconfig.projbuild | 27 ++++ .../ethernet/main/ethernet_example_main.c | 57 ++++--- examples/ethernet/ethernet/main/lan8720_phy.c | 120 +++++++++++++++ examples/ethernet/ethernet/main/lan8720_phy.h | 37 +++++ examples/ethernet/ethernet/main/tlk110_phy.c | 145 ++++++++++++++++++ examples/ethernet/ethernet/main/tlk110_phy.h | 2 +- 6 files changed, 357 insertions(+), 31 deletions(-) create mode 100644 examples/ethernet/ethernet/main/Kconfig.projbuild create mode 100644 examples/ethernet/ethernet/main/lan8720_phy.c create mode 100644 examples/ethernet/ethernet/main/lan8720_phy.h create mode 100644 examples/ethernet/ethernet/main/tlk110_phy.c diff --git a/examples/ethernet/ethernet/main/Kconfig.projbuild b/examples/ethernet/ethernet/main/Kconfig.projbuild new file mode 100644 index 0000000000..9591fb93ed --- /dev/null +++ b/examples/ethernet/ethernet/main/Kconfig.projbuild @@ -0,0 +1,27 @@ +menu "Example Configuration" + +choice PHY_MODEL + prompt "Select the device used for the ethernet PHY" + default CONFIG_PHY_TLK110 + help + Select the TI TLK110 or Microchip LAN8720 PHY + +config PHY_TLK110 + bool "TI TLK110 PHY" + help + Select this to use the TI TLK110 PHY + +config PHY_LAN8720 + bool "Microchip LAN8720 PHY" + help + Select this to use the Microchip LAN8720 PHY + +endchoice + +config PHY_ID + int "Enter the PHY ID (0-31) for the selected PHY model" + default 31 + help + Select the PHY ID (0-31) for the selected PHY model + +endmenu diff --git a/examples/ethernet/ethernet/main/ethernet_example_main.c b/examples/ethernet/ethernet/main/ethernet_example_main.c index d46e2e6d9c..e1f31f7fdd 100644 --- a/examples/ethernet/ethernet/main/ethernet_example_main.c +++ b/examples/ethernet/ethernet/main/ethernet_example_main.c @@ -32,11 +32,16 @@ #include "tcpip_adapter.h" #include "nvs_flash.h" #include "driver/gpio.h" + +#ifdef CONFIG_PHY_LAN8720 +#include "lan8720_phy.h" +#endif +#ifdef CONFIG_PHY_TLK110 #include "tlk110_phy.h" +#endif static const char *TAG = "eth_example"; -#define DEFAULT_PHY_CONFIG (AUTO_MDIX_ENABLE|AUTO_NEGOTIATION_ENABLE|AN_1|AN_0|LED_CFG) #define PIN_PHY_POWER 17 #define PIN_SMI_MDC 23 #define PIN_SMI_MDIO 18 @@ -57,7 +62,7 @@ eth_speed_mode_t phy_tlk110_get_speed_mode(void) return ETH_SPEED_MODE_100M; } else { return ETH_SPEED_MODE_10M; - } + } } eth_duplex_mode_t phy_tlk110_get_duplex_mode(void) @@ -66,7 +71,7 @@ eth_duplex_mode_t phy_tlk110_get_duplex_mode(void) return ETH_MDOE_FULLDUPLEX; } else { return ETH_MODE_HALFDUPLEX; - } + } } bool phy_tlk110_check_phy_link_status(void) @@ -80,7 +85,7 @@ bool phy_tlk110_get_partner_pause_enable(void) return true; } else { return false; - } + } } void phy_enable_flow_ctrl(void) @@ -90,34 +95,31 @@ void phy_enable_flow_ctrl(void) esp_eth_smi_write(AUTO_NEG_ADVERTISEMENT_REG,data|ASM_DIR|PAUSE); } -void phy_tlk110_power_enable(bool enable) +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) { gpio_set_level(PIN_PHY_POWER, 1); + ESP_LOGD(TAG, "phy_device_power_enable(TRUE)"); } else { gpio_set_level(PIN_PHY_POWER, 0); - } -} - -void phy_tlk110_init(void) -{ - esp_eth_smi_write(PHY_RESET_CONTROL_REG, SOFTWARE_RESET); - - while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) { + ESP_LOGD(TAG, "power_enable(FALSE)"); } esp_eth_smi_write(SW_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG | SW_STRAP_CONFIG_DONE); - + ets_delay_us(300); - //if config.flow_ctrl_enable == true ,enable this + //if config.flow_ctrl_enable == true ,enable this phy_enable_flow_ctrl(); } 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 @@ -131,7 +133,7 @@ void eth_gpio_config_rmii(void) //rmii clk ,can not change gpio_set_direction(0, GPIO_MODE_INPUT); - //mdc to gpio23 + //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); @@ -149,11 +151,11 @@ void eth_task(void *pvParameter) vTaskDelay(2000 / portTICK_PERIOD_MS); if (tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip) == 0) { - ESP_LOGI(TAG, "\n~~~~~~~~~~~\n"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); ESP_LOGI(TAG, "ETHIP:"IPSTR, IP2STR(&ip.ip)); ESP_LOGI(TAG, "ETHPMASK:"IPSTR, IP2STR(&ip.netmask)); ESP_LOGI(TAG, "ETHPGW:"IPSTR, IP2STR(&ip.gw)); - ESP_LOGI(TAG, "\n~~~~~~~~~~~\n"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); } } } @@ -164,20 +166,15 @@ void app_main() tcpip_adapter_init(); esp_event_loop_init(NULL, NULL); - eth_config_t config; - config.phy_addr = PHY31; - config.mac_mode = ETH_MODE_RMII; - config.phy_init = phy_tlk110_init; +#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 config.gpio_config = eth_gpio_config_rmii; config.tcpip_input = tcpip_adapter_eth_input; - config.phy_check_init = phy_tlk110_check_phy_init; - config.phy_check_link = phy_tlk110_check_phy_link_status; - config.phy_get_speed_mode = phy_tlk110_get_speed_mode; - config.phy_get_duplex_mode = phy_tlk110_get_duplex_mode; - //Only FULLDUPLEX mode support flow ctrl now! - config.flow_ctrl_enable = true; - config.phy_get_partner_pause_enable = phy_tlk110_get_partner_pause_enable; - config.phy_power_enable = phy_tlk110_power_enable; + config.phy_power_enable = phy_device_power_enable; ret = esp_eth_init(&config); diff --git a/examples/ethernet/ethernet/main/lan8720_phy.c b/examples/ethernet/ethernet/main/lan8720_phy.c new file mode 100644 index 0000000000..d5ee828da4 --- /dev/null +++ b/examples/ethernet/ethernet/main/lan8720_phy.c @@ -0,0 +1,120 @@ + +#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)); +} diff --git a/examples/ethernet/ethernet/main/lan8720_phy.h b/examples/ethernet/ethernet/main/lan8720_phy.h new file mode 100644 index 0000000000..76b1ac01cd --- /dev/null +++ b/examples/ethernet/ethernet/main/lan8720_phy.h @@ -0,0 +1,37 @@ +#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; diff --git a/examples/ethernet/ethernet/main/tlk110_phy.c b/examples/ethernet/ethernet/main/tlk110_phy.c new file mode 100644 index 0000000000..14c8427891 --- /dev/null +++ b/examples/ethernet/ethernet/main/tlk110_phy.c @@ -0,0 +1,145 @@ + +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_eth.h" + +#include "tlk110_phy.h" + +#define DEFAULT_PHY_CONFIG (AUTO_MDIX_ENABLE|AUTO_NEGOTIATION_ENABLE|AN_1|AN_0|LED_CFG) +void phy_dump_tlk110_registers(); + +static const char *TAG = "tlk110"; + + +void phy_tlk110_check_phy_init(void) +{ + phy_dump_tlk110_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) + {}; +} + +eth_speed_mode_t phy_tlk110_get_speed_mode(void) +{ + if((esp_eth_smi_read(PHY_STATUS_REG) & SPEED_STATUS ) != SPEED_STATUS) { + ESP_LOGD(TAG, "phy_tlk110_get_speed_mode(100)"); + return ETH_SPEED_MODE_100M; + } else { + ESP_LOGD(TAG, "phy_tlk110_get_speed_mode(10)"); + return ETH_SPEED_MODE_10M; + } +} + +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; + } else { + ESP_LOGD(TAG, "phy_tlk110_get_duplex_mode(HALF)"); + return ETH_MODE_HALFDUPLEX; + } +} + +bool phy_tlk110_check_phy_link_status(void) +{ + 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; + } +} + +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; + } +} + +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(); + + esp_eth_smi_write(PHY_RESET_CONTROL_REG, SOFTWARE_RESET); + + while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) { + } + + esp_eth_smi_write(SOFTWARE_STRAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE); + + ets_delay_us(300); + + //if config.flow_ctrl_enable == true ,enable this + phy_enable_flow_ctrl(); +} + +const eth_config_t tlk110_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_tlk110_init, + .phy_check_init = phy_tlk110_check_phy_init, + .phy_check_link = phy_tlk110_check_phy_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 +}; + +void phy_dump_tlk110_registers() +{ + ESP_LOGD(TAG, "TLK110 Registers:"); + ESP_LOGD(TAG, "BMCR 0x%04x", esp_eth_smi_read(0x0)); + ESP_LOGD(TAG, "BMSR 0x%04x", esp_eth_smi_read(0x1)); + ESP_LOGD(TAG, "PHYIDR1 0x%04x", esp_eth_smi_read(0x2)); + ESP_LOGD(TAG, "PHYIDR2 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, "ANNPTR 0x%04x", esp_eth_smi_read(0x7)); + ESP_LOGD(TAG, "ANLNPTR 0x%04x", esp_eth_smi_read(0x8)); + ESP_LOGD(TAG, "SWSCR1 0x%04x", esp_eth_smi_read(0x9)); + ESP_LOGD(TAG, "SWSCR2 0x%04x", esp_eth_smi_read(0xa)); + ESP_LOGD(TAG, "SWSCR3 0x%04x", esp_eth_smi_read(0xb)); + ESP_LOGD(TAG, "REGCR 0x%04x", esp_eth_smi_read(0xd)); + ESP_LOGD(TAG, "ADDAR 0x%04x", esp_eth_smi_read(0xe)); + ESP_LOGD(TAG, "PHYSTS 0x%04x", esp_eth_smi_read(0x10)); + ESP_LOGD(TAG, "PHYSCR 0x%04x", esp_eth_smi_read(0x11)); + ESP_LOGD(TAG, "MISR1 0x%04x", esp_eth_smi_read(0x12)); + ESP_LOGD(TAG, "MISR2 0x%04x", esp_eth_smi_read(0x13)); + ESP_LOGD(TAG, "FCSCR 0x%04x", esp_eth_smi_read(0x14)); + ESP_LOGD(TAG, "RECR 0x%04x", esp_eth_smi_read(0x15)); + ESP_LOGD(TAG, "BISCR 0x%04x", esp_eth_smi_read(0x16)); + ESP_LOGD(TAG, "RBR 0x%04x", esp_eth_smi_read(0x17)); + ESP_LOGD(TAG, "LEDCR 0x%04x", esp_eth_smi_read(0x18)); + ESP_LOGD(TAG, "PHYCR 0x%04x", esp_eth_smi_read(0x19)); + ESP_LOGD(TAG, "10BTSCR 0x%04x", esp_eth_smi_read(0x1a)); + ESP_LOGD(TAG, "BICSR1 0x%04x", esp_eth_smi_read(0x1b)); + ESP_LOGD(TAG, "BICSR2 0x%04x", esp_eth_smi_read(0x1c)); + ESP_LOGD(TAG, "CDCR 0x%04x", esp_eth_smi_read(0x1e)); + ESP_LOGD(TAG, "TRXCPSR 0x%04x", esp_eth_smi_read(0x42)); + ESP_LOGD(TAG, "PWRBOCR 0x%04x", esp_eth_smi_read(0xae)); + ESP_LOGD(TAG, "VRCR 0x%04x", esp_eth_smi_read(0xD0)); + ESP_LOGD(TAG, "ALCDRR1 0x%04x", esp_eth_smi_read(0x155)); + ESP_LOGD(TAG, "CDSCR1 0x%04x", esp_eth_smi_read(0x170)); + ESP_LOGD(TAG, "CDSCR2 0x%04x", esp_eth_smi_read(0x171)); +} diff --git a/examples/ethernet/ethernet/main/tlk110_phy.h b/examples/ethernet/ethernet/main/tlk110_phy.h index 63236edebd..45ff474aaf 100644 --- a/examples/ethernet/ethernet/main/tlk110_phy.h +++ b/examples/ethernet/ethernet/main/tlk110_phy.h @@ -33,4 +33,4 @@ #define PHY_RESET_CONTROL_REG (0x1f) #define SOFTWARE_RESET BIT(15) - +extern const eth_config_t tlk110_default_ethernet_phy_config; From 453722ba54101fb23c8b8b5cba4a0c7acd003fa6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 19 Apr 2017 12:02:53 +1000 Subject: [PATCH 2/4] ethernet: Refactor PHY support to be part of ethernet component Move generic PHY support into its own interface --- components/ethernet/component.mk | 2 +- components/ethernet/emac_main.c | 6 +- components/ethernet/eth_phy/phy_common.c | 75 +++++++++ components/ethernet/eth_phy/phy_lan8720.c | 157 ++++++++++++++++++ .../ethernet/eth_phy/phy_tlk110.c | 129 ++++++++------ components/ethernet/include/esp_eth.h | 8 +- components/ethernet/include/eth_phy/phy.h | 59 +++++++ .../ethernet/include/eth_phy/phy_lan8720.h | 67 ++++++++ components/ethernet/include/eth_phy/phy_reg.h | 37 +++++ .../ethernet/include/eth_phy/phy_tlk110.h | 66 ++++++++ docs/api/ethernet/esp_eth.rst | 18 +- .../ethernet/main/ethernet_example_main.c | 122 +++++--------- examples/ethernet/ethernet/main/lan8720_phy.c | 120 ------------- examples/ethernet/ethernet/main/lan8720_phy.h | 37 ----- examples/ethernet/ethernet/main/tlk110_phy.h | 36 ---- 15 files changed, 607 insertions(+), 332 deletions(-) create mode 100644 components/ethernet/eth_phy/phy_common.c create mode 100644 components/ethernet/eth_phy/phy_lan8720.c rename examples/ethernet/ethernet/main/tlk110_phy.c => components/ethernet/eth_phy/phy_tlk110.c (50%) create mode 100644 components/ethernet/include/eth_phy/phy.h create mode 100644 components/ethernet/include/eth_phy/phy_lan8720.h create mode 100644 components/ethernet/include/eth_phy/phy_reg.h create mode 100644 components/ethernet/include/eth_phy/phy_tlk110.h delete mode 100644 examples/ethernet/ethernet/main/lan8720_phy.c delete mode 100644 examples/ethernet/ethernet/main/lan8720_phy.h delete mode 100644 examples/ethernet/ethernet/main/tlk110_phy.h diff --git a/components/ethernet/component.mk b/components/ethernet/component.mk index c2c4c03a1a..0856e27d65 100755 --- a/components/ethernet/component.mk +++ b/components/ethernet/component.mk @@ -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 diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 672ab75dfc..501fd3ef2f 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -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); diff --git a/components/ethernet/eth_phy/phy_common.c b/components/ethernet/eth_phy/phy_common.c new file mode 100644 index 0000000000..a72a6daf86 --- /dev/null +++ b/components/ethernet/eth_phy/phy_common.c @@ -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; + } +} diff --git a/components/ethernet/eth_phy/phy_lan8720.c b/components/ethernet/eth_phy/phy_lan8720.c new file mode 100644 index 0000000000..28dfed56ed --- /dev/null +++ b/components/ethernet/eth_phy/phy_lan8720.c @@ -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)); +} diff --git a/examples/ethernet/ethernet/main/tlk110_phy.c b/components/ethernet/eth_phy/phy_tlk110.c similarity index 50% rename from examples/ethernet/ethernet/main/tlk110_phy.c rename to components/ethernet/eth_phy/phy_tlk110.c index 14c8427891..7c91a76529 100644 --- a/examples/ethernet/ethernet/main/tlk110_phy.c +++ b/components/ethernet/eth_phy/phy_tlk110.c @@ -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)); diff --git a/components/ethernet/include/esp_eth.h b/components/ethernet/include/esp_eth.h index c2a3554b11..24187e305f 100644 --- a/components/ethernet/include/esp_eth.h +++ b/components/ethernet/include/esp_eth.h @@ -15,6 +15,7 @@ #ifndef __ESP_ETH_H__ #define __ESP_ETH_H__ +#include #include #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 * diff --git a/components/ethernet/include/eth_phy/phy.h b/components/ethernet/include/eth_phy/phy.h new file mode 100644 index 0000000000..44edd74910 --- /dev/null +++ b/components/ethernet/include/eth_phy/phy.h @@ -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 diff --git a/components/ethernet/include/eth_phy/phy_lan8720.h b/components/ethernet/include/eth_phy/phy_lan8720.h new file mode 100644 index 0000000000..7bde42eca3 --- /dev/null +++ b/components/ethernet/include/eth_phy/phy_lan8720.h @@ -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 diff --git a/components/ethernet/include/eth_phy/phy_reg.h b/components/ethernet/include/eth_phy/phy_reg.h new file mode 100644 index 0000000000..33c9a064a3 --- /dev/null +++ b/components/ethernet/include/eth_phy/phy_reg.h @@ -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) diff --git a/components/ethernet/include/eth_phy/phy_tlk110.h b/components/ethernet/include/eth_phy/phy_tlk110.h new file mode 100644 index 0000000000..1019044e95 --- /dev/null +++ b/components/ethernet/include/eth_phy/phy_tlk110.h @@ -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 diff --git a/docs/api/ethernet/esp_eth.rst b/docs/api/ethernet/esp_eth.rst index fbecdca339..dc10b7b41a 100644 --- a/docs/api/ethernet/esp_eth.rst +++ b/docs/api/ethernet/esp_eth.rst @@ -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 diff --git a/examples/ethernet/ethernet/main/ethernet_example_main.c b/examples/ethernet/ethernet/main/ethernet_example_main.c index e1f31f7fdd..e1e0254d64 100644 --- a/examples/ethernet/ethernet/main/ethernet_example_main.c +++ b/examples/ethernet/ethernet/main/ethernet_example_main.c @@ -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); diff --git a/examples/ethernet/ethernet/main/lan8720_phy.c b/examples/ethernet/ethernet/main/lan8720_phy.c deleted file mode 100644 index d5ee828da4..0000000000 --- a/examples/ethernet/ethernet/main/lan8720_phy.c +++ /dev/null @@ -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)); -} diff --git a/examples/ethernet/ethernet/main/lan8720_phy.h b/examples/ethernet/ethernet/main/lan8720_phy.h deleted file mode 100644 index 76b1ac01cd..0000000000 --- a/examples/ethernet/ethernet/main/lan8720_phy.h +++ /dev/null @@ -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; diff --git a/examples/ethernet/ethernet/main/tlk110_phy.h b/examples/ethernet/ethernet/main/tlk110_phy.h deleted file mode 100644 index 45ff474aaf..0000000000 --- a/examples/ethernet/ethernet/main/tlk110_phy.h +++ /dev/null @@ -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; From 453b5ded1d030d18e00a19dc16d55f5bc508b856 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 19 Apr 2017 13:43:25 +1000 Subject: [PATCH 3/4] ethernet: Add convenience functions esp_eth_smi_wait_value() & esp_eth_smi_wait_set() Covers the common case of waiting for a particular PHY register to have a particular value. --- components/ethernet/emac_main.c | 19 ++++++++++++++ components/ethernet/eth_phy/phy_lan8720.c | 24 ++++++------------ components/ethernet/eth_phy/phy_tlk110.c | 25 +++++++----------- components/ethernet/include/esp_eth.h | 31 ++++++++++++++++++++++- docs/api/ethernet/esp_eth.rst | 2 ++ 5 files changed, 68 insertions(+), 33 deletions(-) diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 501fd3ef2f..7e299be944 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -204,6 +204,25 @@ uint16_t esp_eth_smi_read(uint32_t reg_num) return value; } +esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t value_mask, int timeout_ms) +{ + unsigned start = xTaskGetTickCount(); + unsigned timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS; + uint16_t current_value = 0; + + while (timeout_ticks == 0 || (xTaskGetTickCount() - start < timeout_ticks)) { + current_value = esp_eth_smi_read(reg_num); + if ((current_value & value_mask) == (value & value_mask)) { + return ESP_OK; + } + vTaskDelay(1); + } + ESP_LOGE(TAG, "Timed out waiting for PHY register 0x%x to have value 0x%04x (mask 0x%04x). Current value 0x%04x", + reg_num, value, value_mask, current_value); + return ESP_ERR_TIMEOUT; +} + + static void emac_set_user_config_data(eth_config_t *config ) { emac_config.phy_addr = config->phy_addr; diff --git a/components/ethernet/eth_phy/phy_lan8720.c b/components/ethernet/eth_phy/phy_lan8720.c index 28dfed56ed..b0f793b33d 100644 --- a/components/ethernet/eth_phy/phy_lan8720.c +++ b/components/ethernet/eth_phy/phy_lan8720.c @@ -18,14 +18,12 @@ #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 +#define LAN8720_PHY_ID2_MASK 0xFFF0 /* LAN8720-specific registers */ #define SW_STRAP_CONTROL_REG (0x9) @@ -55,12 +53,8 @@ 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); - } + esp_eth_smi_wait_set(MII_BASIC_MODE_STATUS_REG, MII_AUTO_NEGOTIATION_COMPLETE, 0); + esp_eth_smi_wait_set(PHY_SPECIAL_CONTROL_STATUS_REG, AUTO_NEGOTIATION_DONE, 0); } eth_speed_mode_t phy_lan8720_get_speed_mode(void) @@ -101,14 +95,12 @@ void phy_lan8720_init(void) esp_eth_smi_write(MII_BASIC_MODE_CONTROL_REG, MII_SOFTWARE_RESET); - unsigned phy_id1, phy_id2; + esp_err_t res1, res2; 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); + // Call esp_eth_smi_wait_value() with a timeout so it prints an error periodically + res1 = esp_eth_smi_wait_value(MII_PHY_IDENTIFIER_1_REG, LAN8720_PHY_ID1, UINT16_MAX, 1000); + res2 = esp_eth_smi_wait_value(MII_PHY_IDENTIFIER_2_REG, LAN8720_PHY_ID2, LAN8720_PHY_ID2_MASK, 1000); + } while(res1 != ESP_OK || res2 != ESP_OK); esp_eth_smi_write(SW_STRAP_CONTROL_REG, DEFAULT_STRAP_CONFIG | SW_STRAP_CONFIG_DONE); diff --git a/components/ethernet/eth_phy/phy_tlk110.c b/components/ethernet/eth_phy/phy_tlk110.c index 7c91a76529..020329a267 100644 --- a/components/ethernet/eth_phy/phy_tlk110.c +++ b/components/ethernet/eth_phy/phy_tlk110.c @@ -26,6 +26,7 @@ */ #define TLK110_PHY_ID1 0x2000 #define TLK110_PHY_ID2 0xa210 +#define TLK110_PHY_ID2_MASK 0xFFF0 /* TLK110-specific registers */ #define SW_STRAP_CONTROL_REG (0x9) @@ -56,15 +57,9 @@ void phy_tlk110_check_phy_init(void) { phy_tlk110_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_STATUS_REG) & AUTO_NEGOTIATION_STATUS ) == 0) { - vTaskDelay(1); - } - while((esp_eth_smi_read(CABLE_DIAGNOSTIC_CONTROL_REG) & DIAGNOSTIC_DONE ) == 0) { - vTaskDelay(1); - } + esp_eth_smi_wait_set(MII_BASIC_MODE_STATUS_REG, MII_AUTO_NEGOTIATION_COMPLETE, 0); + esp_eth_smi_wait_set(PHY_STATUS_REG, AUTO_NEGOTIATION_STATUS, 0); + esp_eth_smi_wait_set(CABLE_DIAGNOSTIC_CONTROL_REG, DIAGNOSTIC_DONE, 0); } eth_speed_mode_t phy_tlk110_get_speed_mode(void) @@ -106,14 +101,12 @@ void phy_tlk110_init(void) esp_eth_smi_write(PHY_RESET_CONTROL_REG, SOFTWARE_RESET); - unsigned phy_id1, phy_id2; + esp_err_t res1, res2; 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); + // Call esp_eth_smi_wait_value() with a timeout so it prints an error periodically + res1 = esp_eth_smi_wait_value(MII_PHY_IDENTIFIER_1_REG, TLK110_PHY_ID1, UINT16_MAX, 1000); + res2 = esp_eth_smi_wait_value(MII_PHY_IDENTIFIER_2_REG, TLK110_PHY_ID2, TLK110_PHY_ID2_MASK, 1000); + } while(res1 != ESP_OK || res2 != ESP_OK); esp_eth_smi_write(SW_STRAP_CONTROL_REG, DEFAULT_STRAP_CONFIG | SW_STRAP_CONFIG_DONE); diff --git a/components/ethernet/include/esp_eth.h b/components/ethernet/include/esp_eth.h index 24187e305f..a7f92ad4ba 100644 --- a/components/ethernet/include/esp_eth.h +++ b/components/ethernet/include/esp_eth.h @@ -175,7 +175,7 @@ void esp_eth_get_mac(uint8_t mac[6]); void esp_eth_smi_write(uint32_t reg_num, uint16_t value); /** - * @brief Write phy reg with smi interface. + * @brief Read phy reg with smi interface. * * @note phy base addr must be right. * @@ -185,6 +185,35 @@ void esp_eth_smi_write(uint32_t reg_num, uint16_t value); */ uint16_t esp_eth_smi_read(uint32_t reg_num); +/** + * @brief Continuously read a PHY register over SMI interface, wait until the register has the desired value. + * + * @note PHY base address must be right. + * + * @param reg_num: PHY register number + * @param value: Value to wait for (masked with value_mask) + * @param value_mask: Mask of bits to match in the register. + * @param timeout_ms: Timeout to wait for this value (milliseconds). 0 means never timeout. + * + * @return ESP_OK if desired value matches, ESP_ERR_TIMEOUT if timed out. + */ +esp_err_t esp_eth_smi_wait_value(uint32_t reg_num, uint16_t value, uint16_t value_mask, int timeout_ms); + +/** + * @brief Continuously read a PHY register over SMI interface, wait until the register has all bits in a mask set. + * + * @note PHY base address must be right. + * + * @param reg_num: PHY register number + * @param value_mask: Value mask to wait for (all bits in this mask must be set) + * @param timeout_ms: Timeout to wait for this value (milliseconds). 0 means never timeout. + * + * @return ESP_OK if desired value matches, ESP_ERR_TIMEOUT if timed out. + */ +static inline esp_err_t esp_eth_smi_wait_set(uint32_t reg_num, uint16_t value_mask, int timeout_ms) { + return esp_eth_smi_wait_value(reg_num, value_mask, value_mask, timeout_ms); +} + /** * @brief Free emac rx buf. * diff --git a/docs/api/ethernet/esp_eth.rst b/docs/api/ethernet/esp_eth.rst index dc10b7b41a..223c7b2267 100644 --- a/docs/api/ethernet/esp_eth.rst +++ b/docs/api/ethernet/esp_eth.rst @@ -62,6 +62,8 @@ Functions .. doxygenfunction:: esp_eth_get_mac .. doxygenfunction:: esp_eth_smi_write .. doxygenfunction:: esp_eth_smi_read +.. doxygenfunction:: esp_eth_smi_wait_value +.. doxygenfunction:: esp_eth_smi_wait_set .. doxygenfunction:: esp_eth_free_rx_buf From abe58c867ec98d2a74793b781a96101a119e5a91 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 3 Mar 2017 11:06:32 +1100 Subject: [PATCH 4/4] Ethernet example: Add README, use menuconfig for all example pin assignments --- examples/ethernet/ethernet/README.md | 56 ++++++++++++++++++- .../ethernet/ethernet/main/Kconfig.projbuild | 37 ++++++++++-- .../ethernet/main/ethernet_example_main.c | 12 ++-- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/examples/ethernet/ethernet/README.md b/examples/ethernet/ethernet/README.md index 71c2590457..e4a6e5cc7e 100644 --- a/examples/ethernet/ethernet/README.md +++ b/examples/ethernet/ethernet/README.md @@ -1,6 +1,56 @@ -# ethernet Example +# Ethernet Example -Init ethernet interface and enable it ,then you can ping it if it got ip address. +Initialises the ethernet interface and enables it, then sends DHCP requests and tries to obtain a DHCP lease. If successful then you will be able to ping the device. +# PHY Configuration -See the README.md file in the upper level 'examples' directory for more information about examples. +Use "make menuconfig" to set the PHY model and the PHY address, and configure the SMI I/O pins (see below). These configuration items will vary depending on the hardware configuration you are using. + +The default example configuration is correct for Espressif's Ethernet board with TLK110 PHY. Other hardware will require different configuration and/or changes to the example. + +## PHY Address + +The PHY address depends on the hardware and the PHY configuration. Consult the documentation/datasheet for the PHY hardware you have. + +* Default address 31 is correct for Espressif's Ethernet board with TLK110 PHY. +* Address 1 is correct for the common Waveshare LAN8720 PHY breakout. +* Other LAN8720 breakouts may take address 0. + +If the PHY address is incorrect then the EMAC will initialise but all attempts to read/write configuration registers on the PHY will fail. + +## RMII PHY Wiring + +The following PHY connections are required for RMII PHY data connections. These GPIO pin assignments cannot be changed. + +| GPIO | RMII Signal | ESP32 EMAC Function | Notes | +| ------- | ----------- | ------------------- | ----- | +| 0 | REF_CLK | EMAC_TX_CLK | Currently this must be a 50MHz reference clock input from the PHY (ext_osc configuration). | +| 21 | TX_EN | EMAC_TX_EN | | +| 19 | TX0 | EMAC_TXD0 | | +| 22 | TX1 | EMAC_TXD1 | | +| 25 | RX0 | EMAC_RXD0 | | +| 26 | RX1 | EMAC_RXD1 | | +| 27 | CRS_DV | EMAC_RX_DRV | | + +## RMII PHY SMI Wiring + +The following PHY connections are required for RMII PHY SMI (aka MDIO) management interface. These GPIO pin assignments can be changed to any unused GPIO pin. + +For the example, these pins are configured via `make menuconfig` under the Example configuration. + +| Default Example GPIO | RMII Signal | Notes | +| -------------------- | ----------- | ------------- | +| 23 | MDC | Output to PHY | +| 18 | MDIO | Bidirectional | + +The defaults in the example are correct for Espressif's Ethernet development board. + +## Note about GPIO0 + +Because GPIO0 is a strapping pin for entering UART flashing mode on reset, care must be taken when also using this pin as EMAC_TX_CLK. If the clock output from the PHY is oscillating during reset, the ESP32 may randomly enter UART flashing mode. + +One solution is to use an additional GPIO as a "power pin", which either powers the PHY on/off or enables/disables the PHY's own oscillator. This prevents the clock signal from being active during a system reset. For this configuration to work, GPIO0 also needs a pullup resistor and the "power pin" GPIO will need a pullup/pulldown resistor - as appropriate in order to keep the PHY clock disabled when the ESP32 is in reset. + +See the example source code to see how the "power pin" GPIO can be managed in software. + +The example defaults to using GPIO 17 for this function, but it can be overriden. On Espressif's Ethernet development board, GPIO 17 is the power pin used to enable/disable the PHY oscillator. diff --git a/examples/ethernet/ethernet/main/Kconfig.projbuild b/examples/ethernet/ethernet/main/Kconfig.projbuild index 9591fb93ed..f6c46b54d6 100644 --- a/examples/ethernet/ethernet/main/Kconfig.projbuild +++ b/examples/ethernet/ethernet/main/Kconfig.projbuild @@ -1,10 +1,10 @@ menu "Example Configuration" choice PHY_MODEL - prompt "Select the device used for the ethernet PHY" + prompt "Ethernet PHY" default CONFIG_PHY_TLK110 help - Select the TI TLK110 or Microchip LAN8720 PHY + Select the PHY driver to use for the example. config PHY_TLK110 bool "TI TLK110 PHY" @@ -18,10 +18,37 @@ config PHY_LAN8720 endchoice -config PHY_ID - int "Enter the PHY ID (0-31) for the selected PHY model" +config PHY_ADDRESS + int "PHY Address (0-31)" default 31 + range 0 31 help - Select the PHY ID (0-31) for the selected PHY model + Select the PHY Address (0-31) for the hardware configuration and PHY model. + +config PHY_USE_POWER_PIN + bool "Use PHY Power (enable/disable) pin" + default y + help + Use a GPIO "power pin" to power the PHY on/off during operation. + Consult the example README for more details + +config PHY_POWER_PIN + int "PHY Power GPIO" + default 17 + depends on PHY_USE_POWER_PIN + help + GPIO number to use for powering on/off the PHY. + +config PHY_SMI_MDC_PIN + int "SMI MDC Pin" + default 23 + help + GPIO number to use for SMI clock output MDC to PHY. + +config PHY_SMI_MDIO_PIN + int "SMI MDIO Pin" + default 18 + help + GPIO number to use for SMI data pin MDIO to/from PHY. endmenu diff --git a/examples/ethernet/ethernet/main/ethernet_example_main.c b/examples/ethernet/ethernet/main/ethernet_example_main.c index e1e0254d64..8345ab6da4 100644 --- a/examples/ethernet/ethernet/main/ethernet_example_main.c +++ b/examples/ethernet/ethernet/main/ethernet_example_main.c @@ -44,10 +44,11 @@ static const char *TAG = "eth_example"; -#define PIN_PHY_POWER 17 -#define PIN_SMI_MDC 23 -#define PIN_SMI_MDIO 18 +#define PIN_PHY_POWER CONFIG_PHY_POWER_PIN +#define PIN_SMI_MDC CONFIG_PHY_SMI_MDC_PIN +#define PIN_SMI_MDIO CONFIG_PHY_SMI_MDIO_PIN +#ifdef CONFIG_PHY_USE_POWER_PIN /* This replaces the default PHY power on/off function with one that also uses a GPIO for power on/off. @@ -81,6 +82,7 @@ static void phy_device_power_enable_via_gpio(bool enable) DEFAULT_ETHERNET_PHY_CONFIG.phy_power_enable(true); } } +#endif static void eth_gpio_config_rmii(void) { @@ -124,13 +126,15 @@ void app_main() eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG; /* Set the PHY address in the example configuration */ - config.phy_addr = CONFIG_PHY_ID; + config.phy_addr = CONFIG_PHY_ADDRESS; config.gpio_config = eth_gpio_config_rmii; config.tcpip_input = tcpip_adapter_eth_input; +#ifdef CONFIG_PHY_USE_POWER_PIN /* 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; +#endif ret = esp_eth_init(&config);