mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
fix(esp_eth): made access to PHY registers for DM9051 more robust
This commit is contained in:
parent
c5389ac2fd
commit
60384d4fc0
@ -30,17 +30,18 @@
|
||||
|
||||
static const char *TAG = "dm9051.mac";
|
||||
|
||||
#define DM9051_SPI_LOCK_TIMEOUT_MS (50)
|
||||
#define DM9051_SPI_LOCK_TIMEOUT_MS (50)
|
||||
#define DM9051_PHY_OPERATION_TIMEOUT_US (1000)
|
||||
#define DM9051_RX_MEM_START_ADDR (3072)
|
||||
#define DM9051_RX_MEM_MAX_SIZE (16384)
|
||||
#define DM9051_RX_HDR_SIZE (4)
|
||||
#define DM9051_MULTI_REG_AXS_TIMEOUT_MS (50)
|
||||
#define DM9051_RX_MEM_START_ADDR (3072)
|
||||
#define DM9051_RX_MEM_MAX_SIZE (16384)
|
||||
#define DM9051_RX_HDR_SIZE (4)
|
||||
#define DM9051_ETH_MAC_RX_BUF_SIZE_AUTO (0)
|
||||
|
||||
typedef struct {
|
||||
uint32_t copy_len;
|
||||
uint32_t byte_cnt;
|
||||
}__attribute__((packed)) dm9051_auto_buf_info_t;
|
||||
} __attribute__((packed)) dm9051_auto_buf_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flag;
|
||||
@ -67,6 +68,7 @@ typedef struct {
|
||||
esp_eth_mediator_t *eth;
|
||||
eth_spi_custom_driver_t spi;
|
||||
TaskHandle_t rx_task_hdl;
|
||||
SemaphoreHandle_t multi_reg_axs_mutex;
|
||||
uint32_t sw_reset_timeout_ms;
|
||||
int int_gpio_num;
|
||||
esp_timer_handle_t poll_timer;
|
||||
@ -93,10 +95,10 @@ static void *dm9051_spi_init(const void *spi_config)
|
||||
spi_devcfg.address_bits = 7;
|
||||
} else {
|
||||
ESP_GOTO_ON_FALSE(dm9051_config->spi_devcfg->command_bits == 1 && dm9051_config->spi_devcfg->address_bits == 7,
|
||||
NULL, err, TAG, "incorrect SPI frame format (command_bits/address_bits)");
|
||||
NULL, err, TAG, "incorrect SPI frame format (command_bits/address_bits)");
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(spi_bus_add_device(dm9051_config->spi_host_id, &spi_devcfg, &spi->hdl) == ESP_OK,
|
||||
NULL, err, TAG, "adding device to SPI host #%i failed", dm9051_config->spi_host_id + 1);
|
||||
NULL, err, TAG, "adding device to SPI host #%i failed", dm9051_config->spi_host_id + 1);
|
||||
|
||||
/* create mutex */
|
||||
spi->lock = xSemaphoreCreateMutex();
|
||||
@ -180,12 +182,22 @@ static esp_err_t dm9051_spi_read(void *spi_ctx, uint32_t cmd, uint32_t addr, voi
|
||||
} else {
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
}
|
||||
if ((trans.flags&SPI_TRANS_USE_RXDATA) && len <= 4) {
|
||||
if ((trans.flags & SPI_TRANS_USE_RXDATA) && len <= 4) {
|
||||
memcpy(value, trans.rx_data, len); // copy register values to output
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool dm9051_mutex_lock(emac_dm9051_t *emac)
|
||||
{
|
||||
return xSemaphoreTake(emac->multi_reg_axs_mutex, pdMS_TO_TICKS(DM9051_MULTI_REG_AXS_TIMEOUT_MS)) == pdTRUE;
|
||||
}
|
||||
|
||||
static inline bool dm9051_mutex_unlock(emac_dm9051_t *emac)
|
||||
{
|
||||
return xSemaphoreGive(emac->multi_reg_axs_mutex) == pdTRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write value to dm9051 internal register
|
||||
*/
|
||||
@ -379,7 +391,7 @@ static esp_err_t emac_dm9051_start(esp_eth_mac_t *mac)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||
/* reset tx and rx memory pointer */
|
||||
/* reset tx and rx memory pointer */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX | MPTRCR_RST_TX), err, TAG, "write MPTRCR failed");
|
||||
/* clear interrupt status */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_ISR, ISR_CLR_STATUS), err, TAG, "write ISR failed");
|
||||
@ -442,59 +454,65 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_dm9051_phy_access_compl(emac_dm9051_t *emac, uint32_t timeout_us)
|
||||
{
|
||||
uint8_t epcr = 0;
|
||||
ESP_RETURN_ON_ERROR(dm9051_register_read(emac, DM9051_EPCR, &epcr), TAG, "read EPCR failed");
|
||||
uint32_t to = 0;
|
||||
if (epcr & EPCR_ERRE) {
|
||||
do {
|
||||
esp_rom_delay_us(100);
|
||||
ESP_RETURN_ON_ERROR(dm9051_register_read(emac, DM9051_EPCR, &epcr), TAG, "read EPCR failed");
|
||||
to += 100;
|
||||
} while ((epcr & EPCR_ERRE) && to < timeout_us);
|
||||
ESP_RETURN_ON_FALSE(!(epcr & EPCR_ERRE), ESP_ERR_TIMEOUT, TAG, "wait for PHY/EEPROM access completion timeouted");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t emac_dm9051_write_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||
/* check if phy access is in progress */
|
||||
uint8_t epcr = 0;
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_EPCR, &epcr), err, TAG, "read EPCR failed");
|
||||
ESP_GOTO_ON_FALSE(!(epcr & EPCR_ERRE), ESP_ERR_INVALID_STATE, err, TAG, "phy is busy");
|
||||
|
||||
/* The following commands need to be performed in atomic manner */
|
||||
ESP_RETURN_ON_FALSE(dm9051_mutex_lock(emac), ESP_ERR_TIMEOUT, TAG, "multiple register access mutex timeout");
|
||||
/* check if no PHY/EEPROM access is in progress */
|
||||
ESP_GOTO_ON_ERROR(emac_dm9051_phy_access_compl(emac, DM9051_PHY_OPERATION_TIMEOUT_US), err, TAG, "PHY is busy");
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_EPAR, (uint8_t)(((phy_addr << 6) & 0xFF) | phy_reg)), err, TAG, "write EPAR failed");
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_EPDRL, (uint8_t)(reg_value & 0xFF)), err, TAG, "write EPDRL failed");
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_EPDRH, (uint8_t)((reg_value >> 8) & 0xFF)), err, TAG, "write EPDRH failed");
|
||||
/* select PHY and select write operation */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRW), err, TAG, "write EPCR failed");
|
||||
/* polling the busy flag */
|
||||
uint32_t to = 0;
|
||||
do {
|
||||
esp_rom_delay_us(100);
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_EPCR, &epcr), err, TAG, "read EPCR failed");
|
||||
to += 100;
|
||||
} while ((epcr & EPCR_ERRE) && to < DM9051_PHY_OPERATION_TIMEOUT_US);
|
||||
ESP_GOTO_ON_FALSE(!(epcr & EPCR_ERRE), ESP_ERR_TIMEOUT, err, TAG, "phy is busy");
|
||||
return ESP_OK;
|
||||
/* wait for PHY access completion */
|
||||
ESP_GOTO_ON_ERROR(emac_dm9051_phy_access_compl(emac, DM9051_PHY_OPERATION_TIMEOUT_US), err, TAG, "PHY access completion check failed");
|
||||
err:
|
||||
dm9051_mutex_unlock(emac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t emac_dm9051_read_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t *reg_value)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(reg_value, ESP_ERR_INVALID_ARG, err, TAG, "can't set reg_value to null");
|
||||
ESP_RETURN_ON_FALSE(reg_value, ESP_ERR_INVALID_ARG, TAG, "can't set reg_value to null");
|
||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||
/* check if phy access is in progress */
|
||||
uint8_t epcr = 0;
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_EPCR, &epcr), err, TAG, "read EPCR failed");
|
||||
ESP_GOTO_ON_FALSE(!(epcr & 0x01), ESP_ERR_INVALID_STATE, err, TAG, "phy is busy");
|
||||
|
||||
/* The following commands need to be performed in atomic manner */
|
||||
ESP_RETURN_ON_FALSE(dm9051_mutex_lock(emac), ESP_ERR_TIMEOUT, TAG, "multiple register access mutex timeout");
|
||||
/* check if no PHY/EEPROM access is in progress */
|
||||
ESP_GOTO_ON_ERROR(emac_dm9051_phy_access_compl(emac, DM9051_PHY_OPERATION_TIMEOUT_US), err, TAG, "PHY is busy");
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_EPAR, (uint8_t)(((phy_addr << 6) & 0xFF) | phy_reg)), err, TAG, "write EPAR failed");
|
||||
/* Select PHY and select read operation */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_EPCR, 0x0C), err, TAG, "write EPCR failed");
|
||||
/* polling the busy flag */
|
||||
uint32_t to = 0;
|
||||
do {
|
||||
esp_rom_delay_us(100);
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_EPCR, &epcr), err, TAG, "read EPCR failed");
|
||||
to += 100;
|
||||
} while ((epcr & EPCR_ERRE) && to < DM9051_PHY_OPERATION_TIMEOUT_US);
|
||||
ESP_GOTO_ON_FALSE(!(epcr & EPCR_ERRE), ESP_ERR_TIMEOUT, err, TAG, "phy is busy");
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRR), err, TAG, "write EPCR failed");
|
||||
/* wait for PHY access completion */
|
||||
ESP_GOTO_ON_ERROR(emac_dm9051_phy_access_compl(emac, DM9051_PHY_OPERATION_TIMEOUT_US), err, TAG, "PHY access completion check failed");
|
||||
uint8_t value_h = 0;
|
||||
uint8_t value_l = 0;
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_EPDRH, &value_h), err, TAG, "read EPDRH failed");
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_EPDRL, &value_l), err, TAG, "read EPDRL failed");
|
||||
*reg_value = (value_h << 8) | value_l;
|
||||
return ESP_OK;
|
||||
err:
|
||||
dm9051_mutex_unlock(emac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -530,14 +548,14 @@ static esp_err_t emac_dm9051_set_link(esp_eth_mac_t *mac, eth_link_t link)
|
||||
ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "dm9051 start failed");
|
||||
if (emac->poll_timer) {
|
||||
ESP_GOTO_ON_ERROR(esp_timer_start_periodic(emac->poll_timer, emac->poll_period_ms * 1000),
|
||||
err, TAG, "start poll timer failed");
|
||||
err, TAG, "start poll timer failed");
|
||||
}
|
||||
break;
|
||||
case ETH_LINK_DOWN:
|
||||
ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "dm9051 stop failed");
|
||||
if (emac->poll_timer) {
|
||||
ESP_GOTO_ON_ERROR(esp_timer_stop(emac->poll_timer),
|
||||
err, TAG, "stop poll timer failed");
|
||||
err, TAG, "stop poll timer failed");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -633,12 +651,12 @@ static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t
|
||||
uint8_t tcr = 0;
|
||||
|
||||
ESP_GOTO_ON_FALSE(length <= ETH_MAX_PACKET_SIZE, ESP_ERR_INVALID_ARG, err,
|
||||
TAG, "frame size is too big (actual %" PRIu32 ", maximum %d)", length, ETH_MAX_PACKET_SIZE);
|
||||
TAG, "frame size is too big (actual %" PRIu32 ", maximum %d)", length, ETH_MAX_PACKET_SIZE);
|
||||
|
||||
int64_t wait_time = esp_timer_get_time();
|
||||
do {
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_TCR, &tcr), err, TAG, "read TCR failed");
|
||||
} while((tcr & TCR_TXREQ) && ((esp_timer_get_time() - wait_time) < 100));
|
||||
} while ((tcr & TCR_TXREQ) && ((esp_timer_get_time() - wait_time) < 100));
|
||||
|
||||
if (tcr & TCR_TXREQ) {
|
||||
ESP_LOGE(TAG, "last transmit still in progress, cannot send.");
|
||||
@ -782,7 +800,7 @@ static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t
|
||||
|
||||
/* dummy read, get the most updated data */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed");
|
||||
/* check for remaing packets */
|
||||
/* check for remaining packets */
|
||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed");
|
||||
emac->packets_remain = rxbyte > 0;
|
||||
return ESP_OK;
|
||||
@ -808,7 +826,7 @@ static esp_err_t emac_dm9051_init(esp_eth_mac_t *mac)
|
||||
/* reset dm9051 */
|
||||
ESP_GOTO_ON_ERROR(dm9051_reset(emac), err, TAG, "reset dm9051 failed");
|
||||
/* verify chip id */
|
||||
ESP_GOTO_ON_ERROR(dm9051_verify_id(emac), err, TAG, "vefiry chip ID failed");
|
||||
ESP_GOTO_ON_ERROR(dm9051_verify_id(emac), err, TAG, "verify chip ID failed");
|
||||
/* default setup of internal registers */
|
||||
ESP_GOTO_ON_ERROR(dm9051_setup_default(emac), err, TAG, "dm9051 default setup failed");
|
||||
/* clear multicast hash table */
|
||||
@ -850,7 +868,7 @@ static void emac_dm9051_task(void *arg)
|
||||
// check if the task receives any notification
|
||||
if (emac->int_gpio_num >= 0) { // if in interrupt mode
|
||||
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ...
|
||||
gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted
|
||||
gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted
|
||||
continue; // -> just continue to check again
|
||||
}
|
||||
} else {
|
||||
@ -909,6 +927,7 @@ static esp_err_t emac_dm9051_del(esp_eth_mac_t *mac)
|
||||
}
|
||||
vTaskDelete(emac->rx_task_hdl);
|
||||
emac->spi.deinit(emac->spi.ctx);
|
||||
vSemaphoreDelete(emac->multi_reg_axs_mutex);
|
||||
heap_caps_free(emac->rx_buffer);
|
||||
free(emac);
|
||||
return ESP_OK;
|
||||
@ -947,7 +966,7 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config,
|
||||
emac->parent.receive = emac_dm9051_receive;
|
||||
|
||||
if (dm9051_config->custom_spi_driver.init != NULL && dm9051_config->custom_spi_driver.deinit != NULL
|
||||
&& dm9051_config->custom_spi_driver.read != NULL && dm9051_config->custom_spi_driver.write != NULL) {
|
||||
&& dm9051_config->custom_spi_driver.read != NULL && dm9051_config->custom_spi_driver.write != NULL) {
|
||||
ESP_LOGD(TAG, "Using user's custom SPI Driver");
|
||||
emac->spi.init = dm9051_config->custom_spi_driver.init;
|
||||
emac->spi.deinit = dm9051_config->custom_spi_driver.deinit;
|
||||
@ -965,13 +984,17 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config,
|
||||
ESP_GOTO_ON_FALSE((emac->spi.ctx = emac->spi.init(dm9051_config)) != NULL, NULL, err, TAG, "SPI initialization failed");
|
||||
}
|
||||
|
||||
/* create mutex for accessing multiple registers in atomic manner */
|
||||
emac->multi_reg_axs_mutex = xSemaphoreCreateMutex();
|
||||
ESP_GOTO_ON_FALSE(emac->multi_reg_axs_mutex, NULL, err, TAG, "create multi registers access mutex failed");
|
||||
|
||||
/* create dm9051 task */
|
||||
BaseType_t core_num = tskNO_AFFINITY;
|
||||
if (mac_config->flags & ETH_MAC_FLAG_PIN_TO_CORE) {
|
||||
core_num = esp_cpu_get_core_id();
|
||||
}
|
||||
BaseType_t xReturned = xTaskCreatePinnedToCore(emac_dm9051_task, "dm9051_tsk", mac_config->rx_task_stack_size, emac,
|
||||
mac_config->rx_task_prio, &emac->rx_task_hdl, core_num);
|
||||
mac_config->rx_task_prio, &emac->rx_task_hdl, core_num);
|
||||
ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, err, TAG, "create dm9051 task failed");
|
||||
|
||||
emac->rx_buffer = heap_caps_malloc(ETH_MAX_PACKET_SIZE + DM9051_RX_HDR_SIZE, MALLOC_CAP_DMA);
|
||||
@ -1000,6 +1023,9 @@ err:
|
||||
if (emac->spi.ctx) {
|
||||
emac->spi.deinit(emac->spi.ctx);
|
||||
}
|
||||
if (emac->multi_reg_axs_mutex) {
|
||||
vSemaphoreDelete(emac->multi_reg_axs_mutex);
|
||||
}
|
||||
heap_caps_free(emac->rx_buffer);
|
||||
free(emac);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user