| /* | |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) | |
| * | |
| * SPDX-License-Identifier: GPL-2.0-only | |
| */ | |
| #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); | |
| } |