feat(emac): add support for emac to use internal (APLL) clock outputs.

This commit is contained in:
Frank Sautter 2017-10-17 05:05:27 +08:00 committed by michael
parent e64b9ecaf4
commit f324458b6a
10 changed files with 156 additions and 62 deletions

View File

@ -53,6 +53,7 @@ enum {
struct emac_config_data {
eth_phy_base_t phy_addr;
eth_mode_t mac_mode;
eth_clock_mode_t clock_mode;
struct dma_extended_desc *dma_etx;
uint32_t cur_tx;
uint32_t dirty_tx;

View File

@ -101,18 +101,6 @@ void emac_enable_clk(bool enable)
}
}
void emac_set_clk_mii(void)
{
//select ex clock source
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
//ex clk enable
REG_SET_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL);
//set mii mode rx/tx clk enable
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_MII_CLK_RX_EN);
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_MII_CLK_TX_EN);
}
void emac_dma_init(void)
{
REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_FORWARD_UNDERSIZED_GOOD_FRAMES);
@ -134,11 +122,3 @@ void emac_mac_init(void)
REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED);
REG_SET_BIT(EMAC_GMACFRAMEFILTER_REG, EMAC_PROMISCUOUS_MODE);
}
void emac_set_clk_rmii(void)
{
//select ex clock source
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
//ex clk enable
REG_SET_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL);
}

View File

@ -40,8 +40,6 @@ struct dma_extended_desc {
};
void emac_enable_clk(bool enable);
void emac_set_clk_rmii(void);
void emac_set_clk_mii(void);
void emac_reset(void);
void emac_set_gpio_pin_rmii(void);
void emac_set_gpio_pin_mii(void);
@ -113,4 +111,3 @@ void inline emac_send_pause_zero_frame_enable(void)
#endif
#endif

View File

