Merge branch 'bugfix/dm9051_phy_axs_wait_v5.2' into 'release/v5.2'

fix(esp_eth): made access to PHY registers for DM9051 more robust (v5.2)

See merge request espressif/esp-idf!30279
This commit is contained in:
morris 2024-04-17 10:41:16 +08:00
commit b60280e2cf

View File

@ -31,6 +31,7 @@ static const char *TAG = "dm9051.mac";
#define DM9051_SPI_LOCK_TIMEOUT_MS (50)
#define DM9051_PHY_OPERATION_TIMEOUT_US (1000)
#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)
@ -66,6 +67,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;
@ -185,6 +187,16 @@ static esp_err_t dm9051_spi_read(void *spi_ctx, uint32_t cmd, uint32_t addr, voi
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
*/
@ -441,59 +453,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;
}
@ -781,7 +799,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;
@ -807,7 +825,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 */
@ -908,6 +926,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;
@ -964,6 +983,10 @@ 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) {
@ -999,6 +1022,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);
}