Skip to content

Commit

Permalink
drivers: eth: e1000: Add driver for Intel PRO/1000 Ethernet controller
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ozhuraki authored and galak committed Oct 10, 2018
1 parent 6ebc766 commit 65ea181
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/ethernet/CMakeLists.txt
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions drivers/ethernet/Kconfig
Expand Up @@ -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"
Expand Down
14 changes: 14 additions & 0 deletions 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.
253 changes: 253 additions & 0 deletions 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 <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 changes: 106 additions & 0 deletions 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_ */

0 comments on commit 65ea181

Please sign in to comment.