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
40d398199f
commit
5c53238853
@ -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)
|
||||
@ -39,7 +40,7 @@ static const char *TAG = "dm9051.mac";
|
||||
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;
|
||||
@ -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;
|
||||
@ -179,12 +181,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
|
||||
*/
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -637,7 +655,7 @@ static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t
|
||||
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.");
|
||||
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user