diff options
author | Marty Connor <mdc@etherboot.org> | 2008-11-18 04:04:07 +0000 |
---|---|---|
committer | Michael Brown <mcb30@etherboot.org> | 2008-11-18 04:04:07 +0000 |
commit | 85f82f09e924ea035e08ff5a779ade8642f803d3 (patch) | |
tree | 02565ba25c50b80d6c597ab4b59199a2c07ba3ee /src | |
parent | cc93556725eb1a68fc088ddeef8e18bb6ec805bf (diff) | |
download | ipxe-85f82f09e924ea035e08ff5a779ade8642f803d3.zip ipxe-85f82f09e924ea035e08ff5a779ade8642f803d3.tar.gz ipxe-85f82f09e924ea035e08ff5a779ade8642f803d3.tar.bz2 |
[r8169] New rtl8169 family driver
This is a completely rewritten version of the rtl8169 family driver
using the gPXE driver API.
Diffstat (limited to 'src')
-rw-r--r-- | src/drivers/net/r8169.c | 3099 | ||||
-rw-r--r-- | src/drivers/net/r8169.h | 566 |
2 files changed, 2595 insertions, 1070 deletions
diff --git a/src/drivers/net/r8169.c b/src/drivers/net/r8169.c index 885f054..0554058 100644 --- a/src/drivers/net/r8169.c +++ b/src/drivers/net/r8169.c @@ -1,1159 +1,434 @@ -/************************************************************************** -* r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit -* Written 2003 by Timothy Legge <tlegge@rogers.com> -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* Portions of this code based on: -* r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver -* for Linux kernel 2.4.x. -* -* Written 2002 ShuChen <shuchen@realtek.com.tw> -* See Linux Driver for full information -* -* Linux Driver Versions: -* 1.27a, 10.02.2002 -* RTL8169_VERSION "2.2" <2004/08/09> -* -* Thanks to: -* Jean Chen of RealTek Semiconductor Corp. for -* providing the evaluation NIC used to develop -* this driver. RealTek's support for Etherboot -* is appreciated. -* -* REVISION HISTORY: -* ================ -* -* v1.0 11-26-2003 timlegge Initial port of Linux driver -* v1.5 01-17-2004 timlegge Initial driver output cleanup -* v1.6 03-27-2004 timlegge Additional Cleanup -* v1.7 11-22-2005 timlegge Update to RealTek Driver Version 2.2 -* -* 03-19-2008 Hilko Bengen Cleanups and fixes for newer cards -* (successfully tested with 8110SC-d onboard NIC) -* -* Indent Options: indent -kr -i8 -***************************************************************************/ - -#include "etherboot.h" -#include "nic.h" -#include <gpxe/pci.h> +/* + * Copyright (c) 2008 Marty Connor <mdc@etherboot.org> + * Copyright (c) 2008 Entity Cyber, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver is based on rtl8169 data sheets and work by: + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include <byteswap.h> +#include <errno.h> #include <gpxe/ethernet.h> +#include <gpxe/if_ether.h> +#include <gpxe/io.h> +#include <gpxe/iobuf.h> #include <gpxe/malloc.h> +#include <gpxe/netdevice.h> +#include <gpxe/pci.h> +#include <gpxe/timer.h> -#define drv_version "v1.7+" -#define drv_date "03-19-2008" - -#define HZ 1000 - -static u32 ioaddr; - -/* Condensed operations for readability. */ -#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) -#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -#undef RTL8169_DEBUG -#undef RTL8169_JUMBO_FRAME_SUPPORT -#undef RTL8169_HW_FLOW_CONTROL_SUPPORT - - -#undef RTL8169_IOCTL_SUPPORT -#undef RTL8169_DYNAMIC_CONTROL -#define RTL8169_USE_IO - -#if 0 -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 20; -#endif - -#if 0 -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). - The RTL chips use a 64 element hash table based on the Ethernet CRC. */ -static int multicast_filter_limit = 32; -#endif - -/* MAC address length*/ -#define MAC_ADDR_LEN 6 - -/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/ -#define MAX_ETH_FRAME_SIZE 1536 - -#define TX_FIFO_THRESH 256 /* In bytes */ - -#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */ -#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ -#define TX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ -#define ETTh 0x3F /* 0x3F means NO threshold */ - -#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */ -#define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */ -#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ - -#define NUM_TX_DESC 1 /* Number of Tx descriptor registers */ -#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ -#define RX_BUF_SIZE 1536 /* Rx Buffer size */ - -#define RTL_MIN_IO_SIZE 0x80 -#define TX_TIMEOUT (6*HZ) - -#define RTL8169_TIMER_EXPIRE_TIME 100 //100 - -#define ETH_HDR_LEN 14 -#define DEFAULT_MTU 1500 -#define DEFAULT_RX_BUF_LEN 1536 - - -#ifdef RTL8169_JUMBO_FRAME_SUPPORT -#define MAX_JUMBO_FRAME_MTU ( 10000 ) -#define MAX_RX_SKBDATA_SIZE ( MAX_JUMBO_FRAME_MTU + ETH_HDR_LEN ) -#else -#define MAX_RX_SKBDATA_SIZE 1600 -#endif //end #ifdef RTL8169_JUMBO_FRAME_SUPPORT - -#ifdef RTL8169_USE_IO -#define RTL_W8(reg, val8) outb ((val8), ioaddr + (reg)) -#define RTL_W16(reg, val16) outw ((val16), ioaddr + (reg)) -#define RTL_W32(reg, val32) outl ((val32), ioaddr + (reg)) -#define RTL_R8(reg) inb (ioaddr + (reg)) -#define RTL_R16(reg) inw (ioaddr + (reg)) -#define RTL_R32(reg) ((unsigned long) inl (ioaddr + (reg))) -#else -/* write/read MMIO register */ -#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) -#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg)) -#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg)) -#define RTL_R8(reg) readb (ioaddr + (reg)) -#define RTL_R16(reg) readw (ioaddr + (reg)) -#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) -#endif - -enum mac_version { - RTL_GIGA_MAC_VER_01 = 0x01, // 8169 - RTL_GIGA_MAC_VER_02 = 0x02, // 8169S - RTL_GIGA_MAC_VER_03 = 0x03, // 8110S - RTL_GIGA_MAC_VER_04 = 0x04, // 8169SB - RTL_GIGA_MAC_VER_05 = 0x05, // 8110SCd - RTL_GIGA_MAC_VER_06 = 0x06, // 8110SCe - RTL_GIGA_MAC_VER_11 = 0x0b, // 8168Bb - RTL_GIGA_MAC_VER_12 = 0x0c, // 8168Be - RTL_GIGA_MAC_VER_13 = 0x0d, // 8101Eb - RTL_GIGA_MAC_VER_14 = 0x0e, // 8101 ? - RTL_GIGA_MAC_VER_15 = 0x0f, // 8101 ? - RTL_GIGA_MAC_VER_16 = 0x11, // 8101Ec - RTL_GIGA_MAC_VER_17 = 0x10, // 8168Bf - RTL_GIGA_MAC_VER_18 = 0x12, // 8168CP - RTL_GIGA_MAC_VER_19 = 0x13, // 8168C - RTL_GIGA_MAC_VER_20 = 0x14 // 8168C -}; - -enum cfg_version { - RTL_CFG_0 = 0x00, - RTL_CFG_1, - RTL_CFG_2 -}; - -static struct { - const char *name; - u8 mac_version; /* depend on RTL8169 docs */ - u32 RxConfigMask; /* should clear the bits supported by this chip */ -} rtl_chip_info[] = { - {"RTL8169", RTL_GIGA_MAC_VER_01, 0xff7e1880}, // 8169 - {"RTL8169s", RTL_GIGA_MAC_VER_02, 0xff7e1880}, // 8169S - {"RTL8110s", RTL_GIGA_MAC_VER_03, 0xff7e1880}, // 8110S - {"RTL8169sb/8110sb", RTL_GIGA_MAC_VER_04, 0xff7e1880}, // 8169SB - {"RTL8169sc/8110sc-d",RTL_GIGA_MAC_VER_05, 0xff7e1880}, // 8110SCd - {"RTL8169sc/8110sc-e",RTL_GIGA_MAC_VER_06, 0xff7e1880}, // 8110SCe - {"RTL8168b/8111b", RTL_GIGA_MAC_VER_11, 0xff7e1880}, // PCI-E - {"RTL8168b/8111b", RTL_GIGA_MAC_VER_12, 0xff7e1880}, // PCI-E - {"RTL8101e", RTL_GIGA_MAC_VER_13, 0xff7e1880}, // PCI-E 8139 - {"RTL8100e", RTL_GIGA_MAC_VER_14, 0xff7e1880}, // PCI-E 8139 - {"RTL8100e", RTL_GIGA_MAC_VER_15, 0xff7e1880}, // PCI-E 8139 - {"RTL8168b/8111b", RTL_GIGA_MAC_VER_17, 0xff7e1880}, // PCI-E - {"RTL8101e", RTL_GIGA_MAC_VER_16, 0xff7e1880}, // PCI-E - {"RTL8168cp/8111cp", RTL_GIGA_MAC_VER_18, 0xff7e1880}, // PCI-E - {"RTL8168c/8111c", RTL_GIGA_MAC_VER_19, 0xff7e1880}, // PCI-E - {"RTL8168c/8111c", RTL_GIGA_MAC_VER_20, 0xff7e1880}, // PCI-E -}; - -enum RTL8169_registers { - MAC0 = 0x0, /* Ethernet hardware address. */ - MAR0 = 0x8, /* Multicast filter. */ - TxDescAddrLow = 0x20, - TxDescAddrHigh = 0x24, - TxHDescStartAddr = 0x28, - FLASH = 0x30, - ERSR = 0x36, - ChipCmd = 0x37, - TxPoll = 0x38, - IntrMask = 0x3C, - IntrStatus = 0x3E, - TxConfig = 0x40, - RxConfig = 0x44, - RxMissed = 0x4C, - Cfg9346 = 0x50, - Config0 = 0x51, - Config1 = 0x52, - Config2 = 0x53, - Config3 = 0x54, - Config4 = 0x55, - Config5 = 0x56, - MultiIntr = 0x5C, - PHYAR = 0x60, - TBICSR = 0x64, - TBI_ANAR = 0x68, - TBI_LPAR = 0x6A, - PHYstatus = 0x6C, - RxMaxSize = 0xda, - CPlusCmd = 0xe0, - IntrMitigate = 0xe2, - RxDescAddrLow = 0xe4, - RxDescAddrHigh = 0xe8, - ETThReg = 0xEC, - FuncEvent = 0xF0, - FuncEventMask = 0xF4, - FuncPresetState = 0xF8, - FuncForceEvent = 0xFC, -}; - -enum RTL8169_register_content { - /*InterruptStatusBits */ - SYSErr = 0x8000, - PCSTimeout = 0x4000, - SWInt = 0x0100, - TxDescUnavail = 0x80, - RxFIFOOver = 0x40, - LinkChg = 0x20, - RxOverflow = 0x10, - TxErr = 0x08, - TxOK = 0x04, - RxErr = 0x02, - RxOK = 0x01, - - /*RxStatusDesc */ - RxRES = 0x00200000, - RxCRC = 0x00080000, - RxRUNT = 0x00100000, - RxRWT = 0x00400000, - - /*ChipCmdBits */ - CmdReset = 0x10, - CmdRxEnb = 0x08, - CmdTxEnb = 0x04, - RxBufEmpty = 0x01, - - /*Cfg9346Bits */ - Cfg9346_Lock = 0x00, - Cfg9346_Unlock = 0xC0, - - /*rx_mode_bits */ - AcceptErr = 0x20, - AcceptRunt = 0x10, - AcceptBroadcast = 0x08, - AcceptMulticast = 0x04, - AcceptMyPhys = 0x02, - AcceptAllPhys = 0x01, - - /*RxConfigBits */ - RxCfgFIFOShift = 13, - RxCfgDMAShift = 8, - - /*TxConfigBits */ - TxInterFrameGapShift = 24, - TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ - - /*rtl8169_PHYstatus */ - TBI_Enable = 0x80, - TxFlowCtrl = 0x40, - RxFlowCtrl = 0x20, - _1000bpsF = 0x10, - _100bps = 0x08, - _10bps = 0x04, - LinkStatus = 0x02, - FullDup = 0x01, - - /*GIGABIT_PHY_registers */ - PHY_CTRL_REG = 0, - PHY_STAT_REG = 1, - PHY_AUTO_NEGO_REG = 4, - PHY_1000_CTRL_REG = 9, - - /*GIGABIT_PHY_REG_BIT */ - PHY_Restart_Auto_Nego = 0x0200, - PHY_Enable_Auto_Nego = 0x1000, - - /* PHY_STAT_REG = 1; */ - PHY_Auto_Neco_Comp = 0x0020, - - /* PHY_AUTO_NEGO_REG = 4; */ - PHY_Cap_10_Half = 0x0020, - PHY_Cap_10_Full = 0x0040, - PHY_Cap_100_Half = 0x0080, - PHY_Cap_100_Full = 0x0100, - - /* PHY_1000_CTRL_REG = 9; */ - PHY_Cap_1000_Full = 0x0200, - PHY_Cap_1000_Half = 0x0100, - - PHY_Cap_PAUSE = 0x0400, - PHY_Cap_ASYM_PAUSE = 0x0800, - - PHY_Cap_Null = 0x0, - - /*_MediaType*/ - _10_Half = 0x01, - _10_Full = 0x02, - _100_Half = 0x04, - _100_Full = 0x08, - _1000_Full = 0x10, - - /*_TBICSRBit*/ - TBILinkOK = 0x02000000, -}; - -enum _DescStatusBit { - OWNbit = 0x80000000, - EORbit = 0x40000000, - FSbit = 0x20000000, - LSbit = 0x10000000, -}; - -struct TxDesc { - u32 status; - u32 vlan_tag; - u32 buf_addr; - u32 buf_Haddr; -}; - -struct RxDesc { - u32 status; - u32 vlan_tag; - u32 buf_addr; - u32 buf_Haddr; -}; +#include "r8169.h" -/* The descriptors for this card are required to be aligned on 256 - * byte boundaries. As the align attribute does not do more than 16 - * bytes of alignment it requires some extra steps. Add 256 to the - * size of the array and the init_ring adjusts the alignment. - * - * UPDATE: This is no longer true; we can request arbitrary alignment. - */ +/*** Low level hardware routines ***/ -/* Define the TX and RX Descriptors and Buffers */ -#define __align_256 __attribute__ (( aligned ( 256 ) )) -struct { - struct TxDesc tx_ring[NUM_TX_DESC] __align_256; - unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; - struct RxDesc rx_ring[NUM_RX_DESC] __align_256; - unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; -} *r8169_bufs; -#define tx_ring r8169_bufs->tx_ring -#define rx_ring r8169_bufs->rx_ring -#define txb r8169_bufs->txb -#define rxb r8169_bufs->rxb - -static struct rtl8169_private { - void *mmio_addr; /* memory map physical address */ - int chipset; - int pcfg; - int mac_version; - unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ - unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ - struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ - struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ - unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */ - unsigned char *Tx_skbuff[NUM_TX_DESC]; -} tpx; - -static const u16 rtl8169_intr_mask = - LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; -static const unsigned int rtl8169_rx_config = - (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift) | - 0x0000000E; - -static void rtl8169_hw_phy_config(struct nic *nic __unused); -//static void rtl8169_hw_phy_reset(struct net_device *dev); - -#define RTL8169_WRITE_GMII_REG_BIT( ioaddr, reg, bitnum, bitval )\ -{ \ - int val; \ - if( bitval == 1 ){ val = ( RTL8169_READ_GMII_REG( ioaddr, reg ) | (bitval<<bitnum) ) & 0xffff ; } \ - else{ val = ( RTL8169_READ_GMII_REG( ioaddr, reg ) & (~(0x0001<<bitnum)) ) & 0xffff ; } \ - RTL8169_WRITE_GMII_REG( ioaddr, reg, val ); \ - } - -//================================================================= -// PHYAR -// bit Symbol -// 31 Flag -// 30-21 reserved -// 20-16 5-bit GMII/MII register address -// 15-0 16-bit GMII/MII register data -//================================================================= -static void RTL8169_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value) +static void mdio_write(void *ioaddr, int reg_addr, int value) { int i; - RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value); - udelay(1000); + DBGP ( "mdio_write\n" ); - for (i = 2000; i > 0; i--) { - // Check if the RTL8169 has completed writing to the specified MII register - if (!(RTL_R32(PHYAR) & 0x80000000)) { + RTL_W32(PHYAR, 0x80000000 | (reg_addr & 0x1f) << 16 | (value & 0xffff)); + + for (i = 20; i > 0; i--) { + /* + * Check if the RTL8169 has completed writing to the specified + * MII register. + */ + if (!(RTL_R32(PHYAR) & 0x80000000)) break; - } else { - udelay(100); - } // end of if( ! (RTL_R32(PHYAR)&0x80000000) ) - } // end of for() loop + udelay(25); + } } -//================================================================= -static int RTL8169_READ_GMII_REG(unsigned long ioaddr, int RegAddr) +static int mdio_read(void *ioaddr, int reg_addr) { int i, value = -1; - RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16); - udelay(1000); + DBGP ( "mdio_read\n" ); + + RTL_W32(PHYAR, 0x0 | (reg_addr & 0x1f) << 16); - for (i = 2000; i > 0; i--) { - // Check if the RTL8169 has completed retrieving data from the specified MII register + for (i = 20; i > 0; i--) { + /* + * Check if the RTL8169 has completed retrieving data from + * the specified MII register. + */ if (RTL_R32(PHYAR) & 0x80000000) { - value = (int) (RTL_R32(PHYAR) & 0xFFFF); + value = RTL_R32(PHYAR) & 0xffff; break; - } else { - udelay(100); - } // end of if( RTL_R32(PHYAR) & 0x80000000 ) - } // end of for() loop + } + udelay(25); + } return value; } +static void mdio_patch(void *ioaddr, int reg_addr, int value) +{ + DBGP ( "mdio_patch\n" ); + + mdio_write(ioaddr, reg_addr, mdio_read(ioaddr, reg_addr) | value); +} -#if 0 -static void mdio_write(int RegAddr, int value) +static void rtl_ephy_write(void *ioaddr, int reg_addr, int value) { - int i; + unsigned int i; + + DBGP ( "rtl_ephy_write\n" ); - RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value); - udelay(1000); + RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) | + (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); - for (i = 2000; i > 0; i--) { - /* Check if the RTL8169 has completed writing to the specified MII register */ - if (!(RTL_R32(PHYAR) & 0x80000000)) { + for (i = 0; i < 100; i++) { + if (!(RTL_R32(EPHYAR) & EPHYAR_FLAG)) break; - } else { - udelay(100); - } + udelay(10); } } -static int mdio_read(int RegAddr) +static u16 rtl_ephy_read(void *ioaddr, int reg_addr) { - int i, value = -1; + u16 value = 0xffff; + unsigned int i; - RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16); - udelay(1000); + DBGP ( "rtl_ephy_read\n" ); - for (i = 2000; i > 0; i--) { - /* Check if the RTL8169 has completed retrieving data from the specified MII register */ - if (RTL_R32(PHYAR) & 0x80000000) { - value = (int) (RTL_R32(PHYAR) & 0xFFFF); + RTL_W32(EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + for (i = 0; i < 100; i++) { + if (RTL_R32(EPHYAR) & EPHYAR_FLAG) { + value = RTL_R32(EPHYAR) & EPHYAR_DATA_MASK; break; - } else { - udelay(100); } + udelay(10); } + return value; } -#endif -static void rtl8169_get_mac_version( struct rtl8169_private *tp, - u32 ioaddr ) +static void rtl_csi_write(void *ioaddr, int addr, int value) { - /* - * The driver currently handles the 8168Bf and the 8168Be identically - * but they can be identified more specifically through the test below - * if needed: - * - * (RTL_R32(TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be - * - * Same thing for the 8101Eb and the 8101Ec: - * - * (RTL_R32(TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec - */ - const struct { - u32 mask; - u32 val; - int mac_version; - } mac_info[] = { - /* 8168B family. */ - { 0x7c800000, 0x3c800000, RTL_GIGA_MAC_VER_18 }, - { 0x7cf00000, 0x3c000000, RTL_GIGA_MAC_VER_19 }, - { 0x7cf00000, 0x3c200000, RTL_GIGA_MAC_VER_20 }, - { 0x7c800000, 0x3c000000, RTL_GIGA_MAC_VER_20 }, - /* 8168B family. */ - { 0x7cf00000, 0x38000000, RTL_GIGA_MAC_VER_12 }, - { 0x7cf00000, 0x38500000, RTL_GIGA_MAC_VER_17 }, - { 0x7c800000, 0x38000000, RTL_GIGA_MAC_VER_17 }, - { 0x7c800000, 0x30000000, RTL_GIGA_MAC_VER_11 }, - /* 8101 family. */ - { 0x7cf00000, 0x34000000, RTL_GIGA_MAC_VER_13 }, - { 0x7cf00000, 0x34200000, RTL_GIGA_MAC_VER_16 }, - { 0x7c800000, 0x34000000, RTL_GIGA_MAC_VER_16 }, - /* FIXME: where did these entries come from ? -- FR */ - { 0xfc800000, 0x38800000, RTL_GIGA_MAC_VER_15 }, - { 0xfc800000, 0x30800000, RTL_GIGA_MAC_VER_14 }, - /* 8110 family. */ - { 0xfc800000, 0x98000000, RTL_GIGA_MAC_VER_06 }, - { 0xfc800000, 0x18000000, RTL_GIGA_MAC_VER_05 }, - { 0xfc800000, 0x10000000, RTL_GIGA_MAC_VER_04 }, - { 0xfc800000, 0x04000000, RTL_GIGA_MAC_VER_03 }, - { 0xfc800000, 0x00800000, RTL_GIGA_MAC_VER_02 }, - { 0xfc800000, 0x00000000, RTL_GIGA_MAC_VER_01 }, - { 0x00000000, 0x00000000, RTL_GIGA_MAC_VER_01 } /* Catch-all */ - }, *p = mac_info; + unsigned int i; - unsigned long rv; + DBGP ( "rtl_csi_write\n" ); - rv = (RTL_R32(TxConfig)); + RTL_W32(CSIDR, value); + RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); - while ((rv & p->mask) != p->val) - p++; - tp->mac_version = p->mac_version; - - if (p->mask == 0x00000000) { - DBG("unknown MAC (%08lx)\n", rv); + for (i = 0; i < 100; i++) { + if (!(RTL_R32(CSIAR) & CSIAR_FLAG)) + break; + udelay(10); } } -#define IORESOURCE_MEM 0x00000200 - -static int rtl8169_init_board(struct pci_device *pdev) +static u32 rtl_csi_read(void *ioaddr, int addr) { - int i; -// unsigned long mmio_end, mmio_flags - unsigned long mmio_start, mmio_len; - struct rtl8169_private *tp = &tpx; - - adjust_pci_device(pdev); - - mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_1); -// mmio_end = pci_resource_end (pdev, 1); -// mmio_flags = pci_resource_flags (pdev, PCI_BASE_ADDRESS_1); - mmio_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_1); - - // make sure PCI base addr 1 is MMIO -// if (!(mmio_flags & IORESOURCE_MEM)) { -// printf ("region #1 not an MMIO resource, aborting\n"); -// return 0; -// } - - // check for weird/broken PCI region reporting - if (mmio_len < RTL_MIN_IO_SIZE) { - printf("Invalid PCI region size(s), aborting\n"); - return 0; - } -#ifdef RTL8169_USE_IO - ioaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); -#else - // ioremap MMIO region - ioaddr = (unsigned long) ioremap(mmio_start, mmio_len); - if (ioaddr == 0) { - printk("cannot remap MMIO, aborting\n"); - return 0; - } -#endif + u32 value = ~0x00; + unsigned int i; - tp->mmio_addr = (void*)ioaddr; - /* Soft reset the chip. */ - RTL_W8(ChipCmd, CmdReset); + DBGP ( "rtl_csi_read\n" ); - /* Check that the chip has finished the reset. */ - for (i = 1000; i > 0; i--) - if ((RTL_R8(ChipCmd) & CmdReset) == 0) - break; - else - udelay(10); + RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); - /* Identify chip attached to board */ - rtl8169_get_mac_version( tp, ioaddr ); - - // rtl8169_print_mac_version(tp); - - { - unsigned char val8 = - (unsigned char) (RTL8169_READ_GMII_REG(ioaddr, 3) & - 0x000f); - if (val8 == 0x00) { - tp->pcfg = RTL_CFG_0; - } else if (val8 == 0x01) { - tp->pcfg = RTL_CFG_1; - } else if (val8 == 0x02) { - tp->pcfg = RTL_CFG_2; - } else { - tp->pcfg = RTL_CFG_2; + for (i = 0; i < 100; i++) { + if (RTL_R32(CSIAR) & CSIAR_FLAG) { + value = RTL_R32(CSIDR); + break; } + udelay(10); } - /* identify chip attached to board */ - - for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) - if (tp->mac_version == rtl_chip_info[i].mac_version) { - tp->chipset = i; - goto match; - } - /* if unknown chip, assume array element #0, original RTL-8169 in this case */ - DBG ( "PCI device: unknown chip version, assuming RTL-8169\n" ); - DBG ( "PCI device: TxConfig = %#lX\n", ( unsigned long ) RTL_R32 ( TxConfig ) ); + return value; +} - tp->chipset = 0; - return 1; +static void rtl8169_irq_mask_and_ack(void *ioaddr) +{ + DBGP ( "rtl8169_irq_mask_and_ack\n" ); - match: - return 0; + RTL_W16(IntrMask, 0x0000); + RTL_W16(IntrStatus, 0xffff); } -/************************************************************************** -IRQ - Wait for a frame -***************************************************************************/ -static void r8169_irq(struct nic *nic __unused, irq_action_t action) +static unsigned int rtl8169_tbi_reset_pending(void *ioaddr) { - int intr_status = 0; - int interested = RxOverflow | RxFIFOOver | RxErr | RxOK; + DBGP ( "rtl8169_tbi_reset_pending\n" ); - switch (action) { - case DISABLE: - case ENABLE: - intr_status = RTL_R16(IntrStatus); - /* h/w no longer present (hotplug?) or major error, - bail */ - if (intr_status == 0xFFFF) - break; - - intr_status = intr_status & ~interested; - if (action == ENABLE) - intr_status = intr_status | interested; - RTL_W16(IntrMask, intr_status); - break; - case FORCE: - RTL_W8(TxPoll, (RTL_R8(TxPoll) | 0x01)); - break; - } + return RTL_R32(TBICSR) & TBIReset; } -/************************************************************************** -POLL - Wait for a frame -***************************************************************************/ -static int r8169_poll(struct nic *nic, int retrieve) -{ - /* return true if there's an ethernet packet ready to read */ - /* nic->packet should contain data on return */ - /* nic->packetlen should contain length of data */ - int cur_rx; - unsigned int intr_status = 0; - struct rtl8169_private *tp = &tpx; - - cur_rx = tp->cur_rx; - if ((tp->RxDescArray[cur_rx].status & OWNbit) == 0) { - /* There is a packet ready */ - DBG("r8169_poll(): packet ready\n"); - if (!retrieve) - return 1; - intr_status = RTL_R16(IntrStatus); - /* h/w no longer present (hotplug?) or major error, - bail */ - if (intr_status == 0xFFFF) { - DBG("r8169_poll(): unknown error\n"); - return 0; - } - RTL_W16(IntrStatus, intr_status & - ~(RxFIFOOver | RxOverflow | RxOK)); - - if (!(tp->RxDescArray[cur_rx].status & RxRES)) { - nic->packetlen = (int) (tp->RxDescArray[cur_rx]. - status & 0x00001FFF) - 4; - memcpy(nic->packet, tp->RxBufferRing[cur_rx], - nic->packetlen); - if (cur_rx == NUM_RX_DESC - 1) - tp->RxDescArray[cur_rx].status = - (OWNbit | EORbit) + RX_BUF_SIZE; - else - tp->RxDescArray[cur_rx].status = - OWNbit + RX_BUF_SIZE; - tp->RxDescArray[cur_rx].buf_addr = - virt_to_bus(tp->RxBufferRing[cur_rx]); - tp->RxDescArray[cur_rx].buf_Haddr = 0; - } else - printf("Error Rx"); - /* FIXME: shouldn't I reset the status on an error */ - cur_rx = (cur_rx + 1) % NUM_RX_DESC; - tp->cur_rx = cur_rx; - RTL_W16(IntrStatus, intr_status & - (RxFIFOOver | RxOverflow | RxOK)); - - return 1; +static unsigned int rtl8169_xmii_reset_pending(void *ioaddr) +{ + DBGP ( "rtl8169_xmii_reset_pending\n" ); - } - tp->cur_rx = cur_rx; - /* FIXME: There is no reason to do this as cur_rx did not change */ - - return (0); /* initially as this is called to flush the input */ - -} - -/************************************************************************** -TRANSMIT - Transmit a frame -***************************************************************************/ -static void r8169_transmit(struct nic *nic, const char *d, /* Destination */ - unsigned int t, /* Type */ - unsigned int s, /* size */ - const char *p) -{ /* Packet */ - /* send the packet to destination */ - - u16 nstype; - u32 to; - u8 *ptxb; - struct rtl8169_private *tp = &tpx; - int entry = tp->cur_tx % NUM_TX_DESC; - - /* point to the current txb incase multiple tx_rings are used */ - ptxb = tp->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE]; - memcpy(ptxb, d, ETH_ALEN); - memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); - nstype = htons((u16) t); - memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); - memcpy(ptxb + ETH_HLEN, p, s); - s += ETH_HLEN; - s &= 0x0FFF; - while (s < ETH_ZLEN) - ptxb[s++] = '\0'; - - tp->TxDescArray[entry].buf_addr = virt_to_bus(ptxb); - tp->TxDescArray[entry].buf_Haddr = 0; - if (entry != (NUM_TX_DESC - 1)) - tp->TxDescArray[entry].status = - (OWNbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s : - ETH_ZLEN); - else - tp->TxDescArray[entry].status = - (OWNbit | EORbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s - : ETH_ZLEN); - RTL_W8(TxPoll, 0x40); /* set polling bit */ - - tp->cur_tx++; - to = currticks() + TX_TIMEOUT; - while ((tp->TxDescArray[entry].status & OWNbit) && (currticks() < to)); /* wait */ - - if (currticks() >= to) { - printf("TX Time Out"); - } + return mdio_read(ioaddr, MII_BMCR) & BMCR_RESET; } -static void rtl8169_set_rx_mode(struct nic *nic __unused) +static unsigned int rtl8169_tbi_link_ok(void *ioaddr) { - u32 mc_filter[2]; /* Multicast hash filter */ - int rx_mode; - u32 tmp = 0; - struct rtl8169_private *tp = &tpx; + DBGP ( "rtl8169_tbi_link_ok\n" ); - /* IFF_ALLMULTI */ - /* Too many to filter perfectly -- accept all multicasts. */ - rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; - mc_filter[1] = mc_filter[0] = 0xffffffff; + return RTL_R32(TBICSR) & TBILinkOk; +} - tmp = - rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) & - rtl_chip_info[tp->chipset]. - RxConfigMask); +static unsigned int rtl8169_xmii_link_ok(void *ioaddr) +{ + DBGP ( "rtl8169_xmii_link_ok\n" ); - RTL_W32(RxConfig, tmp); - RTL_W32(MAR0 + 0, mc_filter[0]); - RTL_W32(MAR0 + 4, mc_filter[1]); + return RTL_R8(PHYstatus) & LinkStatus; } -static void rtl8169_hw_start(struct nic *nic) + +static void rtl8169_tbi_reset_enable(void *ioaddr) { - u32 i; - struct rtl8169_private *tp = &tpx; + DBGP ( "rtl8169_tbi_reset_enable\n" ); - /* Soft reset the chip. */ - RTL_W8(ChipCmd, CmdReset); + RTL_W32(TBICSR, RTL_R32(TBICSR) | TBIReset); +} - /* Check that the chip has finished the reset. */ - for (i = 1000; i > 0; i--) { - if ((RTL_R8(ChipCmd) & CmdReset) == 0) - break; - else - udelay(10); - } +static void rtl8169_xmii_reset_enable(void *ioaddr) +{ + unsigned int val; - RTL_W8(Cfg9346, Cfg9346_Unlock); - RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); - RTL_W8(ETThReg, ETTh); + DBGP ( "rtl8169_xmii_reset_enable\n" ); - /* For gigabit rtl8169 */ - RTL_W16(RxMaxSize, RxPacketMaxSize); + val = mdio_read(ioaddr, MII_BMCR) | BMCR_RESET; + mdio_write(ioaddr, MII_BMCR, val & 0xffff); +} - /* Set Rx Config register */ - i = rtl8169_rx_config | (RTL_R32(RxConfig) & - rtl_chip_info[tp->chipset].RxConfigMask); - RTL_W32(RxConfig, i); +static int rtl8169_set_speed_tbi(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + int ret = 0; + u32 reg; + + DBGP ( "rtl8169_set_speed_tbi\n" ); + + reg = RTL_R32(TBICSR); + if ((autoneg == AUTONEG_DISABLE) && (speed == SPEED_1000) && + (duplex == DUPLEX_FULL)) { + RTL_W32(TBICSR, reg & ~(TBINwEnable | TBINwRestart)); + } else if (autoneg == AUTONEG_ENABLE) + RTL_W32(TBICSR, reg | TBINwEnable | TBINwRestart); + else { + DBG ( "incorrect speed setting refused in TBI mode\n" ); + ret = -EOPNOTSUPP; + } + return ret; +} - /* Set DMA burst size and Interframe Gap Time */ - RTL_W32(TxConfig, - (TX_DMA_BURST << TxDMAShift) | (InterFrameGap << - TxInterFrameGapShift)); +static int rtl8169_set_speed_xmii(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + int auto_nego, giga_ctrl; + DBGP ( "rtl8169_set_speed_xmii\n" ); - RTL_W16(CPlusCmd, RTL_R16(CPlusCmd)); + auto_nego = mdio_read(ioaddr, MII_ADVERTISE); + auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + giga_ctrl = mdio_read(ioaddr, MII_CTRL1000); + giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); - if (tp->mac_version == RTL_GIGA_MAC_VER_02 || tp->mac_version == RTL_GIGA_MAC_VER_03) { - RTL_W16(CPlusCmd, - (RTL_R16(CPlusCmd) | (1 << 14) | (1 << 3))); - DBG - ("Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14\n"); + if (autoneg == AUTONEG_ENABLE) { + auto_nego |= (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; } else { - RTL_W16(CPlusCmd, (RTL_R16(CPlusCmd) | (1 << 3))); - DBG("Set MAC Reg C+CR Offset 0xE0: bit-3.\n"); + if (speed == SPEED_10) + auto_nego |= ADVERTISE_10HALF | ADVERTISE_10FULL; + else if (speed == SPEED_100) + auto_nego |= ADVERTISE_100HALF | ADVERTISE_100FULL; + else if (speed == SPEED_1000) + giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + + if (duplex == DUPLEX_HALF) + auto_nego &= ~(ADVERTISE_10FULL | ADVERTISE_100FULL); + + if (duplex == DUPLEX_FULL) + auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_100HALF); + + /* This tweak comes straight from Realtek's driver. */ + if ((speed == SPEED_100) && (duplex == DUPLEX_HALF) && + ((tp->mac_version == RTL_GIGA_MAC_VER_13) || + (tp->mac_version == RTL_GIGA_MAC_VER_16))) { + auto_nego = ADVERTISE_100HALF | ADVERTISE_CSMA; + } } - { - //RTL_W16(IntrMitigate, 0x1517); - //RTL_W16(IntrMitigate, 0x152a); - //RTL_W16(IntrMitigate, 0x282a); - RTL_W16(IntrMitigate, 0x0000); + /* The 8100e/8101e/8102e do Fast Ethernet only. */ + if ((tp->mac_version == RTL_GIGA_MAC_VER_07) || + (tp->mac_version == RTL_GIGA_MAC_VER_08) || + (tp->mac_version == RTL_GIGA_MAC_VER_09) || + (tp->mac_version == RTL_GIGA_MAC_VER_10) || + (tp->mac_version == RTL_GIGA_MAC_VER_13) || + (tp->mac_version == RTL_GIGA_MAC_VER_14) || + (tp->mac_version == RTL_GIGA_MAC_VER_15) || + (tp->mac_version == RTL_GIGA_MAC_VER_16)) { + if ((giga_ctrl & (ADVERTISE_1000FULL | ADVERTISE_1000HALF))) { + DBG ( "PHY does not support 1000Mbps.\n" ); + } + giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); } - tp->cur_rx = 0; - - RTL_W32(TxDescAddrLow, virt_to_le32desc(tp->TxDescArray)); - RTL_W32(TxDescAddrHigh, virt_to_le32desc(NULL)); - RTL_W32(RxDescAddrLow, virt_to_le32desc(tp->RxDescArray)); - RTL_W32(RxDescAddrHigh, virt_to_le32desc(NULL)); - RTL_W8(Cfg9346, Cfg9346_Lock); - udelay(10); - - RTL_W32(RxMissed, 0); - - rtl8169_set_rx_mode(nic); - - /* no early-rx interrupts */ - RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + + if ((tp->mac_version == RTL_GIGA_MAC_VER_11) || + (tp->mac_version == RTL_GIGA_MAC_VER_12) || + (tp->mac_version >= RTL_GIGA_MAC_VER_17)) { + /* + * Wake up the PHY. + * Vendor specific (0x1f) and reserved (0x0e) MII registers. + */ + mdio_write(ioaddr, 0x1f, 0x0000); + mdio_write(ioaddr, 0x0e, 0x0000); + } - RTL_W16(IntrMask, rtl8169_intr_mask); + tp->phy_auto_nego_reg = auto_nego; + tp->phy_1000_ctrl_reg = giga_ctrl; + mdio_write(ioaddr, MII_ADVERTISE, auto_nego); + mdio_write(ioaddr, MII_CTRL1000, giga_ctrl); + mdio_write(ioaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); + return 0; } -static void rtl8169_init_ring(struct nic *nic __unused) +static int rtl8169_set_speed(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) { - int i; - struct rtl8169_private *tp = &tpx; + struct rtl8169_private *tp = netdev_priv(dev); + int ret; - tp->cur_rx = 0; - tp->cur_tx = 0; - memset(tp->TxDescArray, 0x0, NUM_TX_DESC * sizeof(struct TxDesc)); - memset(tp->RxDescArray, 0x0, NUM_RX_DESC * sizeof(struct RxDesc)); + DBGP ( "rtl8169_set_speed\n" ); - for (i = 0; i < NUM_TX_DESC; i++) { - tp->Tx_skbuff[i] = &txb[i]; - } + ret = tp->set_speed(dev, autoneg, speed, duplex); - for (i = 0; i < NUM_RX_DESC; i++) { - if (i == (NUM_RX_DESC - 1)) - tp->RxDescArray[i].status = - (OWNbit | EORbit) | RX_BUF_SIZE; - else - tp->RxDescArray[i].status = OWNbit | RX_BUF_SIZE; - - tp->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE]; - tp->RxDescArray[i].buf_addr = - virt_to_bus(tp->RxBufferRing[i]); - tp->RxDescArray[i].buf_Haddr = 0; - } + return ret; } -/************************************************************************** -RESET - Finish setting up the ethernet interface -***************************************************************************/ -static void r8169_reset(struct nic *nic) +static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, + int bitnum, int bitval) { - int i; - struct rtl8169_private *tp = &tpx; - - tp->TxDescArray = tx_ring; - tp->RxDescArray = rx_ring; - - rtl8169_init_ring(nic); - rtl8169_hw_start(nic); - /* Construct a perfect filter frame with the mac address as first match - * and broadcast for all others */ - for (i = 0; i < 192; i++) - txb[i] = 0xFF; - - txb[0] = nic->node_addr[0]; - txb[1] = nic->node_addr[1]; - txb[2] = nic->node_addr[2]; - txb[3] = nic->node_addr[3]; - txb[4] = nic->node_addr[4]; - txb[5] = nic->node_addr[5]; -} - -/************************************************************************** -DISABLE - Turn off ethernet interface -***************************************************************************/ -static void r8169_disable ( struct nic *nic __unused ) { - int i; - struct rtl8169_private *tp = &tpx; + int val; - /* Stop the chip's Tx and Rx DMA processes. */ - RTL_W8(ChipCmd, 0x00); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16(IntrMask, 0x0000); + DBGP ( "rtl8169_write_gmii_reg_bit\n" ); - RTL_W32(RxMissed, 0); - - tp->TxDescArray = NULL; - tp->RxDescArray = NULL; - for (i = 0; i < NUM_RX_DESC; i++) { - tp->RxBufferRing[i] = NULL; - } + val = mdio_read(ioaddr, reg); + val = (bitval == 1) ? + val | (bitval << bitnum) : val & ~(0x0001 << bitnum); + mdio_write(ioaddr, reg, val & 0xffff); } -static struct nic_operations r8169_operations = { - .connect = dummy_connect, - .poll = r8169_poll, - .transmit = r8169_transmit, - .irq = r8169_irq, - -}; - -static struct pci_device_id r8169_nics[] = { - PCI_ROM(0x10ec, 0x8169, "r8169", "RealTek RTL8169 Gigabit Ethernet"), - PCI_ROM(0x16ec, 0x0116, "usr-r8169", "US Robotics RTL8169 Gigabit Ethernet"), - PCI_ROM(0x1186, 0x4300, "dlink-r8169", "D-Link RTL8169 Gigabit Ethernet"), - PCI_ROM(0x1737, 0x1032, "linksys-r8169", "Linksys RTL8169 Gigabit Ethernet"), - PCI_ROM(0x10ec, 0x8129, "r8169-8129", "RealTek RT8129 Fast Ethernet Adapter"), - PCI_ROM(0x10ec, 0x8136, "r8169-8101e", "RealTek RTL8101E PCI Express Fast Ethernet controller"), - PCI_ROM(0x10ec, 0x8167, "r8169-8110sc/8169sc", "RealTek RTL-8110SC/8169SC Gigabit Ethernet"), - PCI_ROM(0x10ec, 0x8168, "r8169-8168b", "RealTek RTL8111/8168B PCI Express Gigabit Ethernet controller"), -}; - -PCI_DRIVER ( r8169_driver, r8169_nics, PCI_NO_CLASS ); - -/************************************************************************** -PROBE - Look for an adapter, this routine's visible to the outside -***************************************************************************/ - -#define board_found 1 -#define valid_link 0 -static int r8169_probe ( struct nic *nic, struct pci_device *pci ) { - - static int board_idx = -1; - static int printed_version = 0; - struct rtl8169_private *tp = &tpx; - int i, rc; - int option = -1, Cap10_100 = 0, Cap1000 = 0; - - printf ( "r8169.c: Found %s, Vendor=%hX Device=%hX\n", - pci->driver_name, pci->vendor, pci->device ); - - board_idx++; - - printed_version = 1; - - /* Quick and very dirty hack to get r8169 driver working - * again, pre-rewrite +static void rtl8169_get_mac_version(struct rtl8169_private *tp, + void *ioaddr) +{ + /* + * The driver currently handles the 8168Bf and the 8168Be identically + * but they can be identified more specifically through the test below + * if needed: + * + * (RTL_R32(TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be + * + * Same thing for the 8101Eb and the 8101Ec: + * + * (RTL_R32(TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec */ - if ( ! r8169_bufs ) - r8169_bufs = malloc_dma ( sizeof ( *r8169_bufs ), 256 ); - if ( ! r8169_bufs ) - return 0; - memset ( r8169_bufs, 0, sizeof ( *r8169_bufs ) ); - - rc = rtl8169_init_board(pci); /* Return code is meaningless */ - - /* Get MAC address. FIXME: read EEPROM */ - for (i = 0; i < MAC_ADDR_LEN; i++) - nic->node_addr[i] = RTL_R8(MAC0 + i); - - DBG ( "%s: Identified chip type is '%s'.\n", pci->driver_name, - rtl_chip_info[tp->chipset].name ); - - /* Print out some hardware info */ - DBG ( "%s: %s at ioaddr %#hx, ", pci->driver_name, eth_ntoa ( nic->node_addr ), - (unsigned int) ioaddr ); + const struct { + u32 mask; + u32 val; + int mac_version; + } mac_info[] = { + /* 8168D family. */ + { 0x7c800000, 0x28000000, RTL_GIGA_MAC_VER_25 }, + + /* 8168C family. */ + { 0x7cf00000, 0x3ca00000, RTL_GIGA_MAC_VER_24 }, + { 0x7cf00000, 0x3c900000, RTL_GIGA_MAC_VER_23 }, + { 0x7cf00000, 0x3c800000, RTL_GIGA_MAC_VER_18 }, + { 0x7c800000, 0x3c800000, RTL_GIGA_MAC_VER_24 }, + { 0x7cf00000, 0x3c000000, RTL_GIGA_MAC_VER_19 }, + { 0x7cf00000, 0x3c200000, RTL_GIGA_MAC_VER_20 }, + { 0x7cf00000, 0x3c300000, RTL_GIGA_MAC_VER_21 }, + { 0x7cf00000, 0x3c400000, RTL_GIGA_MAC_VER_22 }, + { 0x7c800000, 0x3c000000, RTL_GIGA_MAC_VER_22 }, - /* Config PHY */ - rtl8169_hw_phy_config(nic); - - DBG("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); - RTL_W8(0x82, 0x01); + /* 8168B family. */ + { 0x7cf00000, 0x38000000, RTL_GIGA_MAC_VER_12 }, + { 0x7cf00000, 0x38500000, RTL_GIGA_MAC_VER_17 }, + { 0x7c800000, 0x38000000, RTL_GIGA_MAC_VER_17 }, + { 0x7c800000, 0x30000000, RTL_GIGA_MAC_VER_11 }, - pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0x40); + /* 8101 family. */ + { 0x7cf00000, 0x34a00000, RTL_GIGA_MAC_VER_09 }, + { 0x7cf00000, 0x24a00000, RTL_GIGA_MAC_VER_09 }, + { 0x7cf00000, 0x34900000, RTL_GIGA_MAC_VER_08 }, + { 0x7cf00000, 0x24900000, RTL_GIGA_MAC_VER_08 }, + { 0x7cf00000, 0x34800000, RTL_GIGA_MAC_VER_07 }, + { 0x7cf00000, 0x24800000, RTL_GIGA_MAC_VER_07 }, + { 0x7cf00000, 0x34000000, RTL_GIGA_MAC_VER_13 }, + { 0x7cf00000, 0x34300000, RTL_GIGA_MAC_VER_10 }, + { 0x7cf00000, 0x34200000, RTL_GIGA_MAC_VER_16 }, + { 0x7c800000, 0x34800000, RTL_GIGA_MAC_VER_09 }, + { 0x7c800000, 0x24800000, RTL_GIGA_MAC_VER_09 }, + { 0x7c800000, 0x34000000, RTL_GIGA_MAC_VER_16 }, + /* FIXME: where did these entries come from ? -- FR */ + { 0xfc800000, 0x38800000, RTL_GIGA_MAC_VER_15 }, + { 0xfc800000, 0x30800000, RTL_GIGA_MAC_VER_14 }, - if (tp->mac_version <= RTL_GIGA_MAC_VER_06) - pci_write_config_byte(pci, PCI_CACHE_LINE_SIZE, 0x08); + /* 8110 family. */ + { 0xfc800000, 0x98000000, RTL_GIGA_MAC_VER_06 }, + { 0xfc800000, 0x18000000, RTL_GIGA_MAC_VER_05 }, + { 0xfc800000, 0x10000000, RTL_GIGA_MAC_VER_04 }, + { 0xfc800000, 0x04000000, RTL_GIGA_MAC_VER_03 }, + { 0xfc800000, 0x00800000, RTL_GIGA_MAC_VER_02 }, + { 0xfc800000, 0x00000000, RTL_GIGA_MAC_VER_01 }, - if (tp->mac_version == RTL_GIGA_MAC_VER_02) { - DBG("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); - RTL_W8(0x82, 0x01); - DBG("Set PHY Reg 0x0bh = 0x00h\n"); - RTL8169_WRITE_GMII_REG(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0 - } + { 0x00000000, 0x00000000, RTL_GIGA_MAC_VER_01 } /* Catch-all */ + }, *p = mac_info; + u32 reg; - r8169_reset(nic); - - /* if TBI is not endbled */ - if (!(RTL_R8(PHYstatus) & TBI_Enable)) { - int val = RTL8169_READ_GMII_REG(ioaddr, PHY_AUTO_NEGO_REG); - -#ifdef RTL8169_HW_FLOW_CONTROL_SUPPORT - val |= PHY_Cap_PAUSE | PHY_Cap_ASYM_PAUSE; -#endif //end #define RTL8169_HW_FLOW_CONTROL_SUPPORT - - /* Force RTL8169 in 10/100/1000 Full/Half mode. */ - if (option > 0) { - printf(" Force-mode Enabled.\n"); - Cap10_100 = 0, Cap1000 = 0; - switch (option) { - case _10_Half: - Cap10_100 = PHY_Cap_10_Half; - Cap1000 = PHY_Cap_Null; - break; - case _10_Full: - Cap10_100 = PHY_Cap_10_Full; - Cap1000 = PHY_Cap_Null; - break; - case _100_Half: - Cap10_100 = PHY_Cap_100_Half; - Cap1000 = PHY_Cap_Null; - break; - case _100_Full: - Cap10_100 = PHY_Cap_100_Full; - Cap1000 = PHY_Cap_Null; - break; - case _1000_Full: - Cap10_100 = PHY_Cap_Null; - Cap1000 = PHY_Cap_1000_Full; - break; - default: - break; - } - RTL8169_WRITE_GMII_REG(ioaddr, PHY_AUTO_NEGO_REG, Cap10_100 | (val & 0xC1F)); //leave PHY_AUTO_NEGO_REG bit4:0 unchanged - RTL8169_WRITE_GMII_REG(ioaddr, PHY_1000_CTRL_REG, - Cap1000); - } else { - DBG ( "%s: Auto-negotiation Enabled.\n", pci->driver_name ); - - // enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged - RTL8169_WRITE_GMII_REG(ioaddr, PHY_AUTO_NEGO_REG, - PHY_Cap_10_Half | - PHY_Cap_10_Full | - PHY_Cap_100_Half | - PHY_Cap_100_Full | (val & - 0xC1F)); - - // enable 1000 Full Mode -// RTL8169_WRITE_GMII_REG( ioaddr, PHY_1000_CTRL_REG, PHY_Cap_1000_Full ); - RTL8169_WRITE_GMII_REG(ioaddr, PHY_1000_CTRL_REG, PHY_Cap_1000_Full | PHY_Cap_1000_Half); //rtl8168 - - } // end of if( option > 0 ) - - // Enable auto-negotiation and restart auto-nigotiation - RTL8169_WRITE_GMII_REG(ioaddr, PHY_CTRL_REG, - PHY_Enable_Auto_Nego | - PHY_Restart_Auto_Nego); - udelay(100); - - // wait for auto-negotiation process - for (i = 10000; i > 0; i--) { - //check if auto-negotiation complete - if (RTL8169_READ_GMII_REG(ioaddr, PHY_STAT_REG) & - PHY_Auto_Neco_Comp) { - udelay(100); - option = RTL_R8(PHYstatus); - if (option & _1000bpsF) { - printf - ("1000Mbps Full-duplex operation.\n"); - } else { - printf - ("%sMbps %s-duplex operation.\n", - (option & _100bps) ? "100" : - "10", - (option & FullDup) ? "Full" : - "Half"); - } - break; - } else { - udelay(100); - } // end of if( RTL8169_READ_GMII_REG(ioaddr, 1) & 0x20 ) - } // end for-loop to wait for auto-negotiation process + DBGP ( "rtl8169_get_mac_version\n" ); + reg = RTL_R32(TxConfig); + while ((reg & p->mask) != p->val) + p++; + tp->mac_version = p->mac_version; - } else { - udelay(100); - printf - ("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n", - pci->driver_name, - (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed"); + DBG ( "tp->mac_version = %d\n", tp->mac_version ); + if (p->mask == 0x00000000) { + DBG ( "unknown MAC (%08lx)\n", reg ); } - - r8169_reset(nic); - - /* point to NIC specific routines */ - nic->nic_op = &r8169_operations; - - nic->irqno = pci->irq; - nic->ioaddr = ioaddr; - - return 1; -} - -//====================================================================================================== -/* -static void rtl8169_hw_PHY_reset(struct nic *nic __unused) -{ - int val, phy_reset_expiretime = 50; - struct rtl8169_private *priv = dev->priv; - unsigned long ioaddr = priv->ioaddr; - - DBG("%s: Reset RTL8169s PHY\n", dev->name); - - val = ( RTL8169_READ_GMII_REG( ioaddr, 0 ) | 0x8000 ) & 0xffff; - RTL8169_WRITE_GMII_REG( ioaddr, 0, val ); - - do //waiting for phy reset - { - if( RTL8169_READ_GMII_REG( ioaddr, 0 ) & 0x8000 ){ - phy_reset_expiretime --; - udelay(100); - } - else{ - break; - } - }while( phy_reset_expiretime >= 0 ); - - assert( phy_reset_expiretime > 0 ); } -*/ - struct phy_reg { u16 reg; u16 val; @@ -1161,8 +436,10 @@ struct phy_reg { static void rtl_phy_write(void *ioaddr, struct phy_reg *regs, int len) { + DBGP ( "rtl_phy_write\n" ); + while (len-- > 0) { - RTL8169_WRITE_GMII_REG((u32)ioaddr, regs->reg, regs->val); + mdio_write(ioaddr, regs->reg, regs->val); regs++; } } @@ -1201,22 +478,24 @@ static void rtl8169s_hw_phy_config(void *ioaddr) }, *p = phy_magic; unsigned int i; - RTL8169_WRITE_GMII_REG((u32)ioaddr, 0x1f, 0x0001); //w 31 2 0 1 - RTL8169_WRITE_GMII_REG((u32)ioaddr, 0x15, 0x1000); //w 21 15 0 1000 - RTL8169_WRITE_GMII_REG((u32)ioaddr, 0x18, 0x65c7); //w 24 15 0 65c7 - RTL8169_WRITE_GMII_REG_BIT((u32)ioaddr, 4, 11, 0); //w 4 11 11 0 + DBGP ( "rtl8169s_hw_phy_config\n" ); + + mdio_write(ioaddr, 0x1f, 0x0001); //w 31 2 0 1 + mdio_write(ioaddr, 0x15, 0x1000); //w 21 15 0 1000 + mdio_write(ioaddr, 0x18, 0x65c7); //w 24 15 0 65c7 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 for (i = 0; i < ARRAY_SIZE(phy_magic); i++, p++) { int val, pos = 4; - val = (RTL8169_READ_GMII_REG((u32)ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff); - RTL8169_WRITE_GMII_REG((u32)ioaddr, pos, val); + val = (mdio_read(ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff); + mdio_write(ioaddr, pos, val); while (--pos >= 0) - RTL8169_WRITE_GMII_REG((u32)ioaddr, pos, p->regs[4 - pos] & 0xffff); - RTL8169_WRITE_GMII_REG_BIT((u32)ioaddr, 4, 11, 1); //w 4 11 11 1 - RTL8169_WRITE_GMII_REG_BIT((u32)ioaddr, 4, 11, 0); //w 4 11 11 0 + mdio_write(ioaddr, pos, p->regs[4 - pos] & 0xffff); + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 1); //w 4 11 11 1 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 } - RTL8169_WRITE_GMII_REG((u32)ioaddr, 0x1f, 0x0000); //w 31 2 0 0 + mdio_write(ioaddr, 0x1f, 0x0000); //w 31 2 0 0 } static void rtl8169sb_hw_phy_config(void *ioaddr) @@ -1227,10 +506,40 @@ static void rtl8169sb_hw_phy_config(void *ioaddr) { 0x1f, 0x0000 } }; + DBGP ( "rtl8169sb_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bb_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + mdio_write(ioaddr, 0x1f, 0x0001); + mdio_patch(ioaddr, 0x16, 1 << 0); + + DBGP ( "rtl8168bb_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bef_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168bef_hw_phy_config\n" ); + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); } -static void rtl8168cp_hw_phy_config(void *ioaddr) +static void rtl8168cp_1_hw_phy_config(void *ioaddr) { struct phy_reg phy_reg_init[] = { { 0x1f, 0x0000 }, @@ -1240,10 +549,29 @@ static void rtl8168cp_hw_phy_config(void *ioaddr) { 0x1f, 0x0000 } }; + DBGP ( "rtl8168cp_1_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168cp_2_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168cp_2_hw_phy_config\n" ); + + mdio_write(ioaddr, 0x1f, 0x0000); + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); } -static void rtl8168c_hw_phy_config(void *ioaddr) +static void rtl8168c_1_hw_phy_config(void *ioaddr) { struct phy_reg phy_reg_init[] = { { 0x1f, 0x0001 }, @@ -1259,33 +587,180 @@ static void rtl8168c_hw_phy_config(void *ioaddr) { 0x1f, 0x0003 }, { 0x12, 0xc096 }, { 0x16, 0x000a }, - { 0x1f, 0x0000 } + { 0x1f, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x09, 0x2000 }, + { 0x09, 0x0000 } }; + DBGP ( "rtl8168c_1_hw_phy_config\n" ); + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + mdio_write(ioaddr, 0x1f, 0x0000); } -static void rtl8168cx_hw_phy_config(void *ioaddr) +static void rtl8168c_2_hw_phy_config(void *ioaddr) { struct phy_reg phy_reg_init[] = { - { 0x1f, 0x0000 }, + { 0x1f, 0x0001 }, { 0x12, 0x2300 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0x9000 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x7eb8 }, + { 0x06, 0x0761 }, { 0x1f, 0x0003 }, { 0x16, 0x0f0a }, - { 0x1f, 0x0000 }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168c_2_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + mdio_patch(ioaddr, 0x16, 1 << 0); + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + mdio_write(ioaddr, 0x1f, 0x0000); +} + +static void rtl8168c_3_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1d, 0x3d98 }, { 0x1f, 0x0002 }, { 0x0c, 0x7eb8 }, + { 0x06, 0x5461 }, + { 0x1f, 0x0003 }, + { 0x16, 0x0f0a }, { 0x1f, 0x0000 } }; + DBGP ( "rtl8168c_3_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + mdio_patch(ioaddr, 0x16, 1 << 0); + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + mdio_write(ioaddr, 0x1f, 0x0000); +} + +static void rtl8168c_4_hw_phy_config(void *ioaddr) +{ + DBGP ( "rtl8168c_4_hw_phy_config\n" ); + + rtl8168c_3_hw_phy_config(ioaddr); +} + +static void rtl8168d_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init_0[] = { + { 0x1f, 0x0001 }, + { 0x09, 0x2770 }, + { 0x08, 0x04d0 }, + { 0x0b, 0xad15 }, + { 0x0c, 0x5bf0 }, + { 0x1c, 0xf101 }, + { 0x1f, 0x0003 }, + { 0x14, 0x94d7 }, + { 0x12, 0xf4d6 }, + { 0x09, 0xca0f }, + { 0x1f, 0x0002 }, + { 0x0b, 0x0b10 }, + { 0x0c, 0xd1f7 }, + { 0x1f, 0x0002 }, + { 0x06, 0x5461 }, + { 0x1f, 0x0002 }, + { 0x05, 0x6662 }, + { 0x1f, 0x0000 }, + { 0x14, 0x0060 }, + { 0x1f, 0x0000 }, + { 0x0d, 0xf8a0 }, + { 0x1f, 0x0005 }, + { 0x05, 0xffc2 } + }; + + DBGP ( "rtl8168d_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init_0, ARRAY_SIZE(phy_reg_init_0)); + + if (mdio_read(ioaddr, 0x06) == 0xc400) { + struct phy_reg phy_reg_init_1[] = { + { 0x1f, 0x0005 }, + { 0x01, 0x0300 }, + { 0x1f, 0x0000 }, + { 0x11, 0x401c }, + { 0x16, 0x4100 }, + { 0x1f, 0x0005 }, + { 0x07, 0x0010 }, + { 0x05, 0x83dc }, + { 0x06, 0x087d }, + { 0x05, 0x8300 }, + { 0x06, 0x0101 }, + { 0x06, 0x05f8 }, + { 0x06, 0xf9fa }, + { 0x06, 0xfbef }, + { 0x06, 0x79e2 }, + { 0x06, 0x835f }, + { 0x06, 0xe0f8 }, + { 0x06, 0x9ae1 }, + { 0x06, 0xf89b }, + { 0x06, 0xef31 }, + { 0x06, 0x3b65 }, + { 0x06, 0xaa07 }, + { 0x06, 0x81e4 }, + { 0x06, 0xf89a }, + { 0x06, 0xe5f8 }, + { 0x06, 0x9baf }, + { 0x06, 0x06ae }, + { 0x05, 0x83dc }, + { 0x06, 0x8300 }, + }; + + rtl_phy_write(ioaddr, phy_reg_init_1, + ARRAY_SIZE(phy_reg_init_1)); + } + + mdio_write(ioaddr, 0x1f, 0x0000); +} + +static void rtl8102e_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0003 }, + { 0x08, 0x441d }, + { 0x01, 0x9100 }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8102e_hw_phy_config\n" ); + + mdio_write(ioaddr, 0x1f, 0x0000); + mdio_patch(ioaddr, 0x11, 1 << 12); + mdio_patch(ioaddr, 0x19, 1 << 13); + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); } -static void rtl8169_hw_phy_config(struct nic *nic __unused) +static void rtl_hw_phy_config(struct net_device *dev) { - struct rtl8169_private *tp = &tpx; + struct rtl8169_private *tp = netdev_priv(dev); void *ioaddr = tp->mmio_addr; - DBG("rtl8169_hw_phy_config(): card at addr=0x%lx: priv->mac_version=%d, priv->pcfg=%d\n", (unsigned long) ioaddr, tp->mac_version, tp->pcfg); + + DBGP ( "rtl_hw_phy_config\n" ); + + DBG ( "mac_version = 0x%02x\n", tp->mac_version ); switch (tp->mac_version) { case RTL_GIGA_MAC_VER_01: @@ -1297,22 +772,1506 @@ static void rtl8169_hw_phy_config(struct nic *nic __unused) case RTL_GIGA_MAC_VER_04: rtl8169sb_hw_phy_config(ioaddr); break; + case RTL_GIGA_MAC_VER_07: + case RTL_GIGA_MAC_VER_08: + case RTL_GIGA_MAC_VER_09: + rtl8102e_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_11: + rtl8168bb_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_12: + rtl8168bef_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_17: + rtl8168bef_hw_phy_config(ioaddr); + break; case RTL_GIGA_MAC_VER_18: - rtl8168cp_hw_phy_config(ioaddr); + rtl8168cp_1_hw_phy_config(ioaddr); break; case RTL_GIGA_MAC_VER_19: - rtl8168c_hw_phy_config(ioaddr); + rtl8168c_1_hw_phy_config(ioaddr); break; case RTL_GIGA_MAC_VER_20: - rtl8168cx_hw_phy_config(ioaddr); + rtl8168c_2_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_21: + rtl8168c_3_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_22: + rtl8168c_4_hw_phy_config(ioaddr); break; + case RTL_GIGA_MAC_VER_23: + case RTL_GIGA_MAC_VER_24: + rtl8168cp_2_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_25: + rtl8168d_hw_phy_config(ioaddr); + break; + + default: + break; + } +} + +static void rtl8169_phy_reset(struct net_device *dev __unused, + struct rtl8169_private *tp) +{ + void *ioaddr = tp->mmio_addr; + unsigned int i; + + DBGP ( "rtl8169_phy_reset\n" ); + + tp->phy_reset_enable(ioaddr); + for (i = 0; i < 100; i++) { + if (!tp->phy_reset_pending(ioaddr)) + return; + mdelay ( 1 ); + } + DBG ( "PHY reset failed.\n" ); +} + +static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp) +{ + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_init_phy\n" ); + + rtl_hw_phy_config(dev); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + DBG ( "Set MAC Reg C+CR Offset 0x82h = 0x01h\n" ); + RTL_W8(0x82, 0x01); + } + + pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) + pci_write_config_byte(tp->pci_dev, PCI_CACHE_LINE_SIZE, 0x08); + + if (tp->mac_version == RTL_GIGA_MAC_VER_02) { + DBG ( "Set MAC Reg C+CR Offset 0x82h = 0x01h\n" ); + RTL_W8(0x82, 0x01); + DBG ( "Set PHY Reg 0x0bh = 0x00h\n" ); + mdio_write(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0 + } + + rtl8169_phy_reset(dev, tp); + + /* + * rtl8169_set_speed_xmii takes good care of the Fast Ethernet + * only 8101. Don't panic. + */ + rtl8169_set_speed(dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL); + + if ((RTL_R8(PHYstatus) & TBI_Enable)) + DBG ( "TBI auto-negotiating\n" ); +} + +static const struct rtl_cfg_info { + void (*hw_start)(struct net_device *); + unsigned int region; + unsigned int align; + u16 intr_event; + u16 napi_event; + unsigned features; +} rtl_cfg_infos [] = { + [RTL_CFG_0] = { + .hw_start = rtl_hw_start_8169, + .region = 1, + .align = 0, + .intr_event = SYSErr | LinkChg | RxOverflow | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + .features = RTL_FEATURE_GMII + }, + [RTL_CFG_1] = { + .hw_start = rtl_hw_start_8168, + .region = 2, + .align = 8, + .intr_event = SYSErr | LinkChg | RxOverflow | + TxErr | TxOK | RxOK | RxErr, + .napi_event = TxErr | TxOK | RxOK | RxOverflow, + .features = RTL_FEATURE_GMII + }, + [RTL_CFG_2] = { + .hw_start = rtl_hw_start_8101, + .region = 2, + .align = 8, + .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + } +}; + +static void rtl8169_hw_reset(void *ioaddr) +{ + DBGP ( "rtl8169_hw_reset\n" ); + + /* Disable interrupts */ + rtl8169_irq_mask_and_ack(ioaddr); + + /* Reset the chipset */ + RTL_W8(ChipCmd, CmdReset); + + /* PCI commit */ + RTL_R8(ChipCmd); +} + +static void rtl_set_rx_tx_config_registers(struct rtl8169_private *tp) +{ + void *ioaddr = tp->mmio_addr; + u32 cfg = rtl8169_rx_config; + + DBGP ( "rtl_set_rx_tx_config_registers\n" ); + + cfg |= (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); + RTL_W32(RxConfig, cfg); + + /* Set DMA burst size and Interframe Gap Time */ + RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); +} + +static void rtl_soft_reset ( struct net_device *dev ) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + unsigned int i; + + DBGP ( "rtl_hw_soft_reset\n" ); + + /* Soft reset the chip. */ + RTL_W8(ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 0; i < 100; i++) { + if ((RTL_R8(ChipCmd) & CmdReset) == 0) + break; + mdelay ( 1 ); + } + + if ( i == 100 ) { + DBG ( "Reset Failed! (> 100 iterations)\n" ); + } +} + +static void rtl_hw_start ( struct net_device *dev ) +{ + struct rtl8169_private *tp = netdev_priv ( dev ); + + DBGP ( "rtl_hw_start\n" ); + + /* Soft reset NIC */ + rtl_soft_reset ( dev ); + + tp->hw_start ( dev ); +} + +static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp, + void *ioaddr) +{ + DBGP ( "rtl_set_rx_tx_desc_registers\n" ); + + /* + * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh + * register to be written before TxDescAddrLow to work. + * Switching from MMIO to I/O access fixes the issue as well. + */ + RTL_W32 ( TxDescStartAddrHigh, 0 ); + RTL_W32 ( TxDescStartAddrLow, virt_to_bus ( tp->tx_base ) ); + RTL_W32 ( RxDescAddrHigh, 0 ); + RTL_W32 ( RxDescAddrLow, virt_to_bus ( tp->rx_base ) ); +} + +static u16 rtl_rw_cpluscmd(void *ioaddr) +{ + u16 cmd; + + DBGP ( "rtl_rw_cpluscmd\n" ); + + cmd = RTL_R16(CPlusCmd); + RTL_W16(CPlusCmd, cmd); + return cmd; +} + +static void rtl_set_rx_max_size(void *ioaddr) +{ + DBGP ( "rtl_set_rx_max_size\n" ); + + RTL_W16 ( RxMaxSize, RX_BUF_SIZE ); +} + +static void rtl8169_set_magic_reg(void *ioaddr, unsigned mac_version) +{ + struct { + u32 mac_version; + u32 clk; + u32 val; + } cfg2_info [] = { + { RTL_GIGA_MAC_VER_05, PCI_Clock_33MHz, 0x000fff00 }, // 8110SCd + { RTL_GIGA_MAC_VER_05, PCI_Clock_66MHz, 0x000fffff }, + { RTL_GIGA_MAC_VER_06, PCI_Clock_33MHz, 0x00ffff00 }, // 8110SCe + { RTL_GIGA_MAC_VER_06, PCI_Clock_66MHz, 0x00ffffff } + }, *p = cfg2_info; + unsigned int i; + u32 clk; + + DBGP ( "rtl8169_set_magic_reg\n" ); + + clk = RTL_R8(Config2) & PCI_Clock_66MHz; + for (i = 0; i < ARRAY_SIZE(cfg2_info); i++, p++) { + if ((p->mac_version == mac_version) && (p->clk == clk)) { + RTL_W32(0x7c, p->val); + break; + } + } +} + +static void rtl_set_rx_mode ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + u32 tmp; + + DBGP ( "rtl_set_rx_mode\n" ); + + /* Accept all Multicast Packets */ + + RTL_W32 ( MAR0 + 0, 0xffffffff ); + RTL_W32 ( MAR0 + 4, 0xffffffff ); + + tmp = rtl8169_rx_config | AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + ( RTL_R32 ( RxConfig ) & rtl_chip_info[tp->chipset].RxConfigMask ); + + RTL_W32 ( RxConfig, tmp ); +} + +static void rtl_hw_start_8169(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + struct pci_device *pdev = tp->pci_dev; + + DBGP ( "rtl_hw_start_8169\n" ); + + if (tp->mac_version == RTL_GIGA_MAC_VER_05) { + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | PCIMulRW); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08); + } + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + if ((tp->mac_version == RTL_GIGA_MAC_VER_01) || + (tp->mac_version == RTL_GIGA_MAC_VER_02) || + (tp->mac_version == RTL_GIGA_MAC_VER_03) || + (tp->mac_version == RTL_GIGA_MAC_VER_04)) + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_set_rx_max_size(ioaddr); + + if ((tp->mac_version == RTL_GIGA_MAC_VER_01) || + (tp->mac_version == RTL_GIGA_MAC_VER_02) || + (tp->mac_version == RTL_GIGA_MAC_VER_03) || + (tp->mac_version == RTL_GIGA_MAC_VER_04)) + rtl_set_rx_tx_config_registers(tp); + + tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW; + + if ((tp->mac_version == RTL_GIGA_MAC_VER_02) || + (tp->mac_version == RTL_GIGA_MAC_VER_03)) { + DBG ( "Set MAC Reg C+CR Offset 0xE0. " + "Bit-3 and bit-14 MUST be 1\n" ); + tp->cp_cmd |= (1 << 14); + } + + RTL_W16(CPlusCmd, tp->cp_cmd); + + rtl8169_set_magic_reg(ioaddr, tp->mac_version); + + /* + * Undocumented corner. Supposedly: + * (TxTimer << 12) | (TxPackets << 8) | (RxTimer << 4) | RxPackets + */ + RTL_W16(IntrMitigate, 0x0000); + + rtl_set_rx_tx_desc_registers(tp, ioaddr); + + if ((tp->mac_version != RTL_GIGA_MAC_VER_01) && + (tp->mac_version != RTL_GIGA_MAC_VER_02) && + (tp->mac_version != RTL_GIGA_MAC_VER_03) && + (tp->mac_version != RTL_GIGA_MAC_VER_04)) { + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + rtl_set_rx_tx_config_registers(tp); + } + + RTL_W8(Cfg9346, Cfg9346_Lock); + + /* Initially a 10 us delay. Turned it into a PCI commit. - FR */ + RTL_R8(IntrMask); + + RTL_W32(RxMissed, 0); + + rtl_set_rx_mode(dev); + + /* no early-rx interrupts */ + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + + // RTL_W16(IntrMask, tp->intr_event); +} + +static void rtl_tx_performance_tweak(struct pci_device *pdev, u16 force) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + DBGP ( "rtl_tx_performance_tweak\n" ); + + if (cap) { + u16 ctl; + + pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl); + ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force; + pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl); + } +} + +static void rtl_csi_access_enable(void *ioaddr) +{ + u32 csi; + + DBGP ( "rtl_csi_access_enable\n" ); + + csi = rtl_csi_read(ioaddr, 0x070c) & 0x00ffffff; + rtl_csi_write(ioaddr, 0x070c, csi | 0x27000000); +} + +struct ephy_info { + unsigned int offset; + u16 mask; + u16 bits; +}; + +static void rtl_ephy_init(void *ioaddr, struct ephy_info *e, int len) +{ + u16 w; + + DBGP ( "rtl_ephy_init\n" ); + + while (len-- > 0) { + w = (rtl_ephy_read(ioaddr, e->offset) & ~e->mask) | e->bits; + rtl_ephy_write(ioaddr, e->offset, w); + e++; + } +} + +static void rtl_disable_clock_request(struct pci_device *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + DBGP ( "rtl_disable_clock_request\n" ); + + if (cap) { + u16 ctl; + + pci_read_config_word(pdev, cap + PCI_EXP_LNKCTL, &ctl); + ctl &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + pci_write_config_word(pdev, cap + PCI_EXP_LNKCTL, ctl); + } +} + +#define R8168_CPCMD_QUIRK_MASK (\ + EnableBist | \ + Mac_dbgo_oe | \ + Force_half_dup | \ + Force_rxflow_en | \ + Force_txflow_en | \ + Cxpl_dbg_sel | \ + ASF | \ + PktCntrDisable | \ + Mac_dbgo_sel) + +static void rtl_hw_start_8168bb(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168bb\n" ); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); + + rtl_tx_performance_tweak(pdev, + (0x5 << MAX_READ_REQUEST_SHIFT) | PCI_EXP_DEVCTL_NOSNOOP_EN); +} + +static void rtl_hw_start_8168bef(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168bef\n" ); + + rtl_hw_start_8168bb(ioaddr, pdev); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + RTL_W8(Config4, RTL_R8(Config4) & ~(1 << 0)); +} + +static void __rtl_hw_start_8168cp(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "__rtl_hw_start_8168cp\n" ); + + RTL_W8(Config1, RTL_R8(Config1) | Speed_down); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + rtl_disable_clock_request(pdev); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168cp_1(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8168cp[] = { + { 0x01, 0, 0x0001 }, + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0042 }, + { 0x06, 0x0080, 0x0000 }, + { 0x07, 0, 0x2000 } + }; + + DBGP ( "rtl_hw_start_8168cp_1\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_ephy_init(ioaddr, e_info_8168cp, ARRAY_SIZE(e_info_8168cp)); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168cp_2(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168cp_2\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168cp_3(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168cp_3\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + /* Magic. */ + RTL_W8(DBG_REG, 0x20); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168c_1(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8168c_1[] = { + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0002 }, + { 0x06, 0x0080, 0x0000 } + }; + + DBGP ( "rtl_hw_start_8168c_1\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2); + + rtl_ephy_init(ioaddr, e_info_8168c_1, ARRAY_SIZE(e_info_8168c_1)); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_2(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8168c_2[] = { + { 0x01, 0, 0x0001 }, + { 0x03, 0x0400, 0x0220 } + }; + + DBGP ( "rtl_hw_start_8168c_2\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_ephy_init(ioaddr, e_info_8168c_2, ARRAY_SIZE(e_info_8168c_2)); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_3(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168c_3\n" ); + + rtl_hw_start_8168c_2(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_4(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168c_4\n" ); + + rtl_csi_access_enable(ioaddr); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168d(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168d\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_disable_clock_request(pdev); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + struct pci_device *pdev = tp->pci_dev; + + DBGP ( "rtl_hw_start_8168\n" ); + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_set_rx_max_size(ioaddr); + + tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1; + + RTL_W16(CPlusCmd, tp->cp_cmd); + + RTL_W16(IntrMitigate, 0x5151); + + /* Work around for RxFIFO overflow. */ + if (tp->mac_version == RTL_GIGA_MAC_VER_11) { + tp->intr_event |= RxFIFOOver | PCSTimeout; + tp->intr_event &= ~RxOverflow; + } + + rtl_set_rx_tx_desc_registers(tp, ioaddr); + + rtl_set_rx_mode(dev); + + RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); + + RTL_R8(IntrMask); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_11: + rtl_hw_start_8168bb(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_12: + case RTL_GIGA_MAC_VER_17: + rtl_hw_start_8168bef(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_18: + rtl_hw_start_8168cp_1(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_19: + rtl_hw_start_8168c_1(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_20: + rtl_hw_start_8168c_2(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_21: + rtl_hw_start_8168c_3(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_22: + rtl_hw_start_8168c_4(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_23: + rtl_hw_start_8168cp_2(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_24: + rtl_hw_start_8168cp_3(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_25: + rtl_hw_start_8168d(ioaddr, pdev); + break; + default: + DBG ( "Unknown chipset (mac_version = %d).\n", + tp->mac_version ); + break; + } + + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(Cfg9346, Cfg9346_Lock); + + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + + // RTL_W16(IntrMask, tp->intr_event); +} + +#define R810X_CPCMD_QUIRK_MASK (\ + EnableBist | \ + Mac_dbgo_oe | \ + Force_half_dup | \ + Force_half_dup | \ + Force_txflow_en | \ + Cxpl_dbg_sel | \ + ASF | \ + PktCntrDisable | \ + PCIDAC | \ + PCIMulRW) + +static void rtl_hw_start_8102e_1(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8102e_1[] = { + { 0x01, 0, 0x6e65 }, + { 0x02, 0, 0x091f }, + { 0x03, 0, 0xc2f9 }, + { 0x06, 0, 0xafb5 }, + { 0x07, 0, 0x0e00 }, + { 0x19, 0, 0xec80 }, + { 0x01, 0, 0x2e65 }, + { 0x01, 0, 0x6e65 } + }; + u8 cfg1; + + DBGP ( "rtl_hw_start_8102e_1\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(DBG_REG, FIX_NAK_1); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W8(Config1, + LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + cfg1 = RTL_R8(Config1); + if ((cfg1 & LEDS0) && (cfg1 & LEDS1)) + RTL_W8(Config1, cfg1 & ~LEDS0); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); + + rtl_ephy_init(ioaddr, e_info_8102e_1, ARRAY_SIZE(e_info_8102e_1)); +} + +static void rtl_hw_start_8102e_2(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8102e_2\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W8(Config1, MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8102e_3(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8102e_3\n" ); + + rtl_hw_start_8102e_2(ioaddr, pdev); + + rtl_ephy_write(ioaddr, 0x03, 0xc2f9); +} + +static void rtl_hw_start_8101(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + struct pci_device *pdev = tp->pci_dev; + + DBGP ( "rtl_hw_start_8101\n" ); + + if ((tp->mac_version == RTL_GIGA_MAC_VER_13) || + (tp->mac_version == RTL_GIGA_MAC_VER_16)) { + int cap = tp->pcie_cap; + + if (cap) { + pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_NOSNOOP_EN); + } + } + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_07: + rtl_hw_start_8102e_1(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_08: + rtl_hw_start_8102e_3(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_09: + rtl_hw_start_8102e_2(ioaddr, pdev); + break; + } + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_set_rx_max_size(ioaddr); + + tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW; + + RTL_W16(CPlusCmd, tp->cp_cmd); + + RTL_W16(IntrMitigate, 0x0000); + + rtl_set_rx_tx_desc_registers(tp, ioaddr); + + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + rtl_set_rx_tx_config_registers(tp); + + RTL_W8(Cfg9346, Cfg9346_Lock); + + RTL_R8(IntrMask); + + rtl_set_rx_mode(dev); + + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000); + + // RTL_W16(IntrMask, tp->intr_event); +} + +/*** gPXE API Support Routines ***/ + +/** + * setup_tx_resources - allocate tx resources (descriptors) + * + * @v tp Driver private storage + * + * @ret rc Returns 0 on success, negative on failure + **/ +static int +rtl8169_setup_tx_resources ( struct rtl8169_private *tp ) +{ + DBGP ( "rtl8169_setup_tx_resources\n" ); + + tp->tx_base = malloc_dma ( R8169_TX_RING_BYTES, TX_RING_ALIGN ); + + if ( ! tp->tx_base ) { + return -ENOMEM; + } + + memset ( tp->tx_base, 0, R8169_TX_RING_BYTES ); + + DBG ( "tp->tx_base = %#08lx\n", virt_to_bus ( tp->tx_base ) ); + + tp->tx_fill_ctr = 0; + tp->tx_curr = 0; + tp->tx_tail = 0; + + return 0; +} + +static void +rtl8169_process_tx_packets ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + + uint32_t tx_status; + struct TxDesc *tx_curr_desc; + + DBGP ( "rtl8169_process_tx_packets\n" ); + + while ( tp->tx_tail != tp->tx_curr ) { + + tx_curr_desc = tp->tx_base + tp->tx_tail; + + tx_status = tx_curr_desc->opts1; + + DBG2 ( "Before DescOwn check tx_status: %#08lx\n", tx_status ); + + /* if the packet at tx_tail is not owned by hardware it is for us */ + if ( tx_status & DescOwn ) + break; + + DBG ( "Transmitted packet.\n" ); + DBG ( "tp->tx_fill_ctr = %ld\n", tp->tx_fill_ctr ); + DBG ( "tp->tx_tail = %ld\n", tp->tx_tail ); + DBG ( "tp->tx_curr = %ld\n", tp->tx_curr ); + DBG ( "tx_status = %ld\n", tx_status ); + DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) ); + + /* Pass packet to core for processing */ + netdev_tx_complete ( netdev, tp->tx_iobuf[tp->tx_tail] ); + + memset ( tx_curr_desc, 0, sizeof ( *tx_curr_desc ) ); + + /* Decrement count of used descriptors */ + tp->tx_fill_ctr--; + + /* Increment sent packets index */ + tp->tx_tail = ( tp->tx_tail + 1 ) % NUM_TX_DESC; + } +} + +static void +rtl8169_free_tx_resources ( struct rtl8169_private *tp ) +{ + DBGP ( "rtl8169_free_tx_resources\n" ); + + free_dma ( tp->tx_base, R8169_TX_RING_BYTES ); +} + +static void +rtl8169_populate_rx_descriptor ( struct rtl8169_private *tp, struct RxDesc *rx_desc, uint32_t index ) +{ + DBGP ( "rtl8169_populate_rx_descriptor\n" ); + + DBG ( "Populating rx descriptor %ld\n", index ); + + memset ( rx_desc, 0, sizeof ( *rx_desc ) ); + + rx_desc->addr_hi = 0; + rx_desc->addr_lo = virt_to_bus ( tp->rx_iobuf[index]->data ); + rx_desc->opts2 = 0; + rx_desc->opts1 = ( index == ( NUM_RX_DESC - 1 ) ? RingEnd : 0 ) | + RX_BUF_SIZE; + rx_desc->opts1 |= DescOwn; +} + +/** + * Refill descriptor ring + * + * @v netdev Net device + */ +static void rtl8169_refill_rx_ring ( struct rtl8169_private *tp ) +{ + struct RxDesc *rx_curr_desc; + int i; + + DBGP ( "rtl8169_refill_rx_ring\n" ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + + rx_curr_desc = ( tp->rx_base ) + i; + + /* Don't touch descriptors owned by the NIC */ + if ( rx_curr_desc->opts1 & DescOwn ) + continue; + + /* Don't touch descriptors with iobufs, they still need to be + processed by the poll routine */ + if ( tp->rx_iobuf[tp->rx_curr] != NULL ) + continue; + + /** If we can't get an iobuf for this descriptor + try again later (next poll). + */ + if ( ! ( tp->rx_iobuf[i] = alloc_iob ( RX_BUF_SIZE ) ) ) { + DBG ( "Refill rx ring failed!!\n" ); + break; + } + + rtl8169_populate_rx_descriptor ( tp, rx_curr_desc, i ); + } +} + +/** + * setup_rx_resources - allocate Rx resources (Descriptors) + * + * @v tp: Driver private structure + * + * @ret rc Returns 0 on success, negative on failure + * + **/ +static int +rtl8169_setup_rx_resources ( struct rtl8169_private *tp ) +{ + DBGP ( "rtl8169_setup_rx_resources\n" ); + + tp->rx_base = malloc_dma ( R8169_RX_RING_BYTES, RX_RING_ALIGN ); + + DBG ( "tp->rx_base = %#08lx\n", virt_to_bus ( tp->rx_base ) ); + + if ( ! tp->rx_base ) { + return -ENOMEM; + } + memset ( tp->rx_base, 0, R8169_RX_RING_BYTES ); + + rtl8169_refill_rx_ring ( tp ); + + tp->rx_curr = 0; + + return 0; +} + +static void +rtl8169_process_rx_packets ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + uint32_t rx_status; + uint16_t rx_len; + struct RxDesc *rx_curr_desc; + int i; + + DBGP ( "rtl8169_process_rx_packets\n" ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + + rx_curr_desc = tp->rx_base + tp->rx_curr; + + rx_status = rx_curr_desc->opts1; + + DBG2 ( "Before DescOwn check rx_status: %#08lx\n", rx_status ); + + /* Hardware still owns the descriptor */ + if ( rx_status & DescOwn ) + break; + + /* We own the descriptor, but it has not been refilled yet */ + if ( tp->rx_iobuf[tp->rx_curr] == NULL ) + break; + + rx_len = rx_status & 0x3fff; + + DBG ( "Received packet.\n" ); + DBG ( "tp->rx_curr = %ld\n", tp->rx_curr ); + DBG ( "rx_len = %d\n", rx_len ); + DBG ( "rx_status = %#08lx\n", rx_status ); + DBG ( "rx_curr_desc = %#08lx\n", virt_to_bus ( rx_curr_desc ) ); + + if ( rx_status & RxRES ) { + + netdev_rx_err ( netdev, tp->rx_iobuf[tp->rx_curr], -EINVAL ); + + DBG ( "rtl8169_poll: Corrupted packet received!\n" + " rx_status: %#08lx\n", rx_status ); + + } else { + + /* Adjust size of the iobuf to reflect received data */ + iob_put ( tp->rx_iobuf[tp->rx_curr], rx_len ); + + /* Add this packet to the receive queue. */ + netdev_rx ( netdev, tp->rx_iobuf[tp->rx_curr] ); + } + + /* Invalidate this iobuf and descriptor */ + tp->rx_iobuf[tp->rx_curr] = NULL; + memset ( rx_curr_desc, 0, sizeof ( *rx_curr_desc ) ); + + /* Update pointer to next available rx descriptor */ + tp->rx_curr = ( tp->rx_curr + 1 ) % NUM_RX_DESC; + } + rtl8169_refill_rx_ring ( tp ); +} + +static void +rtl8169_free_rx_resources ( struct rtl8169_private *tp ) +{ + int i; + + DBGP ( "rtl8169_free_rx_resources\n" ); + + free_dma ( tp->rx_base, R8169_RX_RING_BYTES ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + free_iob ( tp->rx_iobuf[i] ); + tp->rx_iobuf[i] = NULL; + } +} + +/** + FIXME: Because gPXE's pci_device_id structure does not contain a + field to contain arbitrary data, we need the following table to + associate PCI IDs with nic variants, because a lot of driver + routines depend on knowing which kind of variant they are dealing + with. --mdc + **/ + +#define _R(VENDOR,DEVICE,INDEX) \ + { .vendor = VENDOR, .device = DEVICE, .index = INDEX } + +static const struct { + uint16_t vendor; + uint16_t device; + int index; +} nic_variant_table[] = { + _R(0x10ec, 0x8129, RTL_CFG_0), + _R(0x10ec, 0x8136, RTL_CFG_2), + _R(0x10ec, 0x8167, RTL_CFG_0), + _R(0x10ec, 0x8168, RTL_CFG_1), + _R(0x10ec, 0x8169, RTL_CFG_0), + _R(0x1186, 0x4300, RTL_CFG_0), + _R(0x1259, 0xc107, RTL_CFG_0), + _R(0x16ec, 0x0116, RTL_CFG_0), + _R(0x1737, 0x1032, RTL_CFG_0), + _R(0x0001, 0x8168, RTL_CFG_2), +}; +#undef _R + +static int +rtl8169_get_nic_variant ( uint16_t vendor, uint16_t device ) +{ + u32 i; + + DBGP ( "rtl8169_get_nic_variant\n" ); + + for (i = 0; i < ARRAY_SIZE(nic_variant_table); i++) { + if ( ( nic_variant_table[i].vendor == vendor ) && + ( nic_variant_table[i].device == device ) ) { + return ( nic_variant_table[i].index ); + } + } + DBG ( "No matching NIC variant found!\n" ); + return ( RTL_CFG_0 ); +} + +static void rtl8169_irq_enable ( struct rtl8169_private *tp ) +{ + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_irq_enable\n" ); + + RTL_W16 ( IntrMask, tp->intr_event ); +} + +static void rtl8169_irq_disable ( struct rtl8169_private *tp ) +{ + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_irq_disable\n" ); + + rtl8169_irq_mask_and_ack ( ioaddr ); +} + +/*** gPXE Core API Routines ***/ + +/** + * open - Called when a network interface is made active + * + * @v netdev network interface device structure + * @ret rc Return status code, 0 on success, negative value on failure + * + **/ +static int +rtl8169_open ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + int rc; + + DBGP ( "rtl8169_open\n" ); + + /* allocate transmit descriptors */ + rc = rtl8169_setup_tx_resources ( tp ); + if ( rc ) { + DBG ( "Error setting up TX resources!\n" ); + goto err_setup_tx; + } + + /* allocate receive descriptors */ + rc = rtl8169_setup_rx_resources ( tp ); + if ( rc ) { + DBG ( "Error setting up RX resources!\n" ); + goto err_setup_rx; + } + + rtl_hw_start ( netdev ); + + DBG ( "TxDescStartAddrHigh = %#08lx\n", RTL_R32 ( TxDescStartAddrHigh ) ); + DBG ( "TxDescStartAddrLow = %#08lx\n", RTL_R32 ( TxDescStartAddrLow ) ); + DBG ( "RxDescAddrHigh = %#08lx\n", RTL_R32 ( RxDescAddrHigh ) ); + DBG ( "RxDescAddrLow = %#08lx\n", RTL_R32 ( RxDescAddrLow ) ); + + return 0; + +err_setup_rx: + rtl8169_free_tx_resources ( tp ); +err_setup_tx: + rtl8169_hw_reset ( ioaddr ); + + return rc; +} + +/** + * transmit - Transmit a packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * + * @ret rc Returns 0 on success, negative on failure + */ +static int +rtl8169_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + uint32_t tx_len = iob_len ( iobuf ); + + struct TxDesc *tx_curr_desc; + + DBGP ("rtl8169_transmit\n"); + + if ( tp->tx_fill_ctr == NUM_TX_DESC ) { + DBG ("TX overflow\n"); + return -ENOBUFS; + } + + /** + * The rtl8169 family automatically pads short packets to a + * minimum size, but if it did not, like some older cards, + * we could do: + * iob_pad ( iobuf, ETH_ZLEN ); + */ + + /* Save pointer to this iobuf we have been given to transmit so + we can pass it to netdev_tx_complete() later */ + tp->tx_iobuf[tp->tx_curr] = iobuf; + + tx_curr_desc = tp->tx_base + tp->tx_curr; + + DBG ( "tp->tx_fill_ctr = %ld\n", tp->tx_fill_ctr ); + DBG ( "tp->tx_curr = %ld\n", tp->tx_curr ); + DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) ); + DBG ( "iobuf->data = %#08lx\n", virt_to_bus ( iobuf->data ) ); + DBG ( "tx_len = %ld\n", tx_len ); + + /* Configure current descriptor to transmit supplied packet */ + tx_curr_desc->addr_hi = 0; + tx_curr_desc->addr_lo = virt_to_bus ( iobuf->data ); + tx_curr_desc->opts2 = 0; + tx_curr_desc->opts1 = FirstFrag | LastFrag | + ( tp->tx_curr == ( NUM_TX_DESC - 1 ) ? RingEnd : 0 ) | + tx_len; + + /* Mark descriptor as owned by NIC */ + tx_curr_desc->opts1 |= DescOwn; + + DBG ( "tx_curr_desc->opts1 = %#08lx\n", tx_curr_desc->opts1 ); + DBG ( "tx_curr_desc->opts2 = %#08lx\n", tx_curr_desc->opts2 ); + DBG ( "tx_curr_desc->addr_hi = %#08lx\n", tx_curr_desc->addr_hi ); + DBG ( "tx_curr_desc->addr_lo = %#08lx\n", tx_curr_desc->addr_lo ); + + RTL_W8 ( TxPoll, NPQ ); /* set polling bit */ + + /* Point to next free descriptor */ + tp->tx_curr = ( tp->tx_curr + 1 ) % NUM_TX_DESC; + + /* Increment number of tx descriptors in use */ + tp->tx_fill_ctr++; + + return 0; +} + +/** + * poll - Poll for received packets + * + * @v netdev Network device + */ +static void +rtl8169_poll ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + + uint16_t intr_status; + uint16_t intr_mask; + + DBGP ( "rtl8169_poll\n" ); + + intr_status = RTL_R16 ( IntrStatus ); + intr_mask = RTL_R16 ( IntrMask ); + + DBG2 ( "rtl8169_poll (before): intr_mask = %#04x intr_status = %#04x\n", + intr_mask, intr_status ); + + RTL_W16 ( IntrStatus, 0xffff ); + + /* hotplug / major error / no more work / shared irq */ + if ( intr_status == 0xffff ) + return; + + /* Process transmitted packets */ + rtl8169_process_tx_packets ( netdev ); + + /* Process received packets */ + rtl8169_process_rx_packets ( netdev ); +} + +/** + * close - Disable network interface + * + * @v netdev network interface device structure + * + **/ +static void +rtl8169_close ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + + DBGP ( "r8169_close\n" ); + + rtl8169_hw_reset ( ioaddr ); + + rtl8169_free_tx_resources ( tp ); + rtl8169_free_rx_resources ( tp ); +} + +/** + * irq - enable or Disable interrupts + * + * @v netdev network adapter + * @v action requested interrupt action + * + **/ +static void +rtl8169_irq ( struct net_device *netdev, int action ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + + DBGP ( "rtl8169_irq\n" ); + + switch ( action ) { + case 0 : + rtl8169_irq_disable ( tp ); + break; + default : + rtl8169_irq_enable ( tp ); break; } } -DRIVER ( "r8169/PCI", nic_driver, pci_driver, r8169_driver, - r8169_probe, r8169_disable ); +static struct net_device_operations rtl8169_operations = { + .open = rtl8169_open, + .transmit = rtl8169_transmit, + .poll = rtl8169_poll, + .close = rtl8169_close, + .irq = rtl8169_irq, +}; + +/** + * probe - Initial configuration of NIC + * + * @v pci PCI device + * @v id PCI IDs + * + * @ret rc Return status code + **/ +static int +rtl8169_probe ( struct pci_device *pdev, const struct pci_device_id *ent ) +{ + int i, rc; + struct net_device *netdev; + struct rtl8169_private *tp; + void *ioaddr; + + /** FIXME: This lookup is necessary because gPXE does not have a "data" + element in the structure pci_device_id which can pass an arbitrary + piece of data to the driver. It might be useful to add it. Then we + could just use ent->data instead of having to look up cfg_index. + **/ + int cfg_index = rtl8169_get_nic_variant ( ent->vendor, ent->device ); + const struct rtl_cfg_info *cfg = rtl_cfg_infos + cfg_index; + + DBGP ( "rtl8169_probe\n" ); + + DBG ( "ent->vendor = %#04x, ent->device = %#04x\n", ent->vendor, ent->device ); + + DBG ( "cfg_index = %d\n", cfg_index ); + DBG ( "cfg->intr_event = %#04x\n", cfg->intr_event ); + + rc = -ENOMEM; + + /* Allocate net device ( also allocates memory for netdev->priv + and makes netdev-priv point to it ) + */ + netdev = alloc_etherdev ( sizeof ( *tp ) ); + + if ( ! netdev ) + goto err_alloc_etherdev; + + /* Associate driver-specific network operations with + generic network device layer + */ + netdev_init ( netdev, &rtl8169_operations ); + + /* Associate this network device with the given PCI device */ + pci_set_drvdata ( pdev, netdev ); + netdev->dev = &pdev->dev; + + /* Initialize driver private storage */ + tp = netdev_priv ( netdev ); + memset ( tp, 0, ( sizeof ( *tp ) ) ); + + tp->pci_dev = pdev; + tp->irqno = pdev->irq; + tp->netdev = netdev; + tp->cfg_index = cfg_index; + tp->intr_event = cfg->intr_event; + tp->cp_cmd = PCIMulRW; + + tp->hw_start = cfg->hw_start; + + rc = -EIO; + + adjust_pci_device ( pdev ); + + /* ioremap MMIO region */ + ioaddr = ioremap ( pdev->membase, R8169_REGS_SIZE ); + + if ( ! ioaddr ) { + DBG ( "cannot remap MMIO\n" ); + rc = -EIO; + goto err_ioremap; + } + + tp->mmio_addr = ioaddr; + + tp->pcie_cap = pci_find_capability ( pdev, PCI_CAP_ID_EXP ); + if ( tp->pcie_cap ) { + DBG ( "PCI Express capability\n" ); + } else { + DBG ( "No PCI Express capability\n" ); + } + + /* Mask interrupts just in case */ + rtl8169_irq_mask_and_ack ( ioaddr ); + + /* Soft reset NIC */ + rtl_soft_reset ( netdev ); + + /* Identify chip attached to board */ + rtl8169_get_mac_version ( tp, ioaddr ); + + for ( i = 0; (u32) i < ARRAY_SIZE ( rtl_chip_info ); i++ ) { + if ( tp->mac_version == rtl_chip_info[i].mac_version ) + break; + } + if ( i == ARRAY_SIZE(rtl_chip_info ) ) { + /* Unknown chip: assume array element #0, original RTL-8169 */ + DBG ( "Unknown chip version, assuming %s\n", rtl_chip_info[0].name ); + i = 0; + } + tp->chipset = i; + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_06) && + (RTL_R8(PHYstatus) & TBI_Enable)) { + tp->set_speed = rtl8169_set_speed_tbi; + tp->phy_reset_enable = rtl8169_tbi_reset_enable; + tp->phy_reset_pending = rtl8169_tbi_reset_pending; + tp->link_ok = rtl8169_tbi_link_ok; + + tp->phy_1000_ctrl_reg = ADVERTISE_1000FULL; /* Implied by TBI */ + } else { + tp->set_speed = rtl8169_set_speed_xmii; + tp->phy_reset_enable = rtl8169_xmii_reset_enable; + tp->phy_reset_pending = rtl8169_xmii_reset_pending; + tp->link_ok = rtl8169_xmii_link_ok; + } + + /* Get MAC address */ + for ( i = 0; i < MAC_ADDR_LEN; i++ ) + netdev->ll_addr[i] = RTL_R8 ( MAC0 + i ); + + DBG ( "%s\n", eth_ntoa ( netdev->ll_addr ) ); + + rtl8169_init_phy ( netdev, tp ); + + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + /* Mark as link up; we don't yet handle link state */ + netdev_link_up ( netdev ); + + DBG ( "rtl8169_probe succeeded!\n" ); + + /* No errors, return success */ + return 0; + +/* Error return paths */ +err_register: +err_ioremap: + netdev_put ( netdev ); +err_alloc_etherdev: + return rc; +} + +/** + * remove - Device Removal Routine + * + * @v pdev PCI device information struct + * + **/ +static void +rtl8169_remove ( struct pci_device *pdev ) +{ + struct net_device *netdev = pci_get_drvdata ( pdev ); + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_remove\n" ); + + rtl8169_hw_reset ( ioaddr ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +static struct pci_device_id rtl8169_nics[] = { + PCI_ROM(0x10ec, 0x8129, "rtl8169-0x8129", "rtl8169-0x8129"), + PCI_ROM(0x10ec, 0x8136, "rtl8169-0x8136", "rtl8169-0x8136"), + PCI_ROM(0x10ec, 0x8167, "rtl8169-0x8167", "rtl8169-0x8167"), + PCI_ROM(0x10ec, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168"), + PCI_ROM(0x10ec, 0x8169, "rtl8169-0x8169", "rtl8169-0x8169"), + PCI_ROM(0x1186, 0x4300, "rtl8169-0x4300", "rtl8169-0x4300"), + PCI_ROM(0x1259, 0xc107, "rtl8169-0xc107", "rtl8169-0xc107"), + PCI_ROM(0x16ec, 0x0116, "rtl8169-0x0116", "rtl8169-0x0116"), + PCI_ROM(0x1737, 0x1032, "rtl8169-0x1032", "rtl8169-0x1032"), + PCI_ROM(0x0001, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168"), +}; + +struct pci_driver rtl8169_driver __pci_driver = { + .ids = rtl8169_nics, + .id_count = ( sizeof ( rtl8169_nics ) / sizeof ( rtl8169_nics[0] ) ), + .probe = rtl8169_probe, + .remove = rtl8169_remove, +}; /* * Local variables: diff --git a/src/drivers/net/r8169.h b/src/drivers/net/r8169.h new file mode 100644 index 0000000..d353632 --- /dev/null +++ b/src/drivers/net/r8169.h @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2008 Marty Connor <mdc@etherboot.org> + * Copyright (c) 2008 Entity Cyber, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver is based on rtl8169 data sheets and work by: + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + */ + +#ifndef _R8169_H_ +#define _R8169_H_ + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/** FIXME: include/linux/pci_regs.h has these PCI regs, maybe + we need such a file in gPXE? +**/ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ + +/** FIXME: update mii.h in src/include/mii.h from Linux sources + so we don't have to include these definitiions. +**/ +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_10000 10000 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 + +/* Generic MII registers. */ + +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ +#define MII_EXPANSION 0x06 /* Expansion register */ +#define MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_ESTATUS 0x0f /* Extended Status */ +#define MII_DCOUNTER 0x12 /* Disconnect counter */ +#define MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define MII_SREVISION 0x16 /* Silicon revision */ +#define MII_RESV1 0x17 /* Reserved... */ +#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define MII_PHYADDR 0x19 /* PHY address */ +#define MII_RESV2 0x1a /* Reserved... */ +#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ +#define BMCR_RESV 0x003f /* Unused... */ +#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ +#define BMCR_CTST 0x0080 /* Collision test */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* Basic mode status register. */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_JCD 0x0002 /* Jabber detected */ +#define BMSR_LSTATUS 0x0004 /* Link status */ +#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define BMSR_RESV 0x00c0 /* Unused... */ +#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ +#define BMSR_100HALF2 0x0200 /* Can do 100BASE-T2 HDX */ +#define BMSR_100FULL2 0x0400 /* Can do 100BASE-T2 FDX */ +#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ + +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define ADVERTISE_SLCT 0x001f /* Selector bits */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ +#define ADVERTISE_RESV 0x1000 /* Unused... */ +#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define ADVERTISE_NPAGE 0x8000 /* Next page bit */ +#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ + ADVERTISE_CSMA) +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* 1000BASE-T Control register */ +#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ + +/* MAC address length */ +#define MAC_ADDR_LEN 6 + +#define MAX_READ_REQUEST_SHIFT 12 +#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */ +#define RxPacketMaxSize 0x3FE8 /* 16K - 1 - ETH_HLEN - VLAN - CRC... */ +#define SafeMtu 0x1c20 /* ... actually life sucks beyond ~7k */ +#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ + +#define R8169_REGS_SIZE 256 +#define R8169_NAPI_WEIGHT 64 +#define NUM_TX_DESC 8 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 8 /* Number of Rx descriptor registers */ +#define RX_BUF_SIZE 1536 /* Rx Buffer size */ +#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) +#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) + +#define TX_RING_ALIGN 256 +#define RX_RING_ALIGN 256 + +#define RTL8169_TX_TIMEOUT (6*HZ) +#define RTL8169_PHY_TIMEOUT (10*HZ) + +#define RTL_EEPROM_SIG cpu_to_le32(0x8129) +#define RTL_EEPROM_SIG_MASK cpu_to_le32(0xffff) +#define RTL_EEPROM_SIG_ADDR 0x0000 + +/* write/read MMIO register */ +#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg)) +#define RTL_R8(reg) readb (ioaddr + (reg)) +#define RTL_R16(reg) readw (ioaddr + (reg)) +#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) + +enum mac_version { + RTL_GIGA_MAC_VER_01 = 0x01, // 8169 + RTL_GIGA_MAC_VER_02 = 0x02, // 8169S + RTL_GIGA_MAC_VER_03 = 0x03, // 8110S + RTL_GIGA_MAC_VER_04 = 0x04, // 8169SB + RTL_GIGA_MAC_VER_05 = 0x05, // 8110SCd + RTL_GIGA_MAC_VER_06 = 0x06, // 8110SCe + RTL_GIGA_MAC_VER_07 = 0x07, // 8102e + RTL_GIGA_MAC_VER_08 = 0x08, // 8102e + RTL_GIGA_MAC_VER_09 = 0x09, // 8102e + RTL_GIGA_MAC_VER_10 = 0x0a, // 8101e + RTL_GIGA_MAC_VER_11 = 0x0b, // 8168Bb + RTL_GIGA_MAC_VER_12 = 0x0c, // 8168Be + RTL_GIGA_MAC_VER_13 = 0x0d, // 8101Eb + RTL_GIGA_MAC_VER_14 = 0x0e, // 8101 ? + RTL_GIGA_MAC_VER_15 = 0x0f, // 8101 ? + RTL_GIGA_MAC_VER_16 = 0x11, // 8101Ec + RTL_GIGA_MAC_VER_17 = 0x10, // 8168Bf + RTL_GIGA_MAC_VER_18 = 0x12, // 8168CP + RTL_GIGA_MAC_VER_19 = 0x13, // 8168C + RTL_GIGA_MAC_VER_20 = 0x14, // 8168C + RTL_GIGA_MAC_VER_21 = 0x15, // 8168C + RTL_GIGA_MAC_VER_22 = 0x16, // 8168C + RTL_GIGA_MAC_VER_23 = 0x17, // 8168CP + RTL_GIGA_MAC_VER_24 = 0x18, // 8168CP + RTL_GIGA_MAC_VER_25 = 0x19, // 8168D +}; + +#define _R(NAME,MAC,MASK) \ + { .name = NAME, .mac_version = MAC, .RxConfigMask = MASK } + +static const struct { + const char *name; + u8 mac_version; + u32 RxConfigMask; /* Clears the bits supported by this chip */ +} rtl_chip_info[] = { + _R("RTL8169", RTL_GIGA_MAC_VER_01, 0xff7e1880), // 8169 + _R("RTL8169s", RTL_GIGA_MAC_VER_02, 0xff7e1880), // 8169S + _R("RTL8110s", RTL_GIGA_MAC_VER_03, 0xff7e1880), // 8110S + _R("RTL8169sb/8110sb", RTL_GIGA_MAC_VER_04, 0xff7e1880), // 8169SB + _R("RTL8169sc/8110sc", RTL_GIGA_MAC_VER_05, 0xff7e1880), // 8110SCd + _R("RTL8169sc/8110sc", RTL_GIGA_MAC_VER_06, 0xff7e1880), // 8110SCe + _R("RTL8102e", RTL_GIGA_MAC_VER_07, 0xff7e1880), // PCI-E + _R("RTL8102e", RTL_GIGA_MAC_VER_08, 0xff7e1880), // PCI-E + _R("RTL8102e", RTL_GIGA_MAC_VER_09, 0xff7e1880), // PCI-E + _R("RTL8101e", RTL_GIGA_MAC_VER_10, 0xff7e1880), // PCI-E + _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_11, 0xff7e1880), // PCI-E + _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_12, 0xff7e1880), // PCI-E + _R("RTL8101e", RTL_GIGA_MAC_VER_13, 0xff7e1880), // PCI-E 8139 + _R("RTL8100e", RTL_GIGA_MAC_VER_14, 0xff7e1880), // PCI-E 8139 + _R("RTL8100e", RTL_GIGA_MAC_VER_15, 0xff7e1880), // PCI-E 8139 + _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_17, 0xff7e1880), // PCI-E + _R("RTL8101e", RTL_GIGA_MAC_VER_16, 0xff7e1880), // PCI-E + _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_18, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_19, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_20, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_21, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_22, 0xff7e1880), // PCI-E + _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_23, 0xff7e1880), // PCI-E + _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_24, 0xff7e1880), // PCI-E + _R("RTL8168d/8111d", RTL_GIGA_MAC_VER_25, 0xff7e1880) // PCI-E +}; +#undef _R + +enum cfg_version { + RTL_CFG_0 = 0x00, + RTL_CFG_1, + RTL_CFG_2 +}; + +#if 0 +/** Device Table from Linux Driver **/ +static struct pci_device_id rtl8169_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8129), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8136), 0, 0, RTL_CFG_2 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), 0, 0, RTL_CFG_1 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4300), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_AT, 0xc107), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(0x16ec, 0x0116), 0, 0, RTL_CFG_0 }, + { PCI_VENDOR_ID_LINKSYS, 0x1032, + PCI_ANY_ID, 0x0024, 0, 0, RTL_CFG_0 }, + { 0x0001, 0x8168, + PCI_ANY_ID, 0x2410, 0, 0, RTL_CFG_2 }, + {0,}, +}; +#endif + +enum rtl_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAC4 = 4, + MAR0 = 8, /* Multicast filter. */ + CounterAddrLow = 0x10, + CounterAddrHigh = 0x14, + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, + FLASH = 0x30, + ERSR = 0x36, + ChipCmd = 0x37, + TxPoll = 0x38, + IntrMask = 0x3c, + IntrStatus = 0x3e, + TxConfig = 0x40, + RxConfig = 0x44, + RxMissed = 0x4c, + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + Config2 = 0x53, + Config3 = 0x54, + Config4 = 0x55, + Config5 = 0x56, + MultiIntr = 0x5c, + PHYAR = 0x60, + PHYstatus = 0x6c, + RxMaxSize = 0xda, + CPlusCmd = 0xe0, + IntrMitigate = 0xe2, + RxDescAddrLow = 0xe4, + RxDescAddrHigh = 0xe8, + EarlyTxThres = 0xec, + FuncEvent = 0xf0, + FuncEventMask = 0xf4, + FuncPresetState = 0xf8, + FuncForceEvent = 0xfc, +}; + +enum rtl8110_registers { + TBICSR = 0x64, + TBI_ANAR = 0x68, + TBI_LPAR = 0x6a, +}; + +enum rtl8168_8101_registers { + CSIDR = 0x64, + CSIAR = 0x68, +#define CSIAR_FLAG 0x80000000 +#define CSIAR_WRITE_CMD 0x80000000 +#define CSIAR_BYTE_ENABLE 0x0f +#define CSIAR_BYTE_ENABLE_SHIFT 12 +#define CSIAR_ADDR_MASK 0x0fff + + EPHYAR = 0x80, +#define EPHYAR_FLAG 0x80000000 +#define EPHYAR_WRITE_CMD 0x80000000 +#define EPHYAR_REG_MASK 0x1f +#define EPHYAR_REG_SHIFT 16 +#define EPHYAR_DATA_MASK 0xffff + DBG_REG = 0xd1, +#define FIX_NAK_1 (1 << 4) +#define FIX_NAK_2 (1 << 3) +}; + +enum rtl_register_content { + /* InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x0080, + RxFIFOOver = 0x0040, + LinkChg = 0x0020, + RxOverflow = 0x0010, + TxErr = 0x0008, + TxOK = 0x0004, + RxErr = 0x0002, + RxOK = 0x0001, + + /* RxStatusDesc */ + RxFOVF = (1 << 23), + RxRWT = (1 << 22), + RxRES = (1 << 21), + RxRUNT = (1 << 20), + RxCRC = (1 << 19), + + /* ChipCmdBits */ + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, + + /* TXPoll register p.5 */ + HPQ = 0x80, /* Poll cmd on the high prio queue */ + NPQ = 0x40, /* Poll cmd on the low prio queue */ + FSWInt = 0x01, /* Forced software interrupt */ + + /* Cfg9346Bits */ + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xc0, + + /* rx_mode_bits */ + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, + + /* RxConfigBits */ + RxCfgFIFOShift = 13, + RxCfgDMAShift = 8, + + /* TxConfigBits */ + TxInterFrameGapShift = 24, + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + + /* Config1 register p.24 */ + LEDS1 = (1 << 7), + LEDS0 = (1 << 6), + MSIEnable = (1 << 5), /* Enable Message Signaled Interrupt */ + Speed_down = (1 << 4), + MEMMAP = (1 << 3), + IOMAP = (1 << 2), + VPD = (1 << 1), + PMEnable = (1 << 0), /* Power Management Enable */ + + /* Config2 register p. 25 */ + PCI_Clock_66MHz = 0x01, + PCI_Clock_33MHz = 0x00, + + /* Config3 register p.25 */ + MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ + LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ + Beacon_en = (1 << 0), /* 8168 only. Reserved in the 8168b */ + + /* Config5 register p.27 */ + BWF = (1 << 6), /* Accept Broadcast wakeup frame */ + MWF = (1 << 5), /* Accept Multicast wakeup frame */ + UWF = (1 << 4), /* Accept Unicast wakeup frame */ + LanWake = (1 << 1), /* LanWake enable/disable */ + PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ + + /* TBICSR p.28 */ + TBIReset = 0x80000000, + TBILoopback = 0x40000000, + TBINwEnable = 0x20000000, + TBINwRestart = 0x10000000, + TBILinkOk = 0x02000000, + TBINwComplete = 0x01000000, + + /* CPlusCmd p.31 */ + EnableBist = (1 << 15), // 8168 8101 + Mac_dbgo_oe = (1 << 14), // 8168 8101 + Normal_mode = (1 << 13), // unused + Force_half_dup = (1 << 12), // 8168 8101 + Force_rxflow_en = (1 << 11), // 8168 8101 + Force_txflow_en = (1 << 10), // 8168 8101 + Cxpl_dbg_sel = (1 << 9), // 8168 8101 + ASF = (1 << 8), // 8168 8101 + PktCntrDisable = (1 << 7), // 8168 8101 + Mac_dbgo_sel = 0x001c, // 8168 + RxVlan = (1 << 6), + RxChkSum = (1 << 5), + PCIDAC = (1 << 4), + PCIMulRW = (1 << 3), + INTT_0 = 0x0000, // 8168 + INTT_1 = 0x0001, // 8168 + INTT_2 = 0x0002, // 8168 + INTT_3 = 0x0003, // 8168 + + /* rtl8169_PHYstatus */ + TBI_Enable = 0x80, + TxFlowCtrl = 0x40, + RxFlowCtrl = 0x20, + _1000bpsF = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LinkStatus = 0x02, + FullDup = 0x01, + + /* _TBICSRBit */ + TBILinkOK = 0x02000000, + + /* DumpCounterCommand */ + CounterDump = 0x8, +}; + +enum desc_status_bit { + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ + + /* Tx private */ + LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ + MSSShift = 16, /* MSS value position */ + MSSMask = 0xfff, /* MSS value + LargeSend bit: 12 bits */ + IPCS = (1 << 18), /* Calculate IP checksum */ + UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ + TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + + /* Rx private */ + PID1 = (1 << 18), /* Protocol ID bit 1/2 */ + PID0 = (1 << 17), /* Protocol ID bit 2/2 */ + +#define RxProtoUDP (PID1) +#define RxProtoTCP (PID0) +#define RxProtoIP (PID1 | PID0) +#define RxProtoMask RxProtoIP + + IPFail = (1 << 16), /* IP checksum failed */ + UDPFail = (1 << 15), /* UDP/IP checksum failed */ + TCPFail = (1 << 14), /* TCP/IP checksum failed */ + RxVlanTag = (1 << 16), /* VLAN tag available */ +}; + +#define RsvdMask 0x3fffc000 + +struct TxDesc { + volatile uint32_t opts1; + volatile uint32_t opts2; + volatile uint32_t addr_lo; + volatile uint32_t addr_hi; +}; + +struct RxDesc { + volatile uint32_t opts1; + volatile uint32_t opts2; + volatile uint32_t addr_lo; + volatile uint32_t addr_hi; +}; + +enum features { + RTL_FEATURE_WOL = (1 << 0), + RTL_FEATURE_MSI = (1 << 1), + RTL_FEATURE_GMII = (1 << 2), +}; + +static void rtl_hw_start_8169(struct net_device *); +static void rtl_hw_start_8168(struct net_device *); +static void rtl_hw_start_8101(struct net_device *); + +struct rtl8169_private { + + struct pci_device *pci_dev; + struct net_device *netdev; + uint8_t *hw_addr; + void *mmio_addr; + uint32_t irqno; + + int chipset; + int mac_version; + int cfg_index; + u16 intr_event; + + struct io_buffer *tx_iobuf[NUM_TX_DESC]; + struct io_buffer *rx_iobuf[NUM_RX_DESC]; + + struct TxDesc *tx_base; + struct RxDesc *rx_base; + + uint32_t tx_curr; + uint32_t rx_curr; + + uint32_t tx_tail; + + uint32_t tx_fill_ctr; + + u16 cp_cmd; + + int phy_auto_nego_reg; + int phy_1000_ctrl_reg; + + int ( *set_speed ) (struct net_device *, u8 autoneg, u16 speed, u8 duplex ); + void ( *phy_reset_enable ) ( void *ioaddr ); + void ( *hw_start ) ( struct net_device * ); + unsigned int ( *phy_reset_pending ) ( void *ioaddr ); + unsigned int ( *link_ok ) ( void *ioaddr ); + + int pcie_cap; + + unsigned features; + +}; + +static const unsigned int rtl8169_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); + +#endif /* _R8169_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ |