# HG changeset patch # User Martin Troxler # Date 1258638297 -3600 # Node ID 1baac79a40cf55357a1018a808d7b01aa9e96e64 # Parent 326d47aa986c9cd87f585c143e887d31ab8ebacb# Parent cf2fa3be6addcc01f84289b2607c45a6450a1dda Merge diff -r 326d47aa986c -r 1baac79a40cf .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Nov 19 14:44:57 2009 +0100 @@ -0,0 +1,92 @@ +syntax: glob + +**.ko +**.ko.cmd +**.lo +**.mod.c +**.o +**.o.cmd +**.swp +.tmp_versions +ChangeLog +Doxyfile +Kbuild +Makefile +Makefile.in +Module.markers +Module.symvers +TAGS +aclocal.m4 +autoconf +autom4te.cache +config.h +config.h.in +config.log +config.status +configure +devices/Kbuild +devices/Makefile +devices/Makefile.in +devices/TAGS +devices/e1000/Kbuild +devices/e1000/Makefile +devices/e1000/Makefile.in +devices/modules.order +examples/Kbuild +examples/Makefile +examples/Makefile.in +examples/TAGS +examples/dc_rtai/Kbuild +examples/dc_rtai/Makefile +examples/dc_rtai/Makefile.in +examples/dc_user/.deps +examples/dc_user/.libs +examples/dc_user/Makefile +examples/dc_user/Makefile.in +examples/dc_user/TAGS +examples/dc_user/ec_dc_user_example +examples/mini/Kbuild +examples/mini/Makefile +examples/mini/Makefile.in +examples/mini/modules.order +examples/modules.order +examples/rtai/Kbuild +examples/rtai/Makefile +examples/rtai/Makefile.in +examples/user/.deps +examples/user/.libs +examples/user/Makefile +examples/user/Makefile.in +examples/user/TAGS +examples/user/ec_user_example +include/Makefile +include/Makefile.in +include/TAGS +lib/.deps +lib/.libs +lib/Makefile +lib/Makefile.in +lib/TAGS +lib/libethercat.la +libtool +m4/Makefile +m4/Makefile.in +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +modules.order +script/Makefile +script/Makefile.in +script/init.d/Makefile +script/init.d/Makefile.in +script/init.d/ethercat +script/sysconfig/Makefile +script/sysconfig/Makefile.in +stamp-h1 +tool/.deps +tool/Makefile +tool/Makefile.in +tool/TAGS +tool/ethercat diff -r 326d47aa986c -r 1baac79a40cf .hgtags --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgtags Thu Nov 19 14:44:57 2009 +0100 @@ -0,0 +1,1 @@ +b6cfd85db58e116ea155a52a584e863fc6ad0eff version-1.3.2 diff -r 326d47aa986c -r 1baac79a40cf Makefile.am --- a/Makefile.am Thu Nov 19 14:39:10 2009 +0100 +++ b/Makefile.am Thu Nov 19 14:44:57 2009 +0100 @@ -81,14 +81,13 @@ @rm -f Modules.symvers mydist: - svn2cl $(srcdir) - @SVNREV=`svnversion $(srcdir)` && \ - $(MAKE) dist-bzip2 \ - distdir=$(PACKAGE)-$(VERSION)-r$${SVNREV} + hg log --style=changelog $(srcdir) > ChangeLog + @REV=`hg id -i $(srcdir)` && \ + $(MAKE) dist-bzip2 distdir=$(PACKAGE)-$(VERSION)-$${REV} dist-hook: - if which svnversion >/dev/null 2>&1; then \ - svnversion $(srcdir) 2>/dev/null >$(distdir)/svnrevision; \ + if which hg >/dev/null 2>&1; then \ + hg id -i $(srcdir) 2>/dev/null >$(distdir)/revision; \ fi mrproper: clean cleandoc diff -r 326d47aa986c -r 1baac79a40cf NEWS --- a/NEWS Thu Nov 19 14:39:10 2009 +0100 +++ b/NEWS Thu Nov 19 14:44:57 2009 +0100 @@ -26,7 +26,7 @@ * Added 8139too driver for kernels 2.6.25 (F. Pose), 2.6.26 (M. Luescher), 2.6.27, 2.6.28 and 2.6.29 (M. Goetze). * Added e1000 driver for 2.6.26 (M. Luescher). -* Added r8169 driver for 2.6.24 and 2.6.28. +* Added r8169 driver for 2.6.24, 2.6.28 and 2.6.29. * Debug interfaces are created with the Ethernet addresses of the attached physical device. * Improved error case return codes of many functions. @@ -45,6 +45,8 @@ methods to let an application transfer SDOs before activating the master (thanks to Stefan Weiser). * Fixed SDO upload segment response (thanks to Christoph Peter). +* Fixed SDO upload segment response for 10 bytes mailbox length (thanks to + Joerg Mohre). * SDO entry access rights are shown in 'ethercat sdos'. * Added 64-bit data access macros to application header. * Added debug level for all masters as a module parameter. Thanks to Erwin @@ -59,7 +61,10 @@ * Module symbol versions file for ec_master.ko is installed to prefix/modules/ec_master.symvers. * Added 'ethercat eoe' command to display Ethernet over EtherCAT statistics. +* Added 'ethercat cstruct' command to output PDO information in C language. * Significantly improved EoE bandwidth by running EoE processing in a kthread. +* Switched version control from Subversion to Mercurial. +* Implemented CompleteAccess for SDO downloads. Changes in 1.4.0: diff -r 326d47aa986c -r 1baac79a40cf TODO --- a/TODO Thu Nov 19 14:39:10 2009 +0100 +++ b/TODO Thu Nov 19 14:44:57 2009 +0100 @@ -20,7 +20,6 @@ "System Time" register instead of using the application time. - Check if register 0x0980 is working, to avoid clearing it when configuring. - - Create an interface to query the System Time Difference registers. * Remove byte-swapping functions from user space. * EoE: - Only execute one EoE handler per cycle. @@ -47,6 +46,14 @@ * Document ec_fsm_foe members. * Test KBUILD_EXTRA_SYMBOLS. * Remove default buffer size in SDO upload. +* Check for Enable SDO Complete Access flag. +* Implement CompleteAccess for command-line tool. +* Implement CompleteAccess for SDO uploads. +* Implement identifier parameter for cstruct command. +* Implement sync delimiter for cstruct command. +* Change SDO index at runtime for SDO request. +* Implement ecrt_slave_config_request_state(). +* Output skipped datagrams again. Future issues: diff -r 326d47aa986c -r 1baac79a40cf devices/Kbuild.in --- a/devices/Kbuild.in Thu Nov 19 14:39:10 2009 +0100 +++ b/devices/Kbuild.in Thu Nov 19 14:44:57 2009 +0100 @@ -31,24 +31,24 @@ # #------------------------------------------------------------------------------ -REV := $(shell if test -s $(src)/../svnrevision; then \ - cat $(src)/../svnrevision; \ +REV := $(shell if test -s $(src)/../revision; then \ + cat $(src)/../revision; \ else \ - svnversion $(src)/.. 2>/dev/null || echo "unknown"; \ + hg id -i $(src)/.. 2>/dev/null || echo "unknown"; \ fi) ifeq (@ENABLE_8139TOO@,1) EC_8139TOO_OBJ := 8139too-@KERNEL_8139TOO@-ethercat.o obj-m += ec_8139too.o ec_8139too-objs := $(EC_8139TOO_OBJ) - CFLAGS_$(EC_8139TOO_OBJ) = -DSVNREV=$(REV) + CFLAGS_$(EC_8139TOO_OBJ) = -DREV=$(REV) endif ifeq (@ENABLE_E100@,1) EC_E100_OBJ := e100-@KERNEL_E100@-ethercat.o obj-m += ec_e100.o ec_e100-objs := $(EC_E100_OBJ) - CFLAGS_$(EC_E100_OBJ) = -DSVNREV=$(REV) + CFLAGS_$(EC_E100_OBJ) = -DREV=$(REV) endif ifeq (@ENABLE_E1000@,1) @@ -59,7 +59,7 @@ EC_R8169_OBJ := r8169-@KERNEL_R8169@-ethercat.o obj-m += ec_r8169.o ec_r8169-objs := $(EC_R8169_OBJ) - CFLAGS_$(EC_R8169_OBJ) = -DSVNREV=$(REV) + CFLAGS_$(EC_R8169_OBJ) = -DREV=$(REV) endif KBUILD_EXTRA_SYMBOLS := \ diff -r 326d47aa986c -r 1baac79a40cf devices/Makefile.am --- a/devices/Makefile.am Thu Nov 19 14:39:10 2009 +0100 +++ b/devices/Makefile.am Thu Nov 19 14:44:57 2009 +0100 @@ -71,7 +71,9 @@ r8169-2.6.24-ethercat.c \ r8169-2.6.24-orig.c \ r8169-2.6.28-ethercat.c \ - r8169-2.6.28-orig.c + r8169-2.6.28-orig.c \ + r8169-2.6.29-ethercat.c \ + r8169-2.6.29-orig.c EXTRA_DIST = \ Kbuild.in diff -r 326d47aa986c -r 1baac79a40cf devices/e1000/Kbuild.in --- a/devices/e1000/Kbuild.in Thu Nov 19 14:39:10 2009 +0100 +++ b/devices/e1000/Kbuild.in Thu Nov 19 14:44:57 2009 +0100 @@ -33,10 +33,10 @@ TOPDIR := $(src)/../.. -REV := $(shell if test -s $(TOPDIR)/svnrevision; then \ - cat $(TOPDIR)/svnrevision; \ +REV := $(shell if test -s $(TOPDIR)/revision; then \ + cat $(TOPDIR)/revision; \ else \ - svnversion $(TOPDIR) 2>/dev/null || echo "unknown"; \ + hg id -i $(TOPDIR) 2>/dev/null || echo "unknown"; \ fi) ifeq (@ENABLE_E1000@,1) @@ -47,7 +47,7 @@ e1000_param-@KERNEL_E1000@-ethercat.o obj-m += ec_e1000.o ec_e1000-objs := $(EC_E1000_OBJ) - CFLAGS_e1000_main-@KERNEL_E1000@-ethercat.o = -DSVNREV=$(REV) + CFLAGS_e1000_main-@KERNEL_E1000@-ethercat.o = -DREV=$(REV) endif KBUILD_EXTRA_SYMBOLS := \ diff -r 326d47aa986c -r 1baac79a40cf devices/r8169-2.6.29-ethercat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/r8169-2.6.29-ethercat.c Thu Nov 19 14:44:57 2009 +0100 @@ -0,0 +1,3970 @@ +/* + * r8169.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen + * Copyright (c) 2003 - 2007 Francois Romieu + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + * + * vim: noexpandtab + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../globals.h" +#include "ecdev.h" + +#define RTL8169_VERSION "2.3LK-NAPI" +#define MODULENAME "ec_r8169" +#define PFX MODULENAME ": " + +#ifdef RTL8169_DEBUG +#define assert(expr) \ + if (!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__func__,__LINE__); \ + } +#define dprintk(fmt, args...) \ + do { printk(KERN_DEBUG PFX fmt, ## args); } while (0) +#else +#define assert(expr) do {} while (0) +#define dprintk(fmt, args...) do {} while (0) +#endif /* RTL8169_DEBUG */ + +#define R8169_MSG_DEFAULT \ + (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN) + +#define TX_BUFFS_AVAIL(tp) \ + (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx - 1) + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static const int max_interrupt_work = 20; + +/* 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 const int multicast_filter_limit = 32; + +/* 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 64 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 256 /* 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 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 +}; + +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 *); + +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,}, +}; + +/* prevent driver from being loaded automatically */ +//MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); + +static int rx_copybreak = 200; +static int use_dac; +static struct { + u32 msg_enable; +} debug = { -1 }; + +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 { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct RxDesc { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct ring_info { + struct sk_buff *skb; + u32 len; + u8 __pad[sizeof(void *) - sizeof(u32)]; +}; + +enum features { + RTL_FEATURE_WOL = (1 << 0), + RTL_FEATURE_MSI = (1 << 1), + RTL_FEATURE_GMII = (1 << 2), +}; + +struct rtl8169_counters { + __le64 tx_packets; + __le64 rx_packets; + __le64 tx_errors; + __le32 rx_errors; + __le16 rx_missed; + __le16 align_errors; + __le32 tx_one_collision; + __le32 tx_multi_collision; + __le64 rx_unicast; + __le64 rx_broadcast; + __le32 rx_multicast; + __le16 tx_aborted; + __le16 tx_underun; +}; + +struct rtl8169_private { + void __iomem *mmio_addr; /* memory map physical address */ + struct pci_dev *pci_dev; /* Index of PCI device */ + struct net_device *dev; + struct napi_struct napi; + spinlock_t lock; /* spin lock flag */ + u32 msg_enable; + int chipset; + int mac_version; + u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + u32 dirty_rx; + u32 dirty_tx; + struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */ + struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */ + dma_addr_t TxPhyAddr; + dma_addr_t RxPhyAddr; + struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ + struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */ + unsigned align; + unsigned rx_buf_sz; + struct timer_list timer; + u16 cp_cmd; + u16 intr_event; + u16 napi_event; + u16 intr_mask; + int phy_auto_nego_reg; + int phy_1000_ctrl_reg; +#ifdef CONFIG_R8169_VLAN + struct vlan_group *vlgrp; +#endif + int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex); + int (*get_settings)(struct net_device *, struct ethtool_cmd *); + void (*phy_reset_enable)(void __iomem *); + void (*hw_start)(struct net_device *); + unsigned int (*phy_reset_pending)(void __iomem *); + unsigned int (*link_ok)(void __iomem *); + int (*do_ioctl)(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd); + int pcie_cap; + struct delayed_work task; + unsigned features; + + struct mii_if_info mii; + struct rtl8169_counters counters; + + ec_device_t *ecdev; + unsigned long ec_watchdog_jiffies; +}; + +MODULE_AUTHOR("Florian Pose "); +MODULE_DESCRIPTION("EtherCAT-capable RealTek RTL-8169 Gigabit Ethernet driver"); +module_param(rx_copybreak, int, 0); +MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); +module_param(use_dac, int, 0); +MODULE_PARM_DESC(use_dac, "Enable PCI DAC. Unsafe on 32 bit PCI slot."); +module_param_named(debug, debug.msg_enable, int, 0); +MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(EC_MASTER_VERSION); + +static int rtl8169_open(struct net_device *dev); +static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance); +static int rtl8169_init_ring(struct net_device *dev); +static void rtl_hw_start(struct net_device *dev); +static int rtl8169_close(struct net_device *dev); +static void rtl_set_rx_mode(struct net_device *dev); +static void rtl8169_tx_timeout(struct net_device *dev); +static struct net_device_stats *rtl8169_get_stats(struct net_device *dev); +static int rtl8169_rx_interrupt(struct net_device *, struct rtl8169_private *, + void __iomem *, u32 budget); +static int rtl8169_change_mtu(struct net_device *dev, int new_mtu); +static void rtl8169_down(struct net_device *dev); +static void rtl8169_rx_clear(struct rtl8169_private *tp); +static void ec_poll(struct net_device *dev); +static int rtl8169_poll(struct napi_struct *napi, int budget); + +static const unsigned int rtl8169_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); + +static void mdio_write(void __iomem *ioaddr, int reg_addr, int value) +{ + int i; + + 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; + udelay(25); + } +} + +static int mdio_read(void __iomem *ioaddr, int reg_addr) +{ + int i, value = -1; + + RTL_W32(PHYAR, 0x0 | (reg_addr & 0x1f) << 16); + + 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 = RTL_R32(PHYAR) & 0xffff; + break; + } + udelay(25); + } + return value; +} + +static void mdio_patch(void __iomem *ioaddr, int reg_addr, int value) +{ + mdio_write(ioaddr, reg_addr, mdio_read(ioaddr, reg_addr) | value); +} + +static void rtl_mdio_write(struct net_device *dev, int phy_id, int location, + int val) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + mdio_write(ioaddr, location, val); +} + +static int rtl_mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + return mdio_read(ioaddr, location); +} + +static void rtl_ephy_write(void __iomem *ioaddr, int reg_addr, int value) +{ + unsigned int i; + + RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) | + (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(EPHYAR) & EPHYAR_FLAG)) + break; + udelay(10); + } +} + +static u16 rtl_ephy_read(void __iomem *ioaddr, int reg_addr) +{ + u16 value = 0xffff; + unsigned int i; + + 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; + } + udelay(10); + } + + return value; +} + +static void rtl_csi_write(void __iomem *ioaddr, int addr, int value) +{ + unsigned int i; + + RTL_W32(CSIDR, value); + RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(CSIAR) & CSIAR_FLAG)) + break; + udelay(10); + } +} + +static u32 rtl_csi_read(void __iomem *ioaddr, int addr) +{ + u32 value = ~0x00; + unsigned int i; + + RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (RTL_R32(CSIAR) & CSIAR_FLAG) { + value = RTL_R32(CSIDR); + break; + } + udelay(10); + } + + return value; +} + +static void rtl8169_irq_mask_and_ack(void __iomem *ioaddr) +{ + RTL_W16(IntrMask, 0x0000); + + RTL_W16(IntrStatus, 0xffff); +} + +static void rtl8169_asic_down(void __iomem *ioaddr) +{ + RTL_W8(ChipCmd, 0x00); + rtl8169_irq_mask_and_ack(ioaddr); + RTL_R16(CPlusCmd); +} + +static unsigned int rtl8169_tbi_reset_pending(void __iomem *ioaddr) +{ + return RTL_R32(TBICSR) & TBIReset; +} + +static unsigned int rtl8169_xmii_reset_pending(void __iomem *ioaddr) +{ + return mdio_read(ioaddr, MII_BMCR) & BMCR_RESET; +} + +static unsigned int rtl8169_tbi_link_ok(void __iomem *ioaddr) +{ + return RTL_R32(TBICSR) & TBILinkOk; +} + +static unsigned int rtl8169_xmii_link_ok(void __iomem *ioaddr) +{ + return RTL_R8(PHYstatus) & LinkStatus; +} + +static void rtl8169_tbi_reset_enable(void __iomem *ioaddr) +{ + RTL_W32(TBICSR, RTL_R32(TBICSR) | TBIReset); +} + +static void rtl8169_xmii_reset_enable(void __iomem *ioaddr) +{ + unsigned int val; + + val = mdio_read(ioaddr, MII_BMCR) | BMCR_RESET; + mdio_write(ioaddr, MII_BMCR, val & 0xffff); +} + +static void rtl8169_check_link_status(struct net_device *dev, + struct rtl8169_private *tp, + void __iomem *ioaddr) +{ + unsigned long flags; + + if (tp->ecdev) { + ecdev_set_link(tp->ecdev, tp->link_ok(ioaddr) ? 1 : 0); + } else { + spin_lock_irqsave(&tp->lock, flags); + if (tp->link_ok(ioaddr)) { + netif_carrier_on(dev); + if (netif_msg_ifup(tp)) + printk(KERN_INFO PFX "%s: link up\n", dev->name); + } else { + if (netif_msg_ifdown(tp)) + printk(KERN_INFO PFX "%s: link down\n", dev->name); + netif_carrier_off(dev); + } + spin_unlock_irqrestore(&tp->lock, flags); + } +} + +static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u8 options; + + wol->wolopts = 0; + +#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) + wol->supported = WAKE_ANY; + + spin_lock_irq(&tp->lock); + + options = RTL_R8(Config1); + if (!(options & PMEnable)) + goto out_unlock; + + options = RTL_R8(Config3); + if (options & LinkUp) + wol->wolopts |= WAKE_PHY; + if (options & MagicPacket) + wol->wolopts |= WAKE_MAGIC; + + options = RTL_R8(Config5); + if (options & UWF) + wol->wolopts |= WAKE_UCAST; + if (options & BWF) + wol->wolopts |= WAKE_BCAST; + if (options & MWF) + wol->wolopts |= WAKE_MCAST; + +out_unlock: + spin_unlock_irq(&tp->lock); +} + +static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int i; + static struct { + u32 opt; + u16 reg; + u8 mask; + } cfg[] = { + { WAKE_ANY, Config1, PMEnable }, + { WAKE_PHY, Config3, LinkUp }, + { WAKE_MAGIC, Config3, MagicPacket }, + { WAKE_UCAST, Config5, UWF }, + { WAKE_BCAST, Config5, BWF }, + { WAKE_MCAST, Config5, MWF }, + { WAKE_ANY, Config5, LanWake } + }; + + spin_lock_irq(&tp->lock); + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + for (i = 0; i < ARRAY_SIZE(cfg); i++) { + u8 options = RTL_R8(cfg[i].reg) & ~cfg[i].mask; + if (wol->wolopts & cfg[i].opt) + options |= cfg[i].mask; + RTL_W8(cfg[i].reg, options); + } + + RTL_W8(Cfg9346, Cfg9346_Lock); + + if (wol->wolopts) + tp->features |= RTL_FEATURE_WOL; + else + tp->features &= ~RTL_FEATURE_WOL; + device_set_wakeup_enable(&tp->pci_dev->dev, wol->wolopts); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +static void rtl8169_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + strcpy(info->driver, MODULENAME); + strcpy(info->version, RTL8169_VERSION); + strcpy(info->bus_info, pci_name(tp->pci_dev)); +} + +static int rtl8169_get_regs_len(struct net_device *dev) +{ + return R8169_REGS_SIZE; +} + +static int rtl8169_set_speed_tbi(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int ret = 0; + u32 reg; + + 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 { + if (netif_msg_link(tp)) { + printk(KERN_WARNING "%s: " + "incorrect speed setting refused in TBI mode\n", + dev->name); + } + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int rtl8169_set_speed_xmii(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int auto_nego, giga_ctrl; + + 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 (autoneg == AUTONEG_ENABLE) { + auto_nego |= (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + } else { + 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; + } + } + + /* 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)) && + netif_msg_link(tp)) { + printk(KERN_INFO "%s: PHY does not support 1000Mbps.\n", + dev->name); + } + giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + } + + 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); + } + + 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 int rtl8169_set_speed(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret; + + ret = tp->set_speed(dev, autoneg, speed, duplex); + + if (netif_running(dev) && (tp->phy_1000_ctrl_reg & ADVERTISE_1000FULL)) + mod_timer(&tp->timer, jiffies + RTL8169_PHY_TIMEOUT); + + return ret; +} + +static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&tp->lock, flags); + ret = rtl8169_set_speed(dev, cmd->autoneg, cmd->speed, cmd->duplex); + spin_unlock_irqrestore(&tp->lock, flags); + + return ret; +} + +static u32 rtl8169_get_rx_csum(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return tp->cp_cmd & RxChkSum; +} + +static int rtl8169_set_rx_csum(struct net_device *dev, u32 data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + + if (data) + tp->cp_cmd |= RxChkSum; + else + tp->cp_cmd &= ~RxChkSum; + + RTL_W16(CPlusCmd, tp->cp_cmd); + RTL_R16(CPlusCmd); + + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +#ifdef CONFIG_R8169_VLAN + +static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + return (tp->vlgrp && vlan_tx_tag_present(skb)) ? + TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00; +} + +static void rtl8169_vlan_rx_register(struct net_device *dev, + struct vlan_group *grp) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + tp->vlgrp = grp; + if (tp->vlgrp) + tp->cp_cmd |= RxVlan; + else + tp->cp_cmd &= ~RxVlan; + RTL_W16(CPlusCmd, tp->cp_cmd); + RTL_R16(CPlusCmd); + spin_unlock_irqrestore(&tp->lock, flags); +} + +static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, + struct sk_buff *skb) +{ + u32 opts2 = le32_to_cpu(desc->opts2); + struct vlan_group *vlgrp = tp->vlgrp; + int ret; + + if (vlgrp && (opts2 & RxVlanTag)) { + vlan_hwaccel_receive_skb(skb, vlgrp, swab16(opts2 & 0xffff)); + ret = 0; + } else + ret = -1; + desc->opts2 = 0; + return ret; +} + +#else /* !CONFIG_R8169_VLAN */ + +static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + return 0; +} + +static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, + struct sk_buff *skb) +{ + return -1; +} + +#endif + +static int rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 status; + + cmd->supported = + SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE; + cmd->port = PORT_FIBRE; + cmd->transceiver = XCVR_INTERNAL; + + status = RTL_R32(TBICSR); + cmd->advertising = (status & TBINwEnable) ? ADVERTISED_Autoneg : 0; + cmd->autoneg = !!(status & TBINwEnable); + + cmd->speed = SPEED_1000; + cmd->duplex = DUPLEX_FULL; /* Always set */ + + return 0; +} + +static int rtl8169_gset_xmii(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return mii_ethtool_gset(&tp->mii, cmd); +} + +static int rtl8169_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + int rc; + + spin_lock_irqsave(&tp->lock, flags); + + rc = tp->get_settings(dev, cmd); + + spin_unlock_irqrestore(&tp->lock, flags); + return rc; +} + +static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + + if (regs->len > R8169_REGS_SIZE) + regs->len = R8169_REGS_SIZE; + + spin_lock_irqsave(&tp->lock, flags); + memcpy_fromio(p, tp->mmio_addr, regs->len); + spin_unlock_irqrestore(&tp->lock, flags); +} + +static u32 rtl8169_get_msglevel(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return tp->msg_enable; +} + +static void rtl8169_set_msglevel(struct net_device *dev, u32 value) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + tp->msg_enable = value; +} + +static const char rtl8169_gstrings[][ETH_GSTRING_LEN] = { + "tx_packets", + "rx_packets", + "tx_errors", + "rx_errors", + "rx_missed", + "align_errors", + "tx_single_collisions", + "tx_multi_collisions", + "unicast", + "broadcast", + "multicast", + "tx_aborted", + "tx_underrun", +}; + +static int rtl8169_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(rtl8169_gstrings); + default: + return -EOPNOTSUPP; + } +} + +static void rtl8169_update_counters(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + struct rtl8169_counters *counters; + dma_addr_t paddr; + u32 cmd; + int wait = 1000; + + /* + * Some chips are unable to dump tally counters when the receiver + * is disabled. + */ + if ((RTL_R8(ChipCmd) & CmdRxEnb) == 0) + return; + + counters = pci_alloc_consistent(tp->pci_dev, sizeof(*counters), &paddr); + if (!counters) + return; + + RTL_W32(CounterAddrHigh, (u64)paddr >> 32); + cmd = (u64)paddr & DMA_32BIT_MASK; + RTL_W32(CounterAddrLow, cmd); + RTL_W32(CounterAddrLow, cmd | CounterDump); + + while (wait--) { + if ((RTL_R32(CounterAddrLow) & CounterDump) == 0) { + /* copy updated counters */ + memcpy(&tp->counters, counters, sizeof(*counters)); + break; + } + udelay(10); + } + + RTL_W32(CounterAddrLow, 0); + RTL_W32(CounterAddrHigh, 0); + + pci_free_consistent(tp->pci_dev, sizeof(*counters), counters, paddr); +} + +static void rtl8169_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + ASSERT_RTNL(); + + rtl8169_update_counters(dev); + + data[0] = le64_to_cpu(tp->counters.tx_packets); + data[1] = le64_to_cpu(tp->counters.rx_packets); + data[2] = le64_to_cpu(tp->counters.tx_errors); + data[3] = le32_to_cpu(tp->counters.rx_errors); + data[4] = le16_to_cpu(tp->counters.rx_missed); + data[5] = le16_to_cpu(tp->counters.align_errors); + data[6] = le32_to_cpu(tp->counters.tx_one_collision); + data[7] = le32_to_cpu(tp->counters.tx_multi_collision); + data[8] = le64_to_cpu(tp->counters.rx_unicast); + data[9] = le64_to_cpu(tp->counters.rx_broadcast); + data[10] = le32_to_cpu(tp->counters.rx_multicast); + data[11] = le16_to_cpu(tp->counters.tx_aborted); + data[12] = le16_to_cpu(tp->counters.tx_underun); +} + +static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + switch(stringset) { + case ETH_SS_STATS: + memcpy(data, *rtl8169_gstrings, sizeof(rtl8169_gstrings)); + break; + } +} + +static const struct ethtool_ops rtl8169_ethtool_ops = { + .get_drvinfo = rtl8169_get_drvinfo, + .get_regs_len = rtl8169_get_regs_len, + .get_link = ethtool_op_get_link, + .get_settings = rtl8169_get_settings, + .set_settings = rtl8169_set_settings, + .get_msglevel = rtl8169_get_msglevel, + .set_msglevel = rtl8169_set_msglevel, + .get_rx_csum = rtl8169_get_rx_csum, + .set_rx_csum = rtl8169_set_rx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .set_sg = ethtool_op_set_sg, + .set_tso = ethtool_op_set_tso, + .get_regs = rtl8169_get_regs, + .get_wol = rtl8169_get_wol, + .set_wol = rtl8169_set_wol, + .get_strings = rtl8169_get_strings, + .get_sset_count = rtl8169_get_sset_count, + .get_ethtool_stats = rtl8169_get_ethtool_stats, +}; + +static void rtl8169_write_gmii_reg_bit(void __iomem *ioaddr, int reg, + int bitnum, int bitval) +{ + int val; + + val = mdio_read(ioaddr, reg); + val = (bitval == 1) ? + val | (bitval << bitnum) : val & ~(0x0001 << bitnum); + mdio_write(ioaddr, reg, val & 0xffff); +} + +static void rtl8169_get_mac_version(struct rtl8169_private *tp, + void __iomem *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 + */ + 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 }, + + /* 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, 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 }, + + /* 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; + u32 reg; + + reg = RTL_R32(TxConfig); + while ((reg & p->mask) != p->val) + p++; + tp->mac_version = p->mac_version; + + if (p->mask == 0x00000000) { + struct pci_dev *pdev = tp->pci_dev; + + dev_info(&pdev->dev, "unknown MAC (%08x)\n", reg); + } +} + +static void rtl8169_print_mac_version(struct rtl8169_private *tp) +{ + dprintk("mac_version = 0x%02x\n", tp->mac_version); +} + +struct phy_reg { + u16 reg; + u16 val; +}; + +static void rtl_phy_write(void __iomem *ioaddr, struct phy_reg *regs, int len) +{ + while (len-- > 0) { + mdio_write(ioaddr, regs->reg, regs->val); + regs++; + } +} + +static void rtl8169s_hw_phy_config(void __iomem *ioaddr) +{ + struct { + u16 regs[5]; /* Beware of bit-sign propagation */ + } phy_magic[5] = { { + { 0x0000, //w 4 15 12 0 + 0x00a1, //w 3 15 0 00a1 + 0x0008, //w 2 15 0 0008 + 0x1020, //w 1 15 0 1020 + 0x1000 } },{ //w 0 15 0 1000 + { 0x7000, //w 4 15 12 7 + 0xff41, //w 3 15 0 ff41 + 0xde60, //w 2 15 0 de60 + 0x0140, //w 1 15 0 0140 + 0x0077 } },{ //w 0 15 0 0077 + { 0xa000, //w 4 15 12 a + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xfa00 } },{ //w 0 15 0 fa00 + { 0xb000, //w 4 15 12 b + 0xff41, //w 3 15 0 ff41 + 0xde20, //w 2 15 0 de20 + 0x0140, //w 1 15 0 0140 + 0x00bb } },{ //w 0 15 0 00bb + { 0xf000, //w 4 15 12 f + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xbf00 } //w 0 15 0 bf00 + } + }, *p = phy_magic; + unsigned int i; + + 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 = (mdio_read(ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff); + mdio_write(ioaddr, pos, val); + while (--pos >= 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 + } + mdio_write(ioaddr, 0x1f, 0x0000); //w 31 2 0 0 +} + +static void rtl8169sb_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x01, 0x90d0 }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bb_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + mdio_write(ioaddr, 0x1f, 0x0001); + mdio_patch(ioaddr, 0x16, 1 << 0); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bef_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168cp_1_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0000 }, + { 0x1d, 0x0f00 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x1ec8 }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168cp_2_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0000 } + }; + + 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_1_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1f, 0x0002 }, + { 0x00, 0x88d4 }, + { 0x01, 0x82b1 }, + { 0x03, 0x7002 }, + { 0x08, 0x9e30 }, + { 0x09, 0x01f0 }, + { 0x0a, 0x5500 }, + { 0x0c, 0x00c8 }, + { 0x1f, 0x0003 }, + { 0x12, 0xc096 }, + { 0x16, 0x000a }, + { 0x1f, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x09, 0x2000 }, + { 0x09, 0x0000 } + }; + + 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 rtl8168c_2_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 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 } + }; + + 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 __iomem *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 } + }; + + 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 __iomem *ioaddr) +{ + rtl8168c_3_hw_phy_config(ioaddr); +} + +static void rtl8168d_hw_phy_config(void __iomem *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 } + }; + + 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 __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0003 }, + { 0x08, 0x441d }, + { 0x01, 0x9100 }, + { 0x1f, 0x0000 } + }; + + 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 rtl_hw_phy_config(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + rtl8169_print_mac_version(tp); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_01: + break; + case RTL_GIGA_MAC_VER_02: + case RTL_GIGA_MAC_VER_03: + rtl8169s_hw_phy_config(ioaddr); + break; + 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_1_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_19: + rtl8168c_1_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_20: + 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_timer(unsigned long __opaque) +{ + struct net_device *dev = (struct net_device *)__opaque; + struct rtl8169_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->timer; + void __iomem *ioaddr = tp->mmio_addr; + unsigned long timeout = RTL8169_PHY_TIMEOUT; + + assert(tp->mac_version > RTL_GIGA_MAC_VER_01); + + if (!(tp->phy_1000_ctrl_reg & ADVERTISE_1000FULL)) + return; + + if (!tp->ecdev) + spin_lock_irq(&tp->lock); + + if (tp->phy_reset_pending(ioaddr)) { + /* + * A busy loop could burn quite a few cycles on nowadays CPU. + * Let's delay the execution of the timer for a few ticks. + */ + timeout = HZ/10; + goto out_mod_timer; + } + + if (tp->link_ok(ioaddr)) + goto out_unlock; + + if (netif_msg_link(tp)) + printk(KERN_WARNING "%s: PHY reset until link up\n", dev->name); + + tp->phy_reset_enable(ioaddr); + +out_mod_timer: + if (!tp->ecdev) + mod_timer(timer, jiffies + timeout); +out_unlock: + if (!tp->ecdev) + spin_unlock_irq(&tp->lock); +} + +static inline void rtl8169_delete_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->timer; + + if (tp->ecdev || tp->mac_version <= RTL_GIGA_MAC_VER_01) + return; + + del_timer_sync(timer); +} + +static inline void rtl8169_request_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->timer; + + if (tp->ecdev || tp->mac_version <= RTL_GIGA_MAC_VER_01) + return; + + mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ +static void rtl8169_netpoll(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + + disable_irq(pdev->irq); + rtl8169_interrupt(pdev->irq, dev); + enable_irq(pdev->irq); +} +#endif + +static void rtl8169_release_board(struct pci_dev *pdev, struct net_device *dev, + void __iomem *ioaddr) +{ + iounmap(ioaddr); + pci_release_regions(pdev); + pci_disable_device(pdev); + free_netdev(dev); +} + +static void rtl8169_phy_reset(struct net_device *dev, + struct rtl8169_private *tp) +{ + void __iomem *ioaddr = tp->mmio_addr; + unsigned int i; + + tp->phy_reset_enable(ioaddr); + for (i = 0; i < 100; i++) { + if (!tp->phy_reset_pending(ioaddr)) + return; + msleep(1); + } + if (netif_msg_link(tp)) + printk(KERN_ERR "%s: PHY reset failed.\n", dev->name); +} + +static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp) +{ + void __iomem *ioaddr = tp->mmio_addr; + + rtl_hw_phy_config(dev); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + dprintk("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) { + dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); + RTL_W8(0x82, 0x01); + dprintk("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) && netif_msg_link(tp)) + printk(KERN_INFO PFX "%s: TBI auto-negotiating\n", dev->name); +} + +static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr) +{ + void __iomem *ioaddr = tp->mmio_addr; + u32 high; + u32 low; + + low = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24); + high = addr[4] | (addr[5] << 8); + + spin_lock_irq(&tp->lock); + + RTL_W8(Cfg9346, Cfg9346_Unlock); + RTL_W32(MAC0, low); + RTL_W32(MAC4, high); + RTL_W8(Cfg9346, Cfg9346_Lock); + + spin_unlock_irq(&tp->lock); +} + +static int rtl_set_mac_address(struct net_device *dev, void *p) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + rtl_rar_set(tp, dev->dev_addr); + + return 0; +} + +static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(ifr); + + return netif_running(dev) ? tp->do_ioctl(tp, data, cmd) : -ENODEV; +} + +static int rtl_xmii_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 32; /* Internal PHY */ + return 0; + + case SIOCGMIIREG: + data->val_out = mdio_read(tp->mmio_addr, data->reg_num & 0x1f); + return 0; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mdio_write(tp->mmio_addr, data->reg_num & 0x1f, data->val_in); + return 0; + } + return -EOPNOTSUPP; +} + +static int rtl_tbi_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd) +{ + return -EOPNOTSUPP; +} + +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_FEATURE_MSI + }, + [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, + .features = RTL_FEATURE_MSI + } +}; + +/* Cfg9346_Unlock assumed. */ +static unsigned rtl_try_msi(struct pci_dev *pdev, void __iomem *ioaddr, + const struct rtl_cfg_info *cfg) +{ + unsigned msi = 0; + u8 cfg2; + + cfg2 = RTL_R8(Config2) & ~MSIEnable; + if (cfg->features & RTL_FEATURE_MSI) { + if (pci_enable_msi(pdev)) { + dev_info(&pdev->dev, "no MSI. Back to INTx.\n"); + } else { + cfg2 |= MSIEnable; + msi = RTL_FEATURE_MSI; + } + } + RTL_W8(Config2, cfg2); + return msi; +} + +static void rtl_disable_msi(struct pci_dev *pdev, struct rtl8169_private *tp) +{ + if (tp->features & RTL_FEATURE_MSI) { + pci_disable_msi(pdev); + tp->features &= ~RTL_FEATURE_MSI; + } +} + +static const struct net_device_ops rtl8169_netdev_ops = { + .ndo_open = rtl8169_open, + .ndo_stop = rtl8169_close, + .ndo_get_stats = rtl8169_get_stats, + .ndo_start_xmit = rtl8169_start_xmit, + .ndo_tx_timeout = rtl8169_tx_timeout, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = rtl8169_change_mtu, + .ndo_set_mac_address = rtl_set_mac_address, + .ndo_do_ioctl = rtl8169_ioctl, + .ndo_set_multicast_list = rtl_set_rx_mode, +#ifdef CONFIG_R8169_VLAN + .ndo_vlan_rx_register = rtl8169_vlan_rx_register, +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = rtl8169_netpoll, +#endif + +}; + +static int __devinit +rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + const struct rtl_cfg_info *cfg = rtl_cfg_infos + ent->driver_data; + const unsigned int region = cfg->region; + struct rtl8169_private *tp; + struct mii_if_info *mii; + struct net_device *dev; + void __iomem *ioaddr; + unsigned int i; + int rc; + + if (netif_msg_drv(&debug)) { + printk(KERN_INFO "%s Gigabit Ethernet driver %s loaded\n", + MODULENAME, RTL8169_VERSION); + } + + dev = alloc_etherdev(sizeof (*tp)); + if (!dev) { + if (netif_msg_drv(&debug)) + dev_err(&pdev->dev, "unable to alloc new ethernet\n"); + rc = -ENOMEM; + goto out; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + dev->netdev_ops = &rtl8169_netdev_ops; + tp = netdev_priv(dev); + tp->dev = dev; + tp->pci_dev = pdev; + tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT); + + mii = &tp->mii; + mii->dev = dev; + mii->mdio_read = rtl_mdio_read; + mii->mdio_write = rtl_mdio_write; + mii->phy_id_mask = 0x1f; + mii->reg_num_mask = 0x1f; + mii->supports_gmii = !!(cfg->features & RTL_FEATURE_GMII); + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + rc = pci_enable_device(pdev); + if (rc < 0) { + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "enable failure\n"); + goto err_out_free_dev_1; + } + + rc = pci_set_mwi(pdev); + if (rc < 0) + goto err_out_disable_2; + + /* make sure PCI base addr 1 is MMIO */ + if (!(pci_resource_flags(pdev, region) & IORESOURCE_MEM)) { + if (netif_msg_probe(tp)) { + dev_err(&pdev->dev, + "region #%d not an MMIO resource, aborting\n", + region); + } + rc = -ENODEV; + goto err_out_mwi_3; + } + + /* check for weird/broken PCI region reporting */ + if (pci_resource_len(pdev, region) < R8169_REGS_SIZE) { + if (netif_msg_probe(tp)) { + dev_err(&pdev->dev, + "Invalid PCI region size(s), aborting\n"); + } + rc = -ENODEV; + goto err_out_mwi_3; + } + + rc = pci_request_regions(pdev, MODULENAME); + if (rc < 0) { + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "could not request regions.\n"); + goto err_out_mwi_3; + } + + tp->cp_cmd = PCIMulRW | RxChkSum; + + if ((sizeof(dma_addr_t) > 4) && + !pci_set_dma_mask(pdev, DMA_64BIT_MASK) && use_dac) { + tp->cp_cmd |= PCIDAC; + dev->features |= NETIF_F_HIGHDMA; + } else { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc < 0) { + if (netif_msg_probe(tp)) { + dev_err(&pdev->dev, + "DMA configuration failed.\n"); + } + goto err_out_free_res_4; + } + } + + pci_set_master(pdev); + + /* ioremap MMIO region */ + ioaddr = ioremap(pci_resource_start(pdev, region), R8169_REGS_SIZE); + if (!ioaddr) { + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + rc = -EIO; + goto err_out_free_res_4; + } + + tp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!tp->pcie_cap && netif_msg_probe(tp)) + dev_info(&pdev->dev, "no PCI Express capability\n"); + + RTL_W16(IntrMask, 0x0000); + + /* 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; + msleep_interruptible(1); + } + + RTL_W16(IntrStatus, 0xffff); + + /* Identify chip attached to board */ + rtl8169_get_mac_version(tp, ioaddr); + + rtl8169_print_mac_version(tp); + + for (i = 0; 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 */ + if (netif_msg_probe(tp)) { + dev_printk(KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming %s\n", + rtl_chip_info[0].name); + } + i = 0; + } + tp->chipset = i; + + RTL_W8(Cfg9346, Cfg9346_Unlock); + RTL_W8(Config1, RTL_R8(Config1) | PMEnable); + RTL_W8(Config5, RTL_R8(Config5) & PMEStatus); + if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0) + tp->features |= RTL_FEATURE_WOL; + if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0) + tp->features |= RTL_FEATURE_WOL; + tp->features |= rtl_try_msi(pdev, ioaddr, cfg); + RTL_W8(Cfg9346, Cfg9346_Lock); + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_06) && + (RTL_R8(PHYstatus) & TBI_Enable)) { + tp->set_speed = rtl8169_set_speed_tbi; + tp->get_settings = rtl8169_gset_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->do_ioctl = rtl_tbi_ioctl; + + tp->phy_1000_ctrl_reg = ADVERTISE_1000FULL; /* Implied by TBI */ + } else { + tp->set_speed = rtl8169_set_speed_xmii; + tp->get_settings = rtl8169_gset_xmii; + tp->phy_reset_enable = rtl8169_xmii_reset_enable; + tp->phy_reset_pending = rtl8169_xmii_reset_pending; + tp->link_ok = rtl8169_xmii_link_ok; + tp->do_ioctl = rtl_xmii_ioctl; + } + + spin_lock_init(&tp->lock); + + tp->mmio_addr = ioaddr; + + /* Get MAC address */ + for (i = 0; i < MAC_ADDR_LEN; i++) + dev->dev_addr[i] = RTL_R8(MAC0 + i); + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + + SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops); + dev->watchdog_timeo = RTL8169_TX_TIMEOUT; + dev->irq = pdev->irq; + dev->base_addr = (unsigned long) ioaddr; + + netif_napi_add(dev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT); + +#ifdef CONFIG_R8169_VLAN + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; +#endif + + tp->intr_mask = 0xffff; + tp->align = cfg->align; + tp->hw_start = cfg->hw_start; + tp->intr_event = cfg->intr_event; + tp->napi_event = cfg->napi_event; + + init_timer(&tp->timer); + tp->timer.data = (unsigned long) dev; + tp->timer.function = rtl8169_phy_timer; + + // offer device to EtherCAT master module + tp->ecdev = ecdev_offer(dev, ec_poll, THIS_MODULE); + + if (!tp->ecdev) { + rc = register_netdev(dev); + if (rc < 0) + goto err_out_msi_5; + } + + pci_set_drvdata(pdev, dev); + + if (netif_msg_probe(tp)) { + u32 xid = RTL_R32(TxConfig) & 0x7cf0f8ff; + + printk(KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "XID %08x IRQ %d\n", + dev->name, + rtl_chip_info[tp->chipset].name, + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], xid, dev->irq); + } + + rtl8169_init_phy(dev, tp); + device_set_wakeup_enable(&pdev->dev, tp->features & RTL_FEATURE_WOL); + if (tp->ecdev && ecdev_open(tp->ecdev)) { + ecdev_withdraw(tp->ecdev); + goto err_out_msi_5; + } + + +out: + return rc; + +err_out_msi_5: + rtl_disable_msi(pdev, tp); + iounmap(ioaddr); +err_out_free_res_4: + pci_release_regions(pdev); +err_out_mwi_3: + pci_clear_mwi(pdev); +err_out_disable_2: + pci_disable_device(pdev); +err_out_free_dev_1: + free_netdev(dev); + goto out; +} + +static void __devexit rtl8169_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + + flush_scheduled_work(); + + if (tp->ecdev) { + ecdev_close(tp->ecdev); + ecdev_withdraw(tp->ecdev); + } else { + unregister_netdev(dev); + } + rtl_disable_msi(pdev, tp); + rtl8169_release_board(pdev, dev, tp->mmio_addr); + pci_set_drvdata(pdev, NULL); +} + +static void rtl8169_set_rxbufsize(struct rtl8169_private *tp, + struct net_device *dev) +{ + unsigned int mtu = dev->mtu; + + tp->rx_buf_sz = (mtu > RX_BUF_SIZE) ? mtu + ETH_HLEN + 8 : RX_BUF_SIZE; +} + +static int rtl8169_open(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + int retval = -ENOMEM; + + + rtl8169_set_rxbufsize(tp, dev); + + /* + * Rx and Tx desscriptors needs 256 bytes alignment. + * pci_alloc_consistent provides more. + */ + tp->TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES, + &tp->TxPhyAddr); + if (!tp->TxDescArray) + goto out; + + tp->RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES, + &tp->RxPhyAddr); + if (!tp->RxDescArray) + goto err_free_tx_0; + + retval = rtl8169_init_ring(dev); + if (retval < 0) + goto err_free_rx_1; + + INIT_DELAYED_WORK(&tp->task, NULL); + + smp_mb(); + + if (!tp->ecdev) { + retval = request_irq(dev->irq, rtl8169_interrupt, + (tp->features & RTL_FEATURE_MSI) ? 0 : IRQF_SHARED, + dev->name, dev); + if (retval < 0) + goto err_release_ring_2; + + napi_enable(&tp->napi); + + } + rtl_hw_start(dev); + + rtl8169_request_timer(dev); + + rtl8169_check_link_status(dev, tp, tp->mmio_addr); +out: + return retval; + +err_release_ring_2: + rtl8169_rx_clear(tp); +err_free_rx_1: + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); +err_free_tx_0: + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + goto out; +} + +static void rtl8169_hw_reset(void __iomem *ioaddr) +{ + /* 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 __iomem *ioaddr = tp->mmio_addr; + u32 cfg = rtl8169_rx_config; + + 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_hw_start(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int i; + + /* 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; + msleep_interruptible(1); + } + + tp->hw_start(dev); + + if (!tp->ecdev) + netif_start_queue(dev); +} + + +static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp, + void __iomem *ioaddr) +{ + /* + * 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, ((u64) tp->TxPhyAddr) >> 32); + RTL_W32(TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_32BIT_MASK); + RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32); + RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_32BIT_MASK); +} + +static u16 rtl_rw_cpluscmd(void __iomem *ioaddr) +{ + u16 cmd; + + cmd = RTL_R16(CPlusCmd); + RTL_W16(CPlusCmd, cmd); + return cmd; +} + +static void rtl_set_rx_max_size(void __iomem *ioaddr) +{ + /* Low hurts. Let's disable the filtering. */ + RTL_W16(RxMaxSize, 16383); +} + +static void rtl8169_set_magic_reg(void __iomem *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; + + 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_hw_start_8169(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + struct pci_dev *pdev = tp->pci_dev; + + printk(KERN_INFO "%s\n", __func__); + + 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)) { + dprintk("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); + + /* Enable all known interrupts by setting the interrupt mask. */ + if (!tp->ecdev) + RTL_W16(IntrMask, tp->intr_event); +} + +static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + 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 __iomem *ioaddr) +{ + u32 csi; + + 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 __iomem *ioaddr, struct ephy_info *e, int len) +{ + u16 w; + + 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_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + static struct ephy_info e_info_8168cp[] = { + { 0x01, 0, 0x0001 }, + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0042 }, + { 0x06, 0x0080, 0x0000 }, + { 0x07, 0, 0x2000 } + }; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + static struct ephy_info e_info_8168c_1[] = { + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0002 }, + { 0x06, 0x0080, 0x0000 } + }; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + static struct ephy_info e_info_8168c_2[] = { + { 0x01, 0, 0x0001 }, + { 0x03, 0x0400, 0x0220 } + }; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + rtl_hw_start_8168c_2(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_4(void __iomem *ioaddr, struct pci_dev *pdev) +{ + rtl_csi_access_enable(ioaddr); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168d(void __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr = tp->mmio_addr; + struct pci_dev *pdev = tp->pci_dev; + + 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: + printk(KERN_ERR PFX "%s: unknown chipset (mac_version = %d).\n", + dev->name, tp->mac_version); + break; + } + + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(Cfg9346, Cfg9346_Lock); + + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + + if (!tp->ecdev) + 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 __iomem *ioaddr, struct pci_dev *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; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr = tp->mmio_addr; + struct pci_dev *pdev = tp->pci_dev; + + 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); + + if (!tp->ecdev) + RTL_W16(IntrMask, tp->intr_event); +} + +static int rtl8169_change_mtu(struct net_device *dev, int new_mtu) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret = 0; + + if (new_mtu < ETH_ZLEN || new_mtu > SafeMtu) + return -EINVAL; + + dev->mtu = new_mtu; + + if (!netif_running(dev)) + goto out; + + rtl8169_down(dev); + + rtl8169_set_rxbufsize(tp, dev); + + ret = rtl8169_init_ring(dev); + if (ret < 0) + goto out; + + napi_enable(&tp->napi); + + rtl_hw_start(dev); + + rtl8169_request_timer(dev); + +out: + return ret; +} + +static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc) +{ + desc->addr = cpu_to_le64(0x0badbadbadbadbadull); + desc->opts1 &= ~cpu_to_le32(DescOwn | RsvdMask); +} + +static void rtl8169_free_rx_skb(struct rtl8169_private *tp, + struct sk_buff **sk_buff, struct RxDesc *desc) +{ + struct pci_dev *pdev = tp->pci_dev; + + pci_unmap_single(pdev, le64_to_cpu(desc->addr), tp->rx_buf_sz, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(*sk_buff); + *sk_buff = NULL; + rtl8169_make_unusable_by_asic(desc); +} + +static inline void rtl8169_mark_to_asic(struct RxDesc *desc, u32 rx_buf_sz) +{ + u32 eor = le32_to_cpu(desc->opts1) & RingEnd; + + desc->opts1 = cpu_to_le32(DescOwn | eor | rx_buf_sz); +} + +static inline void rtl8169_map_to_asic(struct RxDesc *desc, dma_addr_t mapping, + u32 rx_buf_sz) +{ + desc->addr = cpu_to_le64(mapping); + wmb(); + rtl8169_mark_to_asic(desc, rx_buf_sz); +} + +static struct sk_buff *rtl8169_alloc_rx_skb(struct pci_dev *pdev, + struct net_device *dev, + struct RxDesc *desc, int rx_buf_sz, + unsigned int align) +{ + struct sk_buff *skb; + dma_addr_t mapping; + unsigned int pad; + + pad = align ? align : NET_IP_ALIGN; + + skb = netdev_alloc_skb(dev, rx_buf_sz + pad); + if (!skb) + goto err_out; + + skb_reserve(skb, align ? ((pad - 1) & (unsigned long)skb->data) : pad); + + mapping = pci_map_single(pdev, skb->data, rx_buf_sz, + PCI_DMA_FROMDEVICE); + + rtl8169_map_to_asic(desc, mapping, rx_buf_sz); +out: + return skb; + +err_out: + rtl8169_make_unusable_by_asic(desc); + goto out; +} + +static void rtl8169_rx_clear(struct rtl8169_private *tp) +{ + unsigned int i; + + for (i = 0; i < NUM_RX_DESC; i++) { + if (tp->Rx_skbuff[i]) { + rtl8169_free_rx_skb(tp, tp->Rx_skbuff + i, + tp->RxDescArray + i); + } + } +} + +static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev, + u32 start, u32 end) +{ + u32 cur; + + for (cur = start; end - cur != 0; cur++) { + struct sk_buff *skb; + unsigned int i = cur % NUM_RX_DESC; + + WARN_ON((s32)(end - cur) < 0); + + if (tp->Rx_skbuff[i]) + continue; + + skb = rtl8169_alloc_rx_skb(tp->pci_dev, dev, + tp->RxDescArray + i, + tp->rx_buf_sz, tp->align); + if (!skb) + break; + + tp->Rx_skbuff[i] = skb; + } + return cur - start; +} + +static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc) +{ + desc->opts1 |= cpu_to_le32(RingEnd); +} + +static void rtl8169_init_ring_indexes(struct rtl8169_private *tp) +{ + tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0; +} + +static int rtl8169_init_ring(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + rtl8169_init_ring_indexes(tp); + + memset(tp->tx_skb, 0x0, NUM_TX_DESC * sizeof(struct ring_info)); + memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *)); + + if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC) + goto err_out; + + rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1); + + return 0; + +err_out: + rtl8169_rx_clear(tp); + return -ENOMEM; +} + +static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct ring_info *tx_skb, + struct TxDesc *desc) +{ + unsigned int len = tx_skb->len; + + pci_unmap_single(pdev, le64_to_cpu(desc->addr), len, PCI_DMA_TODEVICE); + desc->opts1 = 0x00; + desc->opts2 = 0x00; + desc->addr = 0x00; + tx_skb->len = 0; +} + +static void rtl8169_tx_clear(struct rtl8169_private *tp) +{ + unsigned int i; + + for (i = tp->dirty_tx; i < tp->dirty_tx + NUM_TX_DESC; i++) { + unsigned int entry = i % NUM_TX_DESC; + struct ring_info *tx_skb = tp->tx_skb + entry; + unsigned int len = tx_skb->len; + + if (len) { + struct sk_buff *skb = tx_skb->skb; + + rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, + tp->TxDescArray + entry); + if (skb) { + if (!tp->ecdev) + dev_kfree_skb(skb); + tx_skb->skb = NULL; + } + tp->dev->stats.tx_dropped++; + } + } + tp->cur_tx = tp->dirty_tx = 0; +} + +static void rtl8169_schedule_work(struct net_device *dev, work_func_t task) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + PREPARE_DELAYED_WORK(&tp->task, task); + schedule_delayed_work(&tp->task, 4); +} + +static void rtl8169_wait_for_quiescence(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + synchronize_irq(dev->irq); + + /* Wait for any pending NAPI task to complete */ + napi_disable(&tp->napi); + + rtl8169_irq_mask_and_ack(ioaddr); + + tp->intr_mask = 0xffff; + RTL_W16(IntrMask, tp->intr_event); + napi_enable(&tp->napi); +} + +static void rtl8169_reinit_task(struct work_struct *work) +{ + struct rtl8169_private *tp = + container_of(work, struct rtl8169_private, task.work); + struct net_device *dev = tp->dev; + int ret; + + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + + rtl8169_wait_for_quiescence(dev); + rtl8169_close(dev); + + ret = rtl8169_open(dev); + if (unlikely(ret < 0)) { + if (net_ratelimit() && netif_msg_drv(tp)) { + printk(KERN_ERR PFX "%s: reinit failure (status = %d)." + " Rescheduling.\n", dev->name, ret); + } + rtl8169_schedule_work(dev, rtl8169_reinit_task); + } + +out_unlock: + rtnl_unlock(); +} + +static void rtl8169_reset_task(struct work_struct *work) +{ + struct rtl8169_private *tp = + container_of(work, struct rtl8169_private, task.work); + struct net_device *dev = tp->dev; + + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + + rtl8169_wait_for_quiescence(dev); + + rtl8169_rx_interrupt(dev, tp, tp->mmio_addr, ~(u32)0); + rtl8169_tx_clear(tp); + + if (tp->dirty_rx == tp->cur_rx) { + rtl8169_init_ring_indexes(tp); + rtl_hw_start(dev); + netif_wake_queue(dev); + rtl8169_check_link_status(dev, tp, tp->mmio_addr); + } else { + if (net_ratelimit() && netif_msg_intr(tp)) { + printk(KERN_EMERG PFX "%s: Rx buffers shortage\n", + dev->name); + } + rtl8169_schedule_work(dev, rtl8169_reset_task); + } + +out_unlock: + rtnl_unlock(); +} + +static void rtl8169_tx_timeout(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (tp->ecdev) + return; + + rtl8169_hw_reset(tp->mmio_addr); + + /* Let's wait a bit while any (async) irq lands on */ + rtl8169_schedule_work(dev, rtl8169_reset_task); +} + +static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb, + u32 opts1) +{ + struct skb_shared_info *info = skb_shinfo(skb); + unsigned int cur_frag, entry; + struct TxDesc * uninitialized_var(txd); + + entry = tp->cur_tx; + for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) { + skb_frag_t *frag = info->frags + cur_frag; + dma_addr_t mapping; + u32 status, len; + void *addr; + + entry = (entry + 1) % NUM_TX_DESC; + + txd = tp->TxDescArray + entry; + len = frag->size; + addr = ((void *) page_address(frag->page)) + frag->page_offset; + mapping = pci_map_single(tp->pci_dev, addr, len, PCI_DMA_TODEVICE); + + /* anti gcc 2.95.3 bugware (sic) */ + status = opts1 | len | (RingEnd * !((entry + 1) % NUM_TX_DESC)); + + txd->opts1 = cpu_to_le32(status); + txd->addr = cpu_to_le64(mapping); + + tp->tx_skb[entry].len = len; + } + + if (cur_frag) { + tp->tx_skb[entry].skb = skb; + txd->opts1 |= cpu_to_le32(LastFrag); + } + + return cur_frag; +} + +static inline u32 rtl8169_tso_csum(struct sk_buff *skb, struct net_device *dev) +{ + if (dev->features & NETIF_F_TSO) { + u32 mss = skb_shinfo(skb)->gso_size; + + if (mss) + return LargeSend | ((mss & MSSMask) << MSSShift); + } + if (skb->ip_summed == CHECKSUM_PARTIAL) { + const struct iphdr *ip = ip_hdr(skb); + + if (ip->protocol == IPPROTO_TCP) + return IPCS | TCPCS; + else if (ip->protocol == IPPROTO_UDP) + return IPCS | UDPCS; + WARN_ON(1); /* we need a WARN() */ + } + return 0; +} + +static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned int frags, entry = tp->cur_tx % NUM_TX_DESC; + struct TxDesc *txd = tp->TxDescArray + entry; + void __iomem *ioaddr = tp->mmio_addr; + dma_addr_t mapping; + u32 status, len; + u32 opts1; + int ret = NETDEV_TX_OK; + + if (unlikely(TX_BUFFS_AVAIL(tp) < skb_shinfo(skb)->nr_frags)) { + if (netif_msg_drv(tp)) { + printk(KERN_ERR + "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + } + goto err_stop; + } + + if (unlikely(le32_to_cpu(txd->opts1) & DescOwn)) + goto err_stop; + + opts1 = DescOwn | rtl8169_tso_csum(skb, dev); + + frags = rtl8169_xmit_frags(tp, skb, opts1); + if (frags) { + len = skb_headlen(skb); + opts1 |= FirstFrag; + } else { + len = skb->len; + opts1 |= FirstFrag | LastFrag; + tp->tx_skb[entry].skb = skb; + } + + mapping = pci_map_single(tp->pci_dev, skb->data, len, PCI_DMA_TODEVICE); + + tp->tx_skb[entry].len = len; + txd->addr = cpu_to_le64(mapping); + txd->opts2 = cpu_to_le32(rtl8169_tx_vlan_tag(tp, skb)); + + wmb(); + + /* anti gcc 2.95.3 bugware (sic) */ + status = opts1 | len | (RingEnd * !((entry + 1) % NUM_TX_DESC)); + txd->opts1 = cpu_to_le32(status); + + dev->trans_start = jiffies; + + tp->cur_tx += frags + 1; + + smp_wmb(); + + RTL_W8(TxPoll, NPQ); /* set polling bit */ + + if (!tp->ecdev) { + if (TX_BUFFS_AVAIL(tp) < MAX_SKB_FRAGS) { + netif_stop_queue(dev); + smp_rmb(); + if (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS) + netif_wake_queue(dev); + } + } + +out: + return ret; + +err_stop: + if (!tp->ecdev) + netif_stop_queue(dev); + ret = NETDEV_TX_BUSY; + dev->stats.tx_dropped++; + goto out; +} + +static void rtl8169_pcierr_interrupt(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + void __iomem *ioaddr = tp->mmio_addr; + u16 pci_status, pci_cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); + pci_read_config_word(pdev, PCI_STATUS, &pci_status); + + if (netif_msg_intr(tp)) { + printk(KERN_ERR + "%s: PCI error (cmd = 0x%04x, status = 0x%04x).\n", + dev->name, pci_cmd, pci_status); + } + + /* + * The recovery sequence below admits a very elaborated explanation: + * - it seems to work; + * - I did not see what else could be done; + * - it makes iop3xx happy. + * + * Feel free to adjust to your needs. + */ + if (pdev->broken_parity_status) + pci_cmd &= ~PCI_COMMAND_PARITY; + else + pci_cmd |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + + pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); + + pci_write_config_word(pdev, PCI_STATUS, + pci_status & (PCI_STATUS_DETECTED_PARITY | + PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_REC_MASTER_ABORT | + PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_SIG_TARGET_ABORT)); + + /* The infamous DAC f*ckup only happens at boot time */ + if ((tp->cp_cmd & PCIDAC) && !tp->dirty_rx && !tp->cur_rx) { + if (netif_msg_intr(tp)) + printk(KERN_INFO "%s: disabling PCI DAC.\n", dev->name); + tp->cp_cmd &= ~PCIDAC; + RTL_W16(CPlusCmd, tp->cp_cmd); + dev->features &= ~NETIF_F_HIGHDMA; + } + + rtl8169_hw_reset(ioaddr); + + rtl8169_schedule_work(dev, rtl8169_reinit_task); +} + +static void rtl8169_tx_interrupt(struct net_device *dev, + struct rtl8169_private *tp, + void __iomem *ioaddr) +{ + unsigned int dirty_tx, tx_left; + + dirty_tx = tp->dirty_tx; + smp_rmb(); + tx_left = tp->cur_tx - dirty_tx; + + while (tx_left > 0) { + unsigned int entry = dirty_tx % NUM_TX_DESC; + struct ring_info *tx_skb = tp->tx_skb + entry; + u32 len = tx_skb->len; + u32 status; + + rmb(); + status = le32_to_cpu(tp->TxDescArray[entry].opts1); + if (status & DescOwn) + break; + + dev->stats.tx_bytes += len; + dev->stats.tx_packets++; + + rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, tp->TxDescArray + entry); + + if (status & LastFrag) { + if (!tp->ecdev) + dev_kfree_skb_irq(tx_skb->skb); + tx_skb->skb = NULL; + } + dirty_tx++; + tx_left--; + } + + if (tp->dirty_tx != dirty_tx) { + tp->dirty_tx = dirty_tx; + smp_wmb(); + if (!tp->ecdev && netif_queue_stopped(dev) && + (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)) { + netif_wake_queue(dev); + } + /* + * 8168 hack: TxPoll requests are lost when the Tx packets are + * too close. Let's kick an extra TxPoll request when a burst + * of start_xmit activity is detected (if it is not detected, + * it is slow enough). -- FR + */ + smp_rmb(); + if (tp->cur_tx != dirty_tx) + RTL_W8(TxPoll, NPQ); + } +} + +static inline int rtl8169_fragmented_frame(u32 status) +{ + return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag); +} + +static inline void rtl8169_rx_csum(struct sk_buff *skb, struct RxDesc *desc) +{ + u32 opts1 = le32_to_cpu(desc->opts1); + u32 status = opts1 & RxProtoMask; + + if (((status == RxProtoTCP) && !(opts1 & TCPFail)) || + ((status == RxProtoUDP) && !(opts1 & UDPFail)) || + ((status == RxProtoIP) && !(opts1 & IPFail))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; +} + +static inline bool rtl8169_try_rx_copy(struct sk_buff **sk_buff, + struct rtl8169_private *tp, int pkt_size, + dma_addr_t addr) +{ + struct sk_buff *skb; + bool done = false; + + if (pkt_size >= rx_copybreak) + goto out; + + skb = netdev_alloc_skb(tp->dev, pkt_size + NET_IP_ALIGN); + if (!skb) + goto out; + + pci_dma_sync_single_for_cpu(tp->pci_dev, addr, pkt_size, + PCI_DMA_FROMDEVICE); + skb_reserve(skb, NET_IP_ALIGN); + skb_copy_from_linear_data(*sk_buff, skb->data, pkt_size); + *sk_buff = skb; + done = true; +out: + return done; +} + +static int rtl8169_rx_interrupt(struct net_device *dev, + struct rtl8169_private *tp, + void __iomem *ioaddr, u32 budget) +{ + unsigned int cur_rx, rx_left; + unsigned int delta, count; + + cur_rx = tp->cur_rx; + rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; + rx_left = min(rx_left, budget); + + for (; rx_left > 0; rx_left--, cur_rx++) { + unsigned int entry = cur_rx % NUM_RX_DESC; + struct RxDesc *desc = tp->RxDescArray + entry; + u32 status; + + rmb(); + status = le32_to_cpu(desc->opts1); + + if (status & DescOwn) + break; + if (unlikely(status & RxRES)) { + if (netif_msg_rx_err(tp)) { + printk(KERN_INFO + "%s: Rx ERROR. status = %08x\n", + dev->name, status); + } + dev->stats.rx_errors++; + if (status & (RxRWT | RxRUNT)) + dev->stats.rx_length_errors++; + if (status & RxCRC) + dev->stats.rx_crc_errors++; + if (status & RxFOVF) { + if (!tp->ecdev) + rtl8169_schedule_work(dev, rtl8169_reset_task); + dev->stats.rx_fifo_errors++; + } + rtl8169_mark_to_asic(desc, tp->rx_buf_sz); + } else { + struct sk_buff *skb = tp->Rx_skbuff[entry]; + dma_addr_t addr = le64_to_cpu(desc->addr); + int pkt_size = (status & 0x00001FFF) - 4; + struct pci_dev *pdev = tp->pci_dev; + + /* + * The driver does not support incoming fragmented + * frames. They are seen as a symptom of over-mtu + * sized frames. + */ + if (unlikely(rtl8169_fragmented_frame(status))) { + dev->stats.rx_dropped++; + dev->stats.rx_length_errors++; + rtl8169_mark_to_asic(desc, tp->rx_buf_sz); + continue; + } + + rtl8169_rx_csum(skb, desc); + + if (tp->ecdev) { + pci_dma_sync_single_for_cpu(pdev, addr, pkt_size, + PCI_DMA_FROMDEVICE); + + ecdev_receive(tp->ecdev, skb->data, pkt_size); + + pci_dma_sync_single_for_device(pdev, addr, + pkt_size, PCI_DMA_FROMDEVICE); + rtl8169_mark_to_asic(desc, tp->rx_buf_sz); + + // No need to detect link status as + // long as frames are received: Reset watchdog. + tp->ec_watchdog_jiffies = jiffies; + } else { + if (rtl8169_try_rx_copy(&skb, tp, pkt_size, addr)) { + pci_dma_sync_single_for_device(pdev, addr, + pkt_size, PCI_DMA_FROMDEVICE); + rtl8169_mark_to_asic(desc, tp->rx_buf_sz); + } else { + pci_unmap_single(pdev, addr, tp->rx_buf_sz, + PCI_DMA_FROMDEVICE); + tp->Rx_skbuff[entry] = NULL; + } + + skb_put(skb, pkt_size); + skb->protocol = eth_type_trans(skb, dev); + + if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0) + netif_receive_skb(skb); + } + + dev->stats.rx_bytes += pkt_size; + dev->stats.rx_packets++; + } + + /* Work around for AMD plateform. */ + if ((desc->opts2 & cpu_to_le32(0xfffe000)) && + (tp->mac_version == RTL_GIGA_MAC_VER_05)) { + desc->opts2 = 0; + cur_rx++; + } + } + + count = cur_rx - tp->cur_rx; + tp->cur_rx = cur_rx; + + if (tp->ecdev) { + /* descriptors are cleaned up immediately. */ + tp->dirty_rx = tp->cur_rx; + } else { + delta = rtl8169_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx); + if (!delta && count && netif_msg_intr(tp)) + printk(KERN_INFO "%s: no Rx buffer allocated\n", dev->name); + tp->dirty_rx += delta; + + /* + * FIXME: until there is periodic timer to try and refill the ring, + * a temporary shortage may definitely kill the Rx process. + * - disable the asic to try and avoid an overflow and kick it again + * after refill ? + * - how do others driver handle this condition (Uh oh...). + */ + if ((tp->dirty_rx + NUM_RX_DESC == tp->cur_rx) && netif_msg_intr(tp)) + printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev->name); + } + + return count; +} + +static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) +{ + struct net_device *dev = dev_instance; + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int handled = 0; + int status; + + status = RTL_R16(IntrStatus); + + /* hotplug/major error/no more work/shared irq */ + if ((status == 0xffff) || !status) + goto out; + + handled = 1; + + if (unlikely(!tp->ecdev && !netif_running(dev))) { + rtl8169_asic_down(ioaddr); + goto out; + } + + status &= tp->intr_mask; + RTL_W16(IntrStatus, + (status & RxFIFOOver) ? (status | RxOverflow) : status); + + if (!(status & tp->intr_event)) + goto out; + + /* Work around for rx fifo overflow */ + if (unlikely(status & RxFIFOOver) && + (tp->mac_version == RTL_GIGA_MAC_VER_11)) { + netif_stop_queue(dev); + rtl8169_tx_timeout(dev); + goto out; + } + + if (unlikely(status & SYSErr)) { + rtl8169_pcierr_interrupt(dev); + goto out; + } + + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); + + if (status & tp->napi_event) { + RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event); + tp->intr_mask = ~tp->napi_event; + + if (likely(netif_rx_schedule_prep(&tp->napi))) + __netif_rx_schedule(&tp->napi); + else if (netif_msg_intr(tp)) { + printk(KERN_INFO "%s: interrupt %04x in poll\n", + dev->name, status); + } + } +out: + return IRQ_RETVAL(handled); +} + +static void ec_poll(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + + rtl8169_interrupt(pdev->irq, dev); + rtl8169_rx_interrupt(dev, tp, tp->mmio_addr, 100); // FIXME + rtl8169_tx_interrupt(dev, tp, tp->mmio_addr); + + if (jiffies - tp->ec_watchdog_jiffies >= 2 * HZ) { + rtl8169_phy_timer((unsigned long) dev); + tp->ec_watchdog_jiffies = jiffies; + } +} + +static int rtl8169_poll(struct napi_struct *napi, int budget) +{ + struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi); + struct net_device *dev = tp->dev; + void __iomem *ioaddr = tp->mmio_addr; + int work_done; + + work_done = rtl8169_rx_interrupt(dev, tp, ioaddr, (u32) budget); + rtl8169_tx_interrupt(dev, tp, ioaddr); + + if (work_done < budget) { + netif_rx_complete(napi); + tp->intr_mask = 0xffff; + /* + * 20040426: the barrier is not strictly required but the + * behavior of the irq handler could be less predictable + * without it. Btw, the lack of flush for the posted pci + * write is safe - FR + */ + smp_wmb(); + RTL_W16(IntrMask, tp->intr_event); + } + + return work_done; +} + +static void rtl8169_rx_missed(struct net_device *dev, void __iomem *ioaddr) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (tp->mac_version > RTL_GIGA_MAC_VER_06) + return; + + dev->stats.rx_missed_errors += (RTL_R32(RxMissed) & 0xffffff); + RTL_W32(RxMissed, 0); +} + +static void rtl8169_down(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int intrmask; + + rtl8169_delete_timer(dev); + + if (!tp->ecdev) { + netif_stop_queue(dev); + + napi_disable(&tp->napi); + + } +core_down: + if (!tp->ecdev) + spin_lock_irq(&tp->lock); + + rtl8169_asic_down(ioaddr); + + rtl8169_rx_missed(dev, ioaddr); + + if (!tp->ecdev) + spin_unlock_irq(&tp->lock); + + if (!tp->ecdev) + synchronize_irq(dev->irq); + + /* Give a racing hard_start_xmit a few cycles to complete. */ + synchronize_sched(); /* FIXME: should this be synchronize_irq()? */ + + /* + * And now for the 50k$ question: are IRQ disabled or not ? + * + * Two paths lead here: + * 1) dev->close + * -> netif_running() is available to sync the current code and the + * IRQ handler. See rtl8169_interrupt for details. + * 2) dev->change_mtu + * -> rtl8169_poll can not be issued again and re-enable the + * interruptions. Let's simply issue the IRQ down sequence again. + * + * No loop if hotpluged or major error (0xffff). + */ + intrmask = RTL_R16(IntrMask); + if (intrmask && (intrmask != 0xffff)) + goto core_down; + + rtl8169_tx_clear(tp); + + rtl8169_rx_clear(tp); +} + +static int rtl8169_close(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + + /* update counters before going down */ + rtl8169_update_counters(dev); + + rtl8169_down(dev); + + if (!tp->ecdev) + free_irq(dev->irq, dev); + + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + tp->TxDescArray = NULL; + tp->RxDescArray = NULL; + + return 0; +} + +static void rtl_set_rx_mode(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + u32 mc_filter[2]; /* Multicast hash filter */ + int rx_mode; + u32 tmp = 0; + + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + if (netif_msg_link(tp)) { + printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", + dev->name); + } + rx_mode = + AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct dev_mc_list *mclist; + unsigned int i; + + rx_mode = AcceptBroadcast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + rx_mode |= AcceptMulticast; + } + } + + spin_lock_irqsave(&tp->lock, flags); + + tmp = rtl8169_rx_config | rx_mode | + (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); + + if (tp->mac_version > RTL_GIGA_MAC_VER_06) { + u32 data = mc_filter[0]; + + mc_filter[0] = swab32(mc_filter[1]); + mc_filter[1] = swab32(data); + } + + RTL_W32(MAR0 + 0, mc_filter[0]); + RTL_W32(MAR0 + 4, mc_filter[1]); + + RTL_W32(RxConfig, tmp); + + spin_unlock_irqrestore(&tp->lock, flags); +} + +/** + * rtl8169_get_stats - Get rtl8169 read/write statistics + * @dev: The Ethernet Device to get statistics for + * + * Get TX/RX statistics for rtl8169 + */ +static struct net_device_stats *rtl8169_get_stats(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + if (netif_running(dev)) { + spin_lock_irqsave(&tp->lock, flags); + rtl8169_rx_missed(dev, ioaddr); + spin_unlock_irqrestore(&tp->lock, flags); + } + + return &dev->stats; +} + +#ifdef CONFIG_PM + +static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + if (tp->ecdev) + return -EBUSY; + + if (!netif_running(dev)) + goto out_pci_suspend; + + netif_device_detach(dev); + netif_stop_queue(dev); + + spin_lock_irq(&tp->lock); + + rtl8169_asic_down(ioaddr); + + rtl8169_rx_missed(dev, ioaddr); + + spin_unlock_irq(&tp->lock); + +out_pci_suspend: + pci_save_state(pdev); + pci_enable_wake(pdev, pci_choose_state(pdev, state), + (tp->features & RTL_FEATURE_WOL) ? 1 : 0); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int rtl8169_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + + if (tp->ecdev) + return -EBUSY; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); + + if (!netif_running(dev)) + goto out; + + netif_device_attach(dev); + + rtl8169_schedule_work(dev, rtl8169_reset_task); +out: + return 0; +} + +static void rtl_shutdown(struct pci_dev *pdev) +{ + rtl8169_suspend(pdev, PMSG_SUSPEND); +} + +#endif /* CONFIG_PM */ + +static struct pci_driver rtl8169_pci_driver = { + .name = MODULENAME, + .id_table = rtl8169_pci_tbl, + .probe = rtl8169_init_one, + .remove = __devexit_p(rtl8169_remove_one), +#ifdef CONFIG_PM + .suspend = rtl8169_suspend, + .resume = rtl8169_resume, + .shutdown = rtl_shutdown, +#endif +}; + +static int __init rtl8169_init_module(void) +{ + return pci_register_driver(&rtl8169_pci_driver); +} + +static void __exit rtl8169_cleanup_module(void) +{ + pci_unregister_driver(&rtl8169_pci_driver); +} + +module_init(rtl8169_init_module); +module_exit(rtl8169_cleanup_module); diff -r 326d47aa986c -r 1baac79a40cf devices/r8169-2.6.29-orig.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/r8169-2.6.29-orig.c Thu Nov 19 14:44:57 2009 +0100 @@ -0,0 +1,3873 @@ +/* + * r8169.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen + * Copyright (c) 2003 - 2007 Francois Romieu + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define RTL8169_VERSION "2.3LK-NAPI" +#define MODULENAME "r8169" +#define PFX MODULENAME ": " + +#ifdef RTL8169_DEBUG +#define assert(expr) \ + if (!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__func__,__LINE__); \ + } +#define dprintk(fmt, args...) \ + do { printk(KERN_DEBUG PFX fmt, ## args); } while (0) +#else +#define assert(expr) do {} while (0) +#define dprintk(fmt, args...) do {} while (0) +#endif /* RTL8169_DEBUG */ + +#define R8169_MSG_DEFAULT \ + (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN) + +#define TX_BUFFS_AVAIL(tp) \ + (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx - 1) + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static const int max_interrupt_work = 20; + +/* 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 const int multicast_filter_limit = 32; + +/* 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 64 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 256 /* 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 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 +}; + +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 *); + +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,}, +}; + +MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); + +static int rx_copybreak = 200; +static int use_dac; +static struct { + u32 msg_enable; +} debug = { -1 }; + +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 { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct RxDesc { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct ring_info { + struct sk_buff *skb; + u32 len; + u8 __pad[sizeof(void *) - sizeof(u32)]; +}; + +enum features { + RTL_FEATURE_WOL = (1 << 0), + RTL_FEATURE_MSI = (1 << 1), + RTL_FEATURE_GMII = (1 << 2), +}; + +struct rtl8169_counters { + __le64 tx_packets; + __le64 rx_packets; + __le64 tx_errors; + __le32 rx_errors; + __le16 rx_missed; + __le16 align_errors; + __le32 tx_one_collision; + __le32 tx_multi_collision; + __le64 rx_unicast; + __le64 rx_broadcast; + __le32 rx_multicast; + __le16 tx_aborted; + __le16 tx_underun; +}; + +struct rtl8169_private { + void __iomem *mmio_addr; /* memory map physical address */ + struct pci_dev *pci_dev; /* Index of PCI device */ + struct net_device *dev; + struct napi_struct napi; + spinlock_t lock; /* spin lock flag */ + u32 msg_enable; + int chipset; + int mac_version; + u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + u32 dirty_rx; + u32 dirty_tx; + struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */ + struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */ + dma_addr_t TxPhyAddr; + dma_addr_t RxPhyAddr; + struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ + struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */ + unsigned align; + unsigned rx_buf_sz; + struct timer_list timer; + u16 cp_cmd; + u16 intr_event; + u16 napi_event; + u16 intr_mask; + int phy_auto_nego_reg; + int phy_1000_ctrl_reg; +#ifdef CONFIG_R8169_VLAN + struct vlan_group *vlgrp; +#endif + int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex); + int (*get_settings)(struct net_device *, struct ethtool_cmd *); + void (*phy_reset_enable)(void __iomem *); + void (*hw_start)(struct net_device *); + unsigned int (*phy_reset_pending)(void __iomem *); + unsigned int (*link_ok)(void __iomem *); + int (*do_ioctl)(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd); + int pcie_cap; + struct delayed_work task; + unsigned features; + + struct mii_if_info mii; + struct rtl8169_counters counters; +}; + +MODULE_AUTHOR("Realtek and the Linux r8169 crew "); +MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver"); +module_param(rx_copybreak, int, 0); +MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); +module_param(use_dac, int, 0); +MODULE_PARM_DESC(use_dac, "Enable PCI DAC. Unsafe on 32 bit PCI slot."); +module_param_named(debug, debug.msg_enable, int, 0); +MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RTL8169_VERSION); + +static int rtl8169_open(struct net_device *dev); +static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance); +static int rtl8169_init_ring(struct net_device *dev); +static void rtl_hw_start(struct net_device *dev); +static int rtl8169_close(struct net_device *dev); +static void rtl_set_rx_mode(struct net_device *dev); +static void rtl8169_tx_timeout(struct net_device *dev); +static struct net_device_stats *rtl8169_get_stats(struct net_device *dev); +static int rtl8169_rx_interrupt(struct net_device *, struct rtl8169_private *, + void __iomem *, u32 budget); +static int rtl8169_change_mtu(struct net_device *dev, int new_mtu); +static void rtl8169_down(struct net_device *dev); +static void rtl8169_rx_clear(struct rtl8169_private *tp); +static int rtl8169_poll(struct napi_struct *napi, int budget); + +static const unsigned int rtl8169_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); + +static void mdio_write(void __iomem *ioaddr, int reg_addr, int value) +{ + int i; + + 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; + udelay(25); + } +} + +static int mdio_read(void __iomem *ioaddr, int reg_addr) +{ + int i, value = -1; + + RTL_W32(PHYAR, 0x0 | (reg_addr & 0x1f) << 16); + + 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 = RTL_R32(PHYAR) & 0xffff; + break; + } + udelay(25); + } + return value; +} + +static void mdio_patch(void __iomem *ioaddr, int reg_addr, int value) +{ + mdio_write(ioaddr, reg_addr, mdio_read(ioaddr, reg_addr) | value); +} + +static void rtl_mdio_write(struct net_device *dev, int phy_id, int location, + int val) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + mdio_write(ioaddr, location, val); +} + +static int rtl_mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + return mdio_read(ioaddr, location); +} + +static void rtl_ephy_write(void __iomem *ioaddr, int reg_addr, int value) +{ + unsigned int i; + + RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) | + (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(EPHYAR) & EPHYAR_FLAG)) + break; + udelay(10); + } +} + +static u16 rtl_ephy_read(void __iomem *ioaddr, int reg_addr) +{ + u16 value = 0xffff; + unsigned int i; + + 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; + } + udelay(10); + } + + return value; +} + +static void rtl_csi_write(void __iomem *ioaddr, int addr, int value) +{ + unsigned int i; + + RTL_W32(CSIDR, value); + RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(CSIAR) & CSIAR_FLAG)) + break; + udelay(10); + } +} + +static u32 rtl_csi_read(void __iomem *ioaddr, int addr) +{ + u32 value = ~0x00; + unsigned int i; + + RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (RTL_R32(CSIAR) & CSIAR_FLAG) { + value = RTL_R32(CSIDR); + break; + } + udelay(10); + } + + return value; +} + +static void rtl8169_irq_mask_and_ack(void __iomem *ioaddr) +{ + RTL_W16(IntrMask, 0x0000); + + RTL_W16(IntrStatus, 0xffff); +} + +static void rtl8169_asic_down(void __iomem *ioaddr) +{ + RTL_W8(ChipCmd, 0x00); + rtl8169_irq_mask_and_ack(ioaddr); + RTL_R16(CPlusCmd); +} + +static unsigned int rtl8169_tbi_reset_pending(void __iomem *ioaddr) +{ + return RTL_R32(TBICSR) & TBIReset; +} + +static unsigned int rtl8169_xmii_reset_pending(void __iomem *ioaddr) +{ + return mdio_read(ioaddr, MII_BMCR) & BMCR_RESET; +} + +static unsigned int rtl8169_tbi_link_ok(void __iomem *ioaddr) +{ + return RTL_R32(TBICSR) & TBILinkOk; +} + +static unsigned int rtl8169_xmii_link_ok(void __iomem *ioaddr) +{ + return RTL_R8(PHYstatus) & LinkStatus; +} + +static void rtl8169_tbi_reset_enable(void __iomem *ioaddr) +{ + RTL_W32(TBICSR, RTL_R32(TBICSR) | TBIReset); +} + +static void rtl8169_xmii_reset_enable(void __iomem *ioaddr) +{ + unsigned int val; + + val = mdio_read(ioaddr, MII_BMCR) | BMCR_RESET; + mdio_write(ioaddr, MII_BMCR, val & 0xffff); +} + +static void rtl8169_check_link_status(struct net_device *dev, + struct rtl8169_private *tp, + void __iomem *ioaddr) +{ + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + if (tp->link_ok(ioaddr)) { + netif_carrier_on(dev); + if (netif_msg_ifup(tp)) + printk(KERN_INFO PFX "%s: link up\n", dev->name); + } else { + if (netif_msg_ifdown(tp)) + printk(KERN_INFO PFX "%s: link down\n", dev->name); + netif_carrier_off(dev); + } + spin_unlock_irqrestore(&tp->lock, flags); +} + +static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u8 options; + + wol->wolopts = 0; + +#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) + wol->supported = WAKE_ANY; + + spin_lock_irq(&tp->lock); + + options = RTL_R8(Config1); + if (!(options & PMEnable)) + goto out_unlock; + + options = RTL_R8(Config3); + if (options & LinkUp) + wol->wolopts |= WAKE_PHY; + if (options & MagicPacket) + wol->wolopts |= WAKE_MAGIC; + + options = RTL_R8(Config5); + if (options & UWF) + wol->wolopts |= WAKE_UCAST; + if (options & BWF) + wol->wolopts |= WAKE_BCAST; + if (options & MWF) + wol->wolopts |= WAKE_MCAST; + +out_unlock: + spin_unlock_irq(&tp->lock); +} + +static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int i; + static struct { + u32 opt; + u16 reg; + u8 mask; + } cfg[] = { + { WAKE_ANY, Config1, PMEnable }, + { WAKE_PHY, Config3, LinkUp }, + { WAKE_MAGIC, Config3, MagicPacket }, + { WAKE_UCAST, Config5, UWF }, + { WAKE_BCAST, Config5, BWF }, + { WAKE_MCAST, Config5, MWF }, + { WAKE_ANY, Config5, LanWake } + }; + + spin_lock_irq(&tp->lock); + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + for (i = 0; i < ARRAY_SIZE(cfg); i++) { + u8 options = RTL_R8(cfg[i].reg) & ~cfg[i].mask; + if (wol->wolopts & cfg[i].opt) + options |= cfg[i].mask; + RTL_W8(cfg[i].reg, options); + } + + RTL_W8(Cfg9346, Cfg9346_Lock); + + if (wol->wolopts) + tp->features |= RTL_FEATURE_WOL; + else + tp->features &= ~RTL_FEATURE_WOL; + device_set_wakeup_enable(&tp->pci_dev->dev, wol->wolopts); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +static void rtl8169_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + strcpy(info->driver, MODULENAME); + strcpy(info->version, RTL8169_VERSION); + strcpy(info->bus_info, pci_name(tp->pci_dev)); +} + +static int rtl8169_get_regs_len(struct net_device *dev) +{ + return R8169_REGS_SIZE; +} + +static int rtl8169_set_speed_tbi(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int ret = 0; + u32 reg; + + 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 { + if (netif_msg_link(tp)) { + printk(KERN_WARNING "%s: " + "incorrect speed setting refused in TBI mode\n", + dev->name); + } + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int rtl8169_set_speed_xmii(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int auto_nego, giga_ctrl; + + 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 (autoneg == AUTONEG_ENABLE) { + auto_nego |= (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + } else { + 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; + } + } + + /* 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)) && + netif_msg_link(tp)) { + printk(KERN_INFO "%s: PHY does not support 1000Mbps.\n", + dev->name); + } + giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + } + + 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); + } + + 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 int rtl8169_set_speed(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret; + + ret = tp->set_speed(dev, autoneg, speed, duplex); + + if (netif_running(dev) && (tp->phy_1000_ctrl_reg & ADVERTISE_1000FULL)) + mod_timer(&tp->timer, jiffies + RTL8169_PHY_TIMEOUT); + + return ret; +} + +static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&tp->lock, flags); + ret = rtl8169_set_speed(dev, cmd->autoneg, cmd->speed, cmd->duplex); + spin_unlock_irqrestore(&tp->lock, flags); + + return ret; +} + +static u32 rtl8169_get_rx_csum(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return tp->cp_cmd & RxChkSum; +} + +static int rtl8169_set_rx_csum(struct net_device *dev, u32 data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + + if (data) + tp->cp_cmd |= RxChkSum; + else + tp->cp_cmd &= ~RxChkSum; + + RTL_W16(CPlusCmd, tp->cp_cmd); + RTL_R16(CPlusCmd); + + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +#ifdef CONFIG_R8169_VLAN + +static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + return (tp->vlgrp && vlan_tx_tag_present(skb)) ? + TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00; +} + +static void rtl8169_vlan_rx_register(struct net_device *dev, + struct vlan_group *grp) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + tp->vlgrp = grp; + if (tp->vlgrp) + tp->cp_cmd |= RxVlan; + else + tp->cp_cmd &= ~RxVlan; + RTL_W16(CPlusCmd, tp->cp_cmd); + RTL_R16(CPlusCmd); + spin_unlock_irqrestore(&tp->lock, flags); +} + +static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, + struct sk_buff *skb) +{ + u32 opts2 = le32_to_cpu(desc->opts2); + struct vlan_group *vlgrp = tp->vlgrp; + int ret; + + if (vlgrp && (opts2 & RxVlanTag)) { + vlan_hwaccel_receive_skb(skb, vlgrp, swab16(opts2 & 0xffff)); + ret = 0; + } else + ret = -1; + desc->opts2 = 0; + return ret; +} + +#else /* !CONFIG_R8169_VLAN */ + +static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + return 0; +} + +static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, + struct sk_buff *skb) +{ + return -1; +} + +#endif + +static int rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 status; + + cmd->supported = + SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE; + cmd->port = PORT_FIBRE; + cmd->transceiver = XCVR_INTERNAL; + + status = RTL_R32(TBICSR); + cmd->advertising = (status & TBINwEnable) ? ADVERTISED_Autoneg : 0; + cmd->autoneg = !!(status & TBINwEnable); + + cmd->speed = SPEED_1000; + cmd->duplex = DUPLEX_FULL; /* Always set */ + + return 0; +} + +static int rtl8169_gset_xmii(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return mii_ethtool_gset(&tp->mii, cmd); +} + +static int rtl8169_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + int rc; + + spin_lock_irqsave(&tp->lock, flags); + + rc = tp->get_settings(dev, cmd); + + spin_unlock_irqrestore(&tp->lock, flags); + return rc; +} + +static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + + if (regs->len > R8169_REGS_SIZE) + regs->len = R8169_REGS_SIZE; + + spin_lock_irqsave(&tp->lock, flags); + memcpy_fromio(p, tp->mmio_addr, regs->len); + spin_unlock_irqrestore(&tp->lock, flags); +} + +static u32 rtl8169_get_msglevel(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return tp->msg_enable; +} + +static void rtl8169_set_msglevel(struct net_device *dev, u32 value) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + tp->msg_enable = value; +} + +static const char rtl8169_gstrings[][ETH_GSTRING_LEN] = { + "tx_packets", + "rx_packets", + "tx_errors", + "rx_errors", + "rx_missed", + "align_errors", + "tx_single_collisions", + "tx_multi_collisions", + "unicast", + "broadcast", + "multicast", + "tx_aborted", + "tx_underrun", +}; + +static int rtl8169_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(rtl8169_gstrings); + default: + return -EOPNOTSUPP; + } +} + +static void rtl8169_update_counters(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + struct rtl8169_counters *counters; + dma_addr_t paddr; + u32 cmd; + int wait = 1000; + + /* + * Some chips are unable to dump tally counters when the receiver + * is disabled. + */ + if ((RTL_R8(ChipCmd) & CmdRxEnb) == 0) + return; + + counters = pci_alloc_consistent(tp->pci_dev, sizeof(*counters), &paddr); + if (!counters) + return; + + RTL_W32(CounterAddrHigh, (u64)paddr >> 32); + cmd = (u64)paddr & DMA_32BIT_MASK; + RTL_W32(CounterAddrLow, cmd); + RTL_W32(CounterAddrLow, cmd | CounterDump); + + while (wait--) { + if ((RTL_R32(CounterAddrLow) & CounterDump) == 0) { + /* copy updated counters */ + memcpy(&tp->counters, counters, sizeof(*counters)); + break; + } + udelay(10); + } + + RTL_W32(CounterAddrLow, 0); + RTL_W32(CounterAddrHigh, 0); + + pci_free_consistent(tp->pci_dev, sizeof(*counters), counters, paddr); +} + +static void rtl8169_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + ASSERT_RTNL(); + + rtl8169_update_counters(dev); + + data[0] = le64_to_cpu(tp->counters.tx_packets); + data[1] = le64_to_cpu(tp->counters.rx_packets); + data[2] = le64_to_cpu(tp->counters.tx_errors); + data[3] = le32_to_cpu(tp->counters.rx_errors); + data[4] = le16_to_cpu(tp->counters.rx_missed); + data[5] = le16_to_cpu(tp->counters.align_errors); + data[6] = le32_to_cpu(tp->counters.tx_one_collision); + data[7] = le32_to_cpu(tp->counters.tx_multi_collision); + data[8] = le64_to_cpu(tp->counters.rx_unicast); + data[9] = le64_to_cpu(tp->counters.rx_broadcast); + data[10] = le32_to_cpu(tp->counters.rx_multicast); + data[11] = le16_to_cpu(tp->counters.tx_aborted); + data[12] = le16_to_cpu(tp->counters.tx_underun); +} + +static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + switch(stringset) { + case ETH_SS_STATS: + memcpy(data, *rtl8169_gstrings, sizeof(rtl8169_gstrings)); + break; + } +} + +static const struct ethtool_ops rtl8169_ethtool_ops = { + .get_drvinfo = rtl8169_get_drvinfo, + .get_regs_len = rtl8169_get_regs_len, + .get_link = ethtool_op_get_link, + .get_settings = rtl8169_get_settings, + .set_settings = rtl8169_set_settings, + .get_msglevel = rtl8169_get_msglevel, + .set_msglevel = rtl8169_set_msglevel, + .get_rx_csum = rtl8169_get_rx_csum, + .set_rx_csum = rtl8169_set_rx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .set_sg = ethtool_op_set_sg, + .set_tso = ethtool_op_set_tso, + .get_regs = rtl8169_get_regs, + .get_wol = rtl8169_get_wol, + .set_wol = rtl8169_set_wol, + .get_strings = rtl8169_get_strings, + .get_sset_count = rtl8169_get_sset_count, + .get_ethtool_stats = rtl8169_get_ethtool_stats, +}; + +static void rtl8169_write_gmii_reg_bit(void __iomem *ioaddr, int reg, + int bitnum, int bitval) +{ + int val; + + val = mdio_read(ioaddr, reg); + val = (bitval == 1) ? + val | (bitval << bitnum) : val & ~(0x0001 << bitnum); + mdio_write(ioaddr, reg, val & 0xffff); +} + +static void rtl8169_get_mac_version(struct rtl8169_private *tp, + void __iomem *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 + */ + 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 }, + + /* 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, 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 }, + + /* 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; + u32 reg; + + reg = RTL_R32(TxConfig); + while ((reg & p->mask) != p->val) + p++; + tp->mac_version = p->mac_version; + + if (p->mask == 0x00000000) { + struct pci_dev *pdev = tp->pci_dev; + + dev_info(&pdev->dev, "unknown MAC (%08x)\n", reg); + } +} + +static void rtl8169_print_mac_version(struct rtl8169_private *tp) +{ + dprintk("mac_version = 0x%02x\n", tp->mac_version); +} + +struct phy_reg { + u16 reg; + u16 val; +}; + +static void rtl_phy_write(void __iomem *ioaddr, struct phy_reg *regs, int len) +{ + while (len-- > 0) { + mdio_write(ioaddr, regs->reg, regs->val); + regs++; + } +} + +static void rtl8169s_hw_phy_config(void __iomem *ioaddr) +{ + struct { + u16 regs[5]; /* Beware of bit-sign propagation */ + } phy_magic[5] = { { + { 0x0000, //w 4 15 12 0 + 0x00a1, //w 3 15 0 00a1 + 0x0008, //w 2 15 0 0008 + 0x1020, //w 1 15 0 1020 + 0x1000 } },{ //w 0 15 0 1000 + { 0x7000, //w 4 15 12 7 + 0xff41, //w 3 15 0 ff41 + 0xde60, //w 2 15 0 de60 + 0x0140, //w 1 15 0 0140 + 0x0077 } },{ //w 0 15 0 0077 + { 0xa000, //w 4 15 12 a + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xfa00 } },{ //w 0 15 0 fa00 + { 0xb000, //w 4 15 12 b + 0xff41, //w 3 15 0 ff41 + 0xde20, //w 2 15 0 de20 + 0x0140, //w 1 15 0 0140 + 0x00bb } },{ //w 0 15 0 00bb + { 0xf000, //w 4 15 12 f + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xbf00 } //w 0 15 0 bf00 + } + }, *p = phy_magic; + unsigned int i; + + 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 = (mdio_read(ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff); + mdio_write(ioaddr, pos, val); + while (--pos >= 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 + } + mdio_write(ioaddr, 0x1f, 0x0000); //w 31 2 0 0 +} + +static void rtl8169sb_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x01, 0x90d0 }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bb_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + mdio_write(ioaddr, 0x1f, 0x0001); + mdio_patch(ioaddr, 0x16, 1 << 0); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bef_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168cp_1_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0000 }, + { 0x1d, 0x0f00 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x1ec8 }, + { 0x1f, 0x0000 } + }; + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168cp_2_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0000 } + }; + + 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_1_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1f, 0x0002 }, + { 0x00, 0x88d4 }, + { 0x01, 0x82b1 }, + { 0x03, 0x7002 }, + { 0x08, 0x9e30 }, + { 0x09, 0x01f0 }, + { 0x0a, 0x5500 }, + { 0x0c, 0x00c8 }, + { 0x1f, 0x0003 }, + { 0x12, 0xc096 }, + { 0x16, 0x000a }, + { 0x1f, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x09, 0x2000 }, + { 0x09, 0x0000 } + }; + + 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 rtl8168c_2_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 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 } + }; + + 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 __iomem *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 } + }; + + 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 __iomem *ioaddr) +{ + rtl8168c_3_hw_phy_config(ioaddr); +} + +static void rtl8168d_hw_phy_config(void __iomem *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 } + }; + + 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 __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0003 }, + { 0x08, 0x441d }, + { 0x01, 0x9100 }, + { 0x1f, 0x0000 } + }; + + 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 rtl_hw_phy_config(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + rtl8169_print_mac_version(tp); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_01: + break; + case RTL_GIGA_MAC_VER_02: + case RTL_GIGA_MAC_VER_03: + rtl8169s_hw_phy_config(ioaddr); + break; + 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_1_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_19: + rtl8168c_1_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_20: + 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_timer(unsigned long __opaque) +{ + struct net_device *dev = (struct net_device *)__opaque; + struct rtl8169_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->timer; + void __iomem *ioaddr = tp->mmio_addr; + unsigned long timeout = RTL8169_PHY_TIMEOUT; + + assert(tp->mac_version > RTL_GIGA_MAC_VER_01); + + if (!(tp->phy_1000_ctrl_reg & ADVERTISE_1000FULL)) + return; + + spin_lock_irq(&tp->lock); + + if (tp->phy_reset_pending(ioaddr)) { + /* + * A busy loop could burn quite a few cycles on nowadays CPU. + * Let's delay the execution of the timer for a few ticks. + */ + timeout = HZ/10; + goto out_mod_timer; + } + + if (tp->link_ok(ioaddr)) + goto out_unlock; + + if (netif_msg_link(tp)) + printk(KERN_WARNING "%s: PHY reset until link up\n", dev->name); + + tp->phy_reset_enable(ioaddr); + +out_mod_timer: + mod_timer(timer, jiffies + timeout); +out_unlock: + spin_unlock_irq(&tp->lock); +} + +static inline void rtl8169_delete_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->timer; + + if (tp->mac_version <= RTL_GIGA_MAC_VER_01) + return; + + del_timer_sync(timer); +} + +static inline void rtl8169_request_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->timer; + + if (tp->mac_version <= RTL_GIGA_MAC_VER_01) + return; + + mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ +static void rtl8169_netpoll(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + + disable_irq(pdev->irq); + rtl8169_interrupt(pdev->irq, dev); + enable_irq(pdev->irq); +} +#endif + +static void rtl8169_release_board(struct pci_dev *pdev, struct net_device *dev, + void __iomem *ioaddr) +{ + iounmap(ioaddr); + pci_release_regions(pdev); + pci_disable_device(pdev); + free_netdev(dev); +} + +static void rtl8169_phy_reset(struct net_device *dev, + struct rtl8169_private *tp) +{ + void __iomem *ioaddr = tp->mmio_addr; + unsigned int i; + + tp->phy_reset_enable(ioaddr); + for (i = 0; i < 100; i++) { + if (!tp->phy_reset_pending(ioaddr)) + return; + msleep(1); + } + if (netif_msg_link(tp)) + printk(KERN_ERR "%s: PHY reset failed.\n", dev->name); +} + +static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp) +{ + void __iomem *ioaddr = tp->mmio_addr; + + rtl_hw_phy_config(dev); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + dprintk("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) { + dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); + RTL_W8(0x82, 0x01); + dprintk("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) && netif_msg_link(tp)) + printk(KERN_INFO PFX "%s: TBI auto-negotiating\n", dev->name); +} + +static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr) +{ + void __iomem *ioaddr = tp->mmio_addr; + u32 high; + u32 low; + + low = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24); + high = addr[4] | (addr[5] << 8); + + spin_lock_irq(&tp->lock); + + RTL_W8(Cfg9346, Cfg9346_Unlock); + RTL_W32(MAC0, low); + RTL_W32(MAC4, high); + RTL_W8(Cfg9346, Cfg9346_Lock); + + spin_unlock_irq(&tp->lock); +} + +static int rtl_set_mac_address(struct net_device *dev, void *p) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + rtl_rar_set(tp, dev->dev_addr); + + return 0; +} + +static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(ifr); + + return netif_running(dev) ? tp->do_ioctl(tp, data, cmd) : -ENODEV; +} + +static int rtl_xmii_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd) +{ + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 32; /* Internal PHY */ + return 0; + + case SIOCGMIIREG: + data->val_out = mdio_read(tp->mmio_addr, data->reg_num & 0x1f); + return 0; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mdio_write(tp->mmio_addr, data->reg_num & 0x1f, data->val_in); + return 0; + } + return -EOPNOTSUPP; +} + +static int rtl_tbi_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd) +{ + return -EOPNOTSUPP; +} + +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_FEATURE_MSI + }, + [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, + .features = RTL_FEATURE_MSI + } +}; + +/* Cfg9346_Unlock assumed. */ +static unsigned rtl_try_msi(struct pci_dev *pdev, void __iomem *ioaddr, + const struct rtl_cfg_info *cfg) +{ + unsigned msi = 0; + u8 cfg2; + + cfg2 = RTL_R8(Config2) & ~MSIEnable; + if (cfg->features & RTL_FEATURE_MSI) { + if (pci_enable_msi(pdev)) { + dev_info(&pdev->dev, "no MSI. Back to INTx.\n"); + } else { + cfg2 |= MSIEnable; + msi = RTL_FEATURE_MSI; + } + } + RTL_W8(Config2, cfg2); + return msi; +} + +static void rtl_disable_msi(struct pci_dev *pdev, struct rtl8169_private *tp) +{ + if (tp->features & RTL_FEATURE_MSI) { + pci_disable_msi(pdev); + tp->features &= ~RTL_FEATURE_MSI; + } +} + +static const struct net_device_ops rtl8169_netdev_ops = { + .ndo_open = rtl8169_open, + .ndo_stop = rtl8169_close, + .ndo_get_stats = rtl8169_get_stats, + .ndo_start_xmit = rtl8169_start_xmit, + .ndo_tx_timeout = rtl8169_tx_timeout, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = rtl8169_change_mtu, + .ndo_set_mac_address = rtl_set_mac_address, + .ndo_do_ioctl = rtl8169_ioctl, + .ndo_set_multicast_list = rtl_set_rx_mode, +#ifdef CONFIG_R8169_VLAN + .ndo_vlan_rx_register = rtl8169_vlan_rx_register, +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = rtl8169_netpoll, +#endif + +}; + +static int __devinit +rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + const struct rtl_cfg_info *cfg = rtl_cfg_infos + ent->driver_data; + const unsigned int region = cfg->region; + struct rtl8169_private *tp; + struct mii_if_info *mii; + struct net_device *dev; + void __iomem *ioaddr; + unsigned int i; + int rc; + + if (netif_msg_drv(&debug)) { + printk(KERN_INFO "%s Gigabit Ethernet driver %s loaded\n", + MODULENAME, RTL8169_VERSION); + } + + dev = alloc_etherdev(sizeof (*tp)); + if (!dev) { + if (netif_msg_drv(&debug)) + dev_err(&pdev->dev, "unable to alloc new ethernet\n"); + rc = -ENOMEM; + goto out; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + dev->netdev_ops = &rtl8169_netdev_ops; + tp = netdev_priv(dev); + tp->dev = dev; + tp->pci_dev = pdev; + tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT); + + mii = &tp->mii; + mii->dev = dev; + mii->mdio_read = rtl_mdio_read; + mii->mdio_write = rtl_mdio_write; + mii->phy_id_mask = 0x1f; + mii->reg_num_mask = 0x1f; + mii->supports_gmii = !!(cfg->features & RTL_FEATURE_GMII); + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + rc = pci_enable_device(pdev); + if (rc < 0) { + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "enable failure\n"); + goto err_out_free_dev_1; + } + + rc = pci_set_mwi(pdev); + if (rc < 0) + goto err_out_disable_2; + + /* make sure PCI base addr 1 is MMIO */ + if (!(pci_resource_flags(pdev, region) & IORESOURCE_MEM)) { + if (netif_msg_probe(tp)) { + dev_err(&pdev->dev, + "region #%d not an MMIO resource, aborting\n", + region); + } + rc = -ENODEV; + goto err_out_mwi_3; + } + + /* check for weird/broken PCI region reporting */ + if (pci_resource_len(pdev, region) < R8169_REGS_SIZE) { + if (netif_msg_probe(tp)) { + dev_err(&pdev->dev, + "Invalid PCI region size(s), aborting\n"); + } + rc = -ENODEV; + goto err_out_mwi_3; + } + + rc = pci_request_regions(pdev, MODULENAME); + if (rc < 0) { + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "could not request regions.\n"); + goto err_out_mwi_3; + } + + tp->cp_cmd = PCIMulRW | RxChkSum; + + if ((sizeof(dma_addr_t) > 4) && + !pci_set_dma_mask(pdev, DMA_64BIT_MASK) && use_dac) { + tp->cp_cmd |= PCIDAC; + dev->features |= NETIF_F_HIGHDMA; + } else { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc < 0) { + if (netif_msg_probe(tp)) { + dev_err(&pdev->dev, + "DMA configuration failed.\n"); + } + goto err_out_free_res_4; + } + } + + pci_set_master(pdev); + + /* ioremap MMIO region */ + ioaddr = ioremap(pci_resource_start(pdev, region), R8169_REGS_SIZE); + if (!ioaddr) { + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + rc = -EIO; + goto err_out_free_res_4; + } + + tp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!tp->pcie_cap && netif_msg_probe(tp)) + dev_info(&pdev->dev, "no PCI Express capability\n"); + + RTL_W16(IntrMask, 0x0000); + + /* 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; + msleep_interruptible(1); + } + + RTL_W16(IntrStatus, 0xffff); + + /* Identify chip attached to board */ + rtl8169_get_mac_version(tp, ioaddr); + + rtl8169_print_mac_version(tp); + + for (i = 0; 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 */ + if (netif_msg_probe(tp)) { + dev_printk(KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming %s\n", + rtl_chip_info[0].name); + } + i = 0; + } + tp->chipset = i; + + RTL_W8(Cfg9346, Cfg9346_Unlock); + RTL_W8(Config1, RTL_R8(Config1) | PMEnable); + RTL_W8(Config5, RTL_R8(Config5) & PMEStatus); + if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0) + tp->features |= RTL_FEATURE_WOL; + if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0) + tp->features |= RTL_FEATURE_WOL; + tp->features |= rtl_try_msi(pdev, ioaddr, cfg); + RTL_W8(Cfg9346, Cfg9346_Lock); + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_06) && + (RTL_R8(PHYstatus) & TBI_Enable)) { + tp->set_speed = rtl8169_set_speed_tbi; + tp->get_settings = rtl8169_gset_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->do_ioctl = rtl_tbi_ioctl; + + tp->phy_1000_ctrl_reg = ADVERTISE_1000FULL; /* Implied by TBI */ + } else { + tp->set_speed = rtl8169_set_speed_xmii; + tp->get_settings = rtl8169_gset_xmii; + tp->phy_reset_enable = rtl8169_xmii_reset_enable; + tp->phy_reset_pending = rtl8169_xmii_reset_pending; + tp->link_ok = rtl8169_xmii_link_ok; + tp->do_ioctl = rtl_xmii_ioctl; + } + + spin_lock_init(&tp->lock); + + tp->mmio_addr = ioaddr; + + /* Get MAC address */ + for (i = 0; i < MAC_ADDR_LEN; i++) + dev->dev_addr[i] = RTL_R8(MAC0 + i); + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + + SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops); + dev->watchdog_timeo = RTL8169_TX_TIMEOUT; + dev->irq = pdev->irq; + dev->base_addr = (unsigned long) ioaddr; + + netif_napi_add(dev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT); + +#ifdef CONFIG_R8169_VLAN + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; +#endif + + tp->intr_mask = 0xffff; + tp->align = cfg->align; + tp->hw_start = cfg->hw_start; + tp->intr_event = cfg->intr_event; + tp->napi_event = cfg->napi_event; + + init_timer(&tp->timer); + tp->timer.data = (unsigned long) dev; + tp->timer.function = rtl8169_phy_timer; + + rc = register_netdev(dev); + if (rc < 0) + goto err_out_msi_5; + + pci_set_drvdata(pdev, dev); + + if (netif_msg_probe(tp)) { + u32 xid = RTL_R32(TxConfig) & 0x7cf0f8ff; + + printk(KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "XID %08x IRQ %d\n", + dev->name, + rtl_chip_info[tp->chipset].name, + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], xid, dev->irq); + } + + rtl8169_init_phy(dev, tp); + device_set_wakeup_enable(&pdev->dev, tp->features & RTL_FEATURE_WOL); + +out: + return rc; + +err_out_msi_5: + rtl_disable_msi(pdev, tp); + iounmap(ioaddr); +err_out_free_res_4: + pci_release_regions(pdev); +err_out_mwi_3: + pci_clear_mwi(pdev); +err_out_disable_2: + pci_disable_device(pdev); +err_out_free_dev_1: + free_netdev(dev); + goto out; +} + +static void __devexit rtl8169_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + + flush_scheduled_work(); + + unregister_netdev(dev); + rtl_disable_msi(pdev, tp); + rtl8169_release_board(pdev, dev, tp->mmio_addr); + pci_set_drvdata(pdev, NULL); +} + +static void rtl8169_set_rxbufsize(struct rtl8169_private *tp, + struct net_device *dev) +{ + unsigned int mtu = dev->mtu; + + tp->rx_buf_sz = (mtu > RX_BUF_SIZE) ? mtu + ETH_HLEN + 8 : RX_BUF_SIZE; +} + +static int rtl8169_open(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + int retval = -ENOMEM; + + + rtl8169_set_rxbufsize(tp, dev); + + /* + * Rx and Tx desscriptors needs 256 bytes alignment. + * pci_alloc_consistent provides more. + */ + tp->TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES, + &tp->TxPhyAddr); + if (!tp->TxDescArray) + goto out; + + tp->RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES, + &tp->RxPhyAddr); + if (!tp->RxDescArray) + goto err_free_tx_0; + + retval = rtl8169_init_ring(dev); + if (retval < 0) + goto err_free_rx_1; + + INIT_DELAYED_WORK(&tp->task, NULL); + + smp_mb(); + + retval = request_irq(dev->irq, rtl8169_interrupt, + (tp->features & RTL_FEATURE_MSI) ? 0 : IRQF_SHARED, + dev->name, dev); + if (retval < 0) + goto err_release_ring_2; + + napi_enable(&tp->napi); + + rtl_hw_start(dev); + + rtl8169_request_timer(dev); + + rtl8169_check_link_status(dev, tp, tp->mmio_addr); +out: + return retval; + +err_release_ring_2: + rtl8169_rx_clear(tp); +err_free_rx_1: + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); +err_free_tx_0: + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + goto out; +} + +static void rtl8169_hw_reset(void __iomem *ioaddr) +{ + /* 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 __iomem *ioaddr = tp->mmio_addr; + u32 cfg = rtl8169_rx_config; + + 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_hw_start(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int i; + + /* 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; + msleep_interruptible(1); + } + + tp->hw_start(dev); + + netif_start_queue(dev); +} + + +static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp, + void __iomem *ioaddr) +{ + /* + * 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, ((u64) tp->TxPhyAddr) >> 32); + RTL_W32(TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_32BIT_MASK); + RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32); + RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_32BIT_MASK); +} + +static u16 rtl_rw_cpluscmd(void __iomem *ioaddr) +{ + u16 cmd; + + cmd = RTL_R16(CPlusCmd); + RTL_W16(CPlusCmd, cmd); + return cmd; +} + +static void rtl_set_rx_max_size(void __iomem *ioaddr) +{ + /* Low hurts. Let's disable the filtering. */ + RTL_W16(RxMaxSize, 16383); +} + +static void rtl8169_set_magic_reg(void __iomem *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; + + 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_hw_start_8169(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + struct pci_dev *pdev = tp->pci_dev; + + 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)) { + dprintk("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); + + /* Enable all known interrupts by setting the interrupt mask. */ + RTL_W16(IntrMask, tp->intr_event); +} + +static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + 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 __iomem *ioaddr) +{ + u32 csi; + + 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 __iomem *ioaddr, struct ephy_info *e, int len) +{ + u16 w; + + 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_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + static struct ephy_info e_info_8168cp[] = { + { 0x01, 0, 0x0001 }, + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0042 }, + { 0x06, 0x0080, 0x0000 }, + { 0x07, 0, 0x2000 } + }; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + static struct ephy_info e_info_8168c_1[] = { + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0002 }, + { 0x06, 0x0080, 0x0000 } + }; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + static struct ephy_info e_info_8168c_2[] = { + { 0x01, 0, 0x0001 }, + { 0x03, 0x0400, 0x0220 } + }; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + rtl_hw_start_8168c_2(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_4(void __iomem *ioaddr, struct pci_dev *pdev) +{ + rtl_csi_access_enable(ioaddr); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168d(void __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr = tp->mmio_addr; + struct pci_dev *pdev = tp->pci_dev; + + 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: + printk(KERN_ERR PFX "%s: unknown chipset (mac_version = %d).\n", + dev->name, 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 __iomem *ioaddr, struct pci_dev *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; + + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr, struct pci_dev *pdev) +{ + 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 __iomem *ioaddr = tp->mmio_addr; + struct pci_dev *pdev = tp->pci_dev; + + 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); +} + +static int rtl8169_change_mtu(struct net_device *dev, int new_mtu) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret = 0; + + if (new_mtu < ETH_ZLEN || new_mtu > SafeMtu) + return -EINVAL; + + dev->mtu = new_mtu; + + if (!netif_running(dev)) + goto out; + + rtl8169_down(dev); + + rtl8169_set_rxbufsize(tp, dev); + + ret = rtl8169_init_ring(dev); + if (ret < 0) + goto out; + + napi_enable(&tp->napi); + + rtl_hw_start(dev); + + rtl8169_request_timer(dev); + +out: + return ret; +} + +static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc) +{ + desc->addr = cpu_to_le64(0x0badbadbadbadbadull); + desc->opts1 &= ~cpu_to_le32(DescOwn | RsvdMask); +} + +static void rtl8169_free_rx_skb(struct rtl8169_private *tp, + struct sk_buff **sk_buff, struct RxDesc *desc) +{ + struct pci_dev *pdev = tp->pci_dev; + + pci_unmap_single(pdev, le64_to_cpu(desc->addr), tp->rx_buf_sz, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(*sk_buff); + *sk_buff = NULL; + rtl8169_make_unusable_by_asic(desc); +} + +static inline void rtl8169_mark_to_asic(struct RxDesc *desc, u32 rx_buf_sz) +{ + u32 eor = le32_to_cpu(desc->opts1) & RingEnd; + + desc->opts1 = cpu_to_le32(DescOwn | eor | rx_buf_sz); +} + +static inline void rtl8169_map_to_asic(struct RxDesc *desc, dma_addr_t mapping, + u32 rx_buf_sz) +{ + desc->addr = cpu_to_le64(mapping); + wmb(); + rtl8169_mark_to_asic(desc, rx_buf_sz); +} + +static struct sk_buff *rtl8169_alloc_rx_skb(struct pci_dev *pdev, + struct net_device *dev, + struct RxDesc *desc, int rx_buf_sz, + unsigned int align) +{ + struct sk_buff *skb; + dma_addr_t mapping; + unsigned int pad; + + pad = align ? align : NET_IP_ALIGN; + + skb = netdev_alloc_skb(dev, rx_buf_sz + pad); + if (!skb) + goto err_out; + + skb_reserve(skb, align ? ((pad - 1) & (unsigned long)skb->data) : pad); + + mapping = pci_map_single(pdev, skb->data, rx_buf_sz, + PCI_DMA_FROMDEVICE); + + rtl8169_map_to_asic(desc, mapping, rx_buf_sz); +out: + return skb; + +err_out: + rtl8169_make_unusable_by_asic(desc); + goto out; +} + +static void rtl8169_rx_clear(struct rtl8169_private *tp) +{ + unsigned int i; + + for (i = 0; i < NUM_RX_DESC; i++) { + if (tp->Rx_skbuff[i]) { + rtl8169_free_rx_skb(tp, tp->Rx_skbuff + i, + tp->RxDescArray + i); + } + } +} + +static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev, + u32 start, u32 end) +{ + u32 cur; + + for (cur = start; end - cur != 0; cur++) { + struct sk_buff *skb; + unsigned int i = cur % NUM_RX_DESC; + + WARN_ON((s32)(end - cur) < 0); + + if (tp->Rx_skbuff[i]) + continue; + + skb = rtl8169_alloc_rx_skb(tp->pci_dev, dev, + tp->RxDescArray + i, + tp->rx_buf_sz, tp->align); + if (!skb) + break; + + tp->Rx_skbuff[i] = skb; + } + return cur - start; +} + +static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc) +{ + desc->opts1 |= cpu_to_le32(RingEnd); +} + +static void rtl8169_init_ring_indexes(struct rtl8169_private *tp) +{ + tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0; +} + +static int rtl8169_init_ring(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + rtl8169_init_ring_indexes(tp); + + memset(tp->tx_skb, 0x0, NUM_TX_DESC * sizeof(struct ring_info)); + memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *)); + + if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC) + goto err_out; + + rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1); + + return 0; + +err_out: + rtl8169_rx_clear(tp); + return -ENOMEM; +} + +static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct ring_info *tx_skb, + struct TxDesc *desc) +{ + unsigned int len = tx_skb->len; + + pci_unmap_single(pdev, le64_to_cpu(desc->addr), len, PCI_DMA_TODEVICE); + desc->opts1 = 0x00; + desc->opts2 = 0x00; + desc->addr = 0x00; + tx_skb->len = 0; +} + +static void rtl8169_tx_clear(struct rtl8169_private *tp) +{ + unsigned int i; + + for (i = tp->dirty_tx; i < tp->dirty_tx + NUM_TX_DESC; i++) { + unsigned int entry = i % NUM_TX_DESC; + struct ring_info *tx_skb = tp->tx_skb + entry; + unsigned int len = tx_skb->len; + + if (len) { + struct sk_buff *skb = tx_skb->skb; + + rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, + tp->TxDescArray + entry); + if (skb) { + dev_kfree_skb(skb); + tx_skb->skb = NULL; + } + tp->dev->stats.tx_dropped++; + } + } + tp->cur_tx = tp->dirty_tx = 0; +} + +static void rtl8169_schedule_work(struct net_device *dev, work_func_t task) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + PREPARE_DELAYED_WORK(&tp->task, task); + schedule_delayed_work(&tp->task, 4); +} + +static void rtl8169_wait_for_quiescence(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + synchronize_irq(dev->irq); + + /* Wait for any pending NAPI task to complete */ + napi_disable(&tp->napi); + + rtl8169_irq_mask_and_ack(ioaddr); + + tp->intr_mask = 0xffff; + RTL_W16(IntrMask, tp->intr_event); + napi_enable(&tp->napi); +} + +static void rtl8169_reinit_task(struct work_struct *work) +{ + struct rtl8169_private *tp = + container_of(work, struct rtl8169_private, task.work); + struct net_device *dev = tp->dev; + int ret; + + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + + rtl8169_wait_for_quiescence(dev); + rtl8169_close(dev); + + ret = rtl8169_open(dev); + if (unlikely(ret < 0)) { + if (net_ratelimit() && netif_msg_drv(tp)) { + printk(KERN_ERR PFX "%s: reinit failure (status = %d)." + " Rescheduling.\n", dev->name, ret); + } + rtl8169_schedule_work(dev, rtl8169_reinit_task); + } + +out_unlock: + rtnl_unlock(); +} + +static void rtl8169_reset_task(struct work_struct *work) +{ + struct rtl8169_private *tp = + container_of(work, struct rtl8169_private, task.work); + struct net_device *dev = tp->dev; + + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + + rtl8169_wait_for_quiescence(dev); + + rtl8169_rx_interrupt(dev, tp, tp->mmio_addr, ~(u32)0); + rtl8169_tx_clear(tp); + + if (tp->dirty_rx == tp->cur_rx) { + rtl8169_init_ring_indexes(tp); + rtl_hw_start(dev); + netif_wake_queue(dev); + rtl8169_check_link_status(dev, tp, tp->mmio_addr); + } else { + if (net_ratelimit() && netif_msg_intr(tp)) { + printk(KERN_EMERG PFX "%s: Rx buffers shortage\n", + dev->name); + } + rtl8169_schedule_work(dev, rtl8169_reset_task); + } + +out_unlock: + rtnl_unlock(); +} + +static void rtl8169_tx_timeout(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + rtl8169_hw_reset(tp->mmio_addr); + + /* Let's wait a bit while any (async) irq lands on */ + rtl8169_schedule_work(dev, rtl8169_reset_task); +} + +static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb, + u32 opts1) +{ + struct skb_shared_info *info = skb_shinfo(skb); + unsigned int cur_frag, entry; + struct TxDesc * uninitialized_var(txd); + + entry = tp->cur_tx; + for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) { + skb_frag_t *frag = info->frags + cur_frag; + dma_addr_t mapping; + u32 status, len; + void *addr; + + entry = (entry + 1) % NUM_TX_DESC; + + txd = tp->TxDescArray + entry; + len = frag->size; + addr = ((void *) page_address(frag->page)) + frag->page_offset; + mapping = pci_map_single(tp->pci_dev, addr, len, PCI_DMA_TODEVICE); + + /* anti gcc 2.95.3 bugware (sic) */ + status = opts1 | len | (RingEnd * !((entry + 1) % NUM_TX_DESC)); + + txd->opts1 = cpu_to_le32(status); + txd->addr = cpu_to_le64(mapping); + + tp->tx_skb[entry].len = len; + } + + if (cur_frag) { + tp->tx_skb[entry].skb = skb; + txd->opts1 |= cpu_to_le32(LastFrag); + } + + return cur_frag; +} + +static inline u32 rtl8169_tso_csum(struct sk_buff *skb, struct net_device *dev) +{ + if (dev->features & NETIF_F_TSO) { + u32 mss = skb_shinfo(skb)->gso_size; + + if (mss) + return LargeSend | ((mss & MSSMask) << MSSShift); + } + if (skb->ip_summed == CHECKSUM_PARTIAL) { + const struct iphdr *ip = ip_hdr(skb); + + if (ip->protocol == IPPROTO_TCP) + return IPCS | TCPCS; + else if (ip->protocol == IPPROTO_UDP) + return IPCS | UDPCS; + WARN_ON(1); /* we need a WARN() */ + } + return 0; +} + +static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned int frags, entry = tp->cur_tx % NUM_TX_DESC; + struct TxDesc *txd = tp->TxDescArray + entry; + void __iomem *ioaddr = tp->mmio_addr; + dma_addr_t mapping; + u32 status, len; + u32 opts1; + int ret = NETDEV_TX_OK; + + if (unlikely(TX_BUFFS_AVAIL(tp) < skb_shinfo(skb)->nr_frags)) { + if (netif_msg_drv(tp)) { + printk(KERN_ERR + "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + } + goto err_stop; + } + + if (unlikely(le32_to_cpu(txd->opts1) & DescOwn)) + goto err_stop; + + opts1 = DescOwn | rtl8169_tso_csum(skb, dev); + + frags = rtl8169_xmit_frags(tp, skb, opts1); + if (frags) { + len = skb_headlen(skb); + opts1 |= FirstFrag; + } else { + len = skb->len; + opts1 |= FirstFrag | LastFrag; + tp->tx_skb[entry].skb = skb; + } + + mapping = pci_map_single(tp->pci_dev, skb->data, len, PCI_DMA_TODEVICE); + + tp->tx_skb[entry].len = len; + txd->addr = cpu_to_le64(mapping); + txd->opts2 = cpu_to_le32(rtl8169_tx_vlan_tag(tp, skb)); + + wmb(); + + /* anti gcc 2.95.3 bugware (sic) */ + status = opts1 | len | (RingEnd * !((entry + 1) % NUM_TX_DESC)); + txd->opts1 = cpu_to_le32(status); + + dev->trans_start = jiffies; + + tp->cur_tx += frags + 1; + + smp_wmb(); + + RTL_W8(TxPoll, NPQ); /* set polling bit */ + + if (TX_BUFFS_AVAIL(tp) < MAX_SKB_FRAGS) { + netif_stop_queue(dev); + smp_rmb(); + if (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS) + netif_wake_queue(dev); + } + +out: + return ret; + +err_stop: + netif_stop_queue(dev); + ret = NETDEV_TX_BUSY; + dev->stats.tx_dropped++; + goto out; +} + +static void rtl8169_pcierr_interrupt(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + void __iomem *ioaddr = tp->mmio_addr; + u16 pci_status, pci_cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); + pci_read_config_word(pdev, PCI_STATUS, &pci_status); + + if (netif_msg_intr(tp)) { + printk(KERN_ERR + "%s: PCI error (cmd = 0x%04x, status = 0x%04x).\n", + dev->name, pci_cmd, pci_status); + } + + /* + * The recovery sequence below admits a very elaborated explanation: + * - it seems to work; + * - I did not see what else could be done; + * - it makes iop3xx happy. + * + * Feel free to adjust to your needs. + */ + if (pdev->broken_parity_status) + pci_cmd &= ~PCI_COMMAND_PARITY; + else + pci_cmd |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + + pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); + + pci_write_config_word(pdev, PCI_STATUS, + pci_status & (PCI_STATUS_DETECTED_PARITY | + PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_REC_MASTER_ABORT | + PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_SIG_TARGET_ABORT)); + + /* The infamous DAC f*ckup only happens at boot time */ + if ((tp->cp_cmd & PCIDAC) && !tp->dirty_rx && !tp->cur_rx) { + if (netif_msg_intr(tp)) + printk(KERN_INFO "%s: disabling PCI DAC.\n", dev->name); + tp->cp_cmd &= ~PCIDAC; + RTL_W16(CPlusCmd, tp->cp_cmd); + dev->features &= ~NETIF_F_HIGHDMA; + } + + rtl8169_hw_reset(ioaddr); + + rtl8169_schedule_work(dev, rtl8169_reinit_task); +} + +static void rtl8169_tx_interrupt(struct net_device *dev, + struct rtl8169_private *tp, + void __iomem *ioaddr) +{ + unsigned int dirty_tx, tx_left; + + dirty_tx = tp->dirty_tx; + smp_rmb(); + tx_left = tp->cur_tx - dirty_tx; + + while (tx_left > 0) { + unsigned int entry = dirty_tx % NUM_TX_DESC; + struct ring_info *tx_skb = tp->tx_skb + entry; + u32 len = tx_skb->len; + u32 status; + + rmb(); + status = le32_to_cpu(tp->TxDescArray[entry].opts1); + if (status & DescOwn) + break; + + dev->stats.tx_bytes += len; + dev->stats.tx_packets++; + + rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, tp->TxDescArray + entry); + + if (status & LastFrag) { + dev_kfree_skb_irq(tx_skb->skb); + tx_skb->skb = NULL; + } + dirty_tx++; + tx_left--; + } + + if (tp->dirty_tx != dirty_tx) { + tp->dirty_tx = dirty_tx; + smp_wmb(); + if (netif_queue_stopped(dev) && + (TX_BUFFS_AVAIL(tp) >= MAX_SKB_FRAGS)) { + netif_wake_queue(dev); + } + /* + * 8168 hack: TxPoll requests are lost when the Tx packets are + * too close. Let's kick an extra TxPoll request when a burst + * of start_xmit activity is detected (if it is not detected, + * it is slow enough). -- FR + */ + smp_rmb(); + if (tp->cur_tx != dirty_tx) + RTL_W8(TxPoll, NPQ); + } +} + +static inline int rtl8169_fragmented_frame(u32 status) +{ + return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag); +} + +static inline void rtl8169_rx_csum(struct sk_buff *skb, struct RxDesc *desc) +{ + u32 opts1 = le32_to_cpu(desc->opts1); + u32 status = opts1 & RxProtoMask; + + if (((status == RxProtoTCP) && !(opts1 & TCPFail)) || + ((status == RxProtoUDP) && !(opts1 & UDPFail)) || + ((status == RxProtoIP) && !(opts1 & IPFail))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; +} + +static inline bool rtl8169_try_rx_copy(struct sk_buff **sk_buff, + struct rtl8169_private *tp, int pkt_size, + dma_addr_t addr) +{ + struct sk_buff *skb; + bool done = false; + + if (pkt_size >= rx_copybreak) + goto out; + + skb = netdev_alloc_skb(tp->dev, pkt_size + NET_IP_ALIGN); + if (!skb) + goto out; + + pci_dma_sync_single_for_cpu(tp->pci_dev, addr, pkt_size, + PCI_DMA_FROMDEVICE); + skb_reserve(skb, NET_IP_ALIGN); + skb_copy_from_linear_data(*sk_buff, skb->data, pkt_size); + *sk_buff = skb; + done = true; +out: + return done; +} + +static int rtl8169_rx_interrupt(struct net_device *dev, + struct rtl8169_private *tp, + void __iomem *ioaddr, u32 budget) +{ + unsigned int cur_rx, rx_left; + unsigned int delta, count; + + cur_rx = tp->cur_rx; + rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; + rx_left = min(rx_left, budget); + + for (; rx_left > 0; rx_left--, cur_rx++) { + unsigned int entry = cur_rx % NUM_RX_DESC; + struct RxDesc *desc = tp->RxDescArray + entry; + u32 status; + + rmb(); + status = le32_to_cpu(desc->opts1); + + if (status & DescOwn) + break; + if (unlikely(status & RxRES)) { + if (netif_msg_rx_err(tp)) { + printk(KERN_INFO + "%s: Rx ERROR. status = %08x\n", + dev->name, status); + } + dev->stats.rx_errors++; + if (status & (RxRWT | RxRUNT)) + dev->stats.rx_length_errors++; + if (status & RxCRC) + dev->stats.rx_crc_errors++; + if (status & RxFOVF) { + rtl8169_schedule_work(dev, rtl8169_reset_task); + dev->stats.rx_fifo_errors++; + } + rtl8169_mark_to_asic(desc, tp->rx_buf_sz); + } else { + struct sk_buff *skb = tp->Rx_skbuff[entry]; + dma_addr_t addr = le64_to_cpu(desc->addr); + int pkt_size = (status & 0x00001FFF) - 4; + struct pci_dev *pdev = tp->pci_dev; + + /* + * The driver does not support incoming fragmented + * frames. They are seen as a symptom of over-mtu + * sized frames. + */ + if (unlikely(rtl8169_fragmented_frame(status))) { + dev->stats.rx_dropped++; + dev->stats.rx_length_errors++; + rtl8169_mark_to_asic(desc, tp->rx_buf_sz); + continue; + } + + rtl8169_rx_csum(skb, desc); + + if (rtl8169_try_rx_copy(&skb, tp, pkt_size, addr)) { + pci_dma_sync_single_for_device(pdev, addr, + pkt_size, PCI_DMA_FROMDEVICE); + rtl8169_mark_to_asic(desc, tp->rx_buf_sz); + } else { + pci_unmap_single(pdev, addr, tp->rx_buf_sz, + PCI_DMA_FROMDEVICE); + tp->Rx_skbuff[entry] = NULL; + } + + skb_put(skb, pkt_size); + skb->protocol = eth_type_trans(skb, dev); + + if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0) + netif_receive_skb(skb); + + dev->stats.rx_bytes += pkt_size; + dev->stats.rx_packets++; + } + + /* Work around for AMD plateform. */ + if ((desc->opts2 & cpu_to_le32(0xfffe000)) && + (tp->mac_version == RTL_GIGA_MAC_VER_05)) { + desc->opts2 = 0; + cur_rx++; + } + } + + count = cur_rx - tp->cur_rx; + tp->cur_rx = cur_rx; + + delta = rtl8169_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx); + if (!delta && count && netif_msg_intr(tp)) + printk(KERN_INFO "%s: no Rx buffer allocated\n", dev->name); + tp->dirty_rx += delta; + + /* + * FIXME: until there is periodic timer to try and refill the ring, + * a temporary shortage may definitely kill the Rx process. + * - disable the asic to try and avoid an overflow and kick it again + * after refill ? + * - how do others driver handle this condition (Uh oh...). + */ + if ((tp->dirty_rx + NUM_RX_DESC == tp->cur_rx) && netif_msg_intr(tp)) + printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev->name); + + return count; +} + +static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) +{ + struct net_device *dev = dev_instance; + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int handled = 0; + int status; + + status = RTL_R16(IntrStatus); + + /* hotplug/major error/no more work/shared irq */ + if ((status == 0xffff) || !status) + goto out; + + handled = 1; + + if (unlikely(!netif_running(dev))) { + rtl8169_asic_down(ioaddr); + goto out; + } + + status &= tp->intr_mask; + RTL_W16(IntrStatus, + (status & RxFIFOOver) ? (status | RxOverflow) : status); + + if (!(status & tp->intr_event)) + goto out; + + /* Work around for rx fifo overflow */ + if (unlikely(status & RxFIFOOver) && + (tp->mac_version == RTL_GIGA_MAC_VER_11)) { + netif_stop_queue(dev); + rtl8169_tx_timeout(dev); + goto out; + } + + if (unlikely(status & SYSErr)) { + rtl8169_pcierr_interrupt(dev); + goto out; + } + + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); + + if (status & tp->napi_event) { + RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event); + tp->intr_mask = ~tp->napi_event; + + if (likely(netif_rx_schedule_prep(&tp->napi))) + __netif_rx_schedule(&tp->napi); + else if (netif_msg_intr(tp)) { + printk(KERN_INFO "%s: interrupt %04x in poll\n", + dev->name, status); + } + } +out: + return IRQ_RETVAL(handled); +} + +static int rtl8169_poll(struct napi_struct *napi, int budget) +{ + struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi); + struct net_device *dev = tp->dev; + void __iomem *ioaddr = tp->mmio_addr; + int work_done; + + work_done = rtl8169_rx_interrupt(dev, tp, ioaddr, (u32) budget); + rtl8169_tx_interrupt(dev, tp, ioaddr); + + if (work_done < budget) { + netif_rx_complete(napi); + tp->intr_mask = 0xffff; + /* + * 20040426: the barrier is not strictly required but the + * behavior of the irq handler could be less predictable + * without it. Btw, the lack of flush for the posted pci + * write is safe - FR + */ + smp_wmb(); + RTL_W16(IntrMask, tp->intr_event); + } + + return work_done; +} + +static void rtl8169_rx_missed(struct net_device *dev, void __iomem *ioaddr) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (tp->mac_version > RTL_GIGA_MAC_VER_06) + return; + + dev->stats.rx_missed_errors += (RTL_R32(RxMissed) & 0xffffff); + RTL_W32(RxMissed, 0); +} + +static void rtl8169_down(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int intrmask; + + rtl8169_delete_timer(dev); + + netif_stop_queue(dev); + + napi_disable(&tp->napi); + +core_down: + spin_lock_irq(&tp->lock); + + rtl8169_asic_down(ioaddr); + + rtl8169_rx_missed(dev, ioaddr); + + spin_unlock_irq(&tp->lock); + + synchronize_irq(dev->irq); + + /* Give a racing hard_start_xmit a few cycles to complete. */ + synchronize_sched(); /* FIXME: should this be synchronize_irq()? */ + + /* + * And now for the 50k$ question: are IRQ disabled or not ? + * + * Two paths lead here: + * 1) dev->close + * -> netif_running() is available to sync the current code and the + * IRQ handler. See rtl8169_interrupt for details. + * 2) dev->change_mtu + * -> rtl8169_poll can not be issued again and re-enable the + * interruptions. Let's simply issue the IRQ down sequence again. + * + * No loop if hotpluged or major error (0xffff). + */ + intrmask = RTL_R16(IntrMask); + if (intrmask && (intrmask != 0xffff)) + goto core_down; + + rtl8169_tx_clear(tp); + + rtl8169_rx_clear(tp); +} + +static int rtl8169_close(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + + /* update counters before going down */ + rtl8169_update_counters(dev); + + rtl8169_down(dev); + + free_irq(dev->irq, dev); + + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + tp->TxDescArray = NULL; + tp->RxDescArray = NULL; + + return 0; +} + +static void rtl_set_rx_mode(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + u32 mc_filter[2]; /* Multicast hash filter */ + int rx_mode; + u32 tmp = 0; + + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + if (netif_msg_link(tp)) { + printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", + dev->name); + } + rx_mode = + AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct dev_mc_list *mclist; + unsigned int i; + + rx_mode = AcceptBroadcast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + rx_mode |= AcceptMulticast; + } + } + + spin_lock_irqsave(&tp->lock, flags); + + tmp = rtl8169_rx_config | rx_mode | + (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); + + if (tp->mac_version > RTL_GIGA_MAC_VER_06) { + u32 data = mc_filter[0]; + + mc_filter[0] = swab32(mc_filter[1]); + mc_filter[1] = swab32(data); + } + + RTL_W32(MAR0 + 0, mc_filter[0]); + RTL_W32(MAR0 + 4, mc_filter[1]); + + RTL_W32(RxConfig, tmp); + + spin_unlock_irqrestore(&tp->lock, flags); +} + +/** + * rtl8169_get_stats - Get rtl8169 read/write statistics + * @dev: The Ethernet Device to get statistics for + * + * Get TX/RX statistics for rtl8169 + */ +static struct net_device_stats *rtl8169_get_stats(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + if (netif_running(dev)) { + spin_lock_irqsave(&tp->lock, flags); + rtl8169_rx_missed(dev, ioaddr); + spin_unlock_irqrestore(&tp->lock, flags); + } + + return &dev->stats; +} + +#ifdef CONFIG_PM + +static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + if (!netif_running(dev)) + goto out_pci_suspend; + + netif_device_detach(dev); + netif_stop_queue(dev); + + spin_lock_irq(&tp->lock); + + rtl8169_asic_down(ioaddr); + + rtl8169_rx_missed(dev, ioaddr); + + spin_unlock_irq(&tp->lock); + +out_pci_suspend: + pci_save_state(pdev); + pci_enable_wake(pdev, pci_choose_state(pdev, state), + (tp->features & RTL_FEATURE_WOL) ? 1 : 0); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int rtl8169_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); + + if (!netif_running(dev)) + goto out; + + netif_device_attach(dev); + + rtl8169_schedule_work(dev, rtl8169_reset_task); +out: + return 0; +} + +static void rtl_shutdown(struct pci_dev *pdev) +{ + rtl8169_suspend(pdev, PMSG_SUSPEND); +} + +#endif /* CONFIG_PM */ + +static struct pci_driver rtl8169_pci_driver = { + .name = MODULENAME, + .id_table = rtl8169_pci_tbl, + .probe = rtl8169_init_one, + .remove = __devexit_p(rtl8169_remove_one), +#ifdef CONFIG_PM + .suspend = rtl8169_suspend, + .resume = rtl8169_resume, + .shutdown = rtl_shutdown, +#endif +}; + +static int __init rtl8169_init_module(void) +{ + return pci_register_driver(&rtl8169_pci_driver); +} + +static void __exit rtl8169_cleanup_module(void) +{ + pci_unregister_driver(&rtl8169_pci_driver); +} + +module_init(rtl8169_init_module); +module_exit(rtl8169_cleanup_module); diff -r 326d47aa986c -r 1baac79a40cf documentation/Makefile --- a/documentation/Makefile Thu Nov 19 14:39:10 2009 +0100 +++ b/documentation/Makefile Thu Nov 19 14:44:57 2009 +0100 @@ -13,6 +13,7 @@ COMMANDS := \ alias \ config \ + cstruct \ data \ debug \ domains \ diff -r 326d47aa986c -r 1baac79a40cf documentation/ethercat_doc.tex --- a/documentation/ethercat_doc.tex Thu Nov 19 14:39:10 2009 +0100 +++ b/documentation/ethercat_doc.tex Thu Nov 19 14:44:57 2009 +0100 @@ -18,6 +18,7 @@ \usepackage{listings} \usepackage{svn} \usepackage{SIunits} +\usepackage{amsmath} % for \text{} \usepackage{hyperref} \hypersetup{pdfpagelabels,plainpages=false} @@ -227,6 +228,20 @@ \end{itemize} +\item Distributed Clocks support (see sec.~\ref{sec:dc}). + + \begin{itemize} + + \item Configuration of the slave's DC parameters through the application + interface. + + \item Synchronization (offset and drift compensation) of the distributed + slave clocks to the reference clock. + + \item Optional synchronization of the reference clock to the master clock. + + \end{itemize} + \item CANopen over EtherCAT (CoE) \begin{itemize} @@ -268,7 +283,7 @@ \end{itemize} -\item Userspace command-line-tool ``ethercat`` (see sec.~\ref{sec:tool}) +\item Userspace command-line-tool ``ethercat'' (see sec.~\ref{sec:tool}) \begin{itemize} @@ -283,7 +298,7 @@ \item Access to slave registers. \item Slave SII (EEPROM) access. \item Controlling application-layer states. - \item Generation of slave description XML from existing slaves. + \item Generation of slave description XML and C-code from existing slaves. \end{itemize} @@ -773,10 +788,10 @@ be sufficient. For that, an important design decision was made: The application that reserved a master must have the total control, therefore it has to take responsibility for providing the appropriate locking mechanisms. -If another instance wants to access the master, it has to request the master -lock by callbacks, that have to be set by the application. Moreover the -application can deny access to the master if it considers it to be awkward at -the moment. +If another instance wants to access the master, it has to request the bus +access via callbacks, that have to be provided by the application. Moreover +the application can deny access to the master if it considers it to be awkward +at the moment. \begin{figure}[htbp] \centering @@ -788,10 +803,126 @@ Figure~\ref{fig:locks} exemplary shows, how two processes share one master: The application's cyclic task uses the master for process data exchange, while the master-internal EoE process uses it to communicate with EoE-capable -slaves. Both have to acquire the master lock before access: The application -task can access the lock natively, while the EoE process has to use the -callbacks. See the application interface documentation (chap.~\ref{chap:api}) -for how to use the locking callbacks. +slaves. Both have to access the bus from time to time, but the EoE process +does this by ``asking'' the application to do the bus access for it. In this +way, the application can use the appropriate locking mechanism to avoid +accessing the bus at the same time. See the application interface +documentation (chap.~\ref{chap:api}) for how to use these callbacks. + +%------------------------------------------------------------------------------ + +\section{Distributed Clocks} +\label{sec:dc} +\index{Distributed Clocks} + +From version 1.5, the master supports EtherCAT's ``Distributed Clocks'' +feature. It is possible to synchronize the slave clocks on the bus to the +``reference clock'' (which is the local clock of the first slave with DC +support) and to synchronize the reference clock to the ``master clock'' (which +is the local clock of the master). All other clocks on the bus (after the +reference clock) are considered as ``slave clocks'' (see fig.~\ref{fig:dc}). + +\begin{figure}[htbp] + \centering + \includegraphics[width=.8\textwidth]{images/dc} + \caption{Distributed Clocks} + \label{fig:dc} +\end{figure} + +\paragraph{Local Clocks} Any EtherCAT slave that supports DC has a local clock +register with nanosecond resolution. If the slave is powered, the clock starts +from zero, meaning that when slaves are powered on at different times, their +clocks will have different values. These ``offsets'' have to be compensated by +the distributed clocks mechanism. On the other hand, the clocks do not run +exactly with the same speed, since the used quarts units have a natural +frequency deviation. This deviation is usually very small, but over longer +periods, the error would accumulate and the difference between local clocks +would grow. This clock ``drift'' has also to be compensated by the DC +mechanism. + +\paragraph{Application Time} The common time base for the bus has to be +provided by the application. This application time $t_\text{app}$ is used + +\begin{enumerate} +\item to configure the slaves' clock offsets (see below), +\item to program the slave's start times for sync pulse generation (see +below). +\item to synchronize the reference clock to the master clock (optional). +\end{enumerate} + +\paragraph{Offset Compensation} For the offset compensation, each slave +provides a ``System Time Offset'' register $t_\text{off}$, that is added to +the internal clock value $t_\text{int}$ to get the ``System Time'' +$t_\text{sys}$: + +\begin{eqnarray} +t_\text{sys} & = & t_\text{int} + t_\text{off} \\ +\Rightarrow t_\text{int} & = & t_\text{sys} - t_\text{off} \nonumber +\end{eqnarray} + +The master reads the values of both registers to calculate a new system time +offset in a way, that the resulting system time shall match the master's +application time $t_\text{app}$: + +\begin{eqnarray} +t_\text{sys} & \stackrel{!}{=} & t_\text{app} \\ +\Rightarrow t_\text{int} + t_\text{off} & \stackrel{!}{=} & t_\text{app} \nonumber \\ +\Rightarrow t_\text{off} & = & t_\text{app} - t_\text{int} \nonumber \\ +\Rightarrow t_\text{off} & = & t_\text{app} - (t_\text{sys} - t_\text{off}) \nonumber \\ +\Rightarrow t_\text{off} & = & t_\text{app} - t_\text{sys} + t_\text{off} +\end{eqnarray} + +The small time offset error resulting from the different times of reading and +writing the registers will be compensated by the drift compensation. + +\paragraph{Drift Compensation} The drift compensation is possible due to a +special mechanism in each DC-capable slave: A write operation to the ``System +time'' register will cause the internal time control loop to compare the +written time (minus the programmed transmission delay, see below) to the +current system time. The calculated time error will be used as an input to the +time controller, that will tune the local clock speed to be a little faster or +slower\footnote{The local slave clock will be incremented either with +\unit{9}{\nano\second}, \unit{10}{\nano\second} or \unit{11}{\nano\second} +every \unit{10}{\nano\second}.}, according to the sign of the error. + +\paragraph{Transmission Delays} The Ethernet frame needs a small amount of +time to get from slave to slave. The resulting transmission delay times +accumulate on the bus and can reach microsecond magnitude and thus have to be +considered during the drift compensation. EtherCAT slaves supporting DC +provide a mechanism to measure the transmission delays: For each of the four +slave ports there is a receive time register. A write operation to the receive +time register of port 0 starts the measuring and the current system time is +latched and stored in a receive time register once the frame is received on +the corresponding port. The master can read out the relative receive times, +then calculate time delays between the slaves (using its knowledge of the bus +topology), and finally calculate the time delays from the reference clock to +each slave. These values are programmed into the slaves' transmission delay +registers. In this way, the drift compensation can reach nanosecond synchrony. + +\paragraph{Checking Synchrony} DC-capable slaves provide the 32-bit ``System +time difference'' register at address \lstinline+0x092c+, where the system +time difference of the last drift compensation is stored in nanosecond +resolution and in sign-and-magnitude coding\footnote{This allows +broadcast-reading all system time difference registers on the bus to get an +upper approximation}. To check for bus synchrony, the system time difference +registers can also be cyclically read via the command-line-tool (see +sec.~\ref{sec:regaccess}): + +\begin{lstlisting} +$ `\textbf{watch -n0 "ethercat reg\_read -p4 -tint32 0x92c"}` +\end{lstlisting} + +\paragraph{Sync Signals} Synchronous clocks are only the prerequisite for +synchronous events on the bus. Each slave with DC support provides two ``sync +signals'', that can be programmed to create events, that will for example +cause the slave application to latch its inputs on a certain time. A sync +event can either be generated once or cyclically, depending on what makes +sense for the slave application. Programming the sync signals is a matter of +setting the so-called ``AssignActivate'' word and the sync signals' cycle- and +shift times. The AssignActivate word is slave-specific and has to be taken +from the XML slave description (\lstinline+Device+ $\rightarrow$ +\lstinline+Dc+), where also typical sync signal configurations ``OpModes'' can +be found. %------------------------------------------------------------------------------ @@ -2088,6 +2219,13 @@ %------------------------------------------------------------------------------ +\subsection{Output PDO information in C Language} +\label{sec:ethercat-cstruct} + +\lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_cstruct} + +%------------------------------------------------------------------------------ + \subsection{Displaying Process Data} \lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_data} @@ -2148,6 +2286,7 @@ %------------------------------------------------------------------------------ \subsection{Register Access} +\label{sec:regaccess} \lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_reg_read} @@ -2315,10 +2454,10 @@ sec.~\ref{sec:cdev}). The function calls of the kernel API are mapped to the userspace via an -\lstinline+ioctl()+ interface. Each function has its own \lstinline+ioctl()+ -call. The kernel part of the interface calls the according API functions -directly, what results in a minimum additional delay (see -sec.~\ref{sec:usertiming}). +\lstinline+ioctl()+ interface. The userspace API functions share a set of +generic \lstinline+ioctl()+ calls. The kernel part of the interface calls the +according API functions directly, what results in a minimum additional delay +(see sec.~\ref{sec:usertiming}). For performance reasons, the actual domain process data (see sec.~\ref{sec:processdata}) are not copied between kernel and user memory on diff -r 326d47aa986c -r 1baac79a40cf documentation/images/Makefile --- a/documentation/images/Makefile Thu Nov 19 14:39:10 2009 +0100 +++ b/documentation/images/Makefile Thu Nov 19 14:44:57 2009 +0100 @@ -8,6 +8,7 @@ app-config.fig \ architecture.fig \ attach.fig \ + dc.fig \ fmmus.fig \ fsm-coedown.fig \ fsm-eoe.fig \ diff -r 326d47aa986c -r 1baac79a40cf documentation/images/dc.fig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/documentation/images/dc.fig Thu Nov 19 14:44:57 2009 +0100 @@ -0,0 +1,136 @@ +#FIG 3.2 +Portrait +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +6 1215 1035 1755 1575 +1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 1485 1305 225 225 1485 1305 1710 1305 +1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 1485 1305 162 162 1485 1305 1647 1305 +1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 1485 1305 23 23 1485 1305 1508 1305 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 1485 1080 1485 1530 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 1598 1110 1372 1500 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 1680 1193 1290 1417 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 1710 1305 1260 1305 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 1680 1418 1290 1193 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 1598 1500 1373 1110 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 1485 1305 1530 1125 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 1485 1305 1575 1260 +-6 +6 4950 1890 5490 2430 +1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 5220 2160 225 225 5220 2160 5445 2160 +1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 5220 2160 162 162 5220 2160 5382 2160 +1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 5220 2160 23 23 5220 2160 5243 2160 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 5220 1935 5220 2385 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 5333 1965 5107 2355 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 5415 2048 5025 2272 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 5445 2160 4995 2160 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 5415 2273 5025 2048 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 5333 2355 5108 1965 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 5220 2160 5265 1980 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 5220 2160 5310 2115 +-6 +6 6030 1305 7380 2475 +6 6795 1890 7335 2430 +1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 7065 2160 225 225 7065 2160 7290 2160 +1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 7065 2160 162 162 7065 2160 7227 2160 +1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 7065 2160 23 23 7065 2160 7088 2160 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 7065 1935 7065 2385 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 7178 1965 6952 2355 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 7260 2048 6870 2272 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 7290 2160 6840 2160 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 7260 2273 6870 2048 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 7178 2355 6953 1965 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 7065 2160 7110 1980 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 7065 2160 7155 2115 +-6 +2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5 + 7380 2475 6030 2475 6030 1305 7380 1305 7380 2475 +4 1 0 49 -1 16 16 0.0000 4 180 810 6705 1665 Slave 2\001 +-6 +6 7875 1305 9225 2475 +6 8640 1890 9180 2430 +1 3 0 1 0 7 50 -1 20 0.000 1 0.0000 8910 2160 225 225 8910 2160 9135 2160 +1 3 0 1 7 7 48 -1 20 0.000 1 0.0000 8910 2160 162 162 8910 2160 9072 2160 +1 3 0 1 7 0 47 -1 20 0.000 1 0.0000 8910 2160 23 23 8910 2160 8933 2160 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 8910 1935 8910 2385 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 9023 1965 8797 2355 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 9105 2048 8715 2272 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 9135 2160 8685 2160 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 9105 2273 8715 2048 +2 1 0 1 0 7 49 -1 -1 0.000 0 0 -1 0 0 2 + 9023 2355 8798 1965 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 8910 2160 8955 1980 +2 1 0 1 0 7 47 -1 -1 0.000 0 0 -1 0 0 2 + 8910 2160 9000 2115 +-6 +2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5 + 9225 2475 7875 2475 7875 1305 9225 1305 9225 2475 +4 1 0 49 -1 16 16 0.0000 4 180 810 8550 1665 Slave n\001 +-6 +2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5 + 1800 1620 450 1620 450 450 1800 450 1800 1620 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 1800 1080 2340 1935 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3690 1935 4185 1935 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1125 1935 1350 1530 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 4860 2880 5094 2391 +2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5 + 5535 2475 4185 2475 4185 1305 5535 1305 5535 2475 +2 4 0 1 0 7 51 -1 20 0.000 0 0 8 0 0 5 + 3690 2475 2340 2475 2340 1305 3690 1305 3690 2475 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5535 1935 6030 1935 +2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 + 7380 1935 7875 1935 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 8370 2880 8730 2385 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 7470 2880 7224 2391 +4 1 0 49 -1 16 16 0.0000 4 180 735 1125 810 Master\001 +4 1 0 50 -1 16 16 0.0000 4 180 1800 4860 3105 Reference Clock\001 +4 1 0 50 -1 16 16 0.0000 4 180 1410 1125 2250 Master Clock\001 +4 1 0 49 -1 16 16 0.0000 4 180 810 4860 1665 Slave 1\001 +4 1 0 49 -1 16 16 0.0000 4 180 810 3015 1665 Slave 0\001 +4 1 0 49 -1 16 10 0.0000 4 150 615 3015 2205 (No DC)\001 +4 1 0 50 -1 16 16 0.0000 4 180 1395 7920 3105 Slave Clocks\001 diff -r 326d47aa986c -r 1baac79a40cf examples/dc_rtai/dc_rtai_sample.c --- a/examples/dc_rtai/dc_rtai_sample.c Thu Nov 19 14:39:10 2009 +0100 +++ b/examples/dc_rtai/dc_rtai_sample.c Thu Nov 19 14:44:57 2009 +0100 @@ -238,24 +238,28 @@ /*****************************************************************************/ -void send_callback(ec_master_t *master) -{ +void send_callback(void *cb_data) +{ + ec_master_t *m = (ec_master_t *) cb_data; + // too close to the next real time cycle: deny access... if (get_cycles() - t_last_cycle <= t_critical) { rt_sem_wait(&master_sem); - ecrt_master_send_ext(master); + ecrt_master_send_ext(m); rt_sem_signal(&master_sem); } } /*****************************************************************************/ -void receive_callback(ec_master_t *master) -{ +void receive_callback(void *cb_data) +{ + ec_master_t *m = (ec_master_t *) cb_data; + // too close to the next real time cycle: deny access... if (get_cycles() - t_last_cycle <= t_critical) { rt_sem_wait(&master_sem); - ecrt_master_receive(master); + ecrt_master_receive(m); rt_sem_signal(&master_sem); } } @@ -281,7 +285,7 @@ goto out_return; } - ecrt_master_callbacks(master, send_callback, receive_callback); + ecrt_master_callbacks(master, send_callback, receive_callback, master); printk(KERN_INFO PFX "Registering domain...\n"); if (!(domain1 = ecrt_master_create_domain(master))) { diff -r 326d47aa986c -r 1baac79a40cf examples/mini/mini.c --- a/examples/mini/mini.c Thu Nov 19 14:39:10 2009 +0100 +++ b/examples/mini/mini.c Thu Nov 19 14:44:57 2009 +0100 @@ -354,19 +354,21 @@ /*****************************************************************************/ -void send_callback(ec_master_t *master) -{ - down(&master_sem); - ecrt_master_send_ext(master); - up(&master_sem); -} - -/*****************************************************************************/ - -void receive_callback(ec_master_t *master) -{ - down(&master_sem); - ecrt_master_receive(master); +void send_callback(void *cb_data) +{ + ec_master_t *m = (ec_master_t *) cb_data; + down(&master_sem); + ecrt_master_send_ext(m); + up(&master_sem); +} + +/*****************************************************************************/ + +void receive_callback(void *cb_data) +{ + ec_master_t *m = (ec_master_t *) cb_data; + down(&master_sem); + ecrt_master_receive(m); up(&master_sem); } @@ -392,8 +394,7 @@ } sema_init(&master_sem, 1); - - ecrt_master_callbacks(master, send_callback, receive_callback); + ecrt_master_callbacks(master, send_callback, receive_callback, master); printk(KERN_INFO PFX "Registering domain...\n"); if (!(domain1 = ecrt_master_create_domain(master))) { diff -r 326d47aa986c -r 1baac79a40cf examples/rtai/rtai_sample.c --- a/examples/rtai/rtai_sample.c Thu Nov 19 14:39:10 2009 +0100 +++ b/examples/rtai/rtai_sample.c Thu Nov 19 14:44:57 2009 +0100 @@ -243,24 +243,28 @@ /*****************************************************************************/ -void send_callback(ec_master_t *master) -{ +void send_callback(void *cb_data) +{ + ec_master_t *m = (ec_master_t *) cb_data; + // too close to the next real time cycle: deny access... if (get_cycles() - t_last_cycle <= t_critical) { rt_sem_wait(&master_sem); - ecrt_master_send_ext(master); + ecrt_master_send_ext(m); rt_sem_signal(&master_sem); } } /*****************************************************************************/ -void receive_callback(ec_master_t *master) -{ +void receive_callback(void *cb_data) +{ + ec_master_t *m = (ec_master_t *) cb_data; + // too close to the next real time cycle: deny access... if (get_cycles() - t_last_cycle <= t_critical) { rt_sem_wait(&master_sem); - ecrt_master_receive(master); + ecrt_master_receive(m); rt_sem_signal(&master_sem); } } @@ -288,7 +292,7 @@ goto out_return; } - ecrt_master_callbacks(master, send_callback, receive_callback); + ecrt_master_callbacks(master, send_callback, receive_callback, master); printk(KERN_INFO PFX "Registering domain...\n"); if (!(domain1 = ecrt_master_create_domain(master))) { diff -r 326d47aa986c -r 1baac79a40cf globals.h --- a/globals.h Thu Nov 19 14:39:10 2009 +0100 +++ b/globals.h Thu Nov 19 14:44:57 2009 +0100 @@ -57,7 +57,7 @@ /** Master version string */ -#define EC_MASTER_VERSION VERSION " r" EC_STR(SVNREV) +#define EC_MASTER_VERSION VERSION " " EC_STR(REV) /*****************************************************************************/ diff -r 326d47aa986c -r 1baac79a40cf include/ecrt.h --- a/include/ecrt.h Thu Nov 19 14:39:10 2009 +0100 +++ b/include/ecrt.h Thu Nov 19 14:44:57 2009 +0100 @@ -45,7 +45,9 @@ * ecrt_slave_config_dc() to configure a slave for cyclic operation, and * ecrt_master_application_time(), ecrt_master_sync_reference_clock() and * ecrt_master_sync_slave_clocks() for offset and drift compensation. The - * EC_TIMEVAL2NANO() macro can be used for epoch time conversion. + * EC_TIMEVAL2NANO() macro can be used for epoch time conversion, while the + * ecrt_master_sync_monitor_queue() and ecrt_master_sync_monitor_process() + * methods can be used to monitor the synchrony. * - Improved the callback mechanism. ecrt_master_callbacks() now takes two * callback functions for sending and receiving datagrams. * ecrt_master_send_ext() is used to execute the sending of non-application @@ -53,6 +55,9 @@ * - Added watchdog configuration (method ecrt_slave_config_watchdog(), * #ec_watchdog_mode_t, \a watchdog_mode parameter in ec_sync_info_t and * ecrt_slave_config_sync_manager()). + * - Added ecrt_slave_config_complete_sdo() method to download an SDO during + * configuration via CompleteAccess. + * - Added ecrt_master_deactivate() to remove the bus configuration. * - Added ecrt_open_master() and ecrt_master_reserve() separation for * userspace. * - Added bus information interface (methods ecrt_master(), @@ -63,7 +68,7 @@ * methods to let an application transfer SDOs before activating the master. * - Changed the meaning of the negative return values of * ecrt_slave_config_reg_pdo_entry() and ecrt_slave_config_sdo*(). - * - Imlemented the Vendor-specific over EtherCAT mailbox protocol. See + * - Implemented the Vendor-specific over EtherCAT mailbox protocol. See * ecrt_slave_config_create_voe_handler(). * - Renamed ec_sdo_request_state_t to #ec_request_state_t, because it is also * used by VoE handlers. @@ -129,7 +134,7 @@ /** Timeval to nanoseconds conversion. * - * This macro converts a unix epoch time to EtherCAT DC time. + * This macro converts a Unix epoch time to EtherCAT DC time. * * \see void ecrt_master_application_time() * @@ -237,7 +242,7 @@ uint8_t al_state; /**< Current state of the slave. */ uint8_t error_flag; /**< Error flag for that slave. */ uint8_t sync_count; /**< Number of sync managers. */ - uint16_t sdo_count; /**< Number of SDO's. */ + uint16_t sdo_count; /**< Number of SDOs. */ char name[EC_MAX_STRING_LENGTH]; /**< Name of the slave. */ } ec_slave_info_t; @@ -469,8 +474,8 @@ * its parameters. Asynchronous master access (like EoE processing) is only * possible if the callbacks have been set. * - * The task of the send callback (\a request_cb) is to decide, if the bus is - * currently accessible. In this case, it can call the ecrt_master_send_ext() + * The task of the send callback (\a send_cb) is to decide, if the bus is + * currently accessible and whether or not to call the ecrt_master_send_ext() * method. * * The task of the receive callback (\a receive_cb) is to decide, if a call to @@ -478,8 +483,10 @@ */ void ecrt_master_callbacks( ec_master_t *master, /**< EtherCAT master */ - void (*send_cb)(ec_master_t *), /**< Datagram sending callback. */ - void (*receive_cb)(ec_master_t *) /**< Receive callback. */ + void (*send_cb)(void *), /**< Datagram sending callback. */ + void (*receive_cb)(void *), /**< Receive callback. */ + void *cb_data /**< Arbitraty pointer passed to the callback functions. + */ ); #endif /* __KERNEL__ */ @@ -644,7 +651,7 @@ * processing. * * \retval 0 Success. - * \retval -1 Error occured. + * \retval -1 Error occurred. */ int ecrt_master_sdo_upload( ec_master_t *master, /**< EtherCAT master. */ @@ -679,6 +686,18 @@ ec_master_t *master /**< EtherCAT master. */ ); +/** Deactivates the master. + * + * Removes the bus configuration. All objects created by + * ecrt_master_create_domain(), ecrt_master_slave_config(), ecrt_domain_data() + * ecrt_slave_config_create_sdo_request() and + * ecrt_slave_config_create_voe_handler() are freed, so pointers to them + * become invalid. + */ +void ecrt_master_deactivate( + ec_master_t *master /**< EtherCAT master. */ + ); + /** Sends all datagrams in the queue. * * This method takes all datagrams, that have been queued for transmission, @@ -759,6 +778,28 @@ ec_master_t *master /**< EtherCAT master. */ ); +/** Queues the DC synchonity monitoring datagram for sending. + * + * The datagram broadcast-reads all "System time difference" registers (\a + * 0x092c) to get an upper estiomation of the DC synchony. The result can be + * checked with the ecrt_master_sync_monitor_process() method. + */ +void ecrt_master_sync_monitor_queue( + ec_master_t *master /**< EtherCAT master. */ + ); + +/** Processes the DC synchonity monitoring datagram. + * + * If the sync monitoring datagram was sent before with + * ecrt_master_sync_monitor_queue(), the result can be queried with this + * method. + * + * \return Upper estination of the maximum time difference in ns. + */ +uint32_t ecrt_master_sync_monitor_process( + ec_master_t *master /**< EtherCAT master. */ + ); + /****************************************************************************** * Slave configuration methods *****************************************************************************/ @@ -779,13 +820,17 @@ ); /** Configure a slave's watchdog times. -*/ + */ void ecrt_slave_config_watchdog( ec_slave_config_t *sc, /**< Slave configuration. */ uint16_t watchdog_divider, /**< Number of 40 ns intervals. Used as a - base unit for all slave watchdogs. */ + base unit for all slave watchdogs. If set + to zero, the value is not written, so the + default ist used. */ uint16_t watchdog_intervals /**< Number of base intervals for process - data watchdog. */ + data watchdog. If set to zero, the value + is not written, so the default is used. + */ ); /** Add a PDO to a sync manager's PDO assignment. @@ -1031,6 +1076,23 @@ uint32_t value /**< Value to set. */ ); +/** Add configuration data for a complete SDO. + * + * The SDO data are transferred via CompleteAccess. Data for the first + * subindex (0) have to be included. + * + * \see ecrt_slave_config_sdo(). + * + * \retval 0 Success. + * \retval <0 Error code. + */ +int ecrt_slave_config_complete_sdo( + ec_slave_config_t *sc, /**< Slave configuration. */ + uint16_t index, /**< Index of the SDO to configure. */ + const uint8_t *data, /**< Pointer to the data. */ + size_t size /**< Size of the \a data. */ + ); + /** Create an SDO request to exchange SDOs during realtime operation. * * The created SDO request object is freed automatically when the master is diff -r 326d47aa986c -r 1baac79a40cf lib/master.c --- a/lib/master.c Thu Nov 19 14:39:10 2009 +0100 +++ b/lib/master.c Thu Nov 19 14:44:57 2009 +0100 @@ -336,6 +336,16 @@ /*****************************************************************************/ +void ecrt_master_deactivate(ec_master_t *master) +{ + if (ioctl(master->fd, EC_IOCTL_DEACTIVATE, NULL) == -1) { + fprintf(stderr, "Failed to deactivate master: %s\n", strerror(errno)); + return; + } +} + +/*****************************************************************************/ + void ecrt_master_send(ec_master_t *master) { if (ioctl(master->fd, EC_IOCTL_SEND, NULL) == -1) { @@ -395,3 +405,28 @@ } /*****************************************************************************/ + +void ecrt_master_sync_monitor_queue(ec_master_t *master) +{ + if (ioctl(master->fd, EC_IOCTL_SYNC_MON_QUEUE, NULL) == -1) { + fprintf(stderr, "Failed to queue sync monitor datagram: %s\n", + strerror(errno)); + } +} + +/*****************************************************************************/ + +uint32_t ecrt_master_sync_monitor_process(ec_master_t *master) +{ + uint32_t time_diff; + + if (ioctl(master->fd, EC_IOCTL_SYNC_MON_PROCESS, &time_diff) == -1) { + time_diff = 0xffffffff; + fprintf(stderr, "Failed to process sync monitor datagram: %s\n", + strerror(errno)); + } + + return time_diff; +} + +/*****************************************************************************/ diff -r 326d47aa986c -r 1baac79a40cf lib/slave_config.c --- a/lib/slave_config.c Thu Nov 19 14:39:10 2009 +0100 +++ b/lib/slave_config.c Thu Nov 19 14:44:57 2009 +0100 @@ -294,6 +294,29 @@ data.subindex = subindex; data.data = sdo_data; data.size = size; + data.complete_access = 0; + + if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) { + fprintf(stderr, "Failed to configure SDO.\n"); + return -1; // FIXME + } + + return 0; +} + +/*****************************************************************************/ + +int ecrt_slave_config_complete_sdo(ec_slave_config_t *sc, uint16_t index, + const uint8_t *sdo_data, size_t size) +{ + ec_ioctl_sc_sdo_t data; + + data.config_index = sc->index; + data.index = index; + data.subindex = 0; + data.data = sdo_data; + data.size = size; + data.complete_access = 1; if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) { fprintf(stderr, "Failed to configure SDO.\n"); diff -r 326d47aa986c -r 1baac79a40cf master/Kbuild.in --- a/master/Kbuild.in Thu Nov 19 14:39:10 2009 +0100 +++ b/master/Kbuild.in Thu Nov 19 14:44:57 2009 +0100 @@ -71,12 +71,12 @@ ec_master-objs += debug.o endif -REV := $(shell if test -s $(src)/../svnrevision; then \ - cat $(src)/../svnrevision; \ +REV := $(shell if test -s $(src)/../revision; then \ + cat $(src)/../revision; \ else \ - svnversion $(src)/.. 2>/dev/null || echo "unknown"; \ + hg id -i $(src)/.. 2>/dev/null || echo "unknown"; \ fi) -CFLAGS_module.o := -DSVNREV=$(REV) +CFLAGS_module.o := -DREV=$(REV) #------------------------------------------------------------------------------ diff -r 326d47aa986c -r 1baac79a40cf master/cdev.c --- a/master/cdev.c Thu Nov 19 14:39:10 2009 +0100 +++ b/master/cdev.c Thu Nov 19 14:44:57 2009 +0100 @@ -171,8 +171,11 @@ data.slave_count = master->slave_count; data.config_count = ec_master_config_count(master); data.domain_count = ec_master_domain_count(master); +#ifdef EC_EOE data.eoe_handler_count = ec_master_eoe_handler_count(master); +#endif data.phase = (uint8_t) master->phase; + data.active = (uint8_t) master->active; data.scan_busy = master->scan_busy; up(&master->master_sem); @@ -1256,9 +1259,12 @@ data.product_code = sc->product_code; for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) { data.syncs[i].dir = sc->sync_configs[i].dir; + data.syncs[i].watchdog_mode = sc->sync_configs[i].watchdog_mode; data.syncs[i].pdo_count = ec_pdo_list_count(&sc->sync_configs[i].pdos); } + data.watchdog_divider = sc->watchdog_divider; + data.watchdog_intervals = sc->watchdog_intervals; data.sdo_count = ec_slave_config_sdo_count(sc); data.slave_position = sc->slave ? sc->slave->ring_position : -1; data.dc_assign_activate = sc->dc_assign_activate; @@ -1429,7 +1435,8 @@ data.index = req->index; data.subindex = req->subindex; data.size = req->data_size; - memcpy(&data.data, req->data, min((u32) data.size, (u32) 4)); + memcpy(&data.data, req->data, + min((u32) data.size, (u32) EC_MAX_SDO_DATA_SIZE)); up(&master->master_sem); @@ -1441,6 +1448,8 @@ /*****************************************************************************/ +#ifdef EC_EOE + /** Get EoE handler information. */ int ec_cdev_ioctl_eoe_handler( @@ -1486,6 +1495,8 @@ return 0; } +#endif + /*****************************************************************************/ /** Request the master from userspace. @@ -1622,7 +1633,7 @@ } ecrt_master_callbacks(master, ec_master_internal_send_cb, - ec_master_internal_receive_cb); + ec_master_internal_receive_cb, master); ret = ecrt_master_activate(master); if (ret < 0) @@ -1637,6 +1648,23 @@ /*****************************************************************************/ +/** Deactivates the master. + */ +int ec_cdev_ioctl_deactivate( + ec_master_t *master, /**< EtherCAT master. */ + unsigned long arg, /**< ioctl() argument. */ + ec_cdev_priv_t *priv /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!priv->requested)) + return -EPERM; + + ecrt_master_deactivate(master); + return 0; +} + +/*****************************************************************************/ + /** Send frames. */ int ec_cdev_ioctl_send( @@ -1759,6 +1787,50 @@ /*****************************************************************************/ +/** Queue the sync monitoring datagram. + */ +int ec_cdev_ioctl_sync_mon_queue( + ec_master_t *master, /**< EtherCAT master. */ + unsigned long arg, /**< ioctl() argument. */ + ec_cdev_priv_t *priv /**< Private data structure of file handle. */ + ) +{ + if (unlikely(!priv->requested)) + return -EPERM; + + down(&master->io_sem); + ecrt_master_sync_monitor_queue(master); + up(&master->io_sem); + return 0; +} + +/*****************************************************************************/ + +/** Processes the sync monitoring datagram. + */ +int ec_cdev_ioctl_sync_mon_process( + ec_master_t *master, /**< EtherCAT master. */ + unsigned long arg, /**< ioctl() argument. */ + ec_cdev_priv_t *priv /**< Private data structure of file handle. */ + ) +{ + uint32_t time_diff; + + if (unlikely(!priv->requested)) + return -EPERM; + + down(&master->io_sem); + time_diff = ecrt_master_sync_monitor_process(master); + up(&master->io_sem); + + if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff))) + return -EFAULT; + + return 0; +} + +/*****************************************************************************/ + /** Configure a sync manager. */ int ec_cdev_ioctl_sc_sync( @@ -2111,8 +2183,12 @@ up(&master->master_sem); // FIXME - ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data, - data.size); + if (data.complete_access) { + ret = ecrt_slave_config_complete_sdo(sc, data.index, sdo_data, data.size); + } else { + ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data, + data.size); + } kfree(sdo_data); return ret; } @@ -3228,8 +3304,10 @@ return ec_cdev_ioctl_config_pdo_entry(master, arg); case EC_IOCTL_CONFIG_SDO: return ec_cdev_ioctl_config_sdo(master, arg); +#ifdef EC_EOE case EC_IOCTL_EOE_HANDLER: return ec_cdev_ioctl_eoe_handler(master, arg); +#endif case EC_IOCTL_REQUEST: if (!(filp->f_mode & FMODE_WRITE)) return -EPERM; @@ -3246,6 +3324,10 @@ if (!(filp->f_mode & FMODE_WRITE)) return -EPERM; return ec_cdev_ioctl_activate(master, arg, priv); + case EC_IOCTL_DEACTIVATE: + if (!(filp->f_mode & FMODE_WRITE)) + return -EPERM; + return ec_cdev_ioctl_deactivate(master, arg, priv); case EC_IOCTL_SEND: if (!(filp->f_mode & FMODE_WRITE)) return -EPERM; @@ -3268,6 +3350,14 @@ if (!(filp->f_mode & FMODE_WRITE)) return -EPERM; return ec_cdev_ioctl_sync_slaves(master, arg, priv); + case EC_IOCTL_SYNC_MON_QUEUE: + if (!(filp->f_mode & FMODE_WRITE)) + return -EPERM; + return ec_cdev_ioctl_sync_mon_queue(master, arg, priv); + case EC_IOCTL_SYNC_MON_PROCESS: + if (!(filp->f_mode & FMODE_WRITE)) + return -EPERM; + return ec_cdev_ioctl_sync_mon_process(master, arg, priv); case EC_IOCTL_SC_SYNC: if (!(filp->f_mode & FMODE_WRITE)) return -EPERM; diff -r 326d47aa986c -r 1baac79a40cf master/device.c --- a/master/device.c Thu Nov 19 14:39:10 2009 +0100 +++ b/master/device.c Thu Nov 19 14:44:57 2009 +0100 @@ -506,6 +506,12 @@ { const void *ec_data = data + ETH_HLEN; size_t ec_size = size - ETH_HLEN; + + if (unlikely(!data)) { + EC_WARN("%s() called with NULL data.\n", __func__); + return; + } + device->rx_count++; if (unlikely(device->master->debug_level > 1)) { diff -r 326d47aa986c -r 1baac79a40cf master/fsm_coe.c --- a/master/fsm_coe.c Thu Nov 19 14:39:10 2009 +0100 +++ b/master/fsm_coe.c Thu Nov 19 14:44:57 2009 +0100 @@ -1074,8 +1074,14 @@ uint8_t size; if (fsm->slave->master->debug_level) { - EC_DBG("Downloading SDO 0x%04X:%02X to slave %u.\n", - request->index, request->subindex, slave->ring_position); + char subidxstr[10]; + if (request->complete_access) { + subidxstr[0] = 0x00; + } else { + sprintf(subidxstr, ":%02X", request->subindex); + } + EC_DBG("Downloading SDO 0x%04X%s to slave %u.\n", + request->index, subidxstr, slave->ring_position); ec_print_data(request->data, request->data_size); } @@ -1097,9 +1103,11 @@ EC_WRITE_U16(data, 0x2 << 12); // SDO request EC_WRITE_U8 (data + 2, (0x3 // size specified, expedited | size << 2 + | ((request->complete_access ? 1 : 0) << 4) | 0x1 << 5)); // Download request EC_WRITE_U16(data + 3, request->index); - EC_WRITE_U8 (data + 5, request->subindex); + EC_WRITE_U8 (data + 5, + request->complete_access ? 0x00 : request->subindex); memcpy(data + 6, request->data, request->data_size); memset(data + 6 + request->data_size, 0x00, 4 - request->data_size); @@ -1110,7 +1118,7 @@ } else { // request->data_size > 4, use normal transfer type if (slave->configured_rx_mailbox_size < 6 + 10 + request->data_size) { - EC_ERR("SDO fragmenting not supported yet!\n"); + EC_ERR("SDO fragmenting not supported yet!\n"); // FIXME fsm->state = ec_fsm_coe_error; return; } @@ -1124,9 +1132,11 @@ EC_WRITE_U16(data, 0x2 << 12); // SDO request EC_WRITE_U8 (data + 2, (0x1 // size indicator, normal + | ((request->complete_access ? 1 : 0) << 4) | 0x1 << 5)); // Download request EC_WRITE_U16(data + 3, request->index); - EC_WRITE_U8 (data + 5, request->subindex); + EC_WRITE_U8 (data + 5, + request->complete_access ? 0x00 : request->subindex); EC_WRITE_U32(data + 6, request->data_size); memcpy(data + 10, request->data, request->data_size); @@ -1311,12 +1321,18 @@ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request - fsm->state = ec_fsm_coe_error; - EC_ERR("SDO download 0x%04X:%02X (%u bytes) aborted on slave %u.\n", - request->index, request->subindex, request->data_size, - slave->ring_position); + char subidxstr[10]; + fsm->state = ec_fsm_coe_error; + if (request->complete_access) { + subidxstr[0] = 0x00; + } else { + sprintf(subidxstr, ":%02X", request->subindex); + } + EC_ERR("SDO download 0x%04X%s (%u bytes) aborted on slave %u.\n", + request->index, subidxstr, request->data_size, + slave->ring_position); if (rec_size < 10) { - EC_ERR("Incomplete Abort command:\n"); + EC_ERR("Incomplete abort command:\n"); ec_print_data(data, rec_size); } else { fsm->request->abort_code = EC_READ_U32(data + 6); @@ -1805,7 +1821,6 @@ uint8_t *data, mbox_prot; size_t rec_size, data_size; ec_sdo_request_t *request = fsm->request; - uint32_t seg_size; unsigned int last_segment; if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) @@ -1883,14 +1898,11 @@ return; } - last_segment = EC_READ_U8(data + 2) & 0x01; - seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1; - if (rec_size > 10) { - data_size = rec_size - 3; /* Header of segment upload is smaller than - normal upload */ - } else { // == 10 - /* seg_size contains the number of trailing bytes to ignore. */ - data_size = rec_size - seg_size; + data_size = rec_size - 3; /* Header of segment upload is smaller than + normal upload */ + if (rec_size == 10) { + uint8_t seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1; + data_size -= seg_size; } if (request->data_size + data_size > fsm->complete_size) { @@ -1904,6 +1916,7 @@ memcpy(request->data + request->data_size, data + 3, data_size); request->data_size += data_size; + last_segment = EC_READ_U8(data + 2) & 0x01; if (!last_segment) { fsm->toggle = !fsm->toggle; ec_fsm_coe_up_prepare_segment_request(fsm); diff -r 326d47aa986c -r 1baac79a40cf master/fsm_slave_config.c --- a/master/fsm_slave_config.c Thu Nov 19 14:39:10 2009 +0100 +++ b/master/fsm_slave_config.c Thu Nov 19 14:44:57 2009 +0100 @@ -933,9 +933,9 @@ fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_slave_config_state_watchdog; - } - - ec_fsm_slave_config_enter_pdo_sync(fsm); + } else { + ec_fsm_slave_config_enter_pdo_sync(fsm); + } } /*****************************************************************************/ diff -r 326d47aa986c -r 1baac79a40cf master/ioctl.h --- a/master/ioctl.h Thu Nov 19 14:39:10 2009 +0100 +++ b/master/ioctl.h Thu Nov 19 14:44:57 2009 +0100 @@ -77,47 +77,52 @@ #define EC_IOCTL_CONFIG_PDO EC_IOWR(0x15, ec_ioctl_config_pdo_t) #define EC_IOCTL_CONFIG_PDO_ENTRY EC_IOWR(0x16, ec_ioctl_config_pdo_entry_t) #define EC_IOCTL_CONFIG_SDO EC_IOWR(0x17, ec_ioctl_config_sdo_t) +#ifdef EC_EOE #define EC_IOCTL_EOE_HANDLER EC_IOWR(0x18, ec_ioctl_eoe_handler_t) +#endif // Application interface #define EC_IOCTL_REQUEST EC_IO(0x19) #define EC_IOCTL_CREATE_DOMAIN EC_IO(0x1a) #define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x1b, ec_ioctl_config_t) #define EC_IOCTL_ACTIVATE EC_IOR(0x1c, size_t) -#define EC_IOCTL_SEND EC_IO(0x1d) -#define EC_IOCTL_RECEIVE EC_IO(0x1e) -#define EC_IOCTL_MASTER_STATE EC_IOR(0x1f, ec_master_state_t) -#define EC_IOCTL_APP_TIME EC_IOW(0x20, ec_ioctl_app_time_t) -#define EC_IOCTL_SYNC_REF EC_IO(0x21) -#define EC_IOCTL_SYNC_SLAVES EC_IO(0x22) -#define EC_IOCTL_SC_SYNC EC_IOW(0x23, ec_ioctl_config_t) -#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x24, ec_ioctl_config_t) -#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x25, ec_ioctl_config_pdo_t) -#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x26, ec_ioctl_config_pdo_t) -#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x27, ec_ioctl_add_pdo_entry_t) -#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x28, ec_ioctl_config_pdo_t) -#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x29, ec_ioctl_reg_pdo_entry_t) -#define EC_IOCTL_SC_DC EC_IOW(0x2a, ec_ioctl_config_t) -#define EC_IOCTL_SC_SDO EC_IOW(0x2b, ec_ioctl_sc_sdo_t) -#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x2c, ec_ioctl_sdo_request_t) -#define EC_IOCTL_SC_VOE EC_IOWR(0x2d, ec_ioctl_voe_t) -#define EC_IOCTL_SC_STATE EC_IOWR(0x2e, ec_ioctl_sc_state_t) -#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x2f) -#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x20) -#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x31) -#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x32, ec_ioctl_domain_state_t) -#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x33, ec_ioctl_sdo_request_t) -#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x34, ec_ioctl_sdo_request_t) -#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x35, ec_ioctl_sdo_request_t) -#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x36, ec_ioctl_sdo_request_t) -#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x37, ec_ioctl_sdo_request_t) -#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x38, ec_ioctl_voe_t) -#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x39, ec_ioctl_voe_t) -#define EC_IOCTL_VOE_READ EC_IOW(0x3a, ec_ioctl_voe_t) -#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x3b, ec_ioctl_voe_t) -#define EC_IOCTL_VOE_WRITE EC_IOWR(0x3c, ec_ioctl_voe_t) -#define EC_IOCTL_VOE_EXEC EC_IOWR(0x3d, ec_ioctl_voe_t) -#define EC_IOCTL_VOE_DATA EC_IOWR(0x3e, ec_ioctl_voe_t) +#define EC_IOCTL_DEACTIVATE EC_IO(0x1d) +#define EC_IOCTL_SEND EC_IO(0x1e) +#define EC_IOCTL_RECEIVE EC_IO(0x1f) +#define EC_IOCTL_MASTER_STATE EC_IOR(0x20, ec_master_state_t) +#define EC_IOCTL_APP_TIME EC_IOW(0x21, ec_ioctl_app_time_t) +#define EC_IOCTL_SYNC_REF EC_IO(0x22) +#define EC_IOCTL_SYNC_SLAVES EC_IO(0x23) +#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x24) +#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x25, uint32_t) +#define EC_IOCTL_SC_SYNC EC_IOW(0x26, ec_ioctl_config_t) +#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x27, ec_ioctl_config_t) +#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x28, ec_ioctl_config_pdo_t) +#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x29, ec_ioctl_config_pdo_t) +#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x2a, ec_ioctl_add_pdo_entry_t) +#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x2b, ec_ioctl_config_pdo_t) +#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x2c, ec_ioctl_reg_pdo_entry_t) +#define EC_IOCTL_SC_DC EC_IOW(0x2d, ec_ioctl_config_t) +#define EC_IOCTL_SC_SDO EC_IOW(0x2e, ec_ioctl_sc_sdo_t) +#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x2f, ec_ioctl_sdo_request_t) +#define EC_IOCTL_SC_VOE EC_IOWR(0x20, ec_ioctl_voe_t) +#define EC_IOCTL_SC_STATE EC_IOWR(0x31, ec_ioctl_sc_state_t) +#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x32) +#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x33) +#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x34) +#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x35, ec_ioctl_domain_state_t) +#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x36, ec_ioctl_sdo_request_t) +#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x37, ec_ioctl_sdo_request_t) +#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x38, ec_ioctl_sdo_request_t) +#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x39, ec_ioctl_sdo_request_t) +#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x3a, ec_ioctl_sdo_request_t) +#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x3b, ec_ioctl_voe_t) +#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x3c, ec_ioctl_voe_t) +#define EC_IOCTL_VOE_READ EC_IOW(0x3d, ec_ioctl_voe_t) +#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x3e, ec_ioctl_voe_t) +#define EC_IOCTL_VOE_WRITE EC_IOWR(0x3f, ec_ioctl_voe_t) +#define EC_IOCTL_VOE_EXEC EC_IOWR(0x40, ec_ioctl_voe_t) +#define EC_IOCTL_VOE_DATA EC_IOWR(0x41, ec_ioctl_voe_t) /*****************************************************************************/ @@ -129,8 +134,11 @@ uint32_t slave_count; uint32_t config_count; uint32_t domain_count; +#ifdef EC_EOE uint32_t eoe_handler_count; +#endif uint8_t phase; + uint8_t active; uint8_t scan_busy; struct { uint8_t address[6]; @@ -434,6 +442,11 @@ /*****************************************************************************/ +/** Maximum size for displayed SDO data. + * \todo Make this dynamic. + */ +#define EC_MAX_SDO_DATA_SIZE 1024 + typedef struct { // inputs uint32_t config_index; @@ -443,11 +456,13 @@ uint16_t index; uint8_t subindex; uint32_t size; - uint8_t data[4]; + uint8_t data[EC_MAX_SDO_DATA_SIZE]; } ec_ioctl_config_sdo_t; /*****************************************************************************/ +#ifdef EC_EOE + typedef struct { // input uint16_t eoe_index; @@ -464,6 +479,8 @@ uint32_t tx_queue_size; } ec_ioctl_eoe_handler_t; +#endif + /*****************************************************************************/ typedef struct { @@ -497,6 +514,7 @@ uint8_t subindex; const uint8_t *data; size_t size; + uint8_t complete_access; } ec_ioctl_sc_sdo_t; /*****************************************************************************/ diff -r 326d47aa986c -r 1baac79a40cf master/master.c --- a/master/master.c Thu Nov 19 14:39:10 2009 +0100 +++ b/master/master.c Thu Nov 19 14:44:57 2009 +0100 @@ -122,6 +122,7 @@ sema_init(&master->device_sem, 1); master->phase = EC_ORPHANED; + master->active = 0; master->injection_seq_fsm = 0; master->injection_seq_rt = 0; @@ -169,8 +170,10 @@ sema_init(&master->io_sem, 1); master->send_cb = NULL; master->receive_cb = NULL; + master->cb_data = NULL; master->app_send_cb = NULL; master->app_receive_cb = NULL; + master->app_cb_data = NULL; INIT_LIST_HEAD(&master->sii_requests); init_waitqueue_head(&master->sii_queue); @@ -225,12 +228,23 @@ EC_ERR("Failed to allocate synchronisation datagram.\n"); goto out_clear_ref_sync; } + + // init sync monitor datagram + ec_datagram_init(&master->sync_mon_datagram); + snprintf(master->sync_mon_datagram.name, EC_DATAGRAM_NAME_SIZE, "syncmon"); + ret = ec_datagram_brd(&master->sync_mon_datagram, 0x092c, 4); + if (ret < 0) { + ec_datagram_clear(&master->sync_mon_datagram); + EC_ERR("Failed to allocate sync monitoring datagram.\n"); + goto out_clear_sync; + } + ec_master_find_dc_ref_clock(master); // init character device ret = ec_cdev_init(&master->cdev, master, device_number); if (ret) - goto out_clear_sync; + goto out_clear_sync_mon; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) master->class_device = device_create(class, NULL, @@ -259,6 +273,8 @@ out_clear_cdev: ec_cdev_clear(&master->cdev); +out_clear_sync_mon: + ec_datagram_clear(&master->sync_mon_datagram); out_clear_sync: ec_datagram_clear(&master->sync_datagram); out_clear_ref_sync: @@ -297,6 +313,7 @@ ec_master_clear_slave_configs(master); ec_master_clear_slaves(master); + ec_datagram_clear(&master->sync_mon_datagram); ec_datagram_clear(&master->sync_datagram); ec_datagram_clear(&master->ref_sync_datagram); ec_fsm_master_clear(&master->fsm); @@ -349,22 +366,25 @@ master->dc_ref_clock = NULL; - // external requests are obsolete, so we wake pending waiters and remove them from the list - // SII + // external requests are obsolete, so we wake pending waiters and remove + // them from the list + // + // SII requests while (1) { ec_sii_write_request_t *request; if (list_empty(&master->sii_requests)) break; // get first request - request = list_entry(master->sii_requests.next, ec_sii_write_request_t, - list); + request = list_entry(master->sii_requests.next, + ec_sii_write_request_t, list); list_del_init(&request->list); // dequeue EC_INFO("Discarding SII request, slave %u does not exist anymore.\n", request->slave->ring_position); request->state = EC_INT_REQUEST_FAILURE; wake_up(&master->sii_queue); } - // Reg + + // Register requests while (1) { ec_reg_request_t *request; if (list_empty(&master->reg_requests)) @@ -378,7 +398,8 @@ request->state = EC_INT_REQUEST_FAILURE; wake_up(&master->reg_queue); } - // SDO + + // SDO requests while (1) { ec_master_sdo_request_t *request; if (list_empty(&master->slave_sdo_requests)) @@ -392,7 +413,8 @@ request->req.state = EC_INT_REQUEST_FAILURE; wake_up(&master->sdo_queue); } - // FOE + + // FoE requests while (1) { ec_master_foe_request_t *request; if (list_empty(&master->foe_requests)) @@ -441,9 +463,10 @@ /** Internal sending callback. */ void ec_master_internal_send_cb( - ec_master_t *master /**< EtherCAT master. */ - ) -{ + void *cb_data /**< Callback data. */ + ) +{ + ec_master_t *master = (ec_master_t *) cb_data; down(&master->io_sem); ecrt_master_send_ext(master); up(&master->io_sem); @@ -454,9 +477,10 @@ /** Internal receiving callback. */ void ec_master_internal_receive_cb( - ec_master_t *master /**< EtherCAT master. */ - ) -{ + void *cb_data /**< Callback data. */ + ) +{ + ec_master_t *master = (ec_master_t *) cb_data; down(&master->io_sem); ecrt_master_receive(master); up(&master->io_sem); @@ -532,6 +556,7 @@ master->send_cb = ec_master_internal_send_cb; master->receive_cb = ec_master_internal_receive_cb; + master->cb_data = master; master->phase = EC_IDLE; ret = ec_master_thread_start(master, ec_master_idle_thread, @@ -633,6 +658,7 @@ master->phase = EC_OPERATION; master->app_send_cb = NULL; master->app_receive_cb = NULL; + master->app_cb_data = NULL; return ret; out_allow: @@ -645,66 +671,17 @@ /** Transition function from OPERATION to IDLE phase. */ -void ec_master_leave_operation_phase(ec_master_t *master - /**< EtherCAT master */) -{ - ec_slave_t *slave; -#ifdef EC_EOE - ec_eoe_t *eoe; -#endif +void ec_master_leave_operation_phase( + ec_master_t *master /**< EtherCAT master */ + ) +{ + if (master->active) + ecrt_master_deactivate(master); if (master->debug_level) EC_DBG("OPERATION -> IDLE.\n"); master->phase = EC_IDLE; - -#ifdef EC_EOE - ec_master_eoe_stop(master); -#endif - ec_master_thread_stop(master); - - master->send_cb = ec_master_internal_send_cb; - master->receive_cb = ec_master_internal_receive_cb; - - down(&master->master_sem); - ec_master_clear_domains(master); - ec_master_clear_slave_configs(master); - up(&master->master_sem); - - for (slave = master->slaves; - slave < master->slaves + master->slave_count; - slave++) { - - // set states for all slaves - ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); - - // mark for reconfiguration, because the master could have no - // possibility for a reconfiguration between two sequential operation - // phases. - slave->force_config = 1; - } - -#ifdef EC_EOE - // ... but leave EoE slaves in OP - list_for_each_entry(eoe, &master->eoe_handlers, list) { - if (ec_eoe_is_open(eoe)) - ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP); - } -#endif - - master->app_time = 0ULL; - master->app_start_time = 0ULL; - master->has_start_time = 0; - - if (ec_master_thread_start(master, ec_master_idle_thread, - "EtherCAT-IDLE")) - EC_WARN("Failed to restart master thread!\n"); -#ifdef EC_EOE - ec_master_eoe_start(master); -#endif - - master->allow_scan = 1; - master->allow_config = 1; } /*****************************************************************************/ @@ -1189,7 +1166,7 @@ goto schedule; // receive datagrams - master->receive_cb(master); + master->receive_cb(master->cb_data); // actual EoE processing sth_to_send = 0; @@ -1209,7 +1186,7 @@ } // (try to) send datagrams down(&master->ext_queue_sem); - master->send_cb(master); + master->send_cb(master->cb_data); up(&master->ext_queue_sem); } @@ -1438,6 +1415,8 @@ /*****************************************************************************/ +#ifdef EC_EOE + /** Get the number of EoE handlers. * * \return Number of EoE handlers. @@ -1480,6 +1459,8 @@ return NULL; } +#endif + /*****************************************************************************/ /** Set the debug level. @@ -1683,6 +1664,11 @@ if (master->debug_level) EC_DBG("ecrt_master_activate(master = 0x%x)\n", (u32) master); + if (master->active) { + EC_WARN("%s: Master already active!\n", __func__); + return 0; + } + down(&master->master_sem); // finish all domains @@ -1718,6 +1704,7 @@ master->send_cb = master->app_send_cb; master->receive_cb = master->app_receive_cb; + master->cb_data = master->app_cb_data; ret = ec_master_thread_start(master, ec_master_operation_thread, "EtherCAT-OP"); @@ -1731,11 +1718,80 @@ master->allow_config = 1; // request the current configuration master->allow_scan = 1; // allow re-scanning on topology change + master->active = 1; return 0; } /*****************************************************************************/ +void ecrt_master_deactivate(ec_master_t *master) +{ + ec_slave_t *slave; +#ifdef EC_EOE + ec_eoe_t *eoe; +#endif + + if (master->debug_level) + EC_DBG("ecrt_master_deactivate(master = 0x%x)\n", (u32) master); + + if (!master->active) { + EC_WARN("%s: Master not active.\n", __func__); + return; + } + +#ifdef EC_EOE + ec_master_eoe_stop(master); +#endif + ec_master_thread_stop(master); + + master->send_cb = ec_master_internal_send_cb; + master->receive_cb = ec_master_internal_receive_cb; + master->cb_data = master; + + down(&master->master_sem); + ec_master_clear_domains(master); + ec_master_clear_slave_configs(master); + up(&master->master_sem); + + for (slave = master->slaves; + slave < master->slaves + master->slave_count; + slave++) { + + // set states for all slaves + ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); + + // mark for reconfiguration, because the master could have no + // possibility for a reconfiguration between two sequential operation + // phases. + slave->force_config = 1; + } + +#ifdef EC_EOE + // ... but leave EoE slaves in OP + list_for_each_entry(eoe, &master->eoe_handlers, list) { + if (ec_eoe_is_open(eoe)) + ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP); + } +#endif + + master->app_time = 0ULL; + master->app_start_time = 0ULL; + master->has_start_time = 0; + + if (ec_master_thread_start(master, ec_master_idle_thread, + "EtherCAT-IDLE")) + EC_WARN("Failed to restart master thread!\n"); +#ifdef EC_EOE + ec_master_eoe_start(master); +#endif + + master->allow_scan = 1; + master->allow_config = 1; + master->active = 0; +} + +/*****************************************************************************/ + void ecrt_master_send(ec_master_t *master) { ec_datagram_t *datagram, *n; @@ -1895,15 +1951,16 @@ /*****************************************************************************/ void ecrt_master_callbacks(ec_master_t *master, - void (*send_cb)(ec_master_t *), void (*receive_cb)(ec_master_t *)) + void (*send_cb)(void *), void (*receive_cb)(void *), void *cb_data) { if (master->debug_level) EC_DBG("ecrt_master_callbacks(master = 0x%x, send_cb = 0x%x, " - " receive_cb = 0x%x)\n", (u32) master, (u32) send_cb, - (u32) receive_cb); + " receive_cb = 0x%x, cb_data = 0x%x)\n", (u32) master, + (u32) send_cb, (u32) receive_cb, (u32) cb_data); master->app_send_cb = send_cb; master->app_receive_cb = receive_cb; + master->app_cb_data = cb_data; } /*****************************************************************************/ @@ -1945,10 +2002,30 @@ /*****************************************************************************/ +void ecrt_master_sync_monitor_queue(ec_master_t *master) +{ + ec_datagram_zero(&master->sync_mon_datagram); + ec_master_queue_datagram(master, &master->sync_mon_datagram); +} + +/*****************************************************************************/ + +uint32_t ecrt_master_sync_monitor_process(ec_master_t *master) +{ + if (master->sync_mon_datagram.state == EC_DATAGRAM_RECEIVED) { + return EC_READ_U32(master->sync_mon_datagram.data) & 0x7fffffff; + } else { + return 0xffffffff; + } +} + +/*****************************************************************************/ + /** \cond */ EXPORT_SYMBOL(ecrt_master_create_domain); EXPORT_SYMBOL(ecrt_master_activate); +EXPORT_SYMBOL(ecrt_master_deactivate); EXPORT_SYMBOL(ecrt_master_send); EXPORT_SYMBOL(ecrt_master_send_ext); EXPORT_SYMBOL(ecrt_master_receive); @@ -1958,6 +2035,8 @@ EXPORT_SYMBOL(ecrt_master_application_time); EXPORT_SYMBOL(ecrt_master_sync_reference_clock); EXPORT_SYMBOL(ecrt_master_sync_slave_clocks); +EXPORT_SYMBOL(ecrt_master_sync_monitor_queue); +EXPORT_SYMBOL(ecrt_master_sync_monitor_process); /** \endcond */ diff -r 326d47aa986c -r 1baac79a40cf master/master.h --- a/master/master.h Thu Nov 19 14:39:10 2009 +0100 +++ b/master/master.h Thu Nov 19 14:44:57 2009 +0100 @@ -108,6 +108,7 @@ ec_fsm_master_t fsm; /**< Master state machine. */ ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */ ec_master_phase_t phase; /**< Master phase. */ + unsigned int active; /**< Master has been activated. */ unsigned int injection_seq_fsm; /**< Datagram injection sequence number for the FSM side. */ unsigned int injection_seq_rt; /**< Datagram injection sequence number @@ -125,6 +126,8 @@ reference clock to the master clock. */ ec_datagram_t sync_datagram; /**< Datagram used for DC drift compensation. */ + ec_datagram_t sync_mon_datagram; /**< Datagram used for DC synchronisation + monitoring. */ ec_slave_t *dc_ref_clock; /**< DC reference clock slave. */ unsigned int scan_busy; /**< Current scan state. */ @@ -166,12 +169,14 @@ struct semaphore io_sem; /**< Semaphore used in \a IDLE phase. */ - void (*send_cb)(ec_master_t *); /**< Current send datagrams callback. */ - void (*receive_cb)(ec_master_t *); /**< Current receive datagrams callback. */ - void (*app_send_cb)(ec_master_t *); /**< Application's send datagrams + void (*send_cb)(void *); /**< Current send datagrams callback. */ + void (*receive_cb)(void *); /**< Current receive datagrams callback. */ + void *cb_data; /**< Current callback data. */ + void (*app_send_cb)(void *); /**< Application's send datagrams callback. */ - void (*app_receive_cb)(ec_master_t *); /**< Application's receive datagrams + void (*app_receive_cb)(void *); /**< Application's receive datagrams callback. */ + void *app_cb_data; /**< Application callback data. */ struct list_head sii_requests; /**< SII write requests. */ wait_queue_head_t sii_queue; /**< Wait queue for SII @@ -236,8 +241,10 @@ ec_domain_t *ec_master_find_domain(ec_master_t *, unsigned int); const ec_domain_t *ec_master_find_domain_const(const ec_master_t *, unsigned int); +#ifdef EC_EOE uint16_t ec_master_eoe_handler_count(const ec_master_t *); const ec_eoe_t *ec_master_get_eoe_handler_const(const ec_master_t *, uint16_t); +#endif int ec_master_debug_level(ec_master_t *, unsigned int); @@ -247,9 +254,9 @@ void ec_master_calc_dc(ec_master_t *); -void ec_master_internal_send_cb(ec_master_t *); -void ec_master_internal_receive_cb(ec_master_t *); - -/*****************************************************************************/ - -#endif +void ec_master_internal_send_cb(void *); +void ec_master_internal_receive_cb(void *); + +/*****************************************************************************/ + +#endif diff -r 326d47aa986c -r 1baac79a40cf master/sdo_request.c --- a/master/sdo_request.c Thu Nov 19 14:39:10 2009 +0100 +++ b/master/sdo_request.c Thu Nov 19 14:44:57 2009 +0100 @@ -56,6 +56,7 @@ ec_sdo_request_t *req /**< SDO request. */ ) { + req->complete_access = 0; req->data = NULL; req->mem_size = 0; req->data_size = 0; @@ -88,6 +89,7 @@ const ec_sdo_request_t *other /**< Other SDO request to copy from. */ ) { + req->complete_access = other->complete_access; req->index = other->index; req->subindex = other->subindex; return ec_sdo_request_copy_data(req, other->data, other->data_size); diff -r 326d47aa986c -r 1baac79a40cf master/sdo_request.h --- a/master/sdo_request.h Thu Nov 19 14:39:10 2009 +0100 +++ b/master/sdo_request.h Thu Nov 19 14:44:57 2009 +0100 @@ -52,6 +52,7 @@ uint8_t *data; /**< Pointer to SDO data. */ size_t mem_size; /**< Size of SDO data memory. */ size_t data_size; /**< Size of SDO data. */ + uint8_t complete_access; /**< SDO shall be transferred completely. */ uint32_t issue_timeout; /**< Maximum time in ms, the processing of the request may take. */ uint32_t response_timeout; /**< Maximum time in ms, the transfer is diff -r 326d47aa986c -r 1baac79a40cf master/slave_config.c --- a/master/slave_config.c Thu Nov 19 14:39:10 2009 +0100 +++ b/master/slave_config.c Thu Nov 19 14:44:57 2009 +0100 @@ -794,6 +794,48 @@ /*****************************************************************************/ +int ecrt_slave_config_complete_sdo(ec_slave_config_t *sc, uint16_t index, + const uint8_t *data, size_t size) +{ + ec_slave_t *slave = sc->slave; + ec_sdo_request_t *req; + int ret; + + if (sc->master->debug_level) + EC_DBG("ecrt_slave_config_complete_sdo(sc = 0x%x, index = 0x%04X, " + "data = 0x%x, size = %u)\n", (u32) sc, + index, (u32) data, size); + + if (slave && !(slave->sii.mailbox_protocols & EC_MBOX_COE)) { + EC_ERR("Slave %u does not support CoE!\n", slave->ring_position); + return -EPROTONOSUPPORT; // protocol not supported + } + + if (!(req = (ec_sdo_request_t *) + kmalloc(sizeof(ec_sdo_request_t), GFP_KERNEL))) { + EC_ERR("Failed to allocate memory for SDO configuration!\n"); + return -ENOMEM; + } + + ec_sdo_request_init(req); + ec_sdo_request_address(req, index, 0); + req->complete_access = 1; + + ret = ec_sdo_request_copy_data(req, data, size); + if (ret < 0) { + ec_sdo_request_clear(req); + kfree(req); + return ret; + } + + down(&sc->master->master_sem); + list_add_tail(&req->list, &sc->sdo_configs); + up(&sc->master->master_sem); + return 0; +} + +/*****************************************************************************/ + /** Same as ecrt_slave_config_create_sdo_request(), but with ERR_PTR() return * value. */ @@ -923,6 +965,7 @@ EXPORT_SYMBOL(ecrt_slave_config_sdo8); EXPORT_SYMBOL(ecrt_slave_config_sdo16); EXPORT_SYMBOL(ecrt_slave_config_sdo32); +EXPORT_SYMBOL(ecrt_slave_config_complete_sdo); EXPORT_SYMBOL(ecrt_slave_config_create_sdo_request); EXPORT_SYMBOL(ecrt_slave_config_create_voe_handler); EXPORT_SYMBOL(ecrt_slave_config_state); diff -r 326d47aa986c -r 1baac79a40cf tool/CommandCStruct.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tool/CommandCStruct.cpp Thu Nov 19 14:44:57 2009 +0100 @@ -0,0 +1,198 @@ +/***************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * + * This file is part of the IgH EtherCAT Master. + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * --- + * + * The license mentioned above concerns the source code only. Using the + * EtherCAT technology and brand is only permitted in compliance with the + * industrial property and similar rights of Beckhoff Automation GmbH. + * + ****************************************************************************/ + +#include +#include +#include +using namespace std; + +#include "CommandCStruct.h" + +/*****************************************************************************/ + +CommandCStruct::CommandCStruct(): + Command("cstruct", "Generate slave PDO information in C language.") +{ +} + +/*****************************************************************************/ + +string CommandCStruct::helpString() const +{ + stringstream str; + + str << getName() << " [OPTIONS]" << endl + << endl + << getBriefDescription() << endl + << endl + << "The output C code can be used directly with the" << endl + << "ecrt_slave_config_pdos() function of the application" << endl + << "interface." << endl + << endl + << "Command-specific options:" << endl + << " --alias -a " << endl + << " --position -p Slave selection. See the help of" << endl + << " the 'slaves' command." << endl + << endl + << numericInfo(); + + return str.str(); +} + +/****************************************************************************/ + +void CommandCStruct::execute(MasterDevice &m, const StringVector &args) +{ + SlaveList slaves; + SlaveList::const_iterator si; + + if (args.size()) { + stringstream err; + err << "'" << getName() << "' takes no arguments!"; + throwInvalidUsageException(err); + } + + m.open(MasterDevice::Read); + slaves = selectedSlaves(m); + + for (si = slaves.begin(); si != slaves.end(); si++) { + generateSlaveCStruct(m, *si); + } +} + +/****************************************************************************/ + +void CommandCStruct::generateSlaveCStruct( + MasterDevice &m, + const ec_ioctl_slave_t &slave + ) +{ + ec_ioctl_slave_sync_t sync; + ec_ioctl_slave_sync_pdo_t pdo; + ec_ioctl_slave_sync_pdo_entry_t entry; + unsigned int i, j, k, pdo_pos = 0, entry_pos = 0; + stringstream id, syncs, pdos, entries; + + if (!slave.sync_count) + return; + + id << "slave_" << dec << slave.position << "_"; + + for (i = 0; i < slave.sync_count; i++) { + m.getSync(&sync, slave.position, i); + + syncs << " {" << dec << sync.sync_index + << ", " << (EC_READ_BIT(&sync.control_register, 2) ? + "EC_DIR_OUTPUT" : "EC_DIR_INPUT") + << ", " << dec << (unsigned int) sync.pdo_count + << ", "; + if (sync.pdo_count) { + syncs << id.str() << "pdos + " << dec << pdo_pos; + } else { + syncs << "NULL"; + } + syncs << ", " << (EC_READ_BIT(&sync.control_register, 6) ? + "EC_WD_ENABLE" : "EC_WD_DISABLE") + << "},"; + syncs << endl; + pdo_pos += sync.pdo_count; + + for (j = 0; j < sync.pdo_count; j++) { + m.getPdo(&pdo, slave.position, i, j); + + pdos << " {0x" << hex << setfill('0') + << setw(4) << pdo.index + << ", " << dec << (unsigned int) pdo.entry_count + << ", "; + if (pdo.entry_count) { + pdos << id.str() << "pdo_entries + " << dec << entry_pos; + } else { + pdos << "NULL"; + } + pdos << "},"; + if (strlen((const char *) pdo.name)) { + pdos << " /* " << pdo.name << " */"; + } + pdos << endl; + entry_pos += pdo.entry_count; + + for (k = 0; k < pdo.entry_count; k++) { + m.getPdoEntry(&entry, slave.position, i, j, k); + + entries << " {0x" << hex << setfill('0') + << setw(4) << entry.index + << ", 0x" << setw(2) << (unsigned int) entry.subindex + << ", " << dec << (unsigned int) entry.bit_length + << "},"; + if (strlen((const char *) entry.name)) { + entries << " /* " << entry.name << " */"; + } + entries << endl; + } + } + } + + cout + << "/* Slave " << slave.position; + if (strlen(slave.order)) { + cout << ", \"" << slave.order << "\""; + } + + cout << endl + << " * Vendor ID: 0x" << hex << setfill('0') + << setw(8) << slave.vendor_id << endl + << " * Product code: 0x" << hex << setfill('0') + << setw(8) << slave.product_code << endl + << " * Revision number: 0x" << hex << setfill('0') + << setw(8) << slave.revision_number << endl + << " */" << endl + << endl; + + if (entry_pos) { + cout << "ec_pdo_entry_info_t " << id.str() + << "pdo_entries[] = {" << endl + << entries.str() + << "};" << endl + << endl; + } + + if (pdo_pos) { + cout << "ec_pdo_info_t " << id.str() << "pdos[] = {" << endl + << pdos.str() + << "};" << endl + << endl; + } + + cout << "ec_sync_info_t " << id.str() << "syncs[] = {" << endl + << syncs.str() + << "};" << endl + << endl; +} + +/*****************************************************************************/ diff -r 326d47aa986c -r 1baac79a40cf tool/CommandCStruct.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tool/CommandCStruct.h Thu Nov 19 14:44:57 2009 +0100 @@ -0,0 +1,52 @@ +/***************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH + * + * This file is part of the IgH EtherCAT Master. + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * --- + * + * The license mentioned above concerns the source code only. Using the + * EtherCAT technology and brand is only permitted in compliance with the + * industrial property and similar rights of Beckhoff Automation GmbH. + * + ****************************************************************************/ + +#ifndef __COMMANDCSTRUCT_H__ +#define __COMMANDCSTRUCT_H__ + +#include "Command.h" + +/****************************************************************************/ + +class CommandCStruct: + public Command +{ + public: + CommandCStruct(); + + string helpString() const; + void execute(MasterDevice &, const StringVector &); + + protected: + void generateSlaveCStruct(MasterDevice &, const ec_ioctl_slave_t &); +}; + +/****************************************************************************/ + +#endif diff -r 326d47aa986c -r 1baac79a40cf tool/CommandConfig.cpp --- a/tool/CommandConfig.cpp Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/CommandConfig.cpp Thu Nov 19 14:44:57 2009 +0100 @@ -131,7 +131,7 @@ ) { ConfigList::const_iterator configIter; - unsigned int j, k, l; + unsigned int i, j, k, l; ec_ioctl_slave_t slave; ec_ioctl_config_pdo_t pdo; ec_ioctl_config_pdo_entry_t entry; @@ -159,11 +159,34 @@ cout << "none" << endl; } + cout << "Watchdog divider: "; + if (configIter->watchdog_divider) { + cout << dec << configIter->watchdog_divider; + } else { + cout << "(Default)"; + } + cout << endl + << "Watchdog intervals: "; + if (configIter->watchdog_intervals) { + cout << dec << configIter->watchdog_intervals; + } else { + cout << "(Default)"; + } + cout << endl; + for (j = 0; j < EC_MAX_SYNC_MANAGERS; j++) { if (configIter->syncs[j].pdo_count) { - cout << "SM" << dec << j << " (" + cout << "SM" << dec << j << ", Dir: " << (configIter->syncs[j].dir == EC_DIR_INPUT - ? "Input" : "Output") << ")" << endl; + ? "Input" : "Output") << ", Watchdog: "; + switch (configIter->syncs[j].watchdog_mode) { + case EC_WD_DEFAULT: cout << "Default"; break; + case EC_WD_ENABLE: cout << "Enable"; break; + case EC_WD_DISABLE: cout << "Disable"; break; + default: cout << "???"; break; + } + cout << endl; + for (k = 0; k < configIter->syncs[j].pdo_count; k++) { m.getConfigPdo(&pdo, configIter->config_index, j, k); @@ -194,26 +217,23 @@ << hex << setfill('0') << setw(4) << sdo.index << ":" << setw(2) << (unsigned int) sdo.subindex - << ", " << dec << sdo.size << " byte: " << hex; - - switch (sdo.size) { - case 1: - cout << "0x" << setw(2) - << (unsigned int) *(uint8_t *) &sdo.data; - break; - case 2: - cout << "0x" << setw(4) - << le16_to_cpup(&sdo.data); - break; - case 4: - cout << "0x" << setw(8) - << le32_to_cpup(&sdo.data); - break; - default: - cout << "???"; + << ", " << dec << sdo.size << " byte" << endl; + + cout << " " << hex; + for (i = 0; i < min((uint32_t) sdo.size, + (uint32_t) EC_MAX_SDO_DATA_SIZE); i++) { + cout << setw(2) << (unsigned int) sdo.data[i]; + if ((i + 1) % 16 == 0 && i < sdo.size - 1) { + cout << endl << " "; + } else { + cout << " "; + } } cout << endl; + if (sdo.size > EC_MAX_SDO_DATA_SIZE) { + cout << " ..." << endl; + } } } else { cout << " None." << endl; diff -r 326d47aa986c -r 1baac79a40cf tool/CommandEoe.cpp --- a/tool/CommandEoe.cpp Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/CommandEoe.cpp Thu Nov 19 14:44:57 2009 +0100 @@ -1,6 +1,6 @@ /***************************************************************************** * - * $Id: CommandSlaves.cpp 1767 2009-05-08 13:35:06Z fp $ + * $Id$ * * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH * diff -r 326d47aa986c -r 1baac79a40cf tool/CommandEoe.h --- a/tool/CommandEoe.h Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/CommandEoe.h Thu Nov 19 14:44:57 2009 +0100 @@ -1,6 +1,6 @@ /***************************************************************************** * - * $Id: CommandSlaves.h 1667 2009-02-24 12:51:39Z fp $ + * $Id$ * * Copyright (C) 2006-2009 Florian Pose, Ingenieurgemeinschaft IgH * diff -r 326d47aa986c -r 1baac79a40cf tool/CommandMaster.cpp --- a/tool/CommandMaster.cpp Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/CommandMaster.cpp Thu Nov 19 14:44:57 2009 +0100 @@ -91,6 +91,7 @@ } cout << endl + << " Active: " << (data.active ? "yes" : "no") << endl << " Slaves: " << data.slave_count << endl << " Ethernet devices:" << endl; diff -r 326d47aa986c -r 1baac79a40cf tool/Makefile.am --- a/tool/Makefile.am Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/Makefile.am Thu Nov 19 14:44:57 2009 +0100 @@ -27,21 +27,23 @@ # # --- # -# vim: syntax=make +# vim: syntax=automake # #------------------------------------------------------------------------------ +EXTRA_DIST = + bin_PROGRAMS = ethercat ethercat_SOURCES = \ Command.cpp \ CommandAlias.cpp \ CommandConfig.cpp \ + CommandCStruct.cpp \ CommandData.cpp \ CommandDebug.cpp \ CommandDomains.cpp \ CommandDownload.cpp \ - CommandEoe.cpp \ CommandFoeRead.cpp \ CommandFoeWrite.cpp \ CommandGraph.cpp \ @@ -64,15 +66,21 @@ main.cpp \ sii_crc.cpp +if ENABLE_EOE +ethercat_SOURCES += CommandEoe.cpp +else +EXTRA_DIST += CommandEoe.cpp +endif + noinst_HEADERS = \ Command.h \ CommandAlias.h \ CommandConfig.h \ + CommandCStruct.h \ CommandData.h \ CommandDebug.h \ CommandDomains.h \ CommandDownload.h \ - CommandEoe.h \ CommandFoeRead.h \ CommandFoeWrite.h \ CommandGraph.h \ @@ -94,12 +102,18 @@ SdoCommand.h \ sii_crc.h -REV = `if test -s $(top_srcdir)/svnrevision; then \ - cat $(top_srcdir)/svnrevision; \ +if ENABLE_EOE +noinst_HEADERS += CommandEoe.h +else +EXTRA_DIST += CommandEoe.h +endif + +REV = `if test -s $(top_srcdir)/revision; then \ + cat $(top_srcdir)/revision; \ else \ - svnversion $(srcdir)/.. 2>/dev/null || echo "unknown"; \ + hg id -i $(top_srcdir) 2>/dev/null || echo "unknown"; \ fi` -ethercat_CXXFLAGS = -I$(top_srcdir)/master -Wall -DSVNREV=$(REV) +ethercat_CXXFLAGS = -I$(top_srcdir)/master -Wall -DREV=$(REV) #------------------------------------------------------------------------------ diff -r 326d47aa986c -r 1baac79a40cf tool/MasterDevice.cpp --- a/tool/MasterDevice.cpp Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/MasterDevice.cpp Thu Nov 19 14:44:57 2009 +0100 @@ -485,6 +485,8 @@ /****************************************************************************/ +#ifdef EC_EOE + void MasterDevice::getEoeHandler( ec_ioctl_eoe_handler_t *eoe, uint16_t eoeHandlerIndex @@ -499,4 +501,6 @@ } } +#endif + /*****************************************************************************/ diff -r 326d47aa986c -r 1baac79a40cf tool/MasterDevice.h --- a/tool/MasterDevice.h Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/MasterDevice.h Thu Nov 19 14:44:57 2009 +0100 @@ -117,7 +117,9 @@ void requestState(uint16_t, uint8_t); void readFoe(ec_ioctl_slave_foe_t *); void writeFoe(ec_ioctl_slave_foe_t *); +#ifdef EC_EOE void getEoeHandler(ec_ioctl_eoe_handler_t *, uint16_t); +#endif private: unsigned int index; diff -r 326d47aa986c -r 1baac79a40cf tool/main.cpp --- a/tool/main.cpp Thu Nov 19 14:39:10 2009 +0100 +++ b/tool/main.cpp Thu Nov 19 14:44:57 2009 +0100 @@ -37,11 +37,14 @@ #include "CommandAlias.h" #include "CommandConfig.h" +#include "CommandCStruct.h" #include "CommandData.h" #include "CommandDebug.h" #include "CommandDomains.h" #include "CommandDownload.h" -#include "CommandEoe.h" +#ifdef EC_EOE +# include "CommandEoe.h" +#endif #include "CommandFoeRead.h" #include "CommandFoeWrite.h" #include "CommandGraph.h" @@ -293,11 +296,14 @@ commandList.push_back(new CommandAlias()); commandList.push_back(new CommandConfig()); + commandList.push_back(new CommandCStruct()); commandList.push_back(new CommandData()); commandList.push_back(new CommandDebug()); commandList.push_back(new CommandDomains()); commandList.push_back(new CommandDownload()); +#ifdef EC_EOE commandList.push_back(new CommandEoe()); +#endif commandList.push_back(new CommandFoeRead()); commandList.push_back(new CommandFoeWrite()); commandList.push_back(new CommandGraph());