drivers: ethernet: add support for microchip lan9250
This PR adds support for LAN9250 spi ethernet controller. This driver is tested on the Mikroe ETH Click 3 https://www.mikroe.com/eth-3-click Signed-off-by: Mario Paja <mariopaja@hotmail.com>
This commit is contained in:
parent
ce6f5294fc
commit
7abe775129
6 changed files with 1096 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
40
drivers/ethernet/Kconfig.lan9250
Normal file
40
drivers/ethernet/Kconfig.lan9250
Normal file
|
@ -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
|
703
drivers/ethernet/eth_lan9250.c
Normal file
703
drivers/ethernet/eth_lan9250.c
Normal file
|
@ -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 <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/net/net_pkt.h>
|
||||
#include <zephyr/net/net_if.h>
|
||||
#include <zephyr/net/ethernet.h>
|
||||
#include <ethernet/eth_stats.h>
|
||||
|
||||
#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);
|
333
drivers/ethernet/eth_lan9250_priv.h
Normal file
333
drivers/ethernet/eth_lan9250_priv.h
Normal file
|
@ -0,0 +1,333 @@
|
|||
/* LAN9250 Stand-alone Ethernet Controller with SPI
|
||||
*
|
||||
* Copyright (c) 2024 Mario Paja
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
|
||||
#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_*/
|
18
dts/bindings/ethernet/microchip,lan9250.yaml
Normal file
18
dts/bindings/ethernet/microchip,lan9250.yaml
Normal file
|
@ -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.
|
Loading…
Add table
Add a link
Reference in a new issue