diff --git a/drivers/ethernet/CMakeLists.txt b/drivers/ethernet/CMakeLists.txt index 054037dacb06ec..36aa0166577d28 100644 --- a/drivers/ethernet/CMakeLists.txt +++ b/drivers/ethernet/CMakeLists.txt @@ -5,6 +5,7 @@ zephyr_sources_ifdef(CONFIG_ETH_SAM_GMAC phy_sam_gmac.c ) zephyr_sources_ifdef(CONFIG_ETH_DW eth_dw.c) +zephyr_sources_ifdef(CONFIG_ETH_E1000 eth_e1000.c) zephyr_sources_ifdef(CONFIG_ETH_ENC28J60 eth_enc28j60.c) zephyr_sources_ifdef(CONFIG_ETH_MCUX eth_mcux.c) zephyr_sources_ifdef(CONFIG_ETH_STM32_HAL eth_stm32_hal.c) diff --git a/drivers/ethernet/Kconfig b/drivers/ethernet/Kconfig index 9c44beba43d2c9..89841bf19552dd 100644 --- a/drivers/ethernet/Kconfig +++ b/drivers/ethernet/Kconfig @@ -29,6 +29,7 @@ config ETH_INIT_PRIORITY source "drivers/ethernet/Kconfig.enc28j60" source "drivers/ethernet/Kconfig.mcux" source "drivers/ethernet/Kconfig.dw" +source "drivers/ethernet/Kconfig.e1000" source "drivers/ethernet/Kconfig.sam_gmac" source "drivers/ethernet/Kconfig.stm32_hal" source "drivers/ethernet/Kconfig.native_posix" diff --git a/drivers/ethernet/Kconfig.e1000 b/drivers/ethernet/Kconfig.e1000 new file mode 100644 index 00000000000000..d566498f103bff --- /dev/null +++ b/drivers/ethernet/Kconfig.e1000 @@ -0,0 +1,14 @@ +# Kconfig - Intel(R) PRO/1000 Gigabit Ethernet driver configuration options + +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig ETH_E1000 + bool + prompt "Intel(R) PRO/1000 Gigabit Ethernet driver" + depends on NET_L2_ETHERNET && PCI_ENUMERATION + help + Enable Intel(R) PRO/1000 Gigabit Ethernet driver. diff --git a/drivers/ethernet/eth_e1000.c b/drivers/ethernet/eth_e1000.c new file mode 100644 index 00000000000000..74a9cb5777308c --- /dev/null +++ b/drivers/ethernet/eth_e1000.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_MODULE_NAME eth_e1000 +#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include +#include +#include +#include "eth_e1000_priv.h" + +#define dev_dbg(fmt, args...) LOG_DBG("%s() " fmt, __func__, ## args) +#define dev_err(fmt, args...) LOG_ERR("%s() " "Error: " fmt, __func__, ## args) + +static const char *e1000_reg_to_string(enum e1000_reg_t r) +{ +#define _(_x) case _x: return #_x + switch (r) { + _(CTRL); + _(ICR); + _(ICS); + _(IMS); + _(RCTL); + _(TCTL); + _(RDBAL); + _(RDBAH); + _(RDLEN); + _(RDH); + _(RDT); + _(TDBAL); + _(TDBAH); + _(TDLEN); + _(TDH); + _(TDT); + _(RAL); + _(RAH); + } +#undef _ + dev_err("Unsupported register: 0x%x", r); + k_oops(); + return NULL; +} + +static enum ethernet_hw_caps e1000_caps(struct device *dev) +{ + return ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T | \ + ETHERNET_LINK_1000BASE_T; +} + +static size_t e1000_linearize(struct net_pkt *pkt, void *buf, size_t bufsize) +{ + size_t len = net_pkt_ll_reserve(pkt) + pkt->frags->len; + struct net_buf *nb; + + /* First fragment contains link layer (Ethernet) header */ + memcpy(buf, net_pkt_ll(pkt), len); + + for (nb = pkt->frags->frags; nb; nb = nb->frags) { + memcpy((u8_t *) buf + len, nb->data, nb->len); + len += nb->len; + } + + return len; +} + +static int e1000_tx(struct e1000_dev *dev, void *data, size_t data_len) +{ + dev->tx.addr = POINTER_TO_INT(data); + dev->tx.len = data_len; + dev->tx.cmd = TDESC_EOP | TDESC_RS; + + iow32(dev, TDT, 1); + + while (!(dev->tx.sta)) { + k_yield(); + } + + dev_dbg("tx.sta: 0x%02hx", dev->tx.sta); + + return (dev->tx.sta & TDESC_STA_DD) ? 0 : -EIO; +} + +static int e1000_send(struct net_if *iface, struct net_pkt *pkt) +{ + struct e1000_dev *dev = net_if_get_device(iface)->driver_data; + + size_t len = e1000_linearize(pkt, dev->txb, sizeof(dev->txb)); + + int err = e1000_tx(dev, dev->txb, len); + + if (err) { + net_pkt_unref(pkt); + } + + return err; +} + +static struct net_pkt *e1000_rx(struct e1000_dev *dev) +{ + struct net_pkt *pkt = NULL; + + dev_dbg("rx.sta: 0x%02hx", dev->rx.sta); + + if (!(dev->rx.sta & RDESC_STA_DD)) { + dev_err("RX descriptor not ready"); + goto out; + } + + pkt = net_pkt_get_reserve_rx(0, K_NO_WAIT); + if (!pkt) { + dev_err("Out of RX buffers"); + goto out; + } + + if (!net_pkt_append_all(pkt, dev->rx.len - 4, + INT_TO_POINTER((u32_t) dev->rx.addr), + K_NO_WAIT)) { + dev_err("Out of memory for received frame"); + net_pkt_unref(pkt); + pkt = NULL; + } +out: + return pkt; +} + +static void e1000_isr(struct device *device) +{ + struct e1000_dev *dev = device->driver_data; + u32_t icr = ior32(dev, ICR); /* Cleared upon read */ + + icr &= ~(ICR_TXDW | ICR_TXQE); + + if (icr & ICR_RXO) { + struct net_pkt *pkt = e1000_rx(dev); + + icr &= ~ICR_RXO; + + if (pkt) { + net_recv_data(dev->iface, pkt); + } + } + + if (icr) { + dev_err("Unhandled interrupt, ICR: 0x%x", icr); + } +} + +int e1000_probe(struct device *device) +{ + struct e1000_dev *dev = device->driver_data; + bool found = false; + + pci_bus_scan_init(); + + if (pci_bus_scan(&dev->pci)) { + + pci_enable_regs(&dev->pci); + + pci_enable_bus_master(&dev->pci); + + pci_show(&dev->pci); + + found = true; + } + + return found; +} + +static struct device DEVICE_NAME_GET(eth_e1000); + +static void e1000_init(struct net_if *iface) +{ + struct e1000_dev *dev = net_if_get_device(iface)->driver_data; + u32_t ral, rah; + + dev->iface = iface; + + /* Setup TX descriptor */ + + iow32(dev, TDBAL, (u32_t) &dev->tx); + iow32(dev, TDBAH, 0); + iow32(dev, TDLEN, 1*16); + + iow32(dev, TDH, 0); + iow32(dev, TDT, 0); + + iow32(dev, TCTL, TCTL_EN); + + /* Setup RX descriptor */ + + dev->rx.addr = POINTER_TO_INT(dev->rxb); + dev->rx.len = sizeof(dev->rxb); + + iow32(dev, RDBAL, (u32_t) &dev->rx); + iow32(dev, RDBAH, 0); + iow32(dev, RDLEN, 1*16); + + iow32(dev, RDH, 0); + iow32(dev, RDT, 1); + + iow32(dev, RCTL, RCTL_EN); + + iow32(dev, IMS, IMS_RXO); + + ral = ior32(dev, RAL); + rah = ior32(dev, RAH); + + memcpy(dev->mac, &ral, 4); + memcpy(dev->mac + 4, &rah, 2); + + net_if_set_link_addr(iface, dev->mac, sizeof(dev->mac), + NET_LINK_ETHERNET); + + iow32(dev, CTRL, CTRL_SLU); /* Set link up */ + + IRQ_CONNECT(CONFIG_ETH_E1000_IRQ, CONFIG_ETH_E1000_IRQ_PRIORITY, + e1000_isr, DEVICE_GET(eth_e1000), + CONFIG_ETH_E1000_IRQ_FLAGS); + + irq_enable(CONFIG_ETH_E1000_IRQ); + + dev_dbg("done"); +} + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_I82540EM 0x100e + +static struct e1000_dev e1000_dev = { + .pci.vendor_id = PCI_VENDOR_ID_INTEL, + .pci.device_id = PCI_DEVICE_ID_I82540EM, +}; + +static const struct ethernet_api e1000_api = { + .iface_api.init = e1000_init, + .iface_api.send = e1000_send, + .get_capabilities = e1000_caps, +}; + +NET_DEVICE_INIT(eth_e1000, + "ETH_0", + e1000_probe, + &e1000_dev, + NULL, + CONFIG_ETH_INIT_PRIORITY, + &e1000_api, + ETHERNET_L2, + NET_L2_GET_CTX_TYPE(ETHERNET_L2), + E1000_MTU); diff --git a/drivers/ethernet/eth_e1000_priv.h b/drivers/ethernet/eth_e1000_priv.h new file mode 100644 index 00000000000000..41e2aacc23bdcb --- /dev/null +++ b/drivers/ethernet/eth_e1000_priv.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ETH_E1000_PRIV_H +#define ETH_E1000_PRIV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define CTRL_SLU (1 << 6) /* Set Link Up */ + +#define TCTL_EN (1 << 1) +#define RCTL_EN (1 << 1) + +#define ICR_TXDW (1) /* Transmit Descriptor Written Back */ +#define ICR_TXQE (1 << 1) /* Transmit Queue Empty */ +#define ICR_RXO (1 << 6) /* Receiver Overrun */ + +#define IMS_RXO (1 << 6) /* Receiver FIFO Overrun */ + +#define TDESC_EOP (1) /* End Of Packet */ +#define TDESC_RS (1 << 3) /* Report Status */ + +#define RDESC_STA_DD (1) /* Descriptor Done */ +#define TDESC_STA_DD (1) /* Descriptor Done */ + +#define E1000_MTU 1500 + +#define ETH_ALEN 6 /* TODO: Add a global reusable definition in OS */ + +enum e1000_reg_t { + CTRL = 0x0000, /* Device Control */ + ICR = 0x00C0, /* Interrupt Cause Read */ + ICS = 0x00C8, /* Interrupt Cause Set */ + IMS = 0x00D0, /* Interrupt Mask Set */ + RCTL = 0x0100, /* Receive Control */ + TCTL = 0x0400, /* Transmit Control */ + RDBAL = 0x2800, /* Rx Descriptor Base Address Low */ + RDBAH = 0x2804, /* Rx Descriptor Base Address High */ + RDLEN = 0x2808, /* Rx Descriptor Length */ + RDH = 0x2810, /* Rx Descriptor Head */ + RDT = 0x2818, /* Rx Descriptor Tail */ + TDBAL = 0x3800, /* Tx Descriptor Base Address Low */ + TDBAH = 0x3804, /* Tx Descriptor Base Address High */ + TDLEN = 0x3808, /* Tx Descriptor Length */ + TDH = 0x3810, /* Tx Descriptor Head */ + TDT = 0x3818, /* Tx Descriptor Tail */ + RAL = 0x5400, /* Receive Address Low */ + RAH = 0x5404, /* Receive Address High */ +}; + +/* Legacy TX Descriptor */ +struct e1000_tx { + u64_t addr; + u16_t len; + u8_t cso; + u8_t cmd; + u8_t sta; + u8_t css; + u16_t special; +}; + +/* Legacy RX Descriptor */ +struct e1000_rx { + u64_t addr; + u16_t len; + u16_t csum; + u8_t sta; + u8_t err; + u16_t special; +}; + +struct e1000_dev { + volatile struct e1000_tx tx __aligned(16); + volatile struct e1000_rx rx __aligned(16); + struct pci_dev_info pci; + struct net_if *iface; + u8_t mac[ETH_ALEN]; + u8_t txb[E1000_MTU]; + u8_t rxb[E1000_MTU]; +}; + +static const char *e1000_reg_to_string(enum e1000_reg_t r) + __attribute__((unused)); + +#define iow32(_dev, _reg, _val) do { \ + dev_dbg("iow32 %s 0x%08x", e1000_reg_to_string(_reg), _val); \ + sys_write32(_val, (_dev)->pci.addr + _reg); \ +} while (0) + +#define ior32(_dev, _reg) \ +({ \ + u32_t val = sys_read32((_dev)->pci.addr + (_reg)); \ + dev_dbg("ior32 %s 0x%08x", e1000_reg_to_string(_reg), val); \ + val; \ +}) + +#ifdef __cplusplus +} +#endif + +#endif /* ETH_E1000_PRIV_H_ */ diff --git a/soc/x86/ia32/soc.c b/soc/x86/ia32/soc.c index e76e27b797b1c6..226f7d4f9f1736 100644 --- a/soc/x86/ia32/soc.c +++ b/soc/x86/ia32/soc.c @@ -26,4 +26,9 @@ MMU_BOOT_REGION(CONFIG_IOAPIC_BASE_ADDRESS, MB(1), MMU_ENTRY_WRITE); #ifdef CONFIG_HPET_TIMER MMU_BOOT_REGION(CONFIG_HPET_TIMER_BASE_ADDRESS, KB(4), MMU_ENTRY_WRITE); #endif + +#ifdef CONFIG_ETH_E1000 +MMU_BOOT_REGION(CONFIG_ETH_E1000_BASE_ADDRESS, KB(128), MMU_ENTRY_WRITE); +#endif + #endif /* CONFIG_X86_MMU*/