mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
f291054465
Co-authored-by: Meisaka Yukara <Meisaka.Yukara@gmail.com>
1036 lines
31 KiB
C++
1036 lines
31 KiB
C++
/*
|
|
* 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 <assert.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <timespec.h>
|
|
|
|
#include <sortix/stat.h>
|
|
|
|
#include <sortix/kernel/addralloc.h>
|
|
#include <sortix/kernel/descriptor.h>
|
|
#include <sortix/kernel/inode.h>
|
|
#include <sortix/kernel/interrupt.h>
|
|
#include <sortix/kernel/ioctx.h>
|
|
#include <sortix/kernel/log.h>
|
|
#include <sortix/kernel/memorymanagement.h>
|
|
#include <sortix/kernel/if.h>
|
|
#include <sortix/kernel/packet.h>
|
|
#include <sortix/kernel/panic.h>
|
|
#include <sortix/kernel/pci.h>
|
|
#include <sortix/kernel/pci-mmio.h>
|
|
#include <sortix/kernel/refcount.h>
|
|
#include <sortix/kernel/time.h>
|
|
|
|
#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<Packet> 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<Packet> pkt);
|
|
bool AddTransmitDescriptor(Ref<Packet> 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<Packet>* rpackets;
|
|
Ref<Packet>* tpackets;
|
|
Ref<Packet> tx_queue_first;
|
|
Ref<Packet> 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<Packet> 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<Packet> 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<Packet> 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<Packet> 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<Packet> 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<Packet> 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<Packet> 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<Packet>[rx_count];
|
|
if ( !tpackets )
|
|
tpackets = new Ref<Packet>[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<Packet> 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<Descriptor> 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<Descriptor> 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
|