drivers: eth: e1000: Add driver for Intel PRO/1000 Ethernet controller
This patch adds a driver for Intel PRO/1000 Gigabit Ethernet controller. The driver currently supports only a single instance of the NIC. Signed-off-by: Oleg Zhurakivskyy <oleg.zhurakivskyy@intel.com>
This commit is contained in:
parent
6ebc766043
commit
65ea181c92
6 changed files with 380 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
14
drivers/ethernet/Kconfig.e1000
Normal file
14
drivers/ethernet/Kconfig.e1000
Normal file
|
@ -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.
|
253
drivers/ethernet/eth_e1000.c
Normal file
253
drivers/ethernet/eth_e1000.c
Normal file
|
@ -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 <logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <pci/pci.h>
|
||||
#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);
|
106
drivers/ethernet/eth_e1000_priv.h
Normal file
106
drivers/ethernet/eth_e1000_priv.h
Normal file
|
@ -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_ */
|
|
@ -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*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue