Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Showing
6 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_ */ |
Oops, something went wrong.