From f29105446583ff036956b92088804fba384b7453 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Mon, 9 Jan 2023 23:58:19 +0100 Subject: [PATCH] Add em(4) driver. Co-authored-by: Meisaka Yukara --- kernel/Makefile | 1 + kernel/kernel.cpp | 9 + kernel/net/em/em.cpp | 1036 +++++++++++++++++++++++++++++++++++ kernel/net/em/em.h | 35 ++ kernel/net/em/emregs.h | 653 ++++++++++++++++++++++ share/man/man4/em.4 | 26 + share/man/man7/kernel.7 | 10 + share/man/man7/user-guide.7 | 8 +- 8 files changed, 1777 insertions(+), 1 deletion(-) create mode 100644 kernel/net/em/em.cpp create mode 100644 kernel/net/em/em.h create mode 100644 kernel/net/em/emregs.h create mode 100644 share/man/man4/em.4 diff --git a/kernel/Makefile b/kernel/Makefile index 04e57594..f63196d1 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -126,6 +126,7 @@ memorymanagement.o \ mouse/ps2.o \ mtable.o \ net/arp.o \ +net/em/em.o \ net/ether.o \ net/fs.o \ net/if.o \ diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index 41802767..d0f255d3 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -83,6 +83,7 @@ #include "logterminal.h" #include "mouse/ps2.h" #include "multiboot.h" +#include "net/em/em.h" #include "net/fs.h" #include "net/lo/lo.h" #include "net/ping.h" @@ -115,6 +116,7 @@ static void SystemIdleThread(void* user); static int argc; static char** argv; static multiboot_info_t* bootinfo; +static bool enable_em = true; static bool enable_network_drivers = true; static char* cmdline_tokenize(char** saved) @@ -296,6 +298,10 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p) HaltKernel(); } } + else if ( !strcmp(arg, "--disable-em") ) + enable_em = false; + else if ( !strcmp(arg, "--enable-em") ) + enable_em = true; else if ( !strcmp(arg, "--disable-network-drivers") ) enable_network_drivers = false; else if ( !strcmp(arg, "--enable-network-drivers") ) @@ -634,6 +640,9 @@ static void BootThread(void* /*user*/) // Initialize the network drivers. if ( enable_network_drivers ) { + // Initialize the EM driver. + if ( enable_em ) + EM::Init("/dev", slashdev); } // diff --git a/kernel/net/em/em.cpp b/kernel/net/em/em.cpp new file mode 100644 index 00000000..fc4e1030 --- /dev/null +++ b/kernel/net/em/em.cpp @@ -0,0 +1,1036 @@ +/* + * Copyright (c) 2015, 2016, 2017, 2022, 2023 Jonas 'Sortie' Termansen. + * Copyright (c) 2015 Meisaka Yukara. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * net/em/em.cpp + * 825xx driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../arp.h" +#include "../ether.h" + +#include "em.h" +#include "emregs.h" + +namespace Sortix { +namespace EM { + +static const int RECEIVE_PACKET_COUNT = 32; + +static const int FEATURE_EEPROM = 1 << 0; // EEPROM access present +static const int FEATURE_SERDES = 1 << 1; // SerDes/TBI supported +static const int FEATURE_PCIE = 1 << 2; // PCIe Device + +enum feature_index +{ + emdefault, + em8254xS, + em8254xM, + em8256xM, + em8257xM, + em8257xS, + em82576S, + em8258xM, + em8258xS, + emfeaturemax +}; + +static const int feature_table[emfeaturemax] = +{ + [emdefault] = 0, + [em8254xS] = FEATURE_EEPROM | FEATURE_SERDES, + [em8254xM] = FEATURE_EEPROM, + [em8256xM] = FEATURE_PCIE, + [em8257xM] = FEATURE_PCIE, + [em8257xS] = FEATURE_SERDES | FEATURE_PCIE, + [em82576S] = FEATURE_SERDES | FEATURE_PCIE, + [em8258xM] = FEATURE_PCIE, + [em8258xS] = FEATURE_SERDES | FEATURE_PCIE, +}; + +struct device +{ + uint16_t feature_index; + uint16_t device_id; +}; + +static const struct device device_table[] = +{ + { em8254xS, PCI_PRODUCT_INTEL_DH89XXCC_SGMII }, + { em8254xS, PCI_PRODUCT_INTEL_DH89XXCC_S }, + { em8254xS, PCI_PRODUCT_INTEL_DH89XXCC_BPLANE }, + { em8254xS, PCI_PRODUCT_INTEL_DH89XXCC_SFP }, + { em8254xS, PCI_PRODUCT_INTEL_82542 }, + { em8254xS, PCI_PRODUCT_INTEL_82543GC_F }, + { em8254xS, PCI_PRODUCT_INTEL_82543GC_C }, + { em8254xS, PCI_PRODUCT_INTEL_82544EI_C }, + { em8254xS, PCI_PRODUCT_INTEL_82544EI_F }, + { em8254xS, PCI_PRODUCT_INTEL_82544GC_C }, + { em8254xM, PCI_PRODUCT_INTEL_82544GC_LOM }, + { em8254xM, PCI_PRODUCT_INTEL_82540EM_D }, + { em8254xS, PCI_PRODUCT_INTEL_82545EM_C }, + { em8254xS, PCI_PRODUCT_INTEL_82546EB_C }, + { em8254xS, PCI_PRODUCT_INTEL_82545EM_F }, + { em8254xS, PCI_PRODUCT_INTEL_82546EB_F }, + { em8254xM, PCI_PRODUCT_INTEL_82541EI_C }, + { em8254xM, PCI_PRODUCT_INTEL_82541ER_LOM }, + { em8254xM, PCI_PRODUCT_INTEL_82540EM_M }, + { em8254xM, PCI_PRODUCT_INTEL_82540EP_M }, + { em8254xM, PCI_PRODUCT_INTEL_82540EP_D }, + { em8254xM, PCI_PRODUCT_INTEL_82541EI_M }, + { em8254xM, PCI_PRODUCT_INTEL_82547EI }, + { em8254xM, PCI_PRODUCT_INTEL_82547EI_M }, + { em8254xS, PCI_PRODUCT_INTEL_82546EB_CQ }, + { em8254xS, PCI_PRODUCT_INTEL_82540EP_LP }, + { em8254xS, PCI_PRODUCT_INTEL_82545GM_C }, + { em8254xS, PCI_PRODUCT_INTEL_82545GM_F }, + { em8254xS, PCI_PRODUCT_INTEL_82545GM_S }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_IGP_M_AMT }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_IGP_AMT }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_IGP_C }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_IFE }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_IGP_M }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_C }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_F }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_S }, + { em8254xS, PCI_PRODUCT_INTEL_82547GI }, + { em8254xM, PCI_PRODUCT_INTEL_82541GI_C }, + { em8254xM, PCI_PRODUCT_INTEL_82541GI_M }, + { em8254xM, PCI_PRODUCT_INTEL_82541ER_C }, + { em8254xS, PCI_PRODUCT_INTEL_82546GB_C }, + { em8254xS, PCI_PRODUCT_INTEL_82546GB_F }, + { em8254xS, PCI_PRODUCT_INTEL_82546GB_S }, + { em8254xM, PCI_PRODUCT_INTEL_82541GI_LF }, + { em8257xS, PCI_PRODUCT_INTEL_82572EI_C }, + { em8257xS, PCI_PRODUCT_INTEL_82572EI_F }, + { em8257xS, PCI_PRODUCT_INTEL_82572EI_S }, + { em8254xS, PCI_PRODUCT_INTEL_82546GB_PCIE }, + { em8257xM, PCI_PRODUCT_INTEL_82573E }, + { em8257xM, PCI_PRODUCT_INTEL_82573E_IAMT }, + { em8257xM, PCI_PRODUCT_INTEL_82573E_IDE }, + { em8257xM, PCI_PRODUCT_INTEL_82573E_KCS }, + { em8257xM, PCI_PRODUCT_INTEL_82573E_SERIAL }, + { em8257xS, PCI_PRODUCT_INTEL_80003ES2LAN_CD }, + { em8257xS, PCI_PRODUCT_INTEL_80003ES2LAN_SD }, + { em8254xS, PCI_PRODUCT_INTEL_82546GB_CQ }, + { em8257xM, PCI_PRODUCT_INTEL_82573L }, + { em8254xS, PCI_PRODUCT_INTEL_82546GB_2 }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_AT }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_AF }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_CQ }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_FQ }, + { em8257xS, PCI_PRODUCT_INTEL_82575EB_C }, + { em8257xS, PCI_PRODUCT_INTEL_82575EB_S }, + { em8257xM, PCI_PRODUCT_INTEL_82573L_PL_1 }, + { em8257xM, PCI_PRODUCT_INTEL_82573V_PM }, + { em8257xM, PCI_PRODUCT_INTEL_82573E_PM }, + { em8257xM, PCI_PRODUCT_INTEL_82573L_PL_2 }, + { em8254xS, PCI_PRODUCT_INTEL_82546GB_CQ_K }, + { em8257xS, PCI_PRODUCT_INTEL_82572EI }, + { em8257xS, PCI_PRODUCT_INTEL_80003ES2LAN_C }, + { em8257xS, PCI_PRODUCT_INTEL_80003ES2LAN_S }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_CQ_LP }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IGP_AMT }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IGP_M }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IFE }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IFE_G }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IFE_GT }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_IFE_GT }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_IFE_G }, + { em82576S, PCI_PRODUCT_INTEL_82576 }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IGP_M_V }, + { em8256xM, PCI_PRODUCT_INTEL_ICH10_R_BM_LM }, + { em8256xM, PCI_PRODUCT_INTEL_ICH10_R_BM_LF }, + { em8256xM, PCI_PRODUCT_INTEL_ICH10_R_BM_V }, + { em8257xM, PCI_PRODUCT_INTEL_82574L }, + { em8257xS, PCI_PRODUCT_INTEL_82571PT_CQ }, + { em8257xS, PCI_PRODUCT_INTEL_82575GB_CQ }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_SD }, + { em8257xS, PCI_PRODUCT_INTEL_82571EB_SQ }, + { em8256xM, PCI_PRODUCT_INTEL_ICH10_D_BM_LM }, + { em8256xM, PCI_PRODUCT_INTEL_ICH10_D_BM_LF }, + { em8257xS, PCI_PRODUCT_INTEL_82575GB_QP_PM }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_BM }, + { em82576S, PCI_PRODUCT_INTEL_82576_F }, + { em82576S, PCI_PRODUCT_INTEL_82576_S }, + { em82576S, PCI_PRODUCT_INTEL_82576_CQ }, + { em8257xM, PCI_PRODUCT_INTEL_82577LM }, + { em8257xM, PCI_PRODUCT_INTEL_82577LC }, + { em8257xS, PCI_PRODUCT_INTEL_82578DM }, + { em8257xS, PCI_PRODUCT_INTEL_82578DC }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IGP_M_AMT }, + { em8257xM, PCI_PRODUCT_INTEL_82574LA }, + { em8254xS, PCI_PRODUCT_INTEL_82544EI_A4 }, + { em8254xS, PCI_PRODUCT_INTEL_82544GC_A4 }, + { em8256xM, PCI_PRODUCT_INTEL_ICH8_82567V_3 }, + { em8257xM, PCI_PRODUCT_INTEL_82579LM }, + { em8257xM, PCI_PRODUCT_INTEL_82579V }, + { em82576S, PCI_PRODUCT_INTEL_82576_NS }, + { em8258xS, PCI_PRODUCT_INTEL_82583V }, + { em82576S, PCI_PRODUCT_INTEL_82576_SQ }, + { em8258xS, PCI_PRODUCT_INTEL_82580_C }, + { em8258xS, PCI_PRODUCT_INTEL_82580_F }, + { em8258xS, PCI_PRODUCT_INTEL_82580_S }, + { em8258xS, PCI_PRODUCT_INTEL_82580_SGMII }, + { em8258xS, PCI_PRODUCT_INTEL_82580_CD }, + { em82576S, PCI_PRODUCT_INTEL_82576_NS_S }, + { em82576S, PCI_PRODUCT_INTEL_I350_C }, + { em82576S, PCI_PRODUCT_INTEL_I350_F }, + { em82576S, PCI_PRODUCT_INTEL_I350_S }, + { em82576S, PCI_PRODUCT_INTEL_I350_SGMII }, + { em82576S, PCI_PRODUCT_INTEL_82576_CQ_ET2 }, + { em8258xS, PCI_PRODUCT_INTEL_82580_FQ }, + { em8257xS, PCI_PRODUCT_INTEL_I210_C }, + { em8257xS, PCI_PRODUCT_INTEL_I210_F }, + { em8257xS, PCI_PRODUCT_INTEL_I210_S }, + { em8257xS, PCI_PRODUCT_INTEL_I210_SGMII }, + { em8257xS, PCI_PRODUCT_INTEL_I211_C }, + { em8257xM, PCI_PRODUCT_INTEL_I217_LM }, + { em8257xM, PCI_PRODUCT_INTEL_I217_V }, + { em8257xM, PCI_PRODUCT_INTEL_I218_V }, + { em8257xM, PCI_PRODUCT_INTEL_I218_LM }, + { em8257xS, PCI_PRODUCT_INTEL_I210_C_NF }, + { em8257xS, PCI_PRODUCT_INTEL_I210_S_NF }, + { em8257xM, PCI_PRODUCT_INTEL_I218_LM_2 }, + { em8257xM, PCI_PRODUCT_INTEL_I218_V_2 }, + { em8257xM, PCI_PRODUCT_INTEL_I218_LM_3 }, + { em8257xM, PCI_PRODUCT_INTEL_I218_V_3 }, + { em8256xM, PCI_PRODUCT_INTEL_ICH9_IGP_C }, + { em8254xM, PCI_PRODUCT_INTEL_EP80579_LAN_1 }, + { em8254xM, PCI_PRODUCT_INTEL_EP80579_LAN_4 }, + { em8254xM, PCI_PRODUCT_INTEL_EP80579_LAN_2 }, + { em8254xM, PCI_PRODUCT_INTEL_EP80579_LAN_5 }, + { em8254xM, PCI_PRODUCT_INTEL_EP80579_LAN_3 }, + { em8254xM, PCI_PRODUCT_INTEL_EP80579_LAN_6 } +}; + +static const size_t device_table_count = + sizeof(device_table) / sizeof(device_table[0]); + +static const uint32_t understood_interrupts = + EM_INTERRUPT_TXDW | EM_INTERRUPT_TXQE | + EM_INTERRUPT_LSC | EM_INTERRUPT_RXDMT0 | EM_INTERRUPT_RXO | + EM_INTERRUPT_RXT0 | EM_INTERRUPT_MDAC | EM_INTERRUPT_RXCFG | + EM_INTERRUPT_TXD_LOW | EM_INTERRUPT_SRPD; + +struct rx_desc +{ + little_uint64_t address; + little_uint16_t length; + little_uint16_t checksum; + little_uint8_t status; + little_uint8_t errors; + little_uint16_t special; +}; + +struct tx_desc_tcpdata +{ + little_uint64_t address; + little_uint32_t lencmd; + little_uint8_t status; + little_uint8_t opts; + little_uint16_t special; +}; + +class EM : public NetworkInterface +{ +public: + EM(uint32_t devaddr, size_t number, int features); + virtual ~EM(); + +public: + virtual bool Send(Ref pkt); + bool Initialize(); + +private: + bool Reset(); + __attribute__((format(printf, 2, 3))) + void Log(const char* format, ...); + uint32_t Read32(uint32_t reg); + void Write32(uint32_t reg, uint32_t value); + bool ReadEEPROM(uint16_t* out, uint16_t reg); + bool ReadPHY(uint16_t* out, uint8_t reg); + bool WritePHY(uint8_t reg, uint16_t value); + bool WaitLinkResolved(); + void RegisterInterrupts(); + bool AddReceiveDescriptor(Ref pkt); + bool AddTransmitDescriptor(Ref pkt); + bool CanAddTransmit(); + static void InterruptHandler(struct interrupt_context*, void*); + static void InterruptWorkHandler(void* context); + void OnInterrupt(); + void InterruptWork(); + +private: + uint32_t devaddr; + struct interrupt_handler interrupt_registration; + struct interrupt_work interrupt_work; + uint32_t interrupt_work_icr; + uint8_t interrupt; + addralloc_t mmio_alloc; + volatile uint8_t* mmio_base; + paddrmapped_t rdesc_alloc; + paddrmapped_t tdesc_alloc; + int features; + struct rx_desc* rdesc; + struct tx_desc_tcpdata* tdesc; + Ref* rpackets; + Ref* tpackets; + Ref tx_queue_first; + Ref tx_queue_last; + kthread_mutex_t tx_lock; + kthread_mutex_t eeprom_lock; + kthread_mutex_t phy_lock; + uint32_t rx_count; + uint32_t tx_count; + uint32_t rx_tail; + uint32_t rx_prochead; + uint32_t tx_tail; + uint32_t tx_prochead; + +}; + +void EM::Log(const char* format, ...) +{ + Log::PrintF("%s: ", ifinfo.name); + va_list ap; + va_start(ap, format); + Log::PrintFV(format, ap); + va_end(ap); + Log::PrintF("\n"); +} + +EM::EM(uint32_t devaddr, size_t number, int features) +{ + snprintf(ifinfo.name, sizeof(ifinfo.name), "em%zu", number); + ifinfo.type = IF_TYPE_ETHERNET; + ifinfo.features = IF_FEATURE_ETHERNET_CRC_OFFLOAD; + ifinfo.addrlen = ETHER_ADDR_LEN; + ifstatus.mtu = ETHERMTU; + this->devaddr = devaddr; + interrupt = 0; + memset(&interrupt_registration, 0, sizeof(interrupt_registration)); + interrupt_work.handler = InterruptWorkHandler; + interrupt_work.context = this; + interrupt_work_icr = 0; + memset(&mmio_alloc, 0, sizeof(mmio_alloc)); + mmio_base = NULL; + memset(&rdesc_alloc, 0, sizeof(rdesc_alloc)); + memset(&tdesc_alloc, 0, sizeof(tdesc_alloc)); + this->features = features; + rdesc = NULL; + tdesc = NULL; + rpackets = NULL; + tpackets = NULL; + // tx_queue_first has constructor. + // tx_queue_last has constructor. + tx_lock = KTHREAD_MUTEX_INITIALIZER; + eeprom_lock = KTHREAD_MUTEX_INITIALIZER; + phy_lock = KTHREAD_MUTEX_INITIALIZER; + rx_count = 0; + tx_count = 0; + rx_tail = 0; + rx_prochead = 0; + tx_tail = 0; + tx_prochead = 0; +} + +EM::~EM() +{ + assert(false); +} + +uint32_t EM::Read32(uint32_t reg) +{ + return *((volatile little_uint32_t*) (mmio_base + reg)); +} + +void EM::Write32(uint32_t reg, uint32_t value) +{ + *((volatile little_uint32_t*) (mmio_base + reg)) = value; +} + +bool EM::ReadEEPROM(uint16_t* out, uint16_t reg) +{ + ScopedLock lock(&eeprom_lock); + uint32_t value = ((uint32_t) reg) << EM_MAIN_REG_EERD_ADDR_SHIFT | + EM_MAIN_REG_EERD_START; + Write32(EM_MAIN_REG_EERD, value); + struct timespec now = Time::Get(CLOCK_MONOTONIC); + struct timespec end = timespec_add(now, timespec_make(1, 0)); + while ( true ) + { + now = Time::Get(CLOCK_MONOTONIC); + if ( (value = Read32(EM_MAIN_REG_EERD)) & EM_MAIN_REG_EERD_DONE ) + break; + if ( timespec_le(end, now) ) + return false; + } + *out = (value & EM_MAIN_REG_EERD_DATA_MASK) >> EM_MAIN_REG_EERD_DATA_SHIFT; + return true; +} + +bool EM::ReadPHY(uint16_t* out, uint8_t reg) +{ + ScopedLock lock(&phy_lock); + uint32_t mdic = Read32(EM_MAIN_REG_MDIC); + if ( mdic & EM_MAIN_REG_MDIC_E ) + return false; + mdic = ((uint32_t) reg) << EM_MAIN_REG_MDIC_REGADD_SHIFT | + EM_MAIN_REG_MDIC_PHYADD_PHY_ONE_AND_ONLY | + EM_MAIN_REG_MDIC_OP_READ; + Write32(EM_MAIN_REG_MDIC, mdic); + uint32_t stop_bits = EM_MAIN_REG_MDIC_R | EM_MAIN_REG_MDIC_E; + struct timespec now = Time::Get(CLOCK_MONOTONIC); + struct timespec end = timespec_add(now, timespec_make(1, 0)); + while ( true ) + { + now = Time::Get(CLOCK_MONOTONIC); + if ( (mdic = Read32(EM_MAIN_REG_MDIC)) & stop_bits ) + break; + if ( timespec_le(end, now) ) + return false; + } + if ( mdic & EM_MAIN_REG_MDIC_E ) + return false; + *out = (mdic & EM_MAIN_REG_MDIC_DATA_MASK) >> EM_MAIN_REG_MDIC_DATA_SHIFT; + return true; +} + +bool EM::WritePHY(uint8_t reg, uint16_t value) +{ + ScopedLock lock(&phy_lock); + uint32_t mdic = Read32(EM_MAIN_REG_MDIC); + if ( mdic & EM_MAIN_REG_MDIC_E ) + return false; + mdic = ((uint32_t) value) << EM_MAIN_REG_MDIC_DATA_SHIFT | + ((uint32_t) reg) << EM_MAIN_REG_MDIC_REGADD_SHIFT | + EM_MAIN_REG_MDIC_PHYADD_PHY_ONE_AND_ONLY | + EM_MAIN_REG_MDIC_OP_WRITE; + Write32(EM_MAIN_REG_MDIC, mdic); + uint32_t stop_bits = EM_MAIN_REG_MDIC_R | EM_MAIN_REG_MDIC_E; + struct timespec now = Time::Get(CLOCK_MONOTONIC); + struct timespec end = timespec_add(now, timespec_make(1, 0)); + while ( true ) + { + now = Time::Get(CLOCK_MONOTONIC); + if ( (mdic = Read32(EM_MAIN_REG_MDIC)) & stop_bits ) + break; + if ( timespec_le(end, now) ) + return false; + } + if ( mdic & EM_MAIN_REG_MDIC_E ) + return false; + return true; +} + +bool EM::WaitLinkResolved() +{ + uint16_t pstatus; + uint16_t psstat; + struct timespec now = Time::Get(CLOCK_MONOTONIC); + struct timespec end = timespec_add(now, timespec_make(1, 0)); + do + { + now = Time::Get(CLOCK_MONOTONIC); + if ( !ReadPHY(&pstatus, EM_PHY_REG_PSTATUS) || + !ReadPHY(&psstat, EM_PHY_REG_PSSTAT) ) + return Log("error: WaitLinkResolved failed to read PHY"), false; + if ( !(psstat & EM_PHY_REG_PSSTAT_LINK) ) + return false; + if ( !(pstatus & EM_PHY_REG_PSTATUS_AN_COMPLETE) ) + continue; + // TODO: Needed? + if ( !(psstat & EM_PHY_REG_PSSTAT_SPEED_DUPLEX_RESOLVED) ) + continue; + return true; + } while ( timespec_lt(now, end) ); + Log("error: WaitLinkResolved timed out"); + return false; +} + +bool EM::AddReceiveDescriptor(Ref pkt) // ordered via interrupt worker +{ + uint32_t next_desc = rx_tail + 1; + if ( rx_count <= next_desc ) + next_desc = 0; + if ( next_desc == rx_prochead ) + return false; + struct rx_desc* desc = &rdesc[rx_tail]; + memset(desc, 0, sizeof(*desc)); + desc->status = 0; + desc->address = pkt->pmap.phys; + rpackets[rx_tail] = pkt; + rx_tail = next_desc; + // TODO: Research whether this is needed, or whether the paging bits do + // the right thing. Do those bits work on all systems? + //asm volatile ("wbinvd"); + Write32(EM_MAIN_REG_RDT, rx_tail); + return true; +} + +bool EM::AddTransmitDescriptor(Ref pkt) // tx_lock must be locked. +{ + uint32_t next_desc = tx_tail + 1; + if ( tx_count <= next_desc ) + next_desc = 0; + if ( next_desc == tx_prochead ) + return false; + struct tx_desc_tcpdata* desc = &tdesc[tx_tail]; + desc->address = pkt->pmap.phys; + desc->lencmd = EM_TDESC_LENGTH(pkt->length) | EM_TDESC_TYPE_TCPDATA | + EM_TDESC_CMD_RS | EM_TDESC_CMD_EOP | EM_TDESC_CMD_IFCS; + desc->status = 0; + desc->opts = 0; + desc->special = 0; + tpackets[tx_tail] = pkt; + tx_tail = next_desc; + // TODO: Research whether this is needed, or whether the paging bits do + // the right thing. Do those bits work on all systems? + //asm volatile ("wbinvd"); + Write32(EM_MAIN_REG_TDT, tx_tail); + return true; +} + +bool EM::CanAddTransmit() // tx_lock must be locked. +{ + uint32_t next_desc = tx_tail + 1; + if ( tx_count <= next_desc ) + next_desc = 0; + if ( next_desc == tx_prochead ) + return false; + return true; +} + +bool EM::Send(Ref pkt) +{ + ScopedLock lock(&tx_lock); + if ( !tx_queue_first && + CanAddTransmit() && + AddTransmitDescriptor(pkt) ) + return true; + if ( tx_queue_last ) + { + tx_queue_last->next = pkt; + tx_queue_last = pkt; + } + else + { + tx_queue_first = pkt; + tx_queue_last = pkt; + } + pkt->next.Reset(); + return true; +} + +void EM::InterruptHandler(struct interrupt_context*, void* user) +{ + ((EM*) user)->OnInterrupt(); +} + +void EM::RegisterInterrupts() +{ + interrupt = PCI::GetInterruptIndex(devaddr); + interrupt_registration.handler = EM::InterruptHandler; + interrupt_registration.context = this; + Interrupt::RegisterHandler(interrupt, &interrupt_registration); +} + +void EM::OnInterrupt() +{ + // TODO: Use "Interrupt Acknowledge Auto Mask Register - IAM (000E0h)" to + // automatically mask set interrupts. + uint32_t icr = Read32(EM_MAIN_REG_ICR); + if ( !icr ) + return; + if ( interrupt_work_icr != 0 ) + { +#if 0 + // TODO: This assertion fired! Is it fine to just ignore the interrupt? + // Does reading ICR have side effects? Should we read it at the + // of the interrupt work handler and reschedule the interrupt + // worker? + if ( interrupt_work_icr ) + PanicF("em: icr=%#x but interrupt_work_icr=%#x\n", icr, interrupt_work_icr); + assert(interrupt_work_icr == 0); +#endif + return; + } + interrupt_work_icr = icr; + // Mask further interrupts while processing the interrupt. + Write32(EM_MAIN_REG_IMC, understood_interrupts); + Interrupt::ScheduleWork(&interrupt_work); +} + +void EM::InterruptWorkHandler(void* context) +{ + ((EM*) context)->InterruptWork(); +} + +void EM::InterruptWork() +{ + uint32_t icr = interrupt_work_icr; + uint32_t unhandled = icr & 0x1FFFF; + if ( icr & EM_INTERRUPT_LSC ) + { + // TODO: This can block the kernel worker thread for a second. + WaitLinkResolved(); + uint32_t status = Read32(EM_MAIN_REG_STATUS); + ScopedLock lock(&cfg_lock); + if ( status & EM_MAIN_REG_STATUS_LU ) + ifstatus.flags |= IF_STATUS_FLAGS_UP; + else + ifstatus.flags &= ~IF_STATUS_FLAGS_UP; + kthread_cond_broadcast(&cfg_cond); + poll_channel.Signal(PollEventStatus()); + unhandled &= ~EM_INTERRUPT_LSC; + } + if ( icr & EM_INTERRUPT_RXDMT0 ) + { + // Add more receive descriptors when we run out faster than we can + // process the incoming packets. + for ( size_t i = 0; i < RECEIVE_PACKET_COUNT; i++ ) + { + Ref buf = GetPacket(); + if ( buf ) + { + if ( !AddReceiveDescriptor(buf) ) + break; + } + } + unhandled &= ~EM_INTERRUPT_RXDMT0; + } + if ( icr & EM_INTERRUPT_MDAC ) + { + // MDI/O Access Complete + } + if ( icr & EM_INTERRUPT_RXT0 ) + { + // Receive timer expired, check descriptors. + while ( rx_prochead != rx_tail && rdesc[rx_prochead].status ) + { + Ref rxpacket = rpackets[rx_prochead]; + rpackets[rx_prochead].Reset(); + assert(rxpacket.IsUnique()); + rxpacket->length = rdesc[rx_prochead].length; + assert(rxpacket->pmap.phys == rdesc[rx_prochead].address); + rxpacket->netif = this; + Ether::Handle(rxpacket, true); + rxpacket.Reset(); + rx_prochead++; + if ( rx_count <= rx_prochead ) + rx_prochead = 0; + if ( rx_prochead != rx_tail ) + { + Ref buf = GetPacket(); + // TODO: Design a solution that handles when there's no more + // packets available, but later adds packets when they + // become available, otherwise the the receive queue might + // deadlock with no available packets. + if ( buf ) + AddReceiveDescriptor(buf); + } + } + unhandled &= ~EM_INTERRUPT_RXT0; + } + if ( icr & EM_INTERRUPT_RXO ) + { + // TODO: Receiver overrun, do we need more buffers? + unhandled &= ~EM_INTERRUPT_RXO; + } + kthread_mutex_lock(&tx_lock); + if ( icr & (EM_INTERRUPT_TXDW | EM_INTERRUPT_TXD_LOW) ) + { + // Transmit descriptor written back. + // Transmit descriptor low threshold. + while ( tx_prochead != tx_tail && + (tdesc[tx_prochead].status & EM_RDESC_STATUS_DD) ) + { + tpackets[tx_prochead].Reset(); + tx_prochead++; + if ( tx_count <= tx_prochead ) + tx_prochead = 0; + } + unhandled &= ~(EM_INTERRUPT_TXDW | EM_INTERRUPT_TXD_LOW); + } + if ( icr & EM_INTERRUPT_TXQE ) + { + // Transmit queue is empty. Head should equal tail. + assert(tx_tail < tx_count); + while ( tx_prochead != tx_tail ) + { + tpackets[tx_prochead].Reset(); + tx_prochead++; + if ( tx_count <= tx_prochead ) + tx_prochead = 0; + } + unhandled &= ~EM_INTERRUPT_TXQE; + } + while ( tx_queue_first && CanAddTransmit() ) + { + Ref pkt = tx_queue_first; + tx_queue_first = pkt->next; + pkt->next.Reset(); + AddTransmitDescriptor(pkt); + } + kthread_mutex_unlock(&tx_lock); + interrupt_work_icr = 0; + // Unmask interrupts so they can be delivered again. + Write32(EM_MAIN_REG_IMS, understood_interrupts); +} + +bool EM::Reset() +{ + uint32_t ctrl; + uint32_t status; + + PCI::DisableBusMaster(devaddr); + PCI::DisableInterruptLine(devaddr); + + if ( features & FEATURE_PCIE ) + { + // For PCIe devices, disable GIO Master prior to reset. + ctrl = Read32(EM_MAIN_REG_CTRL); + ctrl |= EM_MAIN_REG_CTRL_GIOMD; + Write32(EM_MAIN_REG_CTRL, ctrl); + struct timespec now = Time::Get(CLOCK_MONOTONIC); + struct timespec end = timespec_add(now, timespec_make(1, 0)); + while ( true ) + { + now = Time::Get(CLOCK_MONOTONIC); + status = Read32(EM_MAIN_REG_STATUS); + if ( !(status & EM_MAIN_REG_STATUS_GIOME) ) + break; + if ( timespec_le(end, now) ) + { + Log("error: Failed to disable GIO Master prior to reset"); + return false; + } + } + } + + // Clear all interrupts and disable rx/tx. + Write32(EM_MAIN_REG_IMC, UINT32_MAX); + Write32(EM_MAIN_REG_RCTL, 0); + Write32(EM_MAIN_REG_TCTL, 0); + + // Reset the device, this initializes everything to default settings + ctrl = Read32(EM_MAIN_REG_CTRL); + ctrl |= EM_MAIN_REG_CTRL_RST; + Write32(EM_MAIN_REG_CTRL, ctrl); + + // TODO: The documentation mentioned waiting a short interval here before + // checking the register again. + + // Wait for it to finish. + struct timespec now = Time::Get(CLOCK_MONOTONIC); + struct timespec end = timespec_add(now, timespec_make(1, 0)); + while ( true ) + { + // Can exit this wait if card is done loading it's settings. + if ( Read32(EM_MAIN_REG_EECD) & EM_MAIN_REG_EECD_ARD ) + break; + // Hack to make sure the control read below is valid. + // On some hardware, this loop would hang without this. + // Read all the statisics registers (which we do later anyway). + for ( uint32_t x = 0; x < 256; x += 4 ) + Read32(EM_STAT_REG_CRCERRS + x); + // Read, and wait for reset to finish. + ctrl = Read32(EM_MAIN_REG_CTRL); + if ( !(ctrl & EM_MAIN_REG_CTRL_PHY_RST) ) + break; + if ( timespec_le(end, now) ) + { + Log("error: Timed out waiting for device reset"); + return false; + } + } + + // Disable interrupts after the reset. + Write32(EM_MAIN_REG_IMC, UINT32_MAX); + + if ( features & FEATURE_EEPROM ) + { + // If we have EEPROM read the MAC directly from it. + uint16_t macw[3]; + if ( !ReadEEPROM(&macw[0], EM_EEPROM_REG_ETHERNET_ADDR_1) || + !ReadEEPROM(&macw[1], EM_EEPROM_REG_ETHERNET_ADDR_2) || + !ReadEEPROM(&macw[2], EM_EEPROM_REG_ETHERNET_ADDR_3) ) + return Log("error: Failed to read EEPROM"), false; + ifinfo.addr[0] = macw[0] >> 0 & 0xFF; + ifinfo.addr[1] = macw[0] >> 8 & 0xFF; + ifinfo.addr[2] = macw[1] >> 0 & 0xFF; + ifinfo.addr[3] = macw[1] >> 8 & 0xFF; + ifinfo.addr[4] = macw[2] >> 0 & 0xFF; + ifinfo.addr[5] = macw[2] >> 8 & 0xFF; + } + else + { + uint32_t macd[2]; + // Receive Address[0] is programmed with the hardware mac from PROM or + // EEPROM after the device is reset. + macd[0] = Read32(EM_FILTER_REG_RAL); + macd[1] = Read32(EM_FILTER_REG_RAH); + ifinfo.addr[0] = macd[0] >> 0 & 0xFF; + ifinfo.addr[1] = macd[0] >> 8 & 0xFF; + ifinfo.addr[2] = macd[0] >> 16 & 0xFF; + ifinfo.addr[3] = macd[0] >> 24 & 0xFF; + ifinfo.addr[4] = macd[1] >> 0 & 0xFF; + ifinfo.addr[5] = macd[1] >> 8 & 0xFF; + } + memcpy(&cfg.ether.address, ifinfo.addr, sizeof(struct ether_addr)); + + // Enable bus mastering so the card can read/write memory. + PCI::EnableBusMaster(devaddr); + PCI::EnableMemoryWrite(devaddr); + + status = Read32(EM_MAIN_REG_STATUS); + bool inserdes = false; + if ( features & FEATURE_SERDES ) + inserdes = status & EM_MAIN_REG_STATUS_TBIMODE; + + ctrl = Read32(EM_MAIN_REG_CTRL); + + if ( inserdes ) + ctrl &= ~EM_MAIN_REG_CTRL_LRST; // TBI/SerDes only. + ctrl |= EM_MAIN_REG_CTRL_ASDE; + ctrl |= EM_MAIN_REG_CTRL_SLU; + ctrl &= ~EM_MAIN_REG_CTRL_ILOS; + ctrl &= ~EM_MAIN_REG_CTRL_FRCSPD; + ctrl &= ~EM_MAIN_REG_CTRL_FRCDPLX; + ctrl &= ~EM_MAIN_REG_CTRL_VME; + // TODO: CTRL.RFCE (hub 8/9/10 pdf 11.4.3.2 says read from phy regs) + // TODO: CTRL.TFCE (hub 8/9/10 pdf 11.4.3.2 says read from phy regs) + // TODO: CTRL.ILOS + + Write32(EM_MAIN_REG_CTRL, ctrl); + + // CTRL.FRCSPD = CTRL.FRCDPLX = 0b; CTRL.ASDE = 1b + // CTRL.FD Duplex if FRCDPLX is set, ignored otherwise + // CTRL.SLU Enable link + // CTRL.ASDE Auto-Speed Detection Enable, ignored in TBI/Serdes mode + // CTRL.RFCE respond to reception of flow control packets. + // Set by Auto-negotiation if negotiation is enabled. + // CTRL.TFCE Ethernet controller transmits flow control packets + // based on the receive FIFO fullness, or when triggered. + // Set by Auto-negotiation if negotiation is enabled. + // CTRL.ILOS Invert Loss-of-Signal, reserved on some devices. + // set to 0. + // CTRL.SPEED Speed if FRCSPD is set, ignored otherwise. + // CTRL.VME Enable VLAN Tag removal and processing. + + // STATUS.FD Reflects the value of CTRL.FD as above. + // STATUS.LU Reflects internal link status + // STATUS.SPEED Speed status bits reflect speed resolved from ASD function. + /* + For the 82541xx and 82547GI/EI, configure the LED behavior through LEDCTRL. + TODO + */ + + // FCAH and FCAL should contain the flow control Ethernet address. + // 01:80:C2:00:00:01 and ethertype 0x8808 + Write32(EM_MAIN_REG_FCAH, 0x0100); + Write32(EM_MAIN_REG_FCAL, 0x00c28001); + Write32(EM_MAIN_REG_FCT, 0x8808); + Write32(EM_MAIN_REG_FCTTV, 0); + + // Clear all statistical counters. + for ( uint32_t x = 0; x < 256; x += 4 ) + Read32(EM_STAT_REG_CRCERRS + x); + + // Setup the descriptor tables, + Write32(EM_MAIN_REG_RCTL, 0); + Write32(EM_MAIN_REG_TCTL, 0); + rx_tail = 0; + rx_prochead = 0; + tx_tail = 0; + tx_prochead = 0; + rx_count = rdesc_alloc.size / sizeof(struct rx_desc); + tx_count = tdesc_alloc.size / sizeof(struct tx_desc_tcpdata); + rdesc = (struct rx_desc*) rdesc_alloc.from; + tdesc = (struct tx_desc_tcpdata*) tdesc_alloc.from; + if ( !rpackets ) + rpackets = new Ref[rx_count]; + if ( !tpackets ) + tpackets = new Ref[tx_count]; + if ( !rpackets || !tpackets ) + return Log("error: Failed to allocate packets: %m"), false; + Write32(EM_MAIN_REG_RDLEN, rdesc_alloc.size); + Write32(EM_MAIN_REG_RDH, 0); + Write32(EM_MAIN_REG_RDT, 0); + Write32(EM_MAIN_REG_RDBAL, (uint64_t) rdesc_alloc.phys & 0xffffffff); + Write32(EM_MAIN_REG_RDBAH, (uint64_t) rdesc_alloc.phys >> 32); + Write32(EM_MAIN_REG_RADV, 0); + Write32(EM_MAIN_REG_RSRPD, 0); + + Write32(EM_MAIN_REG_TXDCTL, + EM_MAIN_REG_TXDCTL_WTHRESH(1) | EM_MAIN_REG_TXDCTL_GRAN); + uint32_t tipg = Read32(EM_MAIN_REG_TIPG); + // TODO: Is programming TIPG needed? + tipg = EM_MAIN_REG_TIPG_IPGT(10) | + EM_MAIN_REG_TIPG_IPGR1(4) | + EM_MAIN_REG_TIPG_IPGR2(6); + tipg = EM_MAIN_REG_TIPG_IPGT(8) | /* For 82567 */ + EM_MAIN_REG_TIPG_IPGR1(8) | + EM_MAIN_REG_TIPG_IPGR2(7); // Was 0x902008 on my laptop + Write32(EM_MAIN_REG_TIPG, tipg); + Write32(EM_MAIN_REG_TDLEN, tdesc_alloc.size); + Write32(EM_MAIN_REG_TDH, 0); + Write32(EM_MAIN_REG_TDT, 0); + Write32(EM_MAIN_REG_TDBAL, (uint64_t) tdesc_alloc.phys & 0xffffffff); + Write32(EM_MAIN_REG_TDBAH, (uint64_t) tdesc_alloc.phys >> 32); + + for ( size_t i = 0; i < RECEIVE_PACKET_COUNT; i++ ) + { + Ref buf = GetPacket(); + if ( !buf ) + { + if ( !i ) + return Log("error: Failed to allocate packets: %m"), false; + break; + } + if ( !AddReceiveDescriptor(buf) ) + break; + } + + // Enable Receive and Transmit. + Write32(EM_MAIN_REG_RCTL, EM_MAIN_REG_RCTL_EN | EM_MAIN_REG_RCTL_SBP | + EM_MAIN_REG_RCTL_MPE | EM_MAIN_REG_RCTL_BAM | EM_MAIN_REG_RCTL_SECRC); + Write32(EM_MAIN_REG_TCTL, EM_MAIN_REG_TCTL_EN | EM_MAIN_REG_TCTL_PSP | + EM_MAIN_REG_TCTL_CT(15) | EM_MAIN_REG_TCTL_COLD(64) | + EM_MAIN_REG_TCTL_RTLC /**/| EM_MAIN_REG_TCTL_RESERVED1); + + // Check if the link is already up since it might not send an interrupt. + status = Read32(EM_MAIN_REG_STATUS); + { + ScopedLock lock(&cfg_lock); + if ( status & EM_MAIN_REG_STATUS_LU ) + ifstatus.flags |= IF_STATUS_FLAGS_UP; + else + ifstatus.flags &= ~IF_STATUS_FLAGS_UP; + kthread_cond_broadcast(&cfg_cond); + poll_channel.Signal(PollEventStatus()); + } + + RegisterInterrupts(); + PCI::EnableInterruptLine(devaddr); + // Reset all the interrupt status (set all interrupts). + Write32(EM_MAIN_REG_IMS, UINT32_MAX); + // Disable all interrupts. + Write32(EM_MAIN_REG_IMC, UINT32_MAX); + // Enable relevant interrupts. + Write32(EM_MAIN_REG_IMS, understood_interrupts); + + return true; +} + +bool EM::Initialize() +{ + pcibar_t mmio_bar = PCI::GetBAR(devaddr, 0); + if ( mmio_bar.size() < 128*1024 ) + { + Log("error: Register area is too small"); + return errno = EINVAL, false; + } + if ( !MapPCIBAR(&mmio_alloc, mmio_bar, Memory::PAT_UC) ) + { + Log("error: Registers could not be mapped: %m"); + return false; + } + mmio_base = (volatile uint8_t*) mmio_alloc.from; + if ( !AllocateAndMapPage(&rdesc_alloc, PAGE_USAGE_DRIVER, Memory::PAT_UC) ) + { + Log("error: Failed to map descriptor page"); + return false; + } + if ( !AllocateAndMapPage(&tdesc_alloc, PAGE_USAGE_DRIVER, Memory::PAT_UC) ) + { + FreeAllocatedAndMappedPage(&rdesc_alloc); + Log("error: Failed to map descriptor page"); + return false; + } + return Reset(); +} + +struct Search +{ + const char* devpath; + Ref dev; + size_t number; +}; + +static bool InitializeDevice(uint32_t devaddr, + const pciid_t* /*id*/, + const pcitype_t* /*type*/, + void* u, + void* info) +{ + Search* search = (Search*) u; + int features = *(const int*) info; + EM* em = new EM(devaddr, search->number++, features); + if ( !em ) + return true; + if ( !em->Initialize() ) + return true; + if ( !RegisterNetworkInterface(em, search->dev) ) + PanicF("%s: Failed to register as network interface", em->ifinfo.name); + return true; +} + +void Init(const char* devpath, Ref dev) +{ + Search search = { .devpath = devpath, .dev = dev, .number = 0 }; + pcifind_t* filters = new pcifind_t[device_table_count]; + if ( !filters ) + Panic("em: Failed to allocate PCI filters"); + for ( size_t i = 0; i < device_table_count; i++ ) + { + const int* features = &feature_table[device_table[i].feature_index]; + uint32_t device_id = device_table[i].device_id; + filters[i] = pcifind_t((void*) features, PCI_VENDOR_INTEL, device_id, + 0x02, 0x00, 0x00); + } + PCI::Search(InitializeDevice, &search, filters, device_table_count); + delete[] filters; +} + +} // namespace EM +} // namespace Sortix diff --git a/kernel/net/em/em.h b/kernel/net/em/em.h new file mode 100644 index 00000000..c2537bcb --- /dev/null +++ b/kernel/net/em/em.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2015 Meisaka Yukara. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * net/em/em.h + * 825xx driver. + */ + +#ifndef SORTIX_NET_EM_EM_H +#define SORTIX_NET_EM_EM_H + +#include +#include + +namespace Sortix { +namespace EM { + +void Init(const char* devpath, Ref dev); + +} // namespace EM +} // namespace Sortix + +#endif diff --git a/kernel/net/em/emregs.h b/kernel/net/em/emregs.h new file mode 100644 index 00000000..ce889b08 --- /dev/null +++ b/kernel/net/em/emregs.h @@ -0,0 +1,653 @@ +/* + * Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2015 Meisaka Yukara. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * net/em/emregs.h + * 825xx registers. + */ + +#ifndef SORTIX_NET_EM_EMREGS_H +#define SORTIX_NET_EM_EMREGS_H + +#ifndef PCI_VENDOR_INTEL +#define PCI_VENDOR_INTEL 0x8086 +#endif + +/* device IDs for compatible devices. + * A large bit of this list (but not all of it) + * is based on the list found in the OpenBSD em driver. */ +#define PCI_PRODUCT_INTEL_DH89XXCC_SGMII 0x0438 /* DH89XXCC SGMII */ +#define PCI_PRODUCT_INTEL_DH89XXCC_S 0x043a /* DH89XXCC SerDes */ +#define PCI_PRODUCT_INTEL_DH89XXCC_BPLANE 0x043c /* DH89XXCC Backplane */ +#define PCI_PRODUCT_INTEL_DH89XXCC_SFP 0x0440 /* DH89XXCC SFP */ +#define PCI_PRODUCT_INTEL_82542 0x1000 /* 82542 */ +#define PCI_PRODUCT_INTEL_82543GC_F 0x1001 /* 82543GC */ +#define PCI_PRODUCT_INTEL_82543GC_C 0x1004 /* 82543GC */ +#define PCI_PRODUCT_INTEL_82544EI_C 0x1008 /* 82544EI */ +#define PCI_PRODUCT_INTEL_82544EI_F 0x1009 /* 82544EI */ +#define PCI_PRODUCT_INTEL_82544GC_C 0x100c /* 82544GC */ +#define PCI_PRODUCT_INTEL_82544GC_LOM 0x100d /* 82544GC */ +#define PCI_PRODUCT_INTEL_82540EM_D 0x100e /* 82540EM Desktop */ +#define PCI_PRODUCT_INTEL_82545EM_C 0x100f /* 82545EM */ +#define PCI_PRODUCT_INTEL_82546EB_C 0x1010 /* 82546EB */ +#define PCI_PRODUCT_INTEL_82545EM_F 0x1011 /* 82545EM */ +#define PCI_PRODUCT_INTEL_82546EB_F 0x1012 /* 82546EB */ +#define PCI_PRODUCT_INTEL_82541EI_C 0x1013 /* 82541EI Copper */ +#define PCI_PRODUCT_INTEL_82541ER_LOM 0x1014 /* 82541EI */ +#define PCI_PRODUCT_INTEL_82540EM_M 0x1015 /* 82540EM Mobile */ +#define PCI_PRODUCT_INTEL_82540EP_M 0x1016 /* 82540EP Mobile */ +#define PCI_PRODUCT_INTEL_82540EP_D 0x1017 /* 82540EP Desktop */ +#define PCI_PRODUCT_INTEL_82541EI_M 0x1018 /* 82541EI Mobile */ +#define PCI_PRODUCT_INTEL_82547EI 0x1019 /* 82547EI */ +#define PCI_PRODUCT_INTEL_82547EI_M 0x101a /* 82547EI Mobile */ +#define PCI_PRODUCT_INTEL_82546EB_CQ 0x101d /* 82546EB */ +#define PCI_PRODUCT_INTEL_82540EP_LP 0x101e /* 82540EP */ +#define PCI_PRODUCT_INTEL_82545GM_C 0x1026 /* 82545GM */ +#define PCI_PRODUCT_INTEL_82545GM_F 0x1027 /* 82545GM */ +#define PCI_PRODUCT_INTEL_82545GM_S 0x1028 /* 82545GM */ +#define PCI_PRODUCT_INTEL_ICH8_IGP_M_AMT 0x1049 /* ICH8 IGP M AMT */ +#define PCI_PRODUCT_INTEL_ICH8_IGP_AMT 0x104a /* ICH8 IGP AMT */ +#define PCI_PRODUCT_INTEL_ICH8_IGP_C 0x104b /* ICH8 IGP C */ +#define PCI_PRODUCT_INTEL_ICH8_IFE 0x104c /* ICH8 IFE */ +#define PCI_PRODUCT_INTEL_ICH8_IGP_M 0x104d /* ICH8 IGP M */ +#define PCI_PRODUCT_INTEL_82571EB_C 0x105e /* 82571EB */ +#define PCI_PRODUCT_INTEL_82571EB_F 0x105f /* 82571EB */ +#define PCI_PRODUCT_INTEL_82571EB_S 0x1060 /* 82571EB */ +#define PCI_PRODUCT_INTEL_82547GI 0x1075 /* 82547GI */ +#define PCI_PRODUCT_INTEL_82541GI_C 0x1076 /* 82541GI Copper */ +#define PCI_PRODUCT_INTEL_82541GI_M 0x1077 /* 82541GI Mobile */ +#define PCI_PRODUCT_INTEL_82541ER_C 0x1078 /* 82541ER Copper */ +#define PCI_PRODUCT_INTEL_82546GB_C 0x1079 /* 82546GB */ +#define PCI_PRODUCT_INTEL_82546GB_F 0x107a /* 82546GB */ +#define PCI_PRODUCT_INTEL_82546GB_S 0x107b /* 82546GB */ +#define PCI_PRODUCT_INTEL_82541GI_LF 0x107c /* 82541GI */ +#define PCI_PRODUCT_INTEL_82572EI_C 0x107d /* 82572EI */ +#define PCI_PRODUCT_INTEL_82572EI_F 0x107e /* 82572EI */ +#define PCI_PRODUCT_INTEL_82572EI_S 0x107f /* 82572EI */ +#define PCI_PRODUCT_INTEL_82546GB_PCIE 0x108a /* 82546GB */ +#define PCI_PRODUCT_INTEL_82573E 0x108b /* 82573E */ +#define PCI_PRODUCT_INTEL_82573E_IAMT 0x108c /* 82573E */ +#define PCI_PRODUCT_INTEL_82573E_IDE 0x108d /* 82573E IDE */ +#define PCI_PRODUCT_INTEL_82573E_KCS 0x108e /* 82573E KCS */ +#define PCI_PRODUCT_INTEL_82573E_SERIAL 0x108f /* 82573E Serial */ +#define PCI_PRODUCT_INTEL_80003ES2LAN_CD 0x1096 /* 80003ES2 */ +#define PCI_PRODUCT_INTEL_80003ES2LAN_SD 0x1098 /* 80003ES2 */ +#define PCI_PRODUCT_INTEL_82546GB_CQ 0x1099 /* 82546GB */ +#define PCI_PRODUCT_INTEL_82573L 0x109a /* 82573L */ +#define PCI_PRODUCT_INTEL_82546GB_2 0x109b /* 82546GB */ +#define PCI_PRODUCT_INTEL_82571EB_AT 0x10a0 /* 82571EB */ +#define PCI_PRODUCT_INTEL_82571EB_AF 0x10a1 /* 82571EB */ +#define PCI_PRODUCT_INTEL_82571EB_CQ 0x10a4 /* 82571EB */ +#define PCI_PRODUCT_INTEL_82571EB_FQ 0x10a5 /* 82571EB */ +#define PCI_PRODUCT_INTEL_82575EB_C 0x10a7 /* 82575EB */ +#define PCI_PRODUCT_INTEL_82575EB_S 0x10a9 /* 82575EB */ +#define PCI_PRODUCT_INTEL_82573L_PL_1 0x10b0 /* 82573L */ +#define PCI_PRODUCT_INTEL_82573V_PM 0x10b2 /* 82573V */ +#define PCI_PRODUCT_INTEL_82573E_PM 0x10b3 /* 82573E */ +#define PCI_PRODUCT_INTEL_82573L_PL_2 0x10b4 /* 82573L */ +#define PCI_PRODUCT_INTEL_82546GB_CQ_K 0x10b5 /* 82546GB */ +#define PCI_PRODUCT_INTEL_82572EI 0x10b9 /* 82572EI */ +#define PCI_PRODUCT_INTEL_80003ES2LAN_C 0x10ba /* 80003ES2 */ +#define PCI_PRODUCT_INTEL_80003ES2LAN_S 0x10bb /* 80003ES2 */ +#define PCI_PRODUCT_INTEL_82571EB_CQ_LP 0x10bc /* 82571EB */ +#define PCI_PRODUCT_INTEL_ICH9_IGP_AMT 0x10bd /* ICH9 IGP AMT */ +#define PCI_PRODUCT_INTEL_ICH9_IGP_M 0x10bf /* ICH9 IGP M */ +#define PCI_PRODUCT_INTEL_ICH9_IFE 0x10c0 /* ICH9 IFE */ +#define PCI_PRODUCT_INTEL_ICH9_IFE_G 0x10c2 /* ICH9 IFE G */ +#define PCI_PRODUCT_INTEL_ICH9_IFE_GT 0x10c3 /* ICH9 IFE GT */ +#define PCI_PRODUCT_INTEL_ICH8_IFE_GT 0x10c4 /* ICH8 IFE GT */ +#define PCI_PRODUCT_INTEL_ICH8_IFE_G 0x10c5 /* ICH8 IFE G */ +#define PCI_PRODUCT_INTEL_82576 0x10c9 /* 82576 */ +#define PCI_PRODUCT_INTEL_ICH9_IGP_M_V 0x10cb /* ICH9 IGP M V */ +#define PCI_PRODUCT_INTEL_ICH10_R_BM_LM 0x10cc /* ICH10 R BM LM */ +#define PCI_PRODUCT_INTEL_ICH10_R_BM_LF 0x10cd /* ICH10 R BM LF */ +#define PCI_PRODUCT_INTEL_ICH10_R_BM_V 0x10ce /* ICH10 R BM V */ +#define PCI_PRODUCT_INTEL_82574L 0x10d3 /* 82574L */ +#define PCI_PRODUCT_INTEL_82571PT_CQ 0x10d5 /* 82571PT */ +#define PCI_PRODUCT_INTEL_82575GB_CQ 0x10d6 /* 82575GB */ +#define PCI_PRODUCT_INTEL_82571EB_SD 0x10d9 /* 82571EB */ +#define PCI_PRODUCT_INTEL_82571EB_SQ 0x10da /* 82571EB */ +#define PCI_PRODUCT_INTEL_ICH10_D_BM_LM 0x10de /* ICH10 D BM LM */ +#define PCI_PRODUCT_INTEL_ICH10_D_BM_LF 0x10df /* ICH10 D BM LF */ +#define PCI_PRODUCT_INTEL_82575GB_QP_PM 0x10e2 /* 82575GB */ +#define PCI_PRODUCT_INTEL_ICH9_BM 0x10e5 /* ICH9 BM */ +#define PCI_PRODUCT_INTEL_82576_F 0x10e6 /* 82576 */ +#define PCI_PRODUCT_INTEL_82576_S 0x10e7 /* 82576 */ +#define PCI_PRODUCT_INTEL_82576_CQ 0x10e8 /* 82576 */ +#define PCI_PRODUCT_INTEL_82577LM 0x10ea /* 82577LM */ +#define PCI_PRODUCT_INTEL_82577LC 0x10eb /* 82577LC */ +#define PCI_PRODUCT_INTEL_82578DM 0x10ef /* 82578DM */ +#define PCI_PRODUCT_INTEL_82578DC 0x10f0 /* 82578DC */ +#define PCI_PRODUCT_INTEL_ICH9_IGP_M_AMT 0x10f5 /* ICH9 IGP M AMT */ +#define PCI_PRODUCT_INTEL_82574LA 0x10f6 /* 82574L */ +#define PCI_PRODUCT_INTEL_82544EI_A4 0x1107 /* 82544EI-A4 */ +#define PCI_PRODUCT_INTEL_82544GC_A4 0x1112 /* 82544EI-A4 */ +#define PCI_PRODUCT_INTEL_ICH8_82567V_3 0x1501 /* ICH8 82567V-3 */ +#define PCI_PRODUCT_INTEL_82579LM 0x1502 /* 82579LM */ +#define PCI_PRODUCT_INTEL_82579V 0x1503 /* 82579V */ +#define PCI_PRODUCT_INTEL_82576_NS 0x150a /* 82576NS */ +#define PCI_PRODUCT_INTEL_82583V 0x150c /* 82583V */ +#define PCI_PRODUCT_INTEL_82576_SQ 0x150d /* 82576 SerDes QP */ +#define PCI_PRODUCT_INTEL_82580_C 0x150e /* 82580 */ +#define PCI_PRODUCT_INTEL_82580_F 0x150f /* 82580 */ +#define PCI_PRODUCT_INTEL_82580_S 0x1510 /* 82580 */ +#define PCI_PRODUCT_INTEL_82580_SGMII 0x1511 /* 82580 */ +#define PCI_PRODUCT_INTEL_82580_CD 0x1516 /* 82580 */ +#define PCI_PRODUCT_INTEL_82576_NS_S 0x1518 /* 82576NS */ +#define PCI_PRODUCT_INTEL_I350_C 0x1521 /* I350 */ +#define PCI_PRODUCT_INTEL_I350_F 0x1522 /* I350 Fiber */ +#define PCI_PRODUCT_INTEL_I350_S 0x1523 /* I350 SerDes */ +#define PCI_PRODUCT_INTEL_I350_SGMII 0x1524 /* I350 SGMII */ +#define PCI_PRODUCT_INTEL_82576_CQ_ET2 0x1526 /* 82576 */ +#define PCI_PRODUCT_INTEL_82580_FQ 0x1527 /* 82580 QF */ +#define PCI_PRODUCT_INTEL_I210_C 0x1533 /* I210 */ +#define PCI_PRODUCT_INTEL_I210_F 0x1536 /* I210 Fiber */ +#define PCI_PRODUCT_INTEL_I210_S 0x1537 /* I210 SerDes */ +#define PCI_PRODUCT_INTEL_I210_SGMII 0x1538 /* I210 SGMII */ +#define PCI_PRODUCT_INTEL_I211_C 0x1539 /* I211 */ +#define PCI_PRODUCT_INTEL_I217_LM 0x153a /* I217-LM */ +#define PCI_PRODUCT_INTEL_I217_V 0x153b /* I217-V */ +#define PCI_PRODUCT_INTEL_I218_V 0x1559 /* I218-V */ +#define PCI_PRODUCT_INTEL_I218_LM 0x155a /* I218-LM */ +#define PCI_PRODUCT_INTEL_I210_C_NF 0x157b /* I210 */ +#define PCI_PRODUCT_INTEL_I210_S_NF 0x157c /* I210 SerDes */ +#define PCI_PRODUCT_INTEL_I218_LM_2 0x15a0 /* I218-LM */ +#define PCI_PRODUCT_INTEL_I218_V_2 0x15a1 /* I218-V */ +#define PCI_PRODUCT_INTEL_I218_LM_3 0x15a2 /* I218-LM */ +#define PCI_PRODUCT_INTEL_I218_V_3 0x15a3 /* I218-V */ +#define PCI_PRODUCT_INTEL_ICH9_IGP_C 0x294c /* ICH9 IGP C */ +#define PCI_PRODUCT_INTEL_EP80579_LAN_1 0x5040 /* EP80579 LAN */ +#define PCI_PRODUCT_INTEL_EP80579_LAN_4 0x5041 /* EP80579 LAN */ +#define PCI_PRODUCT_INTEL_EP80579_LAN_2 0x5044 /* EP80579 LAN */ +#define PCI_PRODUCT_INTEL_EP80579_LAN_5 0x5045 /* EP80579 LAN */ +#define PCI_PRODUCT_INTEL_EP80579_LAN_3 0x5048 /* EP80579 LAN */ +#define PCI_PRODUCT_INTEL_EP80579_LAN_6 0x5049 /* EP80579 LAN */ + +/* Device Control */ +#define EM_MAIN_REG_CTRL 0x0000 +/* Full-Duplex */ +#define EM_MAIN_REG_CTRL_FD (1U << 0) +/* GIO Master Disable (PCIe chipsets only) */ +#define EM_MAIN_REG_CTRL_GIOMD (1U << 2) +/* Link Reset (not applicable to the 82540EP/EM, 82541xx, or 82547GI/EI) */ +#define EM_MAIN_REG_CTRL_LRST (1U << 3) +/* Auto-Speed Detection Enable */ +#define EM_MAIN_REG_CTRL_ASDE (1U << 5) +/* Set Link Up */ +#define EM_MAIN_REG_CTRL_SLU (1U << 6) +/* Invert Loss-of-Signal (LOS) */ +#define EM_MAIN_REG_CTRL_ILOS (1U << 7) +/* Invert Loss-of-Signal (LOS) */ +#define EM_MAIN_REG_CTRL_SPEED_MASK (3U << 8) +#define EM_MAIN_REG_CTRL_SPEED_10MBS (0U << 8) +#define EM_MAIN_REG_CTRL_SPEED_100MBS (1U << 8) +#define EM_MAIN_REG_CTRL_SPEED_1000MBS (2U << 8) +/* Force Speed */ +#define EM_MAIN_REG_CTRL_FRCSPD (1U << 11) +/* Force Duplex */ +#define EM_MAIN_REG_CTRL_FRCDPLX (1U << 12) +/* SDP0 Data Value */ +#define EM_MAIN_REG_CTRL_SDP0_DATA (1U << 18) +/* SDP1 Data Value */ +#define EM_MAIN_REG_CTRL_SDP1_DATA (1U << 19) +/* D3Cold Wakeup Capability Advertisement Enable (not applicable to the 82541ER) */ +#define EM_MAIN_REG_CTRL_ADVD3WUC (1U << 20) +/* PHY Power-Management Enable */ +#define EM_MAIN_REG_CTRL_EN_PHY_PWR_MGMT (1U << 21) +/* SDP0 Pin Directionality */ +#define EM_MAIN_REG_CTRL_SDP0_IODIR (1U << 22) +/* SDP1 Pin Directionality */ +#define EM_MAIN_REG_CTRL_SDP1_IODIR (1U << 23) +/* Device Reset */ +#define EM_MAIN_REG_CTRL_RST (1U << 26) +/* Receive Flow Control Enable */ +#define EM_MAIN_REG_CTRL_RFCE (1U << 27) +/* Transmit Flow Control Enable */ +#define EM_MAIN_REG_CTRL_TFCE (1U << 28) +/* VLAN Mode Enable (not applicable to the 82541ER) */ +#define EM_MAIN_REG_CTRL_VME (1U << 30) +/* PHY Reset */ +#define EM_MAIN_REG_CTRL_PHY_RST (1U << 31) + +/* Device Status */ +#define EM_MAIN_REG_STATUS 0x0008 + +/* Full Duplex */ +#define EM_MAIN_REG_STATUS_FD (1U << 0) +/* Link Up */ +#define EM_MAIN_REG_STATUS_LU (1U << 1) +/* LAN ID, LAN A or LAN B */ +#define EM_MAIN_REG_STATUS_LANID_MASK (3U << 2) +#define EM_MAIN_REG_STATUS_LANID_LANA (0U << 2) +#define EM_MAIN_REG_STATUS_LANID_LANB (1U << 2) +/* TBI/SerDes mode - reserved bit on some devices */ +#define EM_MAIN_REG_STATUS_TBIMODE (1U << 5) +/* Link Speed */ +#define EM_MAIN_REG_STATUS_SPEED_MASK (3U << 6) +#define EM_MAIN_REG_STATUS_SPEED_10MBS (0U << 6) +#define EM_MAIN_REG_STATUS_SPEED_100MBS (1U << 6) +/* There are two definitions for 1000Mb/s */ +#define EM_MAIN_REG_STATUS_SPEED_1000MBS (2U << 6) +#define EM_MAIN_REG_STATUS_SPEED_1000MBSA (3U << 6) + +#define EM_MAIN_REG_STATUS_GIOME (1U << 19) + +/* EEPROM/Flash Control/Data */ +#define EM_MAIN_REG_EECD 0x0010 +#define EM_MAIN_REG_EECD_ARD (1U << 9) + +/* EEPROM Read (not applicable to the 82544GC/EI) */ +#define EM_MAIN_REG_EERD 0x0014 +#define EM_MAIN_REG_EERD_START (1U << 0) +#define EM_MAIN_REG_EERD_DONE (1U << 4) +#define EM_MAIN_REG_EERD_ADDR_SHIFT 8 +#define EM_MAIN_REG_EERD_ADDR_MASK (0xFFU << 8) +#define EM_MAIN_REG_EERD_DATA_SHIFT 16 +#define EM_MAIN_REG_EERD_DATA_MASK (0xFFFFU << 16) + +/* Flash Access (applicable to the 82541xx and 82547GI/EI only) */ +#define EM_MAIN_REG_FLA 0x001C + +/* Extended Device Control */ +#define EM_MAIN_REG_CTRL_EXT 0x0018 +/* General Purpose Interrupt Enables */ +#define EM_MAIN_REG_CTRL_EXT_GPI_EN_MASK (2U << 3) +#define EM_MAIN_REG_CTRL_EXT_GPI_EN_SHIFT 3 +/* PHY Interrupt Value */ +#define EM_MAIN_REG_CTRL_EXT_PHYINT (1U << 5) +/* SDP6[2] Data Value */ +#define EM_MAIN_REG_CTRL_EXT_SDP2_DATA (1U << 6) +#define EM_MAIN_REG_CTRL_EXT_SDP6_DATA (1U << 6) +/* SDP7[3] Data Value */ +#define EM_MAIN_REG_CTRL_EXT_SDP3_DATA (1U << 7) +#define EM_MAIN_REG_CTRL_EXT_SDP7_DATA (1U << 7) +/* SDP6[2] Pin Directionality */ +#define EM_MAIN_REG_CTRL_EXT_SDP2_IODIR (1U << 10) +#define EM_MAIN_REG_CTRL_EXT_SDP6_IODIR (1U << 10) +/* SDP7[3] Pin Directionality */ +#define EM_MAIN_REG_CTRL_EXT_SDP3_IODIR (1U << 11) +#define EM_MAIN_REG_CTRL_EXT_SDP7_IODIR (1U << 11) +/* ASD Check */ +#define EM_MAIN_REG_CTRL_EXT_ASDCHK (1U << 12) +/* EEPROM Reset */ +#define EM_MAIN_REG_CTRL_EXT_EE_RST (1U << 13) +/* Speed Select Bypass */ +#define EM_MAIN_REG_CTRL_EXT_SPD_BYPS (1U << 15) +/* Relaxed Ordering Disabled */ +#define EM_MAIN_REG_CTRL_EXT_RO_DIS (1U << 17) +/* Voltage Regulator Power Down */ +#define EM_MAIN_REG_CTRL_EXT_VREG POWER_DOWN (1U << 21) +/* Link Mode */ +#define EM_MAIN_REG_CTRL_EXT_LINK_MODE_MASK (3U << 21) +#define EM_MAIN_REG_CTRL_EXT_LINK_MODE_COBBER (0U << 21) +#define EM_MAIN_REG_CTRL_EXT_LINK_MODE_FIBER (2U << 21) +#define EM_MAIN_REG_CTRL_EXT_LINK_MODE_TBI (3U << 21) + +/* MDI Control */ +#define EM_MAIN_REG_MDIC 0x0020 +/* Data */ +#define EM_MAIN_REG_MDIC_DATA_MASK (0xFFFFU << 0) +#define EM_MAIN_REG_MDIC_DATA_SHIFT 0 +/* PHY Register Address */ +#define EM_MAIN_REG_MDIC_REGADD_MASK (0x1FU << 16) +#define EM_MAIN_REG_MDIC_REGADD_SHIFT 16 +/* PHY Address */ +#define EM_MAIN_REG_MDIC_PHYADD_MASK (0x1FU << 21) +#define EM_MAIN_REG_MDIC_PHYADD_PHY_ONE_AND_ONLY (0x01U << 21) +/* Opcode */ +#define EM_MAIN_REG_MDIC_OP_MASK (3U << 26) +#define EM_MAIN_REG_MDIC_OP_WRITE (1U << 26) +#define EM_MAIN_REG_MDIC_OP_READ (2U << 26) +/* Ready */ +#define EM_MAIN_REG_MDIC_R (1U << 28) +/* Interrupt Enable */ +#define EM_MAIN_REG_MDIC_I (1U << 29) +/* Error */ +#define EM_MAIN_REG_MDIC_E (1U << 30) + +/* Flow Control Address Low */ +#define EM_MAIN_REG_FCAL 0x0028 + +/* Flow Control Address High */ +#define EM_MAIN_REG_FCAH 0x002C + +/* Flow Control Type */ +#define EM_MAIN_REG_FCT 0x0030 + +/* VLAN EtherType */ +#define EM_MAIN_REG_VET 0x0038 + +/* Flow Control Transmit Timer Value */ +#define EM_MAIN_REG_FCTTV 0x0170 + +/* Transmit Configuration Word (not applicable to the 82540EP/EM, 82541xx and + 82547GI/EI) */ +#define EM_MAIN_REG_TXCW 0x0170 + +/* Receive Configuration Word (not applicable to the 82540EP/EM, 82541xx and + 82547GI/EI) */ +#define EM_MAIN_REG_RXCW 0x0180 + +/* When writing to this register pair, always set/clear in correct order */ +/* Receive Address Low - base address (clear last, set first) */ +#define EM_FILTER_REG_RAL 0x5400 +/* Receive Address High - base address (clear first, set last) */ +#define EM_FILTER_REG_RAH 0x5404 + +#define EM_EEPROM_REG_ETHERNET_ADDR_1 0x00 +#define EM_EEPROM_REG_ETHERNET_ADDR_2 0x01 +#define EM_EEPROM_REG_ETHERNET_ADDR_3 0x02 + +/* PHY Control Register */ +#define EM_PHY_REG_PCTRL 0 +/* Speed Selection (MSB) */ +#define EM_PHY_REG_PCTRL_MSB (1U<<6) +/* Collision Test */ +#define EM_PHY_REG_PCTRL_COLLISION_TEST (1U<<7) +/* Duplex Mode */ +#define EM_PHY_REG_PCTRL_DUPLEX (1U<<8) +/* Restart Auto-Negotiation */ +#define EM_PHY_REG_PCTRL_RAN (1U<<9) +/* Isolate */ +#define EM_PHY_REG_PCTRL_ISOLATE (1U<<10) +/* Power down */ +#define EM_PHY_REG_PCTRL_POWER_DOWN (1U<<11) +/* Auto-Negotiation Enable */ +#define EM_PHY_REG_PCTRL_ANE (1U<<12) +/* Speed Selection (LSB) */ +#define EM_PHY_REG_PCTRL_LSB (1U<<13) +/* Loopback */ +#define EM_PHY_REG_PCTRL_LOOPBACK (1U<<14) +/* Reset */ +#define EM_PHY_REG_PCTRL_RESET (1U<<15) +/* Speed Selection */ +#define EM_PHY_REG_PCTRL_SPEED_MASK (1U<<6 | 1U<<13) +#define EM_PHY_REG_PCTRL_SPEED_10_MBPS (0U<<6 | 0U<<13) +#define EM_PHY_REG_PCTRL_SPEED_100_MBPS (0U<<6 | 1U<<13) + +/* PHY Status Register */ +#define EM_PHY_REG_PSTATUS 1 +/* Extended Capability */ +#define EM_PHY_REG_PSTATUS_EXTENDED_CAPABILITY (1U<<0) +/* Jabber Detect */ +#define EM_PHY_REG_PSTATUS_JABBER_DETECT (1U<<1) +/* Link Status */ +#define EM_PHY_REG_PSTATUS_LINK_STATUS (1U<<2) +/* Auto-Negotiation Ability */ +#define EM_PHY_REG_PSTATUS_ANA (1U<<3) +/* Remote Fault */ +#define EM_PHY_REG_PSTATUS_REMOTE_FAULT (1U<<4) +/* Auto-Negotiation Complete */ +#define EM_PHY_REG_PSTATUS_AN_COMPLETE (1U<<5) +/* MF Preamble Suppression */ +#define EM_PHY_REG_PSTATUS_MF_PREAMBLE_SUPP (1U<<6) +/* Extended Status */ +#define EM_PHY_REG_PSTATUS_EXTENDED_STATUS (1U<<8) +/* 100BASE-T2 Half Duplex (not able to) */ +#define EM_PHY_REG_PSTATUS_HD_100BASE_T2 (1U<<9) +/* 100BASE-T2 Full Duplex (not able to) */ +#define EM_PHY_REG_PSTATUS_FD_100BASE_T2 (1U<<10) +/* 10 Mb/s Half Duplex (able to) */ +#define EM_PHY_REG_PSTATUS_HD_10BASE_T (1U<<11) +/* 10 Mb/s Fill Duplex (able to) */ +#define EM_PHY_REG_PSTATUS_FD_10BASE_T (1U<<12) +/* 100BASE-X Half Duplex (able to) */ +#define EM_PHY_REG_PSTATUS_HD_100BASE_X (1U<<13) +/* 100BASE-X Half Duplex (able to) */ +#define EM_PHY_REG_PSTATUS_FD_100BASE_X (1U<<14) +/* 100BASE-T4 (not able to) */ +#define EM_PHY_REG_PSTATUS_100BASE_T4 (1U<<15) + +/* PHY Specific Status Register */ +#define EM_PHY_REG_PSSTAT 17 +/* Jabber (real time) */ +#define EM_PHY_REG_PSSTAT_JABBER (1U<<0) +/* Polarity (real time) */ +#define EM_PHY_REG_PSSTAT_POLARITY (1U<<1) +/* Receive Pause Enable */ +#define EM_REG_PHY_PSSTAT_RECEIVE_PAUSE_ENABLE (1U<<2) +/* Transmit Pause Enabled */ +#define EM_REG_PHY_PSSTAT_TRANSMIT_PAUSE_ENABLE (1U<<3) +/* Energy Detect Status */ +#define EM_REG_PHY_PSSTAT_ENERGY_DETECT_STATUS (1U<<4) +/* Downshift Status */ +#define EM_REG_PHY_PSSTAT_DOWNSHIFT (1U<<5) +/* MDI Crossover Status */ +#define EM_PHY_REG_PSSTAT_MDI_CROSSOVER_STATUS (1U<<6) +/* Cable Length (100/1000 modes only) */ +#define EM_PHY_REG_PSSTAT_CABLE_LENGTH_MASK (7U<<7) +#define EM_PHY_REG_PSSTAT_CABLE_LENGTH_0M_50M (0U<<7) +#define EM_PHY_REG_PSSTAT_CABLE_LENGTH_50M_80M (1U<<7) +#define EM_PHY_REG_PSSTAT_CABLE_LENGTH_80M_110M (2U<<7) +#define EM_PHY_REG_PSSTAT_CABLE_LENGTH_110M_140M (3U<<7) +#define EM_PHY_REG_PSSTAT_CABLE_LENGTH_140M_INFM (4U<<7) +/* Link (real time) */ +#define EM_PHY_REG_PSSTAT_LINK (1U<<10) +/* Speed and Duplex Resolved */ +#define EM_PHY_REG_PSSTAT_SPEED_DUPLEX_RESOLVED (1U<<11) +/* Page Received */ +#define EM_PHY_REG_PSSTAT_PAGE_RECEIVED (1U<<12) +/* Duplex */ +#define EM_PHY_REG_PSSTAT_DUPLEX (1U<<13) +/* Speed */ +#define EM_PHY_REG_PSSTAT_SPEED_MASK (3U<<14) +#define EM_PHY_REG_PSSTAT_SPEED_10MBS (0U<<14) +#define EM_PHY_REG_PSSTAT_SPEED_100MBS (1U<<14) +#define EM_PHY_REG_PSSTAT_SPEED_1000MBS (2U<<14) + +#define EM_MAIN_REG_ICR 0x00c0U +#define EM_MAIN_REG_ITR 0x00c4U +#define EM_MAIN_REG_ICS 0x00c8U +#define EM_MAIN_REG_IMS 0x00d0U +#define EM_MAIN_REG_IMC 0x00d8U + +/* Receive Control */ +#define EM_MAIN_REG_RCTL 0x0100U +/* Enable */ +#define EM_MAIN_REG_RCTL_EN (1U << 1) +/* Store Bad packets (with CRC errors), used by some protocols */ +#define EM_MAIN_REG_RCTL_SBP (1U << 2) +/* Unicast Promiscuous */ +#define EM_MAIN_REG_RCTL_UPE (1U << 3) +/* Multicast Promiscuous */ +#define EM_MAIN_REG_RCTL_MPE (1U << 4) +/* Long Packet ( >1522 bytes) Enable */ +#define EM_MAIN_REG_RCTL_LPE (1U << 5) +/* Loopback Mode (mask and enable value) */ +#define EM_MAIN_REG_RCTL_LBM (3U << 6) +/* Receive Descriptor Minimum Threshold size (fraction) */ +#define EM_MAIN_REG_RCTL_RDMTS_MASK (3U << 8) +#define EM_MAIN_REG_RCTL_RDMTS_1_2 (0U << 8) +#define EM_MAIN_REG_RCTL_RDMTS_1_4 (1U << 8) +#define EM_MAIN_REG_RCTL_RDMTS_1_8 (2U << 8) +/* Multicast Offset (for filtering) */ +#define EM_MAIN_REG_RCTL_MO_MASK (3U << 12) +#define EM_MAIN_REG_RCTL_MO_BIT36 (0U << 12) +#define EM_MAIN_REG_RCTL_MO_BIT35 (1U << 12) +#define EM_MAIN_REG_RCTL_MO_BIT34 (2U << 12) +#define EM_MAIN_REG_RCTL_MO_BIT32 (3U << 12) +/* Broadcast Accept Mode (enable) */ +#define EM_MAIN_REG_RCTL_BAM (1U << 15) +/* Receive Buffer Size (including extended) */ +#define EM_MAIN_REG_RCTL_BSIZE_MASK ((1U << 25)|(3U << 16)) +#define EM_MAIN_REG_RCTL_BSIZE_2048 (0U << 16) +#define EM_MAIN_REG_RCTL_BSIZE_1024 (1U << 16) +#define EM_MAIN_REG_RCTL_BSIZE_512 (2U << 16) +#define EM_MAIN_REG_RCTL_BSIZE_256 (3U << 16) +#define EM_MAIN_REG_RCTL_BSIZE_16384 ((1U << 25)|(1U << 16)) +#define EM_MAIN_REG_RCTL_BSIZE_8192 ((1U << 25)|(2U << 16)) +#define EM_MAIN_REG_RCTL_BSIZE_4096 ((1U << 25)|(3U << 16)) +/* VLAN Filter Enable */ +#define EM_MAIN_REG_RCTL_VFE (1U << 18) +/* Canonical Form Indicator Enable (for VLANs) */ +#define EM_MAIN_REG_RCTL_CFIEN (1U << 19) +/* Canonical Form Indicator (for VLANs) */ +#define EM_MAIN_REG_RCTL_CFI (1U << 20) +/* Discard Pause Frames (MAC Flow control) */ +#define EM_MAIN_REG_RCTL_DPF (1U << 22) +/* Pass MAC Control Frames */ +#define EM_MAIN_REG_RCTL_PMCF (1U << 23) +/* Strip CRC */ +#define EM_MAIN_REG_RCTL_SECRC (1U << 26) + +/* Receive Descriptor Base Low */ +#define EM_MAIN_REG_RDBAL 0x2800U +/* Receive Descriptor Base High */ +#define EM_MAIN_REG_RDBAH 0x2804U +/* Receive Descriptor Length (128 byte align minimum) */ +#define EM_MAIN_REG_RDLEN 0x2808U +/* Receive Descriptor Head (Reading is not reliable) */ +#define EM_MAIN_REG_RDH 0x2810U +/* Receive Descriptor Tail */ +#define EM_MAIN_REG_RDT 0x2818U +/* Receive Delay Delay Timer (should usually be disabled) */ +#define EM_MAIN_REG_RDTR 0x2820U +/* Receive Interrupt Absolute Delay Timer (should usually be disabled) */ +#define EM_MAIN_REG_RADV 0x282cU +/* Receive Small Packet Detect Interrupt (size in bytes) */ +#define EM_MAIN_REG_RSRPD 0x2c00U + +/* Transmit Control */ +#define EM_MAIN_REG_TCTL 0x0400U +#define EM_MAIN_REG_TCTL_EN (1U << 1) +#define EM_MAIN_REG_TCTL_PSP (1U << 3) +#define EM_MAIN_REG_TCTL_CT(v) (((v) & 0xff) << 4) +#define EM_MAIN_REG_TCTL_COLD(v) (((v) & 0x3ff) << 12) +#define EM_MAIN_REG_TCTL_SWXOFF (1U << 22) +#define EM_MAIN_REG_TCTL_RTLC (1U << 24) +#define EM_MAIN_REG_TCTL_RESERVED1 (1U << 28) /* TODO: Seems to be set as one */ +#define EM_MAIN_REG_TCTL_RRTHRESH(v) (((v) & 0x3) << 29) + +/* Transmit IPG */ +#define EM_MAIN_REG_TIPG 0x0410U +#define EM_MAIN_REG_TIPG_IPGT(v) (((v) & 0x3FF) << 0) +#define EM_MAIN_REG_TIPG_IPGT_MASK (0x1FFU << 0) +#define EM_MAIN_REG_TIPG_IPGR1(v) (((v) & 0x3FF) << 10) +#define EM_MAIN_REG_TIPG_IPGR1_MASK (0x3FFU << 10) +#define EM_MAIN_REG_TIPG_IPGR2(v) (((v) & 0x3FF) << 20) +#define EM_MAIN_REG_TIPG_IPGR2_MASK (0x3FFU << 20) + +/* TX DMA Control */ +#define EM_MAIN_REG_TXDMAC 0x3000U +/* Transmit Descriptor Base Low and High */ +#define EM_MAIN_REG_TDBAL 0x3800U +#define EM_MAIN_REG_TDBAH 0x3804U +/* Transmit Descriptors Length */ +#define EM_MAIN_REG_TDLEN 0x3808U +/* Transmit Descriptor Head (Reading is not reliable) */ +#define EM_MAIN_REG_TDH 0x3810U +/* Transmit Descriptor Tail */ +#define EM_MAIN_REG_TDT 0x3818U + +/* Transmit Descriptor Control */ +#define EM_MAIN_REG_TXDCTL 0x3828U +#define EM_MAIN_REG_TXDCTL_PTHRESH(v) (((v) & 0x3FU) << 0) +#define EM_MAIN_REG_TXDCTL_HTHRESH(v) (((v) & 0x3FU) << 8) +#define EM_MAIN_REG_TXDCTL_WTHRESH(v) (((v) & 0x3FU) << 16) +#define EM_MAIN_REG_TXDCTL_GRAN (1U << 24) +#define EM_MAIN_REG_TXDCTL_LWTHRESH(v) (((v) & 0x7FU) << 25) + +#define EM_INTERRUPT_TXDW (1U << 0) +#define EM_INTERRUPT_TXQE (1U << 1) +#define EM_INTERRUPT_LSC (1U << 2) +#define EM_INTERRUPT_RXSEQ (1U << 3) +#define EM_INTERRUPT_RXDMT0 (1U << 4) +#define EM_INTERRUPT_RXO (1U << 6) +#define EM_INTERRUPT_RXT0 (1U << 7) +#define EM_INTERRUPT_MDAC (1U << 9) +#define EM_INTERRUPT_RXCFG (1U <<10) +#define EM_INTERRUPT_PHYINT (1U <<12) +#define EM_INTERRUPT_GPI2 (1U <<13) +#define EM_INTERRUPT_GPI3 (1U <<14) +#define EM_INTERRUPT_TXD_LOW (1U <<15) +#define EM_INTERRUPT_SRPD (1U <<16) + +#define EM_STAT_REG_CRCERRS 0x4000 +#define EM_STAT_REG_ALGNERRC 0x4004 +#define EM_STAT_REG_SYMERRS 0x4008 +#define EM_STAT_REG_RXERRC 0x400c +#define EM_STAT_REG_MPC 0x4010 +#define EM_STAT_REG_SCC 0x4014 +#define EM_STAT_REG_ECOL 0x4018 +#define EM_STAT_REG_MCC 0x401c +#define EM_STAT_REG_LATECOL 0x4020 +#define EM_STAT_REG_COLC 0x4028 +#define EM_STAT_REG_DC 0x4030 +#define EM_STAT_REG_TNCRS 0x4034 +#define EM_STAT_REG_SEC 0x4038 +#define EM_STAT_REG_CEXTERR 0x403c +#define EM_STAT_REG_RLEC 0x4040 +#define EM_STAT_REG_XONRXC 0x4048 +#define EM_STAT_REG_XONTXC 0x404c +#define EM_STAT_REG_XOFFRXC 0x4050 +#define EM_STAT_REG_XOFFTXC 0x4054 +#define EM_STAT_REG_FCRUC 0x4058 +#define EM_STAT_REG_PRC64 0x405c +#define EM_STAT_REG_PRC127 0x4060 +#define EM_STAT_REG_PRC255 0x4064 +#define EM_STAT_REG_PRC511 0x4068 +#define EM_STAT_REG_PRC1023 0x406c +#define EM_STAT_REG_PRC1522 0x4070 +#define EM_STAT_REG_GPRC 0x4074 +#define EM_STAT_REG_BPRC 0x4078 +#define EM_STAT_REG_MPRC 0x407c +#define EM_STAT_REG_GPTC 0x4080 +#define EM_STAT_REG_GORCL 0x4088 +#define EM_STAT_REG_GORCH 0x408c +#define EM_STAT_REG_GOTCL 0x4090 +#define EM_STAT_REG_GOTCH 0x4094 +#define EM_STAT_REG_RNBC 0x40a0 +#define EM_STAT_REG_RUC 0x40a4 +#define EM_STAT_REG_RFC 0x40a8 +#define EM_STAT_REG_ROC 0x40ac +#define EM_STAT_REG_RJC 0x40b0 +#define EM_STAT_REG_MGTPRC 0x40b4 +#define EM_STAT_REG_MGTPDC 0x40b8 +#define EM_STAT_REG_MGTPTC 0x40bc +#define EM_STAT_REG_TORL 0x40c0 +#define EM_STAT_REG_TORH 0x40c4 +#define EM_STAT_REG_TOTL 0x40c8 +#define EM_STAT_REG_TOTH 0x40cc +#define EM_STAT_REG_TPR 0x40d0 +#define EM_STAT_REG_TPT 0x40d4 +#define EM_STAT_REG_PTC64 0x40d8 +#define EM_STAT_REG_PTC127 0x40dc +#define EM_STAT_REG_PTC255 0x40e0 +#define EM_STAT_REG_PTC511 0x40e4 +#define EM_STAT_REG_PTC1023 0x40e8 +#define EM_STAT_REG_PTC1522 0x40ec +#define EM_STAT_REG_MPTC 0x40f0 +#define EM_STAT_REG_BPTC 0x40f4 +#define EM_STAT_REG_TSCTC 0x40f8 +#define EM_STAT_REG_TSCTFC 0x40fc + +#define EM_RDESC_STATUS_DD (1U << 0) +#define EM_RDESC_STATUS_EOP (1U << 1) +#define EM_RDESC_STATUS_VP (1U << 3) +#define EM_RDESC_STATUS_UDPCS (1U << 4) +#define EM_RDESC_STATUS_TDPCS (1U << 5) +#define EM_RDESC_STATUS_IPCS (1U << 6) +#define EM_RDESC_STATUS_PIF (1U << 7) + +#define EM_TDESC_TYPE_TCPDATA ((1U << 20) | (1U << 29)) +#define EM_TDESC_CMD_EOP (1U << 24) +#define EM_TDESC_CMD_IFCS (1U << 25) +#define EM_TDESC_CMD_TSE (1U << 26) +#define EM_TDESC_CMD_RS (1U << 27) +#define EM_TDESC_CMD_VLE (1U << 30) +#define EM_TDESC_CMD_IDE (1U << 31) +#define EM_TDESC_LENGTH(l) ((l) & 0xfffff) + +#endif diff --git a/share/man/man4/em.4 b/share/man/man4/em.4 new file mode 100644 index 00000000..f83a9434 --- /dev/null +++ b/share/man/man4/em.4 @@ -0,0 +1,26 @@ +.Dd Jan 9, 2023 +.Dt EM 4 +.Os +.Sh NAME +.Nm em +.Nd intel 825xx ethernet controller driver +.Sh SYNOPSIS +.Nm /dev/em Ns Ar X +.Sh DESCRIPTION +.Nm +is a network interface driver for the Intel 825xx family of ethernet +controllers. +.Sh SEE ALSO +.Xr if 4 , +.Xr kernel 7 +.Sh BUGS +The +.Nm +driver is incomplete and does not work yet on all the ethernet controllers in +the Intel 825xx family. +The 82579LM fails to establish the link when the PHY does not respond to MDIO +requests. +The driver can be disabled using the +.Fl \-disable-em +.Xr kernel 7 +option. diff --git a/share/man/man7/kernel.7 b/share/man/man7/kernel.7 index 3a2356fe..8af8d67d 100644 --- a/share/man/man7/kernel.7 +++ b/share/man/man7/kernel.7 @@ -6,6 +6,8 @@ .Nd operating system kernel .Sh SYNOPSIS .Pa /boot/sortix.bin +.Op Fl \-disable-em +.Op Fl \-enable-em .Op Fl \-disable-network-drivers .Op Fl \-enable-network-drivers .Op Fl \-no-random-seed @@ -48,9 +50,17 @@ otherwise. .Pp The options are as follows: .Bl -tag -width "12345678" +.It Fl \-disable-em +Don't initialize the +.Xr em 4 +driver. .It Fl \-disable-network-drivers Don't initialize any network drivers. This option ensures the booted system is not networked. +.It Fl \-enable-em +Do initialize the +.Xr em 4 +driver. .It Fl \-enable-network-drivers Do initialize network drivers. This is the default behavior. diff --git a/share/man/man7/user-guide.7 b/share/man/man7/user-guide.7 index 79b58250..fcf4357c 100644 --- a/share/man/man7/user-guide.7 +++ b/share/man/man7/user-guide.7 @@ -123,7 +123,13 @@ You can make a compatible filesystem with: Internet Protocol version 4 .Pq Xr ip 4 networking is available if you have a supported network interface -.Pq Xr if 4 . +.Pq Xr if 4 : +.Pp +.Bl -bullet -compact +.It +.Xr em 4 - +Intel 825xx +.El .Pp The Internet Protocol version 6 .Xr ( ip6 4 )