| /* |
| * 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_BUFFERS_BASE_ADDRESS |
| #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_CONTROL_BASE_ADDRESS |
| #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_CONTROL_BASE_ADDRESS) + 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 */ |