diff --git a/drivers/ethernet/CMakeLists.txt b/drivers/ethernet/CMakeLists.txt index abab18562e15..65ce8a453322 100644 --- a/drivers/ethernet/CMakeLists.txt +++ b/drivers/ethernet/CMakeLists.txt @@ -42,6 +42,7 @@ zephyr_library_sources_ifdef(CONFIG_ETH_LAN865X eth_lan865x.c oa_tc6.c) zephyr_library_sources_ifdef(CONFIG_ETH_XMC4XXX eth_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_ETH_TEST eth_test.c) zephyr_library_sources_ifdef(CONFIG_ETH_RENESAS_RA eth_renesas_ra.c) +zephyr_library_sources_ifdef(CONFIG_ETH_LAN9250 eth_lan9250.c) if(CONFIG_ETH_NXP_S32_NETC) zephyr_library_sources(eth_nxp_s32_netc.c) diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index 7164189b1aac..ba7375195825 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -75,6 +75,7 @@ source "drivers/ethernet/Kconfig.numaker" source "drivers/ethernet/Kconfig.lan865x" source "drivers/ethernet/Kconfig.xmc4xxx" source "drivers/ethernet/Kconfig.test" +source "drivers/ethernet/Kconfig.lan9250" source "drivers/ethernet/eth_nxp_enet_qos/Kconfig" diff --git a/drivers/ethernet/Kconfig.lan9250 b/drivers/ethernet/Kconfig.lan9250 new file mode 100644 index 000000000000..041cb7e49dd6 --- /dev/null +++ b/drivers/ethernet/Kconfig.lan9250 @@ -0,0 +1,40 @@ +# LAN9250 Stand-alone Ethernet Controller configuration options + +# Copyright (c) 2024 Mario Paja +# SPDX-License-Identifier: Apache-2.0 + + +menuconfig ETH_LAN9250 + bool "LAN9250 Ethernet Controller" + default y + depends on DT_HAS_MICROCHIP_LAN9250_ENABLED + select SPI + help + LAN9250 Stand-Alone Ethernet Controller + with SPI Interface + +if ETH_LAN9250 + +config ETH_LAN9250_RX_THREAD_STACK_SIZE + int "Stack size for internal incoming packet handler" + default 800 + help + Size of the stack used for internal thread which is ran for + incoming packet processing. + +config ETH_LAN9250_RX_THREAD_PRIO + int "Priority for internal incoming packet handler" + default 2 + help + Priority level for internal thread which is ran for incoming + packet processing. + +config ETH_LAN9250_BUF_ALLOC_TIMEOUT + int "Network buffer allocation timeout" + default 100 + help + Given timeout in milliseconds. Maximum amount of time + that the driver will wait from the IP stack to get + a memory buffer before the Ethernet frame is dropped. + +endif diff --git a/drivers/ethernet/eth_lan9250.c b/drivers/ethernet/eth_lan9250.c new file mode 100644 index 000000000000..334e17a78af2 --- /dev/null +++ b/drivers/ethernet/eth_lan9250.c @@ -0,0 +1,703 @@ +/* LAN9250 Stand-alone Ethernet Controller with SPI + * + * Copyright (c) 2024 Mario Paja + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT microchip_lan9250 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eth_lan9250_priv.h" + +LOG_MODULE_REGISTER(eth_lan9250, CONFIG_ETHERNET_LOG_LEVEL); + +static int lan9250_write_sys_reg(const struct device *dev, uint16_t address, uint32_t data) +{ + const struct lan9250_config *config = dev->config; + uint8_t cmd[1] = {LAN9250_SPI_INSTR_WRITE}; + uint8_t addr[2]; + uint8_t instr[4]; + struct spi_buf tx_buf[3]; + const struct spi_buf_set tx = {.buffers = tx_buf, .count = 3}; + + sys_put_be16(address, addr); + sys_put_le32(data, instr); + + tx_buf[0].buf = &cmd; + tx_buf[0].len = ARRAY_SIZE(cmd); + tx_buf[1].buf = addr; + tx_buf[1].len = ARRAY_SIZE(addr); + tx_buf[2].buf = instr; + tx_buf[2].len = ARRAY_SIZE(instr); + + return spi_write_dt(&config->spi, &tx); +} + +static int lan9250_read_sys_reg(const struct device *dev, uint16_t address, uint32_t *value) +{ + const struct lan9250_config *config = dev->config; + uint8_t cmd[1] = {LAN9250_SPI_INSTR_READ}; + uint8_t addr[2]; + struct spi_buf tx_buf[3]; + struct spi_buf rx_buf[3]; + const struct spi_buf_set tx = {.buffers = tx_buf, .count = 3}; + const struct spi_buf_set rx = {.buffers = rx_buf, .count = 3}; + + sys_put_be16(address, addr); + + tx_buf[0].buf = &cmd; + tx_buf[0].len = ARRAY_SIZE(cmd); + tx_buf[1].buf = addr; + tx_buf[1].len = ARRAY_SIZE(addr); + tx_buf[2].buf = NULL; + tx_buf[2].len = sizeof(uint32_t); + + rx_buf[0].buf = NULL; + rx_buf[0].len = 1; + rx_buf[1].buf = NULL; + rx_buf[1].len = 2; + rx_buf[2].buf = value; + rx_buf[2].len = sizeof(uint32_t); + + return spi_transceive_dt(&config->spi, &tx, &rx); +} + +static int lan9250_wait_ready(const struct device *dev, uint16_t address, uint32_t mask, + uint32_t expected, uint32_t m_second) +{ + uint32_t tmp; + int wait_time = 0; + + while (true) { + lan9250_read_sys_reg(dev, address, &tmp); + wait_time++; + k_busy_wait(USEC_PER_MSEC * 1U); + if ((tmp & mask) == expected) { + return 0; + } else if (wait_time == m_second) { + LOG_ERR("NOT READY"); + return -EIO; + } + } +} + +static int lan9250_read_mac_reg(const struct device *dev, uint8_t address, uint32_t *value) +{ + uint32_t tmp; + + /* Wait for MAC to be ready and send writing register command and data */ + lan9250_wait_ready(dev, LAN9250_MAC_CSR_CMD, LAN9250_MAC_CSR_CMD_BUSY, 0, + LAN9250_MAC_TIMEOUT); + lan9250_write_sys_reg(dev, LAN9250_MAC_CSR_CMD, + address | LAN9250_MAC_CSR_CMD_BUSY | LAN9250_MAC_CSR_CMD_READ); + + /* Wait for MAC to be ready and send writing register command and data */ + lan9250_wait_ready(dev, LAN9250_MAC_CSR_CMD, LAN9250_MAC_CSR_CMD_BUSY, 0, + LAN9250_MAC_TIMEOUT); + lan9250_read_sys_reg(dev, LAN9250_MAC_CSR_DATA, &tmp); + + *value = tmp; + return 0; +} + +static int lan9250_write_mac_reg(const struct device *dev, uint8_t address, uint32_t data) +{ + /* Wait for MAC to be ready and send writing register command and data */ + lan9250_wait_ready(dev, LAN9250_MAC_CSR_CMD, LAN9250_MAC_CSR_CMD_BUSY, 0, + LAN9250_MAC_TIMEOUT); + lan9250_write_sys_reg(dev, LAN9250_MAC_CSR_DATA, data); + lan9250_write_sys_reg(dev, LAN9250_MAC_CSR_CMD, address | LAN9250_MAC_CSR_CMD_BUSY); + + /* Wait until writing MAC is done */ + lan9250_wait_ready(dev, LAN9250_MAC_CSR_CMD, LAN9250_MAC_CSR_CMD_BUSY, 0, + LAN9250_MAC_TIMEOUT); + + return 0; +} + +static int lan9250_wait_mac_ready(const struct device *dev, uint8_t address, uint32_t mask, + uint32_t expected, uint32_t m_second) +{ + uint32_t tmp; + int wait_time = 0; + + while (true) { + lan9250_read_mac_reg(dev, address, &tmp); + wait_time++; + k_msleep(1); + if ((tmp & mask) == expected) { + return 0; + } else if (wait_time == m_second) { + return -EIO; + } + } +} + +static int lan9250_read_phy_reg(const struct device *dev, uint8_t address, uint16_t *value) +{ + uint32_t tmp; + + /* Wait PHY to be ready and send reading register command */ + lan9250_wait_mac_ready(dev, LAN9250_HMAC_MII_ACC, LAN9250_HMAC_MII_ACC_MIIBZY, 0, + LAN9250_PHY_TIMEOUT); + + /* Reference: Microchip Ethernet LAN9250 + * https://github.com/microchip-pic-avr-solutions/ethernet-lan9250/ + * + * Datasheet: + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/00001913A.pdf + * + * 12.2.18 PHY REGISTERS + * The PHY registers are indirectly accessed through the Host MAC MII Access Register + * (HMAC_MII_ACC) and Host MAC MII Data Register (HMAC_MII_DATA). + * + * Write 32bit value to the indirect MAC registers + * Where phy_add = 0b00001 & index = address + * Data = ((phy_add & 0x1F) << 11) | ((index & 0x1F) << 6) + */ + lan9250_write_mac_reg(dev, LAN9250_HMAC_MII_ACC, (1 << 11) | ((address & 0x1F) << 6)); + + /* Wait PHY to be ready and send reading register command */ + lan9250_wait_mac_ready(dev, LAN9250_HMAC_MII_ACC, LAN9250_HMAC_MII_ACC_MIIBZY, 0, + LAN9250_PHY_TIMEOUT); + + /* Read 32bit value from the indirect MAC registers */ + lan9250_read_mac_reg(dev, LAN9250_HMAC_MII_DATA, &tmp); + *value = tmp; + + return 0; +} + +static int lan9250_write_phy_reg(const struct device *dev, uint8_t address, uint16_t data) +{ + /* Wait PHY to be ready and send reading register command */ + lan9250_wait_mac_ready(dev, LAN9250_HMAC_MII_ACC, LAN9250_HMAC_MII_ACC_MIIBZY, 0, + LAN9250_PHY_TIMEOUT); + lan9250_write_mac_reg(dev, LAN9250_HMAC_MII_DATA, data); + + /* Reference: Microchip Ethernet LAN9250 + * https://github.com/microchip-pic-avr-solutions/ethernet-lan9250/ + * + * Datasheet: + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/00001913A.pdf + * + * 12.2.18 PHY REGISTERS + * The PHY registers are indirectly accessed through the Host MAC MII Access Register + * (HMAC_MII_ACC) and Host MAC MII Data Register (HMAC_MII_DATA). + * + * Write 32bit value to the indirect MAC registers + * Where phy_add = 0b00001 & index = address + * Data = ((phy_add & 0x1F) << 11) | ((index & 0x1F)<< 6) | MIIWnR + */ + lan9250_write_mac_reg(dev, LAN9250_HMAC_MII_ACC, + (1 << 11) | ((address & 0x1F) << 6) | LAN9250_HMAC_MII_ACC_MIIW_R); + + /* Wait PHY to be ready and send reading register command */ + lan9250_wait_mac_ready(dev, LAN9250_HMAC_MII_ACC, LAN9250_HMAC_MII_ACC_MIIBZY, 0, + LAN9250_PHY_TIMEOUT); + + return 0; +} + +static int lan9250_set_macaddr(const struct device *dev) +{ + struct lan9250_runtime *ctx = dev->data; + + lan9250_write_mac_reg(dev, LAN9250_HMAC_ADDRL, + ctx->mac_address[0] | (ctx->mac_address[1] << 8) | + (ctx->mac_address[2] << 16) | (ctx->mac_address[3] << 24)); + lan9250_write_mac_reg(dev, LAN9250_HMAC_ADDRH, + ctx->mac_address[4] | (ctx->mac_address[5] << 8)); + + return 0; +} + +static int lan9250_hw_cfg_check(const struct device *dev) +{ + uint32_t tmp; + + do { + lan9250_read_sys_reg(dev, LAN9250_HW_CFG, &tmp); + k_busy_wait(USEC_PER_MSEC * 1U); + } while ((tmp & LAN9250_HW_CFG_DEVICE_READY) == 0); + + return 0; +} + +static int lan9250_sw_reset(const struct device *dev) +{ + lan9250_write_sys_reg(dev, LAN9250_RESET_CTL, + LAN9250_RESET_CTL_HMAC_RST | LAN9250_RESET_CTL_PHY_RST | + LAN9250_RESET_CTL_DIGITAL_RST); + + /* Wait until LAN9250 SPI bus is ready */ + lan9250_wait_ready(dev, LAN9250_BYTE_TEST, BOTR_MASK, LAN9250_BYTE_TEST_DEFAULT, + LAN9250_RESET_TIMEOUT); + + return 0; +} + +static int lan9250_configure(const struct device *dev) +{ + uint32_t tmp; + + lan9250_hw_cfg_check(dev); + + /* Read LAN9250 hardware ID */ + lan9250_read_sys_reg(dev, LAN9250_ID_REV, &tmp); + if ((tmp & LAN9250_ID_REV_CHIP_ID) != LAN9250_ID_REV_CHIP_ID_DEFAULT) { + LOG_ERR("ERROR: Bad Rev ID: %08x\n", tmp); + return -ENODEV; + } + + /* Configure TX FIFO size mode to be 8: + * + * - TX data FIFO size: 7680 + * - RX data FIFO size: 7680 + * - TX status FIFO size: 512 + * - RX status FIFO size: 512 + */ + lan9250_write_sys_reg(dev, LAN9250_HW_CFG, + LAN9250_HW_CFG_MBO | LAN9250_HW_CFG_TX_FIF_SZ_8KB); + + /* Configure MAC automatic flow control: + * + * Reference: Microchip Ethernet LAN9250 + * https://github.com/microchip-pic-avr-solutions/ethernet-lan9250/ + * LAN_Regwrite32(AFC_CFG, 0x006E3741); + * + */ + lan9250_write_sys_reg(dev, LAN9250_AFC_CFG, 0x006e3741); + + /* Configure interrupt: + * + * - Interrupt De-assertion interval: 100 + * - Interrupt output to pin + * - Interrupt pin active output low + * - Interrupt pin push-pull driver + */ + lan9250_write_sys_reg(dev, LAN9250_IRQ_CFG, + LAN9250_IRQ_CFG_INT_DEAS_100US | LAN9250_IRQ_CFG_IRQ_EN | + LAN9250_IRQ_CFG_IRQ_TYPE_PP); + + /* Configure interrupt trigger source, please refer to macro + * LAN9250_INT_SOURCE. + */ + lan9250_write_sys_reg(dev, LAN9250_INT_EN, + LAN9250_INT_EN_PHY_INT_EN | LAN9250_INT_EN_RSFL_EN); + + /* Disable TX data FIFO available interrupt */ + lan9250_write_sys_reg(dev, LAN9250_FIFO_INT, + LAN9250_FIFO_INT_TX_DATA_AVAILABLE_LEVEL | + LAN9250_FIFO_INT_TX_STATUS_LEVEL); + + /* Configure RX: + * + * - RX DMA counter: Ethernet maximum packet size + * - RX data offset: 4, so that need read dummy before reading data + */ + lan9250_write_sys_reg(dev, LAN9250_RX_CFG, 0x06000000 | 0x00000400); + + /* Configure remote power management: + * + * - Auto wakeup + * - Disable 1588 clock + * - Disable 1588 timestamp unit clock + * - Energy-detect + * - Wake on + * - Clear wakeon + */ + lan9250_write_sys_reg(dev, LAN9250_PMT_CTRL, + LAN9250_PMT_CTRL_PM_WAKE | LAN9250_PMT_CTRL_1588_DIS | + LAN9250_PMT_CTRL_1588_TSU_DIS | LAN9250_PMT_CTRL_WOL_EN | + LAN9250_PMT_CTRL_WOL_STS); + + /* Configure PHY basic control: + * + * - Auto-Negotiation for 10/100 Mbits and Half/Full Duplex + */ + lan9250_write_phy_reg(dev, LAN9250_PHY_BASIC_CONTROL, + LAN9250_PHY_BASIC_CONTROL_PHY_AN | + LAN9250_PHY_BASIC_CONTROL_PHY_SPEED_SEL_LSB | + LAN9250_PHY_BASIC_CONTROL_PHY_DUPLEX); + + /* Configure PHY auto-negotiation advertisement capability: + * + * - Asymmetric pause + * - Symmetric pause + * - 100Base-X half/full duplex + * - 10Base-X half/full duplex + * - Select IEEE802.3 + */ + lan9250_write_phy_reg(dev, LAN9250_PHY_AN_ADV, + LAN9250_PHY_AN_ADV_ASYM_PAUSE | LAN9250_PHY_AN_ADV_SYM_PAUSE | + LAN9250_PHY_AN_ADV_100BTX_HD | LAN9250_PHY_AN_ADV_100BTX_FD | + LAN9250_PHY_AN_ADV_10BT_HD | LAN9250_PHY_AN_ADV_10BT_FD | + LAN9250_PHY_AN_ADV_SELECTOR_DEFAULT); + + /* Configure PHY special mode: + * + * - PHY mode = 111b, enable all capable and auto-nagotiation + * - PHY address = 1, default value is fixed to 1 by manufacturer + */ + lan9250_write_phy_reg(dev, LAN9250_PHY_SPECIAL_MODES, 0x00E0 | 1); + + /* Configure PHY special control or status indication: + * + * - Port auto-MDIX determined by bits 14 and 13 + * - Auto-MDIX + * - Disable SQE tests + */ + lan9250_write_phy_reg(dev, LAN9250_PHY_SPECIAL_CONTROL_STAT_IND, + LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_AMDIXCTRL | + LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_AMDIXEN | + LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_SQEOFF); + + /* Configure PHY interrupt source: + * + * - Link up + * - Link down + */ + lan9250_write_phy_reg(dev, LAN9250_PHY_INTERRUPT_MASK, + LAN9250_PHY_INTERRUPT_SOURCE_LINK_UP | + LAN9250_PHY_INTERRUPT_SOURCE_LINK_DOWN); + + /* Configure special control or status: + * + * - Fixed to write 0000010b to reserved filed + */ + lan9250_write_phy_reg(dev, LAN9250_PHY_SPECIAL_CONTROL_STATUS, + LAN9250_PHY_MODE_CONTROL_STATUS_ALTINT); + + /* Clear interrupt status */ + lan9250_write_sys_reg(dev, LAN9250_INT_STS, 0xFFFFFFFF); + + /* Configure HMAC control: + * + * - Automatically strip the pad field on incoming packets + * - TX enable + * - RX enable + * - Full duplex + * - Promiscuous disabled + */ + lan9250_write_mac_reg(dev, LAN9250_HMAC_CR, + LAN9250_HMAC_CR_PADSTR | LAN9250_HMAC_CR_TXEN | LAN9250_HMAC_CR_RXEN | + LAN9250_HMAC_CR_FDPX); + + /* Configure TX: + * + * - TX enable + */ + lan9250_write_sys_reg(dev, LAN9250_TX_CFG, LAN9250_TX_CFG_TX_ON); + + return 0; +} + +static int lan9250_write_buf(const struct device *dev, uint8_t *data_buffer, uint16_t buf_len) +{ + const struct lan9250_config *config = dev->config; + uint8_t cmd[1] = {LAN9250_SPI_INSTR_WRITE}; + uint8_t instr[2] = {(LAN9250_TX_DATA_FIFO >> 8) & 0xFF, (LAN9250_TX_DATA_FIFO & 0xFF)}; + struct spi_buf tx_buf[3]; + const struct spi_buf_set tx = {.buffers = tx_buf, .count = 3}; + + tx_buf[0].buf = &cmd; + tx_buf[0].len = ARRAY_SIZE(cmd); + tx_buf[1].buf = &instr; + tx_buf[1].len = ARRAY_SIZE(instr); + tx_buf[2].buf = data_buffer; + tx_buf[2].len = buf_len; + + return spi_transceive_dt(&config->spi, &tx, NULL); +} + +static int lan9250_read_buf(const struct device *dev, uint8_t *data_buffer, uint16_t buf_len) +{ + const struct lan9250_config *config = dev->config; + uint8_t cmd[1] = {LAN9250_SPI_INSTR_READ}; + uint8_t instr[2] = {(LAN9250_RX_DATA_FIFO >> 8) & 0xFF, (LAN9250_RX_DATA_FIFO & 0xFF)}; + struct spi_buf tx_buf[3]; + struct spi_buf rx_buf[3]; + const struct spi_buf_set tx = {.buffers = tx_buf, .count = 3}; + const struct spi_buf_set rx = {.buffers = rx_buf, .count = 3}; + + tx_buf[0].buf = &cmd; + tx_buf[0].len = ARRAY_SIZE(cmd); + tx_buf[1].buf = &instr; + tx_buf[1].len = ARRAY_SIZE(instr); + tx_buf[2].buf = NULL; + tx_buf[2].len = buf_len; + + rx_buf[0].buf = NULL; + rx_buf[0].len = 1; + rx_buf[1].buf = NULL; + rx_buf[1].len = 2; + rx_buf[2].buf = data_buffer; + rx_buf[2].len = buf_len; + + return spi_transceive_dt(&config->spi, &tx, &rx); +} + +static int lan9250_rx(const struct device *dev) +{ + const struct lan9250_config *config = dev->config; + struct lan9250_runtime *ctx = dev->data; + const uint16_t buf_rx_size = CONFIG_NET_BUF_DATA_SIZE; + struct net_pkt *pkt; + struct net_buf *pkt_buf; + uint16_t pkt_len; + uint8_t pktcnt; + uint32_t tmp; + + /* Check valid packet count */ + lan9250_read_sys_reg(dev, LAN9250_RX_FIFO_INF, &tmp); + pktcnt = (tmp & 0x00ff0000) >> 16; + + /* Check packet length */ + lan9250_read_sys_reg(dev, LAN9250_RX_STATUS_FIFO, &tmp); + pkt_len = (tmp & LAN9250_RX_STS_PACKET_LEN) >> 16; + + if (pktcnt == 0 || pkt_len == 0) { + return 0; + } + + /* Read dummy data */ + lan9250_read_sys_reg(dev, LAN9250_RX_DATA_FIFO, &tmp); + pkt_len -= 4; + + if (pkt_len > NET_ETH_MAX_FRAME_SIZE) { + LOG_ERR("Maximum frame length exceeded, it should be: %d", NET_ETH_MAX_FRAME_SIZE); + eth_stats_update_errors_rx(ctx->iface); + } + + /* Get the frame from the buffer */ + pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, pkt_len, AF_UNSPEC, 0, + K_MSEC(config->timeout)); + if (!pkt) { + LOG_ERR("%s: Could not allocate rx buffer", dev->name); + eth_stats_update_errors_rx(ctx->iface); + return 0; + } + + pkt_buf = pkt->buffer; + + do { + uint8_t *data_ptr = pkt_buf->data; + uint16_t data_len; + + if (pkt_len > buf_rx_size) { + data_len = buf_rx_size; + } else { + data_len = pkt_len; + } + pkt_len -= data_len; + + lan9250_read_buf(dev, data_ptr, data_len); + net_buf_add(pkt_buf, data_len); + + } while (pkt_len > 0); + + lan9250_read_sys_reg(dev, LAN9250_RX_DATA_FIFO, &tmp); + net_pkt_set_iface(pkt, ctx->iface); + + /* Feed buffer frame to IP stack */ + if (net_recv_data(net_pkt_iface(pkt), pkt) < 0) { + net_pkt_unref(pkt); + } + + k_sem_give(&ctx->tx_rx_sem); + + return 0; +} + +static int lan9250_tx(const struct device *dev, struct net_pkt *pkt) +{ + struct lan9250_runtime *ctx = dev->data; + size_t len = net_pkt_get_len(pkt); + uint32_t regval; + uint16_t free_size; + uint8_t status_size; + uint32_t tmp; + + lan9250_read_sys_reg(dev, LAN9250_TX_FIFO_INF, ®val); + status_size = (regval & LAN9250_TX_FIFO_INF_TXSUSED) >> 16; + free_size = regval & LAN9250_TX_FIFO_INF_TXFREE; + + k_sem_take(&ctx->tx_rx_sem, K_FOREVER); + + /* TX command 'A' */ + lan9250_write_sys_reg(dev, LAN9250_TX_DATA_FIFO, + LAN9250_TX_CMD_A_INT_ON_COMP | LAN9250_TX_CMD_A_BUFFER_ALIGN_4B | + LAN9250_TX_CMD_A_START_OFFSET_0B | + LAN9250_TX_CMD_A_FIRST_SEG | LAN9250_TX_CMD_A_LAST_SEG | len); + + /* TX command 'B' */ + lan9250_write_sys_reg(dev, LAN9250_TX_DATA_FIFO, LAN9250_TX_CMD_B_PACKET_TAG | len); + + if (net_pkt_read(pkt, ctx->buf, len)) { + return -EIO; + } + + lan9250_write_buf(dev, ctx->buf, LAN9250_ALIGN(len)); + + for (int i = 0; i < status_size; i++) { + lan9250_read_sys_reg(dev, LAN9250_TX_STATUS_FIFO, &tmp); + } + + k_sem_give(&ctx->tx_rx_sem); + + return 0; +} + +static void lan9250_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) +{ + struct lan9250_runtime *context = CONTAINER_OF(cb, struct lan9250_runtime, gpio_cb); + + k_sem_give(&context->int_sem); +} + +static void lan9250_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + struct lan9250_runtime *context = p1; + uint32_t int_sts; + uint16_t tmp; + uint32_t ier; + + while (true) { + k_sem_take(&context->int_sem, K_FOREVER); + + /* Save interrupt enable register value */ + lan9250_read_sys_reg(context->dev, LAN9250_INT_EN, &ier); + + /* Disable interrupts to release the interrupt line */ + lan9250_write_sys_reg(context->dev, LAN9250_INT_EN, 0); + + /* Read interrupt status register */ + lan9250_read_sys_reg(context->dev, LAN9250_INT_STS, &int_sts); + + if ((int_sts & LAN9250_INT_STS_PHY_INT) != 0) { + + /* Read PHY interrupt source register */ + lan9250_read_phy_reg(context->dev, LAN9250_PHY_INTERRUPT_SOURCE, &tmp); + if (tmp & LAN9250_PHY_INTERRUPT_SOURCE_LINK_UP) { + LOG_DBG("LINK UP"); + net_eth_carrier_on(context->iface); + } else if (tmp & LAN9250_PHY_INTERRUPT_SOURCE_LINK_DOWN) { + LOG_DBG("LINK DOWN"); + net_eth_carrier_off(context->iface); + } + } + + if ((int_sts & LAN9250_INT_STS_RSFL) != 0) { + lan9250_write_sys_reg(context->dev, LAN9250_INT_STS, LAN9250_INT_STS_RSFL); + lan9250_rx(context->dev); + } + + /* Re-enable interrupts */ + lan9250_write_sys_reg(context->dev, LAN9250_INT_EN, ier); + } +} + +static enum ethernet_hw_caps lan9250_get_capabilities(const struct device *dev) +{ + ARG_UNUSED(dev); + + return ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T; +} + +static void lan9250_iface_init(struct net_if *iface) +{ + const struct device *dev = net_if_get_device(iface); + struct lan9250_runtime *context = dev->data; + + net_if_set_link_addr(iface, context->mac_address, sizeof(context->mac_address), + NET_LINK_ETHERNET); + context->iface = iface; + ethernet_init(iface); + + net_if_carrier_off(iface); +} + +static const struct ethernet_api api_funcs = { + .iface_api.init = lan9250_iface_init, + .get_capabilities = lan9250_get_capabilities, + .send = lan9250_tx, +}; + +static int lan9250_init(const struct device *dev) +{ + const struct lan9250_config *config = dev->config; + struct lan9250_runtime *context = dev->data; + + context->dev = dev; + + /* SPI config */ + if (!spi_is_ready_dt(&config->spi)) { + LOG_ERR("SPI master port %s not ready", config->spi.bus->name); + return -EINVAL; + } + + /* Initialize GPIO */ + if (!gpio_is_ready_dt(&config->interrupt)) { + LOG_ERR("GPIO port %s not ready", config->interrupt.port->name); + return -EINVAL; + } + + if (gpio_pin_configure_dt(&config->interrupt, GPIO_INPUT)) { + LOG_ERR("Unable to configure GPIO pin %u", config->interrupt.pin); + return -EINVAL; + } + + gpio_init_callback(&(context->gpio_cb), lan9250_gpio_callback, BIT(config->interrupt.pin)); + if (gpio_add_callback(config->interrupt.port, &(context->gpio_cb))) { + return -EINVAL; + } + + gpio_pin_interrupt_configure_dt(&config->interrupt, GPIO_INT_EDGE_TO_ACTIVE); + + /* Wait until LAN9250 SPI bus is ready */ + lan9250_wait_ready(dev, LAN9250_BYTE_TEST, BOTR_MASK, LAN9250_BYTE_TEST_DEFAULT, + LAN9250_RESET_TIMEOUT); + lan9250_sw_reset(dev); + lan9250_configure(dev); + lan9250_set_macaddr(dev); + + k_thread_create(&context->thread, context->thread_stack, + CONFIG_ETH_LAN9250_RX_THREAD_STACK_SIZE, lan9250_thread, context, NULL, + NULL, K_PRIO_COOP(CONFIG_ETH_LAN9250_RX_THREAD_PRIO), 0, K_NO_WAIT); + + LOG_INF("LAN9250 Initialized"); + + return 0; +} + +#define LAN9250_DEFINE(inst) \ + static struct lan9250_runtime lan9250_##inst##_runtime = { \ + .mac_address = DT_INST_PROP(inst, local_mac_address), \ + .tx_rx_sem = Z_SEM_INITIALIZER(lan9250_##inst##_runtime.tx_rx_sem, 1, UINT_MAX), \ + .int_sem = Z_SEM_INITIALIZER(lan9250_##inst##_runtime.int_sem, 0, UINT_MAX), \ + }; \ + \ + static const struct lan9250_config lan9250_##inst##_config = { \ + .spi = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8), 0), \ + .interrupt = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \ + .timeout = CONFIG_ETH_LAN9250_BUF_ALLOC_TIMEOUT, \ + }; \ + \ + ETH_NET_DEVICE_DT_INST_DEFINE(inst, lan9250_init, NULL, &lan9250_##inst##_runtime, \ + &lan9250_##inst##_config, CONFIG_ETH_INIT_PRIORITY, \ + &api_funcs, NET_ETH_MTU); +DT_INST_FOREACH_STATUS_OKAY(LAN9250_DEFINE); diff --git a/drivers/ethernet/eth_lan9250_priv.h b/drivers/ethernet/eth_lan9250_priv.h new file mode 100644 index 000000000000..b6379216bdf3 --- /dev/null +++ b/drivers/ethernet/eth_lan9250_priv.h @@ -0,0 +1,333 @@ +/* LAN9250 Stand-alone Ethernet Controller with SPI + * + * Copyright (c) 2024 Mario Paja + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#ifndef _LAN9250_ +#define _LAN9250_ + +#define LAN9250_DEFAULT_NUMOF_RETRIES 3U +#define LAN9250_PHY_TIMEOUT 2000 +#define LAN9250_MAC_TIMEOUT 2000 +#define LAN9250_RESET_TIMEOUT 5000 + +#define LAN9250_ALIGN(v) (((v) + 3) & (~3)) + +/* SPI instructions */ +#define LAN9250_SPI_INSTR_WRITE 0x02 +#define LAN9250_SPI_INSTR_READ 0x03 + +/* TX command 'A' format */ +#define LAN9250_TX_CMD_A_INT_ON_COMP 0x80000000 +#define LAN9250_TX_CMD_A_BUFFER_ALIGN_4B 0x00000000 +#define LAN9250_TX_CMD_A_START_OFFSET_0B 0x00000000 +#define LAN9250_TX_CMD_A_FIRST_SEG 0x00002000 +#define LAN9250_TX_CMD_A_LAST_SEG 0x00001000 + +/* TX command 'B' format */ +#define LAN9250_TX_CMD_B_PACKET_TAG 0xFFFF0000 + +/* RX status format */ +#define LAN9250_RX_STS_PACKET_LEN 0x3FFF0000 + +/* LAN9250 System registers */ +#define LAN9250_RX_DATA_FIFO 0x0000 +#define LAN9250_TX_DATA_FIFO 0x0020 +#define LAN9250_RX_STATUS_FIFO 0x0040 +#define LAN9250_TX_STATUS_FIFO 0x0048 +#define LAN9250_ID_REV 0x0050 +#define LAN9250_IRQ_CFG 0x0054 +#define LAN9250_INT_STS 0x0058 +#define LAN9250_INT_EN 0x005C +#define LAN9250_BYTE_TEST 0x0064 +#define LAN9250_FIFO_INT 0x0068 +#define LAN9250_RX_CFG 0x006C +#define LAN9250_TX_CFG 0x0070 +#define LAN9250_HW_CFG 0x0074 +#define LAN9250_RX_FIFO_INF 0x007C +#define LAN9250_TX_FIFO_INF 0x0080 +#define LAN9250_PMT_CTRL 0x0084 +#define LAN9250_MAC_CSR_CMD 0x00A4 +#define LAN9250_MAC_CSR_DATA 0x00A8 +#define LAN9250_AFC_CFG 0x00AC +#define LAN9250_RESET_CTL 0x01F8 + +/* LAN9250 Host MAC registers */ +#define LAN9250_HMAC_CR 0x01 +#define LAN9250_HMAC_ADDRH 0x02 +#define LAN9250_HMAC_ADDRL 0x03 +#define LAN9250_HMAC_MII_ACC 0x06 +#define LAN9250_HMAC_MII_DATA 0x07 + +/* LAN9250 PHY registers */ +#define LAN9250_PHY_BASIC_CONTROL 0x00 +#define LAN9250_PHY_AN_ADV 0x04 +#define LAN9250_PHY_SPECIAL_MODES 0x12 +#define LAN9250_PHY_SPECIAL_CONTROL_STAT_IND 0x1B +#define LAN9250_PHY_INTERRUPT_SOURCE 0x1D +#define LAN9250_PHY_INTERRUPT_MASK 0x1E +#define LAN9250_PHY_SPECIAL_CONTROL_STATUS 0x1F + +/* Interrupt Configuration register */ +#define LAN9250_IRQ_CFG_INT_DEAS 0xFF000000 +#define LAN9250_IRQ_CFG_INT_DEAS_10US 0x01000000 +#define LAN9250_IRQ_CFG_INT_DEAS_100US 0x0A000000 +#define LAN9250_IRQ_CFG_INT_DEAS_1MS 0x64000000 +#define LAN9250_IRQ_CFG_INT_DEAS_CLR 0x00004000 +#define LAN9250_IRQ_CFG_INT_DEAS_STS 0x00002000 +#define LAN9250_IRQ_CFG_IRQ_INT 0x00001000 +#define LAN9250_IRQ_CFG_IRQ_EN 0x00000100 +#define LAN9250_IRQ_CFG_IRQ_POL 0x00000010 +#define LAN9250_IRQ_CFG_IRQ_POL_LOW 0x00000000 +#define LAN9250_IRQ_CFG_IRQ_POL_HIGH 0x00000010 +#define LAN9250_IRQ_CFG_IRQ_CLK_SELECT 0x00000002 +#define LAN9250_IRQ_CFG_IRQ_TYPE 0x00000001 +#define LAN9250_IRQ_CFG_IRQ_TYPE_OD 0x00000000 +#define LAN9250_IRQ_CFG_IRQ_TYPE_PP 0x00000001 + +/* INTERRUPT STATUS REGISTER (INT_STS) */ +#define LAN9250_INT_STS_SW_INT 0x80000000 +#define LAN9250_INT_STS_READY 0x40000000 +#define LAN9250_INT_STS_1588_EVNT 0x20000000 +#define LAN9250_INT_STS_PHY_INT 0x04000000 +#define LAN9250_INT_STS_TXSTOP_INT 0x02000000 +#define LAN9250_INT_STS_RXSTOP_INT 0x01000000 +#define LAN9250_INT_STS_RXDFH_INT 0x00800000 +#define LAN9250_INT_STS_TX_IOC 0x00200000 +#define LAN9250_INT_STS_RXD_INT 0x00100000 +#define LAN9250_INT_STS_GPT_INT 0x00080000 +#define LAN9250_INT_STS_PME_INT 0x00020000 +#define LAN9250_INT_STS_TXSO 0x00010000 +#define LAN9250_INT_STS_RWT 0x00008000 +#define LAN9250_INT_STS_RXE 0x00004000 +#define LAN9250_INT_STS_TXE 0x00002000 +#define LAN9250_INT_STS_GPIO 0x00001000 +#define LAN9250_INT_STS_TDFO 0x00000400 +#define LAN9250_INT_STS_TDFA 0x00000200 +#define LAN9250_INT_STS_TSFF 0x00000100 +#define LAN9250_INT_STS_TSFL 0x00000080 +#define LAN9250_INT_STS_RXDF_INT 0x00000040 +#define LAN9250_INT_STS_RSFF 0x00000010 +#define LAN9250_INT_STS_RSFL 0x00000008 + +/* INTERRUPT ENABLE REGISTER (INT_EN) */ +#define LAN9250_INT_EN_SW_INT_EN 0x80000000 +#define LAN9250_INT_EN_READY_EN 0x40000000 +#define LAN9250_INT_EN_1588_EVNT_EN 0x20000000 +#define LAN9250_INT_EN_PHY_INT_EN 0x04000000 +#define LAN9250_INT_EN_TXSTOP_INT_EN 0x02000000 +#define LAN9250_INT_EN_RXSTOP_INT_EN 0x01000000 +#define LAN9250_INT_EN_RXDFH_INT_EN 0x00800000 +#define LAN9250_INT_EN_TIOC_INT_EN 0x00200000 +#define LAN9250_INT_EN_RXD_INT_EN 0x00100000 +#define LAN9250_INT_EN_GPT_INT_EN 0x00080000 +#define LAN9250_INT_EN_PME_INT_EN 0x00020000 +#define LAN9250_INT_EN_TXSO_EN 0x00010000 +#define LAN9250_INT_EN_RWT_INT_EN 0x00008000 +#define LAN9250_INT_EN_RXE_INT_EN 0x00004000 +#define LAN9250_INT_EN_TXE_INT_EN 0x00002000 +#define LAN9250_INT_EN_GPIO_EN 0x00001000 +#define LAN9250_INT_EN_TDFO_EN 0x00000400 +#define LAN9250_INT_EN_TDFA_EN 0x00000200 +#define LAN9250_INT_EN_TSFF_EN 0x00000100 +#define LAN9250_INT_EN_TSFL_EN 0x00000080 +#define LAN9250_INT_EN_RXDF_INT_EN 0x00000040 +#define LAN9250_INT_EN_RSFF_EN 0x00000010 +#define LAN9250_INT_EN_RSFL_EN 0x00000008 + +/* Byte Order Test register */ +#define LAN9250_BYTE_TEST_DEFAULT 0x87654321 +#define BOTR_MASK 0xffffffff + +/* FIFO Level Interrupt register */ +#define LAN9250_FIFO_INT_TX_DATA_AVAILABLE_LEVEL 0xFF000000 +#define LAN9250_FIFO_INT_TX_STATUS_LEVEL 0x00FF0000 +#define LAN9250_FIFO_INT_RX_STATUS_LEVEL 0x000000FF + +/* TRANSMIT CONFIGURATION REGISTER (TX_CFG) */ +#define LAN9250_TX_CFG_TXS_DUMP 0x00008000 +#define LAN9250_TX_CFG_TXD_DUMP 0x00004000 +#define LAN9250_TX_CFG_TXSAO 0x00000004 +#define LAN9250_TX_CFG_TX_ON 0x00000002 +#define LAN9250_TX_CFG_STOP_TX 0x00000001 + +/* HARDWARE CONFIGURATION REGISTER (HW_CFG) */ +#define LAN9250_HW_CFG_DEVICE_READY 0x08000000 +#define LAN9250_HW_CFG_AMDIX_EN_STRAP_STATE 0x02000000 +#define LAN9250_HW_CFG_MBO 0x00100000 +#define LAN9250_HW_CFG_TX_FIF_SZ 0x000F0000 +#define LAN9250_HW_CFG_TX_FIF_SZ_2KB 0x00020000 +#define LAN9250_HW_CFG_TX_FIF_SZ_3KB 0x00030000 +#define LAN9250_HW_CFG_TX_FIF_SZ_4KB 0x00040000 +#define LAN9250_HW_CFG_TX_FIF_SZ_5KB 0x00050000 +#define LAN9250_HW_CFG_TX_FIF_SZ_6KB 0x00060000 +#define LAN9250_HW_CFG_TX_FIF_SZ_7KB 0x00070000 +#define LAN9250_HW_CFG_TX_FIF_SZ_8KB 0x00080000 +#define LAN9250_HW_CFG_TX_FIF_SZ_9KB 0x00090000 +#define LAN9250_HW_CFG_TX_FIF_SZ_10KB 0x000A0000 +#define LAN9250_HW_CFG_TX_FIF_SZ_11KB 0x000B0000 +#define LAN9250_HW_CFG_TX_FIF_SZ_12KB 0x000C0000 +#define LAN9250_HW_CFG_TX_FIF_SZ_13KB 0x000D0000 +#define LAN9250_HW_CFG_TX_FIF_SZ_14KB 0x000E0000 + +/* RX FIFO Information register */ +#define LAN9250_RX_FIFO_INF_RXSUSED 0x00FF0000 +#define LAN9250_RX_FIFO_INF_RXDUSED 0x0000FFFF + +/* TX FIFO Information register */ +#define LAN9250_TX_FIFO_INF_TXSUSED 0x00FF0000 +#define LAN9250_TX_FIFO_INF_TXFREE 0x0000FFFF + +/* Power Management Control Register (PMT_CTRL) */ +#define LAN9250_PMT_CTRL_PM_MODE 0xE0000000 +#define LAN9250_PMT_CTRL_PM_SLEEP_EN 0x10000000 +#define LAN9250_PMT_CTRL_PM_WAKE 0x08000000 +#define LAN9250_PMT_CTRL_LED_DIS 0x04000000 +#define LAN9250_PMT_CTRL_1588_DIS 0x02000000 +#define LAN9250_PMT_CTRL_1588_TSU_DIS 0x00400000 +#define LAN9250_PMT_CTRL_HMAC_DIS 0x00080000 +#define LAN9250_PMT_CTRL_HMAC_SYS_ONLY_DIS 0x00040000 +#define LAN9250_PMT_CTRL_ED_STS 0x00010000 +#define LAN9250_PMT_CTRL_ED_EN 0x00004000 +#define LAN9250_PMT_CTRL_WOL_EN 0x00000200 +#define LAN9250_PMT_CTRL_PME_TYPE 0x00000040 +#define LAN9250_PMT_CTRL_WOL_STS 0x00000020 +#define LAN9250_PMT_CTRL_PME_IND 0x00000008 +#define LAN9250_PMT_CTRL_PME_POL 0x00000004 +#define LAN9250_PMT_CTRL_PME_EN 0x00000002 +#define LAN9250_PMT_CTRL_READY 0x00000001 + +/* HOST MAC CSR INTERFACE COMMAND REGISTER (MAC_CSR_CMD) */ +#define LAN9250_MAC_CSR_CMD_BUSY 0x80000000 +#define LAN9250_MAC_CSR_CMD_WRITE 0x00000000 +#define LAN9250_MAC_CSR_CMD_READ 0x40000000 +#define LAN9250_MAC_CSR_CMD_ADDR 0x000000FF + +/* Reset Control Register (RESET_CTL) */ +#define LAN9250_RESET_CTL_HMAC_RST 0x00000020 +#define LAN9250_RESET_CTL_PHY_RST 0x00000002 +#define LAN9250_RESET_CTL_DIGITAL_RST 0x00000001 + +/* HOST MAC CONTROL REGISTER (HMAC_CR) */ +#define LAN9250_HMAC_CR_RXALL 0x80000000 +#define LAN9250_HMAC_CR_HMAC_EEE_ENABLE 0x02000000 +#define LAN9250_HMAC_CR_RCVOWN 0x00800000 +#define LAN9250_HMAC_CR_LOOPBK 0x00200000 +#define LAN9250_HMAC_CR_FDPX 0x00100000 +#define LAN9250_HMAC_CR_MCPAS 0x00080000 +#define LAN9250_HMAC_CR_PRMS 0x00040000 +#define LAN9250_HMAC_CR_INVFILT 0x00020000 +#define LAN9250_HMAC_CR_PASSBAD 0x00010000 +#define LAN9250_HMAC_CR_HO 0x00008000 +#define LAN9250_HMAC_CR_HPFILT 0x00002000 +#define LAN9250_HMAC_CR_BCAST 0x00000800 +#define LAN9250_HMAC_CR_DISRTY 0x00000400 +#define LAN9250_HMAC_CR_PADSTR 0x00000100 +#define LAN9250_HMAC_CR_BOLMT 0x000000C0 +#define LAN9250_HMAC_CR_BOLMT_10_BITS 0x00000000 +#define LAN9250_HMAC_CR_BOLMT_8_BITS 0x00000040 +#define LAN9250_HMAC_CR_BOLMT_4_BITS 0x00000080 +#define LAN9250_HMAC_CR_BOLMT_1_BIT 0x000000C0 +#define LAN9250_HMAC_CR_DFCHK 0x00000020 +#define LAN9250_HMAC_CR_TXEN 0x00000008 +#define LAN9250_HMAC_CR_RXEN 0x00000004 + +/* HOST MAC MII ACCESS REGISTER (HMAC_MII_ACC) */ +#define LAN9250_HMAC_MII_ACC_PHY_ADDR 0x0000F800 +#define LAN9250_HMAC_MII_ACC_PHY_ADDR_DEFAULT 0x00000800 +#define LAN9250_HMAC_MII_ACC_MIIRINDA 0x000007C0 +#define LAN9250_HMAC_MII_ACC_MIIW_R 0x00000002 +#define LAN9250_HMAC_MII_ACC_MIIBZY 0x00000001 + +/* PHY Basic Control Register (PHY_BASIC_CONTROL) */ +#define LAN9250_PHY_BASIC_CONTROL_PHY_SRST 0x8000 +#define LAN9250_PHY_BASIC_CONTROL_PHY_LOOPBACK 0x4000 +#define LAN9250_PHY_BASIC_CONTROL_PHY_SPEED_SEL_LSB 0x2000 +#define LAN9250_PHY_BASIC_CONTROL_PHY_AN 0x1000 +#define LAN9250_PHY_BASIC_CONTROL_PHY_PWR_DWN 0x0800 +#define LAN9250_PHY_BASIC_CONTROL_PHY_RST_AN 0x0200 +#define LAN9250_PHY_BASIC_CONTROL_PHY_DUPLEX 0x0100 +#define LAN9250_PHY_BASIC_CONTROL_PHY_COL_TEST 0x0080 + +/* PHY Auto-Negotiation Advertisement Register (PHY_AN_ADV) */ +#define LAN9250_PHY_AN_ADV_NEXT_PAGE 0x8000 +#define LAN9250_PHY_AN_ADV_REMOTE_FAULT 0x2000 +#define LAN9250_PHY_AN_ADV_EXTENDED_NEXT_PAGE 0x1000 +#define LAN9250_PHY_AN_ADV_ASYM_PAUSE 0x0800 +#define LAN9250_PHY_AN_ADV_SYM_PAUSE 0x0400 +#define LAN9250_PHY_AN_ADV_100BTX_FD 0x0100 +#define LAN9250_PHY_AN_ADV_100BTX_HD 0x0080 +#define LAN9250_PHY_AN_ADV_10BT_FD 0x0040 +#define LAN9250_PHY_AN_ADV_10BT_HD 0x0020 +#define LAN9250_PHY_AN_ADV_SELECTOR 0x001F +#define LAN9250_PHY_AN_ADV_SELECTOR_DEFAULT 0x0001 + +/* PHY Mode Control/Status Register (PHY_MODE_CONTROL_STATUS) */ +#define LAN9250_PHY_MODE_CONTROL_STATUS_EDPWRDOWN 0x2000 +#define LAN9250_PHY_MODE_CONTROL_STATUS_ALTINT 0x0040 +#define LAN9250_PHY_MODE_CONTROL_STATUS_ENERGYON 0x0002 + +/* PHY Special Control/Status Indication Register (PHY_SPECIAL_CONTROL_STAT_IND) */ +#define LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_AMDIXCTRL 0x8000 +#define LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_AMDIXEN 0x4000 +#define LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_AMDIXSTATE 0x2000 +#define LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_SQEOFF 0x0800 +#define LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_FEFI_EN 0x0020 +#define LAN9250_PHY_SPECIAL_CONTROL_STAT_IND_XPOL 0x0010 + +/* PHY Interrupt Source Flags Register (PHY_INTERRUPT_SOURCE) */ +#define LAN9250_PHY_INTERRUPT_SOURCE_LINK_UP 0x0200 +#define LAN9250_PHY_INTERRUPT_SOURCE_ENERGYON 0x0080 +#define LAN9250_PHY_INTERRUPT_SOURCE_AN_COMPLETE 0x0040 +#define LAN9250_PHY_INTERRUPT_SOURCE_REMOTE_FAULT 0x0020 +#define LAN9250_PHY_INTERRUPT_SOURCE_LINK_DOWN 0x0010 +#define LAN9250_PHY_INTERRUPT_SOURCE_AN_LP_ACK 0x0008 +#define LAN9250_PHY_INTERRUPT_SOURCE_PARALLEL_DETECT_FAULT 0x0004 +#define LAN9250_PHY_INTERRUPT_SOURCE_AN_PAGE_RECEIVED 0x0002 + +/* PHY Interrupt Mask Register (PHY_INTERRUPT_MASK) */ +#define LAN9250_PHY_INTERRUPT_MASK_LINK_UP 0x0200 +#define LAN9250_PHY_INTERRUPT_MASK_ENERGYON 0x0080 +#define LAN9250_PHY_INTERRUPT_MASK_AN_COMPLETE 0x0040 +#define LAN9250_PHY_INTERRUPT_MASK_REMOTE_FAULT 0x0020 +#define LAN9250_PHY_INTERRUPT_MASK_LINK_DOWN 0x0010 +#define LAN9250_PHY_INTERRUPT_MASK_AN_LP_ACK 0x0008 +#define LAN9250_PHY_INTERRUPT_MASK_PARALLEL_DETECT_FAULT 0x0004 +#define LAN9250_PHY_INTERRUPT_MASK_AN_PAGE_RECEIVED 0x0002 + +/* Chip ID and Revision register */ +#define LAN9250_ID_REV_CHIP_ID 0xFFFF0000 +#define LAN9250_ID_REV_CHIP_ID_DEFAULT 0x92500000 +#define LAN9250_ID_REV_CHIP_REV 0x0000FFFF + +struct lan9250_config { + struct spi_dt_spec spi; + struct gpio_dt_spec interrupt; + struct gpio_dt_spec reset; + uint8_t full_duplex; + int32_t timeout; +}; + +struct lan9250_runtime { + struct net_if *iface; + const struct device *dev; + + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ETH_LAN9250_RX_THREAD_STACK_SIZE); + k_tid_t tid_int; + struct k_thread thread; + + uint8_t mac_address[6]; + struct gpio_callback gpio_cb; + struct k_sem tx_rx_sem; + struct k_sem int_sem; + uint8_t buf[NET_ETH_MAX_FRAME_SIZE]; + struct k_mutex lock; +}; + +#endif /*_LAN9250_*/ diff --git a/dts/bindings/ethernet/microchip,lan9250.yaml b/dts/bindings/ethernet/microchip,lan9250.yaml new file mode 100644 index 000000000000..5f6487599447 --- /dev/null +++ b/dts/bindings/ethernet/microchip,lan9250.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2024, Mario Paja +# SPDX-License-Identifier: Apache-2.0 + +description: | + LAN9250 standalone 10/100BASE-T Ethernet controller with SPI interface + +compatible: "microchip,lan9250" + +include: [spi-device.yaml, ethernet-controller.yaml] + +properties: + int-gpios: + type: phandle-array + required: true + description: | + The interrupt pin of LAN9250 is active low. + If connected directly the MCU pin should be configured + as active low.