@ -19,6 +19,7 @@
#include "rom/gpio.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/gpio_reg.h"
#include "soc/dport_reg.h"
@ -130,10 +131,10 @@ static void emac_set_rx_base_reg(void)
*
* (1) Initializing the Linked List. Connect the numerable nodes to a circular linked list, appoint one of the nodes as the head node, mark* the dirty_rx and cur_rx into the node, and mount the node on the hardware base address. Initialize cnt_rx into 0.
*
* (2) When hardware receives packets, nodes of linked lists will be fed with data packets from the base address by turns, marks the node
* (2) When hardware receives packets, nodes of linked lists will be fed with data packets from the base address by turns, marks the node
* of linked lists as HARDWARE UNUSABLE and reports interrupts.
*
* (3) When the software receives the interrupts, it will handle the linked lists by turns from dirty_rx, send data packets to protocol
* (3) When the software receives the interrupts, it will handle the linked lists by turns from dirty_rx, send data packets to protocol
* stack. dirty_rx will deviate backwards by turns and cnt_rx will by turns ++.
*
* (4) After the protocol stack handles all the data and calls the free function, it will deviate backwards by turns from cur_rx, mark the * node of linked lists as HARDWARE USABLE and cnt_rx will by turns --.
@ -252,6 +253,7 @@ static void emac_set_user_config_data(eth_config_t *config )
{
emac_config.phy_addr = config->phy_addr;
emac_config.mac_mode = config->mac_mode;
emac_config.clock_mode = config->clock_mode;
emac_config.phy_init = config->phy_init;
emac_config.emac_tcpip_input = config->tcpip_input;
emac_config.emac_gpio_config = config->gpio_config;
@ -291,7 +293,12 @@ static esp_err_t emac_verify_args(void)
}
if (emac_config.mac_mode != ETH_MODE_RMII) {
ESP_LOGE(TAG, "mac mode err,now only support RMII");
ESP_LOGE(TAG, "mac mode err, currently only support for RMII");
ret = ESP_FAIL;
}
if (emac_config.clock_mode > ETH_CLOCK_GPIO17_OUT) {
ESP_LOGE(TAG, "emac clock mode err");
ret = ESP_FAIL;
}
@ -480,7 +487,7 @@ static void emac_process_rx_unavail(void)
}
uint32_t tmp_dirty = emac_config.dirty_rx;
emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM;
//copy data to lwip
emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2),
(((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL);
@ -529,7 +536,7 @@ static void emac_process_rx(void)
}
uint32_t tmp_dirty = emac_config.dirty_rx;
emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM;
//copy data to lwip
emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[tmp_dirty].basic.desc2),
(((emac_config.dma_erx[tmp_dirty].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL);
@ -1036,14 +1043,46 @@ esp_err_t esp_eth_init_internal(eth_config_t *config)
//before set emac reg must enable clk
periph_module_enable(PERIPH_EMAC_MODULE);
if (emac_config.clock_mode != ETH_CLOCK_GPIO0_IN) {
// 50 MHz = 40MHz * (6 + 4) / (2 * (2 + 2) = 400MHz / 8
rtc_clk_apll_enable(1, 0, 0, 6, 2);
// the next to values have to be set AFTER "periph_module_enable" is called
REG_SET_FIELD(EMAC_EX_CLKOUT_CONF_REG, EMAC_EX_CLK_OUT_H_DIV_NUM, 0);
REG_SET_FIELD(EMAC_EX_CLKOUT_CONF_REG, EMAC_EX_CLK_OUT_DIV_NUM, 0);
if (emac_config.clock_mode == ETH_CLOCK_GPIO0_OUT) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_WRITE(PIN_CTRL, 6);
ESP_LOGD(TAG, "EMAC 50MHz clock output on GPIO0");
} else if (emac_config.clock_mode == ETH_CLOCK_GPIO16_OUT) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U, FUNC_GPIO16_EMAC_CLK_OUT);
ESP_LOGD(TAG, "EMAC 50MHz clock output on GPIO16");
} else if (emac_config.clock_mode == ETH_CLOCK_GPIO17_OUT) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U, FUNC_GPIO17_EMAC_CLK_OUT_180);
ESP_LOGD(TAG, "EMAC 50MHz inverted clock output on GPIO17");
}
}
emac_enable_clk(true);
REG_SET_FIELD(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_PHY_INTF_SEL, EMAC_EX_PHY_INTF_RMII);
emac_dma_init();
if (emac_config.mac_mode == ETH_MODE_RMII) {
emac_set_clk_rmii();
if (emac_config.clock_mode == ETH_CLOCK_GPIO0_IN) {
// external clock on GPIO0
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN);
REG_SET_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL);
ESP_LOGD(TAG, "External clock input 50MHz on GPIO0");
if (emac_config.mac_mode == ETH_MODE_MII) {
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_MII_CLK_RX_EN);
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_MII_CLK_TX_EN);
}
} else {
emac_set_clk_mii();
// internal clock by APLL
REG_CLR_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_EXT_OSC_EN);
REG_SET_BIT(EMAC_EX_CLK_CTRL_REG, EMAC_EX_INT_OSC_EN);
REG_CLR_BIT(EMAC_EX_OSCCLK_CONF_REG, EMAC_EX_OSC_CLK_SEL);
}
emac_config.emac_gpio_config();
@ -1068,4 +1107,3 @@ esp_err_t esp_eth_init_internal(eth_config_t *config)
_exit:
return ret;
}

View File

