blob: 2baf420a970b8a7c25e94bdd1ac76e84c3f69cce [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(DATA61_GPL)
*/
#include <ethdrivers/gen_config.h>
#include <ethdrivers/intel.h>
#include <assert.h>
#include <ethdrivers/helpers.h>
typedef enum e1000_family {
e1000_82580 = 1,
e1000_82574
} e1000_family_t;
/* An alignment of 128 bytes is required for most structures by the hardware, except for actual packets */
#define DMA_ALIGN 128
/* This driver is hard coded to use 2k buffers, don't just change this */
#define BUF_SIZE 2048
// TX Descriptor Status Bits
#define TX_DD BIT(0) /* Descriptor Done */
/* Descriptor CMD Bits */
#define TX_CMD_EOP BIT(0) /* End of Packet */
#define TX_CMD_IFCS BIT(1) /* Insert FCS (CRC) */
#define TX_CMD_RS BIT(3) /* Report status */
#define TX_CMD_IDE BIT(7) /* Interrupt Delay Enable */
// RX Descriptor Status Bits
#define RX_DD BIT(0) /* Descriptor Done */
#define RX_EOP BIT(1) /* End of Packet */
#define REG(x,y) (*(volatile uint32_t*)(((uintptr_t)(x)->iobase) + (y)))
#define REG_CTRL(x) REG(x, 0x0)
#define REG_82580_IMC(x) REG(x, 0x150c)
#define REG_82574_IMC(x) REG(x, 0xD8)
#define REG_STATUS(x) REG(x, 0x8)
#define REG_82580_TXDCTL(x, y) REG(x, 0xE028 + 0x40 * (y))
#define REG_82574_TXDCTL(x, y) REG(x, 0x3828 + 0x100 * (y))
#define REG_MTA(x, y) REG(x, 0x5200 + 4 * (y))
#define REG_RCTL(x) REG(x, 0x100)
#define REG_EERD(x) REG(x, 0x14)
#define REG_TCTL(x) REG(x, 0x0400)
#define REG_RAL(x, y) REG(x, 0x05400 + (y) * 0x8)
#define REG_RAH(x, y) REG(x, 0x05404 + (y) * 0x8)
#define REG_82580_TDBAL(x, y) REG(x, 0xE000 + (y) * 0x40)
#define REG_82574_TDBAL(x, y) REG(x, 0x3800 + (y) * 0x100)
#define REG_82580_TDBAH(x, y) REG(x, 0xE004 + (y) * 0x40)
#define REG_82574_TDBAH(x, y) REG(x, 0x3804 + (y) * 0x100)
#define REG_82580_TDLEN(x, y) REG(x, 0xE008 + (y) * 0x40)
#define REG_82574_TDLEN(x, y) REG(x, 0x3808 + (y) * 0x100)
#define REG_82580_TDH(x, y) REG(x, 0xE010 + (y) * 0x40)
#define REG_82574_TDH(x, y) REG(x, 0x3810 + (y) * 0x100)
#define REG_82580_TDT(x, y) REG(x, 0xE018 + (y) * 0x40)
#define REG_82574_TDT(x, y) REG(x, 0x3818 + (y) * 0x100)
#define REG_82580_RDBAL(x, y) REG(x, 0xC000 + (y) * 0x40)
#define REG_82574_RDBAL(x, y) REG(x, 0x2800 + (y) * 0x100)
#define REG_82580_RDBAH(x, y) REG(x, 0xC004 + (y) * 0x40)
#define REG_82574_RDBAH(x, y) REG(x, 0x2804 + (y) * 0x100)
#define REG_82580_RDLEN(x, y) REG(x, 0xC008 + (y) * 0x40)
#define REG_82574_RDLEN(x, y) REG(x, 0x2808 + (y) * 0x100)
#define REG_82580_RDH(x, y) REG(x, 0xc010 + (y) * 0x40)
#define REG_82574_RDH(x, y) REG(x, 0x2810 + (y) * 0x100)
#define REG_82580_RDT(x, y) REG(x, 0xc018 + (y) * 0x40)
#define REG_82574_RDT(x, y) REG(x, 0x2818 + (y) * 0x100)
#define REG_82580_RXDCTL(x, y) REG(x, 0xc028 + (y) * 0x40)
#define REG_82574_RXDCTL(x, y) REG(x, 0x2828 + (y) * 0x100)
#define REG_82580_IMS(x) REG(x, 0x1508)
#define REG_82574_IMS(x) REG(x, 0xD0)
#define REG_82580_ICR(x) REG(x, 0x1500)
#define REG_82574_ICR(x) REG(x, 0xC0)
#define REG_TIPG(x) REG(x, 0x410)
#define REG_82574_RDTR(x) REG(x, 0x2820)
#define REG_82574_RADV(x) REG(x, 0x282c)
#define REG_82574_RAID(x) REG(x, 0x2c08)
#define REG_82574_TIDV(x) REG(x, 0x3820)
#define REG_82574_TADV(x) REG(x, 0x382c)
#define REG_82574_PBA(x) REG(x, 0x1000)
#define REG_MDIC(x) REG(x, 0x20)
#define REG_82574_GCR(x) REG(x, 0x5B00)
#define REG_82574_FCT(x) REG(x, 0x030)
#define REG_82574_FCAL(x) REG(x, 0x028)
#define REG_82574_FCAH(x) REG(x, 0x02c)
#define IMC_82580_RESERVED_BITS ((uint32_t)(BIT(1) | BIT(3) | BIT(5) | BIT(9) | BIT(15) | BIT(16) | BIT(17) | BIT(21) | BIT(23) | BIT(27) | BIT(31)))
#define IMC_82574_RESERVED_BITS (BIT(3) | BIT(5) | BIT(8) | (0b11111 << 10) | BIT(19) | (0b1111111 << 25))
#define CTRL_82580_RESERVED_BITS (BIT(24) | BIT(25))
#define CTRL_82574_RESERVED_BITS (BIT(1) | (0b11 << 3) | BIT(7) | BIT(10) | (0b1111111 << 13) | (0b11111 << 21) | BIT(29))
#define CTRL_SLU BIT(6)
#define CTRL_RST BIT(26)
#define STATUS_LU BIT(1)
#define STATUS_82580_LAN_ID_OFFSET 2
#define STATUS_82580_LAN_ID_MASK (BIT(2) | BIT(3))
#define RCTL_82580_RESERVED_BITS (BIT(0) | BIT(10) | BIT(11) | BIT(27) | BIT(28) | BIT(29) | BIT(30) | BIT(31))
#define RCTL_82574_RESERVED_BITS (BIT(0) | BIT(14) | BIT(21) | BIT(24) | BIT(31))
#define RCTL_82574_RDMTS_OFFSET (8)
#define RCTL_82574_RDMTS_1_4 (0b10 << RCTL_82574_RDMTS_OFFSET)
#define RCTL_EN BIT(1)
#define RCTL_UPE BIT(3)
#define RCTL_MPE BIT(4)
#define RCTL_BAM BIT(15)
#define TXDCTL_82580_RESERVED_BITS (0)
#define TXDCTL_82574_RESERVED_BITS (0)
#define TXDCTL_82580_ENABLE BIT(25)
#define TXDCTL_82574_BIT_THAT_SHOULD_BE_1 BIT(22)
#define TXDCTL_82574_GRAN BIT(24)
#define TXDCTL_82574_PTHRESH_OFFSET 0
#define TXDCTL_82574_HTHRESH_OFFSET 8
#define TXDCTL_82574_WTHRESH_OFFSET 16
#define EERD_START BIT(0)
#define EERD_DONE BIT(1)
#define EERD_ADDR_OFFSET 2
#define TCTL_82580_RESERVED_BITS (BIT(0) | BIT(2))
#define TCTL_82574_RESERVED_BITS (BIT(0) | BIT(2) |BIT(31))
#define TCTL_EN BIT(1)
#define TCTL_PSP BIT(3)
#define TCTL_CT_BITS(x) ((x) << 4)
#define TCTL_82574_COLD_BITS(x) (((x) & 0b1111111111) << 12)
#define TCTL_82574_UNORTX BIT(25)
#define TCTL_82574_TXDSCMT_BITS(x) (((x) & 3) << 26)
#define TCTL_82574_RRTHRESH_BITS(x) (((x) & 3) << 29)
#define TCTL_82574_MULR BIT(28)
#define RXDCTL_82574_RESERVED_BITS (0)
#define RXDCTL_82574_PTHRESH_OFFSET (0)
#define RXDCTL_82574_HTHRESH_OFFSET (8)
#define RXDCTL_82574_WTHRESH_OFFSET (16)
#define RXDCTL_82574_GRAN BIT(24)
#define RXDCTL_82580_RESERVED_BITS (0)
#define RXDCTL_82580_ENABLE BIT(25)
#define IMS_82580_RXDW BIT(7)
#define IMS_82580_TXDW BIT(0)
#define IMS_82580_GPHY BIT(10)
#define IMS_82574_RXQ0 BIT(20)
#define IMS_82574_RXTO BIT(7)
#define IMS_82574_RXDMT0 BIT(4)
#define IMS_82574_TXDW BIT(0)
#define IMS_82574_ACK BIT(17)
#define IMS_82574_LSC BIT(2)
#define ICR_82580_RXDW BIT(7)
#define ICR_82580_TXDW BIT(0)
#define ICR_82574_RXQ0 BIT(20)
#define ICR_82580_GPHY BIT(10)
#define ICR_82574_RXTO BIT(7)
#define ICR_82574_RXDMT0 BIT(4)
#define ICR_82574_TXDW BIT(0)
#define ICR_82574_ACK BIT(17)
#define ICR_82574_LSC BIT(2)
#define EEPROM_82580_LAN(id, x) ( ((id) ? 0 : 0x40) * (id) + (x))
#define MTA_LENGTH 128
struct __attribute((packed)) legacy_tx_ldesc {
uint64_t bufferAddress;
uint32_t length: 16;
uint32_t CSO: 8;
uint32_t CMD: 8;
uint32_t STA: 4;
uint32_t ExtCMD : 4; /* This is reserved on the 82580 */
uint32_t CSS: 8;
uint32_t VLAN: 16;
};
struct __attribute((packed)) legacy_rx_ldesc {
uint64_t bufferAddress;
uint32_t length: 16;
uint32_t packetChecksum: 16;
uint32_t status: 8;
uint32_t error: 8;
uint32_t VLAN: 16;
};
typedef struct e1000_dev {
e1000_family_t family;
void *iobase;
/* shadow the value of descriptor tails so we don't have to re-read it to increment */
uint32_t rdt;
uint32_t tdt;
/* track what we think the values of rdh and tdh are in the hardware so we can find
* complete transmit and receive descriptors */
uint32_t tdh;
uint32_t rdh;
/* descriptor rings */
volatile struct legacy_rx_ldesc *rx_ring;
unsigned int rx_size;
unsigned int rx_remain;
void **rx_cookies;
volatile struct legacy_tx_ldesc *tx_ring;
unsigned int tx_size;
unsigned int tx_remain;
void **tx_cookies;
unsigned int *tx_lengths;
uint32_t tx_cmd_bits;
/* whether we believe the link is up or not */
int link_up;
/* if the rx ring is empty */
bool need_rx_buffers;
} e1000_dev_t;
static void disable_all_interrupts(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
REG_82580_IMC(dev) = ~IMC_82580_RESERVED_BITS;
break;
case e1000_82574:
REG_82574_IMC(dev) = ~IMC_82574_RESERVED_BITS;
break;
}
}
static void reset_device(e1000_dev_t *dev)
{
uint32_t val = CTRL_RST;
if (dev->family == e1000_82574) {
/* 82574 docs say that bit 3 must be set, so set it and perform the reset */
val |= BIT(3);
}
REG_CTRL(dev) = val;
/* wait approximately 1us before checking for reset completion */
for (volatile int i = 0; i < 10000000; i++);
/* wait for reset to complete */
while (REG_CTRL(dev) & CTRL_RST);
}
static void set_link_up(e1000_dev_t *dev)
{
uint32_t temp = REG_CTRL(dev);
temp |= CTRL_SLU;
REG_CTRL(dev) = temp;
}
static void check_link_status(e1000_dev_t *dev)
{
dev->link_up = !!(REG_STATUS(dev) & STATUS_LU);
}
static void configure_pba(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
/* Leave at defaults */
break;
case e1000_82574:
/* The default should be 20/20 split for PBA, but set it to that just in case */
REG_82574_PBA(dev) = (20 << 16) | 20;
break;
default:
assert(!"Unknown device");
}
}
static void phy_write(e1000_dev_t *dev, int phy, int reg, uint16_t data)
{
REG_MDIC(dev) = data | (reg << 16) | (phy << 21) | (BIT(26));
while ((REG_MDIC(dev) & BIT(28)) == 0);
}
static uint16_t phy_read(e1000_dev_t *dev, int phy, int reg)
{
uint32_t mdi;
REG_MDIC(dev) = (reg << 16) | (phy << 21) | (2 << 26);
while (((mdi = REG_MDIC(dev)) & BIT(28)) == 0);
return mdi & MASK(16);
}
static void reset_phy(e1000_dev_t *dev)
{
uint32_t temp;
switch (dev->family) {
case e1000_82580:
/* Don't reset the phy for now */
break;
case e1000_82574:
/* for unknown reasons the 82574 this was tested on cannot seem to perform
* well if it ends up being the slave side of a connection. Therefore we
* try and force us to negotiate to be the master */
temp = phy_read(dev, 1, 9);
temp |= BIT(12) | BIT(11);
phy_write(dev, 1, 9, temp);
/* write a reset */
phy_write(dev, 1, 0, BIT(15) | BIT(12));
/* wait until it completes */
while (phy_read(dev, 1, 0) & BIT(15));
break;
default:
assert(!"Unknown device");
}
}
static void configure_flow_control(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
/* Nothing to setup. Default values will allow us to receive
* pause frames, we don't send them however */
break;
case e1000_82574:
/* Manual says to set these values for flow control */
REG_82574_FCAL(dev) = 0x00C28001;
REG_82574_FCAH(dev) = 0x0100;
REG_82574_FCT(dev) = 0x8808;
break;
default:
assert(!"Unknown device");
}
}
static void initialize(e1000_dev_t *dev)
{
disable_all_interrupts(dev);
reset_device(dev);
disable_all_interrupts(dev);
if (dev->family == e1000_82574) {
/* this bit must be set according to the manual */
REG_82574_GCR(dev) |= BIT(22);
}
reset_phy(dev);
set_link_up(dev);
configure_flow_control(dev);
configure_pba(dev);
}
static void initialise_TXDCTL(e1000_dev_t *dev)
{
uint32_t temp;
switch (dev->family) {
case e1000_82580:
/* Enable transmit queue */
temp = REG_82580_TXDCTL(dev, 0);
temp &= ~TXDCTL_82580_RESERVED_BITS;
temp |= TXDCTL_82580_ENABLE;
REG_82580_TXDCTL(dev, 0) = temp;
break;
case e1000_82574:
temp = REG_82574_TXDCTL(dev, 0);
temp &= ~TXDCTL_82574_RESERVED_BITS;
/* set the bit that we have to set */
temp |= TXDCTL_82574_BIT_THAT_SHOULD_BE_1;
/* set gran to 1 */
temp |= TXDCTL_82574_GRAN;
/* wthresh and hthresh to 1 to avoid tx stalls */
temp |= 1 << TXDCTL_82574_WTHRESH_OFFSET;
temp |= 1 << TXDCTL_82574_HTHRESH_OFFSET;
/* prefetch when less than 31 */
temp |= 31 << TXDCTL_82574_PTHRESH_OFFSET;
REG_82574_TXDCTL(dev, 0) = temp;
break;
default:
assert(!"Unknown device");
break;
}
}
static void initialise_TCTL(e1000_dev_t *dev)
{
uint32_t temp = 0;
switch (dev->family) {
case e1000_82580:
break;
case e1000_82574:
/* set cold to 0x3f for FD (or 0x1FF for HD) */
temp |= TCTL_82574_COLD_BITS(0x3f);
/* allow multiple transmit requests from hardware at once */
temp |= TCTL_82574_MULR;
break;
default:
assert(!"Unknown device");
}
temp |= TCTL_EN;
temp |= TCTL_PSP;
temp |= TCTL_CT_BITS(0xf);
REG_TCTL(dev) = temp;
}
static void initialise_TIPG(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
/* use defaults */
break;
case e1000_82574:
/* Write in recommended value from manual */
REG_TIPG(dev) = 0x00602006;
break;
default:
assert(!"Unknown device");
}
}
static void initialise_transmit_timers(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
break;
case e1000_82574:
/* Set the transmit notifcations really high. We will cleanup transmits
* lazily for the most part and there is no real rush to release buffers quickly */
REG_82574_TIDV(dev) = 20000;
REG_82574_TADV(dev) = 60000;
break;
default:
assert(!"Unknown device");
}
}
static void initialize_transmit(e1000_dev_t *dev)
{
initialise_TXDCTL(dev);
initialise_TIPG(dev);
initialise_transmit_timers(dev);
initialise_TCTL(dev);
}
static void initialize_RCTL(e1000_dev_t *dev)
{
uint32_t temp = 0;
switch (dev->family) {
case e1000_82580:
break;
case e1000_82574:
/* set free receive descriptor threshold to one quarter */
temp |= RCTL_82574_RDMTS_1_4;
break;
}
/* Enable receive */
temp |= RCTL_EN;
/* Accept broadcast packets */
temp |= RCTL_BAM;
/* defaults for everything else will give us 2K pages, which is what we want */
REG_RCTL(dev) = temp;
}
static void enable_prom_mode(e1000_dev_t *dev)
{
REG_RCTL(dev) |= RCTL_UPE | RCTL_MPE;
}
static void initialize_RXDCTL(e1000_dev_t *dev)
{
uint32_t temp;
switch (dev->family) {
case e1000_82580:
temp = REG_82580_RXDCTL(dev, 0);
temp &= ~RXDCTL_82580_RESERVED_BITS;
temp |= RXDCTL_82580_ENABLE;
REG_82580_RXDCTL(dev, 0) = temp;
break;
case e1000_82574:
temp = REG_82574_RXDCTL(dev, 0);
temp &= ~RXDCTL_82574_RESERVED_BITS;
/* count in descriptors */
temp |= RXDCTL_82574_GRAN;
/* prefetch once below 4 */
temp |= 4 << RXDCTL_82574_PTHRESH_OFFSET;
/* keep host at 32 free */
temp |= 32 << RXDCTL_82574_HTHRESH_OFFSET;
/* write back 4 at a time */
temp |= 4 << RXDCTL_82574_WTHRESH_OFFSET;
REG_82574_TXDCTL(dev, 0) = temp;
break;
default:
assert(!"Unknown device");
}
}
static void initialize_receive_timers(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
break;
case e1000_82574:
/* set a base delay of 20 microseconds */
REG_82574_RDTR(dev) = 20;
/* force descriptor write back after 20 microseconds */
REG_82574_RADV(dev) = 20;
REG_82574_RAID(dev) = 0;
break;
default:
assert(!"Unknown device");
}
}
static void initialize_receive(e1000_dev_t *dev)
{
/* zero the MTA */
int i;
for (i = 0; i < MTA_LENGTH; i++) {
REG_MTA(dev, i) = 0;
}
initialize_receive_timers(dev);
initialize_RXDCTL(dev);
initialize_RCTL(dev);
}
static void enable_interrupts(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
REG_82580_IMS(dev) = IMS_82580_RXDW | IMS_82580_TXDW | IMS_82580_GPHY;
/* enable link status change interrupts in the phy */
phy_write(dev, 0, 24, BIT(2));
break;
case e1000_82574:
REG_82574_IMS(dev) = IMS_82574_RXQ0 | IMS_82574_RXTO | IMS_82574_RXDMT0 | IMS_82574_ACK | IMS_82574_TXDW |
IMS_82574_LSC;
break;
default:
assert(!"Unknown device");
break;
}
}
void print_state(struct eth_driver *eth_driver)
{
}
static uint16_t read_eeprom(e1000_dev_t *dev, uint16_t reg)
{
REG_EERD(dev) = EERD_START | (reg << EERD_ADDR_OFFSET);
uint32_t val;
while (((val = REG_EERD(dev)) & EERD_DONE) == 0);
return val >> 16;
}
void eth_get_mac(e1000_dev_t *dev, uint8_t *hwaddr)
{
/* read the first 3 shorts of the eeprom */
uint16_t mac[3];
uint16_t base = 0x00;
switch (dev->family) {
case e1000_82580: {
/* read our LAN ID so we know what port we are */
int id = (REG_STATUS(dev) & STATUS_82580_LAN_ID_MASK) >> STATUS_82580_LAN_ID_OFFSET;
base = EEPROM_82580_LAN(id, 0x00);
break;
}
case e1000_82574:
/* Single port, so default base is fine */
break;
default:
assert(!"Unknown device");
return;
}
mac[0] = read_eeprom(dev, base + 0x00);
mac[1] = read_eeprom(dev, base + 0x01);
mac[2] = read_eeprom(dev, base + 0x02);
hwaddr[0] = mac[0] & MASK(8);
hwaddr[1] = mac[0] >> 8;
hwaddr[2] = mac[1] & MASK(8);
hwaddr[3] = mac[1] >> 8;
hwaddr[4] = mac[2] & MASK(8);
hwaddr[5] = mac[2] >> 8;
}
void low_level_init(struct eth_driver *driver, uint8_t *mac, int *mtu)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
eth_get_mac(dev, mac);
/* hardcode MTU for now */
*mtu = 1500;
}
void get_mac(struct eth_driver *driver, uint8_t *mac)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
uint32_t maclow = REG_RAL(dev, 0);
uint32_t machigh = REG_RAH(dev, 0);
mac[0] = maclow;
mac[1] = maclow >> 8;
mac[2] = maclow >> 16;
mac[3] = maclow >> 24;
mac[4] = machigh;
mac[5] = machigh >> 8;
}
static void set_tx_ring(e1000_dev_t *dev, uintptr_t phys)
{
uint32_t phys_low = (uint32_t)phys;
uint32_t phys_high = (uint32_t)(sizeof(phys) > 4 ? phys >> 32 : 0);
switch (dev->family) {
case e1000_82580:
REG_82580_TDBAL(dev, 0) = phys_low;
REG_82580_TDBAH(dev, 0) = phys_high;
break;
case e1000_82574:
REG_82574_TDBAL(dev, 0) = phys_low;
REG_82574_TDBAH(dev, 0) = phys_high;
break;
default:
assert(!"Unknown device");
}
}
static void set_tdh(e1000_dev_t *dev, uint32_t val)
{
switch (dev->family) {
case e1000_82580:
REG_82580_TDH(dev, 0) = val;
break;
case e1000_82574:
REG_82574_TDH(dev, 0) = val;
break;
default:
assert(!"Unknown device");
}
}
static void set_tdt(e1000_dev_t *dev, uint32_t val)
{
switch (dev->family) {
case e1000_82580:
REG_82580_TDT(dev, 0) = val;
break;
case e1000_82574:
REG_82574_TDT(dev, 0) = val;
break;
default:
assert(!"Unknown device");
}
}
static void set_tdlen(e1000_dev_t *dev, uint32_t val)
{
/* tdlen must be multiple of 128 */
assert(val % 128 == 0);
switch (dev->family) {
case e1000_82580:
REG_82580_TDLEN(dev, 0) = val;
break;
case e1000_82574:
REG_82574_TDLEN(dev, 0) = val;
break;
default:
assert(!"Unknown device");
}
}
static void set_rx_ring(e1000_dev_t *dev, uint64_t phys)
{
uint32_t phys_low = (uint32_t)phys;
uint32_t phys_high = (uint32_t)(sizeof(phys) > 4 ? phys >> 32 : 0);
switch (dev->family) {
case e1000_82580:
REG_82580_RDBAL(dev, 0) = phys_low;
REG_82580_RDBAH(dev, 0) = phys_high;
break;
case e1000_82574:
REG_82574_RDBAL(dev, 0) = phys_low;
REG_82574_RDBAH(dev, 0) = phys_high;
break;
default:
assert(!"Unknown device");
}
}
static void set_rdlen(e1000_dev_t *dev, uint32_t val)
{
/* rdlen must be multiple of 128 */
assert(val % 128 == 0);
switch (dev->family) {
case e1000_82580:
REG_82580_RDLEN(dev, 0) = val;
break;
case e1000_82574:
REG_82574_RDLEN(dev, 0) = val;
break;
default:
assert(!"Unknown device");
}
}
static void set_rdt(e1000_dev_t *dev, uint32_t val)
{
switch (dev->family) {
case e1000_82580:
REG_82580_RDT(dev, 0) = val;
break;
case e1000_82574:
REG_82574_RDT(dev, 0) = val;
break;
default:
assert(!"Unknown device");
}
}
static uint32_t read_rdh(e1000_dev_t *dev)
{
switch (dev->family) {
case e1000_82580:
return REG_82580_RDH(dev, 0);
case e1000_82574:
return REG_82574_RDH(dev, 0);
default:
assert(!"Unknown device");
return 0;
}
}
static void free_desc_ring(e1000_dev_t *dev, ps_dma_man_t *dma_man)
{
if (dev->rx_ring) {
dma_unpin_free(dma_man, (void *)dev->rx_ring, sizeof(struct legacy_rx_ldesc) * dev->rx_size);
dev->rx_ring = NULL;
}
if (dev->tx_ring) {
dma_unpin_free(dma_man, (void *)dev->tx_ring, sizeof(struct legacy_tx_ldesc) * dev->tx_size);
dev->tx_ring = NULL;
}
if (dev->rx_cookies) {
free(dev->rx_cookies);
dev->rx_cookies = NULL;
}
if (dev->tx_cookies) {
free(dev->tx_cookies);
dev->tx_cookies = NULL;
}
if (dev->tx_lengths) {
free(dev->tx_lengths);
dev->tx_lengths = NULL;
}
}
static int initialize_desc_ring(e1000_dev_t *dev, ps_dma_man_t *dma_man)
{
dma_addr_t rx_ring = dma_alloc_pin(dma_man, sizeof(struct legacy_rx_ldesc) * dev->rx_size, 1, DMA_ALIGN);
if (!rx_ring.phys) {
LOG_ERROR("Failed to allocate rx_ring");
return -1;
}
dev->rx_ring = rx_ring.virt;
dma_addr_t tx_ring = dma_alloc_pin(dma_man, sizeof(struct legacy_tx_ldesc) * dev->tx_size, 1, DMA_ALIGN);
if (!tx_ring.phys) {
LOG_ERROR("Failed to allocate tx_ring");
free_desc_ring(dev, dma_man);
return -1;
}
dev->rx_cookies = malloc(sizeof(void *) * dev->rx_size);
dev->tx_cookies = malloc(sizeof(void *) * dev->tx_size);
dev->tx_lengths = malloc(sizeof(unsigned int) * dev->tx_size);
if (!dev->rx_cookies || !dev->tx_cookies || !dev->tx_lengths) {
if (dev->rx_cookies) {
free(dev->rx_cookies);
}
if (dev->tx_cookies) {
free(dev->tx_cookies);
}
if (dev->tx_lengths) {
free(dev->tx_lengths);
}
LOG_ERROR("Failed to malloc");
free_desc_ring(dev, dma_man);
return -1;
}
dev->tx_ring = tx_ring.virt;
/* Remaining needs to be 2 less than size as we cannot actually enqueue size many descriptors,
* since then the head and tail pointers would be equal, indicating empty. */
dev->rx_remain = dev->rx_size - 2;
dev->tx_remain = dev->tx_size - 2;
/* Tell the hardware where the rings are and now big they are */
set_tx_ring(dev, tx_ring.phys);
set_tdlen(dev, dev->tx_size * sizeof(struct legacy_tx_ldesc));
set_rx_ring(dev, rx_ring.phys);
set_rdlen(dev, dev->rx_size * sizeof(struct legacy_rx_ldesc));
/* Set transmit ring initially empty */
dev->tdh = dev->tdt = 0;
set_tdh(dev, dev->tdh);
set_tdt(dev, dev->tdt);
/* Set receive ring initially empty */
dev->rdh = dev->rdt = read_rdh(dev);
set_rdt(dev, dev->rdt);
return 0;
}
static void complete_rx(struct eth_driver *driver)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
if (dev->rdh == dev->rdt) {
/* We haven't enqueued anything */
return;
}
unsigned int i, j;
unsigned int count = 1;
unsigned int rdt = dev->rdt;
for (i = dev->rdh; i != rdt; i = (i + 1) % dev->rx_size, count++) {
unsigned int status = dev->rx_ring[i].status;
/* Ensure no memory references get ordered before we checked the descriptor was written back */
asm volatile("lfence" ::: "memory");
if (!(status & RX_DD)) {
/* not complete yet */
break;
}
if (status & RX_EOP) {
void *cookies[count];
unsigned int len[count];
for (j = 0; j < count; j++) {
cookies[j] = dev->rx_cookies[(dev->rdh + j) % dev->rx_size];
len[j] = dev->rx_ring[(dev->rdh + j) % dev->rx_size].length;
}
/* update rdh */
dev->rdh = (dev->rdh + count) % dev->rx_size;
dev->rx_remain += count;
/* Give the buffers back */
driver->i_cb.rx_complete(driver->cb_cookie, count, cookies, len);
count = 0;
}
}
}
static void complete_tx(struct eth_driver *driver)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
while (dev->tdh != dev->tdt) {
unsigned int i;
for (i = 0; i < dev->tx_lengths[dev->tdh]; i++) {
if (!(dev->tx_ring[(i + dev->tdh) % dev->tx_size].STA & TX_DD)) {
/* not all parts complete */
return;
}
}
/* do not let memory loads happen before our checking of the descriptor write back */
asm volatile("lfence" ::: "memory");
/* increase where we believe tdh to be */
void *cookie = dev->tx_cookies[dev->tdh];
dev->tx_remain += dev->tx_lengths[dev->tdh];
dev->tdh = (dev->tdh + dev->tx_lengths[dev->tdh]) % dev->tx_size;
/* give the buffer back */
driver->i_cb.tx_complete(driver->cb_cookie, cookie);
}
}
static int raw_tx(struct eth_driver *driver, unsigned int num, uintptr_t *phys, unsigned int *len, void *cookie)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
if (!dev->link_up) {
return ETHIF_TX_FAILED;
}
/* Ensure we have room */
if (dev->tx_remain < num) {
/* try and complete some */
complete_tx(driver);
if (dev->tx_remain < num) {
return ETHIF_TX_FAILED;
}
}
unsigned int i;
for (i = 0; i < num; i++) {
dev->tx_ring[(dev->tdt + i) % dev->tx_size] = (struct legacy_tx_ldesc) {
.bufferAddress = phys[i],
.length = len[i],
.CSO = 0,
.CMD = dev->tx_cmd_bits | (i + 1 == num ? TX_CMD_EOP : 0),
.STA = 0,
.ExtCMD = 0,
.CSS = 0,
.VLAN = 0
};
}
dev->tx_cookies[dev->tdt] = cookie;
dev->tx_lengths[dev->tdt] = num;
/* ensure update to descriptors visible before updating tdt */
asm volatile("mfence" ::: "memory");
dev->tdt = (dev->tdt + num) % dev->tx_size;
dev->tx_remain -= num;
set_tdt(dev, dev->tdt);
return ETHIF_TX_ENQUEUED;
}
static int fill_rx_bufs(struct eth_driver *driver)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
int rdt = dev->rdt;
/* We want to install buffers in bursts for performance reasons.
* constantly enqueueing single buffers is expensive */
if (dev->rx_remain < 32) {
return 0;
}
while (dev->rx_remain > 0) {
/* request a buffer */
void *cookie;
uintptr_t phys = driver->i_cb.allocate_rx_buf ? driver->i_cb.allocate_rx_buf(driver->cb_cookie, BUF_SIZE, &cookie) : 0;
if (!phys) {
dev->need_rx_buffers = true;
break;
}
dev->rx_cookies[dev->rdt] = cookie;
/* zery the descriptor */
dev->rx_ring[dev->rdt] = (struct legacy_rx_ldesc) {
.bufferAddress = phys,
.length = BUF_SIZE,
.packetChecksum = 0,
.status = 0,
.error = 0,
.VLAN = 0
};
dev->rdt = (dev->rdt + 1) % dev->rx_size;
dev->rx_remain--;
}
if (dev->rdt != rdt) {
/* ensure update to descriptor visible before updating rdt */
asm volatile("sfence" ::: "memory");
set_rdt(dev, dev->rdt);
}
return dev->rx_remain != 0;
}
static void raw_poll(struct eth_driver *driver)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
if (dev->need_rx_buffers) {
dev->need_rx_buffers = false;
fill_rx_bufs(driver);
}
complete_rx(driver);
complete_tx(driver);
fill_rx_bufs(driver);
check_link_status(driver->eth_data);
}
static void handle_irq(struct eth_driver *driver, int irq)
{
e1000_dev_t *dev = (e1000_dev_t *)driver->eth_data;
uint32_t icr;
switch (dev->family) {
case e1000_82580:
icr = REG_82580_ICR(dev);
if (icr & ICR_82580_RXDW) {
complete_rx(driver);
fill_rx_bufs(driver);
}
if (icr & ICR_82580_TXDW) {
complete_tx(driver);
}
if (icr & ICR_82580_GPHY) {
uint32_t phy = phy_read(dev, 0, 25);
if (phy & BIT(3)) {
check_link_status(dev);
}
}
break;
case e1000_82574:
icr = REG_82574_ICR(dev);
/* ack */
REG_82574_ICR(dev) = icr;
if (icr & (ICR_82574_RXQ0 | ICR_82574_RXTO | ICR_82574_ACK | ICR_82574_RXDMT0)) {
complete_rx(driver);
fill_rx_bufs(driver);
}
if (icr & ICR_82574_TXDW) {
complete_tx(driver);
}
if (icr & ICR_82574_LSC) {
check_link_status(dev);
if (!dev->link_up) {
/* should probably remove everything from the TX ring here */
}
}
break;
default:
assert(!"Unknown device");
}
}
static struct raw_iface_funcs iface_fns = {
.raw_handleIRQ = handle_irq,
.print_state = print_state,
.low_level_init = low_level_init,
.raw_tx = raw_tx,
.raw_poll = raw_poll,
.get_mac = get_mac
};
static void eth_irq_handle(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data)
{
struct eth_driver *eth = data;
handle_irq(eth, 0);
int error = acknowledge_fn(ack_data);
if (error) {
LOG_ERROR("Failed to acknowledge IRQ");
}
}
static int common_init(struct eth_driver *driver, ps_io_ops_t io_ops, void *config, e1000_dev_t *dev)
{
int err;
ethif_intel_config_t *eth_config = (ethif_intel_config_t *) config;
dev->iobase = eth_config->bar0;
dev->tx_size = CONFIG_LIB_ETHDRIVER_TX_DESC_COUNT;
dev->rx_size = CONFIG_LIB_ETHDRIVER_RX_DESC_COUNT;
/* technically we support alignemtn of 1, but get better performance with some alignment */
driver->dma_alignment = 16;
driver->eth_data = dev;
driver->i_fn = iface_fns;
initialize(dev);
err = initialize_desc_ring(dev, &io_ops.dma_manager);
if (err) {
/* Reset device */
disable_all_interrupts(dev);
reset_device(dev);
/* Free memory */
free(dev);
return -1;
}
/* If num_irqs are 0 then we assume that this driver is either polled or some external environment
* will call raw_handleIRQ.
*/
if (eth_config->num_irqs == 1) {
irq_id_t irq_id = ps_irq_register(&io_ops.irq_ops, eth_config->irq_info[0], eth_irq_handle, driver);
if (irq_id < 0) {
LOG_ERROR("Failed to register IRQ");
return -1;
}
}
/* the transmit and receive initialization functions assume
* that we have setup descriptor rings for the transmit receive queues */
initialize_transmit(dev);
initialize_receive(dev);
if (eth_config->prom_mode) {
enable_prom_mode(dev);
}
/* fill up the receive ring as much as possible */
fill_rx_bufs(driver);
/* turn interrupts on */
enable_interrupts(dev);
/* check the current status of the link */
check_link_status(dev);
return ps_interface_register(&io_ops.interface_registration_ops, PS_ETHERNET_INTERFACE, driver, NULL);
}
int ethif_e82580_init(struct eth_driver *driver, ps_io_ops_t io_ops, void *config)
{
e1000_dev_t *dev = malloc(sizeof(*dev));
if (!dev) {
LOG_ERROR("Failed to malloc");
return -1;
}
dev->family = e1000_82580;
dev->tx_cmd_bits = TX_CMD_IFCS | TX_CMD_RS;
return common_init(driver, io_ops, config, dev);
}
int ethif_e82574_init(struct eth_driver *driver, ps_io_ops_t io_ops, void *config)
{
e1000_dev_t *dev = malloc(sizeof(*dev));
if (!dev) {
LOG_ERROR("Failed to malloc");
return -1;
}
dev->family = e1000_82574;
dev->tx_cmd_bits = TX_CMD_IFCS | TX_CMD_RS | TX_CMD_IDE;
return common_init(driver, io_ops, config, dev);
}