drivers: ethernet: Add LiteEth driver
Add LiteX Ethernet driver with bindings for this device. Signed-off-by: Mariusz Glebocki <mglebocki@antmicro.com> Signed-off-by: Mateusz Holenko <mholenko@antmicro.com>
This commit is contained in:
parent
6cf26b8fe6
commit
35edfedf68
|
@ -14,6 +14,7 @@ zephyr_sources_ifdef(CONFIG_ETH_ENC28J60 eth_enc28j60.c)
|
|||
zephyr_sources_ifdef(CONFIG_ETH_MCUX eth_mcux.c)
|
||||
zephyr_sources_ifdef(CONFIG_ETH_SMSC911X eth_smsc911x.c)
|
||||
zephyr_sources_ifdef(CONFIG_ETH_STM32_HAL eth_stm32_hal.c)
|
||||
zephyr_sources_ifdef(CONFIG_ETH_LITEETH eth_liteeth.c)
|
||||
|
||||
if(CONFIG_ETH_NATIVE_POSIX)
|
||||
zephyr_library()
|
||||
|
|
|
@ -33,5 +33,6 @@ source "drivers/ethernet/Kconfig.stm32_hal"
|
|||
source "drivers/ethernet/Kconfig.smsc911x"
|
||||
source "drivers/ethernet/Kconfig.native_posix"
|
||||
source "drivers/ethernet/Kconfig.stellaris"
|
||||
source "drivers/ethernet/Kconfig.liteeth"
|
||||
|
||||
endmenu # "Ethernet Drivers"
|
||||
|
|
32
drivers/ethernet/Kconfig.liteeth
Normal file
32
drivers/ethernet/Kconfig.liteeth
Normal file
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Copyright (c) 2019 Antmicro
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
menuconfig ETH_LITEETH
|
||||
bool "LiteEth Ethernet core driver"
|
||||
depends on NET_L2_ETHERNET
|
||||
|
||||
if ETH_LITEETH
|
||||
|
||||
config ETH_LITEETH_0
|
||||
bool "LiteEth Ethernet port 0"
|
||||
|
||||
if ETH_LITEETH_0
|
||||
|
||||
config ETH_LITEETH_0_IRQ_PRI
|
||||
int "LiteEth interrupt priority"
|
||||
default 0
|
||||
help
|
||||
IRQ priority
|
||||
|
||||
config ETH_LITEETH_0_RANDOM_MAC
|
||||
bool "Random MAC address"
|
||||
depends on ENTROPY_GENERATOR
|
||||
default n
|
||||
help
|
||||
Generate a random MAC address dynamically.
|
||||
|
||||
endif # ETH_LITEETH_0
|
||||
|
||||
endif # ETH_LITEETH
|
269
drivers/ethernet/eth_liteeth.c
Normal file
269
drivers/ethernet/eth_liteeth.c
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_MODULE_NAME eth_liteeth
|
||||
#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||
|
||||
#include <kernel.h>
|
||||
#include <device.h>
|
||||
#include <soc.h>
|
||||
#include <stdbool.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/net_if.h>
|
||||
#include <net/net_pkt.h>
|
||||
|
||||
#include <misc/printk.h>
|
||||
|
||||
/* flags */
|
||||
#define LITEETH_EV_TX 0x1
|
||||
#define LITEETH_EV_RX 0x1
|
||||
|
||||
/* slots */
|
||||
#define LITEETH_SLOT_BASE DT_INST_0_LITEX_ETH0_BASE_ADDRESS_0
|
||||
#define LITEETH_SLOT_RX0 ((LITEETH_SLOT_BASE) + 0x0000)
|
||||
#define LITEETH_SLOT_RX1 ((LITEETH_SLOT_BASE) + 0x0800)
|
||||
#define LITEETH_SLOT_TX0 ((LITEETH_SLOT_BASE) + 0x1000)
|
||||
#define LITEETH_SLOT_TX1 ((LITEETH_SLOT_BASE) + 0x1800)
|
||||
|
||||
/* sram - rx */
|
||||
#define LITEETH_RX_BASE DT_INST_0_LITEX_ETH0_BASE_ADDRESS_1
|
||||
#define LITEETH_RX_SLOT ((LITEETH_RX_BASE) + 0x00)
|
||||
#define LITEETH_RX_LENGTH ((LITEETH_RX_BASE) + 0x04)
|
||||
#define LITEETH_RX_EV_PENDING ((LITEETH_RX_BASE) + 0x28)
|
||||
#define LITEETH_RX_EV_ENABLE ((LITEETH_RX_BASE) + 0x2c)
|
||||
|
||||
/* sram - tx */
|
||||
#define LITEETH_TX_BASE ((DT_INST_0_LITEX_ETH0_BASE_ADDRESS_1) + 0x30)
|
||||
#define LITEETH_TX_START ((LITEETH_TX_BASE) + 0x00)
|
||||
#define LITEETH_TX_READY ((LITEETH_TX_BASE) + 0x04)
|
||||
#define LITEETH_TX_SLOT ((LITEETH_TX_BASE) + 0x0c)
|
||||
#define LITEETH_TX_LENGTH ((LITEETH_TX_BASE) + 0x10)
|
||||
#define LITEETH_TX_EV_PENDING ((LITEETH_TX_BASE) + 0x1c)
|
||||
|
||||
/* irq */
|
||||
#define LITEETH_IRQ DT_INST_0_LITEX_ETH0_IRQ_0
|
||||
#define LITEETH_IRQ_PRIORITY CONFIG_ETH_LITEETH_0_IRQ_PRI
|
||||
|
||||
/* label */
|
||||
#define LITEETH_LABEL DT_INST_0_LITEX_ETH0_LABEL
|
||||
|
||||
struct eth_liteeth_dev_data {
|
||||
struct net_if *iface;
|
||||
u8_t mac_addr[6];
|
||||
|
||||
u8_t txslot;
|
||||
u8_t rxslot;
|
||||
|
||||
u8_t *tx_buf[2];
|
||||
u8_t *rx_buf[2];
|
||||
};
|
||||
|
||||
struct eth_liteeth_config {
|
||||
void (*config_func)(void);
|
||||
};
|
||||
|
||||
static int eth_initialize(struct device *dev)
|
||||
{
|
||||
const struct eth_liteeth_config *config = dev->config->config_info;
|
||||
|
||||
config->config_func();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_tx(struct device *dev, struct net_pkt *pkt)
|
||||
{
|
||||
int key;
|
||||
u16_t len;
|
||||
struct eth_liteeth_dev_data *context = dev->driver_data;
|
||||
|
||||
key = irq_lock();
|
||||
|
||||
/* get data from packet and send it */
|
||||
len = net_pkt_get_len(pkt);
|
||||
net_pkt_read(pkt, context->tx_buf[context->txslot], len);
|
||||
|
||||
sys_write8(context->txslot, LITEETH_TX_SLOT);
|
||||
sys_write8(len >> 8, LITEETH_TX_LENGTH);
|
||||
sys_write8(len & 0xFF, LITEETH_TX_LENGTH + 4);
|
||||
|
||||
/* wait for the device to be ready to transmit */
|
||||
while (sys_read8(LITEETH_TX_READY) == 0) {
|
||||
;
|
||||
}
|
||||
|
||||
/* start transmitting */
|
||||
sys_write8(1, LITEETH_TX_START);
|
||||
|
||||
/* change slot */
|
||||
context->txslot = (context->txslot + 1) % 2;
|
||||
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eth_rx(struct device *port)
|
||||
{
|
||||
struct net_pkt *pkt;
|
||||
struct eth_liteeth_dev_data *context = port->driver_data;
|
||||
|
||||
unsigned int key, r;
|
||||
u16_t len = 0;
|
||||
|
||||
key = irq_lock();
|
||||
|
||||
/* get frame's length */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
len <<= 8;
|
||||
len |= sys_read8(LITEETH_RX_LENGTH + i * 0x4);
|
||||
}
|
||||
|
||||
/* which slot is the frame in */
|
||||
context->rxslot = sys_read8(LITEETH_RX_SLOT);
|
||||
|
||||
/* obtain rx buffer */
|
||||
pkt = net_pkt_rx_alloc_with_buffer(context->iface, len, AF_UNSPEC, 0,
|
||||
K_NO_WAIT);
|
||||
if (pkt == NULL) {
|
||||
LOG_ERR("Failed to obtain RX buffer");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* copy data to buffer */
|
||||
if (net_pkt_write(pkt, (void *)context->rx_buf[context->rxslot], len) != 0) {
|
||||
LOG_ERR("Failed to append RX buffer to context buffer");
|
||||
net_pkt_unref(pkt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* receive data */
|
||||
r = net_recv_data(context->iface, pkt);
|
||||
if (r < 0) {
|
||||
LOG_ERR("Failed to enqueue frame into RX queue: %d", r);
|
||||
net_pkt_unref(pkt);
|
||||
}
|
||||
|
||||
out:
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
static void eth_irq_handler(struct device *port)
|
||||
{
|
||||
/* check sram reader events (tx) */
|
||||
if (sys_read8(LITEETH_TX_EV_PENDING) & LITEETH_EV_TX) {
|
||||
/* TX event is not enabled nor used by this driver; ack just
|
||||
* in case if some rogue TX event appeared
|
||||
*/
|
||||
sys_write8(LITEETH_EV_TX, LITEETH_TX_EV_PENDING);
|
||||
}
|
||||
|
||||
/* check sram writer events (rx) */
|
||||
if (sys_read8(LITEETH_RX_EV_PENDING) & LITEETH_EV_RX) {
|
||||
eth_rx(port);
|
||||
|
||||
/* ack writer irq */
|
||||
sys_write8(LITEETH_EV_RX, LITEETH_RX_EV_PENDING);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ETH_LITEETH_0_RANDOM_MAC
|
||||
static void generate_mac(u8_t *mac_addr)
|
||||
{
|
||||
u32_t entropy;
|
||||
|
||||
entropy = sys_rand32_get();
|
||||
|
||||
mac_addr[3] = entropy >> 8;
|
||||
mac_addr[4] = entropy >> 16;
|
||||
/* Locally administered, unicast */
|
||||
mac_addr[5] = ((entropy >> 0) & 0xfc) | 0x02;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ETH_LITEETH_0
|
||||
|
||||
static struct eth_liteeth_dev_data eth_data = {
|
||||
.mac_addr = DT_INST_0_LITEX_ETH0_LOCAL_MAC_ADDRESS
|
||||
};
|
||||
|
||||
static void eth_irq_config(void);
|
||||
static const struct eth_liteeth_config eth_config = {
|
||||
.config_func = eth_irq_config,
|
||||
};
|
||||
|
||||
static void eth_iface_init(struct net_if *iface)
|
||||
{
|
||||
struct device *port = net_if_get_device(iface);
|
||||
struct eth_liteeth_dev_data *context = port->driver_data;
|
||||
static bool init_done;
|
||||
|
||||
/* initialize only once */
|
||||
if (init_done) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* set interface */
|
||||
context->iface = iface;
|
||||
|
||||
/* initialize ethernet L2 */
|
||||
ethernet_init(iface);
|
||||
|
||||
#ifdef CONFIG_ETH_LITEETH_0_RANDOM_MAC
|
||||
/* generate random MAC address */
|
||||
generate_mac(context->mac_addr);
|
||||
#endif
|
||||
|
||||
/* set MAC address */
|
||||
net_if_set_link_addr(iface, context->mac_addr, sizeof(context->mac_addr),
|
||||
NET_LINK_ETHERNET);
|
||||
|
||||
/* clear pending events */
|
||||
sys_write8(LITEETH_EV_TX, LITEETH_TX_EV_PENDING);
|
||||
sys_write8(LITEETH_EV_RX, LITEETH_RX_EV_PENDING);
|
||||
|
||||
/* setup tx slots */
|
||||
context->txslot = 0;
|
||||
context->tx_buf[0] = (u8_t *)LITEETH_SLOT_TX0;
|
||||
context->tx_buf[1] = (u8_t *)LITEETH_SLOT_TX1;
|
||||
|
||||
/* setup rx slots */
|
||||
context->rxslot = 0;
|
||||
context->rx_buf[0] = (u8_t *)LITEETH_SLOT_RX0;
|
||||
context->rx_buf[1] = (u8_t *)LITEETH_SLOT_RX1;
|
||||
|
||||
init_done = true;
|
||||
}
|
||||
|
||||
static enum ethernet_hw_caps eth_caps(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
return ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T |
|
||||
ETHERNET_LINK_1000BASE_T;
|
||||
}
|
||||
|
||||
static const struct ethernet_api eth_api = {
|
||||
.iface_api.init = eth_iface_init,
|
||||
.get_capabilities = eth_caps,
|
||||
.send = eth_tx
|
||||
};
|
||||
|
||||
NET_DEVICE_INIT(eth0, LITEETH_LABEL, eth_initialize, ð_data, ð_config,
|
||||
CONFIG_ETH_INIT_PRIORITY, ð_api, ETHERNET_L2,
|
||||
NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU);
|
||||
|
||||
static void eth_irq_config(void)
|
||||
{
|
||||
IRQ_CONNECT(LITEETH_IRQ, LITEETH_IRQ_PRIORITY, eth_irq_handler,
|
||||
DEVICE_GET(eth0), 0);
|
||||
irq_enable(LITEETH_IRQ);
|
||||
sys_write8(1, LITEETH_RX_EV_ENABLE);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ETH_LITEETH_0 */
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
#define TIMER0_IRQ DT_LITEX_TIMER0_E0002800_IRQ_0
|
||||
#define UART0_IRQ DT_LITEX_UART0_E0001800_IRQ_0
|
||||
|
||||
#define ETH0_IRQ DT_INST_0_LITEX_ETH0_IRQ_0
|
||||
|
||||
static inline void vexriscv_litex_irq_setmask(u32_t mask)
|
||||
{
|
||||
__asm__ volatile ("csrw %0, %1" :: "i"(IRQ_MASK), "r"(mask));
|
||||
|
@ -71,6 +74,13 @@ static void vexriscv_litex_irq_handler(void *device)
|
|||
ite->isr(ite->arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ETH_LITEETH
|
||||
if (irqs & (1 << ETH0_IRQ)) {
|
||||
ite = (struct _isr_table_entry *)&_sw_isr_table[ETH0_IRQ];
|
||||
ite->isr(ite->arg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void z_arch_irq_enable(unsigned int irq)
|
||||
|
|
25
dts/bindings/ethernet/litex,eth0.yaml
Normal file
25
dts/bindings/ethernet/litex,eth0.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Copyright (c) 2019 Antmicro <www.antmicro.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
---
|
||||
title: LiteX Ethernet
|
||||
version: 0.1
|
||||
|
||||
description: >
|
||||
This binding gives a base representation of LiteX Ethernet
|
||||
|
||||
inherits:
|
||||
!include ethernet.yaml
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "litex,eth0"
|
||||
|
||||
reg:
|
||||
category: required
|
||||
|
||||
interrupts:
|
||||
category: required
|
||||
...
|
|
@ -54,5 +54,15 @@
|
|||
label = "timer0";
|
||||
status = "disabled";
|
||||
};
|
||||
eth0: ethernet@e0009800 {
|
||||
compatible = "litex,eth0";
|
||||
interrupt-parent = <&intc0>;
|
||||
interrupts = <3 0>;
|
||||
reg = <0xb0000000 0x2000 0xe0009800 0x6b>;
|
||||
local-mac-address = [10 e2 d5 00 00 02];
|
||||
reg-names = "control";
|
||||
label = "eth0";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue