2021-04-02 17:53:31 +03:00
/*
* Copyright ( c ) 2021 , Idan Horowitz < idan . horowitz @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/MACAddress.h>
# include <Kernel/Debug.h>
# include <Kernel/IO.h>
# include <Kernel/Net/RTL8168NetworkAdapter.h>
# include <Kernel/PCI/IDs.h>
2021-06-22 18:40:16 +03:00
# include <Kernel/Sections.h>
2021-04-02 17:53:31 +03:00
namespace Kernel {
# define REG_MAC 0x00
# define REG_MAR4 0x0B
# define REG_MAR0 0x0F
# define REG_EEE_LED 0x1B
# define REG_TXADDR 0x20
# define REG_COMMAND 0x37
# define REG_TXSTART 0x38
# define REG_IMR 0x3C
# define REG_ISR 0x3E
# define REG_TXCFG 0x40
# define REG_RXCFG 0x44
# define REG_MPC 0x4C
# define REG_CFG9346 0x50
# define REG_CONFIG1 0x52
# define REG_CONFIG2 0x53
# define REG_CONFIG3 0x54
# define REG_CONFIG4 0x55
# define REG_CONFIG5 0x56
# define REG_MULTIINTR 0x5C
# define REG_PHYACCESS 0x60
# define REG_CSI_DATA 0x64
# define REG_CSI_ADDR 0x68
# define REG_PHYSTATUS 0x6C
# define REG_PMCH 0x6F
# define REG_ERI_DATA 0x70
# define REG_ERI_ADDR 0x74
# define REG_EPHYACCESS 0x80
# define REG_OCP_DATA 0xB0
# define REG_OCP_ADDR 0xB4
# define REG_GPHY_OCP 0xB8
# define REG_DLLPR 0xD0
# define REG_MCU 0xD3
# define REG_RMS 0xDA
# define REG_CPLUS_COMMAND 0xE0
# define REG_INT_MOD 0xE2
# define REG_RXADDR 0xE4
# define REG_MTPS 0xEC
# define REG_MISC 0xF0
# define REG_MISC2 0xF2
# define REG_IBCR0 0xF8
# define REG_IBCR2 0xF9
# define REG_IBISR0 0xFB
# define COMMAND_TX_ENABLE 0x4
# define COMMAND_RX_ENABLE 0x8
# define COMMAND_RESET 0x10
# define CPLUS_COMMAND_VERIFY_CHECKSUM 0x20
# define CPLUS_COMMAND_VLAN_STRIP 0x40
# define CPLUS_COMMAND_MAC_DBGO_SEL 0x1C
# define CPLUS_COMMAND_PACKET_CONTROL_DISABLE 0x80
# define CPLUS_COMMAND_ASF 0x100
# define CPLUS_COMMAND_CXPL_DBG_SEL 0x200
# define CPLUS_COMMAND_FORCE_TXFLOW_ENABLE 0x400
# define CPLUS_COMMAND_FORCE_RXFLOW_ENABLE 0x800
# define CPLUS_COMMAND_FORCE_HALF_DUP 0x1000
# define CPLUS_COMMAND_MAC_DBGO_OE 0x4000
# define CPLUS_COMMAND_ENABLE_BIST 0x8000
# define INT_RXOK 0x1
# define INT_RXERR 0x2
# define INT_TXOK 0x4
# define INT_TXERR 0x8
# define INT_RX_OVERFLOW 0x10
# define INT_LINK_CHANGE 0x20
# define INT_RX_FIFO_OVERFLOW 0x40
# define INT_SYS_ERR 0x8000
# define CFG9346_NONE 0x00
# define CFG9346_EEM0 0x40
# define CFG9346_EEM1 0x80
# define CFG9346_UNLOCK (CFG9346_EEM0 | CFG9346_EEM1)
# define TXCFG_AUTO_FIFO 0x80
# define TXCFG_MAX_DMA_UNLIMITED 0x700
# define TXCFG_EMPTY 0x800
# define TXCFG_IFG011 0x3000000
# define RXCFG_READ_MASK 0x3F
# define RXCFG_APM 0x2
# define RXCFG_AM 0x4
# define RXCFG_AB 0x8
# define RXCFG_MAX_DMA_UNLIMITED 0x700
# define RXCFG_EARLY_OFF_V2 0x800
# define RXCFG_FTH_NONE 0xE000
# define RXCFG_MULTI_ENABLE 0x4000
# define RXCFG_128INT_ENABLE 0x8000
# define CFG2_CLOCK_REQUEST_ENABLE 0x80
# define CFG3_BEACON_ENABLE 0x1
# define CFG3_READY_TO_L23 0x2
# define CFG5_ASPM_ENABLE 0x1
2021-06-04 01:31:00 +03:00
# define CFG5_SPI_ENABLE 0x8
2021-04-02 17:53:31 +03:00
# define PHY_LINK_STATUS 0x2
# define PHY_FLAG 0x80000000
# define PHY_REG_BMCR 0x00
# define PHY_REG_ANAR 0x4
# define PHY_REG_GBCR 0x9
# define CSI_FLAG 0x80000000
# define CSI_BYTE_ENABLE 0xF000
# define CSI_FUNC_NIC 0x20000
# define CSI_FUNC_NIC2 0x10000
# define CSI_ACCESS_1 0x17000000
# define CSI_ACCESS_2 0x27000000
# define EPHY_FLAG 0x80000000
# define ERI_FLAG 0x80000000
# define ERI_MASK_0001 0x1000
# define ERI_MASK_0011 0x3000
# define ERI_MASK_0100 0x4000
# define ERI_MASK_0101 0x5000
# define ERI_MASK_1111 0xF000
# define ERI_EXGMAC 0x0
# define OCP_FLAG 0x80000000
# define OCP_STANDARD_PHY_BASE 0xa400
# define TXSTART_START 0x40
# define BMCR_RESET 0x8000
# define BMCR_SPEED_0 0x2000
# define BMCR_AUTO_NEGOTIATE 0x1000
# define BMCR_RESTART_AUTO_NEGOTIATE 0x200
# define BMCR_DUPLEX 0x100
# define BMCR_SPEED_1 0x40
# define ADVERTISE_10_HALF 0x20
# define ADVERTISE_10_FULL 0x40
# define ADVERTISE_100_HALF 0x80
# define ADVERTISE_100_FULL 0x100
# define ADVERTISE_PAUSE_CAP 0x400
# define ADVERTISE_PAUSE_ASYM 0x800
# define ADVERTISE_1000_HALF 0x100
# define ADVERTISE_1000_FULL 0x200
# define DLLPR_PFM_ENABLE 0x40
# define DLLPR_TX_10M_PS_ENABLE 0x80
# define MCU_LINK_LIST_READY 0x2
# define MCU_RX_EMPTY 0x10
# define MCU_TX_EMPTY 0x20
# define MCU_NOW_IS_OOB 0x80
# define MTPS_JUMBO 0x3F
# define MISC_RXDV_GATE_ENABLE 0x80000
2021-06-04 01:31:00 +03:00
# define MISC_PWM_ENABLE 0x400000
2021-04-02 17:53:31 +03:00
# define MISC2_PFM_D3COLD_ENABLE 0x40
# define TX_BUFFER_SIZE 0x1FF8
# define RX_BUFFER_SIZE 0x1FF8 // FIXME: this should be increased (0x3FFF)
UNMAP_AFTER_INIT RefPtr < RTL8168NetworkAdapter > RTL8168NetworkAdapter : : try_to_initialize ( PCI : : Address address )
{
auto id = PCI : : get_id ( address ) ;
if ( id . vendor_id ! = ( u16 ) PCIVendorID : : Realtek )
return { } ;
if ( id . device_id ! = 0x8168 )
return { } ;
u8 irq = PCI : : get_interrupt_line ( address ) ;
return adopt_ref_if_nonnull ( new RTL8168NetworkAdapter ( address , irq ) ) ;
}
UNMAP_AFTER_INIT RTL8168NetworkAdapter : : RTL8168NetworkAdapter ( PCI : : Address address , u8 irq )
: PCI : : Device ( address , irq )
, m_io_base ( PCI : : get_BAR0 ( pci_address ( ) ) & ~ 1 )
, m_rx_descriptors_region ( MM . allocate_contiguous_kernel_region ( page_round_up ( sizeof ( TXDescriptor ) * ( number_of_rx_descriptors + 1 ) ) , " RTL8168 RX " , Region : : Access : : Read | Region : : Access : : Write ) )
, m_tx_descriptors_region ( MM . allocate_contiguous_kernel_region ( page_round_up ( sizeof ( RXDescriptor ) * ( number_of_tx_descriptors + 1 ) ) , " RTL8168 TX " , Region : : Access : : Read | Region : : Access : : Write ) )
{
set_interface_name ( address ) ;
dmesgln ( " RTL8168: Found @ {} " , pci_address ( ) ) ;
dmesgln ( " RTL8168: I/O port base: {} " , m_io_base ) ;
identify_chip_version ( ) ;
dmesgln ( " RTL8168: Version detected - {} ({}{}) " , possible_device_name ( ) , ( u8 ) m_version , m_version_uncertain ? " ? " : " " ) ;
2021-06-04 01:31:00 +03:00
if ( m_version = = ChipVersion : : Unknown | | ( m_version > = ChipVersion : : Version4 & & m_version < = ChipVersion : : Version16 ) | | m_version > = ChipVersion : : Version18 ) {
2021-04-02 17:53:31 +03:00
dmesgln ( " RTL8168: Aborting initialization! Support for your chip version ({}) is not implemented yet, please open a GH issue and include this message. " , ( u8 ) m_version ) ;
return ; // Each ChipVersion requires a specific implementation of configure_phy and hardware_quirks
}
initialize ( ) ;
startup ( ) ;
}
void RTL8168NetworkAdapter : : initialize ( )
{
// set initial REG_RXCFG
auto rx_config = RXCFG_MAX_DMA_UNLIMITED ;
if ( m_version < = ChipVersion : : Version3 ) {
rx_config | = RXCFG_FTH_NONE ;
} else if ( m_version < = ChipVersion : : Version8 | | ( m_version > = ChipVersion : : Version16 & & m_version < = ChipVersion : : Version19 ) ) {
rx_config | = RXCFG_128INT_ENABLE | RXCFG_MULTI_ENABLE ;
} else if ( m_version > = ChipVersion : : Version21 ) {
rx_config | = RXCFG_128INT_ENABLE | RXCFG_MULTI_ENABLE | RXCFG_EARLY_OFF_V2 ;
} else {
rx_config | = RXCFG_128INT_ENABLE ;
}
out32 ( REG_RXCFG , rx_config ) ;
// disable interrupts
out16 ( REG_IMR , 0 ) ;
// initialize hardware
if ( m_version = = ChipVersion : : Version23 | | m_version = = ChipVersion : : Version27 | | m_version = = ChipVersion : : Version28 ) {
// disable CMAC
out8 ( REG_IBCR2 , in8 ( REG_IBCR2 ) & ~ 1 ) ;
while ( ( in32 ( REG_IBISR0 ) & 0x2 ) ! = 0 )
;
out8 ( REG_IBISR0 , in8 ( REG_IBISR0 ) | 0x20 ) ;
out8 ( REG_IBCR0 , in8 ( REG_IBCR0 ) & ~ 1 ) ;
}
if ( m_version > = ChipVersion : : Version21 ) {
m_ocp_base_address = OCP_STANDARD_PHY_BASE ;
// enable RXDV gate
out32 ( REG_MISC , in32 ( REG_MISC ) | MISC_RXDV_GATE_ENABLE ) ;
while ( ( in32 ( REG_TXCFG ) & TXCFG_EMPTY ) = = 0 )
;
while ( ( in32 ( REG_MCU ) & ( MCU_RX_EMPTY | MCU_TX_EMPTY ) ) = = 0 )
;
out8 ( REG_COMMAND , in8 ( REG_COMMAND ) & ~ ( COMMAND_RX_ENABLE | COMMAND_TX_ENABLE ) ) ;
out8 ( REG_MCU , in8 ( REG_MCU ) & ~ MCU_NOW_IS_OOB ) ;
// vendor magic values ???
auto data = ocp_in ( 0xe8de ) ;
data & = ~ ( 1 < < 14 ) ;
ocp_out ( 0xe8de , data ) ;
while ( ( in32 ( REG_MCU ) & MCU_LINK_LIST_READY ) = = 0 )
;
// vendor magic values ???
data = ocp_in ( 0xe8de ) ;
data | = ( 1 < < 15 ) ;
ocp_out ( 0xe8de , data ) ;
while ( ( in32 ( REG_MCU ) & MCU_LINK_LIST_READY ) = = 0 )
;
}
// software reset
reset ( ) ;
// clear interrupts
out16 ( REG_ISR , 0xffff ) ;
enable_bus_mastering ( pci_address ( ) ) ;
read_mac_address ( ) ;
dmesgln ( " RTL8168: MAC address: {} " , mac_address ( ) . to_string ( ) ) ;
// notify about driver start
if ( m_version > = ChipVersion : : Version11 & & m_version < = ChipVersion : : Version13 ) {
// if check_dash
// notify
TODO ( ) ;
} else if ( m_version = = ChipVersion : : Version23 | | m_version = = ChipVersion : : Version27 | | m_version = = ChipVersion : : Version28 ) {
// if check_dash
// notify
TODO ( ) ;
}
}
void RTL8168NetworkAdapter : : startup ( )
{
// initialize descriptors
initialize_rx_descriptors ( ) ;
initialize_tx_descriptors ( ) ;
// register irq
enable_irq ( ) ;
// version specific phy configuration
configure_phy ( ) ;
// software reset phy
phy_out ( PHY_REG_BMCR , phy_in ( PHY_REG_BMCR ) | BMCR_RESET ) ;
while ( ( phy_in ( PHY_REG_BMCR ) & BMCR_RESET ) ! = 0 )
;
set_phy_speed ( ) ;
// set C+ command
auto cplus_command = in16 ( REG_CPLUS_COMMAND ) | CPLUS_COMMAND_VERIFY_CHECKSUM | CPLUS_COMMAND_VLAN_STRIP ;
out16 ( REG_CPLUS_COMMAND , cplus_command ) ;
in16 ( REG_CPLUS_COMMAND ) ; // C+ Command barrier
// power up phy
if ( m_version > = ChipVersion : : Version9 & & m_version < = ChipVersion : : Version15 ) {
out8 ( REG_PMCH , in8 ( REG_PMCH ) | 0x80 ) ;
} else if ( m_version > = ChipVersion : : Version26 ) {
out8 ( REG_PMCH , in8 ( REG_PMCH ) | 0xC0 ) ;
} else if ( m_version > = ChipVersion : : Version21 & & m_version < = ChipVersion : : Version23 ) {
out8 ( REG_PMCH , in8 ( REG_PMCH ) | 0xC0 ) ;
// vendor magic values ???
eri_update ( 0x1a8 , ERI_MASK_1111 , 0xfc000000 , 0 , ERI_EXGMAC ) ;
}
// wakeup phy (more vendor magic values)
phy_out ( 0x1F , 0 ) ;
if ( m_version < = ChipVersion : : Version13 ) {
phy_out ( 0x0E , 0 ) ;
}
phy_out ( PHY_REG_BMCR , BMCR_AUTO_NEGOTIATE ) ; // send known good phy write (acts as a phy barrier)
start_hardware ( ) ;
// re-enable interrupts
auto enabled_interrupts = INT_RXOK | INT_RXERR | INT_TXOK | INT_TXERR | INT_RX_OVERFLOW | INT_LINK_CHANGE | INT_SYS_ERR ;
if ( m_version = = ChipVersion : : Version1 ) {
enabled_interrupts | = INT_RX_FIFO_OVERFLOW ;
enabled_interrupts & = ~ INT_RX_OVERFLOW ;
}
out16 ( REG_IMR , enabled_interrupts ) ;
// update link status
m_link_up = ( in8 ( REG_PHYSTATUS ) & PHY_LINK_STATUS ) ! = 0 ;
}
void RTL8168NetworkAdapter : : configure_phy ( )
{
// this method sets a bunch of magic vendor values to the phy configuration registers based on the hardware version
switch ( m_version ) {
case ChipVersion : : Version1 : {
configure_phy_b_1 ( ) ;
return ;
}
case ChipVersion : : Version2 :
case ChipVersion : : Version3 : {
configure_phy_b_2 ( ) ;
return ;
}
case ChipVersion : : Version4 :
TODO ( ) ;
case ChipVersion : : Version5 :
TODO ( ) ;
case ChipVersion : : Version6 :
TODO ( ) ;
case ChipVersion : : Version7 :
TODO ( ) ;
case ChipVersion : : Version8 :
TODO ( ) ;
case ChipVersion : : Version9 :
TODO ( ) ;
case ChipVersion : : Version10 :
TODO ( ) ;
case ChipVersion : : Version11 :
TODO ( ) ;
case ChipVersion : : Version12 :
TODO ( ) ;
case ChipVersion : : Version13 :
TODO ( ) ;
case ChipVersion : : Version14 :
TODO ( ) ;
case ChipVersion : : Version15 :
TODO ( ) ;
case ChipVersion : : Version16 :
TODO ( ) ;
2021-06-04 01:31:00 +03:00
case ChipVersion : : Version17 : {
configure_phy_e_2 ( ) ;
return ;
}
2021-04-02 17:53:31 +03:00
case ChipVersion : : Version18 :
TODO ( ) ;
case ChipVersion : : Version19 :
TODO ( ) ;
case ChipVersion : : Version20 :
TODO ( ) ;
case ChipVersion : : Version21 :
TODO ( ) ;
case ChipVersion : : Version22 :
TODO ( ) ;
case ChipVersion : : Version23 :
TODO ( ) ;
case ChipVersion : : Version24 :
TODO ( ) ;
case ChipVersion : : Version25 :
TODO ( ) ;
case ChipVersion : : Version26 :
TODO ( ) ;
case ChipVersion : : Version27 :
TODO ( ) ;
case ChipVersion : : Version28 :
TODO ( ) ;
case ChipVersion : : Version29 : {
configure_phy_h_1 ( ) ;
return ;
}
case ChipVersion : : Version30 : {
configure_phy_h_2 ( ) ;
return ;
}
default :
VERIFY_NOT_REACHED ( ) ;
}
}
void RTL8168NetworkAdapter : : configure_phy_b_1 ( )
{
constexpr PhyRegister phy_registers [ ] = {
{ 0x10 , 0xf41b } ,
{ 0x1f , 0 }
} ;
phy_out ( 0x1f , 0x1 ) ;
phy_out ( 0x16 , 1 < < 0 ) ;
phy_out_batch ( phy_registers , 2 ) ;
}
void RTL8168NetworkAdapter : : configure_phy_b_2 ( )
{
constexpr PhyRegister phy_registers [ ] = {
{ 0x1f , 0x1 } ,
{ 0x10 , 0xf41b } ,
{ 0x1f , 0 }
} ;
phy_out_batch ( phy_registers , 3 ) ;
}
2021-06-04 01:31:00 +03:00
void RTL8168NetworkAdapter : : configure_phy_e_2 ( )
{
// FIXME: linux's driver writes a firmware blob to the device at this point, is this needed?
constexpr PhyRegister phy_registers [ ] = {
// Enable delay cap
{ 0x1f , 0x4 } ,
{ 0x1f , 0x7 } ,
{ 0x1e , 0xac } ,
{ 0x18 , 0x6 } ,
{ 0x1f , 0x2 } ,
{ 0x1f , 0 } ,
{ 0x1f , 0 } ,
// Channel estimation fine tune
{ 0x1f , 0x3 } ,
{ 0x9 , 0xa20f } ,
{ 0x1f , 0 } ,
{ 0x1f , 0 } ,
// Green Setting
{ 0x1f , 0x5 } ,
{ 0x5 , 0x8b5b } ,
{ 0x6 , 0x9222 } ,
{ 0x5 , 0x8b6d } ,
{ 0x6 , 0x8000 } ,
{ 0x5 , 0x8b76 } ,
{ 0x6 , 0x8000 } ,
{ 0x1f , 0 } ,
} ;
phy_out_batch ( phy_registers , 19 ) ;
// 4 corner performance improvement
phy_out ( 0x1f , 0x5 ) ;
phy_out ( 0x5 , 0x8b80 ) ;
phy_update ( 0x17 , 0x6 , 0 ) ;
phy_out ( 0x1f , 0 ) ;
// PHY auto speed down
phy_out ( 0x1f , 0x4 ) ;
phy_out ( 0x1f , 0x7 ) ;
phy_out ( 0x1e , 0x2d ) ;
phy_update ( 0x18 , 0x10 , 0 ) ;
phy_out ( 0x1f , 0x2 ) ;
phy_out ( 0x1f , 0 ) ;
phy_update ( 0x14 , 0x8000 , 0 ) ;
// Improve 10M EEE waveform
phy_out ( 0x1f , 0x5 ) ;
phy_out ( 0x5 , 0x8b86 ) ;
phy_update ( 0x6 , 0x1 , 0 ) ;
phy_out ( 0x1f , 0 ) ;
// Improve 2-pair detection performance
phy_out ( 0x1f , 0x5 ) ;
phy_out ( 0x5 , 0x8b85 ) ;
phy_update ( 0x6 , 0x4000 , 0 ) ;
phy_out ( 0x1f , 0 ) ;
// EEE Setting
eri_update ( 0x1b0 , ERI_MASK_1111 , 0 , 0x3 , ERI_EXGMAC ) ;
phy_out ( 0x1f , 0x5 ) ;
phy_out ( 0x5 , 0x8b85 ) ;
phy_update ( 0x6 , 0 , 0x2000 ) ;
phy_out ( 0x1f , 0x4 ) ;
phy_out ( 0x1f , 0x7 ) ;
phy_out ( 0x1e , 0x20 ) ;
phy_update ( 0x15 , 0 , 0x100 ) ;
phy_out ( 0x1f , 0x2 ) ;
phy_out ( 0x1f , 0 ) ;
phy_out ( 0xd , 0x7 ) ;
phy_out ( 0xe , 0x3c ) ;
phy_out ( 0xd , 0x4007 ) ;
phy_out ( 0xe , 0 ) ;
phy_out ( 0xd , 0 ) ;
// Green feature
phy_out ( 0x1f , 0x3 ) ;
phy_update ( 0x19 , 0 , 0x1 ) ;
phy_update ( 0x10 , 0 , 0x400 ) ;
phy_out ( 0x1f , 0 ) ;
// Broken BIOS workaround: feed GigaMAC registers with MAC address.
rar_exgmac_set ( ) ;
}
2021-04-02 17:53:31 +03:00
void RTL8168NetworkAdapter : : configure_phy_h_1 ( )
{
// FIXME: linux's driver writes a firmware blob to the device at this point, is this needed?
// CHN EST parameters adjust - giga master
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x809b ) ;
phy_update ( 0x14 , 0x8000 , 0xf800 ) ;
phy_out ( 0x13 , 0x80a2 ) ;
phy_update ( 0x14 , 0x8000 , 0xff00 ) ;
phy_out ( 0x13 , 0x80a4 ) ;
phy_update ( 0x14 , 0x8500 , 0xff00 ) ;
phy_out ( 0x13 , 0x809c ) ;
phy_update ( 0x14 , 0xbd00 , 0xff00 ) ;
phy_out ( 0x1f , 0 ) ;
// CHN EST parameters adjust - giga slave
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x80ad ) ;
phy_update ( 0x14 , 0x7000 , 0xf800 ) ;
phy_out ( 0x13 , 0x80b4 ) ;
phy_update ( 0x14 , 0x5000 , 0xff00 ) ;
phy_out ( 0x13 , 0x80ac ) ;
phy_update ( 0x14 , 0x4000 , 0xff00 ) ;
phy_out ( 0x1f , 0 ) ;
// CHN EST parameters adjust - fnet
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x808e ) ;
phy_update ( 0x14 , 0x1200 , 0xff00 ) ;
phy_out ( 0x13 , 0x8090 ) ;
phy_update ( 0x14 , 0xe500 , 0xff00 ) ;
phy_out ( 0x13 , 0x8092 ) ;
phy_update ( 0x14 , 0x9f00 , 0xff00 ) ;
phy_out ( 0x1f , 0 ) ;
// enable R-tune & PGA-retune function
u16 dout_tapbin = 0 ;
phy_out ( 0x1f , 0x0a46 ) ;
auto data = phy_in ( 0x13 ) ;
data & = 3 ;
data < < = 2 ;
dout_tapbin | = data ;
data = phy_in ( 0x12 ) ;
data & = 0xc000 ;
data > > = 14 ;
dout_tapbin | = data ;
dout_tapbin = ~ ( dout_tapbin ^ 0x8 ) ;
dout_tapbin < < = 12 ;
dout_tapbin & = 0xf000 ;
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x827a ) ;
phy_update ( 0x14 , dout_tapbin , 0xf000 ) ;
phy_out ( 0x13 , 0x827b ) ;
phy_update ( 0x14 , dout_tapbin , 0xf000 ) ;
phy_out ( 0x13 , 0x827c ) ;
phy_update ( 0x14 , dout_tapbin , 0xf000 ) ;
phy_out ( 0x13 , 0x827d ) ;
phy_update ( 0x14 , dout_tapbin , 0xf000 ) ;
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x811 ) ;
phy_update ( 0x14 , 0x800 , 0 ) ;
phy_out ( 0x1f , 0x0a42 ) ;
phy_update ( 0x16 , 0x2 , 0 ) ;
phy_out ( 0x1f , 0 ) ;
// enable GPHY 10M
phy_out ( 0x1f , 0x0a44 ) ;
phy_update ( 0x11 , 0x800 , 0 ) ;
phy_out ( 0x1f , 0 ) ;
// SAR ADC performance
phy_out ( 0x1f , 0x0bca ) ;
phy_update ( 0x17 , 0x4000 , 0x3000 ) ;
phy_out ( 0x1f , 0 ) ;
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x803f ) ;
phy_update ( 0x14 , 0 , 0x3000 ) ;
phy_out ( 0x13 , 0x8047 ) ;
phy_update ( 0x14 , 0 , 0x3000 ) ;
phy_out ( 0x13 , 0x804f ) ;
phy_update ( 0x14 , 0 , 0x3000 ) ;
phy_out ( 0x13 , 0x8057 ) ;
phy_update ( 0x14 , 0 , 0x3000 ) ;
phy_out ( 0x13 , 0x805f ) ;
phy_update ( 0x14 , 0 , 0x3000 ) ;
phy_out ( 0x13 , 0x8067 ) ;
phy_update ( 0x14 , 0 , 0x3000 ) ;
phy_out ( 0x13 , 0x806f ) ;
phy_update ( 0x14 , 0 , 0x3000 ) ;
phy_out ( 0x1f , 0 ) ;
// disable phy pfm mode
phy_out ( 0x1f , 0x0a44 ) ;
phy_update ( 0x11 , 0 , 0x80 ) ;
phy_out ( 0x1f , 0 ) ;
// Check ALDPS bit, disable it if enabled
phy_out ( 0x1f , 0x0a43 ) ;
if ( phy_in ( 0x10 ) & 0x4 )
phy_update ( 0x10 , 0 , 0x4 ) ;
phy_out ( 0x1f , 0 ) ;
}
void RTL8168NetworkAdapter : : configure_phy_h_2 ( )
{
// FIXME: linux's driver writes a firmware blob to the device at this point, is this needed?
// CHIN EST parameter update
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x808a ) ;
phy_update ( 0x14 , 0x000a , 0x3f ) ;
phy_out ( 0x1f , 0 ) ;
// enable R-tune & PGA-retune function
phy_out ( 0x1f , 0x0a43 ) ;
phy_out ( 0x13 , 0x811 ) ;
phy_update ( 0x14 , 0x800 , 0 ) ;
phy_out ( 0x1f , 0x0a42 ) ;
phy_update ( 0x16 , 0x2 , 0 ) ;
phy_out ( 0x1f , 0 ) ;
// enable GPHY 10M
phy_out ( 0x1f , 0x0a44 ) ;
phy_update ( 0x11 , 0x800 , 0 ) ;
phy_out ( 0x1f , 0 ) ;
ocp_out ( 0xdd02 , 0x807d ) ;
auto data = ocp_in ( 0xdd02 ) ;
u16 ioffset_p3 = ( ( data & 0x80 ) > > 7 ) ;
ioffset_p3 < < = 3 ;
data = ocp_in ( 0xdd00 ) ;
ioffset_p3 | = ( ( data & ( 0xe000 ) ) > > 13 ) ;
u16 ioffset_p2 = ( ( data & ( 0x1e00 ) ) > > 9 ) ;
u16 ioffset_p1 = ( ( data & ( 0x1e0 ) ) > > 5 ) ;
u16 ioffset_p0 = ( ( data & 0x10 ) > > 4 ) ;
ioffset_p0 < < = 3 ;
ioffset_p0 | = ( data & ( 0x7 ) ) ;
data = ( ioffset_p3 < < 12 ) | ( ioffset_p2 < < 8 ) | ( ioffset_p1 < < 4 ) | ( ioffset_p0 ) ;
if ( ( ioffset_p3 ! = 0x0f ) | | ( ioffset_p2 ! = 0x0f ) | | ( ioffset_p1 ! = 0x0f ) | | ( ioffset_p0 ! = 0x0f ) ) {
phy_out ( 0x1f , 0x0bcf ) ;
phy_out ( 0x16 , data ) ;
phy_out ( 0x1f , 0 ) ;
}
// Modify rlen (TX LPF corner frequency) level
phy_out ( 0x1f , 0x0bcd ) ;
data = phy_in ( 0x16 ) ;
data & = 0x000f ;
u16 rlen = 0 ;
if ( data > 3 )
rlen = data - 3 ;
data = rlen | ( rlen < < 4 ) | ( rlen < < 8 ) | ( rlen < < 12 ) ;
phy_out ( 0x17 , data ) ;
phy_out ( 0x1f , 0x0bcd ) ;
phy_out ( 0x1f , 0 ) ;
// disable phy pfm mode
phy_out ( 0x1f , 0x0a44 ) ;
phy_update ( 0x11 , 0 , 0x80 ) ;
phy_out ( 0x1f , 0 ) ;
// Check ALDPS bit, disable it if enabled
phy_out ( 0x1f , 0x0a43 ) ;
if ( phy_in ( 0x10 ) & 0x4 )
phy_update ( 0x10 , 0 , 0x4 ) ;
phy_out ( 0x1f , 0 ) ;
}
2021-06-04 01:31:00 +03:00
void RTL8168NetworkAdapter : : rar_exgmac_set ( )
{
auto mac = mac_address ( ) ;
const u16 w [ ] = {
( u16 ) ( mac [ 0 ] | ( mac [ 1 ] < < 8 ) ) ,
( u16 ) ( mac [ 2 ] | ( mac [ 3 ] < < 8 ) ) ,
( u16 ) ( mac [ 4 ] | ( mac [ 5 ] < < 8 ) ) ,
} ;
const ExgMacRegister exg_mac_registers [ ] = {
{ 0xe0 , ERI_MASK_1111 , ( u32 ) ( w [ 0 ] | ( w [ 1 ] < < 16 ) ) } ,
{ 0xe4 , ERI_MASK_1111 , ( u32 ) w [ 2 ] } ,
{ 0xf0 , ERI_MASK_1111 , ( u32 ) ( w [ 0 ] < < 16 ) } ,
{ 0xf4 , ERI_MASK_1111 , ( u32 ) ( w [ 1 ] | ( w [ 2 ] < < 16 ) ) } ,
} ;
exgmac_out_batch ( exg_mac_registers , 4 ) ;
}
2021-04-02 17:53:31 +03:00
void RTL8168NetworkAdapter : : start_hardware ( )
{
// unlock config registers
out8 ( REG_CFG9346 , CFG9346_UNLOCK ) ;
// configure the maximum transmit packet size
out16 ( REG_MTPS , MTPS_JUMBO ) ;
// configure the maximum receive packet size
out16 ( REG_RMS , RX_BUFFER_SIZE ) ;
auto cplus_command = in16 ( REG_CPLUS_COMMAND ) ;
cplus_command | = CPLUS_COMMAND_PACKET_CONTROL_DISABLE ;
// undocumented magic value???
cplus_command | = 0x1 ;
out16 ( REG_CPLUS_COMMAND , cplus_command ) ;
// setup interrupt moderation, magic from vendor (Linux Driver uses 0x5151, *BSD Driver uses 0x5100, RTL Driver use 0x5f51???)
out16 ( REG_INT_MOD , 0x5151 ) ;
// point to tx descriptors
out64 ( REG_TXADDR , m_tx_descriptors_region - > physical_page ( 0 ) - > paddr ( ) . get ( ) ) ;
// point to rx descriptors
out64 ( REG_RXADDR , m_rx_descriptors_region - > physical_page ( 0 ) - > paddr ( ) . get ( ) ) ;
// configure tx: use the maximum dma transfer size, default interframe gap time.
out32 ( REG_TXCFG , TXCFG_IFG011 | TXCFG_MAX_DMA_UNLIMITED ) ;
// version specific quirks and tweaks
hardware_quirks ( ) ;
in8 ( REG_IMR ) ; // known good read (acts as a barrier)
// relock config registers
out8 ( REG_CFG9346 , CFG9346_NONE ) ;
// enable rx/tx
out8 ( REG_COMMAND , COMMAND_RX_ENABLE | COMMAND_TX_ENABLE ) ;
// turn on all multicast
out32 ( REG_MAR0 , 0xFFFFFFFF ) ;
out32 ( REG_MAR4 , 0xFFFFFFFF ) ;
// configure rx mode: accept physical (MAC) match, multicast, and broadcast
out32 ( REG_RXCFG , ( in32 ( REG_RXCFG ) & ~ RXCFG_READ_MASK ) | RXCFG_APM | RXCFG_AM | RXCFG_AB ) ;
// disable early-rx interrupts
out16 ( REG_MULTIINTR , in16 ( REG_MULTIINTR ) & 0xF000 ) ;
}
void RTL8168NetworkAdapter : : hardware_quirks ( )
{
switch ( m_version ) {
case ChipVersion : : Version1 :
hardware_quirks_b_1 ( ) ;
return ;
case ChipVersion : : Version2 :
case ChipVersion : : Version3 :
hardware_quirks_b_2 ( ) ;
return ;
case ChipVersion : : Version4 :
TODO ( ) ;
case ChipVersion : : Version5 :
TODO ( ) ;
case ChipVersion : : Version6 :
TODO ( ) ;
case ChipVersion : : Version7 :
TODO ( ) ;
case ChipVersion : : Version8 :
TODO ( ) ;
case ChipVersion : : Version9 :
TODO ( ) ;
case ChipVersion : : Version10 :
TODO ( ) ;
case ChipVersion : : Version11 :
TODO ( ) ;
case ChipVersion : : Version12 :
TODO ( ) ;
case ChipVersion : : Version13 :
TODO ( ) ;
case ChipVersion : : Version14 :
TODO ( ) ;
case ChipVersion : : Version15 :
TODO ( ) ;
case ChipVersion : : Version16 :
TODO ( ) ;
case ChipVersion : : Version17 :
2021-06-04 01:31:00 +03:00
hardware_quirks_e_2 ( ) ;
return ;
2021-04-02 17:53:31 +03:00
case ChipVersion : : Version18 :
TODO ( ) ;
case ChipVersion : : Version19 :
TODO ( ) ;
case ChipVersion : : Version20 :
TODO ( ) ;
case ChipVersion : : Version21 :
TODO ( ) ;
case ChipVersion : : Version22 :
TODO ( ) ;
case ChipVersion : : Version23 :
TODO ( ) ;
case ChipVersion : : Version24 :
TODO ( ) ;
case ChipVersion : : Version25 :
TODO ( ) ;
case ChipVersion : : Version26 :
TODO ( ) ;
case ChipVersion : : Version27 :
TODO ( ) ;
case ChipVersion : : Version28 :
TODO ( ) ;
case ChipVersion : : Version29 :
case ChipVersion : : Version30 :
hardware_quirks_h ( ) ;
return ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
void RTL8168NetworkAdapter : : hardware_quirks_b_1 ( )
{
// disable checked reserved bits
out8 ( REG_CONFIG3 , in8 ( REG_CONFIG3 ) & ~ CFG3_BEACON_ENABLE ) ;
constexpr u16 version1_cplus_quirks = CPLUS_COMMAND_ENABLE_BIST | CPLUS_COMMAND_MAC_DBGO_OE | CPLUS_COMMAND_FORCE_HALF_DUP | CPLUS_COMMAND_FORCE_RXFLOW_ENABLE | CPLUS_COMMAND_FORCE_TXFLOW_ENABLE | CPLUS_COMMAND_CXPL_DBG_SEL | CPLUS_COMMAND_ASF | CPLUS_COMMAND_PACKET_CONTROL_DISABLE | CPLUS_COMMAND_MAC_DBGO_SEL ;
out16 ( REG_CPLUS_COMMAND , in16 ( REG_CPLUS_COMMAND ) & ~ version1_cplus_quirks ) ;
}
void RTL8168NetworkAdapter : : hardware_quirks_b_2 ( )
{
hardware_quirks_b_1 ( ) ;
// configure the maximum transmit packet size (again)
out16 ( REG_MTPS , MTPS_JUMBO ) ;
// disable checked reserved bits
out8 ( REG_CONFIG4 , in8 ( REG_CONFIG4 ) & ~ 1 ) ;
}
2021-06-04 01:31:00 +03:00
void RTL8168NetworkAdapter : : hardware_quirks_e_2 ( )
{
constexpr EPhyUpdate ephy_info [ ] = {
{ 0x9 , 0 , 0x80 } ,
{ 0x19 , 0 , 0x224 } ,
} ;
csi_enable ( CSI_ACCESS_1 ) ;
extended_phy_initialize ( ephy_info , 2 ) ;
// FIXME: MTU performance tweak
eri_out ( 0xc0 , ERI_MASK_0011 , 0 , ERI_EXGMAC ) ;
eri_out ( 0xb8 , ERI_MASK_0011 , 0 , ERI_EXGMAC ) ;
eri_out ( 0xc8 , ERI_MASK_1111 , 0x100002 , ERI_EXGMAC ) ;
eri_out ( 0xe8 , ERI_MASK_1111 , 0x100006 , ERI_EXGMAC ) ;
eri_out ( 0xcc , ERI_MASK_1111 , 0x50 , ERI_EXGMAC ) ;
eri_out ( 0xd0 , ERI_MASK_1111 , 0x7ff0060 , ERI_EXGMAC ) ;
eri_update ( 0x1b0 , ERI_MASK_0001 , 0x10 , 0 , ERI_EXGMAC ) ;
eri_update ( 0xd4 , ERI_MASK_0011 , 0xc00 , 0xff00 , ERI_EXGMAC ) ;
// Set early TX
out8 ( REG_MTPS , 0x27 ) ;
// FIXME: Disable PCIe clock request
// enable tx auto fifo
out32 ( REG_TXCFG , in32 ( REG_TXCFG ) | TXCFG_AUTO_FIFO ) ;
out8 ( REG_MCU , in8 ( REG_MCU ) & ~ MCU_NOW_IS_OOB ) ;
// Set EEE LED frequency
out8 ( REG_EEE_LED , in8 ( REG_EEE_LED ) & ~ 0x7 ) ;
out8 ( REG_DLLPR , in8 ( REG_DLLPR ) | DLLPR_PFM_ENABLE ) ;
out32 ( REG_MISC , in32 ( REG_MISC ) | MISC_PWM_ENABLE ) ;
out8 ( REG_CONFIG5 , in8 ( REG_CONFIG5 ) & ~ CFG5_SPI_ENABLE ) ;
}
2021-04-02 17:53:31 +03:00
void RTL8168NetworkAdapter : : hardware_quirks_h ( )
{
// disable aspm and clock request before accessing extended phy
out8 ( REG_CONFIG2 , in8 ( REG_CONFIG2 ) & ~ CFG2_CLOCK_REQUEST_ENABLE ) ;
out8 ( REG_CONFIG5 , in8 ( REG_CONFIG5 ) & ~ CFG5_ASPM_ENABLE ) ;
// initialize extended phy
constexpr EPhyUpdate ephy_info [ ] = {
{ 0x1e , 0x800 , 0x1 } ,
{ 0x1d , 0 , 0x800 } ,
{ 0x5 , 0xffff , 0x2089 } ,
{ 0x6 , 0xffff , 0x5881 } ,
{ 0x4 , 0xffff , 0x154a } ,
{ 0x1 , 0xffff , 0x68b }
} ;
extended_phy_initialize ( ephy_info , 6 ) ;
// enable tx auto fifo
out32 ( REG_TXCFG , in32 ( REG_TXCFG ) | TXCFG_AUTO_FIFO ) ;
// vendor magic values ???
eri_out ( 0xC8 , ERI_MASK_0101 , 0x80002 , ERI_EXGMAC ) ;
eri_out ( 0xCC , ERI_MASK_0001 , 0x38 , ERI_EXGMAC ) ;
eri_out ( 0xD0 , ERI_MASK_0001 , 0x48 , ERI_EXGMAC ) ;
eri_out ( 0xE8 , ERI_MASK_1111 , 0x100006 , ERI_EXGMAC ) ;
csi_enable ( CSI_ACCESS_1 ) ;
// vendor magic values ???
eri_update ( 0xDC , ERI_MASK_0001 , 0x0 , 0x1 , ERI_EXGMAC ) ;
eri_update ( 0xDC , ERI_MASK_0001 , 0x1 , 0x0 , ERI_EXGMAC ) ;
eri_update ( 0xDC , ERI_MASK_1111 , 0x10 , 0x0 , ERI_EXGMAC ) ;
eri_update ( 0xD4 , ERI_MASK_1111 , 0x1F00 , 0x0 , ERI_EXGMAC ) ;
eri_out ( 0x5F0 , ERI_MASK_0011 , 0x4F87 , ERI_EXGMAC ) ;
// disable rxdv gate
out32 ( REG_MISC , in32 ( REG_MISC ) & ~ MISC_RXDV_GATE_ENABLE ) ;
// set early TX
out8 ( REG_MTPS , 0x27 ) ;
// vendor magic values ???
eri_out ( 0xC0 , ERI_MASK_0011 , 0 , ERI_EXGMAC ) ;
eri_out ( 0xB8 , ERI_MASK_0011 , 0 , ERI_EXGMAC ) ;
// Set EEE LED frequency
out8 ( REG_EEE_LED , in8 ( REG_EEE_LED ) & ~ 0x7 ) ;
out8 ( REG_DLLPR , in8 ( REG_DLLPR ) & ~ DLLPR_PFM_ENABLE ) ;
out8 ( REG_MISC2 , in8 ( REG_MISC2 ) & ~ MISC2_PFM_D3COLD_ENABLE ) ;
out8 ( REG_DLLPR , in8 ( REG_DLLPR ) & ~ DLLPR_TX_10M_PS_ENABLE ) ;
// vendor magic values ???
eri_update ( 0x1B0 , ERI_MASK_0011 , 0 , 0x1000 , ERI_EXGMAC ) ;
// disable l2l3 state
out8 ( REG_CONFIG3 , in8 ( REG_CONFIG3 ) & ~ CFG3_READY_TO_L23 ) ;
// blackmagic code taken from linux's r8169
phy_out ( 0x1F , 0x0C42 ) ;
auto rg_saw_count = ( phy_in ( 0x13 ) & 0x3FFF ) ;
phy_out ( 0x1F , 0 ) ;
if ( rg_saw_count > 0 ) {
u16 sw_count_1ms_ini = 16000000 / rg_saw_count ;
sw_count_1ms_ini & = 0x0fff ;
u32 data = ocp_in ( 0xd412 ) ;
data & = ~ 0x0fff ;
data | = sw_count_1ms_ini ;
ocp_out ( 0xd412 , data ) ;
}
u32 data = ocp_in ( 0xe056 ) ;
data & = ~ 0xf0 ;
data | = 0x70 ;
ocp_out ( 0xe056 , data ) ;
data = ocp_in ( 0xe052 ) ;
data & = ~ 0x6000 ;
data | = 0x8008 ;
ocp_out ( 0xe052 , data ) ;
data = ocp_in ( 0xe0d6 ) ;
data & = ~ 0x1ff ;
data | = 0x17f ;
ocp_out ( 0xe0d6 , data ) ;
data = ocp_in ( 0xd420 ) ;
data & = ~ 0x0fff ;
data | = 0x47f ;
ocp_out ( 0xd420 , data ) ;
ocp_out ( 0xe63e , 0x1 ) ;
ocp_out ( 0xe63e , 0 ) ;
ocp_out ( 0xc094 , 0 ) ;
ocp_out ( 0xc09e , 0 ) ;
}
void RTL8168NetworkAdapter : : set_phy_speed ( )
{
// wakeup phy
phy_out ( 0x1F , 0 ) ;
// advertise all available features to get best connection possible
auto auto_negotiation_advertisement = phy_in ( PHY_REG_ANAR ) ;
auto_negotiation_advertisement | = ADVERTISE_10_HALF ; // 10 mbit half duplex
auto_negotiation_advertisement | = ADVERTISE_10_FULL ; // 10 mbit full duplex
auto_negotiation_advertisement | = ADVERTISE_100_HALF ; // 100 mbit half duplex
auto_negotiation_advertisement | = ADVERTISE_100_FULL ; // 100 mbit full duplex
auto_negotiation_advertisement | = ADVERTISE_PAUSE_CAP ; // capable of pause flow control
auto_negotiation_advertisement | = ADVERTISE_PAUSE_ASYM ; // capable of asymmetric pause flow control
phy_out ( PHY_REG_ANAR , auto_negotiation_advertisement ) ;
auto gigabyte_control = phy_in ( PHY_REG_GBCR ) ;
gigabyte_control | = ADVERTISE_1000_HALF ; // 1000 mbit half dulpex
gigabyte_control | = ADVERTISE_1000_FULL ; // 1000 mbit full duplex
phy_out ( PHY_REG_GBCR , gigabyte_control ) ;
// restart auto-negotation with set advertisements
phy_out ( PHY_REG_BMCR , BMCR_AUTO_NEGOTIATE | BMCR_RESTART_AUTO_NEGOTIATE ) ;
}
UNMAP_AFTER_INIT void RTL8168NetworkAdapter : : initialize_rx_descriptors ( )
{
auto * rx_descriptors = ( RXDescriptor * ) m_rx_descriptors_region - > vaddr ( ) . as_ptr ( ) ;
for ( size_t i = 0 ; i < number_of_rx_descriptors ; + + i ) {
auto & descriptor = rx_descriptors [ i ] ;
auto region = MM . allocate_contiguous_kernel_region ( page_round_up ( RX_BUFFER_SIZE ) , " RTL8168 RX buffer " , Region : : Access : : Read | Region : : Access : : Write ) ;
VERIFY ( region ) ;
memset ( region - > vaddr ( ) . as_ptr ( ) , 0 , region - > size ( ) ) ; // MM already zeros out newly allocated pages, but we do it again in case that ever changes
m_rx_buffers_regions . append ( region . release_nonnull ( ) ) ;
descriptor . buffer_size = RX_BUFFER_SIZE ;
descriptor . flags = RXDescriptor : : Ownership ; // let the NIC know it can use this descriptor
auto physical_address = m_rx_buffers_regions [ i ] . physical_page ( 0 ) - > paddr ( ) . get ( ) ;
descriptor . buffer_address_low = physical_address & 0xFFFFFFFF ;
descriptor . buffer_address_high = ( u64 ) physical_address > > 32 ; // cast to prevent shift count >= with of type warnings in 32 bit systems
}
rx_descriptors [ number_of_rx_descriptors - 1 ] . flags = rx_descriptors [ number_of_rx_descriptors - 1 ] . flags | RXDescriptor : : EndOfRing ;
}
UNMAP_AFTER_INIT void RTL8168NetworkAdapter : : initialize_tx_descriptors ( )
{
auto * tx_descriptors = ( TXDescriptor * ) m_tx_descriptors_region - > vaddr ( ) . as_ptr ( ) ;
for ( size_t i = 0 ; i < number_of_tx_descriptors ; + + i ) {
auto & descriptor = tx_descriptors [ i ] ;
auto region = MM . allocate_contiguous_kernel_region ( page_round_up ( TX_BUFFER_SIZE ) , " RTL8168 TX buffer " , Region : : Access : : Read | Region : : Access : : Write ) ;
VERIFY ( region ) ;
memset ( region - > vaddr ( ) . as_ptr ( ) , 0 , region - > size ( ) ) ; // MM already zeros out newly allocated pages, but we do it again in case that ever changes
m_tx_buffers_regions . append ( region . release_nonnull ( ) ) ;
descriptor . flags = TXDescriptor : : FirstSegment | TXDescriptor : : LastSegment ;
auto physical_address = m_tx_buffers_regions [ i ] . physical_page ( 0 ) - > paddr ( ) . get ( ) ;
descriptor . buffer_address_low = physical_address & 0xFFFFFFFF ;
descriptor . buffer_address_high = ( u64 ) physical_address > > 32 ;
}
tx_descriptors [ number_of_tx_descriptors - 1 ] . flags = tx_descriptors [ number_of_tx_descriptors - 1 ] . flags | TXDescriptor : : EndOfRing ;
}
UNMAP_AFTER_INIT RTL8168NetworkAdapter : : ~ RTL8168NetworkAdapter ( )
{
}
2021-06-05 09:00:18 +03:00
bool RTL8168NetworkAdapter : : handle_irq ( const RegisterState & )
2021-04-02 17:53:31 +03:00
{
2021-06-05 09:00:18 +03:00
bool was_handled = false ;
2021-04-02 17:53:31 +03:00
for ( ; ; ) {
int status = in16 ( REG_ISR ) ;
out16 ( REG_ISR , status ) ;
m_entropy_source . add_random_event ( status ) ;
dbgln_if ( RTL8168_DEBUG , " RTL8168: handle_irq status={:#04x} " , status ) ;
if ( ( status & ( INT_RXOK | INT_RXERR | INT_TXOK | INT_TXERR | INT_RX_OVERFLOW | INT_LINK_CHANGE | INT_RX_FIFO_OVERFLOW | INT_SYS_ERR ) ) = = 0 )
break ;
2021-06-05 09:00:18 +03:00
was_handled = true ;
2021-04-02 17:53:31 +03:00
if ( status & INT_RXOK ) {
dbgln_if ( RTL8168_DEBUG , " RTL8168: RX ready " ) ;
receive ( ) ;
}
if ( status & INT_RXERR ) {
dbgln_if ( RTL8168_DEBUG , " RTL8168: RX error - invalid packet " ) ;
}
if ( status & INT_TXOK ) {
dbgln_if ( RTL8168_DEBUG , " RTL8168: TX complete " ) ;
m_wait_queue . wake_one ( ) ;
}
if ( status & INT_TXERR ) {
dbgln_if ( RTL8168_DEBUG , " RTL8168: TX error - invalid packet " ) ;
}
if ( status & INT_RX_OVERFLOW ) {
dmesgln ( " RTL8168: RX descriptor unavailable (packet lost) " ) ;
receive ( ) ;
}
if ( status & INT_LINK_CHANGE ) {
m_link_up = ( in8 ( REG_PHYSTATUS ) & PHY_LINK_STATUS ) ! = 0 ;
dmesgln ( " RTL8168: Link status changed up={} " , m_link_up ) ;
}
if ( status & INT_RX_FIFO_OVERFLOW ) {
dmesgln ( " RTL8168: RX FIFO overflow " ) ;
receive ( ) ;
}
if ( status & INT_SYS_ERR ) {
dmesgln ( " RTL8168: Fatal system error " ) ;
}
}
2021-06-05 09:00:18 +03:00
return was_handled ;
2021-04-02 17:53:31 +03:00
}
void RTL8168NetworkAdapter : : reset ( )
{
out8 ( REG_COMMAND , COMMAND_RESET ) ;
while ( ( in8 ( REG_COMMAND ) & COMMAND_RESET ) ! = 0 )
;
}
UNMAP_AFTER_INIT void RTL8168NetworkAdapter : : read_mac_address ( )
{
MACAddress mac { } ;
for ( int i = 0 ; i < 6 ; i + + )
mac [ i ] = in8 ( REG_MAC + i ) ;
set_mac_address ( mac ) ;
}
void RTL8168NetworkAdapter : : send_raw ( ReadonlyBytes payload )
{
dbgln_if ( RTL8168_DEBUG , " RTL8168: send_raw length={} " , payload . size ( ) ) ;
if ( payload . size ( ) > TX_BUFFER_SIZE ) {
dmesgln ( " RTL8168: Packet was too big; discarding " ) ;
return ;
}
auto * tx_descriptors = ( TXDescriptor * ) m_tx_descriptors_region - > vaddr ( ) . as_ptr ( ) ;
auto & free_descriptor = tx_descriptors [ m_tx_free_index ] ;
if ( ( free_descriptor . flags & TXDescriptor : : Ownership ) ! = 0 ) {
dbgln_if ( RTL8168_DEBUG , " RTL8168: No free TX buffers, sleeping until one is available " ) ;
m_wait_queue . wait_forever ( " RTL8168NetworkAdapter " ) ;
return send_raw ( payload ) ;
// if we woke up a TX descriptor is guaranteed to be available, so this should never recurse more than once
// but this can probably be done more cleanly
}
dbgln_if ( RTL8168_DEBUG , " RTL8168: Chose descriptor {} " , m_tx_free_index ) ;
memcpy ( m_tx_buffers_regions [ m_tx_free_index ] . vaddr ( ) . as_ptr ( ) , payload . data ( ) , payload . size ( ) ) ;
m_tx_free_index = ( m_tx_free_index + 1 ) % number_of_tx_descriptors ;
free_descriptor . frame_length = payload . size ( ) & 0x3FFF ;
free_descriptor . flags = free_descriptor . flags | TXDescriptor : : Ownership ;
out8 ( REG_TXSTART , TXSTART_START ) ; // FIXME: this shouldnt be done so often, we should look into doing this using the watchdog timer
}
void RTL8168NetworkAdapter : : receive ( )
{
auto * rx_descriptors = ( RXDescriptor * ) m_rx_descriptors_region - > vaddr ( ) . as_ptr ( ) ;
for ( u16 i = 0 ; i < number_of_rx_descriptors ; + + i ) {
auto descriptor_index = ( m_rx_free_index + i ) % number_of_rx_descriptors ;
auto & descriptor = rx_descriptors [ descriptor_index ] ;
if ( ( descriptor . flags & RXDescriptor : : Ownership ) ! = 0 ) {
m_rx_free_index = descriptor_index ;
break ;
}
u16 flags = descriptor . flags ;
u16 length = descriptor . buffer_size & 0x3FFF ;
dbgln_if ( RTL8168_DEBUG , " RTL8168: receive, flags={:#04x}, length={}, descriptor={} " , flags , length , descriptor_index ) ;
if ( length > RX_BUFFER_SIZE | | ( flags & RXDescriptor : : ErrorSummary ) ! = 0 ) {
dmesgln ( " RTL8168: receive got bad packet, flags={:#04x}, length={} " , flags , length ) ;
} else if ( ( flags & RXDescriptor : : FirstSegment ) ! = 0 & & ( flags & RXDescriptor : : LastSegment ) = = 0 ) {
VERIFY_NOT_REACHED ( ) ;
// Our maximum received packet size is smaller than the descriptor buffer size, so packets should never be segmented
// if this happens on a real NIC it might not respect that, and we will have to support packet segmentation
} else {
did_receive ( { m_rx_buffers_regions [ descriptor_index ] . vaddr ( ) . as_ptr ( ) , length } ) ;
}
descriptor . buffer_size = RX_BUFFER_SIZE ;
flags = RXDescriptor : : Ownership ;
if ( descriptor_index = = number_of_rx_descriptors - 1 )
flags | = RXDescriptor : : EndOfRing ;
descriptor . flags = flags ; // let the NIC know it can use this descriptor again
}
}
void RTL8168NetworkAdapter : : out8 ( u16 address , u8 data )
{
m_io_base . offset ( address ) . out ( data ) ;
}
void RTL8168NetworkAdapter : : out16 ( u16 address , u16 data )
{
m_io_base . offset ( address ) . out ( data ) ;
}
void RTL8168NetworkAdapter : : out32 ( u16 address , u32 data )
{
m_io_base . offset ( address ) . out ( data ) ;
}
void RTL8168NetworkAdapter : : out64 ( u16 address , u64 data )
{
// ORDER MATTERS: Some NICs require the high part of the address to be written first
m_io_base . offset ( address + 4 ) . out ( ( u32 ) ( data > > 32 ) ) ;
m_io_base . offset ( address ) . out ( ( u32 ) ( data & 0xFFFFFFFF ) ) ;
}
u8 RTL8168NetworkAdapter : : in8 ( u16 address )
{
return m_io_base . offset ( address ) . in < u8 > ( ) ;
}
u16 RTL8168NetworkAdapter : : in16 ( u16 address )
{
return m_io_base . offset ( address ) . in < u16 > ( ) ;
}
u32 RTL8168NetworkAdapter : : in32 ( u16 address )
{
return m_io_base . offset ( address ) . in < u32 > ( ) ;
}
void RTL8168NetworkAdapter : : phy_out ( u8 address , u16 data )
{
if ( m_version = = ChipVersion : : Version11 ) {
TODO ( ) ;
} else if ( m_version = = ChipVersion : : Version12 | | m_version = = ChipVersion : : Version13 ) {
TODO ( ) ;
} else if ( m_version > = ChipVersion : : Version21 ) {
if ( address = = 0x1F ) {
m_ocp_base_address = data ? data < < 4 : OCP_STANDARD_PHY_BASE ;
return ;
}
if ( m_ocp_base_address ! = OCP_STANDARD_PHY_BASE )
address - = 0x10 ;
ocp_phy_out ( m_ocp_base_address + address * 2 , data ) ;
} else {
VERIFY ( ( address & 0xE0 ) = = 0 ) ; // register address is only 5 bit
out32 ( REG_PHYACCESS , PHY_FLAG | ( address & 0x1F ) < < 16 | ( data & 0xFFFF ) ) ;
while ( ( in32 ( REG_PHYACCESS ) & PHY_FLAG ) ! = 0 )
;
}
}
u16 RTL8168NetworkAdapter : : phy_in ( u8 address )
{
if ( m_version = = ChipVersion : : Version11 ) {
TODO ( ) ;
} else if ( m_version = = ChipVersion : : Version12 | | m_version = = ChipVersion : : Version13 ) {
TODO ( ) ;
} else if ( m_version > = ChipVersion : : Version21 ) {
if ( m_ocp_base_address ! = OCP_STANDARD_PHY_BASE )
address - = 0x10 ;
return ocp_phy_in ( m_ocp_base_address + address * 2 ) ;
} else {
VERIFY ( ( address & 0xE0 ) = = 0 ) ; // register address is only 5 bit
out32 ( REG_PHYACCESS , ( address & 0x1F ) < < 16 ) ;
while ( ( in32 ( REG_PHYACCESS ) & PHY_FLAG ) = = 0 )
;
return in32 ( REG_PHYACCESS ) & 0xFFFF ;
}
}
void RTL8168NetworkAdapter : : phy_update ( u32 address , u32 set , u32 clear )
{
auto value = phy_in ( address ) ;
phy_out ( address , ( value & ~ clear ) | set ) ;
}
void RTL8168NetworkAdapter : : phy_out_batch ( const PhyRegister phy_registers [ ] , size_t length )
{
for ( size_t i = 0 ; i < length ; i + + ) {
phy_out ( phy_registers [ i ] . address , phy_registers [ i ] . data ) ;
}
}
void RTL8168NetworkAdapter : : extended_phy_out ( u8 address , u16 data )
{
VERIFY ( ( address & 0xE0 ) = = 0 ) ; // register address is only 5 bit
out32 ( REG_EPHYACCESS , EPHY_FLAG | ( address & 0x1F ) < < 16 | ( data & 0xFFFF ) ) ;
while ( ( in32 ( REG_EPHYACCESS ) & EPHY_FLAG ) ! = 0 )
;
}
u16 RTL8168NetworkAdapter : : extended_phy_in ( u8 address )
{
VERIFY ( ( address & 0xE0 ) = = 0 ) ; // register address is only 5 bit
out32 ( REG_EPHYACCESS , ( address & 0x1F ) < < 16 ) ;
while ( ( in32 ( REG_EPHYACCESS ) & EPHY_FLAG ) = = 0 )
;
return in32 ( REG_EPHYACCESS ) & 0xFFFF ;
}
void RTL8168NetworkAdapter : : extended_phy_initialize ( const EPhyUpdate ephy_info [ ] , size_t length )
{
for ( size_t i = 0 ; i < length ; i + + ) {
auto updated_value = ( extended_phy_in ( ephy_info [ i ] . offset ) & ~ ephy_info [ i ] . clear ) | ephy_info [ i ] . set ;
extended_phy_out ( ephy_info [ i ] . offset , updated_value ) ;
}
}
void RTL8168NetworkAdapter : : eri_out ( u32 address , u32 mask , u32 data , u32 type )
{
out32 ( REG_ERI_DATA , data ) ;
out32 ( REG_ERI_ADDR , ERI_FLAG | type | mask | address ) ;
while ( ( in32 ( REG_ERI_ADDR ) & ERI_FLAG ) ! = 0 )
;
}
u32 RTL8168NetworkAdapter : : eri_in ( u32 address , u32 type )
{
out32 ( REG_ERI_ADDR , type | ERI_MASK_1111 | address ) ;
while ( ( in32 ( REG_ERI_ADDR ) & ERI_FLAG ) = = 0 )
;
return in32 ( REG_ERI_DATA ) ;
}
void RTL8168NetworkAdapter : : eri_update ( u32 address , u32 mask , u32 set , u32 clear , u32 type )
{
auto value = eri_in ( address , type ) ;
eri_out ( address , mask , ( value & ~ clear ) | set , type ) ;
}
2021-06-04 01:31:00 +03:00
void RTL8168NetworkAdapter : : exgmac_out_batch ( const ExgMacRegister exgmac_registers [ ] , size_t length )
{
for ( size_t i = 0 ; i < length ; i + + ) {
eri_out ( exgmac_registers [ i ] . address , exgmac_registers [ i ] . mask , exgmac_registers [ i ] . value , ERI_EXGMAC ) ;
}
}
2021-04-02 17:53:31 +03:00
void RTL8168NetworkAdapter : : csi_out ( u32 address , u32 data )
{
VERIFY ( m_version > = ChipVersion : : Version4 ) ;
out32 ( REG_CSI_DATA , data ) ;
auto modifier = CSI_BYTE_ENABLE ;
if ( m_version = = ChipVersion : : Version20 ) {
modifier | = CSI_FUNC_NIC ;
} else if ( m_version = = ChipVersion : : Version26 ) {
modifier | = CSI_FUNC_NIC2 ;
}
out32 ( REG_CSI_ADDR , CSI_FLAG | ( address & 0xFFF ) | modifier ) ;
while ( ( in32 ( REG_CSI_ADDR ) & CSI_FLAG ) ! = 0 )
;
}
u32 RTL8168NetworkAdapter : : csi_in ( u32 address )
{
VERIFY ( m_version > = ChipVersion : : Version4 ) ;
auto modifier = CSI_BYTE_ENABLE ;
if ( m_version = = ChipVersion : : Version20 ) {
modifier | = CSI_FUNC_NIC ;
} else if ( m_version = = ChipVersion : : Version26 ) {
modifier | = CSI_FUNC_NIC2 ;
}
out32 ( REG_CSI_ADDR , ( address & 0xFFF ) | modifier ) ;
while ( ( in32 ( REG_CSI_ADDR ) & CSI_FLAG ) = = 0 )
;
return in32 ( REG_CSI_DATA ) & 0xFFFF ;
}
void RTL8168NetworkAdapter : : csi_enable ( u32 bits )
{
auto csi = csi_in ( 0x70c ) & 0x00ffffff ;
csi_out ( 0x70c , csi | bits ) ;
}
void RTL8168NetworkAdapter : : ocp_out ( u32 address , u32 data )
{
VERIFY ( ( address & 0xFFFF0001 ) = = 0 ) ;
out32 ( REG_OCP_DATA , OCP_FLAG | address < < 15 | data ) ;
}
u32 RTL8168NetworkAdapter : : ocp_in ( u32 address )
{
VERIFY ( ( address & 0xFFFF0001 ) = = 0 ) ;
out32 ( REG_OCP_DATA , address < < 15 ) ;
return in32 ( REG_OCP_DATA ) ;
}
void RTL8168NetworkAdapter : : ocp_phy_out ( u32 address , u32 data )
{
VERIFY ( ( address & 0xFFFF0001 ) = = 0 ) ;
out32 ( REG_GPHY_OCP , OCP_FLAG | ( address < < 15 ) | data ) ;
while ( ( in32 ( REG_GPHY_OCP ) & OCP_FLAG ) ! = 0 )
;
}
u16 RTL8168NetworkAdapter : : ocp_phy_in ( u32 address )
{
VERIFY ( ( address & 0xFFFF0001 ) = = 0 ) ;
out32 ( REG_GPHY_OCP , address < < 15 ) ;
while ( ( in32 ( REG_GPHY_OCP ) & OCP_FLAG ) = = 0 )
;
return in32 ( REG_GPHY_OCP ) & 0xFFFF ;
}
void RTL8168NetworkAdapter : : identify_chip_version ( )
{
auto transmit_config = in32 ( REG_TXCFG ) ;
auto registers = transmit_config & 0x7c800000 ;
auto hw_version_id = transmit_config & 0x700000 ;
m_version_uncertain = false ;
switch ( registers ) {
case 0x30000000 :
m_version = ChipVersion : : Version1 ;
break ;
case 0x38000000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version2 ;
} else if ( hw_version_id = = 0x500000 ) {
m_version = ChipVersion : : Version3 ;
} else {
m_version = ChipVersion : : Version3 ;
m_version_uncertain = true ;
}
break ;
case 0x3C000000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version4 ;
} else if ( hw_version_id = = 0x200000 ) {
m_version = ChipVersion : : Version5 ;
} else if ( hw_version_id = = 0x400000 ) {
m_version = ChipVersion : : Version6 ;
} else {
m_version = ChipVersion : : Version6 ;
m_version_uncertain = true ;
}
break ;
case 0x3C800000 :
if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version7 ;
} else if ( hw_version_id = = 0x300000 ) {
m_version = ChipVersion : : Version8 ;
} else {
m_version = ChipVersion : : Version8 ;
m_version_uncertain = true ;
}
break ;
case 0x28000000 :
if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version9 ;
} else if ( hw_version_id = = 0x300000 ) {
m_version = ChipVersion : : Version10 ;
} else {
m_version = ChipVersion : : Version10 ;
m_version_uncertain = true ;
}
break ;
case 0x28800000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version11 ;
} else if ( hw_version_id = = 0x200000 ) {
m_version = ChipVersion : : Version12 ;
} else if ( hw_version_id = = 0x300000 ) {
m_version = ChipVersion : : Version13 ;
} else {
m_version = ChipVersion : : Version13 ;
m_version_uncertain = true ;
}
break ;
case 0x2C000000 :
if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version14 ;
} else if ( hw_version_id = = 0x200000 ) {
m_version = ChipVersion : : Version15 ;
} else {
m_version = ChipVersion : : Version15 ;
m_version_uncertain = true ;
}
break ;
case 0x2C800000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version16 ;
} else if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version17 ;
} else {
m_version = ChipVersion : : Version17 ;
m_version_uncertain = true ;
}
break ;
case 0x48000000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version18 ;
} else if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version19 ;
} else {
m_version = ChipVersion : : Version19 ;
m_version_uncertain = true ;
}
break ;
case 0x48800000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version20 ;
} else {
m_version = ChipVersion : : Version20 ;
m_version_uncertain = true ;
}
break ;
case 0x4C000000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version21 ;
} else if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version22 ;
} else {
m_version = ChipVersion : : Version22 ;
m_version_uncertain = true ;
}
break ;
case 0x50000000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version23 ;
} else if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version27 ;
} else if ( hw_version_id = = 0x200000 ) {
m_version = ChipVersion : : Version28 ;
} else {
m_version = ChipVersion : : Version28 ;
m_version_uncertain = true ;
}
break ;
case 0x50800000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version24 ;
} else if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version25 ;
} else {
m_version = ChipVersion : : Version25 ;
m_version_uncertain = true ;
}
break ;
case 0x5C800000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version26 ;
} else {
m_version = ChipVersion : : Version26 ;
m_version_uncertain = true ;
}
break ;
case 0x54000000 :
if ( hw_version_id = = 00000 ) {
m_version = ChipVersion : : Version29 ;
} else if ( hw_version_id = = 0x100000 ) {
m_version = ChipVersion : : Version30 ;
} else {
m_version = ChipVersion : : Version30 ;
m_version_uncertain = true ;
}
break ;
default :
dbgln_if ( RTL8168_DEBUG , " Unable to determine device version: {#04x} " , registers ) ;
m_version = ChipVersion : : Unknown ;
m_version_uncertain = true ;
break ;
}
}
String RTL8168NetworkAdapter : : possible_device_name ( )
{
switch ( m_version ) { // We are following *BSD's versioning scheme, the comments note linux's versioning scheme, but they dont match up exactly
case ChipVersion : : Version1 :
case ChipVersion : : Version2 :
case ChipVersion : : Version3 :
return " RTL8168B/8111B " ; // 11, 12, 17
case ChipVersion : : Version4 :
case ChipVersion : : Version5 :
case ChipVersion : : Version6 :
return " RTL8168C/8111C " ; // 19, 20, 21, 22
case ChipVersion : : Version7 :
case ChipVersion : : Version8 :
return " RTL8168CP/8111CP " ; // 18, 23, 24
case ChipVersion : : Version9 :
case ChipVersion : : Version10 :
return " RTL8168D/8111D " ; // 25, 26
case ChipVersion : : Version11 :
case ChipVersion : : Version12 :
case ChipVersion : : Version13 :
return " RTL8168DP/8111DP " ; // 27, 28, 31
case ChipVersion : : Version14 :
case ChipVersion : : Version15 :
return " RTL8168E/8111E " ; // 32, 33
case ChipVersion : : Version16 :
case ChipVersion : : Version17 :
return " RTL8168E-VL/8111E-VL " ; // 34
case ChipVersion : : Version18 :
case ChipVersion : : Version19 :
return " RTL8168F/8111F " ; // 35, 36
case ChipVersion : : Version20 :
return " RTL8411 " ; // 38
case ChipVersion : : Version21 :
case ChipVersion : : Version22 :
return " RTL8168G/8111G " ; // 40, 41, 42
case ChipVersion : : Version23 :
case ChipVersion : : Version27 :
case ChipVersion : : Version28 :
return " RTL8168EP/8111EP " ; // 49, 50, 51
case ChipVersion : : Version24 :
case ChipVersion : : Version25 :
return " RTL8168GU/8111GU " ; // ???
case ChipVersion : : Version26 :
return " RTL8411B " ; // 44
case ChipVersion : : Version29 :
case ChipVersion : : Version30 :
return " RTL8168H/8111H " ; // 45, 46
case ChipVersion : : Unknown :
return " Unknown " ;
}
VERIFY_NOT_REACHED ( ) ;
}
}