forked from espressif/esp-idf
Add ENC28J60 Ethernet Driver
Merges https://github.com/espressif/esp-idf/pull/4435
This commit is contained in:
committed by
suda-morris
parent
966f4227ad
commit
eda07acc81
@@ -114,11 +114,17 @@ menu "Ethernet"
|
|||||||
if ETH_USE_SPI_ETHERNET
|
if ETH_USE_SPI_ETHERNET
|
||||||
config ETH_SPI_ETHERNET_DM9051
|
config ETH_SPI_ETHERNET_DM9051
|
||||||
bool "Use DM9051"
|
bool "Use DM9051"
|
||||||
default y
|
|
||||||
help
|
help
|
||||||
DM9051 is a fast Ethernet controller with an SPI interface.
|
DM9051 is a fast Ethernet controller with an SPI interface.
|
||||||
It's also integrated with a 10/100M PHY and MAC.
|
It's also integrated with a 10/100M PHY and MAC.
|
||||||
Set true to enable DM9051 driver.
|
Select to enable DM9051 driver.
|
||||||
|
|
||||||
|
config ETH_SPI_ETHERNET_ENC28J60
|
||||||
|
bool "Use ENC28J60"
|
||||||
|
help
|
||||||
|
ENC28J60 is a Stand-Alone Ethernet Controller with SPI Interface.
|
||||||
|
It has an integrated MAC and 10Base-T PHY.
|
||||||
|
Select to enable ENC28J60 driver.
|
||||||
endif
|
endif
|
||||||
|
|
||||||
menuconfig ETH_USE_OPENETH
|
menuconfig ETH_USE_OPENETH
|
||||||
|
@@ -315,6 +315,14 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, const eth_mac_config_t *mac_config);
|
esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, const eth_mac_config_t *mac_config);
|
||||||
#endif
|
#endif
|
||||||
|
#if CONFIG_ETH_SPI_ETHERNET_ENC28J60
|
||||||
|
/**
|
||||||
|
* @brief ENC28J60 specific configuration
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
spi_device_handle_t spi_hdl; /*!< Handle of SPI device driver */
|
||||||
|
} eth_ENC28J60_config_t;
|
||||||
|
|
||||||
#if CONFIG_ETH_USE_OPENETH
|
#if CONFIG_ETH_USE_OPENETH
|
||||||
esp_eth_mac_t *esp_eth_mac_new_openeth(const eth_mac_config_t *config);
|
esp_eth_mac_t *esp_eth_mac_new_openeth(const eth_mac_config_t *config);
|
||||||
|
@@ -252,6 +252,19 @@ esp_eth_phy_t *esp_eth_phy_new_dp83848(const eth_phy_config_t *config);
|
|||||||
*/
|
*/
|
||||||
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config);
|
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_ETH_SPI_ETHERNET_ENC28J60
|
||||||
|
/**
|
||||||
|
* @brief Create a PHY instance of ENC28J60
|
||||||
|
*
|
||||||
|
* @param[in] config: configuration of PHY
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - instance: create PHY instance successfully
|
||||||
|
* - NULL: create PHY instance failed because some error occurred
|
||||||
|
*/
|
||||||
|
esp_eth_phy_t *esp_eth_phy_new_ENC28J60(const eth_phy_config_t *config);
|
||||||
|
#endif
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
1256
components/esp_eth/src/esp_eth_mac_ENC28J60.c
Normal file
1256
components/esp_eth/src/esp_eth_mac_ENC28J60.c
Normal file
File diff suppressed because it is too large
Load Diff
250
components/esp_eth/src/esp_eth_phy_ENC28J60.c
Normal file
250
components/esp_eth/src/esp_eth_phy_ENC28J60.c
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_eth.h"
|
||||||
|
#include "eth_phy_regs_struct.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const char *TAG = "ENC28J60";
|
||||||
|
#define PHY_CHECK(a, str, goto_tag, ...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if (!(a)) \
|
||||||
|
{ \
|
||||||
|
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
goto goto_tag; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHYIDR1(PHY Identifier Register 1)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint32_t PHY_ID_MSB : 16; /*!< Organizationally Unique Identifier(OUI) most significant bits */
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
} phy_id1_reg_t;
|
||||||
|
#define ETH_PHYID1_ADDR (0x02)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHYIDR2(PHY Identifier Register 2)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint32_t PHY_ID_LSB : 6; /*!< Model revision number */
|
||||||
|
uint32_t PHY_PN : 6; /*!< Vendor model number */
|
||||||
|
uint32_t PHY_rev : 4; /*!< Organizationally Unique Identifier(OUI) least significant bits */
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
} phy_id2_reg_t;
|
||||||
|
#define ETH_PHYID2_ADDR (0x03)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHSTAT2(PHY Configuration and Status Register)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
uint32_t reserved_4_0 : 5;
|
||||||
|
uint32_t PLRITY : 1;
|
||||||
|
uint32_t reserved_8_6 : 3;
|
||||||
|
uint32_t DPXSTAT : 1;
|
||||||
|
uint32_t LSTAT : 1;
|
||||||
|
uint32_t COLSTAT : 1;
|
||||||
|
uint32_t RXSTAT : 1;
|
||||||
|
uint32_t TXSTAT : 1;
|
||||||
|
uint32_t reserved_15_14 : 2;
|
||||||
|
};
|
||||||
|
uint32_t val;
|
||||||
|
} phstat2_reg_t;
|
||||||
|
#define ETH_PHY_PHSTAT2_REG_ADDR (0x11)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_eth_phy_t parent;
|
||||||
|
esp_eth_mediator_t *eth;
|
||||||
|
const char *name;
|
||||||
|
uint32_t addr;
|
||||||
|
uint32_t reset_timeout_ms;
|
||||||
|
uint32_t autonego_timeout_ms;
|
||||||
|
eth_link_t link_status;
|
||||||
|
} phy_ENC28J60_t;
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
|
||||||
|
{
|
||||||
|
PHY_CHECK(eth, "can't set mediator for ENC28J60 to null", err);
|
||||||
|
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
|
||||||
|
ENC28J60->eth = eth;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_get_link(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = ENC28J60->eth;
|
||||||
|
bmsr_reg_t PHstat1;
|
||||||
|
phstat2_reg_t PHstat2;
|
||||||
|
|
||||||
|
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHY_BMSR_REG_ADDR, &(PHstat1.val)) == ESP_OK, "read PHSTAT1 failed", err);
|
||||||
|
|
||||||
|
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHY_PHSTAT2_REG_ADDR, &(PHstat2.val)) == ESP_OK, "read PHSTAT1 failed", err);
|
||||||
|
eth_link_t link = PHstat2.LSTAT ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
|
|
||||||
|
if (ENC28J60->link_status != link) {
|
||||||
|
if (link == ETH_LINK_UP) {
|
||||||
|
phy->negotiate(phy);
|
||||||
|
} else {
|
||||||
|
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
|
||||||
|
ENC28J60->link_status = link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_reset(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
/*ENC28J60 should be reset using the MAC reset function*/
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_negotiate(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = ENC28J60->eth;
|
||||||
|
/* Start auto negotiation */
|
||||||
|
bmcr_reg_t PHCON1 = {
|
||||||
|
.speed_select = 0, /* 10Mbps */
|
||||||
|
.duplex_mode = 0, /* Half Duplex */
|
||||||
|
.en_auto_nego = 0, /* no Auto Negotiation */
|
||||||
|
.restart_auto_nego = 0 /* dont Restart Auto Negotiation */
|
||||||
|
};
|
||||||
|
PHY_CHECK(eth->phy_reg_write(eth, ENC28J60->addr, ETH_PHY_BMCR_REG_ADDR, PHCON1.val) == ESP_OK, "write PHCON1 failed", err);
|
||||||
|
|
||||||
|
bmsr_reg_t PHSTAT1;
|
||||||
|
phstat2_reg_t PHSTAT2;
|
||||||
|
|
||||||
|
/* Update information about link, speed, duplex */
|
||||||
|
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHY_BMSR_REG_ADDR, &(PHSTAT1.val)) == ESP_OK, "read PHSTAT1 failed", err);
|
||||||
|
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, 0x11, &(PHSTAT2.val)) == ESP_OK, "read PHSTAT2 failed", err);
|
||||||
|
eth_link_t link = PHSTAT2.LSTAT ? ETH_LINK_UP : ETH_LINK_DOWN;
|
||||||
|
|
||||||
|
eth_speed_t speed = ETH_SPEED_10M;
|
||||||
|
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||||
|
|
||||||
|
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK, "send speed event failed", err);
|
||||||
|
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK, "send duplex event failed", err);
|
||||||
|
|
||||||
|
if (ENC28J60->link_status != link) {
|
||||||
|
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK, "send link event failed", err);
|
||||||
|
ENC28J60->link_status = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_pwrctl(esp_eth_phy_t *phy, bool enable)
|
||||||
|
{
|
||||||
|
/*ToDo: Add power control function*/
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_set_addr(esp_eth_phy_t *phy, uint32_t addr)
|
||||||
|
{
|
||||||
|
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
|
||||||
|
ENC28J60->addr = addr;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
|
||||||
|
{
|
||||||
|
PHY_CHECK(addr, "get phy address failed", err);
|
||||||
|
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
|
||||||
|
*addr = ENC28J60->addr;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_del(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
|
||||||
|
free(ENC28J60);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_init(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
phy_ENC28J60_t *ENC28J60 = __containerof(phy, phy_ENC28J60_t, parent);
|
||||||
|
esp_eth_mediator_t *eth = ENC28J60->eth;
|
||||||
|
/* Power on Ethernet PHY */
|
||||||
|
PHY_CHECK(ENC28J60_pwrctl(phy, true) == ESP_OK, "power on Ethernet PHY failed", err);
|
||||||
|
/* Reset Ethernet PHY */
|
||||||
|
PHY_CHECK(ENC28J60_reset(phy) == ESP_OK, "reset Ethernet PHY failed", err);
|
||||||
|
/* Check PHY ID */
|
||||||
|
phy_id1_reg_t id1;
|
||||||
|
phy_id2_reg_t id2;
|
||||||
|
uint32_t id3;
|
||||||
|
|
||||||
|
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHYID1_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
|
||||||
|
PHY_CHECK(eth->phy_reg_read(eth, ENC28J60->addr, ETH_PHYID2_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
|
||||||
|
|
||||||
|
id3 = ((id1.PHY_ID_MSB << 16) | id2.PHY_ID_LSB);
|
||||||
|
PHY_CHECK(id3, "wrong PHY chip ID", err);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t ENC28J60_deinit(esp_eth_phy_t *phy)
|
||||||
|
{
|
||||||
|
/* Power off Ethernet PHY */
|
||||||
|
PHY_CHECK(ENC28J60_pwrctl(phy, false) == ESP_OK, "power off Ethernet PHY failed", err);
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_eth_phy_t *esp_eth_phy_new_ENC28J60(const eth_phy_config_t *config)
|
||||||
|
{
|
||||||
|
PHY_CHECK(config, "can't set phy config to null", err);
|
||||||
|
PHY_CHECK(config->phy_addr == 1, "ENC28J60's phy address can only set to 1", err);
|
||||||
|
phy_ENC28J60_t *ENC28J60 = calloc(1, sizeof(phy_ENC28J60_t));
|
||||||
|
PHY_CHECK(ENC28J60, "calloc ENC28J60 object failed", err);
|
||||||
|
ENC28J60->name = "ENC28J60";
|
||||||
|
ENC28J60->addr = config->phy_addr;
|
||||||
|
ENC28J60->reset_timeout_ms = config->reset_timeout_ms;
|
||||||
|
ENC28J60->link_status = ETH_LINK_DOWN;
|
||||||
|
ENC28J60->autonego_timeout_ms = config->autonego_timeout_ms;
|
||||||
|
ENC28J60->parent.reset = ENC28J60_reset;
|
||||||
|
ENC28J60->parent.init = ENC28J60_init;
|
||||||
|
ENC28J60->parent.deinit = ENC28J60_deinit;
|
||||||
|
ENC28J60->parent.set_mediator = ENC28J60_set_mediator;
|
||||||
|
ENC28J60->parent.negotiate = ENC28J60_negotiate;
|
||||||
|
ENC28J60->parent.get_link = ENC28J60_get_link;
|
||||||
|
ENC28J60->parent.pwrctl = ENC28J60_pwrctl;
|
||||||
|
ENC28J60->parent.get_addr = ENC28J60_get_addr;
|
||||||
|
ENC28J60->parent.set_addr = ENC28J60_set_addr;
|
||||||
|
ENC28J60->parent.del = ENC28J60_del;
|
||||||
|
|
||||||
|
return &(ENC28J60->parent);
|
||||||
|
err:
|
||||||
|
return NULL;
|
||||||
|
}
|
@@ -18,7 +18,14 @@ menu "Example Configuration"
|
|||||||
select ETH_USE_SPI_ETHERNET
|
select ETH_USE_SPI_ETHERNET
|
||||||
select ETH_SPI_ETHERNET_DM9051
|
select ETH_SPI_ETHERNET_DM9051
|
||||||
help
|
help
|
||||||
Select external SPI-Ethernet module.
|
Select external SPI-Ethernet module (DM9051).
|
||||||
|
|
||||||
|
config EXAMPLE_USE_ENC28J60
|
||||||
|
bool "ENC28J60 Module"
|
||||||
|
select ETH_USE_SPI_ETHERNET
|
||||||
|
select ETH_SPI_ETHERNET_ENC28J60
|
||||||
|
help
|
||||||
|
Select external SPI-Ethernet module (ENC28J60).
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
if EXAMPLE_USE_INTERNAL_ETHERNET
|
if EXAMPLE_USE_INTERNAL_ETHERNET
|
||||||
@@ -72,7 +79,7 @@ menu "Example Configuration"
|
|||||||
range 0 2
|
range 0 2
|
||||||
default 1
|
default 1
|
||||||
help
|
help
|
||||||
Set the SPI host used to communicate with DM9051.
|
Set the SPI host used to communicate with the SPI Ethernet Controller.
|
||||||
|
|
||||||
config EXAMPLE_DM9051_SCLK_GPIO
|
config EXAMPLE_DM9051_SCLK_GPIO
|
||||||
int "SPI SCLK GPIO number"
|
int "SPI SCLK GPIO number"
|
||||||
@@ -104,7 +111,7 @@ menu "Example Configuration"
|
|||||||
|
|
||||||
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
|
config EXAMPLE_DM9051_SPI_CLOCK_MHZ
|
||||||
int "SPI clock speed (MHz)"
|
int "SPI clock speed (MHz)"
|
||||||
range 20 80
|
range 5 80
|
||||||
default 20
|
default 20
|
||||||
help
|
help
|
||||||
Set the clock speed (MHz) of SPI interface.
|
Set the clock speed (MHz) of SPI interface.
|
||||||
|
@@ -119,6 +119,20 @@ void app_main(void)
|
|||||||
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
|
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_DM9051_INT_GPIO;
|
||||||
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
|
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
|
||||||
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
|
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
|
||||||
|
#elif CONFIG_EXAMPLE_ETH_PHY_ENC28J60
|
||||||
|
/* ENC28J60 ethernet driver is based on spi driver */
|
||||||
|
spi_device_interface_config_t devcfg = {
|
||||||
|
.command_bits = 3,
|
||||||
|
.address_bits = 5,
|
||||||
|
.mode = 0,
|
||||||
|
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
|
||||||
|
.spics_io_num = CONFIG_EXAMPLE_ETH_CS_GPIO,
|
||||||
|
.queue_size = 20
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
|
||||||
|
eth_ENC28J60_config_t ENC28J60_config = ETH_ENC28J60_DEFAULT_CONFIG(spi_handle);
|
||||||
|
esp_eth_mac_t *mac = esp_eth_mac_new_ENC28J60(&ENC28J60_config, &mac_config);
|
||||||
|
esp_eth_phy_t *phy = esp_eth_phy_new_ENC28J60(&phy_config);
|
||||||
#endif
|
#endif
|
||||||
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
|
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
|
||||||
esp_eth_handle_t eth_handle = NULL;
|
esp_eth_handle_t eth_handle = NULL;
|
||||||
|
Reference in New Issue
Block a user