Merge branch 'feature/esp_eth_ksz8851snl' into 'master'

Add a KSZ8851SNL SPI Ethernet driver

Closes IDFGH-4736 and IDFGH-4837

See merge request espressif/esp-idf!12574
This commit is contained in:
David Čermák
2021-04-23 05:14:16 +00:00
10 changed files with 1457 additions and 1 deletions

View File

@@ -37,6 +37,11 @@ if(CONFIG_ETH_ENABLED)
"src/esp_eth_phy_w5500.c")
endif()
if(CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL)
list(APPEND srcs "src/esp_eth_mac_ksz8851snl.c"
"src/esp_eth_phy_ksz8851snl.c")
endif()
if(CONFIG_ETH_USE_OPENETH)
list(APPEND srcs "src/esp_eth_mac_openeth.c")
endif()

View File

@@ -139,6 +139,13 @@ menu "Ethernet"
However the driver in ESP-IDF only enables the RAW MAC mode,
making it compatible with the software TCP/IP stack.
Say yes to enable W5500 driver.
config ETH_SPI_ETHERNET_KSZ8851SNL
bool "Use KSZ8851SNL"
help
The KSZ8851SNL is a single-chip Fast Ethernet controller consisting of
a 10/100 physical layer transceiver (PHY), a MAC, and a Serial Peripheral Interface (SPI).
Select this to enable KSZ8851SNL driver.
endif # ETH_USE_SPI_ETHERNET
menuconfig ETH_USE_OPENETH

View File

@@ -16,6 +16,10 @@ ifndef CONFIG_ETH_SPI_ETHERNET_W5500
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_w5500.o src/esp_eth_phy_w5500.o
endif
ifndef CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_ksz8851snl.o src/esp_eth_phy_ksz8851snl.o
endif
ifndef CONFIG_ETH_USE_OPENETH
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_openeth.o
endif

View File

