--- /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
--- /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
--- 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
--- 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:
--- 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:
--- 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 := \
--- 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
--- 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 := \
--- /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 <shuchen@realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ *
+ * vim: noexpandtab
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#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 <fp@igh-essen.com>");
+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);
--- /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 <shuchen@realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#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 <netdev@vger.kernel.org>");
+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);
--- 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 \
--- 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
--- 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 \
--- /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
--- 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))) {
--- 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))) {
--- 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))) {
--- 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)
/*****************************************************************************/
--- 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
--- 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;
+}
+
+/*****************************************************************************/
--- 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");
--- 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)
#------------------------------------------------------------------------------
--- 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;
--- 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)) {
--- 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);
--- 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);
+ }
}
/*****************************************************************************/
--- 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;
/*****************************************************************************/
--- 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 */
--- 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
--- 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);
--- 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
--- 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);
--- /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 <iostream>
+#include <iomanip>
+#include <string.h>
+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 <alias>" << endl
+ << " --position -p <pos> 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;
+}
+
+/*****************************************************************************/
--- /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
--- 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;
--- 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
*
--- 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
*
--- 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;
--- 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)
#------------------------------------------------------------------------------
--- 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
+
/*****************************************************************************/
--- 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;
--- 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());