@ -118,6 +118,7 @@ const eth_config_t phy_lan8720_default_ethernet_config = {
// for defaults.
.phy_addr = 0,
.mac_mode = ETH_MODE_RMII,
.clock_mode = ETH_CLOCK_GPIO0_IN,
//Only FULLDUPLEX mode support flow ctrl now!
.flow_ctrl_enable = true,
.phy_init = phy_lan8720_init,

View File

@ -122,6 +122,7 @@ const eth_config_t phy_tlk110_default_ethernet_config = {
// is used if all pins are unconnected.
.phy_addr = 0x1,
.mac_mode = ETH_MODE_RMII,
.clock_mode = ETH_CLOCK_GPIO0_IN,
//Only FULLDUPLEX mode support flow ctrl now!
.flow_ctrl_enable = true,
.phy_init = phy_tlk110_init,

View File

@ -28,6 +28,13 @@ typedef enum {
ETH_MODE_MII,
} eth_mode_t;
typedef enum {
ETH_CLOCK_GPIO0_IN = 0,
ETH_CLOCK_GPIO0_OUT = 1,
ETH_CLOCK_GPIO16_OUT = 2,
ETH_CLOCK_GPIO17_OUT = 3
} eth_clock_mode_t;
typedef enum {
ETH_SPEED_MODE_10M = 0,
ETH_SPEED_MODE_100M,
@ -90,8 +97,9 @@ typedef void (*eth_phy_power_enable_func)(bool enable);
typedef struct {
eth_phy_base_t phy_addr; /*!< phy base addr (0~31) */
eth_mode_t mac_mode; /*!< mac mode only support RMII now */
eth_tcpip_input_func tcpip_input; /*!< tcpip input func */
eth_phy_func phy_init; /*!< phy init func */
eth_clock_mode_t clock_mode; /*!< external/internal clock mode selecton */
eth_tcpip_input_func tcpip_input; /*!< tcpip input func */
eth_phy_func phy_init; /*!< phy init func */
eth_phy_check_link_func phy_check_link; /*!< phy check link func */
eth_phy_check_init_func phy_check_init; /*!< phy check init func */
eth_phy_get_speed_mode_func phy_get_speed_mode; /*!< phy check init func */
@ -247,4 +255,3 @@ void esp_eth_free_rx_buf(void *buf);
#endif
#endif

View File

@ -1,36 +1,56 @@
# Ethernet Example
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.
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
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.
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.
* Address 31 (default) for Espressif's Ethernet board with TLK110 PHY
* Address 1 for the common Waveshare LAN8720 PHY breakout
* Address 0 for other LAN8720 breakouts
If the PHY address is incorrect then the EMAC will initialise but all attempts to read/write configuration registers on the PHY will fail.
If the PHY address is incorrect then the EMAC will initialise, but all attempts to read/write configuration registers on the PHY will fail.
## PHY Clock Wiring
The ESP32 and the Ethernet PHY need a common 50MHz reference clock. This clock can either be be provided externally by a crystal oscillator (e.g. crystal connected to the PHY or a seperate crystal oscillator) or internally by using the EPS32's APLL.
Because of its freqency the signal integrity has to be observed (ringing, capacitive load, resisitive load, skew, length of PCB trace). It is recommended to add a 33Ω resistor in series to reduce ringing.
Possible configurations of the 50MHz clock signal:
| Mode | GPIO Pin | Signal name | Notes |
| -------- | -------- | -------------- | -------------------------------------------------------------------------------------------------- |
| external | `GPIO0` | `EMAC_TX_CLK` | Input of 50MHz PHY clock |
| internal | `GPIO0` | `CLK_OUT1` | Output of 50MHz APLL clock. Signal quality might be an issue. |
| internal | `GPIO16` | `EMAC_CLK_OUT` | Output of 50MHz APLL clock. |
| internal | `GPIO17` | `EMAC_CLK_180` | Inverted output of 50MHz APLL clock. Found to be best suitable for LAN8720 with long signal lines. |
#### External PHY Clock
The external reference clock of 50MHz must be supplied on `GPIO0`. See note about `GPIO0` below.
#### Internal PHY Clock
The ESP32 can generate a 50MHz clock using its APLL. When the APLL is already used as clock source for other purposes (most likely I²S) external PHY has to be used.
On different test setups clock output on `GPIO0` was found unstable because in most designs the signal path is not ideal for this high frequency (the PCB trace has several devices added to it and therefore the capacitive load is relatively high)
The inverted clock signal `EMAC_CLK_180` was found working best with a LAN8720 PHY.
## RMII PHY Wiring
The following PHY connections are required for RMII PHY data connections. These `GPIO` pin assignments cannot be changed.
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 | |
| GPIO | RMII Signal | ESP32 EMAC Function | Notes |
| -------- | ----------- | ------------------- | ----- |
| `GPIO21` | `TX_EN` | `EMAC_TX_EN` | |
| `GPIO19` | `TX0` | `EMAC_TXD0` | |
| `GPIO22` | `TX1` | `EMAC_TXD1` | |
| `GPIO25` | `RX0` | `EMAC_RXD0` | |
| `GPIO26` | `RX1` | `EMAC_RXD1` | |
| `GPIO27` | `CRS_DV` | `EMAC_RX_DRV` | |
## RMII PHY SMI Wiring
@ -40,17 +60,17 @@ For the example, these pins are configured via `make menuconfig` under the Examp
| Default Example GPIO | RMII Signal | Notes |
| -------------------- | ----------- | ------------- |
| 23 | MDC | Output to PHY |
| 18 | MDIO | Bidirectional |
| `GPIO23` | `MDC` | Output to PHY |
| `GPIO18` | `MDIO` | Bidirectional |
The defaults in the example are correct for Espressif's Ethernet development board.
## Note about GPIO0
## 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.
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.
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.
The example defaults to using `GPIO17` for this function, but it can be overriden. On Espressif's Ethernet development board, `GPIO17` is the power pin used to enable/disable the PHY oscillator.

View File

@ -18,12 +18,53 @@ config PHY_LAN8720
endchoice
config PHY_ADDRESS
int "PHY Address (0-31)"
default 31
range 0 31
help
Select the PHY Address (0-31) for the hardware configuration and PHY model.
TLK110 default 31
LAN8720 default 1 or 0
choice PHY_CLOCK_MODE
prompt "EMAC clock mode"
default PHY_CLOCK_GPIO0_IN
help
Select external (input on GPIO0) or internal (output on GPIO0, GPIO16 or GPIO17) clock
config PHY_CLOCK_GPIO0_IN
bool "GPIO0 input"
help
Input of 50MHz refclock on GPIO0
config PHY_CLOCK_GPIO0_OUT
bool "GPIO0 output"
help
Output the internal 50MHz APLL clock on GPIO0
config PHY_CLOCK_GPIO16_OUT
bool "GPIO16 output"
help
Output the internal 50MHz APLL clock on GPIO16
config PHY_CLOCK_GPIO17_OUT
bool "GPIO17 output (inverted)"
help
Output the internal 50MHz APLL clock on GPIO17 (inverted signal)
endchoice
config PHY_CLOCK_MODE
int
default 0 if PHY_CLOCK_GPIO0_IN
default 1 if PHY_CLOCK_GPIO0_OUT
default 2 if PHY_CLOCK_GPIO16_OUT
default 3 if PHY_CLOCK_GPIO17_OUT
config PHY_USE_POWER_PIN
bool "Use PHY Power (enable/disable) pin"
@ -35,6 +76,7 @@ config PHY_USE_POWER_PIN
config PHY_POWER_PIN
int "PHY Power GPIO"
default 17
range 0 33
depends on PHY_USE_POWER_PIN
help
GPIO number to use for powering on/off the PHY.
@ -42,12 +84,14 @@ config PHY_POWER_PIN
config PHY_SMI_MDC_PIN
int "SMI MDC Pin"
default 23
range 0 33
help
GPIO number to use for SMI clock output MDC to PHY.
config PHY_SMI_MDIO_PIN
int "SMI MDIO Pin"
default 18
range 0 33
help
GPIO number to use for SMI data pin MDIO to/from PHY.

View File

@ -33,6 +33,10 @@
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "soc/emac_ex_reg.h"
#include "driver/periph_ctrl.h"
#ifdef CONFIG_PHY_LAN8720
#include "eth_phy/phy_lan8720.h"
#define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config
@ -129,6 +133,7 @@ void app_main()
config.phy_addr = CONFIG_PHY_ADDRESS;
config.gpio_config = eth_gpio_config_rmii;
config.tcpip_input = tcpip_adapter_eth_input;
config.clock_mode = CONFIG_PHY_CLOCK_MODE;
#ifdef CONFIG_PHY_USE_POWER_PIN
/* Replace the default 'power enable' function with an example-specific