@@ -396,6 +396,39 @@ typedef struct {
esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, const eth_mac_config_t *mac_config);
#endif // CONFIG_ETH_SPI_ETHERNET_W5500
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
/**
* @brief KSZ8851SNL specific configuration
*
*/
typedef struct {
void *spi_hdl; /*!< Handle of SPI device driver */
int int_gpio_num; /*!< Interrupt GPIO number */
} eth_ksz8851snl_config_t;
/**
* @brief Default KSZ8851SNL specific configuration
*
*/
#define ETH_KSZ8851SNL_DEFAULT_CONFIG(spi_device) \
{ \
.spi_hdl = spi_device, \
.int_gpio_num = 14, \
}
/**
* @brief Create KSZ8851SNL Ethernet MAC instance
*
* @param ksz8851snl_config: KSZ8851SNL specific configuration
* @param mac_config: Ethernet MAC configuration
*
* @return
* - instance: create MAC instance successfully
* - NULL: create MAC instance failed because some error occurred
*/
esp_eth_mac_t *esp_eth_mac_new_ksz8851snl(const eth_ksz8851snl_config_t *ksz8851snl_config, const eth_mac_config_t *mac_config);
#endif // CONFIG_ETH_SPI_ETHERNET_KSZ8851
#if CONFIG_ETH_USE_OPENETH
/**
* @brief Create OpenCores Ethernet MAC instance

View File

@@ -300,6 +300,19 @@ esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config);
*/
esp_eth_phy_t *esp_eth_phy_new_w5500(const eth_phy_config_t *config);
#endif
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
/**
* @brief Create a PHY instance of KSZ8851SNL
*
* @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_ksz8851snl(const eth_phy_config_t *config);
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,734 @@
// Copyright (c) 2021 Vladimir Chistyakov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <string.h>
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_eth.h"
#include "ksz8851.h"
typedef struct {
esp_eth_mac_t parent;
esp_eth_mediator_t *eth;
spi_device_handle_t spi_hdl;
SemaphoreHandle_t spi_lock;
TaskHandle_t rx_task_hdl;
uint32_t sw_reset_timeout_ms;
int int_gpio_num;
uint8_t *rx_buffer;
uint8_t *tx_buffer;
} emac_ksz8851snl_t;
typedef enum {
KSZ8851_SPI_COMMAND_READ_REG = 0x0U,
KSZ8851_SPI_COMMAND_WRITE_REG = 0x1U,
KSZ8851_SPI_COMMAND_READ_FIFO = 0x2U,
KSZ8851_SPI_COMMAND_WRITE_FIFO = 0x3U,
} ksz8851_spi_commands_t;
typedef enum {
KSZ8851_QMU_PACKET_LENGTH = 2000U,
KSZ8851_QMU_PACKET_PADDING = 16U,
} ksz8851_qmu_packet_size_t;
static const char *TAG = "ksz8851snl-mac";
static const unsigned KSZ8851_SPI_COMMAND_BITS = 2U;
static const unsigned KSZ8851_SPI_ADDR_SHIFT = 2U;
static const unsigned KSZ8851_SPI_BYTE_MASK_SHIFT = 8U + KSZ8851_SPI_ADDR_SHIFT;
static const unsigned KSZ8851_SPI_LOCK_TIMEOUT_MS = 500U;
static const uint16_t RXDTTR_INIT_VALUE = 0x03E8U;
static const uint16_t RXDBCTR_INIT_VALUE = 0x1000U;
static const uint16_t RXFCT_INIT_VALUE = 0X0001U;
IRAM_ATTR static void ksz8851_isr_handler(void *arg)
{
emac_ksz8851snl_t *emac = (emac_ksz8851snl_t *)arg;
BaseType_t high_task_wakeup = pdFALSE;
vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup);
if (high_task_wakeup != pdFALSE) {
portYIELD_FROM_ISR();
}
}
static inline bool ksz8851_mutex_lock(emac_ksz8851snl_t *emac)
{
return xSemaphoreTakeRecursive(emac->spi_lock, pdMS_TO_TICKS(KSZ8851_SPI_LOCK_TIMEOUT_MS)) == pdTRUE;
}
static inline bool ksz8851_mutex_unlock(emac_ksz8851snl_t *emac)
{
return xSemaphoreGiveRecursive(emac->spi_lock) == pdTRUE;
}
static esp_err_t ksz8851_read_reg(emac_ksz8851snl_t *emac, uint32_t address, uint16_t *value)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(value != NULL, ESP_ERR_INVALID_ARG, err, TAG, "out pointer must not be null");
ESP_GOTO_ON_FALSE((address & ~KSZ8851_VALID_ADDRESS_MASK) == 0U, ESP_ERR_INVALID_ARG, err, TAG, "address is out of bounds");
const unsigned data_size = 16U; // NOTE(v.chistyakov): bits
// NOTE(v.chistyakov): select upper or lower word inside a dword
const unsigned byte_mask = 0x3U << (KSZ8851_SPI_BYTE_MASK_SHIFT + (address & 0x2U));
address <<= KSZ8851_SPI_ADDR_SHIFT;
spi_transaction_ext_t trans = {
.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY | SPI_TRANS_USE_RXDATA,
.base.cmd = KSZ8851_SPI_COMMAND_READ_REG,
.base.addr = address | byte_mask,
.base.length = data_size,
.command_bits = KSZ8851_SPI_COMMAND_BITS,
.address_bits = 16 - KSZ8851_SPI_COMMAND_BITS,
};
if (ksz8851_mutex_lock(emac)) {
if (spi_device_polling_transmit(emac->spi_hdl, &trans.base) != ESP_OK) {
ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__);
ret = ESP_FAIL;
}
ksz8851_mutex_unlock(emac);
memcpy(value, trans.base.rx_data, data_size >> 3U);
ESP_LOGV(TAG, "reading reg 0x%02x == 0x%04x", address, *value);
} else {
ret = ESP_ERR_TIMEOUT;
}
err:
return ret;
}
static esp_err_t ksz8851_write_reg(emac_ksz8851snl_t *emac, uint32_t address, uint16_t value)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE((address & ~KSZ8851_VALID_ADDRESS_MASK) == 0U, ESP_ERR_INVALID_ARG, err, TAG, "address is out of bounds");
ESP_LOGV(TAG, "writing reg 0x%02x = 0x%04x", address, value);
const unsigned data_size = 16U; // NOTE(v.chistyakov): bits
// NOTE(v.chistyakov): select upper or lower word inside a dword
const unsigned byte_mask = 0x3U << (KSZ8851_SPI_BYTE_MASK_SHIFT + (address & 0x2U));
address <<= KSZ8851_SPI_ADDR_SHIFT;
spi_transaction_ext_t trans = {
.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY | SPI_TRANS_USE_TXDATA,
.base.cmd = KSZ8851_SPI_COMMAND_WRITE_REG,
.base.addr = address | byte_mask,
.base.length = data_size,
.command_bits = KSZ8851_SPI_COMMAND_BITS,
.address_bits = 16 - KSZ8851_SPI_COMMAND_BITS,
};
memcpy(trans.base.tx_data, &value, data_size >> 3U);
if (ksz8851_mutex_lock(emac)) {
if (spi_device_polling_transmit(emac->spi_hdl, &trans.base) != ESP_OK) {
ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__);
ret = ESP_FAIL;
}
ksz8851_mutex_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
err:
return ret;
}
static esp_err_t ksz8851_set_bits(emac_ksz8851snl_t *emac, uint32_t address, uint16_t value)
{
esp_err_t ret = ESP_OK;
uint16_t old;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, address, &old), err, TAG, "failed to read reg 0x%x", address);
old |= value;
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, address, old), err, TAG, "failed to write reg 0x%x", address);
err:
return ret;
}
static esp_err_t ksz8851_clear_bits(emac_ksz8851snl_t *emac, uint32_t address, uint16_t value)
{
esp_err_t ret = ESP_OK;
uint16_t old;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, address, &old), err, TAG, "failed to read reg 0x%x", address);
old &= ~value;
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, address, old), err, TAG, "failed to write reg 0x%x", address);
err:
return ret;
}
static esp_err_t emac_ksz8851_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "mediator can not be null");
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
emac->eth = eth;
return ESP_OK;
err:
return ret;
}
static esp_err_t init_soft_reset(emac_ksz8851snl_t *emac)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_PMECR, PMECR_WAKEUP_TO_NORMAL), err, TAG, "PMECR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_GRR, GRR_GLOBAL_SOFT_RESET), err, TAG, "GRR write failed");
vTaskDelay(pdMS_TO_TICKS(emac->sw_reset_timeout_ms));
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_GRR, GRR_GLOBAL_SOFT_RESET), err, TAG, "GRR write failed");
vTaskDelay(pdMS_TO_TICKS(emac->sw_reset_timeout_ms));
return ESP_OK;
err:
return ret;
}
static esp_err_t init_verify_chipid(emac_ksz8851snl_t *emac)
{
uint16_t id;
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_CIDER, &id), err, TAG, "CIDER read failed");
uint8_t family_id = (id & CIDER_FAMILY_ID_MASK) >> CIDER_FAMILY_ID_SHIFT;
uint8_t chip_id = (id & CIDER_CHIP_ID_MASK) >> CIDER_CHIP_ID_SHIFT;
uint8_t revision = (id & CIDER_REVISION_ID_MASK) >> CIDER_REVISION_ID_SHIFT;
ESP_LOGI(TAG, "Family ID = 0x%x\t Chip ID = 0x%x\t Revision ID = 0x%x", family_id, chip_id, revision);
ESP_GOTO_ON_FALSE(family_id == CIDER_KSZ8851SNL_FAMILY_ID, ESP_FAIL, err, TAG, "wrong family id");
ESP_GOTO_ON_FALSE(chip_id == CIDER_KSZ8851SNL_CHIP_ID, ESP_FAIL, err, TAG, "wrong chip id");
return ESP_OK;
err:
return ret;
}
static esp_err_t init_set_defaults(emac_ksz8851snl_t *emac)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_TXFDPR, TXFDPR_TXFPAI), err, TAG, "TXFDPR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_TXCR,
TXCR_TXFCE | TXCR_TXPE | TXCR_TXCE | TXCR_TCGICMP | TXCR_TCGIP | TXCR_TCGTCP), err, TAG, "TXCR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXFDPR, RXFDPR_RXFPAI), err, TAG, "RXFDPR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXFCTR, RXFCT_INIT_VALUE), err, TAG, "RXFCTR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXDTTR, RXDTTR_INIT_VALUE), err, TAG, "RXDTTR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXDBCTR, RXDBCTR_INIT_VALUE), err, TAG, "RXDBCTR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR1,
RXCR1_RXUDPFCC | RXCR1_RXTCPFCC | RXCR1_RXIPFCC | RXCR1_RXPAFMA | RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE), err, TAG, "RXCR1 write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR2,
(4 << RXCR2_SRDBL_SHIFT) | RXCR2_IUFFP | RXCR2_RXIUFCEZ | RXCR2_UDPLFE | RXCR2_RXICMPFCC), err, TAG, "RXCR2 write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_RXIPHTOE | RXQCR_RXFCTE | RXQCR_ADRFE), err, TAG, "RXQCR write failed");
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_P1CR, P1CR_FORCE_DUPLEX), err, TAG, "P1CR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_P1CR, P1CR_RESTART_AN), err, TAG, "P1CR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_ISR, ISR_ALL), err, TAG, "ISR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_IER, IER_TXIE | IER_RXIE | IER_LDIE | IER_SPIBEIE | IER_RXOIE), err, TAG, "IER write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_TXQCR, TXQCR_AETFE), err, TAG, "TXQCR write failed");
return ESP_OK;
err:
return ret;
}
static esp_err_t emac_ksz8851_init(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
esp_eth_mediator_t *eth = emac->eth;
esp_rom_gpio_pad_select_gpio(emac->int_gpio_num);
gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT);
gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY);
gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE); // NOTE(v.chistyakov): active low
gpio_intr_enable(emac->int_gpio_num);
gpio_isr_handler_add(emac->int_gpio_num, ksz8851_isr_handler, emac);
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL), err, TAG, "lowlevel init failed");
// NOTE(v.chistyakov): soft reset
ESP_GOTO_ON_ERROR(init_soft_reset(emac), err, TAG, "soft reset failed");
// NOTE(v.chistyakov): verify chip id
ESP_GOTO_ON_ERROR(init_verify_chipid(emac), err, TAG, "verify chip id failed");
// NOTE(v.chistyakov): set default values
ESP_GOTO_ON_ERROR(init_set_defaults(emac), err, TAG, "set defaults after init failed");
ESP_LOGD(TAG, "MAC initialized");
return ESP_OK;
err:
ESP_LOGD(TAG, "MAC initialization failed");
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ret;
}
static esp_err_t emac_ksz8851_deinit(esp_eth_mac_t *mac)
{
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
esp_eth_mediator_t *eth = emac->eth;
mac->stop(mac);
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
ESP_LOGD(TAG, "MAC deinitialized");
return ESP_OK;
}
static esp_err_t emac_ksz8851_start(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_TXCR, TXCR_TXE), err, TAG, "TXCR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_RXE), err, TAG, "RXCR1 write failed");
ESP_LOGD(TAG, "MAC started");
err:
return ret;
}
static esp_err_t emac_ksz8851_stop(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_TXCR, TXCR_TXE), err, TAG, "TXCR write failed");
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_RXE), err, TAG, "RXCR1 write failed");
ESP_LOGD(TAG, "MAC stopped");
err:
return ret;
}
static esp_err_t emac_ksz8851snl_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t length)
{
static unsigned s_frame_id = 0U;
ESP_LOGV(TAG, "transmitting frame of size %u", length);
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
if (!ksz8851_mutex_lock(emac)) {
return ESP_ERR_TIMEOUT;
}
ESP_GOTO_ON_FALSE(length <= KSZ8851_QMU_PACKET_LENGTH, ESP_ERR_INVALID_ARG, err, TAG, "packet is too big");
// NOTE(v.chistyakov): 4 bytes header + length aligned to 4 bytes
unsigned transmit_length = 4U + ((length + 3U) & ~0x3U);
uint16_t free_space;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_TXMIR, &free_space), err, TAG, "TXMIR read failed");
ESP_GOTO_ON_FALSE(transmit_length <= free_space, ESP_FAIL, err, TAG, "TXQ free space (%d) < send length (%d)", free_space,
transmit_length);
emac->tx_buffer[0] = ++s_frame_id & TXSR_TXFID_MASK;
emac->tx_buffer[1] = 0x80U;
emac->tx_buffer[2] = length & 0xFFU;
emac->tx_buffer[3] = (length >> 8U) & 0xFFU;
memcpy(emac->tx_buffer + 4U, buf, length);
spi_transaction_ext_t trans = {
.base.flags = SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_CMD,
.base.cmd = KSZ8851_SPI_COMMAND_WRITE_FIFO,
.base.length = transmit_length * 8U, // NOTE(v.chistyakov): bits
.base.tx_buffer = emac->tx_buffer,
.command_bits = 2U,
.address_bits = 6U,
};
uint16_t ier;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_IER, &ier), err, TAG, "IER read failed");
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_IER, 0), err, TAG, "IER write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed");
if (spi_device_polling_transmit(emac->spi_hdl, &trans.base) != ESP_OK) {
ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__);
ret = ESP_FAIL;
}
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed");
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_IER, ier), err, TAG, "IER write failed");
err:
ksz8851_mutex_unlock(emac);
return ret;
}
static esp_err_t emac_ksz8851_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
if (!ksz8851_mutex_lock(emac)) {
return ESP_ERR_TIMEOUT;
}
ESP_GOTO_ON_FALSE(buf, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer can not be null");
ESP_GOTO_ON_FALSE(length, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer length can not be null");
ESP_GOTO_ON_FALSE(*length > 0U, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer length must be greater than zero");
uint16_t header_status;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_RXFHSR, &header_status), err, TAG, "RXFHSR read failed");
uint16_t byte_count;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_RXFHBCR, &byte_count), err, TAG, "RXFHBCR read failed");
byte_count &= RXFHBCR_RXBC_MASK;
// NOTE(v.chistyakov): do not include 2 bytes padding at the beginning and 4 bytes CRC at the end
const unsigned frame_size = byte_count - 6U;
ESP_GOTO_ON_FALSE(frame_size <= *length, ESP_FAIL, err, TAG, "frame size is greater than length");
if (header_status & RXFHSR_RXFV) {
// NOTE(v.chistyakov): 4 dummy + 4 header + alignment
const unsigned receive_size = 8U + ((byte_count + 3U) & ~0x3U);
spi_transaction_ext_t trans = {
.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY,
.base.cmd = KSZ8851_SPI_COMMAND_READ_FIFO,
.base.length = receive_size * 8U, // NOTE(v.chistyakov): bits
.base.rx_buffer = emac->rx_buffer,
.command_bits = 2U,
.address_bits = 6U,
};
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXFDPR, RXFDPR_RXFP_MASK), err, TAG, "RXFDPR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed");
if (spi_device_polling_transmit(emac->spi_hdl, &trans.base) != ESP_OK) {
ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__);
ret = ESP_FAIL;
}
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed");
// NOTE(v.chistyakov): skip 4 dummy, 4 header, 2 padding
memcpy(buf, emac->rx_buffer + 10U, frame_size);
*length = frame_size;
ESP_LOGV(TAG, "received frame of size %u", frame_size);
} else if (header_status & (RXFHSR_RXCE | RXFHSR_RXRF | RXFHSR_RXFTL | RXFHSR_RXMR | RXFHSR_RXUDPFCS | RXFHSR_RXTCPFCS |
RXFHSR_RXIPFCS | RXFHSR_RXICMPFCS)) {
// NOTE(v.chistyakov): RRXEF is a self-clearing bit
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_RRXEF), err, TAG, "RXQCR write failed");
*length = 0U;
}
err:
ksz8851_mutex_unlock(emac);
return ret;
}
static esp_err_t emac_ksz8851_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;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
ESP_GOTO_ON_FALSE(reg_value, ESP_ERR_INVALID_ARG, err, TAG, "reg_value can not be null");
uint16_t tmp_val;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, phy_reg, &tmp_val), err, TAG, "read PHY register failed");
*reg_value = tmp_val;
err:
return ret;
}
static esp_err_t emac_ksz8851_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_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, phy_reg, (uint16_t)reg_value), err, TAG, "write PHY register failed");
err:
return ret;
}
static esp_err_t emac_ksz8851_set_addr(esp_eth_mac_t *mac, uint8_t *addr)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can not be null");
uint16_t MARL = addr[5] | ((uint16_t)(addr[4]) << 8);
uint16_t MARM = addr[3] | ((uint16_t)(addr[2]) << 8);
uint16_t MARH = addr[1] | ((uint16_t)(addr[0]) << 8);
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_MARL, MARL), err, TAG, "MARL write failed");
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_MARM, MARM), err, TAG, "MARM write failed");
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_MARH, MARH), err, TAG, "MARH write failed");
ESP_LOGD(TAG, "set MAC address to %02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4],
addr[5]);
err:
return ret;
}
static esp_err_t emac_ksz8851_get_addr(esp_eth_mac_t *mac, uint8_t *addr)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can not be null");
uint16_t MARL, MARM, MARH;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_MARL, &MARL), err, TAG, "MARL read failed");
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_MARM, &MARM), err, TAG, "MARM read failed");
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_MARH, &MARH), err, TAG, "MARH read failed");
addr[0] = (MARH >> 8) & 0xFF;
addr[1] = MARH & 0xFF;
addr[2] = (MARM >> 8) & 0xFF;
addr[3] = MARM & 0xFF;
addr[4] = (MARL >> 8) & 0xFF;
addr[5] = MARL & 0xFF;
err:
return ret;
}
static esp_err_t emac_ksz8851_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
switch (speed) {
case ETH_SPEED_100M:
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_P1CR, P1CR_FORCE_SPEED), err, TAG, "P1CR write failed");
ESP_LOGD(TAG, "set speed to 100M");
break;
case ETH_SPEED_10M:
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_P1CR, P1CR_FORCE_SPEED), err, TAG, "P1CR write failed");
ESP_LOGD(TAG, "set speed to 10M");
break;
default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown speed"); break;
}
err:
return ret;
}
static esp_err_t emac_ksz8851_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
switch (duplex) {
case ETH_DUPLEX_FULL:
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_P1CR, P1CR_FORCE_DUPLEX), err, TAG, "P1CR write failed");
ESP_LOGD(TAG, "set duplex to full");
break;
case ETH_DUPLEX_HALF:
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_P1CR, P1CR_FORCE_DUPLEX), err, TAG, "P1CR write failed");
ESP_LOGD(TAG, "set duplex to half");
break;
default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown duplex"); break;
}
err:
return ret;
}
static esp_err_t emac_ksz8851_set_link(esp_eth_mac_t *mac, eth_link_t link)
{
esp_err_t ret = ESP_OK;
switch (link) {
case ETH_LINK_UP:
ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "ksz8851 start failed");
ESP_LOGD(TAG, "link is up");
break;
case ETH_LINK_DOWN:
ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "ksz8851 stop failed");
ESP_LOGD(TAG, "link is down");
break;
default: ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown link status"); break;
}
err:
return ret;
}
static esp_err_t emac_ksz8851_set_promiscuous(esp_eth_mac_t *mac, bool enable)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
uint16_t rxcr1;
ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_RXCR1, &rxcr1), err, TAG, "RXCR1 read failed");
if (enable) {
// NOTE(v.chistyakov): set promiscuous mode
ESP_LOGD(TAG, "setting promiscuous mode");
rxcr1 |= RXCR1_RXINVF | RXCR1_RXAE;
rxcr1 &= ~(RXCR1_RXPAFMA | RXCR1_RXMAFMA);
} else {
// NOTE(v.chistyakov): set hash perfect (default)
ESP_LOGD(TAG, "setting hash perfect mode");
rxcr1 |= RXCR1_RXPAFMA;
rxcr1 &= ~(RXCR1_RXINVF | RXCR1_RXAE | RXCR1_RXMAFMA);
}
ESP_GOTO_ON_ERROR(ksz8851_write_reg(emac, KSZ8851_RXCR1, rxcr1), err, TAG, "RXCR1 write failed");
err:
return ret;
}
static esp_err_t emac_ksz8851_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
{
esp_err_t ret = ESP_OK;
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
if (enable) {
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_TXCR, TXCR_TXFCE), err, TAG, "TXCR write failed");
ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_RXFCE), err, TAG, "RXCR write failed");
ESP_LOGD(TAG, "flow control enabled");
} else {
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_TXCR, TXCR_TXFCE), err, TAG, "TXCR write failed");
ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_RXFCE), err, TAG, "RXCR write failed");
ESP_LOGD(TAG, "flow control disabled");
}
err:
return ret;
}
static esp_err_t emac_ksz8851_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t ability)
{
// NOTE(v.chistyakov): peer's pause ability is determined with auto-negotiation
return ESP_ERR_NOT_SUPPORTED;
}
static esp_err_t emac_ksz8851_del(esp_eth_mac_t *mac)
{
emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent);
vTaskDelete(emac->rx_task_hdl);
vSemaphoreDelete(emac->spi_lock);
heap_caps_free(emac->rx_buffer);
heap_caps_free(emac->tx_buffer);
free(emac);
return ESP_OK;
}
static void emac_ksz8851snl_task(void *arg)
{
emac_ksz8851snl_t *emac = (emac_ksz8851snl_t *)arg;
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
uint16_t interrupt_status;
ksz8851_read_reg(emac, KSZ8851_ISR, &interrupt_status);
ksz8851_write_reg(emac, KSZ8851_ISR, interrupt_status);
if (interrupt_status & ISR_TXIS) {
ESP_LOGD(TAG, "TX Interrupt");
}
if (interrupt_status & ISR_RXOIS) {
ESP_LOGD(TAG, "RX Overrun Interrupt");
}
if (interrupt_status & ISR_TXPSIS) {
ESP_LOGD(TAG, "TX Process Stopped Interrupt");
}
if (interrupt_status & ISR_RXPSIS) {
ESP_LOGD(TAG, "RX Process Stopped Interrupt");
}
if (interrupt_status & ISR_TXSAIS) {
ESP_LOGD(TAG, "TX Space Available Interrupt");
}
if (interrupt_status & ISR_RXWFDIS) {
ESP_LOGD(TAG, "RX Wakeup Frame Detect Interrupt");
}
if (interrupt_status & ISR_RXMPDIS) {
ESP_LOGD(TAG, "RX Magic Packet Detect Interrupt");
}
if (interrupt_status & ISR_LDIS) {
ESP_LOGD(TAG, "Linkup Detect Interrupt");
ksz8851_set_bits(emac, KSZ8851_PMECR, PMECR_WAKEUP_LINK);
}
if (interrupt_status & ISR_EDIS) {
ESP_LOGD(TAG, "Energy Detect Interrupt");
}
if (interrupt_status & ISR_SPIBEIS) {
ESP_LOGD(TAG, "SPI Bus Error Interrupt");
}
if (interrupt_status & ISR_RXIS) {
ESP_LOGD(TAG, "RX Interrupt");
uint16_t ier;
ksz8851_read_reg(emac, KSZ8851_IER, &ier);
ksz8851_write_reg(emac, KSZ8851_IER, 0);
uint16_t frame_count = 0;
ksz8851_read_reg(emac, KSZ8851_RXFCTR, &frame_count);
frame_count = (frame_count & RXFCTR_RXFC_MASK) >> RXFCTR_RXFC_SHIFT;
while (frame_count--) {
uint32_t length = ETH_MAX_PACKET_SIZE;
uint8_t *packet = malloc(ETH_MAX_PACKET_SIZE);
if (!packet) {
continue;
}
if (emac->parent.receive(&emac->parent, packet, &length) == ESP_OK && length) {
emac->eth->stack_input(emac->eth, packet, length);
// NOTE(v.chistyakov): the packet is freed in the upper layers
} else {
free(packet);
ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_RXE);
ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_FRXQ);
ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_FRXQ);
ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_RXE);
}
}
ksz8851_write_reg(emac, KSZ8851_IER, ier);
}
}
vTaskDelete(NULL);
}
esp_eth_mac_t *esp_eth_mac_new_ksz8851snl(const eth_ksz8851snl_config_t *ksz8851snl_config,
const eth_mac_config_t *mac_config)
{
esp_eth_mac_t *ret = NULL;
emac_ksz8851snl_t *emac = NULL;
ESP_GOTO_ON_FALSE(ksz8851snl_config && mac_config, NULL, err, TAG, "arguments can not be null");
ESP_GOTO_ON_FALSE(ksz8851snl_config->int_gpio_num >= 0, NULL, err, TAG, "invalid interrupt gpio number");
emac = calloc(1, sizeof(emac_ksz8851snl_t));
ESP_GOTO_ON_FALSE(emac, NULL, err, TAG, "no mem for MAC instance");
emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms;
emac->int_gpio_num = ksz8851snl_config->int_gpio_num;
emac->spi_hdl = ksz8851snl_config->spi_hdl;
emac->parent.set_mediator = emac_ksz8851_set_mediator;
emac->parent.init = emac_ksz8851_init;
emac->parent.deinit = emac_ksz8851_deinit;
emac->parent.start = emac_ksz8851_start;
emac->parent.stop = emac_ksz8851_stop;
emac->parent.transmit = emac_ksz8851snl_transmit;
emac->parent.receive = emac_ksz8851_receive;
emac->parent.read_phy_reg = emac_ksz8851_read_phy_reg;
emac->parent.write_phy_reg = emac_ksz8851_write_phy_reg;
emac->parent.set_addr = emac_ksz8851_set_addr;
emac->parent.get_addr = emac_ksz8851_get_addr;
emac->parent.set_speed = emac_ksz8851_set_speed;
emac->parent.set_duplex = emac_ksz8851_set_duplex;
emac->parent.set_link = emac_ksz8851_set_link;
emac->parent.set_promiscuous = emac_ksz8851_set_promiscuous;
emac->parent.enable_flow_ctrl = emac_ksz8851_enable_flow_ctrl;
emac->parent.set_peer_pause_ability = emac_ksz8851_set_peer_pause_ability;
emac->parent.del = emac_ksz8851_del;
emac->spi_lock = xSemaphoreCreateRecursiveMutex();
ESP_GOTO_ON_FALSE(emac->spi_lock, NULL, err, TAG, "create lock failed");
emac->rx_buffer = NULL;
emac->tx_buffer = NULL;
emac->rx_buffer = heap_caps_malloc(KSZ8851_QMU_PACKET_LENGTH + KSZ8851_QMU_PACKET_PADDING, MALLOC_CAP_DMA);
emac->tx_buffer = heap_caps_malloc(KSZ8851_QMU_PACKET_LENGTH + KSZ8851_QMU_PACKET_PADDING, MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(emac->rx_buffer, NULL, err, TAG, "RX buffer allocation failed");
ESP_GOTO_ON_FALSE(emac->tx_buffer, NULL, err, TAG, "TX buffer allocation failed");
BaseType_t core_num = tskNO_AFFINITY;
if (mac_config->flags & ETH_MAC_FLAG_PIN_TO_CORE) {
core_num = cpu_hal_get_core_id();
}
BaseType_t xReturned = xTaskCreatePinnedToCore(emac_ksz8851snl_task, "ksz8851snl_tsk", mac_config->rx_task_stack_size,
emac, mac_config->rx_task_prio, &emac->rx_task_hdl, core_num);
ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, err, TAG, "create ksz8851 task failed");
return &(emac->parent);
err:
if (emac) {
if (emac->rx_task_hdl) {
vTaskDelete(emac->rx_task_hdl);
}
if (emac->spi_lock) {
vSemaphoreDelete(emac->spi_lock);
}
// NOTE(v.chistyakov): safe to call with NULL
heap_caps_free(emac->rx_buffer);
heap_caps_free(emac->tx_buffer);
free(emac);
}
return ret;
}

View File

@@ -0,0 +1,276 @@
// Copyright (c) 2021 Vladimir Chistyakov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <stdlib.h>
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_eth.h"
#include "ksz8851.h"
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
int32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_ksz8851snl_t;
static const char *TAG = "ksz8851snl-phy";
static esp_err_t ksz8851_update_link_duplex_speed(phy_ksz8851snl_t *ksz8851)
{
esp_err_t ret = ESP_OK;
esp_eth_mediator_t *eth = ksz8851->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
uint32_t status;
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1SR, &status), err, TAG, "P1SR read failed");
eth_link_t link = (status & P1SR_LINK_GOOD) ? ETH_LINK_UP : ETH_LINK_DOWN;
if (ksz8851->link_status != link) {
if (link == ETH_LINK_UP) {
if (status & P1SR_OPERATION_SPEED) {
speed = ETH_SPEED_100M;
ESP_LOGD(TAG, "speed 100M");
} else {
speed = ETH_SPEED_10M;
ESP_LOGD(TAG, "speed 10M");
}
if (status & P1SR_OPERATION_DUPLEX) {
duplex = ETH_DUPLEX_FULL;
ESP_LOGD(TAG, "duplex full");
} else {
duplex = ETH_DUPLEX_HALF;
ESP_LOGD(TAG, "duplex half");
}
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed), err, TAG, "change speed failed");
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex), err, TAG, "change duplex failed");
}
ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed");
ksz8851->link_status = link;
}
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(eth, ESP_ERR_INVALID_ARG, err, TAG, "mediator can not be null");
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
ksz8851->eth = eth;
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_reset(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
ksz8851->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = ksz8851->eth;
ESP_LOGD(TAG, "soft reset");
// NOTE(v.chistyakov): PHY_RESET bit is self-clearing
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_PHYRR, PHYRR_PHY_RESET), err, TAG, "PHYRR write failed");
vTaskDelay(pdMS_TO_TICKS(ksz8851->reset_timeout_ms));
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_reset_hw(esp_eth_phy_t *phy)
{
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
// NOTE(v.chistyakov): set reset_gpio_num to a negative value can skip hardware reset phy chip
if (ksz8851->reset_gpio_num >= 0) {
ESP_LOGD(TAG, "hard reset");
esp_rom_gpio_pad_select_gpio(ksz8851->reset_gpio_num);
gpio_set_direction(ksz8851->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(ksz8851->reset_gpio_num, 0);
esp_rom_delay_us(ksz8851->reset_timeout_ms * 1000);
gpio_set_level(ksz8851->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t phy_ksz8851_pwrctl(esp_eth_phy_t *phy, bool enable)
{
esp_err_t ret = ESP_OK;
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
esp_eth_mediator_t *eth = ksz8851->eth;
if (enable) {
ESP_LOGD(TAG, "normal mode");
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_PMECR, PMECR_PME_MODE_POWER_SAVING), err, TAG, "PMECR write failed");
} else {
ESP_LOGD(TAG, "power saving mode");
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_PMECR, PMECR_PME_MODE_NORMAL), err, TAG, "PMECR write failed");
}
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_init(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
ESP_LOGD(TAG, "initializing PHY");
ESP_GOTO_ON_ERROR(phy_ksz8851_pwrctl(phy, true), err, TAG, "power control failed");
ESP_GOTO_ON_ERROR(phy_ksz8851_reset(phy), err, TAG, "reset failed");
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_deinit(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
ESP_LOGD(TAG, "deinitializing PHY");
ESP_GOTO_ON_ERROR(phy_ksz8851_pwrctl(phy, false), err, TAG, "power control failed");
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_negotiate(esp_eth_phy_t *phy)
{
esp_err_t ret = ESP_OK;
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
esp_eth_mediator_t *eth = ksz8851->eth;
ESP_LOGD(TAG, "restart negotiation");
uint32_t control;
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1CR, &control), err, TAG, "P1CR read failed");
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control | P1CR_RESTART_AN), err, TAG, "P1CR write failed");
uint32_t status;
unsigned to;
for (to = 0; to < ksz8851->autonego_timeout_ms / 10; to++) {
vTaskDelay(pdMS_TO_TICKS(10));
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1SR, &status), err, TAG, "P1SR read failed");
if (status & P1SR_AN_DONE) {
break;
}
}
if (to >= ksz8851->autonego_timeout_ms / 10) {
ESP_LOGW(TAG, "Ethernet PHY auto negotiation timeout");
}
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1CR, control), err, TAG, "P1CR write failed");
ESP_GOTO_ON_ERROR(ksz8851_update_link_duplex_speed(ksz8851), err, TAG, "update link duplex speed failed");
ESP_LOGD(TAG, "negotiation succeeded");
return ESP_OK;
err:
ESP_LOGD(TAG, "negotiation failed");
return ret;
}
static esp_err_t phy_ksz8851_get_link(esp_eth_phy_t *phy)
{
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
return ksz8851_update_link_duplex_speed(ksz8851);
}
static esp_err_t phy_ksz8851_set_addr(esp_eth_phy_t *phy, uint32_t addr)
{
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
ksz8851->addr = addr;
ESP_LOGD(TAG, "setting PHY addr to %u", addr);
return ESP_OK;
}
static esp_err_t phy_ksz8851_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(addr, ESP_ERR_INVALID_ARG, err, TAG, "addr can not be null");
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
*addr = ksz8851->addr;
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
esp_err_t ret = ESP_OK;
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
esp_eth_mediator_t *eth = ksz8851->eth;
uint32_t anar;
ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, ksz8851->addr, KSZ8851_P1ANAR, &anar), err, TAG, "P1ANAR read failed");
if (ability) {
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1ANAR, anar | P1ANAR_PAUSE), err, TAG, "P1ANAR write failed");
ESP_LOGD(TAG, "start advertising pause ability");
} else {
ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, ksz8851->addr, KSZ8851_P1ANAR, anar & ~P1ANAR_PAUSE), err, TAG, "P1ANAR write failed");
ESP_LOGD(TAG, "stop advertising pause ability");
}
return ESP_OK;
err:
return ret;
}
static esp_err_t phy_ksz8851_del(esp_eth_phy_t *phy)
{
ESP_LOGD(TAG, "deleting PHY");
phy_ksz8851snl_t *ksz8851 = __containerof(phy, phy_ksz8851snl_t, parent);
free(ksz8851);
return ESP_OK;
}
esp_eth_phy_t *esp_eth_phy_new_ksz8851snl(const eth_phy_config_t *config)
{
esp_eth_phy_t *ret = NULL;
ESP_GOTO_ON_FALSE(config, NULL, err, TAG, "config can not be null");
phy_ksz8851snl_t *ksz8851 = calloc(1, sizeof(phy_ksz8851snl_t));
ESP_GOTO_ON_FALSE(ksz8851, NULL, err, TAG, "no mem for PHY instance");
ksz8851->addr = config->phy_addr;
ksz8851->reset_timeout_ms = config->reset_timeout_ms;
ksz8851->reset_gpio_num = config->reset_gpio_num;
ksz8851->link_status = ETH_LINK_DOWN;
ksz8851->autonego_timeout_ms = config->autonego_timeout_ms;
ksz8851->parent.set_mediator = phy_ksz8851_set_mediator;
ksz8851->parent.reset = phy_ksz8851_reset;
ksz8851->parent.reset_hw = phy_ksz8851_reset_hw;
ksz8851->parent.init = phy_ksz8851_init;
ksz8851->parent.deinit = phy_ksz8851_deinit;
ksz8851->parent.negotiate = phy_ksz8851_negotiate;
ksz8851->parent.get_link = phy_ksz8851_get_link;
ksz8851->parent.pwrctl = phy_ksz8851_pwrctl;
ksz8851->parent.set_addr = phy_ksz8851_set_addr;
ksz8851->parent.get_addr = phy_ksz8851_get_addr;
ksz8851->parent.advertise_pause_ability = phy_ksz8851_advertise_pause_ability;
ksz8851->parent.del = phy_ksz8851_del;
return &(ksz8851->parent);
err:
return ret;
}

View File

@@ -0,0 +1,362 @@
// Copyright (c) 2021 Vladimir Chistyakov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
typedef enum {
KSZ8851_CCR = 0x08, ///< Chip Configuration Register
KSZ8851_MARL = 0x10, ///< Host MAC Address Register Low
KSZ8851_MARM = 0x12, ///< Host MAC Address Register Middle
KSZ8851_MARH = 0x14, ///< Host MAC Address Register High
KSZ8851_OBCR = 0x20, ///< On-Chip Bus Control Register
KSZ8851_EEPCR = 0x22, ///< EEPROM Control Register
KSZ8851_MBIR = 0x24, ///< Memory Built-In Self-Test (BIST) Info Register
KSZ8851_GRR = 0x26, ///< Global Reset Register
KSZ8851_WFCR = 0x2A, ///< Wakeup Frame Control Register
KSZ8851_WF0CRC0 = 0x30, ///< Wakeup Frame 0 CRC0 Register (lower 16 bits)
KSZ8851_WF0CRC1 = 0x32, ///< Wakeup Frame 0 CRC1 Register (upper 16 bits)
KSZ8851_WF0BM0 = 0x34, ///< Wakeup Frame 0 Byte Mask 0 Register (0-15)
KSZ8851_WF0BM1 = 0x36, ///< Wakeup Frame 0 Byte Mask 1 Register (16-31)
KSZ8851_WF0BM2 = 0x38, ///< Wakeup Frame 0 Byte Mask 2 Register (32-47)
KSZ8851_WF0BM3 = 0x3A, ///< Wakeup Frame 0 Byte Mask 3 Register (48-63)
KSZ8851_WF1CRC0 = 0x40, ///< Wakeup Frame 1 CRC0 Register (lower 16 bits)
KSZ8851_WF1CRC1 = 0x42, ///< Wakeup Frame 1 CRC1 Register (upper 16 bits)
KSZ8851_WF1BM0 = 0x44, ///< Wakeup Frame 1 Byte Mask 0 Register (0-15)
KSZ8851_WF1BM1 = 0x46, ///< Wakeup Frame 1 Byte Mask 1 Register (16-31)
KSZ8851_WF1BM2 = 0x48, ///< Wakeup Frame 1 Byte Mask 2 Register (32-47)
KSZ8851_WF1BM3 = 0x4A, ///< Wakeup Frame 1 Byte Mask 3 Register (48-63)
KSZ8851_WF2CRC0 = 0x50, ///< Wakeup Frame 2 CRC0 Register (lower 16 bits)
KSZ8851_WF2CRC1 = 0x52, ///< Wakeup Frame 2 CRC1 Register (upper 16 bits)
KSZ8851_WF2BM0 = 0x54, ///< Wakeup Frame 2 Byte Mask 0 Register (0-15)
KSZ8851_WF2BM1 = 0x56, ///< Wakeup Frame 2 Byte Mask 1 Register (16-31)
KSZ8851_WF2BM2 = 0x58, ///< Wakeup Frame 2 Byte Mask 2 Register (32-47)
KSZ8851_WF2BM3 = 0x5A, ///< Wakeup Frame 2 Byte Mask 3 Register (48-63)
KSZ8851_WF3CRC0 = 0x60, ///< Wakeup Frame 3 CRC0 Register (lower 16 bits)
KSZ8851_WF3CRC1 = 0x62, ///< Wakeup Frame 3 CRC1 Register (upper 16 bits)
KSZ8851_WF3BM0 = 0x64, ///< Wakeup Frame 3 Byte Mask 0 Register (0-15)
KSZ8851_WF3BM1 = 0x66, ///< Wakeup Frame 3 Byte Mask 1 Register (16-31)
KSZ8851_WF3BM2 = 0x68, ///< Wakeup Frame 3 Byte Mask 2 Register (32-47)
KSZ8851_WF3BM3 = 0x6A, ///< Wakeup Frame 3 Byte Mask 3 Register (48-63)
KSZ8851_TXCR = 0x70, ///< Transmit Control Register
KSZ8851_TXSR = 0x72, ///< Transmit Status Register
KSZ8851_RXCR1 = 0x74, ///< Receive Control Register 1
KSZ8851_RXCR2 = 0x76, ///< Receive Control Register 2
KSZ8851_TXMIR = 0x78, ///< TXQ Memory Information Register
KSZ8851_RXFHSR = 0x7C, ///< Receive Frame Header Status Register
KSZ8851_RXFHBCR = 0x7E, ///< Receive Frame Header Byte Count Register
KSZ8851_TXQCR = 0x80, ///< TXQ Command Register
KSZ8851_RXQCR = 0x82, ///< RXQ Command Register
KSZ8851_TXFDPR = 0x84, ///< TX Frame Data Pointer Register
KSZ8851_RXFDPR = 0x86, ///< RX Frame Data Pointer Register
KSZ8851_RXDTTR = 0x8C, ///< RX Duration Timer Threshold Register
KSZ8851_RXDBCTR = 0x8E, ///< RX Data Byte Count Threshold Register
KSZ8851_IER = 0x90, ///< Interrupt Enable Register
KSZ8851_ISR = 0x92, ///< Interrupt Status Register
KSZ8851_RXFCTR = 0x9C, ///< RX Frame Count & Threshold Register
KSZ8851_TXNTFSR = 0x9E, ///< TX Next Total Frames Size Register
KSZ8851_MAHTR0 = 0xA0, ///< MAC Address Hash Table Register 0
KSZ8851_MAHTR1 = 0xA2, ///< MAC Address Hash Table Register 1
KSZ8851_MAHTR2 = 0xA4, ///< MAC Address Hash Table Register 2
KSZ8851_MAHTR3 = 0xA6, ///< MAC Address Hash Table Register 3
KSZ8851_FCLWR = 0xB0, ///< Flow Control Low Watermark Register
KSZ8851_FCHWR = 0xB2, ///< Flow Control High Watermark Register
KSZ8851_FCOWR = 0xB4, ///< Flow Control Overrun Watermark Register
KSZ8851_CIDER = 0xC0, ///< Chip ID and Enable Register
KSZ8851_CGCR = 0xC6, ///< Chip Global Control Register
KSZ8851_IACR = 0xC8, ///< Indirect Access Control Register
KSZ8851_IADLR = 0xD0, ///< Indirect Access Data Low Register
KSZ8851_IADHR = 0xD2, ///< Indirect Access Data High Register
KSZ8851_PMECR = 0xD4, ///< Power Management Event Control Register
KSZ8851_GSWUTR = 0xD6, ///< Go-Sleep & Wake-Up Time Register
KSZ8851_PHYRR = 0xD8, ///< PHY Reset Register
KSZ8851_P1MBCR = 0xE4, ///< PHY 1 MII-Register Basic Control Register
KSZ8851_P1MBSR = 0xE6, ///< PHY 1 MII-Register Basic Status Register
KSZ8851_PHY1ILR = 0xE8, ///< PHY 1 PHY ID Low Register
KSZ8851_PHY1IHR = 0xEA, ///< PHY 1 PHY ID High Register
KSZ8851_P1ANAR = 0xEC, ///< PHY 1 Auto-Negotiation Advertisement Register
KSZ8851_P1ANLPR = 0xEE, ///< PHY 1 Auto-Negotiation Link Partner Ability Register
KSZ8851_P1SCLMD = 0xF4, ///< Port 1 PHY Special Control/Status, LinkMD
KSZ8851_P1CR = 0xF6, ///< Port 1 Control Register
KSZ8851_P1SR = 0xF8, ///< Port 1 Status Register
KSZ8851_VALID_ADDRESS_MASK = 0xFE, ///< All register addresses are under this mask
} ksz8851_registers_t;
typedef enum {
CCR_EEPROM_PRESENCE = 0x0200U, ///< RO EEPROM presence
CCR_SPI_BUS_MODE = 0x0100U, ///< RO SPI bus mode
CCR_32PIN_CHIP_PACKAGE = 0x0001U, ///< RO 32-Pin Chip Package
OBCR_OUTPUT_PIN_DRIVE_STRENGTH = 0x0040U, ///< RW Output Pin Drive Strength: 8mA (0) or 16mA (1)
OBCR_ONCHIP_BUS_CLOCK_SELECTION = 0X0004U, ///< RW On-Chip Bus Clock Selection: 125MHz (0)
OBCR_ONCHIP_BUS_CLOCK_DIVIDE_BY_1 = 0x0000U, ///< RW On-Chip Bus Clock Divider Selection
OBCR_ONCHIP_BUS_CLCOK_DIVIDE_BY_2 = 0x0001U, ///< Rw On-Chip Bus Clock Divider Selection
OBCR_ONCHIP_BUS_CLCOK_DIVIDE_BY_3 = 0x0002U, ///< RW On-Chip Bus Clock Divider Selection
EEPCR_EESRWA = 0x0020U, ///< RW EEPROM Software Read (0) or Write (1) Access
EEPCR_EESA = 0x0010U, ///< RW EEPROM Software Access
EEPCR_EESB = 0x0008U, ///< RO EEPROM Data receive
EEPCR_EECB2 = 0x0004U, ///< RW EEPROM Data transmit
EEPCR_EECB1 = 0x0002U, ///< RW EEPROM Serial clock
EEPCR_EECB0 = 0x0001U, ///< RW EEPROM Chip select
MBIR_TXMBF = 0x1000U, ///< RO TX Memory BIST Test Finish
MBIR_TXMBFA = 0x0800U, ///< RO TX Memory BIST Test Fail
MBIR_TXMBFC_SHIFT = 8U, ///< RO TX Memory BIST Test Fail Count Shift
MBIR_TXMBFC_MASK = 0x7 << MBIR_TXMBFC_SHIFT, ///< RO TX Memory BIST Test Fail Count Mask
MBIR_RXMBF = 0x0010U, ///< RO RX Memory Bist Finish
MBIR_RXMBFA = 0x0008U, ///< RO RX Memory Bist Fail
MBIR_RXMBFC = 0x7U, ///< RO RX Memory BIST Test Fail Count
GRR_QMU_MODULE_SOFT_RESET = 0x0002U, ///< RW QMU Module Soft Reset
GRR_GLOBAL_SOFT_RESET = 0x0001U, ///< Rw Global Soft Reset
WFCR_MPRXE = 0x0080U, ///< RW Magic Packet RX Enable
WFCR_WF3E = 0x0008U, ///< RW Wake up Frame 3 Enable
WFCR_WF2E = 0x0004U, ///< RW Wake up Frame 2 Enable
WFCR_WF1E = 0x0002U, ///< RW Wake up Frame 1 Enable
WFCR_WF0E = 0x0001U, ///< RW Wake up Frame 0 Enable
TXCR_TCGICMP = 0x0100U, ///< RW Transmit Checksum Generation for ICMP
TXCR_TCGTCP = 0x0040U, ///< RW Transmit Checksum Generation for TCP
TXCR_TCGIP = 0x0020U, ///< RW Transmit Checksum Generation for IP
TXCR_FTXQ = 0x0010U, ///< RW Flush Transmit Queue
TXCR_TXFCE = 0x0008U, ///< RW Transmit Flow Control Enable
TXCR_TXPE = 0x0004U, ///< RW Transmit Padding Enable
TXCR_TXCE = 0x0002U, ///< RW Transmit CRC Enable
TXCR_TXE = 0x0001U, ///< RW Transmit Enable
TXSR_TXLC = 0x2000U, ///< RO Transmit Late Collision
TXSR_TXMC = 0x1000U, ///< RO Transmit Maximum Collision
TXSR_TXFID_MASK = 0x003FU, ///< RO Transmit Frame ID Mask
RXCR1_FRXQ = 0x8000U, ///< RW Flush Receive Queue
RXCR1_RXUDPFCC = 0x4000U, ///< RW Receive UDP Frame Checksum Check Enable
RXCR1_RXTCPFCC = 0x2000U, ///< RW Receive TCP Frame Checksum Check Enable
RXCR1_RXIPFCC = 0x1000U, ///< RW Receive IP Frame Checksum Check Enable
RXCR1_RXPAFMA = 0x0800U, ///< RW Receive Physical Address Filtering with MAC Address Enable
RXCR1_RXFCE = 0x0400U, ///< RW Receive Flow Control Enable
RXCR1_RXEFE = 0x0200U, ///< RW Receive Error Frame Enable
RXCR1_RXMAFMA = 0x0100U, ///< RW Receive Multicast Address Filtering with MAC Address Enable
RXCR1_RXBE = 0x0080U, ///< RW Receive Broadcast Enable
RXCR1_RXME = 0x0040U, ///< RW Receive Multicast Enable
RXCR1_RXUE = 0x0020U, ///< RW Receive Unicast Enable
RXCR1_RXAE = 0x0010U, ///< RW Receive All Enable
RXCR1_RXINVF = 0x0002U, ///< RW Receive Inverse Filtering
RXCR1_RXE = 0x0001U, ///< RW Receive Enable
RXCR2_SRDBL_SHIFT = 5U, ///< WO SPI Receive Data Burst Length: 4/8/16/32/frame (0-4)
RXCR2_IUFFP = 0x0010U, ///< RW IPv4/IPv6/UDP Fragment Frame Pass
RXCR2_RXIUFCEZ = 0x0008U, ///< RW Receive IPv4/IPv6/UDP Frame Checksum Equal Zero
RXCR2_UDPLFE = 0x0004U, ///< RW Lite Frame Enable
RXCR2_RXICMPFCC = 0x0002U, ///< RW Receive ICMP Frame Checksum Check Enable
RXCR2_RXSAF = 0x0001U, ///< RW Receive Source Address Filtering
TXMIR_TXMA_MASK = 0x1FFFU, ///< RO Transmit Memory Available Mask
RXFHSR_RXFV = 0x8000U, ///< RO Receive Frame Valid
RXFHSR_RXICMPFCS = 0x2000U, ///< RO Receive ICMP Frame Checksum Status
RXFHSR_RXIPFCS = 0x1000U, ///< RO Receive IP Frame Checksum Status
RXFHSR_RXTCPFCS = 0x0800U, ///< RO Receive TCP Frame Checksum Status
RXFHSR_RXUDPFCS = 0x0400U, ///< RO Receive UDP Frame Checksum Status
RXFHSR_RXBF = 0x0080U, ///< RO Receive Broadcast Frame
RXFHSR_RXMF = 0x0040U, ///< RO Receive Multicast Frame
RXFHSR_RXUF = 0x0020U, ///< RO Receive Unicast Frame
RXFHSR_RXMR = 0x0010U, ///< RO Receive MII Error
RXFHSR_RXFT = 0x0008U, ///< RO Receive Frame Type
RXFHSR_RXFTL = 0x0004U, ///< RO Receive Frame Too Long
RXFHSR_RXRF = 0x0002U, ///< RO Receive Runt Frame
RXFHSR_RXCE = 0x0001U, ///< RO Receive CRC Error
RXFHBCR_RXBC_MASK = 0x0FFFU, ///< RO Receive Byte Count Mask
TXQCR_AETFE = 0x0004U, ///< RW Auto-Enqueue TXQ Frame Enable
TXQCR_TXQMAM = 0x0002U, ///< RW TXQ Memory Available Monitor
TXQCR_METFE = 0x0001U, ///< RW (SC) Manual Enqueue TXQ Frame Enable
RXQCR_RXDTTS = 0x1000U, ///< RO RX Duration Timer Threshold Status
RXQCR_RXDBCTS = 0x0800U, ///< RO RX Data Byte Count Threshold Status
RXQCR_RXFCTS = 0x0400U, ///< RO RX Frame Count Threshold Status
RXQCR_RXIPHTOE = 0x0200U, ///< RW RX IP Header Two-Byte Offset Enable
RXQCR_RXDTTE = 0x0080U, ///< RW RX Duration Timer Threshold Enable
RXQCR_RXDBCTE = 0x0040U, ///< RW RX Data Byte Count Threshold Enable
RXQCR_RXFCTE = 0x0020U, ///< RW RX Frame Count Threshold Enable
RXQCR_ADRFE = 0x0010U, ///< RW Auto-Dequeue RXQ Frame Enable
RXQCR_SDA = 0x0008U, ///< WO Start DMA Access
RXQCR_RRXEF = 0x0001U, ///< RW Release RX Error Frame
TXFDPR_TXFPAI = 0x4000U, ///< RW TX Frame Data Pointer Auto Increment
TXFDPR_TXFP_MASK = 0x07FFU, ///< RO TX Frame Pointer Mask
RXFDPR_RXFPAI = 0x4000U, ///< RW RX Frame Pointer Auto Increment
RXFDPR_RXFP_MASK = 0x07FFU, ///< WO RX Frame Pointer Mask
IER_LCIE = 0x8000U, ///< RW Link Change Interrupt Enable
IER_TXIE = 0x4000U, ///< RW Transmit Interrupt Enable
IER_RXIE = 0x2000U, ///< RW Receive Interrupt Enable
IER_RXOIE = 0x0800U, ///< RW Receive Overrun Interrupt Enable
IER_TXPSIE = 0x0200U, ///< RW Transmit Process Stopped Interrupt Enable
IER_RXPSIE = 0x0100U, ///< RW Receive Process Stopped Interrupt Enable
IER_TXSAIE = 0x0040U, ///< RW Transmit Space Available Interrupt Enable
IER_RXWFDIE = 0x0020U, ///< RW Receive Wake-up Frame Detect Interrupt Enable
IER_RXMPDIE = 0x0010U, ///< RW Receive Magic Packet Detect Interrupt Enable
IER_LDIE = 0x0008U, ///< RW Linkup Detect Interrupt Enable
IER_EDIE = 0x0004U, ///< RW Energy Detect Interrupt Enable
IER_SPIBEIE = 0x0002U, ///< RW SPI Bus Error Interrupt Enable
IER_DEDIE = 0x0001U, ///< RW Delay Energy Detect Interrupt Enable
ISR_LCIS = 0x8000U, ///< RO (W1C) Link Change Interrupt Status
ISR_TXIS = 0x4000U, ///< RO (W1C) Transmit Interrupt Status
ISR_RXIS = 0x2000U, ///< RO (W1C) Receive Interrupt Status
ISR_RXOIS = 0x0800U, ///< RO (W1C) Receive Overrun Interrupt Status
ISR_TXPSIS = 0x0200U, ///< RO (W1C) Transmit Process Stopped Interrupt Status
ISR_RXPSIS = 0x0100U, ///< RO (W1C) Receive Process Stopped Interrupt Status
ISR_TXSAIS = 0x0040U, ///< RO (W1C) Transmit Space Available Interrupt Status
ISR_RXWFDIS = 0x0020U, ///< RO (W1C) Receive Wakeup Frame Detect Interrupt Status
ISR_RXMPDIS = 0x0010U, ///< RO (W1C) Receive Magic Packet Detect Interrupt Status
ISR_LDIS = 0x0008U, ///< RO (W1C) Linkup Detect Interrupt Status
ISR_EDIS = 0x0004U, ///< RO (W1C) Energy Detect Interrupt Status
ISR_SPIBEIS = 0x0002U, ///< RO (W1C) SPI Bus Error Interrupt Status
ISR_ALL = 0xFFFFU, ///< WO Clear register value
RXFCTR_RXFC_SHIFT = 8U, ///< RO RX Frame Count Shift
RXFCTR_RXFC_MASK = 0xFF << RXFCTR_RXFC_SHIFT, ///< RO RX Frame Count Mask
RXFCTR_RXFCT_MASK = 0xFFU, ///< RW Receive Frame Count Threshold
FCLWR_MASK = 0x0FFFU, ///< RW Flow Control Low Watermark Configuration Mask
FCHWR_MASK = 0x0FFFU, ///< RW Flow Control High Watermark Configuration Mask
FCOWR_MASK = 0x0FFFU, ///< RW Flow Control Overrun Watermark Configuration Mask
CIDER_KSZ8851SNL_FAMILY_ID = 0x88U, ///< KSZ8851SNL Family ID
CIDER_KSZ8851SNL_CHIP_ID = 0x7U, ///< KSZ8851SNL Chip ID
CIDER_FAMILY_ID_SHIFT = 8U, ///< RO Family ID Shift
CIDER_FAMILY_ID_MASK = 0xFF << CIDER_FAMILY_ID_SHIFT, ///< RO Family ID Mask
CIDER_CHIP_ID_SHIFT = 4U, ///< RO Chip ID Shift
CIDER_CHIP_ID_MASK = 0xF << CIDER_CHIP_ID_SHIFT, ///< RO Chip ID Mask
CIDER_REVISION_ID_SHIFT = 1U, ///< RO Revision ID Shift
CIDER_REVISION_ID_MASK = 0x7 << CIDER_REVISION_ID_SHIFT, ///< RO Revision ID Mask
CGCR_LEDSEL0 = 0x0200U, ///< RW PHY LED Mode: 0 - 100BT + LINK/ACTU, 1 - ACT + LINK
IACR_READ_ENABLE = 0x1000U, ///< RW Read Enable
IACR_MIB_COUNTER_SELECT = 0x0C00U, ///< RW Table Select
IACR_INDIRECT_ADDRESS_MASK = 0x001FU, ///< RW Indirect Address Mask
PMECR_PME_DELAY_ENABLE = 0x4000U, ///< RW PME Delay Enable
PMECR_PME_OUTPUT_POLARITY = 0x1000U, ///< RW PME Output Polarity
PMECR_WUP_FRAME_EN = 0x0800U, ///< RW Wake-on-LAN to PME Output Enable receive wake-up frame
PMECR_MAGIC_PACKET = 0x0400U, ///< RW Wake-on-LAN to PME Output Enable receive magic packet
PMECR_LINK_CHANGE_TO_UP = 0x0200U, ///< RW Wake-on-LAN to PME Output Enable link change to up
PMECR_SIGNAL_ENERGY_DETECTED = 0x0100U, ///< RW Wake-on-LAN to PME Output Enable energy detected
PMECR_AUTO_WAKEUP_ENABLE = 0x0080U, ///< RW Auto Wake-Up Enable
PMECR_WAKEUP_TO_NORMAL = 0x0040U, ///< RW Wake-Up to Normal Operation Mode
PMECR_WAKEUP_FRAME_EVENT = 0x0020U, ///< RO (W1C) Wake-Up Event Indication wakeup frame event detected
PMECR_WAKEUP_MAGIC_PACKET = 0x0010U, ///< RO (W1C) Wake-Up Event Indication magic packet event detected
PMECR_WAKEUP_LINK = 0x0008U, ///< RO (W1C) Wake-Up Event Indication link up event detected
PMECR_WAKEUP_ENERGY = 0x0004U, ///< RO (W1C) Wake-Up Event Indication energy event detected
PMECR_PME_MODE_MASK = 0x0003U, ///< RW Power Management Mode Mask
PMECR_PME_MODE_NORMAL = 0x0000U, ///< RW Normal Operation Mode
PMECR_PME_MODE_ENERGY_DETECT = 0x0001U, ///< RW Energy Detect Mode
PMECR_PME_MODE_SOFT_POWER_DOWN = 0x0002U, ///< RW Soft Power Down Mode
PMECR_PME_MODE_POWER_SAVING = 0x0003U, ///< RW Power Saving Mode
GSWUTR_WAKE_UP_TIME_SHIFT = 8U, ///< RW Wake-up Time Shift
GSWUTR_WAKE_UP_TIME_MASK = 0xFF << GSWUTR_WAKE_UP_TIME_SHIFT, ///< RW Wake-up Time Mask
GSWUTR_GO_SLEEP_TIME_MASK = 0x0003U, ///< RW Go-sleep Time Mask
PHYRR_PHY_RESET = 0x0001U, ///< WO (SC) PHY Reset Bit
P1MBCR_LOCAL_LOOPBACK = 0x4000U, ///< RW Local (far-end) loopback (llb)
P1MBCR_FORCE100 = 0x2000U, ///< RW Force 100
P1MBCR_AN_ENABLE = 0x1000U, ///< RW AN Enable
P1MBCR_RESTART_AN = 0x0200U, ///< RW Restart AN
P1MBCR_FORCE_FULL_DUPLEX = 0x0100U, ///< RW Force Full-Duplex
P1MBCR_HP_MDIX = 0x0020U, ///< RW HP Auto MDI-X mode
P1MBCR_FORCE_MDIX = 0x0010U, ///< RWForce MDI-X
P1MBCR_DISABLE_MDIX = 0x0008U, ///< RW Disable MDI-X
P1MBCR_DISABLE_TRANSMIT = 0x0002U, ///< RW Disable Transmit
P1MBCR_DISABLE_LED = 0x0001U, ///< RW Disable LED
P1MBSR_T4_CAPABLE = 0x8000U, ///< RO T4 Capable
P1MBSR_100_FULL_CAPABLE = 0x4000U, ///< RO 100 Full Capable
P1MBSR_100_HALF_CAPABLE = 0x2000U, ///< RO 100 Half Capable
P1MBSR_10_FULL_CAPABLE = 0x1000U, ///< RO 10 Full Capable
P1MBSR_10_HALF_CAPABLE = 0x0800U, ///< RO 10 Half Capable
P1MBSR_PREAMBLE_SUPPRESSED = 0x0040U, ///< RO Preamble suppressed (not supported)
P1MBSR_AN_COMPLETE = 0x0020U, ///< RO AN Complete
P1MBSR_AN_CAPABLE = 0x0008U, ///< RO AN Capable
P1MBSR_LINK_STATUS = 0x0004U, ///< RO Link Status
P1MBSR_JABBER_TEST = 0x0002U, ///< RO Jabber test (not supported)
P1MBSR_EXTENDED_CAPABLE = 0x0001U, ///< RO Extended Capable
P1ANAR_NEXT_PAGE = 0x8000U, ///< RO Next page (not supported)
P1ANAR_REMOTE_FAULT = 0x2000U, ///< RO Remote fault (not supported)
P1ANAR_PAUSE = 0x0400U, ///< RW Pause (flow control capability)
P1ANAR_ADV_100_FULL = 0x0100U, ///< RW Adv 100 Full
P1ANAR_ADV_100_HALF = 0x0080U, ///< RW Adv 100 Half
P1ANAR_ADV_10_FULL = 0x0040U, ///< RW Adv 10 Full
P1ANAR_ADV_10_HALF = 0x0020U, ///< RW Adv 10 Half
P1ANLPR_NEXT_PAGE = 0x8000U, ///< RO Next page (not supported)
P1ANLPR_LP_ACK = 0x4000U, ///< RO LP ACK (not suppported)
P1ANLPR_REMOTE_FAULT = 0x2000U, ///< RO Remote fault (not supported)
P1ANLPR_PAUSE = 0x0400U, ///< RO Pause
P1ANLPR_ADV_100_FULL = 0x0100U, ///< RO Adv 100 Full
P1ANLPR_ADV_100_HALF = 0x0080U, ///< RO Adv 100 Half
P1ANLPR_ADV_10_FULL = 0x0040U, ///< RO Adv 10 Full
P1ANLPR_ADV_10_HALF = 0x0020U, ///< RO Adv 10 Half
P1SCLMD_VCT_RESULT_SHIFT = 13U, ///< RO VCT result Shift
P1SCLMD_VCT_RESULT_MASK = 0x3 << P1SCLMD_VCT_RESULT_SHIFT, ///< RO VCT result Mask
P1SCLMD_VCT_RESULT_NORMAL = 0x0U, ///< RO VCT result normal condition
P1SCLMD_VCT_RESULT_OPEN = 0x1U, ///< RO VCT result open cable condition
P1SCLMD_VCT_RESULT_SHORT = 0x2U, ///< RO VCT result short cable condition
P1SCLMD_VCT_RESULT_TEST_FAILED = 0x3U, ///< RO VCT result test failed
P1SCLMD_VCT_ENABLE = 0x1000U, ///< RW (SC) VCT Enable
P1SCLMD_FORCE_LINK = 0x0800U, ///< RW Force link
P1SCLMD_REMOTE_LOOPBACK = 0x0200U, ///< RW Remote (Near-end) loopback (rlb)
P1SCLMD_VCT_FAULT_COUNT_MASK = 0x1FU, ///< RO Distance to the fault * 0.4m
P1CR_LED_OFF = 0x8000U, ///< RW Turn off all of the port 1 LEDs
P1CR_TXIDS = 0x4000U, ///< RW Disable the ports transmitter.
P1CR_RESTART_AN = 0x2000U, ///< RW Restart AN
P1CR_DISABLE_AUTO_MDI_MDIX = 0x0400U, ///< RW Disable auto MDI/MDI-X
P1CR_FORCE_MDIX = 0x0200U, ///< RW Force MDI-X
P1CR_AUTO_NEGOTIATION_ENABLE = 0x0080U, ///< RW Auto Negotiation Enable
P1CR_FORCE_SPEED = 0x0040U, ///< RW Force Speed 100
P1CR_FORCE_DUPLEX = 0x0020U, ///< RW Force Full Duplex
P1CR_ADVERTISED_FLOW_CONTROL_CAPABILITY = 0x0010U, ///< RW Advertise flow control capability
P1CR_ADVERTISED_100BT_FULL_DUPLEX_CAPABILITY = 0x0008U, ///< RW Advertise 100BT full-duplex capability
P1CR_ADVERTISED_100BT_HALF_DUPLEX_CAPABILITY = 0x0004U, ///< RW Advertise 100BT half-duplex capability
P1CR_ADVERTISED_10BT_FULL_DUPLEX_CAPABILITY = 0x0002U, ///< RW Advertise 10BT full-duplex capability
P1CR_ADVERTISED_10BT_HALF_DUPLEX_CAPABILITY = 0x0001U, ///< RW Advertise 10BT half-duplex capability
P1SR_HP_MDIX = 0x8000U, ///< RW HP Auto MDI-X mode
P1SR_POLARITY_REVERSE = 0x2000U, ///< RO Polarity Reverse
P1SR_OPERATION_SPEED = 0x0400U, ///< RO Operation Speed 100
P1SR_OPERATION_DUPLEX = 0x0200U, ///< RO Operation Duplex Full
P1SR_MDIX_STATUS = 0x0080U, ///< RO MDI status
P1SR_AN_DONE = 0x0040U, ///< RO AN Done
P1SR_LINK_GOOD = 0x0020U, ///< RO Link Good
P1SR_PARTNER_FLOW_CONTROL_CAPABILITY = 0x0010U, ///< RO Partner flow control capability
P1SR_PARTNER_100BT_FULL_DUPLEX_CAPABILITY = 0x0008U, ///< RO Partner 100BT full-duplex capability
P1SR_PARTNER_100BT_HALF_DUPLEX_CAPABILITY = 0x0004U, ///< RO Partner 100BT half-duplex capability
P1SR_PARTNER_10BT_FULL_DUPLEX_CAPABILITY = 0x0002U, ///< RO Partner 10BT full-duplex capability
P1SR_PARTNER_10BT_HALF_DUPLEX_CAPABILITY = 0x0001U, ///< RO Partner 10BT half-duplex capability
} ksz8851_register_bits_t;

View File

@@ -32,6 +32,14 @@ menu "Example Configuration"
select ETH_SPI_ETHERNET_W5500
help
Select external SPI-Ethernet module (W5500).
config EXAMPLE_USE_KSZ8851SNL
bool "KSZ8851SNL Module"
select EXAMPLE_USE_SPI_ETHERNET
select ETH_USE_SPI_ETHERNET
select ETH_SPI_ETHERNET_KSZ8851SNL
help
Select external SPI-Ethernet module (KSZ8851SNL).
endchoice # EXAMPLE_ETHERNET_TYPE
if EXAMPLE_USE_INTERNAL_ETHERNET

View File

@@ -112,7 +112,21 @@ void app_main(void)
.quadhd_io_num = -1,
};
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1));
#if CONFIG_EXAMPLE_USE_DM9051
#if CONFIG_EXAMPLE_USE_KSZ8851SNL
spi_device_interface_config_t devcfg = {
.mode = 0,
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
.spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO,
.queue_size = 20
};
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
/* KSZ8851SNL ethernet driver is based on spi driver */
eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(spi_handle);
ksz8851snl_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO;
esp_eth_mac_t *mac = esp_eth_mac_new_ksz8851snl(&ksz8851snl_config, &mac_config);
esp_eth_phy_t *phy = esp_eth_phy_new_ksz8851snl(&phy_config);
#elif CONFIG_EXAMPLE_USE_DM9051
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,