From 89376b7141edb6f927fb940c27391cda6e67c785 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Thu, 19 Feb 2009 19:55:40 +0000 Subject: Initial import. --- INSTALL | 53 + Makefile.am | 16 + bsd_eth_drivers/Makefile.am | 3 + bsd_eth_drivers/if_em/Makefile.am | 39 + bsd_eth_drivers/if_em/README | 405 ++ bsd_eth_drivers/if_em/e1000_80003es2lan.c | 1339 +++++++ bsd_eth_drivers/if_em/e1000_80003es2lan.h | 97 + bsd_eth_drivers/if_em/e1000_82540.c | 677 ++++ bsd_eth_drivers/if_em/e1000_82541.c | 1313 +++++++ bsd_eth_drivers/if_em/e1000_82541.h | 91 + bsd_eth_drivers/if_em/e1000_82542.c | 558 +++ bsd_eth_drivers/if_em/e1000_82543.c | 1651 ++++++++ bsd_eth_drivers/if_em/e1000_82543.h | 50 + bsd_eth_drivers/if_em/e1000_82571.c | 1405 +++++++ bsd_eth_drivers/if_em/e1000_82571.h | 47 + bsd_eth_drivers/if_em/e1000_82575.c | 1397 +++++++ bsd_eth_drivers/if_em/e1000_82575.h | 298 ++ bsd_eth_drivers/if_em/e1000_api.c | 1222 ++++++ bsd_eth_drivers/if_em/e1000_api.h | 165 + bsd_eth_drivers/if_em/e1000_defines.h | 1403 +++++++ bsd_eth_drivers/if_em/e1000_hw.h | 730 ++++ bsd_eth_drivers/if_em/e1000_ich8lan.c | 2491 ++++++++++++ bsd_eth_drivers/if_em/e1000_ich8lan.h | 115 + bsd_eth_drivers/if_em/e1000_mac.c | 2003 ++++++++++ bsd_eth_drivers/if_em/e1000_mac.h | 91 + bsd_eth_drivers/if_em/e1000_nvm.c | 901 +++++ bsd_eth_drivers/if_em/e1000_nvm.h | 66 + bsd_eth_drivers/if_em/e1000_osdep.h | 397 ++ bsd_eth_drivers/if_em/e1000_phy.c | 2064 ++++++++++ bsd_eth_drivers/if_em/e1000_phy.h | 177 + bsd_eth_drivers/if_em/e1000_regs.h | 441 +++ bsd_eth_drivers/if_em/if_em.c | 5272 ++++++++++++++++++++++++++ bsd_eth_drivers/if_em/if_em.h | 443 +++ bsd_eth_drivers/if_le/Makefile.am | 30 + bsd_eth_drivers/if_le/am79900.c | 676 ++++ bsd_eth_drivers/if_le/am79900reg.h | 156 + bsd_eth_drivers/if_le/am79900var.h | 62 + bsd_eth_drivers/if_le/am7990reg.h | 189 + bsd_eth_drivers/if_le/am7990var.h | 62 + bsd_eth_drivers/if_le/if_le_pci.c | 671 ++++ bsd_eth_drivers/if_le/lance.c | 873 +++++ bsd_eth_drivers/if_le/lancereg.h | 619 +++ bsd_eth_drivers/if_le/lancevar.h | 216 ++ bsd_eth_drivers/if_pcn/Makefile.am | 21 + bsd_eth_drivers/if_pcn/if_pcn.c | 1918 ++++++++++ bsd_eth_drivers/if_pcn/if_pcnreg.h | 540 +++ bsd_eth_drivers/if_re/Makefile.am | 19 + bsd_eth_drivers/if_re/if_re.c | 2890 ++++++++++++++ bsd_eth_drivers/if_re/if_rlreg.h | 993 +++++ bsd_eth_drivers/libbsdport/Makefile.am | 75 + bsd_eth_drivers/libbsdport/README | 29 + bsd_eth_drivers/libbsdport/alldrv.c | 16 + bsd_eth_drivers/libbsdport/bus.h | 324 ++ bsd_eth_drivers/libbsdport/callout.h | 43 + bsd_eth_drivers/libbsdport/contigmalloc.c | 33 + bsd_eth_drivers/libbsdport/devicet.c | 374 ++ bsd_eth_drivers/libbsdport/devicet.h | 125 + bsd_eth_drivers/libbsdport/ifmedia.c | 91 + bsd_eth_drivers/libbsdport/ifstuff.c | 79 + bsd_eth_drivers/libbsdport/libbsdport.h | 380 ++ bsd_eth_drivers/libbsdport/libbsdport_api.h | 84 + bsd_eth_drivers/libbsdport/libbsdport_post.h | 95 + bsd_eth_drivers/libbsdport/malloc.c | 17 + bsd_eth_drivers/libbsdport/mutex.h | 55 + bsd_eth_drivers/libbsdport/rtems_callout.c | 250 ++ bsd_eth_drivers/libbsdport/rtems_taskqueue.c | 308 ++ bsd_eth_drivers/libbsdport/rtems_udelay.c | 192 + bsd_eth_drivers/libbsdport/rtems_udelay.h | 22 + bsd_eth_drivers/libbsdport/rtems_verscheck.h | 29 + bsd_eth_drivers/libbsdport/sysbus.c | 267 ++ bsd_eth_drivers/libbsdport/taskqueue.h | 69 + bsd_eth_drivers/links.am | 15 + config.h.in | 48 + configure.ac | 378 ++ m4/config-if-present.m4 | 23 + m4/cvstag.m4 | 41 + m4/rtems-bsplist.m4 | 41 + m4/rtems-check-libargs.m4 | 9 + m4/rtems-checkprog.m4 | 9 + m4/rtems-checktool.m4 | 11 + m4/rtems-checktop.m4 | 23 + m4/rtems-fixup-prefix.m4 | 59 + m4/rtems-isrtems.m4 | 23 + m4/rtems-makevars.m4 | 142 + m4/rtems-options.m4 | 41 + m4/rtems-setup-recurse.m4 | 224 ++ m4/rtems-tools.m4 | 15 + m4/rtems-trim-builddir.m4 | 26 + m4/rtems-verscheck.m4 | 36 + makefile.top.am | 12 + makefile.top.in | 670 ++++ rtems-pre.am | 5 + rtems.am | 41 + ssrlApps.components.in | 1 + 94 files changed, 42205 insertions(+) create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 bsd_eth_drivers/Makefile.am create mode 100644 bsd_eth_drivers/if_em/Makefile.am create mode 100644 bsd_eth_drivers/if_em/README create mode 100644 bsd_eth_drivers/if_em/e1000_80003es2lan.c create mode 100644 bsd_eth_drivers/if_em/e1000_80003es2lan.h create mode 100644 bsd_eth_drivers/if_em/e1000_82540.c create mode 100644 bsd_eth_drivers/if_em/e1000_82541.c create mode 100644 bsd_eth_drivers/if_em/e1000_82541.h create mode 100644 bsd_eth_drivers/if_em/e1000_82542.c create mode 100644 bsd_eth_drivers/if_em/e1000_82543.c create mode 100644 bsd_eth_drivers/if_em/e1000_82543.h create mode 100644 bsd_eth_drivers/if_em/e1000_82571.c create mode 100644 bsd_eth_drivers/if_em/e1000_82571.h create mode 100644 bsd_eth_drivers/if_em/e1000_82575.c create mode 100644 bsd_eth_drivers/if_em/e1000_82575.h create mode 100644 bsd_eth_drivers/if_em/e1000_api.c create mode 100644 bsd_eth_drivers/if_em/e1000_api.h create mode 100644 bsd_eth_drivers/if_em/e1000_defines.h create mode 100644 bsd_eth_drivers/if_em/e1000_hw.h create mode 100644 bsd_eth_drivers/if_em/e1000_ich8lan.c create mode 100644 bsd_eth_drivers/if_em/e1000_ich8lan.h create mode 100644 bsd_eth_drivers/if_em/e1000_mac.c create mode 100644 bsd_eth_drivers/if_em/e1000_mac.h create mode 100644 bsd_eth_drivers/if_em/e1000_nvm.c create mode 100644 bsd_eth_drivers/if_em/e1000_nvm.h create mode 100644 bsd_eth_drivers/if_em/e1000_osdep.h create mode 100644 bsd_eth_drivers/if_em/e1000_phy.c create mode 100644 bsd_eth_drivers/if_em/e1000_phy.h create mode 100644 bsd_eth_drivers/if_em/e1000_regs.h create mode 100644 bsd_eth_drivers/if_em/if_em.c create mode 100644 bsd_eth_drivers/if_em/if_em.h create mode 100644 bsd_eth_drivers/if_le/Makefile.am create mode 100644 bsd_eth_drivers/if_le/am79900.c create mode 100644 bsd_eth_drivers/if_le/am79900reg.h create mode 100644 bsd_eth_drivers/if_le/am79900var.h create mode 100644 bsd_eth_drivers/if_le/am7990reg.h create mode 100644 bsd_eth_drivers/if_le/am7990var.h create mode 100644 bsd_eth_drivers/if_le/if_le_pci.c create mode 100644 bsd_eth_drivers/if_le/lance.c create mode 100644 bsd_eth_drivers/if_le/lancereg.h create mode 100644 bsd_eth_drivers/if_le/lancevar.h create mode 100644 bsd_eth_drivers/if_pcn/Makefile.am create mode 100644 bsd_eth_drivers/if_pcn/if_pcn.c create mode 100644 bsd_eth_drivers/if_pcn/if_pcnreg.h create mode 100644 bsd_eth_drivers/if_re/Makefile.am create mode 100644 bsd_eth_drivers/if_re/if_re.c create mode 100644 bsd_eth_drivers/if_re/if_rlreg.h create mode 100644 bsd_eth_drivers/libbsdport/Makefile.am create mode 100644 bsd_eth_drivers/libbsdport/README create mode 100644 bsd_eth_drivers/libbsdport/alldrv.c create mode 100644 bsd_eth_drivers/libbsdport/bus.h create mode 100644 bsd_eth_drivers/libbsdport/callout.h create mode 100644 bsd_eth_drivers/libbsdport/contigmalloc.c create mode 100644 bsd_eth_drivers/libbsdport/devicet.c create mode 100644 bsd_eth_drivers/libbsdport/devicet.h create mode 100644 bsd_eth_drivers/libbsdport/ifmedia.c create mode 100644 bsd_eth_drivers/libbsdport/ifstuff.c create mode 100644 bsd_eth_drivers/libbsdport/libbsdport.h create mode 100644 bsd_eth_drivers/libbsdport/libbsdport_api.h create mode 100644 bsd_eth_drivers/libbsdport/libbsdport_post.h create mode 100644 bsd_eth_drivers/libbsdport/malloc.c create mode 100644 bsd_eth_drivers/libbsdport/mutex.h create mode 100644 bsd_eth_drivers/libbsdport/rtems_callout.c create mode 100644 bsd_eth_drivers/libbsdport/rtems_taskqueue.c create mode 100644 bsd_eth_drivers/libbsdport/rtems_udelay.c create mode 100644 bsd_eth_drivers/libbsdport/rtems_udelay.h create mode 100644 bsd_eth_drivers/libbsdport/rtems_verscheck.h create mode 100644 bsd_eth_drivers/libbsdport/sysbus.c create mode 100644 bsd_eth_drivers/libbsdport/taskqueue.h create mode 100644 bsd_eth_drivers/links.am create mode 100644 config.h.in create mode 100644 configure.ac create mode 100644 m4/config-if-present.m4 create mode 100644 m4/cvstag.m4 create mode 100644 m4/rtems-bsplist.m4 create mode 100644 m4/rtems-check-libargs.m4 create mode 100644 m4/rtems-checkprog.m4 create mode 100644 m4/rtems-checktool.m4 create mode 100644 m4/rtems-checktop.m4 create mode 100644 m4/rtems-fixup-prefix.m4 create mode 100644 m4/rtems-isrtems.m4 create mode 100644 m4/rtems-makevars.m4 create mode 100644 m4/rtems-options.m4 create mode 100644 m4/rtems-setup-recurse.m4 create mode 100644 m4/rtems-tools.m4 create mode 100644 m4/rtems-trim-builddir.m4 create mode 100644 m4/rtems-verscheck.m4 create mode 100644 makefile.top.am create mode 100644 makefile.top.in create mode 100644 rtems-pre.am create mode 100644 rtems.am create mode 100644 ssrlApps.components.in diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..38c11f6 --- /dev/null +++ b/INSTALL @@ -0,0 +1,53 @@ +HOW TO BUILD THE SSRL RTEMS-APPLICATIONS + +$Id$ + +Till Straumann, 2008/10/3 + +*************************************************** +THIS FILE HAS NOT BEEN WRITTEN YET, SORRY! + +Some useful information regarding 'configure' options +can be found in cexp/INSTALL. + +Basic instructions: + + - make sure PATH contains toolchain directory + - mkdir build + - chdir build + - ../configure --with-rtems-top=/afs/slac/package/rtems/ \ + --prefix=/afs/slac/package/rtems/ + - make + - make install + +This builds ssrlApps for all architectures/BSPs which are installed +under --with-rtems-top. The list of BSPs can be explicitly defined +using --with-rtemsbsp='bsp1 bsp2 bsp3 ...'. + +The default installation path for binaries, libraries and includes is + + /target/ssrlApps/-rtems//bin + /target/ssrlApps/-rtems//lib + /target/ssrlApps/-rtems//include + +but can be modified using the standard --exec-prefix, --libdir, --includedir +options (see 'configure' documentation and cexp/INSTALL). + +Other useful options: + + --enable-std-rtems-installdirs + Install directly into the RTEMS installation directories; + by default a location *outside* of the standard location + is used. If you don't use this option you can also fine-tune + the installation using the usual --prefix, --exec-prefix, + --libdir, --includedir etc. options. If you use this + option '--prefix' & friends are effectively overridden. + + --with-hostbindir= + Where tools, i.e., programs that execute on the development + platform are to be installed. Defaults to + + PREFIX/host/${build_alias}/bin + + +*************************************************** diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..8fae066 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,16 @@ +AUTOMAKE_OPTIONS=foreign + +ACLOCAL_AMFLAGS= -I./m4 + +SUBDIRS=. @enable_subdirs@ + +DIST_SUBDIRS= @all_subdirs@ + +EXTRA_DIST=makefile.top.am makefile.top.in ssrlApps.components.in + +all-local: + $(MAKE) $(AM_MAKEFLAGS) INSTALL="$(INSTALL_IF_CHANGE)" prefix="$(abs_top_builddir)/data" exec_prefix='$$(prefix)' includedir='$$(prefix)/include' install-data + $(MAKE) $(AM_MAKEFLAGS) INSTALL="$(INSTALL_IF_CHANGE)" prefix="$(abs_top_builddir)/data" exec_prefix='$$(prefix)' includedir='$$(prefix)/include' install-exec + +clean-local: + $(RM) -r data diff --git a/bsd_eth_drivers/Makefile.am b/bsd_eth_drivers/Makefile.am new file mode 100644 index 0000000..4495d4a --- /dev/null +++ b/bsd_eth_drivers/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS=foreign + +SUBDIRS=libbsdport if_pcn if_le if_em re diff --git a/bsd_eth_drivers/if_em/Makefile.am b/bsd_eth_drivers/if_em/Makefile.am new file mode 100644 index 0000000..1a6a12f --- /dev/null +++ b/bsd_eth_drivers/if_em/Makefile.am @@ -0,0 +1,39 @@ +# $Id$ +AUTOMAKE_OPTIONS=foreign + +include $(top_srcdir)/rtems-pre.am + +libif_em_a_SOURCES = if_em.c +libif_em_a_SOURCES += e1000_80003es2lan.c e1000_82540.c e1000_82541.c +libif_em_a_SOURCES += e1000_82543.c e1000_82571.c e1000_82575.c +libif_em_a_SOURCES += e1000_api.c e1000_mac.c e1000_manage.c +libif_em_a_SOURCES += e1000_nvm.c e1000_phy.c + +libif_em_a_SOURCES += e1000_80003es2lan.h e1000_82541.h e1000_82543.h +libif_em_a_SOURCES += e1000_82571.h e1000_82575.h e1000_api.h +libif_em_a_SOURCES += e1000_defines.h e1000_hw.h e1000_ich8lan.h +libif_em_a_SOURCES += e1000_mac.h e1000_manage.h e1000_nvm.h +libif_em_a_SOURCES += e1000_osdep.h e1000_phy.h e1000_regs.h if_em.h + +EXTRA_libif_em_a_SOURCES = e1000_82542.c e1000_ich8lan.c + +CPPFLAGS_82542_SUPPORT_NO = -DNO_82542_SUPPORT +CPPFLAGS_ICH8LAN_SUPPORT_NO = -DNO_ICH8LAN_SUPPORT + +O_PIECES_82542_SUPPORT_YES = e1000_82542.$(OBJEXT) +O_PIECES_ICH8LAN_SUPPORT_YES = e1000_ich8lan.$(OBJEXT) + +libif_em_a_LIBADD = +libif_em_a_LIBADD += $(O_PIECES_82542_SUPPORT_$(ENBL_82542_SUPPORT)) +libif_em_a_LIBADD += $(O_PIECES_ICH8LAN_SUPPORT_$(ENBL_ICH8LAN_SUPPORT)) + +libif_em_a_DEPENDENCIES = $(libif_em_a_LIBADD) + +lib_LIBRARIES = libif_em.a + +AM_CPPFLAGS += -I$(srcdir) +AM_CPPFLAGS += -I$(srcdir)/../libbsdport -I../libbsdport -I../libbsdport/dummyheaders +AM_CPPFLAGS += $(CPPFLAGS_82542_SUPPORT_$(ENBL_82542_SUPPORT)) +AM_CPPFLAGS += $(CPPFLAGS_ICH8LAN_SUPPORT_$(ENBL_ICH8LAN_SUPPORT)) + +include $(top_srcdir)/rtems.am diff --git a/bsd_eth_drivers/if_em/README b/bsd_eth_drivers/if_em/README new file mode 100644 index 0000000..98a16e7 --- /dev/null +++ b/bsd_eth_drivers/if_em/README @@ -0,0 +1,405 @@ +$FreeBSD: src/sys/dev/em/README,v 1.15 2007/05/30 23:32:21 jfv Exp $ +FreeBSD* Driver for Intel Network Connection +============================================= + +May 30, 2007 + + +Contents +======== + +- Overview +- Identifying Your Adapter +- Building and Installation +- Speed and Duplex Configuration +- Additional Configurations +- Known Limitations +- Support +- License + + +Overview +======== + +This file describes the FreeBSD* driver for Intel Network Connection. +This driver has been developed for use with FreeBSD, Release 7.x. + +For questions related to hardware requirements, refer to the documentation +supplied with your Gigabit adapter. All hardware requirements listed +apply to use with FreeBSD. + + +Identifying Your Adapter +======================== + +For information on how to identify your adapter, go to the Adapter & +Driver ID Guide at: + +http://support.intel.com/support/network/sb/cs-012904.htm + + +For the latest Intel network drivers for FreeBSD, see: + +http://downloadfinder.intel.com/scripts-df-external/support_intel.aspx + + +NOTE: Mobile adapters are not fully supported. +NOTE: The Intel(R) 82562v 10/100 Network Connection only provides 10/100 +support. + +Building and Installation +========================= + +NOTE: The driver can be installed as a dynamic loadable kernel module or + compiled into the kernel. You must have kernel sources installed in + order to compile the driver module. + +In the instructions below, x.x.x is the driver version as indicated in the +name of the driver tar file. + +1. Move the base driver tar file to the directory of your choice. For + example, use /home/username/em or /usr/local/src/em. + +2. Untar/unzip the archive: + + tar xzvf em-x.x.x.tar.gz + + This will create an em-x.x.x directory. + +3. To create a loadable module, perform the following steps. + NOTE: To compile the driver into the kernel, go directly to step 4. + + a. To compile the module + + cd em-x.x.x + make + + b. To install the compiled module to the system directory: + + make install + + c. If you want the driver to load automatically when the system is booted: + + 1. Edit /boot/loader.conf, and add the following line: + + if_em_load="YES" + +4. To compile the driver into the kernel, enter: + + cd em-x.x.x/src + cp *.[ch] /usr/src/sys/dev/em + + Edit the kernel configuration file (i.e., GENERIC or MYKERNEL) in + /usr/src/sys/i386/conf, and ensure the following line is present: + + device em + + Compile and install the kernel. The system must be rebooted for the + kernel updates to take effect. For additional information on compiling + the kernel, consult the FreeBSD operating system documentation. + +5. To assign an IP address to the interface, enter the following: + + ifconfig em + +6. Verify that the interface works. Enter the following, where + is the IP address for another machine on the same subnet as the interface + that is being tested: + + ping + +7. To configure the IP address to remain after reboot, edit /etc/rc.conf, + and create the appropriate ifconfig_ementry: + + ifconfig_em="" + + Example usage: + + ifconfig_em0="inet 192.168.10.1 netmask 255.255.255.0" + + NOTE: For assistance, see the ifconfig man page. + + +Speed and Duplex Configuration +============================== + +By default, the adapter auto-negotiates the speed and duplex of the +connection. If there is a specific need, the ifconfig utility can be used to +configure the speed and duplex settings on the adapter. Example usage: + + ifconfig em media 100baseTX mediaopt + full-duplex + + NOTE: Only use mediaopt to set the driver to full-duplex. If mediaopt is + not specified and you are not running at gigabit speed, the driver + defaults to half-duplex. + +If the interface is currently forced to 100 full duplex, in order to change +to half duplex you must use this command: + + ifconfig em media 100baseTX -mediaopt + full-duplex + + +This driver supports the following media type options: + + autoselect - Enables auto-negotiation for speed and duplex. + + 10baseT/UTP - Sets speed to 10 Mbps. Use the ifconfig mediaopt + option to select full-duplex mode. + + 100baseTX - Sets speed to 100 Mbps. Use the ifconfig mediaopt + option to select full-duplex mode. + + 1000baseTX - Sets speed to 1000 Mbps. In this case, the driver + supports only full-duplex mode. + + 1000baseSX - Sets speed to 1000 Mbps. In this case, the driver + supports only full-duplex mode. + +For more information on the ifconfig utility, see the ifconfig man page. + + +Additional Configurations +========================= + +The driver supports Transmit/Receive Checksum Offload and Jumbo Frames on +all but the 82542-based adapters. For specific adapters, refer to the +Identifying Your Adapter section. + + Jumbo Frames + ------------ + To enable Jumbo Frames, use the ifconfig utility to set the Maximum + Transport Unit (MTU) frame size above its default of 1500 bytes. + + The Jumbo Frames MTU range for Intel Adapters is 1500 to 16110. To modify + the setting, enter the following: + + ifconfig em mtu 9000 + + To confirm the MTU used between two specific devices, use: + + route get + + Notes: + + - Only enable Jumbo Frames if your network infrastructure supports them. + + - To enable Jumbo Frames, increase the MTU size on the interface beyond + 1500. + + - The Jumbo Frames setting on the switch must be set to at least 22 bytes + larger than that of the MTU. + + - The maximum MTU setting for Jumbo Frames is 16110. This value coincides + with the maximum Jumbo Frames size of 16128. + + - Some Intel gigabit adapters that support Jumbo Frames have a frame size + limit of 9238 bytes, with a corresponding MTU size limit of 9216 bytes. + The adapters with this limitation are based on the Intel(R) 82571EB, + 82572EI, 82573L, 82566, 82562, and 80003ES2LAN controller. These + correspond to the following product names: + Intel(R) PRO/1000 PT Server Adapter + Intel(R) PRO/1000 PT Desktop Adapter + Intel(R) PRO/1000 PT Network Connection + Intel(R) PRO/1000 PT Dual Port Server Adapter + Intel(R) PRO/1000 PT Dual Port Network Connection + Intel(R) PRO/1000 PT Quad Port Server Adapter + Intel(R) PRO/1000 PF Quad Port Server Adapter + Intel(R) PRO/1000 PF Server Adapter + Intel(R) PRO/1000 PF Network Connection + Intel(R) PRO/1000 PF Dual Port Server Adapter + Intel(R) PRO/1000 PB Server Connection + Intel(R) PRO/1000 PL Network Connection + Intel(R) PRO/1000 EB Network Connection with I/O Acceleration + Intel(R) PRO/1000 EB Backplane Connection with I/O Acceleration + Intel(R) 82566DM-2 Gigabit Network Connection + + - Adapters based on the Intel(R) 82542 and 82573V/E controller do not + support Jumbo Frames. These correspond to the following product names: + Intel(R) PRO/1000 Gigabit Server Adapter + Intel(R) PRO/1000 PM Network Connection + + - Using Jumbo Frames at 10 or 100 Mbps may result in poor performance or + loss of link. + + - The following adapters do not support Jumbo Frames: + Intel(R) 82562V 10/100 Network Connection + Intel(R) 82566DM Gigabit Network Connection + Intel(R) 82566DC Gigabit Network Connection + Intel(R) 82566MM Gigabit Network Connection + Intel(R) 82566MC Gigabit Network Connection + Intel(R) 82562GT 10/100 Network Connection + Intel(R) 82562G 10/100 Network Connection + Intel(R) 82566DC-2 Gigabit Network Connection + Intel(R) 82562V-2 10/100 Network Connection + Intel(R) 82562G-2 10/100 Network Connection + Intel(R) 82562GT-2 10/100 Network Connection + + VLANs + ----- + To create a new VLAN interface: + + ifconfig create + + To associate the VLAN interface with a physical interface and + assign a VLAN ID, IP address, and netmask: + + ifconfig netmask vlan + vlandev + + Example: + + ifconfig vlan10 10.0.0.1 netmask 255.255.255.0 vlan 10 vlandev em0 + + In this example, all packets will be marked on egress with 802.1Q VLAN + tags, specifying a VLAN ID of 10. + + To remove a VLAN interface: + + Intel Network Connection ifconfig destroy + + + Polling + ------- + + To enable polling in the driver, add the following options to the kernel + configuration, and then recompile the kernel: + + options DEVICE_POLLING + options HZ=1000 + + At runtime use: + ifconfig emX polling (to turn polling on) + and: + ifconfig emX -polling (to turn it off) + + + Checksum Offload + ---------------- + Checksum offloading is not supported on 82542 Gigabit adapters. + + Checksum offloading supports both TCP and UDP packets and is + supported for both transmit and receive. + + Checksum offloading can be enabled or disabled using ifconfig. + Both transmit and receive offloading will be either enabled or + disabled together. You cannot enable/disable one without the other. + + To enable checksum offloading: + + ifconfig rxcsum + + To disable checksum offloading: + + ifconfig -rxcsum + + To confirm the current setting: + + ifconfig + + Look for the presence or absence of the following line: + + options=3 + + See the ifconfig man page for further information. + + + TSO + --- + The FreeBSD driver offers support for TSO (TCP Segmentation Offload). + + You can enable/disable it in two ways/places: + + - sysctl net.inet.tcp.tso=0 (or 1 to enable it) + + Doing this disables TSO in the stack and will affect all adapters. + + - ifconfig emX -tso + + Doing this will disable TSO only for this adapter. + + To enable: + + - ifconfig emX tso + + NOTES: By default only PCI-Express adapters are ENABLED to do TSO. Others + can be enabled by the user at their own risk + TSO is not supported on 82547 and 82544-based adapters, as well as older adapters. + + +Known Limitations +================= + + Detected Tx Unit Hang in Quad Port Adapters + ------------------------------------------- + + In some cases ports 3 and 4 wont pass traffic. Ports 1 and 2 don't show + any errors and will pass traffic. + + This issue MAY be resolved by updating to the latest BIOS. You can + check your system's BIOS by downloading the Linux Firmware Developer Kit + that can be obtained at http://www.linuxfirmwarekit.org/ + + + There are known performance issues with this driver when running UDP traffic + with Jumbo Frames. + ---------------------------------------------------------------------------- + + 82541/82547 can't link or is slow to link with some link partners + ----------------------------------------------------------------- + + There is a known compatibility issue where time to link is slow or link is not + established between 82541/82547 controllers and some switches. Known switches + include: + Planex FXG-08TE + I-O Data ETG-SH8 + + The driver can be compiled with the following changes: + + Edit ./em.x.x.x/src/if_em.h to change the #define EM_MASTER_SLAVE + For example, change from: + + #define EM_MASTER_SLAVE e1000_ms_hw_default + to: + #define EM_MASTER_SLAVE 2 + + Use one of the following options: + 1 = Master mode + 2 = Slave mode + 3 = Auto master/slave + Setting 2 is recommended. + + Recompile the module: + a. To compile the module + cd em-x.x.x + make clean + make + + b. To install the compiled module in system directory: + make install + + +Support +======= + +For general information and support, go to the Intel support website at: + + http://support.intel.com + +If an issue is identified, support is through email only at: +freebsdnic@mailbox.intel.com + + +License +======= + +This software program is released under the terms of a license agreement +between you ('Licensee') and Intel. Do not use or load this software or any +associated materials (collectively, the 'Software') until you have carefully +read the full terms and conditions of the LICENSE located in this software +package. By loading or using the Software, you agree to the terms of this +Agreement. If you do not agree with the terms of this Agreement, do not +install or use the Software. + +* Other names and brands may be claimed as the property of others. diff --git a/bsd_eth_drivers/if_em/e1000_80003es2lan.c b/bsd_eth_drivers/if_em/e1000_80003es2lan.c new file mode 100644 index 0000000..4e4aead --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_80003es2lan.c @@ -0,0 +1,1339 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_80003es2lan.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + +/* e1000_80003es2lan + */ + +#include "e1000_api.h" +#include "e1000_80003es2lan.h" + +void e1000_init_function_pointers_80003es2lan(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw); +STATIC void e1000_release_phy_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw); +STATIC void e1000_release_nvm_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, + u32 offset, + u16 *data); +STATIC s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, + u32 offset, + u16 data); +STATIC s32 e1000_write_nvm_80003es2lan(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +STATIC s32 e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +STATIC s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw); +STATIC s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw); +STATIC void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw); +static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask); +static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex); +static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw); +static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw); +static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw); +static void e1000_release_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask); + +/* A table for the GG82563 cable length where the range is defined + * with a lower bound at "index" and the upper bound at + * "index + 5". + */ +static const +u16 e1000_gg82563_cable_length_table[] = + { 0, 60, 115, 150, 150, 60, 115, 150, 180, 180, 0xFF }; +#define GG82563_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_gg82563_cable_length_table) / \ + sizeof(e1000_gg82563_cable_length_table[0])) + +/** + * e1000_init_phy_params_80003es2lan - Init ESB2 PHY func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_phy_params_80003es2lan"); + + if (hw->media_type != e1000_media_type_copper) { + phy->type = e1000_phy_none; + goto out; + } + + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; + phy->type = e1000_phy_gg82563; + + func->acquire_phy = e1000_acquire_phy_80003es2lan; + func->check_polarity = e1000_check_polarity_m88; + func->check_reset_block = e1000_check_reset_block_generic; + func->commit_phy = e1000_phy_sw_reset_generic; + func->get_cfg_done = e1000_get_cfg_done_80003es2lan; + func->get_phy_info = e1000_get_phy_info_m88; + func->release_phy = e1000_release_phy_80003es2lan; + func->reset_phy = e1000_phy_hw_reset_generic; + func->set_d3_lplu_state = e1000_set_d3_lplu_state_generic; + + func->force_speed_duplex = e1000_phy_force_speed_duplex_80003es2lan; + func->get_cable_length = e1000_get_cable_length_80003es2lan; + func->read_phy_reg = e1000_read_phy_reg_gg82563_80003es2lan; + func->write_phy_reg = e1000_write_phy_reg_gg82563_80003es2lan; + + /* This can only be done after all function pointers are setup. */ + ret_val = e1000_get_phy_id(hw); + + /* Verify phy id */ + if (phy->id != GG82563_E_PHY_ID) { + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_80003es2lan - Init ESB2 NVM func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u16 size; + + DEBUGFUNC("e1000_init_nvm_params_80003es2lan"); + + nvm->opcode_bits = 8; + nvm->delay_usec = 1; + switch (nvm->override) { + case e1000_nvm_override_spi_large: + nvm->page_size = 32; + nvm->address_bits = 16; + break; + case e1000_nvm_override_spi_small: + nvm->page_size = 8; + nvm->address_bits = 8; + break; + default: + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8; + break; + } + + nvm->type = e1000_nvm_eeprom_spi; + + size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + + /* Added to a constant, "size" becomes the left-shift value + * for setting word_size. + */ + size += NVM_WORD_SIZE_BASE_SHIFT; + nvm->word_size = 1 << size; + + /* Function Pointers */ + func->acquire_nvm = e1000_acquire_nvm_80003es2lan; + func->read_nvm = e1000_read_nvm_eerd; + func->release_nvm = e1000_release_nvm_80003es2lan; + func->update_nvm = e1000_update_nvm_checksum_generic; + func->valid_led_default = e1000_valid_led_default_generic; + func->validate_nvm = e1000_validate_nvm_checksum_generic; + func->write_nvm = e1000_write_nvm_80003es2lan; + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_80003es2lan - Init ESB2 MAC func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_mac_params_80003es2lan(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_mac_params_80003es2lan"); + + /* Set media type */ + switch (hw->device_id) { + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + hw->media_type = e1000_media_type_copper; + break; + } + + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES; + /* Set if part includes ASF firmware */ + mac->asf_firmware_present = TRUE; + /* Set if manageability features are enabled. */ + mac->arc_subsystem_valid = + (E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK) + ? TRUE : FALSE; + + /* Function pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_pcie_generic; + /* reset */ + func->reset_hw = e1000_reset_hw_80003es2lan; + /* hw initialization */ + func->init_hw = e1000_init_hw_80003es2lan; + /* link setup */ + func->setup_link = e1000_setup_link_generic; + /* physical interface link setup */ + func->setup_physical_interface = + (hw->media_type == e1000_media_type_copper) + ? e1000_setup_copper_link_80003es2lan + : e1000_setup_fiber_serdes_link_generic; + /* check for link */ + switch (hw->media_type) { + case e1000_media_type_copper: + func->check_for_link = e1000_check_for_copper_link_generic; + break; + case e1000_media_type_fiber: + func->check_for_link = e1000_check_for_fiber_link_generic; + break; + case e1000_media_type_internal_serdes: + func->check_for_link = e1000_check_for_serdes_link_generic; + break; + default: + ret_val = -E1000_ERR_CONFIG; + goto out; + break; + } + /* check management mode */ + func->check_mng_mode = e1000_check_mng_mode_generic; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_generic; + /* writing VFTA */ + func->write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + func->clear_vfta = e1000_clear_vfta_generic; + /* setting MTA */ + func->mta_set = e1000_mta_set_generic; + /* blink LED */ + func->blink_led = e1000_blink_led_generic; + /* setup LED */ + func->setup_led = e1000_setup_led_generic; + /* cleanup LED */ + func->cleanup_led = e1000_cleanup_led_generic; + /* turn on/off LED */ + func->led_on = e1000_led_on_generic; + func->led_off = e1000_led_off_generic; + /* remove device */ + func->remove_device = e1000_remove_device_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_80003es2lan; + /* link info */ + func->get_link_up_info = e1000_get_link_up_info_80003es2lan; + +out: + return ret_val; +} + +/** + * e1000_init_function_pointers_80003es2lan - Init ESB2 func ptrs. + * @hw: pointer to the HW structure + * + * The only function explicitly called by the api module to initialize + * all function pointers and parameters. + **/ +void +e1000_init_function_pointers_80003es2lan(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_80003es2lan"); + + hw->func.init_mac_params = e1000_init_mac_params_80003es2lan; + hw->func.init_nvm_params = e1000_init_nvm_params_80003es2lan; + hw->func.init_phy_params = e1000_init_phy_params_80003es2lan; +} + +/** + * e1000_acquire_phy_80003es2lan - Acquire rights to access PHY + * @hw: pointer to the HW structure + * + * A wrapper to acquire access rights to the correct PHY. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_acquire_phy_80003es2lan(struct e1000_hw *hw) +{ + u16 mask; + + DEBUGFUNC("e1000_acquire_phy_80003es2lan"); + + mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; + + return e1000_acquire_swfw_sync_80003es2lan(hw, mask); +} + +/** + * e1000_release_phy_80003es2lan - Release rights to access PHY + * @hw: pointer to the HW structure + * + * A wrapper to release access rights to the correct PHY. This is a + * function pointer entry point called by the api module. + **/ +STATIC void +e1000_release_phy_80003es2lan(struct e1000_hw *hw) +{ + u16 mask; + + DEBUGFUNC("e1000_release_phy_80003es2lan"); + + mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; + e1000_release_swfw_sync_80003es2lan(hw, mask); +} + +/** + * e1000_acquire_nvm_80003es2lan - Acquire rights to access NVM + * @hw: pointer to the HW structure + * + * Acquire the semaphore to access the EEPROM. This is a function + * pointer entry point called by the api module. + **/ +STATIC s32 +e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_acquire_nvm_80003es2lan"); + + ret_val = e1000_acquire_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM); + if (ret_val) + goto out; + + ret_val = e1000_acquire_nvm_generic(hw); + + if (ret_val) + e1000_release_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM); + +out: + return ret_val; +} + +/** + * e1000_release_nvm_80003es2lan - Relinquish rights to access NVM + * @hw: pointer to the HW structure + * + * Release the semaphore used to access the EEPROM. This is a + * function pointer entry point called by the api module. + **/ +STATIC void +e1000_release_nvm_80003es2lan(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_80003es2lan"); + + e1000_release_nvm_generic(hw); + e1000_release_swfw_sync_80003es2lan(hw, E1000_SWFW_EEP_SM); +} + +/** + * e1000_acquire_swfw_sync_80003es2lan - Acquire SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Acquire the SW/FW semaphore to access the PHY or NVM. The mask + * will also specify which port we're acquiring the lock for. + **/ +static s32 +e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = E1000_SUCCESS; + s32 i = 0, timeout = 200; + + DEBUGFUNC("e1000_acquire_swfw_sync_80003es2lan"); + + while (i < timeout) { + if (e1000_get_hw_semaphore_generic(hw)) { + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* Firmware currently using resource (fwmask) + * or other software thread using resource (swmask) */ + e1000_put_hw_semaphore_generic(hw); + msec_delay_irq(5); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_release_swfw_sync_80003es2lan - Release SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Release the SW/FW semaphore used to access the PHY or NVM. The mask + * will also specify which port we're releasing the lock for. + **/ +static void +e1000_release_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + + DEBUGFUNC("e1000_release_swfw_sync_80003es2lan"); + + while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS); + /* Empty */ + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + swfw_sync &= ~mask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); +} + +/** + * e1000_read_phy_reg_gg82563_80003es2lan - Read GG82563 PHY register + * @hw: pointer to the HW structure + * @offset: offset of the register to read + * @data: pointer to the data returned from the operation + * + * Read the GG82563 PHY register. This is a function pointer entry + * point called by the api module. + **/ +STATIC s32 +e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, u32 offset, + u16 *data) +{ + s32 ret_val; + u32 page_select; + u16 temp; + + DEBUGFUNC("e1000_read_phy_reg_gg82563_80003es2lan"); + + /* Select Configuration Page */ + if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) + page_select = GG82563_PHY_PAGE_SELECT; + else { + /* Use Alternative Page Select register to access + * registers 30 and 31 + */ + page_select = GG82563_PHY_PAGE_SELECT_ALT; + } + + temp = (u16)((u16)offset >> GG82563_PAGE_SHIFT); + ret_val = e1000_write_phy_reg_m88(hw, page_select, temp); + if (ret_val) + goto out; + + /* The "ready" bit in the MDIC register may be incorrectly set + * before the device has completed the "Page Select" MDI + * transaction. So we wait 200us after each MDI command... + */ + usec_delay(200); + + /* ...and verify the command was successful. */ + ret_val = e1000_read_phy_reg_m88(hw, page_select, &temp); + + if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { + ret_val = -E1000_ERR_PHY; + goto out; + } + + usec_delay(200); + + ret_val = e1000_read_phy_reg_m88(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + + usec_delay(200); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_gg82563_80003es2lan - Write GG82563 PHY register + * @hw: pointer to the HW structure + * @offset: offset of the register to read + * @data: value to write to the register + * + * Write to the GG82563 PHY register. This is a function pointer entry + * point called by the api module. + **/ +STATIC s32 +e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, u32 offset, + u16 data) +{ + s32 ret_val; + u32 page_select; + u16 temp; + + DEBUGFUNC("e1000_write_phy_reg_gg82563_80003es2lan"); + + /* Select Configuration Page */ + if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) + page_select = GG82563_PHY_PAGE_SELECT; + else { + /* Use Alternative Page Select register to access + * registers 30 and 31 + */ + page_select = GG82563_PHY_PAGE_SELECT_ALT; + } + + temp = (u16)((u16)offset >> GG82563_PAGE_SHIFT); + ret_val = e1000_write_phy_reg_m88(hw, page_select, temp); + if (ret_val) + goto out; + + + /* The "ready" bit in the MDIC register may be incorrectly set + * before the device has completed the "Page Select" MDI + * transaction. So we wait 200us after each MDI command... + */ + usec_delay(200); + + /* ...and verify the command was successful. */ + ret_val = e1000_read_phy_reg_m88(hw, page_select, &temp); + + if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { + ret_val = -E1000_ERR_PHY; + goto out; + } + + usec_delay(200); + + ret_val = e1000_write_phy_reg_m88(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + + usec_delay(200); + +out: + return ret_val; +} + +/** + * e1000_write_nvm_80003es2lan - Write to ESB2 NVM + * @hw: pointer to the HW structure + * @offset: offset of the register to read + * @words: number of words to write + * @data: buffer of data to write to the NVM + * + * Write "words" of data to the ESB2 NVM. This is a function + * pointer entry point called by the api module. + **/ +STATIC s32 +e1000_write_nvm_80003es2lan(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data) +{ + DEBUGFUNC("e1000_write_nvm_80003es2lan"); + + return e1000_write_nvm_spi(hw, offset, words, data); +} + +/** + * e1000_get_cfg_done_80003es2lan - Wait for configuration to complete + * @hw: pointer to the HW structure + * + * Wait a specific amount of time for manageability processes to complete. + * This is a function pointer entry point called by the phy module. + **/ +STATIC s32 +e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw) +{ + s32 timeout = PHY_CFG_TIMEOUT; + s32 ret_val = E1000_SUCCESS; + u32 mask = E1000_NVM_CFG_DONE_PORT_0; + + DEBUGFUNC("e1000_get_cfg_done_80003es2lan"); + + if (hw->bus.func == 1) + mask = E1000_NVM_CFG_DONE_PORT_1; + + while (timeout) { + if (E1000_READ_REG(hw, E1000_EEMNGCTL) & mask) + break; + msec_delay(1); + timeout--; + } + if (!timeout) { + DEBUGOUT("MNG configuration cycle has not completed.\n"); + ret_val = -E1000_ERR_RESET; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_80003es2lan - Force PHY speed and duplex + * @hw: pointer to the HW structure + * + * Force the speed and duplex settings onto the PHY. This is a + * function pointer entry point called by the phy module. + **/ +STATIC s32 +e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_data; + boolean_t link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_80003es2lan"); + + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + * forced whenever speed and duplex are forced. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + goto out; + + phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_AUTO; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL, phy_data); + if (ret_val) + goto out; + + DEBUGOUT1("GG82563 PSCR: %X\n", phy_data); + + ret_val = e1000_read_phy_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + goto out; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + /* Reset the phy to commit changes. */ + phy_data |= MII_CR_RESET; + + ret_val = e1000_write_phy_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + goto out; + + usec_delay(1); + + if (hw->phy.wait_for_link) { + DEBUGOUT("Waiting for forced speed/duplex link " + "on GG82563 phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + goto out; + + if (!link) { + /* We didn't get link. + * Reset the DSP and cross our fingers. + */ + ret_val = e1000_phy_reset_dsp_generic(hw); + if (ret_val) + goto out; + } + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + goto out; + } + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, &phy_data); + if (ret_val) + goto out; + + /* Resetting the phy means we need to verify the TX_CLK corresponds + * to the link speed. 10Mbps -> 2.5MHz, else 25MHz. + */ + phy_data &= ~GG82563_MSCR_TX_CLK_MASK; + if (hw->mac.forced_speed_duplex & E1000_ALL_10_SPEED) + phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5; + else + phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25; + + /* In addition, we must re-enable CRS on Tx for both half and full + * duplex. + */ + phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, phy_data); + +out: + return ret_val; +} + +/** + * e1000_get_cable_length_80003es2lan - Set approximate cable length + * @hw: pointer to the HW structure + * + * Find the approximate cable length as measured by the GG82563 PHY. + * This is a function pointer entry point called by the phy module. + **/ +STATIC s32 +e1000_get_cable_length_80003es2lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, index; + + DEBUGFUNC("e1000_get_cable_length_80003es2lan"); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE, &phy_data); + if (ret_val) + goto out; + + index = phy_data & GG82563_DSPD_CABLE_LENGTH; + phy->min_cable_length = e1000_gg82563_cable_length_table[index]; + phy->max_cable_length = e1000_gg82563_cable_length_table[index+5]; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + +out: + return ret_val; +} + +/** + * e1000_get_link_up_info_80003es2lan - Report speed and duplex + * @hw: pointer to the HW structure + * @speed: pointer to speed buffer + * @duplex: pointer to duplex buffer + * + * Retrieve the current speed and duplex configuration. + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed, u16 *duplex) +{ + s32 ret_val; + + DEBUGFUNC("e1000_get_link_up_info_80003es2lan"); + + if (hw->media_type == e1000_media_type_copper) { + ret_val = e1000_get_speed_and_duplex_copper_generic(hw, + speed, + duplex); + if (ret_val) + goto out; + if (*speed == SPEED_1000) + ret_val = e1000_cfg_kmrn_1000_80003es2lan(hw); + else + ret_val = e1000_cfg_kmrn_10_100_80003es2lan(hw, + *duplex); + } else + ret_val = e1000_get_speed_and_duplex_fiber_serdes_generic(hw, + speed, + duplex); + +out: + return ret_val; +} + +/** + * e1000_reset_hw_80003es2lan - Reset the ESB2 controller + * @hw: pointer to the HW structure + * + * Perform a global reset to the ESB2 controller. + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_reset_hw_80003es2lan(struct e1000_hw *hw) +{ + u32 ctrl, icr; + s32 ret_val; + + DEBUGFUNC("e1000_reset_hw_80003es2lan"); + + /* Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) { + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + } + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to MAC\n"); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) + /* We don't want to continue accessing MAC registers. */ + goto out; + + /* Clear any pending interrupt events. */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + icr = E1000_READ_REG(hw, E1000_ICR); + +out: + return ret_val; +} + +/** + * e1000_init_hw_80003es2lan - Initialize the ESB2 controller + * @hw: pointer to the HW structure + * + * Initialize the hw bits, LED, VFTA, MTA, link and hw counters. + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_hw_80003es2lan(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 reg_data; + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_init_hw_80003es2lan"); + + e1000_initialize_hw_bits_80003es2lan(hw); + + /* Initialize identification LED */ + ret_val = e1000_id_led_init_generic(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + goto out; + } + + /* Disabling VLAN filtering */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + e1000_clear_vfta(hw); + + /* Setup the receive address. */ + e1000_init_rx_addrs_generic(hw, mac->rar_entry_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + + /* Setup link and flow control */ + ret_val = e1000_setup_link(hw); + + /* Set the transmit descriptor write-back policy */ + reg_data = E1000_READ_REG(hw, E1000_TXDCTL); + reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, E1000_TXDCTL, reg_data); + + /* ...for both queues. */ + reg_data = E1000_READ_REG(hw, E1000_TXDCTL1); + reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, E1000_TXDCTL1, reg_data); + + /* Enable retransmit on late collisions */ + reg_data = E1000_READ_REG(hw, E1000_TCTL); + reg_data |= E1000_TCTL_RTLC; + E1000_WRITE_REG(hw, E1000_TCTL, reg_data); + + /* Configure Gigabit Carry Extend Padding */ + reg_data = E1000_READ_REG(hw, E1000_TCTL_EXT); + reg_data &= ~E1000_TCTL_EXT_GCEX_MASK; + reg_data |= DEFAULT_TCTL_EXT_GCEX_80003ES2LAN; + E1000_WRITE_REG(hw, E1000_TCTL_EXT, reg_data); + + /* Configure Transmit Inter-Packet Gap */ + reg_data = E1000_READ_REG(hw, E1000_TIPG); + reg_data &= ~E1000_TIPG_IPGT_MASK; + reg_data |= DEFAULT_TIPG_IPGT_1000_80003ES2LAN; + E1000_WRITE_REG(hw, E1000_TIPG, reg_data); + + reg_data = E1000_READ_REG_ARRAY(hw, E1000_FFLT, 0x0001); + reg_data &= ~0x00100000; + E1000_WRITE_REG_ARRAY(hw, E1000_FFLT, 0x0001, reg_data); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_80003es2lan(hw); + +out: + return ret_val; +} + +/** + * e1000_initialize_hw_bits_80003es2lan - Init hw bits of ESB2 + * @hw: pointer to the HW structure + * + * Initializes required hardware-dependent bits needed for normal operation. + **/ +static void +e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_initialize_hw_bits_80003es2lan"); + + if (hw->mac.disable_hw_init_bits) + goto out; + + /* Transmit Descriptor Control 0 */ + reg = E1000_READ_REG(hw, E1000_TXDCTL); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_TXDCTL, reg); + + /* Transmit Descriptor Control 1 */ + reg = E1000_READ_REG(hw, E1000_TXDCTL1); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_TXDCTL1, reg); + + /* Transmit Arbitration Control 0 */ + reg = E1000_READ_REG(hw, E1000_TARC0); + reg &= ~(0xF << 27); /* 30:27 */ + if (hw->media_type != e1000_media_type_copper) + reg &= ~(1 << 20); + E1000_WRITE_REG(hw, E1000_TARC0, reg); + + /* Transmit Arbitration Control 1 */ + reg = E1000_READ_REG(hw, E1000_TARC1); + if (E1000_READ_REG(hw, E1000_TCTL) & E1000_TCTL_MULR) + reg &= ~(1 << 28); + else + reg |= (1 << 28); + E1000_WRITE_REG(hw, E1000_TARC1, reg); + +out: + return; +} + +/** + * e1000_copper_link_setup_gg82563_80003es2lan - Configure GG82563 Link + * @hw: pointer to the HW structure + * + * Setup some GG82563 PHY registers for obtaining link + **/ +static s32 +e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u32 ctrl_ext; + u16 data; + + DEBUGFUNC("e1000_copper_link_setup_gg82563_80003es2lan"); + + if (!phy->reset_disable) { + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, + &data); + if (ret_val) + goto out; + + data |= GG82563_MSCR_ASSERT_CRS_ON_TX; + /* Use 25MHz for both link down and 1000Base-T for Tx clock. */ + data |= GG82563_MSCR_TX_CLK_1000MBPS_25; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, + data); + if (ret_val) + goto out; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL, &data); + if (ret_val) + goto out; + + data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK; + + switch (phy->mdix) { + case 1: + data |= GG82563_PSCR_CROSSOVER_MODE_MDI; + break; + case 2: + data |= GG82563_PSCR_CROSSOVER_MODE_MDIX; + break; + case 0: + default: + data |= GG82563_PSCR_CROSSOVER_MODE_AUTO; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE; + if (phy->disable_polarity_correction == TRUE) + data |= GG82563_PSCR_POLARITY_REVERSAL_DISABLE; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL, data); + if (ret_val) + goto out; + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_commit(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + goto out; + } + + } + + /* Bypass RX and TX FIFO's */ + ret_val = e1000_write_kmrn_reg(hw, + E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL, + E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS | + E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, &data); + if (ret_val) + goto out; + + data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, data); + if (ret_val) + goto out; + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext &= ~(E1000_CTRL_EXT_LINK_MODE_MASK); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, &data); + if (ret_val) + goto out; + + /* Do not init these registers when the HW is in IAMT mode, since the + * firmware will have already initialized them. We only initialize + * them if the HW is not in IAMT mode. + */ + if (e1000_check_mng_mode(hw) == FALSE) { + /* Enable Electrical Idle on the PHY */ + data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_PWR_MGMT_CTRL, + data); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_KMRN_MODE_CTRL, + &data); + if (ret_val) + goto out; + + data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_KMRN_MODE_CTRL, + data); + + if (ret_val) + goto out; + } + + /* Workaround: Disable padding in Kumeran interface in the MAC + * and in the PHY to avoid CRC errors. + */ + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_INBAND_CTRL, &data); + if (ret_val) + goto out; + + data |= GG82563_ICR_DIS_PADDING; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_INBAND_CTRL, data); + if (ret_val) + goto out; + +out: + return ret_val; +} + +/** + * e1000_setup_copper_link_80003es2lan - Setup Copper Link for ESB2 + * @hw: pointer to the HW structure + * + * Essentially a wrapper for setting up all things "copper" related. + * This is a function pointer entry point called by the mac module. + **/ +STATIC s32 +e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + u16 reg_data; + + DEBUGFUNC("e1000_setup_copper_link_80003es2lan"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Set the mac to wait the maximum time between each + * iteration and increase the max iterations when + * polling the phy; this fixes erroneous timeouts at 10Mbps. */ + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF); + if (ret_val) + goto out; + ret_val = e1000_read_kmrn_reg(hw, GG82563_REG(0x34, 9), ®_data); + if (ret_val) + goto out; + reg_data |= 0x3F; + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data); + if (ret_val) + goto out; + ret_val = e1000_read_kmrn_reg(hw, + E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, + ®_data); + if (ret_val) + goto out; + reg_data |= E1000_KMRNCTRLSTA_INB_CTRL_DIS_PADDING; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KMRNCTRLSTA_OFFSET_INB_CTRL, + reg_data); + if (ret_val) + goto out; + + ret_val = e1000_copper_link_setup_gg82563_80003es2lan(hw); + if (ret_val) + goto out; + + ret_val = e1000_setup_copper_link_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_cfg_kmrn_10_100_80003es2lan - Apply "quirks" for 10/100 operation + * @hw: pointer to the HW structure + * @duplex: current duplex setting + * + * Configure the KMRN interface by applying last minute quirks for + * 10/100 operation. + **/ +static s32 +e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex) +{ + s32 ret_val = E1000_SUCCESS; + u32 tipg; + u32 i = 0; + u16 reg_data, reg_data2; + + DEBUGFUNC("e1000_configure_kmrn_for_10_100"); + + reg_data = E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, + reg_data); + if (ret_val) + goto out; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, E1000_TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_TIPG_IPGT_10_100_80003ES2LAN; + E1000_WRITE_REG(hw, E1000_TIPG, tipg); + + + do { + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, + ®_data); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, + ®_data2); + if (ret_val) + goto out; + i++; + } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY)); + + if (duplex == HALF_DUPLEX) + reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER; + else + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + +out: + return ret_val; +} + +/** + * e1000_cfg_kmrn_1000_80003es2lan - Apply "quirks" for gigabit operation + * @hw: pointer to the HW structure + * + * Configure the KMRN interface by applying last minute quirks for + * gigabit operation. + **/ +static s32 +e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 reg_data, reg_data2; + u32 tipg; + u32 i = 0; + + DEBUGFUNC("e1000_configure_kmrn_for_1000"); + + reg_data = E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KMRNCTRLSTA_OFFSET_HD_CTRL, + reg_data); + if (ret_val) + goto out; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, E1000_TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_TIPG_IPGT_1000_80003ES2LAN; + E1000_WRITE_REG(hw, E1000_TIPG, tipg); + + + do { + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, + ®_data); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, + ®_data2); + if (ret_val) + goto out; + i++; + } while ((reg_data != reg_data2) && (i < GG82563_MAX_KMRN_RETRY)); + + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + +out: + return ret_val; +} + +/** + * e1000_clear_hw_cntrs_80003es2lan - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +STATIC void +e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_80003es2lan"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_PRC64); + temp = E1000_READ_REG(hw, E1000_PRC127); + temp = E1000_READ_REG(hw, E1000_PRC255); + temp = E1000_READ_REG(hw, E1000_PRC511); + temp = E1000_READ_REG(hw, E1000_PRC1023); + temp = E1000_READ_REG(hw, E1000_PRC1522); + temp = E1000_READ_REG(hw, E1000_PTC64); + temp = E1000_READ_REG(hw, E1000_PTC127); + temp = E1000_READ_REG(hw, E1000_PTC255); + temp = E1000_READ_REG(hw, E1000_PTC511); + temp = E1000_READ_REG(hw, E1000_PTC1023); + temp = E1000_READ_REG(hw, E1000_PTC1522); + + temp = E1000_READ_REG(hw, E1000_ALGNERRC); + temp = E1000_READ_REG(hw, E1000_RXERRC); + temp = E1000_READ_REG(hw, E1000_TNCRS); + temp = E1000_READ_REG(hw, E1000_CEXTERR); + temp = E1000_READ_REG(hw, E1000_TSCTC); + temp = E1000_READ_REG(hw, E1000_TSCTFC); + + temp = E1000_READ_REG(hw, E1000_MGTPRC); + temp = E1000_READ_REG(hw, E1000_MGTPDC); + temp = E1000_READ_REG(hw, E1000_MGTPTC); + + temp = E1000_READ_REG(hw, E1000_IAC); + temp = E1000_READ_REG(hw, E1000_ICRXOC); + + temp = E1000_READ_REG(hw, E1000_ICRXPTC); + temp = E1000_READ_REG(hw, E1000_ICRXATC); + temp = E1000_READ_REG(hw, E1000_ICTXPTC); + temp = E1000_READ_REG(hw, E1000_ICTXATC); + temp = E1000_READ_REG(hw, E1000_ICTXQEC); + temp = E1000_READ_REG(hw, E1000_ICTXQMTC); + temp = E1000_READ_REG(hw, E1000_ICRXDMTC); +} diff --git a/bsd_eth_drivers/if_em/e1000_80003es2lan.h b/bsd_eth_drivers/if_em/e1000_80003es2lan.h new file mode 100644 index 0000000..8850741 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_80003es2lan.h @@ -0,0 +1,97 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_80003es2lan.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_80003ES2LAN_H_ +#define _E1000_80003ES2LAN_H_ + +#define E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL 0x00 +#define E1000_KMRNCTRLSTA_OFFSET_INB_CTRL 0x02 +#define E1000_KMRNCTRLSTA_OFFSET_HD_CTRL 0x10 + +#define E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS 0x0008 +#define E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS 0x0800 +#define E1000_KMRNCTRLSTA_INB_CTRL_DIS_PADDING 0x0010 + +#define E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT 0x0004 +#define E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT 0x0000 + +#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ +#define DEFAULT_TCTL_EXT_GCEX_80003ES2LAN 0x00010000 + +#define DEFAULT_TIPG_IPGT_1000_80003ES2LAN 0x8 +#define DEFAULT_TIPG_IPGT_10_100_80003ES2LAN 0x9 + +/* GG82563 PHY Specific Status Register (Page 0, Register 16 */ +#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Reversal Disabled */ +#define GG82563_PSCR_CROSSOVER_MODE_MASK 0x0060 +#define GG82563_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI */ +#define GG82563_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX */ +#define GG82563_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Auto crossover */ + +/* PHY Specific Control Register 2 (Page 0, Register 26) */ +#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 + /* 1=Reverse Auto-Negotiation */ + +/* MAC Specific Control Register (Page 2, Register 21) */ +/* Tx clock speed for Link Down and 1000BASE-T for the following speeds */ +#define GG82563_MSCR_TX_CLK_MASK 0x0007 +#define GG82563_MSCR_TX_CLK_10MBPS_2_5 0x0004 +#define GG82563_MSCR_TX_CLK_100MBPS_25 0x0005 +#define GG82563_MSCR_TX_CLK_1000MBPS_2_5 0x0006 +#define GG82563_MSCR_TX_CLK_1000MBPS_25 0x0007 + +#define GG82563_MSCR_ASSERT_CRS_ON_TX 0x0010 /* 1=Assert */ + +/* DSP Distance Register (Page 5, Register 26) */ +#define GG82563_DSPD_CABLE_LENGTH 0x0007 /* 0 = <50M; + 1 = 50-80M; + 2 = 80-110M; + 3 = 110-140M; + 4 = >140M */ + +/* Kumeran Mode Control Register (Page 193, Register 16) */ +#define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800 + +/* Max number of times Kumeran read/write should be validated */ +#define GG82563_MAX_KMRN_RETRY 0x5 + +/* Power Management Control Register (Page 193, Register 20) */ +#define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE 0x0001 + /* 1=Enable SERDES Electrical Idle */ + +/* In-Band Control Register (Page 194, Register 18) */ +#define GG82563_ICR_DIS_PADDING 0x0010 /* Disable Padding */ + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_82540.c b/bsd_eth_drivers/if_em/e1000_82540.c new file mode 100644 index 0000000..cca0dd3 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82540.c @@ -0,0 +1,677 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82540.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +/* e1000_82540 + * e1000_82545 + * e1000_82546 + * e1000_82545_rev_3 + * e1000_82546_rev_3 + */ + +#include "e1000_api.h" + +void e1000_init_function_pointers_82540(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_82540(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_82540(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_82540(struct e1000_hw *hw); +static s32 e1000_adjust_serdes_amplitude_82540(struct e1000_hw *hw); +STATIC void e1000_clear_hw_cntrs_82540(struct e1000_hw *hw); +STATIC s32 e1000_init_hw_82540(struct e1000_hw *hw); +STATIC s32 e1000_reset_hw_82540(struct e1000_hw *hw); +static s32 e1000_set_phy_mode_82540(struct e1000_hw *hw); +static s32 e1000_set_vco_speed_82540(struct e1000_hw *hw); +STATIC s32 e1000_setup_copper_link_82540(struct e1000_hw *hw); +STATIC s32 e1000_setup_fiber_serdes_link_82540(struct e1000_hw *hw); + +/** + * e1000_init_phy_params_82540 - Init PHY func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_phy_params_82540(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 10000; + phy->type = e1000_phy_m88; + + /* Function Pointers */ + func->check_polarity = e1000_check_polarity_m88; + func->commit_phy = e1000_phy_sw_reset_generic; + func->force_speed_duplex = e1000_phy_force_speed_duplex_m88; + func->get_cable_length = e1000_get_cable_length_m88; + func->get_cfg_done = e1000_get_cfg_done_generic; + func->read_phy_reg = e1000_read_phy_reg_m88; + func->reset_phy = e1000_phy_hw_reset_generic; + func->write_phy_reg = e1000_write_phy_reg_m88; + func->get_phy_info = e1000_get_phy_info_m88; + + ret_val = e1000_get_phy_id(hw); + if (ret_val) + goto out; + + /* Verify phy id */ + switch (hw->mac.type) { + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + if (phy->id == M88E1011_I_PHY_ID) + break; + /* Fall Through */ + default: + ret_val = -E1000_ERR_PHY; + goto out; + break; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_82540 - Init NVM func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_nvm_params_82540(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + + DEBUGFUNC("e1000_init_nvm_params_82540"); + + nvm->type = e1000_nvm_eeprom_microwire; + nvm->delay_usec = 50; + nvm->opcode_bits = 3; + switch (nvm->override) { + case e1000_nvm_override_microwire_large: + nvm->address_bits = 8; + nvm->word_size = 256; + break; + case e1000_nvm_override_microwire_small: + nvm->address_bits = 6; + nvm->word_size = 64; + break; + default: + nvm->address_bits = eecd & E1000_EECD_SIZE ? 8 : 6; + nvm->word_size = eecd & E1000_EECD_SIZE ? 256 : 64; + break; + } + + /* Function Pointers */ + func->acquire_nvm = e1000_acquire_nvm_generic; + func->read_nvm = e1000_read_nvm_microwire; + func->release_nvm = e1000_release_nvm_generic; + func->update_nvm = e1000_update_nvm_checksum_generic; + func->valid_led_default = e1000_valid_led_default_generic; + func->validate_nvm = e1000_validate_nvm_checksum_generic; + func->write_nvm = e1000_write_nvm_microwire; + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_82540 - Init MAC func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_mac_params_82540(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_mac_params_82540"); + + /* Set media type */ + switch (hw->device_id) { + case E1000_DEV_ID_82545EM_FIBER: + case E1000_DEV_ID_82545GM_FIBER: + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546GB_FIBER: + hw->media_type = e1000_media_type_fiber; + break; + case E1000_DEV_ID_82545GM_SERDES: + case E1000_DEV_ID_82546GB_SERDES: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + hw->media_type = e1000_media_type_copper; + break; + } + + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES; + + /* Function pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_pci_generic; + /* reset */ + func->reset_hw = e1000_reset_hw_82540; + /* hw initialization */ + func->init_hw = e1000_init_hw_82540; + /* link setup */ + func->setup_link = e1000_setup_link_generic; + /* physical interface setup */ + func->setup_physical_interface = + (hw->media_type == e1000_media_type_copper) + ? e1000_setup_copper_link_82540 + : e1000_setup_fiber_serdes_link_82540; + /* check for link */ + switch (hw->media_type) { + case e1000_media_type_copper: + func->check_for_link = e1000_check_for_copper_link_generic; + break; + case e1000_media_type_fiber: + func->check_for_link = e1000_check_for_fiber_link_generic; + break; + case e1000_media_type_internal_serdes: + func->check_for_link = e1000_check_for_serdes_link_generic; + break; + default: + ret_val = -E1000_ERR_CONFIG; + goto out; + break; + } + /* link info */ + func->get_link_up_info = + (hw->media_type == e1000_media_type_copper) + ? e1000_get_speed_and_duplex_copper_generic + : e1000_get_speed_and_duplex_fiber_serdes_generic; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_generic; + /* writing VFTA */ + func->write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + func->clear_vfta = e1000_clear_vfta_generic; + /* setting MTA */ + func->mta_set = e1000_mta_set_generic; + /* setup LED */ + func->setup_led = e1000_setup_led_generic; + /* cleanup LED */ + func->cleanup_led = e1000_cleanup_led_generic; + /* turn on/off LED */ + func->led_on = e1000_led_on_generic; + func->led_off = e1000_led_off_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_82540; + +out: + return ret_val; +} + +/** + * e1000_init_function_pointers_82540 - Init func ptrs. + * @hw: pointer to the HW structure + * + * The only function explicitly called by the api module to initialize + * all function pointers and parameters. + **/ +void +e1000_init_function_pointers_82540(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82540"); + + hw->func.init_mac_params = e1000_init_mac_params_82540; + hw->func.init_nvm_params = e1000_init_nvm_params_82540; + hw->func.init_phy_params = e1000_init_phy_params_82540; +} + +/** + * e1000_reset_hw_82540 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_reset_hw_82540(struct e1000_hw *hw) +{ + u32 ctrl, icr, manc; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_reset_hw_82540"); + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + /* Delay to allow any outstanding PCI transactions to complete + * before resetting the device. + */ + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to 82540/82545/82546 MAC\n"); + switch (hw->mac.type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + E1000_WRITE_REG(hw, E1000_CTRL_DUP, ctrl | E1000_CTRL_RST); + break; + default: + /* These controllers can't ack the 64-bit write when + * issuing the reset, so we use IO-mapping as a + * workaround to issue the reset. + */ + E1000_WRITE_REG_IO(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + break; + } + + /* Wait for EEPROM reload */ + msec_delay(5); + + /* Disable HW ARPs on ASF enabled adapters */ + manc = E1000_READ_REG(hw, E1000_MANC); + manc &= ~E1000_MANC_ARP_EN; + E1000_WRITE_REG(hw, E1000_MANC, manc); + + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + icr = E1000_READ_REG(hw, E1000_ICR); + + return ret_val; +} + +/** + * e1000_init_hw_82540 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_hw_82540(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 txdctl, ctrl_ext; + s32 ret_val = E1000_SUCCESS; + u16 i; + + DEBUGFUNC("e1000_init_hw_82540"); + + /* Initialize identification LED */ + ret_val = e1000_id_led_init_generic(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + goto out; + } + + /* Disabling VLAN filtering */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + if (mac->type < e1000_82545_rev_3) { + E1000_WRITE_REG(hw, E1000_VET, 0); + } + e1000_clear_vfta(hw); + + /* Setup the receive address. */ + e1000_init_rx_addrs_generic(hw, mac->rar_entry_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) { + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + /* Avoid back to back register writes by adding the register + * read (flush). This is to protect against some strange + * bridge configurations that may issue Memory Write Block + * (MWB) to our register space. The *_rev_3 hardware at + * least doesn't respond correctly to every other dword in an + * MWB to our register space. + */ + E1000_WRITE_FLUSH(hw); + } + + if (mac->type < e1000_82545_rev_3) + e1000_pcix_mmrbc_workaround_generic(hw); + + /* Setup link and flow control */ + ret_val = e1000_setup_link(hw); + + txdctl = E1000_READ_REG(hw, E1000_TXDCTL); + txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, E1000_TXDCTL, txdctl); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82540(hw); + + if ((hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER) || + (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3)) { + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + /* Relaxed ordering must be disabled to avoid a parity + * error crash in a PCI slot. */ + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + } + +out: + return ret_val; +} + +/** + * e1000_setup_copper_link_82540 - Configure copper link settings + * @hw: pointer to the HW structure + * + * Calls the appropriate function to configure the link for auto-neg or forced + * speed and duplex. Then we check for link, once link is established calls + * to configure collision distance and flow control are called. If link is + * not established, we return -E1000_ERR_PHY (-2). This is a function + * pointer entry point called by the api module. + **/ +STATIC s32 +e1000_setup_copper_link_82540(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_setup_copper_link_82540"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + ret_val = e1000_set_phy_mode_82540(hw); + if (ret_val) + goto out; + + if (hw->mac.type == e1000_82545_rev_3 || + hw->mac.type == e1000_82546_rev_3) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &data); + if (ret_val) + goto out; + data |= 0x00000008; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, data); + if (ret_val) + goto out; + } + + ret_val = e1000_copper_link_setup_m88(hw); + if (ret_val) + goto out; + + ret_val = e1000_setup_copper_link_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_setup_fiber_serdes_link_82540 - Setup link for fiber/serdes + * @hw: pointer to the HW structure + * + * Set the output amplitude to the value in the EEPROM and adjust the VCO + * speed to improve Bit Error Rate (BER) performance. Configures collision + * distance and flow control for fiber and serdes links. Upon successful + * setup, poll for link. This is a function pointer entry point called by + * the api module. + **/ +STATIC s32 +e1000_setup_fiber_serdes_link_82540(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_setup_fiber_serdes_link_82540"); + + switch (mac->type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + if (hw->media_type == e1000_media_type_internal_serdes) { + /* If we're on serdes media, adjust the output + * amplitude to value set in the EEPROM. + */ + ret_val = e1000_adjust_serdes_amplitude_82540(hw); + if (ret_val) + goto out; + } + /* Adjust VCO speed to improve BER performance */ + ret_val = e1000_set_vco_speed_82540(hw); + if (ret_val) + goto out; + default: + break; + } + + ret_val = e1000_setup_fiber_serdes_link_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_adjust_serdes_amplitude_82540 - Adjust amplitude based on EEPROM + * @hw: pointer to the HW structure + * + * Adjust the SERDES ouput amplitude based on the EEPROM settings. + **/ +static s32 +e1000_adjust_serdes_amplitude_82540(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 nvm_data; + + DEBUGFUNC("e1000_adjust_serdes_amplitude_82540"); + + ret_val = e1000_read_nvm(hw, NVM_SERDES_AMPLITUDE, 1, &nvm_data); + if (ret_val) { + goto out; + } + + if (nvm_data != NVM_RESERVED_WORD) { + /* Adjust serdes output amplitude only. */ + nvm_data &= NVM_SERDES_AMPLITUDE_MASK; + ret_val = e1000_write_phy_reg(hw, + M88E1000_PHY_EXT_CTRL, + nvm_data); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_set_vco_speed_82540 - Set VCO speed for better performance + * @hw: pointer to the HW structure + * + * Set the VCO speed to improve Bit Error Rate (BER) performance. + **/ +static s32 +e1000_set_vco_speed_82540(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 default_page = 0; + u16 phy_data; + + DEBUGFUNC("e1000_set_vco_speed_82540"); + + /* Set PHY register 30, page 5, bit 8 to 0 */ + + ret_val = e1000_read_phy_reg(hw, + M88E1000_PHY_PAGE_SELECT, + &default_page); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if (ret_val) + goto out; + + phy_data &= ~M88E1000_PHY_VCO_REG_BIT8; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if (ret_val) + goto out; + + /* Set PHY register 30, page 4, bit 11 to 1 */ + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if (ret_val) + goto out; + + phy_data |= M88E1000_PHY_VCO_REG_BIT11; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, + default_page); + +out: + return ret_val; +} + +/** + * e1000_set_phy_mode_82540 - Set PHY to class A mode + * @hw: pointer to the HW structure + * + * Sets the PHY to class A mode and assumes the following operations will + * follow to enable the new class mode: + * 1. Do a PHY soft reset. + * 2. Restart auto-negotiation or force link. + **/ +static s32 +e1000_set_phy_mode_82540(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 nvm_data; + + DEBUGFUNC("e1000_set_phy_mode_82540"); + + if (hw->mac.type != e1000_82545_rev_3) + goto out; + + ret_val = e1000_read_nvm(hw, NVM_PHY_CLASS_WORD, 1, &nvm_data); + if (ret_val) { + ret_val = -E1000_ERR_PHY; + goto out; + } + + if ((nvm_data != NVM_RESERVED_WORD) && (nvm_data & NVM_PHY_CLASS_A)) { + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, + 0x000B); + if (ret_val) { + ret_val = -E1000_ERR_PHY; + goto out; + } + ret_val = e1000_write_phy_reg(hw, + M88E1000_PHY_GEN_CONTROL, + 0x8104); + if (ret_val) { + ret_val = -E1000_ERR_PHY; + goto out; + } + + phy->reset_disable = FALSE; + } + +out: + return ret_val; +} + +/** + * e1000_clear_hw_cntrs_82540 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +STATIC void +e1000_clear_hw_cntrs_82540(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_82540"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_PRC64); + temp = E1000_READ_REG(hw, E1000_PRC127); + temp = E1000_READ_REG(hw, E1000_PRC255); + temp = E1000_READ_REG(hw, E1000_PRC511); + temp = E1000_READ_REG(hw, E1000_PRC1023); + temp = E1000_READ_REG(hw, E1000_PRC1522); + temp = E1000_READ_REG(hw, E1000_PTC64); + temp = E1000_READ_REG(hw, E1000_PTC127); + temp = E1000_READ_REG(hw, E1000_PTC255); + temp = E1000_READ_REG(hw, E1000_PTC511); + temp = E1000_READ_REG(hw, E1000_PTC1023); + temp = E1000_READ_REG(hw, E1000_PTC1522); + + temp = E1000_READ_REG(hw, E1000_ALGNERRC); + temp = E1000_READ_REG(hw, E1000_RXERRC); + temp = E1000_READ_REG(hw, E1000_TNCRS); + temp = E1000_READ_REG(hw, E1000_CEXTERR); + temp = E1000_READ_REG(hw, E1000_TSCTC); + temp = E1000_READ_REG(hw, E1000_TSCTFC); + + temp = E1000_READ_REG(hw, E1000_MGTPRC); + temp = E1000_READ_REG(hw, E1000_MGTPDC); + temp = E1000_READ_REG(hw, E1000_MGTPTC); +} + diff --git a/bsd_eth_drivers/if_em/e1000_82541.c b/bsd_eth_drivers/if_em/e1000_82541.c new file mode 100644 index 0000000..2e7e1e2 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82541.c @@ -0,0 +1,1313 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82541.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +/* e1000_82541 + * e1000_82547 + * e1000_82541_rev_2 + * e1000_82547_rev_2 + */ + +#include "e1000_api.h" +#include "e1000_82541.h" + +void e1000_init_function_pointers_82541(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_82541(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_82541(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_82541(struct e1000_hw *hw); +STATIC s32 e1000_reset_hw_82541(struct e1000_hw *hw); +STATIC s32 e1000_init_hw_82541(struct e1000_hw *hw); +STATIC s32 e1000_get_link_up_info_82541(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +STATIC s32 e1000_phy_hw_reset_82541(struct e1000_hw *hw); +STATIC s32 e1000_setup_copper_link_82541(struct e1000_hw *hw); +STATIC s32 e1000_check_for_link_82541(struct e1000_hw *hw); +STATIC s32 e1000_get_cable_length_igp_82541(struct e1000_hw *hw); +STATIC s32 e1000_set_d3_lplu_state_82541(struct e1000_hw *hw, + boolean_t active); +STATIC s32 e1000_setup_led_82541(struct e1000_hw *hw); +STATIC s32 e1000_cleanup_led_82541(struct e1000_hw *hw); +STATIC void e1000_clear_hw_cntrs_82541(struct e1000_hw *hw); +static s32 e1000_config_dsp_after_link_change_82541(struct e1000_hw *hw, + boolean_t link_up); +static s32 e1000_phy_init_script_82541(struct e1000_hw *hw); + +static const +u16 e1000_igp_cable_length_table[] = + { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25, + 25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120}; +#define IGP01E1000_AGC_LENGTH_TABLE_SIZE \ + (sizeof(e1000_igp_cable_length_table) / \ + sizeof(e1000_igp_cable_length_table[0])) + +struct e1000_dev_spec_82541 { + e1000_dsp_config dsp_config; + e1000_ffe_config ffe_config; + u16 spd_default; + boolean_t phy_init_script; +}; + +/** + * e1000_init_phy_params_82541 - Init PHY func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_phy_params_82541(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_phy_params_82541"); + + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 10000; + phy->type = e1000_phy_igp; + + /* Function Pointers */ + func->check_polarity = e1000_check_polarity_igp; + func->force_speed_duplex = e1000_phy_force_speed_duplex_igp; + func->get_cable_length = e1000_get_cable_length_igp_82541; + func->get_cfg_done = e1000_get_cfg_done_generic; + func->get_phy_info = e1000_get_phy_info_igp; + func->read_phy_reg = e1000_read_phy_reg_igp; + func->reset_phy = e1000_phy_hw_reset_82541; + func->set_d3_lplu_state = e1000_set_d3_lplu_state_82541; + func->write_phy_reg = e1000_write_phy_reg_igp; + + ret_val = e1000_get_phy_id(hw); + if (ret_val) + goto out; + + /* Verify phy id */ + if (phy->id != IGP01E1000_I_PHY_ID) { + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_82541 - Init NVM func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_nvm_params_82541(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u16 size; + + DEBUGFUNC("e1000_init_nvm_params_82541"); + + switch (nvm->override) { + case e1000_nvm_override_spi_large: + nvm->type = e1000_nvm_eeprom_spi; + eecd |= E1000_EECD_ADDR_BITS; + break; + case e1000_nvm_override_spi_small: + nvm->type = e1000_nvm_eeprom_spi; + eecd &= ~E1000_EECD_ADDR_BITS; + break; + case e1000_nvm_override_microwire_large: + nvm->type = e1000_nvm_eeprom_microwire; + eecd |= E1000_EECD_SIZE; + break; + case e1000_nvm_override_microwire_small: + nvm->type = e1000_nvm_eeprom_microwire; + eecd &= ~E1000_EECD_SIZE; + break; + default: + nvm->type = eecd & E1000_EECD_TYPE + ? e1000_nvm_eeprom_spi + : e1000_nvm_eeprom_microwire; + break; + } + + if (nvm->type == e1000_nvm_eeprom_spi) { + nvm->address_bits = (eecd & E1000_EECD_ADDR_BITS) + ? 16 : 8; + nvm->delay_usec = 1; + nvm->opcode_bits = 8; + nvm->page_size = (eecd & E1000_EECD_ADDR_BITS) + ? 32 : 8; + + /* Function Pointers */ + func->acquire_nvm = e1000_acquire_nvm_generic; + func->read_nvm = e1000_read_nvm_spi; + func->release_nvm = e1000_release_nvm_generic; + func->update_nvm = e1000_update_nvm_checksum_generic; + func->valid_led_default = e1000_valid_led_default_generic; + func->validate_nvm = e1000_validate_nvm_checksum_generic; + func->write_nvm = e1000_write_nvm_spi; + + /* nvm->word_size must be discovered after the pointers + * are set so we can verify the size from the nvm image + * itself. Temporarily set it to a dummy value so the + * read will work. + */ + nvm->word_size = 64; + ret_val = e1000_read_nvm(hw, NVM_CFG, 1, &size); + if (ret_val) + goto out; + size = (size & NVM_SIZE_MASK) >> NVM_SIZE_SHIFT; + /* if size != 0, it can be added to a constant and become + * the left-shift value to set the word_size. Otherwise, + * word_size stays at 64. + */ + if (size) { + size += NVM_WORD_SIZE_BASE_SHIFT_82541; + nvm->word_size = 1 << size; + } + } else { + nvm->address_bits = (eecd & E1000_EECD_ADDR_BITS) + ? 8 : 6; + nvm->delay_usec = 50; + nvm->opcode_bits = 3; + nvm->word_size = (eecd & E1000_EECD_ADDR_BITS) + ? 256 : 64; + + /* Function Pointers */ + func->acquire_nvm = e1000_acquire_nvm_generic; + func->read_nvm = e1000_read_nvm_microwire; + func->release_nvm = e1000_release_nvm_generic; + func->update_nvm = e1000_update_nvm_checksum_generic; + func->valid_led_default = e1000_valid_led_default_generic; + func->validate_nvm = e1000_validate_nvm_checksum_generic; + func->write_nvm = e1000_write_nvm_microwire; + } + +out: + return ret_val; +} + +/** + * e1000_init_mac_params_82541 - Init MAC func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_mac_params_82541(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val; + + DEBUGFUNC("e1000_init_mac_params_82541"); + + /* Set media type */ + hw->media_type = e1000_media_type_copper; + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES; + /* Set if part includes ASF firmware */ + mac->asf_firmware_present = TRUE; + + /* Function Pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_pci_generic; + /* reset */ + func->reset_hw = e1000_reset_hw_82541; + /* hw initialization */ + func->init_hw = e1000_init_hw_82541; + /* link setup */ + func->setup_link = e1000_setup_link_generic; + /* physical interface link setup */ + func->setup_physical_interface = e1000_setup_copper_link_82541; + /* check for link */ + func->check_for_link = e1000_check_for_link_82541; + /* link info */ + func->get_link_up_info = e1000_get_link_up_info_82541; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_generic; + /* writing VFTA */ + func->write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + func->clear_vfta = e1000_clear_vfta_generic; + /* setting MTA */ + func->mta_set = e1000_mta_set_generic; + /* setup LED */ + func->setup_led = e1000_setup_led_82541; + /* cleanup LED */ + func->cleanup_led = e1000_cleanup_led_82541; + /* turn on/off LED */ + func->led_on = e1000_led_on_generic; + func->led_off = e1000_led_off_generic; + /* remove device */ + func->remove_device = e1000_remove_device_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_82541; + + hw->dev_spec_size = sizeof(struct e1000_dev_spec_82541); + + /* Device-specific structure allocation */ + ret_val = e1000_alloc_zeroed_dev_spec_struct(hw, hw->dev_spec_size); + + return ret_val; +} + +/** + * e1000_init_function_pointers_82541 - Init func ptrs. + * @hw: pointer to the HW structure + * + * The only function explicitly called by the api module to initialize + * all function pointers and parameters. + **/ +void +e1000_init_function_pointers_82541(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82541"); + + hw->func.init_mac_params = e1000_init_mac_params_82541; + hw->func.init_nvm_params = e1000_init_nvm_params_82541; + hw->func.init_phy_params = e1000_init_phy_params_82541; +} + +/** + * e1000_reset_hw_82541 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_reset_hw_82541(struct e1000_hw *hw) +{ + u32 ledctl, ctrl, icr, manc; + + DEBUGFUNC("e1000_reset_hw_82541"); + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + /* Delay to allow any outstanding PCI transactions to complete + * before resetting the device. + */ + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Must reset the Phy before resetting the MAC */ + if ((hw->mac.type == e1000_82541) || (hw->mac.type == e1000_82547)) { + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl | E1000_CTRL_PHY_RST)); + msec_delay(5); + } + + DEBUGOUT("Issuing a global reset to 82541/82547 MAC\n"); + switch (hw->mac.type) { + case e1000_82541: + case e1000_82541_rev_2: + /* These controllers can't ack the 64-bit write when + * issuing the reset, so we use IO-mapping as a + * workaround to issue the reset. + */ + E1000_WRITE_REG_IO(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + break; + default: + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + break; + } + + /* Wait for NVM reload */ + msec_delay(20); + + /* Disable HW ARPs on ASF enabled adapters */ + manc = E1000_READ_REG(hw, E1000_MANC); + manc &= ~E1000_MANC_ARP_EN; + E1000_WRITE_REG(hw, E1000_MANC, manc); + + if ((hw->mac.type == e1000_82541) || (hw->mac.type == e1000_82547)) { + e1000_phy_init_script_82541(hw); + + /* Configure activity LED after Phy reset */ + ledctl = E1000_READ_REG(hw, E1000_LEDCTL); + ledctl &= IGP_ACTIVITY_LED_MASK; + ledctl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl); + } + + /* Once again, mask the interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF); + + /* Clear any pending interrupt events. */ + icr = E1000_READ_REG(hw, E1000_ICR); + + return E1000_SUCCESS; +} + +/** + * e1000_init_hw_82541 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_hw_82541(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 i, txdctl; + s32 ret_val; + + DEBUGFUNC("e1000_init_hw_82541"); + + /* Initialize identification LED */ + ret_val = e1000_id_led_init_generic(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + goto out; + } + + /* Disabling VLAN filtering */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + e1000_clear_vfta(hw); + + /* Setup the receive address. */ + e1000_init_rx_addrs_generic(hw, mac->rar_entry_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) { + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + /* Avoid back to back register writes by adding the register + * read (flush). This is to protect against some strange + * bridge configurations that may issue Memory Write Block + * (MWB) to our register space. + */ + E1000_WRITE_FLUSH(hw); + } + + /* Setup link and flow control */ + ret_val = e1000_setup_link(hw); + + txdctl = E1000_READ_REG(hw, E1000_TXDCTL); + txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, E1000_TXDCTL, txdctl); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82541(hw); + +out: + return ret_val; +} + +/** + * e1000_get_link_up_info_82541 - Report speed and duplex + * @hw: pointer to the HW structure + * @speed: pointer to speed buffer + * @duplex: pointer to duplex buffer + * + * Retrieve the current speed and duplex configuration. + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_get_link_up_info_82541(struct e1000_hw *hw, u16 *speed, u16 *duplex) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_get_link_up_info_82541"); + + ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed, duplex); + if (ret_val) + goto out; + + if (!phy->speed_downgraded) + goto out; + + /* IGP01 PHY may advertise full duplex operation after speed + * downgrade even if it is operating at half duplex. + * Here we set the duplex settings to match the duplex in the + * link partner's capabilities. + */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &data); + if (ret_val) + goto out; + + if (!(data & NWAY_ER_LP_NWAY_CAPS)) + *duplex = HALF_DUPLEX; + else { + ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, &data); + if (ret_val) + goto out; + + if (*speed == SPEED_100) { + if (!(data & NWAY_LPAR_100TX_FD_CAPS)) + *duplex = HALF_DUPLEX; + } else if (*speed == SPEED_10) { + if (!(data & NWAY_LPAR_10T_FD_CAPS)) + *duplex = HALF_DUPLEX; + } + } + +out: + return ret_val; +} + +/** + * e1000_phy_hw_reset_82541 - PHY hardware reset + * @hw: pointer to the HW structure + * + * Verify the reset block is not blocking us from resetting. Acquire + * semaphore (if necessary) and read/set/write the device control reset + * bit in the PHY. Wait the appropriate delay time for the device to + * reset and relase the semaphore (if necessary). + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_phy_hw_reset_82541(struct e1000_hw *hw) +{ + s32 ret_val; + u32 ledctl; + + DEBUGFUNC("e1000_phy_hw_reset_82541"); + + ret_val = e1000_phy_hw_reset_generic(hw); + if (ret_val) + goto out; + + e1000_phy_init_script_82541(hw); + + if ((hw->mac.type == e1000_82541) || (hw->mac.type == e1000_82547)) { + /* Configure activity LED after PHY reset */ + ledctl = E1000_READ_REG(hw, E1000_LEDCTL); + ledctl &= IGP_ACTIVITY_LED_MASK; + ledctl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl); + } + +out: + return ret_val; +} + +/** + * e1000_setup_copper_link_82541 - Configure copper link settings + * @hw: pointer to the HW structure + * + * Calls the appropriate function to configure the link for auto-neg or forced + * speed and duplex. Then we check for link, once link is established calls + * to configure collision distance and flow control are called. If link is + * not established, we return -E1000_ERR_PHY (-2). This is a function + * pointer entry point called by the api module. + **/ +STATIC s32 +e1000_setup_copper_link_82541(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_dev_spec_82541 *dev_spec; + s32 ret_val; + u32 ctrl, ledctl; + + DEBUGFUNC("e1000_setup_copper_link_82541"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + hw->phy.reset_disable = FALSE; + + dev_spec = (struct e1000_dev_spec_82541 *)hw->dev_spec; + + /* Earlier revs of the IGP phy require us to force MDI. */ + if (hw->mac.type == e1000_82541 || hw->mac.type == e1000_82547) { + dev_spec->dsp_config = e1000_dsp_config_disabled; + phy->mdix = 1; + } else + dev_spec->dsp_config = e1000_dsp_config_enabled; + + ret_val = e1000_copper_link_setup_igp(hw); + if (ret_val) + goto out; + + if (hw->mac.autoneg) { + if (dev_spec->ffe_config == e1000_ffe_config_active) + dev_spec->ffe_config = e1000_ffe_config_enabled; + } + + /* Configure activity LED after Phy reset */ + ledctl = E1000_READ_REG(hw, E1000_LEDCTL); + ledctl &= IGP_ACTIVITY_LED_MASK; + ledctl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl); + + ret_val = e1000_setup_copper_link_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_check_for_link_82541 - Check/Store link connection + * @hw: pointer to the HW structure + * + * This checks the link condition of the adapter and stores the + * results in the hw->mac structure. This is a function pointer entry + * point called by the api module. + **/ +STATIC s32 +e1000_check_for_link_82541(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + boolean_t link; + + DEBUGFUNC("e1000_check_for_link_82541"); + + /* We only want to go out to the PHY registers to see if Auto-Neg + * has completed and/or if our link status has changed. The + * get_link_status flag is set upon receiving a Link Status + * Change or Rx Sequence Error interrupt. + */ + if (!mac->get_link_status) { + ret_val = E1000_SUCCESS; + goto out; + } + + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + */ + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) { + ret_val = e1000_config_dsp_after_link_change_82541(hw, FALSE); + goto out; /* No link detected */ + } + + mac->get_link_status = FALSE; + + /* Check if there was DownShift, must be checked + * immediately after link-up */ + e1000_check_downshift_generic(hw); + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if (!mac->autoneg) { + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + ret_val = e1000_config_dsp_after_link_change_82541(hw, TRUE); + + /* Auto-Neg is enabled. Auto Speed Detection takes care + * of MAC speed/duplex configuration. So we only need to + * configure Collision Distance in the MAC. + */ + e1000_config_collision_dist_generic(hw); + + /* Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + } + +out: + return ret_val; +} + +/** + * e1000_config_dsp_after_link_change_82541 - Config DSP after link + * @hw: pointer to the HW structure + * @link_up: boolean flag for link up status + * + * Return E1000_ERR_PHY when failing to read/write the PHY, else E1000_SUCCESS + * at any other case. + * + * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a + * gigabit link is achieved to improve link quality. + * This is a function pointer entry point called by the api module. + **/ +static s32 +e1000_config_dsp_after_link_change_82541(struct e1000_hw *hw, boolean_t link_up) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_dev_spec_82541 *dev_spec; + s32 ret_val; + u32 idle_errs = 0; + u16 phy_data, phy_saved_data, speed, duplex, i; + u16 ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20; + u16 dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = + {IGP01E1000_PHY_AGC_PARAM_A, + IGP01E1000_PHY_AGC_PARAM_B, + IGP01E1000_PHY_AGC_PARAM_C, + IGP01E1000_PHY_AGC_PARAM_D}; + + DEBUGFUNC("e1000_config_dsp_after_link_change_82541"); + + dev_spec = (struct e1000_dev_spec_82541 *)hw->dev_spec; + + if (link_up) { + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + goto out; + } + + if (speed != SPEED_1000) { + ret_val = E1000_SUCCESS; + goto out; + } + + ret_val = e1000_get_cable_length(hw); + if (ret_val) + goto out; + + if ((dev_spec->dsp_config == e1000_dsp_config_enabled) && + phy->min_cable_length >= 50) { + + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = e1000_read_phy_reg(hw, + dsp_reg_array[i], + &phy_data); + if (ret_val) + goto out; + + phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; + + ret_val = e1000_write_phy_reg(hw, + dsp_reg_array[i], + phy_data); + if (ret_val) + goto out; + } + dev_spec->dsp_config = e1000_dsp_config_activated; + } + + if ((dev_spec->ffe_config != e1000_ffe_config_enabled) || + (phy->min_cable_length >= 50)) { + ret_val = E1000_SUCCESS; + goto out; + } + + /* clear previous idle error counts */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + goto out; + + for (i = 0; i < ffe_idle_err_timeout; i++) { + usec_delay(1000); + ret_val = e1000_read_phy_reg(hw, + PHY_1000T_STATUS, + &phy_data); + if (ret_val) + goto out; + + idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT); + if (idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) { + dev_spec->ffe_config = e1000_ffe_config_active; + + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_CM_CP); + if (ret_val) + goto out; + break; + } + + if (idle_errs) + ffe_idle_err_timeout = + FFE_IDLE_ERR_COUNT_TIMEOUT_100; + } + } else { + if (dev_spec->dsp_config == e1000_dsp_config_activated) { + /* Save off the current value of register 0x2F5B + * to be restored at the end of the routines. */ + ret_val = e1000_read_phy_reg(hw, + 0x2F5B, + &phy_saved_data); + if (ret_val) + goto out; + + /* Disable the PHY transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + if (ret_val) + goto out; + + msec_delay_irq(20); + + ret_val = e1000_write_phy_reg(hw, + 0x0000, + IGP01E1000_IEEE_FORCE_GIG); + if (ret_val) + goto out; + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = e1000_read_phy_reg(hw, + dsp_reg_array[i], + &phy_data); + if (ret_val) + goto out; + + phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; + phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS; + + ret_val = e1000_write_phy_reg(hw, + dsp_reg_array[i], + phy_data); + if (ret_val) + goto out; + } + + ret_val = e1000_write_phy_reg(hw, + 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if (ret_val) + goto out; + + msec_delay_irq(20); + + /* Now enable the transmitter */ + ret_val = e1000_write_phy_reg(hw, + 0x2F5B, + phy_saved_data); + if (ret_val) + goto out; + + dev_spec->dsp_config = e1000_dsp_config_enabled; + } + + if (dev_spec->ffe_config != e1000_ffe_config_active) { + ret_val = E1000_SUCCESS; + goto out; + } + + /* Save off the current value of register 0x2F5B + * to be restored at the end of the routines. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + if (ret_val) + goto out; + + /* Disable the PHY transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + if (ret_val) + goto out; + + msec_delay_irq(20); + + ret_val = e1000_write_phy_reg(hw, + 0x0000, + IGP01E1000_IEEE_FORCE_GIG); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_DEFAULT); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, + 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if (ret_val) + goto out; + + msec_delay_irq(20); + + /* Now enable the transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (ret_val) + goto out; + + dev_spec->ffe_config = e1000_ffe_config_enabled; + } + +out: + return ret_val; +} + +/** + * e1000_get_cable_length_igp_82541 - Determine cable length for igp PHY + * @hw: pointer to the HW structure + * + * The automatic gain control (agc) normalizes the amplitude of the + * received signal, adjusting for the attenuation produced by the + * cable. By reading the AGC registers, which reperesent the + * cobination of course and fine gain value, the value can be put + * into a lookup table to obtain the approximate cable length + * for each channel. This is a function pointer entry point called by the + * api module. + **/ +STATIC s32 +e1000_get_cable_length_igp_82541(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 i, data; + u16 cur_agc_value, agc_value = 0; + u16 min_agc_value = IGP01E1000_AGC_LENGTH_TABLE_SIZE; + u16 agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = + {IGP01E1000_PHY_AGC_A, + IGP01E1000_PHY_AGC_B, + IGP01E1000_PHY_AGC_C, + IGP01E1000_PHY_AGC_D}; + + DEBUGFUNC("e1000_get_cable_length_igp_82541"); + + /* Read the AGC registers for all channels */ + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &data); + if (ret_val) + goto out; + + cur_agc_value = data >> IGP01E1000_AGC_LENGTH_SHIFT; + + /* Bounds checking */ + if ((cur_agc_value >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) || + (cur_agc_value == 0)) { + ret_val = -E1000_ERR_PHY; + goto out; + } + + agc_value += cur_agc_value; + + if (min_agc_value > cur_agc_value) + min_agc_value = cur_agc_value; + } + + /* Remove the minimal AGC result for length < 50m */ + if (agc_value < IGP01E1000_PHY_CHANNEL_NUM * 50) { + agc_value -= min_agc_value; + /* Average the three remaining channels for the length. */ + agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1); + } else { + /* Average the channels for the length. */ + agc_value /= IGP01E1000_PHY_CHANNEL_NUM; + } + + phy->min_cable_length = (e1000_igp_cable_length_table[agc_value] > + IGP01E1000_AGC_RANGE) + ? (e1000_igp_cable_length_table[agc_value] - + IGP01E1000_AGC_RANGE) + : 0; + phy->max_cable_length = e1000_igp_cable_length_table[agc_value] + + IGP01E1000_AGC_RANGE; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + +out: + return ret_val; +} + +/** + * e1000_set_d3_lplu_state_82541 - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by the + * api module. + **/ +STATIC s32 +e1000_set_d3_lplu_state_82541(struct e1000_hw *hw, boolean_t active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_set_d3_lplu_state_82541"); + + switch (hw->mac.type) { + case e1000_82541_rev_2: + case e1000_82547_rev_2: + break; + default: + ret_val = e1000_set_d3_lplu_state_generic(hw, active); + goto out; + break; + } + + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &data); + if (ret_val) + goto out; + + if (!active) { + data &= ~IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, data); + if (ret_val) + goto out; + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || + (phy->autoneg_advertised == E1000_ALL_10_SPEED)) { + data |= IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, data); + if (ret_val) + goto out; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + } + +out: + return ret_val; +} + +/** + * e1000_setup_led_82541 - Configures SW controllable LED + * @hw: pointer to the HW structure + * + * This prepares the SW controllable LED for use and saves the current state + * of the LED so it can be later restored. This is a function pointer entry + * point called by the api module. + **/ +STATIC s32 +e1000_setup_led_82541(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82541 *dev_spec; + s32 ret_val; + + DEBUGFUNC("e1000_setup_led_82541"); + + dev_spec = (struct e1000_dev_spec_82541 *)hw->dev_spec; + + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_GMII_FIFO, + &dev_spec->spd_default); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_GMII_FIFO, + (u16)(dev_spec->spd_default & + ~IGP01E1000_GMII_SPD)); + if (ret_val) + goto out; + + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + +out: + return ret_val; +} + +/** + * e1000_cleanup_led_82541 - Set LED config to default operation + * @hw: pointer to the HW structure + * + * Remove the current LED configuration and set the LED configuration + * to the default value, saved from the EEPROM. This is a function pointer + * entry point called by the api module. + **/ +STATIC s32 +e1000_cleanup_led_82541(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82541 *dev_spec; + s32 ret_val; + + DEBUGFUNC("e1000_cleanup_led_82541"); + + dev_spec = (struct e1000_dev_spec_82541 *)hw->dev_spec; + + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_GMII_FIFO, + dev_spec->spd_default); + if (ret_val) + goto out; + + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default); + +out: + return ret_val; +} + +/** + * e1000_phy_init_script_82541 - Initialize GbE PHY + * @hw: pointer to the HW structure + * + * Initializes the IGP PHY. + **/ +static s32 +e1000_phy_init_script_82541(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82541 *dev_spec; + u32 ret_val; + u16 phy_saved_data; + + DEBUGFUNC("e1000_phy_init_script_82541"); + + dev_spec = (struct e1000_dev_spec_82541 *)hw->dev_spec; + + if (!dev_spec->phy_init_script) { + ret_val = E1000_SUCCESS; + goto out; + } + + /* Delay after phy reset to enable NVM configuration to load */ + msec_delay(20); + + /* Save off the current value of register 0x2F5B to be restored at + * the end of this routine. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + /* Disabled the PHY transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + + msec_delay(20); + + e1000_write_phy_reg(hw, 0x0000, 0x0140); + + msec_delay(5); + + switch (hw->mac.type) { + case e1000_82541: + case e1000_82547: + e1000_write_phy_reg(hw, 0x1F95, 0x0001); + + e1000_write_phy_reg(hw, 0x1F71, 0xBD21); + + e1000_write_phy_reg(hw, 0x1F79, 0x0018); + + e1000_write_phy_reg(hw, 0x1F30, 0x1600); + + e1000_write_phy_reg(hw, 0x1F31, 0x0014); + + e1000_write_phy_reg(hw, 0x1F32, 0x161C); + + e1000_write_phy_reg(hw, 0x1F94, 0x0003); + + e1000_write_phy_reg(hw, 0x1F96, 0x003F); + + e1000_write_phy_reg(hw, 0x2010, 0x0008); + break; + case e1000_82541_rev_2: + case e1000_82547_rev_2: + e1000_write_phy_reg(hw, 0x1F73, 0x0099); + break; + default: + break; + } + + e1000_write_phy_reg(hw, 0x0000, 0x3300); + + msec_delay(20); + + /* Now enable the transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (hw->mac.type == e1000_82547) { + u16 fused, fine, coarse; + + /* Move to analog registers page */ + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_SPARE_FUSE_STATUS, + &fused); + + if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) { + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_STATUS, + &fused); + + fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK; + coarse = fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK; + + if (coarse > IGP01E1000_ANALOG_FUSE_COARSE_THRESH) { + coarse -= IGP01E1000_ANALOG_FUSE_COARSE_10; + fine -= IGP01E1000_ANALOG_FUSE_FINE_1; + } else if (coarse == + IGP01E1000_ANALOG_FUSE_COARSE_THRESH) + fine -= IGP01E1000_ANALOG_FUSE_FINE_10; + + fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) | + (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) | + (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK); + + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_CONTROL, + fused); + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_BYPASS, + IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL); + } + } + +out: + return ret_val; +} + +/** + * e1000_init_script_state_82541 - Enable/Disable PHY init script + * @hw: pointer to the HW structure + * @state: boolean value used to enable/disable PHY init script + * + * Allows the driver to enable/disable the PHY init script, if the PHY is an + * IGP PHY. This is a function pointer entry point called by the api module. + **/ +void +e1000_init_script_state_82541(struct e1000_hw *hw, boolean_t state) +{ + struct e1000_dev_spec_82541 *dev_spec; + + DEBUGFUNC("e1000_init_script_state_82541"); + + if (hw->phy.type != e1000_phy_igp) { + DEBUGOUT("Initialization script not necessary.\n"); + goto out; + } + + dev_spec = (struct e1000_dev_spec_82541 *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + goto out; + } + + dev_spec->phy_init_script = state; + +out: + return; +} + +/** + * e1000_clear_hw_cntrs_82541 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +STATIC void +e1000_clear_hw_cntrs_82541(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_82541"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_PRC64); + temp = E1000_READ_REG(hw, E1000_PRC127); + temp = E1000_READ_REG(hw, E1000_PRC255); + temp = E1000_READ_REG(hw, E1000_PRC511); + temp = E1000_READ_REG(hw, E1000_PRC1023); + temp = E1000_READ_REG(hw, E1000_PRC1522); + temp = E1000_READ_REG(hw, E1000_PTC64); + temp = E1000_READ_REG(hw, E1000_PTC127); + temp = E1000_READ_REG(hw, E1000_PTC255); + temp = E1000_READ_REG(hw, E1000_PTC511); + temp = E1000_READ_REG(hw, E1000_PTC1023); + temp = E1000_READ_REG(hw, E1000_PTC1522); + + temp = E1000_READ_REG(hw, E1000_ALGNERRC); + temp = E1000_READ_REG(hw, E1000_RXERRC); + temp = E1000_READ_REG(hw, E1000_TNCRS); + temp = E1000_READ_REG(hw, E1000_CEXTERR); + temp = E1000_READ_REG(hw, E1000_TSCTC); + temp = E1000_READ_REG(hw, E1000_TSCTFC); + + temp = E1000_READ_REG(hw, E1000_MGTPRC); + temp = E1000_READ_REG(hw, E1000_MGTPDC); + temp = E1000_READ_REG(hw, E1000_MGTPTC); +} diff --git a/bsd_eth_drivers/if_em/e1000_82541.h b/bsd_eth_drivers/if_em/e1000_82541.h new file mode 100644 index 0000000..f399035 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82541.h @@ -0,0 +1,91 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82541.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_82541_H_ +#define _E1000_82541_H_ + +#define NVM_WORD_SIZE_BASE_SHIFT_82541 (NVM_WORD_SIZE_BASE_SHIFT + 1) + +#define IGP01E1000_PHY_CHANNEL_NUM 4 + +#define IGP01E1000_PHY_AGC_A 0x1172 +#define IGP01E1000_PHY_AGC_B 0x1272 +#define IGP01E1000_PHY_AGC_C 0x1472 +#define IGP01E1000_PHY_AGC_D 0x1872 + +#define IGP01E1000_PHY_AGC_PARAM_A 0x1171 +#define IGP01E1000_PHY_AGC_PARAM_B 0x1271 +#define IGP01E1000_PHY_AGC_PARAM_C 0x1471 +#define IGP01E1000_PHY_AGC_PARAM_D 0x1871 + +#define IGP01E1000_PHY_EDAC_MU_INDEX 0xC000 +#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000 + +#define IGP01E1000_PHY_DSP_RESET 0x1F33 + +#define IGP01E1000_PHY_DSP_FFE 0x1F35 +#define IGP01E1000_PHY_DSP_FFE_CM_CP 0x0069 +#define IGP01E1000_PHY_DSP_FFE_DEFAULT 0x002A + +#define IGP01E1000_IEEE_FORCE_GIG 0x0140 +#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300 + +#define IGP01E1000_AGC_LENGTH_SHIFT 7 +#define IGP01E1000_AGC_RANGE 10 + +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 + +#define IGP01E1000_ANALOG_FUSE_STATUS 0x20D0 +#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1 +#define IGP01E1000_ANALOG_FUSE_CONTROL 0x20DC +#define IGP01E1000_ANALOG_FUSE_BYPASS 0x20DE + +#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED 0x0100 +#define IGP01E1000_ANALOG_FUSE_FINE_MASK 0x0F80 +#define IGP01E1000_ANALOG_FUSE_COARSE_MASK 0x0070 +#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH 0x0040 +#define IGP01E1000_ANALOG_FUSE_COARSE_10 0x0010 +#define IGP01E1000_ANALOG_FUSE_FINE_1 0x0080 +#define IGP01E1000_ANALOG_FUSE_FINE_10 0x0500 +#define IGP01E1000_ANALOG_FUSE_POLY_MASK 0xF000 +#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002 + +#define IGP01E1000_MSE_CHANNEL_D 0x000F +#define IGP01E1000_MSE_CHANNEL_C 0x00F0 +#define IGP01E1000_MSE_CHANNEL_B 0x0F00 +#define IGP01E1000_MSE_CHANNEL_A 0xF000 + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_82542.c b/bsd_eth_drivers/if_em/e1000_82542.c new file mode 100644 index 0000000..5995088 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82542.c @@ -0,0 +1,558 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82542.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +/* e1000_82542 (rev 1 & 2) + */ + +#include "e1000_api.h" + +void e1000_init_function_pointers_82542(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_82542(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_82542(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_82542(struct e1000_hw *hw); +STATIC s32 e1000_get_bus_info_82542(struct e1000_hw *hw); +STATIC s32 e1000_reset_hw_82542(struct e1000_hw *hw); +STATIC s32 e1000_init_hw_82542(struct e1000_hw *hw); +STATIC s32 e1000_setup_link_82542(struct e1000_hw *hw); +STATIC s32 e1000_led_on_82542(struct e1000_hw *hw); +STATIC s32 e1000_led_off_82542(struct e1000_hw *hw); +STATIC void e1000_clear_hw_cntrs_82542(struct e1000_hw *hw); + +struct e1000_dev_spec_82542 { + boolean_t dma_fairness; +}; + +/** + * e1000_init_phy_params_82542 - Init PHY func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_phy_params_82542(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_phy_params_82542"); + + phy->type = e1000_phy_none; + + return ret_val; +} + +/** + * e1000_init_nvm_params_82542 - Init NVM func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_nvm_params_82542(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + + DEBUGFUNC("e1000_init_nvm_params_82542"); + + nvm->address_bits = 6; + nvm->delay_usec = 50; + nvm->opcode_bits = 3; + nvm->type = e1000_nvm_eeprom_microwire; + nvm->word_size = 64; + + /* Function Pointers */ + func->read_nvm = e1000_read_nvm_microwire; + func->release_nvm = e1000_stop_nvm; + func->write_nvm = e1000_write_nvm_microwire; + func->update_nvm = e1000_update_nvm_checksum_generic; + func->validate_nvm = e1000_validate_nvm_checksum_generic; + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_82542 - Init MAC func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_mac_params_82542(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_mac_params_82542"); + + /* Set media type */ + hw->media_type = e1000_media_type_fiber; + + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES; + + /* Function pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_82542; + /* reset */ + func->reset_hw = e1000_reset_hw_82542; + /* hw initialization */ + func->init_hw = e1000_init_hw_82542; + /* link setup */ + func->setup_link = e1000_setup_link_82542; + /* phy/fiber/serdes setup */ + func->setup_physical_interface = e1000_setup_fiber_serdes_link_generic; + /* check for link */ + func->check_for_link = e1000_check_for_fiber_link_generic; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_generic; + /* writing VFTA */ + func->write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + func->clear_vfta = e1000_clear_vfta_generic; + /* setting MTA */ + func->mta_set = e1000_mta_set_generic; + /* turn on/off LED */ + func->led_on = e1000_led_on_82542; + func->led_off = e1000_led_off_82542; + /* remove device */ + func->remove_device = e1000_remove_device_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_82542; + /* link info */ + func->get_link_up_info = e1000_get_speed_and_duplex_fiber_serdes_generic; + + hw->dev_spec_size = sizeof(struct e1000_dev_spec_82542); + + /* Device-specific structure allocation */ + ret_val = e1000_alloc_zeroed_dev_spec_struct(hw, hw->dev_spec_size); + + return ret_val; +} + +/** + * e1000_init_function_pointers_82542 - Init func ptrs. + * @hw: pointer to the HW structure + * + * The only function explicitly called by the api module to initialize + * all function pointers and parameters. + **/ +void +e1000_init_function_pointers_82542(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82542"); + + hw->func.init_mac_params = e1000_init_mac_params_82542; + hw->func.init_nvm_params = e1000_init_nvm_params_82542; + hw->func.init_phy_params = e1000_init_phy_params_82542; +} + +/** + * e1000_get_bus_info_82542 - Obtain bus information for adapter + * @hw: pointer to the HW structure + * + * This will obtain information about the HW bus for which the + * adaper is attached and stores it in the hw structure. This is a function + * pointer entry point called by the api module. + **/ +STATIC s32 +e1000_get_bus_info_82542(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_get_bus_info_82542"); + + hw->bus.type = e1000_bus_type_pci; + hw->bus.speed = e1000_bus_speed_unknown; + hw->bus.width = e1000_bus_width_unknown; + + return E1000_SUCCESS; +} + +/** + * e1000_reset_hw_82542 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_reset_hw_82542(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + s32 ret_val = E1000_SUCCESS; + u32 ctrl, icr; + + DEBUGFUNC("e1000_reset_hw_82542"); + + if (hw->revision_id == E1000_REVISION_2) { + DEBUGOUT("Disabling MWI on 82542 rev 2\n"); + e1000_pci_clear_mwi(hw); + } + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + /* Delay to allow any outstanding PCI transactions to complete before + * resetting the device + */ + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to 82542/82543 MAC\n"); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + + e1000_reload_nvm(hw); + msec_delay(2); + + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + icr = E1000_READ_REG(hw, E1000_ICR); + + if (hw->revision_id == E1000_REVISION_2) { + if (bus->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) + e1000_pci_set_mwi(hw); + } + + return ret_val; +} + +/** + * e1000_init_hw_82542 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_hw_82542(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_dev_spec_82542 *dev_spec; + s32 ret_val = E1000_SUCCESS; + u32 ctrl; + u16 i; + + DEBUGFUNC("e1000_init_hw_82542"); + + dev_spec = (struct e1000_dev_spec_82542 *)hw->dev_spec; + + /* Disabling VLAN filtering */ + E1000_WRITE_REG(hw, E1000_VET, 0); + e1000_clear_vfta(hw); + + /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ + if (hw->revision_id == E1000_REVISION_2) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + e1000_pci_clear_mwi(hw); + E1000_WRITE_REG(hw, E1000_RCTL, E1000_RCTL_RST); + E1000_WRITE_FLUSH(hw); + msec_delay(5); + } + + /* Setup the receive address. */ + e1000_init_rx_addrs_generic(hw, mac->rar_entry_count); + + /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ + if (hw->revision_id == E1000_REVISION_2) { + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_FLUSH(hw); + msec_delay(1); + if (hw->bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) + e1000_pci_set_mwi(hw); + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + + /* Set the PCI priority bit correctly in the CTRL register. This + * determines if the adapter gives priority to receives, or if it + * gives equal priority to transmits and receives. + */ + if (dev_spec->dma_fairness) { + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PRIOR); + } + + /* Setup link and flow control */ + ret_val = e1000_setup_link_82542(hw); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82542(hw); + + return ret_val; +} + +/** + * e1000_setup_link_82542 - Setup flow control and link settings + * @hw: pointer to the HW structure + * + * Determines which flow control settings to use, then configures flow + * control. Calls the appropriate media-specific link configuration + * function. Assuming the adapter has a valid link partner, a valid link + * should be established. Assumes the hardware has previously been reset + * and the transmitter and receiver are not enabled. This is a function + * pointer entry point called by the api module. + **/ +STATIC s32 +e1000_setup_link_82542(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_setup_link_82542"); + + ret_val = e1000_set_default_fc_generic(hw); + if (ret_val) + goto out; + + mac->fc &= ~e1000_fc_tx_pause; + + if (mac->report_tx_early == 1) + mac->fc &= ~e1000_fc_rx_pause; + + /* We want to save off the original Flow Control configuration just in + * case we get disconnected and then reconnected into a different hub + * or switch with different Flow Control capabilities. + */ + mac->original_fc = mac->fc; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", mac->fc); + + /* Call the necessary subroutine to configure the link. */ + ret_val = func->setup_physical_interface(hw); + if (ret_val) + goto out; + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing Flow Control address, type and timer regs\n"); + + E1000_WRITE_REG(hw, E1000_FCAL, FLOW_CONTROL_ADDRESS_LOW); + E1000_WRITE_REG(hw, E1000_FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, E1000_FCT, FLOW_CONTROL_TYPE); + + E1000_WRITE_REG(hw, E1000_FCTTV, mac->fc_pause_time); + + ret_val = e1000_set_fc_watermarks_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_led_on_82542 - Turn on SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED on. This is a function pointer entry point + * called by the api module. + **/ +STATIC s32 +e1000_led_on_82542(struct e1000_hw *hw) +{ + u32 ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGFUNC("e1000_led_on_82542"); + + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + return E1000_SUCCESS; +} + +/** + * e1000_led_off_82542 - Turn off SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED off. This is a function pointer entry point + * called by the api module. + **/ +STATIC s32 +e1000_led_off_82542(struct e1000_hw *hw) +{ + u32 ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGFUNC("e1000_led_off_82542"); + + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + return E1000_SUCCESS; +} + +/** + * e1000_translate_register_82542 - Translate the proper regiser offset + * @reg: e1000 register to be read + * + * Registers in 82542 are located in different offsets than other adapters + * even though they function in the same manner. This function takes in + * the name of the register to read and returns the correct offset for + * 82542 silicon. + **/ +u32 +e1000_translate_register_82542(u32 reg) +{ + /* Some of the 82542 registers are located at different + * offsets than they are in newer adapters. + * Despite the difference in location, the registers + * function in the same manner. + */ + switch (reg) { + case E1000_RA: + reg = 0x00040; + break; + case E1000_RDTR: + reg = 0x00108; + break; + case E1000_RDBAL: + reg = 0x00110; + break; + case E1000_RDBAH: + reg = 0x00114; + break; + case E1000_RDLEN: + reg = 0x00118; + break; + case E1000_RDH: + reg = 0x00120; + break; + case E1000_RDT: + reg = 0x00128; + break; + case E1000_RDBAL1: + reg = 0x00138; + break; + case E1000_RDBAH1: + reg = 0x0013C; + break; + case E1000_RDLEN1: + reg = 0x00140; + break; + case E1000_RDH1: + reg = 0x00148; + break; + case E1000_RDT1: + reg = 0x00150; + break; + case E1000_FCRTH: + reg = 0x00160; + break; + case E1000_FCRTL: + reg = 0x00168; + break; + case E1000_MTA: + reg = 0x00200; + break; + case E1000_TDBAL: + reg = 0x00420; + break; + case E1000_TDBAH: + reg = 0x00424; + break; + case E1000_TDLEN: + reg = 0x00428; + break; + case E1000_TDH: + reg = 0x00430; + break; + case E1000_TDT: + reg = 0x00438; + break; + case E1000_TIDV: + reg = 0x00440; + break; + case E1000_VFTA: + reg = 0x00600; + break; + case E1000_TDFH: + reg = 0x08010; + break; + case E1000_TDFT: + reg = 0x08018; + break; + default: + break; + } + + return reg; +} + +/** + * e1000_clear_hw_cntrs_82542 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +STATIC void +e1000_clear_hw_cntrs_82542(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_82542"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_PRC64); + temp = E1000_READ_REG(hw, E1000_PRC127); + temp = E1000_READ_REG(hw, E1000_PRC255); + temp = E1000_READ_REG(hw, E1000_PRC511); + temp = E1000_READ_REG(hw, E1000_PRC1023); + temp = E1000_READ_REG(hw, E1000_PRC1522); + temp = E1000_READ_REG(hw, E1000_PTC64); + temp = E1000_READ_REG(hw, E1000_PTC127); + temp = E1000_READ_REG(hw, E1000_PTC255); + temp = E1000_READ_REG(hw, E1000_PTC511); + temp = E1000_READ_REG(hw, E1000_PTC1023); + temp = E1000_READ_REG(hw, E1000_PTC1522); +} diff --git a/bsd_eth_drivers/if_em/e1000_82543.c b/bsd_eth_drivers/if_em/e1000_82543.c new file mode 100644 index 0000000..39256e2 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82543.c @@ -0,0 +1,1651 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82543.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +/* e1000_82543 + * e1000_82544 + */ + +#include "e1000_api.h" +#include "e1000_82543.h" + +void e1000_init_function_pointers_82543(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_82543(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_82543(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_82543(struct e1000_hw *hw); +STATIC s32 e1000_read_phy_reg_82543(struct e1000_hw *hw, u32 offset, + u16 *data); +STATIC s32 e1000_write_phy_reg_82543(struct e1000_hw *hw, u32 offset, + u16 data); +STATIC s32 e1000_phy_force_speed_duplex_82543(struct e1000_hw *hw); +STATIC s32 e1000_phy_hw_reset_82543(struct e1000_hw *hw); +STATIC s32 e1000_reset_hw_82543(struct e1000_hw *hw); +STATIC s32 e1000_init_hw_82543(struct e1000_hw *hw); +STATIC s32 e1000_setup_link_82543(struct e1000_hw *hw); +STATIC s32 e1000_setup_copper_link_82543(struct e1000_hw *hw); +STATIC s32 e1000_setup_fiber_link_82543(struct e1000_hw *hw); +STATIC s32 e1000_check_for_copper_link_82543(struct e1000_hw *hw); +STATIC s32 e1000_check_for_fiber_link_82543(struct e1000_hw *hw); +STATIC s32 e1000_led_on_82543(struct e1000_hw *hw); +STATIC s32 e1000_led_off_82543(struct e1000_hw *hw); +STATIC void e1000_write_vfta_82543(struct e1000_hw *hw, u32 offset, + u32 value); +STATIC void e1000_mta_set_82543(struct e1000_hw *hw, u32 hash_value); +STATIC void e1000_clear_hw_cntrs_82543(struct e1000_hw *hw); +static s32 e1000_config_mac_to_phy_82543(struct e1000_hw *hw); +static boolean_t e1000_init_phy_disabled_82543(struct e1000_hw *hw); +static void e1000_lower_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl); +static s32 e1000_polarity_reversal_workaround_82543(struct e1000_hw *hw); +static void e1000_raise_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl); +static u16 e1000_shift_in_mdi_bits_82543(struct e1000_hw *hw); +static void e1000_shift_out_mdi_bits_82543(struct e1000_hw *hw, u32 data, + u16 count); +static boolean_t e1000_tbi_compatibility_enabled_82543(struct e1000_hw *hw); +static void e1000_set_tbi_sbp_82543(struct e1000_hw *hw, boolean_t state); + +struct e1000_dev_spec_82543 { + u32 tbi_compatibility; + boolean_t dma_fairness; + boolean_t init_phy_disabled; +}; + +/** + * e1000_init_phy_params_82543 - Init PHY func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_phy_params_82543(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_phy_params_82543"); + + if (hw->media_type != e1000_media_type_copper) { + phy->type = e1000_phy_none; + goto out; + } + + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 10000; + phy->type = e1000_phy_m88; + + /* Function Pointers */ + func->check_polarity = e1000_check_polarity_m88; + func->commit_phy = e1000_phy_sw_reset_generic; + func->force_speed_duplex = e1000_phy_force_speed_duplex_82543; + func->get_cable_length = e1000_get_cable_length_m88; + func->get_cfg_done = e1000_get_cfg_done_generic; + func->read_phy_reg = (hw->mac.type == e1000_82543) + ? e1000_read_phy_reg_82543 + : e1000_read_phy_reg_m88; + func->reset_phy = (hw->mac.type == e1000_82543) + ? e1000_phy_hw_reset_82543 + : e1000_phy_hw_reset_generic; + func->write_phy_reg = (hw->mac.type == e1000_82543) + ? e1000_write_phy_reg_82543 + : e1000_write_phy_reg_m88; + func->get_phy_info = e1000_get_phy_info_m88; + + /* The external PHY of the 82543 can be in a funky state. + * Resetting helps us read the PHY registers for acquiring + * the PHY ID. + */ + if (!e1000_init_phy_disabled_82543(hw)) { + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) { + DEBUGOUT("Resetting PHY during init failed.\n"); + goto out; + } + msec_delay(20); + } + + ret_val = e1000_get_phy_id(hw); + if (ret_val) + goto out; + + /* Verify phy id */ + switch (hw->mac.type) { + case e1000_82543: + if (phy->id != M88E1000_E_PHY_ID) { + ret_val = -E1000_ERR_PHY; + goto out; + } + break; + case e1000_82544: + if (phy->id != M88E1000_I_PHY_ID) { + ret_val = -E1000_ERR_PHY; + goto out; + } + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + break; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_82543 - Init NVM func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_nvm_params_82543(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + + DEBUGFUNC("e1000_init_nvm_params_82543"); + + nvm->type = e1000_nvm_eeprom_microwire; + nvm->word_size = 64; + nvm->delay_usec = 50; + nvm->address_bits = 6; + nvm->opcode_bits = 3; + + /* Function Pointers */ + func->read_nvm = e1000_read_nvm_microwire; + func->update_nvm = e1000_update_nvm_checksum_generic; + func->valid_led_default = e1000_valid_led_default_generic; + func->validate_nvm = e1000_validate_nvm_checksum_generic; + func->write_nvm = e1000_write_nvm_microwire; + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_82543 - Init MAC func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_mac_params_82543(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val; + + DEBUGFUNC("e1000_init_mac_params_82543"); + + /* Set media type */ + switch (hw->device_id) { + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82544EI_FIBER: + hw->media_type = e1000_media_type_fiber; + break; + default: + hw->media_type = e1000_media_type_copper; + break; + } + + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES; + + /* Function pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_pci_generic; + /* reset */ + func->reset_hw = e1000_reset_hw_82543; + /* hw initialization */ + func->init_hw = e1000_init_hw_82543; + /* link setup */ + func->setup_link = e1000_setup_link_82543; + /* physical interface setup */ + func->setup_physical_interface = + (hw->media_type == e1000_media_type_copper) + ? e1000_setup_copper_link_82543 + : e1000_setup_fiber_link_82543; + /* check for link */ + func->check_for_link = + (hw->media_type == e1000_media_type_copper) + ? e1000_check_for_copper_link_82543 + : e1000_check_for_fiber_link_82543; + /* link info */ + func->get_link_up_info = + (hw->media_type == e1000_media_type_copper) + ? e1000_get_speed_and_duplex_copper_generic + : e1000_get_speed_and_duplex_fiber_serdes_generic; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_generic; + /* writing VFTA */ + func->write_vfta = e1000_write_vfta_82543; + /* clearing VFTA */ + func->clear_vfta = e1000_clear_vfta_generic; + /* setting MTA */ + func->mta_set = e1000_mta_set_82543; + /* turn on/off LED */ + func->led_on = e1000_led_on_82543; + func->led_off = e1000_led_off_82543; + /* remove device */ + func->remove_device = e1000_remove_device_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_82543; + + hw->dev_spec_size = sizeof(struct e1000_dev_spec_82543); + + /* Device-specific structure allocation */ + ret_val = e1000_alloc_zeroed_dev_spec_struct(hw, hw->dev_spec_size); + if (ret_val) + goto out; + + /* Set tbi compatibility */ + if ((hw->mac.type != e1000_82543) || + (hw->media_type == e1000_media_type_fiber)) + e1000_set_tbi_compatibility_82543(hw, FALSE); + +out: + return ret_val; +} + +/** + * e1000_init_function_pointers_82543 - Init func ptrs. + * @hw: pointer to the HW structure + * + * The only function explicitly called by the api module to initialize + * all function pointers and parameters. + **/ +void +e1000_init_function_pointers_82543(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82543"); + + hw->func.init_mac_params = e1000_init_mac_params_82543; + hw->func.init_nvm_params = e1000_init_nvm_params_82543; + hw->func.init_phy_params = e1000_init_phy_params_82543; +} + +/** + * e1000_tbi_compatibility_enabled_82543 - Returns TBI compat status + * @hw: pointer to the HW structure + * + * Returns the curent status of 10-bit Interface (TBI) compatibility + * (enabled/disabled). + **/ +static boolean_t +e1000_tbi_compatibility_enabled_82543(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82543 *dev_spec; + boolean_t state = FALSE; + + DEBUGFUNC("e1000_tbi_compatibility_enabled_82543"); + + if (hw->mac.type != e1000_82543) { + DEBUGOUT("TBI compatibility workaround for 82543 only.\n"); + goto out; + } + + dev_spec = (struct e1000_dev_spec_82543 *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + goto out; + } + + state = (dev_spec->tbi_compatibility & TBI_COMPAT_ENABLED) + ? TRUE : FALSE; + +out: + return state; +} + +/** + * e1000_set_tbi_compatibility_82543 - Set TBI compatibility + * @hw: pointer to the HW structure + * @state: enable/disable TBI compatibility + * + * Enables or disabled 10-bit Interface (TBI) compatibility. + **/ +void +e1000_set_tbi_compatibility_82543(struct e1000_hw *hw, boolean_t state) +{ + struct e1000_dev_spec_82543 *dev_spec; + + DEBUGFUNC("e1000_set_tbi_compatibility_82543"); + + if (hw->mac.type != e1000_82543) { + DEBUGOUT("TBI compatibility workaround for 82543 only.\n"); + goto out; + } + + dev_spec = (struct e1000_dev_spec_82543 *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + goto out; + } + + if (state) + dev_spec->tbi_compatibility |= TBI_COMPAT_ENABLED; + else + dev_spec->tbi_compatibility &= ~TBI_COMPAT_ENABLED; + +out: + return; +} + +/** + * e1000_tbi_sbp_enabled_82543 - Returns TBI SBP status + * @hw: pointer to the HW structure + * + * Returns the curent status of 10-bit Interface (TBI) store bad packet (SBP) + * (enabled/disabled). + **/ +boolean_t +e1000_tbi_sbp_enabled_82543(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82543 *dev_spec; + boolean_t state = FALSE; + + DEBUGFUNC("e1000_tbi_sbp_enabled_82543"); + + if (hw->mac.type != e1000_82543) { + DEBUGOUT("TBI compatibility workaround for 82543 only.\n"); + goto out; + } + + dev_spec = (struct e1000_dev_spec_82543 *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + goto out; + } + + state = (dev_spec->tbi_compatibility & TBI_SBP_ENABLED) + ? TRUE : FALSE; + +out: + return state; +} + +/** + * e1000_set_tbi_sbp_82543 - Set TBI SBP + * @hw: pointer to the HW structure + * @state: enable/disable TBI store bad packet + * + * Enables or disabled 10-bit Interface (TBI) store bad packet (SBP). + **/ +static void +e1000_set_tbi_sbp_82543(struct e1000_hw *hw, boolean_t state) +{ + struct e1000_dev_spec_82543 *dev_spec; + + DEBUGFUNC("e1000_set_tbi_sbp_82543"); + + dev_spec = (struct e1000_dev_spec_82543 *)hw->dev_spec; + + if (state && e1000_tbi_compatibility_enabled_82543(hw)) + dev_spec->tbi_compatibility |= TBI_SBP_ENABLED; + else + dev_spec->tbi_compatibility &= ~TBI_SBP_ENABLED; + + return; +} + +/** + * e1000_init_phy_disabled_82543 - Returns init PHY status + * @hw: pointer to the HW structure + * + * Returns the current status of whether PHY initialization is disabled. + * True if PHY initialization is disabled else false. + **/ +static boolean_t +e1000_init_phy_disabled_82543(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82543 *dev_spec; + boolean_t ret_val; + + DEBUGFUNC("e1000_init_phy_disabled_82543"); + + if (hw->mac.type != e1000_82543) { + ret_val = FALSE; + goto out; + } + + dev_spec = (struct e1000_dev_spec_82543 *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + ret_val = FALSE; + goto out; + } + + ret_val = dev_spec->init_phy_disabled; + +out: + return ret_val; +} + +/** + * e1000_tbi_adjust_stats_82543 - Adjust stats when TBI enabled + * @hw: pointer to the HW structure + * @stats: Struct containing statistic register values + * @frame_len: The length of the frame in question + * @mac_addr: The Ethernet destination address of the frame in question + * + * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT + **/ +void +e1000_tbi_adjust_stats_82543(struct e1000_hw *hw, struct e1000_hw_stats *stats, + u32 frame_len, u8 *mac_addr) +{ + u64 carry_bit; + + if (e1000_tbi_sbp_enabled_82543(hw) == FALSE) + goto out; + + /* First adjust the frame length. */ + frame_len--; + /* We need to adjust the statistics counters, since the hardware + * counters overcount this packet as a CRC error and undercount + * the packet as a good packet + */ + /* This packet should not be counted as a CRC error. */ + stats->crcerrs--; + /* This packet does count as a Good Packet Received. */ + stats->gprc++; + + /* Adjust the Good Octets received counters */ + carry_bit = 0x80000000 & stats->gorcl; + stats->gorcl += frame_len; + /* If the high bit of Gorcl (the low 32 bits of the Good Octets + * Received Count) was one before the addition, + * AND it is zero after, then we lost the carry out, + * need to add one to Gorch (Good Octets Received Count High). + * This could be simplified if all environments supported + * 64-bit integers. + */ + if (carry_bit && ((stats->gorcl & 0x80000000) == 0)) + stats->gorch++; + /* Is this a broadcast or multicast? Check broadcast first, + * since the test for a multicast frame will test positive on + * a broadcast frame. + */ + if ((mac_addr[0] == 0xff) && (mac_addr[1] == 0xff)) + /* Broadcast packet */ + stats->bprc++; + else if (*mac_addr & 0x01) + /* Multicast packet */ + stats->mprc++; + + /* In this case, the hardware has overcounted the number of + * oversize frames. + */ + if ((frame_len == hw->mac.max_frame_size) && (stats->roc > 0)) + stats->roc--; + + /* Adjust the bin counters when the extra byte put the frame in the + * wrong bin. Remember that the frame_len was adjusted above. + */ + if (frame_len == 64) { + stats->prc64++; + stats->prc127--; + } else if (frame_len == 127) { + stats->prc127++; + stats->prc255--; + } else if (frame_len == 255) { + stats->prc255++; + stats->prc511--; + } else if (frame_len == 511) { + stats->prc511++; + stats->prc1023--; + } else if (frame_len == 1023) { + stats->prc1023++; + stats->prc1522--; + } else if (frame_len == 1522) { + stats->prc1522++; + } + +out: + return; +} + +/** + * e1000_read_phy_reg_82543 - Read PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY at offset and stores the information read to data. + **/ +STATIC s32 +e1000_read_phy_reg_82543(struct e1000_hw *hw, u32 offset, u16 *data) +{ + u32 mdic; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_phy_reg_82543"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + ret_val = -E1000_ERR_PARAM; + goto out; + } + + /* We must first send a preamble through the MDIO pin to signal the + * beginning of an MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits_82543(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the next few fields that are required for a read + * operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine five different times. The format + * of an MII read instruction consists of a shift out of 14 bits and + * is defined as follows: + * + * followed by a shift in of 18 bits. This first two bits shifted in + * are TurnAround bits used to avoid contention on the MDIO pin when a + * READ operation is performed. These two bits are thrown away + * followed by a shift in of 16 bits which contains the desired data. + */ + mdic = (offset | (hw->phy.addr << 5) | + (PHY_OP_READ << 10) | (PHY_SOF << 12)); + + e1000_shift_out_mdi_bits_82543(hw, mdic, 14); + + /* Now that we've shifted out the read command to the MII, we need to + * "shift in" the 16-bit value (18 total bits) of the requested PHY + * register address. + */ + *data = e1000_shift_in_mdi_bits_82543(hw); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_82543 - Write PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be written + * @data: pointer to the data to be written at offset + * + * Writes data to the PHY at offset. + **/ +STATIC s32 +e1000_write_phy_reg_82543(struct e1000_hw *hw, u32 offset, u16 data) +{ + u32 mdic; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_phy_reg_82543"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + ret_val = -E1000_ERR_PARAM; + goto out; + } + + /* We'll need to use the SW defined pins to shift the write command + * out to the PHY. We first send a preamble to the PHY to signal the + * beginning of the MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits_82543(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the remaining required fields that will indicate a + * write operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine for each field in the command. The + * format of a MII write instruction is as follows: + * . + */ + mdic = ((PHY_TURNAROUND) | (offset << 2) | (hw->phy.addr << 7) | + (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); + mdic <<= 16; + mdic |= (u32) data; + + e1000_shift_out_mdi_bits_82543(hw, mdic, 32); + +out: + return ret_val; +} + +/** + * e1000_raise_mdi_clk_82543 - Raise Management Data Input clock + * @hw: pointer to the HW structure + * @ctrl: pointer to the control register + * + * Raise the management data input clock by setting the MDC bit in the control + * register. + **/ +static void +e1000_raise_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl) +{ + /* Raise the clock input to the Management Data Clock (by setting the + * MDC bit), and then delay a sufficient amount of time. + */ + E1000_WRITE_REG(hw, E1000_CTRL, (*ctrl | E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + usec_delay(10); +} + +/** + * e1000_lower_mdi_clk_82543 - Lower Management Data Input clock + * @hw: pointer to the HW structure + * @ctrl: pointer to the control register + * + * Lower the management data input clock by clearing the MDC bit in the control + * register. + **/ +static void +e1000_lower_mdi_clk_82543(struct e1000_hw *hw, u32 *ctrl) +{ + /* Lower the clock input to the Management Data Clock (by clearing the + * MDC bit), and then delay a sufficient amount of time. + */ + E1000_WRITE_REG(hw, E1000_CTRL, (*ctrl & ~E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + usec_delay(10); +} + +/** + * e1000_shift_out_mdi_bits_82543 - Shift data bits our to the PHY + * @hw: pointer to the HW structure + * @data: data to send to the PHY + * @count: number of bits to shift out + * + * We need to shift 'count' bits out to the PHY. So, the value in the + * "data" parameter will be shifted out to the PHY one bit at a time. + * In order to do this, "data" must be broken down into bits. + **/ +static void +e1000_shift_out_mdi_bits_82543(struct e1000_hw *hw, u32 data, u16 count) +{ + u32 ctrl, mask; + + /* We need to shift "count" number of bits out to the PHY. So, the + * value in the "data" parameter will be shifted out to the PHY one + * bit at a time. In order to do this, "data" must be broken down + * into bits. + */ + mask = 0x01; + mask <<= (count -1); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */ + ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); + + while (mask) { + /* A "1" is shifted out to the PHY by setting the MDIO bit to + * "1" and then raising and lowering the Management Data Clock. + * A "0" is shifted out to the PHY by setting the MDIO bit to + * "0" and then raising and lowering the clock. + */ + if (data & mask) ctrl |= E1000_CTRL_MDIO; + else ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + usec_delay(10); + + e1000_raise_mdi_clk_82543(hw, &ctrl); + e1000_lower_mdi_clk_82543(hw, &ctrl); + + mask >>= 1; + } +} + +/** + * e1000_shift_in_mdi_bits_82543 - Shift data bits in from the PHY + * @hw: pointer to the HW structure + * + * In order to read a register from the PHY, we need to shift 18 bits + * in from the PHY. Bits are "shifted in" by raising the clock input to + * the PHY (setting the MDC bit), and then reading the value of the data out + * MDIO bit. + **/ +static u16 +e1000_shift_in_mdi_bits_82543(struct e1000_hw *hw) +{ + u32 ctrl; + u16 data = 0; + u8 i; + + /* In order to read a register from the PHY, we need to shift in a + * total of 18 bits from the PHY. The first two bit (turnaround) + * times are used to avoid contention on the MDIO pin when a read + * operation is performed. These two bits are ignored by us and + * thrown away. Bits are "shifted in" by raising the input to the + * Management Data Clock (setting the MDC bit) and then reading the + * value of the MDIO bit. + */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as + * input. + */ + ctrl &= ~E1000_CTRL_MDIO_DIR; + ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + /* Raise and lower the clock before reading in the data. This accounts + * for the turnaround bits. The first clock occurred when we clocked + * out the last bit of the Register Address. + */ + e1000_raise_mdi_clk_82543(hw, &ctrl); + e1000_lower_mdi_clk_82543(hw, &ctrl); + + for (data = 0, i = 0; i < 16; i++) { + data <<= 1; + e1000_raise_mdi_clk_82543(hw, &ctrl); + ctrl = E1000_READ_REG(hw, E1000_CTRL); + /* Check to see if we shifted in a "1". */ + if (ctrl & E1000_CTRL_MDIO) + data |= 1; + e1000_lower_mdi_clk_82543(hw, &ctrl); + } + + e1000_raise_mdi_clk_82543(hw, &ctrl); + e1000_lower_mdi_clk_82543(hw, &ctrl); + + return data; +} + +/** + * e1000_phy_force_speed_duplex_82543 - Force speed/duplex for PHY + * @hw: pointer to the HW structure + * + * Calls the function to force speed and duplex for the m88 PHY, and + * if the PHY is not auto-negotiating and the speed is forced to 10Mbit, + * then call the function for polarity reversal workaround. + **/ +STATIC s32 +e1000_phy_force_speed_duplex_82543(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_phy_force_speed_duplex_82543"); + + ret_val = e1000_phy_force_speed_duplex_m88(hw); + if (ret_val) + goto out; + + if (!hw->mac.autoneg && + (hw->mac.forced_speed_duplex & E1000_ALL_10_SPEED)) + ret_val = e1000_polarity_reversal_workaround_82543(hw); + +out: + return ret_val; +} + +/** + * e1000_polarity_reversal_workaround_82543 - Workaround polarity reversal + * @hw: pointer to the HW structure + * + * When forcing link to 10 Full or 10 Half, the PHY can reverse the polarity + * inadvertantly. To workaround the issue, we disable the transmitter on + * the PHY until we have established the link partner's link parameters. + **/ +static s32 +e1000_polarity_reversal_workaround_82543(struct e1000_hw *hw) +{ + s32 ret_val; + u16 mii_status_reg; + u16 i; + boolean_t link; + + /* Polarity reversal workaround for forced 10F/10H links. */ + + /* Disable the transmitter on the PHY */ + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if (ret_val) + goto out; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFFF); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if (ret_val) + goto out; + + /* This loop will early-out if the NO link condition has been met. + * In other words, DO NOT use e1000_phy_has_link_generic() here. + */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Link Status bit + * to be clear. + */ + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + goto out; + + if ((mii_status_reg & ~MII_SR_LINK_STATUS) == 0) + break; + msec_delay_irq(100); + } + + /* Recommended delay time after link has been lost */ + msec_delay_irq(1000); + + /* Now we will re-enable the transmitter on the PHY */ + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if (ret_val) + goto out; + msec_delay_irq(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0); + if (ret_val) + goto out; + msec_delay_irq(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00); + if (ret_val) + goto out; + msec_delay_irq(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if (ret_val) + goto out; + + /* Read the MII Status Register and wait for Link Status bit + * to be set. + */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_TIME, 100000, &link); + if (ret_val) + goto out; + +out: + return ret_val; +} + +/** + * e1000_phy_hw_reset_82543 - PHY hardware reset + * @hw: pointer to the HW structure + * + * Sets the PHY_RESET_DIR bit in the extended device control register + * to put the PHY into a reset and waits for completion. Once the reset + * has been accomplished, clear the PHY_RESET_DIR bit to take the PHY out + * of reset. This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_phy_hw_reset_82543(struct e1000_hw *hw) +{ + struct e1000_functions *func = &hw->func; + u32 ctrl_ext; + s32 ret_val; + + DEBUGFUNC("e1000_phy_hw_reset_82543"); + + /* Read the Extended Device Control Register, assert the PHY_RESET_DIR + * bit to put the PHY into reset... + */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; + ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + /* ...then take it out of reset. */ + ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + + usec_delay(150); + + ret_val = func->get_cfg_done(hw); + + return ret_val; +} + +/** + * e1000_reset_hw_82543 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_reset_hw_82543(struct e1000_hw *hw) +{ + u32 ctrl, icr; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_reset_hw_82543"); + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + e1000_set_tbi_sbp_82543(hw, FALSE); + + /* Delay to allow any outstanding PCI transactions to complete before + * resetting the device + */ + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to 82543/82544 MAC\n"); + if (hw->mac.type == e1000_82543) { + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + } else { + /* The 82544 can't ACK the 64-bit write when issuing the + * reset, so use IO-mapping as a workaround. + */ + E1000_WRITE_REG_IO(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + } + + /* After MAC reset, force reload of NVM to restore power-on + * settings to device. + */ + e1000_reload_nvm(hw); + msec_delay(2); + + /* Masking off and clearing any pending interrupts */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + icr = E1000_READ_REG(hw, E1000_ICR); + + return ret_val; +} + +/** + * e1000_init_hw_82543 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. + **/ +STATIC s32 +e1000_init_hw_82543(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_dev_spec_82543 *dev_spec; + u32 ctrl; + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_init_hw_82543"); + + dev_spec = (struct e1000_dev_spec_82543 *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + /* Disabling VLAN filtering */ + E1000_WRITE_REG(hw, E1000_VET, 0); + e1000_clear_vfta(hw); + + /* Setup the receive address. */ + e1000_init_rx_addrs_generic(hw, mac->rar_entry_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) { + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + E1000_WRITE_FLUSH(hw); + } + + /* Set the PCI priority bit correctly in the CTRL register. This + * determines if the adapter gives priority to receives, or if it + * gives equal priority to transmits and receives. + */ + if (hw->mac.type == e1000_82543 && dev_spec->dma_fairness) { + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PRIOR); + } + + e1000_pcix_mmrbc_workaround_generic(hw); + + /* Setup link and flow control */ + ret_val = e1000_setup_link(hw); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82543(hw); + +out: + return ret_val; +} + +/** + * e1000_setup_link_82543 - Setup flow control and link settings + * @hw: pointer to the HW structure + * + * Read the EEPROM to determine the initial polarity value and write the + * extended device control register with the information before calling + * the generic setup link function, which does the following: + * Determines which flow control settings to use, then configures flow + * control. Calls the appropriate media-specific link configuration + * function. Assuming the adapter has a valid link partner, a valid link + * should be established. Assumes the hardware has previously been reset + * and the transmitter and receiver are not enabled. + **/ +STATIC s32 +e1000_setup_link_82543(struct e1000_hw *hw) +{ + u32 ctrl_ext; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_setup_link_82543"); + + /* Take the 4 bits from NVM word 0xF that determine the initial + * polarity value for the SW controlled pins, and setup the + * Extended Device Control reg with that info. + * This is needed because one of the SW controlled pins is used for + * signal detection. So this should be done before phy setup. + */ + if (hw->mac.type == e1000_82543) { + ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + ctrl_ext = ((data & NVM_WORD0F_SWPDIO_EXT_MASK) << + NVM_SWDPIO_EXT_SHIFT); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + } + + ret_val = e1000_setup_link_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_setup_copper_link_82543 - Configure copper link settings + * @hw: pointer to the HW structure + * + * Configures the link for auto-neg or forced speed and duplex. Then we check + * for link, once link is established calls to configure collision distance + * and flow control are called. + **/ +STATIC s32 +e1000_setup_copper_link_82543(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + boolean_t link; + + DEBUGFUNC("e1000_setup_copper_link_82543"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL) | E1000_CTRL_SLU; + /* With 82543, we need to force speed and duplex on the MAC + * equal to what the PHY speed and duplex configuration is. + * In addition, we need to perform a hardware reset on the + * PHY to take it out of reset. + */ + if (hw->mac.type == e1000_82543) { + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + goto out; + hw->phy.reset_disable = FALSE; + } else { + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + } + + /* Set MDI/MDI-X, Polarity Reversal, and downshift settings */ + ret_val = e1000_copper_link_setup_m88(hw); + if (ret_val) + goto out; + + if (hw->mac.autoneg) { + /* Setup autoneg and flow control advertisement and perform + * autonegotiation. */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + goto out; + } else { + /* PHY will be set to 10H, 10F, 100H or 100F + * depending on user settings. */ + DEBUGOUT("Forcing Speed and Duplex\n"); + ret_val = e1000_phy_force_speed_duplex_82543(hw); + if (ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + goto out; + } + } + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + ret_val = e1000_phy_has_link_generic(hw, + COPPER_LINK_UP_LIMIT, + 10, + &link); + if (ret_val) + goto out; + + + if (link) { + DEBUGOUT("Valid link established!!!\n"); + /* Config the MAC and PHY after link is up */ + if (hw->mac.type == e1000_82544) + e1000_config_collision_dist_generic(hw); + else { + ret_val = e1000_config_mac_to_phy_82543(hw); + if (ret_val) + goto out; + } + ret_val = e1000_config_fc_after_link_up_generic(hw); + } else { + DEBUGOUT("Unable to establish link!!!\n"); + } + +out: + return ret_val; +} + +/** + * e1000_setup_fiber_link_82543 - Setup link for fiber + * @hw: pointer to the HW structure + * + * Configures collision distance and flow control for fiber links. Upon + * successful setup, poll for link. + **/ +STATIC s32 +e1000_setup_fiber_link_82543(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + + DEBUGFUNC("e1000_setup_fiber_link_82543"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Take the link out of reset */ + ctrl &= ~E1000_CTRL_LRST; + + e1000_config_collision_dist_generic(hw); + + ret_val = e1000_commit_fc_settings_generic(hw); + if (ret_val) + goto out; + + DEBUGOUT("Auto-negotiation enabled\n"); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + msec_delay(1); + + /* For these adapters, the SW defineable pin 1 is cleared when the + * optics detect a signal. If we have a signal, then poll for a + * "Link-Up" indication. + */ + if (!(E1000_READ_REG(hw, E1000_CTRL) & E1000_CTRL_SWDPIN1)) { + ret_val = e1000_poll_fiber_serdes_link_generic(hw); + } else { + DEBUGOUT("No signal detected\n"); + } + +out: + return ret_val; +} + +/** + * e1000_check_for_copper_link_82543 - Check for link (Copper) + * @hw: pointer to the HW structure + * + * Checks the phy for link, if link exists, do the following: + * - check for downshift + * - do polarity workaround (if necessary) + * - configure collision distance + * - configure flow control after link up + * - configure tbi compatibility + **/ +STATIC s32 +e1000_check_for_copper_link_82543(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 icr, rctl; + s32 ret_val; + u16 speed, duplex; + boolean_t link; + + DEBUGFUNC("e1000_check_for_copper_link_82543"); + + if (!mac->get_link_status) { + ret_val = E1000_SUCCESS; + goto out; + } + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) + goto out; /* No link detected */ + + mac->get_link_status = FALSE; + + e1000_check_downshift_generic(hw); + + /* If we are forcing speed/duplex, then we can return since + * we have already determined whether we have link or not. + */ + if (!mac->autoneg) { + /* If speed and duplex are forced to 10H or 10F, then we will + * implement the polarity reversal workaround. We disable + * interrupts first, and upon returning, place the devices + * interrupt state to its previous value except for the link + * status change interrupt which will happened due to the + * execution of this workaround. + */ + if (mac->forced_speed_duplex & E1000_ALL_10_SPEED) { + E1000_WRITE_REG(hw, E1000_IMC, 0xFFFFFFFF); + ret_val = e1000_polarity_reversal_workaround_82543(hw); + icr = E1000_READ_REG(hw, E1000_ICR); + E1000_WRITE_REG(hw, E1000_ICS, (icr & ~E1000_ICS_LSC)); + E1000_WRITE_REG(hw, E1000_IMS, IMS_ENABLE_MASK); + } + + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + /* We have a M88E1000 PHY and Auto-Neg is enabled. If we + * have Si on board that is 82544 or newer, Auto + * Speed Detection takes care of MAC speed/duplex + * configuration. So we only need to configure Collision + * Distance in the MAC. Otherwise, we need to force + * speed/duplex on the MAC to the current PHY speed/duplex + * settings. + */ + if (mac->type == e1000_82544) + e1000_config_collision_dist_generic(hw); + else { + ret_val = e1000_config_mac_to_phy_82543(hw); + if (ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + goto out; + } + } + + /* Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + } + + /* At this point we know that we are on copper and we have + * auto-negotiated link. These are conditions for checking the link + * partner capability register. We use the link speed to determine if + * TBI compatibility needs to be turned on or off. If the link is not + * at gigabit speed, then TBI compatibility is not needed. If we are + * at gigabit speed, we turn on TBI compatibility. + */ + if (e1000_tbi_compatibility_enabled_82543(hw)) { + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + if (speed != SPEED_1000) { + /* If link speed is not set to gigabit speed, + * we do not need to enable TBI compatibility. + */ + if (e1000_tbi_sbp_enabled_82543(hw)) { + /* If we previously were in the mode, + * turn it off. + */ + e1000_set_tbi_sbp_82543(hw, FALSE); + rctl = E1000_READ_REG(hw, E1000_RCTL); + rctl &= ~E1000_RCTL_SBP; + E1000_WRITE_REG(hw, E1000_RCTL, rctl); + } + } else { + /* If TBI compatibility is was previously off, + * turn it on. For compatibility with a TBI link + * partner, we will store bad packets. Some + * frames have an additional byte on the end and + * will look like CRC errors to to the hardware. + */ + if (!e1000_tbi_sbp_enabled_82543(hw)) { + e1000_set_tbi_sbp_82543(hw, TRUE); + rctl = E1000_READ_REG(hw, E1000_RCTL); + rctl |= E1000_RCTL_SBP; + E1000_WRITE_REG(hw, E1000_RCTL, rctl); + } + } + } +out: + return ret_val; +} + +/** + * e1000_check_for_fiber_link_82543 - Check for link (Fiber) + * @hw: pointer to the HW structure + * + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + **/ +STATIC s32 +e1000_check_for_fiber_link_82543(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 rxcw, ctrl, status; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_check_for_fiber_link_82543"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + status = E1000_READ_REG(hw, E1000_CTRL); + rxcw = E1000_READ_REG(hw, E1000_CTRL); + + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), the cable is plugged in (we have signal), + * and our link partner is not trying to auto-negotiate with us (we + * are receiving idles or data), we need to force link up. We also + * need to give auto-negotiation time to complete, in case the cable + * was just plugged in. The autoneg_failed flag does this. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 0 == have signal */ + if ((!(ctrl & E1000_CTRL_SWDPIN1)) && + (!(status & E1000_STATUS_LU)) && + (!(rxcw & E1000_RXCW_C))) { + if (mac->autoneg_failed == 0) { + mac->autoneg_failed = 1; + ret_val = 0; + goto out; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + goto out; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU)); + + mac->serdes_has_link = TRUE; + } + +out: + return ret_val; +} + +/** + * e1000_config_mac_to_phy_82543 - Configure MAC to PHY settings + * @hw: pointer to the HW structure + * + * For the 82543 silicon, we need to set the MAC to match the settings + * of the PHY, even if the PHY is auto-negotiating. + **/ +static s32 +e1000_config_mac_to_phy_82543(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_config_mac_to_phy_82543"); + + /* Set the bits to force speed and duplex */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); + + /* Set up duplex in the Device Control and Transmit Control + * registers depending on negotiated values. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + goto out; + + ctrl &= ~E1000_CTRL_FD; + if (phy_data & M88E1000_PSSR_DPLX) + ctrl |= E1000_CTRL_FD; + + e1000_config_collision_dist_generic(hw); + + /* Set up speed in the Device Control register depending on + * negotiated values. + */ + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) + ctrl |= E1000_CTRL_SPD_1000; + else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) + ctrl |= E1000_CTRL_SPD_100; + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + +out: + return ret_val; +} + +/** + * e1000_write_vfta_82543 - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: the 32-bit offset in which to write the value to. + * @value: the 32-bit value to write at location offset. + * + * This writes a 32-bit value to a 32-bit offset in the VLAN filter + * table. + **/ +STATIC void +e1000_write_vfta_82543(struct e1000_hw *hw, u32 offset, u32 value) +{ + u32 temp; + + DEBUGFUNC("e1000_write_vfta_82543"); + + if ((hw->mac.type == e1000_82544) && (offset & 1)) { + temp = E1000_READ_REG_ARRAY(hw, E1000_VFTA, offset - 1); + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset - 1, temp); + E1000_WRITE_FLUSH(hw); + } else + e1000_write_vfta_generic(hw, offset, value); +} + +/** + * e1000_mta_set_82543 - Set multicast filter table address + * @hw: pointer to the HW structure + * @hash_value: determines the MTA register and bit to set + * + * The multicast table address is a register array of 32-bit registers. + * The hash_value is used to determine what register the bit is in, the + * current value is read, the new bit is OR'd in and the new value is + * written back into the register. + **/ +STATIC void +e1000_mta_set_82543(struct e1000_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg, mta, temp; + + DEBUGFUNC("e1000_mta_set_82543"); + + hash_reg = (hash_value >> 5); + + /* If we are on an 82544 and we are trying to write an odd offset + * in the MTA, save off the previous entry before writing and + * restore the old value after writing. + */ + if ((hw->mac.type == e1000_82544) && (hash_reg & 1)) { + hash_reg &= (hw->mac.mta_reg_count - 1); + hash_bit = hash_value & 0x1F; + mta = E1000_READ_REG_ARRAY(hw, E1000_MTA, hash_reg); + mta |= (1 << hash_bit); + temp = E1000_READ_REG_ARRAY(hw, E1000_MTA, hash_reg - 1); + + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, hash_reg, mta); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, hash_reg - 1, temp); + E1000_WRITE_FLUSH(hw); + } else + e1000_mta_set_generic(hw, hash_value); +} + +/** + * e1000_led_on_82543 - Turn on SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED on. This is a function pointer entry point + * called by the api module. + **/ +STATIC s32 +e1000_led_on_82543(struct e1000_hw *hw) +{ + u32 ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGFUNC("e1000_led_on_82543"); + + if (hw->mac.type == e1000_82544 && + hw->media_type == e1000_media_type_copper) { + /* Clear SW-defineable Pin 0 to turn on the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + /* Fiber 82544 and all 82543 use this method */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + return E1000_SUCCESS; +} + +/** + * e1000_led_off_82543 - Turn off SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED off. This is a function pointer entry point + * called by the api module. + **/ +STATIC s32 +e1000_led_off_82543(struct e1000_hw *hw) +{ + u32 ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGFUNC("e1000_led_off_82543"); + + if (hw->mac.type == e1000_82544 && + hw->media_type == e1000_media_type_copper) { + /* Set SW-defineable Pin 0 to turn off the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + return E1000_SUCCESS; +} + +/** + * e1000_clear_hw_cntrs_82543 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +STATIC void +e1000_clear_hw_cntrs_82543(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_82543"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_PRC64); + temp = E1000_READ_REG(hw, E1000_PRC127); + temp = E1000_READ_REG(hw, E1000_PRC255); + temp = E1000_READ_REG(hw, E1000_PRC511); + temp = E1000_READ_REG(hw, E1000_PRC1023); + temp = E1000_READ_REG(hw, E1000_PRC1522); + temp = E1000_READ_REG(hw, E1000_PTC64); + temp = E1000_READ_REG(hw, E1000_PTC127); + temp = E1000_READ_REG(hw, E1000_PTC255); + temp = E1000_READ_REG(hw, E1000_PTC511); + temp = E1000_READ_REG(hw, E1000_PTC1023); + temp = E1000_READ_REG(hw, E1000_PTC1522); + + temp = E1000_READ_REG(hw, E1000_ALGNERRC); + temp = E1000_READ_REG(hw, E1000_RXERRC); + temp = E1000_READ_REG(hw, E1000_TNCRS); + temp = E1000_READ_REG(hw, E1000_CEXTERR); + temp = E1000_READ_REG(hw, E1000_TSCTC); + temp = E1000_READ_REG(hw, E1000_TSCTFC); +} diff --git a/bsd_eth_drivers/if_em/e1000_82543.h b/bsd_eth_drivers/if_em/e1000_82543.h new file mode 100644 index 0000000..6abce22 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82543.h @@ -0,0 +1,50 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82543.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_82543_H_ +#define _E1000_82543_H_ + +#define PHY_PREAMBLE 0xFFFFFFFF +#define PHY_PREAMBLE_SIZE 32 +#define PHY_SOF 0x1 +#define PHY_OP_READ 0x2 +#define PHY_OP_WRITE 0x1 +#define PHY_TURNAROUND 0x2 + +#define TBI_COMPAT_ENABLED 0x1 /* Global "knob" for the workaround */ +#define TBI_SBP_ENABLED 0x2 /* If TBI_COMPAT_ENABLED, + * then this is the current state (on/off) */ + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_82571.c b/bsd_eth_drivers/if_em/e1000_82571.c new file mode 100644 index 0000000..0aa0a7a --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82571.c @@ -0,0 +1,1405 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82571.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +/* e1000_82571 + * e1000_82572 + * e1000_82573 + */ + +#include "e1000_api.h" +#include "e1000_82571.h" + +void e1000_init_function_pointers_82571(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_82571(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_82571(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_82571(struct e1000_hw *hw); +STATIC s32 e1000_acquire_nvm_82571(struct e1000_hw *hw); +STATIC void e1000_release_nvm_82571(struct e1000_hw *hw); +STATIC s32 e1000_write_nvm_82571(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +STATIC s32 e1000_update_nvm_checksum_82571(struct e1000_hw *hw); +STATIC s32 e1000_validate_nvm_checksum_82571(struct e1000_hw *hw); +STATIC s32 e1000_get_cfg_done_82571(struct e1000_hw *hw); +STATIC s32 e1000_set_d0_lplu_state_82571(struct e1000_hw *hw, + boolean_t active); +STATIC s32 e1000_reset_hw_82571(struct e1000_hw *hw); +STATIC s32 e1000_init_hw_82571(struct e1000_hw *hw); +STATIC void e1000_clear_vfta_82571(struct e1000_hw *hw); +STATIC void e1000_mc_addr_list_update_82571(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count, + u32 rar_used_count, u32 rar_count); +STATIC s32 e1000_setup_link_82571(struct e1000_hw *hw); +STATIC s32 e1000_setup_copper_link_82571(struct e1000_hw *hw); +STATIC s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw); +STATIC s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data); +STATIC void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw); +static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw); +static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw); +static s32 e1000_get_phy_id_82571(struct e1000_hw *hw); +static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw); +static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw); +static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); + +struct e1000_dev_spec_82571 { + boolean_t laa_is_present; +}; + +/** + * e1000_init_phy_params_82571 - Init PHY func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_phy_params_82571(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_phy_params_82571"); + + if (hw->media_type != e1000_media_type_copper) { + phy->type = e1000_phy_none; + goto out; + } + + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; + + func->acquire_phy = e1000_get_hw_semaphore_82571; + func->check_polarity = e1000_check_polarity_igp; + func->check_reset_block = e1000_check_reset_block_generic; + func->release_phy = e1000_put_hw_semaphore_82571; + func->reset_phy = e1000_phy_hw_reset_generic; + func->set_d0_lplu_state = e1000_set_d0_lplu_state_82571; + func->set_d3_lplu_state = e1000_set_d3_lplu_state_generic; + + switch (hw->mac.type) { + case e1000_82571: + case e1000_82572: + phy->type = e1000_phy_igp_2; + func->get_cfg_done = e1000_get_cfg_done_82571; + func->get_phy_info = e1000_get_phy_info_igp; + func->force_speed_duplex = e1000_phy_force_speed_duplex_igp; + func->get_cable_length = e1000_get_cable_length_igp_2; + func->read_phy_reg = e1000_read_phy_reg_igp; + func->write_phy_reg = e1000_write_phy_reg_igp; + break; + case e1000_82573: + phy->type = e1000_phy_m88; + func->get_cfg_done = e1000_get_cfg_done_generic; + func->get_phy_info = e1000_get_phy_info_m88; + func->commit_phy = e1000_phy_sw_reset_generic; + func->force_speed_duplex = e1000_phy_force_speed_duplex_m88; + func->get_cable_length = e1000_get_cable_length_m88; + func->read_phy_reg = e1000_read_phy_reg_m88; + func->write_phy_reg = e1000_write_phy_reg_m88; + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + break; + } + + /* This can only be done after all function pointers are setup. */ + ret_val = e1000_get_phy_id_82571(hw); + + /* Verify phy id */ + switch (hw->mac.type) { + case e1000_82571: + case e1000_82572: + if (phy->id != IGP01E1000_I_PHY_ID) { + ret_val = -E1000_ERR_PHY; + goto out; + } + break; + case e1000_82573: + if (phy->id != M88E1111_I_PHY_ID) { + ret_val = -E1000_ERR_PHY; + goto out; + } + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + break; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_82571 - Init NVM func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_nvm_params_82571(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u16 size; + + DEBUGFUNC("e1000_init_nvm_params_82571"); + + nvm->opcode_bits = 8; + nvm->delay_usec = 1; + switch (nvm->override) { + case e1000_nvm_override_spi_large: + nvm->page_size = 32; + nvm->address_bits = 16; + break; + case e1000_nvm_override_spi_small: + nvm->page_size = 8; + nvm->address_bits = 8; + break; + default: + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8; + break; + } + + switch (hw->mac.type) { + case e1000_82573: + if (((eecd >> 15) & 0x3) == 0x3) { + nvm->type = e1000_nvm_flash_hw; + nvm->word_size = 2048; + /* Autonomous Flash update bit must be cleared due + * to Flash update issue. + */ + eecd &= ~E1000_EECD_AUPDEN; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + break; + } + /* Fall Through */ + default: + nvm->type = e1000_nvm_eeprom_spi; + size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + /* Added to a constant, "size" becomes the left-shift value + * for setting word_size. + */ + size += NVM_WORD_SIZE_BASE_SHIFT; + nvm->word_size = 1 << size; + break; + } + + /* Function Pointers */ + func->acquire_nvm = e1000_acquire_nvm_82571; + func->read_nvm = (hw->mac.type == e1000_82573) + ? e1000_read_nvm_eerd + : e1000_read_nvm_spi; + func->release_nvm = e1000_release_nvm_82571; + func->update_nvm = e1000_update_nvm_checksum_82571; + func->validate_nvm = e1000_validate_nvm_checksum_82571; + func->valid_led_default = e1000_valid_led_default_82571; + func->write_nvm = e1000_write_nvm_82571; + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_82571 - Init MAC func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_mac_params_82571(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_mac_params_82571"); + + /* Set media type */ + switch (hw->device_id) { + case E1000_DEV_ID_82571EB_FIBER: + case E1000_DEV_ID_82572EI_FIBER: + case E1000_DEV_ID_82571EB_QUAD_FIBER: + hw->media_type = e1000_media_type_fiber; + break; + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82572EI_SERDES: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + hw->media_type = e1000_media_type_copper; + break; + } + + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES; + /* Set if part includes ASF firmware */ + mac->asf_firmware_present = TRUE; + /* Set if manageability features are enabled. */ + mac->arc_subsystem_valid = + (E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK) + ? TRUE : FALSE; + + /* Function pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_pcie_generic; + /* reset */ + func->reset_hw = e1000_reset_hw_82571; + /* hw initialization */ + func->init_hw = e1000_init_hw_82571; + /* link setup */ + func->setup_link = e1000_setup_link_82571; + /* physical interface link setup */ + func->setup_physical_interface = + (hw->media_type == e1000_media_type_copper) + ? e1000_setup_copper_link_82571 + : e1000_setup_fiber_serdes_link_82571; + /* check for link */ + switch (hw->media_type) { + case e1000_media_type_copper: + func->check_for_link = e1000_check_for_copper_link_generic; + break; + case e1000_media_type_fiber: + func->check_for_link = e1000_check_for_fiber_link_generic; + break; + case e1000_media_type_internal_serdes: + func->check_for_link = e1000_check_for_serdes_link_generic; + break; + default: + ret_val = -E1000_ERR_CONFIG; + goto out; + break; + } + /* check management mode */ + func->check_mng_mode = e1000_check_mng_mode_generic; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_82571; + /* writing VFTA */ + func->write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + func->clear_vfta = e1000_clear_vfta_82571; + /* setting MTA */ + func->mta_set = e1000_mta_set_generic; + /* blink LED */ + func->blink_led = e1000_blink_led_generic; + /* setup LED */ + func->setup_led = e1000_setup_led_generic; + /* cleanup LED */ + func->cleanup_led = e1000_cleanup_led_generic; + /* turn on/off LED */ + func->led_on = e1000_led_on_generic; + func->led_off = e1000_led_off_generic; + /* remove device */ + func->remove_device = e1000_remove_device_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_82571; + /* link info */ + func->get_link_up_info = + (hw->media_type == e1000_media_type_copper) + ? e1000_get_speed_and_duplex_copper_generic + : e1000_get_speed_and_duplex_fiber_serdes_generic; + + hw->dev_spec_size = sizeof(struct e1000_dev_spec_82571); + + /* Device-specific structure allocation */ + ret_val = e1000_alloc_zeroed_dev_spec_struct(hw, hw->dev_spec_size); + +out: + return ret_val; +} + +/** + * e1000_init_function_pointers_82571 - Init func ptrs. + * @hw: pointer to the HW structure + * + * The only function explicitly called by the api module to initialize + * all function pointers and parameters. + **/ +void +e1000_init_function_pointers_82571(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82571"); + + hw->func.init_mac_params = e1000_init_mac_params_82571; + hw->func.init_nvm_params = e1000_init_nvm_params_82571; + hw->func.init_phy_params = e1000_init_phy_params_82571; +} + +/** + * e1000_get_phy_id_82571 - Retrieve the PHY ID and revision + * @hw: pointer to the HW structure + * + * Reads the PHY registers and stores the PHY ID and possibly the PHY + * revision in the hardware structure. + **/ +static s32 +e1000_get_phy_id_82571(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_get_phy_id_82571"); + + switch (hw->mac.type) { + case e1000_82571: + case e1000_82572: + /* The 82571 firmware may still be configuring the PHY. + * In this case, we cannot access the PHY until the + * configuration is done. So we explicitly set the + * PHY ID. */ + phy->id = IGP01E1000_I_PHY_ID; + break; + case e1000_82573: + ret_val = e1000_get_phy_id(hw); + break; + default: + ret_val = -E1000_ERR_PHY; + break; + } + + return ret_val; +} + +/** + * e1000_get_hw_semaphore_82571 - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +s32 +e1000_get_hw_semaphore_82571(struct e1000_hw *hw) +{ + u32 swsm; + s32 ret_val = E1000_SUCCESS; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("e1000_get_hw_semaphore_82571"); + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + } + + if (i == timeout) { + /* Release semaphores */ + e1000_put_hw_semaphore_generic(hw); + DEBUGOUT("Driver can't access the NVM\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_put_hw_semaphore_82571 - Release hardware semaphore + * @hw: pointer to the HW structure + * + * Release hardware semaphore used to access the PHY or NVM + **/ +void +e1000_put_hw_semaphore_82571(struct e1000_hw *hw) +{ + u32 swsm; + + DEBUGFUNC("e1000_put_hw_semaphore_82571"); + + swsm = E1000_READ_REG(hw, E1000_SWSM); + + swsm &= ~E1000_SWSM_SWESMBI; + + E1000_WRITE_REG(hw, E1000_SWSM, swsm); +} + +/** + * e1000_acquire_nvm_82571 - Request for access to the EEPROM + * @hw: pointer to the HW structure + * + * To gain access to the EEPROM, first we must obtain a hardware semaphore. + * Then for non-82573 hardware, set the EEPROM access request bit and wait + * for EEPROM access grant bit. If the access grant bit is not set, release + * hardware semaphore. + **/ +STATIC s32 +e1000_acquire_nvm_82571(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_acquire_nvm_82571"); + + ret_val = e1000_get_hw_semaphore_82571(hw); + if (ret_val) + goto out; + + if (hw->mac.type != e1000_82573) + ret_val = e1000_acquire_nvm_generic(hw); + + if (ret_val) + e1000_put_hw_semaphore_82571(hw); + +out: + return ret_val; +} + +/** + * e1000_release_nvm_82571 - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit. + **/ +STATIC void +e1000_release_nvm_82571(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_82571"); + + e1000_release_nvm_generic(hw); + e1000_put_hw_semaphore_82571(hw); +} + +/** + * e1000_write_nvm_82571 - Write to EEPROM using appropriate interface + * @hw: pointer to the HW structure + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the EEPROM + * + * For non-82573 silicon, write data to EEPROM at offset using SPI interface. + * + * If e1000_update_nvm_checksum is not called after this function, the + * EEPROM will most likley contain an invalid checksum. + **/ +STATIC s32 +e1000_write_nvm_82571(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_nvm_82571"); + + switch (hw->mac.type) { + case e1000_82573: + ret_val = e1000_write_nvm_eewr_82571(hw, offset, words, data); + break; + case e1000_82571: + case e1000_82572: + ret_val = e1000_write_nvm_spi(hw, offset, words, data); + break; + default: + ret_val = -E1000_ERR_NVM; + break; + } + + return ret_val; +} + +/** + * e1000_update_nvm_checksum_82571 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. + **/ +STATIC s32 +e1000_update_nvm_checksum_82571(struct e1000_hw *hw) +{ + u32 eecd; + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_update_nvm_checksum_82571"); + + ret_val = e1000_update_nvm_checksum_generic(hw); + if (ret_val) + goto out; + + /* If our nvm is an EEPROM, then we're done + * otherwise, commit the checksum to the flash NVM. */ + if (hw->nvm.type != e1000_nvm_flash_hw) + goto out; + + /* Check for pending operations. */ + for (i = 0; i < E1000_FLASH_UPDATES; i++) { + msec_delay(1); + if ((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_FLUPD) == 0) + break; + } + + if (i == E1000_FLASH_UPDATES) { + ret_val = -E1000_ERR_NVM; + goto out; + } + + /* Reset the firmware if using STM opcode. */ + if ((E1000_READ_REG(hw, E1000_FLOP) & 0xFF00) == E1000_STM_OPCODE) { + /* The enabling of and the actual reset must be done + * in two write cycles. + */ + E1000_WRITE_REG(hw, E1000_HICR, E1000_HICR_FW_RESET_ENABLE); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG(hw, E1000_HICR, E1000_HICR_FW_RESET); + } + + /* Commit the write to flash */ + eecd = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + + for (i = 0; i < E1000_FLASH_UPDATES; i++) { + msec_delay(1); + if ((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_FLUPD) == 0) + break; + } + + if (i == E1000_FLASH_UPDATES) { + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_82571 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +STATIC s32 +e1000_validate_nvm_checksum_82571(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_validate_nvm_checksum_82571"); + + if (hw->nvm.type == e1000_nvm_flash_hw) + e1000_fix_nvm_checksum_82571(hw); + + return e1000_validate_nvm_checksum_generic(hw); +} + +/** + * e1000_write_nvm_eewr_82571 - Write to EEPROM for 82573 silicon + * @hw: pointer to the HW structure + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the EEPROM + * + * After checking for invalid values, poll the EEPROM to ensure the previous + * command has completed before trying to write the next word. After write + * poll for completion. + * + * If e1000_update_nvm_checksum is not called after this function, the + * EEPROM will most likley contain an invalid checksum. + **/ +static s32 +e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i, eewr = 0; + s32 ret_val = 0; + + DEBUGFUNC("e1000_write_nvm_eewr_82571"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + for (i = 0; i < words; i++) { + eewr = (data[i] << E1000_NVM_RW_REG_DATA) | + ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) | + E1000_NVM_RW_REG_START; + + ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_WRITE); + if (ret_val) + break; + + E1000_WRITE_REG(hw, E1000_EEWR, eewr); + + ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_WRITE); + if (ret_val) + break; + } + +out: + return ret_val; +} + +/** + * e1000_get_cfg_done_82571 - Poll for configuration done + * @hw: pointer to the HW structure + * + * Reads the management control register for the config done bit to be set. + **/ +STATIC s32 +e1000_get_cfg_done_82571(struct e1000_hw *hw) +{ + s32 timeout = PHY_CFG_TIMEOUT; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_get_cfg_done_82571"); + + while (timeout) { + if (E1000_READ_REG(hw, E1000_EEMNGCTL) & E1000_NVM_CFG_DONE_PORT_0) + break; + msec_delay(1); + timeout--; + } + if (!timeout) { + DEBUGOUT("MNG configuration cycle has not completed.\n"); + ret_val = -E1000_ERR_RESET; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_set_d0_lplu_state_82571 - Set Low Power Linkup D0 state + * @hw: pointer to the HW structure + * @active: TRUE to enable LPLU, FALSE to disable + * + * Sets the LPLU D0 state according to the active flag. When activating LPLU + * this function also disables smart speed and vice versa. LPLU will not be + * activated unless the device autonegotiation advertisement meets standards + * of either 10 or 10/100 or 10/100/1000 at all duplexes. This is a function + * pointer entry point only called by PHY setup routines. + **/ +STATIC s32 +e1000_set_d0_lplu_state_82571(struct e1000_hw *hw, boolean_t active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_set_d0_lplu_state_82571"); + + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); + if (ret_val) + goto out; + + if (active) { + data |= IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + goto out; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else { + data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, + data); + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_reset_hw_82571 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_reset_hw_82571(struct e1000_hw *hw) +{ + u32 ctrl, extcnf_ctrl, ctrl_ext, icr; + s32 ret_val; + u16 i = 0; + + DEBUGFUNC("e1000_reset_hw_82571"); + + /* Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) { + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + } + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + /* Must acquire the MDIO ownership before MAC reset. + * Ownership defaults to firmware after a reset. */ + if (hw->mac.type == e1000_82573) { + extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL); + extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; + + do { + E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl); + extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL); + + if (extcnf_ctrl & E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP) + break; + + extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; + + msec_delay(2); + i++; + } while (i < MDIO_OWNERSHIP_TIMEOUT); + } + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to MAC\n"); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + + if (hw->nvm.type == e1000_nvm_flash_hw) { + usec_delay(10); + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) + /* We don't want to continue accessing MAC registers. */ + goto out; + + /* Phy configuration from NVM just starts after EECD_AUTO_RD is set. + * Need to wait for Phy configuration completion before accessing + * NVM and Phy. + */ + if (hw->mac.type == e1000_82573) + msec_delay(25); + + /* Clear any pending interrupt events. */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + icr = E1000_READ_REG(hw, E1000_ICR); + +out: + return ret_val; +} + +/** + * e1000_init_hw_82571 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. + **/ +STATIC s32 +e1000_init_hw_82571(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 reg_data; + s32 ret_val; + u16 i, rar_count = mac->rar_entry_count; + + DEBUGFUNC("e1000_init_hw_82571"); + + e1000_initialize_hw_bits_82571(hw); + + /* Initialize identification LED */ + ret_val = e1000_id_led_init_generic(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + goto out; + } + + /* Disabling VLAN filtering */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + e1000_clear_vfta(hw); + + /* Setup the receive address. */ + /* If, however, a locally administered address was assigned to the + * 82571, we must reserve a RAR for it to work around an issue where + * resetting one port will reload the MAC on the other port. + */ + if (e1000_get_laa_state_82571(hw) == TRUE) + rar_count--; + e1000_init_rx_addrs_generic(hw, rar_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + + /* Setup link and flow control */ + ret_val = e1000_setup_link(hw); + + /* Set the transmit descriptor write-back policy */ + reg_data = E1000_READ_REG(hw, E1000_TXDCTL); + reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | + E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, E1000_TXDCTL, reg_data); + + /* ...for both queues. */ + if (mac->type != e1000_82573) { + reg_data = E1000_READ_REG(hw, E1000_TXDCTL1); + reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB | + E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, E1000_TXDCTL1, reg_data); + } else { + e1000_enable_tx_pkt_filtering(hw); + reg_data = E1000_READ_REG(hw, E1000_GCR); + reg_data |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX; + E1000_WRITE_REG(hw, E1000_GCR, reg_data); + } + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82571(hw); + +out: + return ret_val; +} + +/** + * e1000_initialize_hw_bits_82571 - Initialize hardware-dependent bits + * @hw: pointer to the HW structure + * + * Initializes required hardware-dependent bits needed for normal operation. + **/ +static void +e1000_initialize_hw_bits_82571(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_initialize_hw_bits_82571"); + + if (hw->mac.disable_hw_init_bits) + goto out; + + /* Transmit Descriptor Control 0 */ + reg = E1000_READ_REG(hw, E1000_TXDCTL); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_TXDCTL, reg); + + /* Transmit Descriptor Control 1 */ + reg = E1000_READ_REG(hw, E1000_TXDCTL1); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_TXDCTL1, reg); + + /* Transmit Arbitration Control 0 */ + reg = E1000_READ_REG(hw, E1000_TARC0); + reg &= ~(0xF << 27); /* 30:27 */ + switch (hw->mac.type) { + case e1000_82571: + case e1000_82572: + reg |= (1 << 23) | (1 << 24) | (1 << 25) | (1 << 26); + break; + default: + break; + } + E1000_WRITE_REG(hw, E1000_TARC0, reg); + + /* Transmit Arbitration Control 1 */ + reg = E1000_READ_REG(hw, E1000_TARC1); + switch (hw->mac.type) { + case e1000_82571: + case e1000_82572: + reg &= ~((1 << 29) | (1 << 30)); + reg |= (1 << 22) | (1 << 24) | (1 << 25) | (1 << 26); + if (E1000_READ_REG(hw, E1000_TCTL) & E1000_TCTL_MULR) + reg &= ~(1 << 28); + else + reg |= (1 << 28); + E1000_WRITE_REG(hw, E1000_TARC1, reg); + break; + default: + break; + } + + /* Device Control */ + if (hw->mac.type == e1000_82573) { + reg = E1000_READ_REG(hw, E1000_CTRL); + reg &= ~(1 << 29); + E1000_WRITE_REG(hw, E1000_CTRL, reg); + } + + /* Extended Device Control */ + if (hw->mac.type == e1000_82573) { + reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + reg &= ~(1 << 23); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg); + } + +out: + return; +} + +/** + * e1000_clear_vfta_82571 - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +STATIC void +e1000_clear_vfta_82571(struct e1000_hw *hw) +{ + u32 offset; + u32 vfta_value = 0; + u32 vfta_offset = 0; + u32 vfta_bit_in_reg = 0; + + DEBUGFUNC("e1000_clear_vfta_82571"); + + if (hw->mac.type == e1000_82573) { + if (hw->mng_cookie.vlan_id != 0) { + /* The VFTA is a 4096b bit-field, each identifying + * a single VLAN ID. The following operations + * determine which 32b entry (i.e. offset) into the + * array we want to set the VLAN ID (i.e. bit) of + * the manageability unit. + */ + vfta_offset = (hw->mng_cookie.vlan_id >> + E1000_VFTA_ENTRY_SHIFT) & + E1000_VFTA_ENTRY_MASK; + vfta_bit_in_reg = 1 << (hw->mng_cookie.vlan_id & + E1000_VFTA_ENTRY_BIT_SHIFT_MASK); + } + } + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + /* If the offset we want to clear is the same offset of the + * manageability VLAN ID, then clear all bits except that of + * the manageability unit. + */ + vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0; + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, vfta_value); + E1000_WRITE_FLUSH(hw); + } +} + +/** + * e1000_mc_addr_list_update_82571 - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * @rar_used_count: the first RAR register free to program + * @rar_count: total number of supported Receive Address Registers + * + * Updates the Receive Address Registers and Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + * The parameter rar_count will usually be hw->mac.rar_entry_count + * unless there are workarounds that change this. + **/ +STATIC void +e1000_mc_addr_list_update_82571(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count, + u32 rar_used_count, u32 rar_count) +{ + DEBUGFUNC("e1000_mc_addr_list_update_82571"); + + if (e1000_get_laa_state_82571(hw)) + rar_count--; + + e1000_mc_addr_list_update_generic(hw, mc_addr_list, mc_addr_count, + rar_used_count, rar_count); +} + +/** + * e1000_setup_link_82571 - Setup flow control and link settings + * @hw: pointer to the HW structure + * + * Determines which flow control settings to use, then configures flow + * control. Calls the appropriate media-specific link configuration + * function. Assuming the adapter has a valid link partner, a valid link + * should be established. Assumes the hardware has previously been reset + * and the transmitter and receiver are not enabled. + **/ +STATIC s32 +e1000_setup_link_82571(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_setup_link_82571"); + + /* 82573 does not have a word in the NVM to determine + * the default flow control setting, so we explicitly + * set it to full. + */ + if (hw->mac.type == e1000_82573) + hw->mac.fc = e1000_fc_full; + + return e1000_setup_link_generic(hw); +} + +/** + * e1000_setup_copper_link_82571 - Configure copper link settings + * @hw: pointer to the HW structure + * + * Configures the link for auto-neg or forced speed and duplex. Then we check + * for link, once link is established calls to configure collision distance + * and flow control are called. + **/ +STATIC s32 +e1000_setup_copper_link_82571(struct e1000_hw *hw) +{ + u32 ctrl, led_ctrl; + s32 ret_val; + + DEBUGFUNC("e1000_setup_copper_link_82571"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + switch (hw->phy.type) { + case e1000_phy_m88: + ret_val = e1000_copper_link_setup_m88(hw); + break; + case e1000_phy_igp_2: + ret_val = e1000_copper_link_setup_igp(hw); + /* Setup activity LED */ + led_ctrl = E1000_READ_REG(hw, E1000_LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, E1000_LEDCTL, led_ctrl); + break; + default: + ret_val = -E1000_ERR_PHY; + break; + } + + if (ret_val) + goto out; + + ret_val = e1000_setup_copper_link_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_setup_fiber_serdes_link_82571 - Setup link for fiber/serdes + * @hw: pointer to the HW structure + * + * Configures collision distance and flow control for fiber and serdes links. + * Upon successful setup, poll for link. + **/ +STATIC s32 +e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_setup_fiber_serdes_link_82571"); + + switch (hw->mac.type) { + case e1000_82571: + case e1000_82572: + /* If SerDes loopback mode is entered, there is no form + * of reset to take the adapter out of that mode. So we + * have to explicitly take the adapter out of loopback + * mode. This prevents drivers from twidling their thumbs + * if another tool failed to take it out of loopback mode. + */ + E1000_WRITE_REG(hw, E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK); + break; + default: + break; + } + + return e1000_setup_fiber_serdes_link_generic(hw); +} + +/** + * e1000_valid_led_default_82571 - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +STATIC s32 +e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_82571"); + + ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (hw->mac.type == e1000_82573 && + *data == ID_LED_RESERVED_F746) + *data = ID_LED_DEFAULT_82573; + else if (*data == ID_LED_RESERVED_0000 || + *data == ID_LED_RESERVED_FFFF) + *data = ID_LED_DEFAULT; + +out: + return ret_val; +} + +/** + * e1000_get_laa_state_82571 - Get locally administered address state + * @hw: pointer to the HW structure + * + * Retrieve and return the current locally administed address state. + **/ +boolean_t +e1000_get_laa_state_82571(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82571 *dev_spec; + boolean_t state = FALSE; + + DEBUGFUNC("e1000_get_laa_state_82571"); + + if (hw->mac.type != e1000_82571) + goto out; + + dev_spec = (struct e1000_dev_spec_82571 *)hw->dev_spec; + + state = dev_spec->laa_is_present; + +out: + return state; +} + +/** + * e1000_set_laa_state_82571 - Set locally administered address state + * @hw: pointer to the HW structure + * @state: enable/disable locally administered address + * + * Enable/Disable the current locally administed address state. + **/ +void +e1000_set_laa_state_82571(struct e1000_hw *hw, boolean_t state) +{ + struct e1000_dev_spec_82571 *dev_spec; + + DEBUGFUNC("e1000_set_laa_state_82571"); + + if (hw->mac.type != e1000_82571) + goto out; + + dev_spec = (struct e1000_dev_spec_82571 *)hw->dev_spec; + + dev_spec->laa_is_present = state; + + /* If workaround is activated... */ + if (state == TRUE) { + /* Hold a copy of the LAA in RAR[14] This is done so that + * between the time RAR[0] gets clobbered and the time it + * gets fixed, the actual LAA is in one of the RARs and no + * incoming packets directed to this port are dropped. + * Eventually the LAA will be in RAR[0] and RAR[14]. + */ + e1000_rar_set_generic(hw, hw->mac.addr, + hw->mac.rar_entry_count - 1); + } + +out: + return; +} + +/** + * e1000_fix_nvm_checksum_82571 - Fix EEPROM checksum + * @hw: pointer to the HW structure + * + * Verifies that the EEPROM has completed the update. After updating the + * EEPROM, we need to check bit 15 in work 0x23 for the checksum fix. If + * the checksum fix is not implemented, we need to set the bit and update + * the checksum. Otherwise, if bit 15 is set and the checksum is incorrect, + * we need to return bad checksum. + **/ +static s32 +e1000_fix_nvm_checksum_82571(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_fix_nvm_checksum_82571"); + + if (nvm->type != e1000_nvm_flash_hw) + goto out; + + /* Check bit 4 of word 10h. If it is 0, firmware is done updating + * 10h-12h. Checksum may need to be fixed. + */ + ret_val = e1000_read_nvm(hw, 0x10, 1, &data); + if (ret_val) + goto out; + + if (!(data & 0x10)) { + /* Read 0x23 and check bit 15. This bit is a 1 + * when the checksum has already been fixed. If + * the checksum is still wrong and this bit is a + * 1, we need to return bad checksum. Otherwise, + * we need to set this bit to a 1 and update the + * checksum. + */ + ret_val = e1000_read_nvm(hw, 0x23, 1, &data); + if (ret_val) + goto out; + + if (!(data & 0x8000)) { + data |= 0x8000; + ret_val = e1000_write_nvm(hw, 0x23, 1, &data); + if (ret_val) + goto out; + ret_val = e1000_update_nvm_checksum(hw); + } + } + +out: + return ret_val; +} + +/** + * e1000_clear_hw_cntrs_82571 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +STATIC void +e1000_clear_hw_cntrs_82571(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_82571"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_PRC64); + temp = E1000_READ_REG(hw, E1000_PRC127); + temp = E1000_READ_REG(hw, E1000_PRC255); + temp = E1000_READ_REG(hw, E1000_PRC511); + temp = E1000_READ_REG(hw, E1000_PRC1023); + temp = E1000_READ_REG(hw, E1000_PRC1522); + temp = E1000_READ_REG(hw, E1000_PTC64); + temp = E1000_READ_REG(hw, E1000_PTC127); + temp = E1000_READ_REG(hw, E1000_PTC255); + temp = E1000_READ_REG(hw, E1000_PTC511); + temp = E1000_READ_REG(hw, E1000_PTC1023); + temp = E1000_READ_REG(hw, E1000_PTC1522); + + temp = E1000_READ_REG(hw, E1000_ALGNERRC); + temp = E1000_READ_REG(hw, E1000_RXERRC); + temp = E1000_READ_REG(hw, E1000_TNCRS); + temp = E1000_READ_REG(hw, E1000_CEXTERR); + temp = E1000_READ_REG(hw, E1000_TSCTC); + temp = E1000_READ_REG(hw, E1000_TSCTFC); + + temp = E1000_READ_REG(hw, E1000_MGTPRC); + temp = E1000_READ_REG(hw, E1000_MGTPDC); + temp = E1000_READ_REG(hw, E1000_MGTPTC); + + temp = E1000_READ_REG(hw, E1000_IAC); + temp = E1000_READ_REG(hw, E1000_ICRXOC); + + temp = E1000_READ_REG(hw, E1000_ICRXPTC); + temp = E1000_READ_REG(hw, E1000_ICRXATC); + temp = E1000_READ_REG(hw, E1000_ICTXPTC); + temp = E1000_READ_REG(hw, E1000_ICTXATC); + temp = E1000_READ_REG(hw, E1000_ICTXQEC); + temp = E1000_READ_REG(hw, E1000_ICTXQMTC); + temp = E1000_READ_REG(hw, E1000_ICRXDMTC); +} diff --git a/bsd_eth_drivers/if_em/e1000_82571.h b/bsd_eth_drivers/if_em/e1000_82571.h new file mode 100644 index 0000000..02eb341 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82571.h @@ -0,0 +1,47 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82571.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_82571_H_ +#define _E1000_82571_H_ + +#define ID_LED_RESERVED_F746 0xF746 +#define ID_LED_DEFAULT_82573 ((ID_LED_DEF1_DEF2 << 12) | \ + (ID_LED_OFF1_ON2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) + +#define E1000_GCR_L1_ACT_WITHOUT_L0S_RX 0x08000000 + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_82575.c b/bsd_eth_drivers/if_em/e1000_82575.c new file mode 100644 index 0000000..1f9b31a --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82575.c @@ -0,0 +1,1397 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82575.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +/* e1000_82575 + */ + +#include "e1000_api.h" +#include "e1000_82575.h" + +void e1000_init_function_pointers_82575(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_82575(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_82575(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_82575(struct e1000_hw *hw); +STATIC s32 e1000_acquire_phy_82575(struct e1000_hw *hw); +STATIC void e1000_release_phy_82575(struct e1000_hw *hw); +STATIC s32 e1000_acquire_nvm_82575(struct e1000_hw *hw); +STATIC void e1000_release_nvm_82575(struct e1000_hw *hw); +STATIC s32 e1000_check_for_link_82575(struct e1000_hw *hw); +STATIC s32 e1000_get_cfg_done_82575(struct e1000_hw *hw); +STATIC s32 e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +STATIC s32 e1000_init_hw_82575(struct e1000_hw *hw); +STATIC s32 e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw); +STATIC s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, + u16 *data); +STATIC void e1000_rar_set_82575(struct e1000_hw *hw, u8 *addr, u32 index); +STATIC s32 e1000_reset_hw_82575(struct e1000_hw *hw); +STATIC s32 e1000_set_d0_lplu_state_82575(struct e1000_hw *hw, + boolean_t active); +STATIC s32 e1000_setup_copper_link_82575(struct e1000_hw *hw); +STATIC s32 e1000_setup_fiber_serdes_link_82575(struct e1000_hw *hw); +STATIC s32 e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw, + u32 offset, u16 data); +STATIC void e1000_clear_hw_cntrs_82575(struct e1000_hw *hw); +static s32 e1000_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask); +static s32 e1000_configure_pcs_link_82575(struct e1000_hw *hw); +static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, + u16 *speed, u16 *duplex); +static s32 e1000_get_phy_id_82575(struct e1000_hw *hw); +static void e1000_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask); +static boolean_t e1000_sgmii_active_82575(struct e1000_hw *hw); +STATIC s32 e1000_reset_init_script_82575(struct e1000_hw* hw); + +struct e1000_dev_spec_82575 { + boolean_t sgmii_active; +}; + +/** + * e1000_init_phy_params_82575 - Init PHY func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_phy_params_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_phy_params_82575"); + + if (hw->media_type != e1000_media_type_copper) { + phy->type = e1000_phy_none; + goto out; + } + + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; + + func->acquire_phy = e1000_acquire_phy_82575; + func->check_reset_block = e1000_check_reset_block_generic; + func->commit_phy = e1000_phy_sw_reset_generic; + func->get_cfg_done = e1000_get_cfg_done_82575; + func->release_phy = e1000_release_phy_82575; + + if (e1000_sgmii_active_82575(hw) == TRUE) { + func->reset_phy = e1000_phy_hw_reset_sgmii_82575; + func->read_phy_reg = e1000_read_phy_reg_sgmii_82575; + func->write_phy_reg = e1000_write_phy_reg_sgmii_82575; + } else { + func->reset_phy = e1000_phy_hw_reset_generic; + func->read_phy_reg = e1000_read_phy_reg_igp; + func->write_phy_reg = e1000_write_phy_reg_igp; + } + + /* Set phy->phy_addr and phy->id. */ + ret_val = e1000_get_phy_id_82575(hw); + + /* Verify phy id and set remaining function pointers */ + switch (phy->id) { + case M88E1111_I_PHY_ID: + phy->type = e1000_phy_m88; + func->check_polarity = e1000_check_polarity_m88; + func->get_phy_info = e1000_get_phy_info_m88; + func->get_cable_length = e1000_get_cable_length_m88; + func->force_speed_duplex = e1000_phy_force_speed_duplex_m88; + break; + case IGP03E1000_E_PHY_ID: + phy->type = e1000_phy_igp_3; + func->check_polarity = e1000_check_polarity_igp; + func->get_phy_info = e1000_get_phy_info_igp; + func->get_cable_length = e1000_get_cable_length_igp_2; + func->force_speed_duplex = e1000_phy_force_speed_duplex_igp; + func->set_d0_lplu_state = e1000_set_d0_lplu_state_82575; + func->set_d3_lplu_state = e1000_set_d3_lplu_state_generic; + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_82575 - Init NVM func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_nvm_params_82575(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u16 size; + + DEBUGFUNC("e1000_init_nvm_params_82575"); + + nvm->opcode_bits = 8; + nvm->delay_usec = 1; + switch (nvm->override) { + case e1000_nvm_override_spi_large: + nvm->page_size = 32; + nvm->address_bits = 16; + break; + case e1000_nvm_override_spi_small: + nvm->page_size = 8; + nvm->address_bits = 8; + break; + default: + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8; + break; + } + + nvm->type = e1000_nvm_eeprom_spi; + + size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + + /* Added to a constant, "size" becomes the left-shift value + * for setting word_size. + */ + size += NVM_WORD_SIZE_BASE_SHIFT; + nvm->word_size = 1 << size; + + /* Function Pointers */ + func->acquire_nvm = e1000_acquire_nvm_82575; + func->read_nvm = e1000_read_nvm_eerd; + func->release_nvm = e1000_release_nvm_82575; + func->update_nvm = e1000_update_nvm_checksum_generic; + func->valid_led_default = e1000_valid_led_default_generic; + func->validate_nvm = e1000_validate_nvm_checksum_generic; + func->write_nvm = e1000_write_nvm_spi; + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_82575 - Init MAC func ptrs. + * @hw: pointer to the HW structure + * + * This is a function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_init_mac_params_82575(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + struct e1000_dev_spec_82575 *dev_spec; + u32 ctrl, ctrl_ext; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_mac_params_82575"); + + hw->dev_spec_size = sizeof(struct e1000_dev_spec_82575); + + /* Device-specific structure allocation */ + ret_val = e1000_alloc_zeroed_dev_spec_struct(hw, hw->dev_spec_size); + if (ret_val) + goto out; + + dev_spec = (struct e1000_dev_spec_82575 *)hw->dev_spec; + + /* Set media type */ + /* The 82575 uses bits 22:23 for link mode. The mode can be changed + * based on the EEPROM. We cannot rely upon device ID. There + * is no distinguishable difference between fiber and internal + * SerDes mode on the 82575. There can be an external PHY attached + * on the SGMII interface. For this, we'll set sgmii_active to TRUE. + */ + hw->media_type = e1000_media_type_copper; + dev_spec->sgmii_active = FALSE; + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + if ((ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) == + E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES) { + hw->media_type = e1000_media_type_internal_serdes; + } else if (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_SGMII) { + dev_spec->sgmii_active = TRUE; + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl | E1000_CTRL_I2C_ENA)); + } + + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES_82575; + /* Set if part includes ASF firmware */ + mac->asf_firmware_present = TRUE; + /* Set if manageability features are enabled. */ + mac->arc_subsystem_valid = + (E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK) + ? TRUE : FALSE; + + /* Function pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_pcie_generic; + /* reset */ + func->reset_hw = e1000_reset_hw_82575; + /* hw initialization */ + func->init_hw = e1000_init_hw_82575; + /* link setup */ + func->setup_link = e1000_setup_link_generic; + /* physical interface link setup */ + func->setup_physical_interface = + (hw->media_type == e1000_media_type_copper) + ? e1000_setup_copper_link_82575 + : e1000_setup_fiber_serdes_link_82575; + /* check for link */ + func->check_for_link = e1000_check_for_link_82575; + /* receive address register setting */ + func->rar_set = e1000_rar_set_82575; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_generic; + /* writing VFTA */ + func->write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + func->clear_vfta = e1000_clear_vfta_generic; + /* setting MTA */ + func->mta_set = e1000_mta_set_generic; + /* blink LED */ + func->blink_led = e1000_blink_led_generic; + /* setup LED */ + func->setup_led = e1000_setup_led_generic; + /* cleanup LED */ + func->cleanup_led = e1000_cleanup_led_generic; + /* turn on/off LED */ + func->led_on = e1000_led_on_generic; + func->led_off = e1000_led_off_generic; + /* remove device */ + func->remove_device = e1000_remove_device_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_82575; + /* link info */ + func->get_link_up_info = e1000_get_link_up_info_82575; + +out: + return ret_val; +} + +/** + * e1000_init_function_pointers_82575 - Init func ptrs. + * @hw: pointer to the HW structure + * + * The only function explicitly called by the api module to initialize + * all function pointers and parameters. + **/ +void +e1000_init_function_pointers_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82575"); + + hw->func.init_mac_params = e1000_init_mac_params_82575; + hw->func.init_nvm_params = e1000_init_nvm_params_82575; + hw->func.init_phy_params = e1000_init_phy_params_82575; +} + +/** + * e1000_acquire_phy_82575 - Acquire rights to access PHY + * @hw: pointer to the HW structure + * + * Acquire access rights to the correct PHY. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_acquire_phy_82575(struct e1000_hw *hw) +{ + u16 mask; + + DEBUGFUNC("e1000_acquire_phy_82575"); + + mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; + + return e1000_acquire_swfw_sync_82575(hw, mask); +} + +/** + * e1000_release_phy_82575 - Release rights to access PHY + * @hw: pointer to the HW structure + * + * A wrapper to release access rights to the correct PHY. This is a + * function pointer entry point called by the api module. + **/ +STATIC void +e1000_release_phy_82575(struct e1000_hw *hw) +{ + u16 mask; + + DEBUGFUNC("e1000_release_phy_82575"); + + mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM; + e1000_release_swfw_sync_82575(hw, mask); +} + +/** + * e1000_read_phy_reg_sgmii_82575 - Read PHY register using sgmii + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the serial gigabit media independent + * interface and stores the retrieved information in data. + **/ +STATIC s32 +e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, u16 *data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + + DEBUGFUNC("e1000_read_phy_reg_sgmii_82575"); + + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { + DEBUGOUT1("PHY Address %u is out of range\n", offset); + return -E1000_ERR_PARAM; + } + + /* Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + (E1000_I2CCMD_OPCODE_READ)); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + /* Need to byte-swap the 16-bit value. */ + *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_sgmii_82575 - Write PHY register using sgmii + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset using the serial gigabit + * media independent interface. + **/ +STATIC s32 +e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, u16 data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + u16 phy_data_swapped; + + DEBUGFUNC("e1000_write_phy_reg_sgmii_82575"); + + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + return -E1000_ERR_PARAM; + } + + /* Swap the data bytes for the I2C interface */ + phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00); + + /* Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | + phy_data_swapped); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_id_82575 - Retreive PHY addr and id + * @hw: pointer to the HW structure + * + * Retreives the PHY address and ID for both PHY's which do and do not use + * sgmi interface. + **/ +static s32 +e1000_get_phy_id_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_id; + + DEBUGFUNC("e1000_get_phy_id_82575"); + + /* For SGMII PHYs, we try the list of possible addresses until + * we find one that works. For non-SGMII PHYs + * (e.g. integrated copper PHYs), an address of 1 should + * work. The result of this function should mean phy->phy_addr + * and phy->id are set correctly. + */ + if (e1000_sgmii_active_82575(hw) == FALSE) { + phy->addr = 1; + ret_val = e1000_get_phy_id(hw); + goto out; + } + + /* The address field in the I2CCMD register is 3 bits and 0 is invalid. + * Therefore, we need to test 1-7 + */ + for (phy->addr = 1; phy->addr < 8; phy->addr++) { + ret_val = e1000_read_phy_reg_sgmii_82575(hw, PHY_ID1, &phy_id); + if (ret_val == E1000_SUCCESS) { + DEBUGOUT2("Vendor ID 0x%08X read at address %u\n", + phy_id, + phy->addr); + /* At the time of this writing, The M88 part is + * the only supported SGMII PHY product. */ + if (phy_id == M88_VENDOR) + break; + } else { + DEBUGOUT1("PHY address %u was unreadable\n", + phy->addr); + } + } + + /* A valid PHY type couldn't be found. */ + if (phy->addr == 8) { + phy->addr = 0; + ret_val = -E1000_ERR_PHY; + goto out; + } + + ret_val = e1000_get_phy_id(hw); + +out: + return ret_val; +} + +/** + * e1000_phy_hw_reset_sgmii_82575 - Performs a PHY reset + * @hw: pointer to the HW structure + * + * Resets the PHY using the serial gigabit media independent interface. + **/ +STATIC s32 +e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_phy_hw_reset_sgmii_82575"); + + /* This isn't a true "hard" reset, but is the only reset + * available to us at this time. + */ + + DEBUGOUT("Soft resetting SGMII attached PHY...\n"); + + /* SFP documentation requires the following to configure the SPF module + * to work on SGMII. No further documentation is given. + */ + ret_val = e1000_write_phy_reg(hw, 0x1B, 0x8084); + if (ret_val) + goto out; + + ret_val = e1000_phy_commit(hw); + +out: + return ret_val; +} + +/** + * e1000_set_d0_lplu_state_82575 - Set Low Power Linkup D0 state + * @hw: pointer to the HW structure + * @active: TRUE to enable LPLU, FALSE to disable + * + * Sets the LPLU D0 state according to the active flag. When + * activating LPLU this function also disables smart speed + * and vice versa. LPLU will not be activated unless the + * device autonegotiation advertisement meets standards of + * either 10 or 10/100 or 10/100/1000 at all duplexes. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +STATIC s32 +e1000_set_d0_lplu_state_82575(struct e1000_hw *hw, boolean_t active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_set_d0_lplu_state_82575"); + + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); + if (ret_val) + goto out; + + if (active) { + data |= IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + goto out; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else { + data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, + data); + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_acquire_nvm_82575 - Request for access to EEPROM + * @hw: pointer to the HW structure + * + * Acquire the necessary semaphores for exclussive access to the EEPROM. + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +STATIC s32 +e1000_acquire_nvm_82575(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_acquire_nvm_82575"); + + ret_val = e1000_acquire_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); + if (ret_val) + goto out; + + ret_val = e1000_acquire_nvm_generic(hw); + + if (ret_val) + e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); + +out: + return ret_val; +} + +/** + * e1000_release_nvm_82575 - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit, + * then release the semaphores acquired. + **/ +STATIC void +e1000_release_nvm_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_82575"); + + e1000_release_nvm_generic(hw); + e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); +} + +/** + * e1000_acquire_swfw_sync_82575 - Acquire SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Acquire the SW/FW semaphore to access the PHY or NVM. The mask + * will also specify which port we're acquiring the lock for. + **/ +static s32 +e1000_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = E1000_SUCCESS; + s32 i = 0, timeout = 200; /* FIXME: find real value to use here */ + + DEBUGFUNC("e1000_acquire_swfw_sync_82575"); + + while (i < timeout) { + if (e1000_get_hw_semaphore_generic(hw)) { + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* Firmware currently using resource (fwmask) + * or other software thread using resource (swmask) */ + e1000_put_hw_semaphore_generic(hw); + msec_delay_irq(5); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_release_swfw_sync_82575 - Release SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Release the SW/FW semaphore used to access the PHY or NVM. The mask + * will also specify which port we're releasing the lock for. + **/ +static void +e1000_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + + DEBUGFUNC("e1000_release_swfw_sync_82575"); + + while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS); + /* Empty */ + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + swfw_sync &= ~mask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); +} + +/** + * e1000_get_cfg_done_82575 - Read config done bit + * @hw: pointer to the HW structure + * + * Read the management control register for the config done bit for + * completion status. NOTE: silicon which is EEPROM-less will fail trying + * to read the config done bit, so an error is *ONLY* logged and returns + * E1000_SUCCESS. If we were to return with error, EEPROM-less silicon + * would not be able to be reset or change link. + **/ +STATIC s32 +e1000_get_cfg_done_82575(struct e1000_hw *hw) +{ + s32 timeout = PHY_CFG_TIMEOUT; + s32 ret_val = E1000_SUCCESS; + u32 mask = E1000_NVM_CFG_DONE_PORT_0; + + DEBUGFUNC("e1000_get_cfg_done_82575"); + + if (hw->bus.func == 1) + mask = E1000_NVM_CFG_DONE_PORT_1; + + while (timeout) { + if (E1000_READ_REG(hw, E1000_EEMNGCTL) & mask) + break; + msec_delay(1); + timeout--; + } + if (!timeout) { + DEBUGOUT("MNG configuration cycle has not completed.\n"); + } + + /* If EEPROM is not marked present, init the PHY manually */ + if (((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) && + (hw->phy.type == e1000_phy_igp_3)) { + e1000_phy_init_script_igp3(hw); + } + + return ret_val; +} + +/** + * e1000_get_link_up_info_82575 - Get link speed/duplex info + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * This is a wrapper function, if using the serial gigabit media independent + * interface, use pcs to retreive the link speed and duplex information. + * Otherwise, use the generic function to get the link speed and duplex info. + **/ +STATIC s32 +e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed, u16 *duplex) +{ + s32 ret_val; + + DEBUGFUNC("e1000_get_link_up_info_82575"); + + if (hw->media_type != e1000_media_type_copper || + e1000_sgmii_active_82575(hw) == TRUE) { + ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, speed, + duplex); + } else + ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed, + duplex); + + return ret_val; +} + +/** + * e1000_check_for_link_82575 - Check for link + * @hw: pointer to the HW structure + * + * If sgmii is enabled, then use the pcs register to determine link, otherwise + * use the generic interface for determining link. + **/ +STATIC s32 +e1000_check_for_link_82575(struct e1000_hw *hw) +{ + s32 ret_val; + u16 speed, duplex; + + DEBUGFUNC("e1000_check_for_link_82575"); + + /* SGMII link check is done through the PCS register. */ + if ((hw->media_type != e1000_media_type_copper) || + (e1000_sgmii_active_82575(hw) == TRUE)) + ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, &speed, + &duplex); + else + ret_val = e1000_check_for_copper_link_generic(hw); + + return ret_val; +} + +/** + * e1000_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Using the physical coding sub-layer (PCS), retreive the current speed and + * duplex, then store the values in the pointers provided. + **/ +static s32 +e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, u16 *speed, + u16 *duplex) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 pcs; + + DEBUGFUNC("e1000_get_pcs_speed_and_duplex_82575"); + + /* Set up defaults for the return values of this function */ + mac->serdes_has_link = FALSE; + *speed = 0; + *duplex = 0; + + /* Read the PCS Status register for link state. For non-copper mode, + * the status register is not accurate. The PCS status register is + * used instead. */ + pcs = E1000_READ_REG(hw, E1000_PCS_LSTAT); + + /* The link up bit determines when link is up on autoneg. The sync ok + * gets set once both sides sync up and agree upon link. Stable link + * can be determined by checking for both link up and link sync ok + */ + if ((pcs & E1000_PCS_LSTS_LINK_OK) && (pcs & E1000_PCS_LSTS_SYNK_OK)) { + mac->serdes_has_link = TRUE; + + /* Detect and store PCS speed */ + if (pcs & E1000_PCS_LSTS_SPEED_1000) { + *speed = SPEED_1000; + } else if (pcs & E1000_PCS_LSTS_SPEED_100) { + *speed = SPEED_100; + } else { + *speed = SPEED_10; + } + + /* Detect and store PCS duplex */ + if (pcs & E1000_PCS_LSTS_DUPLEX_FULL) { + *duplex = FULL_DUPLEX; + } else { + *duplex = HALF_DUPLEX; + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_rar_set_82575 - Set receive address register + * @hw: pointer to the HW structure + * @addr: pointer to the receive address + * @index: receive address array register + * + * Sets the receive address array register at index to the address passed + * in by addr. + **/ +void +e1000_rar_set_82575(struct e1000_hw *hw, u8 *addr, u32 index) +{ + DEBUGFUNC("e1000_rar_set_82575"); + + if (index < E1000_RAR_ENTRIES_82575) { + e1000_rar_set_generic(hw, addr, index); + goto out; + } + +out: + return; +} + +/** + * e1000_reset_hw_82575 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a + * function pointer entry point called by the api module. + **/ +STATIC s32 +e1000_reset_hw_82575(struct e1000_hw *hw) +{ + u32 ctrl, icr; + s32 ret_val; + + DEBUGFUNC("e1000_reset_hw_82575"); + + /* Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) { + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + } + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to MAC\n"); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) { + /* When auto config read does not complete, do not + * return with an error. This can happen in situations + * where there is no eeprom and prevents getting link. + */ + DEBUGOUT("Auto Read Done did not complete\n"); + } + + /* If EEPROM is not present, run manual init scripts */ + if ((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) + e1000_reset_init_script_82575(hw); + + /* Clear any pending interrupt events. */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + icr = E1000_READ_REG(hw, E1000_ICR); + + return ret_val; +} + +/** + * e1000_init_hw_82575 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. + **/ +STATIC s32 +e1000_init_hw_82575(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + u16 i, rar_count = mac->rar_entry_count; + + DEBUGFUNC("e1000_init_hw_82575"); + + /* Initialize identification LED */ + ret_val = e1000_id_led_init_generic(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + goto out; + } + + /* Disabling VLAN filtering */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + e1000_clear_vfta(hw); + + /* Setup the receive address. */ + e1000_init_rx_addrs_generic(hw, rar_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + + /* Setup link and flow control */ + ret_val = e1000_setup_link(hw); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82575(hw); + +out: + return ret_val; +} + +/** + * e1000_setup_copper_link_82575 - Configure copper link settings + * @hw: pointer to the HW structure + * + * Configures the link for auto-neg or forced speed and duplex. Then we check + * for link, once link is established calls to configure collision distance + * and flow control are called. + **/ +STATIC s32 +e1000_setup_copper_link_82575(struct e1000_hw *hw) +{ + u32 ctrl, led_ctrl; + s32 ret_val; + boolean_t link; + + DEBUGFUNC("e1000_setup_copper_link_82575"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + switch (hw->phy.type) { + case e1000_phy_m88: + ret_val = e1000_copper_link_setup_m88(hw); + break; + case e1000_phy_igp_3: + ret_val = e1000_copper_link_setup_igp(hw); + /* Setup activity LED */ + led_ctrl = E1000_READ_REG(hw, E1000_LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, E1000_LEDCTL, led_ctrl); + break; + default: + ret_val = -E1000_ERR_PHY; + break; + } + + if (ret_val) + goto out; + + if (hw->mac.autoneg) { + /* Setup autoneg and flow control advertisement + * and perform autonegotiation. */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + goto out; + } else { + /* PHY will be set to 10H, 10F, 100H or 100F + * depending on user settings. */ + DEBUGOUT("Forcing Speed and Duplex\n"); + ret_val = e1000_phy_force_speed_duplex(hw); + if (ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + goto out; + } + } + + ret_val = e1000_configure_pcs_link_82575(hw); + if (ret_val) + goto out; + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + ret_val = e1000_phy_has_link_generic(hw, + COPPER_LINK_UP_LIMIT, + 10, + &link); + if (ret_val) + goto out; + + if (link) { + DEBUGOUT("Valid link established!!!\n"); + /* Config the MAC and PHY after link is up */ + e1000_config_collision_dist_generic(hw); + ret_val = e1000_config_fc_after_link_up_generic(hw); + } else { + DEBUGOUT("Unable to establish link!!!\n"); + } + +out: + return ret_val; +} + +/** + * e1000_setup_fiber_serdes_link_82575 - Setup link for fiber/serdes + * @hw: pointer to the HW structure + * + * Configures speed and duplex for fiber and serdes links. + **/ +STATIC s32 +e1000_setup_fiber_serdes_link_82575(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_setup_fiber_serdes_link_82575"); + + /* On the 82575, SerDes loopback mode persists until it is + * explicitly turned off or a power cycle is performed. A read to + * the register does not indicate its status. Therefore, we ensure + * loopback mode is disabled during initialization. + */ + E1000_WRITE_REG(hw, E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK); + + /* Force link up, set 1gb, set both sw defined pins */ + reg = E1000_READ_REG(hw, E1000_CTRL); + reg |= E1000_CTRL_SLU | + E1000_CTRL_SPD_1000 | + E1000_CTRL_FRCSPD | + E1000_CTRL_SWDPIN0 | + E1000_CTRL_SWDPIN1; + E1000_WRITE_REG(hw, E1000_CTRL, reg); + + /* Set switch control to serdes energy detect */ + reg = E1000_READ_REG(hw, E1000_CONNSW); + reg |= E1000_CONNSW_ENRGSRC; + E1000_WRITE_REG(hw, E1000_CONNSW, reg); + + /* New SerDes mode allows for forcing speed or autonegotiating speed + * at 1gb. Autoneg should be default set by most drivers. This is the + * mode that will be compatible with older link partners and switches. + * However, both are supported by the hardware and some drivers/tools. + */ + reg = E1000_READ_REG(hw, E1000_PCS_LCTL); + if (hw->mac.autoneg) { + /* Set PCS register for autoneg */ + reg |= E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */ + E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */ + E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */ + E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */ + DEBUGOUT1("Configuring Autoneg; PCS_LCTL = 0x%08X\n", reg); + } else { + /* Set PCS register for forced speed */ + reg |= E1000_PCS_LCTL_FLV_LINK_UP | /* Force link up */ + E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */ + E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */ + E1000_PCS_LCTL_FSD | /* Force Speed */ + E1000_PCS_LCTL_FORCE_LINK; /* Force Link */ + DEBUGOUT1("Configuring Forced Link; PCS_LCTL = 0x%08X\n", reg); + } + E1000_WRITE_REG(hw, E1000_PCS_LCTL, reg); + + return E1000_SUCCESS; +} + +/** + * e1000_configure_pcs_link_82575 - Configure PCS link + * @hw: pointer to the HW structure + * + * Configure the physical coding sub-layer (PCS) link. The PCS link is + * only used on copper connections where the serialized gigabit media + * independent interface (sgmii) is being used. Configures the link + * for auto-negotiation or forces speed/duplex. + **/ +static s32 +e1000_configure_pcs_link_82575(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 reg = 0; + + DEBUGFUNC("e1000_configure_pcs_link_82575"); + + if (hw->media_type != e1000_media_type_copper || + e1000_sgmii_active_82575(hw) == FALSE) + goto out; + + /* For SGMII, we need to issue a PCS autoneg restart */ + reg = E1000_READ_REG(hw, E1000_PCS_LCTL); + + /* AN time out should be disabled for SGMII mode */ + reg &= ~(E1000_PCS_LCTL_AN_TIMEOUT); + + if (mac->autoneg) { + /* Make sure forced speed and force link are not set */ + reg &= ~(E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK); + + /* The PHY should be setup prior to calling this function. + * All we need to do is restart autoneg and enable autoneg. + */ + reg |= E1000_PCS_LCTL_AN_RESTART | E1000_PCS_LCTL_AN_ENABLE; + } else { + /* Set PCS regiseter for forced speed */ + + /* Turn off bits for full duplex, speed, and autoneg */ + reg &= ~(E1000_PCS_LCTL_FSV_1000 | + E1000_PCS_LCTL_FSV_100 | + E1000_PCS_LCTL_FDV_FULL | + E1000_PCS_LCTL_AN_ENABLE); + + /* Check for duplex first */ + if (mac->forced_speed_duplex & E1000_ALL_FULL_DUPLEX) + reg |= E1000_PCS_LCTL_FDV_FULL; + + /* Now set speed */ + if (mac->forced_speed_duplex & E1000_ALL_100_SPEED) + reg |= E1000_PCS_LCTL_FSV_100; + + /* Force speed and force link */ + reg |= E1000_PCS_LCTL_FSD | + E1000_PCS_LCTL_FORCE_LINK | + E1000_PCS_LCTL_FLV_LINK_UP; + + DEBUGOUT1("Wrote 0x%08X to PCS_LCTL to configure forced link\n", + reg); + } + E1000_WRITE_REG(hw, E1000_PCS_LCTL, reg); + +out: + return E1000_SUCCESS; +} + +/** + * e1000_sgmii_active_82575 - Return sgmii state + * @hw: pointer to the HW structure + * + * 82575 silicon has a serialized gigabit media independent interface (sgmii) + * which can be enabled for use in the embedded applications. Simply + * return the current state of the sgmii interface. + **/ +static boolean_t +e1000_sgmii_active_82575(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82575 *dev_spec; + boolean_t ret_val; + + DEBUGFUNC("e1000_sgmii_active_82575"); + + if (hw->mac.type != e1000_82575) { + ret_val = FALSE; + goto out; + } + + dev_spec = (struct e1000_dev_spec_82575 *)hw->dev_spec; + + ret_val = dev_spec->sgmii_active; + +out: + return ret_val; +} + +/** + * e1000_reset_init_script_82575 - Inits HW defaults after reset + * @hw: pointer to the HW structure + * + * Inits recommended HW defaults after a reset when there is no EEPROM + * detected. This is only for the 82575. + **/ +STATIC s32 +e1000_reset_init_script_82575(struct e1000_hw* hw) +{ + DEBUGFUNC("e1000_reset_init_script_82575"); + + if (hw->mac.type == e1000_82575) { + DEBUGOUT("Running reset init script for 82575\n"); + /* SerDes configuration via SERDESCTRL */ + e1000_write_8bit_ctrl_reg(hw, E1000_SCTL, 0x00, 0x0C); + e1000_write_8bit_ctrl_reg(hw, E1000_SCTL, 0x01, 0x78); + e1000_write_8bit_ctrl_reg(hw, E1000_SCTL, 0x1B, 0x23); + e1000_write_8bit_ctrl_reg(hw, E1000_SCTL, 0x23, 0x15); + + /* CCM configuration via CCMCTL register */ + e1000_write_8bit_ctrl_reg(hw, E1000_CCMCTL, 0x14, 0x00); + e1000_write_8bit_ctrl_reg(hw, E1000_CCMCTL, 0x10, 0x00); + + /* PCIe lanes configuration */ + e1000_write_8bit_ctrl_reg(hw, E1000_GIOCTL, 0x00, 0xEC); + e1000_write_8bit_ctrl_reg(hw, E1000_GIOCTL, 0x61, 0xDF); + e1000_write_8bit_ctrl_reg(hw, E1000_GIOCTL, 0x34, 0x05); + e1000_write_8bit_ctrl_reg(hw, E1000_GIOCTL, 0x2F, 0x81); + + /* PCIe PLL Configuration */ + e1000_write_8bit_ctrl_reg(hw, E1000_SCCTL, 0x02, 0x47); + e1000_write_8bit_ctrl_reg(hw, E1000_SCCTL, 0x14, 0x00); + e1000_write_8bit_ctrl_reg(hw, E1000_SCCTL, 0x10, 0x00); + } + + return E1000_SUCCESS; +} + +/** + * e1000_clear_hw_cntrs_82575 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +STATIC void +e1000_clear_hw_cntrs_82575(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_82575"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_PRC64); + temp = E1000_READ_REG(hw, E1000_PRC127); + temp = E1000_READ_REG(hw, E1000_PRC255); + temp = E1000_READ_REG(hw, E1000_PRC511); + temp = E1000_READ_REG(hw, E1000_PRC1023); + temp = E1000_READ_REG(hw, E1000_PRC1522); + temp = E1000_READ_REG(hw, E1000_PTC64); + temp = E1000_READ_REG(hw, E1000_PTC127); + temp = E1000_READ_REG(hw, E1000_PTC255); + temp = E1000_READ_REG(hw, E1000_PTC511); + temp = E1000_READ_REG(hw, E1000_PTC1023); + temp = E1000_READ_REG(hw, E1000_PTC1522); + + temp = E1000_READ_REG(hw, E1000_ALGNERRC); + temp = E1000_READ_REG(hw, E1000_RXERRC); + temp = E1000_READ_REG(hw, E1000_TNCRS); + temp = E1000_READ_REG(hw, E1000_CEXTERR); + temp = E1000_READ_REG(hw, E1000_TSCTC); + temp = E1000_READ_REG(hw, E1000_TSCTFC); + + temp = E1000_READ_REG(hw, E1000_MGTPRC); + temp = E1000_READ_REG(hw, E1000_MGTPDC); + temp = E1000_READ_REG(hw, E1000_MGTPTC); + + temp = E1000_READ_REG(hw, E1000_IAC); + temp = E1000_READ_REG(hw, E1000_ICRXOC); + + temp = E1000_READ_REG(hw, E1000_ICRXPTC); + temp = E1000_READ_REG(hw, E1000_ICRXATC); + temp = E1000_READ_REG(hw, E1000_ICTXPTC); + temp = E1000_READ_REG(hw, E1000_ICTXATC); + temp = E1000_READ_REG(hw, E1000_ICTXQEC); + temp = E1000_READ_REG(hw, E1000_ICTXQMTC); + temp = E1000_READ_REG(hw, E1000_ICRXDMTC); + + temp = E1000_READ_REG(hw, E1000_CBTMPC); + temp = E1000_READ_REG(hw, E1000_HTDPMC); + temp = E1000_READ_REG(hw, E1000_CBRMPC); + temp = E1000_READ_REG(hw, E1000_RPTHC); + temp = E1000_READ_REG(hw, E1000_HGPTC); + temp = E1000_READ_REG(hw, E1000_HTCBDPC); + temp = E1000_READ_REG(hw, E1000_HGORCL); + temp = E1000_READ_REG(hw, E1000_HGORCH); + temp = E1000_READ_REG(hw, E1000_HGOTCL); + temp = E1000_READ_REG(hw, E1000_HGOTCH); + temp = E1000_READ_REG(hw, E1000_LENERRS); + + /* This register should not be read in copper configurations */ + if (hw->media_type == e1000_media_type_internal_serdes) + temp = E1000_READ_REG(hw, E1000_SCVPC); +} diff --git a/bsd_eth_drivers/if_em/e1000_82575.h b/bsd_eth_drivers/if_em/e1000_82575.h new file mode 100644 index 0000000..0fb6fc7 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_82575.h @@ -0,0 +1,298 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_82575.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_82575_H_ +#define _E1000_82575_H_ + +/* Receive Address Register Count + * Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. + * These entries are also used for MAC-based filtering. + */ +#define E1000_RAR_ENTRIES_82575 16 + +#ifdef E1000_BIT_FIELDS +struct e1000_adv_data_desc { + u64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + u32 data; + struct { + u32 datalen :16; /* Data buffer length */ + u32 rsvd :4; + u32 dtyp :4; /* Descriptor type */ + u32 dcmd :8; /* Descriptor command */ + } config; + } lower; + union { + u32 data; + struct { + u32 status :4; /* Descriptor status */ + u32 idx :4; + u32 popts :6; /* Packet Options */ + u32 paylen :18; /* Payload length */ + } options; + } upper; +}; + +#define E1000_TXD_DTYP_ADV_C 0x2 /* Advanced Context Descriptor */ +#define E1000_TXD_DTYP_ADV_D 0x3 /* Advanced Data Descriptor */ +#define E1000_ADV_TXD_CMD_DEXT 0x20 /* Descriptor extension (0 = legacy) */ +#define E1000_ADV_TUCMD_IPV4 0x2 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADV_TUCMD_IPV6 0x0 /* IP Packet Type: 0=IPv6 */ +#define E1000_ADV_TUCMD_L4T_UDP 0x0 /* L4 Packet TYPE of UDP */ +#define E1000_ADV_TUCMD_L4T_TCP 0x4 /* L4 Packet TYPE of TCP */ +#define E1000_ADV_TUCMD_MKRREQ 0x10 /* Indicates markers are required */ +#define E1000_ADV_DCMD_EOP 0x1 /* End of Packet */ +#define E1000_ADV_DCMD_IFCS 0x2 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADV_DCMD_RS 0x8 /* Report Status */ +#define E1000_ADV_DCMD_VLE 0x40 /* Add VLAN tag */ +#define E1000_ADV_DCMD_TSE 0x80 /* TCP Seg enable */ + +struct e1000_adv_context_desc { + union { + u32 ip_config; + struct { + u32 iplen :9; + u32 maclen :7; + u32 vlan_tag :16; + } fields; + } ip_setup; + u32 seq_num; + union { + u64 l4_config; + struct { + u32 mkrloc :9; + u32 tucmd :11; + u32 dtyp :4; + u32 adv :8; + u32 rsvd :4; + u32 idx :4; + u32 l4len :8; + u32 mss :16; + } fields; + } l4_setup; +}; +#endif + +/* SRRCTL bit definitions */ +#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */ +#define E1000_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00 +#define E1000_SRRCTL_BSIZEHDRSIZE_SHIFT 2 /* Shift _left_ */ +#define E1000_SRRCTL_DESCTYPE_LEGACY 0x00000000 +#define E1000_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000 +#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION 0x06000000 +#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000 + +#define E1000_TX_HEAD_WB_ENABLE 0x1 +#define E1000_TX_SEQNUM_WB_ENABLE 0x2 + +#define E1000_MRQC_ENABLE_RSS_4Q 0x00000002 +#define E1000_MRQC_RSS_FIELD_IPV4_UDP 0x00400000 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP 0x00800000 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP_EX 0x01000000 + +#define E1000_EICR_TX_QUEUE ( \ + E1000_EICR_TX_QUEUE0 | \ + E1000_EICR_TX_QUEUE1 | \ + E1000_EICR_TX_QUEUE2 | \ + E1000_EICR_TX_QUEUE3) + +#define E1000_EICR_RX_QUEUE ( \ + E1000_EICR_RX_QUEUE0 | \ + E1000_EICR_RX_QUEUE1 | \ + E1000_EICR_RX_QUEUE2 | \ + E1000_EICR_RX_QUEUE3) + +#define E1000_EIMS_RX_QUEUE E1000_EICR_RX_QUEUE +#define E1000_EIMS_TX_QUEUE E1000_EICR_TX_QUEUE + +#define EIMS_ENABLE_MASK ( \ + E1000_EIMS_RX_QUEUE | \ + E1000_EIMS_TX_QUEUE | \ + E1000_EIMS_TCP_TIMER | \ + E1000_EIMS_OTHER) + +/* Immediate Interrupt RX (A.K.A. Low Latency Interrupt) */ +#define E1000_IMIR_PORT_IM_EN 0x00010000 /* TCP port enable */ +#define E1000_IMIR_PORT_BP 0x00020000 /* TCP port check bypass */ +#define E1000_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */ +#define E1000_IMIREXT_CTRL_URG 0x00002000 /* Check URG bit in header */ +#define E1000_IMIREXT_CTRL_ACK 0x00004000 /* Check ACK bit in header */ +#define E1000_IMIREXT_CTRL_PSH 0x00008000 /* Check PSH bit in header */ +#define E1000_IMIREXT_CTRL_RST 0x00010000 /* Check RST bit in header */ +#define E1000_IMIREXT_CTRL_SYN 0x00020000 /* Check SYN bit in header */ +#define E1000_IMIREXT_CTRL_FIN 0x00040000 /* Check FIN bit in header */ +#define E1000_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of control bits */ + +/* Receive Descriptor - Advanced */ +union e1000_adv_rx_desc { + struct { + u64 pkt_addr; /* Packet buffer address */ + u64 hdr_addr; /* Header buffer address */ + } read; + struct { + struct { + struct { + u16 pkt_info; /* RSS type, Packet type */ + u16 hdr_info; /* Split Header, + * header buffer length */ + } lo_dword; + union { + u32 rss; /* RSS Hash */ + struct { + u16 ip_id; /* IP id */ + u16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + u32 status_error; /* ext status/error */ + u16 length; /* Packet length */ + u16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define E1000_RXDADV_RSSTYPE_MASK 0x0000F000 +#define E1000_RXDADV_RSSTYPE_SHIFT 12 +#define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0 +#define E1000_RXDADV_HDRBUFLEN_SHIFT 5 +#define E1000_RXDADV_SPLITHEADER_EN 0x00001000 +#define E1000_RXDADV_SPH 0x8000 +#define E1000_RXDADV_HBO 0x00800000 + +/* RSS Hash results */ +#define E1000_RXDADV_RSSTYPE_NONE 0x00000000 +#define E1000_RXDADV_RSSTYPE_IPV4_TCP 0x00000001 +#define E1000_RXDADV_RSSTYPE_IPV4 0x00000002 +#define E1000_RXDADV_RSSTYPE_IPV6_TCP 0x00000003 +#define E1000_RXDADV_RSSTYPE_IPV6_EX 0x00000004 +#define E1000_RXDADV_RSSTYPE_IPV6 0x00000005 +#define E1000_RXDADV_RSSTYPE_IPV6_TCP_EX 0x00000006 +#define E1000_RXDADV_RSSTYPE_IPV4_UDP 0x00000007 +#define E1000_RXDADV_RSSTYPE_IPV6_UDP 0x00000008 +#define E1000_RXDADV_RSSTYPE_IPV6_UDP_EX 0x00000009 + +/* Transmit Descriptor - Advanced */ +union e1000_adv_tx_desc { + struct { + u64 buffer_addr; /* Address of descriptor's data buf */ + u32 cmd_type_len; + u32 olinfo_status; + } read; + struct { + u64 rsvd; /* Reserved */ + u32 nxtseq_seed; + u32 status; + } wb; +}; + +/* Adv Transmit Descriptor Config Masks */ +#define E1000_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */ +#define E1000_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */ +#define E1000_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */ +#define E1000_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADVTXD_DCMD_RDMA 0x04000000 /* RDMA */ +#define E1000_ADVTXD_DCMD_RS 0x08000000 /* Report Status */ +#define E1000_ADVTXD_DCMD_DDTYP_ISCSI 0x10000000 /* DDP hdr type or iSCSI */ +#define E1000_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */ +#define E1000_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */ +#define E1000_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */ +#define E1000_ADVTXD_MAC_LINKSEC 0x00040000 /* Apply LinkSec on packet */ +#define E1000_ADVTXD_MAC_TSTAMP 0x00080000 /* IEEE1588 Timestamp packet */ +#define E1000_ADVTXD_STAT_SN_CRC 0x00000002 /* NXTSEQ/SEED present in WB */ +#define E1000_ADVTXD_IDX_SHIFT 4 /* Adv desc Index shift */ +#define E1000_ADVTXD_POPTS_EOM 0x00000400 /* Enable L bit in RDMA DDP hdr */ +#define E1000_ADVTXD_POPTS_ISCO_1ST 0x00000000 /* 1st TSO of iSCSI PDU */ +#define E1000_ADVTXD_POPTS_ISCO_MDL 0x00000800 /* Middle TSO of iSCSI PDU */ +#define E1000_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */ +#define E1000_ADVTXD_POPTS_ISCO_FULL 0x00001800 /* 1st&Last TSO-full iSCSI PDU*/ +#define E1000_ADVTXD_POPTS_IPSEC 0x00000400 /* IPSec offload request */ +#define E1000_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */ + +/* Context descriptors */ +struct e1000_adv_tx_context_desc { + u32 vlan_macip_lens; + u32 seqnum_seed; + u32 type_tucmd_mlhl; + u32 mss_l4len_idx; +}; + +#define E1000_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */ +#define E1000_ADVTXD_VLAN_SHIFT 16 /* Adv ctxt vlan tag shift */ +#define E1000_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADVTXD_TUCMD_IPV6 0x00000000 /* IP Packet Type: 0=IPv6 */ +#define E1000_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */ +#define E1000_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */ +#define E1000_ADVTXD_TUCMD_IPSEC_TYPE_ESP 0x00002000 /* IPSec Type ESP */ +/* IPSec Encrypt Enable for ESP */ +#define E1000_ADVTXD_TUCMD_IPSEC_ENCRYPT_EN 0x00004000 +#define E1000_ADVTXD_TUCMD_MKRREQ 0x00002000 /* Req requires Markers and CRC */ +#define E1000_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */ +#define E1000_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */ +/* Adv ctxt IPSec SA IDX mask */ +#define E1000_ADVTXD_IPSEC_SA_INDEX_MASK 0x000000FF +/* Adv ctxt IPSec ESP len mask */ +#define E1000_ADVTXD_IPSEC_ESP_LEN_MASK 0x000000FF + +/* Additional Transmit Descriptor Control definitions */ +#define E1000_TXDCTL_QUEUE_ENABLE 0x02000000 /* Enable specific Tx Queue */ +#define E1000_TXDCTL_SWFLSH 0x04000000 /* Tx Desc. write-back flushing */ +#define E1000_TXDCTL_PRIORITY 0x08000000 /* Tx Queue Arbitration Priority + 0=low, 1=high */ + +/* Additional Receive Descriptor Control definitions */ +#define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 /* Enable specific Rx Queue */ +#define E1000_RXDCTL_SWFLSH 0x04000000 /* Rx Desc. write-back flushing */ + +/* Direct Cache Access (DCA) definitions */ +#define E1000_DCA_CTRL_DCA_ENABLE 0x00000000 /* DCA Enable */ +#define E1000_DCA_CTRL_DCA_DISABLE 0x00000001 /* DCA Disable */ + +#define E1000_DCA_CTRL_DCA_MODE_CB1 0x00 /* DCA Mode CB1 */ +#define E1000_DCA_CTRL_DCA_MODE_CB2 0x02 /* DCA Mode CB2 */ + +#define E1000_DCA_RXCTRL_CPUID_MASK 0x0000001F /* Rx CPUID Mask */ +#define E1000_DCA_RXCTRL_DESC_DCA_EN (1 << 5) /* DCA Rx Desc enable */ +#define E1000_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* DCA Rx Desc header enable */ +#define E1000_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* DCA Rx Desc payload enable */ + +#define E1000_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */ +#define E1000_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */ + + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_api.c b/bsd_eth_drivers/if_em/e1000_api.c new file mode 100644 index 0000000..320a340 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_api.c @@ -0,0 +1,1222 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_api.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#include "e1000_api.h" +#include "e1000_mac.h" +#include "e1000_nvm.h" +#include "e1000_phy.h" + +#ifndef NO_82542_SUPPORT +extern void e1000_init_function_pointers_82542(struct e1000_hw *hw); +#endif +extern void e1000_init_function_pointers_82543(struct e1000_hw *hw); +extern void e1000_init_function_pointers_82540(struct e1000_hw *hw); +extern void e1000_init_function_pointers_82571(struct e1000_hw *hw); +extern void e1000_init_function_pointers_82541(struct e1000_hw *hw); +extern void e1000_init_function_pointers_80003es2lan(struct e1000_hw *hw); +#ifndef NO_ICH8LAN_SUPPORT +extern void e1000_init_function_pointers_ich8lan(struct e1000_hw *hw); +#endif +extern void e1000_init_function_pointers_82575(struct e1000_hw *hw); + +/** + * e1000_init_mac_params - Initialize MAC function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the MAC + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 +e1000_init_mac_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->func.init_mac_params != NULL) { + ret_val = hw->func.init_mac_params(hw); + if (ret_val) { + DEBUGOUT("MAC Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("mac.init_mac_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params - Initialize NVM function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the NVM + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 +e1000_init_nvm_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->func.init_nvm_params != NULL) { + ret_val = hw->func.init_nvm_params(hw); + if (ret_val) { + DEBUGOUT("NVM Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("nvm.init_nvm_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_phy_params - Initialize PHY function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the PHY + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 +e1000_init_phy_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->func.init_phy_params != NULL) { + ret_val = hw->func.init_phy_params(hw); + if (ret_val) { + DEBUGOUT("PHY Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("phy.init_phy_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_set_mac_type - Sets MAC type + * @hw: pointer to the HW structure + * + * This function sets the mac type of the adapter based on the + * device ID stored in the hw structure. + * MUST BE FIRST FUNCTION CALLED (explicitly or through + * e1000_setup_init_funcs()). + **/ +s32 +e1000_set_mac_type(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_set_mac_type"); + + switch (hw->device_id) { +#ifndef NO_82542_SUPPORT + case E1000_DEV_ID_82542: + mac->type = e1000_82542; + break; +#endif + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + mac->type = e1000_82543; + break; + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + mac->type = e1000_82544; + break; + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82540EP: + case E1000_DEV_ID_82540EP_LOM: + case E1000_DEV_ID_82540EP_LP: + mac->type = e1000_82540; + break; + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + mac->type = e1000_82545; + break; + case E1000_DEV_ID_82545GM_COPPER: + case E1000_DEV_ID_82545GM_FIBER: + case E1000_DEV_ID_82545GM_SERDES: + mac->type = e1000_82545_rev_3; + break; + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546EB_QUAD_COPPER: + mac->type = e1000_82546; + break; + case E1000_DEV_ID_82546GB_COPPER: + case E1000_DEV_ID_82546GB_FIBER: + case E1000_DEV_ID_82546GB_SERDES: + case E1000_DEV_ID_82546GB_PCIE: + case E1000_DEV_ID_82546GB_QUAD_COPPER: + case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: + mac->type = e1000_82546_rev_3; + break; + case E1000_DEV_ID_82541EI: + case E1000_DEV_ID_82541EI_MOBILE: + case E1000_DEV_ID_82541ER_LOM: + mac->type = e1000_82541; + break; + case E1000_DEV_ID_82541ER: + case E1000_DEV_ID_82541GI: + case E1000_DEV_ID_82541GI_LF: + case E1000_DEV_ID_82541GI_MOBILE: + mac->type = e1000_82541_rev_2; + break; + case E1000_DEV_ID_82547EI: + case E1000_DEV_ID_82547EI_MOBILE: + mac->type = e1000_82547; + break; + case E1000_DEV_ID_82547GI: + mac->type = e1000_82547_rev_2; + break; + case E1000_DEV_ID_82571EB_COPPER: + case E1000_DEV_ID_82571EB_FIBER: + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82571EB_QUAD_COPPER: + case E1000_DEV_ID_82571EB_QUAD_FIBER: + case E1000_DEV_ID_82571EB_QUAD_COPPER_LP: + mac->type = e1000_82571; + break; + case E1000_DEV_ID_82572EI: + case E1000_DEV_ID_82572EI_COPPER: + case E1000_DEV_ID_82572EI_FIBER: + case E1000_DEV_ID_82572EI_SERDES: + mac->type = e1000_82572; + break; + case E1000_DEV_ID_82573E: + case E1000_DEV_ID_82573E_IAMT: + case E1000_DEV_ID_82573L: + mac->type = e1000_82573; + break; + case E1000_DEV_ID_80003ES2LAN_COPPER_DPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + case E1000_DEV_ID_80003ES2LAN_COPPER_SPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_SPT: + mac->type = e1000_80003es2lan; + break; +#ifndef NO_ICH8LAN_SUPPORT + case E1000_DEV_ID_ICH8_IFE: + case E1000_DEV_ID_ICH8_IFE_GT: + case E1000_DEV_ID_ICH8_IFE_G: + case E1000_DEV_ID_ICH8_IGP_M: + case E1000_DEV_ID_ICH8_IGP_M_AMT: + case E1000_DEV_ID_ICH8_IGP_AMT: + case E1000_DEV_ID_ICH8_IGP_C: + mac->type = e1000_ich8lan; + break; + case E1000_DEV_ID_ICH9_IFE: + case E1000_DEV_ID_ICH9_IFE_GT: + case E1000_DEV_ID_ICH9_IFE_G: + case E1000_DEV_ID_ICH9_IGP_AMT: + case E1000_DEV_ID_ICH9_IGP_C: + mac->type = e1000_ich9lan; + break; +#endif + case E1000_DEV_ID_82575EB_COPPER: + case E1000_DEV_ID_82575EB_FIBER_SERDES: + case E1000_DEV_ID_82575EM_COPPER: + case E1000_DEV_ID_82575EM_FIBER_SERDES: + case E1000_DEV_ID_82575GB_QUAD_COPPER: + mac->type = e1000_82575; + break; + default: + /* Should never have loaded on this device */ + ret_val = -E1000_ERR_MAC_INIT; + break; + } + + return ret_val; +} + +/** + * e1000_setup_init_funcs - Initializes function pointers + * @hw: pointer to the HW structure + * @init_device: TRUE will initialize the rest of the function pointers + * getting the device ready for use. FALSE will only set + * MAC type and the function pointers for the other init + * functions. Passing FALSE will not generate any hardware + * reads or writes. + * + * This function must be called by a driver in order to use the rest + * of the 'shared' code files. Called by drivers only. + **/ +s32 +e1000_setup_init_funcs(struct e1000_hw *hw, boolean_t init_device) +{ + s32 ret_val; + + /* Can't do much good without knowing the MAC type. + */ + ret_val = e1000_set_mac_type(hw); + if (ret_val) { + DEBUGOUT("ERROR: MAC type could not be set properly.\n"); + goto out; + } + + if (!hw->hw_addr) { + DEBUGOUT("ERROR: Registers not mapped\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + /* Init some generic function pointers that are currently all pointing + * to generic implementations. We do this first allowing a driver + * module to override it afterwards. + */ + hw->func.config_collision_dist = e1000_config_collision_dist_generic; + hw->func.rar_set = e1000_rar_set_generic; + hw->func.validate_mdi_setting = e1000_validate_mdi_setting_generic; + hw->func.mng_host_if_write = e1000_mng_host_if_write_generic; + hw->func.mng_write_cmd_header = e1000_mng_write_cmd_header_generic; + hw->func.mng_enable_host_if = e1000_mng_enable_host_if_generic; + hw->func.wait_autoneg = e1000_wait_autoneg_generic; + hw->func.reload_nvm = e1000_reload_nvm_generic; + + /* Set up the init function pointers. These are functions within the + * adapter family file that sets up function pointers for the rest of + * the functions in that family. + */ + switch (hw->mac.type) { +#ifndef NO_82542_SUPPORT + case e1000_82542: + e1000_init_function_pointers_82542(hw); + break; +#endif + case e1000_82543: + case e1000_82544: + e1000_init_function_pointers_82543(hw); + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + e1000_init_function_pointers_82540(hw); + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + e1000_init_function_pointers_82541(hw); + break; + case e1000_82571: + case e1000_82572: + case e1000_82573: + e1000_init_function_pointers_82571(hw); + break; + case e1000_80003es2lan: + e1000_init_function_pointers_80003es2lan(hw); + break; +#ifndef NO_ICH8LAN_SUPPORT + case e1000_ich8lan: + case e1000_ich9lan: + e1000_init_function_pointers_ich8lan(hw); + break; +#endif + case e1000_82575: + e1000_init_function_pointers_82575(hw); + break; + default: + DEBUGOUT("Hardware not supported\n"); + ret_val = -E1000_ERR_CONFIG; + break; + } + + /* Initialize the rest of the function pointers. These require some + * register reads/writes in some cases. + */ + if ((ret_val == E1000_SUCCESS) && (init_device == TRUE)) { + ret_val = e1000_init_mac_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_nvm_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_phy_params(hw); + if (ret_val) + goto out; + + } + +out: + return ret_val; +} + +/** + * e1000_remove_device - Free device specific structure + * @hw: pointer to the HW structure + * + * If a device specific structure was allocated, this function will + * free it. This is a function pointer entry point called by drivers. + **/ +void +e1000_remove_device(struct e1000_hw *hw) +{ + if (hw->func.remove_device != NULL) + hw->func.remove_device(hw); +} + +/** + * e1000_get_bus_info - Obtain bus information for adapter + * @hw: pointer to the HW structure + * + * This will obtain information about the HW bus for which the + * adaper is attached and stores it in the hw structure. This is a + * function pointer entry point called by drivers. + **/ +s32 +e1000_get_bus_info(struct e1000_hw *hw) +{ + if (hw->func.get_bus_info != NULL) + return hw->func.get_bus_info(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_clear_vfta - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * This clears the VLAN filter table on the adapter. This is a function + * pointer entry point called by drivers. + **/ +void +e1000_clear_vfta(struct e1000_hw *hw) +{ + if (hw->func.clear_vfta != NULL) + hw->func.clear_vfta (hw); +} + +/** + * e1000_write_vfta - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: the 32-bit offset in which to write the value to. + * @value: the 32-bit value to write at location offset. + * + * This writes a 32-bit value to a 32-bit offset in the VLAN filter + * table. This is a function pointer entry point called by drivers. + **/ +void +e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value) +{ + if (hw->func.write_vfta != NULL) + hw->func.write_vfta(hw, offset, value); +} + +/** + * e1000_mc_addr_list_update - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * @rar_used_count: the first RAR register free to program + * @rar_count: total number of supported Receive Address Registers + * + * Updates the Receive Address Registers and Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + * The parameter rar_count will usually be hw->mac.rar_entry_count + * unless there are workarounds that change this. Currently no func pointer + * exists and all implementations are handled in the generic version of this + * function. + **/ +void +e1000_mc_addr_list_update(struct e1000_hw *hw, + u8 *mc_addr_list, + u32 mc_addr_count, + u32 rar_used_count, + u32 rar_count) +{ + if (hw->func.mc_addr_list_update != NULL) + hw->func.mc_addr_list_update(hw, + mc_addr_list, + mc_addr_count, + rar_used_count, + rar_count); +} + +/** + * e1000_force_mac_fc - Force MAC flow control + * @hw: pointer to the HW structure + * + * Force the MAC's flow control settings. Currently no func pointer exists + * and all implementations are handled in the generic version of this + * function. + **/ +s32 +e1000_force_mac_fc(struct e1000_hw *hw) +{ + return e1000_force_mac_fc_generic(hw); +} + +/** + * e1000_check_for_link - Check/Store link connection + * @hw: pointer to the HW structure + * + * This checks the link condition of the adapter and stores the + * results in the hw->mac structure. This is a function pointer entry + * point called by drivers. + **/ +s32 +e1000_check_for_link(struct e1000_hw *hw) +{ + if (hw->func.check_for_link != NULL) + return hw->func.check_for_link(hw); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_check_mng_mode - Check management mode + * @hw: pointer to the HW structure + * + * This checks if the adapter has manageability enabled. + * This is a function pointer entry point called by drivers. + **/ +boolean_t +e1000_check_mng_mode(struct e1000_hw *hw) +{ + if (hw->func.check_mng_mode != NULL) + return hw->func.check_mng_mode(hw); + else + return FALSE; +} + +/** + * e1000_mng_write_dhcp_info - Writes DHCP info to host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface + * @length: size of the buffer + * + * Writes the DHCP information to the host interface. + **/ +s32 +e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length) +{ + return e1000_mng_write_dhcp_info_generic(hw, buffer, length); +} + +/** + * e1000_reset_hw - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a function pointer + * entry point called by drivers. + **/ +s32 +e1000_reset_hw(struct e1000_hw *hw) +{ + if (hw->func.reset_hw != NULL) + return hw->func.reset_hw(hw); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_init_hw - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. This is a function + * pointer entry point called by drivers. + **/ +s32 +e1000_init_hw(struct e1000_hw *hw) +{ + if (hw->func.init_hw != NULL) + return hw->func.init_hw(hw); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_setup_link - Configures link and flow control + * @hw: pointer to the HW structure + * + * This configures link and flow control settings for the adapter. This + * is a function pointer entry point called by drivers. While modules can + * also call this, they probably call their own version of this function. + **/ +s32 +e1000_setup_link(struct e1000_hw *hw) +{ + if (hw->func.setup_link != NULL) + return hw->func.setup_link(hw); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_get_speed_and_duplex - Returns current speed and duplex + * @hw: pointer to the HW structure + * @speed: pointer to a 16-bit value to store the speed + * @duplex: pointer to a 16-bit value to store the duplex. + * + * This returns the speed and duplex of the adapter in the two 'out' + * variables passed in. This is a function pointer entry point called + * by drivers. + **/ +s32 +e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex) +{ + if (hw->func.get_link_up_info != NULL) + return hw->func.get_link_up_info(hw, speed, duplex); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_setup_led - Configures SW controllable LED + * @hw: pointer to the HW structure + * + * This prepares the SW controllable LED for use and saves the current state + * of the LED so it can be later restored. This is a function pointer entry + * point called by drivers. + **/ +s32 +e1000_setup_led(struct e1000_hw *hw) +{ + if (hw->func.setup_led != NULL) + return hw->func.setup_led(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_cleanup_led - Restores SW controllable LED + * @hw: pointer to the HW structure + * + * This restores the SW controllable LED to the value saved off by + * e1000_setup_led. This is a function pointer entry point called by drivers. + **/ +s32 +e1000_cleanup_led(struct e1000_hw *hw) +{ + if (hw->func.cleanup_led != NULL) + return hw->func.cleanup_led(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_blink_led - Blink SW controllable LED + * @hw: pointer to the HW structure + * + * This starts the adapter LED blinking. Request the LED to be setup first + * and cleaned up after. This is a function pointer entry point called by + * drivers. + **/ +s32 +e1000_blink_led(struct e1000_hw *hw) +{ + if (hw->func.blink_led != NULL) + return hw->func.blink_led(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_led_on - Turn on SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED on. This is a function pointer entry point + * called by drivers. + **/ +s32 +e1000_led_on(struct e1000_hw *hw) +{ + if (hw->func.led_on != NULL) + return hw->func.led_on(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_led_off - Turn off SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED off. This is a function pointer entry point + * called by drivers. + **/ +s32 +e1000_led_off(struct e1000_hw *hw) +{ + if (hw->func.led_off != NULL) + return hw->func.led_off(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_reset_adaptive - Reset adaptive IFS + * @hw: pointer to the HW structure + * + * Resets the adaptive IFS. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +void +e1000_reset_adaptive(struct e1000_hw *hw) +{ + e1000_reset_adaptive_generic(hw); +} + +/** + * e1000_update_adaptive - Update adaptive IFS + * @hw: pointer to the HW structure + * + * Updates adapter IFS. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +void +e1000_update_adaptive(struct e1000_hw *hw) +{ + e1000_update_adaptive_generic(hw); +} + +/** + * e1000_disable_pcie_master - Disable PCI-Express master access + * @hw: pointer to the HW structure + * + * Disables PCI-Express master access and verifies there are no pending + * requests. Currently no func pointer exists and all implementations are + * handled in the generic version of this function. + **/ +s32 +e1000_disable_pcie_master(struct e1000_hw *hw) +{ + return e1000_disable_pcie_master_generic(hw); +} + +/** + * e1000_config_collision_dist - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. + **/ +void +e1000_config_collision_dist(struct e1000_hw *hw) +{ + if (hw->func.config_collision_dist != NULL) + hw->func.config_collision_dist(hw); +} + +/** + * e1000_rar_set - Sets a receive address register + * @hw: pointer to the HW structure + * @addr: address to set the RAR to + * @index: the RAR to set + * + * Sets a Receive Address Register (RAR) to the specified address. + **/ +void +e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index) +{ + if (hw->func.rar_set != NULL) + hw->func.rar_set(hw, addr, index); +} + +/** + * e1000_validate_mdi_setting - Ensures valid MDI/MDIX SW state + * @hw: pointer to the HW structure + * + * Ensures that the MDI/MDIX SW state is valid. + **/ +s32 +e1000_validate_mdi_setting(struct e1000_hw *hw) +{ + if (hw->func.validate_mdi_setting != NULL) + return hw->func.validate_mdi_setting(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_mta_set - Sets multicast table bit + * @hw: pointer to the HW structure + * @hash_value: Multicast hash value. + * + * This sets the bit in the multicast table corresponding to the + * hash value. This is a function pointer entry point called by drivers. + **/ +void +e1000_mta_set(struct e1000_hw *hw, u32 hash_value) +{ + if (hw->func.mta_set != NULL) + hw->func.mta_set(hw, hash_value); +} + +/** + * e1000_hash_mc_addr - Determines address location in multicast table + * @hw: pointer to the HW structure + * @mc_addr: Multicast address to hash. + * + * This hashes an address to determine its location in the multicast + * table. Currently no func pointer exists and all implementations + * are handled in the generic version of this function. + **/ +u32 +e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) +{ + return e1000_hash_mc_addr_generic(hw, mc_addr); +} + +/** + * e1000_enable_tx_pkt_filtering - Enable packet filtering on TX + * @hw: pointer to the HW structure + * + * Enables packet filtering on transmit packets if manageability is enabled + * and host interface is enabled. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +boolean_t +e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) +{ + return e1000_enable_tx_pkt_filtering_generic(hw); +} + +/** + * e1000_mng_host_if_write - Writes to the manageability host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface buffer + * @length: size of the buffer + * @offset: location in the buffer to write to + * @sum: sum of the data (not checksum) + * + * This function writes the buffer content at the offset given on the host if. + * It also does alignment considerations to do the writes in most efficient + * way. Also fills up the sum of the buffer in *buffer parameter. + **/ +s32 +e1000_mng_host_if_write(struct e1000_hw * hw, u8 *buffer, u16 length, + u16 offset, u8 *sum) +{ + if (hw->func.mng_host_if_write != NULL) + return hw->func.mng_host_if_write(hw, buffer, length, offset, + sum); + else + return E1000_NOT_IMPLEMENTED; +} + +/** + * e1000_mng_write_cmd_header - Writes manageability command header + * @hw: pointer to the HW structure + * @hdr: pointer to the host interface command header + * + * Writes the command header after does the checksum calculation. + **/ +s32 +e1000_mng_write_cmd_header(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr) +{ + if (hw->func.mng_write_cmd_header != NULL) + return hw->func.mng_write_cmd_header(hw, hdr); + else + return E1000_NOT_IMPLEMENTED; +} + +/** + * e1000_mng_enable_host_if - Checks host interface is enabled + * @hw: pointer to the HW structure + * + * Returns E1000_success upon success, else E1000_ERR_HOST_INTERFACE_COMMAND + * + * This function checks whether the HOST IF is enabled for command operaton + * and also checks whether the previous command is completed. It busy waits + * in case of previous command is not completed. + **/ +s32 +e1000_mng_enable_host_if(struct e1000_hw * hw) +{ + if (hw->func.mng_enable_host_if != NULL) + return hw->func.mng_enable_host_if(hw); + else + return E1000_NOT_IMPLEMENTED; +} + +/** + * e1000_wait_autoneg - Waits for autonegotiation completion + * @hw: pointer to the HW structure + * + * Waits for autoneg to complete. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +s32 +e1000_wait_autoneg(struct e1000_hw *hw) +{ + if (hw->func.wait_autoneg != NULL) + return hw->func.wait_autoneg(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_check_reset_block - Verifies PHY can be reset + * @hw: pointer to the HW structure + * + * Checks if the PHY is in a state that can be reset or if manageability + * has it tied up. This is a function pointer entry point called by drivers. + **/ +s32 +e1000_check_reset_block(struct e1000_hw *hw) +{ + if (hw->func.check_reset_block != NULL) + return hw->func.check_reset_block(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_read_phy_reg - Reads PHY register + * @hw: pointer to the HW structure + * @offset: the register to read + * @data: the buffer to store the 16-bit read. + * + * Reads the PHY register and returns the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 +e1000_read_phy_reg(struct e1000_hw *hw, u32 offset, u16 *data) +{ + if (hw->func.read_phy_reg != NULL) + return hw->func.read_phy_reg(hw, offset, data); + else + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg - Writes PHY register + * @hw: pointer to the HW structure + * @offset: the register to write + * @data: the value to write. + * + * Writes the PHY register at offset with the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 +e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data) +{ + if (hw->func.write_phy_reg != NULL) + return hw->func.write_phy_reg(hw, offset, data); + else + return E1000_SUCCESS; +} + +/** + * e1000_read_kmrn_reg - Reads register using Kumeran interface + * @hw: pointer to the HW structure + * @offset: the register to read + * @data: the location to store the 16-bit value read. + * + * Reads a register out of the Kumeran interface. Currently no func pointer + * exists and all implementations are handled in the generic version of + * this function. + **/ +s32 +e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return e1000_read_kmrn_reg_generic(hw, offset, data); +} + +/** + * e1000_write_kmrn_reg - Writes register using Kumeran interface + * @hw: pointer to the HW structure + * @offset: the register to write + * @data: the value to write. + * + * Writes a register to the Kumeran interface. Currently no func pointer + * exists and all implementations are handled in the generic version of + * this function. + **/ +s32 +e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data) +{ + return e1000_write_kmrn_reg_generic(hw, offset, data); +} + +/** + * e1000_get_cable_length - Retrieves cable length estimation + * @hw: pointer to the HW structure + * + * This function estimates the cable length and stores them in + * hw->phy.min_length and hw->phy.max_length. This is a function pointer + * entry point called by drivers. + **/ +s32 +e1000_get_cable_length(struct e1000_hw *hw) +{ + if (hw->func.get_cable_length != NULL) + return hw->func.get_cable_length(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_info - Retrieves PHY information from registers + * @hw: pointer to the HW structure + * + * This function gets some information from various PHY registers and + * populates hw->phy values with it. This is a function pointer entry + * point called by drivers. + **/ +s32 +e1000_get_phy_info(struct e1000_hw *hw) +{ + if (hw->func.get_phy_info != NULL) + return hw->func.get_phy_info(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_phy_hw_reset - Hard PHY reset + * @hw: pointer to the HW structure + * + * Performs a hard PHY reset. This is a function pointer entry point called + * by drivers. + **/ +s32 +e1000_phy_hw_reset(struct e1000_hw *hw) +{ + if (hw->func.reset_phy != NULL) + return hw->func.reset_phy(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_phy_commit - Soft PHY reset + * @hw: pointer to the HW structure + * + * Performs a soft PHY reset on those that apply. This is a function pointer + * entry point called by drivers. + **/ +s32 +e1000_phy_commit(struct e1000_hw *hw) +{ + if (hw->func.commit_phy != NULL) + return hw->func.commit_phy(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_set_d3_lplu_state - Sets low power link up state for D0 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D0 + * and SmartSpeed is disabled when active is true, else clear lplu for D0 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 +e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active) +{ + if (hw->func.set_d0_lplu_state != NULL) + return hw->func.set_d0_lplu_state(hw, active); + else + return E1000_SUCCESS; +} + +/** + * e1000_set_d3_lplu_state - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 +e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active) +{ + if (hw->func.set_d3_lplu_state != NULL) + return hw->func.set_d3_lplu_state(hw, active); + else + return E1000_SUCCESS; +} + +/** + * e1000_read_mac_addr - Reads MAC address + * @hw: pointer to the HW structure + * + * Reads the MAC address out of the adapter and stores it in the HW structure. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 +e1000_read_mac_addr(struct e1000_hw *hw) +{ + return e1000_read_mac_addr_generic(hw); +} + +/** + * e1000_read_part_num - Read device part number + * @hw: pointer to the HW structure + * @part_num: pointer to device part number + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in part_num. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 +e1000_read_part_num(struct e1000_hw *hw, u32 *part_num) +{ + return e1000_read_part_num_generic(hw, part_num); +} + +/** + * e1000_validate_nvm_checksum - Verifies NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Validates the NVM checksum is correct. This is a function pointer entry + * point called by drivers. + **/ +s32 +e1000_validate_nvm_checksum(struct e1000_hw *hw) +{ + if (hw->func.validate_nvm != NULL) + return hw->func.validate_nvm(hw); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_update_nvm_checksum - Updates NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Updates the NVM checksum. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +s32 +e1000_update_nvm_checksum(struct e1000_hw *hw) +{ + if (hw->func.update_nvm != NULL) + return hw->func.update_nvm(hw); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_reload_nvm - Reloads EEPROM + * @hw: pointer to the HW structure + * + * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the + * extended control register. + **/ +void +e1000_reload_nvm(struct e1000_hw *hw) +{ + if (hw->func.reload_nvm != NULL) + hw->func.reload_nvm(hw); +} + +/** + * e1000_read_nvm - Reads NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to read + * @data: pointer to the properly sized buffer for the data. + * + * Reads 16-bit chunks of data from the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 +e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->func.read_nvm != NULL) + return hw->func.read_nvm(hw, offset, words, data); + else + return -E1000_ERR_CONFIG; +} + +/** + * e1000_write_nvm - Writes to NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to write + * @data: pointer to the properly sized buffer for the data. + * + * Writes 16-bit chunks of data to the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 +e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->func.write_nvm != NULL) + return hw->func.write_nvm(hw, offset, words, data); + else + return E1000_SUCCESS; +} + +/** + * e1000_write_8bit_ctrl_reg - Writes 8bit Control register + * @hw: pointer to the HW structure + * @reg: 32bit register offset + * @offset: the register to write + * @data: the value to write. + * + * Writes the PHY register at offset with the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 +e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, u32 offset, u8 data) +{ + return e1000_write_8bit_ctrl_reg_generic(hw, reg, offset, data); +} diff --git a/bsd_eth_drivers/if_em/e1000_api.h b/bsd_eth_drivers/if_em/e1000_api.h new file mode 100644 index 0000000..0c277c5 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_api.h @@ -0,0 +1,165 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_api.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_API_H_ +#define _E1000_API_H_ + +#include "e1000_hw.h" + +s32 e1000_set_mac_type(struct e1000_hw *hw); +s32 e1000_setup_init_funcs(struct e1000_hw *hw, boolean_t init_device); +s32 e1000_init_mac_params(struct e1000_hw *hw); +s32 e1000_init_nvm_params(struct e1000_hw *hw); +s32 e1000_init_phy_params(struct e1000_hw *hw); +void e1000_remove_device(struct e1000_hw *hw); +s32 e1000_get_bus_info(struct e1000_hw *hw); +void e1000_clear_vfta(struct e1000_hw *hw); +void e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value); +s32 e1000_force_mac_fc(struct e1000_hw *hw); +s32 e1000_check_for_link(struct e1000_hw *hw); +s32 e1000_reset_hw(struct e1000_hw *hw); +s32 e1000_init_hw(struct e1000_hw *hw); +s32 e1000_setup_link(struct e1000_hw *hw); +s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +s32 e1000_disable_pcie_master(struct e1000_hw *hw); +void e1000_config_collision_dist(struct e1000_hw *hw); +void e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index); +void e1000_mta_set(struct e1000_hw *hw, u32 hash_value); +u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr); +void e1000_mc_addr_list_update(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count, + u32 rar_used_count, u32 rar_count); +s32 e1000_setup_led(struct e1000_hw *hw); +s32 e1000_cleanup_led(struct e1000_hw *hw); +s32 e1000_check_reset_block(struct e1000_hw *hw); +s32 e1000_blink_led(struct e1000_hw *hw); +s32 e1000_led_on(struct e1000_hw *hw); +s32 e1000_led_off(struct e1000_hw *hw); +void e1000_reset_adaptive(struct e1000_hw *hw); +void e1000_update_adaptive(struct e1000_hw *hw); +s32 e1000_get_cable_length(struct e1000_hw *hw); +s32 e1000_validate_mdi_setting(struct e1000_hw *hw); +s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, + u32 offset, u8 data); +s32 e1000_get_phy_info(struct e1000_hw *hw); +s32 e1000_phy_hw_reset(struct e1000_hw *hw); +s32 e1000_phy_commit(struct e1000_hw *hw); +s32 e1000_read_mac_addr(struct e1000_hw *hw); +s32 e1000_read_part_num(struct e1000_hw *hw, u32 *part_num); +void e1000_reload_nvm(struct e1000_hw *hw); +s32 e1000_update_nvm_checksum(struct e1000_hw *hw); +s32 e1000_validate_nvm_checksum(struct e1000_hw *hw); +s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +s32 e1000_wait_autoneg(struct e1000_hw *hw); +s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active); +s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active); +boolean_t e1000_check_mng_mode(struct e1000_hw *hw); +boolean_t e1000_enable_mng_pass_thru(struct e1000_hw *hw); +boolean_t e1000_enable_tx_pkt_filtering(struct e1000_hw *hw); +s32 e1000_mng_enable_host_if(struct e1000_hw *hw); +s32 e1000_mng_host_if_write(struct e1000_hw *hw, + u8 *buffer, u16 length, u16 offset, u8 *sum); +s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr); +s32 e1000_mng_write_dhcp_info(struct e1000_hw * hw, + u8 *buffer, u16 length); +void e1000_tbi_adjust_stats_82543(struct e1000_hw *hw, + struct e1000_hw_stats *stats, + u32 frame_len, u8 *mac_addr); +void e1000_set_tbi_compatibility_82543(struct e1000_hw *hw, + boolean_t state); +boolean_t e1000_tbi_sbp_enabled_82543(struct e1000_hw *hw); +#ifndef NO_82542_SUPPORT +u32 e1000_translate_register_82542(u32 reg); +#endif +void e1000_init_script_state_82541(struct e1000_hw *hw, boolean_t state); +boolean_t e1000_get_laa_state_82571(struct e1000_hw *hw); +void e1000_set_laa_state_82571(struct e1000_hw *hw, boolean_t state); +#ifndef NO_ICH8LAN_SUPPORT +void e1000_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw, + boolean_t state); +void e1000_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw); +void e1000_gig_downshift_workaround_ich8lan(struct e1000_hw *hw); +#endif + + +/* TBI_ACCEPT macro definition: + * + * This macro requires: + * adapter = a pointer to struct e1000_hw + * status = the 8 bit status field of the RX descriptor with EOP set + * error = the 8 bit error field of the RX descriptor with EOP set + * length = the sum of all the length fields of the RX descriptors that + * make up the current frame + * last_byte = the last byte of the frame DMAed by the hardware + * max_frame_length = the maximum frame length we want to accept. + * min_frame_length = the minimum frame length we want to accept. + * + * This macro is a conditional that should be used in the interrupt + * handler's Rx processing routine when RxErrors have been detected. + * + * Typical use: + * ... + * if (TBI_ACCEPT) { + * accept_frame = TRUE; + * e1000_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = FALSE; + * } + * ... + */ + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +#define TBI_ACCEPT(a, status, errors, length, last_byte) \ + (e1000_tbi_sbp_enabled_82543(a) && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + (((status) & E1000_RXD_STAT_VP) ? \ + (((length) > ((a)->mac.min_frame_size - VLAN_TAG_SIZE)) && \ + ((length) <= ((a)->mac.max_frame_size + 1))) : \ + (((length) > (a)->mac.min_frame_size) && \ + ((length) <= ((a)->mac.max_frame_size + VLAN_TAG_SIZE + 1))))) + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_defines.h b/bsd_eth_drivers/if_em/e1000_defines.h new file mode 100644 index 0000000..567a86a --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_defines.h @@ -0,0 +1,1403 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_defines.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_DEFINES_H_ +#define _E1000_DEFINES_H_ + +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ +/* Extended desc bits for Linksec and timesync */ +/* Number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ +#define E1000_WUC_SPM 0x80000000 /* Enable SPM */ + +/* Wake Up Filter Control */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */ +#define E1000_WUFC_IGNORE_TCO 0x00008000 /* Ignore WakeOn TCO packets */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ +#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */ +#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */ +#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */ +#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */ +#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */ +#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Wake Up Status */ +#define E1000_WUS_LNKC E1000_WUFC_LNKC +#define E1000_WUS_MAG E1000_WUFC_MAG +#define E1000_WUS_EX E1000_WUFC_EX +#define E1000_WUS_MC E1000_WUFC_MC +#define E1000_WUS_BC E1000_WUFC_BC +#define E1000_WUS_ARP E1000_WUFC_ARP +#define E1000_WUS_IPV4 E1000_WUFC_IPV4 +#define E1000_WUS_IPV6 E1000_WUFC_IPV6 +#define E1000_WUS_FLX0 E1000_WUFC_FLX0 +#define E1000_WUS_FLX1 E1000_WUFC_FLX1 +#define E1000_WUS_FLX2 E1000_WUFC_FLX2 +#define E1000_WUS_FLX3 E1000_WUFC_FLX3 +#define E1000_WUS_FLX_FILTERS E1000_WUFC_FLX_FILTERS + +/* Wake Up Packet Length */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ + +/* Four Flexible Filters are supported */ +#define E1000_FLEXIBLE_FILTER_COUNT_MAX 4 + +/* Each Flexible Filter is at most 128 (0x80) bytes in length */ +#define E1000_FLEXIBLE_FILTER_SIZE_MAX 128 + +#define E1000_FFLT_SIZE E1000_FLEXIBLE_FILTER_COUNT_MAX +#define E1000_FFMT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX +#define E1000_FFVT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX + +/* Extended Device Control */ +#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */ +#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */ +#define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN +#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */ +#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */ +#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */ +#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ +#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ +#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ +#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */ +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ +#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_PCIX_SERDES 0x00800000 +#define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000 +#define E1000_CTRL_EXT_EIAME 0x01000000 +#define E1000_CTRL_EXT_IRCA 0x00000001 +#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000 +#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000 +#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000 +#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000 +#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000 +#define E1000_CTRL_EXT_CANC 0x04000000 /* Interrupt delay cancellation */ +#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */ +#define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */ +#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */ +#define E1000_CRTL_EXT_PB_PAREN 0x01000000 /* packet buffer parity error detection enabled */ +#define E1000_CTRL_EXT_DF_PAREN 0x02000000 /* descriptor FIFO parity error detection enable */ +#define E1000_CTRL_EXT_GHOST_PAREN 0x40000000 +#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ +#define E1000_I2CCMD_REG_ADDR_SHIFT 16 +#define E1000_I2CCMD_REG_ADDR 0x00FF0000 +#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 +#define E1000_I2CCMD_PHY_ADDR 0x07000000 +#define E1000_I2CCMD_OPCODE_READ 0x08000000 +#define E1000_I2CCMD_OPCODE_WRITE 0x00000000 +#define E1000_I2CCMD_RESET 0x10000000 +#define E1000_I2CCMD_READY 0x20000000 +#define E1000_I2CCMD_INTERRUPT_ENA 0x40000000 +#define E1000_I2CCMD_ERROR 0x80000000 +#define E1000_MAX_SGMII_PHY_REG_ADDR 255 +#define E1000_I2CCMD_PHY_TIMEOUT 200 + +/* Receive Decriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_STAT_CRCV 0x100 /* Speculative CRC Valid */ +#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ +#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ +#define E1000_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */ +#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ +#define E1000_RXD_SPC_PRI_SHIFT 13 +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_SHIFT 12 + +#define E1000_RXDEXT_STATERR_CE 0x01000000 +#define E1000_RXDEXT_STATERR_SE 0x02000000 +#define E1000_RXDEXT_STATERR_SEQ 0x04000000 +#define E1000_RXDEXT_STATERR_CXE 0x10000000 +#define E1000_RXDEXT_STATERR_TCPE 0x20000000 +#define E1000_RXDEXT_STATERR_IPE 0x40000000 +#define E1000_RXDEXT_STATERR_RXE 0x80000000 + +/* mask to determine if packets should be dropped due to frame errors */ +#define E1000_RXD_ERR_FRAME_ERR_MASK ( \ + E1000_RXD_ERR_CE | \ + E1000_RXD_ERR_SE | \ + E1000_RXD_ERR_SEQ | \ + E1000_RXD_ERR_CXE | \ + E1000_RXD_ERR_RXE) + +/* Same mask, but for extended and packet split descriptors */ +#define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \ + E1000_RXDEXT_STATERR_CE | \ + E1000_RXDEXT_STATERR_SE | \ + E1000_RXDEXT_STATERR_SEQ | \ + E1000_RXDEXT_STATERR_CXE | \ + E1000_RXDEXT_STATERR_RXE) + +#define E1000_MRQC_ENABLE_MASK 0x00000007 +#define E1000_MRQC_ENABLE_RSS_2Q 0x00000001 +#define E1000_MRQC_ENABLE_RSS_INT 0x00000004 +#define E1000_MRQC_RSS_FIELD_MASK 0xFFFF0000 +#define E1000_MRQC_RSS_FIELD_IPV4_TCP 0x00010000 +#define E1000_MRQC_RSS_FIELD_IPV4 0x00020000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000 +#define E1000_MRQC_RSS_FIELD_IPV6_EX 0x00080000 +#define E1000_MRQC_RSS_FIELD_IPV6 0x00100000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP 0x00200000 + +#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 +#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ +#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ +#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ +#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ +#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ +#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery + * Filtering */ +#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ +#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */ +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address + * filtering */ +#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host + * memory */ +#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address + * filtering */ +#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */ +#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */ +#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ +#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ +#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ +#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ +#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ +#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ + +#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ +#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ +#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ +#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ +#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ + +/* Use byte values for the following shift parameters + * Usage: + * psrctl |= (((ROUNDUP(value0, 128) >> E1000_PSRCTL_BSIZE0_SHIFT) & + * E1000_PSRCTL_BSIZE0_MASK) | + * ((ROUNDUP(value1, 1024) >> E1000_PSRCTL_BSIZE1_SHIFT) & + * E1000_PSRCTL_BSIZE1_MASK) | + * ((ROUNDUP(value2, 1024) << E1000_PSRCTL_BSIZE2_SHIFT) & + * E1000_PSRCTL_BSIZE2_MASK) | + * ((ROUNDUP(value3, 1024) << E1000_PSRCTL_BSIZE3_SHIFT) |; + * E1000_PSRCTL_BSIZE3_MASK)) + * where value0 = [128..16256], default=256 + * value1 = [1024..64512], default=4096 + * value2 = [0..64512], default=4096 + * value3 = [0..64512], default=0 + */ + +#define E1000_PSRCTL_BSIZE0_MASK 0x0000007F +#define E1000_PSRCTL_BSIZE1_MASK 0x00003F00 +#define E1000_PSRCTL_BSIZE2_MASK 0x003F0000 +#define E1000_PSRCTL_BSIZE3_MASK 0x3F000000 + +#define E1000_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */ +#define E1000_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */ +#define E1000_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */ +#define E1000_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */ + +/* SWFW_SYNC Definitions */ +#define E1000_SWFW_EEP_SM 0x1 +#define E1000_SWFW_PHY0_SM 0x2 +#define E1000_SWFW_PHY1_SM 0x4 + +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ +#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ +#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ +#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ +#define E1000_CTRL_I2C_ENA 0x02000000 /* I2C enable */ + +/* Bit definitions for the Management Data IO (MDIO) and Management Data + * Clock (MDC) pins in the Device Control Register. + */ +#define E1000_CTRL_PHY_RESET_DIR E1000_CTRL_SWDPIO0 +#define E1000_CTRL_PHY_RESET E1000_CTRL_SWDPIN0 +#define E1000_CTRL_MDIO_DIR E1000_CTRL_SWDPIO2 +#define E1000_CTRL_MDIO E1000_CTRL_SWDPIN2 +#define E1000_CTRL_MDC_DIR E1000_CTRL_SWDPIO3 +#define E1000_CTRL_MDC E1000_CTRL_SWDPIN3 +#define E1000_CTRL_PHY_RESET_DIR4 E1000_CTRL_EXT_SDP4_DIR +#define E1000_CTRL_PHY_RESET4 E1000_CTRL_EXT_SDP4_DATA + +#define E1000_CONNSW_ENRGSRC 0x4 +#define E1000_PCS_LCTL_FLV_LINK_UP 1 +#define E1000_PCS_LCTL_FSV_10 0 +#define E1000_PCS_LCTL_FSV_100 2 +#define E1000_PCS_LCTL_FSV_1000 4 +#define E1000_PCS_LCTL_FDV_FULL 8 +#define E1000_PCS_LCTL_FSD 0x10 +#define E1000_PCS_LCTL_FORCE_LINK 0x20 +#define E1000_PCS_LCTL_LOW_LINK_LATCH 0x40 +#define E1000_PCS_LCTL_AN_ENABLE 0x10000 +#define E1000_PCS_LCTL_AN_RESTART 0x20000 +#define E1000_PCS_LCTL_AN_TIMEOUT 0x40000 +#define E1000_PCS_LCTL_AN_SGMII_BYPASS 0x80000 +#define E1000_PCS_LCTL_AN_SGMII_TRIGGER 0x100000 +#define E1000_PCS_LCTL_FAST_LINK_TIMER 0x1000000 +#define E1000_PCS_LCTL_LINK_OK_FIX 0x2000000 +#define E1000_PCS_LCTL_CRS_ON_NI 0x4000000 +#define E1000_ENABLE_SERDES_LOOPBACK 0x0410 + +#define E1000_PCS_LSTS_LINK_OK 1 +#define E1000_PCS_LSTS_SPEED_10 0 +#define E1000_PCS_LSTS_SPEED_100 2 +#define E1000_PCS_LSTS_SPEED_1000 4 +#define E1000_PCS_LSTS_DUPLEX_FULL 8 +#define E1000_PCS_LSTS_SYNK_OK 0x10 +#define E1000_PCS_LSTS_AN_COMPLETE 0x10000 +#define E1000_PCS_LSTS_AN_PAGE_RX 0x20000 +#define E1000_PCS_LSTS_AN_TIMED_OUT 0x40000 +#define E1000_PCS_LSTS_AN_REMOTE_FAULT 0x80000 +#define E1000_PCS_LSTS_AN_ERROR_RWS 0x100000 + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_SHIFT 2 +#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion by NVM */ +#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ +#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ +#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ +#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ +#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ +#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */ +#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */ +#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */ +#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */ +#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */ +#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */ +#define E1000_STATUS_FUSE_8 0x04000000 +#define E1000_STATUS_FUSE_9 0x08000000 +#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */ +#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */ + +/* Constants used to intrepret the masked PCI-X bus speed. */ +#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ +#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */ +#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */ + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +#define PHY_FORCE_TIME 20 + +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */ +#define ADVERTISE_1000_FULL 0x0020 + +/* 1000/H is not supported, nor spec-compliant. */ +#define E1000_ALL_SPEED_DUPLEX ( ADVERTISE_10_HALF | ADVERTISE_10_FULL | \ + ADVERTISE_100_HALF | ADVERTISE_100_FULL | \ + ADVERTISE_1000_FULL) +#define E1000_ALL_NOT_GIG ( ADVERTISE_10_HALF | ADVERTISE_10_FULL | \ + ADVERTISE_100_HALF | ADVERTISE_100_FULL) +#define E1000_ALL_100_SPEED (ADVERTISE_100_HALF | ADVERTISE_100_FULL) +#define E1000_ALL_10_SPEED (ADVERTISE_10_HALF | ADVERTISE_10_FULL) +#define E1000_ALL_FULL_DUPLEX (ADVERTISE_10_FULL | ADVERTISE_100_FULL | \ + ADVERTISE_1000_FULL) +#define E1000_ALL_HALF_DUPLEX (ADVERTISE_10_HALF | ADVERTISE_100_HALF) + +#define AUTONEG_ADVERTISE_SPEED_DEFAULT E1000_ALL_SPEED_DUPLEX + +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_BLINK_RATE 0x00000020 +#define E1000_LEDCTL_LED0_IVRT 0x00000040 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED1_MODE_MASK 0x00000F00 +#define E1000_LEDCTL_LED1_MODE_SHIFT 8 +#define E1000_LEDCTL_LED1_BLINK_RATE 0x00002000 +#define E1000_LEDCTL_LED1_IVRT 0x00004000 +#define E1000_LEDCTL_LED1_BLINK 0x00008000 +#define E1000_LEDCTL_LED2_MODE_MASK 0x000F0000 +#define E1000_LEDCTL_LED2_MODE_SHIFT 16 +#define E1000_LEDCTL_LED2_BLINK_RATE 0x00200000 +#define E1000_LEDCTL_LED2_IVRT 0x00400000 +#define E1000_LEDCTL_LED2_BLINK 0x00800000 +#define E1000_LEDCTL_LED3_MODE_MASK 0x0F000000 +#define E1000_LEDCTL_LED3_MODE_SHIFT 24 +#define E1000_LEDCTL_LED3_BLINK_RATE 0x20000000 +#define E1000_LEDCTL_LED3_IVRT 0x40000000 +#define E1000_LEDCTL_LED3_BLINK 0x80000000 + +#define E1000_LEDCTL_MODE_LINK_10_1000 0x0 +#define E1000_LEDCTL_MODE_LINK_100_1000 0x1 +#define E1000_LEDCTL_MODE_LINK_UP 0x2 +#define E1000_LEDCTL_MODE_ACTIVITY 0x3 +#define E1000_LEDCTL_MODE_LINK_ACTIVITY 0x4 +#define E1000_LEDCTL_MODE_LINK_10 0x5 +#define E1000_LEDCTL_MODE_LINK_100 0x6 +#define E1000_LEDCTL_MODE_LINK_1000 0x7 +#define E1000_LEDCTL_MODE_PCIX_MODE 0x8 +#define E1000_LEDCTL_MODE_FULL_DUPLEX 0x9 +#define E1000_LEDCTL_MODE_COLLISION 0xA +#define E1000_LEDCTL_MODE_BUS_SPEED 0xB +#define E1000_LEDCTL_MODE_BUS_SIZE 0xC +#define E1000_LEDCTL_MODE_PAUSED 0xD +#define E1000_LEDCTL_MODE_LED_ON 0xE +#define E1000_LEDCTL_MODE_LED_OFF 0xF + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ + +/* Transmit Arbitration Count */ +#define E1000_TARC0_ENABLE 0x00000400 /* Enable Tx Queue 0 */ + +/* SerDes Control */ +#define E1000_SCTL_DISABLE_SERDES_LOOPBACK 0x0400 + +/* Receive Checksum Control */ +#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */ +#define E1000_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */ +#define E1000_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */ +#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */ + +/* Header split receive */ +#define E1000_RFCTL_ISCSI_DIS 0x00000001 +#define E1000_RFCTL_ISCSI_DWC_MASK 0x0000003E +#define E1000_RFCTL_ISCSI_DWC_SHIFT 1 +#define E1000_RFCTL_NFSW_DIS 0x00000040 +#define E1000_RFCTL_NFSR_DIS 0x00000080 +#define E1000_RFCTL_NFS_VER_MASK 0x00000300 +#define E1000_RFCTL_NFS_VER_SHIFT 8 +#define E1000_RFCTL_IPV6_DIS 0x00000400 +#define E1000_RFCTL_IPV6_XSUM_DIS 0x00000800 +#define E1000_RFCTL_ACK_DIS 0x00001000 +#define E1000_RFCTL_ACKD_DIS 0x00002000 +#define E1000_RFCTL_IPFRSP_DIS 0x00004000 +#define E1000_RFCTL_EXTEN 0x00008000 +#define E1000_RFCTL_IPV6_EX_DIS 0x00010000 +#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 15 +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 63 +#define E1000_COLD_SHIFT 12 + +/* Default values for the transmit IPG register */ +#ifndef NO_82542_SUPPORT +#define DEFAULT_82542_TIPG_IPGT 10 +#endif +#define DEFAULT_82543_TIPG_IPGT_FIBER 9 +#define DEFAULT_82543_TIPG_IPGT_COPPER 8 + +#define E1000_TIPG_IPGT_MASK 0x000003FF +#define E1000_TIPG_IPGR1_MASK 0x000FFC00 +#define E1000_TIPG_IPGR2_MASK 0x3FF00000 + +#ifndef NO_82542_SUPPORT +#define DEFAULT_82542_TIPG_IPGR1 2 +#endif +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#ifndef NO_82542_SUPPORT +#define DEFAULT_82542_TIPG_IPGR2 10 +#endif +#define DEFAULT_82543_TIPG_IPGR2 6 +#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7 +#define E1000_TIPG_IPGR2_SHIFT 20 + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ + +#define ETHERNET_FCS_SIZE 4 +#define MAX_JUMBO_FRAME_SIZE 0x3F00 + +/* Extended Configuration Control and Size */ +#define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020 +#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK 0x00FF0000 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT 16 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK 0x0FFF0000 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT 16 + +#define E1000_PHY_CTRL_SPD_EN 0x00000001 +#define E1000_PHY_CTRL_D0A_LPLU 0x00000002 +#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004 +#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008 +#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040 + +#define E1000_KABGTXD_BGSQLBIAS 0x00050000 + +/* PBA constants */ +#define E1000_PBA_8K 0x0008 /* 8KB, default Rx allocation */ +#define E1000_PBA_12K 0x000C /* 12KB, default Rx allocation */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_20K 0x0014 +#define E1000_PBA_22K 0x0016 +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_30K 0x001E +#define E1000_PBA_32K 0x0020 +#define E1000_PBA_34K 0x0022 +#define E1000_PBA_38K 0x0026 +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ + +#define E1000_PBS_16K E1000_PBA_16K +#define E1000_PBS_24K E1000_PBA_24K + +#define IFS_MAX 80 +#define IFS_MIN 40 +#define IFS_RATIO 4 +#define IFS_STEP 10 +#define MIN_NUM_XMITS 1000 + +/* SW Semaphore Register */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_WMNG 0x00000004 /* Wake MNG Clock */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_SRPD 0x00010000 +#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ +#define E1000_ICR_MNG 0x00040000 /* Manageability event */ +#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ +#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ +#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ +#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ +#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ +#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ +#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ + +/* Extended Interrupt Cause Read */ +#define E1000_EICR_RX_QUEUE0 0x00000001 /* Rx Queue 0 Interrupt */ +#define E1000_EICR_RX_QUEUE1 0x00000002 /* Rx Queue 1 Interrupt */ +#define E1000_EICR_RX_QUEUE2 0x00000004 /* Rx Queue 2 Interrupt */ +#define E1000_EICR_RX_QUEUE3 0x00000008 /* Rx Queue 3 Interrupt */ +#define E1000_EICR_TX_QUEUE0 0x00000100 /* Tx Queue 0 Interrupt */ +#define E1000_EICR_TX_QUEUE1 0x00000200 /* Tx Queue 1 Interrupt */ +#define E1000_EICR_TX_QUEUE2 0x00000400 /* Tx Queue 2 Interrupt */ +#define E1000_EICR_TX_QUEUE3 0x00000800 /* Tx Queue 3 Interrupt */ +#define E1000_EICR_TCP_TIMER 0x40000000 /* TCP Timer */ +#define E1000_EICR_OTHER 0x80000000 /* Interrupt Cause Active */ +/* TCP Timer */ +#define E1000_TCPTIMER_KS 0x00000100 /* KickStart */ +#define E1000_TCPTIMER_COUNT_ENABLE 0x00000200 /* Count Enable */ +#define E1000_TCPTIMER_COUNT_FINISH 0x00000400 /* Count finish */ +#define E1000_TCPTIMER_LOOP 0x00000800 /* Loop */ + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + */ +#define POLL_IMS_ENABLE_MASK ( \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ) + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXT0 = Receiver Timer Interrupt (ring 0) + * o TXDW = Transmit Descriptor Written Back + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + * o LSC = Link Status Change + */ +#define IMS_ENABLE_MASK ( \ + E1000_IMS_RXT0 | \ + E1000_IMS_TXDW | \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ | \ + E1000_IMS_LSC) + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_SRPD E1000_ICR_SRPD +#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMS_DSW E1000_ICR_DSW +#define E1000_IMS_PHYINT E1000_ICR_PHYINT +#define E1000_IMS_EPRST E1000_ICR_EPRST + +/* Extended Interrupt Mask Set */ +#define E1000_EIMS_RX_QUEUE0 E1000_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */ +#define E1000_EIMS_RX_QUEUE1 E1000_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */ +#define E1000_EIMS_RX_QUEUE2 E1000_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */ +#define E1000_EIMS_RX_QUEUE3 E1000_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */ +#define E1000_EIMS_TX_QUEUE0 E1000_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */ +#define E1000_EIMS_TX_QUEUE1 E1000_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */ +#define E1000_EIMS_TX_QUEUE2 E1000_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */ +#define E1000_EIMS_TX_QUEUE3 E1000_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */ +#define E1000_EIMS_TCP_TIMER E1000_EICR_TCP_TIMER /* TCP Timer */ +#define E1000_EIMS_OTHER E1000_EICR_OTHER /* Interrupt Cause Active */ + +/* Interrupt Cause Set */ +#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_ICS_SRPD E1000_ICR_SRPD +#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICS_DSW E1000_ICR_DSW +#define E1000_ICS_PHYINT E1000_ICR_PHYINT +#define E1000_ICS_EPRST E1000_ICR_EPRST + +/* Extended Interrupt Cause Set */ +#define E1000_EICS_RX_QUEUE0 E1000_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */ +#define E1000_EICS_RX_QUEUE1 E1000_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */ +#define E1000_EICS_RX_QUEUE2 E1000_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */ +#define E1000_EICS_RX_QUEUE3 E1000_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */ +#define E1000_EICS_TX_QUEUE0 E1000_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */ +#define E1000_EICS_TX_QUEUE1 E1000_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */ +#define E1000_EICS_TX_QUEUE2 E1000_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */ +#define E1000_EICS_TX_QUEUE3 E1000_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */ +#define E1000_EICS_TCP_TIMER E1000_EICR_TCP_TIMER /* TCP Timer */ +#define E1000_EICS_OTHER E1000_EICR_OTHER /* Interrupt Cause Active */ + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */ +#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ +#define E1000_TXDCTL_MAX_TX_DESC_PREFETCH 0x0100001F /* GRAN=1, PTHRESH=31 */ +#define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc. + still to be processed. */ + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* 802.1q VLAN Packet Size */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + +/* Receive Address */ +/* Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. + * Technically, we have 16 spots. However, we reserve one of these spots + * (RAR[15]) for our directed address used by controllers with + * manageability enabled, allowing us room for 15 multicast addresses. + */ +#define E1000_RAR_ENTRIES 15 +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ + +/* Error Codes */ +#define E1000_SUCCESS 0 +#define E1000_ERR_NVM 1 +#define E1000_ERR_PHY 2 +#define E1000_ERR_CONFIG 3 +#define E1000_ERR_PARAM 4 +#define E1000_ERR_MAC_INIT 5 +#define E1000_ERR_PHY_TYPE 6 +#define E1000_ERR_RESET 9 +#define E1000_ERR_MASTER_REQUESTS_PENDING 10 +#define E1000_ERR_HOST_INTERFACE_COMMAND 11 +#define E1000_BLK_PHY_RESET 12 +#define E1000_ERR_SWFW_SYNC 13 +#define E1000_NOT_IMPLEMENTED 14 + +/* Loop limit on how long we wait for auto-negotiation to complete */ +#define FIBER_LINK_UP_LIMIT 50 +#define COPPER_LINK_UP_LIMIT 10 +#define PHY_AUTO_NEG_LIMIT 45 +#define PHY_FORCE_LIMIT 20 +/* Number of 100 microseconds we wait for PCI Express master disable */ +#define MASTER_DISABLE_TIMEOUT 800 +/* Number of milliseconds we wait for PHY configuration done after MAC reset */ +#define PHY_CFG_TIMEOUT 100 +/* Number of 2 milliseconds we wait for acquiring MDIO ownership. */ +#define MDIO_OWNERSHIP_TIMEOUT 10 +/* Number of milliseconds for NVM auto read done after MAC reset. */ +#define AUTO_READ_DONE_TIMEOUT 10 + +/* Flow Control */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ + +/* Transmit Configuration Word */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */ +#define E1000_TXCW_NP 0x00008000 /* TXCW next page */ +#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */ +#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ + +/* Receive Configuration Word */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_CC 0x10000000 /* Receive config change */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ +#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */ + +/* PCI Express Control */ +#define E1000_GCR_RXD_NO_SNOOP 0x00000001 +#define E1000_GCR_RXDSCW_NO_SNOOP 0x00000002 +#define E1000_GCR_RXDSCR_NO_SNOOP 0x00000004 +#define E1000_GCR_TXD_NO_SNOOP 0x00000008 +#define E1000_GCR_TXDSCW_NO_SNOOP 0x00000010 +#define E1000_GCR_TXDSCR_NO_SNOOP 0x00000020 + +#define PCIE_NO_SNOOP_ALL (E1000_GCR_RXD_NO_SNOOP | \ + E1000_GCR_RXDSCW_NO_SNOOP | \ + E1000_GCR_RXDSCR_NO_SNOOP | \ + E1000_GCR_TXD_NO_SNOOP | \ + E1000_GCR_TXDSCW_NO_SNOOP | \ + E1000_GCR_TXDSCR_NO_SNOOP) + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ + +/* 1000BASE-T Control Register */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ + +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CONTROL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* NVM Control */ +#define E1000_EECD_SK 0x00000001 /* NVM Clock */ +#define E1000_EECD_CS 0x00000002 /* NVM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* NVM Data In */ +#define E1000_EECD_DO 0x00000008 /* NVM Data Out */ +#define E1000_EECD_FWE_MASK 0x00000030 +#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ +#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ +#define E1000_EECD_FWE_SHIFT 4 +#define E1000_EECD_REQ 0x00000040 /* NVM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* NVM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* NVM Present */ +#define E1000_EECD_SIZE 0x00000200 /* NVM Size (0=64 word 1=256 word) */ +#define E1000_EECD_ADDR_BITS 0x00000400 /* NVM Addressing bits based on type + * (0-small, 1-large) */ +#define E1000_EECD_TYPE 0x00002000 /* NVM Type (1-SPI, 0-Microwire) */ +#ifndef E1000_NVM_GRANT_ATTEMPTS +#define E1000_NVM_GRANT_ATTEMPTS 1000 /* NVM # attempts to gain grant */ +#endif +#define E1000_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* NVM Size */ +#define E1000_EECD_SIZE_EX_SHIFT 11 +#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ +#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ +#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ +#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ +#define E1000_EECD_SECVAL_SHIFT 22 + +#define E1000_NVM_SWDPIN0 0x0001 /* SWDPIN 0 NVM Value */ +#define E1000_NVM_LED_LOGIC 0x0020 /* Led Logic Word */ +#define E1000_NVM_RW_REG_DATA 16 /* Offset to data in NVM read/write registers */ +#define E1000_NVM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_NVM_RW_REG_START 1 /* Start operation */ +#define E1000_NVM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_NVM_POLL_WRITE 1 /* Flag for polling for write complete */ +#define E1000_NVM_POLL_READ 0 /* Flag for polling for read complete */ +#define E1000_FLASH_UPDATES 2000 + +/* NVM Word Offsets */ +#define NVM_COMPAT 0x0003 +#define NVM_ID_LED_SETTINGS 0x0004 +#define NVM_VERSION 0x0005 +#define NVM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ +#define NVM_PHY_CLASS_WORD 0x0007 +#define NVM_INIT_CONTROL1_REG 0x000A +#define NVM_INIT_CONTROL2_REG 0x000F +#define NVM_SWDEF_PINS_CTRL_PORT_1 0x0010 +#define NVM_INIT_CONTROL3_PORT_B 0x0014 +#define NVM_INIT_3GIO_3 0x001A +#define NVM_SWDEF_PINS_CTRL_PORT_0 0x0020 +#define NVM_INIT_CONTROL3_PORT_A 0x0024 +#define NVM_CFG 0x0012 +#define NVM_FLASH_VERSION 0x0032 +#define NVM_CHECKSUM_REG 0x003F + +#define E1000_NVM_CFG_DONE_PORT_0 0x40000 /* MNG config cycle done */ +#define E1000_NVM_CFG_DONE_PORT_1 0x80000 /* ...for second port */ + +/* Mask bits for fields in Word 0x0f of the NVM */ +#define NVM_WORD0F_PAUSE_MASK 0x3000 +#define NVM_WORD0F_PAUSE 0x1000 +#define NVM_WORD0F_ASM_DIR 0x2000 +#define NVM_WORD0F_ANE 0x0800 +#define NVM_WORD0F_SWPDIO_EXT_MASK 0x00F0 +#define NVM_WORD0F_LPLU 0x0001 + +/* Mask bits for fields in Word 0x1a of the NVM */ +#define NVM_WORD1A_ASPM_MASK 0x000C + +/* For checksumming, the sum of all words in the NVM should equal 0xBABA. */ +#define NVM_SUM 0xBABA + +#define NVM_MAC_ADDR_OFFSET 0 +#define NVM_PBA_OFFSET_0 8 +#define NVM_PBA_OFFSET_1 9 +#define NVM_RESERVED_WORD 0xFFFF +#define NVM_PHY_CLASS_A 0x8000 +#define NVM_SERDES_AMPLITUDE_MASK 0x000F +#define NVM_SIZE_MASK 0x1C00 +#define NVM_SIZE_SHIFT 10 +#define NVM_WORD_SIZE_BASE_SHIFT 6 +#define NVM_SWDPIO_EXT_SHIFT 4 + +/* NVM Commands - Microwire */ +#define NVM_READ_OPCODE_MICROWIRE 0x6 /* NVM read opcode */ +#define NVM_WRITE_OPCODE_MICROWIRE 0x5 /* NVM write opcode */ +#define NVM_ERASE_OPCODE_MICROWIRE 0x7 /* NVM erase opcode */ +#define NVM_EWEN_OPCODE_MICROWIRE 0x13 /* NVM erase/write enable */ +#define NVM_EWDS_OPCODE_MICROWIRE 0x10 /* NVM erast/write disable */ + +/* NVM Commands - SPI */ +#define NVM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define NVM_READ_OPCODE_SPI 0x03 /* NVM read opcode */ +#define NVM_WRITE_OPCODE_SPI 0x02 /* NVM write opcode */ +#define NVM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define NVM_WREN_OPCODE_SPI 0x06 /* NVM set Write Enable latch */ +#define NVM_WRDI_OPCODE_SPI 0x04 /* NVM reset Write Enable latch */ +#define NVM_RDSR_OPCODE_SPI 0x05 /* NVM read Status register */ +#define NVM_WRSR_OPCODE_SPI 0x01 /* NVM write Status register */ + +/* SPI NVM Status Register */ +#define NVM_STATUS_RDY_SPI 0x01 +#define NVM_STATUS_WEN_SPI 0x02 +#define NVM_STATUS_BP0_SPI 0x04 +#define NVM_STATUS_BP1_SPI 0x08 +#define NVM_STATUS_WPEN_SPI 0x80 + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ + (ID_LED_OFF1_OFF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEF1_DEF2 0x1 +#define ID_LED_DEF1_ON2 0x2 +#define ID_LED_DEF1_OFF2 0x3 +#define ID_LED_ON1_DEF2 0x4 +#define ID_LED_ON1_ON2 0x5 +#define ID_LED_ON1_OFF2 0x6 +#define ID_LED_OFF1_DEF2 0x7 +#define ID_LED_OFF1_ON2 0x8 +#define ID_LED_OFF1_OFF2 0x9 + +#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF +#define IGP_ACTIVITY_LED_ENABLE 0x0300 +#define IGP_LED3_MODE 0x07000000 + +/* PCI/PCI-X/PCI-EX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA +#define PCI_HEADER_TYPE_REGISTER 0x0E +#define PCIE_LINK_STATUS 0x12 + +#define PCIX_COMMAND_MMRBC_MASK 0x000C +#define PCIX_COMMAND_MMRBC_SHIFT 0x2 +#define PCIX_STATUS_HI_MMRBC_MASK 0x0060 +#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5 +#define PCIX_STATUS_HI_MMRBC_4K 0x3 +#define PCIX_STATUS_HI_MMRBC_2K 0x2 +#define PCIX_STATUS_LO_FUNC_MASK 0x7 +#define PCI_HEADER_TYPE_MULTIFUNC 0x80 +#define PCIE_LINK_WIDTH_MASK 0x3F0 +#define PCIE_LINK_WIDTH_SHIFT 4 + +#ifndef ETH_ADDR_LEN +#define ETH_ADDR_LEN 6 +#endif + +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF + +/* Bit definitions for valid PHY IDs. */ +/* I = Integrated + * E = External + */ +#define M88E1000_E_PHY_ID 0x01410C50 +#define M88E1000_I_PHY_ID 0x01410C30 +#define M88E1011_I_PHY_ID 0x01410C20 +#define IGP01E1000_I_PHY_ID 0x02A80380 +#define M88E1011_I_REV_4 0x04 +#define M88E1111_I_PHY_ID 0x01410CC0 +#define GG82563_E_PHY_ID 0x01410CA0 +#define IGP03E1000_E_PHY_ID 0x02A80390 +#define IFE_E_PHY_ID 0x02A80330 +#define IFE_PLUS_E_PHY_ID 0x02A80320 +#define IFE_C_E_PHY_ID 0x02A80310 +#define M88_VENDOR 0x0141 + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ +#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */ +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ +#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */ +#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */ + +/* M88E1000 PHY Specific Control Register */ +#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, + * 0=CLK125 toggling + */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ + /* Manual MDI configuration */ +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, + * 100BASE-TX/10BASE-T: + * MDI Mode + */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled + * all speeds. + */ +#define M88E1000_PSCR_EN_10BT_EXT_DIST 0x0080 + /* 1=Enable Extended 10BASE-T distance + * (Lower 10BASE-T RX Threshold) + * 0=Normal 10BASE-T RX Threshold */ +#define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100 + /* 1=5-Bit interface in 100BASE-TX + * 0=MII interface in 100BASE-TX */ +#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ + +/* M88E1000 PHY Specific Status Register */ +#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M; + * 3=110-140M;4=>140M */ +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */ +#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 + +/* M88E1000 Extended PHY Specific Control Register */ +#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */ +#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled. + * Will assert lost lock and bring + * link down if idle not seen + * within 1ms in 1000BASE-T + */ +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master */ +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_2X 0x0400 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_3X 0x0800 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_4X 0x0C00 +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the slave */ +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_DIS 0x0000 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_2X 0x0200 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_3X 0x0300 +#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ + +/* M88EC018 Rev 2 specific DownShift settings */ +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_1X 0x0000 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_2X 0x0200 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_3X 0x0400 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_4X 0x0600 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_6X 0x0A00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X 0x0C00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X 0x0E00 + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define GG82563_PAGE_SHIFT 5 +#define GG82563_REG(page, reg) \ + (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) +#define GG82563_MIN_ALT_REG 30 + +/* GG82563 Specific Registers */ +#define GG82563_PHY_SPEC_CTRL \ + GG82563_REG(0, 16) /* PHY Specific Control */ +#define GG82563_PHY_SPEC_STATUS \ + GG82563_REG(0, 17) /* PHY Specific Status */ +#define GG82563_PHY_INT_ENABLE \ + GG82563_REG(0, 18) /* Interrupt Enable */ +#define GG82563_PHY_SPEC_STATUS_2 \ + GG82563_REG(0, 19) /* PHY Specific Status 2 */ +#define GG82563_PHY_RX_ERR_CNTR \ + GG82563_REG(0, 21) /* Receive Error Counter */ +#define GG82563_PHY_PAGE_SELECT \ + GG82563_REG(0, 22) /* Page Select */ +#define GG82563_PHY_SPEC_CTRL_2 \ + GG82563_REG(0, 26) /* PHY Specific Control 2 */ +#define GG82563_PHY_PAGE_SELECT_ALT \ + GG82563_REG(0, 29) /* Alternate Page Select */ +#define GG82563_PHY_TEST_CLK_CTRL \ + GG82563_REG(0, 30) /* Test Clock Control (use reg. 29 to select) */ + +#define GG82563_PHY_MAC_SPEC_CTRL \ + GG82563_REG(2, 21) /* MAC Specific Control Register */ +#define GG82563_PHY_MAC_SPEC_CTRL_2 \ + GG82563_REG(2, 26) /* MAC Specific Control 2 */ + +#define GG82563_PHY_DSP_DISTANCE \ + GG82563_REG(5, 26) /* DSP Distance */ + +/* Page 193 - Port Control Registers */ +#define GG82563_PHY_KMRN_MODE_CTRL \ + GG82563_REG(193, 16) /* Kumeran Mode Control */ +#define GG82563_PHY_PORT_RESET \ + GG82563_REG(193, 17) /* Port Reset */ +#define GG82563_PHY_REVISION_ID \ + GG82563_REG(193, 18) /* Revision ID */ +#define GG82563_PHY_DEVICE_ID \ + GG82563_REG(193, 19) /* Device ID */ +#define GG82563_PHY_PWR_MGMT_CTRL \ + GG82563_REG(193, 20) /* Power Management Control */ +#define GG82563_PHY_RATE_ADAPT_CTRL \ + GG82563_REG(193, 25) /* Rate Adaptation Control */ + +/* Page 194 - KMRN Registers */ +#define GG82563_PHY_KMRN_FIFO_CTRL_STAT \ + GG82563_REG(194, 16) /* FIFO's Control/Status */ +#define GG82563_PHY_KMRN_CTRL \ + GG82563_REG(194, 17) /* Control */ +#define GG82563_PHY_INBAND_CTRL \ + GG82563_REG(194, 18) /* Inband Control */ +#define GG82563_PHY_KMRN_DIAGNOSTIC \ + GG82563_REG(194, 19) /* Diagnostic */ +#define GG82563_PHY_ACK_TIMEOUTS \ + GG82563_REG(194, 20) /* Acknowledge Timeouts */ +#define GG82563_PHY_ADV_ABILITY \ + GG82563_REG(194, 21) /* Advertised Ability */ +#define GG82563_PHY_LINK_PARTNER_ADV_ABILITY \ + GG82563_REG(194, 23) /* Link Partner Advertised Ability */ +#define GG82563_PHY_ADV_NEXT_PAGE \ + GG82563_REG(194, 24) /* Advertised Next Page */ +#define GG82563_PHY_LINK_PARTNER_ADV_NEXT_PAGE \ + GG82563_REG(194, 25) /* Link Partner Advertised Next page */ +#define GG82563_PHY_KMRN_MISC \ + GG82563_REG(194, 26) /* Misc. */ + +/* MDI Control */ +#define E1000_MDIC_DATA_MASK 0x0000FFFF +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_INT_EN 0x20000000 +#define E1000_MDIC_ERROR 0x40000000 + +/* SerDes Control */ +#define E1000_GEN_CTL_READY 0x80000000 +#define E1000_GEN_CTL_ADDRESS_SHIFT 8 +#define E1000_GEN_POLL_TIMEOUT 640 +#endif diff --git a/bsd_eth_drivers/if_em/e1000_hw.h b/bsd_eth_drivers/if_em/e1000_hw.h new file mode 100644 index 0000000..f741a30 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_hw.h @@ -0,0 +1,730 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_hw.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_HW_H_ +#define _E1000_HW_H_ + +#include "e1000_osdep.h" +#include "e1000_regs.h" +#include "e1000_defines.h" + +struct e1000_hw; + +#ifndef NO_82542_SUPPORT +#define E1000_DEV_ID_82542 0x1000 +#endif +#define E1000_DEV_ID_82543GC_FIBER 0x1001 +#define E1000_DEV_ID_82543GC_COPPER 0x1004 +#define E1000_DEV_ID_82544EI_COPPER 0x1008 +#define E1000_DEV_ID_82544EI_FIBER 0x1009 +#define E1000_DEV_ID_82544GC_COPPER 0x100C +#define E1000_DEV_ID_82544GC_LOM 0x100D +#define E1000_DEV_ID_82540EM 0x100E +#define E1000_DEV_ID_82540EM_LOM 0x1015 +#define E1000_DEV_ID_82540EP_LOM 0x1016 +#define E1000_DEV_ID_82540EP 0x1017 +#define E1000_DEV_ID_82540EP_LP 0x101E +#define E1000_DEV_ID_82545EM_COPPER 0x100F +#define E1000_DEV_ID_82545EM_FIBER 0x1011 +#define E1000_DEV_ID_82545GM_COPPER 0x1026 +#define E1000_DEV_ID_82545GM_FIBER 0x1027 +#define E1000_DEV_ID_82545GM_SERDES 0x1028 +#define E1000_DEV_ID_82546EB_COPPER 0x1010 +#define E1000_DEV_ID_82546EB_FIBER 0x1012 +#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D +#define E1000_DEV_ID_82546GB_COPPER 0x1079 +#define E1000_DEV_ID_82546GB_FIBER 0x107A +#define E1000_DEV_ID_82546GB_SERDES 0x107B +#define E1000_DEV_ID_82546GB_PCIE 0x108A +#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 +#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 +#define E1000_DEV_ID_82541EI 0x1013 +#define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER_LOM 0x1014 +#define E1000_DEV_ID_82541ER 0x1078 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_LF 0x107C +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#define E1000_DEV_ID_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82571EB_COPPER 0x105E +#define E1000_DEV_ID_82571EB_FIBER 0x105F +#define E1000_DEV_ID_82571EB_SERDES 0x1060 +#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 +#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA +#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 +#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 +#define E1000_DEV_ID_82571EB_QUAD_COPPER_LP 0x10BC +#define E1000_DEV_ID_82572EI_COPPER 0x107D +#define E1000_DEV_ID_82572EI_FIBER 0x107E +#define E1000_DEV_ID_82572EI_SERDES 0x107F +#define E1000_DEV_ID_82572EI 0x10B9 +#define E1000_DEV_ID_82573E 0x108B +#define E1000_DEV_ID_82573E_IAMT 0x108C +#define E1000_DEV_ID_82573L 0x109A +#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 +#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 +#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA +#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB +#ifndef NO_ICH8LAN_SUPPORT +#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 +#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A +#define E1000_DEV_ID_ICH8_IGP_C 0x104B +#define E1000_DEV_ID_ICH8_IFE 0x104C +#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 +#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 +#define E1000_DEV_ID_ICH8_IGP_M 0x104D +#define E1000_DEV_ID_ICH9_IGP_AMT 0x10BD +#define E1000_DEV_ID_ICH9_IGP_C 0x294C +#define E1000_DEV_ID_ICH9_IFE 0x10C0 +#define E1000_DEV_ID_ICH9_IFE_GT 0x10C3 +#define E1000_DEV_ID_ICH9_IFE_G 0x10C2 +#endif +#define E1000_DEV_ID_82575EB_COPPER 0x10A7 +#define E1000_DEV_ID_82575EB_FIBER_SERDES 0x10A9 +#define E1000_DEV_ID_82575EM_COPPER 0x10AA +#define E1000_DEV_ID_82575EM_FIBER_SERDES 0x10AC +#define E1000_DEV_ID_82575GB_QUAD_COPPER 0x10D6 + +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 +#define E1000_REVISION_3 3 +#define E1000_REVISION_4 4 + +#define E1000_FUNC_0 0 +#define E1000_FUNC_1 1 + +typedef enum { + e1000_undefined = 0, +#ifndef NO_82542_SUPPORT + e1000_82542, +#endif + e1000_82543, + e1000_82544, + e1000_82540, + e1000_82545, + e1000_82545_rev_3, + e1000_82546, + e1000_82546_rev_3, + e1000_82541, + e1000_82541_rev_2, + e1000_82547, + e1000_82547_rev_2, + e1000_82571, + e1000_82572, + e1000_82573, + e1000_80003es2lan, +#ifndef NO_ICH8LAN_SUPPORT + e1000_ich8lan, + e1000_ich9lan, +#endif + e1000_82575, + e1000_num_macs /* List is 1-based, so subtract 1 for true count. */ +} e1000_mac_type; + +typedef enum { + e1000_media_type_unknown = 0, + e1000_media_type_copper = 1, + e1000_media_type_fiber = 2, + e1000_media_type_internal_serdes = 3, + e1000_num_media_types +} e1000_media_type; + +typedef enum { + e1000_nvm_unknown = 0, + e1000_nvm_none, + e1000_nvm_eeprom_spi, + e1000_nvm_eeprom_microwire, + e1000_nvm_flash_hw, + e1000_nvm_flash_sw +} e1000_nvm_type; + +typedef enum { + e1000_nvm_override_none = 0, + e1000_nvm_override_spi_small, + e1000_nvm_override_spi_large, + e1000_nvm_override_microwire_small, + e1000_nvm_override_microwire_large +} e1000_nvm_override; + +typedef enum { + e1000_phy_unknown = 0, + e1000_phy_none, + e1000_phy_m88, + e1000_phy_igp, + e1000_phy_igp_2, + e1000_phy_gg82563, + e1000_phy_igp_3, + e1000_phy_ife, +} e1000_phy_type; + +typedef enum { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + e1000_bus_type_pci_express, + e1000_bus_type_reserved +} e1000_bus_type; + +typedef enum { + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_120, + e1000_bus_speed_133, + e1000_bus_speed_2500, + e1000_bus_speed_reserved +} e1000_bus_speed; + +typedef enum { + e1000_bus_width_unknown = 0, + e1000_bus_width_pcie_x1, + e1000_bus_width_pcie_x2, + e1000_bus_width_pcie_x4 = 4, + e1000_bus_width_32, + e1000_bus_width_64, + e1000_bus_width_reserved +} e1000_bus_width; + +typedef enum { + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF +} e1000_1000t_rx_status; + +typedef enum { + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF +} e1000_rev_polarity; + +typedef enum { + e1000_fc_none = 0, + e1000_fc_rx_pause, + e1000_fc_tx_pause, + e1000_fc_full, + e1000_fc_default = 0xFF +} e1000_fc_mode; + +typedef enum { + e1000_ffe_config_enabled = 0, + e1000_ffe_config_active, + e1000_ffe_config_blocked +} e1000_ffe_config; + +typedef enum { + e1000_dsp_config_disabled = 0, + e1000_dsp_config_enabled, + e1000_dsp_config_activated, + e1000_dsp_config_undefined = 0xFF +} e1000_dsp_config; + +/* Receive Descriptor */ +struct e1000_rx_desc { + u64 buffer_addr; /* Address of the descriptor's data buffer */ + u16 length; /* Length of data DMAed into data buffer */ + u16 csum; /* Packet checksum */ + u8 status; /* Descriptor status */ + u8 errors; /* Descriptor Errors */ + u16 special; +}; + +/* Receive Descriptor - Extended */ +union e1000_rx_desc_extended { + struct { + u64 buffer_addr; + u64 reserved; + } read; + struct { + struct { + u32 mrq; /* Multiple Rx Queues */ + union { + u32 rss; /* RSS Hash */ + struct { + u16 ip_id; /* IP id */ + u16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + u32 status_error; /* ext status/error */ + u16 length; + u16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 +/* Receive Descriptor - Packet Split */ +union e1000_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + u64 buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + u32 mrq; /* Multiple Rx Queues */ + union { + u32 rss; /* RSS Hash */ + struct { + u16 ip_id; /* IP id */ + u16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + u32 status_error; /* ext status/error */ + u16 length0; /* length of buffer 0 */ + u16 vlan; /* VLAN tag */ + } middle; + struct { + u16 header_status; + u16 length[3]; /* length of buffers 1-3 */ + } upper; + u64 reserved; + } wb; /* writeback */ +}; + +/* Transmit Descriptor */ +struct e1000_tx_desc { + u64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + u32 data; + struct { + u16 length; /* Data buffer length */ + u8 cso; /* Checksum offset */ + u8 cmd; /* Descriptor control */ + } flags; + } lower; + union { + u32 data; + struct { + u8 status; /* Descriptor status */ + u8 css; /* Checksum start */ + u16 special; + } fields; + } upper; +}; + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + u32 ip_config; + struct { + u8 ipcss; /* IP checksum start */ + u8 ipcso; /* IP checksum offset */ + u16 ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + u32 tcp_config; + struct { + u8 tucss; /* TCP checksum start */ + u8 tucso; /* TCP checksum offset */ + u16 tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + u32 cmd_and_length; + union { + u32 data; + struct { + u8 status; /* Descriptor status */ + u8 hdr_len; /* Header length */ + u16 mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct e1000_data_desc { + u64 buffer_addr; /* Address of the descriptor's buffer address */ + union { + u32 data; + struct { + u16 length; /* Data buffer length */ + u8 typ_len_ext; + u8 cmd; + } flags; + } lower; + union { + u32 data; + struct { + u8 status; /* Descriptor status */ + u8 popts; /* Packet Options */ + u16 special; /* */ + } fields; + } upper; +}; + +/* Statistics counters collected by the MAC */ +struct e1000_hw_stats { + u64 crcerrs; + u64 algnerrc; + u64 symerrs; + u64 rxerrc; + u64 mpc; + u64 scc; + u64 ecol; + u64 mcc; + u64 latecol; + u64 colc; + u64 dc; + u64 tncrs; + u64 sec; + u64 cexterr; + u64 rlec; + u64 xonrxc; + u64 xontxc; + u64 xoffrxc; + u64 xofftxc; + u64 fcruc; + u64 prc64; + u64 prc127; + u64 prc255; + u64 prc511; + u64 prc1023; + u64 prc1522; + u64 gprc; + u64 bprc; + u64 mprc; + u64 gptc; + u64 gorcl; + u64 gorch; + u64 gotcl; + u64 gotch; + u64 rnbc; + u64 ruc; + u64 rfc; + u64 roc; + u64 rjc; + u64 mgprc; + u64 mgpdc; + u64 mgptc; + u64 torl; + u64 torh; + u64 totl; + u64 toth; + u64 tpr; + u64 tpt; + u64 ptc64; + u64 ptc127; + u64 ptc255; + u64 ptc511; + u64 ptc1023; + u64 ptc1522; + u64 mptc; + u64 bptc; + u64 tsctc; + u64 tsctfc; + u64 iac; + u64 icrxptc; + u64 icrxatc; + u64 ictxptc; + u64 ictxatc; + u64 ictxqec; + u64 ictxqmtc; + u64 icrxdmtc; + u64 icrxoc; + u64 cbtmpc; + u64 htdpmc; + u64 cbrdpc; + u64 cbrmpc; + u64 rpthc; + u64 hgptc; + u64 htcbdpc; + u64 hgorcl; + u64 hgorch; + u64 hgotcl; + u64 hgotch; + u64 lenerrs; + u64 scvpc; + u64 hrmpc; +}; + +struct e1000_phy_stats { + u32 idle_errors; + u32 receive_errors; +}; + +struct e1000_host_mng_dhcp_cookie { + u32 signature; + u8 status; + u8 reserved0; + u16 vlan_id; + u32 reserved1; + u16 reserved2; + u8 reserved3; + u8 checksum; +}; + +/* Host Interface "Rev 1" */ +struct e1000_host_command_header { + u8 command_id; + u8 command_length; + u8 command_options; + u8 checksum; +}; + +#define E1000_HI_MAX_DATA_LENGTH 252 +struct e1000_host_command_info { + struct e1000_host_command_header command_header; + u8 command_data[E1000_HI_MAX_DATA_LENGTH]; +}; + +/* Host Interface "Rev 2" */ +struct e1000_host_mng_command_header { + u8 command_id; + u8 checksum; + u16 reserved1; + u16 reserved2; + u16 command_length; +}; + +#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 +struct e1000_host_mng_command_info { + struct e1000_host_mng_command_header command_header; + u8 command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; +}; + +#include "e1000_mac.h" +#include "e1000_phy.h" +#include "e1000_nvm.h" +#include "e1000_manage.h" + +struct e1000_functions { + /* Function pointers for the MAC. */ + s32 (*init_mac_params)(struct e1000_hw *); + s32 (*blink_led)(struct e1000_hw *); + s32 (*check_for_link)(struct e1000_hw *); + boolean_t (*check_mng_mode)(struct e1000_hw *hw); + s32 (*cleanup_led)(struct e1000_hw *); + void (*clear_hw_cntrs)(struct e1000_hw *); + void (*clear_vfta)(struct e1000_hw *); + s32 (*get_bus_info)(struct e1000_hw *); + s32 (*get_link_up_info)(struct e1000_hw *, u16 *, u16 *); + s32 (*led_on)(struct e1000_hw *); + s32 (*led_off)(struct e1000_hw *); + void (*mc_addr_list_update)(struct e1000_hw *, u8 *, u32, u32, + u32); + void (*remove_device)(struct e1000_hw *); + s32 (*reset_hw)(struct e1000_hw *); + s32 (*init_hw)(struct e1000_hw *); + s32 (*setup_link)(struct e1000_hw *); + s32 (*setup_physical_interface)(struct e1000_hw *); + s32 (*setup_led)(struct e1000_hw *); + void (*write_vfta)(struct e1000_hw *, u32, u32); + void (*mta_set)(struct e1000_hw *, u32); + void (*config_collision_dist)(struct e1000_hw*); + void (*rar_set)(struct e1000_hw*, u8*, u32); + s32 (*validate_mdi_setting)(struct e1000_hw*); + s32 (*mng_host_if_write)(struct e1000_hw*, u8*, u16, u16, u8*); + s32 (*mng_write_cmd_header)(struct e1000_hw *hw, + struct e1000_host_mng_command_header*); + s32 (*mng_enable_host_if)(struct e1000_hw*); + s32 (*wait_autoneg)(struct e1000_hw*); + + /* Function pointers for the PHY. */ + s32 (*init_phy_params)(struct e1000_hw *); + s32 (*acquire_phy)(struct e1000_hw *); + s32 (*check_polarity)(struct e1000_hw *); + s32 (*check_reset_block)(struct e1000_hw *); + s32 (*commit_phy)(struct e1000_hw *); + s32 (*force_speed_duplex)(struct e1000_hw *); + s32 (*get_cfg_done)(struct e1000_hw *hw); + s32 (*get_cable_length)(struct e1000_hw *); + s32 (*get_phy_info)(struct e1000_hw *); + s32 (*read_phy_reg)(struct e1000_hw *, u32, u16 *); + void (*release_phy)(struct e1000_hw *); + s32 (*reset_phy)(struct e1000_hw *); + s32 (*set_d0_lplu_state)(struct e1000_hw *, boolean_t); + s32 (*set_d3_lplu_state)(struct e1000_hw *, boolean_t); + s32 (*write_phy_reg)(struct e1000_hw *, u32, u16); + + /* Function pointers for the NVM. */ + s32 (*init_nvm_params)(struct e1000_hw *); + s32 (*acquire_nvm)(struct e1000_hw *); + s32 (*read_nvm)(struct e1000_hw *, u16, u16, u16 *); + void (*release_nvm)(struct e1000_hw *); + void (*reload_nvm)(struct e1000_hw *); + s32 (*update_nvm)(struct e1000_hw *); + s32 (*valid_led_default)(struct e1000_hw *, u16 *); + s32 (*validate_nvm)(struct e1000_hw *); + s32 (*write_nvm)(struct e1000_hw *, u16, u16, u16 *); +}; + +struct e1000_mac_info { + u8 addr[6]; + u8 perm_addr[6]; + + e1000_mac_type type; + e1000_fc_mode fc; + e1000_fc_mode original_fc; + + u32 collision_delta; + u32 ledctl_default; + u32 ledctl_mode1; + u32 ledctl_mode2; + u32 max_frame_size; + u32 mc_filter_type; + u32 min_frame_size; + u32 tx_packet_delta; + u32 txcw; + + u16 current_ifs_val; + u16 ifs_max_val; + u16 ifs_min_val; + u16 ifs_ratio; + u16 ifs_step_size; + u16 mta_reg_count; + u16 rar_entry_count; + u16 fc_high_water; + u16 fc_low_water; + u16 fc_pause_time; + + u8 forced_speed_duplex; + + boolean_t adaptive_ifs; + boolean_t arc_subsystem_valid; + boolean_t asf_firmware_present; + boolean_t autoneg; + boolean_t autoneg_failed; + boolean_t disable_av; + boolean_t disable_hw_init_bits; + boolean_t fc_send_xon; + boolean_t fc_strict_ieee; + boolean_t get_link_status; + boolean_t ifs_params_forced; + boolean_t in_ifs_mode; + boolean_t report_tx_early; + boolean_t serdes_has_link; + boolean_t tx_pkt_filtering; +}; + +struct e1000_phy_info { + e1000_phy_type type; + + e1000_1000t_rx_status local_rx; + e1000_1000t_rx_status remote_rx; + e1000_ms_type ms_type; + e1000_ms_type original_ms_type; + e1000_rev_polarity cable_polarity; + e1000_smart_speed smart_speed; + + u32 addr; + u32 id; + u32 reset_delay_us; /* in usec */ + u32 revision; + + u16 autoneg_advertised; + u16 autoneg_mask; + u16 cable_length; + u16 max_cable_length; + u16 min_cable_length; + + u8 mdix; + + boolean_t disable_polarity_correction; + boolean_t is_mdix; + boolean_t polarity_correction; + boolean_t reset_disable; + boolean_t speed_downgraded; + boolean_t wait_for_link; +}; + +struct e1000_nvm_info { + e1000_nvm_type type; + e1000_nvm_override override; + + u32 flash_bank_size; + u32 flash_base_addr; + + u16 word_size; + u16 delay_usec; + u16 address_bits; + u16 opcode_bits; + u16 page_size; +}; + +struct e1000_bus_info { + e1000_bus_type type; + e1000_bus_speed speed; + e1000_bus_width width; + + u32 snoop; + + u16 func; + u16 pci_cmd_word; +}; + +struct e1000_hw { + void *back; + void *dev_spec; + + u8 *hw_addr; + u8 *flash_address; + unsigned long io_base; + + struct e1000_functions func; + struct e1000_mac_info mac; + struct e1000_phy_info phy; + struct e1000_nvm_info nvm; + struct e1000_bus_info bus; + struct e1000_host_mng_dhcp_cookie mng_cookie; + + e1000_media_type media_type; + + u32 dev_spec_size; + + u16 device_id; + u16 subsystem_vendor_id; + u16 subsystem_device_id; + u16 vendor_id; + + u8 revision_id; +}; + +/* These functions must be implemented by drivers */ +void e1000_pci_clear_mwi(struct e1000_hw *hw); +void e1000_pci_set_mwi(struct e1000_hw *hw); +s32 e1000_alloc_zeroed_dev_spec_struct(struct e1000_hw *hw, u32 size); +s32 e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); +void e1000_free_dev_spec_struct(struct e1000_hw *hw); +void e1000_read_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); +void e1000_write_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_ich8lan.c b/bsd_eth_drivers/if_em/e1000_ich8lan.c new file mode 100644 index 0000000..f680cc9 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_ich8lan.c @@ -0,0 +1,2491 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_ich8lan.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +/* e1000_ich8lan + * e1000_ich9lan + */ + +#include "e1000_api.h" +#include "e1000_ich8lan.h" + +void e1000_init_function_pointers_ich8lan(struct e1000_hw *hw); + +STATIC s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw); +STATIC void e1000_release_swflag_ich8lan(struct e1000_hw *hw); +STATIC boolean_t e1000_check_mng_mode_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, + boolean_t active); +STATIC s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, + boolean_t active); +STATIC s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +STATIC s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +STATIC s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, + u16 *data); +STATIC s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_init_hw_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_setup_link_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_get_link_up_info_ich8lan(struct e1000_hw *hw, + u16 *speed, u16 *duplex); +STATIC s32 e1000_cleanup_led_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_led_on_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_led_off_ich8lan(struct e1000_hw *hw); +STATIC void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw); +STATIC s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank); +static s32 e1000_flash_cycle_ich8lan(struct e1000_hw *hw, u32 timeout); +static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw); +static s32 e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw); +static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw); +static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw); +static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, + u8 size, u16* data); +STATIC s32 e1000_read_flash_word_ich8lan(struct e1000_hw *hw, + u32 offset, u16 *data); +static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw, + u32 offset, u8 byte); +STATIC s32 e1000_write_flash_byte_ich8lan(struct e1000_hw *hw, + u32 offset, u8 data); +static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, + u8 size, u16 data); +STATIC s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw); + +/* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */ +/* Offset 04h HSFSTS */ +union ich8_hws_flash_status { + struct ich8_hsfsts { + u16 flcdone :1; /* bit 0 Flash Cycle Done */ + u16 flcerr :1; /* bit 1 Flash Cycle Error */ + u16 dael :1; /* bit 2 Direct Access error Log */ + u16 berasesz :2; /* bit 4:3 Sector Erase Size */ + u16 flcinprog :1; /* bit 5 flash cycle in Progress */ + u16 reserved1 :2; /* bit 13:6 Reserved */ + u16 reserved2 :6; /* bit 13:6 Reserved */ + u16 fldesvalid :1; /* bit 14 Flash Descriptor Valid */ + u16 flockdn :1; /* bit 15 Flash Config Lock-Down */ + } hsf_status; + u16 regval; +}; + +/* ICH GbE Flash Hardware Sequencing Flash control Register bit breakdown */ +/* Offset 06h FLCTL */ +union ich8_hws_flash_ctrl { + struct ich8_hsflctl { + u16 flcgo :1; /* 0 Flash Cycle Go */ + u16 flcycle :2; /* 2:1 Flash Cycle */ + u16 reserved :5; /* 7:3 Reserved */ + u16 fldbcount :2; /* 9:8 Flash Data Byte Count */ + u16 flockdn :6; /* 15:10 Reserved */ + } hsf_ctrl; + u16 regval; +}; + +/* ICH Flash Region Access Permissions */ +union ich8_hws_flash_regacc { + struct ich8_flracc { + u32 grra :8; /* 0:7 GbE region Read Access */ + u32 grwa :8; /* 8:15 GbE region Write Access */ + u32 gmrag :8; /* 23:16 GbE Master Read Access Grant */ + u32 gmwag :8; /* 31:24 GbE Master Write Access Grant */ + } hsf_flregacc; + u16 regval; +}; + +struct e1000_shadow_ram { + u16 value; + boolean_t modified; +}; + +struct e1000_dev_spec_ich8lan { + boolean_t kmrn_lock_loss_workaround_enabled; + struct e1000_shadow_ram shadow_ram[E1000_SHADOW_RAM_WORDS]; +}; + +/** + * e1000_init_phy_params_ich8lan - Initialize PHY function pointers + * @hw: pointer to the HW structure + * + * Initialize family-specific PHY parameters and function pointers. + **/ +STATIC s32 +e1000_init_phy_params_ich8lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + u16 i = 0; + + DEBUGFUNC("e1000_init_phy_params_ich8lan"); + + phy->addr = 1; + phy->reset_delay_us = 100; + + func->acquire_phy = e1000_acquire_swflag_ich8lan; + func->check_polarity = e1000_check_polarity_ife_ich8lan; + func->check_reset_block = e1000_check_reset_block_ich8lan; + func->force_speed_duplex = e1000_phy_force_speed_duplex_ich8lan; + func->get_cable_length = e1000_get_cable_length_igp_2; + func->get_cfg_done = e1000_get_cfg_done_ich8lan; + func->get_phy_info = e1000_get_phy_info_ich8lan; + func->read_phy_reg = e1000_read_phy_reg_igp; + func->release_phy = e1000_release_swflag_ich8lan; + func->reset_phy = e1000_phy_hw_reset_ich8lan; + func->set_d0_lplu_state = e1000_set_d0_lplu_state_ich8lan; + func->set_d3_lplu_state = e1000_set_d3_lplu_state_ich8lan; + func->write_phy_reg = e1000_write_phy_reg_igp; + + + phy->id = 0; + while ((e1000_phy_unknown == e1000_get_phy_type_from_id(phy->id)) && + (i++ < 100)) { + msec_delay(1); + ret_val = e1000_get_phy_id(hw); + if (ret_val) + goto out; + } + + /* Verify phy id */ + switch (phy->id) { + case IGP03E1000_E_PHY_ID: + phy->type = e1000_phy_igp_3; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + phy->type = e1000_phy_ife; + phy->autoneg_mask = E1000_ALL_NOT_GIG; + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_ich8lan - Initialize NVM function pointers + * @hw: pointer to the HW structure + * + * Initialize family-specific NVM parameters and function + * pointers. + **/ +STATIC s32 +e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_functions *func = &hw->func; + struct e1000_dev_spec_ich8lan *dev_spec; + u32 gfpreg, sector_base_addr, sector_end_addr; + s32 ret_val = E1000_SUCCESS; + u16 i; + + DEBUGFUNC("e1000_init_nvm_params_ich8lan"); + + /* Can't read flash registers if the register set isn't mapped. + */ + if (!hw->flash_address) { + DEBUGOUT("ERROR: Flash registers not mapped\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + nvm->type = e1000_nvm_flash_sw; + + gfpreg = E1000_READ_FLASH_REG(hw, ICH_FLASH_GFPREG); + + /* sector_X_addr is a "sector"-aligned address (4096 bytes) + * Add 1 to sector_end_addr since this sector is included in + * the overall size. */ + sector_base_addr = gfpreg & FLASH_GFPREG_BASE_MASK; + sector_end_addr = ((gfpreg >> 16) & FLASH_GFPREG_BASE_MASK) + 1; + + /* flash_base_addr is byte-aligned */ + nvm->flash_base_addr = sector_base_addr << FLASH_SECTOR_ADDR_SHIFT; + + /* find total size of the NVM, then cut in half since the total + * size represents two separate NVM banks. */ + nvm->flash_bank_size = (sector_end_addr - sector_base_addr) + << FLASH_SECTOR_ADDR_SHIFT; + nvm->flash_bank_size /= 2; + /* Adjust to word count */ + nvm->flash_bank_size /= sizeof(u16); + + nvm->word_size = E1000_SHADOW_RAM_WORDS; + + dev_spec = (struct e1000_dev_spec_ich8lan *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + /* Clear shadow ram */ + for (i = 0; i < nvm->word_size; i++) { + dev_spec->shadow_ram[i].modified = FALSE; + dev_spec->shadow_ram[i].value = 0xFFFF; + } + + /* Function Pointers */ + func->acquire_nvm = e1000_acquire_swflag_ich8lan; + func->read_nvm = e1000_read_nvm_ich8lan; + func->release_nvm = e1000_release_swflag_ich8lan; + func->update_nvm = e1000_update_nvm_checksum_ich8lan; + func->valid_led_default = e1000_valid_led_default_ich8lan; + func->validate_nvm = e1000_validate_nvm_checksum_ich8lan; + func->write_nvm = e1000_write_nvm_ich8lan; + +out: + return ret_val; +} + +/** + * e1000_init_mac_params_ich8lan - Initialize MAC function pointers + * @hw: pointer to the HW structure + * + * Initialize family-specific MAC parameters and function + * pointers. + **/ +STATIC s32 +e1000_init_mac_params_ich8lan(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_init_mac_params_ich8lan"); + + /* Set media type function pointer */ + hw->media_type = e1000_media_type_copper; + + /* Set mta register count */ + mac->mta_reg_count = 32; + /* Set rar entry count */ + mac->rar_entry_count = E1000_ICH_RAR_ENTRIES; + if (mac->type == e1000_ich8lan) + mac->rar_entry_count--; + /* Set if part includes ASF firmware */ + mac->asf_firmware_present = TRUE; + /* Set if manageability features are enabled. */ + mac->arc_subsystem_valid = TRUE; + + /* Function pointers */ + + /* bus type/speed/width */ + func->get_bus_info = e1000_get_bus_info_ich8lan; + /* reset */ + func->reset_hw = e1000_reset_hw_ich8lan; + /* hw initialization */ + func->init_hw = e1000_init_hw_ich8lan; + /* link setup */ + func->setup_link = e1000_setup_link_ich8lan; + /* physical interface setup */ + func->setup_physical_interface = e1000_setup_copper_link_ich8lan; + /* check for link */ + func->check_for_link = e1000_check_for_copper_link_generic; + /* check management mode */ + func->check_mng_mode = e1000_check_mng_mode_ich8lan; + /* link info */ + func->get_link_up_info = e1000_get_link_up_info_ich8lan; + /* multicast address update */ + func->mc_addr_list_update = e1000_mc_addr_list_update_generic; + /* setting MTA */ + func->mta_set = e1000_mta_set_generic; + /* blink LED */ + func->blink_led = e1000_blink_led_generic; + /* setup LED */ + func->setup_led = e1000_setup_led_generic; + /* cleanup LED */ + func->cleanup_led = e1000_cleanup_led_ich8lan; + /* turn on/off LED */ + func->led_on = e1000_led_on_ich8lan; + func->led_off = e1000_led_off_ich8lan; + /* remove device */ + func->remove_device = e1000_remove_device_generic; + /* clear hardware counters */ + func->clear_hw_cntrs = e1000_clear_hw_cntrs_ich8lan; + + hw->dev_spec_size = sizeof(struct e1000_dev_spec_ich8lan); + + /* Device-specific structure allocation */ + ret_val = e1000_alloc_zeroed_dev_spec_struct(hw, hw->dev_spec_size); + if (ret_val) + goto out; + + /* Enable PCS Lock-loss workaround for ICH8 */ + if (mac->type == e1000_ich8lan) + e1000_set_kmrn_lock_loss_workaround_ich8lan(hw, TRUE); + + +out: + return ret_val; +} + +/** + * e1000_init_function_pointers_ich8lan - Initialize ICH8 function pointers + * @hw: pointer to the HW structure + * + * Initialize family-specific function pointers for PHY, MAC, and NVM. + **/ +void +e1000_init_function_pointers_ich8lan(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_ich8lan"); + + hw->func.init_mac_params = e1000_init_mac_params_ich8lan; + hw->func.init_nvm_params = e1000_init_nvm_params_ich8lan; + hw->func.init_phy_params = e1000_init_phy_params_ich8lan; +} + +/** + * e1000_acquire_swflag_ich8lan - Acquire software control flag + * @hw: pointer to the HW structure + * + * Acquires the software control flag for performing NVM and PHY + * operations. This is a function pointer entry point only called by + * read/write routines for the PHY and NVM parts. + **/ +STATIC s32 +e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) +{ + u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_acquire_swflag_ich8lan"); + + while (timeout) { + extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL); + extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; + E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl); + + extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL); + if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) + break; + msec_delay_irq(1); + timeout--; + } + + if (!timeout) { + DEBUGOUT("FW or HW has locked the resource for too long.\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_release_swflag_ich8lan - Release software control flag + * @hw: pointer to the HW structure + * + * Releases the software control flag for performing NVM and PHY operations. + * This is a function pointer entry point only called by read/write + * routines for the PHY and NVM parts. + **/ +STATIC void +e1000_release_swflag_ich8lan(struct e1000_hw *hw) +{ + u32 extcnf_ctrl; + + DEBUGFUNC("e1000_release_swflag_ich8lan"); + + extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL); + extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; + E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl); + + return; +} + +/** + * e1000_check_mng_mode_ich8lan - Checks management mode + * @hw: pointer to the HW structure + * + * This checks if the adapter has manageability enabled. + * This is a function pointer entry point only called by read/write + * routines for the PHY and NVM parts. + **/ +STATIC boolean_t +e1000_check_mng_mode_ich8lan(struct e1000_hw *hw) +{ + u32 fwsm; + + DEBUGFUNC("e1000_check_mng_mode_ich8lan"); + + fwsm = E1000_READ_REG(hw, E1000_FWSM); + + return ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)); +} + +/** + * e1000_check_reset_block_ich8lan - Check if PHY reset is blocked + * @hw: pointer to the HW structure + * + * Checks if firmware is blocking the reset of the PHY. + * This is a function pointer entry point only called by + * reset routines. + **/ +STATIC s32 +e1000_check_reset_block_ich8lan(struct e1000_hw *hw) +{ + u32 fwsm; + + DEBUGFUNC("e1000_check_reset_block_ich8lan"); + + fwsm = E1000_READ_REG(hw, E1000_FWSM); + + return (fwsm & E1000_ICH_FWSM_RSPCIPHY) ? E1000_SUCCESS + : E1000_BLK_PHY_RESET; +} + +/** + * e1000_phy_force_speed_duplex_ich8lan - Force PHY speed & duplex + * @hw: pointer to the HW structure + * + * Forces the speed and duplex settings of the PHY. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +STATIC s32 +e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + boolean_t link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_ich8lan"); + + if (phy->type != e1000_phy_ife) { + ret_val = e1000_phy_force_speed_duplex_igp(hw); + goto out; + } + + ret_val = e1000_read_phy_reg(hw, PHY_CONTROL, &data); + if (ret_val) + goto out; + + e1000_phy_force_speed_duplex_setup(hw, &data); + + ret_val = e1000_write_phy_reg(hw, PHY_CONTROL, data); + if (ret_val) + goto out; + + /* Disable MDI-X support for 10/100 */ + ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + goto out; + + data &= ~IFE_PMC_AUTO_MDIX; + data &= ~IFE_PMC_FORCE_MDIX; + + ret_val = e1000_write_phy_reg(hw, IFE_PHY_MDIX_CONTROL, data); + if (ret_val) + goto out; + + DEBUGOUT1("IFE PMC: %X\n", data); + + usec_delay(1); + + if (phy->wait_for_link) { + DEBUGOUT("Waiting for forced speed/duplex link on IFE phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + + if (!link) { + DEBUGOUT("Link taking longer than expected.\n"); + } + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_phy_hw_reset_ich8lan - Performs a PHY reset + * @hw: pointer to the HW structure + * + * Resets the PHY + * This is a function pointer entry point called by drivers + * or other shared routines. + **/ +STATIC s32 +e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask; + s32 ret_val; + u16 loop = E1000_ICH8_LAN_INIT_TIMEOUT; + u16 word_addr, reg_data, reg_addr, phy_page = 0; + + DEBUGFUNC("e1000_phy_hw_reset_ich8lan"); + + ret_val = e1000_phy_hw_reset_generic(hw); + if (ret_val) + goto out; + + /* Initialize the PHY from the NVM on ICH platforms. This + * is needed due to an issue where the NVM configuration is + * not properly autoloaded after power transitions. + * Therefore, after each PHY reset, we will load the + * configuration data out of the NVM manually. + */ + if (hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) { + /* Check if SW needs configure the PHY */ + if ((hw->device_id == E1000_DEV_ID_ICH8_IGP_M_AMT) || + (hw->device_id == E1000_DEV_ID_ICH8_IGP_M)) + sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M; + else + sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG; + + data = E1000_READ_REG(hw, E1000_FEXTNVM); + if (!(data & sw_cfg_mask)) + goto out; + + /* Wait for basic configuration completes before proceeding*/ + do { + data = E1000_READ_REG(hw, E1000_STATUS); + data &= E1000_STATUS_LAN_INIT_DONE; + usec_delay(100); + } while ((!data) && --loop); + + /* If basic configuration is incomplete before the above loop + * count reaches 0, loading the configuration from NVM will + * leave the PHY in a bad state possibly resulting in no link. + */ + if (loop == 0) { + DEBUGOUT("LAN_INIT_DONE not set, increase timeout\n"); + } + + /* Clear the Init Done bit for the next init event */ + data = E1000_READ_REG(hw, E1000_STATUS); + data &= ~E1000_STATUS_LAN_INIT_DONE; + E1000_WRITE_REG(hw, E1000_STATUS, data); + + /* Make sure HW does not configure LCD from PHY + * extended configuration before SW configuration */ + data = E1000_READ_REG(hw, E1000_EXTCNF_CTRL); + if (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) + goto out; + + cnf_size = E1000_READ_REG(hw, E1000_EXTCNF_SIZE); + cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK; + cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT; + if (!cnf_size) + goto out; + + cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK; + cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT; + + /* Configure LCD from extended configuration + * region. */ + + /* cnf_base_addr is in DWORD */ + word_addr = (u16)(cnf_base_addr << 1); + + for (i = 0; i < cnf_size; i++) { + ret_val = e1000_read_nvm(hw, + (word_addr + i * 2), + 1, + ®_data); + if (ret_val) + goto out; + + ret_val = e1000_read_nvm(hw, + (word_addr + i * 2 + 1), + 1, + ®_addr); + if (ret_val) + goto out; + + /* Save off the PHY page for future writes. */ + if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) { + phy_page = reg_data; + continue; + } + + reg_addr |= phy_page; + + ret_val = e1000_write_phy_reg(hw, + (u32)reg_addr, + reg_data); + if (ret_val) + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_get_phy_info_ich8lan - Calls appropriate PHY type get_phy_info + * @hw: pointer to the HW structure + * + * Wrapper for calling the get_phy_info routines for the appropriate phy type. + * This is a function pointer entry point called by drivers + * or other shared routines. + **/ +STATIC s32 +e1000_get_phy_info_ich8lan(struct e1000_hw *hw) +{ + s32 ret_val = -E1000_ERR_PHY_TYPE; + + DEBUGFUNC("e1000_get_phy_info_ich8lan"); + + switch (hw->phy.type) { + case e1000_phy_ife: + ret_val = e1000_get_phy_info_ife_ich8lan(hw); + break; + case e1000_phy_igp_3: + ret_val = e1000_get_phy_info_igp(hw); + break; + default: + break; + } + + return ret_val; +} + +/** + * e1000_get_phy_info_ife_ich8lan - Retrieves various IFE PHY states + * @hw: pointer to the HW structure + * + * Populates "phy" structure with various feature states. + * This function is only called by other family-specific + * routines. + **/ +static s32 +e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + boolean_t link; + + DEBUGFUNC("e1000_get_phy_info_ife_ich8lan"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &data); + if (ret_val) + goto out; + phy->polarity_correction = (data & IFE_PSC_AUTO_POLARITY_DISABLE) + ? FALSE : TRUE; + + if (phy->polarity_correction) { + ret_val = e1000_check_polarity_ife_ich8lan(hw); + if (ret_val) + goto out; + } else { + /* Polarity is forced */ + phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal; + } + + ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + goto out; + + phy->is_mdix = (data & IFE_PMC_MDIX_STATUS) ? TRUE : FALSE; + + /* The following parameters are undefined for 10/100 operation. */ + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + +out: + return ret_val; +} + +/** + * e1000_check_polarity_ife_ich8lan - Check cable polarity for IFE PHY + * @hw: pointer to the HW structure + * + * Polarity is determined on the polarity reveral feature being enabled. + * This function is only called by other family-specific + * routines. + **/ +STATIC s32 +e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, offset, mask; + + DEBUGFUNC("e1000_check_polarity_ife_ich8lan"); + + /* Polarity is determined based on the reversal feature + * being enabled. + */ + if (phy->polarity_correction) { + offset = IFE_PHY_EXTENDED_STATUS_CONTROL; + mask = IFE_PESC_POLARITY_REVERSED; + } else { + offset = IFE_PHY_SPECIAL_CONTROL; + mask = IFE_PSC_FORCE_POLARITY; + } + + ret_val = e1000_read_phy_reg(hw, offset, &phy_data); + + if (!ret_val) + phy->cable_polarity = (phy_data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal; + + return ret_val; +} + +/** + * e1000_set_d0_lplu_state_ich8lan - Set Low Power Linkup D0 state + * @hw: pointer to the HW structure + * @active: TRUE to enable LPLU, FALSE to disable + * + * Sets the LPLU D0 state according to the active flag. When + * activating LPLU this function also disables smart speed + * and vice versa. LPLU will not be activated unless the + * device autonegotiation advertisement meets standards of + * either 10 or 10/100 or 10/100/1000 at all duplexes. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +STATIC s32 +e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, boolean_t active) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 phy_ctrl; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_set_d0_lplu_state_ich8lan"); + + if (phy->type != e1000_phy_igp_3) + goto out; + + phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL); + + if (active) { + phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl); + + /* Call gig speed drop workaround on LPLU before accessing + * any PHY registers */ + if ((hw->mac.type == e1000_ich8lan) && + (hw->phy.type == e1000_phy_igp_3)) + e1000_gig_downshift_workaround_ich8lan(hw); + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else { + phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl); + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_set_d3_lplu_state_ich8lan - Set Low Power Linkup D3 state + * @hw: pointer to the HW structure + * @active: TRUE to enable LPLU, FALSE to disable + * + * Sets the LPLU D3 state according to the active flag. When + * activating LPLU this function also disables smart speed + * and vice versa. LPLU will not be activated unless the + * device autonegotiation advertisement meets standards of + * either 10 or 10/100 or 10/100/1000 at all duplexes. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +STATIC s32 +e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, boolean_t active) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 phy_ctrl; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_set_d3_lplu_state_ich8lan"); + + phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL); + + if (!active) { + phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl); + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || + (phy->autoneg_advertised == E1000_ALL_10_SPEED)) { + phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl); + + /* Call gig speed drop workaround on LPLU before accessing + * any PHY registers */ + if ((hw->mac.type == e1000_ich8lan) && + (hw->phy.type == e1000_phy_igp_3)) + e1000_gig_downshift_workaround_ich8lan(hw); + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + } + +out: + return ret_val; +} + +/** + * e1000_read_nvm_ich8lan - Read word(s) from the NVM + * @hw: pointer to the HW structure + * @offset: The offset (in bytes) of the word(s) to read. + * @words: Size of data to read in words + * @data: Pointer to the word(s) to read at offset. + * + * Reads a word(s) from the NVM using the flash access registers. + **/ +STATIC s32 +e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_dev_spec_ich8lan *dev_spec; + u32 act_offset; + s32 ret_val = E1000_SUCCESS; + u16 i, word; + + DEBUGFUNC("e1000_read_nvm_ich8lan"); + + dev_spec = (struct e1000_dev_spec_ich8lan *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + ret_val = e1000_acquire_nvm(hw); + if (ret_val) + goto out; + + /* Start with the bank offset, then add the relative offset. */ + act_offset = (E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_SEC1VAL) + ? nvm->flash_bank_size + : 0; + act_offset += offset; + + for (i = 0; i < words; i++) { + if ((dev_spec->shadow_ram != NULL) && + (dev_spec->shadow_ram[offset+i].modified == TRUE)) { + data[i] = dev_spec->shadow_ram[offset+i].value; + } else { + ret_val = e1000_read_flash_word_ich8lan(hw, + act_offset + i, + &word); + if (ret_val) + break; + data[i] = word; + } + } + + e1000_release_nvm(hw); + +out: + return ret_val; +} + +/** + * e1000_flash_cycle_init_ich8lan - Initialize flash + * @hw: pointer to the HW structure + * + * This function does initial flash setup so that a new read/write/erase cycle + * can be started. + **/ +static s32 +e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) +{ + union ich8_hws_flash_status hsfsts; + s32 ret_val = -E1000_ERR_NVM; + s32 i = 0; + + DEBUGFUNC("e1000_flash_cycle_init_ich8lan"); + + hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + + /* Check if the flash descriptor is valid */ + if (hsfsts.hsf_status.fldesvalid == 0) { + DEBUGOUT("Flash descriptor invalid. " + "SW Sequencing must be used."); + goto out; + } + + /* Clear FCERR and DAEL in hw status by writing 1 */ + hsfsts.hsf_status.flcerr = 1; + hsfsts.hsf_status.dael = 1; + + E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); + + /* Either we should have a hardware SPI cycle in progress + * bit to check against, in order to start a new cycle or + * FDONE bit should be changed in the hardware so that it + * is 1 after harware reset, which can then be used as an + * indication whether a cycle is in progress or has been + * completed. + */ + + if (hsfsts.hsf_status.flcinprog == 0) { + /* There is no cycle running at present, + * so we can start a cycle */ + /* Begin by setting Flash Cycle Done. */ + hsfsts.hsf_status.flcdone = 1; + E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); + ret_val = E1000_SUCCESS; + } else { + /* otherwise poll for sometime so the current + * cycle has a chance to end before giving up. */ + for (i = 0; i < ICH_FLASH_READ_COMMAND_TIMEOUT; i++) { + hsfsts.regval = E1000_READ_FLASH_REG16(hw, + ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcinprog == 0) { + ret_val = E1000_SUCCESS; + break; + } + usec_delay(1); + } + if (ret_val == E1000_SUCCESS) { + /* Successful in waiting for previous cycle to timeout, + * now set the Flash Cycle Done. */ + hsfsts.hsf_status.flcdone = 1; + E1000_WRITE_FLASH_REG16(hw, + ICH_FLASH_HSFSTS, + hsfsts.regval); + } else { + DEBUGOUT("Flash controller busy, cannot get access"); + } + } + +out: + return ret_val; +} + +/** + * e1000_flash_cycle_ich8lan - Starts flash cycle (read/write/erase) + * @hw: pointer to the HW structure + * @timeout: maximum time to wait for completion + * + * This function starts a flash cycle and waits for its completion. + **/ +static s32 +e1000_flash_cycle_ich8lan(struct e1000_hw *hw, u32 timeout) +{ + union ich8_hws_flash_ctrl hsflctl; + union ich8_hws_flash_status hsfsts; + s32 ret_val = -E1000_ERR_NVM; + u32 i = 0; + + DEBUGFUNC("e1000_flash_cycle_ich8lan"); + + /* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */ + hsflctl.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFCTL); + hsflctl.hsf_ctrl.flcgo = 1; + E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); + + /* wait till FDONE bit is set to 1 */ + do { + hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcdone == 1) + break; + usec_delay(1); + } while (i++ < timeout); + + if (hsfsts.hsf_status.flcdone == 1 && hsfsts.hsf_status.flcerr == 0) + ret_val = E1000_SUCCESS; + + return ret_val; +} + +/** + * e1000_read_flash_word_ich8lan - Read word from flash + * @hw: pointer to the HW structure + * @offset: offset to data location + * @data: pointer to the location for storing the data + * + * Reads the flash word at offset into data. Offset is converted + * to bytes before read. + **/ +STATIC s32 +e1000_read_flash_word_ich8lan(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_flash_word_ich8lan"); + + if (data == NULL) { + ret_val = -E1000_ERR_NVM; + goto out; + } + + /* Must convert offset into bytes. */ + offset <<= 1; + + ret_val = e1000_read_flash_data_ich8lan(hw, offset, 2, data); + +out: + return ret_val; +} + +/** + * e1000_read_flash_data_ich8lan - Read byte or word from NVM + * @hw: pointer to the HW structure + * @offset: The offset (in bytes) of the byte or word to read. + * @size: Size of data to read, 1=byte 2=word + * @data: Pointer to the word to store the value read. + * + * Reads a byte or word from the NVM using the flash access registers. + **/ +static s32 +e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, + u8 size, u16* data) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + u32 flash_linear_addr; + u32 flash_data = 0; + s32 ret_val = -E1000_ERR_NVM; + u8 count = 0; + + DEBUGFUNC("e1000_read_flash_data_ich8lan"); + + if (size < 1 || size > 2 || data == 0x0 || + offset > ICH_FLASH_LINEAR_ADDR_MASK) + goto out; + + flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) + + hw->nvm.flash_base_addr; + + do { + usec_delay(1); + /* Steps */ + ret_val = e1000_flash_cycle_init_ich8lan(hw); + if (ret_val != E1000_SUCCESS) + break; + + hsflctl.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFCTL); + /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ + hsflctl.hsf_ctrl.fldbcount = size - 1; + hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_READ; + E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); + + E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_addr); + + ret_val = e1000_flash_cycle_ich8lan(hw, + ICH_FLASH_READ_COMMAND_TIMEOUT); + + /* Check if FCERR is set to 1, if set to 1, clear it + * and try the whole sequence a few more times, else + * read in (shift in) the Flash Data0, the order is + * least significant byte first msb to lsb */ + if (ret_val == E1000_SUCCESS) { + flash_data = E1000_READ_FLASH_REG(hw, ICH_FLASH_FDATA0); + if (size == 1) { + *data = (u8)(flash_data & 0x000000FF); + } else if (size == 2) { + *data = (u16)(flash_data & 0x0000FFFF); + } + break; + } else { + /* If we've gotten here, then things are probably + * completely hosed, but if the error condition is + * detected, it won't hurt to give it another try... + * ICH_FLASH_CYCLE_REPEAT_COUNT times. + */ + hsfsts.regval = E1000_READ_FLASH_REG16(hw, + ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* Repeat for some time before giving up. */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + DEBUGOUT("Timeout error - flash cycle " + "did not complete."); + break; + } + } + } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT); + +out: + return ret_val; +} + +/** + * e1000_write_nvm_ich8lan - Write word(s) to the NVM + * @hw: pointer to the HW structure + * @offset: The offset (in bytes) of the word(s) to write. + * @words: Size of data to write in words + * @data: Pointer to the word(s) to write at offset. + * + * Writes a byte or word to the NVM using the flash access registers. + **/ +STATIC s32 +e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_dev_spec_ich8lan *dev_spec; + s32 ret_val = E1000_SUCCESS; + u16 i; + + DEBUGFUNC("e1000_write_nvm_ich8lan"); + + dev_spec = (struct e1000_dev_spec_ich8lan *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + ret_val = e1000_acquire_nvm(hw); + if (ret_val) + goto out; + + for (i = 0; i < words; i++) { + dev_spec->shadow_ram[offset+i].modified = TRUE; + dev_spec->shadow_ram[offset+i].value = data[i]; + } + + e1000_release_nvm(hw); + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_ich8lan - Update the checksum for NVM + * @hw: pointer to the HW structure + * + * The NVM checksum is updated by calling the generic update_nvm_checksum, + * which writes the checksum to the shadow ram. The changes in the shadow + * ram are then committed to the EEPROM by processing each bank at a time + * checking for the modified bit and writing only the pending changes. + * After a succesful commit, the shadow ram is cleared and is ready for + * future writes. + **/ +STATIC s32 +e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + struct e1000_dev_spec_ich8lan *dev_spec; + u32 i, act_offset, new_bank_offset, old_bank_offset; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_update_nvm_checksum_ich8lan"); + + dev_spec = (struct e1000_dev_spec_ich8lan *)hw->dev_spec; + + ret_val = e1000_update_nvm_checksum_generic(hw); + if (ret_val) + goto out; + + if (nvm->type != e1000_nvm_flash_sw) + goto out; + + ret_val = e1000_acquire_nvm(hw); + if (ret_val) + goto out; + + /* We're writing to the opposite bank so if we're on bank 1, + * write to bank 0 etc. We also need to erase the segment that + * is going to be written */ + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_SEC1VAL)) { + new_bank_offset = nvm->flash_bank_size; + old_bank_offset = 0; + e1000_erase_flash_bank_ich8lan(hw, 1); + } else { + old_bank_offset = nvm->flash_bank_size; + new_bank_offset = 0; + e1000_erase_flash_bank_ich8lan(hw, 0); + } + + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + /* Determine whether to write the value stored + * in the other NVM bank or a modified value stored + * in the shadow RAM */ + if (dev_spec->shadow_ram[i].modified == TRUE) { + data = dev_spec->shadow_ram[i].value; + } else { + e1000_read_flash_word_ich8lan(hw, + i + old_bank_offset, + &data); + } + + /* If the word is 0x13, then make sure the signature bits + * (15:14) are 11b until the commit has completed. + * This will allow us to write 10b which indicates the + * signature is valid. We want to do this after the write + * has completed so that we don't mark the segment valid + * while the write is still in progress */ + if (i == E1000_ICH_NVM_SIG_WORD) + data |= E1000_ICH_NVM_SIG_MASK; + + /* Convert offset to bytes. */ + act_offset = (i + new_bank_offset) << 1; + + usec_delay(100); + /* Write the bytes to the new bank. */ + ret_val = e1000_retry_write_flash_byte_ich8lan(hw, + act_offset, + (u8)data); + if (ret_val) + break; + + usec_delay(100); + ret_val = e1000_retry_write_flash_byte_ich8lan(hw, + act_offset + 1, + (u8)(data >> 8)); + if (ret_val) + break; + } + + /* Don't bother writing the segment valid bits if sector + * programming failed. */ + if (ret_val) { + DEBUGOUT("Flash commit failed.\n"); + e1000_release_nvm(hw); + goto out; + } + + /* Finally validate the new segment by setting bit 15:14 + * to 10b in word 0x13 , this can be done without an + * erase as well since these bits are 11 to start with + * and we need to change bit 14 to 0b */ + act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD; + e1000_read_flash_word_ich8lan(hw, act_offset, &data); + data &= 0xBFFF; + ret_val = e1000_retry_write_flash_byte_ich8lan(hw, + act_offset * 2 + 1, + (u8)(data >> 8)); + if (ret_val) { + e1000_release_nvm(hw); + goto out; + } + + /* And invalidate the previously valid segment by setting + * its signature word (0x13) high_byte to 0b. This can be + * done without an erase because flash erase sets all bits + * to 1's. We can write 1's to 0's without an erase */ + act_offset = (old_bank_offset + E1000_ICH_NVM_SIG_WORD) * 2 + 1; + ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0); + if (ret_val) { + e1000_release_nvm(hw); + goto out; + } + + /* Great! Everything worked, we can now clear the cached entries. */ + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + dev_spec->shadow_ram[i].modified = FALSE; + dev_spec->shadow_ram[i].value = 0xFFFF; + } + + e1000_release_nvm(hw); + + /* Reload the EEPROM, or else modifications will not appear + * until after the next adapter reset. + */ + e1000_reload_nvm(hw); + msec_delay(10); + +out: + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_ich8lan - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Check to see if checksum needs to be fixed by reading bit 6 in word 0x19. + * If the bit is 0, that the EEPROM had been modified, but the checksum was not + * calculated, in which case we need to calculate the checksum and set bit 6. + **/ +STATIC s32 +e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_validate_nvm_checksum_ich8lan"); + + /* Read 0x19 and check bit 6. If this bit is 0, the checksum + * needs to be fixed. This bit is an indication that the NVM + * was prepared by OEM software and did not calculate the + * checksum...a likely scenario. + */ + ret_val = e1000_read_nvm(hw, 0x19, 1, &data); + if (ret_val) + goto out; + + if ((data & 0x40) == 0) { + data |= 0x40; + ret_val = e1000_write_nvm(hw, 0x19, 1, &data); + if (ret_val) + goto out; + ret_val = e1000_update_nvm_checksum(hw); + if (ret_val) + goto out; + } + + ret_val = e1000_validate_nvm_checksum_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_write_flash_data_ich8lan - Writes bytes to the NVM + * @hw: pointer to the HW structure + * @offset: The offset (in bytes) of the byte/word to read. + * @size: Size of data to read, 1=byte 2=word + * @data: The byte(s) to write to the NVM. + * + * Writes one/two bytes to the NVM using the flash access registers. + **/ +static s32 +e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, + u8 size, u16 data) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + u32 flash_linear_addr; + u32 flash_data = 0; + s32 ret_val = -E1000_ERR_NVM; + u8 count = 0; + + DEBUGFUNC("e1000_write_ich8_data"); + + if (size < 1 || size > 2 || data > size * 0xff || + offset > ICH_FLASH_LINEAR_ADDR_MASK) + goto out; + + flash_linear_addr = (ICH_FLASH_LINEAR_ADDR_MASK & offset) + + hw->nvm.flash_base_addr; + + do { + usec_delay(1); + /* Steps */ + ret_val = e1000_flash_cycle_init_ich8lan(hw); + if (ret_val != E1000_SUCCESS) + break; + + hsflctl.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFCTL); + /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ + hsflctl.hsf_ctrl.fldbcount = size -1; + hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_WRITE; + E1000_WRITE_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); + + E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_addr); + + if (size == 1) + flash_data = (u32)data & 0x00FF; + else + flash_data = (u32)data; + + E1000_WRITE_FLASH_REG(hw, ICH_FLASH_FDATA0, flash_data); + + /* check if FCERR is set to 1 , if set to 1, clear it + * and try the whole sequence a few more times else done */ + ret_val = e1000_flash_cycle_ich8lan(hw, + ICH_FLASH_WRITE_COMMAND_TIMEOUT); + if (ret_val == E1000_SUCCESS) { + break; + } else { + /* If we're here, then things are most likely + * completely hosed, but if the error condition + * is detected, it won't hurt to give it another + * try...ICH_FLASH_CYCLE_REPEAT_COUNT times. + */ + hsfsts.regval = E1000_READ_FLASH_REG16(hw, + ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* Repeat for some time before giving up. */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + DEBUGOUT("Timeout error - flash cycle " + "did not complete."); + break; + } + } + } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT); + +out: + return ret_val; +} + +/** + * e1000_write_flash_byte_ich8lan - Write a single byte to NVM + * @hw: pointer to the HW structure + * @offset: The index of the byte to read. + * @data: The byte to write to the NVM. + * + * Writes a single byte to the NVM using the flash access registers. + **/ +STATIC s32 +e1000_write_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset, u8 data) +{ + u16 word = (u16)data; + + DEBUGFUNC("e1000_write_flash_byte_ich8lan"); + + return e1000_write_flash_data_ich8lan(hw, offset, 1, word); +} + +/** + * e1000_retry_write_flash_byte_ich8lan - Writes a single byte to NVM + * @hw: pointer to the HW structure + * @offset: The offset of the byte to write. + * @byte: The byte to write to the NVM. + * + * Writes a single byte to the NVM using the flash access registers. + * Goes through a retry algorithm before giving up. + **/ +static s32 +e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset, u8 byte) +{ + s32 ret_val; + u16 program_retries; + + DEBUGFUNC("e1000_retry_write_flash_byte_ich8lan"); + + ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte); + if (ret_val) + goto out; + + usec_delay(100); + + for (program_retries = 0; program_retries < 100; program_retries++) { + DEBUGOUT2("Retrying Byte %2.2X at offset %u\n", byte, offset); + usec_delay(100); + ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte); + if (ret_val == E1000_SUCCESS) + break; + } + if (program_retries == 100) { + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_erase_flash_bank_ich8lan - Erase a bank (4k) from NVM + * @hw: pointer to the HW structure + * @bank: 0 for first bank, 1 for second bank, etc. + * + * Erases the bank specified. Each bank is a 4k block. Banks are 0 based. + * bank N is 4096 * N + flash_reg_addr. + **/ +STATIC s32 +e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + u32 flash_linear_addr; + u32 flash_bank_size = nvm->flash_bank_size * 2; /* bank size is in 16bit words - adjust to bytes */ + s32 ret_val = E1000_SUCCESS; + s32 count = 0; + s32 j, iteration, sector_size; + + DEBUGFUNC("e1000_erase_flash_bank_ich8lan"); + + hsfsts.regval = E1000_READ_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + + /* Determine HW Sector size: Read BERASE bits of hw flash status + * register */ + /* 00: The Hw sector is 256 bytes, hence we need to erase 16 + * consecutive sectors. The start index for the nth Hw sector + * can be calculated as = bank * 4096 + n * 256 + * 01: The Hw sector is 4K bytes, hence we need to erase 1 sector. + * The start index for the nth Hw sector can be calculated + * as = bank * 4096 + * 10: The Hw sector is 8K bytes, nth sector = bank * 8192 + * (ich9 only, otherwise error condition) + * 11: The Hw sector is 64K bytes, nth sector = bank * 65536 + */ + switch (hsfsts.hsf_status.berasesz) { + case 0: + /* Hw sector size 256 */ + sector_size = ICH_FLASH_SEG_SIZE_256; + iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_256; + break; + case 1: + sector_size = ICH_FLASH_SEG_SIZE_4K; + iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_4K; + break; + case 2: + if (hw->mac.type == e1000_ich9lan) { + sector_size = ICH_FLASH_SEG_SIZE_8K; + iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_8K; + } else { + ret_val = -E1000_ERR_NVM; + goto out; + } + break; + case 3: + sector_size = ICH_FLASH_SEG_SIZE_64K; + iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_64K; + break; + default: + ret_val = -E1000_ERR_NVM; + goto out; + } + + /* Start with the base address, then add the sector offset. */ + flash_linear_addr = hw->nvm.flash_base_addr; + flash_linear_addr += (bank) ? (sector_size * iteration) : 0; + + for (j = 0; j < iteration ; j++) { + do { + /* Steps */ + ret_val = e1000_flash_cycle_init_ich8lan(hw); + if (ret_val) + goto out; + + /* Write a value 11 (block Erase) in Flash + * Cycle field in hw flash control */ + hsflctl.regval = E1000_READ_FLASH_REG16(hw, + ICH_FLASH_HSFCTL); + hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_ERASE; + E1000_WRITE_FLASH_REG16(hw, + ICH_FLASH_HSFCTL, + hsflctl.regval); + + /* Write the last 24 bits of an index within the + * block into Flash Linear address field in Flash + * Address. + */ + flash_linear_addr += (j * sector_size); + E1000_WRITE_FLASH_REG(hw, + ICH_FLASH_FADDR, + flash_linear_addr); + + ret_val = e1000_flash_cycle_ich8lan(hw, + ICH_FLASH_ERASE_COMMAND_TIMEOUT); + if (ret_val == E1000_SUCCESS) { + break; + } else { + /* Check if FCERR is set to 1. If 1, + * clear it and try the whole sequence + * a few more times else Done */ + hsfsts.regval = E1000_READ_FLASH_REG16(hw, + ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* repeat for some time before + * giving up */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) + goto out; + } + } while (++count < ICH_FLASH_CYCLE_REPEAT_COUNT); + } + +out: + return ret_val; +} + +/** + * e1000_valid_led_default_ich8lan - Set the default LED settings + * @hw: pointer to the HW structure + * @data: Pointer to the LED settings + * + * Reads the LED default settings from the NVM to data. If the NVM LED + * settings is all 0's or F's, set the LED default to a valid LED default + * setting. + **/ +STATIC s32 +e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_ich8lan"); + + ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (*data == ID_LED_RESERVED_0000 || + *data == ID_LED_RESERVED_FFFF) + *data = ID_LED_DEFAULT_ICH8LAN; + +out: + return ret_val; +} + +/** + * e1000_get_bus_info_ich8lan - Get/Set the bus type and width + * @hw: pointer to the HW structure + * + * ICH8 use the PCI Express bus, but does not contain a PCI Express Capability + * register, so the the bus width is hard coded. + **/ +STATIC s32 +e1000_get_bus_info_ich8lan(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + s32 ret_val; + + DEBUGFUNC("e1000_get_bus_info_ich8lan"); + + ret_val = e1000_get_bus_info_pcie_generic(hw); + + /* ICH devices are "PCI Express"-ish. They have + * a configuration space, but do not contain + * PCI Express Capability registers, so bus width + * must be hardcoded. + */ + if (bus->width == e1000_bus_width_unknown) + bus->width = e1000_bus_width_pcie_x1; + + return ret_val; +} + +/** + * e1000_reset_hw_ich8lan - Reset the hardware + * @hw: pointer to the HW structure + * + * Does a full reset of the hardware which includes a reset of the PHY and + * MAC. + **/ +STATIC s32 +e1000_reset_hw_ich8lan(struct e1000_hw *hw) +{ + u32 ctrl, icr, kab; + s32 ret_val; + + DEBUGFUNC("e1000_reset_hw_ich8lan"); + + /* Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) { + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + } + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + /* Disable the Transmit and Receive units. Then delay to allow + * any pending transactions to complete before we hit the MAC + * with the global reset. + */ + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + /* Workaround for ICH8 bit corruption issue in FIFO memory */ + if (hw->mac.type == e1000_ich8lan) { + /* Set Tx and Rx buffer allocation to 8k apiece. */ + E1000_WRITE_REG(hw, E1000_PBA, E1000_PBA_8K); + /* Set Packet Buffer Size to 16k. */ + E1000_WRITE_REG(hw, E1000_PBS, E1000_PBS_16K); + } + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + if (!e1000_check_reset_block(hw) && !hw->phy.reset_disable) { + /* PHY HW reset requires MAC CORE reset at the same + * time to make sure the interface between MAC and the + * external PHY is reset. + */ + ctrl |= E1000_CTRL_PHY_RST; + } + ret_val = e1000_acquire_swflag_ich8lan(hw); + DEBUGOUT("Issuing a global reset to ich8lan"); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl | E1000_CTRL_RST)); + msec_delay(20); + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) { + /* + * When auto config read does not complete, do not + * return with an error. This can happen in situations + * where there is no eeprom and prevents getting link. + */ + DEBUGOUT("Auto Read Done did not complete\n"); + } + + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + icr = E1000_READ_REG(hw, E1000_ICR); + + kab = E1000_READ_REG(hw, E1000_KABGTXD); + kab |= E1000_KABGTXD_BGSQLBIAS; + E1000_WRITE_REG(hw, E1000_KABGTXD, kab); + + return ret_val; +} + +/** + * e1000_init_hw_ich8lan - Initialize the hardware + * @hw: pointer to the HW structure + * + * Prepares the hardware for transmit and receive by doing the following: + * - initialize hardware bits + * - initialize LED identification + * - setup receive address registers + * - setup flow control + * - setup transmit discriptors + * - clear statistics + **/ +STATIC s32 +e1000_init_hw_ich8lan(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 ctrl_ext, txdctl, snoop; + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_init_hw_ich8lan"); + + e1000_initialize_hw_bits_ich8lan(hw); + + /* Initialize identification LED */ + ret_val = e1000_id_led_init_generic(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + goto out; + } + + /* Setup the receive address. */ + e1000_init_rx_addrs_generic(hw, mac->rar_entry_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + + /* Setup link and flow control */ + ret_val = e1000_setup_link(hw); + + /* Set the transmit descriptor write-back policy for both queues */ + txdctl = E1000_READ_REG(hw, E1000_TXDCTL); + txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB; + txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) | + E1000_TXDCTL_MAX_TX_DESC_PREFETCH; + E1000_WRITE_REG(hw, E1000_TXDCTL, txdctl); + txdctl = E1000_READ_REG(hw, E1000_TXDCTL1); + txdctl = (txdctl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB; + txdctl = (txdctl & ~E1000_TXDCTL_PTHRESH) | + E1000_TXDCTL_MAX_TX_DESC_PREFETCH; + E1000_WRITE_REG(hw, E1000_TXDCTL1, txdctl); + + /* ICH8 has opposite polarity of no_snoop bits. + * By default, we should use snoop behavior. */ + if (mac->type == e1000_ich8lan) + snoop = PCIE_ICH8_SNOOP_ALL; + else + snoop = (u32)~(PCIE_NO_SNOOP_ALL); + e1000_set_pcie_no_snoop_generic(hw, snoop); + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_ich8lan(hw); + +out: + return ret_val; +} +/** + * e1000_initialize_hw_bits_ich8lan - Initialize required hardware bits + * @hw: pointer to the HW structure + * + * Sets/Clears required hardware bits necessary for correctly setting up the + * hardware for transmit and receive. + **/ +static void +e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_initialize_hw_bits_ich8lan"); + + if (hw->mac.disable_hw_init_bits) + goto out; + + /* Extended Device Control */ + reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg); + + /* Transmit Descriptor Control 0 */ + reg = E1000_READ_REG(hw, E1000_TXDCTL); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_TXDCTL, reg); + + /* Transmit Descriptor Control 1 */ + reg = E1000_READ_REG(hw, E1000_TXDCTL1); + reg |= (1 << 22); + E1000_WRITE_REG(hw, E1000_TXDCTL1, reg); + + /* Transmit Arbitration Control 0 */ + reg = E1000_READ_REG(hw, E1000_TARC0); + if (hw->mac.type == e1000_ich8lan) + reg |= (1 << 28) | (1 << 29); + reg |= (1 << 23) | (1 << 24) | (1 << 26) | (1 << 27); + E1000_WRITE_REG(hw, E1000_TARC0, reg); + + /* Transmit Arbitration Control 1 */ + reg = E1000_READ_REG(hw, E1000_TARC1); + if (E1000_READ_REG(hw, E1000_TCTL) & E1000_TCTL_MULR) + reg &= ~(1 << 28); + else + reg |= (1 << 28); + reg |= (1 << 24) | (1 << 26) | (1 << 30); + E1000_WRITE_REG(hw, E1000_TARC1, reg); + + /* Device Status */ + if (hw->mac.type == e1000_ich8lan) { + reg = E1000_READ_REG(hw, E1000_STATUS); + reg &= ~(1 << 31); + E1000_WRITE_REG(hw, E1000_STATUS, reg); + } + +out: + return; +} + +/** + * e1000_setup_link_ich8lan - Setup flow control and link settings + * @hw: pointer to the HW structure + * + * Determines which flow control settings to use, then configures flow + * control. Calls the appropriate media-specific link configuration + * function. Assuming the adapter has a valid link partner, a valid link + * should be established. Assumes the hardware has previously been reset + * and the transmitter and receiver are not enabled. + **/ +STATIC s32 +e1000_setup_link_ich8lan(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_setup_link_ich8lan"); + + if (e1000_check_reset_block(hw)) + goto out; + + /* ICH parts do not have a word in the NVM to determine + * the default flow control setting, so we explicitly + * set it to full. + */ + if (mac->fc == e1000_fc_default) + mac->fc = e1000_fc_full; + + mac->original_fc = mac->fc; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", mac->fc); + + /* Continue to configure the copper link. */ + ret_val = func->setup_physical_interface(hw); + if (ret_val) + goto out; + + E1000_WRITE_REG(hw, E1000_FCTTV, mac->fc_pause_time); + + ret_val = e1000_set_fc_watermarks_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_setup_copper_link_ich8lan - Configure MAC/PHY interface + * @hw: pointer to the HW structure + * + * Configures the kumeran interface to the PHY to wait the appropriate time + * when polling the PHY, then call the generic setup_copper_link to finish + * configuring the copper link. + **/ +STATIC s32 +e1000_setup_copper_link_ich8lan(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + u16 reg_data; + + DEBUGFUNC("e1000_setup_copper_link_ich8lan"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Set the mac to wait the maximum time between each iteration + * and increase the max iterations when polling the phy; + * this fixes erroneous timeouts at 10Mbps. */ + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF); + if (ret_val) + goto out; + ret_val = e1000_read_kmrn_reg(hw, GG82563_REG(0x34, 9), ®_data); + if (ret_val) + goto out; + reg_data |= 0x3F; + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data); + if (ret_val) + goto out; + + if (hw->phy.type == e1000_phy_igp_3) { + ret_val = e1000_copper_link_setup_igp(hw); + if (ret_val) + goto out; + } + + ret_val = e1000_setup_copper_link_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_get_link_up_info_ich8lan - Get current link speed and duplex + * @hw: pointer to the HW structure + * @speed: pointer to store current link speed + * @duplex: pointer to store the current link duplex + * + * Calls the generic get_speed_and_duplex to retreive the current link + * information and then calls the Kumeran lock loss workaround for links at + * gigabit speeds. + **/ +STATIC s32 +e1000_get_link_up_info_ich8lan(struct e1000_hw *hw, u16 *speed, u16 *duplex) +{ + s32 ret_val; + + DEBUGFUNC("e1000_get_link_up_info_ich8lan"); + + ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed, duplex); + if (ret_val) + goto out; + + if ((hw->mac.type == e1000_ich8lan) && + (hw->phy.type == e1000_phy_igp_3) && + (*speed == SPEED_1000)) { + ret_val = e1000_kmrn_lock_loss_workaround_ich8lan(hw); + } + +out: + return ret_val; +} + +/** + * e1000_kmrn_lock_loss_workaround_ich8lan - Kumeran workaround + * @hw: pointer to the HW structure + * + * Work-around for 82566 Kumeran PCS lock loss: + * On link status change (i.e. PCI reset, speed change) and link is up and + * speed is gigabit- + * 0) if workaround is optionally disabled do nothing + * 1) wait 1ms for Kumeran link to come up + * 2) check Kumeran Diagnostic register PCS lock loss bit + * 3) if not set the link is locked (all is good), otherwise... + * 4) reset the PHY + * 5) repeat up to 10 times + * Note: this is only called for IGP3 copper when speed is 1gb. + **/ +static s32 +e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw) +{ + struct e1000_dev_spec_ich8lan *dev_spec; + u32 phy_ctrl; + s32 ret_val = E1000_SUCCESS; + u16 i, data; + boolean_t link; + + DEBUGFUNC("e1000_kmrn_lock_loss_workaround_ich8lan"); + + dev_spec = (struct e1000_dev_spec_ich8lan *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + if (dev_spec->kmrn_lock_loss_workaround_enabled == FALSE) + goto out; + + /* Make sure link is up before proceeding. If not just return. + * Attempting this while link is negotiating fouled up link + * stability */ + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (!link) { + ret_val = E1000_SUCCESS; + goto out; + } + + for (i = 0; i < 10; i++) { + /* read once to clear */ + ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &data); + if (ret_val) + goto out; + /* and again to get new status */ + ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &data); + if (ret_val) + goto out; + + /* check for PCS lock */ + if (!(data & IGP3_KMRN_DIAG_PCS_LOCK_LOSS)) { + ret_val = E1000_SUCCESS; + goto out; + } + + /* Issue PHY reset */ + e1000_phy_hw_reset(hw); + msec_delay_irq(5); + } + /* Disable GigE link negotiation */ + phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL); + phy_ctrl |= (E1000_PHY_CTRL_GBE_DISABLE | + E1000_PHY_CTRL_NOND0A_GBE_DISABLE); + E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl); + + /* Call gig speed drop workaround on Giga disable before accessing + * any PHY registers */ + e1000_gig_downshift_workaround_ich8lan(hw); + + /* unable to acquire PCS lock */ + ret_val = -E1000_ERR_PHY; + +out: + return ret_val; +} + +/** + * e1000_set_kmrn_lock_loss_workaound_ich8lan - Set Kumeran workaround state + * @hw: pointer to the HW structure + * @state: boolean value used to set the current Kumaran workaround state + * + * If ICH8, set the current Kumeran workaround state (enabled - TRUE + * /disabled - FALSE). + **/ +void +e1000_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw, + boolean_t state) +{ + struct e1000_dev_spec_ich8lan *dev_spec; + + DEBUGFUNC("e1000_set_kmrn_lock_loss_workaround_ich8lan"); + + if (hw->mac.type != e1000_ich8lan) { + DEBUGOUT("Workaround applies to ICH8 only.\n"); + goto out; + } + + dev_spec = (struct e1000_dev_spec_ich8lan *)hw->dev_spec; + + if (dev_spec == NULL) { + DEBUGOUT("dev_spec pointer is set to NULL.\n"); + goto out; + } + + dev_spec->kmrn_lock_loss_workaround_enabled = state; + +out: + return; +} + +/** + * e1000_ipg3_phy_powerdown_workaround_ich8lan - Power down workaround on D3 + * @hw: pointer to the HW structure + * + * Workaround for 82566 power-down on D3 entry: + * 1) disable gigabit link + * 2) write VR power-down enable + * 3) read it back + * Continue if successful, else issue LCD reset and repeat + **/ +void +e1000_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw) +{ + u32 reg; + u16 data; + u8 retry = 0; + + DEBUGFUNC("e1000_igp3_phy_powerdown_workaround_ich8lan"); + + if (hw->phy.type != e1000_phy_igp_3) + goto out; + + /* Try the workaround twice (if needed) */ + do { + /* Disable link */ + reg = E1000_READ_REG(hw, E1000_PHY_CTRL); + reg |= (E1000_PHY_CTRL_GBE_DISABLE | + E1000_PHY_CTRL_NOND0A_GBE_DISABLE); + E1000_WRITE_REG(hw, E1000_PHY_CTRL, reg); + + /* Call gig speed drop workaround on Giga disable before + * accessing any PHY registers */ + if (hw->mac.type == e1000_ich8lan) + e1000_gig_downshift_workaround_ich8lan(hw); + + /* Write VR power-down enable */ + e1000_read_phy_reg(hw, IGP3_VR_CTRL, &data); + data &= ~IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK; + e1000_write_phy_reg(hw, + IGP3_VR_CTRL, + data | IGP3_VR_CTRL_MODE_SHUTDOWN); + + /* Read it back and test */ + e1000_read_phy_reg(hw, IGP3_VR_CTRL, &data); + data &= IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK; + if ((data == IGP3_VR_CTRL_MODE_SHUTDOWN) || retry) + break; + + /* Issue PHY reset and repeat at most one more time */ + reg = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, reg | E1000_CTRL_PHY_RST); + retry++; + } while (retry); + +out: + return; +} + +/** + * e1000_gig_downshift_workaround_ich8lan - WoL from S5 stops working + * @hw: pointer to the HW structure + * + * Steps to take when dropping from 1Gb/s (eg. link cable removal (LSC), + * LPLU, Giga disable, MDIC PHY reset): + * 1) Set Kumeran Near-end loopback + * 2) Clear Kumeran Near-end loopback + * Should only be called for ICH8[m] devices with IGP_3 Phy. + **/ +void +e1000_gig_downshift_workaround_ich8lan(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 reg_data; + + DEBUGFUNC("e1000_gig_downshift_workaround_ich8lan"); + + if ((hw->mac.type != e1000_ich8lan) || + (hw->phy.type != e1000_phy_igp_3)) + goto out; + + ret_val = e1000_read_kmrn_reg(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET, + ®_data); + if (ret_val) + goto out; + reg_data |= E1000_KMRNCTRLSTA_DIAG_NELPBK; + ret_val = e1000_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET, + reg_data); + if (ret_val) + goto out; + reg_data &= ~E1000_KMRNCTRLSTA_DIAG_NELPBK; + ret_val = e1000_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_DIAG_OFFSET, + reg_data); +out: + return; +} + +/** + * e1000_cleanup_led_ich8lan - Restore the default LED operation + * @hw: pointer to the HW structure + * + * Return the LED back to the default configuration. + **/ +STATIC s32 +e1000_cleanup_led_ich8lan(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_cleanup_led_ich8lan"); + + if (hw->phy.type == e1000_phy_ife) + ret_val = e1000_write_phy_reg(hw, + IFE_PHY_SPECIAL_CONTROL_LED, + 0); + else + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default); + + return ret_val; +} + +/** + * e1000_led_on_ich8lan - Turn LED's on + * @hw: pointer to the HW structure + * + * Turn on the LED's. + **/ +STATIC s32 +e1000_led_on_ich8lan(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_led_on_ich8lan"); + + if (hw->phy.type == e1000_phy_ife) + ret_val = e1000_write_phy_reg(hw, + IFE_PHY_SPECIAL_CONTROL_LED, + (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON)); + else + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode2); + + return ret_val; +} + +/** + * e1000_led_off_ich8lan - Turn LED's off + * @hw: pointer to the HW structure + * + * Turn off the LED's. + **/ +STATIC s32 +e1000_led_off_ich8lan(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_led_off_ich8lan"); + + if (hw->phy.type == e1000_phy_ife) + ret_val = e1000_write_phy_reg(hw, + IFE_PHY_SPECIAL_CONTROL_LED, + (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF)); + else + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + + return ret_val; +} + +/** + * e1000_get_cfg_done_ich8lan - Read config done bit + * @hw: pointer to the HW structure + * + * Read the management control register for the config done bit for + * completion status. NOTE: silicon which is EEPROM-less will fail trying + * to read the config done bit, so an error is *ONLY* logged and returns + * E1000_SUCCESS. If we were to return with error, EEPROM-less silicon + * would not be able to be reset or change link. + **/ +STATIC s32 +e1000_get_cfg_done_ich8lan(struct e1000_hw *hw) +{ + e1000_get_cfg_done_generic(hw); + + /* If EEPROM is not marked present, init the IGP 3 PHY manually */ + if (((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) && + (hw->phy.type == e1000_phy_igp_3)) { + e1000_phy_init_script_igp3(hw); + } + + return E1000_SUCCESS; +} + +/** + * e1000_clear_hw_cntrs_ich8lan - Clear statistical counters + * @hw: pointer to the HW structure + * + * Clears hardware counters specific to the silicon family and calls + * clear_hw_cntrs_generic to clear all general purpose counters. + **/ +STATIC void +e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_ich8lan"); + + e1000_clear_hw_cntrs_base_generic(hw); + + temp = E1000_READ_REG(hw, E1000_ALGNERRC); + temp = E1000_READ_REG(hw, E1000_RXERRC); + temp = E1000_READ_REG(hw, E1000_TNCRS); + temp = E1000_READ_REG(hw, E1000_CEXTERR); + temp = E1000_READ_REG(hw, E1000_TSCTC); + temp = E1000_READ_REG(hw, E1000_TSCTFC); + + temp = E1000_READ_REG(hw, E1000_MGTPRC); + temp = E1000_READ_REG(hw, E1000_MGTPDC); + temp = E1000_READ_REG(hw, E1000_MGTPTC); + + temp = E1000_READ_REG(hw, E1000_IAC); + temp = E1000_READ_REG(hw, E1000_ICRXOC); +} + diff --git a/bsd_eth_drivers/if_em/e1000_ich8lan.h b/bsd_eth_drivers/if_em/e1000_ich8lan.h new file mode 100644 index 0000000..b5ba257 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_ich8lan.h @@ -0,0 +1,115 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_ich8lan.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_ICH8LAN_H_ +#define _E1000_ICH8LAN_H_ + +#define ICH_FLASH_GFPREG 0x0000 +#define ICH_FLASH_HSFSTS 0x0004 +#define ICH_FLASH_HSFCTL 0x0006 +#define ICH_FLASH_FADDR 0x0008 +#define ICH_FLASH_FDATA0 0x0010 + +#define ICH_FLASH_READ_COMMAND_TIMEOUT 500 +#define ICH_FLASH_WRITE_COMMAND_TIMEOUT 500 +#define ICH_FLASH_ERASE_COMMAND_TIMEOUT 3000000 +#define ICH_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF +#define ICH_FLASH_CYCLE_REPEAT_COUNT 10 + +#define ICH_CYCLE_READ 0 +#define ICH_CYCLE_WRITE 2 +#define ICH_CYCLE_ERASE 3 + +#define FLASH_GFPREG_BASE_MASK 0x1FFF +#define FLASH_SECTOR_ADDR_SHIFT 12 + +#define E1000_SHADOW_RAM_WORDS 2048 + +#define ICH_FLASH_SEG_SIZE_256 256 +#define ICH_FLASH_SEG_SIZE_4K 4096 +#define ICH_FLASH_SEG_SIZE_8K 8192 +#define ICH_FLASH_SEG_SIZE_64K 65536 +#define ICH_FLASH_SECTOR_SIZE 4096 + +#define ICH_FLASH_REG_MAPSIZE 0x00A0 + +#define E1000_ICH_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI Reset */ +#define E1000_ICH_FWSM_DISSW 0x10000000 /* FW Disables SW Writes */ +#define E1000_ICH_FWSM_FW_VALID 0x00008000 /* FW established a valid + * mode. + */ + +#define E1000_ICH_MNG_IAMT_MODE 0x2 + +#define ID_LED_DEFAULT_ICH8LAN ((ID_LED_DEF1_DEF2 << 12) | \ + (ID_LED_DEF1_OFF2 << 8) | \ + (ID_LED_DEF1_ON2 << 4) | \ + (ID_LED_DEF1_DEF2)) + +#define E1000_ICH_NVM_SIG_WORD 0x13 +#define E1000_ICH_NVM_SIG_MASK 0xC000 + +#define E1000_ICH8_LAN_INIT_TIMEOUT 1500 + +#define E1000_FEXTNVM_SW_CONFIG 1 +#define E1000_FEXTNVM_SW_CONFIG_ICH8M (1 << 27) /* Bit redefined for ICH8M :/ */ + +#define PCIE_ICH8_SNOOP_ALL PCIE_NO_SNOOP_ALL + +#define E1000_ICH_RAR_ENTRIES 7 + +#define PHY_PAGE_SHIFT 5 +#define PHY_REG(page, reg) (((page) << PHY_PAGE_SHIFT) | \ + ((reg) & MAX_PHY_REG_ADDRESS)) +#define IGP3_KMRN_DIAG PHY_REG(770, 19) /* KMRN Diagnostic */ +#define IGP3_VR_CTRL PHY_REG(776, 18) /* Voltage Regulator Control */ +#define IGP3_CAPABILITY PHY_REG(776, 19) /* Capability */ +#define IGP3_PM_CTRL PHY_REG(769, 20) /* Power Management Control */ + +#define IGP3_KMRN_DIAG_PCS_LOCK_LOSS 0x0002 +#define IGP3_VR_CTRL_DEV_POWERDOWN_MODE_MASK 0x0300 +#define IGP3_VR_CTRL_MODE_SHUTDOWN 0x0200 +#define IGP3_PM_CTRL_FORCE_PWR_DOWN 0x0020 + +/* Additional interrupts need to be handled for ICH family: + DSW = The FW changed the status of the DISSW bit in FWSM + PHYINT = The LAN connected device generates an interrupt + EPRST = Manageability reset event */ +#define IMS_ICH_ENABLE_MASK (\ + E1000_IMS_DSW | \ + E1000_IMS_PHYINT | \ + E1000_IMS_EPRST) + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_mac.c b/bsd_eth_drivers/if_em/e1000_mac.c new file mode 100644 index 0000000..f4795a4 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_mac.c @@ -0,0 +1,2003 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_mac.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#include "e1000_api.h" +#include "e1000_mac.h" + +/** + * e1000_remove_device_generic - Free device specific structure + * @hw: pointer to the HW structure + * + * If a device specific structure was allocated, this function will + * free it. + **/ +void +e1000_remove_device_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_remove_device_generic"); + + /* Freeing the dev_spec member of e1000_hw structure */ + e1000_free_dev_spec_struct(hw); +} + +/** + * e1000_get_bus_info_pci_generic - Get PCI(x) bus information + * @hw: pointer to the HW structure + * + * Determines and stores the system bus information for a particular + * network interface. The following bus information is determined and stored: + * bus speed, bus width, type (PCI/PCIx), and PCI(-x) function. + **/ +s32 +e1000_get_bus_info_pci_generic(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + u32 status = E1000_READ_REG(hw, E1000_STATUS); + s32 ret_val = E1000_SUCCESS; + u16 pci_header_type; + + DEBUGFUNC("e1000_get_bus_info_pci_generic"); + + /* PCI or PCI-X? */ + bus->type = (status & E1000_STATUS_PCIX_MODE) + ? e1000_bus_type_pcix + : e1000_bus_type_pci; + + /* Bus speed */ + if (bus->type == e1000_bus_type_pci) { + bus->speed = (status & E1000_STATUS_PCI66) + ? e1000_bus_speed_66 + : e1000_bus_speed_33; + } else { + switch (status & E1000_STATUS_PCIX_SPEED) { + case E1000_STATUS_PCIX_SPEED_66: + bus->speed = e1000_bus_speed_66; + break; + case E1000_STATUS_PCIX_SPEED_100: + bus->speed = e1000_bus_speed_100; + break; + case E1000_STATUS_PCIX_SPEED_133: + bus->speed = e1000_bus_speed_133; + break; + default: + bus->speed = e1000_bus_speed_reserved; + break; + } + } + + /* Bus width */ + bus->width = (status & E1000_STATUS_BUS64) + ? e1000_bus_width_64 + : e1000_bus_width_32; + + /* Which PCI(-X) function? */ + e1000_read_pci_cfg(hw, PCI_HEADER_TYPE_REGISTER, &pci_header_type); + if (pci_header_type & PCI_HEADER_TYPE_MULTIFUNC) + bus->func = (status & E1000_STATUS_FUNC_MASK) + >> E1000_STATUS_FUNC_SHIFT; + else + bus->func = 0; + + return ret_val; +} + +/** + * e1000_get_bus_info_pcie_generic - Get PCIe bus information + * @hw: pointer to the HW structure + * + * Determines and stores the system bus information for a particular + * network interface. The following bus information is determined and stored: + * bus speed, bus width, type (PCIe), and PCIe function. + **/ +s32 +e1000_get_bus_info_pcie_generic(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + s32 ret_val; + u32 status; + u16 pcie_link_status, pci_header_type; + + DEBUGFUNC("e1000_get_bus_info_pcie_generic"); + + bus->type = e1000_bus_type_pci_express; + bus->speed = e1000_bus_speed_2500; + + ret_val = e1000_read_pcie_cap_reg(hw, + PCIE_LINK_STATUS, + &pcie_link_status); + if (ret_val) + bus->width = e1000_bus_width_unknown; + else + bus->width = (e1000_bus_width)((pcie_link_status & + PCIE_LINK_WIDTH_MASK) >> + PCIE_LINK_WIDTH_SHIFT); + + e1000_read_pci_cfg(hw, PCI_HEADER_TYPE_REGISTER, &pci_header_type); + if (pci_header_type & PCI_HEADER_TYPE_MULTIFUNC) { + status = E1000_READ_REG(hw, E1000_STATUS); + bus->func = (status & E1000_STATUS_FUNC_MASK) + >> E1000_STATUS_FUNC_SHIFT; + } else + bus->func = 0; + + return E1000_SUCCESS; +} + +/** + * e1000_clear_vfta_generic - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +void +e1000_clear_vfta_generic(struct e1000_hw *hw) +{ + u32 offset; + + DEBUGFUNC("e1000_clear_vfta_generic"); + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, 0); + E1000_WRITE_FLUSH(hw); + } +} + +/** + * e1000_write_vfta_generic - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: register offset in VLAN filter table + * @value: register value written to VLAN filter table + * + * Writes value at the given offset in the register array which stores + * the VLAN filter table. + **/ +void +e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value) +{ + DEBUGFUNC("e1000_write_vfta_generic"); + + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_init_rx_addrs_generic - Initialize receive address's + * @hw: pointer to the HW structure + * @rar_count: receive address registers + * + * Setups the receive address registers by setting the base receive address + * register to the devices MAC address and clearing all the other receive + * address registers to 0. + **/ +void +e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count) +{ + u32 i; + + DEBUGFUNC("e1000_init_rx_addrs_generic"); + + /* Setup the receive address */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + + e1000_rar_set_generic(hw, hw->mac.addr, 0); + + /* Zero out the other (rar_entry_count - 1) receive addresses */ + DEBUGOUT1("Clearing RAR[1-%u]\n", rar_count-1); + for (i = 1; i < rar_count; i++) { + E1000_WRITE_REG_ARRAY(hw, E1000_RA, (i << 1), 0); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, E1000_RA, ((i << 1) + 1), 0); + E1000_WRITE_FLUSH(hw); + } +} + +/** + * e1000_rar_set_generic - Set receive address register + * @hw: pointer to the HW structure + * @addr: pointer to the receive address + * @index: receive address array register + * + * Sets the receive address array register at index to the address passed + * in by addr. + **/ +void +e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) +{ + u32 rar_low, rar_high; + + DEBUGFUNC("e1000_rar_set_generic"); + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((u32) addr[0] | + ((u32) addr[1] << 8) | + ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); + + rar_high = ((u32) addr[4] | ((u32) addr[5] << 8)); + + if (!hw->mac.disable_av) + rar_high |= E1000_RAH_AV; + + E1000_WRITE_REG_ARRAY(hw, E1000_RA, (index << 1), rar_low); + E1000_WRITE_REG_ARRAY(hw, E1000_RA, ((index << 1) + 1), rar_high); +} + +/** + * e1000_mta_set_generic - Set multicast filter table address + * @hw: pointer to the HW structure + * @hash_value: determines the MTA register and bit to set + * + * The multicast table address is a register array of 32-bit registers. + * The hash_value is used to determine what register the bit is in, the + * current value is read, the new bit is OR'd in and the new value is + * written back into the register. + **/ +void +e1000_mta_set_generic(struct e1000_hw *hw, u32 hash_value) +{ + u32 hash_bit, hash_reg, mta; + + DEBUGFUNC("e1000_mta_set_generic"); + /* The MTA is a register array of 32-bit registers. It is + * treated like an array of (32*mta_reg_count) bits. We want to + * set bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The (hw->mac.mta_reg_count - 1) serves as a + * mask to bits 31:5 of the hash value which gives us the + * register we're modifying. The hash bit within that register + * is determined by the lower 5 bits of the hash value. + */ + hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); + hash_bit = hash_value & 0x1F; + + mta = E1000_READ_REG_ARRAY(hw, E1000_MTA, hash_reg); + + mta |= (1 << hash_bit); + + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, hash_reg, mta); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_mc_addr_list_update_generic - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * @rar_used_count: the first RAR register free to program + * @rar_count: total number of supported Receive Address Registers + * + * Updates the Receive Address Registers and Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + * The parameter rar_count will usually be hw->mac.rar_entry_count + * unless there are workarounds that change this. + **/ +void +e1000_mc_addr_list_update_generic(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count, + u32 rar_used_count, u32 rar_count) +{ + u32 hash_value; + u32 i; + + DEBUGFUNC("e1000_mc_addr_list_update_generic"); + + /* Load the first set of multicast addresses into the exact + * filters (RAR). If there are not enough to fill the RAR + * array, clear the filters. + */ + for (i = rar_used_count; i < rar_count; i++) { + if (mc_addr_count) { + e1000_rar_set(hw, mc_addr_list, i); + mc_addr_count--; + mc_addr_list += ETH_ADDR_LEN; + } else { + E1000_WRITE_REG_ARRAY(hw, E1000_RA, i << 1, 0); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, E1000_RA, (i << 1) + 1, 0); + E1000_WRITE_FLUSH(hw); + } + } + + /* Clear the old settings from the MTA */ + DEBUGOUT("Clearing MTA\n"); + for (i = 0; i < hw->mac.mta_reg_count; i++) { + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + E1000_WRITE_FLUSH(hw); + } + + /* Load any remaining multicast addresses into the hash table. */ + for (; mc_addr_count > 0; mc_addr_count--) { + hash_value = e1000_hash_mc_addr(hw, mc_addr_list); + DEBUGOUT1("Hash value = 0x%03X\n", hash_value); + e1000_mta_set(hw, hash_value); + mc_addr_list += ETH_ADDR_LEN; + } +} + +/** + * e1000_hash_mc_addr_generic - Generate a multicast hash value + * @hw: pointer to the HW structure + * @mc_addr: pointer to a multicast address + * + * Generates a multicast address hash value which is used to determine + * the multicast filter table array address and new table value. See + * e1000_mta_set_generic() + **/ +u32 +e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr) +{ + u32 hash_value, hash_mask; + u8 bit_shift = 0; + + DEBUGFUNC("e1000_hash_mc_addr_generic"); + + /* Register count multiplied by bits per register */ + hash_mask = (hw->mac.mta_reg_count * 32) - 1; + + /* For a mc_filter_type of 0, bit_shift is the number of left-shifts + * where 0xFF would still fall within the hash mask. */ + while (hash_mask >> bit_shift != 0xFF) + bit_shift++; + + /* The portion of the address that is used for the hash table + * is determined by the mc_filter_type setting. + * The algorithm is such that there is a total of 8 bits of shifting. + * The bit_shift for a mc_filter_type of 0 represents the number of + * left-shifts where the MSB of mc_addr[5] would still fall within + * the hash_mask. Case 0 does this exactly. Since there are a total + * of 8 bits of shifting, then mc_addr[4] will shift right the + * remaining number of bits. Thus 8 - bit_shift. The rest of the + * cases are a variation of this algorithm...essentially raising the + * number of bits to shift mc_addr[5] left, while still keeping the + * 8-bit shifting total. + */ + /* For example, given the following Destination MAC Address and an + * mta register count of 128 (thus a 4096-bit vector and 0xFFF mask), + * we can see that the bit_shift for case 0 is 4. These are the hash + * values resulting from each mc_filter_type... + * [0] [1] [2] [3] [4] [5] + * 01 AA 00 12 34 56 + * LSB MSB + * + * case 0: hash_value = ((0x34 >> 4) | (0x56 << 4)) & 0xFFF = 0x563 + * case 1: hash_value = ((0x34 >> 3) | (0x56 << 5)) & 0xFFF = 0xAC6 + * case 2: hash_value = ((0x34 >> 2) | (0x56 << 6)) & 0xFFF = 0x163 + * case 3: hash_value = ((0x34 >> 0) | (0x56 << 8)) & 0xFFF = 0x634 + */ + switch (hw->mac.mc_filter_type) { + default: + case 0: + break; + case 1: + bit_shift += 1; + break; + case 2: + bit_shift += 2; + break; + case 3: + bit_shift += 4; + break; + } + + hash_value = hash_mask & (((mc_addr[4] >> (8 - bit_shift)) | + (((u16) mc_addr[5]) << bit_shift))); + + return hash_value; +} + +/** + * e1000_pcix_mmrbc_workaround_generic - Fix incorrect MMRBC value + * @hw: pointer to the HW structure + * + * In certain situations, a system BIOS may report that the PCIx maximum + * memory read byte count (MMRBC) value is higher than than the actual + * value. We check the PCIx command regsiter with the current PCIx status + * regsiter. + **/ +void +e1000_pcix_mmrbc_workaround_generic(struct e1000_hw *hw) +{ + u16 cmd_mmrbc; + u16 pcix_cmd; + u16 pcix_stat_hi_word; + u16 stat_mmrbc; + + DEBUGFUNC("e1000_pcix_mmrbc_workaround_generic"); + + /* Workaround for PCI-X issue when BIOS sets MMRBC incorrectly */ + if (hw->bus.type != e1000_bus_type_pcix) + return; + + e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd); + e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, &pcix_stat_hi_word); + cmd_mmrbc = (pcix_cmd & PCIX_COMMAND_MMRBC_MASK) >> + PCIX_COMMAND_MMRBC_SHIFT; + stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >> + PCIX_STATUS_HI_MMRBC_SHIFT; + if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K) + stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K; + if (cmd_mmrbc > stat_mmrbc) { + pcix_cmd &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd); + } +} + +/** + * e1000_clear_hw_cntrs_base_generic - Clear base hardware counters + * @hw: pointer to the HW structure + * + * Clears the base hardware counters by reading the counter registers. + **/ +void +e1000_clear_hw_cntrs_base_generic(struct e1000_hw *hw) +{ + volatile u32 temp; + + DEBUGFUNC("e1000_clear_hw_cntrs_base_generic"); + + temp = E1000_READ_REG(hw, E1000_CRCERRS); + temp = E1000_READ_REG(hw, E1000_SYMERRS); + temp = E1000_READ_REG(hw, E1000_MPC); + temp = E1000_READ_REG(hw, E1000_SCC); + temp = E1000_READ_REG(hw, E1000_ECOL); + temp = E1000_READ_REG(hw, E1000_MCC); + temp = E1000_READ_REG(hw, E1000_LATECOL); + temp = E1000_READ_REG(hw, E1000_COLC); + temp = E1000_READ_REG(hw, E1000_DC); + temp = E1000_READ_REG(hw, E1000_SEC); + temp = E1000_READ_REG(hw, E1000_RLEC); + temp = E1000_READ_REG(hw, E1000_XONRXC); + temp = E1000_READ_REG(hw, E1000_XONTXC); + temp = E1000_READ_REG(hw, E1000_XOFFRXC); + temp = E1000_READ_REG(hw, E1000_XOFFTXC); + temp = E1000_READ_REG(hw, E1000_FCRUC); + temp = E1000_READ_REG(hw, E1000_GPRC); + temp = E1000_READ_REG(hw, E1000_BPRC); + temp = E1000_READ_REG(hw, E1000_MPRC); + temp = E1000_READ_REG(hw, E1000_GPTC); + temp = E1000_READ_REG(hw, E1000_GORCL); + temp = E1000_READ_REG(hw, E1000_GORCH); + temp = E1000_READ_REG(hw, E1000_GOTCL); + temp = E1000_READ_REG(hw, E1000_GOTCH); + temp = E1000_READ_REG(hw, E1000_RNBC); + temp = E1000_READ_REG(hw, E1000_RUC); + temp = E1000_READ_REG(hw, E1000_RFC); + temp = E1000_READ_REG(hw, E1000_ROC); + temp = E1000_READ_REG(hw, E1000_RJC); + temp = E1000_READ_REG(hw, E1000_TORL); + temp = E1000_READ_REG(hw, E1000_TORH); + temp = E1000_READ_REG(hw, E1000_TOTL); + temp = E1000_READ_REG(hw, E1000_TOTH); + temp = E1000_READ_REG(hw, E1000_TPR); + temp = E1000_READ_REG(hw, E1000_TPT); + temp = E1000_READ_REG(hw, E1000_MPTC); + temp = E1000_READ_REG(hw, E1000_BPTC); +} + +/** + * e1000_check_for_copper_link_generic - Check for link (Copper) + * @hw: pointer to the HW structure + * + * Checks to see of the link status of the hardware has changed. If a + * change in link status has been detected, then we read the PHY registers + * to get the current speed/duplex if link exists. + **/ +s32 +e1000_check_for_copper_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + boolean_t link; + + DEBUGFUNC("e1000_check_for_copper_link"); + + /* We only want to go out to the PHY registers to see if Auto-Neg + * has completed and/or if our link status has changed. The + * get_link_status flag is set upon receiving a Link Status + * Change or Rx Sequence Error interrupt. + */ + if (!mac->get_link_status) { + ret_val = E1000_SUCCESS; + goto out; + } + + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + */ + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) + goto out; /* No link detected */ + + mac->get_link_status = FALSE; + + /* Check if there was DownShift, must be checked + * immediately after link-up */ + e1000_check_downshift_generic(hw); + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if (!mac->autoneg) { + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + /* Auto-Neg is enabled. Auto Speed Detection takes care + * of MAC speed/duplex configuration. So we only need to + * configure Collision Distance in the MAC. + */ + e1000_config_collision_dist_generic(hw); + + /* Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + } + +out: + return ret_val; +} + +/** + * e1000_check_for_fiber_link_generic - Check for link (Fiber) + * @hw: pointer to the HW structure + * + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + **/ +s32 +e1000_check_for_fiber_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 rxcw; + u32 ctrl; + u32 status; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_check_for_fiber_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + status = E1000_READ_REG(hw, E1000_STATUS); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), the cable is plugged in (we have signal), + * and our link partner is not trying to auto-negotiate with us (we + * are receiving idles or data), we need to force link up. We also + * need to give auto-negotiation time to complete, in case the cable + * was just plugged in. The autoneg_failed flag does this. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ + if ((ctrl & E1000_CTRL_SWDPIN1) && (!(status & E1000_STATUS_LU)) && + (!(rxcw & E1000_RXCW_C))) { + if (mac->autoneg_failed == 0) { + mac->autoneg_failed = 1; + goto out; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + goto out; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU)); + + mac->serdes_has_link = TRUE; + } + +out: + return ret_val; +} + +/** + * e1000_check_for_serdes_link_generic - Check for link (Serdes) + * @hw: pointer to the HW structure + * + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + **/ +s32 +e1000_check_for_serdes_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 rxcw; + u32 ctrl; + u32 status; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_check_for_serdes_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + status = E1000_READ_REG(hw, E1000_STATUS); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), and our link partner is not trying to + * auto-negotiate with us (we are receiving idles or data), + * we need to force link up. We also need to give auto-negotiation + * time to complete. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ + if ((!(status & E1000_STATUS_LU)) && (!(rxcw & E1000_RXCW_C))) { + if (mac->autoneg_failed == 0) { + mac->autoneg_failed = 1; + goto out; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + goto out; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU)); + + mac->serdes_has_link = TRUE; + } else if (!(E1000_TXCW_ANE & E1000_READ_REG(hw, E1000_TXCW))) { + /* If we force link for non-auto-negotiation switch, check + * link status based on MAC synchronization for internal + * serdes media type. + */ + /* SYNCH bit and IV bit are sticky. */ + usec_delay(10); + if (E1000_RXCW_SYNCH & E1000_READ_REG(hw, E1000_RXCW)) { + if (!(rxcw & E1000_RXCW_IV)) { + mac->serdes_has_link = TRUE; + DEBUGOUT("SERDES: Link is up.\n"); + } + } else { + mac->serdes_has_link = FALSE; + DEBUGOUT("SERDES: Link is down.\n"); + } + } + + if (E1000_TXCW_ANE & E1000_READ_REG(hw, E1000_TXCW)) { + status = E1000_READ_REG(hw, E1000_STATUS); + mac->serdes_has_link = (status & E1000_STATUS_LU) + ? TRUE + : FALSE; + } + +out: + return ret_val; +} + +/** + * e1000_setup_link_generic - Setup flow control and link settings + * @hw: pointer to the HW structure + * + * Determines which flow control settings to use, then configures flow + * control. Calls the appropriate media-specific link configuration + * function. Assuming the adapter has a valid link partner, a valid link + * should be established. Assumes the hardware has previously been reset + * and the transmitter and receiver are not enabled. + **/ +s32 +e1000_setup_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_functions *func = &hw->func; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_setup_link_generic"); + + /* In the case of the phy reset being blocked, we already have a link. + * We do not need to set it up again. + */ + if (e1000_check_reset_block(hw)) + goto out; + + ret_val = e1000_set_default_fc_generic(hw); + if (ret_val) + goto out; + + /* We want to save off the original Flow Control configuration just + * in case we get disconnected and then reconnected into a different + * hub or switch with different Flow Control capabilities. + */ + mac->original_fc = mac->fc; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", mac->fc); + + /* Call the necessary media_type subroutine to configure the link. */ + ret_val = func->setup_physical_interface(hw); + if (ret_val) + goto out; + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); + E1000_WRITE_REG(hw, E1000_FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(hw, E1000_FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, E1000_FCAL, FLOW_CONTROL_ADDRESS_LOW); + + E1000_WRITE_REG(hw, E1000_FCTTV, mac->fc_pause_time); + + ret_val = e1000_set_fc_watermarks_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_setup_fiber_serdes_link_generic - Setup link for fiber/serdes + * @hw: pointer to the HW structure + * + * Configures collision distance and flow control for fiber and serdes + * links. Upon successful setup, poll for link. + **/ +s32 +e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_setup_fiber_serdes_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Take the link out of reset */ + ctrl &= ~E1000_CTRL_LRST; + + e1000_config_collision_dist_generic(hw); + + ret_val = e1000_commit_fc_settings_generic(hw); + if (ret_val) + goto out; + + /* Since auto-negotiation is enabled, take the link out of reset (the + * link will be in reset, because we previously reset the chip). This + * will restart auto-negotiation. If auto-negotiation is successful + * then the link-up status bit will be set and the flow control enable + * bits (RFCE and TFCE) will be set according to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled\n"); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + msec_delay(1); + + /* For these adapters, the SW defineable pin 1 is set when the optics + * detect a signal. If we have a signal, then poll for a "Link-Up" + * indication. + */ + if (hw->media_type == e1000_media_type_internal_serdes || + (E1000_READ_REG(hw, E1000_CTRL) & E1000_CTRL_SWDPIN1)) { + ret_val = e1000_poll_fiber_serdes_link_generic(hw); + } else { + DEBUGOUT("No signal detected\n"); + } + +out: + return ret_val; +} + +/** + * e1000_config_collision_dist_generic - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +void +e1000_config_collision_dist_generic(struct e1000_hw *hw) +{ + u32 tctl; + + DEBUGFUNC("e1000_config_collision_dist_generic"); + + tctl = E1000_READ_REG(hw, E1000_TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, E1000_TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_poll_fiber_serdes_link_generic - Poll for link up + * @hw: pointer to the HW structure + * + * Polls for link up by reading the status register, if link fails to come + * up with auto-negotiation, then the link is forced if a signal is detected. + **/ +s32 +e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 i, status; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_poll_fiber_serdes_link_generic"); + + /* If we have a signal (the cable is plugged in, or assumed true for + * serdes media) then poll for a "Link-Up" indication in the Device + * Status Register. Time-out if a link isn't seen in 500 milliseconds + * seconds (Auto-negotiation should complete in less than 500 + * milliseconds even if the other end is doing it in SW). + */ + for (i = 0; i < FIBER_LINK_UP_LIMIT; i++) { + msec_delay(10); + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_LU) + break; + } + if (i == FIBER_LINK_UP_LIMIT) { + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + mac->autoneg_failed = 1; + /* AutoNeg failed to achieve a link, so we'll call + * mac->check_for_link. This routine will force the + * link up if we detect a signal. This will allow us to + * communicate with non-autonegotiating link partners. + */ + ret_val = e1000_check_for_link(hw); + if (ret_val) { + DEBUGOUT("Error while checking for link\n"); + goto out; + } + mac->autoneg_failed = 0; + } else { + mac->autoneg_failed = 0; + DEBUGOUT("Valid Link Found\n"); + } + +out: + return ret_val; +} + +/** + * e1000_commit_fc_settings_generic - Configure flow control + * @hw: pointer to the HW structure + * + * Write the flow control settings to the Transmit Config Word Register (TXCW) + * base on the flow control settings in e1000_mac_info. + **/ +s32 +e1000_commit_fc_settings_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 txcw; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_commit_fc_settings_generic"); + + /* Check for a software override of the flow control settings, and + * setup the device accordingly. If auto-negotiation is enabled, then + * software will have to set the "PAUSE" bits to the correct value in + * the Transmit Config Word Register (TXCW) and re-start auto- + * negotiation. However, if auto-negotiation is disabled, then + * software will have to manually configure the two flow control enable + * bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we + * do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + */ + switch (mac->fc) { + case e1000_fc_none: + /* Flow control completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case e1000_fc_rx_pause: + /* RX Flow control is enabled and TX Flow control is disabled + * by a software over-ride. Since there really isn't a way to + * advertise that we are capable of RX Pause ONLY, we will + * advertise that we support both symmetric and asymmetric RX + * PAUSE. Later, we will disable the adapter's ability to send + * PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case e1000_fc_tx_pause: + /* TX Flow control is enabled, and RX Flow control is disabled, + * by a software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case e1000_fc_full: + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + break; + } + + E1000_WRITE_REG(hw, E1000_TXCW, txcw); + mac->txcw = txcw; + +out: + return ret_val; +} + +/** + * e1000_set_fc_watermarks_generic - Set flow control high/low watermarks + * @hw: pointer to the HW structure + * + * Sets the flow control high/low threshold (watermark) registers. If + * flow control XON frame transmission is enabled, then set XON frame + * tansmission as well. + **/ +s32 +e1000_set_fc_watermarks_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + u32 fcrtl = 0, fcrth = 0; + + DEBUGFUNC("e1000_set_fc_watermarks_generic"); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames is not enabled, then these + * registers will be set to 0. + */ + if (mac->fc & e1000_fc_tx_pause) { + /* We need to set up the Receive Threshold high and low water + * marks as well as (optionally) enabling the transmission of + * XON frames. + */ + fcrtl = mac->fc_low_water; + if (mac->fc_send_xon) + fcrtl |= E1000_FCRTL_XONE; + + fcrth = mac->fc_high_water; + } + E1000_WRITE_REG(hw, E1000_FCRTL, fcrtl); + E1000_WRITE_REG(hw, E1000_FCRTH, fcrth); + + return ret_val; +} + +/** + * e1000_set_default_fc_generic - Set flow control default values + * @hw: pointer to the HW structure + * + * Read the EEPROM for the default values for flow control and store the + * values. + **/ +s32 +e1000_set_default_fc_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + u16 nvm_data; + + DEBUGFUNC("e1000_set_default_fc_generic"); + + if (mac->fc != e1000_fc_default) + goto out; + + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &nvm_data); + + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == 0) + mac->fc = e1000_fc_none; + else if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == + NVM_WORD0F_ASM_DIR) + mac->fc = e1000_fc_tx_pause; + else + mac->fc = e1000_fc_full; + +out: + return ret_val; +} + +/** + * e1000_force_mac_fc_generic - Force the MAC's flow control settings + * @hw: pointer to the HW structure + * + * Force the MAC's flow control settings. Sets the TFCE and RFCE bits in the + * device control register to reflect the adapter settings. TFCE and RFCE + * need to be explicitly set by software when a copper PHY is used because + * autonegotiation is managed by the PHY rather than the MAC. Software must + * also configure these bits when link is forced on a fiber connection. + **/ +s32 +e1000_force_mac_fc_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 ctrl; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_force_mac_fc_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "mac->fc" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and TX flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + DEBUGOUT1("mac->fc = %u\n", mac->fc); + + switch (mac->fc) { + case e1000_fc_none: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case e1000_fc_rx_pause: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case e1000_fc_tx_pause: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case e1000_fc_full: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + +out: + return ret_val; +} + +/** + * e1000_config_fc_after_link_up_generic - Configures flow control after link + * @hw: pointer to the HW structure + * + * Checks the status of auto-negotiation after link up to ensure that the + * speed and duplex were not forced. If the link needed to be forced, then + * flow control needs to be forced also. If auto-negotiation is enabled + * and did not fail, then we configure flow control based on our link + * partner. + **/ +s32 +e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg; + u16 speed, duplex; + + DEBUGFUNC("e1000_config_fc_after_link_up_generic"); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if (mac->autoneg_failed) { + if (hw->media_type == e1000_media_type_fiber || + hw->media_type == e1000_media_type_internal_serdes) + ret_val = e1000_force_mac_fc_generic(hw); + } else { + if (hw->media_type == e1000_media_type_copper) + ret_val = e1000_force_mac_fc_generic(hw); + } + + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + goto out; + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->media_type == e1000_media_type_copper) && mac->autoneg) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + goto out; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + goto out; + + if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) { + DEBUGOUT("Copper PHY and Auto Neg " + "has not completed.\n"); + goto out; + } + + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement + * Register (Address 4) and the Auto_Negotiation Base + * Page Ability Register (Address 5) to determine how + * flow control was negotiated. + */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg); + if (ret_val) + goto out; + ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg); + if (ret_val) + goto out; + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + /* Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | E1000_fc_full + * + */ + if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected RX ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (mac->original_fc == e1000_fc_full) { + mac->fc = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\r\n"); + } else { + mac->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = " + "RX PAUSE frames only.\r\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * + */ + else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + mac->fc = e1000_fc_tx_pause; + DEBUGOUT("Flow Control = TX PAUSE frames only.\r\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + mac->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n"); + } + /* Per the IEEE spec, at this point flow control should be + * disabled. However, we want to consider that we could + * be connected to a legacy switch that doesn't advertise + * desired flow control, but can be forced on the link + * partner. So if we advertised no flow control, that is + * what we will resolve to. If we advertised some kind of + * receive capability (Rx Pause Only or Full Flow Control) + * and the link partner advertised none, we will configure + * ourselves to enable Rx Flow Control only. We can do + * this safely for two reasons: If the link partner really + * didn't want flow control enabled, and we enable Rx, no + * harm done since we won't be receiving any PAUSE frames + * anyway. If the intent on the link partner was to have + * flow control enabled, then by us enabling RX only, we + * can at least receive pause frames and process them. + * This is a good idea because in most cases, since we are + * predominantly a server NIC, more times than not we will + * be asked to delay transmission of packets than asking + * our link partner to pause transmission of frames. + */ + else if ((mac->original_fc == e1000_fc_none || + mac->original_fc == e1000_fc_tx_pause) || + mac->fc_strict_ieee) { + mac->fc = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\r\n"); + } else { + mac->fc = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + goto out; + } + + if (duplex == HALF_DUPLEX) + mac->fc = e1000_fc_none; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = e1000_force_mac_fc_generic(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_get_speed_and_duplex_copper_generic - Retreive current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Read the status register for the current speed/duplex and store the current + * speed and duplex for copper connections. + **/ +s32 +e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, + u16 *duplex) +{ + u32 status; + + DEBUGFUNC("e1000_get_speed_and_duplex_copper_generic"); + + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if (status & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if (status & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_speed_and_duplex_fiber_generic - Retreive current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Sets the speed and duplex to gigabit full duplex (the only possible option) + * for fiber/serdes links. + **/ +s32 +e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw *hw, u16 *speed, + u16 *duplex) +{ + DEBUGFUNC("e1000_get_speed_and_duplex_fiber_serdes_generic"); + + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + + return E1000_SUCCESS; +} + +/** + * e1000_get_hw_semaphore_generic - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +s32 +e1000_get_hw_semaphore_generic(struct e1000_hw *hw) +{ + u32 swsm; + s32 ret_val = E1000_SUCCESS; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("e1000_get_hw_semaphore_generic"); + + /* Get the SW semaphore */ + while (i < timeout) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + if (!(swsm & E1000_SWSM_SMBI)) + break; + + usec_delay(50); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + } + + if (i == timeout) { + /* Release semaphores */ + e1000_put_hw_semaphore_generic(hw); + DEBUGOUT("Driver can't access the NVM\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_put_hw_semaphore_generic - Release hardware semaphore + * @hw: pointer to the HW structure + * + * Release hardware semaphore used to access the PHY or NVM + **/ +void +e1000_put_hw_semaphore_generic(struct e1000_hw *hw) +{ + u32 swsm; + + DEBUGFUNC("e1000_put_hw_semaphore_generic"); + + swsm = E1000_READ_REG(hw, E1000_SWSM); + + swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); + + E1000_WRITE_REG(hw, E1000_SWSM, swsm); +} + +/** + * e1000_get_auto_rd_done_generic - Check for auto read completion + * @hw: pointer to the HW structure + * + * Check EEPROM for Auto Read done bit. + **/ +s32 +e1000_get_auto_rd_done_generic(struct e1000_hw *hw) +{ + s32 i = 0; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_get_auto_rd_done_generic"); + + while (i < AUTO_READ_DONE_TIMEOUT) { + if (E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_AUTO_RD) + break; + msec_delay(1); + i++; + } + + if (i == AUTO_READ_DONE_TIMEOUT) { + DEBUGOUT("Auto read by HW from NVM has not completed.\n"); + ret_val = -E1000_ERR_RESET; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_valid_led_default_generic - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +s32 +e1000_valid_led_default_generic(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_generic"); + + ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) + *data = ID_LED_DEFAULT; + +out: + return ret_val; +} + +/** + * e1000_id_led_init_generic - + * @hw: pointer to the HW structure + * + **/ +s32 +e1000_id_led_init_generic(struct e1000_hw * hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + const u32 ledctl_mask = 0x000000FF; + const u32 ledctl_on = E1000_LEDCTL_MODE_LED_ON; + const u32 ledctl_off = E1000_LEDCTL_MODE_LED_OFF; + u16 data, i, temp; + const u16 led_mask = 0x0F; + + DEBUGFUNC("e1000_id_led_init_generic"); + + ret_val = hw->func.valid_led_default(hw, &data); + if (ret_val) + goto out; + + mac->ledctl_default = E1000_READ_REG(hw, E1000_LEDCTL); + mac->ledctl_mode1 = mac->ledctl_default; + mac->ledctl_mode2 = mac->ledctl_default; + + for (i = 0; i < 4; i++) { + temp = (data >> (i << 2)) & led_mask; + switch (temp) { + case ID_LED_ON1_DEF2: + case ID_LED_ON1_ON2: + case ID_LED_ON1_OFF2: + mac->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode1 |= ledctl_on << (i << 3); + break; + case ID_LED_OFF1_DEF2: + case ID_LED_OFF1_ON2: + case ID_LED_OFF1_OFF2: + mac->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode1 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + switch (temp) { + case ID_LED_DEF1_ON2: + case ID_LED_ON1_ON2: + case ID_LED_OFF1_ON2: + mac->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode2 |= ledctl_on << (i << 3); + break; + case ID_LED_DEF1_OFF2: + case ID_LED_ON1_OFF2: + case ID_LED_OFF1_OFF2: + mac->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode2 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + } + +out: + return ret_val; +} + +/** + * e1000_setup_led_generic - Configures SW controllable LED + * @hw: pointer to the HW structure + * + * This prepares the SW controllable LED for use and saves the current state + * of the LED so it can be later restored. + **/ +s32 +e1000_setup_led_generic(struct e1000_hw *hw) +{ + u32 ledctl; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_setup_led_generic"); + + if (hw->func.setup_led != e1000_setup_led_generic) { + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + if (hw->media_type == e1000_media_type_fiber) { + ledctl = E1000_READ_REG(hw, E1000_LEDCTL); + hw->mac.ledctl_default = ledctl; + /* Turn off LED0 */ + ledctl &= ~(E1000_LEDCTL_LED0_IVRT | + E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_LED0_MODE_MASK); + ledctl |= (E1000_LEDCTL_MODE_LED_OFF << + E1000_LEDCTL_LED0_MODE_SHIFT); + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl); + } else if (hw->media_type == e1000_media_type_copper) { + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + } + +out: + return ret_val; +} + +/** + * e1000_cleanup_led_generic - Set LED config to default operation + * @hw: pointer to the HW structure + * + * Remove the current LED configuration and set the LED configuration + * to the default value, saved from the EEPROM. + **/ +s32 +e1000_cleanup_led_generic(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_cleanup_led_generic"); + + if (hw->func.cleanup_led != e1000_cleanup_led_generic) { + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default); + +out: + return ret_val; +} + +/** + * e1000_blink_led_generic - Blink LED + * @hw: pointer to the HW structure + * + * Blink the led's which are set to be on. + **/ +s32 +e1000_blink_led_generic(struct e1000_hw *hw) +{ + u32 ledctl_blink = 0; + u32 i; + + DEBUGFUNC("e1000_blink_led_generic"); + + if (hw->media_type == e1000_media_type_fiber) { + /* always blink LED0 for PCI-E fiber */ + ledctl_blink = E1000_LEDCTL_LED0_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); + } else { + /* set the blink bit for each LED that's "on" (0x0E) + * in ledctl_mode2 */ + ledctl_blink = hw->mac.ledctl_mode2; + for (i = 0; i < 4; i++) + if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) == + E1000_LEDCTL_MODE_LED_ON) + ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << + (i * 8)); + } + + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl_blink); + + return E1000_SUCCESS; +} + +/** + * e1000_led_on_generic - Turn LED on + * @hw: pointer to the HW structure + * + * Turn LED on. + **/ +s32 +e1000_led_on_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_led_on_generic"); + + switch (hw->media_type) { + case e1000_media_type_fiber: + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + break; + case e1000_media_type_copper: + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode2); + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_led_off_generic - Turn LED off + * @hw: pointer to the HW structure + * + * Turn LED off. + **/ +s32 +e1000_led_off_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_led_off_generic"); + + switch (hw->media_type) { + case e1000_media_type_fiber: + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + break; + case e1000_media_type_copper: + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_set_pcie_no_snoop_generic - Set PCI-express capabilities + * @hw: pointer to the HW structure + * @no_snoop: bitmap of snoop events + * + * Set the PCI-express register to snoop for events enabled in 'no_snoop'. + **/ +void +e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop) +{ + u32 gcr; + + DEBUGFUNC("e1000_set_pcie_no_snoop_generic"); + + if (hw->bus.type != e1000_bus_type_pci_express) + goto out; + + if (no_snoop) { + gcr = E1000_READ_REG(hw, E1000_GCR); + gcr &= ~(PCIE_NO_SNOOP_ALL); + gcr |= no_snoop; + E1000_WRITE_REG(hw, E1000_GCR, gcr); + } +out: + return; +} + +/** + * e1000_disable_pcie_master_generic - Disables PCI-express master access + * @hw: pointer to the HW structure + * + * Returns 0 (E1000_SUCCESS) if successful, else returns -10 + * (-E1000_ERR_MASTER_REQUESTS_PENDING) if master disable bit has not casued + * the master requests to be disabled. + * + * Disables PCI-Express master access and verifies there are no pending + * requests. + **/ +s32 +e1000_disable_pcie_master_generic(struct e1000_hw *hw) +{ + u32 ctrl; + s32 timeout = MASTER_DISABLE_TIMEOUT; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_disable_pcie_master_generic"); + + if (hw->bus.type != e1000_bus_type_pci_express) + goto out; + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + while (timeout) { + if (!(E1000_READ_REG(hw, E1000_STATUS) & + E1000_STATUS_GIO_MASTER_ENABLE)) + break; + usec_delay(100); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Master requests are pending.\n"); + ret_val = -E1000_ERR_MASTER_REQUESTS_PENDING; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_reset_adaptive_generic - Reset Adaptive Interframe Spacing + * @hw: pointer to the HW structure + * + * Reset the Adaptive Interframe Spacing throttle to default values. + **/ +void +e1000_reset_adaptive_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + + DEBUGFUNC("e1000_reset_adaptive_generic"); + + if (!mac->adaptive_ifs) { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + goto out; + } + + if (!mac->ifs_params_forced) { + mac->current_ifs_val = 0; + mac->ifs_min_val = IFS_MIN; + mac->ifs_max_val = IFS_MAX; + mac->ifs_step_size = IFS_STEP; + mac->ifs_ratio = IFS_RATIO; + } + + mac->in_ifs_mode = FALSE; + E1000_WRITE_REG(hw, E1000_AIT, 0); +out: + return; +} + +/** + * e1000_update_adaptive_generic - Update Adaptive Interframe Spacing + * @hw: pointer to the HW structure + * + * Update the Adaptive Interframe Spacing Throttle value based on the + * time between transmitted packets and time between collisions. + **/ +void +e1000_update_adaptive_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + + DEBUGFUNC("e1000_update_adaptive_generic"); + + if (!mac->adaptive_ifs) { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + goto out; + } + + if ((mac->collision_delta * mac->ifs_ratio) > mac->tx_packet_delta) { + if (mac->tx_packet_delta > MIN_NUM_XMITS) { + mac->in_ifs_mode = TRUE; + if (mac->current_ifs_val < mac->ifs_max_val) { + if (!mac->current_ifs_val) + mac->current_ifs_val = mac->ifs_min_val; + else + mac->current_ifs_val += + mac->ifs_step_size; + E1000_WRITE_REG(hw, E1000_AIT, mac->current_ifs_val); + } + } + } else { + if (mac->in_ifs_mode && + (mac->tx_packet_delta <= MIN_NUM_XMITS)) { + mac->current_ifs_val = 0; + mac->in_ifs_mode = FALSE; + E1000_WRITE_REG(hw, E1000_AIT, 0); + } + } +out: + return; +} + +/** + * e1000_validate_mdi_setting_generic - Verify MDI/MDIx settings + * @hw: pointer to the HW structure + * + * Verify that when not using auto-negotitation that MDI/MDIx is correctly + * set, which is forced to MDI mode only. + **/ +s32 +e1000_validate_mdi_setting_generic(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_validate_mdi_setting_generic"); + + if (!hw->mac.autoneg && (hw->phy.mdix == 0 || hw->phy.mdix == 3)) { + DEBUGOUT("Invalid MDI setting detected\n"); + hw->phy.mdix = 1; + ret_val = -E1000_ERR_CONFIG; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_write_8bit_ctrl_reg_generic - Write a 8bit CTRL register + * @hw: pointer to the HW structure + * @reg: 32bit register offset such as E1000_SCTL + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes an address/data control type register. There are several of these + * and they all have the format address << 8 | data and bit 31 is polled for + * completion. + **/ +s32 +e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, + u32 offset, u8 data) +{ + u32 i, regvalue = 0; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_8bit_ctrl_reg_generic"); + + /* Set up the address and data */ + regvalue = ((u32)data) | (offset << E1000_GEN_CTL_ADDRESS_SHIFT); + E1000_WRITE_REG(hw, reg, regvalue); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < E1000_GEN_POLL_TIMEOUT; i++) { + usec_delay(5); + regvalue = E1000_READ_REG(hw, reg); + if (regvalue & E1000_GEN_CTL_READY) + break; + } + if (!(regvalue & E1000_GEN_CTL_READY)) { + DEBUGOUT1("Reg %08x did not indicate ready\n", reg); + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} diff --git a/bsd_eth_drivers/if_em/e1000_mac.h b/bsd_eth_drivers/if_em/e1000_mac.h new file mode 100644 index 0000000..ee2750b --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_mac.h @@ -0,0 +1,91 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_mac.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_MAC_H_ +#define _E1000_MAC_H_ + +/* Functions that should not be called directly from drivers but can be used + * by other files in this 'shared code' + */ +s32 e1000_blink_led_generic(struct e1000_hw *hw); +s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw); +s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw); +s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw); +s32 e1000_cleanup_led_generic(struct e1000_hw *hw); +s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw); +s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw); +s32 e1000_disable_pcie_master_generic(struct e1000_hw *hw); +s32 e1000_force_mac_fc_generic(struct e1000_hw *hw); +s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw); +s32 e1000_get_bus_info_pci_generic(struct e1000_hw *hw); +s32 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw); +s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw); +s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw *hw, + u16 *speed, u16 *duplex); +s32 e1000_id_led_init_generic(struct e1000_hw *hw); +s32 e1000_led_on_generic(struct e1000_hw *hw); +s32 e1000_led_off_generic(struct e1000_hw *hw); +void e1000_mc_addr_list_update_generic(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count, + u32 rar_used_count, u32 rar_count); +s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw); +s32 e1000_set_default_fc_generic(struct e1000_hw *hw); +s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw); +s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw); +s32 e1000_setup_led_generic(struct e1000_hw *hw); +s32 e1000_setup_link_generic(struct e1000_hw *hw); +s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw); +s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, + u32 offset, u8 data); + +u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr); + +void e1000_clear_hw_cntrs_base_generic(struct e1000_hw *hw); +void e1000_clear_vfta_generic(struct e1000_hw *hw); +void e1000_config_collision_dist_generic(struct e1000_hw *hw); +void e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count); +void e1000_mta_set_generic(struct e1000_hw *hw, u32 hash_value); +void e1000_pcix_mmrbc_workaround_generic(struct e1000_hw *hw); +void e1000_put_hw_semaphore_generic(struct e1000_hw *hw); +void e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index); +void e1000_remove_device_generic(struct e1000_hw *hw); +void e1000_reset_adaptive_generic(struct e1000_hw *hw); +void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop); +void e1000_update_adaptive_generic(struct e1000_hw *hw); +void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value); + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_nvm.c b/bsd_eth_drivers/if_em/e1000_nvm.c new file mode 100644 index 0000000..6fc4987 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_nvm.c @@ -0,0 +1,901 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_nvm.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#include "e1000_api.h" +#include "e1000_nvm.h" + +/** + * e1000_raise_eec_clk - Raise EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Enable/Raise the EEPROM clock bit. + **/ +static void +e1000_raise_eec_clk(struct e1000_hw *hw, u32 *eecd) +{ + *eecd = *eecd | E1000_EECD_SK; + E1000_WRITE_REG(hw, E1000_EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * e1000_lower_eec_clk - Lower EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Clear/Lower the EEPROM clock bit. + **/ +static void +e1000_lower_eec_clk(struct e1000_hw *hw, u32 *eecd) +{ + *eecd = *eecd & ~E1000_EECD_SK; + E1000_WRITE_REG(hw, E1000_EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * e1000_shift_out_eec_bits - Shift data bits our to the EEPROM + * @hw: pointer to the HW structure + * @data: data to send to the EEPROM + * @count: number of bits to shift out + * + * We need to shift 'count' bits out to the EEPROM. So, the value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + **/ +static void +e1000_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u32 mask; + + DEBUGFUNC("e1000_shift_out_eec_bits"); + + mask = 0x01 << (count - 1); + if (nvm->type == e1000_nvm_eeprom_microwire) + eecd &= ~E1000_EECD_DO; + else if (nvm->type == e1000_nvm_eeprom_spi) + eecd |= E1000_EECD_DO; + + do { + eecd &= ~E1000_EECD_DI; + + if (data & mask) + eecd |= E1000_EECD_DI; + + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + + usec_delay(nvm->delay_usec); + + e1000_raise_eec_clk(hw, &eecd); + e1000_lower_eec_clk(hw, &eecd); + + mask >>= 1; + } while (mask); + + eecd &= ~E1000_EECD_DI; + E1000_WRITE_REG(hw, E1000_EECD, eecd); +} + +/** + * e1000_shift_in_eec_bits - Shift data bits in from the EEPROM + * @hw: pointer to the HW structure + * @count: number of bits to shift in + * + * In order to read a register from the EEPROM, we need to shift 'count' bits + * in from the EEPROM. Bits are "shifted in" by raising the clock input to + * the EEPROM (setting the SK bit), and then reading the value of the data out + * "DO" bit. During this "shifting in" process the data in "DI" bit should + * always be clear. + **/ +static u16 +e1000_shift_in_eec_bits(struct e1000_hw *hw, u16 count) +{ + u32 eecd; + u32 i; + u16 data; + + DEBUGFUNC("e1000_shift_in_eec_bits"); + + eecd = E1000_READ_REG(hw, E1000_EECD); + + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for (i = 0; i < count; i++) { + data <<= 1; + e1000_raise_eec_clk(hw, &eecd); + + eecd = E1000_READ_REG(hw, E1000_EECD); + + eecd &= ~E1000_EECD_DI; + if (eecd & E1000_EECD_DO) + data |= 1; + + e1000_lower_eec_clk(hw, &eecd); + } + + return data; +} + +/** + * e1000_poll_eerd_eewr_done - Poll for EEPROM read/write completion + * @hw: pointer to the HW structure + * @ee_reg: EEPROM flag for polling + * + * Polls the EEPROM status bit for either read or write completion based + * upon the value of 'ee_reg'. + **/ +s32 +e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg) +{ + u32 attempts = 100000; + u32 i, reg = 0; + s32 ret_val = -E1000_ERR_NVM; + + DEBUGFUNC("e1000_poll_eerd_eewr_done"); + + for (i = 0; i < attempts; i++) { + if (ee_reg == E1000_NVM_POLL_READ) + reg = E1000_READ_REG(hw, E1000_EERD); + else + reg = E1000_READ_REG(hw, E1000_EEWR); + + if (reg & E1000_NVM_RW_REG_DONE) { + ret_val = E1000_SUCCESS; + break; + } + + usec_delay(5); + } + + return ret_val; +} + +/** + * e1000_acquire_nvm_generic - Generic request for access to EEPROM + * @hw: pointer to the HW structure + * + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +s32 +e1000_acquire_nvm_generic(struct e1000_hw *hw) +{ + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + s32 timeout = E1000_NVM_GRANT_ATTEMPTS; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_acquire_nvm_generic"); + + E1000_WRITE_REG(hw, E1000_EECD, eecd | E1000_EECD_REQ); + eecd = E1000_READ_REG(hw, E1000_EECD); + + while (timeout) { + if (eecd & E1000_EECD_GNT) + break; + usec_delay(5); + eecd = E1000_READ_REG(hw, E1000_EECD); + timeout--; + } + + if (!timeout) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + DEBUGOUT("Could not acquire NVM grant\n"); + ret_val = -E1000_ERR_NVM; + } + + return ret_val; +} + +/** + * e1000_standby_nvm - Return EEPROM to standby state + * @hw: pointer to the HW structure + * + * Return the EEPROM to a standby state. + **/ +static void +e1000_standby_nvm(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + + DEBUGFUNC("e1000_standby_nvm"); + + if (nvm->type == e1000_nvm_eeprom_microwire) { + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + + e1000_raise_eec_clk(hw, &eecd); + + /* Select EEPROM */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + + e1000_lower_eec_clk(hw, &eecd); + } else if (nvm->type == e1000_nvm_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + eecd &= ~E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + } +} + +/** + * e1000_stop_nvm - Terminate EEPROM command + * @hw: pointer to the HW structure + * + * Terminates the current command by inverting the EEPROM's chip select pin. + **/ +void +e1000_stop_nvm(struct e1000_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("e1000_stop_nvm"); + + eecd = E1000_READ_REG(hw, E1000_EECD); + if (hw->nvm.type == e1000_nvm_eeprom_spi) { + /* Pull CS high */ + eecd |= E1000_EECD_CS; + e1000_lower_eec_clk(hw, &eecd); + } else if (hw->nvm.type == e1000_nvm_eeprom_microwire) { + /* CS on Microcwire is active-high */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); + E1000_WRITE_REG(hw, E1000_EECD, eecd); + e1000_raise_eec_clk(hw, &eecd); + e1000_lower_eec_clk(hw, &eecd); + } +} + +/** + * e1000_release_nvm_generic - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit. + **/ +void +e1000_release_nvm_generic(struct e1000_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("e1000_release_nvm_generic"); + + e1000_stop_nvm(hw); + + eecd = E1000_READ_REG(hw, E1000_EECD); + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, E1000_EECD, eecd); +} + +/** + * e1000_ready_nvm_eeprom - Prepares EEPROM for read/write + * @hw: pointer to the HW structure + * + * Setups the EEPROM for reading and writing. + **/ +static s32 +e1000_ready_nvm_eeprom(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + s32 ret_val = E1000_SUCCESS; + u16 timeout = 0; + u8 spi_stat_reg; + + DEBUGFUNC("e1000_ready_nvm_eeprom"); + + if (nvm->type == e1000_nvm_eeprom_microwire) { + /* Clear SK and DI */ + eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); + E1000_WRITE_REG(hw, E1000_EECD, eecd); + /* Set CS */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + } else if (nvm->type == e1000_nvm_eeprom_spi) { + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, E1000_EECD, eecd); + usec_delay(1); + timeout = NVM_MAX_RETRY_SPI; + + /* Read "Status Register" repeatedly until the LSB is cleared. + * The EEPROM will signal that the command has been completed + * by clearing bit 0 of the internal status register. If it's + * not cleared within 'timeout', then error out. */ + while (timeout) { + e1000_shift_out_eec_bits(hw, NVM_RDSR_OPCODE_SPI, + hw->nvm.opcode_bits); + spi_stat_reg = (u8)e1000_shift_in_eec_bits(hw, 8); + if (!(spi_stat_reg & NVM_STATUS_RDY_SPI)) + break; + + usec_delay(5); + e1000_standby_nvm(hw); + timeout--; + } + + if (!timeout) { + DEBUGOUT("SPI NVM Status error\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_read_nvm_spi - Read EEPROM's using SPI + * @hw: pointer to the HW structure + * @offset: offset of word in the EEPROM to read + * @words: number of words to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM. + **/ +s32 +e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i = 0; + s32 ret_val; + u16 word_in; + u8 read_opcode = NVM_READ_OPCODE_SPI; + + DEBUGFUNC("e1000_read_nvm_spi"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + ret_val = e1000_acquire_nvm(hw); + if (ret_val) + goto out; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) + goto release; + + e1000_standby_nvm(hw); + + if ((nvm->address_bits == 8) && (offset >= 128)) + read_opcode |= NVM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits); + e1000_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits); + + /* Read the data. SPI NVMs increment the address with each byte + * read and will roll over if reading beyond the end. This allows + * us to read the whole NVM from any offset */ + for (i = 0; i < words; i++) { + word_in = e1000_shift_in_eec_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + +release: + e1000_release_nvm(hw); + +out: + return ret_val; +} + +/** + * e1000_read_nvm_microwire - Reads EEPROM's using microwire + * @hw: pointer to the HW structure + * @offset: offset of word in the EEPROM to read + * @words: number of words to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM. + **/ +s32 +e1000_read_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i = 0; + s32 ret_val; + u8 read_opcode = NVM_READ_OPCODE_MICROWIRE; + + DEBUGFUNC("e1000_read_nvm_microwire"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + ret_val = e1000_acquire_nvm(hw); + if (ret_val) + goto out; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) + goto release; + + for (i = 0; i < words; i++) { + /* Send the READ command (opcode + addr) */ + e1000_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits); + e1000_shift_out_eec_bits(hw, (u16)(offset + i), + nvm->address_bits); + + /* Read the data. For microwire, each word requires the + * overhead of setup and tear-down. */ + data[i] = e1000_shift_in_eec_bits(hw, 16); + e1000_standby_nvm(hw); + } + +release: + e1000_release_nvm(hw); + +out: + return ret_val; +} + +/** + * e1000_read_nvm_eerd - Reads EEPROM using EERD register + * @hw: pointer to the HW structure + * @offset: offset of word in the EEPROM to read + * @words: number of words to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM using the EERD register. + **/ +s32 +e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i, eerd = 0; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_nvm_eerd"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + for (i = 0; i < words; i++) { + eerd = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) + + E1000_NVM_RW_REG_START; + + E1000_WRITE_REG(hw, E1000_EERD, eerd); + ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ); + if (ret_val) + break; + + data[i] = (E1000_READ_REG(hw, E1000_EERD) >> E1000_NVM_RW_REG_DATA); + } + +out: + return ret_val; +} + +/** + * e1000_write_nvm_spi - Write to EEPROM using SPI + * @hw: pointer to the HW structure + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the EEPROM + * + * Writes data to EEPROM at offset using SPI interface. + * + * If e1000_update_nvm_checksum is not called after this function , the + * EEPROM will most likley contain an invalid checksum. + **/ +s32 +e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + s32 ret_val; + u16 widx = 0; + + DEBUGFUNC("e1000_write_nvm_spi"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + ret_val = e1000_acquire_nvm(hw); + if (ret_val) + goto out; + + msec_delay(10); + + while (widx < words) { + u8 write_opcode = NVM_WRITE_OPCODE_SPI; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) + goto release; + + e1000_standby_nvm(hw); + + /* Send the WRITE ENABLE command (8 bit opcode) */ + e1000_shift_out_eec_bits(hw, NVM_WREN_OPCODE_SPI, + nvm->opcode_bits); + + e1000_standby_nvm(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the + * opcode */ + if ((nvm->address_bits == 8) && (offset >= 128)) + write_opcode |= NVM_A8_OPCODE_SPI; + + /* Send the Write command (8-bit opcode + addr) */ + e1000_shift_out_eec_bits(hw, write_opcode, nvm->opcode_bits); + e1000_shift_out_eec_bits(hw, (u16)((offset + widx) * 2), + nvm->address_bits); + + /* Loop to allow for up to whole page write of eeprom */ + while (widx < words) { + u16 word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); + e1000_shift_out_eec_bits(hw, word_out, 16); + widx++; + + if ((((offset + widx) * 2) % nvm->page_size) == 0) { + e1000_standby_nvm(hw); + break; + } + } + } + + msec_delay(10); +release: + e1000_release_nvm(hw); + +out: + return ret_val; +} + +/** + * e1000_write_nvm_microwire - Writes EEPROM using microwire + * @hw: pointer to the HW structure + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the EEPROM + * + * Writes data to EEPROM at offset using microwire interface. + * + * If e1000_update_nvm_checksum is not called after this function , the + * EEPROM will most likley contain an invalid checksum. + **/ +s32 +e1000_write_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + s32 ret_val; + u32 eecd; + u16 words_written = 0; + u16 widx = 0; + + DEBUGFUNC("e1000_write_nvm_microwire"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + ret_val = e1000_acquire_nvm(hw); + if (ret_val) + goto out; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) + goto release; + + e1000_shift_out_eec_bits(hw, NVM_EWEN_OPCODE_MICROWIRE, + (u16)(nvm->opcode_bits + 2)); + + e1000_shift_out_eec_bits(hw, 0, (u16)(nvm->address_bits - 2)); + + e1000_standby_nvm(hw); + + while (words_written < words) { + e1000_shift_out_eec_bits(hw, NVM_WRITE_OPCODE_MICROWIRE, + nvm->opcode_bits); + + e1000_shift_out_eec_bits(hw, (u16)(offset + words_written), + nvm->address_bits); + + e1000_shift_out_eec_bits(hw, data[words_written], 16); + + e1000_standby_nvm(hw); + + for (widx = 0; widx < 200; widx++) { + eecd = E1000_READ_REG(hw, E1000_EECD); + if (eecd & E1000_EECD_DO) + break; + usec_delay(50); + } + + if (widx == 200) { + DEBUGOUT("NVM Write did not complete\n"); + ret_val = -E1000_ERR_NVM; + goto release; + } + + e1000_standby_nvm(hw); + + words_written++; + } + + e1000_shift_out_eec_bits(hw, NVM_EWDS_OPCODE_MICROWIRE, + (u16)(nvm->opcode_bits + 2)); + + e1000_shift_out_eec_bits(hw, 0, (u16)(nvm->address_bits - 2)); + +release: + e1000_release_nvm(hw); + +out: + return ret_val; +} + +/** + * e1000_read_part_num_generic - Read device part number + * @hw: pointer to the HW structure + * @part_num: pointer to device part number + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in part_num. + **/ +s32 +e1000_read_part_num_generic(struct e1000_hw *hw, u32 *part_num) +{ + s32 ret_val; + u16 nvm_data; + + DEBUGFUNC("e1000_read_part_num_generic"); + + ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_0, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + *part_num = (u32)(nvm_data << 16); + + ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_1, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + *part_num |= nvm_data; + +out: + return ret_val; +} + +/** + * e1000_read_mac_addr_generic - Read device MAC address + * @hw: pointer to the HW structure + * + * Reads the device MAC address from the EEPROM and stores the value. + * Since devices with two ports use the same EEPROM, we increment the + * last bit in the MAC address for the second port. + **/ +s32 +e1000_read_mac_addr_generic(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 offset, nvm_data, i; + + DEBUGFUNC("e1000_read_mac_addr"); + + for (i = 0; i < ETH_ADDR_LEN; i += 2) { + offset = i >> 1; + ret_val = e1000_read_nvm(hw, offset, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF); + hw->mac.perm_addr[i+1] = (u8)(nvm_data >> 8); + } + + /* Flip last bit of mac address if we're on second port */ + if (hw->bus.func == E1000_FUNC_1) + hw->mac.perm_addr[5] ^= 1; + + for (i = 0; i < ETH_ADDR_LEN; i++) + hw->mac.addr[i] = hw->mac.perm_addr[i]; + +out: + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_generic - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 +e1000_validate_nvm_checksum_generic(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_validate_nvm_checksum_generic"); + + for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) { + ret_val = e1000_read_nvm(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + checksum += nvm_data; + } + + if (checksum != (u16) NVM_SUM) { + DEBUGOUT("NVM Checksum Invalid\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_generic - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. + **/ +s32 +e1000_update_nvm_checksum_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum"); + + for (i = 0; i < NVM_CHECKSUM_REG; i++) { + ret_val = e1000_read_nvm(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum.\n"); + goto out; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = e1000_write_nvm(hw, NVM_CHECKSUM_REG, 1, &checksum); + if (ret_val) { + DEBUGOUT("NVM Write Error while updating checksum.\n"); + } + +out: + return ret_val; +} + +/** + * e1000_reload_nvm_generic - Reloads EEPROM + * @hw: pointer to the HW structure + * + * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the + * extended control register. + **/ +void +e1000_reload_nvm_generic(struct e1000_hw *hw) +{ + u32 ctrl_ext; + + DEBUGFUNC("e1000_reload_nvm_generic"); + + usec_delay(10); + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); +} + +/* Function pointers local to this file and not intended for public use */ + +/** + * e1000_acquire_nvm - Acquire exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * For those silicon families which have implemented a NVM acquire function, + * run the defined function else return success. + **/ +s32 +e1000_acquire_nvm(struct e1000_hw *hw) +{ + if (hw->func.acquire_nvm != NULL) + return hw->func.acquire_nvm(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_release_nvm - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * For those silicon families which have implemented a NVM release function, + * run the defined fucntion else return success. + **/ +void +e1000_release_nvm(struct e1000_hw *hw) +{ + if (hw->func.release_nvm != NULL) + hw->func.release_nvm(hw); +} + diff --git a/bsd_eth_drivers/if_em/e1000_nvm.h b/bsd_eth_drivers/if_em/e1000_nvm.h new file mode 100644 index 0000000..f4bd123 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_nvm.h @@ -0,0 +1,66 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_nvm.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_NVM_H_ +#define _E1000_NVM_H_ + +s32 e1000_acquire_nvm_generic(struct e1000_hw *hw); + +s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg); +s32 e1000_read_mac_addr_generic(struct e1000_hw *hw); +s32 e1000_read_part_num_generic(struct e1000_hw *hw, u32 *part_num); +s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_read_nvm_microwire(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_valid_led_default_generic(struct e1000_hw *hw, u16 *data); +s32 e1000_validate_nvm_checksum_generic(struct e1000_hw *hw); +s32 e1000_write_nvm_eewr(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_write_nvm_microwire(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_update_nvm_checksum_generic(struct e1000_hw *hw); +void e1000_stop_nvm(struct e1000_hw *hw); +void e1000_release_nvm_generic(struct e1000_hw *hw); +void e1000_reload_nvm_generic(struct e1000_hw *hw); + +/* Function pointers */ +s32 e1000_acquire_nvm(struct e1000_hw *hw); +void e1000_release_nvm(struct e1000_hw *hw); + +#define E1000_STM_OPCODE 0xDB00 + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_osdep.h b/bsd_eth_drivers/if_em/e1000_osdep.h new file mode 100644 index 0000000..b5aa603 --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_osdep.h @@ -0,0 +1,397 @@ +/************************************************************************** + +Copyright (c) 2001-2007, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_osdep.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _FREEBSD_OS_H_ +#define _FREEBSD_OS_H_ + +#include +#define _KERNEL +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT(x) if(!(x)) panic("EM: x") + +/* The happy-fun DELAY macro is defined in /usr/src/sys/i386/include/clock.h */ +#define usec_delay(x) DELAY(x) +#define msec_delay(x) DELAY(1000*(x)) +/* TODO: Should we be paranoid about delaying in interrupt context? */ +#define msec_delay_irq(x) DELAY(1000*(x)) +#include + +#define MSGOUT(S, A, B) printf(S "\n", A, B) +#define DEBUGFUNC(F) DEBUGOUT(F); + #define DEBUGOUT(S) + #define DEBUGOUT1(S,A) + #define DEBUGOUT2(S,A,B) + #define DEBUGOUT3(S,A,B,C) + #define DEBUGOUT7(S,A,B,C,D,E,F,G) + +#define STATIC static +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#define CMD_MEM_WRT_INVALIDATE 0x0010 /* BIT_4 */ +#define PCI_COMMAND_REGISTER PCIR_COMMAND + +/* +** These typedefs are necessary due to the new +** shared code, they are native to Linux. +*/ +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8 ; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8 ; + +#include + +struct e1000_osdep +{ + uint32_t mem_bus_space_handle; + uint32_t io_bus_space_handle; + uint32_t flash_bus_space_handle; + /* these are currently unused; present for freebsd compatibility only */ + uint32_t mem_bus_space_tag; + uint32_t io_bus_space_tag; + uint32_t flash_bus_space_tag; + device_t dev; +}; + +typedef volatile uint32_t __attribute__((may_alias)) *__uint32_a_p_t; +typedef volatile uint16_t __attribute__((may_alias)) *__uint16_a_p_t; +typedef volatile uint8_t __attribute__((may_alias)) * __uint8_a_p_t; + +#ifdef __PPC__ +#include +static inline uint8_t __in_8(uint32_t base, uint32_t offset) +{ +__uint8_a_p_t a = (__uint8_a_p_t)(base+offset); +uint8_t rval; + __asm__ __volatile__( + "sync;\n" + "lbz%U1%X1 %0,%1;\n" + "twi 0,%0,0;\n" + "isync" : "=r" (rval) : "m"(*a)); + return rval; +} + +static inline void __out_8(uint32_t base, uint32_t offset, uint8_t val) +{ +__uint8_a_p_t a = (__uint8_a_p_t)(base+offset); + __asm__ __volatile__( + "stb%U0%X0 %1,%0; eieio" : "=m" (*a) : "r"(val) + ); +} + +static inline uint16_t __in_le16(uint32_t base, uint32_t offset) +{ +__uint16_a_p_t a = (__uint16_a_p_t)(base+offset); +uint16_t rval; + __asm__ __volatile__( + "sync;\n" + "lhbrx %0,0,%1;\n" + "twi 0,%0,0;\n" + "isync" : "=r" (rval) : "r"(a), "m"(*a)); + return rval; +} + +static inline void __out_le16(uint32_t base, uint32_t offset, uint16_t val) +{ +__uint16_a_p_t a = (__uint16_a_p_t)(base+offset); + __asm__ __volatile__( + "sync; sthbrx %1,0,%2" : "=m" (*a) : "r"(val), "r"(a) + ); +} + +static inline uint32_t __in_le32(uint32_t base, uint32_t offset) +{ +__uint32_a_p_t a = (__uint32_a_p_t)(base+offset); +uint32_t rval; + __asm__ __volatile__( + "sync;\n" + "lwbrx %0,0,%1;\n" + "twi 0,%0,0;\n" + "isync" : "=r" (rval) : "r"(a), "m"(*a)); + return rval; +} + +static inline void __out_le32(uint32_t base, uint32_t offset, uint32_t val) +{ +__uint32_a_p_t a = (__uint32_a_p_t)(base+offset); + __asm__ __volatile__( + "sync; stwbrx %1,0,%2" : "=m" (*a) : "r"(val), "r"(a) + ); +} + +#ifdef _IO_BASE +static inline void __outport_dword(uint32_t base, uint32_t off, uint32_t val) +{ + __out_le32(_IO_BASE+base+off,0,val); +} +#else +#error "_IO_BASE needs to be defined by BSP (bsp.h)" +#endif + +#elif defined(__i386__) +#include +static inline uint8_t __in_8(uint32_t base, uint32_t offset) +{ +__uint8_a_p_t a = (__uint8_a_p_t)(base+offset); + return *a; +} + +static inline void __out_8(uint32_t base, uint32_t offset, uint8_t val) +{ +__uint8_a_p_t a = (__uint8_a_p_t)(base+offset); + *a = val; +} + +static inline uint16_t __in_le16(uint32_t base, uint32_t offset) +{ +__uint16_a_p_t a = (__uint16_a_p_t)(base+offset); + return *a; +} + +static inline void __out_le16(uint32_t base, uint32_t offset, uint16_t val) +{ +__uint16_a_p_t a = (__uint16_a_p_t)(base+offset); + *a = val; +} + +static inline uint32_t __in_le32(uint32_t base, uint32_t offset) +{ +__uint32_a_p_t a = (__uint32_a_p_t)(base+offset); + return *a; +} + +static inline void __out_le32(uint32_t base, uint32_t offset, uint32_t val) +{ +__uint32_a_p_t a = (__uint32_a_p_t)(base+offset); + *a = val; +} + + +static inline void __outport_dword(uint32_t base, uint32_t off, uint32_t val) +{ + i386_outport_long( (base + off), val ); +} + +#else +#error "not ported to this CPU architecture yet" +#endif + +#ifdef NO_82542_SUPPORT +#define E1000_REGISTER(hw, reg) reg +#else +#define E1000_REGISTER(hw, reg) (((hw)->mac.type >= e1000_82543) \ + ? reg : e1000_translate_register_82542(reg)) +#endif + +#define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, E1000_STATUS) + +#define USE_EXPLICIT_BUSTAGS + +#ifdef USE_EXPLICIT_BUSTAGS /* Help compiler by specifying explicit bus tags */ + +/* Read from an absolute offset in the adapter's memory space */ +#define E1000_READ_OFFSET(hw, offset) \ + bus_space_read_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, offset) + +/* Write to an absolute offset in the adapter's memory space */ +#define E1000_WRITE_OFFSET(hw, offset, value) \ + bus_space_write_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, offset, value) + +/* Register READ/WRITE macros */ + +#define E1000_READ_REG(hw, reg) \ + bus_space_read_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg)) + +#define E1000_WRITE_REG(hw, reg, value) \ + bus_space_write_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg), value) + +#define E1000_READ_REG_ARRAY(hw, reg, index) \ + bus_space_read_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + ((index)<< 2)) + +#define E1000_WRITE_REG_ARRAY(hw, reg, index, value) \ + bus_space_write_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + ((index)<< 2), value) + +#define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY +#define E1000_WRITE_REG_ARRAY_DWORD E1000_WRITE_REG_ARRAY + +#define E1000_READ_REG_ARRAY_BYTE(hw, reg, index) \ + bus_space_read_1(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + index) + +#define E1000_WRITE_REG_ARRAY_BYTE(hw, reg, index, value) \ + bus_space_write_1(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + index, value) + +#define E1000_WRITE_REG_ARRAY_WORD(hw, reg, index, value) \ + bus_space_write_2(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + (index << 1), value) + +#define E1000_WRITE_REG_IO(hw, reg, value) do {\ + bus_space_write_4(bus_space_io, \ + ((struct e1000_osdep *)(hw)->back)->io_bus_space_handle, \ + (hw)->io_base, reg); \ + bus_space_write_4(bus_space_io, \ + ((struct e1000_osdep *)(hw)->back)->io_bus_space_handle, \ + (hw)->io_base + 4, value); } while (0) + +#define E1000_READ_FLASH_REG(hw, reg) \ + bus_space_read_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg) + +#define E1000_READ_FLASH_REG16(hw, reg) \ + bus_space_read_2(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg) + +#define E1000_WRITE_FLASH_REG(hw, reg, value) \ + bus_space_write_4(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg, value) + +#define E1000_WRITE_FLASH_REG16(hw, reg, value) \ + bus_space_write_2(bus_space_mem, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg, value) + +#else /* USE_EXPLICIT_BUSTAGS */ + +/* Read from an absolute offset in the adapter's memory space */ +#define E1000_READ_OFFSET(hw, offset) \ + bus_space_read_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, offset) + +/* Write to an absolute offset in the adapter's memory space */ +#define E1000_WRITE_OFFSET(hw, offset, value) \ + bus_space_write_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, offset, value) + +/* Register READ/WRITE macros */ + +#define E1000_READ_REG(hw, reg) \ + bus_space_read_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg)) + +#define E1000_WRITE_REG(hw, reg, value) \ + bus_space_write_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg), value) + +#define E1000_READ_REG_ARRAY(hw, reg, index) \ + bus_space_read_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + ((index)<< 2)) + +#define E1000_WRITE_REG_ARRAY(hw, reg, index, value) \ + bus_space_write_4(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + ((index)<< 2), value) + +#define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY +#define E1000_WRITE_REG_ARRAY_DWORD E1000_WRITE_REG_ARRAY + +#define E1000_READ_REG_ARRAY_BYTE(hw, reg, index) \ + bus_space_read_1(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + index) + +#define E1000_WRITE_REG_ARRAY_BYTE(hw, reg, index, value) \ + bus_space_write_1(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + index, value) + +#define E1000_WRITE_REG_ARRAY_WORD(hw, reg, index, value) \ + bus_space_write_2(((struct e1000_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REGISTER(hw, reg) + (index << 1), value) + +#define E1000_WRITE_REG_IO(hw, reg, value) do {\ + bus_space_write_4(((struct e1000_osdep *)(hw)->back)->io_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->io_bus_space_handle, \ + (hw)->io_base, reg); \ + bus_space_write_4(((struct e1000_osdep *)(hw)->back)->io_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->io_bus_space_handle, \ + (hw)->io_base + 4, value); } while (0) + +#define E1000_READ_FLASH_REG(hw, reg) \ + bus_space_read_4(((struct e1000_osdep *)(hw)->back)->flash_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg) + +#define E1000_READ_FLASH_REG16(hw, reg) \ + bus_space_read_2(((struct e1000_osdep *)(hw)->back)->flash_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg) + +#define E1000_WRITE_FLASH_REG(hw, reg, value) \ + bus_space_write_4(((struct e1000_osdep *)(hw)->back)->flash_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg, value) + +#define E1000_WRITE_FLASH_REG16(hw, reg, value) \ + bus_space_write_2(((struct e1000_osdep *)(hw)->back)->flash_bus_space_tag, \ + ((struct e1000_osdep *)(hw)->back)->flash_bus_space_handle, reg, value) +#endif /* USE_EXPLICIT_BUSTAGS */ + +#endif /* _FREEBSD_OS_H_ */ + diff --git a/bsd_eth_drivers/if_em/e1000_phy.c b/bsd_eth_drivers/if_em/e1000_phy.c new file mode 100644 index 0000000..26e791c --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_phy.c @@ -0,0 +1,2064 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_phy.c,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#include "e1000_api.h" +#include "e1000_phy.h" + +static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw); +static void e1000_release_phy(struct e1000_hw *hw); +static s32 e1000_acquire_phy(struct e1000_hw *hw); + +/* Cable length tables */ +static const +u16 e1000_m88_cable_length_table[] = + { 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED }; +#define M88E1000_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_m88_cable_length_table) / \ + sizeof(e1000_m88_cable_length_table[0])) + +static const +u16 e1000_igp_2_cable_length_table[] = + { 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, + 0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41, + 6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61, + 21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82, + 40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104, + 60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121, + 83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124, + 104, 109, 114, 118, 121, 124}; +#define IGP02E1000_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_igp_2_cable_length_table) / \ + sizeof(e1000_igp_2_cable_length_table[0])) + +/** + * e1000_check_reset_block_generic - Check if PHY reset is blocked + * @hw: pointer to the HW structure + * + * Read the PHY management control register and check whether a PHY reset + * is blocked. If a reset is not blocked return E1000_SUCCESS, otherwise + * return E1000_BLK_PHY_RESET (12). + **/ +s32 +e1000_check_reset_block_generic(struct e1000_hw *hw) +{ + u32 manc; + + DEBUGFUNC("e1000_check_reset_block"); + + manc = E1000_READ_REG(hw, E1000_MANC); + + return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? + E1000_BLK_PHY_RESET : E1000_SUCCESS; +} + +/** + * e1000_get_phy_id - Retrieve the PHY ID and revision + * @hw: pointer to the HW structure + * + * Reads the PHY registers and stores the PHY ID and possibly the PHY + * revision in the hardware structure. + **/ +s32 +e1000_get_phy_id(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_id; + + DEBUGFUNC("e1000_get_phy_id"); + + ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id); + if (ret_val) + goto out; + + phy->id = (u32)(phy_id << 16); + usec_delay(20); + ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id); + if (ret_val) + goto out; + + phy->id |= (u32)(phy_id & PHY_REVISION_MASK); + phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK); + +out: + return ret_val; +} + +/** + * e1000_phy_reset_dsp_generic - Reset PHY DSP + * @hw: pointer to the HW structure + * + * Reset the digital signal processor. + **/ +s32 +e1000_phy_reset_dsp_generic(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_phy_reset_dsp_generic"); + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0); + +out: + return ret_val; +} + +/** + * e1000_read_phy_reg_mdic - Read MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the MDI control regsiter in the PHY at offset and stores the + * information read to data. + **/ +static s32 +e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, mdic = 0; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_phy_reg_mdic"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + ret_val = -E1000_ERR_PARAM; + goto out; + } + + /* Set up Op-code, Phy Address, and register offset in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = ((offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + E1000_WRITE_REG(hw, E1000_MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < 64; i++) { + usec_delay(50); + mdic = E1000_READ_REG(hw, E1000_MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Read did not complete\n"); + ret_val = -E1000_ERR_PHY; + goto out; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + ret_val = -E1000_ERR_PHY; + goto out; + } + *data = (u16) mdic; + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_mdic - Write MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write to register at offset + * + * Writes data to MDI control register in the PHY at offset. + **/ +static s32 +e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, mdic = 0; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_phy_reg_mdic"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + ret_val = -E1000_ERR_PARAM; + goto out; + } + + /* Set up Op-code, Phy Address, and register offset in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = (((u32)data) | + (offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + E1000_WRITE_REG(hw, E1000_MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < E1000_GEN_POLL_TIMEOUT; i++) { + usec_delay(5); + mdic = E1000_READ_REG(hw, E1000_MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_read_phy_reg_m88 - Read m88 PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore, if necessary, then reads the PHY register at offset + * and storing the retrieved information in data. Release any acquired + * semaphores before exiting. + **/ +s32 +e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_phy_reg_m88"); + + ret_val = e1000_acquire_phy(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + + e1000_release_phy(hw); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_m88 - Write m88 PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 +e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_write_phy_reg_m88"); + + ret_val = e1000_acquire_phy(hw); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + + e1000_release_phy(hw); + +out: + return ret_val; +} + +/** + * e1000_read_phy_reg_igp - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore, if necessary, then reads the PHY register at offset + * and storing the retrieved information in data. Release any acquired + * semaphores before exiting. + **/ +s32 +e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_phy_reg_igp"); + + ret_val = e1000_acquire_phy(hw); + if (ret_val) + goto out; + + if (offset > MAX_PHY_MULTI_PAGE_REG) { + ret_val = e1000_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); + if (ret_val) { + e1000_release_phy(hw); + goto out; + } + } + + ret_val = e1000_read_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + + e1000_release_phy(hw); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_igp - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 +e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_write_phy_reg_igp"); + + ret_val = e1000_acquire_phy(hw); + if (ret_val) + goto out; + + if (offset > MAX_PHY_MULTI_PAGE_REG) { + ret_val = e1000_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); + if (ret_val) { + e1000_release_phy(hw); + goto out; + } + } + + ret_val = e1000_write_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + + e1000_release_phy(hw); + +out: + return ret_val; +} + +/** + * e1000_read_kmrn_reg_generic - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore, if necessary. Then reads the PHY register at offset + * using the kumeran interface. The information retrieved is stored in data. + * Release any acquired semaphores before exiting. + **/ +s32 +e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data) +{ + u32 kmrnctrlsta; + s32 ret_val; + + DEBUGFUNC("e1000_read_kmrn_reg_generic"); + + ret_val = e1000_acquire_phy(hw); + if (ret_val) + goto out; + + kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & + E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN; + E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta); + + usec_delay(2); + + kmrnctrlsta = E1000_READ_REG(hw, E1000_KMRNCTRLSTA); + *data = (u16)kmrnctrlsta; + + e1000_release_phy(hw); + +out: + return ret_val; +} + +/** + * e1000_write_kmrn_reg_generic - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore, if necessary. Then write the data to PHY register + * at the offset using the kumeran interface. Release any acquired semaphores + * before exiting. + **/ +s32 +e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data) +{ + u32 kmrnctrlsta; + s32 ret_val; + + DEBUGFUNC("e1000_write_kmrn_reg_generic"); + + ret_val = e1000_acquire_phy(hw); + if (ret_val) + goto out; + + kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & + E1000_KMRNCTRLSTA_OFFSET) | data; + E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta); + + usec_delay(2); + e1000_release_phy(hw); + +out: + return ret_val; +} + +/** + * e1000_copper_link_setup_m88 - Setup m88 PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up MDI/MDI-X and polarity for m88 PHY's. If necessary, transmit clock + * and downshift values are set also. + **/ +s32 +e1000_copper_link_setup_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_setup_m88"); + + if (phy->reset_disable) { + ret_val = E1000_SUCCESS; + goto out; + } + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + goto out; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (phy->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if (phy->disable_polarity_correction == 1) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + goto out; + + if (phy->revision < E1000_REVISION_4) { + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = e1000_read_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if (ret_val) + goto out; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((phy->revision == E1000_REVISION_2) && + (phy->id == M88E1111_I_PHY_ID)) { + /* 82573L PHY - set the downshift counter to 5x. */ + phy_data &= ~M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK; + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + } + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + goto out; + } + + /* Commit the changes. */ + ret_val = e1000_phy_commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_copper_link_setup_igp - Setup igp PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up LPLU, MDI/MDI-X, polarity, Smartspeed and Master/Slave config for + * igp PHY's. + **/ +s32 +e1000_copper_link_setup_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_copper_link_setup_igp"); + + if (phy->reset_disable) { + ret_val = E1000_SUCCESS; + goto out; + } + + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) { + DEBUGOUT("Error resetting the PHY.\n"); + goto out; + } + + /* Wait 15ms for MAC to configure PHY from NVM settings. */ + msec_delay(15); + + /* The NVM settings will configure LPLU in D3 for + * non-IGP1 PHYs. */ + if (phy->type == e1000_phy_igp) { + /* disable lplu d3 during driver init */ + ret_val = e1000_set_d3_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D3\n"); + goto out; + } + } + + /* disable lplu d0 during driver init */ + ret_val = e1000_set_d0_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D0\n"); + goto out; + } + /* Configure mdi-mdix settings */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (phy->mdix) { + case 1: + data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, data); + if (ret_val) + goto out; + + /* set auto-master slave resolution settings */ + if (hw->mac.autoneg) { + /* when autonegotiation advertisement is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. */ + if (phy->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + + /* Set auto Master/Slave resolution process */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &data); + if (ret_val) + goto out; + + data &= ~CR_1000T_MS_ENABLE; + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, data); + if (ret_val) + goto out; + } + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &data); + if (ret_val) + goto out; + + /* load defaults for future use */ + phy->original_ms_type = (data & CR_1000T_MS_ENABLE) ? + ((data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : + e1000_ms_auto; + + switch (phy->ms_type) { + case e1000_ms_force_master: + data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + data |= CR_1000T_MS_ENABLE; + data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + data &= ~CR_1000T_MS_ENABLE; + default: + break; + } + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, data); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_copper_link_autoneg - Setup/Enable autoneg for copper link + * @hw: pointer to the HW structure + * + * Performs initial bounds checking on autoneg advertisement parameter, then + * configure to advertise the full capability. Setup the PHY to autoneg + * and restart the negotiation process between the link partner. If + * wait_for_link, then wait for autoneg to complete before exiting. + **/ +s32 +e1000_copper_link_autoneg(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_copper_link_autoneg"); + + /* Perform some bounds checking on the autoneg advertisement + * parameter. + */ + phy->autoneg_advertised &= phy->autoneg_mask; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (phy->autoneg_advertised == 0) + phy->autoneg_advertised = phy->autoneg_mask; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + goto out; + } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = e1000_read_phy_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + goto out; + + phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = e1000_write_phy_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + goto out; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if (phy->wait_for_link) { + ret_val = e1000_wait_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error while waiting for " + "autoneg to complete\n"); + goto out; + } + } + + hw->mac.get_link_status = TRUE; + +out: + return ret_val; +} + +/** + * e1000_phy_setup_autoneg - Configure PHY for auto-negotiation + * @hw: pointer to the HW structure + * + * Reads the MII auto-neg advertisement register and/or the 1000T control + * register and if the PHY is already setup for auto-negotiation, then + * return successful. Otherwise, setup advertisement and flow control to + * the appropriate values for the wanted auto-negotiation. + **/ +s32 +e1000_phy_setup_autoneg(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg = 0; + + DEBUGFUNC("e1000_phy_setup_autoneg"); + + phy->autoneg_advertised &= phy->autoneg_mask; + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if (ret_val) + goto out; + + if (phy->autoneg_mask & ADVERTISE_1000_FULL) { + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = e1000_read_phy_reg(hw, + PHY_1000T_CTRL, + &mii_1000t_ctrl_reg); + if (ret_val) + goto out; + } + + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | + NWAY_AR_100TX_HD_CAPS | + NWAY_AR_10T_FD_CAPS | + NWAY_AR_10T_HD_CAPS); + mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); + + DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } + + /* Do we want to advertise 10 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } + + /* Do we want to advertise 100 Mb Half Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } + + /* Do we want to advertise 100 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } + + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if (phy->autoneg_advertised & ADVERTISE_1000_HALF) { + DEBUGOUT("Advertise 1000mb Half duplex request denied!\n"); + } + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + } + + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto- + * negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (hw->mac.fc) { + case e1000_fc_none: + /* Flow control (RX & TX) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_rx_pause: + /* RX Flow control is enabled, and TX Flow control is + * disabled, by a software over-ride. + */ + /* Since there really isn't a way to advertise that we are + * capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + * hw's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_tx_pause: + /* TX Flow control is enabled, and RX Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case e1000_fc_full: + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if (ret_val) + goto out; + + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + if (phy->autoneg_mask & ADVERTISE_1000_FULL) { + ret_val = e1000_write_phy_reg(hw, + PHY_1000T_CTRL, + mii_1000t_ctrl_reg); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_setup_copper_link_generic - Configure copper link settings + * @hw: pointer to the HW structure + * + * Calls the appropriate function to configure the link for auto-neg or forced + * speed and duplex. Then we check for link, once link is established calls + * to configure collision distance and flow control are called. If link is + * not established, we return -E1000_ERR_PHY (-2). + **/ +s32 +e1000_setup_copper_link_generic(struct e1000_hw *hw) +{ + s32 ret_val; + boolean_t link; + + DEBUGFUNC("e1000_setup_copper_link_generic"); + + if (hw->mac.autoneg) { + /* Setup autoneg and flow control advertisement and perform + * autonegotiation. */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + goto out; + } else { + /* PHY will be set to 10H, 10F, 100H or 100F + * depending on user settings. */ + DEBUGOUT("Forcing Speed and Duplex\n"); + ret_val = e1000_phy_force_speed_duplex(hw); + if (ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + goto out; + } + } + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + ret_val = e1000_phy_has_link_generic(hw, + COPPER_LINK_UP_LIMIT, + 10, + &link); + if (ret_val) + goto out; + + if (link) { + DEBUGOUT("Valid link established!!!\n"); + e1000_config_collision_dist_generic(hw); + ret_val = e1000_config_fc_after_link_up_generic(hw); + } else { + DEBUGOUT("Unable to establish link!!!\n"); + } + +out: + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_igp - Force speed/duplex for igp PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. Clears the + * auto-crossover to force MDI manually. Waits for link and returns + * successful if link up is successful, else -E1000_ERR_PHY (-2). + **/ +s32 +e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + boolean_t link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_igp"); + + ret_val = e1000_read_phy_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + goto out; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = e1000_write_phy_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + goto out; + + /* Clear Auto-Crossover to force MDI manually. IGP requires MDI + * forced whenever speed and duplex are forced. + */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + goto out; + + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + goto out; + + DEBUGOUT1("IGP PSCR: %X\n", phy_data); + + usec_delay(1); + + if (phy->wait_for_link) { + DEBUGOUT("Waiting for forced speed/duplex link on IGP phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + + if (!link) { + DEBUGOUT("Link taking longer than expected.\n"); + } + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_m88 - Force speed/duplex for m88 PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. Clears the + * auto-crossover to force MDI manually. Resets the PHY to commit the + * changes. If time expires while waiting for link up, we reset the DSP. + * After reset, TX_CLK and CRS on TX must be set. Return successful upon + * successful completion, else return corresponding error code. + **/ +s32 +e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + boolean_t link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_m88"); + + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + * forced whenever speed and duplex are forced. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + goto out; + + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + goto out; + + DEBUGOUT1("M88E1000 PSCR: %X\n", phy_data); + + ret_val = e1000_read_phy_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + goto out; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + /* Reset the phy to commit changes. */ + phy_data |= MII_CR_RESET; + + ret_val = e1000_write_phy_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + goto out; + + usec_delay(1); + + if (phy->wait_for_link) { + DEBUGOUT("Waiting for forced speed/duplex link on M88 phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + + if (!link) { + /* We didn't get link. + * Reset the DSP and cross our fingers. + */ + ret_val = e1000_write_phy_reg(hw, + M88E1000_PHY_PAGE_SELECT, + 0x001d); + if (ret_val) + goto out; + ret_val = e1000_phy_reset_dsp_generic(hw); + if (ret_val) + goto out; + } + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, + PHY_FORCE_LIMIT, + 100000, + &link); + if (ret_val) + goto out; + } + + ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + goto out; + + /* Resetting the phy means we need to re-force TX_CLK in the + * Extended PHY Specific Control Register to 25MHz clock from + * the reset value of 2.5MHz. + */ + phy_data |= M88E1000_EPSCR_TX_CLK_25; + ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + goto out; + + /* In addition, we must re-enable CRS on Tx for both half and full + * duplex. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + goto out; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + +out: + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex + * @hw: pointer to the HW structure + * @phy_ctrl: pointer to current value of PHY_CONTROL + * + * Forces speed and duplex on the PHY by doing the following: disable flow + * control, force speed/duplex on the MAC, disable auto speed detection, + * disable auto-negotiation, configure duplex, configure speed, configure + * the collision distance, write configuration to CTRL register. The + * caller must write to the PHY_CONTROL register for these settings to + * take affect. + **/ +void +e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 ctrl; + + DEBUGFUNC("e1000_phy_force_speed_duplex_setup"); + + /* Turn off flow control when forcing speed/duplex */ + mac->fc = e1000_fc_none; + + /* Force speed/duplex on the mac */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~E1000_CTRL_SPD_SEL; + + /* Disable Auto Speed Detection */ + ctrl &= ~E1000_CTRL_ASDE; + + /* Disable autoneg on the phy */ + *phy_ctrl &= ~MII_CR_AUTO_NEG_EN; + + /* Forcing Full or Half Duplex? */ + if (mac->forced_speed_duplex & E1000_ALL_HALF_DUPLEX) { + ctrl &= ~E1000_CTRL_FD; + *phy_ctrl &= ~MII_CR_FULL_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } else { + ctrl |= E1000_CTRL_FD; + *phy_ctrl |= MII_CR_FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } + + /* Forcing 10mb or 100mb? */ + if (mac->forced_speed_duplex & E1000_ALL_100_SPEED) { + ctrl |= E1000_CTRL_SPD_100; + *phy_ctrl |= MII_CR_SPEED_100; + *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10); + DEBUGOUT("Forcing 100mb\n"); + } else { + ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + *phy_ctrl |= MII_CR_SPEED_10; + *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + DEBUGOUT("Forcing 10mb\n"); + } + + e1000_config_collision_dist_generic(hw); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); +} + +/** + * e1000_set_d3_lplu_state_generic - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. + **/ +s32 +e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, boolean_t active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_set_d3_lplu_state_generic"); + + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); + if (ret_val) + goto out; + + if (!active) { + data &= ~IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + goto out; + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || + (phy->autoneg_advertised == E1000_ALL_10_SPEED)) { + data |= IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + goto out; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + } + +out: + return ret_val; +} + +/** + * e1000_check_downshift_generic - Checks whether a downshift in speed occured + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns 1 + * + * A downshift is detected by querying the PHY link health. + **/ +s32 +e1000_check_downshift_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, offset, mask; + + DEBUGFUNC("e1000_check_downshift_generic"); + + switch (phy->type) { + case e1000_phy_m88: + case e1000_phy_gg82563: + offset = M88E1000_PHY_SPEC_STATUS; + mask = M88E1000_PSSR_DOWNSHIFT; + break; + case e1000_phy_igp_2: + case e1000_phy_igp: + case e1000_phy_igp_3: + offset = IGP01E1000_PHY_LINK_HEALTH; + mask = IGP01E1000_PLHR_SS_DOWNGRADE; + break; + default: + /* speed downshift not supported */ + phy->speed_downgraded = FALSE; + ret_val = E1000_SUCCESS; + goto out; + } + + ret_val = e1000_read_phy_reg(hw, offset, &phy_data); + + if (!ret_val) + phy->speed_downgraded = (phy_data & mask) ? TRUE : FALSE; + +out: + return ret_val; +} + +/** + * e1000_check_polarity_m88 - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY specific status register. + **/ +s32 +e1000_check_polarity_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_check_polarity_m88"); + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &data); + + if (!ret_val) + phy->cable_polarity = (data & M88E1000_PSSR_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal; + + return ret_val; +} + +/** + * e1000_check_polarity_igp - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY port status register, and the + * current speed (since there is no polarity at 100Mbps). + **/ +s32 +e1000_check_polarity_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data, offset, mask; + + DEBUGFUNC("e1000_check_polarity_igp"); + + /* Polarity is determined based on the speed of + * our connection. */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); + if (ret_val) + goto out; + + if ((data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + offset = IGP01E1000_PHY_PCS_INIT_REG; + mask = IGP01E1000_PHY_POLARITY_MASK; + } else { + /* This really only applies to 10Mbps since + * there is no polarity for 100Mbps (always 0). + */ + offset = IGP01E1000_PHY_PORT_STATUS; + mask = IGP01E1000_PSSR_POLARITY_REVERSED; + } + + ret_val = e1000_read_phy_reg(hw, offset, &data); + + if (!ret_val) + phy->cable_polarity = (data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal; + +out: + return ret_val; +} + +/** + * e1000_wait_autoneg_generic - Wait for auto-neg compeletion + * @hw: pointer to the HW structure + * + * Waits for auto-negotiation to complete or for the auto-negotiation time + * limit to expire, which ever happens first. + **/ +s32 +e1000_wait_autoneg_generic(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 i, phy_status; + + DEBUGFUNC("e1000_wait_autoneg_generic"); + + /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ + for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) { + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + if (phy_status & MII_SR_AUTONEG_COMPLETE) + break; + msec_delay(100); + } + + /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation + * has completed. + */ + return ret_val; +} + +/** + * e1000_phy_has_link_generic - Polls PHY for link + * @hw: pointer to the HW structure + * @iterations: number of times to poll for link + * @usec_interval: delay between polling attempts + * @success: pointer to whether polling was successful or not + * + * Polls the PHY status register for link, 'iterations' number of times. + **/ +s32 +e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, + u32 usec_interval, boolean_t *success) +{ + s32 ret_val = E1000_SUCCESS; + u16 i, phy_status; + + DEBUGFUNC("e1000_phy_has_link_generic"); + + for (i = 0; i < iterations; i++) { + /* Some PHYs require the PHY_STATUS register to be read + * twice due to the link bit being sticky. No harm doing + * it across the board. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + if (phy_status & MII_SR_LINK_STATUS) + break; + if (usec_interval >= 1000) + msec_delay_irq(usec_interval/1000); + else + usec_delay(usec_interval); + } + + *success = (i < iterations) ? TRUE : FALSE; + + return ret_val; +} + +/** + * e1000_get_cable_length_m88 - Determine cable length for m88 PHY + * @hw: pointer to the HW structure + * + * Reads the PHY specific status register to retrieve the cable length + * information. The cable length is determined by averaging the minimum and + * maximum values to get the "average" cable length. The m88 PHY has four + * possible cable length values, which are: + * Register Value Cable Length + * 0 < 50 meters + * 1 50 - 80 meters + * 2 80 - 110 meters + * 3 110 - 140 meters + * 4 > 140 meters + **/ +s32 +e1000_get_cable_length_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, index; + + DEBUGFUNC("e1000_get_cable_length_m88"); + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + goto out; + + index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT; + phy->min_cable_length = e1000_m88_cable_length_table[index]; + phy->max_cable_length = e1000_m88_cable_length_table[index+1]; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + +out: + return ret_val; +} + +/** + * e1000_get_cable_length_igp_2 - Determine cable length for igp2 PHY + * @hw: pointer to the HW structure + * + * The automatic gain control (agc) normalizes the amplitude of the + * received signal, adjusting for the attenuation produced by the + * cable. By reading the AGC registers, which reperesent the + * cobination of course and fine gain value, the value can be put + * into a lookup table to obtain the approximate cable length + * for each channel. + **/ +s32 +e1000_get_cable_length_igp_2(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, i, agc_value = 0; + u16 cur_agc_index, max_agc_index = 0; + u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1; + u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = + {IGP02E1000_PHY_AGC_A, + IGP02E1000_PHY_AGC_B, + IGP02E1000_PHY_AGC_C, + IGP02E1000_PHY_AGC_D}; + + DEBUGFUNC("e1000_get_cable_length_igp_2"); + + /* Read the AGC registers for all channels */ + for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) { + ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); + if (ret_val) + goto out; + + /* Getting bits 15:9, which represent the combination of + * course and fine gain values. The result is a number + * that can be put into the lookup table to obtain the + * approximate cable length. */ + cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & + IGP02E1000_AGC_LENGTH_MASK; + + /* Array index bound check. */ + if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) || + (cur_agc_index == 0)) { + ret_val = -E1000_ERR_PHY; + goto out; + } + + /* Remove min & max AGC values from calculation. */ + if (e1000_igp_2_cable_length_table[min_agc_index] > + e1000_igp_2_cable_length_table[cur_agc_index]) + min_agc_index = cur_agc_index; + if (e1000_igp_2_cable_length_table[max_agc_index] < + e1000_igp_2_cable_length_table[cur_agc_index]) + max_agc_index = cur_agc_index; + + agc_value += e1000_igp_2_cable_length_table[cur_agc_index]; + } + + agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] + + e1000_igp_2_cable_length_table[max_agc_index]); + agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2); + + /* Calculate cable length with the error range of +/- 10 meters. */ + phy->min_cable_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ? + (agc_value - IGP02E1000_AGC_RANGE) : 0; + phy->max_cable_length = agc_value + IGP02E1000_AGC_RANGE; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + +out: + return ret_val; +} + +/** + * e1000_get_phy_info_m88 - Retrieve PHY information + * @hw: pointer to the HW structure + * + * Valid for only copper links. Read the PHY status register (sticky read) + * to verify that link is up. Read the PHY special control register to + * determine the polarity and 10base-T extended distance. Read the PHY + * special status register to determine MDI/MDIx and current speed. If + * speed is 1000, then determine cable length, local and remote receiver. + **/ +s32 +e1000_get_phy_info_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + boolean_t link; + + DEBUGFUNC("e1000_get_phy_info_m88"); + + if (hw->media_type != e1000_media_type_copper) { + DEBUGOUT("Phy info is only valid for copper media\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + goto out; + + phy->polarity_correction = (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) + ? TRUE + : FALSE; + + ret_val = e1000_check_polarity_m88(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + goto out; + + phy->is_mdix = (phy_data & M88E1000_PSSR_MDIX) ? TRUE : FALSE; + + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + ret_val = e1000_get_cable_length(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + goto out; + + phy->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + /* Set values to "undefined" */ + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + +out: + return ret_val; +} + +/** + * e1000_get_phy_info_igp - Retrieve igp PHY information + * @hw: pointer to the HW structure + * + * Read PHY status to determine if link is up. If link is up, then + * set/determine 10base-T extended distance and polarity correction. Read + * PHY port status to determine MDI/MDIx and speed. Based on the speed, + * determine on the cable length, local and remote receiver. + **/ +s32 +e1000_get_phy_info_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + boolean_t link; + + DEBUGFUNC("e1000_get_phy_info_igp"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + goto out; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + phy->polarity_correction = TRUE; + + ret_val = e1000_check_polarity_igp(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); + if (ret_val) + goto out; + + phy->is_mdix = (data & IGP01E1000_PSSR_MDIX) ? TRUE : FALSE; + + if ((data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + ret_val = e1000_get_cable_length(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &data); + if (ret_val) + goto out; + + phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + +out: + return ret_val; +} + +/** + * e1000_phy_sw_reset_generic - PHY software reset + * @hw: pointer to the HW structure + * + * Does a software reset of the PHY by reading the PHY control register and + * setting/write the control register reset bit to the PHY. + **/ +s32 +e1000_phy_sw_reset_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_phy_sw_reset_generic"); + + ret_val = e1000_read_phy_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + goto out; + + phy_ctrl |= MII_CR_RESET; + ret_val = e1000_write_phy_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + goto out; + + usec_delay(1); + +out: + return ret_val; +} + +/** + * e1000_phy_hw_reset_generic - PHY hardware reset + * @hw: pointer to the HW structure + * + * Verify the reset block is not blocking us from resetting. Acquire + * semaphore (if necessary) and read/set/write the device control reset + * bit in the PHY. Wait the appropriate delay time for the device to + * reset and relase the semaphore (if necessary). + **/ +s32 +e1000_phy_hw_reset_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u32 ctrl; + + DEBUGFUNC("e1000_phy_hw_reset_generic"); + + ret_val = e1000_check_reset_block(hw); + if (ret_val) { + ret_val = E1000_SUCCESS; + goto out; + } + + ret_val = e1000_acquire_phy(hw); + if (ret_val) + goto out; + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + + usec_delay(phy->reset_delay_us); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + usec_delay(150); + + e1000_release_phy(hw); + + ret_val = e1000_get_phy_cfg_done(hw); + +out: + return ret_val; +} + +/** + * e1000_get_cfg_done_generic - Generic configuration done + * @hw: pointer to the HW structure + * + * Generic function to wait 10 milli-seconds for configuration to complete + * and return success. + **/ +s32 +e1000_get_cfg_done_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_get_cfg_done_generic"); + + msec_delay_irq(10); + + return E1000_SUCCESS; +} + +/* Internal function pointers */ + +/** + * e1000_get_phy_cfg_done - Generic PHY configuration done + * @hw: pointer to the HW structure + * + * Return success if silicon family did not implement a family specific + * get_cfg_done function. + **/ +s32 +e1000_get_phy_cfg_done(struct e1000_hw *hw) +{ + if (hw->func.get_cfg_done != NULL) + return hw->func.get_cfg_done(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_release_phy - Generic release PHY + * @hw: pointer to the HW structure + * + * Return if silicon family does not require a semaphore when accessing the + * PHY. + **/ +void +e1000_release_phy(struct e1000_hw *hw) +{ + if (hw->func.release_phy != NULL) + hw->func.release_phy(hw); +} + +/** + * e1000_acquire_phy - Generic acquire PHY + * @hw: pointer to the HW structure + * + * Return success if silicon family does not require a semaphore when + * accessing the PHY. + **/ +s32 +e1000_acquire_phy(struct e1000_hw *hw) +{ + if (hw->func.acquire_phy != NULL) + return hw->func.acquire_phy(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_phy_force_speed_duplex - Generic force PHY speed/duplex + * @hw: pointer to the HW structure + * + * When the silicon family has not implemented a forced speed/duplex + * function for the PHY, simply return E1000_SUCCESS. + **/ +s32 +e1000_phy_force_speed_duplex(struct e1000_hw *hw) +{ + if (hw->func.force_speed_duplex != NULL) + return hw->func.force_speed_duplex(hw); + else + return E1000_SUCCESS; +} + +/** + * e1000_phy_init_script_igp3 - Inits the IGP3 PHY + * @hw: pointer to the HW structure + * + * Initializes a Intel Gigabit PHY3 when an EEPROM is not present. + **/ +s32 +e1000_phy_init_script_igp3(struct e1000_hw *hw) +{ + DEBUGOUT("Running IGP 3 PHY init script\n"); + + /* PHY init IGP 3 */ + /* Enable rise/fall, 10-mode work in class-A */ + e1000_write_phy_reg(hw, 0x2F5B, 0x9018); + /* Remove all caps from Replica path filter */ + e1000_write_phy_reg(hw, 0x2F52, 0x0000); + /* Bias trimming for ADC, AFE and Driver (Default) */ + e1000_write_phy_reg(hw, 0x2FB1, 0x8B24); + /* Increase Hybrid poly bias */ + e1000_write_phy_reg(hw, 0x2FB2, 0xF8F0); + /* Add 4% to TX amplitude in Giga mode */ + e1000_write_phy_reg(hw, 0x2010, 0x10B0); + /* Disable trimming (TTT) */ + e1000_write_phy_reg(hw, 0x2011, 0x0000); + /* Poly DC correction to 94.6% + 2% for all channels */ + e1000_write_phy_reg(hw, 0x20DD, 0x249A); + /* ABS DC correction to 95.9% */ + e1000_write_phy_reg(hw, 0x20DE, 0x00D3); + /* BG temp curve trim */ + e1000_write_phy_reg(hw, 0x28B4, 0x04CE); + /* Increasing ADC OPAMP stage 1 currents to max */ + e1000_write_phy_reg(hw, 0x2F70, 0x29E4); + /* Force 1000 ( required for enabling PHY regs configuration) */ + e1000_write_phy_reg(hw, 0x0000, 0x0140); + /* Set upd_freq to 6 */ + e1000_write_phy_reg(hw, 0x1F30, 0x1606); + /* Disable NPDFE */ + e1000_write_phy_reg(hw, 0x1F31, 0xB814); + /* Disable adaptive fixed FFE (Default) */ + e1000_write_phy_reg(hw, 0x1F35, 0x002A); + /* Enable FFE hysteresis */ + e1000_write_phy_reg(hw, 0x1F3E, 0x0067); + /* Fixed FFE for short cable lengths */ + e1000_write_phy_reg(hw, 0x1F54, 0x0065); + /* Fixed FFE for medium cable lengths */ + e1000_write_phy_reg(hw, 0x1F55, 0x002A); + /* Fixed FFE for long cable lengths */ + e1000_write_phy_reg(hw, 0x1F56, 0x002A); + /* Enable Adaptive Clip Threshold */ + e1000_write_phy_reg(hw, 0x1F72, 0x3FB0); + /* AHT reset limit to 1 */ + e1000_write_phy_reg(hw, 0x1F76, 0xC0FF); + /* Set AHT master delay to 127 msec */ + e1000_write_phy_reg(hw, 0x1F77, 0x1DEC); + /* Set scan bits for AHT */ + e1000_write_phy_reg(hw, 0x1F78, 0xF9EF); + /* Set AHT Preset bits */ + e1000_write_phy_reg(hw, 0x1F79, 0x0210); + /* Change integ_factor of channel A to 3 */ + e1000_write_phy_reg(hw, 0x1895, 0x0003); + /* Change prop_factor of channels BCD to 8 */ + e1000_write_phy_reg(hw, 0x1796, 0x0008); + /* Change cg_icount + enable integbp for channels BCD */ + e1000_write_phy_reg(hw, 0x1798, 0xD008); + /* Change cg_icount + enable integbp + change prop_factor_master + * to 8 for channel A + */ + e1000_write_phy_reg(hw, 0x1898, 0xD918); + /* Disable AHT in Slave mode on channel A */ + e1000_write_phy_reg(hw, 0x187A, 0x0800); + /* Enable LPLU and disable AN to 1000 in non-D0a states, + * Enable SPD+B2B + */ + e1000_write_phy_reg(hw, 0x0019, 0x008D); + /* Enable restart AN on an1000_dis change */ + e1000_write_phy_reg(hw, 0x001B, 0x2080); + /* Enable wh_fifo read clock in 10/100 modes */ + e1000_write_phy_reg(hw, 0x0014, 0x0045); + /* Restart AN, Speed selection is 1000 */ + e1000_write_phy_reg(hw, 0x0000, 0x1340); + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_type_from_id - Get PHY type from id + * @phy_id: phy_id read from the phy + * + * Returns the phy type from the id. + **/ +e1000_phy_type +e1000_get_phy_type_from_id(u32 phy_id) +{ + e1000_phy_type phy_type = e1000_phy_unknown; + + switch (phy_id) { + case M88E1000_I_PHY_ID: + case M88E1000_E_PHY_ID: + case M88E1111_I_PHY_ID: + case M88E1011_I_PHY_ID: + phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ + phy_type = e1000_phy_igp_2; + break; + case GG82563_E_PHY_ID: + phy_type = e1000_phy_gg82563; + break; + case IGP03E1000_E_PHY_ID: + phy_type = e1000_phy_igp_3; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + phy_type = e1000_phy_ife; + break; + default: + phy_type = e1000_phy_unknown; + break; + } + return phy_type; +} + + diff --git a/bsd_eth_drivers/if_em/e1000_phy.h b/bsd_eth_drivers/if_em/e1000_phy.h new file mode 100644 index 0000000..5aa718a --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_phy.h @@ -0,0 +1,177 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_phy.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_PHY_H_ +#define _E1000_PHY_H_ + +typedef enum { + e1000_ms_hw_default = 0, + e1000_ms_force_master, + e1000_ms_force_slave, + e1000_ms_auto +} e1000_ms_type; + +typedef enum { + e1000_smart_speed_default = 0, + e1000_smart_speed_on, + e1000_smart_speed_off +} e1000_smart_speed; + +s32 e1000_check_downshift_generic(struct e1000_hw *hw); +s32 e1000_check_polarity_m88(struct e1000_hw *hw); +s32 e1000_check_polarity_igp(struct e1000_hw *hw); +s32 e1000_check_reset_block_generic(struct e1000_hw *hw); +s32 e1000_copper_link_autoneg(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw); +s32 e1000_copper_link_setup_igp(struct e1000_hw *hw); +s32 e1000_copper_link_setup_m88(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw); +s32 e1000_get_cable_length_m88(struct e1000_hw *hw); +s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw); +s32 e1000_get_cfg_done_generic(struct e1000_hw *hw); +s32 e1000_get_phy_id(struct e1000_hw *hw); +s32 e1000_get_phy_info_igp(struct e1000_hw *hw); +s32 e1000_get_phy_info_m88(struct e1000_hw *hw); +s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw); +void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl); +s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw); +s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw); +s32 e1000_phy_setup_autoneg(struct e1000_hw *hw); +s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, boolean_t active); +s32 e1000_setup_copper_link_generic(struct e1000_hw *hw); +s32 e1000_wait_autoneg_generic(struct e1000_hw *hw); +s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_phy_reset_dsp(struct e1000_hw *hw); +s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, + u32 usec_interval, boolean_t *success); +s32 e1000_phy_init_script_igp3(struct e1000_hw *hw); +e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id); +#define E1000_MAX_PHY_ADDR 4 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* Status */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* Control */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health */ +#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO */ +#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 /* Power Management */ +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* Page Select */ +#define IGP4_PHY_PAGE_SELECT 22 /* Page Select for IGP 4 */ +#define IGP_PAGE_SHIFT 5 +#define PHY_REG_MASK 0x1F + +#define IGP4_WUC_PAGE 800 +#define IGP4_WUC_ADDRESS_OPCODE 0x11 +#define IGP4_WUC_DATA_OPCODE 0x12 +#define IGP4_WUC_ENABLE_PAGE 769 +#define IGP4_WUC_ENABLE_REG 17 +#define IGP4_WUC_ENABLE_BIT (1 << 2) +#define IGP4_WUC_HOST_WU_BIT (1 << 4) + +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_POLARITY_MASK 0x0078 + +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */ + +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 + +#define IGP01E1000_GMII_FLEX_SPD 0x0010 /* Enable flexible speed + * on link-up */ +#define IGP01E1000_GMII_SPD 0x0020 /* Enable SPD */ + +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* For D0a states */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* For all other states */ + +#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 + +#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002 +#define IGP01E1000_PSSR_MDIX 0x0008 +#define IGP01E1000_PSSR_SPEED_MASK 0xC000 +#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000 + +#define IGP02E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 + +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Course - 15:13, Fine - 12:9 */ +#define IGP02E1000_AGC_LENGTH_MASK 0x7F +#define IGP02E1000_AGC_RANGE 15 + +#define IGP03E1000_PHY_MISC_CTRL 0x1B +#define IGP03E1000_PHY_MISC_DUPLEX_MANUAL_SET 0x1000 /* Manually Set Duplex */ + +#define E1000_CABLE_LENGTH_UNDEFINED 0xFF + +#define E1000_KMRNCTRLSTA_OFFSET 0x001F0000 +#define E1000_KMRNCTRLSTA_OFFSET_SHIFT 16 +#define E1000_KMRNCTRLSTA_REN 0x00200000 +#define E1000_KMRNCTRLSTA_DIAG_OFFSET 0x3 /* Kumeran Diagnostic */ +#define E1000_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */ + +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Control */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY Special and LED Control */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control */ + +/* IFE PHY Extended Status Control */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 + +/* IFE PHY Special Control */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 +#define IFE_PSC_FORCE_POLARITY 0x0020 +#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 + +/* IFE PHY Special Control and LED Control */ +#define IFE_PSCL_PROBE_MODE 0x0020 +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + +/* IFE PHY MDIX Control */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDI-X, 0=force MDI */ +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable auto MDI/MDI-X, 0=disable */ + +#endif diff --git a/bsd_eth_drivers/if_em/e1000_regs.h b/bsd_eth_drivers/if_em/e1000_regs.h new file mode 100644 index 0000000..c3cfa9b --- /dev/null +++ b/bsd_eth_drivers/if_em/e1000_regs.h @@ -0,0 +1,441 @@ +/******************************************************************************* + + Copyright (c) 2001-2007, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ +/*$FreeBSD: src/sys/dev/em/e1000_regs.h,v 1.3 2007/05/16 00:14:23 jfv Exp $*/ + + +#ifndef _E1000_REGS_H_ +#define _E1000_REGS_H_ + +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_FLA 0x0001C /* Flash Access - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_SCTL 0x00024 /* SerDes Control - RW */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FEXTNVM 0x00028 /* Future Extended NVM - RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_CONNSW 0x00034 /* Copper/Fiber switch control - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */ +#define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */ +#define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */ +#define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */ +#define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */ +#define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ +#define E1000_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */ +#define E1000_EITR0 0x01680 /* Ext. Int. Throttling Rate Vector 0 - RW */ +#define E1000_EITR1 0x01684 /* Ext. Int. Throttling Rate Vector 1 - RW */ +#define E1000_EITR2 0x01688 /* Ext. Int. Throttling Rate Vector 2 - RW */ +#define E1000_EITR3 0x0168C /* Ext. Int. Throttling Rate Vector 3 - RW */ +#define E1000_EITR4 0x01690 /* Ext. Int. Throttling Rate Vector 4 - RW */ +#define E1000_EITR5 0x01694 /* Ext. Int. Throttling Rate Vector 5 - RW */ +#define E1000_EITR6 0x01698 /* Ext. Int. Throttling Rate Vector 6 - RW */ +#define E1000_EITR7 0x0169C /* Ext. Int. Throttling Rate Vector 7 - RW */ +#define E1000_EITR8 0x016A0 /* Ext. Int. Throttling Rate Vector 8 - RW */ +#define E1000_EITR9 0x016A4 /* Ext. Int. Throttling Rate Vector 9 - RW */ +#define E1000_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */ +#define E1000_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */ +#define E1000_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */ +#define E1000_EIAC 0x0152C /* Ext. Interrupt Auto Clear - RW */ +#define E1000_EIAM 0x01530 /* Ext. Interrupt Ack Auto Clear Mask - RW */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ +#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ +#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_FLASHT 0x01028 /* FLASH Timer Register */ +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLSWCTL 0x01030 /* FLASH control register */ +#define E1000_FLSWDATA 0x01034 /* FLASH data register */ +#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */ +#define E1000_I2CPARAMS 0x0102C /* SFPI2C Parameters Register - RW */ +#define E1000_WDSTP 0x01040 /* Watchdog Setup - RW */ +#define E1000_SWDSTS 0x01044 /* SW Device Status - RW */ +#define E1000_FRTIMER 0x01048 /* Free Running Timer - RW */ +#define E1000_TCPTIMER 0x0104C /* TCP Timer - RW */ +#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ +#define E1000_RDFPCQ0 0x02430 +#define E1000_RDFPCQ1 0x02434 +#define E1000_RDFPCQ2 0x02438 +#define E1000_RDFPCQ3 0x0243C +#define E1000_PBRTH 0x02458 /* PB RX Arbitration Threshold - RW */ +#define E1000_FCRTV 0x02460 /* Flow Control Refresh Timer Value - RW */ +#define E1000_SRRCTL0 0x0280C +#define E1000_SRRCTL(_n) (0x280C + (_n << 8)) /* Split and Replication + * RX Control - RW */ +#define E1000_RDPUMB 0x025CC /* DMA RX Descriptor uC Mailbox - RW */ +#define E1000_RDPUAD 0x025D0 /* DMA RX Descriptor uC Addr Command - RW */ +#define E1000_RDPUWD 0x025D4 /* DMA RX Descriptor uC Data Write - RW */ +#define E1000_RDPURD 0x025D8 /* DMA RX Descriptor uC Data Read - RW */ +#define E1000_RDPUCTL 0x025DC /* DMA RX Descriptor uC Control - RW */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ +#define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */ +#define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */ +#define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */ +#define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */ +#define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */ +#define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */ +#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */ +#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ +/* Convenience macros + * + * Note: "_n" is the queue number of the register to be written to. + * + * Example usage: + * E1000_RDBAL_REG(current_rx_queue) + * + */ +#define E1000_RDBAL_REG(_n) (E1000_RDBAL + (_n << 8)) +#define E1000_RDBAH_REG(_n) (E1000_RDBAH + (_n << 8)) +#define E1000_RDLEN_REG(_n) (E1000_RDLEN + (_n << 8)) +#define E1000_RDH_REG(_n) (E1000_RDH + (_n << 8)) +#define E1000_RDT_REG(_n) (E1000_RDT + (_n << 8)) +#define E1000_RXDCTL_REG(_n) (E1000_RXDCTL + (_n << 8)) +#define E1000_TDBAL_REG(_n) (E1000_TDBAL + (_n << 8)) +#define E1000_TDBAH_REG(_n) (E1000_TDBAH + (_n << 8)) +#define E1000_TDLEN_REG(_n) (E1000_TDLEN + (_n << 8)) +#define E1000_TDH_REG(_n) (E1000_TDH + (_n << 8)) +#define E1000_TDT_REG(_n) (E1000_TDT + (_n << 8)) +#define E1000_TXDCTL_REG(_n) (E1000_TXDCTL + (_n << 8)) +#define E1000_TARC_REG(_n) (E1000_TARC0 + (_n << 8)) +#define E1000_DCA_RXCTRL(_n) (0x02814 + (_n << 8)) +#define E1000_DCA_RXCTRL0 0x02814 /* RX Queue 0 DCA CTRL - RW */ +#define E1000_DCA_RXCTRL1 0x02914 /* RX Queue 1 DCA CTRL - RW */ +#define E1000_RDBAL2 0x02A00 /* RX Descriptor Base Low Queue 2 - RW */ +#define E1000_RDBAH2 0x02A04 /* RX Descriptor Base High Queue 2 - RW */ +#define E1000_RDLEN2 0x02A08 /* RX Descriptor Length Queue 2 - RW */ +#define E1000_RDH2 0x02A10 /* RX Descriptor Head Queue 2 - RW */ +#define E1000_DCA_RXCTRL2 0x02A14 /* RX Queue 2 DCA CTRL - RW */ +#define E1000_RDT2 0x02A18 /* RX Descriptor Tail Queue 2 - RW */ +#define E1000_RXDCTL2 0x02A28 /* RX Descriptor Control queue 2 - RW */ +#define E1000_RDBAL3 0x02B00 /* RX Descriptor Base Low Queue 3 - RW */ +#define E1000_RDBAH3 0x02B04 /* RX Descriptor Base High Queue 3 - RW */ +#define E1000_RDLEN3 0x02B08 /* RX Descriptor Length Queue 3 - RW */ +#define E1000_RDH3 0x02B10 /* RX Descriptor Head Queue 3 - RW */ +#define E1000_DCA_RXCTRL3 0x02B14 /* RX Queue 3 DCA Control - RW */ +#define E1000_RDT3 0x02B18 /* RX Descriptor Tail Queue 3 - RW */ +#define E1000_RXDCTL3 0x02B28 /* RX Descriptor Control Queue 3 - RW */ +#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ +#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ +#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ +#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ +#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ +#define E1000_TDPUMB 0x0357C /* DMA TX Descriptor uC Mail Box - RW */ +#define E1000_TDPUAD 0x03580 /* DMA TX Descriptor uC Addr Command - RW */ +#define E1000_TDPUWD 0x03584 /* DMA TX Descriptor uC Data Write - RW */ +#define E1000_TDPURD 0x03588 /* DMA TX Descriptor uC Data Read - RW */ +#define E1000_TDPUCTL 0x0358C /* DMA TX Descriptor uC Control - RW */ +#define E1000_DTXCTL 0x03590 /* DMA TX Control - RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descriptor Tail - RW */ +#define E1000_TDBAL0 E1000_TDBAL /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH0 E1000_TDBAH /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN0 E1000_TDLEN /* TX Descriptor Length - RW */ +#define E1000_TDH0 E1000_TDH /* TX Descriptor Head - RW */ +#define E1000_TDT0 E1000_TDT /* TX Descriptor Tail - RW */ +#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ +#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ +#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ +#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ +#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */ +#define E1000_DCA_TXCTRL0 0x03814 /* TX Queue 0 DCA CTRL - RW */ +#define E1000_TDWBAL0 0x03838 /* TX Desc. WB Addr Low Queue 0 - RW */ +#define E1000_TDWBAH0 0x0383C /* TX Desc. WB Addr High Queue 0 - RW */ +#define E1000_DCA_TXCTRL(_n) (E1000_DCA_TXCTRL0 + (_n << 8)) +#define E1000_TDWBAL_REG(_n) (E1000_TDWBAL0 + (_n << 8)) +#define E1000_TDWBAH_REG(_n) (E1000_TDWBAH0 + (_n << 8)) +#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */ +#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */ +#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */ +#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */ +#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ +#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ +#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ +#define E1000_DCA_TXCTRL1 0x03914 /* TX Queue 0 DCA CTRL - RW */ +#define E1000_TDWBAL1 0x03938 /* TX Descriptor WB Addr Low Queue 1 - RW */ +#define E1000_TDWBAH1 0x0393C /* TX Descriptor WB Addr High Queue 1 - RW */ +#define E1000_TDBAL2 0x03A00 /* TX Descriptor Base Low Queue 2 - RW */ +#define E1000_TDBAH2 0x03A04 /* TX Descriptor Base High Queue 2 - RW */ +#define E1000_TDLEN2 0x03A08 /* TX Descriptor Length Queue 2 - RW */ +#define E1000_TDH2 0x03A10 /* TX Descriptor Head Queue 2 - RW */ +#define E1000_DCA_TXCTRL2 0x03A14 /* TX Queue 2 DCA Control - RW */ +#define E1000_TDT2 0x03A18 /* TX Descriptor Tail Queue 2 - RW */ +#define E1000_TXDCTL2 0x03A28 /* TX Descriptor Control 2 - RW */ +#define E1000_TDWBAL2 0x03A38 /* TX Descriptor WB Addr Low Queue 2 - RW */ +#define E1000_TDWBAH2 0x03A3C /* TX Descriptor WB Addr High Queue 2 - RW */ +#define E1000_TDBAL3 0x03B00 /* TX Descriptor Base Low Queue 3 - RW */ +#define E1000_TDBAH3 0x03B04 /* TX Descriptor Base High Queue 3 - RW */ +#define E1000_TDLEN3 0x03B08 /* TX Descriptor Length Queue 3 - RW */ +#define E1000_TDH3 0x03B10 /* TX Descriptor Head Queue 3 - RW */ +#define E1000_DCA_TXCTRL3 0x03B14 /* TX Queue 3 DCA Control - RW */ +#define E1000_TDT3 0x03B18 /* TX Descriptor Tail Queue 3 - RW */ +#define E1000_TXDCTL3 0x03B28 /* TX Descriptor Control 3 - RW */ +#define E1000_TDWBAL3 0x03B38 /* TX Descriptor WB Addr Low Queue 3 - RW */ +#define E1000_TDWBAH3 0x03B3C /* TX Descriptor WB Addr High Queue 3 - RW */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ +#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ +#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */ +#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */ +#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */ +#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */ +#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ +#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ +#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ +#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ +#define E1000_PCS_CFG0 0x04200 /* PCS Configuration 0 - RW */ +#define E1000_PCS_LCTL 0x04208 /* PCS Link Control - RW */ +#define E1000_PCS_LSTAT 0x0420C /* PCS Link Status - RO */ +#define E1000_CBTMPC 0x0402C /* Circuit Breaker TX Packet Count */ +#define E1000_HTDPMC 0x0403C /* Host Transmit Discarded Packets */ +#define E1000_CBRDPC 0x04044 /* Circuit Breaker RX Dropped Count */ +#define E1000_CBRMPC 0x040FC /* Circuit Breaker RX Packet Count */ +#define E1000_RPTHC 0x04104 /* Rx Packets To Host */ +#define E1000_HGPTC 0x04118 /* Host Good Packets TX Count */ +#define E1000_HTCBDPC 0x04124 /* Host TX Circuit Breaker Dropped Count */ +#define E1000_HGORCL 0x04128 /* Host Good Octets Received Count Low */ +#define E1000_HGORCH 0x0412C /* Host Good Octets Received Count High */ +#define E1000_HGOTCL 0x04130 /* Host Good Octets Transmit Count Low */ +#define E1000_HGOTCH 0x04134 /* Host Good Octets Transmit Count High */ +#define E1000_LENERRS 0x04138 /* Length Errors Count */ +#define E1000_SCVPC 0x04228 /* SerDes/SGMII Code Violation Pkt Count */ +#define E1000_HRMPC 0x0A018 /* Header Redirection Missed Packet Count */ +#define E1000_PCS_ANADV 0x04218 /* AN advertisement - RW */ +#define E1000_PCS_LPAB 0x0421C /* Link Partner Ability - RW */ +#define E1000_PCS_NPTX 0x04220 /* AN Next Page Transmit - RW */ +#define E1000_PCS_LPABNP 0x04224 /* Link Partner Ability Next Page - RW */ +#define E1000_1GSTAT_RCV 0x04228 /* 1GSTAT Code Violation Packet Count - RW */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#define E1000_RLPML 0x05004 /* RX Long Packet Max Length */ +#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_PSRTYPE 0x05480 /* Packet Split Receive Type - RW */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_VMD_CTL 0x0581C /* VMDq Control - RW */ +#define E1000_VFQA0 0x0B000 /* VLAN Filter Queue Array 0 - RW Array */ +#define E1000_VFQA1 0x0B200 /* VLAN Filter Queue Array 1 - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_HOST_IF 0x08800 /* Host Interface */ +#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ + +#define E1000_KMRNCTRLSTA 0x00034 /* MAC-PHY interface - RW */ +#define E1000_MDPHYA 0x0003C /* PHY address - RW */ +#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ +#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ +#define E1000_CCMCTL 0x05B48 /* CCM Control Register */ +#define E1000_GIOCTL 0x05B44 /* GIO Analog Control Register */ +#define E1000_SCCTL 0x05B4C /* PCIc PLL Configuration Register */ +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +#define E1000_DCA_ID 0x05B70 /* DCA Requester ID Information - RO */ +#define E1000_DCA_CTRL 0x05B74 /* DCA Control - RW */ +#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Inteface Control */ + +/* RSS registers */ +#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ +#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ +#define E1000_IMIR(_i) (0x05A80 + ((_i) * 4)) /* Immediate Interrupt */ +#define E1000_IMIREXT(_i) (0x05AA0 + ((_i) * 4)) /* Immediate Interrupt Ext*/ +#define E1000_IMIRVP 0x05AC0 /* Immediate Interrupt RX VLAN Priority - RW */ +#define E1000_MSIXBM0 0x01600 /* MSI-X Allocation Register 0 - RW */ +#define E1000_MSIXBM1 0x01604 /* MSI-X Allocation Register 1 - RW */ +#define E1000_MSIXBM2 0x01608 /* MSI-X Allocation Register 2 - RW */ +#define E1000_MSIXBM3 0x0160C /* MSI-X Allocation Register 3 - RW */ +#define E1000_MSIXBM4 0x01610 /* MSI-X Allocation Register 4 - RW */ +#define E1000_MSIXBM5 0x01614 /* MSI-X Allocation Register 5 - RW */ +#define E1000_MSIXBM6 0x01618 /* MSI-X Allocation Register 6 - RW */ +#define E1000_MSIXBM7 0x0161C /* MSI-X Allocation Register 7 - RW */ +#define E1000_MSIXBM8 0x01620 /* MSI-X Allocation Register 8 - RW */ +#define E1000_MSIXBM9 0x01624 /* MSI-X Allocation Register 9 - RW */ +#define E1000_MSIXTADD0 0x0C000 /* MSI-X Table entry addr low reg 0 - RW */ +#define E1000_MSIXTADD1 0x0C010 /* MSI-X Table entry addr low reg 1 - RW */ +#define E1000_MSIXTADD2 0x0C020 /* MSI-X Table entry addr low reg 2 - RW */ +#define E1000_MSIXTADD3 0x0C030 /* MSI-X Table entry addr low reg 3 - RW */ +#define E1000_MSIXTADD4 0x0C040 /* MSI-X Table entry addr low reg 4 - RW */ +#define E1000_MSIXTADD5 0x0C050 /* MSI-X Table entry addr low reg 5 - RW */ +#define E1000_MSIXTADD6 0x0C060 /* MSI-X Table entry addr low reg 6 - RW */ +#define E1000_MSIXTADD7 0x0C070 /* MSI-X Table entry addr low reg 7 - RW */ +#define E1000_MSIXTADD8 0x0C080 /* MSI-X Table entry addr low reg 8 - RW */ +#define E1000_MSIXTADD9 0x0C090 /* MSI-X Table entry addr low reg 9 - RW */ +#define E1000_MSIXTUADD0 0x0C004 /* MSI-X Table entry addr upper reg 0 - RW */ +#define E1000_MSIXTUADD1 0x0C014 /* MSI-X Table entry addr upper reg 1 - RW */ +#define E1000_MSIXTUADD2 0x0C024 /* MSI-X Table entry addr upper reg 2 - RW */ +#define E1000_MSIXTUADD3 0x0C034 /* MSI-X Table entry addr upper reg 3 - RW */ +#define E1000_MSIXTUADD4 0x0C044 /* MSI-X Table entry addr upper reg 4 - RW */ +#define E1000_MSIXTUADD5 0x0C054 /* MSI-X Table entry addr upper reg 5 - RW */ +#define E1000_MSIXTUADD6 0x0C064 /* MSI-X Table entry addr upper reg 6 - RW */ +#define E1000_MSIXTUADD7 0x0C074 /* MSI-X Table entry addr upper reg 7 - RW */ +#define E1000_MSIXTUADD8 0x0C084 /* MSI-X Table entry addr upper reg 8 - RW */ +#define E1000_MSIXTUADD9 0x0C094 /* MSI-X Table entry addr upper reg 9 - RW */ +#define E1000_MSIXTMSG0 0x0C008 /* MSI-X Table entry message reg 0 - RW */ +#define E1000_MSIXTMSG1 0x0C018 /* MSI-X Table entry message reg 1 - RW */ +#define E1000_MSIXTMSG2 0x0C028 /* MSI-X Table entry message reg 2 - RW */ +#define E1000_MSIXTMSG3 0x0C038 /* MSI-X Table entry message reg 3 - RW */ +#define E1000_MSIXTMSG4 0x0C048 /* MSI-X Table entry message reg 4 - RW */ +#define E1000_MSIXTMSG5 0x0C058 /* MSI-X Table entry message reg 5 - RW */ +#define E1000_MSIXTMSG6 0x0C068 /* MSI-X Table entry message reg 6 - RW */ +#define E1000_MSIXTMSG7 0x0C078 /* MSI-X Table entry message reg 7 - RW */ +#define E1000_MSIXTMSG8 0x0C088 /* MSI-X Table entry message reg 8 - RW */ +#define E1000_MSIXTMSG9 0x0C098 /* MSI-X Table entry message reg 9 - RW */ +#define E1000_MSIXVCTRL0 0x0C00C /* MSI-X Table entry vector ctrl reg 0 - RW */ +#define E1000_MSIXVCTRL1 0x0C01C /* MSI-X Table entry vector ctrl reg 1 - RW */ +#define E1000_MSIXVCTRL2 0x0C02C /* MSI-X Table entry vector ctrl reg 2 - RW */ +#define E1000_MSIXVCTRL3 0x0C03C /* MSI-X Table entry vector ctrl reg 3 - RW */ +#define E1000_MSIXVCTRL4 0x0C04C /* MSI-X Table entry vector ctrl reg 4 - RW */ +#define E1000_MSIXVCTRL5 0x0C05C /* MSI-X Table entry vector ctrl reg 5 - RW */ +#define E1000_MSIXVCTRL6 0x0C06C /* MSI-X Table entry vector ctrl reg 6 - RW */ +#define E1000_MSIXVCTRL7 0x0C07C /* MSI-X Table entry vector ctrl reg 7 - RW */ +#define E1000_MSIXVCTRL8 0x0C08C /* MSI-X Table entry vector ctrl reg 8 - RW */ +#define E1000_MSIXVCTRL9 0x0C09C /* MSI-X Table entry vector ctrl reg 9 - RW */ +#define E1000_MSIXPBA 0x0E000 /* MSI-X Pending bit array */ +#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */ +#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */ +#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ +#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ + +#endif diff --git a/bsd_eth_drivers/if_em/if_em.c b/bsd_eth_drivers/if_em/if_em.c new file mode 100644 index 0000000..71d1227 --- /dev/null +++ b/bsd_eth_drivers/if_em/if_em.c @@ -0,0 +1,5272 @@ +/************************************************************************** + +Copyright (c) 2001-2007, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +/*$FreeBSD: src/sys/dev/em/if_em.c,v 1.181 2007/05/31 23:36:21 jfv Exp $*/ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#ifdef __rtems__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "e1000_api.h" +#include "e1000_82575.h" +#include "if_em.h" + +#ifdef __rtems__ +/* Override things as necessary */ +#include +#endif + +/********************************************************************* + * Set this to one to display debug statistics + *********************************************************************/ +int em_display_debug_stats = 0; + +/********************************************************************* + * Driver version: + *********************************************************************/ +char em_driver_version[] = "Version - 6.5.3"; + + +/********************************************************************* + * PCI Device ID Table + * + * Used by probe to select devices to load on + * Last field stores an index into e1000_strings + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } + *********************************************************************/ + +static em_vendor_info_t em_vendor_info_array[] = +{ + /* Intel(R) PRO/1000 Network Connection */ + { 0x8086, E1000_DEV_ID_82540EM, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82540EM_LOM, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82540EP, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82540EP_LOM, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82540EP_LP, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82541EI, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82541ER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82541ER_LOM, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82541EI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82541GI, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82541GI_LF, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82541GI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0}, + +#if !defined(NO_82542_SUPPORT) /* __rtems__ */ + { 0x8086, E1000_DEV_ID_82542, PCI_ANY_ID, PCI_ANY_ID, 0}, +#endif + + { 0x8086, E1000_DEV_ID_82543GC_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82543GC_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82544EI_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82544EI_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82544GC_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82544GC_LOM, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82545EM_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82545EM_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82545GM_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82545GM_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82545GM_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82546EB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546EB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546EB_QUAD_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546GB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546GB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546GB_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546GB_PCIE, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3, + PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82547EI, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82547EI_MOBILE, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82547GI, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82571EB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82571EB_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82571EB_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82571EB_QUAD_COPPER, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82571EB_QUAD_COPPER_LP, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82571EB_QUAD_FIBER, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82572EI_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82572EI_FIBER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82572EI_SERDES, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82572EI, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82573E, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82573E_IAMT, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82573L, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_80003ES2LAN_COPPER_SPT, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_80003ES2LAN_SERDES_SPT, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_80003ES2LAN_COPPER_DPT, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_80003ES2LAN_SERDES_DPT, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH8_IGP_M_AMT, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH8_IGP_AMT, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH8_IGP_C, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH8_IFE, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH8_IFE_GT, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH8_IFE_G, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH8_IGP_M, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_ICH9_IGP_AMT, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH9_IGP_C, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH9_IFE, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH9_IFE_GT, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_ICH9_IFE_G, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 0x8086, E1000_DEV_ID_82575EB_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82575EB_FIBER_SERDES, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82575EM_COPPER, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82575EM_FIBER_SERDES, + PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82575GB_QUAD_COPPER, + PCI_ANY_ID, PCI_ANY_ID, 0}, + /* required last entry */ + { 0, 0, 0, 0, 0} +}; + +/********************************************************************* + * Table of branding strings for all supported NICs. + *********************************************************************/ + +static char *em_strings[] = { + "Intel(R) PRO/1000 Network Connection" +}; + +/********************************************************************* + * Function prototypes + *********************************************************************/ +static int em_probe(device_t); +static int em_attach(device_t); +static int em_detach(device_t); +#ifndef __rtems__ +static int em_shutdown(device_t); +static int em_suspend(device_t); +static int em_resume(device_t); +#endif +static void em_start(struct ifnet *); +static void em_start_locked(struct ifnet *ifp); +#ifndef __rtems__ +static int em_ioctl(struct ifnet *, u_long, caddr_t); +#else +static int em_ioctl(struct ifnet *, ioctl_command_t, caddr_t); +#endif +static void em_watchdog(struct adapter *); +static void em_init(void *); +static void em_init_locked(struct adapter *); +static void em_stop(void *); +static void em_media_status(struct ifnet *, struct ifmediareq *); +static int em_media_change(struct ifnet *); +static void em_identify_hardware(struct adapter *); +static int em_allocate_pci_resources(struct adapter *); +static int em_allocate_intr(struct adapter *); +static void em_free_intr(struct adapter *); +static void em_free_pci_resources(struct adapter *); +static void em_local_timer(void *); +static int em_hardware_init(struct adapter *); +static void em_setup_interface(device_t, struct adapter *); +static int em_setup_transmit_structures(struct adapter *); +static void em_initialize_transmit_unit(struct adapter *); +static int em_setup_receive_structures(struct adapter *); +static void em_initialize_receive_unit(struct adapter *); +static void em_enable_intr(struct adapter *); +static void em_disable_intr(struct adapter *); +static void em_free_transmit_structures(struct adapter *); +static void em_free_receive_structures(struct adapter *); +static void em_update_stats_counters(struct adapter *); +static void em_txeof(struct adapter *); +static int em_allocate_receive_structures(struct adapter *); +static int em_allocate_transmit_structures(struct adapter *); +static int em_rxeof(struct adapter *, int); +#ifndef __NO_STRICT_ALIGNMENT +static int em_fixup_rx(struct adapter *); +#endif +#ifndef __rtems__ +static void em_receive_checksum(struct adapter *, struct e1000_rx_desc *, + struct mbuf *); +static void em_transmit_checksum_setup(struct adapter *, struct mbuf *, + uint32_t *, uint32_t *); +static boolean_t em_tx_adv_ctx_setup(struct adapter *, struct mbuf *); +static boolean_t em_tso_setup(struct adapter *, struct mbuf *, uint32_t *, + uint32_t *); +static boolean_t em_tso_adv_setup(struct adapter *, struct mbuf *, uint32_t *); +#endif +static void em_set_promisc(struct adapter *); +static void em_disable_promisc(struct adapter *); +static void em_set_multi(struct adapter *); +#ifndef __rtems__ +static void em_print_hw_stats(struct adapter *); +#else +void em_print_hw_stats(struct adapter *); +#endif +static void em_update_link_status(struct adapter *); +static int em_get_buf(struct adapter *, int); +#ifndef __rtems__ +static void em_enable_vlans(struct adapter *); +#endif +static int em_encap(struct adapter *, struct mbuf **); +static int em_adv_encap(struct adapter *, struct mbuf **); +static void em_smartspeed(struct adapter *); +static int em_82547_fifo_workaround(struct adapter *, int); +static void em_82547_update_fifo_head(struct adapter *, int); +static int em_82547_tx_fifo_reset(struct adapter *); +static void em_82547_move_tail(void *); +static int em_dma_malloc(struct adapter *, bus_size_t, + struct em_dma_alloc *, int); +static void em_dma_free(struct adapter *, struct em_dma_alloc *); +#ifndef __rtems__ +static void em_print_debug_info(struct adapter *); +#else +void em_print_debug_info(struct adapter *); +#endif +static int em_is_valid_ether_addr(uint8_t *); +#ifndef __rtems__ +static int em_sysctl_stats(SYSCTL_HANDLER_ARGS); +static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS); +#endif +static uint32_t em_fill_descriptors (bus_addr_t address, uint32_t length, + PDESC_ARRAY desc_array); +#ifndef __rtems__ +static int em_sysctl_int_delay(SYSCTL_HANDLER_ARGS); +#endif +static void em_add_int_delay_sysctl(struct adapter *, const char *, + const char *, struct em_int_delay_info *, int, int); +/* Management and WOL Support */ +static void em_init_manageability(struct adapter *); +static void em_release_manageability(struct adapter *); +static void em_get_hw_control(struct adapter *); +static void em_release_hw_control(struct adapter *); +static void em_enable_wakeup(device_t); + +#ifdef DEVICE_POLLING +static poll_handler_t em_poll; +static void em_intr(void *); +#else +static int em_intr_fast(void *); +static void em_add_rx_process_limit(struct adapter *, const char *, + const char *, int *, int); +static void em_handle_rxtx(void *context, int pending); +static void em_handle_link(void *context, int pending); +#endif + +/********************************************************************* + * FreeBSD Device Interface Entry Points + *********************************************************************/ + +#ifndef __rtems__ +static device_method_t em_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, em_probe), + DEVMETHOD(device_attach, em_attach), + DEVMETHOD(device_detach, em_detach), + DEVMETHOD(device_shutdown, em_shutdown), + DEVMETHOD(device_suspend, em_suspend), + DEVMETHOD(device_resume, em_resume), + {0, 0} +}; + +static driver_t em_driver = { + "em", em_methods, sizeof(struct adapter), +}; + +static devclass_t em_devclass; +DRIVER_MODULE(em, pci, em_driver, em_devclass, 0, 0); +MODULE_DEPEND(em, pci, 1, 1, 1); +MODULE_DEPEND(em, ether, 1, 1, 1); + +#else +static device_method_t em_methods = { + probe: em_probe, + attach: em_attach, + detach: em_detach, + irq_en: 0, + irq_check_dis: 0, +}; + +driver_t libbsdport_em_driver = { + "em", + &em_methods, + DEV_TYPE_PCI, + sizeof(struct adapter) +}; +#endif + +/********************************************************************* + * Tunable default values. + *********************************************************************/ + +#define EM_TICKS_TO_USECS(ticks) ((1024 * (ticks) + 500) / 1000) +#define EM_USECS_TO_TICKS(usecs) ((1000 * (usecs) + 512) / 1024) +#define M_TSO_LEN 66 + +#ifdef __rtems__ +#define static +#define TUNABLE_INT(a,b) +#endif + +static int em_tx_int_delay_dflt = EM_TICKS_TO_USECS(EM_TIDV); +static int em_rx_int_delay_dflt = EM_TICKS_TO_USECS(EM_RDTR); +static int em_tx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_TADV); +static int em_rx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_RADV); +static int em_rxd = EM_DEFAULT_RXD; +static int em_txd = EM_DEFAULT_TXD; +static int em_smart_pwr_down = FALSE; + +TUNABLE_INT("hw.em.tx_int_delay", &em_tx_int_delay_dflt); +TUNABLE_INT("hw.em.rx_int_delay", &em_rx_int_delay_dflt); +TUNABLE_INT("hw.em.tx_abs_int_delay", &em_tx_abs_int_delay_dflt); +TUNABLE_INT("hw.em.rx_abs_int_delay", &em_rx_abs_int_delay_dflt); +TUNABLE_INT("hw.em.rxd", &em_rxd); +TUNABLE_INT("hw.em.txd", &em_txd); +TUNABLE_INT("hw.em.smart_pwr_down", &em_smart_pwr_down); +#ifndef DEVICE_POLLING +/* How many packets rxeof tries to clean at a time */ +static int em_rx_process_limit = 100; +TUNABLE_INT("hw.em.rx_process_limit", &em_rx_process_limit); +#endif + +#ifdef __rtems__ +int em_bootverbose = 0; +#define bootverbose em_bootverbose +#undef static +#define static static +#endif + +/* Global used in WOL setup with multiport cards */ +static int global_quad_port_a = 0; + +/********************************************************************* + * Device identification routine + * + * em_probe determines if the driver should be loaded on + * adapter based on PCI vendor/device id of the adapter. + * + * return BUS_PROBE_DEFAULT on success, positive on failure + *********************************************************************/ + +static int +em_probe(device_t dev) +{ + char adapter_name[60]; + uint16_t pci_vendor_id = 0; + uint16_t pci_device_id = 0; + uint16_t pci_subvendor_id = 0; + uint16_t pci_subdevice_id = 0; + em_vendor_info_t *ent; + + INIT_DEBUGOUT("em_probe: begin"); + + pci_vendor_id = pci_get_vendor(dev); + if (pci_vendor_id != EM_VENDOR_ID) + return (ENXIO); + + pci_device_id = pci_get_device(dev); + pci_subvendor_id = pci_get_subvendor(dev); + pci_subdevice_id = pci_get_subdevice(dev); + + ent = em_vendor_info_array; + while (ent->vendor_id != 0) { + if ((pci_vendor_id == ent->vendor_id) && + (pci_device_id == ent->device_id) && + + ((pci_subvendor_id == ent->subvendor_id) || + (ent->subvendor_id == PCI_ANY_ID)) && + + ((pci_subdevice_id == ent->subdevice_id) || + (ent->subdevice_id == PCI_ANY_ID))) { + sprintf(adapter_name, "%s %s", + em_strings[ent->index], + em_driver_version); + device_set_desc_copy(dev, adapter_name); + return (BUS_PROBE_DEFAULT); + } + ent++; + } + + return (ENXIO); +} + +/********************************************************************* + * Device initialization routine + * + * The attach entry point is called when the driver is being loaded. + * This routine identifies the type of hardware, allocates all resources + * and initializes the hardware. + * + * return 0 on success, positive on failure + *********************************************************************/ + +static int +em_attach(device_t dev) +{ + struct adapter *adapter; + int tsize, rsize; + int error = 0; + u16 eeprom_data, device_id; + + INIT_DEBUGOUT("em_attach: begin"); + + adapter = device_get_softc(dev); + adapter->dev = adapter->osdep.dev = dev; + EM_LOCK_INIT(adapter, device_get_nameunit(dev)); + + /* SYSCTL stuff */ + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "debug_info", CTLTYPE_INT|CTLFLAG_RW, adapter, 0, + em_sysctl_debug_info, "I", "Debug Information"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "stats", CTLTYPE_INT|CTLFLAG_RW, adapter, 0, + em_sysctl_stats, "I", "Statistics"); + + callout_init_mtx(&adapter->timer, &adapter->mtx, 0); + callout_init_mtx(&adapter->tx_fifo_timer, &adapter->mtx, 0); + + /* Determine hardware and mac info */ + em_identify_hardware(adapter); + + /* Setup PCI resources */ + if (em_allocate_pci_resources(adapter)) { + device_printf(dev, "Allocation of PCI resources failed\n"); + error = ENXIO; + goto err_pci; + } + + /* + ** For ICH8 and family we need to + ** map the flash memory, and this + ** must happen after the MAC is + ** identified + */ + if ((adapter->hw.mac.type == e1000_ich8lan) || + (adapter->hw.mac.type == e1000_ich9lan)) { + int rid = EM_BAR_TYPE_FLASH; + adapter->flash_mem = bus_alloc_resource_any(dev, + SYS_RES_MEMORY, &rid, RF_ACTIVE); + /* This is used in the shared code */ + adapter->hw.flash_address = (u8 *)adapter->flash_mem; + adapter->osdep.flash_bus_space_tag = + rman_get_bustag(adapter->flash_mem); + adapter->osdep.flash_bus_space_handle = + rman_get_bushandle(adapter->flash_mem); + } + + /* Do Shared Code initialization */ + if (e1000_setup_init_funcs(&adapter->hw, TRUE)) { + device_printf(dev, "Setup of Shared code failed\n"); + error = ENXIO; + goto err_pci; + } + + e1000_get_bus_info(&adapter->hw); + + /* Set up some sysctls for the tunable interrupt delays */ + em_add_int_delay_sysctl(adapter, "rx_int_delay", + "receive interrupt delay in usecs", &adapter->rx_int_delay, + E1000_REGISTER(&adapter->hw, E1000_RDTR), em_rx_int_delay_dflt); + em_add_int_delay_sysctl(adapter, "tx_int_delay", + "transmit interrupt delay in usecs", &adapter->tx_int_delay, + E1000_REGISTER(&adapter->hw, E1000_TIDV), em_tx_int_delay_dflt); + if (adapter->hw.mac.type >= e1000_82540) { + em_add_int_delay_sysctl(adapter, "rx_abs_int_delay", + "receive interrupt delay limit in usecs", + &adapter->rx_abs_int_delay, + E1000_REGISTER(&adapter->hw, E1000_RADV), + em_rx_abs_int_delay_dflt); + em_add_int_delay_sysctl(adapter, "tx_abs_int_delay", + "transmit interrupt delay limit in usecs", + &adapter->tx_abs_int_delay, + E1000_REGISTER(&adapter->hw, E1000_TADV), + em_tx_abs_int_delay_dflt); + } + +#ifndef DEVICE_POLLING + /* Sysctls for limiting the amount of work done in the taskqueue */ + em_add_rx_process_limit(adapter, "rx_processing_limit", + "max number of rx packets to process", &adapter->rx_process_limit, + em_rx_process_limit); +#endif + +#ifdef __rtems__ + { + int em_txd_tmp = em_txd; + int em_rxd_tmp = em_rxd; +#define em_txd em_txd_tmp +#define em_rxd em_rxd_tmp + /* Does ifconfig override ? */ + if ( dev->ifconfig ) { + if ( dev->ifconfig->xbuf_count ) + em_txd_tmp = dev->ifconfig->xbuf_count; + if ( dev->ifconfig->rbuf_count ) + em_rxd_tmp = dev->ifconfig->rbuf_count; + } +#endif + + /* + * Validate number of transmit and receive descriptors. It + * must not exceed hardware maximum, and must be multiple + * of E1000_DBA_ALIGN. + */ + if (((em_txd * sizeof(struct e1000_tx_desc)) % EM_DBA_ALIGN) != 0 || + (adapter->hw.mac.type >= e1000_82544 && em_txd > EM_MAX_TXD) || + (adapter->hw.mac.type < e1000_82544 && em_txd > EM_MAX_TXD_82543) || + (em_txd < EM_MIN_TXD)) { + device_printf(dev, "Using %d TX descriptors instead of %d!\n", + EM_DEFAULT_TXD, em_txd); + adapter->num_tx_desc = EM_DEFAULT_TXD; + } else + adapter->num_tx_desc = em_txd; + if (((em_rxd * sizeof(struct e1000_rx_desc)) % EM_DBA_ALIGN) != 0 || + (adapter->hw.mac.type >= e1000_82544 && em_rxd > EM_MAX_RXD) || + (adapter->hw.mac.type < e1000_82544 && em_rxd > EM_MAX_RXD_82543) || + (em_rxd < EM_MIN_RXD)) { + device_printf(dev, "Using %d RX descriptors instead of %d!\n", + EM_DEFAULT_RXD, em_rxd); + adapter->num_rx_desc = EM_DEFAULT_RXD; + } else + adapter->num_rx_desc = em_rxd; + +#ifdef __rtems__ +#undef em_txd +#undef em_rxd + } +#endif + + adapter->hw.mac.autoneg = DO_AUTO_NEG; + adapter->hw.phy.wait_for_link = FALSE; + adapter->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT; + adapter->rx_buffer_len = 2048; + + e1000_init_script_state_82541(&adapter->hw, TRUE); + e1000_set_tbi_compatibility_82543(&adapter->hw, TRUE); + + /* Copper options */ + if (adapter->hw.media_type == e1000_media_type_copper) { + adapter->hw.phy.mdix = AUTO_ALL_MODES; + adapter->hw.phy.disable_polarity_correction = FALSE; + adapter->hw.phy.ms_type = EM_MASTER_SLAVE; + } + + /* + * Set the max frame size assuming standard ethernet + * sized frames. + */ + adapter->hw.mac.max_frame_size = + ETHERMTU + ETHER_HDR_LEN + ETHERNET_FCS_SIZE; + + adapter->hw.mac.min_frame_size = ETH_ZLEN + ETHERNET_FCS_SIZE; + + /* + * This controls when hardware reports transmit completion + * status. + */ + adapter->hw.mac.report_tx_early = 1; + + tsize = roundup2(adapter->num_tx_desc * sizeof(struct e1000_tx_desc), + EM_DBA_ALIGN); + + /* Allocate Transmit Descriptor ring */ + if (em_dma_malloc(adapter, tsize, &adapter->txdma, BUS_DMA_NOWAIT)) { + device_printf(dev, "Unable to allocate tx_desc memory\n"); + error = ENOMEM; + goto err_tx_desc; + } + adapter->tx_desc_base = + (struct e1000_tx_desc *)adapter->txdma.dma_vaddr; + + rsize = roundup2(adapter->num_rx_desc * sizeof(struct e1000_rx_desc), + EM_DBA_ALIGN); + + /* Allocate Receive Descriptor ring */ + if (em_dma_malloc(adapter, rsize, &adapter->rxdma, BUS_DMA_NOWAIT)) { + device_printf(dev, "Unable to allocate rx_desc memory\n"); + error = ENOMEM; + goto err_rx_desc; + } + adapter->rx_desc_base = + (struct e1000_rx_desc *)adapter->rxdma.dma_vaddr; + + /* Make sure we have a good EEPROM before we read from it */ + if (e1000_validate_nvm_checksum(&adapter->hw) < 0) { + /* + ** Some PCI-E parts fail the first check due to + ** the link being in sleep state, call it again, + ** if it fails a second time its a real issue. + */ + if (e1000_validate_nvm_checksum(&adapter->hw) < 0) { + device_printf(dev, + "The EEPROM Checksum Is Not Valid\n"); + error = EIO; + goto err_hw_init; + } + } + + if (e1000_read_part_num(&adapter->hw, &(adapter->part_num)) < 0) { + device_printf(dev, "EEPROM read error " + "reading part number\n"); + error = EIO; + goto err_hw_init; + } + + /* Initialize the hardware */ + if (em_hardware_init(adapter)) { + device_printf(dev, "Unable to initialize the hardware\n"); + error = EIO; + goto err_hw_init; + } + + /* Copy the permanent MAC address out of the EEPROM */ + if (e1000_read_mac_addr(&adapter->hw) < 0) { + device_printf(dev, "EEPROM read error while reading MAC" + " address\n"); + error = EIO; + goto err_hw_init; + } + + if (!em_is_valid_ether_addr((uint8_t*)adapter->hw.mac.addr)) { + device_printf(dev, "Invalid MAC address\n"); + error = EIO; + goto err_hw_init; + } + + /* Setup OS specific network interface */ + em_setup_interface(dev, adapter); + + em_allocate_intr(adapter); + + /* Initialize statistics */ + em_update_stats_counters(adapter); + + adapter->hw.mac.get_link_status = 1; + em_update_link_status(adapter); + + /* Indicate SOL/IDER usage */ + if (e1000_check_reset_block(&adapter->hw)) + device_printf(dev, + "PHY reset is blocked due to SOL/IDER session.\n"); + + /* Determine if we have to control management hardware */ + adapter->has_manage = e1000_enable_mng_pass_thru(&adapter->hw); + + /* + * Setup Wake-on-Lan + */ + switch (adapter->hw.mac.type) { + +#if !defined(NO_82542_SUPPORT) /* __rtems__ */ + case e1000_82542: +#endif + case e1000_82543: + break; + case e1000_82546: + case e1000_82546_rev_3: + case e1000_82571: + case e1000_80003es2lan: + if (adapter->hw.bus.func == 1) + e1000_read_nvm(&adapter->hw, + NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); + else + e1000_read_nvm(&adapter->hw, + NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); + eeprom_data &= EM_EEPROM_APME; + break; + default: + /* APME bit in EEPROM is mapped to WUC.APME */ + eeprom_data = E1000_READ_REG(&adapter->hw, E1000_WUC) & + E1000_WUC_APME; + break; + } + if (eeprom_data) + adapter->wol = E1000_WUFC_MAG; + /* + * We have the eeprom settings, now apply the special cases + * where the eeprom may be wrong or the board won't support + * wake on lan on a particular port + */ + device_id = pci_get_device(dev); + switch (device_id) { + case E1000_DEV_ID_82546GB_PCIE: + adapter->wol = 0; + break; + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546GB_FIBER: + case E1000_DEV_ID_82571EB_FIBER: + /* Wake events only supported on port A for dual fiber + * regardless of eeprom setting */ + if (E1000_READ_REG(&adapter->hw, E1000_STATUS) & + E1000_STATUS_FUNC_1) + adapter->wol = 0; + break; + case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: + case E1000_DEV_ID_82571EB_QUAD_COPPER: + case E1000_DEV_ID_82571EB_QUAD_FIBER: + case E1000_DEV_ID_82571EB_QUAD_COPPER_LP: + /* if quad port adapter, disable WoL on all but port A */ + if (global_quad_port_a != 0) + adapter->wol = 0; + /* Reset for multiple quad port adapters */ + if (++global_quad_port_a == 4) + global_quad_port_a = 0; + break; + } + + /* Do we need workaround for 82544 PCI-X adapter? */ + if (adapter->hw.bus.type == e1000_bus_type_pcix && + adapter->hw.mac.type == e1000_82544) + adapter->pcix_82544 = TRUE; + else + adapter->pcix_82544 = FALSE; + + /* Tell the stack that the interface is not active */ + adapter->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + INIT_DEBUGOUT("em_attach: end"); + + return (0); + +err_hw_init: + em_release_hw_control(adapter); + e1000_remove_device(&adapter->hw); + em_dma_free(adapter, &adapter->rxdma); +err_rx_desc: + em_dma_free(adapter, &adapter->txdma); +err_tx_desc: +err_pci: + em_free_intr(adapter); + em_free_pci_resources(adapter); + EM_LOCK_DESTROY(adapter); + + return (error); +} + +/********************************************************************* + * Device removal routine + * + * The detach entry point is called when the driver is being removed. + * This routine stops the adapter and deallocates all the resources + * that were allocated for driver operation. + * + * return 0 on success, positive on failure + *********************************************************************/ + +static int +em_detach(device_t dev) +{ + struct adapter *adapter = device_get_softc(dev); + struct ifnet *ifp = adapter->ifp; + + INIT_DEBUGOUT("em_detach: begin"); + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(ifp); +#endif + + em_disable_intr(adapter); + em_free_intr(adapter); + EM_LOCK(adapter); + adapter->in_detach = 1; + em_stop(adapter); + e1000_phy_hw_reset(&adapter->hw); + + em_release_manageability(adapter); + + if (((adapter->hw.mac.type == e1000_82573) || + (adapter->hw.mac.type == e1000_ich8lan) || + (adapter->hw.mac.type == e1000_ich9lan)) && + e1000_check_mng_mode(&adapter->hw)) + em_release_hw_control(adapter); + + if (adapter->wol) { + E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN); + E1000_WRITE_REG(&adapter->hw, E1000_WUFC, adapter->wol); + em_enable_wakeup(dev); + } + + EM_UNLOCK(adapter); + ether_ifdetach(adapter->ifp); + + callout_drain(&adapter->timer); + callout_drain(&adapter->tx_fifo_timer); + + em_free_pci_resources(adapter); + bus_generic_detach(dev); + if_free(ifp); + + e1000_remove_device(&adapter->hw); + em_free_transmit_structures(adapter); + em_free_receive_structures(adapter); + + /* Free Transmit Descriptor ring */ + if (adapter->tx_desc_base) { + em_dma_free(adapter, &adapter->txdma); + adapter->tx_desc_base = NULL; + } + + /* Free Receive Descriptor ring */ + if (adapter->rx_desc_base) { + em_dma_free(adapter, &adapter->rxdma); + adapter->rx_desc_base = NULL; + } + + EM_LOCK_DESTROY(adapter); + + return (0); +} + +#ifndef __rtems__ +/********************************************************************* + * + * Shutdown entry point + * + **********************************************************************/ + +static int +em_shutdown(device_t dev) +{ + return em_suspend(dev); +} + +/* + * Suspend/resume device methods. + */ +static int +em_suspend(device_t dev) +{ + struct adapter *adapter = device_get_softc(dev); + + EM_LOCK(adapter); + em_stop(adapter); + + em_release_manageability(adapter); + + if (((adapter->hw.mac.type == e1000_82573) || + (adapter->hw.mac.type == e1000_ich8lan) || + (adapter->hw.mac.type == e1000_ich9lan)) && + e1000_check_mng_mode(&adapter->hw)) + em_release_hw_control(adapter); + + if (adapter->wol) { + E1000_WRITE_REG(&adapter->hw, E1000_WUC, E1000_WUC_PME_EN); + E1000_WRITE_REG(&adapter->hw, E1000_WUFC, adapter->wol); + em_enable_wakeup(dev); + } + + EM_UNLOCK(adapter); + + return bus_generic_suspend(dev); +} + +static int +em_resume(device_t dev) +{ + struct adapter *adapter = device_get_softc(dev); + struct ifnet *ifp = adapter->ifp; + + EM_LOCK(adapter); + em_init_locked(adapter); + em_init_manageability(adapter); + + if ((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & IFF_DRV_RUNNING)) + em_start_locked(ifp); + + EM_UNLOCK(adapter); + + return bus_generic_resume(dev); +} +#endif + + +/********************************************************************* + * Transmit entry point + * + * em_start is called by the stack to initiate a transmit. + * The driver will remain in this routine as long as there are + * packets to transmit and transmit resources are available. + * In case resources are not available stack is notified and + * the packet is requeued. + **********************************************************************/ + +static void +em_start_locked(struct ifnet *ifp) +{ + struct adapter *adapter = ifp->if_softc; + struct mbuf *m_head; + + EM_LOCK_ASSERT(adapter); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + if (!adapter->link_active) + return; + + while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + /* + * Encapsulation can modify our pointer, and or make it + * NULL on failure. In that event, we can't requeue. + * + * We now use a pointer to accomodate legacy and + * advanced transmit functions. + */ + if (adapter->em_xmit(adapter, &m_head)) { + if (m_head == NULL) + break; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + break; + } + + /* Send a copy of the frame to the BPF listener */ + ETHER_BPF_MTAP(ifp, m_head); + + /* Set timeout in case hardware has problems transmitting. */ + adapter->watchdog_timer = EM_TX_TIMEOUT; + } +} + +static void +em_start(struct ifnet *ifp) +{ + struct adapter *adapter = ifp->if_softc; + + EM_LOCK(adapter); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + em_start_locked(ifp); + EM_UNLOCK(adapter); +} + +/********************************************************************* + * Ioctl entry point + * + * em_ioctl is called when the user wants to configure the + * interface. + * + * return 0 on success, positive on failure + **********************************************************************/ + +static int +#ifndef __rtems__ +em_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +#else +em_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) +#endif +{ + struct adapter *adapter = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int error = 0; + + if (adapter->in_detach) + return (error); + + switch (command) { + case SIOCSIFADDR: + if (ifa->ifa_addr->sa_family == AF_INET) { + /* + * XXX + * Since resetting hardware takes a very long time + * and results in link renegotiation we only + * initialize the hardware only when it is absolutely + * required. + */ + ifp->if_flags |= IFF_UP; + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + EM_LOCK(adapter); + em_init_locked(adapter); + EM_UNLOCK(adapter); + } + arp_ifinit(ifp, ifa); + } else + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFMTU: + { + int max_frame_size; + uint16_t eeprom_data = 0; + + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)"); + + EM_LOCK(adapter); + switch (adapter->hw.mac.type) { + case e1000_82573: + /* + * 82573 only supports jumbo frames + * if ASPM is disabled. + */ + e1000_read_nvm(&adapter->hw, + NVM_INIT_3GIO_3, 1, &eeprom_data); + if (eeprom_data & NVM_WORD1A_ASPM_MASK) { + max_frame_size = ETHER_MAX_LEN; + break; + } + /* Allow Jumbo frames - fall thru */ + case e1000_82571: + case e1000_82572: + case e1000_ich9lan: + case e1000_82575: + case e1000_80003es2lan: /* Limit Jumbo Frame size */ + max_frame_size = 9234; + break; + /* Adapters that do not support jumbo frames */ +#if !defined(NO_82542_SUPPORT) /* __rtems__ */ + case e1000_82542: +#endif + case e1000_ich8lan: + max_frame_size = ETHER_MAX_LEN; + break; + default: + max_frame_size = MAX_JUMBO_FRAME_SIZE; + } + if (ifr->ifr_mtu > max_frame_size - ETHER_HDR_LEN - + ETHER_CRC_LEN) { + EM_UNLOCK(adapter); + error = EINVAL; + break; + } + + ifp->if_mtu = ifr->ifr_mtu; + adapter->hw.mac.max_frame_size = + ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; + em_init_locked(adapter); + EM_UNLOCK(adapter); + break; + } + case SIOCSIFFLAGS: + IOCTL_DEBUGOUT("ioctl rcv'd:\ + SIOCSIFFLAGS (Set Interface Flags)"); + EM_LOCK(adapter); + if (ifp->if_flags & IFF_UP) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) { + if ((ifp->if_flags ^ adapter->if_flags) & + IFF_PROMISC) { + em_disable_promisc(adapter); + em_set_promisc(adapter); + } + } else + em_init_locked(adapter); + } else + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + em_stop(adapter); + adapter->if_flags = ifp->if_flags; + EM_UNLOCK(adapter); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + IOCTL_DEBUGOUT("ioctl rcv'd: SIOC(ADD|DEL)MULTI"); +#ifdef __rtems__ + if ( ETHER_SIOCMULTIFRAG(error, command, ifr, ifp) ) + break; +#endif + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + EM_LOCK(adapter); + em_disable_intr(adapter); + em_set_multi(adapter); +#if !defined(NO_82542_SUPPORT) /* __rtems__ */ + if (adapter->hw.mac.type == e1000_82542 && + adapter->hw.revision_id == E1000_REVISION_2) { + em_initialize_receive_unit(adapter); + } +#endif +#ifdef DEVICE_POLLING + if (!(ifp->if_capenable & IFCAP_POLLING)) +#endif + em_enable_intr(adapter); + EM_UNLOCK(adapter); + } +#ifdef __rtems__ + else + error = 0; +#endif + break; + case SIOCSIFMEDIA: + /* Check SOL/IDER usage */ + EM_LOCK(adapter); + if (e1000_check_reset_block(&adapter->hw)) { + EM_UNLOCK(adapter); + device_printf(adapter->dev, "Media change is" + " blocked due to SOL/IDER session.\n"); +#ifdef __rtems__ + /* it seems we should set error here */ + error = EBUSY; +#endif + break; + } + EM_UNLOCK(adapter); + case SIOCGIFMEDIA: + IOCTL_DEBUGOUT("ioctl rcv'd: \ + SIOCxIFMEDIA (Get/Set Interface Media)"); + error = ifmedia_ioctl(ifp, ifr, &adapter->media, command); + break; +#ifndef __rtems__ + case SIOCSIFCAP: + { + int mask, reinit; + + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFCAP (Set Capabilities)"); + reinit = 0; + mask = ifr->ifr_reqcap ^ ifp->if_capenable; +#ifdef DEVICE_POLLING + if (mask & IFCAP_POLLING) { + if (ifr->ifr_reqcap & IFCAP_POLLING) { + error = ether_poll_register(em_poll, ifp); + if (error) + return (error); + EM_LOCK(adapter); + em_disable_intr(adapter); + ifp->if_capenable |= IFCAP_POLLING; + EM_UNLOCK(adapter); + } else { + error = ether_poll_deregister(ifp); + /* Enable interrupt even in error case */ + EM_LOCK(adapter); + em_enable_intr(adapter); + ifp->if_capenable &= ~IFCAP_POLLING; + EM_UNLOCK(adapter); + } + } +#endif + if (mask & IFCAP_HWCSUM) { + ifp->if_capenable ^= IFCAP_HWCSUM; + reinit = 1; + } + if (mask & IFCAP_TSO4) { + ifp->if_capenable ^= IFCAP_TSO4; + reinit = 1; + } + if (mask & IFCAP_VLAN_HWTAGGING) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + reinit = 1; + } + if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING)) + em_init(adapter); + VLAN_CAPABILITIES(ifp); + break; + } +#endif + +#ifdef __rtems__ + case SIO_RTEMS_SHOW_STATS: + em_print_hw_stats(adapter); + error = 0; + break; +#endif + + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return (error); +} + +/********************************************************************* + * Watchdog timer: + * + * This routine is called from the local timer every second. + * As long as transmit descriptors are being cleaned the value + * is non-zero and we do nothing. Reaching 0 indicates a tx hang + * and we then reset the device. + * + **********************************************************************/ + +static void +em_watchdog(struct adapter *adapter) +{ + + EM_LOCK_ASSERT(adapter); + + /* + ** The timer is set to 5 every time start queues a packet. + ** Then txeof keeps resetting to 5 as long as it cleans at + ** least one descriptor. + ** Finally, anytime all descriptors are clean the timer is + ** set to 0. + */ + if (adapter->watchdog_timer == 0 || --adapter->watchdog_timer) + return; + + /* If we are in this routine because of pause frames, then + * don't reset the hardware. + */ + if (E1000_READ_REG(&adapter->hw, E1000_STATUS) & + E1000_STATUS_TXOFF) { + adapter->watchdog_timer = EM_TX_TIMEOUT; + return; + } + + if (e1000_check_for_link(&adapter->hw) == 0) + device_printf(adapter->dev, "watchdog timeout -- resetting\n"); + adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + adapter->watchdog_events++; + + em_init_locked(adapter); +} + +/********************************************************************* + * Init entry point + * + * This routine is used in two ways. It is used by the stack as + * init entry point in network interface structure. It is also used + * by the driver as a hw/sw initialization routine to get to a + * consistent state. + * + * return 0 on success, positive on failure + **********************************************************************/ + +static void +em_init_locked(struct adapter *adapter) +{ + struct ifnet *ifp = adapter->ifp; + device_t dev = adapter->dev; + uint32_t pba; + + INIT_DEBUGOUT("em_init: begin"); + + EM_LOCK_ASSERT(adapter); + + em_stop(adapter); + + /* + * Packet Buffer Allocation (PBA) + * Writing PBA sets the receive portion of the buffer + * the remainder is used for the transmit buffer. + * + * Devices before the 82547 had a Packet Buffer of 64K. + * Default allocation: PBA=48K for Rx, leaving 16K for Tx. + * After the 82547 the buffer was reduced to 40K. + * Default allocation: PBA=30K for Rx, leaving 10K for Tx. + * Note: default does not leave enough room for Jumbo Frame >10k. + */ + switch (adapter->hw.mac.type) { + case e1000_82547: + case e1000_82547_rev_2: /* 82547: Total Packet Buffer is 40K */ + if (adapter->hw.mac.max_frame_size > 8192) + pba = E1000_PBA_22K; /* 22K for Rx, 18K for Tx */ + else + pba = E1000_PBA_30K; /* 30K for Rx, 10K for Tx */ + adapter->tx_fifo_head = 0; + adapter->tx_head_addr = pba << EM_TX_HEAD_ADDR_SHIFT; + adapter->tx_fifo_size = + (E1000_PBA_40K - pba) << EM_PBA_BYTES_SHIFT; + break; + /* Total Packet Buffer on these is 48K */ + case e1000_82571: + case e1000_82572: + case e1000_82575: + case e1000_80003es2lan: + pba = E1000_PBA_32K; /* 32K for Rx, 16K for Tx */ + break; + case e1000_82573: /* 82573: Total Packet Buffer is 32K */ + pba = E1000_PBA_12K; /* 12K for Rx, 20K for Tx */ + break; + case e1000_ich9lan: +#define E1000_PBA_10K 0x000A + pba = E1000_PBA_10K; + break; + case e1000_ich8lan: + pba = E1000_PBA_8K; + break; + default: + /* Devices before 82547 had a Packet Buffer of 64K. */ + if (adapter->hw.mac.max_frame_size > 8192) + pba = E1000_PBA_40K; /* 40K for Rx, 24K for Tx */ + else + pba = E1000_PBA_48K; /* 48K for Rx, 16K for Tx */ + } + + INIT_DEBUGOUT1("em_init: pba=%dK",(int)pba); + E1000_WRITE_REG(&adapter->hw, E1000_PBA, pba); + + /* Get the latest mac address, User can use a LAA */ + bcopy(IF_LLADDR(adapter->ifp), adapter->hw.mac.addr, + ETHER_ADDR_LEN); + + /* Put the address into the Receive Address Array */ + e1000_rar_set(&adapter->hw, adapter->hw.mac.addr, 0); + + /* + * With 82571 controllers, LAA may be overwritten + * due to controller reset from the other port. + */ + if (adapter->hw.mac.type == e1000_82571) + e1000_set_laa_state_82571(&adapter->hw, TRUE); + + /* Initialize the hardware */ + if (em_hardware_init(adapter)) { + device_printf(dev, "Unable to initialize the hardware\n"); + return; + } + em_update_link_status(adapter); + +#ifndef __rtems__ + if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) + em_enable_vlans(adapter); + + /* Set hardware offload abilities */ + ifp->if_hwassist = 0; + if (adapter->hw.mac.type >= e1000_82543) { + if (ifp->if_capenable & IFCAP_TXCSUM) + ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); + if (ifp->if_capenable & IFCAP_TSO4) + ifp->if_hwassist |= CSUM_TSO; + } +#endif + + /* Configure for OS presence */ + em_init_manageability(adapter); + + /* Prepare transmit descriptors and buffers */ + if (em_setup_transmit_structures(adapter)) { + device_printf(dev, "Could not setup transmit structures\n"); + em_stop(adapter); + return; + } + em_initialize_transmit_unit(adapter); + + /* Setup Multicast table */ + em_set_multi(adapter); + + /* Prepare receive descriptors and buffers */ + if (em_setup_receive_structures(adapter)) { + device_printf(dev, "Could not setup receive structures\n"); + em_stop(adapter); + return; + } + em_initialize_receive_unit(adapter); + + /* Don't lose promiscuous settings */ + em_set_promisc(adapter); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&adapter->timer, hz, em_local_timer, adapter); + e1000_clear_hw_cntrs_base_generic(&adapter->hw); + +#ifdef DEVICE_POLLING + /* + * Only enable interrupts if we are not polling, make sure + * they are off otherwise. + */ + if (ifp->if_capenable & IFCAP_POLLING) + em_disable_intr(adapter); + else +#endif /* DEVICE_POLLING */ + em_enable_intr(adapter); + + /* Don't reset the phy next time init gets called */ + adapter->hw.phy.reset_disable = TRUE; +} + +static void +em_init(void *arg) +{ + struct adapter *adapter = arg; + + EM_LOCK(adapter); + em_init_locked(adapter); + EM_UNLOCK(adapter); +} + + +#ifdef DEVICE_POLLING +/********************************************************************* + * + * Legacy polling routine + * + *********************************************************************/ +static void +em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct adapter *adapter = ifp->if_softc; + uint32_t reg_icr; + + EM_LOCK(adapter); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + EM_UNLOCK(adapter); + return; + } + + if (cmd == POLL_AND_CHECK_STATUS) { + reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR); + if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { + callout_stop(&adapter->timer); + adapter->hw.mac.get_link_status = 1; + e1000_check_for_link(&adapter->hw); + em_update_link_status(adapter); + callout_reset(&adapter->timer, hz, + em_local_timer, adapter); + } + } + em_rxeof(adapter, count); + em_txeof(adapter); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp); + EM_UNLOCK(adapter); +} + +/********************************************************************* + * + * Legacy Interrupt Service routine + * + *********************************************************************/ + +static void +em_intr(void *arg) +{ + struct adapter *adapter = arg; + struct ifnet *ifp; + uint32_t reg_icr; + + EM_LOCK(adapter); + ifp = adapter->ifp; + + if (ifp->if_capenable & IFCAP_POLLING) { + EM_UNLOCK(adapter); + return; + } + + for (;;) { + reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR); + + if (adapter->hw.mac.type >= e1000_82571 && + (reg_icr & E1000_ICR_INT_ASSERTED) == 0) + break; + else if (reg_icr == 0) + break; + + /* + * XXX: some laptops trigger several spurious interrupts + * on em(4) when in the resume cycle. The ICR register + * reports all-ones value in this case. Processing such + * interrupts would lead to a freeze. I don't know why. + */ + if (reg_icr == 0xffffffff) + break; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + em_rxeof(adapter, -1); + em_txeof(adapter); + } + + /* Link status change */ + if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { + callout_stop(&adapter->timer); + adapter->hw.mac.get_link_status = 1; + e1000_check_for_link(&adapter->hw); + em_update_link_status(adapter); + callout_reset(&adapter->timer, hz, + em_local_timer, adapter); + } + + if (reg_icr & E1000_ICR_RXO) + adapter->rx_overruns++; + } + + if (ifp->if_drv_flags & IFF_DRV_RUNNING && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp); + EM_UNLOCK(adapter); +} + +#else /* if not DEVICE_POLLING, then fast interrupt routines only */ + +static void +em_handle_link(void *context, int pending) +{ + struct adapter *adapter = context; + struct ifnet *ifp; + + ifp = adapter->ifp; + + EM_LOCK(adapter); + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + EM_UNLOCK(adapter); + return; + } + + callout_stop(&adapter->timer); + adapter->hw.mac.get_link_status = 1; + e1000_check_for_link(&adapter->hw); + em_update_link_status(adapter); + callout_reset(&adapter->timer, hz, em_local_timer, adapter); + EM_UNLOCK(adapter); +} + +int em_rxtx_irqs=0; +int em_rxtx_hirqs=0; + +static void +em_handle_rxtx(void *context, int pending) +{ + struct adapter *adapter = context; + struct ifnet *ifp; + + em_rxtx_irqs ++ ; + + NET_LOCK_GIANT(); + ifp = adapter->ifp; + + /* + * TODO: + * It should be possible to run the tx clean loop without the lock. + */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (em_rxeof(adapter, adapter->rx_process_limit) != 0) + taskqueue_enqueue(adapter->tq, &adapter->rxtx_task); + EM_LOCK(adapter); + em_txeof(adapter); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp); + EM_UNLOCK(adapter); + } + + em_enable_intr(adapter); + NET_UNLOCK_GIANT(); +} + +/********************************************************************* + * + * Fast Interrupt Service routine + * + *********************************************************************/ +static int +em_intr_fast(void *arg) +{ + struct adapter *adapter = arg; + struct ifnet *ifp; + uint32_t reg_icr; + + ifp = adapter->ifp; + + reg_icr = E1000_READ_REG(&adapter->hw, E1000_ICR); + + /* Hot eject? */ + if (reg_icr == 0xffffffff) + return (FILTER_STRAY); + + /* Definitely not our interrupt. */ + if (reg_icr == 0x0) + return (FILTER_STRAY); + + /* + * Starting with the 82571 chip, bit 31 should be used to + * determine whether the interrupt belongs to us. + */ + if (adapter->hw.mac.type >= e1000_82571 && + (reg_icr & E1000_ICR_INT_ASSERTED) == 0) + return (FILTER_STRAY); + + /* + * Mask interrupts until the taskqueue is finished running. This is + * cheap, just assume that it is needed. This also works around the + * MSI message reordering errata on certain systems. + */ + em_disable_intr(adapter); + taskqueue_enqueue(adapter->tq, &adapter->rxtx_task); + em_rxtx_hirqs++; + + /* Link status change */ + if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) + taskqueue_enqueue(taskqueue_fast, &adapter->link_task); + + if (reg_icr & E1000_ICR_RXO) + adapter->rx_overruns++; + return (FILTER_HANDLED); +} +#endif /* ! DEVICE_POLLING */ + +/********************************************************************* + * + * Media Ioctl callback + * + * This routine is called whenever the user queries the status of + * the interface using ifconfig. + * + **********************************************************************/ +static void +em_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct adapter *adapter = ifp->if_softc; + u_char fiber_type = IFM_1000_SX; + + INIT_DEBUGOUT("em_media_status: begin"); + + EM_LOCK(adapter); + e1000_check_for_link(&adapter->hw); + em_update_link_status(adapter); + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (!adapter->link_active) { + EM_UNLOCK(adapter); + return; + } + + ifmr->ifm_status |= IFM_ACTIVE; + + if ((adapter->hw.media_type == e1000_media_type_fiber) || + (adapter->hw.media_type == e1000_media_type_internal_serdes)) { + if (adapter->hw.mac.type == e1000_82545) + fiber_type = IFM_1000_LX; + ifmr->ifm_active |= fiber_type | IFM_FDX; + } else { + switch (adapter->link_speed) { + case 10: + ifmr->ifm_active |= IFM_10_T; + break; + case 100: + ifmr->ifm_active |= IFM_100_TX; + break; + case 1000: + ifmr->ifm_active |= IFM_1000_T; + break; + } + if (adapter->link_duplex == FULL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + } + EM_UNLOCK(adapter); +} + +/********************************************************************* + * + * Media Ioctl callback + * + * This routine is called when the user changes speed/duplex using + * media/mediopt option with ifconfig. + * + **********************************************************************/ +static int +em_media_change(struct ifnet *ifp) +{ + struct adapter *adapter = ifp->if_softc; + struct ifmedia *ifm = &adapter->media; + + INIT_DEBUGOUT("em_media_change: begin"); + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return (EINVAL); + + EM_LOCK(adapter); + switch (IFM_SUBTYPE(ifm->ifm_media)) { + case IFM_AUTO: + adapter->hw.mac.autoneg = DO_AUTO_NEG; + adapter->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT; + break; + case IFM_1000_LX: + case IFM_1000_SX: + case IFM_1000_T: + adapter->hw.mac.autoneg = DO_AUTO_NEG; + adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL; + break; + case IFM_100_TX: + adapter->hw.mac.autoneg = FALSE; + adapter->hw.phy.autoneg_advertised = 0; + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) + adapter->hw.mac.forced_speed_duplex = ADVERTISE_100_FULL; + else + adapter->hw.mac.forced_speed_duplex = ADVERTISE_100_HALF; + break; + case IFM_10_T: + adapter->hw.mac.autoneg = FALSE; + adapter->hw.phy.autoneg_advertised = 0; + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) + adapter->hw.mac.forced_speed_duplex = ADVERTISE_10_FULL; + else + adapter->hw.mac.forced_speed_duplex = ADVERTISE_10_HALF; + break; + default: + device_printf(adapter->dev, "Unsupported media type\n"); + } + + /* As the speed/duplex settings my have changed we need to + * reset the PHY. + */ + adapter->hw.phy.reset_disable = FALSE; + + em_init_locked(adapter); + EM_UNLOCK(adapter); + + return (0); +} + +/********************************************************************* + * + * This routine maps the mbufs to tx descriptors. + * + * return 0 on success, positive on failure + **********************************************************************/ + +static int +em_encap(struct adapter *adapter, struct mbuf **m_headp) +{ + bus_dma_segment_t segs[EM_MAX_SCATTER]; + bus_dmamap_t map; + struct em_buffer *tx_buffer, *tx_buffer_mapped; + struct e1000_tx_desc *ctxd = NULL; + struct mbuf *m_head; + uint32_t txd_upper, txd_lower, txd_used, txd_saved; + int nsegs, i, j, first, last = 0; + int error, do_tso, tso_desc = 0; + + m_head = *m_headp; + txd_upper = txd_lower = txd_used = txd_saved = 0; + +#ifndef __rtems__ + do_tso = ((m_head->m_pkthdr.csum_flags & CSUM_TSO) != 0); +#else + do_tso = 0; +#endif + + /* + * Force a cleanup if number of TX descriptors + * available hits the threshold + */ + if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) { + em_txeof(adapter); + /* Now do we at least have a minimal? */ + if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD) { + adapter->no_tx_desc_avail1++; + return (ENOBUFS); + } + } + + + /* + * TSO workaround: + * If an mbuf is only header we need + * to pull 4 bytes of data into it. + */ + if (do_tso && (m_head->m_len <= M_TSO_LEN)) { + m_head = m_pullup(m_head, M_TSO_LEN + 4); + *m_headp = m_head; + if (m_head == NULL) + return (ENOBUFS); + } + + /* + * Map the packet for DMA + * + * Capture the first descriptor index, + * this descriptor will have the index + * of the EOP which is the only one that + * now gets a DONE bit writeback. + */ + first = adapter->next_avail_tx_desc; + tx_buffer = &adapter->tx_buffer_area[first]; + tx_buffer_mapped = tx_buffer; + map = tx_buffer->map; + + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, + *m_headp, segs, &nsegs, BUS_DMA_NOWAIT); + + /* + * There are two types of errors we can (try) to handle: + * - EFBIG means the mbuf chain was too long and bus_dma ran + * out of segments. Defragment the mbuf chain and try again. + * - ENOMEM means bus_dma could not obtain enough bounce buffers + * at this point in time. Defer sending and try again later. + * All other errors, in particular EINVAL, are fatal and prevent the + * mbuf chain from ever going through. Drop it and report error. + */ + if (error == EFBIG) { + struct mbuf *m; + + m = m_defrag(*m_headp, M_DONTWAIT); + if (m == NULL) { + adapter->mbuf_alloc_failed++; + m_freem(*m_headp); + *m_headp = NULL; + return (ENOBUFS); + } + *m_headp = m; + + /* Try it again */ + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, + *m_headp, segs, &nsegs, BUS_DMA_NOWAIT); + + if (error == ENOMEM) { + adapter->no_tx_dma_setup++; + return (error); + } else if (error != 0) { + adapter->no_tx_dma_setup++; + m_freem(*m_headp); + *m_headp = NULL; + return (error); + } + } else if (error == ENOMEM) { + adapter->no_tx_dma_setup++; + return (error); + } else if (error != 0) { + adapter->no_tx_dma_setup++; + m_freem(*m_headp); + *m_headp = NULL; + return (error); + } + + /* + * TSO Hardware workaround, if this packet is not + * TSO, and is only a single descriptor long, and + * it follows a TSO burst, then we need to add a + * sentinel descriptor to prevent premature writeback. + */ + if ((do_tso == 0) && (adapter->tx_tso == TRUE)) { + if (nsegs == 1) + tso_desc = TRUE; + adapter->tx_tso = FALSE; + } + + if (nsegs > (adapter->num_tx_desc_avail - 2)) { + adapter->no_tx_desc_avail2++; + bus_dmamap_unload(adapter->txtag, map); + return (ENOBUFS); + } + m_head = *m_headp; + +#ifndef __rtems__ + /* Do hardware assists */ + if (em_tso_setup(adapter, m_head, &txd_upper, &txd_lower)) + /* we need to make a final sentinel transmit desc */ + tso_desc = TRUE; + else if (m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) + em_transmit_checksum_setup(adapter, m_head, + &txd_upper, &txd_lower); +#endif + + i = adapter->next_avail_tx_desc; + if (adapter->pcix_82544) + txd_saved = i; + + /* Set up our transmit descriptors */ + for (j = 0; j < nsegs; j++) { + bus_size_t seg_len; + bus_addr_t seg_addr; + /* If adapter is 82544 and on PCIX bus */ + if(adapter->pcix_82544) { + DESC_ARRAY desc_array; + uint32_t array_elements, counter; + /* + * Check the Address and Length combination and + * split the data accordingly + */ + array_elements = em_fill_descriptors(segs[j].ds_addr, + segs[j].ds_len, &desc_array); + for (counter = 0; counter < array_elements; counter++) { + if (txd_used == adapter->num_tx_desc_avail) { + adapter->next_avail_tx_desc = txd_saved; + adapter->no_tx_desc_avail2++; + bus_dmamap_unload(adapter->txtag, map); + return (ENOBUFS); + } + tx_buffer = &adapter->tx_buffer_area[i]; + ctxd = &adapter->tx_desc_base[i]; + ctxd->buffer_addr = htole64( + desc_array.descriptor[counter].address); + ctxd->lower.data = htole32( + (adapter->txd_cmd | txd_lower | (uint16_t) + desc_array.descriptor[counter].length)); + ctxd->upper.data = + htole32((txd_upper)); + last = i; + if (++i == adapter->num_tx_desc) + i = 0; + tx_buffer->m_head = NULL; + tx_buffer->next_eop = -1; + txd_used++; + } + } else { + tx_buffer = &adapter->tx_buffer_area[i]; + ctxd = &adapter->tx_desc_base[i]; + seg_addr = segs[j].ds_addr; + seg_len = segs[j].ds_len; + /* + ** TSO Workaround: + ** If this is the last descriptor, we want to + ** split it so we have a small final sentinel + */ + if (tso_desc && (j == (nsegs -1)) && (seg_len > 8)) { + seg_len -= 4; + ctxd->buffer_addr = htole64(seg_addr); + ctxd->lower.data = htole32( + adapter->txd_cmd | txd_lower | seg_len); + ctxd->upper.data = + htole32(txd_upper); + if (++i == adapter->num_tx_desc) + i = 0; + /* Now make the sentinel */ + ++txd_used; /* using an extra txd */ + ctxd = &adapter->tx_desc_base[i]; + tx_buffer = &adapter->tx_buffer_area[i]; + ctxd->buffer_addr = + htole64(seg_addr + seg_len); + ctxd->lower.data = htole32( + adapter->txd_cmd | txd_lower | 4); + ctxd->upper.data = + htole32(txd_upper); + last = i; + if (++i == adapter->num_tx_desc) + i = 0; + } else { + ctxd->buffer_addr = htole64(seg_addr); + ctxd->lower.data = htole32( + adapter->txd_cmd | txd_lower | seg_len); + ctxd->upper.data = + htole32(txd_upper); + last = i; + if (++i == adapter->num_tx_desc) + i = 0; + } + tx_buffer->m_head = NULL; + tx_buffer->next_eop = -1; + } + } + + adapter->next_avail_tx_desc = i; + if (adapter->pcix_82544) + adapter->num_tx_desc_avail -= txd_used; + else { + adapter->num_tx_desc_avail -= nsegs; + if (tso_desc) /* TSO used an extra for sentinel */ + adapter->num_tx_desc_avail -= txd_used; + } + +#ifndef __rtems__ + if (m_head->m_flags & M_VLANTAG) { + /* Set the vlan id. */ + ctxd->upper.fields.special = + htole16(m_head->m_pkthdr.ether_vtag); + /* Tell hardware to add tag */ + ctxd->lower.data |= htole32(E1000_TXD_CMD_VLE); + } +#endif + + tx_buffer->m_head = m_head; + tx_buffer_mapped->map = tx_buffer->map; + tx_buffer->map = map; + bus_dmamap_sync(adapter->txtag, map, BUS_DMASYNC_PREWRITE); + + /* + * Last Descriptor of Packet + * needs End Of Packet (EOP) + * and Report Status (RS) + */ + ctxd->lower.data |= + htole32(E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS); + /* + * Keep track in the first buffer which + * descriptor will be written back + */ + tx_buffer = &adapter->tx_buffer_area[first]; + tx_buffer->next_eop = last; + + /* + * Advance the Transmit Descriptor Tail (TDT), this tells the E1000 + * that this frame is available to transmit. + */ + bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + if (adapter->hw.mac.type == e1000_82547 && + adapter->link_duplex == HALF_DUPLEX) + em_82547_move_tail(adapter); + else { + E1000_WRITE_REG(&adapter->hw, E1000_TDT, i); + if (adapter->hw.mac.type == e1000_82547) + em_82547_update_fifo_head(adapter, + m_head->m_pkthdr.len); + } + + return (0); +} + +/********************************************************************* + * + * This routine maps the mbufs to Advanced TX descriptors. + * used by the 82575 adapter. It also needs no workarounds. + * + **********************************************************************/ + +static int +em_adv_encap(struct adapter *adapter, struct mbuf **m_headp) +{ + bus_dma_segment_t segs[EM_MAX_SCATTER]; + bus_dmamap_t map; + struct em_buffer *tx_buffer, *tx_buffer_mapped; + union e1000_adv_tx_desc *txd = NULL; + struct mbuf *m_head; + u32 olinfo_status = 0, cmd_type_len = 0; +#ifndef __rtems__ + u32 paylen = 0; +#endif + int nsegs, i, j, error, first, last = 0; + + m_head = *m_headp; + + + /* Set basic descriptor constants */ + cmd_type_len |= E1000_ADVTXD_DTYP_DATA; + cmd_type_len |= E1000_ADVTXD_DCMD_IFCS | E1000_ADVTXD_DCMD_DEXT; + + /* + * Force a cleanup if number of TX descriptors + * available hits the threshold + */ + if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) { + em_txeof(adapter); + /* Now do we at least have a minimal? */ + if (adapter->num_tx_desc_avail <= EM_TX_OP_THRESHOLD) { + adapter->no_tx_desc_avail1++; + return (ENOBUFS); + } + } + + /* + * Map the packet for DMA. + * + * Capture the first descriptor index, + * this descriptor will have the index + * of the EOP which is the only one that + * now gets a DONE bit writeback. + */ + first = adapter->next_avail_tx_desc; + tx_buffer = &adapter->tx_buffer_area[first]; + tx_buffer_mapped = tx_buffer; + map = tx_buffer->map; + + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, + *m_headp, segs, &nsegs, BUS_DMA_NOWAIT); + + if (error == EFBIG) { + struct mbuf *m; + + m = m_defrag(*m_headp, M_DONTWAIT); + if (m == NULL) { + adapter->mbuf_alloc_failed++; + m_freem(*m_headp); + *m_headp = NULL; + return (ENOBUFS); + } + *m_headp = m; + + /* Try it again */ + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, + *m_headp, segs, &nsegs, BUS_DMA_NOWAIT); + + if (error == ENOMEM) { + adapter->no_tx_dma_setup++; + return (error); + } else if (error != 0) { + adapter->no_tx_dma_setup++; + m_freem(*m_headp); + *m_headp = NULL; + return (error); + } + } else if (error == ENOMEM) { + adapter->no_tx_dma_setup++; + return (error); + } else if (error != 0) { + adapter->no_tx_dma_setup++; + m_freem(*m_headp); + *m_headp = NULL; + return (error); + } + + /* Check again to be sure we have enough descriptors */ + if (nsegs > (adapter->num_tx_desc_avail - 2)) { + adapter->no_tx_desc_avail2++; + bus_dmamap_unload(adapter->txtag, map); + return (ENOBUFS); + } + m_head = *m_headp; + +#ifndef __rtems__ + /* + * Set up the context descriptor: + * used when any hardware offload is done. + * This includes CSUM, VLAN, and TSO. It + * will use the first descriptor. + */ + /* First try TSO */ + if (em_tso_adv_setup(adapter, m_head, &paylen)) { + cmd_type_len |= E1000_ADVTXD_DCMD_TSE; + olinfo_status |= E1000_TXD_POPTS_IXSM << 8; + olinfo_status |= E1000_TXD_POPTS_TXSM << 8; + olinfo_status |= paylen << E1000_ADVTXD_PAYLEN_SHIFT; + } else if (m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) { + if (em_tx_adv_ctx_setup(adapter, m_head)) + olinfo_status |= E1000_TXD_POPTS_TXSM << 8; + } +#endif + + /* Set up our transmit descriptors */ + i = adapter->next_avail_tx_desc; + for (j = 0; j < nsegs; j++) { + bus_size_t seg_len; + bus_addr_t seg_addr; + + tx_buffer = &adapter->tx_buffer_area[i]; + txd = (union e1000_adv_tx_desc *)&adapter->tx_desc_base[i]; + seg_addr = segs[j].ds_addr; + seg_len = segs[j].ds_len; + + txd->read.buffer_addr = htole64(seg_addr); + txd->read.cmd_type_len = htole32( + adapter->txd_cmd | cmd_type_len | seg_len); + txd->read.olinfo_status = htole32(olinfo_status); + last = i; + if (++i == adapter->num_tx_desc) + i = 0; + tx_buffer->m_head = NULL; + tx_buffer->next_eop = -1; + } + + adapter->next_avail_tx_desc = i; + adapter->num_tx_desc_avail -= nsegs; + + tx_buffer->m_head = m_head; + tx_buffer_mapped->map = tx_buffer->map; + tx_buffer->map = map; + bus_dmamap_sync(adapter->txtag, map, BUS_DMASYNC_PREWRITE); + + /* + * Last Descriptor of Packet + * needs End Of Packet (EOP) + * and Report Status (RS) + */ + txd->read.cmd_type_len |= + htole32(E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS); + /* + * Keep track in the first buffer which + * descriptor will be written back + */ + tx_buffer = &adapter->tx_buffer_area[first]; + tx_buffer->next_eop = last; + + /* + * Advance the Transmit Descriptor Tail (TDT), this tells the E1000 + * that this frame is available to transmit. + */ + bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + E1000_WRITE_REG(&adapter->hw, E1000_TDT, i); + + return (0); + +} + +/********************************************************************* + * + * 82547 workaround to avoid controller hang in half-duplex environment. + * The workaround is to avoid queuing a large packet that would span + * the internal Tx FIFO ring boundary. We need to reset the FIFO pointers + * in this case. We do that only when FIFO is quiescent. + * + **********************************************************************/ +static void +em_82547_move_tail(void *arg) +{ + struct adapter *adapter = arg; + uint16_t hw_tdt; + uint16_t sw_tdt; + struct e1000_tx_desc *tx_desc; + uint16_t length = 0; + boolean_t eop = 0; + + EM_LOCK_ASSERT(adapter); + + hw_tdt = E1000_READ_REG(&adapter->hw, E1000_TDT); + sw_tdt = adapter->next_avail_tx_desc; + + while (hw_tdt != sw_tdt) { + tx_desc = &adapter->tx_desc_base[hw_tdt]; + length += tx_desc->lower.flags.length; + eop = tx_desc->lower.data & E1000_TXD_CMD_EOP; + if (++hw_tdt == adapter->num_tx_desc) + hw_tdt = 0; + + if (eop) { + if (em_82547_fifo_workaround(adapter, length)) { + adapter->tx_fifo_wrk_cnt++; + callout_reset(&adapter->tx_fifo_timer, 1, + em_82547_move_tail, adapter); + break; + } + E1000_WRITE_REG(&adapter->hw, E1000_TDT, hw_tdt); + em_82547_update_fifo_head(adapter, length); + length = 0; + } + } +} + +static int +em_82547_fifo_workaround(struct adapter *adapter, int len) +{ + int fifo_space, fifo_pkt_len; + + fifo_pkt_len = roundup2(len + EM_FIFO_HDR, EM_FIFO_HDR); + + if (adapter->link_duplex == HALF_DUPLEX) { + fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head; + + if (fifo_pkt_len >= (EM_82547_PKT_THRESH + fifo_space)) { + if (em_82547_tx_fifo_reset(adapter)) + return (0); + else + return (1); + } + } + + return (0); +} + +static void +em_82547_update_fifo_head(struct adapter *adapter, int len) +{ + int fifo_pkt_len = roundup2(len + EM_FIFO_HDR, EM_FIFO_HDR); + + /* tx_fifo_head is always 16 byte aligned */ + adapter->tx_fifo_head += fifo_pkt_len; + if (adapter->tx_fifo_head >= adapter->tx_fifo_size) { + adapter->tx_fifo_head -= adapter->tx_fifo_size; + } +} + + +static int +em_82547_tx_fifo_reset(struct adapter *adapter) +{ + uint32_t tctl; + + if ((E1000_READ_REG(&adapter->hw, E1000_TDT) == + E1000_READ_REG(&adapter->hw, E1000_TDH)) && + (E1000_READ_REG(&adapter->hw, E1000_TDFT) == + E1000_READ_REG(&adapter->hw, E1000_TDFH)) && + (E1000_READ_REG(&adapter->hw, E1000_TDFTS) == + E1000_READ_REG(&adapter->hw, E1000_TDFHS)) && + (E1000_READ_REG(&adapter->hw, E1000_TDFPC) == 0)) { + /* Disable TX unit */ + tctl = E1000_READ_REG(&adapter->hw, E1000_TCTL); + E1000_WRITE_REG(&adapter->hw, E1000_TCTL, + tctl & ~E1000_TCTL_EN); + + /* Reset FIFO pointers */ + E1000_WRITE_REG(&adapter->hw, E1000_TDFT, + adapter->tx_head_addr); + E1000_WRITE_REG(&adapter->hw, E1000_TDFH, + adapter->tx_head_addr); + E1000_WRITE_REG(&adapter->hw, E1000_TDFTS, + adapter->tx_head_addr); + E1000_WRITE_REG(&adapter->hw, E1000_TDFHS, + adapter->tx_head_addr); + + /* Re-enable TX unit */ + E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl); + E1000_WRITE_FLUSH(&adapter->hw); + + adapter->tx_fifo_head = 0; + adapter->tx_fifo_reset_cnt++; + + return (TRUE); + } + else { + return (FALSE); + } +} + +static void +em_set_promisc(struct adapter *adapter) +{ + struct ifnet *ifp = adapter->ifp; + uint32_t reg_rctl; + + reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL); + + if (ifp->if_flags & IFF_PROMISC) { + reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl); + } else if (ifp->if_flags & IFF_ALLMULTI) { + reg_rctl |= E1000_RCTL_MPE; + reg_rctl &= ~E1000_RCTL_UPE; + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl); + } +} + +static void +em_disable_promisc(struct adapter *adapter) +{ + uint32_t reg_rctl; + + reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL); + + reg_rctl &= (~E1000_RCTL_UPE); + reg_rctl &= (~E1000_RCTL_MPE); + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl); +} + + +/********************************************************************* + * Multicast Update + * + * This routine is called whenever multicast address list is updated. + * + **********************************************************************/ + +static void +em_set_multi(struct adapter *adapter) +{ + struct ifnet *ifp = adapter->ifp; +#ifndef __rtems__ + struct ifmultiaddr *ifma; +#endif + uint32_t reg_rctl = 0; + uint8_t mta[MAX_NUM_MULTICAST_ADDRESSES * ETH_ADDR_LEN]; /* Largest MTS is 4096 bits */ + int mcnt = 0; + + IOCTL_DEBUGOUT("em_set_multi: begin"); + +#if !defined(NO_82542_SUPPORT) /* __rtems__ */ + if (adapter->hw.mac.type == e1000_82542 && + adapter->hw.revision_id == E1000_REVISION_2) { + reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL); + if (adapter->hw.bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) + e1000_pci_clear_mwi(&adapter->hw); + reg_rctl |= E1000_RCTL_RST; + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl); + msec_delay(5); + } +#endif + +#ifndef __rtems__ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + if (mcnt == MAX_NUM_MULTICAST_ADDRESSES) + break; + + bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + &mta[mcnt * ETH_ADDR_LEN], ETH_ADDR_LEN); + mcnt++; + } + IF_ADDR_UNLOCK(ifp); +#else + { + /* This is untested -- in particular I don't know how to + * handle address ranges in the ether_multi struct + */ + struct ether_multi *enm; + struct ether_multistep step; + ETHER_FIRST_MULTI(step, (struct arpcom *)ifp, enm); + while ( enm != NULL ) { + if ( mcnt == MAX_NUM_MULTICAST_ADDRESSES ) + break; + if ( memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) ) { + printk("if_em: Unable to handle multicast wildcard (not ported yet); skipping\n"); + goto skiptonext; + } else { + bcopy(enm->enm_addrlo, &mta[mcnt * ETH_ADDR_LEN], ETH_ADDR_LEN); + } + mcnt++; + skiptonext: + ETHER_NEXT_MULTI( step, enm ); + } + } +#endif + + if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) { + reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL); + reg_rctl |= E1000_RCTL_MPE; + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl); + } else + e1000_mc_addr_list_update(&adapter->hw, mta, + mcnt, 1, adapter->hw.mac.rar_entry_count); + +#if !defined(NO_82542_SUPPORT) /* __rtems__ */ + if (adapter->hw.mac.type == e1000_82542 && + adapter->hw.revision_id == E1000_REVISION_2) { + reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL); + reg_rctl &= ~E1000_RCTL_RST; + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl); + msec_delay(5); + if (adapter->hw.bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) + e1000_pci_set_mwi(&adapter->hw); + } +#endif +} + + +/********************************************************************* + * Timer routine + * + * This routine checks for link status and updates statistics. + * + **********************************************************************/ + +static void +em_local_timer(void *arg) +{ + struct adapter *adapter = arg; + struct ifnet *ifp = adapter->ifp; + + EM_LOCK_ASSERT(adapter); + + e1000_check_for_link(&adapter->hw); + em_update_link_status(adapter); + em_update_stats_counters(adapter); + + /* Check for 82571 LAA reset by other port */ + if (e1000_get_laa_state_82571(&adapter->hw) == TRUE) + e1000_rar_set(&adapter->hw, adapter->hw.mac.addr, 0); + + if (em_display_debug_stats && ifp->if_drv_flags & IFF_DRV_RUNNING) + em_print_hw_stats(adapter); + + em_smartspeed(adapter); + + /* + * Each second we check the watchdog to + * protect against hardware hangs. + */ + em_watchdog(adapter); + + callout_reset(&adapter->timer, hz, em_local_timer, adapter); +} + +static void +em_update_link_status(struct adapter *adapter) +{ + struct ifnet *ifp = adapter->ifp; + device_t dev = adapter->dev; + + if (E1000_READ_REG(&adapter->hw, E1000_STATUS) & + E1000_STATUS_LU) { + if (adapter->link_active == 0) { + e1000_get_speed_and_duplex(&adapter->hw, + &adapter->link_speed, &adapter->link_duplex); + /* Check if we must disable SPEED_MODE bit on PCI-E */ + if ((adapter->link_speed != SPEED_1000) && + ((adapter->hw.mac.type == e1000_82571) || + (adapter->hw.mac.type == e1000_82572))) { + int tarc0; + + tarc0 = E1000_READ_REG(&adapter->hw, + E1000_TARC0); + tarc0 &= ~SPEED_MODE_BIT; + E1000_WRITE_REG(&adapter->hw, + E1000_TARC0, tarc0); + } + if (bootverbose) + device_printf(dev, "Link is up %d Mbps %s\n", + adapter->link_speed, + ((adapter->link_duplex == FULL_DUPLEX) ? + "Full Duplex" : "Half Duplex")); + adapter->link_active = 1; + adapter->smartspeed = 0; + ifp->if_baudrate = adapter->link_speed * 1000000; + if_link_state_change(ifp, LINK_STATE_UP); + } + } else { + if (adapter->link_active == 1) { + ifp->if_baudrate = adapter->link_speed = 0; + adapter->link_duplex = 0; + if (bootverbose) + device_printf(dev, "Link is Down\n"); + adapter->link_active = 0; + if_link_state_change(ifp, LINK_STATE_DOWN); + } + } +} + +/********************************************************************* + * + * This routine disables all traffic on the adapter by issuing a + * global reset on the MAC and deallocates TX/RX buffers. + * + **********************************************************************/ + +static void +em_stop(void *arg) +{ + struct adapter *adapter = arg; + struct ifnet *ifp = adapter->ifp; + + EM_LOCK_ASSERT(adapter); + + INIT_DEBUGOUT("em_stop: begin"); + + em_disable_intr(adapter); + callout_stop(&adapter->timer); + callout_stop(&adapter->tx_fifo_timer); + em_free_transmit_structures(adapter); + em_free_receive_structures(adapter); + + /* Tell the stack that the interface is no longer active */ + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + e1000_reset_hw(&adapter->hw); + if (adapter->hw.mac.type >= e1000_82544) + E1000_WRITE_REG(&adapter->hw, E1000_WUC, 0); +} + + +/********************************************************************* + * + * Determine hardware revision. + * + **********************************************************************/ +static void +em_identify_hardware(struct adapter *adapter) +{ + device_t dev = adapter->dev; + + /* Make sure our PCI config space has the necessary stuff set */ + adapter->hw.bus.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); + if ((adapter->hw.bus.pci_cmd_word & PCIM_CMD_BUSMASTEREN) == 0 && + (adapter->hw.bus.pci_cmd_word & PCIM_CMD_MEMEN)) { + device_printf(dev, "Memory Access and/or Bus Master bits " + "were not set!\n"); + adapter->hw.bus.pci_cmd_word |= + (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); + pci_write_config(dev, PCIR_COMMAND, + adapter->hw.bus.pci_cmd_word, 2); + } + + /* Save off the information about this board */ + adapter->hw.vendor_id = pci_get_vendor(dev); + adapter->hw.device_id = pci_get_device(dev); + adapter->hw.revision_id = pci_read_config(dev, PCIR_REVID, 1); + adapter->hw.subsystem_vendor_id = + pci_read_config(dev, PCIR_SUBVEND_0, 2); + adapter->hw.subsystem_device_id = pci_read_config(dev, PCIR_SUBDEV_0, 2); + + /* Do Shared Code Init and Setup */ + if (e1000_set_mac_type(&adapter->hw)) { + device_printf(dev, "Setup init failure\n"); + return; + } +} + +static int +em_allocate_pci_resources(struct adapter *adapter) +{ + device_t dev = adapter->dev; + int val, rid; + + rid = PCIR_BAR(0); + adapter->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (adapter->res_memory == NULL) { + device_printf(dev, "Unable to allocate bus resource: memory\n"); + return (ENXIO); + } + adapter->osdep.mem_bus_space_tag = + rman_get_bustag(adapter->res_memory); + adapter->osdep.mem_bus_space_handle = + rman_get_bushandle(adapter->res_memory); + adapter->hw.hw_addr = (uint8_t*)&adapter->osdep.mem_bus_space_handle; + + /* Only older adapters use IO mapping */ + if ((adapter->hw.mac.type >= e1000_82543) && /* __rtems__ >82542 -> >= 82543 */ + (adapter->hw.mac.type < e1000_82571)) { + /* Figure our where our IO BAR is ? */ + for (rid = PCIR_BAR(0); rid < PCIR_CIS;) { + val = pci_read_config(dev, rid, 4); + if (EM_BAR_TYPE(val) == EM_BAR_TYPE_IO) { + adapter->io_rid = rid; + break; + } + rid += 4; + /* check for 64bit BAR */ + if (EM_BAR_MEM_TYPE(val) == EM_BAR_MEM_TYPE_64BIT) + rid += 4; + } + if (rid >= PCIR_CIS) { + device_printf(dev, "Unable to locate IO BAR\n"); + return (ENXIO); + } + adapter->res_ioport = bus_alloc_resource_any(dev, + SYS_RES_IOPORT, &adapter->io_rid, RF_ACTIVE); + if (adapter->res_ioport == NULL) { + device_printf(dev, "Unable to allocate bus resource: " + "ioport\n"); + return (ENXIO); + } + adapter->hw.io_base = 0; + adapter->osdep.io_bus_space_tag = + rman_get_bustag(adapter->res_ioport); + adapter->osdep.io_bus_space_handle = + rman_get_bushandle(adapter->res_ioport); + } + + /* + * Setup MSI/X or MSI if PCI Express + * only the latest can use MSI/X and + * real support for it is forthcoming + */ + adapter->msi = 0; /* Set defaults */ + rid = 0x0; + if (adapter->hw.mac.type >= e1000_82575) { + /* + * Setup MSI/X + */ + rid = PCIR_BAR(EM_MSIX_BAR); + adapter->msix_mem = bus_alloc_resource_any(dev, + SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (!adapter->msix_mem) { + device_printf(dev,"Unable to map MSIX table \n"); + return (ENXIO); + } + /* + * Eventually this may be used + * for Multiqueue, for now we will + * just use one vector. + * + * val = pci_msix_count(dev); + */ + val = 1; + if ((val) && pci_alloc_msix(dev, &val) == 0) { + rid = 1; + adapter->msi = 1; + } + } else if (adapter->hw.mac.type > e1000_82571) { + val = pci_msi_count(dev); + if (val == 1 && pci_alloc_msi(dev, &val) == 0) { + rid = 1; + adapter->msi = 1; + } + } + adapter->res_interrupt = bus_alloc_resource_any(dev, + SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); + if (adapter->res_interrupt == NULL) { + device_printf(dev, "Unable to allocate bus resource: " + "interrupt\n"); + return (ENXIO); + } + + adapter->hw.back = &adapter->osdep; + + return (0); +} + +/********************************************************************* + * + * Setup the appropriate Interrupt handlers. + * + **********************************************************************/ +int +em_allocate_intr(struct adapter *adapter) +{ + device_t dev = adapter->dev; + int error; + + /* Manually turn off all interrupts */ + E1000_WRITE_REG(&adapter->hw, E1000_IMC, 0xffffffff); + +#ifdef DEVICE_POLLING + /* We do Legacy setup */ + if (adapter->int_handler_tag == NULL && + (error = bus_setup_intr(dev, adapter->res_interrupt, + INTR_TYPE_NET | INTR_MPSAFE, NULL, em_intr, adapter, + &adapter->int_handler_tag)) != 0) { + device_printf(dev, "Failed to register interrupt handler"); + return (error); + } + +#else + /* + * Try allocating a fast interrupt and the associated deferred + * processing contexts. + */ + TASK_INIT(&adapter->rxtx_task, 0, em_handle_rxtx, adapter); + TASK_INIT(&adapter->link_task, 0, em_handle_link, adapter); + adapter->tq = taskqueue_create_fast("em_taskq", M_NOWAIT, + taskqueue_thread_enqueue, &adapter->tq); + taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s taskq", + device_get_nameunit(adapter->dev)); + if ((error = bus_setup_intr(dev, adapter->res_interrupt, + INTR_TYPE_NET, em_intr_fast, NULL, adapter, + &adapter->int_handler_tag)) != 0) { + device_printf(dev, "Failed to register fast interrupt " + "handler: %d\n", error); + taskqueue_free(adapter->tq); + adapter->tq = NULL; + return (error); + } +#endif + + em_enable_intr(adapter); + return (0); +} + +static void +em_free_intr(struct adapter *adapter) +{ + device_t dev = adapter->dev; + + if (adapter->res_interrupt != NULL) { + bus_teardown_intr(dev, adapter->res_interrupt, + adapter->int_handler_tag); + adapter->int_handler_tag = NULL; + } + if (adapter->tq != NULL) { + taskqueue_drain(adapter->tq, &adapter->rxtx_task); + taskqueue_drain(taskqueue_fast, &adapter->link_task); + taskqueue_free(adapter->tq); + adapter->tq = NULL; + } +} + +static void +em_free_pci_resources(struct adapter *adapter) +{ + device_t dev = adapter->dev; + + if (adapter->res_interrupt != NULL) + bus_release_resource(dev, SYS_RES_IRQ, + adapter->msi ? 1 : 0, adapter->res_interrupt); + + if (adapter->msix_mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + PCIR_BAR(EM_MSIX_BAR), adapter->msix_mem); + + if (adapter->msi) + pci_release_msi(dev); + + if (adapter->res_memory != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + PCIR_BAR(0), adapter->res_memory); + + if (adapter->flash_mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + EM_FLASH, adapter->flash_mem); + + if (adapter->res_ioport != NULL) + bus_release_resource(dev, SYS_RES_IOPORT, + adapter->io_rid, adapter->res_ioport); +} + +/********************************************************************* + * + * Initialize the hardware to a configuration + * as specified by the adapter structure. + * + **********************************************************************/ +static int +em_hardware_init(struct adapter *adapter) +{ + device_t dev = adapter->dev; + uint16_t rx_buffer_size; + + INIT_DEBUGOUT("em_hardware_init: begin"); + + /* Issue a global reset */ + e1000_reset_hw(&adapter->hw); + + /* Get control from any management/hw control */ + if (((adapter->hw.mac.type == e1000_82573) || + (adapter->hw.mac.type == e1000_ich8lan) || + (adapter->hw.mac.type == e1000_ich9lan)) && + e1000_check_mng_mode(&adapter->hw)) + em_get_hw_control(adapter); + + /* When hardware is reset, fifo_head is also reset */ + adapter->tx_fifo_head = 0; + + /* Set up smart power down as default off on newer adapters. */ + if (!em_smart_pwr_down && (adapter->hw.mac.type == e1000_82571 || + adapter->hw.mac.type == e1000_82572)) { + uint16_t phy_tmp = 0; + + /* Speed up time to link by disabling smart power down. */ + e1000_read_phy_reg(&adapter->hw, + IGP02E1000_PHY_POWER_MGMT, &phy_tmp); + phy_tmp &= ~IGP02E1000_PM_SPD; + e1000_write_phy_reg(&adapter->hw, + IGP02E1000_PHY_POWER_MGMT, phy_tmp); + } + + /* + * These parameters control the automatic generation (Tx) and + * response (Rx) to Ethernet PAUSE frames. + * - High water mark should allow for at least two frames to be + * received after sending an XOFF. + * - Low water mark works best when it is very near the high water mark. + * This allows the receiver to restart by sending XON when it has + * drained a bit. Here we use an arbitary value of 1500 which will + * restart after one full frame is pulled from the buffer. There + * could be several smaller frames in the buffer and if so they will + * not trigger the XON until their total number reduces the buffer + * by 1500. + * - The pause time is fairly large at 1000 x 512ns = 512 usec. + */ + rx_buffer_size = ((E1000_READ_REG(&adapter->hw, E1000_PBA) & + 0xffff) << 10 ); + + adapter->hw.mac.fc_high_water = rx_buffer_size - + roundup2(adapter->hw.mac.max_frame_size, 1024); + adapter->hw.mac.fc_low_water = adapter->hw.mac.fc_high_water - 1500; + if (adapter->hw.mac.type == e1000_80003es2lan) + adapter->hw.mac.fc_pause_time = 0xFFFF; + else + adapter->hw.mac.fc_pause_time = EM_FC_PAUSE_TIME; + adapter->hw.mac.fc_send_xon = TRUE; + adapter->hw.mac.fc = e1000_fc_full; + + if (e1000_init_hw(&adapter->hw) < 0) { + device_printf(dev, "Hardware Initialization Failed\n"); + return (EIO); + } + + e1000_check_for_link(&adapter->hw); + + return (0); +} + +/********************************************************************* + * + * Setup networking device structure and register an interface. + * + **********************************************************************/ +static void +em_setup_interface(device_t dev, struct adapter *adapter) +{ + struct ifnet *ifp; + + INIT_DEBUGOUT("em_setup_interface: begin"); + + ifp = adapter->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) + panic("%s: can not if_alloc()", device_get_nameunit(dev)); + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + + ifp->if_mtu = ETHERMTU; + ifp->if_init = em_init; + ifp->if_softc = adapter; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = em_ioctl; + ifp->if_start = em_start; + IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1); + ifp->if_snd.ifq_drv_maxlen = adapter->num_tx_desc - 1; + IFQ_SET_READY(&ifp->if_snd); + + ether_ifattach(ifp, adapter->hw.mac.addr); + +#ifndef __rtems__ + ifp->if_capabilities = ifp->if_capenable = 0; + + if (adapter->hw.mac.type >= e1000_82543) { + ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM; + ifp->if_capenable |= IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM; + } + + /* Identify TSO capable adapters */ + if ((adapter->hw.mac.type > e1000_82544) && + (adapter->hw.mac.type != e1000_82547)) + ifp->if_capabilities |= IFCAP_TSO4; + /* + * By default only enable on PCI-E, this + * can be overriden by ifconfig. + */ + if (adapter->hw.mac.type >= e1000_82571) + ifp->if_capenable |= IFCAP_TSO4; + + /* + * Tell the upper layer(s) we support long frames. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; + ifp->if_capenable |= IFCAP_VLAN_MTU; + +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif +#endif + + /* + * Specify the media types supported by this adapter and register + * callbacks to update media and link information + */ + ifmedia_init(&adapter->media, IFM_IMASK, + em_media_change, em_media_status); + if ((adapter->hw.media_type == e1000_media_type_fiber) || + (adapter->hw.media_type == e1000_media_type_internal_serdes)) { + u_char fiber_type = IFM_1000_SX; /* default type */ + + if (adapter->hw.mac.type == e1000_82545) + fiber_type = IFM_1000_LX; + ifmedia_add(&adapter->media, IFM_ETHER | fiber_type | IFM_FDX, + 0, NULL); + ifmedia_add(&adapter->media, IFM_ETHER | fiber_type, 0, NULL); + } else { + ifmedia_add(&adapter->media, IFM_ETHER | IFM_10_T, 0, NULL); + ifmedia_add(&adapter->media, IFM_ETHER | IFM_10_T | IFM_FDX, + 0, NULL); + ifmedia_add(&adapter->media, IFM_ETHER | IFM_100_TX, + 0, NULL); + ifmedia_add(&adapter->media, IFM_ETHER | IFM_100_TX | IFM_FDX, + 0, NULL); + if (adapter->hw.phy.type != e1000_phy_ife) { + ifmedia_add(&adapter->media, + IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); + ifmedia_add(&adapter->media, + IFM_ETHER | IFM_1000_T, 0, NULL); + } + } + ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO); +} + + +/********************************************************************* + * + * Workaround for SmartSpeed on 82541 and 82547 controllers + * + **********************************************************************/ +static void +em_smartspeed(struct adapter *adapter) +{ + uint16_t phy_tmp; + + if (adapter->link_active || (adapter->hw.phy.type != e1000_phy_igp) || + adapter->hw.mac.autoneg == 0 || + (adapter->hw.phy.autoneg_advertised & ADVERTISE_1000_FULL) == 0) + return; + + if (adapter->smartspeed == 0) { + /* If Master/Slave config fault is asserted twice, + * we assume back-to-back */ + e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp); + if (!(phy_tmp & SR_1000T_MS_CONFIG_FAULT)) + return; + e1000_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp); + if (phy_tmp & SR_1000T_MS_CONFIG_FAULT) { + e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_tmp); + if(phy_tmp & CR_1000T_MS_ENABLE) { + phy_tmp &= ~CR_1000T_MS_ENABLE; + e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, + phy_tmp); + adapter->smartspeed++; + if(adapter->hw.mac.autoneg && + !e1000_phy_setup_autoneg(&adapter->hw) && + !e1000_read_phy_reg(&adapter->hw, PHY_CONTROL, + &phy_tmp)) { + phy_tmp |= (MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + e1000_write_phy_reg(&adapter->hw, PHY_CONTROL, + phy_tmp); + } + } + } + return; + } else if(adapter->smartspeed == EM_SMARTSPEED_DOWNSHIFT) { + /* If still no link, perhaps using 2/3 pair cable */ + e1000_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_tmp); + phy_tmp |= CR_1000T_MS_ENABLE; + e1000_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, phy_tmp); + if(adapter->hw.mac.autoneg && + !e1000_phy_setup_autoneg(&adapter->hw) && + !e1000_read_phy_reg(&adapter->hw, PHY_CONTROL, &phy_tmp)) { + phy_tmp |= (MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + e1000_write_phy_reg(&adapter->hw, PHY_CONTROL, phy_tmp); + } + } + /* Restart process after EM_SMARTSPEED_MAX iterations */ + if(adapter->smartspeed++ == EM_SMARTSPEED_MAX) + adapter->smartspeed = 0; +} + + +/* + * Manage DMA'able memory. + */ +static void +em_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + if (error) + return; + *(bus_addr_t *) arg = segs[0].ds_addr; +} + +static int +em_dma_malloc(struct adapter *adapter, bus_size_t size, + struct em_dma_alloc *dma, int mapflags) +{ + int error; + + error = bus_dma_tag_create(bus_get_dma_tag(adapter->dev), /* parent */ + EM_DBA_ALIGN, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + size, /* maxsize */ + 1, /* nsegments */ + size, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &dma->dma_tag); + if (error) { + device_printf(adapter->dev, + "%s: bus_dma_tag_create failed: %d\n", + __func__, error); + goto fail_0; + } + + error = bus_dmamem_alloc(dma->dma_tag, (void**) &dma->dma_vaddr, + BUS_DMA_NOWAIT, &dma->dma_map); + if (error) { + device_printf(adapter->dev, + "%s: bus_dmamem_alloc(%ju) failed: %d\n", + __func__, (uintmax_t)size, error); + goto fail_2; + } + + dma->dma_paddr = 0; + error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, + size, em_dmamap_cb, &dma->dma_paddr, mapflags | BUS_DMA_NOWAIT); + if (error || dma->dma_paddr == 0) { + device_printf(adapter->dev, + "%s: bus_dmamap_load failed: %d\n", + __func__, error); + goto fail_3; + } + + return (0); + +fail_3: + bus_dmamap_unload(dma->dma_tag, dma->dma_map); +fail_2: + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + bus_dma_tag_destroy(dma->dma_tag); +fail_0: + dma->dma_map = NULL; + dma->dma_tag = NULL; + + return (error); +} + +static void +em_dma_free(struct adapter *adapter, struct em_dma_alloc *dma) +{ + if (dma->dma_tag == NULL) + return; + if (dma->dma_map != NULL) { + bus_dmamap_sync(dma->dma_tag, dma->dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + dma->dma_map = NULL; + } + bus_dma_tag_destroy(dma->dma_tag); + dma->dma_tag = NULL; +} + + +/********************************************************************* + * + * Allocate memory for tx_buffer structures. The tx_buffer stores all + * the information needed to transmit a packet on the wire. + * + **********************************************************************/ + +#ifdef __rtems__ +#define M_ZERO 0 +#endif + +static int +em_allocate_transmit_structures(struct adapter *adapter) +{ + device_t dev = adapter->dev; + + adapter->tx_buffer_area = malloc(sizeof(struct em_buffer) * + adapter->num_tx_desc, M_DEVBUF, M_NOWAIT | M_ZERO); + if (adapter->tx_buffer_area == NULL) { + device_printf(dev, "Unable to allocate tx_buffer memory\n"); + return (ENOMEM); + } + + bzero(adapter->tx_buffer_area, + (sizeof(struct em_buffer)) * adapter->num_tx_desc); + + return (0); +} + +/********************************************************************* + * + * Initialize transmit structures. + * + **********************************************************************/ +static int +em_setup_transmit_structures(struct adapter *adapter) +{ + device_t dev = adapter->dev; + struct em_buffer *tx_buffer; + int error, i; + + /* + * Create DMA tags for tx descriptors + */ + if ((error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + EM_TSO_SIZE, /* maxsize */ + EM_MAX_SCATTER, /* nsegments */ + EM_TSO_SEG_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &adapter->txtag)) != 0) { + device_printf(dev, "Unable to allocate TX DMA tag\n"); + goto fail; + } + + if ((error = em_allocate_transmit_structures(adapter)) != 0) + goto fail; + + /* Clear the old ring contents */ + bzero(adapter->tx_desc_base, + (sizeof(struct e1000_tx_desc)) * adapter->num_tx_desc); + + /* Create the descriptor buffer dma maps */ + tx_buffer = adapter->tx_buffer_area; + for (i = 0; i < adapter->num_tx_desc; i++) { + error = bus_dmamap_create(adapter->txtag, 0, &tx_buffer->map); + if (error != 0) { + device_printf(dev, "Unable to create TX DMA map\n"); + goto fail; + } + tx_buffer->next_eop = -1; + tx_buffer++; + } + + adapter->next_avail_tx_desc = 0; + adapter->next_tx_to_clean = 0; + + /* Set number of descriptors available */ + adapter->num_tx_desc_avail = adapter->num_tx_desc; + + bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); + +fail: + em_free_transmit_structures(adapter); + return (error); +} + +/********************************************************************* + * + * Enable transmit unit. + * + **********************************************************************/ +static void +em_initialize_transmit_unit(struct adapter *adapter) +{ + uint32_t tctl, tarc, tipg = 0; + uint64_t bus_addr; + + INIT_DEBUGOUT("em_initialize_transmit_unit: begin"); + /* Setup the Base and Length of the Tx Descriptor Ring */ + bus_addr = adapter->txdma.dma_paddr; + E1000_WRITE_REG(&adapter->hw, E1000_TDLEN, + adapter->num_tx_desc * sizeof(struct e1000_tx_desc)); + E1000_WRITE_REG(&adapter->hw, E1000_TDBAH, (uint32_t)(bus_addr >> 32)); + E1000_WRITE_REG(&adapter->hw, E1000_TDBAL, (uint32_t)bus_addr); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + E1000_WRITE_REG(&adapter->hw, E1000_TDT, 0); + E1000_WRITE_REG(&adapter->hw, E1000_TDH, 0); + + HW_DEBUGOUT2("Base = %x, Length = %x\n", + E1000_READ_REG(&adapter->hw, E1000_TDBAL), + E1000_READ_REG(&adapter->hw, E1000_TDLEN)); + + /* Set the default values for the Tx Inter Packet Gap timer */ + switch (adapter->hw.mac.type) { +#if !defined(NO_82542_SUPPORT) /* __rtems__ */ + case e1000_82542: + tipg = DEFAULT_82542_TIPG_IPGT; + tipg |= DEFAULT_82542_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; + tipg |= DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; + break; +#endif + case e1000_80003es2lan: + tipg = DEFAULT_82543_TIPG_IPGR1; + tipg |= DEFAULT_80003ES2LAN_TIPG_IPGR2 << + E1000_TIPG_IPGR2_SHIFT; + break; + default: + if ((adapter->hw.media_type == e1000_media_type_fiber) || + (adapter->hw.media_type == + e1000_media_type_internal_serdes)) + tipg = DEFAULT_82543_TIPG_IPGT_FIBER; + else + tipg = DEFAULT_82543_TIPG_IPGT_COPPER; + tipg |= DEFAULT_82543_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; + tipg |= DEFAULT_82543_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; + } + + E1000_WRITE_REG(&adapter->hw, E1000_TIPG, tipg); + E1000_WRITE_REG(&adapter->hw, E1000_TIDV, adapter->tx_int_delay.value); + if(adapter->hw.mac.type >= e1000_82540) + E1000_WRITE_REG(&adapter->hw, E1000_TADV, + adapter->tx_abs_int_delay.value); + + if ((adapter->hw.mac.type == e1000_82571) || + (adapter->hw.mac.type == e1000_82572)) { + tarc = E1000_READ_REG(&adapter->hw, E1000_TARC0); + tarc |= SPEED_MODE_BIT; + E1000_WRITE_REG(&adapter->hw, E1000_TARC0, tarc); + } else if (adapter->hw.mac.type == e1000_80003es2lan) { + tarc = E1000_READ_REG(&adapter->hw, E1000_TARC0); + tarc |= 1; + E1000_WRITE_REG(&adapter->hw, E1000_TARC0, tarc); + tarc = E1000_READ_REG(&adapter->hw, E1000_TARC1); + tarc |= 1; + E1000_WRITE_REG(&adapter->hw, E1000_TARC1, tarc); + } + + /* Program the Transmit Control Register */ + tctl = E1000_READ_REG(&adapter->hw, E1000_TCTL); + tctl &= ~E1000_TCTL_CT; + tctl |= (E1000_TCTL_PSP | E1000_TCTL_RTLC | E1000_TCTL_EN | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT)); + + if (adapter->hw.mac.type >= e1000_82571) + tctl |= E1000_TCTL_MULR; + + /* This write will effectively turn on the transmit unit. */ + E1000_WRITE_REG(&adapter->hw, E1000_TCTL, tctl); + + /* Setup Transmit Descriptor Base Settings */ + adapter->txd_cmd = E1000_TXD_CMD_IFCS; + + if ((adapter->tx_int_delay.value > 0) && + (adapter->hw.mac.type != e1000_82575)) + adapter->txd_cmd |= E1000_TXD_CMD_IDE; + + /* Set the function pointer for the transmit routine */ + if (adapter->hw.mac.type >= e1000_82575) + adapter->em_xmit = em_adv_encap; + else + adapter->em_xmit = em_encap; +} + +/********************************************************************* + * + * Free all transmit related data structures. + * + **********************************************************************/ +static void +em_free_transmit_structures(struct adapter *adapter) +{ + struct em_buffer *tx_buffer; + int i; + + INIT_DEBUGOUT("free_transmit_structures: begin"); + + if (adapter->tx_buffer_area != NULL) { + tx_buffer = adapter->tx_buffer_area; + for (i = 0; i < adapter->num_tx_desc; i++, tx_buffer++) { + if (tx_buffer->m_head != NULL) { + bus_dmamap_sync(adapter->txtag, tx_buffer->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(adapter->txtag, + tx_buffer->map); + m_freem(tx_buffer->m_head); + tx_buffer->m_head = NULL; + } else if (tx_buffer->map != NULL) + bus_dmamap_unload(adapter->txtag, + tx_buffer->map); + if (tx_buffer->map != NULL) { + bus_dmamap_destroy(adapter->txtag, + tx_buffer->map); + tx_buffer->map = NULL; + } + } + } + if (adapter->tx_buffer_area != NULL) { + free(adapter->tx_buffer_area, M_DEVBUF); + adapter->tx_buffer_area = NULL; + } + if (adapter->txtag != NULL) { + bus_dma_tag_destroy(adapter->txtag); + adapter->txtag = NULL; + } +} + +#ifndef __rtems__ +/********************************************************************* + * + * The offload context needs to be set when we transfer the first + * packet of a particular protocol (TCP/UDP). This routine has been + * enhanced to deal with inserted VLAN headers, and IPV6 (not complete) + * + **********************************************************************/ +static void +em_transmit_checksum_setup(struct adapter *adapter, struct mbuf *mp, + uint32_t *txd_upper, uint32_t *txd_lower) +{ + struct e1000_context_desc *TXD; + struct em_buffer *tx_buffer; + struct ether_vlan_header *eh; + struct ip *ip; + struct ip6_hdr *ip6; + struct tcp_hdr *th; + int curr_txd, ehdrlen, hdr_len, ip_hlen; + uint32_t cmd = 0; + uint16_t etype; + uint8_t ipproto; + + /* Setup checksum offload context. */ + curr_txd = adapter->next_avail_tx_desc; + tx_buffer = &adapter->tx_buffer_area[curr_txd]; + TXD = (struct e1000_context_desc *) &adapter->tx_desc_base[curr_txd]; + + *txd_lower = E1000_TXD_CMD_DEXT | /* Extended descr type */ + E1000_TXD_DTYP_D; /* Data descr */ + + /* + * Determine where frame payload starts. + * Jump over vlan headers if already present, + * helpful for QinQ too. + */ + eh = mtod(mp, struct ether_vlan_header *); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + etype = ntohs(eh->evl_proto); + ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + } else { + etype = ntohs(eh->evl_encap_proto); + ehdrlen = ETHER_HDR_LEN; + } + + /* + * We only support TCP/UDP for IPv4 and IPv6 for the moment. + * TODO: Support SCTP too when it hits the tree. + */ + switch (etype) { + case ETHERTYPE_IP: + ip = (struct ip *)(mp->m_data + ehdrlen); + ip_hlen = ip->ip_hl << 2; + + /* Setup of IP header checksum. */ + if (mp->m_pkthdr.csum_flags & CSUM_IP) { + /* + * Start offset for header checksum calculation. + * End offset for header checksum calculation. + * Offset of place to put the checksum. + */ + TXD->lower_setup.ip_fields.ipcss = ehdrlen; + TXD->lower_setup.ip_fields.ipcse = + htole16(ehdrlen + ip_hlen); + TXD->lower_setup.ip_fields.ipcso = + ehdrlen + offsetof(struct ip, ip_sum); + cmd |= E1000_TXD_CMD_IP; + *txd_upper |= E1000_TXD_POPTS_IXSM << 8; + } + + if (mp->m_len < ehdrlen + ip_hlen) + return; /* failure */ + + hdr_len = ehdrlen + ip_hlen; + ipproto = ip->ip_p; + + break; + case ETHERTYPE_IPV6: + ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); + ip_hlen = sizeof(struct ip6_hdr); /* XXX: No header stacking. */ + + if (mp->m_len < ehdrlen + ip_hlen) + return; /* failure */ + + /* IPv6 doesn't have a header checksum. */ + + hdr_len = ehdrlen + ip_hlen; + ipproto = ip6->ip6_nxt; + + break; + default: + *txd_upper = 0; + *txd_lower = 0; + return; + } + + switch (ipproto) { + case IPPROTO_TCP: + if (mp->m_pkthdr.csum_flags & CSUM_TCP) { + /* + * Start offset for payload checksum calculation. + * End offset for payload checksum calculation. + * Offset of place to put the checksum. + */ + th = (struct tcp_hdr *)(mp->m_data + hdr_len); + TXD->upper_setup.tcp_fields.tucss = hdr_len; + TXD->upper_setup.tcp_fields.tucse = htole16(0); + TXD->upper_setup.tcp_fields.tucso = + hdr_len + offsetof(struct tcphdr, th_sum); + cmd |= E1000_TXD_CMD_TCP; + *txd_upper |= E1000_TXD_POPTS_TXSM << 8; + } + break; + case IPPROTO_UDP: + if (mp->m_pkthdr.csum_flags & CSUM_UDP) { + /* + * Start offset for header checksum calculation. + * End offset for header checksum calculation. + * Offset of place to put the checksum. + */ + TXD->upper_setup.tcp_fields.tucss = hdr_len; + TXD->upper_setup.tcp_fields.tucse = htole16(0); + TXD->upper_setup.tcp_fields.tucso = + hdr_len + offsetof(struct udphdr, uh_sum); + *txd_upper |= E1000_TXD_POPTS_TXSM << 8; + } + break; + default: + break; + } + + TXD->tcp_seg_setup.data = htole32(0); + TXD->cmd_and_length = + htole32(adapter->txd_cmd | E1000_TXD_CMD_DEXT | cmd); + tx_buffer->m_head = NULL; + tx_buffer->next_eop = -1; + + if (++curr_txd == adapter->num_tx_desc) + curr_txd = 0; + + adapter->num_tx_desc_avail--; + adapter->next_avail_tx_desc = curr_txd; +} + +/********************************************************************** + * + * Setup work for hardware segmentation offload (TSO) + * + **********************************************************************/ +static boolean_t +em_tso_setup(struct adapter *adapter, struct mbuf *mp, uint32_t *txd_upper, + uint32_t *txd_lower) +{ + struct e1000_context_desc *TXD; + struct em_buffer *tx_buffer; + struct ether_vlan_header *eh; + struct ip *ip; + struct ip6_hdr *ip6; + struct tcphdr *th; + int curr_txd, ehdrlen, hdr_len, ip_hlen, isip6; + uint16_t etype; + + /* + * XXX: This is not really correct as the stack would not have + * set up all checksums. + * XXX: Return FALSE is not sufficient as we may have to return + * in true failure cases as well. Should do -1 (failure), 0 (no) + * and 1 (success). + */ + if (((mp->m_pkthdr.csum_flags & CSUM_TSO) == 0) || + (mp->m_pkthdr.len <= EM_TX_BUFFER_SIZE)) + return FALSE; + + /* + * This function could/should be extended to support IP/IPv6 + * fragmentation as well. But as they say, one step at a time. + */ + + /* + * Determine where frame payload starts. + * Jump over vlan headers if already present, + * helpful for QinQ too. + */ + eh = mtod(mp, struct ether_vlan_header *); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + etype = ntohs(eh->evl_proto); + ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + } else { + etype = ntohs(eh->evl_encap_proto); + ehdrlen = ETHER_HDR_LEN; + } + + /* Ensure we have at least the IP+TCP header in the first mbuf. */ + if (mp->m_len < ehdrlen + sizeof(struct ip) + sizeof(struct tcphdr)) + return FALSE; /* -1 */ + + /* + * We only support TCP for IPv4 and IPv6 (notyet) for the moment. + * TODO: Support SCTP too when it hits the tree. + */ + switch (etype) { + case ETHERTYPE_IP: + isip6 = 0; + ip = (struct ip *)(mp->m_data + ehdrlen); + if (ip->ip_p != IPPROTO_TCP) + return FALSE; /* 0 */ + ip->ip_len = 0; + ip->ip_sum = 0; + ip_hlen = ip->ip_hl << 2; + if (mp->m_len < ehdrlen + ip_hlen + sizeof(struct tcphdr)) + return FALSE; /* -1 */ + th = (struct tcphdr *)((caddr_t)ip + ip_hlen); +#if 1 + th->th_sum = in_pseudo(ip->ip_src.s_addr, + ip->ip_dst.s_addr, htons(IPPROTO_TCP)); +#else + th->th_sum = mp->m_pkthdr.csum_data; +#endif + break; + case ETHERTYPE_IPV6: + isip6 = 1; + return FALSE; /* Not supported yet. */ + ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); + if (ip6->ip6_nxt != IPPROTO_TCP) + return FALSE; /* 0 */ + ip6->ip6_plen = 0; + ip_hlen = sizeof(struct ip6_hdr); /* XXX: no header stacking. */ + if (mp->m_len < ehdrlen + ip_hlen + sizeof(struct tcphdr)) + return FALSE; /* -1 */ + th = (struct tcphdr *)((caddr_t)ip6 + ip_hlen); +#if 0 + th->th_sum = in6_pseudo(ip6->ip6_src, ip->ip6_dst, + htons(IPPROTO_TCP)); /* XXX: function notyet. */ +#else + th->th_sum = mp->m_pkthdr.csum_data; +#endif + break; + default: + return FALSE; + } + hdr_len = ehdrlen + ip_hlen + (th->th_off << 2); + + *txd_lower = (E1000_TXD_CMD_DEXT | /* Extended descr type */ + E1000_TXD_DTYP_D | /* Data descr type */ + E1000_TXD_CMD_TSE); /* Do TSE on this packet */ + + /* IP and/or TCP header checksum calculation and insertion. */ + *txd_upper = ((isip6 ? 0 : E1000_TXD_POPTS_IXSM) | + E1000_TXD_POPTS_TXSM) << 8; + + curr_txd = adapter->next_avail_tx_desc; + tx_buffer = &adapter->tx_buffer_area[curr_txd]; + TXD = (struct e1000_context_desc *) &adapter->tx_desc_base[curr_txd]; + + /* IPv6 doesn't have a header checksum. */ + if (!isip6) { + /* + * Start offset for header checksum calculation. + * End offset for header checksum calculation. + * Offset of place put the checksum. + */ + TXD->lower_setup.ip_fields.ipcss = ehdrlen; + TXD->lower_setup.ip_fields.ipcse = + htole16(ehdrlen + ip_hlen - 1); + TXD->lower_setup.ip_fields.ipcso = + ehdrlen + offsetof(struct ip, ip_sum); + } + /* + * Start offset for payload checksum calculation. + * End offset for payload checksum calculation. + * Offset of place to put the checksum. + */ + TXD->upper_setup.tcp_fields.tucss = + ehdrlen + ip_hlen; + TXD->upper_setup.tcp_fields.tucse = 0; + TXD->upper_setup.tcp_fields.tucso = + ehdrlen + ip_hlen + offsetof(struct tcphdr, th_sum); + /* + * Payload size per packet w/o any headers. + * Length of all headers up to payload. + */ + TXD->tcp_seg_setup.fields.mss = htole16(mp->m_pkthdr.tso_segsz); + TXD->tcp_seg_setup.fields.hdr_len = hdr_len; + + TXD->cmd_and_length = htole32(adapter->txd_cmd | + E1000_TXD_CMD_DEXT | /* Extended descr */ + E1000_TXD_CMD_TSE | /* TSE context */ + (isip6 ? 0 : E1000_TXD_CMD_IP) | /* Do IP csum */ + E1000_TXD_CMD_TCP | /* Do TCP checksum */ + (mp->m_pkthdr.len - (hdr_len))); /* Total len */ + + tx_buffer->m_head = NULL; + tx_buffer->next_eop = -1; + + if (++curr_txd == adapter->num_tx_desc) + curr_txd = 0; + + adapter->num_tx_desc_avail--; + adapter->next_avail_tx_desc = curr_txd; + adapter->tx_tso = TRUE; + + return TRUE; +} + + +/********************************************************************** + * + * Setup work for hardware segmentation offload (TSO) on + * adapters using advanced tx descriptors + * + **********************************************************************/ +static boolean_t +em_tso_adv_setup(struct adapter *adapter, struct mbuf *mp, u32 *paylen) +{ + struct e1000_adv_tx_context_desc *TXD; + struct em_buffer *tx_buffer; + u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0; + u32 mss_l4len_idx = 0; + u16 vtag = 0; + int ctxd, ehdrlen, hdrlen, ip_hlen, tcp_hlen; + struct ether_vlan_header *eh; + struct ip *ip; + struct tcphdr *th; + + if (((mp->m_pkthdr.csum_flags & CSUM_TSO) == 0) || + (mp->m_pkthdr.len <= EM_TX_BUFFER_SIZE)) + return FALSE; + + /* + * Determine where frame payload starts. + * Jump over vlan headers if already present + */ + eh = mtod(mp, struct ether_vlan_header *); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) + ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + else + ehdrlen = ETHER_HDR_LEN; + + /* Ensure we have at least the IP+TCP header in the first mbuf. */ + if (mp->m_len < ehdrlen + sizeof(struct ip) + sizeof(struct tcphdr)) + return FALSE; + + /* Only supports IPV4 for now */ + ctxd = adapter->next_avail_tx_desc; + tx_buffer = &adapter->tx_buffer_area[ctxd]; + TXD = (struct e1000_adv_tx_context_desc *) &adapter->tx_desc_base[ctxd]; + + ip = (struct ip *)(mp->m_data + ehdrlen); + if (ip->ip_p != IPPROTO_TCP) + return FALSE; /* 0 */ + ip->ip_len = 0; + ip->ip_sum = 0; + ip_hlen = ip->ip_hl << 2; + th = (struct tcphdr *)((caddr_t)ip + ip_hlen); + th->th_sum = in_pseudo(ip->ip_src.s_addr, + ip->ip_dst.s_addr, htons(IPPROTO_TCP)); + tcp_hlen = th->th_off << 2; + hdrlen = ehdrlen + ip_hlen + tcp_hlen; + /* Calculate payload, this is used in the transmit desc in encap */ + *paylen = mp->m_pkthdr.len - hdrlen; + +#ifndef __rtems__ + /* VLAN MACLEN IPLEN */ + if (mp->m_flags & M_VLANTAG) { + vtag = htole16(mp->m_pkthdr.ether_vtag); + vlan_macip_lens |= (vtag << E1000_ADVTXD_VLAN_SHIFT); + } +#endif + vlan_macip_lens |= (ehdrlen << E1000_ADVTXD_MACLEN_SHIFT); + vlan_macip_lens |= ip_hlen; + TXD->vlan_macip_lens |= htole32(vlan_macip_lens); + + /* ADV DTYPE TUCMD */ + type_tucmd_mlhl |= E1000_ADVTXD_DCMD_DEXT | E1000_ADVTXD_DTYP_CTXT; + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_TCP; + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4; + TXD->type_tucmd_mlhl |= htole32(type_tucmd_mlhl); + + /* MSS L4LEN IDX */ + mss_l4len_idx |= (mp->m_pkthdr.tso_segsz << E1000_ADVTXD_MSS_SHIFT); + mss_l4len_idx |= (tcp_hlen << E1000_ADVTXD_L4LEN_SHIFT); + TXD->mss_l4len_idx = htole32(mss_l4len_idx); + + TXD->seqnum_seed = htole32(0); + tx_buffer->m_head = NULL; + tx_buffer->next_eop = -1; + + if (++ctxd == adapter->num_tx_desc) + ctxd = 0; + + adapter->num_tx_desc_avail--; + adapter->next_avail_tx_desc = ctxd; + return TRUE; +} + + +/********************************************************************* + * + * Advanced Context Descriptor setup for VLAN or CSUM + * + **********************************************************************/ + +static boolean_t +em_tx_adv_ctx_setup(struct adapter *adapter, struct mbuf *mp) +{ + struct e1000_adv_tx_context_desc *TXD; + struct em_buffer *tx_buffer; + uint32_t vlan_macip_lens = 0, type_tucmd_mlhl = 0; + struct ether_vlan_header *eh; + struct ip *ip; + struct ip6_hdr *ip6; + int ehdrlen, ip_hlen; + u16 etype; + u8 ipproto; + + int ctxd = adapter->next_avail_tx_desc; + u16 vtag = 0; + + tx_buffer = &adapter->tx_buffer_area[ctxd]; + TXD = (struct e1000_adv_tx_context_desc *) &adapter->tx_desc_base[ctxd]; + + /* + ** In advanced descriptors the vlan tag must + ** be placed into the descriptor itself. + */ + if (mp->m_flags & M_VLANTAG) { + vtag = htole16(mp->m_pkthdr.ether_vtag); + vlan_macip_lens |= (vtag << E1000_ADVTXD_VLAN_SHIFT); + } + + /* + * Determine where frame payload starts. + * Jump over vlan headers if already present, + * helpful for QinQ too. + */ + eh = mtod(mp, struct ether_vlan_header *); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + etype = ntohs(eh->evl_proto); + ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + } else { + etype = ntohs(eh->evl_encap_proto); + ehdrlen = ETHER_HDR_LEN; + } + + /* Set the ether header length */ + vlan_macip_lens |= ehdrlen << E1000_ADVTXD_MACLEN_SHIFT; + + switch (etype) { + case ETHERTYPE_IP: + ip = (struct ip *)(mp->m_data + ehdrlen); + ip_hlen = ip->ip_hl << 2; + if (mp->m_len < ehdrlen + ip_hlen) + return FALSE; /* failure */ + ipproto = ip->ip_p; + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV4; + break; + case ETHERTYPE_IPV6: + ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); + ip_hlen = sizeof(struct ip6_hdr); + if (mp->m_len < ehdrlen + ip_hlen) + return FALSE; /* failure */ + ipproto = ip6->ip6_nxt; + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_IPV6; + break; + default: + return FALSE; + } + + vlan_macip_lens |= ip_hlen; + type_tucmd_mlhl |= E1000_ADVTXD_DCMD_DEXT | E1000_ADVTXD_DTYP_CTXT; + + switch (ipproto) { + case IPPROTO_TCP: + if (mp->m_pkthdr.csum_flags & CSUM_TCP) + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_TCP; + break; + case IPPROTO_UDP: + if (mp->m_pkthdr.csum_flags & CSUM_UDP) + type_tucmd_mlhl |= E1000_ADVTXD_TUCMD_L4T_TCP; + break; + } + + /* Now copy bits into descriptor */ + TXD->vlan_macip_lens |= htole32(vlan_macip_lens); + TXD->type_tucmd_mlhl |= htole32(type_tucmd_mlhl); + TXD->seqnum_seed = htole32(0); + TXD->mss_l4len_idx = htole32(0); + + tx_buffer->m_head = NULL; + tx_buffer->next_eop = -1; + + /* We've consumed the first desc, adjust counters */ + if (++ctxd == adapter->num_tx_desc) + ctxd = 0; + adapter->next_avail_tx_desc = ctxd; + --adapter->num_tx_desc_avail; + + return TRUE; +} +#endif + + +/********************************************************************** + * + * Examine each tx_buffer in the used queue. If the hardware is done + * processing the packet then free associated resources. The + * tx_buffer is put back on the free queue. + * + **********************************************************************/ +static void +em_txeof(struct adapter *adapter) +{ + int first, last, done, num_avail; + struct em_buffer *tx_buffer; + struct e1000_tx_desc *tx_desc, *eop_desc; + struct ifnet *ifp = adapter->ifp; + + EM_LOCK_ASSERT(adapter); + + if (adapter->num_tx_desc_avail == adapter->num_tx_desc) + return; + + num_avail = adapter->num_tx_desc_avail; + first = adapter->next_tx_to_clean; + tx_desc = &adapter->tx_desc_base[first]; + tx_buffer = &adapter->tx_buffer_area[first]; + last = tx_buffer->next_eop; + eop_desc = &adapter->tx_desc_base[last]; + + /* + * What this does is get the index of the + * first descriptor AFTER the EOP of the + * first packet, that way we can do the + * simple comparison on the inner while loop. + */ + if (++last == adapter->num_tx_desc) + last = 0; + done = last; + + bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, + BUS_DMASYNC_POSTREAD); + + while (eop_desc->upper.fields.status & E1000_TXD_STAT_DD) { + /* We clean the range of the packet */ + while (first != done) { + tx_desc->upper.data = 0; + tx_desc->lower.data = 0; + tx_desc->buffer_addr = 0; + num_avail++; + + if (tx_buffer->m_head) { + ifp->if_opackets++; + bus_dmamap_sync(adapter->txtag, + tx_buffer->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(adapter->txtag, + tx_buffer->map); + + m_freem(tx_buffer->m_head); + tx_buffer->m_head = NULL; + } + tx_buffer->next_eop = -1; + + if (++first == adapter->num_tx_desc) + first = 0; + + tx_buffer = &adapter->tx_buffer_area[first]; + tx_desc = &adapter->tx_desc_base[first]; + } + /* See if we can continue to the next packet */ + last = tx_buffer->next_eop; + if (last != -1) { + eop_desc = &adapter->tx_desc_base[last]; + /* Get new done point */ + if (++last == adapter->num_tx_desc) last = 0; + done = last; + } else + break; + } + bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + adapter->next_tx_to_clean = first; + + /* + * If we have enough room, clear IFF_DRV_OACTIVE to tell the stack + * that it is OK to send packets. + * If there are no pending descriptors, clear the timeout. Otherwise, + * if some descriptors have been freed, restart the timeout. + */ + if (num_avail > EM_TX_CLEANUP_THRESHOLD) { + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + /* All clean, turn off the timer */ + if (num_avail == adapter->num_tx_desc) + adapter->watchdog_timer = 0; + /* Some cleaned, reset the timer */ + else if (num_avail != adapter->num_tx_desc_avail) + adapter->watchdog_timer = EM_TX_TIMEOUT; + } + adapter->num_tx_desc_avail = num_avail; + return; +} + +/********************************************************************* + * + * Get a buffer from system mbuf buffer pool. + * + **********************************************************************/ +static int +em_get_buf(struct adapter *adapter, int i) +{ + struct mbuf *m; + bus_dma_segment_t segs[1]; + bus_dmamap_t map; + struct em_buffer *rx_buffer; + int error, nsegs; + + m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + adapter->mbuf_cluster_failed++; + return (ENOBUFS); + } + m->m_len = m->m_pkthdr.len = MCLBYTES; + + if (adapter->hw.mac.max_frame_size <= (MCLBYTES - ETHER_ALIGN)) + m_adj(m, ETHER_ALIGN); + + /* + * Using memory from the mbuf cluster pool, invoke the + * bus_dma machinery to arrange the memory mapping. + */ + error = bus_dmamap_load_mbuf_sg(adapter->rxtag, + adapter->rx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + m_free(m); + return (error); + } + + /* If nsegs is wrong then the stack is corrupt. */ + KASSERT(nsegs == 1, ("Too many segments returned!")); + + rx_buffer = &adapter->rx_buffer_area[i]; + if (rx_buffer->m_head != NULL) + bus_dmamap_unload(adapter->rxtag, rx_buffer->map); + + map = rx_buffer->map; + rx_buffer->map = adapter->rx_sparemap; + adapter->rx_sparemap = map; + bus_dmamap_sync(adapter->rxtag, rx_buffer->map, BUS_DMASYNC_PREREAD); + rx_buffer->m_head = m; + + adapter->rx_desc_base[i].buffer_addr = htole64(segs[0].ds_addr); + return (0); +} + +/********************************************************************* + * + * Allocate memory for rx_buffer structures. Since we use one + * rx_buffer per received packet, the maximum number of rx_buffer's + * that we'll need is equal to the number of receive descriptors + * that we've allocated. + * + **********************************************************************/ +static int +em_allocate_receive_structures(struct adapter *adapter) +{ + device_t dev = adapter->dev; + struct em_buffer *rx_buffer; + int i, error; + + adapter->rx_buffer_area = malloc(sizeof(struct em_buffer) * + adapter->num_rx_desc, M_DEVBUF, M_NOWAIT); + if (adapter->rx_buffer_area == NULL) { + device_printf(dev, "Unable to allocate rx_buffer memory\n"); + return (ENOMEM); + } + + bzero(adapter->rx_buffer_area, + sizeof(struct em_buffer) * adapter->num_rx_desc); + + error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, /* maxsize */ + 1, /* nsegments */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &adapter->rxtag); + if (error) { + device_printf(dev, "%s: bus_dma_tag_create failed %d\n", + __func__, error); + goto fail; + } + + /* Create the spare map (used by getbuf) */ + error = bus_dmamap_create(adapter->rxtag, BUS_DMA_NOWAIT, + &adapter->rx_sparemap); + if (error) { + device_printf(dev, "%s: bus_dmamap_create failed: %d\n", + __func__, error); + goto fail; + } + + rx_buffer = adapter->rx_buffer_area; + for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) { + error = bus_dmamap_create(adapter->rxtag, BUS_DMA_NOWAIT, + &rx_buffer->map); + if (error) { + device_printf(dev, "%s: bus_dmamap_create failed: %d\n", + __func__, error); + goto fail; + } + } + + /* Setup the initial buffers */ + for (i = 0; i < adapter->num_rx_desc; i++) { + error = em_get_buf(adapter, i); + if (error) + goto fail; + } + bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return (0); + +fail: + em_free_receive_structures(adapter); + return (error); +} + +/********************************************************************* + * + * Allocate and initialize receive structures. + * + **********************************************************************/ +static int +em_setup_receive_structures(struct adapter *adapter) +{ + int error; + + bzero(adapter->rx_desc_base, + (sizeof(struct e1000_rx_desc)) * adapter->num_rx_desc); + + if ((error = em_allocate_receive_structures(adapter)) !=0) + return (error); + + /* Setup our descriptor pointers */ + adapter->next_rx_desc_to_check = 0; + + return (0); +} + +/********************************************************************* + * + * Enable receive unit. + * + **********************************************************************/ +static void +em_initialize_receive_unit(struct adapter *adapter) +{ + struct ifnet *ifp = adapter->ifp; + uint64_t bus_addr; + uint32_t reg_rctl; +#ifndef __rtems__ + uint32_t reg_rxcsum; +#endif + + INIT_DEBUGOUT("em_initialize_receive_unit: begin"); + + /* + * Make sure receives are disabled while setting + * up the descriptor ring + */ + reg_rctl = E1000_READ_REG(&adapter->hw, E1000_RCTL); + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl & ~E1000_RCTL_EN); + + if(adapter->hw.mac.type >= e1000_82540) { + E1000_WRITE_REG(&adapter->hw, E1000_RADV, + adapter->rx_abs_int_delay.value); + /* + * Set the interrupt throttling rate. Value is calculated + * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns) + */ +#define MAX_INTS_PER_SEC 8000 +#define DEFAULT_ITR 1000000000/(MAX_INTS_PER_SEC * 256) + E1000_WRITE_REG(&adapter->hw, E1000_ITR, DEFAULT_ITR); + } + + /* Setup the Base and Length of the Rx Descriptor Ring */ + bus_addr = adapter->rxdma.dma_paddr; + E1000_WRITE_REG(&adapter->hw, E1000_RDLEN, adapter->num_rx_desc * + sizeof(struct e1000_rx_desc)); + E1000_WRITE_REG(&adapter->hw, E1000_RDBAH, (uint32_t)(bus_addr >> 32)); + E1000_WRITE_REG(&adapter->hw, E1000_RDBAL, (uint32_t)bus_addr); + + /* Setup the Receive Control Register */ + reg_rctl &= ~(3 << E1000_RCTL_MO_SHIFT); + reg_rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | + E1000_RCTL_RDMTS_HALF | + (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT); + + if (e1000_tbi_sbp_enabled_82543(&adapter->hw)) + reg_rctl |= E1000_RCTL_SBP; + else + reg_rctl &= ~E1000_RCTL_SBP; + + switch (adapter->rx_buffer_len) { + default: + case 2048: + reg_rctl |= E1000_RCTL_SZ_2048; + break; + case 4096: + reg_rctl |= E1000_RCTL_SZ_4096 | + E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + case 8192: + reg_rctl |= E1000_RCTL_SZ_8192 | + E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + case 16384: + reg_rctl |= E1000_RCTL_SZ_16384 | + E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + } + + if (ifp->if_mtu > ETHERMTU) + reg_rctl |= E1000_RCTL_LPE; + else + reg_rctl &= ~E1000_RCTL_LPE; + +#ifndef __rtems__ + /* Enable 82543 Receive Checksum Offload for TCP and UDP */ + if ((adapter->hw.mac.type >= e1000_82543) && + (ifp->if_capenable & IFCAP_RXCSUM)) { + reg_rxcsum = E1000_READ_REG(&adapter->hw, E1000_RXCSUM); + reg_rxcsum |= (E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL); + E1000_WRITE_REG(&adapter->hw, E1000_RXCSUM, reg_rxcsum); + } +#endif + + /* + ** XXX TEMPORARY WORKAROUND: on some systems with 82573 + ** long latencies are observed, like Lenovo X60. This + ** change eliminates the problem, but since having positive + ** values in RDTR is a known source of problems on other + ** platforms another solution is being sought. + */ + if (adapter->hw.mac.type == e1000_82573) + E1000_WRITE_REG(&adapter->hw, E1000_RDTR, 0x20); + + /* Enable Receives */ + E1000_WRITE_REG(&adapter->hw, E1000_RCTL, reg_rctl); + + /* + * Setup the HW Rx Head and + * Tail Descriptor Pointers + */ + E1000_WRITE_REG(&adapter->hw, E1000_RDH, 0); + E1000_WRITE_REG(&adapter->hw, E1000_RDT, adapter->num_rx_desc - 1); + + return; +} + +/********************************************************************* + * + * Free receive related data structures. + * + **********************************************************************/ +static void +em_free_receive_structures(struct adapter *adapter) +{ + struct em_buffer *rx_buffer; + int i; + + INIT_DEBUGOUT("free_receive_structures: begin"); + + if (adapter->rx_sparemap) { + bus_dmamap_destroy(adapter->rxtag, adapter->rx_sparemap); + adapter->rx_sparemap = NULL; + } + + /* Cleanup any existing buffers */ + if (adapter->rx_buffer_area != NULL) { + rx_buffer = adapter->rx_buffer_area; + for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) { + if (rx_buffer->m_head != NULL) { + bus_dmamap_sync(adapter->rxtag, rx_buffer->map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(adapter->rxtag, + rx_buffer->map); + m_freem(rx_buffer->m_head); + rx_buffer->m_head = NULL; + } else if (rx_buffer->map != NULL) + bus_dmamap_unload(adapter->rxtag, + rx_buffer->map); + if (rx_buffer->map != NULL) { + bus_dmamap_destroy(adapter->rxtag, + rx_buffer->map); + rx_buffer->map = NULL; + } + } + } + + if (adapter->rx_buffer_area != NULL) { + free(adapter->rx_buffer_area, M_DEVBUF); + adapter->rx_buffer_area = NULL; + } + + if (adapter->rxtag != NULL) { + bus_dma_tag_destroy(adapter->rxtag); + adapter->rxtag = NULL; + } +} + +/********************************************************************* + * + * This routine executes in interrupt context. It replenishes + * the mbufs in the descriptor and sends data which has been + * dma'ed into host memory to upper layer. + * + * We loop at most count times if count is > 0, or until done if + * count < 0. + * + *********************************************************************/ +static int +em_rxeof(struct adapter *adapter, int count) +{ + struct ifnet *ifp; + struct mbuf *mp; + uint8_t accept_frame = 0; + uint8_t eop = 0; + uint16_t len, desc_len, prev_len_adj; + int i; + + /* Pointer to the receive descriptor being examined. */ + struct e1000_rx_desc *current_desc; + uint8_t status; + + ifp = adapter->ifp; + i = adapter->next_rx_desc_to_check; + current_desc = &adapter->rx_desc_base[i]; + bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, + BUS_DMASYNC_POSTREAD); + + if (!((current_desc->status) & E1000_RXD_STAT_DD)) + return (0); + + while ((current_desc->status & E1000_RXD_STAT_DD) && + (count != 0) && + (ifp->if_drv_flags & IFF_DRV_RUNNING)) { + struct mbuf *m = NULL; + + mp = adapter->rx_buffer_area[i].m_head; + /* + * Can't defer bus_dmamap_sync(9) because TBI_ACCEPT + * needs to access the last received byte in the mbuf. + */ + bus_dmamap_sync(adapter->rxtag, adapter->rx_buffer_area[i].map, + BUS_DMASYNC_POSTREAD); + + accept_frame = 1; + prev_len_adj = 0; + desc_len = le16toh(current_desc->length); + status = current_desc->status; + if (status & E1000_RXD_STAT_EOP) { + count--; + eop = 1; + if (desc_len < ETHER_CRC_LEN) { + len = 0; + prev_len_adj = ETHER_CRC_LEN - desc_len; + } else + len = desc_len - ETHER_CRC_LEN; + } else { + eop = 0; + len = desc_len; + } + + if (current_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK) { + uint8_t last_byte; + uint32_t pkt_len = desc_len; + + if (adapter->fmp != NULL) + pkt_len += adapter->fmp->m_pkthdr.len; + + last_byte = *(mtod(mp, caddr_t) + desc_len - 1); + if (TBI_ACCEPT(&adapter->hw, status, + current_desc->errors, pkt_len, last_byte)) { + e1000_tbi_adjust_stats_82543(&adapter->hw, + &adapter->stats, pkt_len, + adapter->hw.mac.addr); + if (len > 0) + len--; + } else + accept_frame = 0; + } + + if (accept_frame) { + if (em_get_buf(adapter, i) != 0) { + ifp->if_iqdrops++; + goto discard; + } + + /* Assign correct length to the current fragment */ + mp->m_len = len; + + if (adapter->fmp == NULL) { + mp->m_pkthdr.len = len; + adapter->fmp = mp; /* Store the first mbuf */ + adapter->lmp = mp; + } else { + /* Chain mbuf's together */ + mp->m_flags &= ~M_PKTHDR; + /* + * Adjust length of previous mbuf in chain if + * we received less than 4 bytes in the last + * descriptor. + */ + if (prev_len_adj > 0) { + adapter->lmp->m_len -= prev_len_adj; + adapter->fmp->m_pkthdr.len -= + prev_len_adj; + } + adapter->lmp->m_next = mp; + adapter->lmp = adapter->lmp->m_next; + adapter->fmp->m_pkthdr.len += len; + } + + if (eop) { + adapter->fmp->m_pkthdr.rcvif = ifp; + ifp->if_ipackets++; +#ifndef __rtems__ + em_receive_checksum(adapter, current_desc, + adapter->fmp); +#endif +#ifndef __NO_STRICT_ALIGNMENT + if (adapter->hw.mac.max_frame_size > + (MCLBYTES - ETHER_ALIGN) && + em_fixup_rx(adapter) != 0) + goto skip; +#endif + if (status & E1000_RXD_STAT_VP) { +#ifndef __rtems__ + adapter->fmp->m_pkthdr.ether_vtag = + (le16toh(current_desc->special) & + E1000_RXD_SPC_VLAN_MASK); + adapter->fmp->m_flags |= M_VLANTAG; +#else + ifp->if_iqdrops++; + goto discard; +#endif + } +#ifndef __NO_STRICT_ALIGNMENT +skip: +#endif + m = adapter->fmp; + adapter->fmp = NULL; + adapter->lmp = NULL; + } + } else { + ifp->if_ierrors++; +discard: + /* Reuse loaded DMA map and just update mbuf chain */ + mp = adapter->rx_buffer_area[i].m_head; + mp->m_len = mp->m_pkthdr.len = MCLBYTES; + mp->m_data = mp->m_ext.ext_buf; + mp->m_next = NULL; + if (adapter->hw.mac.max_frame_size <= + (MCLBYTES - ETHER_ALIGN)) + m_adj(mp, ETHER_ALIGN); + if (adapter->fmp != NULL) { + m_freem(adapter->fmp); + adapter->fmp = NULL; + adapter->lmp = NULL; + } + m = NULL; + } + + /* Zero out the receive descriptors status. */ + current_desc->status = 0; + bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Advance our pointers to the next descriptor. */ + if (++i == adapter->num_rx_desc) + i = 0; + if (m != NULL) { + adapter->next_rx_desc_to_check = i; +#ifdef DEVICE_POLLING + EM_UNLOCK(adapter); +# ifndef __rtems__ + (*ifp->if_input)(ifp, m); +# else + ether_input_skipping(ifp, m); +# endif + EM_LOCK(adapter); +#else + /* Already running unlocked */ +# ifndef __rtems__ + (*ifp->if_input)(ifp, m); +# else + ether_input_skipping(ifp, m); +# endif +#endif + i = adapter->next_rx_desc_to_check; + } + current_desc = &adapter->rx_desc_base[i]; + } + adapter->next_rx_desc_to_check = i; + + /* Advance the E1000's Receive Queue #0 "Tail Pointer". */ + if (--i < 0) + i = adapter->num_rx_desc - 1; + E1000_WRITE_REG(&adapter->hw, E1000_RDT, i); + if (!((current_desc->status) & E1000_RXD_STAT_DD)) + return (0); + + return (1); +} + +#ifndef __NO_STRICT_ALIGNMENT +/* + * When jumbo frames are enabled we should realign entire payload on + * architecures with strict alignment. This is serious design mistake of 8254x + * as it nullifies DMA operations. 8254x just allows RX buffer size to be + * 2048/4096/8192/16384. What we really want is 2048 - ETHER_ALIGN to align its + * payload. On architecures without strict alignment restrictions 8254x still + * performs unaligned memory access which would reduce the performance too. + * To avoid copying over an entire frame to align, we allocate a new mbuf and + * copy ethernet header to the new mbuf. The new mbuf is prepended into the + * existing mbuf chain. + * + * Be aware, best performance of the 8254x is achived only when jumbo frame is + * not used at all on architectures with strict alignment. + */ +static int +em_fixup_rx(struct adapter *adapter) +{ + struct mbuf *m, *n; + int error; + + error = 0; + m = adapter->fmp; + if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) { + bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len); + m->m_data += ETHER_HDR_LEN; + } else { + MGETHDR(n, M_DONTWAIT, MT_DATA); + if (n != NULL) { + bcopy(m->m_data, n->m_data, ETHER_HDR_LEN); + m->m_data += ETHER_HDR_LEN; + m->m_len -= ETHER_HDR_LEN; + n->m_len = ETHER_HDR_LEN; + M_MOVE_PKTHDR(n, m); + n->m_next = m; + adapter->fmp = n; + } else { + adapter->dropped_pkts++; + m_freem(adapter->fmp); + adapter->fmp = NULL; + error = ENOMEM; + } + } + + return (error); +} +#endif + +#ifndef __rtems__ +/********************************************************************* + * + * Verify that the hardware indicated that the checksum is valid. + * Inform the stack about the status of checksum so that stack + * doesn't spend time verifying the checksum. + * + *********************************************************************/ +static void +em_receive_checksum(struct adapter *adapter, + struct e1000_rx_desc *rx_desc, struct mbuf *mp) +{ + /* 82543 or newer only */ + if ((adapter->hw.mac.type < e1000_82543) || + /* Ignore Checksum bit is set */ + (rx_desc->status & E1000_RXD_STAT_IXSM)) { + mp->m_pkthdr.csum_flags = 0; + return; + } + + if (rx_desc->status & E1000_RXD_STAT_IPCS) { + /* Did it pass? */ + if (!(rx_desc->errors & E1000_RXD_ERR_IPE)) { + /* IP Checksum Good */ + mp->m_pkthdr.csum_flags = CSUM_IP_CHECKED; + mp->m_pkthdr.csum_flags |= CSUM_IP_VALID; + + } else { + mp->m_pkthdr.csum_flags = 0; + } + } + + if (rx_desc->status & E1000_RXD_STAT_TCPCS) { + /* Did it pass? */ + if (!(rx_desc->errors & E1000_RXD_ERR_TCPE)) { + mp->m_pkthdr.csum_flags |= + (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + mp->m_pkthdr.csum_data = htons(0xffff); + } + } +} + + +static void +em_enable_vlans(struct adapter *adapter) +{ + uint32_t ctrl; + + E1000_WRITE_REG(&adapter->hw, E1000_VET, ETHERTYPE_VLAN); + + ctrl = E1000_READ_REG(&adapter->hw, E1000_CTRL); + ctrl |= E1000_CTRL_VME; + E1000_WRITE_REG(&adapter->hw, E1000_CTRL, ctrl); +} +#endif + +static void +em_enable_intr(struct adapter *adapter) +{ + E1000_WRITE_REG(&adapter->hw, E1000_IMS, + (IMS_ENABLE_MASK)); +} + +static void +em_disable_intr(struct adapter *adapter) +{ + E1000_WRITE_REG(&adapter->hw, E1000_IMC, 0xffffffff); +} + +/* + * Bit of a misnomer, what this really means is + * to enable OS management of the system... aka + * to disable special hardware management features + */ +static void +em_init_manageability(struct adapter *adapter) +{ + /* A shared code workaround */ +#define E1000_82542_MANC2H E1000_MANC2H + if (adapter->has_manage) { + int manc2h = E1000_READ_REG(&adapter->hw, E1000_MANC2H); + int manc = E1000_READ_REG(&adapter->hw, E1000_MANC); + + /* disable hardware interception of ARP */ + manc &= ~(E1000_MANC_ARP_EN); + + /* enable receiving management packets to the host */ + if (adapter->hw.mac.type >= e1000_82571) { + manc |= E1000_MANC_EN_MNG2HOST; +#define E1000_MNG2HOST_PORT_623 (1 << 5) +#define E1000_MNG2HOST_PORT_664 (1 << 6) + manc2h |= E1000_MNG2HOST_PORT_623; + manc2h |= E1000_MNG2HOST_PORT_664; + E1000_WRITE_REG(&adapter->hw, E1000_MANC2H, manc2h); + } + + E1000_WRITE_REG(&adapter->hw, E1000_MANC, manc); + } +} + +/* + * Give control back to hardware management + * controller if there is one. + */ +static void +em_release_manageability(struct adapter *adapter) +{ + if (adapter->has_manage) { + int manc = E1000_READ_REG(&adapter->hw, E1000_MANC); + + /* re-enable hardware interception of ARP */ + manc |= E1000_MANC_ARP_EN; + + if (adapter->hw.mac.type >= e1000_82571) + manc &= ~E1000_MANC_EN_MNG2HOST; + + E1000_WRITE_REG(&adapter->hw, E1000_MANC, manc); + } +} + +/* + * em_get_hw_control sets {CTRL_EXT|FWSM}:DRV_LOAD bit. + * For ASF and Pass Through versions of f/w this means that + * the driver is loaded. For AMT version (only with 82573) + * of the f/w this means that the network i/f is open. + * + */ +static void +em_get_hw_control(struct adapter *adapter) +{ + u32 ctrl_ext, swsm; + + /* Let firmware know the driver has taken over */ + switch (adapter->hw.mac.type) { + case e1000_82573: + swsm = E1000_READ_REG(&adapter->hw, E1000_SWSM); + E1000_WRITE_REG(&adapter->hw, E1000_SWSM, + swsm | E1000_SWSM_DRV_LOAD); + break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: + case e1000_ich8lan: + case e1000_ich9lan: + ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT); + E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT, + ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); + break; + default: + break; + } +} + +/* + * em_release_hw_control resets {CTRL_EXT|FWSM}:DRV_LOAD bit. + * For ASF and Pass Through versions of f/w this means that the + * driver is no longer loaded. For AMT version (only with 82573) i + * of the f/w this means that the network i/f is closed. + * + */ +static void +em_release_hw_control(struct adapter *adapter) +{ + u32 ctrl_ext, swsm; + + /* Let firmware taken over control of h/w */ + switch (adapter->hw.mac.type) { + case e1000_82573: + swsm = E1000_READ_REG(&adapter->hw, E1000_SWSM); + E1000_WRITE_REG(&adapter->hw, E1000_SWSM, + swsm & ~E1000_SWSM_DRV_LOAD); + break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: + case e1000_ich8lan: + case e1000_ich9lan: + ctrl_ext = E1000_READ_REG(&adapter->hw, E1000_CTRL_EXT); + E1000_WRITE_REG(&adapter->hw, E1000_CTRL_EXT, + ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); + break; + default: + break; + + } +} + +static int +em_is_valid_ether_addr(uint8_t *addr) +{ + char zero_addr[6] = { 0, 0, 0, 0, 0, 0 }; + + if ((addr[0] & 1) || (!bcmp(addr, zero_addr, ETHER_ADDR_LEN))) { + return (FALSE); + } + + return (TRUE); +} + +/* + * NOTE: the following routines using the e1000 + * naming style are provided to the shared + * code which expects that rather than 'em' + */ + +void +e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t *value) +{ + pci_write_config(((struct e1000_osdep *)hw->back)->dev, reg, *value, 2); +} + +void +e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t *value) +{ + *value = pci_read_config(((struct e1000_osdep *)hw->back)->dev, reg, 2); +} + +void +e1000_pci_set_mwi(struct e1000_hw *hw) +{ + pci_write_config(((struct e1000_osdep *)hw->back)->dev, PCIR_COMMAND, + (hw->bus.pci_cmd_word | CMD_MEM_WRT_INVALIDATE), 2); +} + +void +e1000_pci_clear_mwi(struct e1000_hw *hw) +{ + pci_write_config(((struct e1000_osdep *)hw->back)->dev, PCIR_COMMAND, + (hw->bus.pci_cmd_word & ~CMD_MEM_WRT_INVALIDATE), 2); +} + +/* + * Read the PCI Express capabilities + */ +int32_t +e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value) +{ + int32_t error = E1000_SUCCESS; + uint16_t cap_off; + + switch (hw->mac.type) { + + case e1000_82571: + case e1000_82572: + case e1000_82573: + case e1000_80003es2lan: + cap_off = 0xE0; + e1000_read_pci_cfg(hw, cap_off + reg, value); + break; + default: + error = ~E1000_NOT_IMPLEMENTED; + break; + } + + return (error); +} + +int32_t +e1000_alloc_zeroed_dev_spec_struct(struct e1000_hw *hw, uint32_t size) +{ + int32_t error = 0; + + hw->dev_spec = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); + if (hw->dev_spec == NULL) + error = ENOMEM; +#ifdef __rtems__ + /* M_ZERO not implemented :-( */ + if ( hw->dev_spec ) + memset(hw->dev_spec, 0, size); +#endif + + return (error); +} + +void +e1000_free_dev_spec_struct(struct e1000_hw *hw) +{ + if (hw->dev_spec != NULL) + free(hw->dev_spec, M_DEVBUF); + return; +} + +/* + * Enable PCI Wake On Lan capability + */ +void +em_enable_wakeup(device_t dev) +{ + u16 cap, status; + u8 id; + + /* First find the capabilities pointer*/ + cap = pci_read_config(dev, PCIR_CAP_PTR, 2); + /* Read the PM Capabilities */ + id = pci_read_config(dev, cap, 1); + if (id != PCIY_PMG) /* Something wrong */ + return; + /* OK, we have the power capabilities, so + now get the status register */ + cap += PCIR_POWER_STATUS; + status = pci_read_config(dev, cap, 2); + status |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + pci_write_config(dev, cap, status, 2); + return; +} + + +/********************************************************************* +* 82544 Coexistence issue workaround. +* There are 2 issues. +* 1. Transmit Hang issue. +* To detect this issue, following equation can be used... +* SIZE[3:0] + ADDR[2:0] = SUM[3:0]. +* If SUM[3:0] is in between 1 to 4, we will have this issue. +* +* 2. DAC issue. +* To detect this issue, following equation can be used... +* SIZE[3:0] + ADDR[2:0] = SUM[3:0]. +* If SUM[3:0] is in between 9 to c, we will have this issue. +* +* +* WORKAROUND: +* Make sure we do not have ending address +* as 1,2,3,4(Hang) or 9,a,b,c (DAC) +* +*************************************************************************/ +static uint32_t +em_fill_descriptors (bus_addr_t address, uint32_t length, + PDESC_ARRAY desc_array) +{ + /* Since issue is sensitive to length and address.*/ + /* Let us first check the address...*/ + uint32_t safe_terminator; + if (length <= 4) { + desc_array->descriptor[0].address = address; + desc_array->descriptor[0].length = length; + desc_array->elements = 1; + return (desc_array->elements); + } + safe_terminator = (uint32_t)((((uint32_t)address & 0x7) + + (length & 0xF)) & 0xF); + /* if it does not fall between 0x1 to 0x4 and 0x9 to 0xC then return */ + if (safe_terminator == 0 || + (safe_terminator > 4 && + safe_terminator < 9) || + (safe_terminator > 0xC && + safe_terminator <= 0xF)) { + desc_array->descriptor[0].address = address; + desc_array->descriptor[0].length = length; + desc_array->elements = 1; + return (desc_array->elements); + } + + desc_array->descriptor[0].address = address; + desc_array->descriptor[0].length = length - 4; + desc_array->descriptor[1].address = address + (length - 4); + desc_array->descriptor[1].length = 4; + desc_array->elements = 2; + return (desc_array->elements); +} + +/********************************************************************** + * + * Update the board statistics counters. + * + **********************************************************************/ +static void +em_update_stats_counters(struct adapter *adapter) +{ + struct ifnet *ifp; + + if(adapter->hw.media_type == e1000_media_type_copper || + (E1000_READ_REG(&adapter->hw, E1000_STATUS) & E1000_STATUS_LU)) { + adapter->stats.symerrs += E1000_READ_REG(&adapter->hw, E1000_SYMERRS); + adapter->stats.sec += E1000_READ_REG(&adapter->hw, E1000_SEC); + } + adapter->stats.crcerrs += E1000_READ_REG(&adapter->hw, E1000_CRCERRS); + adapter->stats.mpc += E1000_READ_REG(&adapter->hw, E1000_MPC); + adapter->stats.scc += E1000_READ_REG(&adapter->hw, E1000_SCC); + adapter->stats.ecol += E1000_READ_REG(&adapter->hw, E1000_ECOL); + + adapter->stats.mcc += E1000_READ_REG(&adapter->hw, E1000_MCC); + adapter->stats.latecol += E1000_READ_REG(&adapter->hw, E1000_LATECOL); + adapter->stats.colc += E1000_READ_REG(&adapter->hw, E1000_COLC); + adapter->stats.dc += E1000_READ_REG(&adapter->hw, E1000_DC); + adapter->stats.rlec += E1000_READ_REG(&adapter->hw, E1000_RLEC); + adapter->stats.xonrxc += E1000_READ_REG(&adapter->hw, E1000_XONRXC); + adapter->stats.xontxc += E1000_READ_REG(&adapter->hw, E1000_XONTXC); + adapter->stats.xoffrxc += E1000_READ_REG(&adapter->hw, E1000_XOFFRXC); + adapter->stats.xofftxc += E1000_READ_REG(&adapter->hw, E1000_XOFFTXC); + adapter->stats.fcruc += E1000_READ_REG(&adapter->hw, E1000_FCRUC); + adapter->stats.prc64 += E1000_READ_REG(&adapter->hw, E1000_PRC64); + adapter->stats.prc127 += E1000_READ_REG(&adapter->hw, E1000_PRC127); + adapter->stats.prc255 += E1000_READ_REG(&adapter->hw, E1000_PRC255); + adapter->stats.prc511 += E1000_READ_REG(&adapter->hw, E1000_PRC511); + adapter->stats.prc1023 += E1000_READ_REG(&adapter->hw, E1000_PRC1023); + adapter->stats.prc1522 += E1000_READ_REG(&adapter->hw, E1000_PRC1522); + adapter->stats.gprc += E1000_READ_REG(&adapter->hw, E1000_GPRC); + adapter->stats.bprc += E1000_READ_REG(&adapter->hw, E1000_BPRC); + adapter->stats.mprc += E1000_READ_REG(&adapter->hw, E1000_MPRC); + adapter->stats.gptc += E1000_READ_REG(&adapter->hw, E1000_GPTC); + + /* For the 64-bit byte counters the low dword must be read first. */ + /* Both registers clear on the read of the high dword */ + + adapter->stats.gorcl += E1000_READ_REG(&adapter->hw, E1000_GORCL); + adapter->stats.gorch += E1000_READ_REG(&adapter->hw, E1000_GORCH); + adapter->stats.gotcl += E1000_READ_REG(&adapter->hw, E1000_GOTCL); + adapter->stats.gotch += E1000_READ_REG(&adapter->hw, E1000_GOTCH); + + adapter->stats.rnbc += E1000_READ_REG(&adapter->hw, E1000_RNBC); + adapter->stats.ruc += E1000_READ_REG(&adapter->hw, E1000_RUC); + adapter->stats.rfc += E1000_READ_REG(&adapter->hw, E1000_RFC); + adapter->stats.roc += E1000_READ_REG(&adapter->hw, E1000_ROC); + adapter->stats.rjc += E1000_READ_REG(&adapter->hw, E1000_RJC); + + adapter->stats.torl += E1000_READ_REG(&adapter->hw, E1000_TORL); + adapter->stats.torh += E1000_READ_REG(&adapter->hw, E1000_TORH); + adapter->stats.totl += E1000_READ_REG(&adapter->hw, E1000_TOTL); + adapter->stats.toth += E1000_READ_REG(&adapter->hw, E1000_TOTH); + + adapter->stats.tpr += E1000_READ_REG(&adapter->hw, E1000_TPR); + adapter->stats.tpt += E1000_READ_REG(&adapter->hw, E1000_TPT); + adapter->stats.ptc64 += E1000_READ_REG(&adapter->hw, E1000_PTC64); + adapter->stats.ptc127 += E1000_READ_REG(&adapter->hw, E1000_PTC127); + adapter->stats.ptc255 += E1000_READ_REG(&adapter->hw, E1000_PTC255); + adapter->stats.ptc511 += E1000_READ_REG(&adapter->hw, E1000_PTC511); + adapter->stats.ptc1023 += E1000_READ_REG(&adapter->hw, E1000_PTC1023); + adapter->stats.ptc1522 += E1000_READ_REG(&adapter->hw, E1000_PTC1522); + adapter->stats.mptc += E1000_READ_REG(&adapter->hw, E1000_MPTC); + adapter->stats.bptc += E1000_READ_REG(&adapter->hw, E1000_BPTC); + + if (adapter->hw.mac.type >= e1000_82543) { + adapter->stats.algnerrc += + E1000_READ_REG(&adapter->hw, E1000_ALGNERRC); + adapter->stats.rxerrc += + E1000_READ_REG(&adapter->hw, E1000_RXERRC); + adapter->stats.tncrs += + E1000_READ_REG(&adapter->hw, E1000_TNCRS); + adapter->stats.cexterr += + E1000_READ_REG(&adapter->hw, E1000_CEXTERR); + adapter->stats.tsctc += + E1000_READ_REG(&adapter->hw, E1000_TSCTC); + adapter->stats.tsctfc += + E1000_READ_REG(&adapter->hw, E1000_TSCTFC); + } + ifp = adapter->ifp; + + ifp->if_collisions = adapter->stats.colc; + + /* Rx Errors */ + ifp->if_ierrors = adapter->dropped_pkts + adapter->stats.rxerrc + + adapter->stats.crcerrs + adapter->stats.algnerrc + + adapter->stats.ruc + adapter->stats.roc + + adapter->stats.mpc + adapter->stats.cexterr; + + /* Tx Errors */ + ifp->if_oerrors = adapter->stats.ecol + + adapter->stats.latecol + adapter->watchdog_events; +} + + +/********************************************************************** + * + * This routine is called only when em_display_debug_stats is enabled. + * This routine provides a way to take a look at important statistics + * maintained by the driver and hardware. + * + **********************************************************************/ +#ifndef __rtems__ +static void +#else +void +#endif +em_print_debug_info(struct adapter *adapter) +{ +#ifndef __rtems__ + device_t dev = adapter->dev; + uint8_t *hw_addr = adapter->hw.hw_addr; +#else + device_t dev; + uint8_t *hw_addr; + if ( !adapter ) { + printk("Need 'adapter' arg\n"); + return; + } + dev = adapter->dev; + hw_addr = adapter->hw.hw_addr; +#endif + + device_printf(dev, "Adapter hardware address = %p \n", hw_addr); + device_printf(dev, "CTRL = 0x%x RCTL = 0x%x \n", + E1000_READ_REG(&adapter->hw, E1000_CTRL), + E1000_READ_REG(&adapter->hw, E1000_RCTL)); + device_printf(dev, "Packet buffer = Tx=%dk Rx=%dk \n", + ((E1000_READ_REG(&adapter->hw, E1000_PBA) & 0xffff0000) >> 16),\ + (E1000_READ_REG(&adapter->hw, E1000_PBA) & 0xffff) ); + device_printf(dev, "Flow control watermarks high = %d low = %d\n", + adapter->hw.mac.fc_high_water, + adapter->hw.mac.fc_low_water); + device_printf(dev, "tx_int_delay = %d, tx_abs_int_delay = %d\n", + E1000_READ_REG(&adapter->hw, E1000_TIDV), + E1000_READ_REG(&adapter->hw, E1000_TADV)); + device_printf(dev, "rx_int_delay = %d, rx_abs_int_delay = %d\n", + E1000_READ_REG(&adapter->hw, E1000_RDTR), + E1000_READ_REG(&adapter->hw, E1000_RADV)); + device_printf(dev, "fifo workaround = %lld, fifo_reset_count = %lld\n", + (long long)adapter->tx_fifo_wrk_cnt, + (long long)adapter->tx_fifo_reset_cnt); + device_printf(dev, "hw tdh = %d, hw tdt = %d\n", + E1000_READ_REG(&adapter->hw, E1000_TDH), + E1000_READ_REG(&adapter->hw, E1000_TDT)); + device_printf(dev, "hw rdh = %d, hw rdt = %d\n", + E1000_READ_REG(&adapter->hw, E1000_RDH), + E1000_READ_REG(&adapter->hw, E1000_RDT)); + device_printf(dev, "Num Tx descriptors avail = %d\n", + adapter->num_tx_desc_avail); + device_printf(dev, "Tx Descriptors not avail1 = %ld\n", + adapter->no_tx_desc_avail1); + device_printf(dev, "Tx Descriptors not avail2 = %ld\n", + adapter->no_tx_desc_avail2); + device_printf(dev, "Std mbuf failed = %ld\n", + adapter->mbuf_alloc_failed); + device_printf(dev, "Std mbuf cluster failed = %ld\n", + adapter->mbuf_cluster_failed); + device_printf(dev, "Driver dropped packets = %ld\n", + adapter->dropped_pkts); + device_printf(dev, "Driver tx dma failure in encap = %ld\n", + adapter->no_tx_dma_setup); +} + +#ifndef __rtems__ +static void +#else +void +#endif +em_print_hw_stats(struct adapter *adapter) +{ + device_t dev = adapter->dev; + + device_printf(dev, "Excessive collisions = %lld\n", + (long long)adapter->stats.ecol); +#if (DEBUG_HW > 0) /* Dont output these errors normally */ + device_printf(dev, "Symbol errors = %lld\n", + (long long)adapter->stats.symerrs); +#endif + device_printf(dev, "Sequence errors = %lld\n", + (long long)adapter->stats.sec); + device_printf(dev, "Defer count = %lld\n", + (long long)adapter->stats.dc); + device_printf(dev, "Missed Packets = %lld\n", + (long long)adapter->stats.mpc); + device_printf(dev, "Receive No Buffers = %lld\n", + (long long)adapter->stats.rnbc); + /* RLEC is inaccurate on some hardware, calculate our own. */ + device_printf(dev, "Receive Length Errors = %lld\n", + ((long long)adapter->stats.roc + (long long)adapter->stats.ruc)); + device_printf(dev, "Receive errors = %lld\n", + (long long)adapter->stats.rxerrc); + device_printf(dev, "Crc errors = %lld\n", + (long long)adapter->stats.crcerrs); + device_printf(dev, "Alignment errors = %lld\n", + (long long)adapter->stats.algnerrc); + device_printf(dev, "Carrier extension errors = %lld\n", + (long long)adapter->stats.cexterr); + device_printf(dev, "RX overruns = %ld\n", adapter->rx_overruns); + device_printf(dev, "watchdog timeouts = %ld\n", + adapter->watchdog_events); + device_printf(dev, "XON Rcvd = %lld\n", + (long long)adapter->stats.xonrxc); + device_printf(dev, "XON Xmtd = %lld\n", + (long long)adapter->stats.xontxc); + device_printf(dev, "XOFF Rcvd = %lld\n", + (long long)adapter->stats.xoffrxc); + device_printf(dev, "XOFF Xmtd = %lld\n", + (long long)adapter->stats.xofftxc); + device_printf(dev, "Good Packets Rcvd = %lld\n", + (long long)adapter->stats.gprc); + device_printf(dev, "Good Packets Xmtd = %lld\n", + (long long)adapter->stats.gptc); + device_printf(dev, "TSO Contexts Xmtd = %lld\n", + (long long)adapter->stats.tsctc); + device_printf(dev, "TSO Contexts Failed = %lld\n", + (long long)adapter->stats.tsctfc); +} + +#ifndef __rtems__ +static int +em_sysctl_debug_info(SYSCTL_HANDLER_ARGS) +{ + struct adapter *adapter; + int error; + int result; + + result = -1; + error = sysctl_handle_int(oidp, &result, 0, req); + + if (error || !req->newptr) + return (error); + + if (result == 1) { + adapter = (struct adapter *)arg1; + em_print_debug_info(adapter); + } + + return (error); +} + + +static int +em_sysctl_stats(SYSCTL_HANDLER_ARGS) +{ + struct adapter *adapter; + int error; + int result; + + result = -1; + error = sysctl_handle_int(oidp, &result, 0, req); + + if (error || !req->newptr) + return (error); + + if (result == 1) { + adapter = (struct adapter *)arg1; + em_print_hw_stats(adapter); + } + + return (error); +} + +static int +em_sysctl_int_delay(SYSCTL_HANDLER_ARGS) +{ + struct em_int_delay_info *info; + struct adapter *adapter; + uint32_t regval; + int error; + int usecs; + int ticks; + + info = (struct em_int_delay_info *)arg1; + usecs = info->value; + error = sysctl_handle_int(oidp, &usecs, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (usecs < 0 || usecs > EM_TICKS_TO_USECS(65535)) + return (EINVAL); + info->value = usecs; + ticks = EM_USECS_TO_TICKS(usecs); + + adapter = info->adapter; + + EM_LOCK(adapter); + regval = E1000_READ_OFFSET(&adapter->hw, info->offset); + regval = (regval & ~0xffff) | (ticks & 0xffff); + /* Handle a few special cases. */ + switch (info->offset) { + case E1000_RDTR: + break; + case E1000_TIDV: + if (ticks == 0) { + adapter->txd_cmd &= ~E1000_TXD_CMD_IDE; + /* Don't write 0 into the TIDV register. */ + regval++; + } else + if (adapter->hw.mac.type != e1000_82575) + adapter->txd_cmd |= E1000_TXD_CMD_IDE; + break; + } + E1000_WRITE_OFFSET(&adapter->hw, info->offset, regval); + EM_UNLOCK(adapter); + return (0); +} +#endif + +static void +em_add_int_delay_sysctl(struct adapter *adapter, const char *name, + const char *description, struct em_int_delay_info *info, + int offset, int value) +{ + info->adapter = adapter; + info->offset = offset; + info->value = value; + SYSCTL_ADD_PROC(device_get_sysctl_ctx(adapter->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)), + OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, + info, 0, em_sysctl_int_delay, "I", description); +} + +#ifndef DEVICE_POLLING +static void +em_add_rx_process_limit(struct adapter *adapter, const char *name, + const char *description, int *limit, int value) +{ + *limit = value; + SYSCTL_ADD_INT(device_get_sysctl_ctx(adapter->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)), + OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, limit, value, description); +} +#endif diff --git a/bsd_eth_drivers/if_em/if_em.h b/bsd_eth_drivers/if_em/if_em.h new file mode 100644 index 0000000..70797c0 --- /dev/null +++ b/bsd_eth_drivers/if_em/if_em.h @@ -0,0 +1,443 @@ +/************************************************************************** + +Copyright (c) 2001-2007, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ +/*$FreeBSD: src/sys/dev/em/if_em.h,v 1.61 2007/05/17 00:14:03 jfv Exp $*/ + +#ifndef _EM_H_DEFINED_ +#define _EM_H_DEFINED_ + +/* Tunables */ + +/* + * EM_TXD: Maximum number of Transmit Descriptors + * Valid Range: 80-256 for 82542 and 82543-based adapters + * 80-4096 for others + * Default Value: 256 + * This value is the number of transmit descriptors allocated by the driver. + * Increasing this value allows the driver to queue more transmits. Each + * descriptor is 16 bytes. + * Since TDLEN should be multiple of 128bytes, the number of transmit + * desscriptors should meet the following condition. + * (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0 + */ +#define EM_MIN_TXD 80 +#define EM_MAX_TXD_82543 256 +#define EM_MAX_TXD 4096 +#define EM_DEFAULT_TXD EM_MAX_TXD_82543 + +/* + * EM_RXD - Maximum number of receive Descriptors + * Valid Range: 80-256 for 82542 and 82543-based adapters + * 80-4096 for others + * Default Value: 256 + * This value is the number of receive descriptors allocated by the driver. + * Increasing this value allows the driver to buffer more incoming packets. + * Each descriptor is 16 bytes. A receive buffer is also allocated for each + * descriptor. The maximum MTU size is 16110. + * Since TDLEN should be multiple of 128bytes, the number of transmit + * desscriptors should meet the following condition. + * (num_tx_desc * sizeof(struct e1000_tx_desc)) % 128 == 0 + */ +#define EM_MIN_RXD 80 +#define EM_MAX_RXD_82543 256 +#define EM_MAX_RXD 4096 +#define EM_DEFAULT_RXD EM_MAX_RXD_82543 + +/* + * EM_TIDV - Transmit Interrupt Delay Value + * Valid Range: 0-65535 (0=off) + * Default Value: 64 + * This value delays the generation of transmit interrupts in units of + * 1.024 microseconds. Transmit interrupt reduction can improve CPU + * efficiency if properly tuned for specific network traffic. If the + * system is reporting dropped transmits, this value may be set too high + * causing the driver to run out of available transmit descriptors. + */ +#define EM_TIDV 64 + +/* + * EM_TADV - Transmit Absolute Interrupt Delay Value + * (Not valid for 82542/82543/82544) + * Valid Range: 0-65535 (0=off) + * Default Value: 64 + * This value, in units of 1.024 microseconds, limits the delay in which a + * transmit interrupt is generated. Useful only if EM_TIDV is non-zero, + * this value ensures that an interrupt is generated after the initial + * packet is sent on the wire within the set amount of time. Proper tuning, + * along with EM_TIDV, may improve traffic throughput in specific + * network conditions. + */ +#define EM_TADV 64 + +/* + * EM_RDTR - Receive Interrupt Delay Timer (Packet Timer) + * Valid Range: 0-65535 (0=off) + * Default Value: 0 + * This value delays the generation of receive interrupts in units of 1.024 + * microseconds. Receive interrupt reduction can improve CPU efficiency if + * properly tuned for specific network traffic. Increasing this value adds + * extra latency to frame reception and can end up decreasing the throughput + * of TCP traffic. If the system is reporting dropped receives, this value + * may be set too high, causing the driver to run out of available receive + * descriptors. + * + * CAUTION: When setting EM_RDTR to a value other than 0, adapters + * may hang (stop transmitting) under certain network conditions. + * If this occurs a WATCHDOG message is logged in the system + * event log. In addition, the controller is automatically reset, + * restoring the network connection. To eliminate the potential + * for the hang ensure that EM_RDTR is set to 0. + */ +#define EM_RDTR 0 + +/* + * Receive Interrupt Absolute Delay Timer (Not valid for 82542/82543/82544) + * Valid Range: 0-65535 (0=off) + * Default Value: 64 + * This value, in units of 1.024 microseconds, limits the delay in which a + * receive interrupt is generated. Useful only if EM_RDTR is non-zero, + * this value ensures that an interrupt is generated after the initial + * packet is received within the set amount of time. Proper tuning, + * along with EM_RDTR, may improve traffic throughput in specific network + * conditions. + */ +#define EM_RADV 64 + +/* + * This parameter controls the duration of transmit watchdog timer. + */ +#define EM_TX_TIMEOUT 5 /* set to 5 seconds */ + +/* + * This parameter controls when the driver calls the routine to reclaim + * transmit descriptors. + */ +#define EM_TX_CLEANUP_THRESHOLD (adapter->num_tx_desc / 8) +#define EM_TX_OP_THRESHOLD (adapter->num_tx_desc / 32) + +/* + * This parameter controls whether or not autonegotation is enabled. + * 0 - Disable autonegotiation + * 1 - Enable autonegotiation + */ +#define DO_AUTO_NEG 1 + +/* + * This parameter control whether or not the driver will wait for + * autonegotiation to complete. + * 1 - Wait for autonegotiation to complete + * 0 - Don't wait for autonegotiation to complete + */ +#define WAIT_FOR_AUTO_NEG_DEFAULT 0 + +/* Tunables -- End */ + +#define AUTONEG_ADV_DEFAULT (ADVERTISE_10_HALF | ADVERTISE_10_FULL | \ + ADVERTISE_100_HALF | ADVERTISE_100_FULL | \ + ADVERTISE_1000_FULL) + +#define AUTO_ALL_MODES 0 + +/* PHY master/slave setting */ +#define EM_MASTER_SLAVE e1000_ms_hw_default + +/* + * Micellaneous constants + */ +#define EM_VENDOR_ID 0x8086 +#define EM_FLASH 0x0014 + +#define EM_JUMBO_PBA 0x00000028 +#define EM_DEFAULT_PBA 0x00000030 +#define EM_SMARTSPEED_DOWNSHIFT 3 +#define EM_SMARTSPEED_MAX 15 +#define EM_MAX_INTR 10 +#define EM_TSO_SEG_SIZE 4096 /* Max dma seg size */ + +#define MAX_NUM_MULTICAST_ADDRESSES 128 +#define PCI_ANY_ID (~0U) +#define ETHER_ALIGN 2 +#define EM_TX_BUFFER_SIZE ((uint32_t) 1514) +#define EM_FC_PAUSE_TIME 0x0680 +#define EM_EEPROM_APME 0x400; + +/* + * TDBA/RDBA should be aligned on 16 byte boundary. But TDLEN/RDLEN should be + * multiple of 128 bytes. So we align TDBA/RDBA on 128 byte boundary. This will + * also optimize cache line size effect. H/W supports up to cache line size 128. + */ +#define EM_DBA_ALIGN 128 + +#define SPEED_MODE_BIT (1<<21) /* On PCI-E MACs only */ + +/* PCI Config defines */ +#define EM_BAR_TYPE(v) ((v) & EM_BAR_TYPE_MASK) +#define EM_BAR_TYPE_MASK 0x00000001 +#define EM_BAR_TYPE_MMEM 0x00000000 +#define EM_BAR_TYPE_IO 0x00000001 +#define EM_BAR_TYPE_FLASH 0x0014 +#define EM_BAR_MEM_TYPE(v) ((v) & EM_BAR_MEM_TYPE_MASK) +#define EM_BAR_MEM_TYPE_MASK 0x00000006 +#define EM_BAR_MEM_TYPE_32BIT 0x00000000 +#define EM_BAR_MEM_TYPE_64BIT 0x00000004 +#define EM_MSIX_BAR 3 /* On 82575 */ + +/* Defines for printing debug information */ +#define DEBUG_INIT 0 +#define DEBUG_IOCTL 0 +#define DEBUG_HW 0 + +#define INIT_DEBUGOUT(S) if (DEBUG_INIT) printf(S "\n") +#define INIT_DEBUGOUT1(S, A) if (DEBUG_INIT) printf(S "\n", A) +#define INIT_DEBUGOUT2(S, A, B) if (DEBUG_INIT) printf(S "\n", A, B) +#define IOCTL_DEBUGOUT(S) if (DEBUG_IOCTL) printf(S "\n") +#define IOCTL_DEBUGOUT1(S, A) if (DEBUG_IOCTL) printf(S "\n", A) +#define IOCTL_DEBUGOUT2(S, A, B) if (DEBUG_IOCTL) printf(S "\n", A, B) +#define HW_DEBUGOUT(S) if (DEBUG_HW) printf(S "\n") +#define HW_DEBUGOUT1(S, A) if (DEBUG_HW) printf(S "\n", A) +#define HW_DEBUGOUT2(S, A, B) if (DEBUG_HW) printf(S "\n", A, B) + +#define EM_MAX_SCATTER 64 +#define EM_TSO_SIZE 65535 /* maxsize of a dma transfer */ +#define EM_TSO_SEG_SIZE 4096 /* Max dma segment size */ +#define ETH_ZLEN 60 +#define ETH_ADDR_LEN 6 +#define CSUM_OFFLOAD 7 /* Offload bits in csum flags */ + +struct adapter; + +struct em_int_delay_info { + struct adapter *adapter; /* Back-pointer to the adapter struct */ + int offset; /* Register offset to read/write */ + int value; /* Current value in usecs */ +}; + +/* + * Bus dma allocation structure used by + * e1000_dma_malloc and e1000_dma_free. + */ +struct em_dma_alloc { + bus_addr_t dma_paddr; +#ifndef __rtems__ + caddr_t dma_vaddr; +#else + void * dma_vaddr; +#endif + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_dma_segment_t dma_seg; + int dma_nseg; +}; + +/* Our adapter structure */ +struct adapter { + struct ifnet *ifp; + struct e1000_hw hw; + + /* FreeBSD operating-system-specific structures. */ + struct e1000_osdep osdep; + struct device *dev; + struct resource *res_memory; + struct resource *flash_mem; + struct resource *msix_mem; + struct resource *res_ioport; + struct resource *res_interrupt; + void *int_handler_tag; + struct ifmedia media; + struct callout timer; + struct callout tx_fifo_timer; + int watchdog_timer; + int io_rid; + int msi; + int if_flags; + struct mtx mtx; +#ifndef __rtems__ + int em_insert_vlan_header; +#endif + struct task link_task; + struct task rxtx_task; + struct taskqueue *tq; /* private task queue */ + /* Management and WOL features */ + int wol; + int has_manage; + + /* Info about the board itself */ + uint32_t part_num; + uint8_t link_active; + uint16_t link_speed; + uint16_t link_duplex; + uint32_t smartspeed; + struct em_int_delay_info tx_int_delay; + struct em_int_delay_info tx_abs_int_delay; + struct em_int_delay_info rx_int_delay; + struct em_int_delay_info rx_abs_int_delay; + + /* + * Transmit definitions + * + * We have an array of num_tx_desc descriptors (handled + * by the controller) paired with an array of tx_buffers + * (at tx_buffer_area). + * The index of the next available descriptor is next_avail_tx_desc. + * The number of remaining tx_desc is num_tx_desc_avail. + */ + struct em_dma_alloc txdma; /* bus_dma glue for tx desc */ + struct e1000_tx_desc *tx_desc_base; + uint32_t next_avail_tx_desc; + uint32_t next_tx_to_clean; + volatile uint16_t num_tx_desc_avail; + uint16_t num_tx_desc; + uint32_t txd_cmd; + struct em_buffer *tx_buffer_area; + bus_dma_tag_t txtag; /* dma tag for tx */ + uint32_t tx_tso; /* last tx was tso */ + + /* + * Transmit function pointer: + * legacy or advanced (82575 and later) + */ + int (*em_xmit) (struct adapter *adapter, struct mbuf **m_headp); + + /* + * Receive definitions + * + * we have an array of num_rx_desc rx_desc (handled by the + * controller), and paired with an array of rx_buffers + * (at rx_buffer_area). + * The next pair to check on receive is at offset next_rx_desc_to_check + */ + struct em_dma_alloc rxdma; /* bus_dma glue for rx desc */ + struct e1000_rx_desc *rx_desc_base; + uint32_t next_rx_desc_to_check; + uint32_t rx_buffer_len; + uint16_t num_rx_desc; + int rx_process_limit; + struct em_buffer *rx_buffer_area; + bus_dma_tag_t rxtag; + bus_dmamap_t rx_sparemap; + + /* + * First/last mbuf pointers, for + * collecting multisegment RX packets. + */ + struct mbuf *fmp; + struct mbuf *lmp; + + /* Misc stats maintained by the driver */ + unsigned long dropped_pkts; + unsigned long mbuf_alloc_failed; + unsigned long mbuf_cluster_failed; + unsigned long no_tx_desc_avail1; + unsigned long no_tx_desc_avail2; + unsigned long no_tx_map_avail; + unsigned long no_tx_dma_setup; + unsigned long watchdog_events; + unsigned long rx_overruns; + + /* Used in for 82547 10Mb Half workaround */ + #define EM_PBA_BYTES_SHIFT 0xA + #define EM_TX_HEAD_ADDR_SHIFT 7 + #define EM_PBA_TX_MASK 0xFFFF0000 + #define EM_FIFO_HDR 0x10 + + #define EM_82547_PKT_THRESH 0x3e0 + + uint32_t tx_fifo_size; + uint32_t tx_fifo_head; + uint32_t tx_fifo_head_addr; + uint64_t tx_fifo_reset_cnt; + uint64_t tx_fifo_wrk_cnt; + uint32_t tx_head_addr; + + /* For 82544 PCIX Workaround */ + boolean_t pcix_82544; + boolean_t in_detach; + + struct e1000_hw_stats stats; +}; + +/* ****************************************************************************** + * vendor_info_array + * + * This array contains the list of Subvendor/Subdevice IDs on which the driver + * should load. + * + * ******************************************************************************/ +typedef struct _em_vendor_info_t { + unsigned int vendor_id; + unsigned int device_id; + unsigned int subvendor_id; + unsigned int subdevice_id; + unsigned int index; +} em_vendor_info_t; + + +struct em_buffer { + int next_eop; /* Index of the desc to watch */ + struct mbuf *m_head; + bus_dmamap_t map; /* bus_dma map for packet */ +}; + +/* For 82544 PCIX Workaround */ +typedef struct _ADDRESS_LENGTH_PAIR +{ + uint64_t address; + uint32_t length; +} ADDRESS_LENGTH_PAIR, *PADDRESS_LENGTH_PAIR; + +typedef struct _DESCRIPTOR_PAIR +{ + ADDRESS_LENGTH_PAIR descriptor[4]; + uint32_t elements; +} DESC_ARRAY, *PDESC_ARRAY; + +#ifndef __rtems__ +#define EM_LOCK_INIT(_sc, _name) \ + mtx_init(&(_sc)->mtx, _name, MTX_NETWORK_LOCK, MTX_DEF) +#define EM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx) +#define EM_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define EM_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define EM_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) +#else +/* Under RTEMS everything is protected by the global + * bsdnet lock/mutex + */ +#define EM_LOCK_INIT(_sc, _name) \ + do { (_sc)->mtx.mtx_id = 0; } while (0) +#define EM_LOCK_DESTROY(_sc) do {} while(0) +/* (_sc && !_sc) test is to silence compiler warnings about unused variables */ +#define EM_LOCK(_sc) do {} while( _sc && !_sc ) +#define EM_UNLOCK(_sc) do {} while( _sc && !_sc ) +#define EM_LOCK_ASSERT(_sc) do {} while(0) +#endif + +#endif /* _EM_H_DEFINED_ */ diff --git a/bsd_eth_drivers/if_le/Makefile.am b/bsd_eth_drivers/if_le/Makefile.am new file mode 100644 index 0000000..5ae69fd --- /dev/null +++ b/bsd_eth_drivers/if_le/Makefile.am @@ -0,0 +1,30 @@ +# $Id$ +AUTOMAKE_OPTIONS=foreign + +include $(top_srcdir)/rtems-pre.am + +libif_le_a_SOURCES = lance.c am79900.c if_le_pci.c + +libif_le_a_SOURCES += am79900reg.h am79900var.h am7990reg.h +libif_le_a_SOURCES += am7990var.h lancereg.h lancevar.h + +lib_LIBRARIES = libif_le.a + + +AM_CPPFLAGS += -I$(srcdir) +AM_CPPFLAGS += -I$(srcdir)/../libbsdport -I../libbsdport -I../libbsdport/dummyheaders +#AM_CPPFLAGS += -DLEDEBUG + +include $(top_srcdir)/rtems.am + +LINKS = +LINKS +=dev/le/lancereg.h +LINKS +=dev/le/lancevar.h +LINKS +=dev/le/am79900reg.h +LINKS +=dev/le/am79900var.h + +BUILT_SOURCES = + +$(libif_le_a_OBJECTS): $(LINKS) + +include ../links.am diff --git a/bsd_eth_drivers/if_le/am79900.c b/bsd_eth_drivers/if_le/am79900.c new file mode 100644 index 0000000..f6d8072 --- /dev/null +++ b/bsd_eth_drivers/if_le/am79900.c @@ -0,0 +1,676 @@ +/* $NetBSD: am79900.c,v 1.17 2005/12/24 20:27:29 perry Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_le.c 8.2 (Berkeley) 11/16/93 + */ + +/*- + * Copyright (c) 1998 + * Matthias Drochner. All rights reserved. + * Copyright (c) 1995 Charles M. Hannum. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_le.c 8.2 (Berkeley) 11/16/93 + */ + +#ifdef __rtems__ +#include +#endif + +#include +__FBSDID("$FreeBSD: src/sys/dev/le/am79900.c,v 1.4 2006/12/06 02:14:31 marius Exp $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef __rtems__ +#include +#endif + +static void am79900_meminit(struct lance_softc *); +static void am79900_rint(struct lance_softc *); +static void am79900_tint(struct lance_softc *); +static void am79900_start_locked(struct lance_softc *sc); + +#ifdef LEDEBUG +static void am79900_recv_print(struct lance_softc *, int); +static void am79900_xmit_print(struct lance_softc *, int); +#endif + +int +am79900_config(struct am79900_softc *sc, const char* name, int unit) +{ + int error, mem; + + sc->lsc.sc_meminit = am79900_meminit; + sc->lsc.sc_start_locked = am79900_start_locked; + + error = lance_config(&sc->lsc, name, unit); + if (error != 0) + return (error); + + mem = 0; + sc->lsc.sc_initaddr = mem; + mem += sizeof(struct leinit); + sc->lsc.sc_rmdaddr = mem; + mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf; + sc->lsc.sc_tmdaddr = mem; + mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf; + sc->lsc.sc_rbufaddr = mem; + mem += LEBLEN * sc->lsc.sc_nrbuf; + sc->lsc.sc_tbufaddr = mem; + mem += LEBLEN * sc->lsc.sc_ntbuf; + + if (mem > sc->lsc.sc_memsize) + panic("%s: memsize", __func__); + + lance_attach(&sc->lsc); + + return (0); +} + +void +am79900_detach(struct am79900_softc *sc) +{ + + lance_detach(&sc->lsc); +} + +/* + * Set up the initialization block and the descriptor rings. + */ +static void +am79900_meminit(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct leinit init; + struct lermd rmd; + struct letmd tmd; + u_long a; + int bix; + + LE_LOCK_ASSERT(sc, MA_OWNED); + + if (ifp->if_flags & IFF_PROMISC) + init.init_mode = LE_HTOLE32(LE_MODE_NORMAL | LE_MODE_PROM); + else + init.init_mode = LE_HTOLE32(LE_MODE_NORMAL); + + init.init_mode |= LE_HTOLE32(((ffs(sc->sc_ntbuf) - 1) << 28) | + ((ffs(sc->sc_nrbuf) - 1) << 20)); + + init.init_padr[0] = LE_HTOLE32(sc->sc_enaddr[0] | + (sc->sc_enaddr[1] << 8) | (sc->sc_enaddr[2] << 16) | + (sc->sc_enaddr[3] << 24)); + init.init_padr[1] = LE_HTOLE32(sc->sc_enaddr[4] | + (sc->sc_enaddr[5] << 8)); + lance_setladrf(sc, init.init_ladrf); + + sc->sc_last_rd = 0; + sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0; + + a = sc->sc_addr + LE_RMDADDR(sc, 0); + init.init_rdra = LE_HTOLE32(a); + + a = sc->sc_addr + LE_TMDADDR(sc, 0); + init.init_tdra = LE_HTOLE32(a); + + (*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init)); + + /* + * Set up receive ring descriptors. + */ + for (bix = 0; bix < sc->sc_nrbuf; bix++) { + a = sc->sc_addr + LE_RBUFADDR(sc, bix); + rmd.rmd0 = LE_HTOLE32(a); + rmd.rmd1 = LE_HTOLE32(LE_R1_OWN | LE_R1_ONES | + (-LEBLEN & 0xfff)); + rmd.rmd2 = 0; + rmd.rmd3 = 0; + (*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix), + sizeof(rmd)); + } + + /* + * Set up transmit ring descriptors. + */ + for (bix = 0; bix < sc->sc_ntbuf; bix++) { + a = sc->sc_addr + LE_TBUFADDR(sc, bix); + tmd.tmd0 = LE_HTOLE32(a); + tmd.tmd1 = LE_HTOLE32(LE_T1_ONES); + tmd.tmd2 = 0; + tmd.tmd3 = 0; + (*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix), + sizeof(tmd)); + } +} + +static inline void +am79900_rint(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct lermd rmd; + uint32_t rmd1; + int bix, rp; +#if defined(__i386__) && !defined(PC98) + struct ether_header *eh; +#endif + + bix = sc->sc_last_rd; + + /* Process all buffers with valid data. */ + for (;;) { + rp = LE_RMDADDR(sc, bix); + (*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd)); + + rmd1 = LE_LE32TOH(rmd.rmd1); + if (rmd1 & LE_R1_OWN) + break; + + m = NULL; + if ((rmd1 & (LE_R1_ERR | LE_R1_STP | LE_R1_ENP)) != + (LE_R1_STP | LE_R1_ENP)){ + if (rmd1 & LE_R1_ERR) { +#ifdef LEDEBUG + if (rmd1 & LE_R1_ENP) { + if ((rmd1 & LE_R1_OFLO) == 0) { + if (rmd1 & LE_R1_FRAM) + if_printf(ifp, + "framing error\n"); + if (rmd1 & LE_R1_CRC) + if_printf(ifp, + "crc mismatch\n"); + } + } else + if (rmd1 & LE_R1_OFLO) + if_printf(ifp, "overflow\n"); +#endif + if (rmd1 & LE_R1_BUFF) + if_printf(ifp, + "receive buffer error\n"); + } else if ((rmd1 & (LE_R1_STP | LE_R1_ENP)) != + (LE_R1_STP | LE_R1_ENP)) + if_printf(ifp, "dropping chained buffer\n"); + } else { +#ifdef LEDEBUG + if (sc->sc_flags & LE_DEBUG) + am79900_recv_print(sc, bix); +#endif + /* Pull the packet off the interface. */ + m = lance_get(sc, LE_RBUFADDR(sc, bix), + (LE_LE32TOH(rmd.rmd2) & 0xfff) - ETHER_CRC_LEN); + } + + rmd.rmd1 = LE_HTOLE32(LE_R1_OWN | LE_R1_ONES | + (-LEBLEN & 0xfff)); + rmd.rmd2 = 0; + rmd.rmd3 = 0; + (*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd)); + + if (++bix == sc->sc_nrbuf) + bix = 0; + + if (m != NULL) { + ifp->if_ipackets++; + +#if defined(__i386__) && !defined(PC98) + /* + * The VMware LANCE does not present IFF_SIMPLEX + * behavior on multicast packets. Thus drop the + * packet if it is from ourselves. + */ + eh = mtod(m, struct ether_header *); + if (!ether_cmp(eh->ether_shost, sc->sc_enaddr)) { + m_freem(m); + continue; + } +#endif + + /* Pass the packet up. */ + LE_UNLOCK(sc); +#ifndef __rtems__ + (*ifp->if_input)(ifp, m); +#else + ether_input_skipping(ifp, m); +#endif + LE_LOCK(sc); + } else + ifp->if_ierrors++; + } + + sc->sc_last_rd = bix; +} + +static inline void +am79900_tint(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct letmd tmd; + uint32_t tmd1, tmd2; + int bix; + + bix = sc->sc_first_td; + + for (;;) { + if (sc->sc_no_td <= 0) + break; + + (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix), + sizeof(tmd)); + + tmd1 = LE_LE32TOH(tmd.tmd1); + +#ifdef LEDEBUG + if (sc->sc_flags & LE_DEBUG) + if_printf(ifp, "trans tmd: " + "adr %08x, flags/blen %08x\n", + LE_LE32TOH(tmd.tmd0), tmd1); +#endif + + if (tmd1 & LE_T1_OWN) + break; + + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + if (tmd1 & LE_T1_ERR) { + tmd2 = LE_LE32TOH(tmd.tmd2); + if (tmd2 & LE_T2_BUFF) + if_printf(ifp, "transmit buffer error\n"); + else if (tmd2 & LE_T2_UFLO) + if_printf(ifp, "underflow\n"); + if (tmd2 & (LE_T2_BUFF | LE_T2_UFLO)) { + lance_init_locked(sc); + return; + } + if (tmd2 & LE_T2_LCAR) { + if (sc->sc_flags & LE_CARRIER) + if_link_state_change(ifp, + LINK_STATE_DOWN); + sc->sc_flags &= ~LE_CARRIER; + if (sc->sc_nocarrier) + (*sc->sc_nocarrier)(sc); + else + if_printf(ifp, "lost carrier\n"); + } + if (tmd2 & LE_T2_LCOL) + ifp->if_collisions++; + if (tmd2 & LE_T2_RTRY) { +#ifdef LEDEBUG + if_printf(ifp, "excessive collisions\n"); +#endif + ifp->if_collisions += 16; + } + ifp->if_oerrors++; + } else { + if (tmd1 & LE_T1_ONE) + ifp->if_collisions++; + else if (tmd1 & LE_T1_MORE) + /* Real number is unknown. */ + ifp->if_collisions += 2; + ifp->if_opackets++; + } + + if (++bix == sc->sc_ntbuf) + bix = 0; + + --sc->sc_no_td; + } + + sc->sc_first_td = bix; + + sc->sc_wdog_timer = sc->sc_no_td > 0 ? 5 : 0; +} + +/* + * Controller interrupt + */ +void +am79900_intr(void *arg) +{ + struct lance_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + uint16_t isr; + + LE_LOCK(sc); + + if (sc->sc_hwintr && (*sc->sc_hwintr)(sc) == -1) { + ifp->if_ierrors++; + lance_init_locked(sc); + LE_UNLOCK(sc); + return; + } + + isr = (*sc->sc_rdcsr)(sc, LE_CSR0); +#if defined(LEDEBUG) && LEDEBUG > 1 + if (sc->sc_flags & LE_DEBUG) + if_printf(ifp, "%s: entering with isr=%04x\n", __func__, isr); +#endif + if ((isr & LE_C0_INTR) == 0) { + LE_UNLOCK(sc); + return; + } + + /* + * Clear interrupt source flags and turn off interrupts. If we + * don't clear these flags before processing their sources we + * could completely miss some interrupt events as the NIC can + * change these flags while we're in this handler. We turn off + * interrupts so we don't get another RX interrupt while still + * processing the previous one in ifp->if_input() with the + * driver lock dropped. + */ + (*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD | + LE_C0_STOP | LE_C0_STRT | LE_C0_INIT)); + + if (isr & LE_C0_ERR) { + if (isr & LE_C0_BABL) { +#ifdef LEDEBUG + if_printf(ifp, "babble\n"); +#endif + ifp->if_oerrors++; + } +#if 0 + if (isr & LE_C0_CERR) { + if_printf(ifp, "collision error\n"); + ifp->if_collisions++; + } +#endif + if (isr & LE_C0_MISS) { +#ifdef LEDEBUG + if_printf(ifp, "missed packet\n"); +#endif + ifp->if_ierrors++; + } + if (isr & LE_C0_MERR) { + if_printf(ifp, "memory error\n"); + lance_init_locked(sc); + LE_UNLOCK(sc); + return; + } + } + + if ((isr & LE_C0_RXON) == 0) { + if_printf(ifp, "receiver disabled\n"); + ifp->if_ierrors++; + lance_init_locked(sc); + LE_UNLOCK(sc); + return; + } + if ((isr & LE_C0_TXON) == 0) { + if_printf(ifp, "transmitter disabled\n"); + ifp->if_oerrors++; + lance_init_locked(sc); + LE_UNLOCK(sc); + return; + } + + /* + * Pretend we have carrier; if we don't this will be cleared shortly. + */ + if (!(sc->sc_flags & LE_CARRIER)) + if_link_state_change(ifp, LINK_STATE_UP); + sc->sc_flags |= LE_CARRIER; + + if (isr & LE_C0_RINT) + am79900_rint(sc); + if (isr & LE_C0_TINT) + am79900_tint(sc); + + /* Enable interrupts again. */ + (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + am79900_start_locked(sc); + + LE_UNLOCK(sc); +} + +/* + * Set up output on interface. + * Get another datagram to send off of the interface queue, and map it to the + * interface before starting the output. + */ +static void +am79900_start_locked(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct letmd tmd; + struct mbuf *m; + int bix, enq, len, rp; + + LE_LOCK_ASSERT(sc, MA_OWNED); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + bix = sc->sc_last_td; + enq = 0; + + for (; sc->sc_no_td < sc->sc_ntbuf && + !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) { + rp = LE_TMDADDR(sc, bix); + (*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd)); + + if (LE_LE32TOH(tmd.tmd1) & LE_T1_OWN) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + if_printf(ifp, + "missing buffer, no_td = %d, last_td = %d\n", + sc->sc_no_td, sc->sc_last_td); + } + + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; + + /* + * If BPF is listening on this interface, let it see the packet + * before we commit it to the wire. + */ + BPF_MTAP(ifp, m); + + /* + * Copy the mbuf chain into the transmit buffer. + */ + len = lance_put(sc, LE_TBUFADDR(sc, bix), m); + +#ifdef LEDEBUG + if (len > ETHERMTU + ETHER_HDR_LEN) + if_printf(ifp, "packet length %d\n", len); +#endif + + /* + * Init transmit registers, and set transmit start flag. + */ + tmd.tmd1 = LE_HTOLE32(LE_T1_OWN | LE_T1_STP | LE_T1_ENP | + LE_T1_ONES | (-len & 0xfff)); + tmd.tmd2 = 0; + tmd.tmd3 = 0; + + (*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd)); + +#ifdef LEDEBUG + if (sc->sc_flags & LE_DEBUG) + am79900_xmit_print(sc, bix); +#endif + + (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD); + enq++; + + if (++bix == sc->sc_ntbuf) + bix = 0; + + if (++sc->sc_no_td == sc->sc_ntbuf) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + } + + sc->sc_last_td = bix; + + if (enq > 0) + sc->sc_wdog_timer = 5; +} + +#ifdef LEDEBUG +static void +am79900_recv_print(struct lance_softc *sc, int no) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ether_header eh; + struct lermd rmd; + uint16_t len; + + (*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd)); + len = LE_LE32TOH(rmd.rmd2) & 0xfff; + if_printf(ifp, "receive buffer %d, len = %d\n", no, len); + if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0)); + if_printf(ifp, "adr %08x, flags/blen %08x\n", LE_LE32TOH(rmd.rmd0), + LE_LE32TOH(rmd.rmd1)); + if (len - ETHER_CRC_LEN >= sizeof(eh)) { + (*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh)); + if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost)); + printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost), + ntohs(eh.ether_type)); + } +} + +static void +am79900_xmit_print(struct lance_softc *sc, int no) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ether_header eh; + struct letmd tmd; + uint16_t len; + + (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd)); + len = -(LE_LE32TOH(tmd.tmd1) & 0xfff); + if_printf(ifp, "transmit buffer %d, len = %d\n", no, len); + if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0)); + if_printf(ifp, "adr %08x, flags/blen %08x\n", LE_LE32TOH(tmd.tmd0), + LE_LE32TOH(tmd.tmd1)); + if (len >= sizeof(eh)) { + (*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh)); + if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost)); + printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost), + ntohs(eh.ether_type)); + } +} +#endif /* LEDEBUG */ diff --git a/bsd_eth_drivers/if_le/am79900reg.h b/bsd_eth_drivers/if_le/am79900reg.h new file mode 100644 index 0000000..1edc0ee --- /dev/null +++ b/bsd_eth_drivers/if_le/am79900reg.h @@ -0,0 +1,156 @@ +/* $NetBSD: am79900reg.h,v 1.8 2005/12/11 12:21:25 christos Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_lereg.h 8.1 (Berkeley) 6/10/93 + */ + +/* $FreeBSD: src/sys/dev/le/am79900reg.h,v 1.1 2006/01/31 14:48:58 marius Exp $ */ + +#ifndef _DEV_LE_AM79900REG_H_ +#define _DEV_LE_AM79900REG_H_ + +/* + * Receive message descriptor + */ +struct lermd { + uint32_t rmd0; + uint32_t rmd1; + uint32_t rmd2; + int32_t rmd3; +}; + +/* + * Transmit message descriptor + */ +struct letmd { + uint32_t tmd0; + uint32_t tmd1; + uint32_t tmd2; + int32_t tmd3; +}; + +/* + * Initialization block + */ +struct leinit { + uint32_t init_mode; /* +0x0000 */ + uint32_t init_padr[2]; /* +0x0002 */ + uint16_t init_ladrf[4]; /* +0x0008 */ + uint32_t init_rdra; /* +0x0010 */ + uint32_t init_tdra; /* +0x0014 */ + int32_t pad; /* Pad to 8 ints. */ +}; + +/* Receive message descriptor 1 (rmd1_bits) */ +#define LE_R1_OWN (1U << 31) /* LANCE owns the packet */ +#define LE_R1_ERR (1U << 30) /* error summary */ +#define LE_R1_FRAM (1U << 29) /* framing error */ +#define LE_R1_OFLO (1U << 28) /* overflow error */ +#define LE_R1_CRC (1U << 27) /* CRC error */ +#define LE_R1_BUFF (1U << 26) /* buffer error */ +#define LE_R1_STP (1U << 25) /* start of packet */ +#define LE_R1_ENP (1U << 24) /* end of packet */ +#define LE_R1_ONES (0xfU << 12) /* must be ones */ +#define LE_R1_BCNT_MASK (0xfff) /* byte count mask */ + +#define LE_R1_BITS \ + "\20\40OWN\37ERR\36FRAM\35OFLO\34CRC\33BUFF\32STP\31ENP" + +/* Transmit message descriptor 1 (tmd1_bits) */ +#define LE_T1_OWN (1U << 31) /* LANCE owns the packet */ +#define LE_T1_ERR (1U << 30) /* error summary */ +#define LE_T1_ADD_FCS (1U << 29) /* add FCS (PCnet-PCI) */ +#define LE_T1_NO_FCS (1U << 29) /* no FCS (ILACC) */ +#define LE_T1_MORE (1U << 28) /* multiple collisions */ +#define LE_T1_LTINT (1U << 28) /* transmit interrupt (if LTINTEN) */ +#define LE_T1_ONE (1U << 27) /* single collision */ +#define LE_T1_DEF (1U << 26) /* deferred transmit */ +#define LE_T1_STP (1U << 25) /* start of packet */ +#define LE_T1_ENP (1U << 24) /* end of packet */ +#define LE_T1_ONES (0xfU << 12) /* must be ones */ +#define LE_T1_BCNT_MASK (0xfff) /* byte count mask */ + +#define LE_T1_BITS \ + "\20\40OWN\37ERR\36RES\35MORE\34ONE\33DEF\32STP\31ENP" + +/* Transmit message descriptor 3 (tmd3) */ +#define LE_T2_BUFF (1U << 31) /* buffer error */ +#define LE_T2_UFLO (1U << 30) /* underflow error */ +#define LE_T2_EXDEF (1U << 29) /* excessive defferral */ +#define LE_T2_LCOL (1U << 28) /* late collision */ +#define LE_T2_LCAR (1U << 27) /* loss of carrier */ +#define LE_T2_RTRY (1U << 26) /* retry error */ +#if 0 +#define LE_T3_TDR_MASK 0x03ff /* time domain reflectometry counter */ +#endif + +#define LE_T3_BITS \ + "\12\40BUFF\37UFLO\35LCOL\34LCAR\33RTRY" + +#endif /* !_DEV_LE_AM7990REG_H_ */ diff --git a/bsd_eth_drivers/if_le/am79900var.h b/bsd_eth_drivers/if_le/am79900var.h new file mode 100644 index 0000000..848d290 --- /dev/null +++ b/bsd_eth_drivers/if_le/am79900var.h @@ -0,0 +1,62 @@ +/* $NetBSD: am79900var.h,v 1.4 2005/12/11 12:21:25 christos Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD: src/sys/dev/le/am79900var.h,v 1.1 2006/01/31 14:48:58 marius Exp $ */ + +#ifndef _DEV_LE_AM79900VAR_H_ +#define _DEV_LE_AM79900VAR_H_ + +/* + * Ethernet software status per device. + * + * NOTE: this structure MUST be the first element in machine-dependent + * le_softc structures! This is designed SPECIFICALLY to make it possible + * to simply cast a "void *" to "struct le_softc *" or to + * "struct am79900_softc *". Among other things, this saves a lot of hair + * in the interrupt handlers. + */ +struct am79900_softc { + struct lance_softc lsc; +}; + +int am79900_config(struct am79900_softc *, const char*, int); +void am79900_detach(struct am79900_softc *); +void am79900_intr(void *); + +#endif /* _DEV_LE_AM79900VAR_H_ */ diff --git a/bsd_eth_drivers/if_le/am7990reg.h b/bsd_eth_drivers/if_le/am7990reg.h new file mode 100644 index 0000000..2005e0f --- /dev/null +++ b/bsd_eth_drivers/if_le/am7990reg.h @@ -0,0 +1,189 @@ +/* $NetBSD: am7990reg.h,v 1.11 2005/12/11 12:21:25 christos Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_lereg.h 8.1 (Berkeley) 6/10/93 + */ + +/* $FreeBSD: src/sys/dev/le/am7990reg.h,v 1.1 2006/01/31 14:48:58 marius Exp $ */ + +#ifndef _DEV_LE_AM7990REG_H_ +#define _DEV_LE_AM7990REG_H_ + +/* + * Receive message descriptor + */ +struct lermd { + uint16_t rmd0; +#if BYTE_ORDER == BIG_ENDIAN + uint8_t rmd1_bits; + uint8_t rmd1_hadr; +#else + uint8_t rmd1_hadr; + uint8_t rmd1_bits; +#endif + int16_t rmd2; + uint16_t rmd3; +} __packed; + +/* + * Transmit message descriptor + */ +struct letmd { + uint16_t tmd0; +#if BYTE_ORDER == BIG_ENDIAN + uint8_t tmd1_bits; + uint8_t tmd1_hadr; +#else + uint8_t tmd1_hadr; + uint8_t tmd1_bits; +#endif + int16_t tmd2; + uint16_t tmd3; +} __packed; + +/* + * Initialization block + */ +struct leinit { + uint16_t init_mode; /* +0x0000 */ + uint16_t init_padr[3]; /* +0x0002 */ + uint16_t init_ladrf[4]; /* +0x0008 */ + uint16_t init_rdra; /* +0x0010 */ + uint16_t init_rlen; /* +0x0012 */ + uint16_t init_tdra; /* +0x0014 */ + uint16_t init_tlen; /* +0x0016 */ + int16_t pad0[4]; /* Pad to 16 shorts. */ +} __packed; + +/* Receive message descriptor 1 (rmd1_bits) */ +#define LE_R1_OWN 0x80 /* LANCE owns the packet */ +#define LE_R1_ERR 0x40 /* error summary */ +#define LE_R1_FRAM 0x20 /* framing error */ +#define LE_R1_OFLO 0x10 /* overflow error */ +#define LE_R1_CRC 0x08 /* CRC error */ +#define LE_R1_BUFF 0x04 /* buffer error */ +#define LE_R1_STP 0x02 /* start of packet */ +#define LE_R1_ENP 0x01 /* end of packet */ + +#define LE_R1_BITS \ + "\20\10OWN\7ERR\6FRAM\5OFLO\4CRC\3BUFF\2STP\1ENP" + +/* Transmit message descriptor 1 (tmd1_bits) */ +#define LE_T1_OWN 0x80 /* LANCE owns the packet */ +#define LE_T1_ERR 0x40 /* error summary */ +#define LE_T1_MORE 0x10 /* multiple collisions */ +#define LE_T1_ONE 0x08 /* single collision */ +#define LE_T1_DEF 0x04 /* deferred transmit */ +#define LE_T1_STP 0x02 /* start of packet */ +#define LE_T1_ENP 0x01 /* end of packet */ + +#define LE_T1_BITS \ + "\20\10OWN\7ERR\6RES\5MORE\4ONE\3DEF\2STP\1ENP" + +/* Transmit message descriptor 3 (tmd3) */ +#define LE_T3_BUFF 0x8000 /* buffer error */ +#define LE_T3_UFLO 0x4000 /* underflow error */ +#define LE_T3_LCOL 0x1000 /* late collision */ +#define LE_T3_LCAR 0x0800 /* loss of carrier */ +#define LE_T3_RTRY 0x0400 /* retry error */ +#define LE_T3_TDR_MASK 0x03ff /* time domain reflectometry counter */ + +#define LE_XMD2_ONES 0xf000 + +#define LE_T3_BITS \ + "\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY" + +/* + * PCnet-ISA defines which are not available on LANCE 7990. + */ + +/* (ISA) Bus Configuration Registers */ +#define LE_BCR_MSRDA 0x0000 +#define LE_BCR_MSWRA 0x0001 +#define LE_BCR_MC 0x0002 +#define LE_BCR_LED1 0x0005 +#define LE_BCR_LED2 0x0006 +#define LE_BCR_LED3 0x0007 + +/* Bus configurations bits (MC) */ +#define LE_MC_EADISEL 0x0008 /* EADI selection */ +#define LE_MC_AWAKE 0x0004 /* auto-wake */ +#define LE_MC_ASEL 0x0002 /* auto selection */ +#define LE_MC_XMAUSEL 0x0001 /* external MAU selection */ + +/* LED bis (LED[123]) */ +#define LE_LED_LEDOUT 0x8000 +#define LE_LED_PSE 0x0080 +#define LE_LED_XMTE 0x0010 +#define LE_LED_PVPE 0x0008 +#define LE_LED_PCVE 0x0004 +#define LE_LED_JABE 0x0002 +#define LE_LED_COLE 0x0001 + +#endif /* !_DEV_LE_AM7990REG_H_ */ diff --git a/bsd_eth_drivers/if_le/am7990var.h b/bsd_eth_drivers/if_le/am7990var.h new file mode 100644 index 0000000..0cc5397 --- /dev/null +++ b/bsd_eth_drivers/if_le/am7990var.h @@ -0,0 +1,62 @@ +/* $NetBSD: am7990var.h,v 1.23 2005/12/11 12:21:25 christos Exp $ */ + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD: src/sys/dev/le/am7990var.h,v 1.1 2006/01/31 14:48:58 marius Exp $ */ + +#ifndef _DEV_LE_AM7990VAR_H_ +#define _DEV_LE_AM7990VAR_H_ + +/* + * Ethernet software status per device. + * + * NOTE: this structure MUST be the first element in machine-dependent + * le_softc structures! This is designed SPECIFICALLY to make it possible + * to simply cast a "void *" to "struct le_softc *" or to + * "struct am7990_softc *". Among other things, this saves a lot of hair + * in the interrupt handlers. + */ +struct am7990_softc { + struct lance_softc lsc; +}; + +int am7990_config(struct am7990_softc *, const char*, int); +void am7990_detach(struct am7990_softc *); +void am7990_intr(void *); + +#endif /* !_DEV_LE_AM7990VAR_H_ */ diff --git a/bsd_eth_drivers/if_le/if_le_pci.c b/bsd_eth_drivers/if_le/if_le_pci.c new file mode 100644 index 0000000..870ddb8 --- /dev/null +++ b/bsd_eth_drivers/if_le/if_le_pci.c @@ -0,0 +1,671 @@ +/* $NetBSD: if_le_pci.c,v 1.43 2005/12/11 12:22:49 christos Exp $ */ + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_le.c 8.2 (Berkeley) 11/16/93 + */ +#ifdef __rtems__ +#include +#endif + +#include +__FBSDID("$FreeBSD: src/sys/dev/le/if_le_pci.c,v 1.7 2007/02/23 12:18:45 piso Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef __rtems__ +#include /* for memory size calculation */ +#include +#endif + +#define AMD_VENDOR 0x1022 +#define AMD_PCNET_PCI 0x2000 +#define AMD_PCNET_HOME 0x2001 +#define PCNET_MEMSIZE (32*1024) +#define PCNET_PCI_RDP 0x10 +#define PCNET_PCI_RAP 0x12 +#define PCNET_PCI_BDP 0x16 + +struct le_pci_softc { + struct am79900_softc sc_am79900; /* glue to MI code */ + + int sc_rrid; + struct resource *sc_rres; + bus_space_tag_t sc_regt; + bus_space_handle_t sc_regh; + + int sc_irid; + struct resource *sc_ires; + void *sc_ih; + + bus_dma_tag_t sc_pdmat; + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmam; +}; + +device_probe_t le_pci_probe; +device_attach_t le_pci_attach; +static device_detach_t le_pci_detach; +#ifndef __rtems__ +static device_resume_t le_pci_resume; +static device_suspend_t le_pci_suspend; + +static device_method_t le_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, le_pci_probe), + DEVMETHOD(device_attach, le_pci_attach), + DEVMETHOD(device_detach, le_pci_detach), + /* We can just use the suspend method here. */ + DEVMETHOD(device_shutdown, le_pci_suspend), + DEVMETHOD(device_suspend, le_pci_suspend), + DEVMETHOD(device_resume, le_pci_resume), + + { 0, 0 } +}; + +DEFINE_CLASS_0(le, le_pci_driver, le_pci_methods, sizeof(struct le_pci_softc)); +DRIVER_MODULE(le, pci, le_pci_driver, le_devclass, 0, 0); +MODULE_DEPEND(le, ether, 1, 1, 1); + +#else +static int +le_pci_irq_check_dis(device_t d); + +static void +le_pci_irq_en(device_t d); + +static device_method_t le_pci_methods = { + probe: le_pci_probe, + attach: le_pci_attach, + shutdown: 0, + detach: le_pci_detach, + irq_check_dis: le_pci_irq_check_dis, + irq_en: le_pci_irq_en, +}; + +driver_t libbsdport_le_pci_driver = { + "le", + &le_pci_methods, + DEV_TYPE_PCI, + sizeof(struct le_pci_softc) +}; + +#endif + +static const int le_home_supmedia[] = { + IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0, 0) +}; + +static const int le_pci_supmedia[] = { + IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FDX, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_5, 0, 0), + IFM_MAKEWORD(IFM_ETHER, IFM_10_5, IFM_FDX, 0) +}; + + +static void le_pci_wrbcr(struct lance_softc *, uint16_t, uint16_t); +static uint16_t le_pci_rdbcr(struct lance_softc *, uint16_t); +static void le_pci_wrcsr(struct lance_softc *, uint16_t, uint16_t); +static uint16_t le_pci_rdcsr(struct lance_softc *, uint16_t); +static int le_pci_mediachange(struct lance_softc *); +static void le_pci_hwreset(struct lance_softc *); +static bus_dmamap_callback_t le_pci_dma_callback; + +static void +le_pci_wrbcr(struct lance_softc *sc, uint16_t port, uint16_t val) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_BDP, val); +} + +static uint16_t +le_pci_rdbcr(struct lance_softc *sc, uint16_t port) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_BDP)); +} + +static void +le_pci_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RDP, val); +} + +static uint16_t +le_pci_rdcsr(struct lance_softc *sc, uint16_t port) +{ + struct le_pci_softc *lesc = (struct le_pci_softc *)sc; + + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, port); + bus_space_barrier(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, 2, + BUS_SPACE_BARRIER_WRITE); + return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RDP)); +} + +static int +le_pci_mediachange(struct lance_softc *sc) +{ + struct ifmedia *ifm = &sc->sc_media; + uint16_t reg; + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return (EINVAL); + + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) + le_pci_wrbcr(sc, LE_BCR49, + (le_pci_rdbcr(sc, LE_BCR49) & ~LE_B49_PHYSEL) | 0x1); + else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) + le_pci_wrbcr(sc, LE_BCR2, + le_pci_rdbcr(sc, LE_BCR2) | LE_B2_ASEL); + else { + le_pci_wrbcr(sc, LE_BCR2, + le_pci_rdbcr(sc, LE_BCR2) & ~LE_B2_ASEL); + + reg = le_pci_rdcsr(sc, LE_CSR15); + reg &= ~LE_C15_PORTSEL(LE_PORTSEL_MASK); + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) + reg |= LE_C15_PORTSEL(LE_PORTSEL_10T); + else + reg |= LE_C15_PORTSEL(LE_PORTSEL_AUI); + le_pci_wrcsr(sc, LE_CSR15, reg); + } + + reg = le_pci_rdbcr(sc, LE_BCR9); + if (IFM_OPTIONS(ifm->ifm_media) & IFM_FDX) { + reg |= LE_B9_FDEN; + /* + * Allow FDX on AUI only if explicitly chosen, + * not in autoselect mode. + */ + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_5) + reg |= LE_B9_AUIFD; + else + reg &= ~LE_B9_AUIFD; + } else + reg &= ~LE_B9_FDEN; + le_pci_wrbcr(sc, LE_BCR9, reg); + + return (0); +} + +static void +le_pci_hwreset(struct lance_softc *sc) +{ + + /* + * Chip is stopped. Set software style to PCnet-PCI (32-bit). + * Actually, am79900.c implements ILACC support (hence its + * name) but unfortunately VMware does not. As far as this + * driver is concerned that should not make a difference + * though, as the settings used have the same meaning for + * both, ILACC and PCnet-PCI (note that there would be a + * difference for the ADD_FCS/NO_FCS bit if used). + */ + le_pci_wrbcr(sc, LE_BCR20, LE_B20_SSTYLE_PCNETPCI2); +} + +static void +le_pci_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct lance_softc *sc = (struct lance_softc *)xsc; + + if (error != 0) + return; + KASSERT(nsegs == 1, ("%s: bad DMA segment count", __func__)); + sc->sc_addr = segs[0].ds_addr; +} + +int +le_pci_probe(device_t dev) +{ + + if (pci_get_vendor(dev) != AMD_VENDOR) + return (ENXIO); + + switch (pci_get_device(dev)) { + case AMD_PCNET_PCI: + device_set_desc(dev, "AMD PCnet-PCI"); + /* Let pcn(4) win. */ + return (BUS_PROBE_LOW_PRIORITY); + case AMD_PCNET_HOME: + device_set_desc(dev, "AMD PCnet-Home"); + /* Let pcn(4) win. */ + return (BUS_PROBE_LOW_PRIORITY); + default: + return (ENXIO); + } +} + +int +le_pci_attach(device_t dev) +{ + struct le_pci_softc *lesc; + struct lance_softc *sc; + int error, i; + + lesc = device_get_softc(dev); + sc = &lesc->sc_am79900.lsc; + + LE_LOCK_INIT(sc, device_get_nameunit(dev)); + + pci_enable_busmaster(dev); + pci_enable_io(dev, PCIM_CMD_PORTEN); + + lesc->sc_rrid = PCIR_BAR(0); + lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &lesc->sc_rrid, RF_ACTIVE); + if (lesc->sc_rres == NULL) { + device_printf(dev, "cannot allocate registers\n"); + error = ENXIO; + goto fail_mtx; + } + lesc->sc_regt = rman_get_bustag(lesc->sc_rres); + lesc->sc_regh = rman_get_bushandle(lesc->sc_rres); + + lesc->sc_irid = 0; + if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &lesc->sc_irid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "cannot allocate interrupt\n"); + error = ENXIO; + goto fail_rres; + } + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &lesc->sc_pdmat); + if (error != 0) { + device_printf(dev, "cannot allocate parent DMA tag\n"); + goto fail_ires; + } + + sc->sc_memsize = PCNET_MEMSIZE; + +#ifdef __rtems__ + { + int nbuf = 0; + /* Override ring sizes */ + if ( dev->ifconfig ) { + nbuf += (sc->sc_nrbuf = dev->ifconfig->rbuf_count); + nbuf += (sc->sc_ntbuf = dev->ifconfig->xbuf_count); + } + if ( nbuf ) { + /* at least one of them is set */ + if ( !sc->sc_ntbuf ) { + sc->sc_ntbuf = nbuf/4; /* default proportions as in lance.c */ + /* need at least one */ + if ( !sc->sc_ntbuf ) + sc->sc_ntbuf = 1; + } + if ( !sc->sc_nrbuf ) + sc->sc_nrbuf = nbuf*4; /* default proportions as in lance.c */ + /* It seems the driver assumes ring sizes to be powers of two; + * (look for ffs()-1). + * So we give them what they want. + */ + /* both, nrbuf / ntbuf are > 0 at this point */ + for ( nbuf = 1; nbuf < sc->sc_nrbuf; nbuf<<=1 ) + /* nothing else to do*/; + sc->sc_nrbuf = nbuf; + for ( nbuf = 1; nbuf < sc->sc_ntbuf; nbuf<<=1 ) + /* nothing else to do*/; + sc->sc_ntbuf = nbuf; + + nbuf = sc->sc_ntbuf + sc->sc_nrbuf; + + /* now override memory size; still need to override + * individual ring sizes in 'lance.c' :-( + */ + sc->sc_memsize = nbuf * (LEBLEN + sizeof(struct lermd) + sizeof(struct letmd)) ; + sc->sc_memsize+= sizeof(struct leinit); + } + } +#endif + /* + * For Am79C970A, Am79C971 and Am79C978 the init block must be 2-byte + * aligned and the ring descriptors must be 16-byte aligned when using + * a 32-bit software style. + */ + error = bus_dma_tag_create( + lesc->sc_pdmat, /* parent */ + 16, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sc->sc_memsize, /* maxsize */ + 1, /* nsegments */ + sc->sc_memsize, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &lesc->sc_dmat); + if (error != 0) { + device_printf(dev, "cannot allocate buffer DMA tag\n"); + goto fail_pdtag; + } + + error = bus_dmamem_alloc(lesc->sc_dmat, (void **)&sc->sc_mem, + BUS_DMA_WAITOK | BUS_DMA_COHERENT, &lesc->sc_dmam); + if (error != 0) { + device_printf(dev, "cannot allocate DMA buffer memory\n"); + goto fail_dtag; + } + + sc->sc_addr = 0; + error = bus_dmamap_load(lesc->sc_dmat, lesc->sc_dmam, sc->sc_mem, + sc->sc_memsize, le_pci_dma_callback, sc, 0); + if (error != 0 || sc->sc_addr == 0) { + device_printf(dev, "cannot load DMA buffer map\n"); + goto fail_dmem; + } + + sc->sc_flags = LE_BSWAP; + sc->sc_conf3 = 0; + + sc->sc_mediastatus = NULL; + switch (pci_get_device(dev)) { + case AMD_PCNET_HOME: + sc->sc_mediachange = le_pci_mediachange; + sc->sc_supmedia = le_home_supmedia; + sc->sc_nsupmedia = sizeof(le_home_supmedia) / sizeof(int); + sc->sc_defaultmedia = le_home_supmedia[0]; + break; + default: + sc->sc_mediachange = le_pci_mediachange; + sc->sc_supmedia = le_pci_supmedia; + sc->sc_nsupmedia = sizeof(le_pci_supmedia) / sizeof(int); + sc->sc_defaultmedia = le_pci_supmedia[0]; + } + + /* + * Extract the physical MAC address from the ROM. + */ + for (i = 0; i < sizeof(sc->sc_enaddr); i++) + sc->sc_enaddr[i] = + bus_space_read_1(lesc->sc_regt, lesc->sc_regh, i); + + sc->sc_copytodesc = lance_copytobuf_contig; + sc->sc_copyfromdesc = lance_copyfrombuf_contig; + sc->sc_copytobuf = lance_copytobuf_contig; + sc->sc_copyfrombuf = lance_copyfrombuf_contig; + sc->sc_zerobuf = lance_zerobuf_contig; + + sc->sc_rdcsr = le_pci_rdcsr; + sc->sc_wrcsr = le_pci_wrcsr; + sc->sc_hwreset = le_pci_hwreset; + sc->sc_hwinit = NULL; + sc->sc_hwintr = NULL; + sc->sc_nocarrier = NULL; + + error = am79900_config(&lesc->sc_am79900, device_get_name(dev), + device_get_unit(dev)); + if (error != 0) { + device_printf(dev, "cannot attach Am79900\n"); + goto fail_dmap; + } + + error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE, + NULL, am79900_intr, sc, &lesc->sc_ih); + if (error != 0) { + device_printf(dev, "cannot set up interrupt\n"); + goto fail_am79900; + } + + return (0); + + fail_am79900: + am79900_detach(&lesc->sc_am79900); + fail_dmap: + bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam); + fail_dmem: + bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam); + fail_dtag: + bus_dma_tag_destroy(lesc->sc_dmat); + fail_pdtag: + bus_dma_tag_destroy(lesc->sc_pdmat); + fail_ires: + bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); + fail_rres: + bus_release_resource(dev, SYS_RES_IOPORT, lesc->sc_rrid, lesc->sc_rres); + fail_mtx: + LE_LOCK_DESTROY(sc); + return (error); +} + +static int +le_pci_detach(device_t dev) +{ + struct le_pci_softc *lesc; + struct lance_softc *sc; + + lesc = device_get_softc(dev); + sc = &lesc->sc_am79900.lsc; + + bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih); + am79900_detach(&lesc->sc_am79900); + bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam); + bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam); + bus_dma_tag_destroy(lesc->sc_dmat); + bus_dma_tag_destroy(lesc->sc_pdmat); + bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires); + bus_release_resource(dev, SYS_RES_IOPORT, lesc->sc_rrid, lesc->sc_rres); + LE_LOCK_DESTROY(sc); + + return (0); +} + +#ifndef __rtems__ +static int +le_pci_suspend(device_t dev) +{ + struct le_pci_softc *lesc; + + lesc = device_get_softc(dev); + + lance_suspend(&lesc->sc_am79900.lsc); + + return (0); +} + +static int +le_pci_resume(device_t dev) +{ + struct le_pci_softc *lesc; + + lesc = device_get_softc(dev); + + lance_resume(&lesc->sc_am79900.lsc); + + return (0); +} +#else +static int +le_pci_irq_check_dis(device_t d) +{ +struct le_pci_softc *lesc = device_get_softc(d); +/* This can be called from IRQ context -- since all register accesses + * involve RAP we must take care to preserve it across this routine! + */ +u_int16_t rap = bus_space_read_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP); +u_int16_t csr; +int rval; + + csr = le_pci_rdcsr(&lesc->sc_am79900.lsc, LE_CSR0); + if ( LE_C0_INTR & csr ) { + /* must not write 1 to any bit as this might clear things */ + le_pci_wrcsr(&lesc->sc_am79900.lsc, LE_CSR0, LE_C0_INEA & ~(LE_C0_INEA)); + rval = FILTER_HANDLED; + } else { + rval = FILTER_STRAY; + } + /* restore RAP */ + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, rap); + return rval; +} + +static void +le_pci_irq_en(device_t d) +{ +struct le_pci_softc *lesc = device_get_softc(d); +/* This can be called from IRQ context -- since all register accesses + * involve RAP we must take care to preserve it across this routine! + */ +u_int16_t rap = bus_space_read_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP); + /* do NOT |= INTEN because writing 1es in the wrong place may clear flags */ + le_pci_wrcsr(&lesc->sc_am79900.lsc, LE_CSR0, LE_C0_INEA); + /* restore RAP */ + bus_space_write_2(lesc->sc_regt, lesc->sc_regh, PCNET_PCI_RAP, rap); +} + +u_int32_t +le_pci_read_csr(device_t d, int off) +{ +u_int32_t rval; + rtems_bsdnet_semaphore_obtain(); + rval = le_pci_rdcsr(device_get_softc(d), off); + rtems_bsdnet_semaphore_release(); + return rval; +} + +void +le_pci_write_csr(device_t d, int off, u_int16_t v) +{ + rtems_bsdnet_semaphore_obtain(); + le_pci_wrcsr(device_get_softc(d), off, v); + rtems_bsdnet_semaphore_release(); +} + + +u_int32_t +le_pci_read_bcr(device_t d, int off) +{ +u_int32_t rval; + rtems_bsdnet_semaphore_obtain(); + rval = le_pci_rdbcr(device_get_softc(d), off); + rtems_bsdnet_semaphore_release(); + return rval; +} + +void +le_pci_write_bcr(device_t d, int off, u_int16_t v) +{ + rtems_bsdnet_semaphore_obtain(); + le_pci_wrbcr(device_get_softc(d), off, v); + rtems_bsdnet_semaphore_release(); +} + + +#endif diff --git a/bsd_eth_drivers/if_le/lance.c b/bsd_eth_drivers/if_le/lance.c new file mode 100644 index 0000000..24a3713 --- /dev/null +++ b/bsd_eth_drivers/if_le/lance.c @@ -0,0 +1,873 @@ +/* $NetBSD: lance.c,v 1.34 2005/12/24 20:27:30 perry Exp $ */ + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_le.c 8.2 (Berkeley) 11/16/93 + */ + +#ifdef __rtems__ +#include +#include +#endif + +#include +__FBSDID("$FreeBSD: src/sys/dev/le/lance.c,v 1.4 2007/01/20 10:47:16 marius Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef __rtems__ +#include +#endif + +devclass_t le_devclass; + +static void lance_start(struct ifnet *); +static void lance_stop(struct lance_softc *); +static void lance_init(void *); +static void lance_watchdog(void *s); +static int lance_mediachange(struct ifnet *); +static void lance_mediastatus(struct ifnet *, struct ifmediareq *); +#ifndef __rtems__ +static int lance_ioctl(struct ifnet *, u_long, caddr_t); +#else +static int lance_ioctl(struct ifnet *, ioctl_command_t, caddr_t); +#endif + +int +lance_config(struct lance_softc *sc, const char* name, int unit) +{ + struct ifnet *ifp; + int i, nbuf; + + if (LE_LOCK_INITIALIZED(sc) == 0) + return (ENXIO); + + ifp = sc->sc_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) + return (ENOSPC); + + callout_init_mtx(&sc->sc_wdog_ch, &sc->sc_mtx, 0); + + /* Initialize ifnet structure. */ + ifp->if_softc = sc; + if_initname(ifp, name, unit); + ifp->if_start = lance_start; + ifp->if_ioctl = lance_ioctl; + ifp->if_init = lance_init; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; +#ifdef LANCE_REVC_BUG + ifp->if_flags &= ~IFF_MULTICAST; +#endif + ifp->if_baudrate = IF_Mbps(10); + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + /* Initialize ifmedia structures. */ + ifmedia_init(&sc->sc_media, 0, lance_mediachange, lance_mediastatus); + if (sc->sc_supmedia != NULL) { + for (i = 0; i < sc->sc_nsupmedia; i++) + ifmedia_add(&sc->sc_media, sc->sc_supmedia[i], 0, NULL); + ifmedia_set(&sc->sc_media, sc->sc_defaultmedia); + } else { + ifmedia_add(&sc->sc_media, + IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0), 0, NULL); + ifmedia_set(&sc->sc_media, + IFM_MAKEWORD(IFM_ETHER, IFM_MANUAL, 0, 0)); + } +#ifdef __rtems__ + assert( (sc->sc_nrbuf == 0) == (sc->sc_ntbuf == 0) ); + assert( sc->sc_memsize >= (sc->sc_nrbuf + sc->sc_ntbuf)*LEBLEN); + /* Only set ring sizes if not defined yet */ + if ( sc->sc_nrbuf == 0 ) +#endif + + switch (sc->sc_memsize) { + case 8192: + sc->sc_nrbuf = 4; + sc->sc_ntbuf = 1; + break; + case 16384: + sc->sc_nrbuf = 8; + sc->sc_ntbuf = 2; + break; + case 32768: + sc->sc_nrbuf = 16; + sc->sc_ntbuf = 4; + break; + case 65536: + sc->sc_nrbuf = 32; + sc->sc_ntbuf = 8; + break; + case 131072: + sc->sc_nrbuf = 64; + sc->sc_ntbuf = 16; + break; + case 262144: + sc->sc_nrbuf = 128; + sc->sc_ntbuf = 32; + break; + default: + /* weird memory size; cope with it */ + nbuf = sc->sc_memsize / LEBLEN; + sc->sc_ntbuf = nbuf / 5; + sc->sc_nrbuf = nbuf - sc->sc_ntbuf; + } + + if_printf(ifp, "%d receive buffers, %d transmit buffers\n", + sc->sc_nrbuf, sc->sc_ntbuf); + + /* Make sure the chip is stopped. */ + LE_LOCK(sc); + lance_stop(sc); + LE_UNLOCK(sc); + + return (0); +} + +void +lance_attach(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + /* Attach the interface. */ + ether_ifattach(ifp, sc->sc_enaddr); + +#ifndef __rtems__ + /* Claim 802.1q capability. */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + ifp->if_capabilities |= IFCAP_VLAN_MTU; + ifp->if_capenable |= IFCAP_VLAN_MTU; +#endif +} + +void +lance_detach(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + LE_LOCK(sc); + lance_stop(sc); + LE_UNLOCK(sc); + callout_drain(&sc->sc_wdog_ch); + ether_ifdetach(ifp); + if_free(ifp); +} + +void +lance_suspend(struct lance_softc *sc) +{ + + LE_LOCK(sc); + lance_stop(sc); + LE_UNLOCK(sc); +} + +void +lance_resume(struct lance_softc *sc) +{ + + LE_LOCK(sc); + if (sc->sc_ifp->if_flags & IFF_UP) + lance_init_locked(sc); + LE_UNLOCK(sc); +} + +static void +lance_start(struct ifnet *ifp) +{ + struct lance_softc *sc = ifp->if_softc; + + LE_LOCK(sc); + (*sc->sc_start_locked)(sc); + LE_UNLOCK(sc); +} + +static void +lance_stop(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + LE_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Mark the interface down and cancel the watchdog timer. + */ + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + callout_stop(&sc->sc_wdog_ch); + sc->sc_wdog_timer = 0; + + (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); +} + +static void +lance_init(void *xsc) +{ + struct lance_softc *sc = (struct lance_softc *)xsc; + + LE_LOCK(sc); + lance_init_locked(sc); + LE_UNLOCK(sc); +} + +/* + * Initialization of interface; set up initialization block + * and transmit/receive descriptor rings. + */ +void +lance_init_locked(struct lance_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + u_long a; + int timo; + + LE_LOCK_ASSERT(sc, MA_OWNED); + + (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP); + DELAY(100); + + /* Newer LANCE chips have a reset register. */ + if (sc->sc_hwreset) + (*sc->sc_hwreset)(sc); + + /* Set the correct byte swapping mode, etc. */ + (*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3); + + /* Set the current media. This may require the chip to be stopped. */ + if (sc->sc_mediachange) + (void)(*sc->sc_mediachange)(sc); + + /* + * Update our private copy of the Ethernet address. + * We NEED the copy so we can ensure its alignment! + */ + memcpy(sc->sc_enaddr, IF_LLADDR(ifp), ETHER_ADDR_LEN); + + /* Set up LANCE init block. */ + (*sc->sc_meminit)(sc); + + /* Give LANCE the physical address of its init block. */ + a = sc->sc_addr + LE_INITADDR(sc); + (*sc->sc_wrcsr)(sc, LE_CSR1, a & 0xffff); + (*sc->sc_wrcsr)(sc, LE_CSR2, a >> 16); + + /* Try to initialize the LANCE. */ + DELAY(100); + (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT); + + /* Wait for initialization to finish. */ + for (timo = 100000; timo; timo--) + if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) + break; + + if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) { + /* Start the LANCE. */ + (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT); + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + sc->sc_wdog_timer = 0; + callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc); + (*sc->sc_start_locked)(sc); + } else + if_printf(ifp, "controller failed to initialize\n"); + + if (sc->sc_hwinit) + (*sc->sc_hwinit)(sc); +} + +/* + * Routine to copy from mbuf chain to transmit buffer in + * network buffer memory. + */ +int +lance_put(struct lance_softc *sc, int boff, struct mbuf *m) +{ + struct mbuf *n; + int len, tlen = 0; + + LE_LOCK_ASSERT(sc, MA_OWNED); + + for (; m; m = n) { + len = m->m_len; + if (len == 0) { + n = m_free(m); + m = NULL; + continue; + } + (*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, len); + boff += len; + tlen += len; + n = m_free(m); + m = NULL; + } + if (tlen < LEMINSIZE) { + (*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen); + tlen = LEMINSIZE; + } + return (tlen); +} + +/* + * Pull data off an interface. + * Len is length of data, with local net header stripped. + * We copy the data into mbufs. When full cluster sized units are present + * we copy into clusters. + */ +struct mbuf * +lance_get(struct lance_softc *sc, int boff, int totlen) +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m, *m0, *newm; + caddr_t newdata; + int len; + + if (totlen <= ETHER_HDR_LEN || totlen > LEBLEN - ETHER_CRC_LEN) { +#ifdef LEDEBUG + if_printf(ifp, "invalid packet size %d; dropping\n", totlen); +#endif + return (NULL); + } + + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 == NULL) + return (NULL); + m0->m_pkthdr.rcvif = ifp; + m0->m_pkthdr.len = totlen; + len = MHLEN; + m = m0; + + while (totlen > 0) { + if (totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) + goto bad; + len = MCLBYTES; + } + + if (m == m0) { + newdata = (caddr_t) + ALIGN(m->m_data + ETHER_HDR_LEN) - ETHER_HDR_LEN; + len -= newdata - m->m_data; + m->m_data = newdata; + } + + m->m_len = len = min(totlen, len); + (*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, len); + boff += len; + + totlen -= len; + if (totlen > 0) { + MGET(newm, M_DONTWAIT, MT_DATA); + if (newm == 0) + goto bad; + len = MLEN; + m = m->m_next = newm; + } + } + + return (m0); + + bad: + m_freem(m0); + return (NULL); +} + +static void +lance_watchdog(void *xsc) +{ + struct lance_softc *sc = (struct lance_softc *)xsc; + struct ifnet *ifp = sc->sc_ifp; + + LE_LOCK_ASSERT(sc, MA_OWNED); + + if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) { + callout_reset(&sc->sc_wdog_ch, hz, lance_watchdog, sc); + return; + } + + if_printf(ifp, "device timeout\n"); + ++ifp->if_oerrors; + lance_init_locked(sc); +} + +static int +lance_mediachange(struct ifnet *ifp) +{ + struct lance_softc *sc = ifp->if_softc; + + if (sc->sc_mediachange) { + /* + * For setting the port in LE_CSR15 the PCnet chips must + * be powered down or stopped and unlike documented may + * not take effect without an initialization. So don't + * invoke (*sc_mediachange) directly here but go through + * lance_init_locked(). + */ + LE_LOCK(sc); + lance_stop(sc); + lance_init_locked(sc); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + (*sc->sc_start_locked)(sc); + LE_UNLOCK(sc); + } + return (0); +} + +static void +lance_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct lance_softc *sc = ifp->if_softc; + + LE_LOCK(sc); + if (!(ifp->if_flags & IFF_UP)) { + LE_UNLOCK(sc); + return; + } + + ifmr->ifm_status = IFM_AVALID; + if (sc->sc_flags & LE_CARRIER) + ifmr->ifm_status |= IFM_ACTIVE; + + if (sc->sc_mediastatus) + (*sc->sc_mediastatus)(sc, ifmr); + LE_UNLOCK(sc); +} + +/* + * Process an ioctl request. + */ +static int +#ifndef __rtems__ +lance_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +#else +lance_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +#endif +{ + struct lance_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int error = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + LE_LOCK(sc); + if (ifp->if_flags & IFF_PROMISC) { + if (!(sc->sc_flags & LE_PROMISC)) { + sc->sc_flags |= LE_PROMISC; + lance_init_locked(sc); + } + } else if (sc->sc_flags & LE_PROMISC) { + sc->sc_flags &= ~LE_PROMISC; + lance_init_locked(sc); + } + + if ((ifp->if_flags & IFF_ALLMULTI) && + !(sc->sc_flags & LE_ALLMULTI)) { + sc->sc_flags |= LE_ALLMULTI; + lance_init_locked(sc); + } else if (!(ifp->if_flags & IFF_ALLMULTI) && + (sc->sc_flags & LE_ALLMULTI)) { + sc->sc_flags &= ~LE_ALLMULTI; + lance_init_locked(sc); + } + + if (!(ifp->if_flags & IFF_UP) && + ifp->if_drv_flags & IFF_DRV_RUNNING) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + lance_stop(sc); + } else if (ifp->if_flags & IFF_UP && + !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + lance_init_locked(sc); + } +#ifdef LEDEBUG + if (ifp->if_flags & IFF_DEBUG) + sc->sc_flags |= LE_DEBUG; + else + sc->sc_flags &= ~LE_DEBUG; +#endif + LE_UNLOCK(sc); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: +#ifdef __rtems__ + if ( ETHER_SIOCMULTIFRAG( error, cmd, ifr, ifp ) ) + break; +#endif + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + LE_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + lance_init_locked(sc); + LE_UNLOCK(sc); + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); + break; + + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return (error); +} + +/* + * Set up the logical address filter. + */ +void +lance_setladrf(struct lance_softc *sc, uint16_t *af) +{ + struct ifnet *ifp = sc->sc_ifp; +#ifndef __rtems__ + struct ifmultiaddr *ifma; +#endif + uint32_t crc; + + /* + * Set up multicast address filter by passing all multicast addresses + * through a crc generator, and then using the high order 6 bits as an + * index into the 64 bit logical address filter. The high order bit + * selects the word, while the rest of the bits select the bit within + * the word. + */ + + if (ifp->if_flags & IFF_PROMISC || sc->sc_flags & LE_ALLMULTI) { + af[0] = af[1] = af[2] = af[3] = 0xffff; + return; + } + + af[0] = af[1] = af[2] = af[3] = 0x0000; +#ifndef __rtems__ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN); + + /* Just want the 6 most significant bits. */ + crc >>= 26; + + /* Set the corresponding bit in the filter. */ + af[crc >> 4] |= LE_HTOLE16(1 << (crc & 0xf)); + } + IF_ADDR_UNLOCK(ifp); +#else + { + /* UNTESTED */ + struct ether_multi *enm; + struct ether_multistep step; + ETHER_FIRST_MULTI(step, (struct arpcom *)ifp, enm); + while ( enm != NULL ) { + crc = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26; + + /* Just want the 6 most significant bits. */ + crc >>= 26; + + /* Set the corresponding bit in the filter. */ + af[crc >> 4] |= LE_HTOLE16(1 << (crc & 0xf)); + ETHER_NEXT_MULTI( step, enm ); + } + } +#endif +} + +/* + * Routines for accessing the transmit and receive buffers. + * The various CPU and adapter configurations supported by this + * driver require three different access methods for buffers + * and descriptors: + * (1) contig (contiguous data; no padding), + * (2) gap2 (two bytes of data followed by two bytes of padding), + * (3) gap16 (16 bytes of data followed by 16 bytes of padding). + */ + +/* + * contig: contiguous data with no padding. + * + * Buffers may have any alignment. + */ + +void +lance_copytobuf_contig(struct lance_softc *sc, void *from, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + + /* + * Just call memcpy() to do the work. + */ + memcpy(buf + boff, from, len); +} + +void +lance_copyfrombuf_contig(struct lance_softc *sc, void *to, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + + /* + * Just call memcpy() to do the work. + */ + memcpy(to, buf + boff, len); +} + +void +lance_zerobuf_contig(struct lance_softc *sc, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + + /* + * Just let memset() do the work + */ + memset(buf + boff, 0, len); +} + +#if 0 +/* + * Examples only; duplicate these and tweak (if necessary) in + * machine-specific front-ends. + */ + +/* + * gap2: two bytes of data followed by two bytes of pad. + * + * Buffers must be 4-byte aligned. The code doesn't worry about + * doing an extra byte. + */ + +static void +lance_copytobuf_gap2(struct lance_softc *sc, void *fromv, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + caddr_t from = fromv; + volatile uint16_t *bptr; + + if (boff & 0x1) { + /* Handle unaligned first byte. */ + bptr = ((volatile uint16_t *)buf) + (boff - 1); + *bptr = (*from++ << 8) | (*bptr & 0xff); + bptr += 2; + len--; + } else + bptr = ((volatile uint16_t *)buf) + boff; + while (len > 1) { + *bptr = (from[1] << 8) | (from[0] & 0xff); + bptr += 2; + from += 2; + len -= 2; + } + if (len == 1) + *bptr = (uint16_t)*from; +} + +static void +lance_copyfrombuf_gap2(struct lance_softc *sc, void *tov, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + caddr_t to = tov; + volatile uint16_t *bptr; + uint16_t tmp; + + if (boff & 0x1) { + /* Handle unaligned first byte. */ + bptr = ((volatile uint16_t *)buf) + (boff - 1); + *to++ = (*bptr >> 8) & 0xff; + bptr += 2; + len--; + } else + bptr = ((volatile uint16_t *)buf) + boff; + while (len > 1) { + tmp = *bptr; + *to++ = tmp & 0xff; + *to++ = (tmp >> 8) & 0xff; + bptr += 2; + len -= 2; + } + if (len == 1) + *to = *bptr & 0xff; +} + +static void +lance_zerobuf_gap2(struct lance_softc *sc, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + volatile uint16_t *bptr; + + if ((unsigned)boff & 0x1) { + bptr = ((volatile uint16_t *)buf) + (boff - 1); + *bptr &= 0xff; + bptr += 2; + len--; + } else + bptr = ((volatile uint16_t *)buf) + boff; + while (len > 0) { + *bptr = 0; + bptr += 2; + len -= 2; + } +} + +/* + * gap16: 16 bytes of data followed by 16 bytes of pad. + * + * Buffers must be 32-byte aligned. + */ + +static void +lance_copytobuf_gap16(struct lance_softc *sc, void *fromv, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + caddr_t bptr, from = fromv; + int xfer; + + bptr = buf + ((boff << 1) & ~0x1f); + boff &= 0xf; + xfer = min(len, 16 - boff); + while (len > 0) { + memcpy(bptr + boff, from, xfer); + from += xfer; + bptr += 32; + boff = 0; + len -= xfer; + xfer = min(len, 16); + } +} + +static void +lance_copyfrombuf_gap16(struct lance_softc *sc, void *tov, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + caddr_t bptr, to = tov; + int xfer; + + bptr = buf + ((boff << 1) & ~0x1f); + boff &= 0xf; + xfer = min(len, 16 - boff); + while (len > 0) { + memcpy(to, bptr + boff, xfer); + to += xfer; + bptr += 32; + boff = 0; + len -= xfer; + xfer = min(len, 16); + } +} + +static void +lance_zerobuf_gap16(struct lance_softc *sc, int boff, int len) +{ + volatile caddr_t buf = sc->sc_mem; + caddr_t bptr; + int xfer; + + bptr = buf + ((boff << 1) & ~0x1f); + boff &= 0xf; + xfer = min(len, 16 - boff); + while (len > 0) { + memset(bptr + boff, 0, xfer); + bptr += 32; + boff = 0; + len -= xfer; + xfer = min(len, 16); + } +} +#endif /* Example only */ diff --git a/bsd_eth_drivers/if_le/lancereg.h b/bsd_eth_drivers/if_le/lancereg.h new file mode 100644 index 0000000..cbb129e --- /dev/null +++ b/bsd_eth_drivers/if_le/lancereg.h @@ -0,0 +1,619 @@ +/* $NetBSD: lancereg.h,v 1.12 2005/12/11 12:21:27 christos Exp $ */ + +/*- + * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_lereg.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Register description for the following Advanced Micro Devices + * Ethernet chips: + * + * - Am7990 Local Area Network Controller for Ethernet (LANCE) + * (and its descendent Am79c90 C-LANCE). + * + * - Am79c900 Integrated Local Area Communications Controller (ILACC) + * + * - Am79c960 PCnet-ISA Single-Chip Ethernet Controller for ISA + * + * - Am79c961 PCnet-ISA+ Jumperless Single-Chip Ethernet Controller + * for ISA + * + * - Am79c961A PCnet-ISA II Jumperless Full-Duplex Single-Chip + * Ethernet Controller for ISA + * + * - Am79c965A PCnet-32 Single-Chip 32-bit Ethernet Controller + * (for VESA and 486 local busses) + * + * - Am79c970 PCnet-PCI Single-Chip Ethernet Controller for PCI + * Local Bus + * + * - Am79c970A PCnet-PCI II Single-Chip Full-Duplex Ethernet Controller + * for PCI Local Bus + * + * - Am79c971 PCnet-FAST Single-Chip Full-Duplex 10/100Mbps + * Ethernet Controller for PCI Local Bus + * + * - Am79c972 PCnet-FAST+ Enhanced 10/100Mbps PCI Ethernet Controller + * with OnNow Support + * + * - Am79c973/Am79c975 PCnet-FAST III Single-Chip 10/100Mbps PCI + * Ethernet Controller with Integrated PHY + * + * - Am79c978 PCnet-Home Single-Chip 1/10 Mbps PCI Home + * Networking Controller. + * + * Initialization block, transmit descriptor, and receive descriptor + * formats are described in two separate files: + * + * 16-bit software model (LANCE) am7990reg.h + * + * 32-bit software model (ILACC) am79900reg.h + * + * Note that the vast majority of the registers described in this file + * belong to follow-on chips to the original LANCE. Only CSR0-CSR3 are + * valid on the LANCE. + */ + +/* $FreeBSD: src/sys/dev/le/lancereg.h,v 1.2 2006/05/16 21:04:01 marius Exp $ */ + +#ifndef _DEV_LE_LANCEREG_H_ +#define _DEV_LE_LANCEREG_H_ + +#define LEBLEN (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) +/* LEMINSIZE should be ETHER_MIN_LEN when LE_MODE_DTCR is set. */ +#define LEMINSIZE (ETHER_MIN_LEN - ETHER_CRC_LEN) + +#define LE_INITADDR(sc) (sc->sc_initaddr) +#define LE_RMDADDR(sc, bix) (sc->sc_rmdaddr + sizeof(struct lermd) * (bix)) +#define LE_TMDADDR(sc, bix) (sc->sc_tmdaddr + sizeof(struct letmd) * (bix)) +#define LE_RBUFADDR(sc, bix) (sc->sc_rbufaddr + LEBLEN * (bix)) +#define LE_TBUFADDR(sc, bix) (sc->sc_tbufaddr + LEBLEN * (bix)) + +/* + * The byte count fields in descriptors are in two's complement. + * This macro does the conversion for us on unsigned numbers. + */ +#define LE_BCNT(x) (~(x) + 1) + +/* + * Control and Status Register addresses + */ +#define LE_CSR0 0x0000 /* Control and status register */ +#define LE_CSR1 0x0001 /* low address of init block */ +#define LE_CSR2 0x0002 /* high address of init block */ +#define LE_CSR3 0x0003 /* Bus master and control */ +#define LE_CSR4 0x0004 /* Test and features control */ +#define LE_CSR5 0x0005 /* Extended control and Interrupt 1 */ +#define LE_CSR6 0x0006 /* Rx/Tx Descriptor table length */ +#define LE_CSR7 0x0007 /* Extended control and interrupt 2 */ +#define LE_CSR8 0x0008 /* Logical Address Filter 0 */ +#define LE_CSR9 0x0009 /* Logical Address Filter 1 */ +#define LE_CSR10 0x000a /* Logical Address Filter 2 */ +#define LE_CSR11 0x000b /* Logical Address Filter 3 */ +#define LE_CSR12 0x000c /* Physical Address 0 */ +#define LE_CSR13 0x000d /* Physical Address 1 */ +#define LE_CSR14 0x000e /* Physical Address 2 */ +#define LE_CSR15 0x000f /* Mode */ +#define LE_CSR16 0x0010 /* Initialization Block addr lower */ +#define LE_CSR17 0x0011 /* Initialization Block addr upper */ +#define LE_CSR18 0x0012 /* Current Rx Buffer addr lower */ +#define LE_CSR19 0x0013 /* Current Rx Buffer addr upper */ +#define LE_CSR20 0x0014 /* Current Tx Buffer addr lower */ +#define LE_CSR21 0x0015 /* Current Tx Buffer addr upper */ +#define LE_CSR22 0x0016 /* Next Rx Buffer addr lower */ +#define LE_CSR23 0x0017 /* Next Rx Buffer addr upper */ +#define LE_CSR24 0x0018 /* Base addr of Rx ring lower */ +#define LE_CSR25 0x0019 /* Base addr of Rx ring upper */ +#define LE_CSR26 0x001a /* Next Rx Desc addr lower */ +#define LE_CSR27 0x001b /* Next Rx Desc addr upper */ +#define LE_CSR28 0x001c /* Current Rx Desc addr lower */ +#define LE_CSR29 0x001d /* Current Rx Desc addr upper */ +#define LE_CSR30 0x001e /* Base addr of Tx ring lower */ +#define LE_CSR31 0x001f /* Base addr of Tx ring upper */ +#define LE_CSR32 0x0020 /* Next Tx Desc addr lower */ +#define LE_CSR33 0x0021 /* Next Tx Desc addr upper */ +#define LE_CSR34 0x0022 /* Current Tx Desc addr lower */ +#define LE_CSR35 0x0023 /* Current Tx Desc addr upper */ +#define LE_CSR36 0x0024 /* Next Next Rx Desc addr lower */ +#define LE_CSR37 0x0025 /* Next Next Rx Desc addr upper */ +#define LE_CSR38 0x0026 /* Next Next Tx Desc addr lower */ +#define LE_CSR39 0x0027 /* Next Next Tx Desc adddr upper */ +#define LE_CSR40 0x0028 /* Current Rx Byte Count */ +#define LE_CSR41 0x0029 /* Current Rx Status */ +#define LE_CSR42 0x002a /* Current Tx Byte Count */ +#define LE_CSR43 0x002b /* Current Tx Status */ +#define LE_CSR44 0x002c /* Next Rx Byte Count */ +#define LE_CSR45 0x002d /* Next Rx Status */ +#define LE_CSR46 0x002e /* Tx Poll Time Counter */ +#define LE_CSR47 0x002f /* Tx Polling Interval */ +#define LE_CSR48 0x0030 /* Rx Poll Time Counter */ +#define LE_CSR49 0x0031 /* Rx Polling Interval */ +#define LE_CSR58 0x003a /* Software Style */ +#define LE_CSR60 0x003c /* Previous Tx Desc addr lower */ +#define LE_CSR61 0x003d /* Previous Tx Desc addr upper */ +#define LE_CSR62 0x003e /* Previous Tx Byte Count */ +#define LE_CSR63 0x003f /* Previous Tx Status */ +#define LE_CSR64 0x0040 /* Next Tx Buffer addr lower */ +#define LE_CSR65 0x0041 /* Next Tx Buffer addr upper */ +#define LE_CSR66 0x0042 /* Next Tx Byte Count */ +#define LE_CSR67 0x0043 /* Next Tx Status */ +#define LE_CSR72 0x0048 /* Receive Ring Counter */ +#define LE_CSR74 0x004a /* Transmit Ring Counter */ +#define LE_CSR76 0x004c /* Receive Ring Length */ +#define LE_CSR78 0x004e /* Transmit Ring Length */ +#define LE_CSR80 0x0050 /* DMA Transfer Counter and FIFO + Threshold Control */ +#define LE_CSR82 0x0052 /* Tx Desc addr Pointer lower */ +#define LE_CSR84 0x0054 /* DMA addr register lower */ +#define LE_CSR85 0x0055 /* DMA addr register upper */ +#define LE_CSR86 0x0056 /* Buffer Byte Counter */ +#define LE_CSR88 0x0058 /* Chip ID Register lower */ +#define LE_CSR89 0x0059 /* Chip ID Register upper */ +#define LE_CSR92 0x005c /* Ring Length Conversion */ +#define LE_CSR100 0x0064 /* Bus Timeout */ +#define LE_CSR112 0x0070 /* Missed Frame Count */ +#define LE_CSR114 0x0072 /* Receive Collision Count */ +#define LE_CSR116 0x0074 /* OnNow Power Mode Register */ +#define LE_CSR122 0x007a /* Advanced Feature Control */ +#define LE_CSR124 0x007c /* Test Register 1 */ +#define LE_CSR125 0x007d /* MAC Enhanced Configuration Control */ + +/* + * Bus Configuration Register addresses + */ +#define LE_BCR0 0x0000 /* Master Mode Read Active */ +#define LE_BCR1 0x0001 /* Master Mode Write Active */ +#define LE_BCR2 0x0002 /* Misc. Configuration */ +#define LE_BCR4 0x0004 /* LED0 Status */ +#define LE_BCR5 0x0005 /* LED1 Status */ +#define LE_BCR6 0x0006 /* LED2 Status */ +#define LE_BCR7 0x0007 /* LED3 Status */ +#define LE_BCR9 0x0009 /* Full-duplex Control */ +#define LE_BCR16 0x0010 /* I/O Base Address lower */ +#define LE_BCR17 0x0011 /* I/O Base Address upper */ +#define LE_BCR18 0x0012 /* Burst and Bus Control Register */ +#define LE_BCR19 0x0013 /* EEPROM Control and Status */ +#define LE_BCR20 0x0014 /* Software Style */ +#define LE_BCR22 0x0016 /* PCI Latency Register */ +#define LE_BCR23 0x0017 /* PCI Subsystem Vendor ID */ +#define LE_BCR24 0x0018 /* PCI Subsystem ID */ +#define LE_BCR25 0x0019 /* SRAM Size Register */ +#define LE_BCR26 0x001a /* SRAM Boundary Register */ +#define LE_BCR27 0x001b /* SRAM Interface Control Register */ +#define LE_BCR28 0x001c /* Exp. Bus Port Addr lower */ +#define LE_BCR29 0x001d /* Exp. Bus Port Addr upper */ +#define LE_BCR30 0x001e /* Exp. Bus Data Port */ +#define LE_BCR31 0x001f /* Software Timer Register */ +#define LE_BCR32 0x0020 /* PHY Control and Status Register */ +#define LE_BCR33 0x0021 /* PHY Address Register */ +#define LE_BCR34 0x0022 /* PHY Management Data Register */ +#define LE_BCR35 0x0023 /* PCI Vendor ID Register */ +#define LE_BCR36 0x0024 /* PCI Power Management Cap. Alias */ +#define LE_BCR37 0x0025 /* PCI DATA0 Alias */ +#define LE_BCR38 0x0026 /* PCI DATA1 Alias */ +#define LE_BCR39 0x0027 /* PCI DATA2 Alias */ +#define LE_BCR40 0x0028 /* PCI DATA3 Alias */ +#define LE_BCR41 0x0029 /* PCI DATA4 Alias */ +#define LE_BCR42 0x002a /* PCI DATA5 Alias */ +#define LE_BCR43 0x002b /* PCI DATA6 Alias */ +#define LE_BCR44 0x002c /* PCI DATA7 Alias */ +#define LE_BCR45 0x002d /* OnNow Pattern Matching 1 */ +#define LE_BCR46 0x002e /* OnNow Pattern Matching 2 */ +#define LE_BCR47 0x002f /* OnNow Pattern Matching 3 */ +#define LE_BCR48 0x0030 /* LED4 Status */ +#define LE_BCR49 0x0031 /* PHY Select */ + +/* Control and status register 0 (csr0) */ +#define LE_C0_ERR 0x8000 /* error summary */ +#define LE_C0_BABL 0x4000 /* transmitter timeout error */ +#define LE_C0_CERR 0x2000 /* collision */ +#define LE_C0_MISS 0x1000 /* missed a packet */ +#define LE_C0_MERR 0x0800 /* memory error */ +#define LE_C0_RINT 0x0400 /* receiver interrupt */ +#define LE_C0_TINT 0x0200 /* transmitter interrupt */ +#define LE_C0_IDON 0x0100 /* initialization done */ +#define LE_C0_INTR 0x0080 /* interrupt condition */ +#define LE_C0_INEA 0x0040 /* interrupt enable */ +#define LE_C0_RXON 0x0020 /* receiver on */ +#define LE_C0_TXON 0x0010 /* transmitter on */ +#define LE_C0_TDMD 0x0008 /* transmit demand */ +#define LE_C0_STOP 0x0004 /* disable all external activity */ +#define LE_C0_STRT 0x0002 /* enable external activity */ +#define LE_C0_INIT 0x0001 /* begin initialization */ + +#define LE_C0_BITS \ + "\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\ +\12TINT\11IDON\10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT" + +/* Control and status register 3 (csr3) */ +#define LE_C3_BABLM 0x4000 /* babble mask */ +#define LE_C3_MISSM 0x1000 /* missed frame mask */ +#define LE_C3_MERRM 0x0800 /* memory error mask */ +#define LE_C3_RINTM 0x0400 /* receive interrupt mask */ +#define LE_C3_TINTM 0x0200 /* transmit interrupt mask */ +#define LE_C3_IDONM 0x0100 /* initialization done mask */ +#define LE_C3_DXSUFLO 0x0040 /* disable tx stop on underflow */ +#define LE_C3_LAPPEN 0x0020 /* look ahead packet processing enbl */ +#define LE_C3_DXMT2PD 0x0010 /* disable tx two part deferral */ +#define LE_C3_EMBA 0x0008 /* enable modified backoff algorithm */ +#define LE_C3_BSWP 0x0004 /* byte swap */ +#define LE_C3_ACON 0x0002 /* ALE control, eh? */ +#define LE_C3_BCON 0x0001 /* byte control */ + +/* Control and status register 4 (csr4) */ +#define LE_C4_EN124 0x8000 /* enable CSR124 */ +#define LE_C4_DMAPLUS 0x4000 /* always set (PCnet-PCI) */ +#define LE_C4_TIMER 0x2000 /* enable bus activity timer */ +#define LE_C4_TXDPOLL 0x1000 /* disable transmit polling */ +#define LE_C4_APAD_XMT 0x0800 /* auto pad transmit */ +#define LE_C4_ASTRP_RCV 0x0400 /* auto strip receive */ +#define LE_C4_MFCO 0x0200 /* missed frame counter overflow */ +#define LE_C4_MFCOM 0x0100 /* missed frame coutner overflow mask */ +#define LE_C4_UINTCMD 0x0080 /* user interrupt command */ +#define LE_C4_UINT 0x0040 /* user interrupt */ +#define LE_C4_RCVCCO 0x0020 /* receive collision counter overflow */ +#define LE_C4_RCVCCOM 0x0010 /* receive collision counter overflow + mask */ +#define LE_C4_TXSTRT 0x0008 /* transmit start status */ +#define LE_C4_TXSTRTM 0x0004 /* transmit start mask */ + +/* Control and status register 5 (csr5) */ +#define LE_C5_TOKINTD 0x8000 /* transmit ok interrupt disable */ +#define LE_C5_LTINTEN 0x4000 /* last transmit interrupt enable */ +#define LE_C5_SINT 0x0800 /* system interrupt */ +#define LE_C5_SINTE 0x0400 /* system interrupt enable */ +#define LE_C5_EXDINT 0x0080 /* excessive deferral interrupt */ +#define LE_C5_EXDINTE 0x0040 /* excessive deferral interrupt enbl */ +#define LE_C5_MPPLBA 0x0020 /* magic packet physical logical + broadcast accept */ +#define LE_C5_MPINT 0x0010 /* magic packet interrupt */ +#define LE_C5_MPINTE 0x0008 /* magic packet interrupt enable */ +#define LE_C5_MPEN 0x0004 /* magic packet enable */ +#define LE_C5_MPMODE 0x0002 /* magic packet mode */ +#define LE_C5_SPND 0x0001 /* suspend */ + +/* Control and status register 6 (csr6) */ +#define LE_C6_TLEN 0xf000 /* TLEN from init block */ +#define LE_C6_RLEN 0x0f00 /* RLEN from init block */ + +/* Control and status register 7 (csr7) */ +#define LE_C7_FASTSPNDE 0x8000 /* fast suspend enable */ +#define LE_C7_RDMD 0x2000 /* receive demand */ +#define LE_C7_RDXPOLL 0x1000 /* receive disable polling */ +#define LE_C7_STINT 0x0800 /* software timer interrupt */ +#define LE_C7_STINTE 0x0400 /* software timer interrupt enable */ +#define LE_C7_MREINT 0x0200 /* PHY management read error intr */ +#define LE_C7_MREINTE 0x0100 /* PHY management read error intr + enable */ +#define LE_C7_MAPINT 0x0080 /* PHY management auto-poll intr */ +#define LE_C7_MAPINTE 0x0040 /* PHY management auto-poll intr + enable */ +#define LE_C7_MCCINT 0x0020 /* PHY management command complete + interrupt */ +#define LE_C7_MCCINTE 0x0010 /* PHY management command complete + interrupt enable */ +#define LE_C7_MCCIINT 0x0008 /* PHY management command complete + internal interrupt */ +#define LE_C7_MCCIINTE 0x0004 /* PHY management command complete + internal interrupt enable */ +#define LE_C7_MIIPDTINT 0x0002 /* PHY management detect transition + interrupt */ +#define LE_C7_MIIPDTINTE 0x0001 /* PHY management detect transition + interrupt enable */ + +/* Control and status register 15 (csr15) */ +#define LE_C15_PROM 0x8000 /* promiscuous mode */ +#define LE_C15_DRCVBC 0x4000 /* disable Rx of broadcast */ +#define LE_C15_DRCVPA 0x2000 /* disable Rx of physical address */ +#define LE_C15_DLNKTST 0x1000 /* disable link status */ +#define LE_C15_DAPC 0x0800 /* disable auto-polarity correction */ +#define LE_C15_MENDECL 0x0400 /* MENDEC Loopback mode */ +#define LE_C15_LRT 0x0200 /* low receive threshold (TMAU) */ +#define LE_C15_TSEL 0x0200 /* transmit mode select (AUI) */ +#define LE_C15_PORTSEL(x) ((x) << 7) /* port select */ +#define LE_C15_INTL 0x0040 /* internal loopback */ +#define LE_C15_DRTY 0x0020 /* disable retry */ +#define LE_C15_FCOLL 0x0010 /* force collision */ +#define LE_C15_DXMTFCS 0x0008 /* disable Tx FCS (ADD_FCS overrides) */ +#define LE_C15_LOOP 0x0004 /* loopback enable */ +#define LE_C15_DTX 0x0002 /* disable transmit */ +#define LE_C15_DRX 0x0001 /* disable receiver */ + +#define LE_PORTSEL_AUI 0 +#define LE_PORTSEL_10T 1 +#define LE_PORTSEL_GPSI 2 +#define LE_PORTSEL_MII 3 +#define LE_PORTSEL_MASK 3 + +/* control and status register 80 (csr80) */ +#define LE_C80_RCVFW(x) ((x) << 12) /* Receive FIFO Watermark */ +#define LE_C80_RCVFW_MAX 3 +#define LE_C80_XMTSP(x) ((x) << 10) /* Transmit Start Point */ +#define LE_C80_XMTSP_MAX 3 +#define LE_C80_XMTFW(x) ((x) << 8) /* Transmit FIFO Watermark */ +#define LE_C80_XMTFW_MAX 3 +#define LE_C80_DMATC 0x00ff /* DMA transfer counter */ + +/* control and status register 116 (csr116) */ +#define LE_C116_PME_EN_OVR 0x0400 /* PME_EN overwrite */ +#define LE_C116_LCDET 0x0200 /* link change detected */ +#define LE_C116_LCMODE 0x0100 /* link change wakeup mode */ +#define LE_C116_PMAT 0x0080 /* pattern matched */ +#define LE_C116_EMPPLBA 0x0040 /* magic packet physical logical + broadcast accept */ +#define LE_C116_MPMAT 0x0020 /* magic packet match */ +#define LE_C116_MPPEN 0x0010 /* magic packet pin enable */ +#define LE_C116_RST_POL 0x0001 /* PHY_RST pin polarity */ + +/* control and status register 122 (csr122) */ +#define LE_C122_RCVALGN 0x0001 /* receive packet align */ + +/* control and status register 124 (csr124) */ +#define LE_C124_RPA 0x0008 /* runt packet accept */ + +/* control and status register 125 (csr125) */ +#define LE_C125_IPG 0xff00 /* inter-packet gap */ +#define LE_C125_IFS1 0x00ff /* inter-frame spacing part 1 */ + +/* bus configuration register 0 (bcr0) */ +#define LE_B0_MSRDA 0xffff /* reserved locations */ + +/* bus configuration register 1 (bcr1) */ +#define LE_B1_MSWRA 0xffff /* reserved locations */ + +/* bus configuration register 2 (bcr2) */ +#define LE_B2_PHYSSELEN 0x2000 /* enable writes to BCR18[4:3] */ +#define LE_B2_LEDPE 0x1000 /* LED program enable */ +#define LE_B2_APROMWE 0x0100 /* Address PROM Write Enable */ +#define LE_B2_INTLEVEL 0x0080 /* 1 == edge triggered */ +#define LE_B2_DXCVRCTL 0x0020 /* DXCVR control */ +#define LE_B2_DXCVRPOL 0x0010 /* DXCVR polarity */ +#define LE_B2_EADISEL 0x0008 /* EADI select */ +#define LE_B2_AWAKE 0x0004 /* power saving mode select */ +#define LE_B2_ASEL 0x0002 /* auto-select PORTSEL */ +#define LE_B2_XMAUSEL 0x0001 /* reserved location */ + +/* bus configuration register 4 (bcr4) */ +/* bus configuration register 5 (bcr5) */ +/* bus configuration register 6 (bcr6) */ +/* bus configuration register 7 (bcr7) */ +/* bus configuration register 48 (bcr48) */ +#define LE_B4_LEDOUT 0x8000 /* LED output active */ +#define LE_B4_LEDPOL 0x4000 /* LED polarity */ +#define LE_B4_LEDDIS 0x2000 /* LED disable */ +#define LE_B4_100E 0x1000 /* 100Mb/s enable */ +#define LE_B4_MPSE 0x0200 /* magic packet status enable */ +#define LE_B4_FDLSE 0x0100 /* full-duplex link status enable */ +#define LE_B4_PSE 0x0080 /* pulse stretcher enable */ +#define LE_B4_LNKSE 0x0040 /* link status enable */ +#define LE_B4_RCVME 0x0020 /* receive match status enable */ +#define LE_B4_XMTE 0x0010 /* transmit status enable */ +#define LE_B4_POWER 0x0008 /* power enable */ +#define LE_B4_RCVE 0x0004 /* receive status enable */ +#define LE_B4_SPEED 0x0002 /* high speed enable */ +#define LE_B4_COLE 0x0001 /* collision status enable */ + +/* bus configuration register 9 (bcr9) */ +#define LE_B9_FDRPAD 0x0004 /* full-duplex runt packet accept + disable */ +#define LE_B9_AUIFD 0x0002 /* AUI full-duplex */ +#define LE_B9_FDEN 0x0001 /* full-duplex enable */ + +/* bus configuration register 18 (bcr18) */ +#define LE_B18_ROMTMG 0xf000 /* expansion rom timing */ +#define LE_B18_NOUFLO 0x0800 /* no underflow on transmit */ +#define LE_B18_MEMCMD 0x0200 /* memory read multiple enable */ +#define LE_B18_EXTREQ 0x0100 /* extended request */ +#define LE_B18_DWIO 0x0080 /* double-word I/O */ +#define LE_B18_BREADE 0x0040 /* burst read enable */ +#define LE_B18_BWRITE 0x0020 /* burst write enable */ +#define LE_B18_PHYSEL1 0x0010 /* PHYSEL 1 */ +#define LE_B18_PHYSEL0 0x0008 /* PHYSEL 0 */ + /* 00 ex ROM/Flash */ + /* 01 EADI/MII snoop */ + /* 10 reserved */ + /* 11 reserved */ +#define LE_B18_LINBC 0x0007 /* reserved locations */ + +/* bus configuration register 19 (bcr19) */ +#define LE_B19_PVALID 0x8000 /* EEPROM status valid */ +#define LE_B19_PREAD 0x4000 /* EEPROM read command */ +#define LE_B19_EEDET 0x2000 /* EEPROM detect */ +#define LE_B19_EEN 0x0010 /* EEPROM port enable */ +#define LE_B19_ECS 0x0004 /* EEPROM chip select */ +#define LE_B19_ESK 0x0002 /* EEPROM serial clock */ +#define LE_B19_EDI 0x0001 /* EEPROM data in */ +#define LE_B19_EDO 0x0001 /* EEPROM data out */ + +/* bus configuration register 20 (bcr20) */ +#define LE_B20_APERREN 0x0400 /* Advanced parity error handling */ +#define LE_B20_CSRPCNET 0x0200 /* PCnet-style CSRs (0 = ILACC) */ +#define LE_B20_SSIZE32 0x0100 /* Software Size 32-bit */ +#define LE_B20_SSTYLE 0x0007 /* Software Style */ +#define LE_B20_SSTYLE_LANCE 0 /* LANCE/PCnet-ISA (16-bit) */ +#define LE_B20_SSTYLE_ILACC 1 /* ILACC (32-bit) */ +#define LE_B20_SSTYLE_PCNETPCI2 2 /* PCnet-PCI (32-bit) */ +#define LE_B20_SSTYLE_PCNETPCI3 3 /* PCnet-PCI II (32-bit) */ + +/* bus configuration register 25 (bcr25) */ +#define LE_B25_SRAM_SIZE 0x00ff /* SRAM size */ + +/* bus configuration register 26 (bcr26) */ +#define LE_B26_SRAM_BND 0x00ff /* SRAM boundary */ + +/* bus configuration register 27 (bcr27) */ +#define LE_B27_PTRTST 0x8000 /* reserved for manuf. tests */ +#define LE_B27_LOLATRX 0x4000 /* low latency receive */ +#define LE_B27_EBCS 0x0038 /* expansion bus clock source */ + /* 000 CLK pin */ + /* 001 time base clock */ + /* 010 EBCLK pin */ + /* 011 reserved */ + /* 1xx reserved */ +#define LE_B27_CLK_FAC 0x0007 /* clock factor */ + /* 000 1 */ + /* 001 1/2 */ + /* 010 reserved */ + /* 011 1/4 */ + /* 1xx reserved */ + +/* bus configuration register 28 (bcr28) */ +#define LE_B28_EADDRL 0xffff /* expansion port address lower */ + +/* bus configuration register 29 (bcr29) */ +#define LE_B29_FLASH 0x8000 /* flash access */ +#define LE_B29_LAAINC 0x4000 /* lower address auto increment */ +#define LE_B29_EPADDRU 0x0007 /* expansion port address upper */ + +/* bus configuration register 30 (bcr30) */ +#define LE_B30_EBDATA 0xffff /* expansion bus data port */ + +/* bus configuration register 31 (bcr31) */ +#define LE_B31_STVAL 0xffff /* software timer value */ + +/* bus configuration register 32 (bcr32) */ +#define LE_B32_ANTST 0x8000 /* reserved for manuf. tests */ +#define LE_B32_MIIPD 0x4000 /* MII PHY Detect (manuf. tests) */ +#define LE_B32_FMDC 0x3000 /* fast management data clock */ +#define LE_B32_APEP 0x0800 /* auto-poll PHY */ +#define LE_B32_APDW 0x0700 /* auto-poll dwell time */ +#define LE_B32_DANAS 0x0080 /* disable autonegotiation */ +#define LE_B32_XPHYRST 0x0040 /* PHY reset */ +#define LE_B32_XPHYANE 0x0020 /* PHY autonegotiation enable */ +#define LE_B32_XPHYFD 0x0010 /* PHY full-duplex */ +#define LE_B32_XPHYSP 0x0008 /* PHY speed */ +#define LE_B32_MIIILP 0x0002 /* MII internal loopback */ + +/* bus configuration register 33 (bcr33) */ +#define LE_B33_SHADOW 0x8000 /* shadow enable */ +#define LE_B33_MII_SEL 0x4000 /* MII selected */ +#define LE_B33_ACOMP 0x2000 /* internal PHY autonegotiation comp */ +#define LE_B33_LINK 0x1000 /* link status */ +#define LE_B33_FDX 0x0800 /* full-duplex */ +#define LE_B33_SPEED 0x0400 /* 1 == high speed */ +#define LE_B33_PHYAD 0x03e0 /* PHY address */ +#define PHYAD_SHIFT 5 +#define LE_B33_REGAD 0x001f /* register address */ + +/* bus configuration register 34 (bcr34) */ +#define LE_B34_MIIMD 0xffff /* MII data */ + +/* bus configuration register 49 (bcr49) */ +#define LE_B49_PCNET 0x8000 /* PCnet mode - Must Be One */ +#define LE_B49_PHYSEL_D 0x0300 /* PHY_SEL_Default */ +#define LE_B49_PHYSEL_L 0x0010 /* PHY_SEL_Lock */ +#define LE_B49_PHYSEL 0x0003 /* PHYSEL */ + /* 00 10baseT PHY */ + /* 01 HomePNA PHY */ + /* 10 external PHY */ + /* 11 reserved */ + +/* Initialization block (mode) */ +#define LE_MODE_PROM 0x8000 /* promiscuous mode */ +/* 0x7f80 reserved, must be zero */ +/* 0x4000 - 0x0080 are not available on LANCE 7990. */ +#define LE_MODE_DRCVBC 0x4000 /* disable receive brodcast */ +#define LE_MODE_DRCVPA 0x2000 /* disable physical address detection */ +#define LE_MODE_DLNKTST 0x1000 /* disable link status */ +#define LE_MODE_DAPC 0x0800 /* disable automatic polarity correction */ +#define LE_MODE_MENDECL 0x0400 /* MENDEC loopback mode */ +#define LE_MODE_LRTTSEL 0x0200 /* lower receive threshold / + transmit mode selection */ +#define LE_MODE_PSEL1 0x0100 /* port selection bit1 */ +#define LE_MODE_PSEL0 0x0080 /* port selection bit0 */ +#define LE_MODE_INTL 0x0040 /* internal loopback */ +#define LE_MODE_DRTY 0x0020 /* disable retry */ +#define LE_MODE_COLL 0x0010 /* force a collision */ +#define LE_MODE_DTCR 0x0008 /* disable transmit CRC */ +#define LE_MODE_LOOP 0x0004 /* loopback mode */ +#define LE_MODE_DTX 0x0002 /* disable transmitter */ +#define LE_MODE_DRX 0x0001 /* disable receiver */ +#define LE_MODE_NORMAL 0 /* none of the above */ + +/* + * Chip ID (CSR88 IDL, CSR89 IDU) values for various AMD PCnet parts + */ +#define CHIPID_MANFID(x) (((x) >> 1) & 0x3ff) +#define CHIPID_PARTID(x) (((x) >> 12) & 0xffff) +#define CHIPID_VER(x) (((x) >> 28) & 0x7) + +#define PARTID_Am79c960 0x0003 +#define PARTID_Am79c961 0x2260 +#define PARTID_Am79c961A 0x2261 +#define PARTID_Am79c965 0x2430 /* yes, these... */ +#define PARTID_Am79c970 0x2430 /* ...are the same */ +#define PARTID_Am79c970A 0x2621 +#define PARTID_Am79c971 0x2623 +#define PARTID_Am79c972 0x2624 +#define PARTID_Am79c973 0x2625 +#define PARTID_Am79c978 0x2626 +#define PARTID_Am79c975 0x2627 +#define PARTID_Am79c976 0x2628 + +#endif /* !_DEV_LE_LANCEREG_H_ */ diff --git a/bsd_eth_drivers/if_le/lancevar.h b/bsd_eth_drivers/if_le/lancevar.h new file mode 100644 index 0000000..d107c3b --- /dev/null +++ b/bsd_eth_drivers/if_le/lancevar.h @@ -0,0 +1,216 @@ +/* $NetBSD: lancevar.h,v 1.10 2005/12/11 12:21:27 christos Exp $ */ + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD: src/sys/dev/le/lancevar.h,v 1.3 2006/12/06 02:14:31 marius Exp $ */ + +#ifndef _DEV_LE_LANCEVAR_H_ +#define _DEV_LE_LANCEVAR_H_ + +extern devclass_t le_devclass; + +struct lance_softc { + struct ifnet *sc_ifp; + struct ifmedia sc_media; + struct mtx sc_mtx; + struct callout sc_wdog_ch; + int sc_wdog_timer; + + /* + * Memory functions: + * + * copy to/from descriptor + * copy to/from buffer + * zero bytes in buffer + */ + void (*sc_copytodesc)(struct lance_softc *, void *, int, int); + void (*sc_copyfromdesc)(struct lance_softc *, void *, int, int); + void (*sc_copytobuf)(struct lance_softc *, void *, int, int); + void (*sc_copyfrombuf)(struct lance_softc *, void *, int, int); + void (*sc_zerobuf)(struct lance_softc *, int, int); + + /* + * Machine-dependent functions: + * + * read/write CSR + * hardware reset hook - may be NULL + * hardware init hook - may be NULL + * no carrier hook - may be NULL + * media change hook - may be NULL + */ + uint16_t (*sc_rdcsr)(struct lance_softc *, uint16_t); + void (*sc_wrcsr)(struct lance_softc *, uint16_t, uint16_t); + void (*sc_hwreset)(struct lance_softc *); + void (*sc_hwinit)(struct lance_softc *); + int (*sc_hwintr)(struct lance_softc *); + void (*sc_nocarrier)(struct lance_softc *); + int (*sc_mediachange)(struct lance_softc *); + void (*sc_mediastatus)(struct lance_softc *, struct ifmediareq *); + + /* + * Media-supported by this interface. If this is NULL, + * the only supported media is assumed to be "manual". + */ + const int *sc_supmedia; + int sc_nsupmedia; + int sc_defaultmedia; + + uint16_t sc_conf3; /* CSR3 value */ + + void *sc_mem; /* base address of RAM - CPU's view */ + bus_addr_t sc_addr; /* base address of RAM - LANCE's view */ + + bus_size_t sc_memsize; /* size of RAM */ + + int sc_nrbuf; /* number of receive buffers */ + int sc_ntbuf; /* number of transmit buffers */ + int sc_last_rd; + int sc_first_td; + int sc_last_td; + int sc_no_td; + + int sc_initaddr; + int sc_rmdaddr; + int sc_tmdaddr; + int sc_rbufaddr; + int sc_tbufaddr; + + uint8_t sc_enaddr[ETHER_ADDR_LEN]; + + void (*sc_meminit)(struct lance_softc *); + void (*sc_start_locked)(struct lance_softc *); + + int sc_flags; +#define LE_ALLMULTI (1 << 0) +#define LE_BSWAP (1 << 1) +#define LE_CARRIER (1 << 2) +#define LE_DEBUG (1 << 3) +#define LE_PROMISC (1 << 4) +}; + +#define LE_LOCK_INIT(_sc, _name) \ + mtx_init(&(_sc)->sc_mtx, _name, MTX_NETWORK_LOCK, MTX_DEF) +#define LE_LOCK_INITIALIZED(_sc) mtx_initialized(&(_sc)->sc_mtx) +#define LE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define LE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define LE_LOCK_ASSERT(_sc, _what) mtx_assert(&(_sc)->sc_mtx, (_what)) +#define LE_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) + +/* + * Unfortunately, manual byte swapping is only necessary for the PCnet-PCI + * variants but not for the original LANCE or ILACC so we cannot do this + * with #ifdefs resolved at compile time. + */ +#define LE_HTOLE16(v) (((sc)->sc_flags & LE_BSWAP) ? htole16(v) : (v)) +#define LE_HTOLE32(v) (((sc)->sc_flags & LE_BSWAP) ? htole32(v) : (v)) +#define LE_LE16TOH(v) (((sc)->sc_flags & LE_BSWAP) ? le16toh(v) : (v)) +#define LE_LE32TOH(v) (((sc)->sc_flags & LE_BSWAP) ? le32toh(v) : (v)) + +int lance_config(struct lance_softc *, const char*, int); +void lance_attach(struct lance_softc *); +void lance_detach(struct lance_softc *); +void lance_suspend(struct lance_softc *); +void lance_resume(struct lance_softc *); +void lance_init_locked(struct lance_softc *); +int lance_put(struct lance_softc *, int, struct mbuf *); +struct mbuf *lance_get(struct lance_softc *, int, int); +void lance_setladrf(struct lance_softc *, u_int16_t *); + +/* + * The following functions are only useful on certain CPU/bus + * combinations. They should be written in assembly language for + * maximum efficiency, but machine-independent versions are provided + * for drivers that have not yet been optimized. + */ +void lance_copytobuf_contig(struct lance_softc *, void *, int, int); +void lance_copyfrombuf_contig(struct lance_softc *, void *, int, int); +void lance_zerobuf_contig(struct lance_softc *, int, int); + +#if 0 /* Example only - see lance.c */ +void lance_copytobuf_gap2(struct lance_softc *, void *, int, int); +void lance_copyfrombuf_gap2(struct lance_softc *, void *, int, int); +void lance_zerobuf_gap2(struct lance_softc *, int, int); + +void lance_copytobuf_gap16(struct lance_softc *, void *, int, int); +void lance_copyfrombuf_gap16(struct lance_softc *, void *, int, int); +void lance_zerobuf_gap16(struct lance_softc *, int, int); +#endif /* Example only */ + +/* + * Compare two Ether/802 addresses for equality, inlined and + * unrolled for speed. Use this like memcmp(). + * + * XXX: Add for stuff like this? + * XXX: or maybe add it to libkern.h instead? + * + * "I'd love to have an inline assembler version of this." + * XXX: Who wanted that? mycroft? I wrote one, but this + * version in C is as good as hand-coded assembly. -gwr + * + * Please do NOT tweak this without looking at the actual + * assembly code generated before and after your tweaks! + */ +static inline uint16_t +ether_cmp(void *one, void *two) +{ + uint16_t *a = (u_short *)one; + uint16_t *b = (u_short *)two; + uint16_t diff; + +#ifdef m68k + /* + * The post-increment-pointer form produces the best + * machine code for m68k. This was carefully tuned + * so it compiles to just 8 short (2-byte) op-codes! + */ + diff = *a++ - *b++; + diff |= *a++ - *b++; + diff |= *a++ - *b++; +#else + /* + * Most modern CPUs do better with a single expresion. + * Note that short-cut evaluation is NOT helpful here, + * because it just makes the code longer, not faster! + */ + diff = (a[0] - b[0]) | (a[1] - b[1]) | (a[2] - b[2]); +#endif + + return (diff); +} + +#endif /* _DEV_LE_LANCEVAR_H_ */ diff --git a/bsd_eth_drivers/if_pcn/Makefile.am b/bsd_eth_drivers/if_pcn/Makefile.am new file mode 100644 index 0000000..b3c877c --- /dev/null +++ b/bsd_eth_drivers/if_pcn/Makefile.am @@ -0,0 +1,21 @@ +# $Id$ +AUTOMAKE_OPTIONS=foreign + +include $(top_srcdir)/rtems-pre.am + +libif_pcn_a_SOURCES = if_pcn.c if_pcnreg.h +lib_LIBRARIES = libif_pcn.a + +AM_CPPFLAGS +=-I$(srcdir) +AM_CPPFLAGS += -I$(srcdir)/../libbsdport -I../libbsdport -I../libbsdport/dummyheaders +#AM_CPPFLAGS += -DPCN_DEBUG + +include $(top_srcdir)/rtems.am + +LINKS = pci/if_pcnreg.h + +BUILT_SOURCES = + +$(libif_pcn_a_OBJECTS): $(LINKS) + +include ../links.am diff --git a/bsd_eth_drivers/if_pcn/if_pcn.c b/bsd_eth_drivers/if_pcn/if_pcn.c new file mode 100644 index 0000000..7dde188 --- /dev/null +++ b/bsd_eth_drivers/if_pcn/if_pcn.c @@ -0,0 +1,1918 @@ +/*- + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifdef __rtems__ +#include +#endif + +#include +__FBSDID("$FreeBSD: src/sys/pci/if_pcn.c,v 1.83 2007/02/23 12:19:03 piso Exp $"); + +/* + * AMD Am79c972 fast ethernet PCI NIC driver. Datasheets are available + * from http://www.amd.com. + * + * The AMD PCnet/PCI controllers are more advanced and functional + * versions of the venerable 7990 LANCE. The PCnet/PCI chips retain + * backwards compatibility with the LANCE and thus can be made + * to work with older LANCE drivers. This is in fact how the + * PCnet/PCI chips were supported in FreeBSD originally. The trouble + * is that the PCnet/PCI devices offer several performance enhancements + * which can't be exploited in LANCE compatibility mode. Chief among + * these enhancements is the ability to perform PCI DMA operations + * using 32-bit addressing (which eliminates the need for ISA + * bounce-buffering), and special receive buffer alignment (which + * allows the receive handler to pass packets to the upper protocol + * layers without copying on both the x86 and alpha platforms). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include /* for vtophys */ +#include /* for vtophys */ +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define PCN_USEIOSPACE +#undef USE_EXPLICIT_BUSTAGS /* help compiler to optimize */ + +#include + +MODULE_DEPEND(pcn, pci, 1, 1, 1); +MODULE_DEPEND(pcn, ether, 1, 1, 1); +MODULE_DEPEND(pcn, miibus, 1, 1, 1); + +/* "device miibus" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifdef __rtems__ +#include +#endif + +/* + * Various supported device vendors/types and their names. + */ +static const struct pcn_type pcn_devs[] = { + { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" }, + { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" }, + { 0, 0, NULL } +}; + +static const struct pcn_chipid { + u_int32_t id; + const char *name; +} pcn_chipid[] = { + { Am79C971, "Am79C971" }, + { Am79C972, "Am79C972" }, + { Am79C973, "Am79C973" }, + { Am79C978, "Am79C978" }, + { Am79C975, "Am79C975" }, + { Am79C976, "Am79C976" }, + { 0, NULL }, +}; + +static const char *pcn_chipid_name(u_int32_t); +static u_int32_t pcn_chip_id(device_t); +static const struct pcn_type *pcn_match(u_int16_t, u_int16_t); + +static u_int32_t pcn_csr_read(struct pcn_softc *, int); +static u_int16_t pcn_csr_read16(struct pcn_softc *, int); +static u_int16_t pcn_bcr_read16(struct pcn_softc *, int); +static void pcn_csr_write(struct pcn_softc *, int, int); +static u_int32_t pcn_bcr_read(struct pcn_softc *, int); +static void pcn_bcr_write(struct pcn_softc *, int, int); + +static int pcn_probe(device_t); +static int pcn_attach(device_t); +static int pcn_detach(device_t); + +static int pcn_newbuf(struct pcn_softc *, int, struct mbuf *); +static int pcn_encap(struct pcn_softc *, struct mbuf *, u_int32_t *); +static void pcn_rxeof(struct pcn_softc *); +static void pcn_txeof(struct pcn_softc *); +static void pcn_intr(void *); +static void pcn_tick(void *); +static void pcn_start(struct ifnet *); +static void pcn_start_locked(struct ifnet *); +#ifndef __rtems__ +static int pcn_ioctl(struct ifnet *, u_long, caddr_t); +#else +static int pcn_ioctl(struct ifnet *, ioctl_command_t, caddr_t); +#endif +static void pcn_init(void *); +static void pcn_init_locked(struct pcn_softc *); +static void pcn_stop(struct pcn_softc *); +static void pcn_watchdog(struct ifnet *); +static void pcn_shutdown(device_t); +#ifndef __rtems__ +static int pcn_ifmedia_upd(struct ifnet *); +static void pcn_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static int pcn_miibus_readreg(device_t, int, int); +static int pcn_miibus_writereg(device_t, int, int, int); +static void pcn_miibus_statchg(device_t); +#endif + +static void pcn_setfilt(struct ifnet *); +static void pcn_setmulti(struct pcn_softc *); +static void pcn_reset(struct pcn_softc *); +static int pcn_list_rx_init(struct pcn_softc *); +static int pcn_list_tx_init(struct pcn_softc *); + +#ifdef PCN_USEIOSPACE +#define PCN_RES SYS_RES_IOPORT +#define PCN_RID PCN_PCI_LOIO +#else +#define PCN_RES SYS_RES_MEMORY +#define PCN_RID PCN_PCI_LOMEM +#endif + +#ifndef __rtems__ +static device_method_t pcn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pcn_probe), + DEVMETHOD(device_attach, pcn_attach), + DEVMETHOD(device_detach, pcn_detach), + DEVMETHOD(device_shutdown, pcn_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, pcn_miibus_readreg), + DEVMETHOD(miibus_writereg, pcn_miibus_writereg), + DEVMETHOD(miibus_statchg, pcn_miibus_statchg), + + { 0, 0 } +}; + +static driver_t pcn_driver = { + "pcn", + pcn_methods, + sizeof(struct pcn_softc) +}; + +static devclass_t pcn_devclass; + +DRIVER_MODULE(pcn, pci, pcn_driver, pcn_devclass, 0, 0); +DRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0); +#else +static int +pcn_irq_check_dis(device_t d) +{ +struct pcn_softc *sc = device_get_softc(d); +/* This can be called from IRQ context -- since all register accesses + * involve RAP we must take care to preserve it across this routine! + */ +u_int32_t rap = CSR_READ_4(sc, PCN_IO32_RAP); +u_int32_t csr; +int rval; + + csr = pcn_csr_read(sc, PCN_CSR_CSR); + + if ( PCN_CSR_INTR & csr ) { + /* must not write 1 to any bit as this might clear things */ + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN & ~(PCN_CSR_INTEN)); + rval = FILTER_HANDLED; + } else { + rval = FILTER_STRAY; + } + /* restore RAP */ + CSR_WRITE_4(sc, PCN_IO32_RAP, rap); + return rval; +} + +static void +pcn_irq_en(device_t d) +{ +struct pcn_softc *sc = device_get_softc(d); +/* This can be called from IRQ context -- since all register accesses + * involve RAP we must take care to preserve it across this routine! + */ +uint32_t rap = CSR_READ_4(sc, PCN_IO32_RAP); + /* do NOT |= INTEN since writing 1 in the wrong place may clear things */ + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN); + CSR_WRITE_4(sc, PCN_IO32_RAP, rap); +} + +static device_method_t pcn_methods = { + probe: pcn_probe, + attach: pcn_attach, + shutdown: pcn_shutdown, + detach: pcn_detach, + irq_check_dis: pcn_irq_check_dis, + irq_en: pcn_irq_en, +}; + +driver_t libbsdport_pcn_driver = { + "pcn", + &pcn_methods, + DEV_TYPE_PCI, + sizeof(struct pcn_softc) +}; + +#endif + +#define PCN_CSR_SETBIT(sc, reg, x) \ + pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x)) + +#define PCN_CSR_CLRBIT(sc, reg, x) \ + pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x)) + +#define PCN_BCR_SETBIT(sc, reg, x) \ + pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x)) + +#define PCN_BCR_CLRBIT(sc, reg, x) \ + pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x)) + +static u_int32_t +pcn_csr_read(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + return(CSR_READ_4(sc, PCN_IO32_RDP)); +} + +static u_int16_t +pcn_csr_read16(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, PCN_IO16_RAP, reg); + return(CSR_READ_2(sc, PCN_IO16_RDP)); +} + +static void +pcn_csr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; + int val; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + CSR_WRITE_4(sc, PCN_IO32_RDP, val); + return; +} + +static u_int32_t +pcn_bcr_read(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + return(CSR_READ_4(sc, PCN_IO32_BDP)); +} + +static u_int16_t +pcn_bcr_read16(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, PCN_IO16_RAP, reg); + return(CSR_READ_2(sc, PCN_IO16_BDP)); +} + +static void +pcn_bcr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; + int val; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + CSR_WRITE_4(sc, PCN_IO32_BDP, val); + return; +} + +#ifndef __rtems__ +static int +pcn_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct pcn_softc *sc; + int val; + + sc = device_get_softc(dev); + + /* + * At least Am79C971 with DP83840A wedge when isolating the + * external PHY so we can't allow multiple external PHYs. + * There are cards that use Am79C971 with both the internal + * and an external PHY though. + * For internal PHYs it doesn't really matter whether we can + * isolate the remaining internal and the external ones in + * the PHY drivers as the internal PHYs have to be enabled + * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc. + * With Am79C97{3,5,8} we don't support switching beetween + * the internal and external PHYs, yet, so we can't allow + * multiple PHYs with these either. + * Am79C97{2,6} actually only support external PHYs (not + * connectable internal ones respond at the usual addresses, + * which don't hurt if we let them show up on the bus) and + * isolating them works. + */ + if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || + sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || + sc->pcn_type == Am79C978) && sc->pcn_extphyaddr != -1 && + phy != sc->pcn_extphyaddr) + return(0); + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; + if (val == 0xFFFF) + return(0); + + if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || + sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || + sc->pcn_type == Am79C978) && sc->pcn_extphyaddr == -1) + sc->pcn_extphyaddr = phy; + + return(val); +} + +static int +pcn_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct pcn_softc *sc; + + sc = device_get_softc(dev); + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); + + return(0); +} +#else +static int +mdio_r(int phy, void *uarg, unsigned reg, uint32_t *pval) +{ + struct pcn_softc *sc = uarg; + uint32_t val; + + if ( phy != 0 ) + return EINVAL; + + if ( sc->pcn_extphyaddr < 0 ) { + printk("Have no PHY address\n"); + return EINVAL; + } + + /* Map index to address */ + phy = sc->pcn_extphyaddr; + + + /* + * At least Am79C971 with DP83840A wedge when isolating the + * external PHY so we can't allow multiple external PHYs. + * There are cards that use Am79C971 with both the internal + * and an external PHY though. + * For internal PHYs it doesn't really matter whether we can + * isolate the remaining internal and the external ones in + * the PHY drivers as the internal PHYs have to be enabled + * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc. + * With Am79C97{3,5,8} we don't support switching beetween + * the internal and external PHYs, yet, so we can't allow + * multiple PHYs with these either. + * Am79C97{2,6} actually only support external PHYs (not + * connectable internal ones respond at the usual addresses, + * which don't hurt if we let them show up on the bus) and + * isolating them works. + */ + if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || + sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || + sc->pcn_type == Am79C978) && sc->pcn_extphyaddr != -1 && + phy != sc->pcn_extphyaddr) + return(-1); + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; + if (val == 0xFFFF) + return(-1); + + if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || + sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || + sc->pcn_type == Am79C978) && sc->pcn_extphyaddr == -1) + sc->pcn_extphyaddr = phy; + + *pval = val; + + return(0); +} + +static int +mdio_w(int phy, void *uarg, unsigned reg, uint32_t data) +{ + struct pcn_softc *sc = uarg; + + if ( phy != 0 ) + return EINVAL; + + if ( sc->pcn_extphyaddr < 0 ) { + printk("Have no PHY address\n"); + return EINVAL; + } + + /* Map index to address */ + phy = sc->pcn_extphyaddr; + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); + + return(0); +} + +struct rtems_mdio_info pcn_mdio = { + mdio_r: mdio_r, + mdio_w: mdio_w, + has_gmii:0 +}; +#endif + +#ifndef __rtems__ +static void +pcn_miibus_statchg(dev) + device_t dev; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->pcn_miibus); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); + } else { + PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); + } + + return; +} +#endif + +static void +pcn_setmulti(sc) + struct pcn_softc *sc; +{ + struct ifnet *ifp; +#ifndef __rtems__ + struct ifmultiaddr *ifma; +#endif + u_int32_t h, i; + u_int16_t hashes[4] = { 0, 0, 0, 0 }; + + ifp = sc->pcn_ifp; + + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + return; + } + + /* first, zot all the existing hash bits */ + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); + + /* now program new ones */ +#ifndef __rtems__ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_le(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashes[h >> 4] |= 1 << (h & 0xF); + } + IF_ADDR_UNLOCK(ifp); +#else + { + /* UNTESTED */ + struct ether_multi *enm; + struct ether_multistep step; + ETHER_FIRST_MULTI(step, (struct arpcom *)ifp, enm); + while ( enm != NULL ) { + h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN) >> 26; + hashes[h >> 4] |= 1 << (h & 0xF); + ETHER_NEXT_MULTI( step, enm ); + } + } +#endif + + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); + + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + + return; +} + +static void +pcn_reset(sc) + struct pcn_softc *sc; +{ + /* + * Issue a reset by reading from the RESET register. + * Note that we don't know if the chip is operating in + * 16-bit or 32-bit mode at this point, so we attempt + * to reset the chip both ways. If one fails, the other + * will succeed. + */ + CSR_READ_2(sc, PCN_IO16_RESET); + CSR_READ_4(sc, PCN_IO32_RESET); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + /* Select 32-bit (DWIO) mode */ + CSR_WRITE_4(sc, PCN_IO32_RDP, 0); + + /* Select software style 3. */ + pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); + + return; +} + +static const char * +pcn_chipid_name(u_int32_t id) +{ + const struct pcn_chipid *p; + + p = pcn_chipid; + while (p->name) { + if (id == p->id) + return (p->name); + p++; + } + return ("Unknown"); +} + +static u_int32_t +pcn_chip_id(device_t dev) +{ + struct pcn_softc *sc; + u_int32_t chip_id; + + sc = device_get_softc(dev); + /* + * Note: we can *NOT* put the chip into + * 32-bit mode yet. The le(4) driver will only + * work in 16-bit mode, and once the chip + * goes into 32-bit mode, the only way to + * get it out again is with a hardware reset. + * So if pcn_probe() is called before the + * le(4) driver's probe routine, the chip will + * be locked into 32-bit operation and the + * le(4) driver will be unable to attach to it. + * Note II: if the chip happens to already + * be in 32-bit mode, we still need to check + * the chip ID, but first we have to detect + * 32-bit mode using only 16-bit operations. + * The safest way to do this is to read the + * PCI subsystem ID from BCR23/24 and compare + * that with the value read from PCI config + * space. + */ + chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID); + chip_id <<= 16; + chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID); +#if defined (__rtems__) && defined(PCN_DEBUG) + printf("Chip ID 0x%08x\n", chip_id); +#endif + /* + * Note III: the test for 0x10001000 is a hack to + * pacify VMware, who's pseudo-PCnet interface is + * broken. Reading the subsystem register from PCI + * config space yields 0x00000000 while reading the + * same value from I/O space yields 0x10001000. It's + * not supposed to be that way. + */ + if (chip_id == pci_read_config(dev, + PCIR_SUBVEND_0, 4) || chip_id == 0x10001000) { + /* We're in 16-bit mode. */ + chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1); + chip_id <<= 16; + chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0); +#if defined (__rtems__) && defined(PCN_DEBUG) + printf("Chip ID 0x%08x (16-bit mode)\n", chip_id); +#endif + } else { + /* We're in 32-bit mode. */ + chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); + chip_id <<= 16; + chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); +#if defined (__rtems__) && defined(PCN_DEBUG) + printf("Chip ID 0x%08x (32-bit mode)\n", chip_id); +#endif + } + + return (chip_id); +} + +static const struct pcn_type * +pcn_match(u_int16_t vid, u_int16_t did) +{ + const struct pcn_type *t; + + t = pcn_devs; + while (t->pcn_name != NULL) { +#if defined (__rtems__) && defined(PCN_DEBUG) + printf("Matching vid/did 0x%04x/0x%04x against known 0x%04x/0x%04x\n", + vid, did, t->pcn_vid, t->pcn_did); +#endif + if ((vid == t->pcn_vid) && (did == t->pcn_did)) + return (t); + t++; + } + return (NULL); +} + +/* + * Probe for an AMD chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int +pcn_probe(dev) + device_t dev; +{ + const struct pcn_type *t; + struct pcn_softc *sc; + int rid; + u_int32_t chip_id; + + t = pcn_match(pci_get_vendor(dev), pci_get_device(dev)); + if (t == NULL) + return (ENXIO); + sc = device_get_softc(dev); + + /* + * Temporarily map the I/O space so we can read the chip ID register. + */ + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); + if (sc->pcn_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + return(ENXIO); + } + sc->pcn_btag = rman_get_bustag(sc->pcn_res); + sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); + + chip_id = pcn_chip_id(dev); + + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + + switch((chip_id >> 12) & PART_MASK) { + case Am79C971: + case Am79C972: + case Am79C973: + case Am79C975: + case Am79C976: + case Am79C978: + break; + default: + return(ENXIO); + } + device_set_desc(dev, t->pcn_name); + return(BUS_PROBE_DEFAULT); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +pcn_attach(dev) + device_t dev; +{ + u_int32_t eaddr[2]; + struct pcn_softc *sc; +#ifndef __rtems__ + struct mii_data *mii; + struct mii_softc *miisc; +#endif + struct ifnet *ifp; + int error = 0, rid; + + sc = device_get_softc(dev); + + /* Initialize our mutex. */ + mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + + /* Retrieve the chip ID */ + sc->pcn_type = (pcn_chip_id(dev) >> 12) & PART_MASK; + device_printf(dev, "Chip ID %04x (%s)\n", + sc->pcn_type, pcn_chipid_name(sc->pcn_type)); + + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); + + if (sc->pcn_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + error = ENXIO; + goto fail; + } + + sc->pcn_btag = rman_get_bustag(sc->pcn_res); + sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); + + /* Allocate interrupt */ + rid = 0; + sc->pcn_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->pcn_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } + + /* Reset the adapter. */ + pcn_reset(sc); + + /* + * Get station address from the EEPROM. + */ + eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); + eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); + + /* if we are on a big endian host, read did swap the byte order + * and we need to swap back. On a LE host, the following is a noop + */ + eaddr[0] = htole32(eaddr[0]); + eaddr[1] = htole32(eaddr[1]); + + callout_init_mtx(&sc->pcn_stat_callout, &sc->pcn_mtx, 0); + + sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->pcn_ldata == NULL) { + device_printf(dev, "no memory for list buffers!\n"); + error = ENXIO; + goto fail; + } + bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); + + ifp = sc->pcn_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not if_alloc()\n"); + error = ENOSPC; + goto fail; + } + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = pcn_ioctl; + ifp->if_start = pcn_start; + ifp->if_watchdog = pcn_watchdog; + ifp->if_init = pcn_init; + ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ +#ifndef __rtems__ + sc->pcn_extphyaddr = -1; + if (mii_phy_probe(dev, &sc->pcn_miibus, + pcn_ifmedia_upd, pcn_ifmedia_sts)) { + device_printf(dev, "MII without any PHY!\n"); + error = ENXIO; + goto fail; + } + /* + * Record the media instances of internal PHYs, which map the + * built-in interfaces to the MII, so we can set the active + * PHY/port based on the currently selected media. + */ + sc->pcn_inst_10bt = -1; + mii = device_get_softc(sc->pcn_miibus); + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + switch (miisc->mii_phy) { + case PCN_PHYAD_10BT: + sc->pcn_inst_10bt = miisc->mii_inst; + break; + /* + * XXX deal with the Am79C97{3,5} internal 100baseT + * and the Am79C978 internal HomePNA PHYs. + */ + } + } +#else + /* use extphyaddr for the internal phy */ + switch ( sc->pcn_type ) { + case Am79C973: + case Am79C975: + sc->pcn_extphyaddr = PCN_PHYAD_100BTX; + break; + default: + if_printf(ifp,"Dunno what phy to use for this chip type (not 973 nor 975); SIOCGIFMEDIA/SIOCSIFMEDIA do not work\n"); + break; + } +#endif + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, (u_int8_t *) eaddr); + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, pcn_intr, sc, &sc->pcn_intrhand); + + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + goto fail; + } + +fail: + if (error) + pcn_detach(dev); + + return(error); +} + +/* + * Shutdown hardware and free up resources. This can be called any + * time after the mutex has been initialized. It is called in both + * the error case in attach and the normal detach case so it needs + * to be careful about only freeing resources that have actually been + * allocated. + */ +static int +pcn_detach(dev) + device_t dev; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = sc->pcn_ifp; + + KASSERT(mtx_initialized(&sc->pcn_mtx), ("pcn mutex not initialized")); + + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + PCN_LOCK(sc); + pcn_reset(sc); + pcn_stop(sc); + PCN_UNLOCK(sc); + callout_drain(&sc->pcn_stat_callout); + ether_ifdetach(ifp); + } + if (sc->pcn_miibus) + device_delete_child(dev, sc->pcn_miibus); + bus_generic_detach(dev); + + if (sc->pcn_intrhand) + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + if (sc->pcn_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + if (sc->pcn_res) + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + + if (ifp) + if_free(ifp); + + if (sc->pcn_ldata) { + contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), + M_DEVBUF); + } + + mtx_destroy(&sc->pcn_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int +pcn_list_tx_init(sc) + struct pcn_softc *sc; +{ + struct pcn_list_data *ld; + struct pcn_ring_data *cd; + int i; + + cd = &sc->pcn_cdata; + ld = sc->pcn_ldata; + + for (i = 0; i < PCN_TX_LIST_CNT; i++) { + cd->pcn_tx_chain[i] = NULL; + ld->pcn_tx_list[i].pcn_tbaddr = htole32(0); + ld->pcn_tx_list[i].pcn_txctl = htole32(0); + ld->pcn_tx_list[i].pcn_txstat = htole32(0); + } + + cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; + + return(0); +} + + +/* + * Initialize the RX descriptors and allocate mbufs for them. + */ +static int +pcn_list_rx_init(sc) + struct pcn_softc *sc; +{ + struct pcn_ring_data *cd; + int i; + + cd = &sc->pcn_cdata; + + for (i = 0; i < PCN_RX_LIST_CNT; i++) { + if (pcn_newbuf(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + } + + cd->pcn_rx_prod = 0; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int +pcn_newbuf(sc, idx, m) + struct pcn_softc *sc; + int idx; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct pcn_rx_desc *c; + + c = &sc->pcn_ldata->pcn_rx_list[idx]; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, ETHER_ALIGN); + + sc->pcn_cdata.pcn_rx_chain[idx] = m_new; + c->pcn_rbaddr = htole32(vtophys(mtod(m_new, caddr_t))); + c->pcn_bufsz = htole16((~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ); + c->pcn_bufsz |= htole16(PCN_RXLEN_MBO); +#ifdef __rtems__ + membarrier_w(); +#endif + c->pcn_rxstat = htole16(PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN); + + return(0); +} + +int pcn_pkt_debug=0; +int pcn_lnk_debug=0; + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +pcn_rxeof(sc) + struct pcn_softc *sc; +{ + struct mbuf *m; + struct ifnet *ifp; + struct pcn_rx_desc *cur_rx; + int i,len; + + PCN_LOCK_ASSERT(sc); + + ifp = sc->pcn_ifp; + i = sc->pcn_cdata.pcn_rx_prod; + + while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { +#ifdef __rtems__ + membarrier_rw(); +#endif + cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; + m = sc->pcn_cdata.pcn_rx_chain[i]; + sc->pcn_cdata.pcn_rx_chain[i] = NULL; + len = le16toh(cur_rx->pcn_rxlen); + +#if defined (__rtems__) && defined(PCN_DEBUG) + if ( pcn_pkt_debug ) { + printf("DESC: rxlen: %"PRId16", bufsz %"PRId16", stat 0x%04"PRIx16", rbaddr 0x%08"PRIx32", data %p\n", + le16toh(cur_rx->pcn_rxlen), + le16toh(cur_rx->pcn_bufsz), + le16toh(cur_rx->pcn_rxstat), + le32toh(cur_rx->pcn_rbaddr), + mtod(m, void*)); + { + int jjj; + uint8_t *p = mtod(m, uint8_t*); + for ( jjj=0; jjjpcn_rxstat) & PCN_RXSTAT_ERR) { + ifp->if_ierrors++; + pcn_newbuf(sc, i, m); + PCN_INC(i, PCN_RX_LIST_CNT); + continue; + } + + /* MUST NOT use cur_rx descriptor beyond this point; + * pcn_newbuf() writes to it and hands it over to the chip. + */ + if (pcn_newbuf(sc, i, NULL)) { + /* Ran out of mbufs; recycle this one. */ + pcn_newbuf(sc, i, m); + ifp->if_ierrors++; + PCN_INC(i, PCN_RX_LIST_CNT); + continue; + } + + PCN_INC(i, PCN_RX_LIST_CNT); + + /* No errors; receive the packet. */ + ifp->if_ipackets++; + m->m_len = m->m_pkthdr.len = + len - ETHER_CRC_LEN; + m->m_pkthdr.rcvif = ifp; + + PCN_UNLOCK(sc); +#ifndef __rtems__ + (*ifp->if_input)(ifp, m); +#else + ether_input_skipping(ifp, m); +#endif + PCN_LOCK(sc); + } + + sc->pcn_cdata.pcn_rx_prod = i; + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void +pcn_txeof(sc) + struct pcn_softc *sc; +{ + struct pcn_tx_desc *cur_tx = NULL; + struct ifnet *ifp; + u_int32_t idx; + + ifp = sc->pcn_ifp; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + idx = sc->pcn_cdata.pcn_tx_cons; + while (idx != sc->pcn_cdata.pcn_tx_prod) { + cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; + + if (!PCN_OWN_TXDESC(cur_tx)) + break; +#ifdef __rtems__ + membarrier_rw(); +#endif + if (!(le32toh(cur_tx->pcn_txctl) & PCN_TXCTL_ENP)) { + sc->pcn_cdata.pcn_tx_cnt--; + PCN_INC(idx, PCN_TX_LIST_CNT); + continue; + } + + if (le32toh(cur_tx->pcn_txctl) & PCN_TXCTL_ERR) { + ifp->if_oerrors++; + if (le32toh(cur_tx->pcn_txstat) & PCN_TXSTAT_EXDEF) + ifp->if_collisions++; + if (le32toh(cur_tx->pcn_txstat) & PCN_TXSTAT_RTRY) + ifp->if_collisions++; + } + + ifp->if_collisions += + le32toh(cur_tx->pcn_txstat) & PCN_TXSTAT_TRC; + + ifp->if_opackets++; + if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { + m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); + sc->pcn_cdata.pcn_tx_chain[idx] = NULL; + } + + sc->pcn_cdata.pcn_tx_cnt--; + PCN_INC(idx, PCN_TX_LIST_CNT); + } + + if (idx != sc->pcn_cdata.pcn_tx_cons) { + /* Some buffers have been freed. */ + sc->pcn_cdata.pcn_tx_cons = idx; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + } + ifp->if_timer = (sc->pcn_cdata.pcn_tx_cnt == 0) ? 0 : 5; + + return; +} + +static void +pcn_tick(xsc) + void *xsc; +{ + struct pcn_softc *sc; +#ifndef __rtems__ + struct mii_data *mii; +#endif + struct ifnet *ifp; + + sc = xsc; + ifp = sc->pcn_ifp; + PCN_LOCK_ASSERT(sc); + +#ifndef __rtems__ + mii = device_get_softc(sc->pcn_miibus); + mii_tick(mii); + + /* link just died */ + if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) + sc->pcn_link = 0; + + /* link just came up, restart */ + if (!sc->pcn_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->pcn_link++; + if (ifp->if_snd.ifq_head != NULL) + pcn_start_locked(ifp); + } +#else + { + int med, err; + + med = IFM_MAKEWORD(0,0,0,0); + + if ( (err = rtems_mii_ioctl( &pcn_mdio, sc, SIOCGIFMEDIA, &med )) ) { + if ( pcn_lnk_debug ) + printf("pcn: link check failed: %s\n",strerror(err)); + med = IFM_LINK_OK; /* pretend */ + } + + /* link just died */ + if (sc->pcn_link & !(IFM_LINK_OK & med) ) { + if ( pcn_lnk_debug ) + printf("pcn: Link died\n"); + sc->pcn_link = 0; + } + + /* link just came up, restart */ + if (!sc->pcn_link && (IFM_LINK_OK & med) ) { + if ( pcn_lnk_debug ) { + printf("pcn: Link up: "); + rtems_ifmedia2str(med, 0, 0); + printf("\n"); + } + sc->pcn_link++; + if (ifp->if_snd.ifq_head != NULL) + pcn_start_locked(ifp); + } + } +#endif + + callout_reset(&sc->pcn_stat_callout, hz, pcn_tick, sc); + + return; +} + +static void +pcn_intr(arg) + void *arg; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + ifp = sc->pcn_ifp; + +#if defined (__rtems__) && defined(PCN_DEBUG) + printf("entering pcn_intr\n"); +#endif + + PCN_LOCK(sc); + + /* Suppress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + pcn_stop(sc); + PCN_UNLOCK(sc); + return; + } + + CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); + + while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { + CSR_WRITE_4(sc, PCN_IO32_RDP, status); + + if (status & PCN_CSR_RINT) + pcn_rxeof(sc); + + if (status & PCN_CSR_TINT) + pcn_txeof(sc); + + if (status & PCN_CSR_ERR) { +#if defined(__rtems__) && defined(PCN_DEBUG) + if_printf(ifp,"pcn_intr() error; status is 0x%04x\n", status); +#endif + pcn_init_locked(sc); + break; + } + } + + if (ifp->if_snd.ifq_head != NULL) + pcn_start_locked(ifp); + + PCN_UNLOCK(sc); + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +pcn_encap(sc, m_head, txidx) + struct pcn_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct pcn_tx_desc *f = NULL; + struct mbuf *m; + int frag, cur, cnt = 0; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + cur = frag = *txidx; + + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len == 0) + continue; + + if ((PCN_TX_LIST_CNT - (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) + return(ENOBUFS); + f = &sc->pcn_ldata->pcn_tx_list[frag]; + f->pcn_txctl = htole32((~(m->m_len) + 1) & PCN_TXCTL_BUFSZ); + f->pcn_txctl |= htole32(PCN_TXCTL_MBO); + f->pcn_tbaddr = htole32(vtophys(mtod(m, vm_offset_t))); +#ifdef __rtems__ + membarrier_w(); +#endif + if (cnt == 0) + f->pcn_txctl |= htole32(PCN_TXCTL_STP); + else + f->pcn_txctl |= htole32(PCN_TXCTL_OWN); + cur = frag; + PCN_INC(frag, PCN_TX_LIST_CNT); + cnt++; + } + + if (m != NULL) + return(ENOBUFS); + + sc->pcn_cdata.pcn_tx_chain[cur] = m_head; + sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= + htole32(PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT); +#ifdef __rtems__ + membarrier_w(); +#endif + sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= htole32(PCN_TXCTL_OWN); + sc->pcn_cdata.pcn_tx_cnt += cnt; + *txidx = frag; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ +static void +pcn_start(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + PCN_LOCK(sc); + pcn_start_locked(ifp); + PCN_UNLOCK(sc); +} + +static void +pcn_start_locked(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + struct mbuf *m_head = NULL; + u_int32_t idx; + + sc = ifp->if_softc; + + PCN_LOCK_ASSERT(sc); + + if (!sc->pcn_link) + return; + + idx = sc->pcn_cdata.pcn_tx_prod; + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE) + return; + + while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (pcn_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + BPF_MTAP(ifp, m_head); + + } + + /* Transmit */ + sc->pcn_cdata.pcn_tx_prod = idx; + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + return; +} + +static void +pcn_setfilt(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); + } else { + PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); + } + + /* Set the capture broadcast bit to capture broadcast frames. */ + if (ifp->if_flags & IFF_BROADCAST) { + PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); + } else { + PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); + } + + return; +} + +static void +pcn_init(xsc) + void *xsc; +{ + struct pcn_softc *sc = xsc; + + PCN_LOCK(sc); + pcn_init_locked(sc); + PCN_UNLOCK(sc); +} + +static void +pcn_init_locked(sc) + struct pcn_softc *sc; +{ + struct ifnet *ifp = sc->pcn_ifp; +#ifndef __rtems__ + struct mii_data *mii = NULL; + struct ifmedia_entry *ife; +#endif + +#if defined (__rtems__) && defined(PCN_DEBUG) + printf("entering pcn_init_locked\n"); +#endif + + PCN_LOCK_ASSERT(sc); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + pcn_stop(sc); + pcn_reset(sc); + +#ifndef __rtems__ + mii = device_get_softc(sc->pcn_miibus); + ife = mii->mii_media.ifm_cur; +#endif + + /* Set MAC address */ + { unsigned tmp; + /* fix endinanness; LLADDR gets swapped on a BE machine */ + tmp = htole16(((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[0]); + pcn_csr_write(sc, PCN_CSR_PAR0, tmp); + tmp = htole16(((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[1]); + pcn_csr_write(sc, PCN_CSR_PAR1, tmp); + tmp = htole16(((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[2]); + pcn_csr_write(sc, PCN_CSR_PAR2, tmp); + } + + /* Init circular RX list. */ + if (pcn_list_rx_init(sc) == ENOBUFS) { + if_printf(ifp, "initialization failed: no " + "memory for rx buffers\n"); + pcn_stop(sc); + return; + } + + /* + * Init tx descriptors. + */ + pcn_list_tx_init(sc); + + /* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */ + PCN_BCR_CLRBIT(sc, PCN_BCR_MISCCFG, PCN_MISC_ASEL); + + /* + * Set up the port based on the currently selected media. + * For Am79C978 we've to unconditionally set PCN_PORT_MII and + * set the PHY in PCN_BCR_PHYSEL instead. + */ +#ifndef __rtems__ + if (sc->pcn_type != Am79C978 && + IFM_INST(ife->ifm_media) == sc->pcn_inst_10bt) +#else + /* a hack!! */ + if (sc->pcn_type != Am79C978 && sc->pcn_extphyaddr == PCN_PHYAD_10BT) +#endif + pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_10BASET); + else + pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); + + /* Set up RX filter. */ + pcn_setfilt(ifp); + + /* + * Load the multicast filter. + */ + pcn_setmulti(sc); + + /* + * Load the addresses of the RX and TX lists. + */ + pcn_csr_write(sc, PCN_CSR_RXADDR0, + vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_RXADDR1, + (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_TXADDR0, + vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_TXADDR1, + (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); + + /* Set the RX and TX ring sizes. */ + pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); + pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); + + /* We're not using the initialization block. */ + pcn_csr_write(sc, PCN_CSR_IAB1, 0); + + /* Enable fast suspend mode. */ + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); + + /* + * Enable burst read and write. Also set the no underflow + * bit. This will avoid transmit underruns in certain + * conditions while still providing decent performance. + */ + PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| + PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); + + /* Enable graceful recovery from underflow. */ + PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); + + /* Enable auto-padding of short TX frames. */ + PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); + +#ifndef __rtems__ /* mii_mediachg & friends not implemented yet */ + /* Disable MII autoneg (we handle this ourselves). */ + PCN_BCR_SETBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); +#endif + + if (sc->pcn_type == Am79C978) + /* XXX support other PHYs? */ + pcn_bcr_write(sc, PCN_BCR_PHYSEL, + PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); + + /* Enable interrupts and start the controller running. */ + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); + + mii_mediachg(mii); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->pcn_stat_callout, hz, pcn_tick, sc); + + return; +} + +#ifndef __rtems__ +/* + * Set media options. + */ +static int +pcn_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + PCN_LOCK(sc); + + /* + * At least Am79C971 with DP83840A can wedge when switching + * from the internal 10baseT PHY to the external PHY without + * issuing pcn_reset(). For setting the port in PCN_CSR_MODE + * the PCnet chip has to be powered down or stopped anyway + * and although documented otherwise it doesn't take effect + * until the next initialization. + */ + sc->pcn_link = 0; + pcn_stop(sc); + pcn_reset(sc); + pcn_init_locked(sc); + if (ifp->if_snd.ifq_head != NULL) + pcn_start_locked(ifp); + + PCN_UNLOCK(sc); + + return(0); +} + +/* + * Report current media status. + */ +static void +pcn_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + mii = device_get_softc(sc->pcn_miibus); + PCN_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + PCN_UNLOCK(sc); + + return; +} +#endif + +static int +pcn_ioctl(ifp, command, data) + struct ifnet *ifp; +#ifndef __rtems__ + u_long command; +#else + ioctl_command_t command; +#endif + caddr_t data; +{ + struct pcn_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; +#ifndef __rtems__ + struct mii_data *mii = NULL; +#endif + int error = 0; + + switch(command) { + case SIOCSIFFLAGS: + PCN_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->pcn_if_flags & IFF_PROMISC)) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_setfilt(ifp); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_csr_write(sc, PCN_CSR_CSR, + PCN_CSR_INTEN|PCN_CSR_START); + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->pcn_if_flags & IFF_PROMISC) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_setfilt(ifp); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_csr_write(sc, PCN_CSR_CSR, + PCN_CSR_INTEN|PCN_CSR_START); + } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) + pcn_init_locked(sc); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + pcn_stop(sc); + } + sc->pcn_if_flags = ifp->if_flags; + PCN_UNLOCK(sc); + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: +#ifdef __rtems__ + if ( ETHER_SIOCMULTIFRAG(error, command, ifr, ifp) ) + break; +#endif + PCN_LOCK(sc); + pcn_setmulti(sc); + PCN_UNLOCK(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: +#ifndef __rtems__ + mii = device_get_softc(sc->pcn_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); +#else + error = rtems_mii_ioctl(&pcn_mdio, sc, command, &ifr->ifr_media); +#endif + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return(error); +} + +static void +pcn_watchdog(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + PCN_LOCK(sc); + + ifp->if_oerrors++; + if_printf(ifp, "watchdog timeout\n"); + + pcn_stop(sc); + pcn_reset(sc); + pcn_init_locked(sc); + + if (ifp->if_snd.ifq_head != NULL) + pcn_start(ifp); + + PCN_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +pcn_stop(sc) + struct pcn_softc *sc; +{ + register int i; + struct ifnet *ifp; + + PCN_LOCK_ASSERT(sc); + ifp = sc->pcn_ifp; + ifp->if_timer = 0; + + callout_stop(&sc->pcn_stat_callout); + + /* Turn off interrupts */ + PCN_CSR_CLRBIT(sc, PCN_CSR_CSR, PCN_CSR_INTEN); + /* Stop adapter */ + PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); + sc->pcn_link = 0; + + /* + * Free data in the RX lists. + */ + for (i = 0; i < PCN_RX_LIST_CNT; i++) { + if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { + m_freem(sc->pcn_cdata.pcn_rx_chain[i]); + sc->pcn_cdata.pcn_rx_chain[i] = NULL; + } + } + bzero((char *)&sc->pcn_ldata->pcn_rx_list, + sizeof(sc->pcn_ldata->pcn_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < PCN_TX_LIST_CNT; i++) { + if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { + m_freem(sc->pcn_cdata.pcn_tx_chain[i]); + sc->pcn_cdata.pcn_tx_chain[i] = NULL; + } + } + + bzero((char *)&sc->pcn_ldata->pcn_tx_list, + sizeof(sc->pcn_ldata->pcn_tx_list)); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void +pcn_shutdown(dev) + device_t dev; +{ + struct pcn_softc *sc; + + sc = device_get_softc(dev); + + PCN_LOCK(sc); + pcn_reset(sc); + pcn_stop(sc); + PCN_UNLOCK(sc); + + return; +} + +#ifdef __rtems__ +u_int32_t +pcn_read_csr(device_t dev, int off) +{ +u_int32_t rval; + rtems_bsdnet_semaphore_obtain(); + rval = pcn_csr_read(device_get_softc(dev), off); + rtems_bsdnet_semaphore_release(); + return rval; +} + +u_int32_t +pcn_read_csr16(device_t dev, int off) +{ +u_int32_t rval; + rtems_bsdnet_semaphore_obtain(); + rval = pcn_csr_read16(device_get_softc(dev), off); + rtems_bsdnet_semaphore_release(); + return rval; +} + + +void +pcn_write_csr(device_t dev, int off, int val) +{ + rtems_bsdnet_semaphore_obtain(); + pcn_csr_write(device_get_softc(dev), off, val); + rtems_bsdnet_semaphore_release(); +} + +u_int32_t +pcn_read_bcr(device_t dev, int off) +{ +u_int32_t rval; + rtems_bsdnet_semaphore_obtain(); + rval = pcn_bcr_read(device_get_softc(dev), off); + rtems_bsdnet_semaphore_release(); + return rval; +} + +u_int32_t +pcn_read_bcr16(device_t dev, int off) +{ +u_int32_t rval; + rtems_bsdnet_semaphore_obtain(); + rval = pcn_bcr_read16(device_get_softc(dev), off); + rtems_bsdnet_semaphore_release(); + return rval; +} + + +void +pcn_write_bcr(device_t dev, int off, int val) +{ + rtems_bsdnet_semaphore_obtain(); + pcn_bcr_write(device_get_softc(dev), off, val); + rtems_bsdnet_semaphore_release(); +} + +#endif diff --git a/bsd_eth_drivers/if_pcn/if_pcnreg.h b/bsd_eth_drivers/if_pcn/if_pcnreg.h new file mode 100644 index 0000000..c1de9af --- /dev/null +++ b/bsd_eth_drivers/if_pcn/if_pcnreg.h @@ -0,0 +1,540 @@ +/*- + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/pci/if_pcnreg.h,v 1.15 2006/11/28 01:33:17 marius Exp $ + */ + +/* + * I/O map in 16-bit mode. To switch to 32-bit mode, + * you need to perform a 32-bit write to the RDP register + * (writing a 0 is recommended). + */ +#define PCN_IO16_APROM00 0x00 +#define PCN_IO16_APROM01 0x02 +#define PCN_IO16_APROM02 0x04 +#define PCN_IO16_APROM03 0x06 +#define PCN_IO16_APROM04 0x08 +#define PCN_IO16_APROM05 0x0A +#define PCN_IO16_APROM06 0x0C +#define PCN_IO16_APROM07 0x0E +#define PCN_IO16_RDP 0x10 +#define PCN_IO16_RAP 0x12 +#define PCN_IO16_RESET 0x14 +#define PCN_IO16_BDP 0x16 + +/* + * I/O map in 32-bit mode. + */ +#define PCN_IO32_APROM00 0x00 +#define PCN_IO32_APROM01 0x04 +#define PCN_IO32_APROM02 0x08 +#define PCN_IO32_APROM03 0x0C +#define PCN_IO32_RDP 0x10 +#define PCN_IO32_RAP 0x14 +#define PCN_IO32_RESET 0x18 +#define PCN_IO32_BDP 0x1C + +/* + * CSR registers + */ +#define PCN_CSR_CSR 0x00 +#define PCN_CSR_IAB0 0x01 +#define PCN_CSR_IAB1 0x02 +#define PCN_CSR_IMR 0x03 +#define PCN_CSR_TFEAT 0x04 +#define PCN_CSR_EXTCTL1 0x05 +#define PCN_CSR_DTBLLEN 0x06 +#define PCN_CSR_EXTCTL2 0x07 +#define PCN_CSR_MAR0 0x08 +#define PCN_CSR_MAR1 0x09 +#define PCN_CSR_MAR2 0x0A +#define PCN_CSR_MAR3 0x0B +#define PCN_CSR_PAR0 0x0C +#define PCN_CSR_PAR1 0x0D +#define PCN_CSR_PAR2 0x0E +#define PCN_CSR_MODE 0x0F +#define PCN_CSR_RXADDR0 0x18 +#define PCN_CSR_RXADDR1 0x19 +#define PCN_CSR_TXADDR0 0x1E +#define PCN_CSR_TXADDR1 0x1F +#define PCN_CSR_TXPOLL 0x2F +#define PCN_CSR_RXPOLL 0x31 +#define PCN_CSR_RXRINGLEN 0x4C +#define PCN_CSR_TXRINGLEN 0x4E +#define PCN_CSR_DMACTL 0x50 +#define PCN_CSR_BUSTIMER 0x52 +#define PCN_CSR_MEMERRTIMEO 0x64 +#define PCN_CSR_ONNOWMISC 0x74 +#define PCN_CSR_ADVFEAT 0x7A +#define PCN_CSR_MACCFG 0x7D +#define PCN_CSR_CHIPID0 0x58 +#define PCN_CSR_CHIPID1 0x59 + +/* + * Control and status register (CSR0) + */ +#define PCN_CSR_INIT 0x0001 +#define PCN_CSR_START 0x0002 +#define PCN_CSR_STOP 0x0004 +#define PCN_CSR_TX 0x0008 +#define PCN_CSR_TXON 0x0010 +#define PCN_CSR_RXON 0x0020 +#define PCN_CSR_INTEN 0x0040 +#define PCN_CSR_INTR 0x0080 +#define PCN_CSR_IDONE 0x0100 +#define PCN_CSR_TINT 0x0200 +#define PCN_CSR_RINT 0x0400 +#define PCN_CSR_MERR 0x0800 +#define PCN_CSR_MISS 0x1000 +#define PCN_CSR_CERR 0x2000 +#define PCN_CSR_ERR 0x8000 + +/* + * Interrupt masks and deferral control (CSR3) + */ +#define PCN_IMR_BSWAP 0x0004 +#define PCN_IMR_ENMBA 0x0008 /* enable modified backoff alg */ +#define PCN_IMR_DXMT2PD 0x0010 +#define PCN_IMR_LAPPEN 0x0020 /* lookahead packet processing enb */ +#define PCN_IMR_DXSUFLO 0x0040 /* disable TX stop on underflow */ +#define PCN_IMR_IDONE 0x0100 +#define PCN_IMR_TINT 0x0200 +#define PCN_IMR_RINT 0x0400 +#define PCN_IMR_MERR 0x0800 +#define PCN_IMR_MISS 0x1000 + +/* + * Test and features control (CSR4) + */ +#define PCN_TFEAT_TXSTRTMASK 0x0004 +#define PCN_TFEAT_TXSTRT 0x0008 +#define PCN_TFEAT_RXCCOFLOWM 0x0010 /* Rx collision counter oflow */ +#define PCN_TFEAT_RXCCOFLOW 0x0020 +#define PCN_TFEAT_UINT 0x0040 +#define PCN_TFEAT_UINTREQ 0x0080 +#define PCN_TFEAT_MISSOFLOWM 0x0100 +#define PCN_TFEAT_MISSOFLOW 0x0200 +#define PCN_TFEAT_STRIP_FCS 0x0400 +#define PCN_TFEAT_PAD_TX 0x0800 +#define PCN_TFEAT_TXDPOLL 0x1000 +#define PCN_TFEAT_DMAPLUS 0x4000 + +/* + * Extended control and interrupt 1 (CSR5) + */ +#define PCN_EXTCTL1_SPND 0x0001 /* suspend */ +#define PCN_EXTCTL1_MPMODE 0x0002 /* magic packet mode */ +#define PCN_EXTCTL1_MPENB 0x0004 /* magic packet enable */ +#define PCN_EXTCTL1_MPINTEN 0x0008 /* magic packet interrupt enable */ +#define PCN_EXTCTL1_MPINT 0x0010 /* magic packet interrupt */ +#define PCN_EXTCTL1_MPPLBA 0x0020 /* magic packet phys. logical bcast */ +#define PCN_EXTCTL1_EXDEFEN 0x0040 /* excessive deferral interrupt enb. */ +#define PCN_EXTCTL1_EXDEF 0x0080 /* excessive deferral interrupt */ +#define PCN_EXTCTL1_SINTEN 0x0400 /* system interrupt enable */ +#define PCN_EXTCTL1_SINT 0x0800 /* system interrupt */ +#define PCN_EXTCTL1_LTINTEN 0x4000 /* last TX interrupt enb */ +#define PCN_EXTCTL1_TXOKINTD 0x8000 /* TX OK interrupt disable */ + +/* + * RX/TX descriptor len (CSR6) + */ +#define PCN_DTBLLEN_RLEN 0x0F00 +#define PCN_DTBLLEN_TLEN 0xF000 + +/* + * Extended control and interrupt 2 (CSR7) + */ +#define PCN_EXTCTL2_MIIPDTINTE 0x0001 +#define PCN_EXTCTL2_MIIPDTINT 0x0002 +#define PCN_EXTCTL2_MCCIINTE 0x0004 +#define PCN_EXTCTL2_MCCIINT 0x0008 +#define PCN_EXTCTL2_MCCINTE 0x0010 +#define PCN_EXTCTL2_MCCINT 0x0020 +#define PCN_EXTCTL2_MAPINTE 0x0040 +#define PCN_EXTCTL2_MAPINT 0x0080 +#define PCN_EXTCTL2_MREINTE 0x0100 +#define PCN_EXTCTL2_MREINT 0x0200 +#define PCN_EXTCTL2_STINTE 0x0400 +#define PCN_EXTCTL2_STINT 0x0800 +#define PCN_EXTCTL2_RXDPOLL 0x1000 +#define PCN_EXTCTL2_RDMD 0x2000 +#define PCN_EXTCTL2_RXFRTG 0x4000 +#define PCN_EXTCTL2_FASTSPNDE 0x8000 + + +/* + * Mode (CSR15) + */ +#define PCN_MODE_RXD 0x0001 /* RX disable */ +#define PCN_MODE_TXD 0x0002 /* TX disable */ +#define PCN_MODE_LOOP 0x0004 /* loopback enable */ +#define PCN_MODE_TXCRCD 0x0008 +#define PCN_MODE_FORCECOLL 0x0010 +#define PCN_MODE_RETRYD 0x0020 +#define PCN_MODE_INTLOOP 0x0040 +#define PCN_MODE_PORTSEL 0x0180 +#define PCN_MODE_RXVPAD 0x2000 +#define PCN_MODE_RXNOBROAD 0x4000 +#define PCN_MODE_PROMISC 0x8000 + +/* Settings for PCN_MODE_PORTSEL when ASEL (BCR2[1]) is 0 */ +#define PCN_PORT_AUI 0x0000 +#define PCN_PORT_10BASET 0x0080 +#define PCN_PORT_GPSI 0x0100 +#define PCN_PORT_MII 0x0180 + +/* + * Chip ID values. + */ +/* CSR88-89: Chip ID masks */ +#define AMD_MASK 0x003 +#define PART_MASK 0xffff +#define Am79C971 0x2623 +#define Am79C972 0x2624 +#define Am79C973 0x2625 +#define Am79C978 0x2626 +#define Am79C975 0x2627 +#define Am79C976 0x2628 + +/* + * Advanced feature control (CSR122) + */ +#define PCN_AFC_RXALIGN 0x0001 + +/* + * BCR (bus control) registers + */ +#define PCN_BCR_MMRA 0x00 /* Master Mode Read Active */ +#define PCN_BCR_MMW 0x01 /* Master Mode Write Active */ +#define PCN_BCR_MISCCFG 0x02 +#define PCN_BCR_LED0 0x04 +#define PCN_BCR_LED1 0x05 +#define PCN_BCR_LED2 0x06 +#define PCN_BCR_LED3 0x07 +#define PCN_BCR_DUPLEX 0x09 +#define PCN_BCR_BUSCTL 0x12 +#define PCN_BCR_EECTL 0x13 +#define PCN_BCR_SSTYLE 0x14 +#define PCN_BCR_PCILAT 0x16 +#define PCN_BCR_PCISUBVENID 0x17 +#define PCN_BCR_PCISUBSYSID 0x18 +#define PCN_BCR_SRAMSIZE 0x19 +#define PCN_BCR_SRAMBOUND 0x1A +#define PCN_BCR_SRAMCTL 0x1B +#define PCN_BCR_MIICTL 0x20 +#define PCN_BCR_MIIADDR 0x21 +#define PCN_BCR_MIIDATA 0x22 +#define PCN_BCR_PCIVENID 0x23 +#define PCN_BCR_PCIPCAP 0x24 +#define PCN_BCR_DATA0 0x25 +#define PCN_BCR_DATA1 0x26 +#define PCN_BCR_DATA2 0x27 +#define PCN_BCR_DATA3 0x28 +#define PCN_BCR_DATA4 0x29 +#define PCN_BCR_DATA5 0x2A +#define PCN_BCR_DATA6 0x2B +#define PCN_BCR_DATA7 0x2C +#define PCN_BCR_ONNOWPAT0 0x2D +#define PCN_BCR_ONNOWPAT1 0x2E +#define PCN_BCR_ONNOWPAT2 0x2F +#define PCN_BCR_PHYSEL 0x31 + +/* + * Miscellaneous Configuration (BCR2) + */ +#define PCN_MISC_TMAULOOP 1<<14 /* T-MAU Loopback packet enable. */ +#define PCN_MISC_LEDPE 1<<12 /* LED Program Enable */ +#define PCN_MISC_APROMWE 1<<8 /* Address PROM Write Enable */ +#define PCN_MISC_INTLEVEL 1<<7 /* Interrupt level */ +#define PCN_MISC_EADISEL 1<<3 /* EADI Select */ +#define PCN_MISC_AWAKE 1<<2 /* Power saving mode select */ +#define PCN_MISC_ASEL 1<<1 /* Auto Select */ +#define PCN_MISC_XMAUSEL 1<<0 /* Reserved. */ + +/* + * Full duplex control (BCR9) + */ +#define PCN_DUPLEX_FDEN 0x0001 /* Full-duplex enable */ +#define PCN_DUPLEX_AUI 0x0002 /* AUI full-duplex */ +#define PCN_DUPLEX_FDRPAD 0x0004 /* Full-duplex runt pkt accept dis. */ + +/* + * Burst and bus control register (BCR18) + */ +#define PCN_BUSCTL_BWRITE 0x0020 +#define PCN_BUSCTL_BREAD 0x0040 +#define PCN_BUSCTL_DWIO 0x0080 +#define PCN_BUSCTL_EXTREQ 0x0100 +#define PCN_BUSCTL_MEMCMD 0x0200 +#define PCN_BUSCTL_NOUFLOW 0x0800 +#define PCN_BUSCTL_ROMTMG 0xF000 + +/* + * EEPROM control (BCR19) + */ +#define PCN_EECTL_EDATA 0x0001 +#define PCN_EECTL_ECLK 0x0002 +#define PCN_EECTL_EECS 0x0004 +#define PCN_EECTL_EEN 0x0100 +#define PCN_EECTL_EEDET 0x2000 +#define PCN_EECTL_PREAD 0x4000 +#define PCN_EECTL_PVALID 0x8000 + +/* + * Software style (BCR20) + */ +#define PCN_SSTYLE_APERREN 0x0400 /* advanced parity error checking */ +#define PCN_SSTYLE_SSIZE32 0x0100 +#define PCN_SSTYLE_SWSTYLE 0x00FF + +#define PCN_SWSTYLE_LANCE 0x0000 +#define PCN_SWSTYLE_PCNETPCI 0x0102 +#define PCN_SWSTYLE_PCNETPCI_BURST 0x0103 + +/* + * MII control and status (BCR32) + */ +#define PCN_MIICTL_MIILP 0x0002 /* MII internal loopback */ +#define PCN_MIICTL_XPHYSP 0x0008 /* external PHY speed */ +#define PCN_MIICTL_XPHYFD 0x0010 /* external PHY full duplex */ +#define PCN_MIICTL_XPHYANE 0x0020 /* external phy auto-neg enable */ +#define PCN_MIICTL_XPHYRST 0x0040 /* external PHY reset */ +#define PCN_MIICTL_DANAS 0x0080 /* disable auto-neg auto-setup */ +#define PCN_MIICTL_APDW 0x0700 /* auto-poll dwell time */ +#define PCN_MIICTL_APEP 0x0100 /* auto-poll external PHY */ +#define PCN_MIICTL_FMDC 0x3000 /* data clock speed */ +#define PCN_MIICTL_MIIPD 0x4000 /* PHY detect */ +#define PCN_MIICTL_ANTST 0x8000 /* Manufacturing test */ + +/* + * MII address register (BCR33) + */ +#define PCN_MIIADDR_REGAD 0x001F +#define PCN_MIIADDR_PHYAD 0x03E0 + +/* addresses of internal PHYs */ +#define PCN_PHYAD_100BTX 30 +#define PCN_PHYAD_10BT 31 + +/* + * MII data register (BCR34) + */ +#define PCN_MIIDATA_MIIMD 0xFFFF + +/* + * PHY selection (BCR49) (HomePNA NIC only) + */ +#define PCN_PHYSEL_PHYSEL 0x0003 +#define PCN_PHYSEL_DEFAULT 0x0300 +#define PCN_PHYSEL_PCNET 0x8000 + +#define PCN_PHY_10BT 0x0000 +#define PCN_PHY_HOMEPNA 0x0001 +#define PCN_PHY_EXTERNAL 0x0002 + +struct pcn_rx_desc { + volatile u_int16_t pcn_rxlen; + volatile u_int16_t pcn_rsvd0; + u_int16_t pcn_bufsz; + volatile u_int16_t pcn_rxstat; + u_int32_t pcn_rbaddr; + u_int32_t pcn_uspace; +}; + +#define PCN_RXSTAT_BPE 0x0080 /* bus parity error */ +#define PCN_RXSTAT_ENP 0x0100 /* end of packet */ +#define PCN_RXSTAT_STP 0x0200 /* start of packet */ +#define PCN_RXSTAT_BUFF 0x0400 /* buffer error */ +#define PCN_RXSTAT_CRC 0x0800 /* CRC error */ +#define PCN_RXSTAT_OFLOW 0x1000 /* rx overrun */ +#define PCN_RXSTAT_FRAM 0x2000 /* framing error */ +#define PCN_RXSTAT_ERR 0x4000 /* error summary */ +#define PCN_RXSTAT_OWN 0x8000 + +#define PCN_RXLEN_MBO 0xF000 +#define PCN_RXLEN_BUFSZ 0x0FFF + +#define PCN_OWN_RXDESC(x) ((le16toh((x)->pcn_rxstat) & PCN_RXSTAT_OWN) == 0) + +struct pcn_tx_desc { + volatile u_int32_t pcn_txstat; + volatile u_int32_t pcn_txctl; + u_int32_t pcn_tbaddr; + u_int32_t pcn_uspace; +}; + +#define PCN_TXSTAT_TRC 0x0000000F /* transmit retries */ +#define PCN_TXSTAT_RTRY 0x04000000 /* retry */ +#define PCN_TXSTAT_LCAR 0x08000000 /* lost carrier */ +#define PCN_TXSTAT_LCOL 0x10000000 /* late collision */ +#define PCN_TXSTAT_EXDEF 0x20000000 /* excessive deferrals */ +#define PCN_TXSTAT_UFLOW 0x40000000 /* transmit underrun */ +#define PCN_TXSTAT_BUFF 0x80000000 /* buffer error */ + +#define PCN_TXCTL_OWN 0x80000000 +#define PCN_TXCTL_ERR 0x40000000 /* error summary */ +#define PCN_TXCTL_ADD_FCS 0x20000000 /* add FCS to pkt */ +#define PCN_TXCTL_MORE_LTINT 0x10000000 +#define PCN_TXCTL_ONE 0x08000000 +#define PCN_TXCTL_DEF 0x04000000 +#define PCN_TXCTL_STP 0x02000000 +#define PCN_TXCTL_ENP 0x01000000 +#define PCN_TXCTL_BPE 0x00800000 +#define PCN_TXCTL_MBO 0x0000F000 +#define PCN_TXCTL_BUFSZ 0x00000FFF + +#define PCN_OWN_TXDESC(x) ((le32toh((x)->pcn_txctl) & PCN_TXCTL_OWN) == 0) + +#define PCN_RX_LIST_CNT 64 +#define PCN_TX_LIST_CNT 256 + +struct pcn_list_data { + struct pcn_rx_desc pcn_rx_list[PCN_RX_LIST_CNT]; + struct pcn_tx_desc pcn_tx_list[PCN_TX_LIST_CNT]; +}; + +struct pcn_ring_data { + struct mbuf *pcn_rx_chain[PCN_RX_LIST_CNT]; + struct mbuf *pcn_tx_chain[PCN_TX_LIST_CNT]; + int pcn_rx_prod; + int pcn_tx_prod; + int pcn_tx_cons; + int pcn_tx_cnt; +}; + +/* + * AMD PCI vendor ID. + */ +#define PCN_VENDORID 0x1022 + +/* + * AMD PCnet/PCI device IDs + */ +#define PCN_DEVICEID_PCNET 0x2000 +#define PCN_DEVICEID_HOME 0x2001 + +struct pcn_type { + u_int16_t pcn_vid; + u_int16_t pcn_did; + const char *pcn_name; +}; + +struct pcn_softc { + struct ifnet *pcn_ifp; + bus_space_handle_t pcn_bhandle; + bus_space_tag_t pcn_btag; + struct resource *pcn_res; + struct resource *pcn_irq; + void *pcn_intrhand; + device_t pcn_miibus; + u_int8_t pcn_link; + int8_t pcn_extphyaddr; + int8_t pcn_inst_10bt; + int pcn_if_flags; + int pcn_type; + struct pcn_list_data *pcn_ldata; + struct pcn_ring_data pcn_cdata; + struct callout pcn_stat_callout; + struct mtx pcn_mtx; +}; + +#define PCN_LOCK(_sc) mtx_lock(&(_sc)->pcn_mtx) +#define PCN_UNLOCK(_sc) mtx_unlock(&(_sc)->pcn_mtx) +#define PCN_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->pcn_mtx, MA_OWNED) + +#ifdef USE_EXPLICIT_BUSTAGS +#ifdef PCN_USEIOSPACE +#define PCN_BTAG(sc) bus_space_io +#else +#define PCN_BTAG(sc) bus_space_mem +#endif +#else +#define PCN_BTAG(sc) (sc)->pcn_btag +#endif + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(PCN_BTAG(sc), sc->pcn_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(PCN_BTAG(sc), sc->pcn_bhandle, reg) + +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(PCN_BTAG(sc), sc->pcn_bhandle, reg, val) + +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(PCN_BTAG(sc), sc->pcn_bhandle, reg) + +#define PCN_TIMEOUT 1000 +#define ETHER_ALIGN 2 +#define PCN_RXLEN 1536 +#define PCN_MIN_FRAMELEN 60 +#define PCN_INC(x, y) (x) = (x + 1) % y +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define PCN_PCI_VENDOR_ID 0x00 +#define PCN_PCI_DEVICE_ID 0x02 +#define PCN_PCI_COMMAND 0x04 +#define PCN_PCI_STATUS 0x06 +#define PCN_PCI_REVID 0x08 +#define PCN_PCI_CLASSCODE 0x09 +#define PCN_PCI_CACHELEN 0x0C +#define PCN_PCI_LATENCY_TIMER 0x0D +#define PCN_PCI_HEADER_TYPE 0x0E +#define PCN_PCI_LOIO 0x10 +#define PCN_PCI_LOMEM 0x14 +#define PCN_PCI_BIOSROM 0x30 +#define PCN_PCI_INTLINE 0x3C +#define PCN_PCI_INTPIN 0x3D +#define PCN_PCI_MINGNT 0x3E +#define PCN_PCI_MINLAT 0x3F +#define PCN_PCI_RESETOPT 0x48 +#define PCN_PCI_EEPROM_DATA 0x4C + +/* power management registers */ +#define PCN_PCI_CAPID 0x50 /* 8 bits */ +#define PCN_PCI_NEXTPTR 0x51 /* 8 bits */ +#define PCN_PCI_PWRMGMTCAP 0x52 /* 16 bits */ +#define PCN_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ + +#define PCN_PSTATE_MASK 0x0003 +#define PCN_PSTATE_D0 0x0000 +#define PCN_PSTATE_D1 0x0001 +#define PCN_PSTATE_D2 0x0002 +#define PCN_PSTATE_D3 0x0003 +#define PCN_PME_EN 0x0010 +#define PCN_PME_STATUS 0x8000 diff --git a/bsd_eth_drivers/if_re/Makefile.am b/bsd_eth_drivers/if_re/Makefile.am new file mode 100644 index 0000000..5c33e09 --- /dev/null +++ b/bsd_eth_drivers/if_re/Makefile.am @@ -0,0 +1,19 @@ +# $Id$ +AUTOMAKE_OPTIONS=foreign + +include $(top_srcdir)/rtems-pre.am + +libif_re_a_SOURCES = if_re.c + +##EXTRA_libif_re_a_SOURCES = + +libif_re_a_LIBADD = + +libif_re_a_DEPENDENCIES = $(libif_re_a_LIBADD) + +lib_LIBRARIES = libif_re.a + +AM_CPPFLAGS += -I$(srcdir) +AM_CPPFLAGS += -I$(srcdir)/../libbsdport -I../libbsdport -I../libbsdport/dummyheaders + +include $(top_srcdir)/rtems.am diff --git a/bsd_eth_drivers/if_re/if_re.c b/bsd_eth_drivers/if_re/if_re.c new file mode 100644 index 0000000..e39a52a --- /dev/null +++ b/bsd_eth_drivers/if_re/if_re.c @@ -0,0 +1,2890 @@ +/*- + * Copyright (c) 1997, 1998-2003 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __rtems__ +#include +#endif + +#include +__FBSDID("$FreeBSD: src/sys/dev/re/if_re.c,v 1.46.2.39.2.1 2008/10/02 02:57:24 kensmith Exp $"); + +/* + * RealTek 8139C+/8169/8169S/8110S/8168/8111/8101E PCI NIC driver + * + * Written by Bill Paul + * Senior Networking Software Engineer + * Wind River Systems + */ + +/* + * This driver is designed to support RealTek's next generation of + * 10/100 and 10/100/1000 PCI ethernet controllers. There are currently + * seven devices in this family: the RTL8139C+, the RTL8169, the RTL8169S, + * RTL8110S, the RTL8168, the RTL8111 and the RTL8101E. + * + * The 8139C+ is a 10/100 ethernet chip. It is backwards compatible + * with the older 8139 family, however it also supports a special + * C+ mode of operation that provides several new performance enhancing + * features. These include: + * + * o Descriptor based DMA mechanism. Each descriptor represents + * a single packet fragment. Data buffers may be aligned on + * any byte boundary. + * + * o 64-bit DMA + * + * o TCP/IP checksum offload for both RX and TX + * + * o High and normal priority transmit DMA rings + * + * o VLAN tag insertion and extraction + * + * o TCP large send (segmentation offload) + * + * Like the 8139, the 8139C+ also has a built-in 10/100 PHY. The C+ + * programming API is fairly straightforward. The RX filtering, EEPROM + * access and PHY access is the same as it is on the older 8139 series + * chips. + * + * The 8169 is a 64-bit 10/100/1000 gigabit ethernet MAC. It has almost the + * same programming API and feature set as the 8139C+ with the following + * differences and additions: + * + * o 1000Mbps mode + * + * o Jumbo frames + * + * o GMII and TBI ports/registers for interfacing with copper + * or fiber PHYs + * + * o RX and TX DMA rings can have up to 1024 descriptors + * (the 8139C+ allows a maximum of 64) + * + * o Slight differences in register layout from the 8139C+ + * + * The TX start and timer interrupt registers are at different locations + * on the 8169 than they are on the 8139C+. Also, the status word in the + * RX descriptor has a slightly different bit layout. The 8169 does not + * have a built-in PHY. Most reference boards use a Marvell 88E1000 'Alaska' + * copper gigE PHY. + * + * The 8169S/8110S 10/100/1000 devices have built-in copper gigE PHYs + * (the 'S' stands for 'single-chip'). These devices have the same + * programming API as the older 8169, but also have some vendor-specific + * registers for the on-board PHY. The 8110S is a LAN-on-motherboard + * part designed to be pin-compatible with the RealTek 8100 10/100 chip. + * + * This driver takes advantage of the RX and TX checksum offload and + * VLAN tag insertion/extraction features. It also implements TX + * interrupt moderation using the timer interrupt registers, which + * significantly reduces TX interrupt load. There is also support + * for jumbo frames, however the 8169/8169S/8110S can not transmit + * jumbo frames larger than 7440, so the max MTU possible with this + * driver is 7422 bytes. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +MODULE_DEPEND(re, pci, 1, 1, 1); +MODULE_DEPEND(re, ether, 1, 1, 1); +MODULE_DEPEND(re, miibus, 1, 1, 1); + +/* "device miibus" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifdef __rtems__ +#include +#endif + +/* + * Default to using PIO access for this driver. + */ +#define RE_USEIOSPACE + +#ifndef __rtems__ +#include +#else +#include "if_rlreg.h" +#endif + +#define RE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + +/* + * Various supported device vendors/types and their names. + */ +static struct rl_type re_devs[] = { + { DLINK_VENDORID, DLINK_DEVICEID_528T, RL_HWREV_8169S, + "D-Link DGE-528(T) Gigabit Ethernet Adapter" }, + { DLINK_VENDORID, DLINK_DEVICEID_528T, RL_HWREV_8169_8110SB, + "D-Link DGE-528(T) Rev.B1 Gigabit Ethernet Adapter" }, + { RT_VENDORID, RT_DEVICEID_8139, RL_HWREV_8139CPLUS, + "RealTek 8139C+ 10/100BaseTX" }, + { RT_VENDORID, RT_DEVICEID_8101E, RL_HWREV_8101E, + "RealTek 8101E PCIe 10/100baseTX" }, + { RT_VENDORID, RT_DEVICEID_8168, RL_HWREV_8168_SPIN1, + "RealTek 8168/8111B PCIe Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8168, RL_HWREV_8168_SPIN2, + "RealTek 8168/8111B PCIe Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8168, RL_HWREV_8168_SPIN3, + "RealTek 8168/8111B PCIe Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169, + "RealTek 8169 Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169S, + "RealTek 8169S Single-chip Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169_8110SB, + "RealTek 8169SB/8110SB Single-chip Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8169_8110SC, + "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8169SC, RL_HWREV_8169_8110SC, + "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" }, + { RT_VENDORID, RT_DEVICEID_8169, RL_HWREV_8110S, + "RealTek 8110S Single-chip Gigabit Ethernet" }, + { COREGA_VENDORID, COREGA_DEVICEID_CGLAPCIGT, RL_HWREV_8169S, + "Corega CG-LAPCIGT (RTL8169S) Gigabit Ethernet" }, + { LINKSYS_VENDORID, LINKSYS_DEVICEID_EG1032, RL_HWREV_8169S, + "Linksys EG1032 (RTL8169S) Gigabit Ethernet" }, + { USR_VENDORID, USR_DEVICEID_997902, RL_HWREV_8169S, + "US Robotics 997902 (RTL8169S) Gigabit Ethernet" }, + { 0, 0, 0, NULL } +}; + +static struct rl_hwrev re_hwrevs[] = { + { RL_HWREV_8139, RL_8139, "" }, + { RL_HWREV_8139A, RL_8139, "A" }, + { RL_HWREV_8139AG, RL_8139, "A-G" }, + { RL_HWREV_8139B, RL_8139, "B" }, + { RL_HWREV_8130, RL_8139, "8130" }, + { RL_HWREV_8139C, RL_8139, "C" }, + { RL_HWREV_8139D, RL_8139, "8139D/8100B/8100C" }, + { RL_HWREV_8139CPLUS, RL_8139CPLUS, "C+"}, + { RL_HWREV_8168_SPIN1, RL_8169, "8168"}, + { RL_HWREV_8169, RL_8169, "8169"}, + { RL_HWREV_8169S, RL_8169, "8169S"}, + { RL_HWREV_8110S, RL_8169, "8110S"}, + { RL_HWREV_8169_8110SB, RL_8169, "8169SB"}, + { RL_HWREV_8169_8110SC, RL_8169, "8169SC"}, + { RL_HWREV_8100, RL_8139, "8100"}, + { RL_HWREV_8101, RL_8139, "8101"}, + { RL_HWREV_8100E, RL_8169, "8100E"}, + { RL_HWREV_8101E, RL_8169, "8101E"}, + { RL_HWREV_8168_SPIN2, RL_8169, "8168"}, + { RL_HWREV_8168_SPIN3, RL_8169, "8168"}, + { 0, 0, NULL } +}; + +static int re_probe (device_t); +static int re_attach (device_t); +static int re_detach (device_t); + +static int re_encap (struct rl_softc *, struct mbuf **, int *); + +static void re_dma_map_addr (void *, bus_dma_segment_t *, int, int); +static void re_dma_map_desc (void *, bus_dma_segment_t *, int, + bus_size_t, int); +static int re_allocmem (device_t, struct rl_softc *); +static int re_newbuf (struct rl_softc *, int, struct mbuf *); +static int re_rx_list_init (struct rl_softc *); +static int re_tx_list_init (struct rl_softc *); +#ifdef RE_FIXUP_RX +static __inline void re_fixup_rx + (struct mbuf *); +#endif +static int re_rxeof (struct rl_softc *); +static void re_txeof (struct rl_softc *); +#ifdef DEVICE_POLLING +static void re_poll (struct ifnet *, enum poll_cmd, int); +static void re_poll_locked (struct ifnet *, enum poll_cmd, int); +#endif +static void re_intr (void *); +static void re_tick (void *); +static void re_tx_task (void *, int); +static void re_int_task (void *, int); +static void re_start (struct ifnet *); +#ifndef __rtems__ +static int re_ioctl(struct ifnet *, u_long, caddr_t); +#else +static int re_ioctl(struct ifnet *, ioctl_command_t, caddr_t); +#endif +static void re_init (void *); +static void re_init_locked (struct rl_softc *); +static void re_stop (struct rl_softc *); +static void re_watchdog (struct rl_softc *); +#ifndef __rtems__ +static int re_suspend (device_t); +static int re_resume (device_t); +#endif +static void re_shutdown (device_t); +#ifndef __rtems__ +static int re_ifmedia_upd (struct ifnet *); +static void re_ifmedia_sts (struct ifnet *, struct ifmediareq *); +#endif + +static void re_eeprom_putbyte (struct rl_softc *, int); +static void re_eeprom_getword (struct rl_softc *, int, u_int16_t *); +static void re_read_eeprom (struct rl_softc *, caddr_t, int, int); +#ifndef __rtems__ +static int re_gmii_readreg (device_t, int, int); +#endif +static int re_gmii_writereg (device_t, int, int, int); + +#ifndef __rtems__ +static int re_miibus_readreg (device_t, int, int); +static int re_miibus_writereg (device_t, int, int, int); +static void re_miibus_statchg (device_t); +#endif + +static void re_setmulti (struct rl_softc *); +static void re_reset (struct rl_softc *); + +#ifdef RE_DIAG +static int re_diag (struct rl_softc *); +#endif + +#ifdef RE_USEIOSPACE +#define RL_RES SYS_RES_IOPORT +#define RL_RID RL_PCI_LOIO +#else +#define RL_RES SYS_RES_MEMORY +#define RL_RID RL_PCI_LOMEM +#endif + +#ifndef __rtems__ +static device_method_t re_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, re_probe), + DEVMETHOD(device_attach, re_attach), + DEVMETHOD(device_detach, re_detach), + DEVMETHOD(device_suspend, re_suspend), + DEVMETHOD(device_resume, re_resume), + DEVMETHOD(device_shutdown, re_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, re_miibus_readreg), + DEVMETHOD(miibus_writereg, re_miibus_writereg), + DEVMETHOD(miibus_statchg, re_miibus_statchg), + + { 0, 0 } +}; + +static driver_t re_driver = { + "re", + re_methods, + sizeof(struct rl_softc) +}; + +static devclass_t re_devclass; + +DRIVER_MODULE(re, pci, re_driver, re_devclass, 0, 0); +DRIVER_MODULE(re, cardbus, re_driver, re_devclass, 0, 0); +DRIVER_MODULE(miibus, re, miibus_driver, miibus_devclass, 0, 0); +#else + +static int +re_irq_check_dis(device_t d) +{ + // struct re_softc *sc = device_get_softc(d); + printk( "check_dis\n" ); + return 0; +} + +static void +re_irq_en(device_t d) +{ + // struct re_softc *sc = device_get_softc(d); + /* This can be called from IRQ context -- since all register accesses + * involve RAP we must take care to preserve it across this routine! + */ + printk( "irq_en\n" ); +} + + +static device_method_t re_methods = { + probe: re_probe, + attach: re_attach, + shutdown: re_shutdown, + detach: re_detach, + irq_check_dis: re_irq_check_dis, + irq_en: re_irq_en, +}; + +driver_t libbsdport_re_driver = { + "re", + &re_methods, + DEV_TYPE_PCI, + sizeof(struct rl_softc) +}; + + +#endif + +#define EE_SET(x) \ + CSR_WRITE_1(sc, RL_EECMD, \ + CSR_READ_1(sc, RL_EECMD) | x) + +#define EE_CLR(x) \ + CSR_WRITE_1(sc, RL_EECMD, \ + CSR_READ_1(sc, RL_EECMD) & ~x) + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void +re_eeprom_putbyte(sc, addr) + struct rl_softc *sc; + int addr; +{ + register int d, i; + + d = addr | (RL_9346_READ << sc->rl_eewidth); + + /* + * Feed in each bit and strobe the clock. + */ + + for (i = 1 << (sc->rl_eewidth + 3); i; i >>= 1) { + if (d & i) { + EE_SET(RL_EE_DATAIN); + } else { + EE_CLR(RL_EE_DATAIN); + } + DELAY(100); + EE_SET(RL_EE_CLK); + DELAY(150); + EE_CLR(RL_EE_CLK); + DELAY(100); + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void +re_eeprom_getword(sc, addr, dest) + struct rl_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int16_t word = 0; + + /* + * Send address of word we want to read. + */ + re_eeprom_putbyte(sc, addr); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + EE_SET(RL_EE_CLK); + DELAY(100); + if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) + word |= i; + EE_CLR(RL_EE_CLK); + DELAY(100); + } + + *dest = word; + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void +re_read_eeprom(sc, dest, off, cnt) + struct rl_softc *sc; + caddr_t dest; + int off; + int cnt; +{ + int i; + u_int16_t word = 0, *ptr; + + CSR_SETBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM); + + DELAY(100); + + for (i = 0; i < cnt; i++) { + CSR_SETBIT_1(sc, RL_EECMD, RL_EE_SEL); + re_eeprom_getword(sc, off + i, &word); + CSR_CLRBIT_1(sc, RL_EECMD, RL_EE_SEL); + ptr = (u_int16_t *)(dest + (i * 2)); + *ptr = word; + } + + CSR_CLRBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM); + + return; +} + +#ifndef __rtems__ +static int +re_gmii_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct rl_softc *sc; + u_int32_t rval; + int i; + + if (phy != 1) + return (0); + + sc = device_get_softc(dev); + + /* Let the rgephy driver read the GMEDIASTAT register */ + + if (reg == RL_GMEDIASTAT) { + rval = CSR_READ_1(sc, RL_GMEDIASTAT); + return (rval); + } + + CSR_WRITE_4(sc, RL_PHYAR, reg << 16); + DELAY(1000); + + for (i = 0; i < RL_TIMEOUT; i++) { + rval = CSR_READ_4(sc, RL_PHYAR); + if (rval & RL_PHYAR_BUSY) + break; + DELAY(100); + } + + if (i == RL_TIMEOUT) { + device_printf(sc->rl_dev, "PHY read failed\n"); + return (0); + } + + return (rval & RL_PHYAR_PHYDATA); +} +#endif + +static int +re_gmii_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct rl_softc *sc; + u_int32_t rval; + int i; + + sc = device_get_softc(dev); + + CSR_WRITE_4(sc, RL_PHYAR, (reg << 16) | + (data & RL_PHYAR_PHYDATA) | RL_PHYAR_BUSY); + DELAY(1000); + + for (i = 0; i < RL_TIMEOUT; i++) { + rval = CSR_READ_4(sc, RL_PHYAR); + if (!(rval & RL_PHYAR_BUSY)) + break; + DELAY(100); + } + + if (i == RL_TIMEOUT) { + device_printf(sc->rl_dev, "PHY write failed\n"); + return (0); + } + + return (0); +} + +#ifndef __rtems__ +static int +re_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct rl_softc *sc; + u_int16_t rval = 0; + u_int16_t re8139_reg = 0; + + sc = device_get_softc(dev); + + if (sc->rl_type == RL_8169) { + rval = re_gmii_readreg(dev, phy, reg); + return (rval); + } + + /* Pretend the internal PHY is only at address 0 */ + if (phy) { + return (0); + } + switch (reg) { + case MII_BMCR: + re8139_reg = RL_BMCR; + break; + case MII_BMSR: + re8139_reg = RL_BMSR; + break; + case MII_ANAR: + re8139_reg = RL_ANAR; + break; + case MII_ANER: + re8139_reg = RL_ANER; + break; + case MII_ANLPAR: + re8139_reg = RL_LPAR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + return (0); + /* + * Allow the rlphy driver to read the media status + * register. If we have a link partner which does not + * support NWAY, this is the register which will tell + * us the results of parallel detection. + */ + case RL_MEDIASTAT: + rval = CSR_READ_1(sc, RL_MEDIASTAT); + return (rval); + default: + device_printf(sc->rl_dev, "bad phy register\n"); + return (0); + } + rval = CSR_READ_2(sc, re8139_reg); + if (sc->rl_type == RL_8139CPLUS && re8139_reg == RL_BMCR) { + /* 8139C+ has different bit layout. */ + rval &= ~(BMCR_LOOP | BMCR_ISO); + } + return (rval); +} +#endif + +#ifndef __rtems__ +static int +re_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct rl_softc *sc; + u_int16_t re8139_reg = 0; + int rval = 0; + + sc = device_get_softc(dev); + + if (sc->rl_type == RL_8169) { + rval = re_gmii_writereg(dev, phy, reg, data); + return (rval); + } + + /* Pretend the internal PHY is only at address 0 */ + if (phy) + return (0); + + switch (reg) { + case MII_BMCR: + re8139_reg = RL_BMCR; + if (sc->rl_type == RL_8139CPLUS) { + /* 8139C+ has different bit layout. */ + data &= ~(BMCR_LOOP | BMCR_ISO); + } + break; + case MII_BMSR: + re8139_reg = RL_BMSR; + break; + case MII_ANAR: + re8139_reg = RL_ANAR; + break; + case MII_ANER: + re8139_reg = RL_ANER; + break; + case MII_ANLPAR: + re8139_reg = RL_LPAR; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + return (0); + break; + default: + device_printf(sc->rl_dev, "bad phy register\n"); + return (0); + } + CSR_WRITE_2(sc, re8139_reg, data); + return (0); +} +#endif + +#ifndef __rtems__ +static void +re_miibus_statchg(dev) + device_t dev; +{ + +} +#endif + +/* + * Program the 64-bit multicast hash filter. + */ +static void +re_setmulti(sc) + struct rl_softc *sc; +{ + struct ifnet *ifp; +#ifndef __rtems__ + int h = 0; + struct ifmultiaddr *ifma; +#endif + u_int32_t hashes[2] = { 0, 0 }; + u_int32_t rxfilt; + int mcnt = 0; + u_int32_t hwrev; + + RL_LOCK_ASSERT(sc); + + ifp = sc->rl_ifp; + + + rxfilt = CSR_READ_4(sc, RL_RXCFG); + rxfilt &= ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_MULTI); + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + if (ifp->if_flags & IFF_PROMISC) + rxfilt |= RL_RXCFG_RX_ALLPHYS; + /* + * Unlike other hardwares, we have to explicitly set + * RL_RXCFG_RX_MULTI to receive multicast frames in + * promiscuous mode. + */ + rxfilt |= RL_RXCFG_RX_MULTI; + CSR_WRITE_4(sc, RL_RXCFG, rxfilt); + CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF); + CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF); + return; + } + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, RL_MAR0, 0); + CSR_WRITE_4(sc, RL_MAR4, 0); + + /* now program new ones */ +#ifndef __rtems__ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + IF_ADDR_UNLOCK(ifp); +#endif + + if (mcnt) + rxfilt |= RL_RXCFG_RX_MULTI; + else + rxfilt &= ~RL_RXCFG_RX_MULTI; + + CSR_WRITE_4(sc, RL_RXCFG, rxfilt); + + /* + * For some unfathomable reason, RealTek decided to reverse + * the order of the multicast hash registers in the PCI Express + * parts. This means we have to write the hash pattern in reverse + * order for those devices. + */ + + hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV; + + switch (hwrev) { + case RL_HWREV_8100E: + case RL_HWREV_8101E: + case RL_HWREV_8168_SPIN1: + case RL_HWREV_8168_SPIN2: + case RL_HWREV_8168_SPIN3: + CSR_WRITE_4(sc, RL_MAR0, bswap32(hashes[1])); + CSR_WRITE_4(sc, RL_MAR4, bswap32(hashes[0])); + break; + default: + CSR_WRITE_4(sc, RL_MAR0, hashes[0]); + CSR_WRITE_4(sc, RL_MAR4, hashes[1]); + break; + } +} + +static void +re_reset(sc) + struct rl_softc *sc; +{ + register int i; + + RL_LOCK_ASSERT(sc); + + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); + + for (i = 0; i < RL_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) + break; + } + if (i == RL_TIMEOUT) + device_printf(sc->rl_dev, "reset never completed!\n"); + + CSR_WRITE_1(sc, 0x82, 1); +} + +#ifdef RE_DIAG + +/* + * The following routine is designed to test for a defect on some + * 32-bit 8169 cards. Some of these NICs have the REQ64# and ACK64# + * lines connected to the bus, however for a 32-bit only card, they + * should be pulled high. The result of this defect is that the + * NIC will not work right if you plug it into a 64-bit slot: DMA + * operations will be done with 64-bit transfers, which will fail + * because the 64-bit data lines aren't connected. + * + * There's no way to work around this (short of talking a soldering + * iron to the board), however we can detect it. The method we use + * here is to put the NIC into digital loopback mode, set the receiver + * to promiscuous mode, and then try to send a frame. We then compare + * the frame data we sent to what was received. If the data matches, + * then the NIC is working correctly, otherwise we know the user has + * a defective NIC which has been mistakenly plugged into a 64-bit PCI + * slot. In the latter case, there's no way the NIC can work correctly, + * so we print out a message on the console and abort the device attach. + */ + +static int +re_diag(sc) + struct rl_softc *sc; +{ + struct ifnet *ifp = sc->rl_ifp; + struct mbuf *m0; + struct ether_header *eh; + struct rl_desc *cur_rx; + u_int16_t status; + u_int32_t rxstat; + int total_len, i, error = 0, phyaddr; + u_int8_t dst[] = { 0x00, 'h', 'e', 'l', 'l', 'o' }; + u_int8_t src[] = { 0x00, 'w', 'o', 'r', 'l', 'd' }; + + /* Allocate a single mbuf */ + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 == NULL) + return (ENOBUFS); + + RL_LOCK(sc); + + /* + * Initialize the NIC in test mode. This sets the chip up + * so that it can send and receive frames, but performs the + * following special functions: + * - Puts receiver in promiscuous mode + * - Enables digital loopback mode + * - Leaves interrupts turned off + */ + + ifp->if_flags |= IFF_PROMISC; + sc->rl_testmode = 1; + re_reset(sc); + re_init_locked(sc); + sc->rl_link = 1; + if (sc->rl_type == RL_8169) + phyaddr = 1; + else + phyaddr = 0; + + re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_RESET); + for (i = 0; i < RL_TIMEOUT; i++) { + status = re_miibus_readreg(sc->rl_dev, phyaddr, MII_BMCR); + if (!(status & BMCR_RESET)) + break; + } + + re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_LOOP); + CSR_WRITE_2(sc, RL_ISR, RL_INTRS); + + DELAY(100000); + + /* Put some data in the mbuf */ + + eh = mtod(m0, struct ether_header *); + bcopy ((char *)&dst, eh->ether_dhost, ETHER_ADDR_LEN); + bcopy ((char *)&src, eh->ether_shost, ETHER_ADDR_LEN); + eh->ether_type = htons(ETHERTYPE_IP); + m0->m_pkthdr.len = m0->m_len = ETHER_MIN_LEN - ETHER_CRC_LEN; + + /* + * Queue the packet, start transmission. + * Note: IF_HANDOFF() ultimately calls re_start() for us. + */ + + CSR_WRITE_2(sc, RL_ISR, 0xFFFF); + RL_UNLOCK(sc); + /* XXX: re_diag must not be called when in ALTQ mode */ + IF_HANDOFF(&ifp->if_snd, m0, ifp); + RL_LOCK(sc); + m0 = NULL; + + /* Wait for it to propagate through the chip */ + + DELAY(100000); + for (i = 0; i < RL_TIMEOUT; i++) { + status = CSR_READ_2(sc, RL_ISR); + CSR_WRITE_2(sc, RL_ISR, status); + if ((status & (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK)) == + (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK)) + break; + DELAY(10); + } + + if (i == RL_TIMEOUT) { + device_printf(sc->rl_dev, + "diagnostic failed, failed to receive packet in" + " loopback mode\n"); + error = EIO; + goto done; + } + + /* + * The packet should have been dumped into the first + * entry in the RX DMA ring. Grab it from there. + */ + + bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_sync(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[0], + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[0]); + + m0 = sc->rl_ldata.rl_rx_mbuf[0]; + sc->rl_ldata.rl_rx_mbuf[0] = NULL; + eh = mtod(m0, struct ether_header *); + + cur_rx = &sc->rl_ldata.rl_rx_list[0]; + total_len = RL_RXBYTES(cur_rx); + rxstat = le32toh(cur_rx->rl_cmdstat); + + if (total_len != ETHER_MIN_LEN) { + device_printf(sc->rl_dev, + "diagnostic failed, received short packet\n"); + error = EIO; + goto done; + } + + /* Test that the received packet data matches what we sent. */ + + if (bcmp((char *)&eh->ether_dhost, (char *)&dst, ETHER_ADDR_LEN) || + bcmp((char *)&eh->ether_shost, (char *)&src, ETHER_ADDR_LEN) || + ntohs(eh->ether_type) != ETHERTYPE_IP) { + device_printf(sc->rl_dev, "WARNING, DMA FAILURE!\n"); + device_printf(sc->rl_dev, "expected TX data: %6D/%6D/0x%x\n", + dst, ":", src, ":", ETHERTYPE_IP); + device_printf(sc->rl_dev, "received RX data: %6D/%6D/0x%x\n", + eh->ether_dhost, ":", eh->ether_shost, ":", + ntohs(eh->ether_type)); + device_printf(sc->rl_dev, "You may have a defective 32-bit " + "NIC plugged into a 64-bit PCI slot.\n"); + device_printf(sc->rl_dev, "Please re-install the NIC in a " + "32-bit slot for proper operation.\n"); + device_printf(sc->rl_dev, "Read the re(4) man page for more " + "details.\n"); + error = EIO; + } + +done: + /* Turn interface off, release resources */ + + sc->rl_testmode = 0; + sc->rl_link = 0; + ifp->if_flags &= ~IFF_PROMISC; + re_stop(sc); + if (m0 != NULL) + m_freem(m0); + + RL_UNLOCK(sc); + + return (error); +} + +#endif + +/* + * Probe for a RealTek 8139C+/8169/8110 chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int +re_probe(dev) + device_t dev; +{ + struct rl_type *t; + struct rl_softc *sc; + int rid; + u_int32_t hwrev; + + t = re_devs; + sc = device_get_softc(dev); + + while (t->rl_name != NULL) { + if ((pci_get_vendor(dev) == t->rl_vid) && + (pci_get_device(dev) == t->rl_did)) { + /* + * Only attach to rev. 3 of the Linksys EG1032 adapter. + * Rev. 2 i supported by sk(4). + */ + if ((t->rl_vid == LINKSYS_VENDORID) && + (t->rl_did == LINKSYS_DEVICEID_EG1032) && + (pci_get_subdevice(dev) != + LINKSYS_SUBDEVICE_EG1032_REV3)) { + t++; + continue; + } + + /* + * Temporarily map the I/O space + * so we can read the chip ID register. + */ + rid = RL_RID; + sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid, + RF_ACTIVE); + if (sc->rl_res == NULL) { + device_printf(dev, + "couldn't map ports/memory\n"); + return (ENXIO); + } + sc->rl_btag = rman_get_bustag(sc->rl_res); + sc->rl_bhandle = rman_get_bushandle(sc->rl_res); + hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV; + bus_release_resource(dev, RL_RES, + RL_RID, sc->rl_res); + if (t->rl_basetype == hwrev) { + device_set_desc(dev, t->rl_name); + return (BUS_PROBE_DEFAULT); + } + } + t++; + } + + return (ENXIO); +} + +/* + * This routine takes the segment list provided as the result of + * a bus_dma_map_load() operation and assigns the addresses/lengths + * to RealTek DMA descriptors. This can be called either by the RX + * code or the TX code. In the RX case, we'll probably wind up mapping + * at most one segment. For the TX case, there could be any number of + * segments since TX packets may span multiple mbufs. In either case, + * if the number of segments is larger than the rl_maxsegs limit + * specified by the caller, we abort the mapping operation. Sadly, + * whoever designed the buffer mapping API did not provide a way to + * return an error from here, so we have to fake it a bit. + */ + +static void +re_dma_map_desc(arg, segs, nseg, mapsize, error) + void *arg; + bus_dma_segment_t *segs; + int nseg; + bus_size_t mapsize; + int error; +{ + struct rl_dmaload_arg *ctx; + struct rl_desc *d = NULL; + int i = 0, idx; + u_int32_t cmdstat; + int totlen = 0; + + if (error) + return; + + ctx = arg; + + /* Signal error to caller if there's too many segments */ + if (nseg > ctx->rl_maxsegs) { + ctx->rl_maxsegs = 0; + return; + } + + /* + * Map the segment array into descriptors. Note that we set the + * start-of-frame and end-of-frame markers for either TX or RX, but + * they really only have meaning in the TX case. (In the RX case, + * it's the chip that tells us where packets begin and end.) + * We also keep track of the end of the ring and set the + * end-of-ring bits as needed, and we set the ownership bits + * in all except the very first descriptor. (The caller will + * set this descriptor later when it start transmission or + * reception.) + */ + idx = ctx->rl_idx; + for (;;) { + d = &ctx->rl_ring[idx]; + if (le32toh(d->rl_cmdstat) & RL_RDESC_STAT_OWN) { + ctx->rl_maxsegs = 0; + return; + } + cmdstat = segs[i].ds_len; + totlen += segs[i].ds_len; + d->rl_vlanctl = 0; + d->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[i].ds_addr)); + d->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[i].ds_addr)); + if (i == 0) + cmdstat |= RL_TDESC_CMD_SOF; + else + cmdstat |= RL_TDESC_CMD_OWN; + if (idx == (RL_RX_DESC_CNT - 1)) + cmdstat |= RL_TDESC_CMD_EOR; + d->rl_cmdstat = htole32(cmdstat | ctx->rl_flags); + i++; + if (i == nseg) + break; + RL_DESC_INC(idx); + } + + d->rl_cmdstat |= htole32(RL_TDESC_CMD_EOF); + ctx->rl_maxsegs = nseg; + ctx->rl_idx = idx; +} + +/* + * Map a single buffer address. + */ + +static void +re_dma_map_addr(arg, segs, nseg, error) + void *arg; + bus_dma_segment_t *segs; + int nseg; + int error; +{ + bus_addr_t *addr; + + if (error) + return; + + KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); + addr = arg; + *addr = segs->ds_addr; +} + +static int +re_allocmem(dev, sc) + device_t dev; + struct rl_softc *sc; +{ + int error; + int nseg; + int i; + + /* + * Allocate map for RX mbufs. + */ + nseg = 32; + error = bus_dma_tag_create(sc->rl_parent_tag, ETHER_ALIGN, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, + NULL, MCLBYTES * nseg, nseg, MCLBYTES, BUS_DMA_ALLOCNOW, + NULL, NULL, &sc->rl_ldata.rl_mtag); + if (error) { + device_printf(dev, "could not allocate dma tag\n"); + return (ENOMEM); + } + + /* + * Allocate map for TX descriptor list. + */ + error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN, + 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, + NULL, RL_TX_LIST_SZ, 1, RL_TX_LIST_SZ, 0, + NULL, NULL, &sc->rl_ldata.rl_tx_list_tag); + if (error) { + device_printf(dev, "could not allocate dma tag\n"); + return (ENOMEM); + } + + /* Allocate DMA'able memory for the TX ring */ + + error = bus_dmamem_alloc(sc->rl_ldata.rl_tx_list_tag, + (void **)&sc->rl_ldata.rl_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, + &sc->rl_ldata.rl_tx_list_map); + if (error) + return (ENOMEM); + + /* Load the map for the TX ring. */ + + error = bus_dmamap_load(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list_map, (caddr_t) sc->rl_ldata.rl_tx_list, + RL_TX_LIST_SZ, re_dma_map_addr, + &sc->rl_ldata.rl_tx_list_addr, BUS_DMA_NOWAIT); + + /* Create DMA maps for TX buffers */ + + for (i = 0; i < RL_TX_DESC_CNT; i++) { + error = bus_dmamap_create(sc->rl_ldata.rl_mtag, 0, + &sc->rl_ldata.rl_tx_dmamap[i]); + if (error) { + device_printf(dev, "can't create DMA map for TX\n"); + return (ENOMEM); + } + } + + /* + * Allocate map for RX descriptor list. + */ + error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN, + 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, + NULL, RL_RX_LIST_SZ, 1, RL_RX_LIST_SZ, 0, + NULL, NULL, &sc->rl_ldata.rl_rx_list_tag); + if (error) { + device_printf(dev, "could not allocate dma tag\n"); + return (ENOMEM); + } + + /* Allocate DMA'able memory for the RX ring */ + + error = bus_dmamem_alloc(sc->rl_ldata.rl_rx_list_tag, + (void **)&sc->rl_ldata.rl_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, + &sc->rl_ldata.rl_rx_list_map); + if (error) + return (ENOMEM); + + /* Load the map for the RX ring. */ + + error = bus_dmamap_load(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list_map, (caddr_t) sc->rl_ldata.rl_rx_list, + RL_RX_LIST_SZ, re_dma_map_addr, + &sc->rl_ldata.rl_rx_list_addr, BUS_DMA_NOWAIT); + + /* Create DMA maps for RX buffers */ + + for (i = 0; i < RL_RX_DESC_CNT; i++) { + error = bus_dmamap_create(sc->rl_ldata.rl_mtag, 0, + &sc->rl_ldata.rl_rx_dmamap[i]); + if (error) { + device_printf(dev, "can't create DMA map for RX\n"); + return (ENOMEM); + } + } + + return (0); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +re_attach(dev) + device_t dev; +{ + u_char eaddr[ETHER_ADDR_LEN]; + u_int16_t as[ETHER_ADDR_LEN / 2]; + struct rl_softc *sc; + struct ifnet *ifp; + struct rl_hwrev *hw_rev; + int hwrev; + u_int16_t re_did = 0; + int error = 0, rid, i; + + sc = device_get_softc(dev); + sc->rl_dev = dev; + + mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->rl_stat_callout, &sc->rl_mtx, 0); + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + + rid = RL_RID; + sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid, + RF_ACTIVE); + + if (sc->rl_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + error = ENXIO; + goto fail; + } + + sc->rl_btag = rman_get_bustag(sc->rl_res); + sc->rl_bhandle = rman_get_bushandle(sc->rl_res); + + /* Allocate interrupt */ + rid = 0; + sc->rl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->rl_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } + + /* Reset the adapter. */ + RL_LOCK(sc); + re_reset(sc); + RL_UNLOCK(sc); + + hw_rev = re_hwrevs; + hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV; + while (hw_rev->rl_desc != NULL) { + if (hw_rev->rl_rev == hwrev) { + sc->rl_type = hw_rev->rl_type; + break; + } + hw_rev++; + } + + sc->rl_eewidth = RL_9356_ADDR_LEN; + re_read_eeprom(sc, (caddr_t)&re_did, 0, 1); + if (re_did != 0x8129) + sc->rl_eewidth = RL_9346_ADDR_LEN; + + /* + * Get station address from the EEPROM. + */ + re_read_eeprom(sc, (caddr_t)as, RL_EE_EADDR, 3); + for (i = 0; i < ETHER_ADDR_LEN / 2; i++) + as[i] = le16toh(as[i]); + bcopy(as, eaddr, sizeof(eaddr)); + + if (sc->rl_type == RL_8169) { + /* Set RX length mask */ + sc->rl_rxlenmask = RL_RDESC_STAT_GFRAGLEN; + sc->rl_txstart = RL_GTXSTART; + } else { + /* Set RX length mask */ + sc->rl_rxlenmask = RL_RDESC_STAT_FRAGLEN; + sc->rl_txstart = RL_TXSTART; + } + + /* + * Allocate the parent bus DMA tag appropriate for PCI. + */ +#define RL_NSEG_NEW 32 + error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MAXBSIZE, RL_NSEG_NEW, BUS_SPACE_MAXSIZE_32BIT, 0, + NULL, NULL, &sc->rl_parent_tag); + if (error) + goto fail; + + error = re_allocmem(dev, sc); + + if (error) + goto fail; + + ifp = sc->rl_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not if_alloc()\n"); + error = ENOSPC; + goto fail; + } + + /* Do MII setup */ +#ifndef __rtems__ + if (mii_phy_probe(dev, &sc->rl_miibus, + re_ifmedia_upd, re_ifmedia_sts)) { + device_printf(dev, "MII without any phy!\n"); + error = ENXIO; + goto fail; + } +#endif + + /* Take PHY out of power down mode. */ + if (sc->rl_type == RL_8169) { + uint32_t rev; + + rev = CSR_READ_4(sc, RL_TXCFG); + /* HWVERID 0, 1 and 2 : bit26-30, bit23 */ + rev &= 0x7c800000; + if (rev != 0) { + /* RTL8169S single chip */ + switch (rev) { + case RL_HWREV_8169_8110SB: + case RL_HWREV_8169_8110SC: + case RL_HWREV_8168_SPIN2: + case RL_HWREV_8168_SPIN3: + re_gmii_writereg(dev, 1, 0x1f, 0); + re_gmii_writereg(dev, 1, 0x0e, 0); + break; + default: + break; + } + } + } + + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = re_ioctl; + ifp->if_start = re_start; +#ifndef __rtems__ + ifp->if_hwassist = RE_CSUM_FEATURES; + ifp->if_capabilities = IFCAP_HWCSUM; + ifp->if_capenable = ifp->if_capabilities; +#endif + ifp->if_init = re_init; + IFQ_SET_MAXLEN(&ifp->if_snd, RL_IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = RL_IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + TASK_INIT(&sc->rl_txtask, 1, re_tx_task, ifp); + TASK_INIT(&sc->rl_inttask, 0, re_int_task, sc); + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, eaddr); + +#ifndef __rtems__ + /* VLAN capability setup */ + ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING; +#ifdef IFCAP_VLAN_HWCSUM + if (ifp->if_capabilities & IFCAP_HWCSUM) + ifp->if_capabilities |= IFCAP_VLAN_HWCSUM; +#endif + ifp->if_capenable = ifp->if_capabilities; +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif + /* + * Tell the upper layer(s) we support long frames. + * Must appear after the call to ether_ifattach() because + * ether_ifattach() sets ifi_hdrlen to the default value. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); +#endif + +#ifdef RE_DIAG + /* + * Perform hardware diagnostic on the original RTL8169. + * Some 32-bit cards were incorrectly wired and would + * malfunction if plugged into a 64-bit slot. + */ + + if (hwrev == RL_HWREV_8169) { + error = re_diag(sc); + if (error) { + device_printf(dev, + "attach aborted due to hardware diag failure\n"); + ether_ifdetach(ifp); + goto fail; + } + } +#endif + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->rl_irq, INTR_TYPE_NET | INTR_MPSAFE | + INTR_FAST, NULL, re_intr, sc, &sc->rl_intrhand); + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + } + +fail: + + if (error) + re_detach(dev); + + return (error); +} + +/* + * Shutdown hardware and free up resources. This can be called any + * time after the mutex has been initialized. It is called in both + * the error case in attach and the normal detach case so it needs + * to be careful about only freeing resources that have actually been + * allocated. + */ +static int +re_detach(dev) + device_t dev; +{ + struct rl_softc *sc; + struct ifnet *ifp; + int i; + + sc = device_get_softc(dev); + ifp = sc->rl_ifp; + KASSERT(mtx_initialized(&sc->rl_mtx), ("re mutex not initialized")); + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(ifp); +#endif + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + RL_LOCK(sc); +#if 0 + sc->suspended = 1; +#endif + re_stop(sc); + RL_UNLOCK(sc); + callout_drain(&sc->rl_stat_callout); + taskqueue_drain(taskqueue_fast, &sc->rl_inttask); + taskqueue_drain(taskqueue_fast, &sc->rl_txtask); + /* + * Force off the IFF_UP flag here, in case someone + * still had a BPF descriptor attached to this + * interface. If they do, ether_ifdetach() will cause + * the BPF code to try and clear the promisc mode + * flag, which will bubble down to re_ioctl(), + * which will try to call re_init() again. This will + * turn the NIC back on and restart the MII ticker, + * which will panic the system when the kernel tries + * to invoke the re_tick() function that isn't there + * anymore. + */ + ifp->if_flags &= ~IFF_UP; + ether_ifdetach(ifp); + } + if (sc->rl_miibus) + device_delete_child(dev, sc->rl_miibus); + bus_generic_detach(dev); + + /* + * The rest is resource deallocation, so we should already be + * stopped here. + */ + + if (sc->rl_intrhand) + bus_teardown_intr(dev, sc->rl_irq, sc->rl_intrhand); + if (ifp != NULL) + if_free(ifp); + if (sc->rl_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq); + if (sc->rl_res) + bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); + + /* Unload and free the RX DMA ring memory and map */ + + if (sc->rl_ldata.rl_rx_list_tag) { + bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list_map); + bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list, + sc->rl_ldata.rl_rx_list_map); + bus_dma_tag_destroy(sc->rl_ldata.rl_rx_list_tag); + } + + /* Unload and free the TX DMA ring memory and map */ + + if (sc->rl_ldata.rl_tx_list_tag) { + bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list_map); + bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list, + sc->rl_ldata.rl_tx_list_map); + bus_dma_tag_destroy(sc->rl_ldata.rl_tx_list_tag); + } + + /* Destroy all the RX and TX buffer maps */ + + if (sc->rl_ldata.rl_mtag) { + for (i = 0; i < RL_TX_DESC_CNT; i++) + bus_dmamap_destroy(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_tx_dmamap[i]); + for (i = 0; i < RL_RX_DESC_CNT; i++) + bus_dmamap_destroy(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[i]); + bus_dma_tag_destroy(sc->rl_ldata.rl_mtag); + } + + /* Unload and free the stats buffer and map */ + + if (sc->rl_ldata.rl_stag) { + bus_dmamap_unload(sc->rl_ldata.rl_stag, + sc->rl_ldata.rl_rx_list_map); + bus_dmamem_free(sc->rl_ldata.rl_stag, + sc->rl_ldata.rl_stats, + sc->rl_ldata.rl_smap); + bus_dma_tag_destroy(sc->rl_ldata.rl_stag); + } + + if (sc->rl_parent_tag) + bus_dma_tag_destroy(sc->rl_parent_tag); + + mtx_destroy(&sc->rl_mtx); + + return (0); +} + +static int +re_newbuf(sc, idx, m) + struct rl_softc *sc; + int idx; + struct mbuf *m; +{ + struct rl_dmaload_arg arg; + struct mbuf *n = NULL; + int error; + + if (m == NULL) { + n = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (n == NULL) + return (ENOBUFS); + m = n; + } else + m->m_data = m->m_ext.ext_buf; + + m->m_len = m->m_pkthdr.len = MCLBYTES; +#ifdef RE_FIXUP_RX + /* + * This is part of an evil trick to deal with non-x86 platforms. + * The RealTek chip requires RX buffers to be aligned on 64-bit + * boundaries, but that will hose non-x86 machines. To get around + * this, we leave some empty space at the start of each buffer + * and for non-x86 hosts, we copy the buffer back six bytes + * to achieve word alignment. This is slightly more efficient + * than allocating a new buffer, copying the contents, and + * discarding the old buffer. + */ + m_adj(m, RE_ETHER_ALIGN); +#endif + arg.rl_idx = idx; + arg.rl_maxsegs = 1; + arg.rl_flags = 0; + arg.rl_ring = sc->rl_ldata.rl_rx_list; + + error = bus_dmamap_load_mbuf(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[idx], m, re_dma_map_desc, + &arg, BUS_DMA_NOWAIT); + if (error || arg.rl_maxsegs != 1) { + if (n != NULL) + m_freem(n); + if (arg.rl_maxsegs == 0) + bus_dmamap_unload(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[idx]); + return (ENOMEM); + } + + sc->rl_ldata.rl_rx_list[idx].rl_cmdstat |= htole32(RL_RDESC_CMD_OWN); + sc->rl_ldata.rl_rx_mbuf[idx] = m; + + bus_dmamap_sync(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[idx], + BUS_DMASYNC_PREREAD); + + return (0); +} + +#ifdef RE_FIXUP_RX +static __inline void +re_fixup_rx(m) + struct mbuf *m; +{ + int i; + uint16_t *src, *dst; + + src = mtod(m, uint16_t *); + dst = src - (RE_ETHER_ALIGN - ETHER_ALIGN) / sizeof *src; + + for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) + *dst++ = *src++; + + m->m_data -= RE_ETHER_ALIGN - ETHER_ALIGN; + + return; +} +#endif + +static int +re_tx_list_init(sc) + struct rl_softc *sc; +{ + + RL_LOCK_ASSERT(sc); + + bzero ((char *)sc->rl_ldata.rl_tx_list, RL_TX_LIST_SZ); + bzero ((char *)&sc->rl_ldata.rl_tx_mbuf, + (RL_TX_DESC_CNT * sizeof(struct mbuf *))); + + bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_PREWRITE); + sc->rl_ldata.rl_tx_prodidx = 0; + sc->rl_ldata.rl_tx_considx = 0; + sc->rl_ldata.rl_tx_free = RL_TX_DESC_CNT; + + return (0); +} + +static int +re_rx_list_init(sc) + struct rl_softc *sc; +{ + int i; + + bzero ((char *)sc->rl_ldata.rl_rx_list, RL_RX_LIST_SZ); + bzero ((char *)&sc->rl_ldata.rl_rx_mbuf, + (RL_RX_DESC_CNT * sizeof(struct mbuf *))); + + for (i = 0; i < RL_RX_DESC_CNT; i++) { + if (re_newbuf(sc, i, NULL) == ENOBUFS) + return (ENOBUFS); + } + + /* Flush the RX descriptors */ + + bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list_map, + BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); + + sc->rl_ldata.rl_rx_prodidx = 0; + sc->rl_head = sc->rl_tail = NULL; + + return (0); +} + +/* + * RX handler for C+ and 8169. For the gigE chips, we support + * the reception of jumbo frames that have been fragmented + * across multiple 2K mbuf cluster buffers. + */ +static int +re_rxeof(sc) + struct rl_softc *sc; +{ + struct mbuf *m; + struct ifnet *ifp; + int i, total_len; + struct rl_desc *cur_rx; + u_int32_t rxstat, rxvlan; + int maxpkt = 16; + + RL_LOCK_ASSERT(sc); + + ifp = sc->rl_ifp; + i = sc->rl_ldata.rl_rx_prodidx; + + /* Invalidate the descriptor memory */ + + bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list_map, + BUS_DMASYNC_POSTREAD); + + while (!RL_OWN(&sc->rl_ldata.rl_rx_list[i]) && maxpkt) { + cur_rx = &sc->rl_ldata.rl_rx_list[i]; + m = sc->rl_ldata.rl_rx_mbuf[i]; + total_len = RL_RXBYTES(cur_rx); + rxstat = le32toh(cur_rx->rl_cmdstat); + rxvlan = le32toh(cur_rx->rl_vlanctl); + + /* Invalidate the RX mbuf and unload its map */ + + bus_dmamap_sync(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[i], + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[i]); + + if (!(rxstat & RL_RDESC_STAT_EOF)) { + m->m_len = RE_RX_DESC_BUFLEN; + if (sc->rl_head == NULL) + sc->rl_head = sc->rl_tail = m; + else { + m->m_flags &= ~M_PKTHDR; + sc->rl_tail->m_next = m; + sc->rl_tail = m; + } + re_newbuf(sc, i, NULL); + RL_DESC_INC(i); + continue; + } + + /* + * NOTE: for the 8139C+, the frame length field + * is always 12 bits in size, but for the gigE chips, + * it is 13 bits (since the max RX frame length is 16K). + * Unfortunately, all 32 bits in the status word + * were already used, so to make room for the extra + * length bit, RealTek took out the 'frame alignment + * error' bit and shifted the other status bits + * over one slot. The OWN, EOR, FS and LS bits are + * still in the same places. We have already extracted + * the frame length and checked the OWN bit, so rather + * than using an alternate bit mapping, we shift the + * status bits one space to the right so we can evaluate + * them using the 8169 status as though it was in the + * same format as that of the 8139C+. + */ + if (sc->rl_type == RL_8169) + rxstat >>= 1; + + /* + * if total_len > 2^13-1, both _RXERRSUM and _GIANT will be + * set, but if CRC is clear, it will still be a valid frame. + */ + if (rxstat & RL_RDESC_STAT_RXERRSUM && !(total_len > 8191 && + (rxstat & RL_RDESC_STAT_ERRS) == RL_RDESC_STAT_GIANT)) { + ifp->if_ierrors++; + /* + * If this is part of a multi-fragment packet, + * discard all the pieces. + */ + if (sc->rl_head != NULL) { + m_freem(sc->rl_head); + sc->rl_head = sc->rl_tail = NULL; + } + re_newbuf(sc, i, m); + RL_DESC_INC(i); + continue; + } + + /* + * If allocating a replacement mbuf fails, + * reload the current one. + */ + + if (re_newbuf(sc, i, NULL)) { + ifp->if_ierrors++; + if (sc->rl_head != NULL) { + m_freem(sc->rl_head); + sc->rl_head = sc->rl_tail = NULL; + } + re_newbuf(sc, i, m); + RL_DESC_INC(i); + continue; + } + + RL_DESC_INC(i); + + if (sc->rl_head != NULL) { + m->m_len = total_len % RE_RX_DESC_BUFLEN; + if (m->m_len == 0) + m->m_len = RE_RX_DESC_BUFLEN; + /* + * Special case: if there's 4 bytes or less + * in this buffer, the mbuf can be discarded: + * the last 4 bytes is the CRC, which we don't + * care about anyway. + */ + if (m->m_len <= ETHER_CRC_LEN) { + sc->rl_tail->m_len -= + (ETHER_CRC_LEN - m->m_len); + m_freem(m); + } else { + m->m_len -= ETHER_CRC_LEN; + m->m_flags &= ~M_PKTHDR; + sc->rl_tail->m_next = m; + } + m = sc->rl_head; + sc->rl_head = sc->rl_tail = NULL; + m->m_pkthdr.len = total_len - ETHER_CRC_LEN; + } else + m->m_pkthdr.len = m->m_len = + (total_len - ETHER_CRC_LEN); + +#ifdef RE_FIXUP_RX + re_fixup_rx(m); +#endif + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + + /* Do RX checksumming if enabled */ + +#ifndef __rtems__ + if (ifp->if_capenable & IFCAP_RXCSUM) { + + /* Check IP header checksum */ + if (rxstat & RL_RDESC_STAT_PROTOID) + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + if (!(rxstat & RL_RDESC_STAT_IPSUMBAD)) + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + + /* Check TCP/UDP checksum */ + if ((RL_TCPPKT(rxstat) && + !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) || + (RL_UDPPKT(rxstat) && + !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID|CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } +#endif + maxpkt--; +#ifndef __rtems__ + if (rxvlan & RL_RDESC_VLANCTL_TAG) { + VLAN_INPUT_TAG_NEW(ifp, m, + ntohs((rxvlan & RL_RDESC_VLANCTL_DATA))); + if (m == NULL) + continue; + } +#endif + RL_UNLOCK(sc); +#ifndef __rtems__ + (*ifp->if_input)(ifp, m); +#else + ether_input_skipping(ifp, m); +#endif + RL_LOCK(sc); + } + + /* Flush the RX DMA ring */ + + bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, + sc->rl_ldata.rl_rx_list_map, + BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); + + sc->rl_ldata.rl_rx_prodidx = i; + + if (maxpkt) + return(EAGAIN); + + return(0); +} + +static void +re_txeof(sc) + struct rl_softc *sc; +{ + struct ifnet *ifp; + u_int32_t txstat; + int idx; + + ifp = sc->rl_ifp; + idx = sc->rl_ldata.rl_tx_considx; + + /* Invalidate the TX descriptor list */ + bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list_map, + BUS_DMASYNC_POSTREAD); + + while (sc->rl_ldata.rl_tx_free < RL_TX_DESC_CNT) { + txstat = le32toh(sc->rl_ldata.rl_tx_list[idx].rl_cmdstat); + if (txstat & RL_TDESC_CMD_OWN) + break; + + sc->rl_ldata.rl_tx_list[idx].rl_bufaddr_lo = 0; + + /* + * We only stash mbufs in the last descriptor + * in a fragment chain, which also happens to + * be the only place where the TX status bits + * are valid. + */ + if (txstat & RL_TDESC_CMD_EOF) { + m_freem(sc->rl_ldata.rl_tx_mbuf[idx]); + sc->rl_ldata.rl_tx_mbuf[idx] = NULL; + bus_dmamap_unload(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_tx_dmamap[idx]); + if (txstat & (RL_TDESC_STAT_EXCESSCOL| + RL_TDESC_STAT_COLCNT)) + ifp->if_collisions++; + if (txstat & RL_TDESC_STAT_TXERRSUM) + ifp->if_oerrors++; + else + ifp->if_opackets++; + } + sc->rl_ldata.rl_tx_free++; + RL_DESC_INC(idx); + } + sc->rl_ldata.rl_tx_considx = idx; + + /* No changes made to the TX ring, so no flush needed */ + + if (sc->rl_ldata.rl_tx_free > RL_TX_DESC_THLD) + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + if (sc->rl_ldata.rl_tx_free < RL_TX_DESC_CNT) { + /* + * Some chips will ignore a second TX request issued + * while an existing transmission is in progress. If + * the transmitter goes idle but there are still + * packets waiting to be sent, we need to restart the + * channel here to flush them out. This only seems to + * be required with the PCIe devices. + */ + CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); + +#ifdef RE_TX_MODERATION + /* + * If not all descriptors have been reaped yet, reload + * the timer so that we will eventually get another + * interrupt that will cause us to re-enter this routine. + * This is done in case the transmitter has gone idle. + */ + CSR_WRITE_4(sc, RL_TIMERCNT, 1); +#endif + } else + sc->rl_watchdog_timer = 0; +} + +static void +re_tick(xsc) + void *xsc; +{ + struct rl_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + + sc = xsc; + ifp = sc->rl_ifp; + + RL_LOCK_ASSERT(sc); + + re_watchdog(sc); + + mii = device_get_softc(sc->rl_miibus); +#ifndef __rtems__ + mii_tick(mii); + if (sc->rl_link) { + if (!(mii->mii_media_status & IFM_ACTIVE)) + sc->rl_link = 0; + } else { + if (mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->rl_link = 1; + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + taskqueue_enqueue_fast(taskqueue_fast, + &sc->rl_txtask); + } + } + +#endif + callout_reset(&sc->rl_stat_callout, hz, re_tick, sc); +} + +#ifdef DEVICE_POLLING +static void +re_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct rl_softc *sc = ifp->if_softc; + + RL_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + re_poll_locked(ifp, cmd, count); + RL_UNLOCK(sc); +} + +static void +re_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct rl_softc *sc = ifp->if_softc; + + RL_LOCK_ASSERT(sc); + + sc->rxcycles = count; + re_rxeof(sc); + re_txeof(sc); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); + + if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ + u_int16_t status; + + status = CSR_READ_2(sc, RL_ISR); + if (status == 0xffff) + return; + if (status) + CSR_WRITE_2(sc, RL_ISR, status); + + /* + * XXX check behaviour on receiver stalls. + */ + + if (status & RL_ISR_SYSTEM_ERR) { + re_reset(sc); + re_init_locked(sc); + } + } +} +#endif /* DEVICE_POLLING */ + +static void +re_intr(arg) + void *arg; +{ + struct rl_softc *sc; + uint16_t status; + + sc = arg; + +printk( "re_intr " ); + status = CSR_READ_2(sc, RL_ISR); + if (status == 0xFFFF || (status & RL_INTRS_CPLUS) == 0) + return; + CSR_WRITE_2(sc, RL_IMR, 0); + + taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask); + + return; +} + +static void +re_int_task(arg, npending) + void *arg; + int npending; +{ + struct rl_softc *sc; + struct ifnet *ifp; + u_int16_t status; + int rval = 0; + + sc = arg; + ifp = sc->rl_ifp; + + NET_LOCK_GIANT(); + RL_LOCK(sc); + + status = CSR_READ_2(sc, RL_ISR); + CSR_WRITE_2(sc, RL_ISR, status); + + if (sc->suspended || !(ifp->if_flags & IFF_UP)) { + RL_UNLOCK(sc); + NET_UNLOCK_GIANT(); + return; + } + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + RL_UNLOCK(sc); + NET_UNLOCK_GIANT(); + return; + } +#endif + + if (status & (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_FIFO_OFLOW)) + rval = re_rxeof(sc); + +#ifdef RE_TX_MODERATION + if (status & (RL_ISR_TIMEOUT_EXPIRED| +#else + if (status & (RL_ISR_TX_OK| +#endif + RL_ISR_TX_ERR|RL_ISR_TX_DESC_UNAVAIL)) + re_txeof(sc); + + if (status & RL_ISR_SYSTEM_ERR) { + re_reset(sc); + re_init_locked(sc); + } + + if (status & RL_ISR_LINKCHG) { + callout_stop(&sc->rl_stat_callout); + re_tick(sc); + } + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); + + RL_UNLOCK(sc); + NET_UNLOCK_GIANT(); + + if ((CSR_READ_2(sc, RL_ISR) & RL_INTRS_CPLUS) || rval) { + taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask); + return; + } + + CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); + + return; +} + +static int +re_encap(sc, m_head, idx) + struct rl_softc *sc; + struct mbuf **m_head; + int *idx; +{ + struct mbuf *m_new = NULL; + struct rl_dmaload_arg arg; + bus_dmamap_t map; + int error; +#ifndef __rtems__ + struct m_tag *mtag; +#endif + + RL_LOCK_ASSERT(sc); + + if (sc->rl_ldata.rl_tx_free <= RL_TX_DESC_THLD) + return (EFBIG); + + /* + * Set up checksum offload. Note: checksum offload bits must + * appear in all descriptors of a multi-descriptor transmit + * attempt. This is according to testing done with an 8169 + * chip. This is a requirement. + */ + + arg.rl_flags = 0; + +#ifndef __rtems__ + if ((*m_head)->m_pkthdr.csum_flags & CSUM_IP) + arg.rl_flags |= RL_TDESC_CMD_IPCSUM; + if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) + arg.rl_flags |= RL_TDESC_CMD_TCPCSUM; + if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) + arg.rl_flags |= RL_TDESC_CMD_UDPCSUM; +#endif + + arg.rl_idx = *idx; + arg.rl_maxsegs = sc->rl_ldata.rl_tx_free; + if (arg.rl_maxsegs > RL_TX_DESC_THLD) + arg.rl_maxsegs -= RL_TX_DESC_THLD; + arg.rl_ring = sc->rl_ldata.rl_tx_list; + + map = sc->rl_ldata.rl_tx_dmamap[*idx]; + + /* + * With some of the RealTek chips, using the checksum offload + * support in conjunction with the autopadding feature results + * in the transmission of corrupt frames. For example, if we + * need to send a really small IP fragment that's less than 60 + * bytes in size, and IP header checksumming is enabled, the + * resulting ethernet frame that appears on the wire will + * have garbled payload. To work around this, if TX checksum + * offload is enabled, we always manually pad short frames out + * to the minimum ethernet frame size. We do this by pretending + * the mbuf chain has too many fragments so the coalescing code + * below can assemble the packet into a single buffer that's + * padded out to the mininum frame size. + * + * Note: this appears unnecessary for TCP, and doing it for TCP + * with PCIe adapters seems to result in bad checksums. + */ + + if (arg.rl_flags && !(arg.rl_flags & RL_TDESC_CMD_TCPCSUM) && + (*m_head)->m_pkthdr.len < RL_MIN_FRAMELEN) + error = EFBIG; + else + error = bus_dmamap_load_mbuf(sc->rl_ldata.rl_mtag, map, + *m_head, re_dma_map_desc, &arg, BUS_DMA_NOWAIT); + + if (error && error != EFBIG) { + device_printf(sc->rl_dev, "can't map mbuf (error %d)\n", error); + return (ENOBUFS); + } + + /* Too many segments to map, coalesce into a single mbuf */ + + if (error || arg.rl_maxsegs == 0) { + if (arg.rl_maxsegs == 0) + bus_dmamap_unload(sc->rl_ldata.rl_mtag, map); + m_new = m_defrag(*m_head, M_DONTWAIT); + if (m_new == NULL) { + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m_new; + + /* + * Manually pad short frames, and zero the pad space + * to avoid leaking data. + */ + if (m_new->m_pkthdr.len < RL_MIN_FRAMELEN) { + bzero(mtod(m_new, char *) + m_new->m_pkthdr.len, + RL_MIN_FRAMELEN - m_new->m_pkthdr.len); + m_new->m_pkthdr.len += RL_MIN_FRAMELEN - + m_new->m_pkthdr.len; + m_new->m_len = m_new->m_pkthdr.len; + } + + /* Note that we'll run over RL_TX_DESC_THLD here. */ + arg.rl_maxsegs = sc->rl_ldata.rl_tx_free; + error = bus_dmamap_load_mbuf(sc->rl_ldata.rl_mtag, map, + *m_head, re_dma_map_desc, &arg, BUS_DMA_NOWAIT); + if (error || arg.rl_maxsegs == 0) { + device_printf(sc->rl_dev, + "can't map defragmented mbuf (error %d)\n", error); + m_freem(m_new); + *m_head = NULL; + if (arg.rl_maxsegs == 0) + bus_dmamap_unload(sc->rl_ldata.rl_mtag, map); + return (EFBIG); + } + } + + /* + * Insure that the map for this transmission + * is placed at the array index of the last descriptor + * in this chain. (Swap last and first dmamaps.) + */ + sc->rl_ldata.rl_tx_dmamap[*idx] = + sc->rl_ldata.rl_tx_dmamap[arg.rl_idx]; + sc->rl_ldata.rl_tx_dmamap[arg.rl_idx] = map; + + sc->rl_ldata.rl_tx_mbuf[arg.rl_idx] = *m_head; + sc->rl_ldata.rl_tx_free -= arg.rl_maxsegs; + + /* + * Set up hardware VLAN tagging. Note: vlan tag info must + * appear in the first descriptor of a multi-descriptor + * transmission attempt. + */ + +#ifndef __rtems__ + mtag = VLAN_OUTPUT_TAG(sc->rl_ifp, *m_head); + if (mtag != NULL) + sc->rl_ldata.rl_tx_list[*idx].rl_vlanctl = + htole32(htons(VLAN_TAG_VALUE(mtag)) | RL_TDESC_VLANCTL_TAG); +#endif + + /* Transfer ownership of packet to the chip. */ + + sc->rl_ldata.rl_tx_list[arg.rl_idx].rl_cmdstat |= + htole32(RL_TDESC_CMD_OWN); + if (*idx != arg.rl_idx) + sc->rl_ldata.rl_tx_list[*idx].rl_cmdstat |= + htole32(RL_TDESC_CMD_OWN); + + RL_DESC_INC(arg.rl_idx); + *idx = arg.rl_idx; + + return (0); +} + +static void +re_tx_task(arg, npending) + void *arg; + int npending; +{ + struct ifnet *ifp; + + ifp = arg; + NET_LOCK_GIANT(); + re_start(ifp); + NET_UNLOCK_GIANT(); + + return; +} + +/* + * Main transmit routine for C+ and gigE NICs. + */ +static void +re_start(ifp) + struct ifnet *ifp; +{ + struct rl_softc *sc; + struct mbuf *m_head = NULL; + int idx, queued = 0; + + sc = ifp->if_softc; + + RL_LOCK(sc); + + if (!sc->rl_link || ifp->if_drv_flags & IFF_DRV_OACTIVE) { + RL_UNLOCK(sc); + return; + } + + idx = sc->rl_ldata.rl_tx_prodidx; + + while (sc->rl_ldata.rl_tx_mbuf[idx] == NULL) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (re_encap(sc, &m_head, &idx)) { + if (m_head == NULL) + break; + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + ETHER_BPF_MTAP(ifp, m_head); + + queued++; + } + + if (queued == 0) { +#ifdef RE_TX_MODERATION + if (sc->rl_ldata.rl_tx_free != RL_TX_DESC_CNT) + CSR_WRITE_4(sc, RL_TIMERCNT, 1); +#endif + RL_UNLOCK(sc); + return; + } + + /* Flush the TX descriptors */ + + bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, + sc->rl_ldata.rl_tx_list_map, + BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); + + sc->rl_ldata.rl_tx_prodidx = idx; + + CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); + +#ifdef RE_TX_MODERATION + /* + * Use the countdown timer for interrupt moderation. + * 'TX done' interrupts are disabled. Instead, we reset the + * countdown timer, which will begin counting until it hits + * the value in the TIMERINT register, and then trigger an + * interrupt. Each time we write to the TIMERCNT register, + * the timer count is reset to 0. + */ + CSR_WRITE_4(sc, RL_TIMERCNT, 1); +#endif + + /* + * Set a timeout in case the chip goes out to lunch. + */ + sc->rl_watchdog_timer = 5; + + RL_UNLOCK(sc); + + return; +} + +static void +re_init(xsc) + void *xsc; +{ + struct rl_softc *sc = xsc; + + RL_LOCK(sc); + re_init_locked(sc); + RL_UNLOCK(sc); +} + +static void +re_init_locked(sc) + struct rl_softc *sc; +{ + struct ifnet *ifp = sc->rl_ifp; + struct mii_data *mii; + u_int32_t rxcfg = 0; + union { + uint32_t align_dummy; + u_char eaddr[ETHER_ADDR_LEN]; + } eaddr; + + RL_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->rl_miibus); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + re_stop(sc); + + /* + * Enable C+ RX and TX mode, as well as VLAN stripping and + * RX checksum offload. We must configure the C+ register + * before all others. + */ + CSR_WRITE_2(sc, RL_CPLUS_CMD, RL_CPLUSCMD_RXENB| + RL_CPLUSCMD_TXENB|RL_CPLUSCMD_PCI_MRW| + RL_CPLUSCMD_VLANSTRIP|RL_CPLUSCMD_RXCSUM_ENB); + + /* + * Init our MAC address. Even though the chipset + * documentation doesn't mention it, we need to enter "Config + * register write enable" mode to modify the ID registers. + */ + /* Copy MAC address on stack to align. */ + bcopy(IF_LLADDR(ifp), eaddr.eaddr, ETHER_ADDR_LEN); + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG); + CSR_WRITE_4(sc, RL_IDR0, + htole32(*(u_int32_t *)(&eaddr.eaddr[0]))); + CSR_WRITE_4(sc, RL_IDR4, + htole32(*(u_int32_t *)(&eaddr.eaddr[4]))); + CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); + + /* + * For C+ mode, initialize the RX descriptors and mbufs. + */ + re_rx_list_init(sc); + re_tx_list_init(sc); + + /* + * Load the addresses of the RX and TX lists into the chip. + */ + + CSR_WRITE_4(sc, RL_RXLIST_ADDR_HI, + RL_ADDR_HI(sc->rl_ldata.rl_rx_list_addr)); + CSR_WRITE_4(sc, RL_RXLIST_ADDR_LO, + RL_ADDR_LO(sc->rl_ldata.rl_rx_list_addr)); + + CSR_WRITE_4(sc, RL_TXLIST_ADDR_HI, + RL_ADDR_HI(sc->rl_ldata.rl_tx_list_addr)); + CSR_WRITE_4(sc, RL_TXLIST_ADDR_LO, + RL_ADDR_LO(sc->rl_ldata.rl_tx_list_addr)); + + /* + * Enable transmit and receive. + */ + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); + + /* + * Set the initial TX and RX configuration. + */ + if (sc->rl_testmode) { + if (sc->rl_type == RL_8169) + CSR_WRITE_4(sc, RL_TXCFG, + RL_TXCFG_CONFIG|RL_LOOPTEST_ON); + else + CSR_WRITE_4(sc, RL_TXCFG, + RL_TXCFG_CONFIG|RL_LOOPTEST_ON_CPLUS); + } else + CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); + + CSR_WRITE_1(sc, RL_EARLY_TX_THRESH, 16); + + CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG); + + /* Set the individual bit to receive frames for this host only. */ + rxcfg = CSR_READ_4(sc, RL_RXCFG); + rxcfg |= RL_RXCFG_RX_INDIV; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + rxcfg |= RL_RXCFG_RX_ALLPHYS; + else + rxcfg &= ~RL_RXCFG_RX_ALLPHYS; + CSR_WRITE_4(sc, RL_RXCFG, rxcfg); + + /* + * Set capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) + rxcfg |= RL_RXCFG_RX_BROAD; + else + rxcfg &= ~RL_RXCFG_RX_BROAD; + CSR_WRITE_4(sc, RL_RXCFG, rxcfg); + + /* + * Program the multicast filter, if necessary. + */ + re_setmulti(sc); + +#ifdef DEVICE_POLLING + /* + * Disable interrupts if we are polling. + */ + if (ifp->if_capenable & IFCAP_POLLING) + CSR_WRITE_2(sc, RL_IMR, 0); + else /* otherwise ... */ +#endif + + /* + * Enable interrupts. + */ + if (sc->rl_testmode) + CSR_WRITE_2(sc, RL_IMR, 0); + else + CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); + CSR_WRITE_2(sc, RL_ISR, RL_INTRS_CPLUS); + + /* Set initial TX threshold */ + sc->rl_txthresh = RL_TX_THRESH_INIT; + + /* Start RX/TX process. */ + CSR_WRITE_4(sc, RL_MISSEDPKT, 0); +#ifdef notdef + /* Enable receiver and transmitter. */ + CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); +#endif + +#ifdef RE_TX_MODERATION + /* + * Initialize the timer interrupt register so that + * a timer interrupt will be generated once the timer + * reaches a certain number of ticks. The timer is + * reloaded on each transmit. This gives us TX interrupt + * moderation, which dramatically improves TX frame rate. + */ + if (sc->rl_type == RL_8169) + CSR_WRITE_4(sc, RL_TIMERINT_8169, 0x800); + else + CSR_WRITE_4(sc, RL_TIMERINT, 0x400); +#endif + + /* + * For 8169 gigE NICs, set the max allowed RX packet + * size so we can receive jumbo frames. + */ + if (sc->rl_type == RL_8169) + CSR_WRITE_2(sc, RL_MAXRXPKTLEN, 16383); + + if (sc->rl_testmode) + return; + + mii_mediachg(mii); + + CSR_WRITE_1(sc, RL_CFG1, CSR_READ_1(sc, RL_CFG1) | RL_CFG1_DRVLOAD); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + sc->rl_link = 0; + sc->rl_watchdog_timer = 0; + callout_reset(&sc->rl_stat_callout, hz, re_tick, sc); +} + +/* + * Set media options. + */ +#ifndef __rtems__ +static int +re_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct rl_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->rl_miibus); + RL_LOCK(sc); + mii_mediachg(mii); + RL_UNLOCK(sc); + + return (0); +} +#endif + +/* + * Report current media status. + */ +#ifndef __rtems__ +static void +re_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct rl_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->rl_miibus); + + RL_LOCK(sc); + mii_pollstat(mii); + RL_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} +#endif + +static int +#ifndef __rtems__ +re_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +#else +re_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) +#endif +{ + struct rl_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFMTU: + RL_LOCK(sc); + if (ifr->ifr_mtu > RL_JUMBO_MTU) + error = EINVAL; + ifp->if_mtu = ifr->ifr_mtu; + RL_UNLOCK(sc); + break; + case SIOCSIFFLAGS: + RL_LOCK(sc); + if ((ifp->if_flags & IFF_UP) != 0) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + if (((ifp->if_flags ^ sc->rl_if_flags) + & IFF_PROMISC) != 0) + re_setmulti(sc); + } else + re_init_locked(sc); + } else { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + re_stop(sc); + } + sc->rl_if_flags = ifp->if_flags; + RL_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + RL_LOCK(sc); + re_setmulti(sc); + RL_UNLOCK(sc); + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->rl_miibus); +#ifndef __rtems__ + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); +#endif + break; +#ifndef __rtems__ + case SIOCSIFCAP: + { + int mask, reinit; + + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + reinit = 0; +#ifdef DEVICE_POLLING + if (mask & IFCAP_POLLING) { + if (ifr->ifr_reqcap & IFCAP_POLLING) { + error = ether_poll_register(re_poll, ifp); + if (error) + return(error); + RL_LOCK(sc); + /* Disable interrupts */ + CSR_WRITE_2(sc, RL_IMR, 0x0000); + ifp->if_capenable |= IFCAP_POLLING; + RL_UNLOCK(sc); + } else { + error = ether_poll_deregister(ifp); + /* Enable interrupts. */ + RL_LOCK(sc); + CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); + ifp->if_capenable &= ~IFCAP_POLLING; + RL_UNLOCK(sc); + } + } +#endif /* DEVICE_POLLING */ + if (mask & IFCAP_HWCSUM) { + ifp->if_capenable ^= IFCAP_HWCSUM; + if (ifp->if_capenable & IFCAP_TXCSUM) + ifp->if_hwassist = RE_CSUM_FEATURES; + else + ifp->if_hwassist = 0; + reinit = 1; + } + if (mask & IFCAP_VLAN_HWTAGGING) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + reinit = 1; + } + if (reinit && ifp->if_drv_flags & IFF_DRV_RUNNING) + re_init(sc); +#ifdef VLAN_CAPABILITIES + VLAN_CAPABILITIES(ifp); +#endif + } +#endif + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return (error); +} + +static void +re_watchdog(sc) + struct rl_softc *sc; +{ + + RL_LOCK_ASSERT(sc); + + if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer != 0) + return; + + device_printf(sc->rl_dev, "watchdog timeout\n"); + sc->rl_ifp->if_oerrors++; + + re_txeof(sc); + re_rxeof(sc); + re_init_locked(sc); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +re_stop(sc) + struct rl_softc *sc; +{ + register int i; + struct ifnet *ifp; + + RL_LOCK_ASSERT(sc); + + ifp = sc->rl_ifp; + + sc->rl_watchdog_timer = 0; + callout_stop(&sc->rl_stat_callout); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + CSR_WRITE_1(sc, RL_COMMAND, 0x00); + CSR_WRITE_2(sc, RL_IMR, 0x0000); + CSR_WRITE_2(sc, RL_ISR, 0xFFFF); + + if (sc->rl_head != NULL) { + m_freem(sc->rl_head); + sc->rl_head = sc->rl_tail = NULL; + } + + /* Free the TX list buffers. */ + + for (i = 0; i < RL_TX_DESC_CNT; i++) { + if (sc->rl_ldata.rl_tx_mbuf[i] != NULL) { + bus_dmamap_unload(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_tx_dmamap[i]); + m_freem(sc->rl_ldata.rl_tx_mbuf[i]); + sc->rl_ldata.rl_tx_mbuf[i] = NULL; + } + } + + /* Free the RX list buffers. */ + + for (i = 0; i < RL_RX_DESC_CNT; i++) { + if (sc->rl_ldata.rl_rx_mbuf[i] != NULL) { + bus_dmamap_unload(sc->rl_ldata.rl_mtag, + sc->rl_ldata.rl_rx_dmamap[i]); + m_freem(sc->rl_ldata.rl_rx_mbuf[i]); + sc->rl_ldata.rl_rx_mbuf[i] = NULL; + } + } +} + +/* + * Device suspend routine. Stop the interface and save some PCI + * settings in case the BIOS doesn't restore them properly on + * resume. + */ +#ifndef __rtems__ +static int +re_suspend(dev) + device_t dev; +{ + struct rl_softc *sc; + + sc = device_get_softc(dev); + + RL_LOCK(sc); + re_stop(sc); + sc->suspended = 1; + RL_UNLOCK(sc); + + return (0); +} +#endif + +/* + * Device resume routine. Restore some PCI settings in case the BIOS + * doesn't, re-enable busmastering, and restart the interface if + * appropriate. + */ +#ifndef __rtems__ +static int +re_resume(dev) + device_t dev; +{ + struct rl_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + + RL_LOCK(sc); + + ifp = sc->rl_ifp; + + /* reinitialize interface if necessary */ + if (ifp->if_flags & IFF_UP) + re_init_locked(sc); + + sc->suspended = 0; + RL_UNLOCK(sc); + + return (0); +} +#endif + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void +re_shutdown(dev) + device_t dev; +{ + struct rl_softc *sc; + + sc = device_get_softc(dev); + + RL_LOCK(sc); + re_stop(sc); + /* + * Mark interface as down since otherwise we will panic if + * interrupt comes in later on, which can happen in some + * cases. + */ + sc->rl_ifp->if_flags &= ~IFF_UP; + RL_UNLOCK(sc); +} diff --git a/bsd_eth_drivers/if_re/if_rlreg.h b/bsd_eth_drivers/if_re/if_rlreg.h new file mode 100644 index 0000000..5d9db5d --- /dev/null +++ b/bsd_eth_drivers/if_re/if_rlreg.h @@ -0,0 +1,993 @@ +/*- + * Copyright (c) 1997, 1998-2003 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/pci/if_rlreg.h,v 1.51.2.14.2.1 2008/10/02 02:57:24 kensmith Exp $ + */ + +/* + * RealTek 8129/8139 register offsets + */ +#define RL_IDR0 0x0000 /* ID register 0 (station addr) */ +#define RL_IDR1 0x0001 /* Must use 32-bit accesses (?) */ +#define RL_IDR2 0x0002 +#define RL_IDR3 0x0003 +#define RL_IDR4 0x0004 +#define RL_IDR5 0x0005 + /* 0006-0007 reserved */ +#define RL_MAR0 0x0008 /* Multicast hash table */ +#define RL_MAR1 0x0009 +#define RL_MAR2 0x000A +#define RL_MAR3 0x000B +#define RL_MAR4 0x000C +#define RL_MAR5 0x000D +#define RL_MAR6 0x000E +#define RL_MAR7 0x000F + +#define RL_TXSTAT0 0x0010 /* status of TX descriptor 0 */ +#define RL_TXSTAT1 0x0014 /* status of TX descriptor 1 */ +#define RL_TXSTAT2 0x0018 /* status of TX descriptor 2 */ +#define RL_TXSTAT3 0x001C /* status of TX descriptor 3 */ + +#define RL_TXADDR0 0x0020 /* address of TX descriptor 0 */ +#define RL_TXADDR1 0x0024 /* address of TX descriptor 1 */ +#define RL_TXADDR2 0x0028 /* address of TX descriptor 2 */ +#define RL_TXADDR3 0x002C /* address of TX descriptor 3 */ + +#define RL_RXADDR 0x0030 /* RX ring start address */ +#define RL_RX_EARLY_BYTES 0x0034 /* RX early byte count */ +#define RL_RX_EARLY_STAT 0x0036 /* RX early status */ +#define RL_COMMAND 0x0037 /* command register */ +#define RL_CURRXADDR 0x0038 /* current address of packet read */ +#define RL_CURRXBUF 0x003A /* current RX buffer address */ +#define RL_IMR 0x003C /* interrupt mask register */ +#define RL_ISR 0x003E /* interrupt status register */ +#define RL_TXCFG 0x0040 /* transmit config */ +#define RL_RXCFG 0x0044 /* receive config */ +#define RL_TIMERCNT 0x0048 /* timer count register */ +#define RL_MISSEDPKT 0x004C /* missed packet counter */ +#define RL_EECMD 0x0050 /* EEPROM command register */ +#define RL_CFG0 0x0051 /* config register #0 */ +#define RL_CFG1 0x0052 /* config register #1 */ + /* 0053-0057 reserved */ +#define RL_MEDIASTAT 0x0058 /* media status register (8139) */ + /* 0059-005A reserved */ +#define RL_MII 0x005A /* 8129 chip only */ +#define RL_HALTCLK 0x005B +#define RL_MULTIINTR 0x005C /* multiple interrupt */ +#define RL_PCIREV 0x005E /* PCI revision value */ + /* 005F reserved */ +#define RL_TXSTAT_ALL 0x0060 /* TX status of all descriptors */ + +/* Direct PHY access registers only available on 8139 */ +#define RL_BMCR 0x0062 /* PHY basic mode control */ +#define RL_BMSR 0x0064 /* PHY basic mode status */ +#define RL_ANAR 0x0066 /* PHY autoneg advert */ +#define RL_LPAR 0x0068 /* PHY link partner ability */ +#define RL_ANER 0x006A /* PHY autoneg expansion */ + +#define RL_DISCCNT 0x006C /* disconnect counter */ +#define RL_FALSECAR 0x006E /* false carrier counter */ +#define RL_NWAYTST 0x0070 /* NWAY test register */ +#define RL_RX_ER 0x0072 /* RX_ER counter */ +#define RL_CSCFG 0x0074 /* CS configuration register */ + +/* + * When operating in special C+ mode, some of the registers in an + * 8139C+ chip have different definitions. These are also used for + * the 8169 gigE chip. + */ +#define RL_DUMPSTATS_LO 0x0010 /* counter dump command register */ +#define RL_DUMPSTATS_HI 0x0014 /* counter dump command register */ +#define RL_TXLIST_ADDR_LO 0x0020 /* 64 bits, 256 byte alignment */ +#define RL_TXLIST_ADDR_HI 0x0024 /* 64 bits, 256 byte alignment */ +#define RL_TXLIST_ADDR_HPRIO_LO 0x0028 /* 64 bits, 256 byte alignment */ +#define RL_TXLIST_ADDR_HPRIO_HI 0x002C /* 64 bits, 256 byte alignment */ +#define RL_CFG2 0x0053 +#define RL_TIMERINT 0x0054 /* interrupt on timer expire */ +#define RL_TXSTART 0x00D9 /* 8 bits */ +#define RL_CPLUS_CMD 0x00E0 /* 16 bits */ +#define RL_RXLIST_ADDR_LO 0x00E4 /* 64 bits, 256 byte alignment */ +#define RL_RXLIST_ADDR_HI 0x00E8 /* 64 bits, 256 byte alignment */ +#define RL_EARLY_TX_THRESH 0x00EC /* 8 bits */ + +/* + * Registers specific to the 8169 gigE chip + */ +#define RL_TIMERINT_8169 0x0058 /* different offset than 8139 */ +#define RL_PHYAR 0x0060 +#define RL_TBICSR 0x0064 +#define RL_TBI_ANAR 0x0068 +#define RL_TBI_LPAR 0x006A +#define RL_GMEDIASTAT 0x006C /* 8 bits */ +#define RL_MAXRXPKTLEN 0x00DA /* 16 bits, chip multiplies by 8 */ +#define RL_GTXSTART 0x0038 /* 16 bits */ + +/* + * TX config register bits + */ +#define RL_TXCFG_CLRABRT 0x00000001 /* retransmit aborted pkt */ +#define RL_TXCFG_MAXDMA 0x00000700 /* max DMA burst size */ +#define RL_TXCFG_CRCAPPEND 0x00010000 /* CRC append (0 = yes) */ +#define RL_TXCFG_LOOPBKTST 0x00060000 /* loopback test */ +#define RL_TXCFG_IFG2 0x00080000 /* 8169 only */ +#define RL_TXCFG_IFG 0x03000000 /* interframe gap */ +#define RL_TXCFG_HWREV 0x7CC00000 + +#define RL_LOOPTEST_OFF 0x00000000 +#define RL_LOOPTEST_ON 0x00020000 +#define RL_LOOPTEST_ON_CPLUS 0x00060000 + +/* Known revision codes. */ + +#define RL_HWREV_8169 0x00000000 +#define RL_HWREV_8110S 0x00800000 +#define RL_HWREV_8169S 0x04000000 +#define RL_HWREV_8169_8110SB 0x10000000 +#define RL_HWREV_8169_8110SC 0x18000000 +#define RL_HWREV_8168_SPIN1 0x30000000 +#define RL_HWREV_8100E 0x30800000 +#define RL_HWREV_8101E 0x34000000 +#define RL_HWREV_8168_SPIN2 0x38000000 +#define RL_HWREV_8168_SPIN3 0x38400000 +#define RL_HWREV_8139 0x60000000 +#define RL_HWREV_8139A 0x70000000 +#define RL_HWREV_8139AG 0x70800000 +#define RL_HWREV_8139B 0x78000000 +#define RL_HWREV_8130 0x7C000000 +#define RL_HWREV_8139C 0x74000000 +#define RL_HWREV_8139D 0x74400000 +#define RL_HWREV_8139CPLUS 0x74800000 +#define RL_HWREV_8101 0x74c00000 +#define RL_HWREV_8100 0x78800000 + +#define RL_TXDMA_16BYTES 0x00000000 +#define RL_TXDMA_32BYTES 0x00000100 +#define RL_TXDMA_64BYTES 0x00000200 +#define RL_TXDMA_128BYTES 0x00000300 +#define RL_TXDMA_256BYTES 0x00000400 +#define RL_TXDMA_512BYTES 0x00000500 +#define RL_TXDMA_1024BYTES 0x00000600 +#define RL_TXDMA_2048BYTES 0x00000700 + +/* + * Transmit descriptor status register bits. + */ +#define RL_TXSTAT_LENMASK 0x00001FFF +#define RL_TXSTAT_OWN 0x00002000 +#define RL_TXSTAT_TX_UNDERRUN 0x00004000 +#define RL_TXSTAT_TX_OK 0x00008000 +#define RL_TXSTAT_EARLY_THRESH 0x003F0000 +#define RL_TXSTAT_COLLCNT 0x0F000000 +#define RL_TXSTAT_CARR_HBEAT 0x10000000 +#define RL_TXSTAT_OUTOFWIN 0x20000000 +#define RL_TXSTAT_TXABRT 0x40000000 +#define RL_TXSTAT_CARRLOSS 0x80000000 + +/* + * Interrupt status register bits. + */ +#define RL_ISR_RX_OK 0x0001 +#define RL_ISR_RX_ERR 0x0002 +#define RL_ISR_TX_OK 0x0004 +#define RL_ISR_TX_ERR 0x0008 +#define RL_ISR_RX_OVERRUN 0x0010 +#define RL_ISR_PKT_UNDERRUN 0x0020 +#define RL_ISR_LINKCHG 0x0020 /* 8169 only */ +#define RL_ISR_FIFO_OFLOW 0x0040 /* 8139 only */ +#define RL_ISR_TX_DESC_UNAVAIL 0x0080 /* C+ only */ +#define RL_ISR_SWI 0x0100 /* C+ only */ +#define RL_ISR_CABLE_LEN_CHGD 0x2000 +#define RL_ISR_PCS_TIMEOUT 0x4000 /* 8129 only */ +#define RL_ISR_TIMEOUT_EXPIRED 0x4000 +#define RL_ISR_SYSTEM_ERR 0x8000 + +#define RL_INTRS \ + (RL_ISR_TX_OK|RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ + RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ + RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR) + +#ifdef RE_TX_MODERATION +#define RL_INTRS_CPLUS \ + (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ + RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ + RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED) +#else +#define RL_INTRS_CPLUS \ + (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR|RL_ISR_TX_OK| \ + RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ + RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED) +#endif + +/* + * Media status register. (8139 only) + */ +#define RL_MEDIASTAT_RXPAUSE 0x01 +#define RL_MEDIASTAT_TXPAUSE 0x02 +#define RL_MEDIASTAT_LINK 0x04 +#define RL_MEDIASTAT_SPEED10 0x08 +#define RL_MEDIASTAT_RXFLOWCTL 0x40 /* duplex mode */ +#define RL_MEDIASTAT_TXFLOWCTL 0x80 /* duplex mode */ + +/* + * Receive config register. + */ +#define RL_RXCFG_RX_ALLPHYS 0x00000001 /* accept all nodes */ +#define RL_RXCFG_RX_INDIV 0x00000002 /* match filter */ +#define RL_RXCFG_RX_MULTI 0x00000004 /* accept all multicast */ +#define RL_RXCFG_RX_BROAD 0x00000008 /* accept all broadcast */ +#define RL_RXCFG_RX_RUNT 0x00000010 +#define RL_RXCFG_RX_ERRPKT 0x00000020 +#define RL_RXCFG_WRAP 0x00000080 +#define RL_RXCFG_MAXDMA 0x00000700 +#define RL_RXCFG_BUFSZ 0x00001800 +#define RL_RXCFG_FIFOTHRESH 0x0000E000 +#define RL_RXCFG_EARLYTHRESH 0x07000000 + +#define RL_RXDMA_16BYTES 0x00000000 +#define RL_RXDMA_32BYTES 0x00000100 +#define RL_RXDMA_64BYTES 0x00000200 +#define RL_RXDMA_128BYTES 0x00000300 +#define RL_RXDMA_256BYTES 0x00000400 +#define RL_RXDMA_512BYTES 0x00000500 +#define RL_RXDMA_1024BYTES 0x00000600 +#define RL_RXDMA_UNLIMITED 0x00000700 + +#define RL_RXBUF_8 0x00000000 +#define RL_RXBUF_16 0x00000800 +#define RL_RXBUF_32 0x00001000 +#define RL_RXBUF_64 0x00001800 + +#define RL_RXFIFO_16BYTES 0x00000000 +#define RL_RXFIFO_32BYTES 0x00002000 +#define RL_RXFIFO_64BYTES 0x00004000 +#define RL_RXFIFO_128BYTES 0x00006000 +#define RL_RXFIFO_256BYTES 0x00008000 +#define RL_RXFIFO_512BYTES 0x0000A000 +#define RL_RXFIFO_1024BYTES 0x0000C000 +#define RL_RXFIFO_NOTHRESH 0x0000E000 + +/* + * Bits in RX status header (included with RX'ed packet + * in ring buffer). + */ +#define RL_RXSTAT_RXOK 0x00000001 +#define RL_RXSTAT_ALIGNERR 0x00000002 +#define RL_RXSTAT_CRCERR 0x00000004 +#define RL_RXSTAT_GIANT 0x00000008 +#define RL_RXSTAT_RUNT 0x00000010 +#define RL_RXSTAT_BADSYM 0x00000020 +#define RL_RXSTAT_BROAD 0x00002000 +#define RL_RXSTAT_INDIV 0x00004000 +#define RL_RXSTAT_MULTI 0x00008000 +#define RL_RXSTAT_LENMASK 0xFFFF0000 + +#define RL_RXSTAT_UNFINISHED 0xFFF0 /* DMA still in progress */ +/* + * Command register. + */ +#define RL_CMD_EMPTY_RXBUF 0x0001 +#define RL_CMD_TX_ENB 0x0004 +#define RL_CMD_RX_ENB 0x0008 +#define RL_CMD_RESET 0x0010 + +/* + * EEPROM control register + */ +#define RL_EE_DATAOUT 0x01 /* Data out */ +#define RL_EE_DATAIN 0x02 /* Data in */ +#define RL_EE_CLK 0x04 /* clock */ +#define RL_EE_SEL 0x08 /* chip select */ +#define RL_EE_MODE (0x40|0x80) + +#define RL_EEMODE_OFF 0x00 +#define RL_EEMODE_AUTOLOAD 0x40 +#define RL_EEMODE_PROGRAM 0x80 +#define RL_EEMODE_WRITECFG (0x80|0x40) + +/* 9346 EEPROM commands */ +#define RL_9346_ADDR_LEN 6 /* 93C46 1K: 128x16 */ +#define RL_9356_ADDR_LEN 8 /* 93C56 2K: 256x16 */ + +#define RL_9346_WRITE 0x5 +#define RL_9346_READ 0x6 +#define RL_9346_ERASE 0x7 +#define RL_9346_EWEN 0x4 +#define RL_9346_EWEN_ADDR 0x30 +#define RL_9456_EWDS 0x4 +#define RL_9346_EWDS_ADDR 0x00 + +#define RL_EECMD_WRITE 0x140 +#define RL_EECMD_READ_6BIT 0x180 +#define RL_EECMD_READ_8BIT 0x600 +#define RL_EECMD_ERASE 0x1c0 + +#define RL_EE_ID 0x00 +#define RL_EE_PCI_VID 0x01 +#define RL_EE_PCI_DID 0x02 +/* Location of station address inside EEPROM */ +#define RL_EE_EADDR 0x07 + +/* + * MII register (8129 only) + */ +#define RL_MII_CLK 0x01 +#define RL_MII_DATAIN 0x02 +#define RL_MII_DATAOUT 0x04 +#define RL_MII_DIR 0x80 /* 0 == input, 1 == output */ + +/* + * Config 0 register + */ +#define RL_CFG0_ROM0 0x01 +#define RL_CFG0_ROM1 0x02 +#define RL_CFG0_ROM2 0x04 +#define RL_CFG0_PL0 0x08 +#define RL_CFG0_PL1 0x10 +#define RL_CFG0_10MBPS 0x20 /* 10 Mbps internal mode */ +#define RL_CFG0_PCS 0x40 +#define RL_CFG0_SCR 0x80 + +/* + * Config 1 register + */ +#define RL_CFG1_PWRDWN 0x01 +#define RL_CFG1_SLEEP 0x02 +#define RL_CFG1_IOMAP 0x04 +#define RL_CFG1_MEMMAP 0x08 +#define RL_CFG1_RSVD 0x10 +#define RL_CFG1_DRVLOAD 0x20 +#define RL_CFG1_LED0 0x40 +#define RL_CFG1_FULLDUPLEX 0x40 /* 8129 only */ +#define RL_CFG1_LED1 0x80 + +/* + * 8139C+ register definitions + */ + +/* RL_DUMPSTATS_LO register */ + +#define RL_DUMPSTATS_START 0x00000008 + +/* Transmit start register */ + +#define RL_TXSTART_SWI 0x01 /* generate TX interrupt */ +#define RL_TXSTART_START 0x40 /* start normal queue transmit */ +#define RL_TXSTART_HPRIO_START 0x80 /* start hi prio queue transmit */ + +/* + * Config 2 register, 8139C+/8169/8169S/8110S only + */ +#define RL_CFG2_BUSFREQ 0x07 +#define RL_CFG2_BUSWIDTH 0x08 +#define RL_CFG2_AUXPWRSTS 0x10 + +#define RL_BUSFREQ_33MHZ 0x00 +#define RL_BUSFREQ_66MHZ 0x01 + +#define RL_BUSWIDTH_32BITS 0x00 +#define RL_BUSWIDTH_64BITS 0x08 + +/* C+ mode command register */ + +#define RL_CPLUSCMD_TXENB 0x0001 /* enable C+ transmit mode */ +#define RL_CPLUSCMD_RXENB 0x0002 /* enable C+ receive mode */ +#define RL_CPLUSCMD_PCI_MRW 0x0008 /* enable PCI multi-read/write */ +#define RL_CPLUSCMD_PCI_DAC 0x0010 /* PCI dual-address cycle only */ +#define RL_CPLUSCMD_RXCSUM_ENB 0x0020 /* enable RX checksum offload */ +#define RL_CPLUSCMD_VLANSTRIP 0x0040 /* enable VLAN tag stripping */ + +/* C+ early transmit threshold */ + +#define RL_EARLYTXTHRESH_CNT 0x003F /* byte count times 8 */ + +/* + * Gigabit PHY access register (8169 only) + */ + +#define RL_PHYAR_PHYDATA 0x0000FFFF +#define RL_PHYAR_PHYREG 0x001F0000 +#define RL_PHYAR_BUSY 0x80000000 + +/* + * Gigabit media status (8169 only) + */ +#define RL_GMEDIASTAT_FDX 0x01 /* full duplex */ +#define RL_GMEDIASTAT_LINK 0x02 /* link up */ +#define RL_GMEDIASTAT_10MBPS 0x04 /* 10mps link */ +#define RL_GMEDIASTAT_100MBPS 0x08 /* 100mbps link */ +#define RL_GMEDIASTAT_1000MBPS 0x10 /* gigE link */ +#define RL_GMEDIASTAT_RXFLOW 0x20 /* RX flow control on */ +#define RL_GMEDIASTAT_TXFLOW 0x40 /* TX flow control on */ +#define RL_GMEDIASTAT_TBI 0x80 /* TBI enabled */ + +/* + * The RealTek doesn't use a fragment-based descriptor mechanism. + * Instead, there are only four register sets, each or which represents + * one 'descriptor.' Basically, each TX descriptor is just a contiguous + * packet buffer (32-bit aligned!) and we place the buffer addresses in + * the registers so the chip knows where they are. + * + * We can sort of kludge together the same kind of buffer management + * used in previous drivers, but we have to do buffer copies almost all + * the time, so it doesn't really buy us much. + * + * For reception, there's just one large buffer where the chip stores + * all received packets. + */ + +#define RL_RX_BUF_SZ RL_RXBUF_64 +#define RL_RXBUFLEN (1 << ((RL_RX_BUF_SZ >> 11) + 13)) +#define RL_TX_LIST_CNT 4 +#define RL_MIN_FRAMELEN 60 +#define RL_TXTHRESH(x) ((x) << 11) +#define RL_TX_THRESH_INIT 96 +#define RL_RX_FIFOTHRESH RL_RXFIFO_NOTHRESH +#define RL_RX_MAXDMA RL_RXDMA_UNLIMITED +#define RL_TX_MAXDMA RL_TXDMA_2048BYTES + +#define RL_RXCFG_CONFIG (RL_RX_FIFOTHRESH|RL_RX_MAXDMA|RL_RX_BUF_SZ) +#define RL_TXCFG_CONFIG (RL_TXCFG_IFG|RL_TX_MAXDMA) + +#define RL_ETHER_ALIGN 2 + +struct rl_chain_data { + uint16_t cur_rx; + uint8_t *rl_rx_buf; + uint8_t *rl_rx_buf_ptr; + bus_dmamap_t rl_rx_dmamap; + + struct mbuf *rl_tx_chain[RL_TX_LIST_CNT]; + bus_dmamap_t rl_tx_dmamap[RL_TX_LIST_CNT]; + uint8_t last_tx; + uint8_t cur_tx; +}; + +#define RL_INC(x) (x = (x + 1) % RL_TX_LIST_CNT) +#define RL_CUR_TXADDR(x) ((x->rl_cdata.cur_tx * 4) + RL_TXADDR0) +#define RL_CUR_TXSTAT(x) ((x->rl_cdata.cur_tx * 4) + RL_TXSTAT0) +#define RL_CUR_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.cur_tx]) +#define RL_CUR_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.cur_tx]) +#define RL_LAST_TXADDR(x) ((x->rl_cdata.last_tx * 4) + RL_TXADDR0) +#define RL_LAST_TXSTAT(x) ((x->rl_cdata.last_tx * 4) + RL_TXSTAT0) +#define RL_LAST_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.last_tx]) +#define RL_LAST_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.last_tx]) + +struct rl_type { + uint16_t rl_vid; + uint16_t rl_did; + int rl_basetype; + char *rl_name; +}; + +struct rl_hwrev { + uint32_t rl_rev; + int rl_type; + char *rl_desc; +}; + +struct rl_mii_frame { + uint8_t mii_stdelim; + uint8_t mii_opcode; + uint8_t mii_phyaddr; + uint8_t mii_regaddr; + uint8_t mii_turnaround; + uint16_t mii_data; +}; + +/* + * MII constants + */ +#define RL_MII_STARTDELIM 0x01 +#define RL_MII_READOP 0x02 +#define RL_MII_WRITEOP 0x01 +#define RL_MII_TURNAROUND 0x02 + +#define RL_8129 1 +#define RL_8139 2 +#define RL_8139CPLUS 3 +#define RL_8169 4 + +#define RL_ISCPLUS(x) ((x)->rl_type == RL_8139CPLUS || \ + (x)->rl_type == RL_8169) + +/* + * The 8139C+ and 8160 gigE chips support descriptor-based TX + * and RX. In fact, they even support TCP large send. Descriptors + * must be allocated in contiguous blocks that are aligned on a + * 256-byte boundary. The rings can hold a maximum of 64 descriptors. + */ + +/* + * RX/TX descriptor definition. When large send mode is enabled, the + * lower 11 bits of the TX rl_cmd word are used to hold the MSS, and + * the checksum offload bits are disabled. The structure layout is + * the same for RX and TX descriptors + */ + +struct rl_desc { + uint32_t rl_cmdstat; + uint32_t rl_vlanctl; + uint32_t rl_bufaddr_lo; + uint32_t rl_bufaddr_hi; +}; + +#define RL_TDESC_CMD_FRAGLEN 0x0000FFFF +#define RL_TDESC_CMD_TCPCSUM 0x00010000 /* TCP checksum enable */ +#define RL_TDESC_CMD_UDPCSUM 0x00020000 /* UDP checksum enable */ +#define RL_TDESC_CMD_IPCSUM 0x00040000 /* IP header checksum enable */ +#define RL_TDESC_CMD_MSSVAL 0x07FF0000 /* Large send MSS value */ +#define RL_TDESC_CMD_LGSEND 0x08000000 /* TCP large send enb */ +#define RL_TDESC_CMD_EOF 0x10000000 /* end of frame marker */ +#define RL_TDESC_CMD_SOF 0x20000000 /* start of frame marker */ +#define RL_TDESC_CMD_EOR 0x40000000 /* end of ring marker */ +#define RL_TDESC_CMD_OWN 0x80000000 /* chip owns descriptor */ + +#define RL_TDESC_VLANCTL_TAG 0x00020000 /* Insert VLAN tag */ +#define RL_TDESC_VLANCTL_DATA 0x0000FFFF /* TAG data */ + +/* + * Error bits are valid only on the last descriptor of a frame + * (i.e. RL_TDESC_CMD_EOF == 1) + */ + +#define RL_TDESC_STAT_COLCNT 0x000F0000 /* collision count */ +#define RL_TDESC_STAT_EXCESSCOL 0x00100000 /* excessive collisions */ +#define RL_TDESC_STAT_LINKFAIL 0x00200000 /* link faulure */ +#define RL_TDESC_STAT_OWINCOL 0x00400000 /* out-of-window collision */ +#define RL_TDESC_STAT_TXERRSUM 0x00800000 /* transmit error summary */ +#define RL_TDESC_STAT_UNDERRUN 0x02000000 /* TX underrun occured */ +#define RL_TDESC_STAT_OWN 0x80000000 + +/* + * RX descriptor cmd/vlan definitions + */ + +#define RL_RDESC_CMD_EOR 0x40000000 +#define RL_RDESC_CMD_OWN 0x80000000 +#define RL_RDESC_CMD_BUFLEN 0x00001FFF + +#define RL_RDESC_STAT_OWN 0x80000000 +#define RL_RDESC_STAT_EOR 0x40000000 +#define RL_RDESC_STAT_SOF 0x20000000 +#define RL_RDESC_STAT_EOF 0x10000000 +#define RL_RDESC_STAT_FRALIGN 0x08000000 /* frame alignment error */ +#define RL_RDESC_STAT_MCAST 0x04000000 /* multicast pkt received */ +#define RL_RDESC_STAT_UCAST 0x02000000 /* unicast pkt received */ +#define RL_RDESC_STAT_BCAST 0x01000000 /* broadcast pkt received */ +#define RL_RDESC_STAT_BUFOFLOW 0x00800000 /* out of buffer space */ +#define RL_RDESC_STAT_FIFOOFLOW 0x00400000 /* FIFO overrun */ +#define RL_RDESC_STAT_GIANT 0x00200000 /* pkt > 4096 bytes */ +#define RL_RDESC_STAT_RXERRSUM 0x00100000 /* RX error summary */ +#define RL_RDESC_STAT_RUNT 0x00080000 /* runt packet received */ +#define RL_RDESC_STAT_CRCERR 0x00040000 /* CRC error */ +#define RL_RDESC_STAT_PROTOID 0x00030000 /* Protocol type */ +#define RL_RDESC_STAT_IPSUMBAD 0x00008000 /* IP header checksum bad */ +#define RL_RDESC_STAT_UDPSUMBAD 0x00004000 /* UDP checksum bad */ +#define RL_RDESC_STAT_TCPSUMBAD 0x00002000 /* TCP checksum bad */ +#define RL_RDESC_STAT_FRAGLEN 0x00001FFF /* RX'ed frame/frag len */ +#define RL_RDESC_STAT_GFRAGLEN 0x00003FFF /* RX'ed frame/frag len */ +#define RL_RDESC_STAT_ERRS (RL_RDESC_STAT_GIANT|RL_RDESC_STAT_RUNT| \ + RL_RDESC_STAT_CRCERR) + +#define RL_RDESC_VLANCTL_TAG 0x00010000 /* VLAN tag available + (rl_vlandata valid)*/ +#define RL_RDESC_VLANCTL_DATA 0x0000FFFF /* TAG data */ + +#define RL_PROTOID_NONIP 0x00000000 +#define RL_PROTOID_TCPIP 0x00010000 +#define RL_PROTOID_UDPIP 0x00020000 +#define RL_PROTOID_IP 0x00030000 +#define RL_TCPPKT(x) (((x) & RL_RDESC_STAT_PROTOID) == \ + RL_PROTOID_TCPIP) +#define RL_UDPPKT(x) (((x) & RL_RDESC_STAT_PROTOID) == \ + RL_PROTOID_UDPIP) + +/* + * Statistics counter structure (8139C+ and 8169 only) + */ +struct rl_stats { + uint32_t rl_tx_pkts_lo; + uint32_t rl_tx_pkts_hi; + uint32_t rl_tx_errs_lo; + uint32_t rl_tx_errs_hi; + uint32_t rl_tx_errs; + uint16_t rl_missed_pkts; + uint16_t rl_rx_framealign_errs; + uint32_t rl_tx_onecoll; + uint32_t rl_tx_multicolls; + uint32_t rl_rx_ucasts_hi; + uint32_t rl_rx_ucasts_lo; + uint32_t rl_rx_bcasts_lo; + uint32_t rl_rx_bcasts_hi; + uint32_t rl_rx_mcasts; + uint16_t rl_tx_aborts; + uint16_t rl_rx_underruns; +}; + +/* + * Rx/Tx descriptor parameters (8139C+ and 8169 only) + * + * Tx/Rx count must be equal. Shared code like re_dma_map_desc assumes this. + * Buffers must be a multiple of 8 bytes. Currently limit to 64 descriptors + * due to the 8139C+. We need to put the number of descriptors in the ring + * structure and use that value instead. + */ +#if !defined(__i386__) && !defined(__amd64__) +#define RE_FIXUP_RX 1 +#endif + +#define RL_TX_DESC_CNT 64 +#define RL_TX_DESC_THLD 4 +#define RL_RX_DESC_CNT RL_TX_DESC_CNT + +#define RL_RX_LIST_SZ (RL_RX_DESC_CNT * sizeof(struct rl_desc)) +#define RL_TX_LIST_SZ (RL_TX_DESC_CNT * sizeof(struct rl_desc)) +#define RL_RING_ALIGN 256 +#define RL_IFQ_MAXLEN 512 +#define RL_DESC_INC(x) (x = (x + 1) % RL_TX_DESC_CNT) +#define RL_OWN(x) (le32toh((x)->rl_cmdstat) & RL_RDESC_STAT_OWN) +#define RL_RXBYTES(x) (le32toh((x)->rl_cmdstat) & sc->rl_rxlenmask) +#define RL_PKTSZ(x) ((x)/* >> 3*/) +#ifdef RE_FIXUP_RX +#define RE_ETHER_ALIGN sizeof(uint64_t) +#define RE_RX_DESC_BUFLEN (MCLBYTES - RE_ETHER_ALIGN) +#else +#define RE_ETHER_ALIGN 0 +#define RE_RX_DESC_BUFLEN MCLBYTES +#endif + +#define RL_ADDR_LO(y) ((uint64_t) (y) & 0xFFFFFFFF) +#define RL_ADDR_HI(y) ((uint64_t) (y) >> 32) + +/* see comment in dev/re/if_re.c */ +#define RL_JUMBO_FRAMELEN 7440 +#define RL_JUMBO_MTU (RL_JUMBO_FRAMELEN-ETHER_HDR_LEN-ETHER_CRC_LEN) + +struct rl_softc; + +struct rl_dmaload_arg { + int rl_idx; + int rl_maxsegs; + uint32_t rl_flags; + struct rl_desc *rl_ring; +}; + +struct rl_list_data { + struct mbuf *rl_tx_mbuf[RL_TX_DESC_CNT]; + struct mbuf *rl_rx_mbuf[RL_RX_DESC_CNT]; + int rl_tx_prodidx; + int rl_rx_prodidx; + int rl_tx_considx; + int rl_tx_free; + bus_dmamap_t rl_tx_dmamap[RL_TX_DESC_CNT]; + bus_dmamap_t rl_rx_dmamap[RL_RX_DESC_CNT]; + bus_dma_tag_t rl_mtag; /* mbuf mapping tag */ + bus_dma_tag_t rl_stag; /* stats mapping tag */ + bus_dmamap_t rl_smap; /* stats map */ + struct rl_stats *rl_stats; + bus_addr_t rl_stats_addr; + bus_dma_tag_t rl_rx_list_tag; + bus_dmamap_t rl_rx_list_map; + struct rl_desc *rl_rx_list; + bus_addr_t rl_rx_list_addr; + bus_dma_tag_t rl_tx_list_tag; + bus_dmamap_t rl_tx_list_map; + struct rl_desc *rl_tx_list; + bus_addr_t rl_tx_list_addr; +}; + +struct rl_softc { + struct ifnet *rl_ifp; /* interface info */ + bus_space_handle_t rl_bhandle; /* bus space handle */ + bus_space_tag_t rl_btag; /* bus space tag */ + device_t rl_dev; + struct resource *rl_res; + struct resource *rl_irq; + void *rl_intrhand; + device_t rl_miibus; + bus_dma_tag_t rl_parent_tag; + bus_dma_tag_t rl_tag; + uint8_t rl_type; + int rl_eecmd_read; + int rl_eewidth; + uint8_t rl_stats_no_timeout; + int rl_txthresh; + struct rl_chain_data rl_cdata; + struct rl_list_data rl_ldata; + struct callout rl_stat_callout; + int rl_watchdog_timer; + struct mtx rl_mtx; + struct mbuf *rl_head; + struct mbuf *rl_tail; + uint32_t rl_hwrev; + uint32_t rl_rxlenmask; + int rl_testmode; + int rl_if_flags; + int suspended; /* 0 = normal 1 = suspended */ +#ifdef DEVICE_POLLING + int rxcycles; +#endif + + struct task rl_txtask; + struct task rl_inttask; + + struct mtx rl_intlock; + int rl_txstart; + int rl_link; +}; + +#define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx) +#define RL_UNLOCK(_sc) mtx_unlock(&(_sc)->rl_mtx) +#define RL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->rl_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define CSR_WRITE_STREAM_4(sc, reg, val) \ + bus_space_write_stream_4(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->rl_btag, sc->rl_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->rl_btag, sc->rl_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->rl_btag, sc->rl_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->rl_btag, sc->rl_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->rl_btag, sc->rl_bhandle, reg) + +#define CSR_SETBIT_1(sc, offset, val) \ + CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) | (val)) + +#define CSR_CLRBIT_1(sc, offset, val) \ + CSR_WRITE_1(sc, offset, CSR_READ_1(sc, offset) & ~(val)) + +#define CSR_SETBIT_2(sc, offset, val) \ + CSR_WRITE_2(sc, offset, CSR_READ_2(sc, offset) | (val)) + +#define CSR_CLRBIT_2(sc, offset, val) \ + CSR_WRITE_2(sc, offset, CSR_READ_2(sc, offset) & ~(val)) + +#define CSR_SETBIT_4(sc, offset, val) \ + CSR_WRITE_4(sc, offset, CSR_READ_4(sc, offset) | (val)) + +#define CSR_CLRBIT_4(sc, offset, val) \ + CSR_WRITE_4(sc, offset, CSR_READ_4(sc, offset) & ~(val)) + +#define RL_TIMEOUT 1000 + +/* + * General constants that are fun to know. + * + * RealTek PCI vendor ID + */ +#define RT_VENDORID 0x10EC + +/* + * RealTek chip device IDs. + */ +#define RT_DEVICEID_8139D 0x8039 +#define RT_DEVICEID_8129 0x8129 +#define RT_DEVICEID_8101E 0x8136 +#define RT_DEVICEID_8138 0x8138 +#define RT_DEVICEID_8139 0x8139 +#define RT_DEVICEID_8169SC 0x8167 +#define RT_DEVICEID_8168 0x8168 +#define RT_DEVICEID_8169 0x8169 +#define RT_DEVICEID_8100 0x8100 + +#define RT_REVID_8139CPLUS 0x20 + +/* + * Accton PCI vendor ID + */ +#define ACCTON_VENDORID 0x1113 + +/* + * Accton MPX 5030/5038 device ID. + */ +#define ACCTON_DEVICEID_5030 0x1211 + +/* + * Nortel PCI vendor ID + */ +#define NORTEL_VENDORID 0x126C + +/* + * Delta Electronics Vendor ID. + */ +#define DELTA_VENDORID 0x1500 + +/* + * Delta device IDs. + */ +#define DELTA_DEVICEID_8139 0x1360 + +/* + * Addtron vendor ID. + */ +#define ADDTRON_VENDORID 0x4033 + +/* + * Addtron device IDs. + */ +#define ADDTRON_DEVICEID_8139 0x1360 + +/* + * D-Link vendor ID. + */ +#define DLINK_VENDORID 0x1186 + +/* + * D-Link DFE-530TX+ device ID + */ +#define DLINK_DEVICEID_530TXPLUS 0x1300 + +/* + * D-Link DFE-5280T device ID + */ +#define DLINK_DEVICEID_528T 0x4300 + +/* + * D-Link DFE-690TXD device ID + */ +#define DLINK_DEVICEID_690TXD 0x1340 + +/* + * Corega K.K vendor ID + */ +#define COREGA_VENDORID 0x1259 + +/* + * Corega FEther CB-TXD device ID + */ +#define COREGA_DEVICEID_FETHERCBTXD 0xa117 + +/* + * Corega FEtherII CB-TXD device ID + */ +#define COREGA_DEVICEID_FETHERIICBTXD 0xa11e + +/* + * Corega CG-LAPCIGT device ID + */ +#define COREGA_DEVICEID_CGLAPCIGT 0xc107 + +/* + * Linksys vendor ID + */ +#define LINKSYS_VENDORID 0x1737 + +/* + * Linksys EG1032 device ID + */ +#define LINKSYS_DEVICEID_EG1032 0x1032 + +/* + * Linksys EG1032 rev 3 sub-device ID + */ +#define LINKSYS_SUBDEVICE_EG1032_REV3 0x0024 + +/* + * Peppercon vendor ID + */ +#define PEPPERCON_VENDORID 0x1743 + +/* + * Peppercon ROL-F device ID + */ +#define PEPPERCON_DEVICEID_ROLF 0x8139 + +/* + * Planex Communications, Inc. vendor ID + */ +#define PLANEX_VENDORID 0x14ea + +/* + * Planex FNW-3603-TX device ID + */ +#define PLANEX_DEVICEID_FNW3603TX 0xab06 + +/* + * Planex FNW-3800-TX device ID + */ +#define PLANEX_DEVICEID_FNW3800TX 0xab07 + +/* + * LevelOne vendor ID + */ +#define LEVEL1_VENDORID 0x018A + +/* + * LevelOne FPC-0106TX devide ID + */ +#define LEVEL1_DEVICEID_FPC0106TX 0x0106 + +/* + * Compaq vendor ID + */ +#define CP_VENDORID 0x021B + +/* + * Edimax vendor ID + */ +#define EDIMAX_VENDORID 0x13D1 + +/* + * Edimax EP-4103DL cardbus device ID + */ +#define EDIMAX_DEVICEID_EP4103DL 0xAB06 + +/* US Robotics vendor ID */ + +#define USR_VENDORID 0x16EC + +/* US Robotics 997902 device ID */ + +#define USR_DEVICEID_997902 0x0116 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define RL_PCI_VENDOR_ID 0x00 +#define RL_PCI_DEVICE_ID 0x02 +#define RL_PCI_COMMAND 0x04 +#define RL_PCI_STATUS 0x06 +#define RL_PCI_CLASSCODE 0x09 +#define RL_PCI_LATENCY_TIMER 0x0D +#define RL_PCI_HEADER_TYPE 0x0E +#define RL_PCI_LOIO 0x10 +#define RL_PCI_LOMEM 0x14 +#define RL_PCI_BIOSROM 0x30 +#define RL_PCI_INTLINE 0x3C +#define RL_PCI_INTPIN 0x3D +#define RL_PCI_MINGNT 0x3E +#define RL_PCI_MINLAT 0x0F +#define RL_PCI_RESETOPT 0x48 +#define RL_PCI_EEPROM_DATA 0x4C + +#define RL_PCI_CAPID 0x50 /* 8 bits */ +#define RL_PCI_NEXTPTR 0x51 /* 8 bits */ +#define RL_PCI_PWRMGMTCAP 0x52 /* 16 bits */ +#define RL_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ + +#define RL_PSTATE_MASK 0x0003 +#define RL_PSTATE_D0 0x0000 +#define RL_PSTATE_D1 0x0002 +#define RL_PSTATE_D2 0x0002 +#define RL_PSTATE_D3 0x0003 +#define RL_PME_EN 0x0010 +#define RL_PME_STATUS 0x8000 diff --git a/bsd_eth_drivers/libbsdport/Makefile.am b/bsd_eth_drivers/libbsdport/Makefile.am new file mode 100644 index 0000000..44096b5 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/Makefile.am @@ -0,0 +1,75 @@ +# $Id$ +AUTOMAKE_OPTIONS=foreign + +include $(top_srcdir)/rtems-pre.am + +libbsdport_a_SOURCES = rtems_callout.c rtems_taskqueue.c rtems_udelay.c +libbsdport_a_SOURCES += ifstuff.c devicet.c alldrv.c contigmalloc.c +libbsdport_a_SOURCES += sysbus.c malloc.c ifmedia.c + +libbsdport_a_SOURCES += rtems_verscheck.h bus.h callout.h devicet.h +libbsdport_a_SOURCES += libbsdport.h libbsdport_post.h mutex.h +libbsdport_a_SOURCES += rtems_udelay.h rtems_verscheck.h taskqueue.h + +include_bsp_HEADERS = libbsdport_api.h + +lib_LIBRARIES = libbsdport.a + +include $(top_srcdir)/rtems.am + +LINKS = +LINKS+=sys/taskqueue.h +LINKS+=sys/bus.h +LINKS+=sys/mutex.h +LINKS+=bsp/rtems_verscheck.h + +DUMMYHEADERS = +DUMMYHEADERS+=dummyheaders/machine/bus.h +DUMMYHEADERS+=dummyheaders/machine/resource.h +DUMMYHEADERS+=dummyheaders/dev/pci/pcivar.h +DUMMYHEADERS+=dummyheaders/dev/pci/pcireg.h +DUMMYHEADERS+=dummyheaders/dev/mii/miivar.h +DUMMYHEADERS+=dummyheaders/sys/module.h +DUMMYHEADERS+=dummyheaders/sys/rman.h +DUMMYHEADERS+=dummyheaders/sys/kthread.h +DUMMYHEADERS+=dummyheaders/sys/endian.h +DUMMYHEADERS+=dummyheaders/net/if_vlan_var.h +DUMMYHEADERS+=dummyheaders/netinet/ip6.h +DUMMYHEADERS+=dummyheaders/vm/pmap.h +DUMMYHEADERS+=dummyheaders/miibus_if.h + +DUMMYHEADERS+=dummyheaders/miidevs.h +DUMMYHEADERS+=dummyheaders/dev/mii/brgphyreg.h + +BUILT_SOURCES= + +include ../links.am + +# +# I found no good way to have something made first thing. +# all-local is made after 'all' and xxx_DEPENDENCIES are +# after OBJECTS are made :-( +$(libbsdport_a_OBJECTS): $(DUMMYHEADERS) $(LINKS) + +if FALSE +CLOBBER_ADDITIONS = +CLOBBER_ADDITIONS += $(srcdir)/dummyheaders +CLOBBER_ADDITIONS += $(addprefix $(srcdir)/,$(sort $(foreach n,$(LINKS),$(firstword $(subst /, ,$(n)))))) + +dummyheaders/%: + @if [ ! -d $(srcdir)/`dirname $@` ] ; then mkdir -p $(srcdir)/`dirname $@`; fi + @touch $(srcdir)/$@ + +# for each name listed in LINKS, create parent directories (if needed) +# and a symlink to file in . +# E.g., LINKS=a/b/c.h +# creates a/b/c.h -> ../../c.h +$(LINKS): + @if [ ! -d $(srcdir)/$(dir $@) ] ; then mkdir -p $(srcdir)/$(dir $@); fi + @ln -s `echo $@ | sed -e 's%[^/]\+[/]\+%../%g'` $(srcdir)/$@ + + +distclean-local: + $(RM) -r $(CLOBBER_ADDITIONS) +endif + diff --git a/bsd_eth_drivers/libbsdport/README b/bsd_eth_drivers/libbsdport/README new file mode 100644 index 0000000..14fd4a5 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/README @@ -0,0 +1,29 @@ +This is a library and collection of header files +intended to ease porting recent FreeBSD (as of 7/2007) +drivers to RTEMS. Currently, only the PCI bus is supported. + +A lot of macro-magic is used in the 'libbsdport.h' +and 'libbsdport_post.h' headers which are te be included +from the driver source file prior and after the driver +includes the normal headers it uses: + +At the top of if_xxx.c add: + +#ifdef __rtems__ +#include +#endif + +... leave all normal include statements in place... +#include + +lastly, add: + +#ifdef __rtems__ +#include +#endif + +Note that libbsdport does not provide the full freebsd +kernel API but only a subset. + +<< MORE TO COME >> + diff --git a/bsd_eth_drivers/libbsdport/alldrv.c b/bsd_eth_drivers/libbsdport/alldrv.c new file mode 100644 index 0000000..0738179 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/alldrv.c @@ -0,0 +1,16 @@ +#include +#include "libbsdport_api.h" + +driver_t *libbsdport_netdriver_table_all[] = { + &libbsdport_em_driver, + &libbsdport_pcn_driver, + &libbsdport_le_pci_driver, + &libbsdport_re_driver, + 0 +}; + +/* weak alias defaults to a table that includes all currently supported drivers */ +extern driver_t *libbsdport_netdriver_table + [ + sizeof(libbsdport_netdriver_table_all)/sizeof(libbsdport_netdriver_table_all[0]) + ] __attribute__((weak,alias("libbsdport_netdriver_table_all"))); diff --git a/bsd_eth_drivers/libbsdport/bus.h b/bsd_eth_drivers/libbsdport/bus.h new file mode 100644 index 0000000..76d78a8 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/bus.h @@ -0,0 +1,324 @@ +#ifndef LIBBSDPORT_SYS_BUS_H +#define LIBBSDPORT_SYS_BUS_H + +#include +#include +#include +#include +#include + +typedef uint32_t bus_addr_t; +typedef size_t bus_size_t; + +typedef enum { + bus_space_mem = 0, + bus_space_io = 1 +} bus_space_tag_t; + +struct resource; + +typedef bus_addr_t bus_space_handle_t; + +/* The 'bus_space_xxx()' inlines can be helped if the + * tag is hardcoded in the driver so that the compiler + * can optimize part of the implementation away. + */ + +#define BUS_SPACE_BARRIER_WRITE 1 +#define BUS_SPACE_BARRIER_READ 2 + +#if defined(__i386__) + +#include + +static inline void +bus_space_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, int width, int type) +{ +} + +#define BUS_SPACE_DECL(type, width, nwidth) \ +static inline type \ +bus_space_read_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) \ +{ \ +type v; \ + if ( bus_space_io == t ) { \ + /* this is a macro setting the second argument */ \ + inport_##width( h+o, v ); \ + } else { \ + v = *(volatile type __attribute__((may_alias)) *)(h+o); \ + } \ + return v; \ +} \ + \ +static inline void \ +bus_space_write_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, type v) \ +{ \ + if ( bus_space_io == t ) { \ + outport_##width( h+o, v ); \ + } else { \ + *(volatile type __attribute__((may_alias)) *)(h+o) = v; \ + }\ +} + +BUS_SPACE_DECL(u_int32_t, long, 4) +BUS_SPACE_DECL(u_int16_t, word, 2) +BUS_SPACE_DECL(u_int8_t, byte, 1) + +#elif defined(__PPC__) + +#include + +#if defined(_IO_BASE) && _IO_BASE == 0 +#define BUS_SPACE_ALWAYS_MEM 1 +#else +#define BUS_SPACE_ALWAYS_MEM 0 +#endif + +static inline void +bus_space_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, int width, int type) +{ + asm volatile("eieio"); +} + + +#define BUS_SPACE_DECL(type, width, nwidth, op) \ +static inline type \ +bus_space_read_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) \ +{ \ +type v; \ + if ( !BUS_SPACE_ALWAYS_MEM && bus_space_io == t ) { \ + /* this is a macro setting the second argument */ \ + v = in_##op((volatile type *)(_IO_BASE+h+o)); \ + } else { \ + v = in_##op((volatile type *)(h+o)); \ + } \ + return v; \ +} \ + \ +static inline void \ +bus_space_write_##nwidth(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, type v) \ +{ \ + if ( !BUS_SPACE_ALWAYS_MEM && bus_space_io == t ) { \ + out_##op((volatile type *)(_IO_BASE+h+o), v); \ + } else { \ + out_##op((volatile type *)(h+o), v); \ + }\ +} + +BUS_SPACE_DECL(u_int32_t, long, 4, le32) +BUS_SPACE_DECL(u_int16_t, word, 2, le16) +BUS_SPACE_DECL(u_int8_t, byte, 1, 8) + +#undef BUS_SPACE_ALWAYS_MEM + +#else +#error "Missing definitions of bus_space_XXX() for this CPU architecture" +#endif + + +#undef BUS_SPACE_DECL + +#ifndef BUS_PROBE_DEFAULT +#define BUS_PROBE_DEFAULT 0 +#endif + +/* error codes are > 0 ; low priority says that probe + * was successful but another driver returning BUS_PROBE_DEFAULT + * is to be preferred... + */ +#ifndef BUS_PROBE_LOW_PRIORITY +#define BUS_PROBE_LOW_PRIORITY (-1) +#endif + + + +/* types -> -1 means unsupported */ +#define SYS_RES_IOPORT 1 +#define SYS_RES_MEMORY 2 +#define SYS_RES_IRQ 3 + +/* flags (1<<31) means unsupported */ +#define RF_ACTIVE (1<<1) +#define RF_SHAREABLE (1<<2) + +struct resource * +bus_alloc_resource_any(device_t dev, int type, int *prid, unsigned flags); + +#define FILTER_STRAY 1 +#define FILTER_HANDLED 0 + +typedef void (*driver_intr_t)(void *); +typedef int (*driver_filter_t)(void *); + +int +bus_setup_intr(device_t dev, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep); + +/* Flags currently ignored... */ +#define INTR_MPSAFE 0 +#define INTR_TYPE_NET 0 +/* INTR_FAST indicates that a 'handler' is actually + * a 'fast' handler which already uses taskqueues + */ +#define INTR_FAST 1 + +int +bus_teardown_intr(device_t dev, struct resource *r, void *cookiep); + +static inline int +bus_release_resource(device_t dev, int type, int rid, struct resource *r) +{ + return 0; +} + +#define bus_generic_detach(dev) do {} while (0) + +#define bus_generic_suspend(dev) (0) +#define bus_generic_resume(dev) (0) + +bus_space_handle_t +rman_get_bushandle(struct resource *r); + +bus_space_tag_t +rman_get_bustag(struct resource *r); + +#ifndef BUS_DMA_NOWAIT +/* ignored anyways */ +#define BUS_DMA_NOWAIT 0 +#endif + +#ifndef BUS_DMA_WAITOK +/* ignored anyways */ +#define BUS_DMA_WAITOK 0 +#endif + +#ifndef BUS_DMA_COHERENT +/* ignored anyways */ +#define BUS_DMA_COHERENT 0 +#endif + +#ifndef BUS_DMA_ZERO +/* ignored anyways */ +#define BUS_DMA_ZERO 0 +#endif + +#ifndef BUS_DMA_ALLOCNOW +/* ignored anyways */ +#define BUS_DMA_ALLOCNOW 0 +#endif + +/* unused */ +#ifndef BUS_SPACE_MAXADDR +#define BUS_SPACE_MAXADDR 0xdeadbeef +#endif + +/* unused */ +#ifndef BUS_SPACE_MAXADDR_32BIT +#define BUS_SPACE_MAXADDR_32BIT 0xdeadbeef +#endif + +/* unused */ +#ifndef BUS_SPACE_MAXSIZE_32BIT +#define BUS_SPACE_MAXSIZE_32BIT 0x10000000 +#endif + +typedef struct _bus_dma_tag_t { + unsigned alignment; + unsigned maxsize; + unsigned maxsegs; +} * bus_dma_tag_t; + +typedef struct _bus_dma_segment_t { + bus_addr_t ds_addr; + bus_size_t ds_len; +} bus_dma_segment_t; + +typedef void *bus_dmamap_t; + +int +bus_dma_tag_create(void *parent, unsigned alignment, unsigned bounds, uint32_t lowadd, uint32_t hiaddr, void (*filter)(void*), void *filterarg, unsigned maxsize, int nsegs, unsigned maxsegsize, unsigned flags, void (*lockfunc)(void*), void *lockarg, bus_dma_tag_t *ptag); + +void +bus_dma_tag_destroy(bus_dma_tag_t tag); + +int +bus_dmamem_alloc(bus_dma_tag_t tag, void **p_vaddr, unsigned flags, bus_dmamap_t *p_map); + +void +bus_dmamem_free(bus_dma_tag_t tag, void *vaddr, bus_dmamap_t map); + +#ifndef CPU2BUSADDR +#ifndef PCI_DRAM_OFFSET +#define PCI_DRAM_OFFSET 0 +#endif +#define CPU2BUSADDR(x) ((uint32_t)(x) + (PCI_DRAM_OFFSET)) +#endif + +#define kvtop(a) CPU2BUSADDR((bus_addr_t)(a)) +#define vtophys(a) CPU2BUSADDR((bus_addr_t)(a)) + + +static inline int +bus_dmamap_load_mbuf_sg(bus_dma_tag_t tag, bus_dmamap_t map, struct mbuf *m_head, bus_dma_segment_t *segs, int *pnsegs, unsigned flags) +{ +struct mbuf *m; +int n; + for ( m=m_head, n=0; m; m=m->m_next, n++ ) { + if ( n >= tag->maxsegs ) { + return EFBIG; + } + segs[n].ds_addr = CPU2BUSADDR(mtod(m, unsigned)); + segs[n].ds_len = m->m_len; + } + *pnsegs = n; + return 0; +} + +static inline bus_dma_tag_t +bus_get_dma_tag(device_t dev) +{ + return 0; +} + +typedef void bus_dmamap_callback_t (void *arg, bus_dma_segment_t *segs, int nseg, int error); + +static inline int +bus_dmamap_load(bus_dma_tag_t tag, bus_dmamap_t map, caddr_t vaddr, bus_size_t size, bus_dmamap_callback_t cb, void *arg, unsigned flags) +{ +bus_dma_segment_t segs[1]; + segs[0].ds_addr = CPU2BUSADDR(vaddr); + segs[0].ds_len = size; + cb(arg, segs, 1, 0); + return 0; +} + +typedef void bus_dmamap_callback2_t (void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize, int error); + +static inline int +bus_dmamap_load_mbuf(bus_dma_tag_t tag, bus_dmamap_t map, struct mbuf *m_head, bus_dmamap_callback2_t cb, void *arg, unsigned flags) +{ +/* hopefully there's enough stack ... */ +bus_dma_segment_t segs[tag->maxsegs]; +struct mbuf *m; +int n; +bus_size_t sz; + for ( m=m_head, sz=0, n=0; m; m=m->m_next, n++ ) { + if ( n >= tag->maxsegs ) { + cb(arg, segs, n, sz, EFBIG); + return EFBIG; + } + segs[n].ds_addr = CPU2BUSADDR(mtod(m, unsigned)); + sz += (segs[n].ds_len = m->m_len); + } + cb(arg, segs, n, sz, 0); + return 0; +} + +#define bus_dmamap_unload(tag, map) do {} while (0) + +/* should we do something if we have no HW snooping ? */ +#define bus_dmamap_sync(tag, map, flags) do { membarrier_rw(); } while (0) + +#define bus_dmamap_create(tag, flags, pmap) ( *(pmap) = 0, 0 ) +#define bus_dmamap_destroy(tag, map) do {} while (0) + +#endif diff --git a/bsd_eth_drivers/libbsdport/callout.h b/bsd_eth_drivers/libbsdport/callout.h new file mode 100644 index 0000000..3f586bf --- /dev/null +++ b/bsd_eth_drivers/libbsdport/callout.h @@ -0,0 +1,43 @@ +#ifndef _SYS_CALLOUT_H +#define _SYS_CALLOUT_H /* include this to override rtems stack's */ + +/* RTEMS systm.h still declares old timout stuff which is not + * fully compatible with more recent 'callout' functionality. + * + * Also: our struct callout it incompatible with the one + * declared in rtems' sys/callout.h. + * Make sure to include the proper header (first). + */ + +typedef unsigned callout_time_t; + +struct callout { + struct callout *c_next; + struct callout **c_pprev; + void (*c_func)(void*); + void *c_arg; + struct mtx *c_mtx; + callout_time_t c_time; +}; + +/* We cannot stop a callout that's in progress */ + +void +callout_stop(struct callout *c); + +#define callout_drain callout_stop + +void +callout_reset(struct callout *c, int ticks, void (*fn)(void*), void *arg); + +void +callout_init(struct callout *c, int mpsafe); + +void +callout_init_mtx(struct callout *c, struct mtx *m, unsigned flags); + +/* Initialize callout facility [networking must have been initialized already] */ +rtems_id +rtems_callout_initialize(); + +#endif diff --git a/bsd_eth_drivers/libbsdport/contigmalloc.c b/bsd_eth_drivers/libbsdport/contigmalloc.c new file mode 100644 index 0000000..7c9f1b7 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/contigmalloc.c @@ -0,0 +1,33 @@ +#include + +#define _KERNEL +#include +#include + +void * +contigmalloc( + unsigned long size, + int type, + int flags, + unsigned long lo, + unsigned long hi, + unsigned long align, + unsigned long bound) +{ +void *ptr = rtems_bsdnet_malloc(size + sizeof(ptr) + align-1, type, flags); +char *rval = 0; + if ( ptr ) { + unsigned tmp = (unsigned)ptr + align - 1; + tmp -= tmp % align; + rval = (char*)tmp; + /* save backlink */ + *(void**)(rval+size) = ptr; + } + return rval; +} + +void +contigfree(void *ptr, size_t size, int type) +{ + rtems_bsdnet_free( *(void**)((unsigned)ptr + size), type); +} diff --git a/bsd_eth_drivers/libbsdport/devicet.c b/bsd_eth_drivers/libbsdport/devicet.c new file mode 100644 index 0000000..48ddd9a --- /dev/null +++ b/bsd_eth_drivers/libbsdport/devicet.c @@ -0,0 +1,374 @@ +#define DEVICET_EXTERN_INLINE + +#include "devicet.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbsdport_api.h" + +#define DEBUG + +extern void real_libc_free(void*); + +static STAILQ_HEAD(devq_t, device) devq = STAILQ_HEAD_INITIALIZER(devq); + +static device_t +devalloc(driver_t *dr) +{ +void *m; +device_t rval; +int l = sizeof(*rval) + dr->softc_size + DEVICE_SOFTC_ALIGNMENT -1; + + if ( !(m = malloc( l, M_DEVBUF, M_NOWAIT )) ) + return 0; + + memset(m, 0, l); + + rval = (device_t)(((uintptr_t)m + (DEVICE_SOFTC_ALIGNMENT-1)) & ~(DEVICE_SOFTC_ALIGNMENT-1)); + rval->rawmem = m; + rval->type = dr->type; + rval->name = dr->name; + rval->drv = dr; + + return rval; +} + +static void +devclean(device_t dev) +{ + assert( !dev->attached ); + memset(device_get_softc(dev), 0, dev->drv->softc_size); + real_libc_free(dev->desc); + dev->desc = 0; + dev->unit = 0; + dev->nameunit[0]=0; + memset( &dev->bushdr, 0, sizeof(dev->bushdr)); +} + +static void +devfree(device_t dev) +{ + /* paranoia */ + devclean(dev); + dev->drv = 0; + free(dev->rawmem, M_DEVBUF); +} + +static int +devattach(device_t dev, int unit, struct rtems_bsdnet_ifconfig *cfg) +{ +int error; + +#ifdef DEBUG + printf("Now attaching %s%d: (0x%x:%x.%x)\n", + dev->name, unit, + dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun); +#endif + + dev->unit = unit; + dev->ifconfig = cfg; + sprintf(dev->nameunit,"%s%d",dev->drv->name,unit); + + /* Try to attach */ + if ( (error = dev->drv->methods->attach(dev)) ) { + fprintf(stderr,"Attaching '%s%d' failed: %s", dev->drv->name, unit, strerror(error)); + return error; + } + /* Successfully attached new device */ + dev->attached = 1; + cfg->name = (char*)device_get_nameunit(dev); + STAILQ_INSERT_TAIL(&devq, dev, list); + return 0; +} + +static int +devequal(device_t a, device_t b) +{ + if ( a->type != b->type ) + return 0; + switch ( a->type ) { + case DEV_TYPE_PCI: + return a->bushdr.pci.bus == b->bushdr.pci.bus + && a->bushdr.pci.dev == b->bushdr.pci.dev + && a->bushdr.pci.fun == b->bushdr.pci.fun; + + default: + rtems_panic("devequal: Unsupported device type %i\n", a->type); + } + return 0; +} + +/* Check if a particular device is already listed */ +static device_t +devattached(device_t dev) +{ +struct device *ldev; + STAILQ_FOREACH(ldev, &devq, list) { + if ( devequal(ldev, dev) ) + return ldev; + } + return 0; +} + + +int +device_printf(device_t dev, const char *fmt, ...) +{ +int rval; +va_list ap; + rval = fprintf(stdout,"%s:",device_get_nameunit(dev)); + va_start(ap, fmt); + rval += vfprintf(stdout,fmt,ap); + va_end(ap); + return rval; +} + +static uint32_t +get_pci_triple(const char *drvnam) +{ +unsigned b,d,f; + if ( drvnam && 3 == sscanf(drvnam,"%i:%i.%i",&b,&d,&f) ) + return (b<<8) | PCI_DEVFN(d,f); + return -1; +} + +static void +get_name_unit(const char *drvnam, char *nm, int *punit) +{ +int l = strlen(drvnam); +int i; + if ( l > 0 ) { + for ( i=l-1; i>=0 && isdigit(drvnam[i]); i-- ) + /* nothing else to do */; + if ( 1 != sscanf(drvnam+i,"%d",punit) ) + *punit = 0; /* wildcard */ + strncpy(nm, drvnam, i+1); + nm[i+1]=0; + } else { + /* wildcards */ + *nm = 0; + *punit = 0; + } +} + +static int +matches(driver_t *dr, const char *pat) +{ + if ( 0 == *pat || '*' == *pat ) + return 1; + return !strcmp(pat, dr->name); +} + +static int +pci_slot_empty(int b, int d, int f) +{ +uint16_t id; + pci_read_config_word(b,d,f,PCI_VENDOR_ID,&id); + return ( 0xffff == id ); +} + +static int +pci_is_ether(int b, int d, int f) +{ +uint16_t dclass; + if ( pci_slot_empty(b,d,f) ) + return 0; + pci_read_config_word(b,d,f,PCI_CLASS_DEVICE, &dclass); + return PCI_CLASS_NETWORK_ETHERNET == dclass; +} + +/* this catches the case of an unpopulated slot (returning 0) */ +static int +pci_num_functions(int b, int d) +{ +uint8_t h; + if ( pci_slot_empty(b,d,0) ) + return 0; + pci_read_config_byte(b,d,0,PCI_HEADER_TYPE,&h); + return (h & 0x80) ? PCI_MAX_FUNCTIONS : 1; /* multifunction device ? */ +} + +int +libbsdport_netdriver_dump(FILE *f) +{ +struct device *ldev; +int ndevs; +unsigned w; + + if ( !f ) + f = stdout; + + ndevs = 0; + fprintf(f, "PCI Network device information:\n"); + rtems_bsdnet_semaphore_obtain(); + STAILQ_FOREACH(ldev, &devq, list) { + /* ASSUME LIST ELEMENTS DO NOT DISAPPEAR + * so we can release the lock while printing... + */ + rtems_bsdnet_semaphore_release(); + w=fprintf(f,"%-6s -- (0x%x:%x.%x)", + device_get_nameunit(ldev), + ldev->bushdr.pci.bus, + ldev->bushdr.pci.dev, + ldev->bushdr.pci.fun); + for ( ; w < 24 ; w++) + fputc(' ',f); + if ( ldev->desc ) + fprintf(f," %s",ldev->desc); + fputc('\n',f); + + + ndevs++; + rtems_bsdnet_semaphore_obtain(); + } + rtems_bsdnet_semaphore_release(); + return ndevs; +} + +#define UNITMATCH(wanted, unit, bdfunit) \ + ((wanted) < 0 ? ((wanted) & 0xffff) == (bdfunit) : (wanted) == (unit)) + +int +libbsdport_netdriver_attach(struct rtems_bsdnet_ifconfig *cfg, int attaching) +{ +char nm[20]; /* copy of the name */ +int unit,thisunit,wantedunit; +int i,b,d,f; +int prob; +driver_t *dr; +device_t dev = 0; +device_t tmpdev; +int error = 0; +int bdfunit; + + if ( !attaching ) + return ENOTSUP; + + if ( (wantedunit = get_pci_triple(cfg->name)) < 0 ) { + get_name_unit(cfg->name, nm, &wantedunit); + } else { + wantedunit |= 1<<31; + nm[0]=0; + } +#ifdef DEBUG + printf("Wanted unit is 0x%x, pattern '%s'\n", wantedunit, nm); +#endif + + unit = 0; + for ( i=0; (dr=libbsdport_netdriver_table[i]); i++ ) { + /* Find matching driver */ +#ifdef DEBUG + printf("Trying driver '%s' ...", dr->name); +#endif + if ( matches(dr, nm) ) { +#ifdef DEBUG + printf("MATCH\n"); +#endif + + assert( dr->methods ); + + thisunit = 0; + + if ( DEV_TYPE_PCI != dr->type ) { + fprintf(stderr,"Non-PCI driver '%s' not supported; skipping\n", dr->name); + continue; + } + + dev = devalloc(dr); + for ( b=0; bbushdr.pci.bus = b; + dev->bushdr.pci.dev = d; + dev->bushdr.pci.fun = f; + + bdfunit = (b<<8) | PCI_DEVFN(d,f); + +#ifdef DEBUG + printf("Probing PCI 0x%x:%x.%x\n", + bdfunit>>8, PCI_SLOT(bdfunit), PCI_FUNC(bdfunit)); +#endif + + /* has this device been attached already ? */ + if ( (tmpdev = devattached(dev)) ) { + if ( dev->drv == tmpdev->drv ) + thisunit++; + unit++; + if ( UNITMATCH(wantedunit, unit, bdfunit) ) { + fprintf(stderr,"Device '%s' has already been attached\n", device_get_nameunit(dev)); + error = EBUSY; + goto bail; + } + } else { + switch ( ( prob = dr->methods->probe(dev) ) ) { + /* LOW_PRIORITY currently unsupported; list preferred drivers first */ + case BUS_PROBE_LOW_PRIORITY: + case BUS_PROBE_DEFAULT: + /* accepted */ + thisunit++; + unit++; + /* wanted unit == 0 means next avail. + * unit is acceptable. + */ +#ifdef DEBUG + printf("->SUCCESS\n"); +#endif + if ( 0 == wantedunit || UNITMATCH(wantedunit, unit, bdfunit) ) { + error = devattach(dev, thisunit, cfg); + if ( !error ) + dev = 0; /* is now on list */ + goto bail; + } + break; + + default: +#ifdef DEBUG + printf("->FAILED\n"); +#endif + /* probe failed */ + break; + } + } + devclean(dev); + } /* for all functions */ + } /* for all busses + slots */ + devfree(dev); dev = 0; + } /* matching driver */ +#ifdef DEBUG + else printf("NO MATCH\n"); +#endif + } /* for all drivers */ + + /* Nothing found */ + error = ENODEV; +bail: + if (dev) + devfree(dev); + return error; +} + +device_t +libbsdport_netdriver_get_dev(const char *name) +{ +struct device *ldev; + + if ( !name ) + return 0; + + rtems_bsdnet_semaphore_obtain(); + STAILQ_FOREACH(ldev, &devq, list) { + if ( !strcmp(name, device_get_nameunit(ldev)) ) + break; + } + rtems_bsdnet_semaphore_release(); + return ldev; +} diff --git a/bsd_eth_drivers/libbsdport/devicet.h b/bsd_eth_drivers/libbsdport/devicet.h new file mode 100644 index 0000000..bbb4e71 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/devicet.h @@ -0,0 +1,125 @@ +#ifndef RTEMS_NETDEV_T_DECL_H +#define RTEMS_NETDEV_T_DECL_H + +#include +#include +#include +#include + +#include + +#if RTEMS_REV_AT_LEAST(4,8,99) +#include +#else +#include +#endif + +#include + +/* se we can generate a non-inlined version somewhere */ +#ifndef DEVICET_EXTERN_INLINE +#define DEVICET_EXTERN_INLINE extern inline +#endif + +/* unused for now: */ +typedef int devclass_t; + +typedef struct device *device_t; + +typedef struct _pcidev_t { + unsigned short bus; + unsigned char dev; + unsigned char fun; +} pcidev_t; + +#define DEV_TYPE_PCI 1 + +typedef int device_probe_t (device_t); +typedef int device_attach_t (device_t); +typedef int device_detach_t (device_t); +typedef int device_resume_t (device_t); +typedef int device_suspend_t (device_t); + +typedef struct device_methods { + int (*probe ) (device_t); + int (*attach) (device_t); + void (*shutdown) (device_t); + int (*detach) (device_t); + int (*irq_check_dis) (device_t); + void (*irq_en) (device_t); +} device_method_t; + +struct driver { + const char *name; + device_method_t *methods; + int type; + int softc_size; +}; + +#define DEVICE_SOFTC_ALIGNMENT 16 + +struct device { + union { + pcidev_t pci; + } bushdr; + int type; + STAILQ_ENTRY(device) list; + const char *name; + char nameunit[16]; /* NEVER use knowledge about the size of this -- we may change it */ + int unit; + char *desc; + driver_t *drv; + int attached; + void *rawmem; /* back pointer */ + struct rtems_bsdnet_ifconfig *ifconfig; + char softc[] __attribute__ ((aligned(DEVICE_SOFTC_ALIGNMENT), may_alias)); + /* a pointer to back to the device is installed past the 'softc' */ +}; + +static inline device_t +rtems_softc2dev(void *softc) +{ +uintptr_t diff = (uintptr_t)&((device_t)(0))->softc - (uintptr_t)(device_t)(0); + return (device_t)((uintptr_t)softc - diff); +} + +#define device_set_desc_copy(dev, nm) \ + do { real_libc_free((dev)->desc); (dev)->desc = strdup((nm)); } while (0) + +#define device_set_desc(dev, nm) device_set_desc_copy(dev, nm) + +static inline const char * +device_get_nameunit(device_t dev) +{ + return dev->nameunit; +} + +static inline const char * +device_get_name(device_t dev) +{ + return dev->name; +} + +static inline int +device_get_unit(device_t dev) +{ + return dev->unit; +} + +DEVICET_EXTERN_INLINE void * +device_get_softc(device_t dev) +{ + return dev->softc; +} + +#define device_delete_child(dev,bus) do {} while (0) + +static inline int +device_is_attached(device_t dev) +{ + return dev->attached; +} + +int device_printf(device_t dev, const char *fmt, ...); + +#endif diff --git a/bsd_eth_drivers/libbsdport/ifmedia.c b/bsd_eth_drivers/libbsdport/ifmedia.c new file mode 100644 index 0000000..77c3bd3 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/ifmedia.c @@ -0,0 +1,91 @@ +#include +#define _KERNEL +#include +#include + +#include + +#if RTEMS_REV_AT_LEAST(4,8,99) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +void +ifmedia_init(struct ifmedia *ifm, int dontcare_mask, + ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback) +{ + ifm->ifm_mask = dontcare_mask; + ifm->ifm_media = 0; + ifm->ifm_cur = 0; + ifm->ifm_list.lh_first = NULL; + ifm->ifm_change = change_callback; + ifm->ifm_status = status_callback; +} + +void +ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux) +{ +struct ifmedia_entry *ifmen, *ifmep, *ifme; + if ( ( ifme = malloc(sizeof(*ifme), M_DEVBUF, M_NOWAIT) ) ) { + ifme->ifm_media = mword; + ifme->ifm_data = data; + ifme->ifm_aux = aux; + for ( ifmep = LIST_FIRST(&ifm->ifm_list); ifmep; ifmep = ifmen ) { + if ( !(ifmen = LIST_NEXT(ifmep, ifm_list)) ) + break; + } + if ( ifmep ) + LIST_INSERT_AFTER(ifmep, ifme, ifm_list); + else + LIST_INSERT_HEAD( &ifm->ifm_list, ifme, ifm_list); + } +} + +int +ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm, u_long cmd) +{ +int rval = 0; +struct ifmediareq ifmr; + if ( SIOCGIFMEDIA == cmd ) { + if ( !ifm->ifm_status ) + return ENOTSUP; + ifm->ifm_status(ifp, &ifmr); + if ( ! (IFM_AVALID & ifmr.ifm_status) ) + return EINVAL; + /* translate */ + ifr->ifr_media = ifmr.ifm_active; + if ( IFM_ACTIVE & ifmr.ifm_status ) + ifr->ifr_media |= IFM_LINK_OK; + /* no way to determine if autoneg is forcefully disabled + * from ifmr :-( + * Look at current ifm_media for now. + */ + if ( IFM_SUBTYPE(ifm->ifm_media) != IFM_AUTO ) + ifr->ifr_media |= IFM_ANEG_DIS; + } else { + if ( !ifm->ifm_change ) + return ENOTSUP; + ifm->ifm_media = ifr->ifr_media; + rval = ifm->ifm_change(ifp); + } + return rval; +} + +void +ifmedia_set(struct ifmedia *ifm, int mword) +{ + ifm->ifm_media = mword; + /* cannot invoke the ifm_change callback because we have + * no ifp here. + */ +} + diff --git a/bsd_eth_drivers/libbsdport/ifstuff.c b/bsd_eth_drivers/libbsdport/ifstuff.c new file mode 100644 index 0000000..40e525e --- /dev/null +++ b/bsd_eth_drivers/libbsdport/ifstuff.c @@ -0,0 +1,79 @@ + +#include "libbsdport.h" + +#define _KERNEL +#include +#include +#include +#include +#include +#include +#include + +static struct ifnet *rtems_bsdnet_if_freelist = 0; + +struct ifnet * +if_alloc(int type) +{ +struct ifnet *rval = 0; + if ( IFT_ETHER == type ) { + /* Hack with freelist allows for debugging drivers as modules */ + if ( (rval = rtems_bsdnet_if_freelist) ) { + /* use softc pointer to link free list */ + rtems_bsdnet_if_freelist = rtems_bsdnet_if_freelist->if_softc; + } + if ( (rval = malloc(sizeof(struct arpcom), M_DEVBUF, M_WAIT)) ) + memset(rval, 0, sizeof(struct arpcom)); + } + return rval; +} + +void +if_free(struct ifnet *ifp) +{ + /* save on free-list so subsequent alloc gets old + * interface back (which is still on the bsdnet stack's list + * of known interfaces. The old rtems stack doesn't provide + * means to remove an interface once it has been attached. + * This hack allows for detaching a *driver* and reattaching + * it to the same interface later (good for development/debugging). + */ + ifp->if_softc = rtems_bsdnet_if_freelist; + rtems_bsdnet_if_freelist = ifp; +} + +/* Ugly hack to allow unloading/reloading the driver core. + * Needed because rtems' bsdnet release doesn't implement + * if_detach(). Therefore, we bring the interface down but + * keep the device record alive... + */ +void +ether_ifdetach(struct ifnet *ifp) +{ + (ifp)->if_flags = 0; + (ifp)->if_ioctl = 0; + (ifp)->if_start = 0; + (ifp)->if_watchdog = 0; + (ifp)->if_init = 0; +} + + +/* copy ethernet addr into arpcom if nothing is set yet */ +void +ether_setaddr(struct ifnet *ifp, u_int8_t *eaddr) +{ +int i; +device_t dev = rtems_softc2dev(ifp->if_softc); + /* If LLADDR has already been set, then use it */ + for ( i=0; i< ETHER_ADDR_LEN; i++ ) { + if ( IF_LLADDR(ifp)[i] ) + break; + } + if ( i >= ETHER_ADDR_LEN ) { + /* not set; see if the ifconfig struct provides one */ + if ( dev->ifconfig && dev->ifconfig->hardware_address ) + memcpy(IF_LLADDR(ifp), dev->ifconfig->hardware_address, ETHER_ADDR_LEN); + else + memcpy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); + } +} diff --git a/bsd_eth_drivers/libbsdport/libbsdport.h b/bsd_eth_drivers/libbsdport/libbsdport.h new file mode 100644 index 0000000..8b88784 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/libbsdport.h @@ -0,0 +1,380 @@ +#ifndef RTEMS_COMPAT_DEFS_H +#define RTEMS_COMPAT_DEFS_H + +#include +#include + +#define _KERNEL +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +/* +#include +*/ + + + +#include + +#ifndef bswap32 +#define bswap32(_x) CPU_swap_u32(_x) +#endif + +#if defined(__LITTLE_ENDIAN__) || defined(__i386__) +static inline uint16_t htole16(uint16_t v) { return v; } +static inline uint32_t htole32(uint32_t v) { return v; } +static inline uint64_t htole64(uint64_t v) { return v; } +static inline uint16_t le16toh(uint16_t v) { return v; } +static inline uint32_t le32toh(uint32_t v) { return v; } +static inline uint64_t le64toh(uint64_t v) { return v; } + +#ifdef __i386__ + +#ifdef __SSE__ +static inline void membarrier_r() { asm volatile("lfence":::"memory"); } +static inline void membarrier_rw() { asm volatile("mfence":::"memory"); } +/* Current x86 CPUs always do in-order stores - prevent the compiler from reordering, neverthelesss */ +static inline void membarrier_w() { asm volatile(/*"sfence"*/"":::"memory"); } +#else +static inline void membarrier_r() { asm volatile("lock; addl $0,0(%%esp)":::"memory"); } +static inline void membarrier_rw() { asm volatile("lock; addl $0,0(%%esp)":::"memory"); } +/* Current x86 CPUs always do in-order stores - prevent the compiler from reordering, neverthelesss */ +static inline void membarrier_w() { asm volatile(/*"lock; addl $0,0(%%esp)"*/"":::"memory"); } +#endif + +#endif + +#elif defined(__BIG_ENDIAN__) +#ifdef __PPC__ +#include + +/* Note the 'may_alias' constructs. They are + * a safeguard agains the alias rule should the + * pointer argument of st_leXX change (again) in + * the future (and it should be safe to use older + * versions of 'byteorder.h' + */ + +static inline uint16_t +htole16(uint16_t v) +{ +uint16_t rval __attribute__((may_alias)); + st_le16((volatile uint16_t*)&rval,v); + return rval; +} + +static inline uint16_t +le16toh(uint16_t v) +{ +uint16_t vv __attribute__((may_alias)) = v; + return ld_le16((volatile uint16_t*)&vv); +} + +static inline uint32_t +htole32(uint32_t v) +{ +uint32_t rval __attribute__((may_alias)); + st_le32((volatile libbsdport_u32_t*)&rval,v); + return rval; +} + +static inline uint32_t +le32toh(uint32_t v) +{ +uint32_t vv __attribute__((may_alias)) = v; + return ld_le32((volatile libbsdport_u32_t*)&vv); +} + +/* Compiler generated floating point instructions for this + * and rtems_bsdnet_newproc()-generated tasks are non-FP + * :-( + */ +static inline uint64_t +htole64(uint64_t v) +{ +union { + libbsdport_u32_t tmp[2] __attribute__((may_alias)); + uint64_t rval __attribute__((may_alias)); +} u; + + st_le32( &u.tmp[0], (unsigned)(v&0xffffffff) ); + st_le32( &u.tmp[1], (unsigned)((v>>32)&0xffffffff) ); + + return u.rval; +} + +static inline void membarrier_r() { asm volatile("sync":::"memory"); } + +static inline void membarrier_rw() { asm volatile("sync":::"memory"); } + +static inline void membarrier_w() { asm volatile("eieio":::"memory"); } + +#else +#error "need htoleXX() implementation for this CPU arch" +#endif + +#else +#error "Unknown CPU endianness" +#endif + +#include +#include + +#ifndef PCIR_BAR +#define PCIR_BAR(x) (0x10+4*(x)) +#endif + +#ifndef PCIR_COMMAND +#define PCIR_COMMAND PCI_COMMAND +#endif + +#ifndef PCIR_REVID +#define PCIR_REVID PCI_REVISION_ID +#endif + +#ifndef PCIR_SUBVEND_0 +#define PCIR_SUBVEND_0 PCI_SUBSYSTEM_VENDOR_ID +#endif + +#ifndef PCIR_SUBDEV_0 +#define PCIR_SUBDEV_0 PCI_SUBSYSTEM_ID +#endif + +#ifndef PCIR_CIS +#define PCIR_CIS PCI_CARDBUS_CIS +#endif + +#ifndef PCIM_CMD_BUSMASTEREN +#define PCIM_CMD_BUSMASTEREN PCI_COMMAND_MASTER +#endif + +#ifndef PCIM_CMD_MEMEN +#define PCIM_CMD_MEMEN PCI_COMMAND_MEMORY +#endif + +#ifndef PCIM_CMD_PORTEN +#define PCIM_CMD_PORTEN PCI_COMMAND_IO +#endif + +#ifndef PCIR_CAP_PTR +#define PCIR_CAP_PTR 0x34 +#endif + +#ifndef PCIR_POWER_STATUS +#define PCIR_POWER_STATUS 0x4 +#endif + +#ifndef PCIM_PSTAT_PME +#define PCIM_PSTAT_PME 0x8000 +#endif + +#ifndef PCIM_PSTAT_PMEENABLE +#define PCIM_PSTAT_PMEENABLE 0x0100 +#endif + +#ifndef PCIY_PMG +#define PCIY_PMG 0x01 +#endif + +#ifndef PCI_RF_DENSE +#define PCI_RF_DENSE 0 +#endif + +static inline uint32_t +pci_read_config(device_t dev, unsigned reg, int width) +{ + switch ( width ) { + default: + case 4: + { + libbsdport_u32_t v; + pci_read_config_dword(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, &v); + return v; + } + case 2: + { + uint16_t v; + pci_read_config_word(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, &v); + return (uint32_t)v; + } + case 1: + { + uint8_t v; + pci_read_config_byte(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, &v); + return (uint32_t)v; + } + } +} + +static inline void +pci_write_config(device_t dev, unsigned reg, uint32_t val, int width) +{ + switch ( width ) { + default: + case 4: + { + pci_write_config_dword(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, val); + } + case 2: + { + pci_write_config_word(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, val); + } + case 1: + { + pci_write_config_byte(dev->bushdr.pci.bus, dev->bushdr.pci.dev, dev->bushdr.pci.fun, reg, val); + } + } +} + + +static inline uint16_t +pci_get_vendor(device_t dev) +{ + return pci_read_config(dev, PCI_VENDOR_ID, 2); +} + +static inline uint16_t +pci_get_device(device_t dev) +{ + return pci_read_config(dev, PCI_DEVICE_ID, 2); +} + +static inline uint16_t +pci_get_subvendor(device_t dev) +{ + return pci_read_config(dev, PCIR_SUBVEND_0, 2); +} + +static inline uint16_t +pci_get_subdevice(device_t dev) +{ + return pci_read_config(dev, PCIR_SUBDEV_0, 2); +} + +static inline void +pci_enable_busmaster(device_t dev) +{ + pci_write_config( + dev, + PCI_COMMAND, + pci_read_config(dev, PCI_COMMAND, 2) | PCI_COMMAND_MASTER, + 2 + ); +} + +static inline void +pci_enable_io(device_t dev, int space) +{ + pci_write_config( + dev, + PCI_COMMAND, + pci_read_config(dev, PCI_COMMAND, 2) | space, + 2 + ); +} + +static inline void +pci_disable_io(device_t dev, int space) +{ + pci_write_config( + dev, + PCI_COMMAND, + pci_read_config(dev, PCI_COMMAND, 2) & ~space, + 2 + ); +} + + + +/* MSI / MSIX not supported */ +static inline int +pci_msi_count(device_t dev) { return 0; } + +static inline int +pci_alloc_msi(device_t dev, int *pval) { return -1; } + +static inline int +pci_alloc_msix(device_t dev, int *pval) { return -1; } + +static inline void +pci_release_msi(device_t dev) { } + + + + +#define IFQ_DRV_IS_EMPTY(q) (0 == (q)->ifq_head) +#define IFQ_DRV_DEQUEUE(q,m) IF_DEQUEUE((q),(m)) +#define IFQ_DRV_PREPEND(q,m) IF_PREPEND((q),(m)) + +#define ifq_drv_maxlen ifq_maxlen +#define IFQ_SET_MAXLEN(q, len) do {} while (0) +#define IFQ_SET_READY(q) do {} while (0) + +#define ETHER_BPF_MTAP(ifp, m) do {} while (0) +#define BPF_MTAP(ifp, m) do {} while (0) + +#define IF_LLADDR(ifp) (((struct arpcom *)(ifp))->ac_enaddr) + +#define if_link_state_change(ifp, state) do {} while (0) + +/* if_name should probably be const char * but isn't */ +#define if_initname(ifp, name, unit) \ + do { (ifp)->if_name = (char*)(name); (ifp)->if_unit = (unit); } while (0) + +struct ifnet * if_alloc(int type); + +void if_free(struct ifnet *ifp); + +#define if_printf(ifp,args...) do { printf("%s: ",(ifp)->if_name); printf(args); } while (0) + +void * +contigmalloc( + unsigned long size, + int type, + int flags, + unsigned long lo, + unsigned long hi, + unsigned long align, + unsigned long bound); + +void +contigfree(void *ptr, size_t size, int type); + +/* locking is handled by 'super-lock' outside driver; watch for link intr task, though */ +#define NET_LOCK_GIANT() do {} while (0) +#define NET_UNLOCK_GIANT() do {} while (0) + +#define KASSERT(cond, msg...) \ + do { \ + if ( ! (cond) ) { \ + rtems_panic msg; \ + } \ + } while (0) + +#define __FBSDID(x) +#define MODULE_DEPEND(x1,x2,x3,x4,x5) + +#define mii_mediachg(mii) do {} while (0) + +void * +real_libc_malloc(size_t); + +void +real_libc_free(void*); + +#endif diff --git a/bsd_eth_drivers/libbsdport/libbsdport_api.h b/bsd_eth_drivers/libbsdport/libbsdport_api.h new file mode 100644 index 0000000..ba3e54e --- /dev/null +++ b/bsd_eth_drivers/libbsdport/libbsdport_api.h @@ -0,0 +1,84 @@ +#ifndef LIBBSDPORT_API_H +#define LIBBSDPORT_API_H + +#include +#include + +/* $Id$ */ + +/* User API to libbsdport driver attach function, driver table etc. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct driver driver_t; + +/* NULL terminated list of all drivers configured into the system. + * To be defined by the application. + */ +extern driver_t *libbsdport_netdriver_table[]; + +/* Drivers ported so far: */ +/* Intel E1000 chips */ +extern driver_t libbsdport_em_driver; +/* AMD 79C971..976 pcnet PCI */ +extern driver_t libbsdport_pcn_driver; +/* RealTek RTL8139, 8168, 8169, 8169S, 8110, 8101E, and 8111 PCI */ +extern driver_t libbsdport_re_driver; +/* AMD/Lance older (and later) chips; this driver also supports what 'pcn' + * does but might not be as efficient. + * NOTE: The 'le_pci' driver works with the pcnet32 (79C970A) emulation + * of qemu. + */ +extern driver_t libbsdport_le_pci_driver; + + +/* Generic driver attach function (can be used in rtems_bsdnet_ifconfig). + * This routine selects a driver/device combination based on + * - drivers available / listed in libbsdport_netdriver_table[]; + * - devices detected in PCI config space compatible with a listed + * driver. + * - name and unit specified in the rtems_bsdnet_ifconfig.name field + * (empty name: "" is a wildcard). + * + * E.g. assume that + * 1) the 'em' and 'pcn' drivers are listed in the table. + * 2) a AMD Am79C973 chip is somewhere on the PCI bus + * 3) ifconfig name is "" + * -> the 'pcn' driver is selected and the only AMD chip present + * is used as 'pcn1'. + * -> If the name was 'em' or 'pcn2' no device would be found + * (no em device found; no 2nd pcn device found). + * + * Now assume that you have a 82544 and two AMD 79C973 devices: + * + * name: "" picks the first of the three chips found in PCI space + * name: "pcn" picks the first AMD chip found + * name: "em" picks the first (and only) 82544 + * name: "pcn2" picks the second AMD chip + * + * Also, it is possible to specify a PCI-triple: :. + * i.e., name: "2:3.0" tries to find a driver that supports the device + * at bus #2, slot #2. + * + * NOTE: detaching a driver is not supported (since rtems bsdnet cannot detach + * an interface). + */ +int +libbsdport_netdriver_attach(struct rtems_bsdnet_ifconfig *cfg, int attaching); + +/* Print information about all attached drivers to FILE (stdout if NULL) + * + * RETURNS: number of devices attached so far. + * + * BUGS: more info should be printed. + */ +int +libbsdport_netdriver_dump(FILE *f); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsd_eth_drivers/libbsdport/libbsdport_post.h b/bsd_eth_drivers/libbsdport/libbsdport_post.h new file mode 100644 index 0000000..fcf7e2d --- /dev/null +++ b/bsd_eth_drivers/libbsdport/libbsdport_post.h @@ -0,0 +1,95 @@ +/* This file is to be included from 'if_xxx.c' AFTER all other + * includes so we can override some rtems-bsdnet things... + */ +#if 1 +/* These are defined in sys/sysctl.h and we + * could one day add support ... + */ +#undef SYSCTL_ADD_PROC +#define SYSCTL_ADD_PROC(unused...) do { } while (0) + +#undef SYSCTL_ADD_INT +#define SYSCTL_ADD_INT(unused...) do { } while (0) +#endif + +#include +#include + +/* include after & friends */ +#include + +#define if_drv_flags if_flags +#define IFF_DRV_RUNNING IFF_RUNNING +#define IFF_DRV_OACTIVE IFF_OACTIVE + +/* FIXME: should implement m_defrag() */ +#define m_defrag(m_headp, opt) NULL + +static inline struct mbuf * +m_getcl(int how, int type, unsigned flags) +{ +struct mbuf *mp = 0; + if ( ! (flags & M_PKTHDR) ) { + printk("m_getcl: DUNNO WHAT TO DO HERE\n"); + return 0; + } + MGETHDR(mp, M_DONTWAIT, MT_DATA); + if ( mp ) { + MCLGET( mp, M_DONTWAIT ); + if ( !(mp->m_flags & M_EXT) ) { + m_freem(mp); + mp = 0; + } + } + return mp; +} + +static inline void +ether_input_skipping(struct ifnet *ifp, struct mbuf *m) +{ struct ether_header *eh; + eh = mtod(m, struct ether_header*); +#if 1 + m_adj(m, sizeof(struct ether_header)); +#else + /* faster hack */ + m->m_data += sizeof(struct ether_header); + m->m_len -= sizeof(struct ether_header); + m->m_pkthdr.len -= sizeof(struct ether_header); +#endif + /* some drivers don't set this */ + m->m_pkthdr.rcvif = ifp; + ether_input(ifp, eh, m); +} + +void +ether_setaddr(struct ifnet *ifp, u_int8_t *eaddr); + +#define ether_ifattach(ifp, eaddr) \ + do { \ + (ifp)->if_output = ether_output; \ + if ( !(ifp)->if_addrlist ) { \ + /* reattach hack; do this only the first time -- detaching is not implemented, however!! */ \ + ether_setaddr(ifp, eaddr); \ + if_attach(ifp); \ + ether_ifattach(ifp); \ + } \ + } while (0) + + +/* Not 100% sure this is correct */ +#define M_MOVE_PKTHDR(to, from) \ + do { \ + (to)->m_flags = ((from)->m_flags & M_COPYFLAGS) | ((to)->m_flags & M_EXT); \ + if (((to)->m_flags & M_EXT) == 0) \ + (to)->m_data = (to)->m_pktdat; \ + (to)->m_pkthdr = (from)->m_pkthdr; \ + (from)->m_flags &= ~M_PKTHDR; \ + } while (0) + +#define ETHER_SIOCMULTIFRAG(e, c, ifr, ifp) \ + ( ENETRESET != (e = (SIOCADDMULTI == (c) ? \ + ether_addmulti((ifr), (struct arpcom*)(ifp)) : \ + ether_delmulti((ifr), (struct arpcom*)(ifp)) ))) \ + +#define arp_ifinit(ifp, ifa) arp_ifinit((struct arpcom *)ifp, ifa) + diff --git a/bsd_eth_drivers/libbsdport/malloc.c b/bsd_eth_drivers/libbsdport/malloc.c new file mode 100644 index 0000000..116b0e5 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/malloc.c @@ -0,0 +1,17 @@ +#include + +/* sometimes we want the original versions, + * not malloc/free shadowed by rtems' bsdnet port + */ + +void * +real_libc_malloc(size_t s) +{ + return malloc(s); +} + +void +real_libc_free(void *p) +{ + free(p); +} diff --git a/bsd_eth_drivers/libbsdport/mutex.h b/bsd_eth_drivers/libbsdport/mutex.h new file mode 100644 index 0000000..5476e37 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/mutex.h @@ -0,0 +1,55 @@ +#ifndef _RTEMS_BSDNET_MUTEX_H +#define _RTEMS_BSDNET_MUTEX_H + +/* NOTE: mutexes should never be necessary since + * the RTEMS BSD code protects everything with a + * big fat lock + */ + +struct mtx { + rtems_id mtx_id; +}; + +#define MTX_DEF 0 /* default sleeping lock */ +#define MTX_RECURSE 4 /* nesting */ + +#define MTX_NETWORK_LOCK "xxx" + +static inline void +mtx_init(struct mtx *m, const char *name, const char *type, int opts) +{ + /* Set ID to zero in case they want to submit this mutex + * to callout_init_mtx() + */ + m->mtx_id = 0; +} + +static inline int +mtx_initialized(struct mtx *m) +{ + return m->mtx_id == 0; +} + +static inline void +mtx_lock(struct mtx *m) +{ +} + +static inline void +mtx_unlock(struct mtx *m) +{ +} + +static inline void +mtx_destroy(struct mtx *m) +{ +} + +/* what ? */ +#define MA_OWNED 1 +static inline void +mtx_assert(struct mtx *m, int what) +{ +} + +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_callout.c b/bsd_eth_drivers/libbsdport/rtems_callout.c new file mode 100644 index 0000000..ce9a477 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_callout.c @@ -0,0 +1,250 @@ +#include +#include +#include + +#include +#include + +#include "mutex.h" +#include "callout.h" + +#define STATIC static + +/* Implementation modelled after + * + * "Redesign the BSD Callout and Timer Facilities" + * Adam M. Costello, George Varghese + * Dpt. of Computer Science, Washington University, 1995. + */ + +#include + +/* rely on networking semaphore for now */ +#define LIST_KEY_DECL(k) +#define LIST_LOCK(k) do {} while (0) +#define LIST_UNLOCK(k) do {} while (0) + +#define WHEELBITS 5 + +#define WHEELMASK ((1<<(WHEELBITS))-1) + +#define CALLOUT_EVENT RTEMS_EVENT_1 +#define KILL_EVENT RTEMS_EVENT_2 + + +typedef void (*timeout_t)(void*); + +STATIC volatile callout_time_t hard_ticks = 0; +STATIC volatile callout_time_t soft_ticks = 0; + +STATIC struct callout *c_wheel[1<c_pprev == 0 && c->c_next == 0 ); + if ( (c->c_next = *where) ) + (*where)->c_pprev = &c->c_next; + c->c_pprev = where; + *where = c; +} + +static inline void +c_deq(struct callout *c) +{ +struct callout *n; + assert( c->c_pprev ); + if ( (n = *c->c_pprev = c->c_next) ) + n->c_pprev = c->c_pprev; + c->c_next = 0; + c->c_pprev = 0; +} + +static inline void +softclock() +{ +struct callout *c, *n; +rtems_interrupt_level k1; +callout_time_t st,ht; +LIST_KEY_DECL(k); + + /* I believe this is free of a race condition (softclock + * and hardclock both update volatile 'soft_ticks' variable): + * a) 'hardclock' runs at IRQ level and is atomic + * b) inside while loop 'soft_ticks' is != 'hard_ticks' + * c) hardclock only modifies soft_ticks if 'soft_ticks'=='hard_ticks' + * hence this could only happen just after the update of 'soft_ticks' + * at the end of the while loop completes. + */ + + while ( 1 ) { + /* Must atomically read 'soft_ticks' and 'hard_ticks' -- otherwise, + * hardclock might update both but we get one old and one new value + */ + rtems_interrupt_disable(k1); + st = soft_ticks; + ht = hard_ticks; + rtems_interrupt_enable(k1); + if ( st == ht ) + break; /* caught up */ + + /* at this point, we know that st != ht and therefore, + * hardclock will only increment hard_ticks but leave + * soft_ticks alone. + */ + + st++; + + LIST_LOCK(k); + for ( c = c_wheel[ st & WHEELMASK ]; c; c=n ) { + n = c->c_next; + if ( c->c_time <= 0 ) { + /* this one expired */ + c_deq(c); + if ( c->c_func ) + c->c_func(c->c_arg); + } else { + c->c_time--; + } + } + LIST_UNLOCK(k); + soft_ticks = st; + /* here, soft_ticks could have caught up and + * a hardclock occurring here could also + * update soft_ticks. + */ + } +} + +static inline void +hardclock(rtems_id tid) +{ + if ( hard_ticks++ == soft_ticks && !c_wheel[hard_ticks & WHEELMASK] ) { + /* nothing to do */ + soft_ticks++; + } else { + rtems_event_send(tid, CALLOUT_EVENT); + } +} + +static void +calloutTick(rtems_id myself, void *arg) +{ +rtems_id tid = (rtems_id)arg; + + hardclock(tid); + + rtems_timer_fire_after(myself, 1, calloutTick, arg); +} + +static void +calloutTask(void *arg) +{ +rtems_event_set ev; +rtems_status_code sc; +rtems_id ticker = 0; +rtems_id me; + + sc = rtems_timer_create(rtems_build_name('b','s','d','c'), &ticker); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "Creation of timer failed\n"); + goto bail; + } + rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &me); + + rtems_timer_fire_after(ticker, 1, calloutTick, (void*)me); + + while ( 1 ) { + sc = rtems_bsdnet_event_receive (CALLOUT_EVENT | KILL_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &ev); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc, "calloutTask: unable to receive event; terminating\n"); + break; + } + if ( ev & KILL_EVENT ) { + break; + } + softclock(); + } +bail: + rtems_timer_delete(ticker); + rtems_task_delete(RTEMS_SELF); +} + + +/* We cannot stop a callout that's in progress */ + +void +callout_stop(struct callout *c) +{ +LIST_KEY_DECL(k); + + if ( !c->c_pprev ) + return; /* not currently on a list */ + + LIST_LOCK(k); + /* remove from list */ + c_deq(c); + LIST_UNLOCK(k); +} + + +void +callout_reset(struct callout *c, int ticks, timeout_t fn, void *arg) +{ +LIST_KEY_DECL(k); +int i; + + if ( ticks <= 0 ) + ticks = 1; + + callout_stop(c); + + c->c_func = fn; + c->c_arg = arg; + + LIST_LOCK(k); + i = (hard_ticks + ticks) & WHEELMASK; + c->c_time = ticks >> WHEELBITS; + + /* enqueue */ + c_enq(&c_wheel[i], c); + + LIST_UNLOCK(k); +} + +static rtems_id callout_tid = 0; + +void +callout_init(struct callout *c, int mpsafe) +{ + /* non thread-safe lazy init in case nobody cared to do it ... */ + if ( !callout_tid ) + rtems_callout_initialize(); + memset(c,0,sizeof(*c)); +} + +void +callout_init_mtx(struct callout *c, struct mtx *m, unsigned flags) +{ + if ( m->mtx_id ) + rtems_panic("callout_init_mtx: using mutex not supported\n"); + callout_init(c,0); + c->c_mtx = m; +} + +rtems_id +rtems_callout_initialize() +{ + if ( !callout_tid ) + callout_tid=rtems_bsdnet_newproc ("cout", 4096, calloutTask, NULL); + return callout_tid; +} + +#ifdef DEBUG +void +_cexpModuleInitialize(void*u) +{ + rtems_bsdnet_initialize_network(); + rtems_callout_initialize(); +} +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_taskqueue.c b/bsd_eth_drivers/libbsdport/rtems_taskqueue.c new file mode 100644 index 0000000..97153ca --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_taskqueue.c @@ -0,0 +1,308 @@ +#include +#include + +#include +#include + +#include "taskqueue.h" + +/* +#define STATIC static +*/ +#undef DEBUG + +#ifdef DEBUG +#include +#ifndef STATIC +#define STATIC +#endif +#else +#ifndef STATIC +#define STATIC static +#endif +#endif + +#define TQ_WAKE_EVENT RTEMS_EVENT_0 + +/* This implementation is extremely simple; we assume + * that all taskqueues (and as a matter of fact there is + * only a single one) are manipulated with the rtems + * bsdnet semaphore held. I.e., + * taskqueue_enqueue() + * taskqueue_drain() + * etc. + * are called from an environment that holds the + * bsdnet semaphore. + * Likewise, the thread that works the taskqueue + * holds the semaphore while doing so. + * + */ + +/* use single-linked list; 'drain' which would benefit from + * double-linked list is seldom used and performance doesn't + * matter much there. OTOH, the frequent case of working + * the list + enqueueing is more efficient for the single-linked + * list. +struct task { + struct task *ta_next; + int ta_pending; + int ta_priority; + task_fn ta_fn; + void *ta_fn_arg; +}; + */ + +struct taskqueue { + struct task anchor; + struct task *tail; + tq_enq_fn enq_fn; + void *enq_fn_arg; + rtems_id tid; +}; + + +STATIC struct taskqueue the_taskqueue = { + { 0, 0, 0, 0, 0 }, + &the_taskqueue.anchor, + taskqueue_thread_enqueue, + &taskqueue_fast, + 0 +}; + +struct taskqueue *taskqueue_fast = &the_taskqueue; + +struct taskqueue * +taskqueue_create(const char *name, int mflags, tq_enq_fn enq_fn, void *arg) +{ + if ( enq_fn != taskqueue_thread_enqueue ) + rtems_panic("rtems_taskqueue: attempt to create non-standard TQ; implementation needs to be modified\n"); + return &the_taskqueue; +} + +struct taskqueue * +taskqueue_create_fast(const char *name, int mflags, tq_enq_fn enq_fn, void *arg) +{ + return taskqueue_create(name, mflags, enq_fn, arg); +} + +/* taskqueue_enqueue must be allowed from an ISR; + * hence, all critical list manipulation must lock out + * interrupts... + */ +int +taskqueue_enqueue(struct taskqueue *tq, struct task *ta) +{ +rtems_interrupt_level l; + +rtems_interrupt_disable(l); + if ( 0 == ta->ta_pending ++ ) { + /* hook into list */ + ta->ta_next = 0; + tq->tail->ta_next = ta; + tq->tail = ta; + } + tq->enq_fn(tq->enq_fn_arg); +rtems_interrupt_enable(l); + return 0; +} + +void +taskqueue_thread_enqueue(void *ctxt) +{ +int dopost; +/* pointer-to-pointer is what bsd provides; we currently + * follow the scheme even we don't directly use the argument + * passed to taskqueue_create... + */ +struct taskqueue *tq = *(struct taskqueue **)ctxt; + /* If this is the first entry on the list then the + * task needs to be notified... + */ + dopost = ( tq->anchor.ta_next == tq->tail && 1 == tq->tail->ta_pending ); + + if ( dopost ) + rtems_event_send(tq->tid, TQ_WAKE_EVENT); +} + +/* Returns 0 on success */ +int +taskqueue_start_threads(struct taskqueue **ptq, int count, int prio, const char *fmt, ...) +{ + if ( count != 1 ) + rtems_panic("rtems_taskqueue: taskqueue_start_threads cannot currently deal with count != 1\n"); + + /* Do (non thread-safe) lazy init as a fallback */ + if ( ! the_taskqueue.tid ) + rtems_taskqueue_initialize(); + return 0; +} + +void +taskqueue_drain(struct taskqueue *tq, struct task *ta) +{ +rtems_interrupt_level l; +struct task *p, *q; +int i; + + /* find predecessor; searching the list should be + * safe; an ISR might append a new record to the tail + * while we are working but that should be OK. + */ + for ( p = &tq->anchor; (q = p->ta_next); p=q ) { + if ( q == ta ) { + rtems_interrupt_disable(l); + /* found; do work */ + /* remember 'pending' count and extract */ + i = ta->ta_pending; + ta->ta_pending = 0; + p->ta_next = ta->ta_next; + ta->ta_next = 0; + /* adjust tail */ + if ( tq->tail == q ) + tq->tail = p; + rtems_interrupt_enable(l); + for ( ; i>0; i-- ) { + ta->ta_fn(ta->ta_fn_arg, i); + } + return; + } + } +} + +/* work the task queue and return + * nonzero if the list is not empty + * (which means that some callback has + * rescheduled itself) + */ +static void * +taskqueue_work(struct taskqueue *tq) +{ +rtems_interrupt_level l; +struct task *p, *q; +task_fn f; +void *arg; +int i; + +/* work off a temporary list in case any callback reschedules + * itself or if new tasks are queued from an ISR. + */ +rtems_interrupt_disable(l); + p = tq->anchor.ta_next; + + tq->anchor.ta_next = 0; + tq->tail = &tq->anchor; +rtems_interrupt_enable(l); + + while ( (q=p) ) { + rtems_interrupt_disable(l); + i = q->ta_pending; + q->ta_pending = 0; + /* extract */ + p = q->ta_next; + q->ta_next = 0; + f = q->ta_fn; + arg = q->ta_fn_arg; + rtems_interrupt_enable(l); + for ( ; i>0; i-- ) { + f(arg, i); + } + } + return tq->anchor.ta_next; +} + +void +taskqueue_free(struct taskqueue *tq) +{ + taskqueue_work(tq); +} + +static void +taskqueueDoWork(void *arg) +{ +struct taskqueue *tq = arg; +rtems_event_set evs; +rtems_status_code sc; + while ( 1 ) { + sc = rtems_bsdnet_event_receive(TQ_WAKE_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &evs); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,"rtems_taskqueue: taskqueueDoWork() unable to receive wakup event\n"); + rtems_panic("Can't proceed\n"); + } + if ( taskqueue_work(tq) ) { +#if 0 + /* chance to reschedule */ + rtems_bsdnet_semaphore_release(); + rtems_task_wake_after(0); + rtems_bsdnet_semaphore_obtain(); +#else + /* hopefully, releasing the semaphore (as part of bsdnet_event_receive) + * and obtaining the event (which has been posted already) + * yields the CPU if necessary... + */ +#endif + } + } +} + +#ifdef DEBUG +struct task_dbg { + struct task t; + char *nm; +}; + +struct task_dbg taskA = { + {0}, + "taskA" +}; + +struct task_dbg taskB = { + {0}, + "taskB" +}; + +struct task_dbg taskC = { + {0}, + "taskC" +}; + +static void the_task_fn(void *arg, int pending) +{ +struct task_dbg *td = arg; + printf("%s (pending: %i)\n", td->nm, pending); + /* Test rescheduling */ + if ( pending > 3 ) + taskqueue_enqueue(&the_taskqueue,&td->t); +} + +void taskqueue_dump() +{ +struct task *p; + printf("Anchor %p, Tail %p\n", &the_taskqueue.anchor, the_taskqueue.tail); + for ( p = the_taskqueue.anchor.ta_next; p; p=p->ta_next ) { + printf("%p: (pending %2i, next %p)\n", + p, p->ta_pending, p->ta_next); + } +} +#endif + +rtems_id +rtems_taskqueue_initialize() +{ +#ifdef DEBUG + TASK_INIT( &taskA.t, 0, the_task_fn, &taskA ); + TASK_INIT( &taskB.t, 0, the_task_fn, &taskB ); + TASK_INIT( &taskC.t, 0, the_task_fn, &taskC ); +#endif + if ( ! the_taskqueue.tid ) + the_taskqueue.tid = rtems_bsdnet_newproc("tskq", 10000, taskqueueDoWork, &the_taskqueue); + return the_taskqueue.tid; +} + +#ifdef DEBUG +void +_cexpModuleInitialize(void *u) +{ + rtems_bsdnet_initialize_network(); + the_taskqueue.tid = rtems_taskqueue_initialize(); +} +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_udelay.c b/bsd_eth_drivers/libbsdport/rtems_udelay.c new file mode 100644 index 0000000..d8f80f4 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_udelay.c @@ -0,0 +1,192 @@ +#include +#include +#include + +#include "rtems_udelay.h" + +#if defined(__PPC__) + +#include + +/* Ouch. stupid bookE doesn't implement mftb so we must + * use mfspr (which wouldn't work on classic ppc if we + * were in user mode but luckily we're not. + */ +static inline uint64_t __read_hires_clicks() +{ +uint32_t tbl, tbu1, tbu2; + asm volatile( + " mfspr %0, %4 \n" + " mfspr %1, %3 \n" + " mfspr %2, %4 \n" + :"=r"(tbu1),"=r"(tbl),"=r"(tbu2) + :"i"(TBRL), "i"(TBRU) + ); + if ( tbu1 != tbu2 ) + asm volatile("mfspr %0, %1":"=r"(tbl):"i"(TBRL)); + return ((uint64_t)tbu2<<32) | tbl; +} + +#define __rtems_hires_kHz (BSP_bus_frequency/BSP_time_base_divisor) + +#elif defined(__i386__) + +static inline uint64_t __read_hires_clicks() +{ +uint32_t lo,hi; + asm volatile("rdtsc":"=a"(lo),"=d"(hi)); + return ( (uint64_t)hi << 32 ) | lo; +} + +#else +#error "rtems_udelay.c not ported to this CPU yet" +#endif + +#ifndef __rtems_hires_kHz +/* Clock frequency of high-resolution timer */ +uint32_t __rtems_hires_kHz = 0; +uint32_t rtems_udelay_calibrate(); +#endif + + +void rtems_usec_delay(uint32_t usecs) +{ +uint64_t clicks = __read_hires_clicks(); +int ticks; + if (usecs > 10) { + if ( _ISR_Is_in_progress() ) { + rtems_panic("rtems_usec_delay for more than 10us in ISR!!"); + } + if ( _ISR_Get_level() > 0 ) { + rtems_panic("rtems_usec_delay for more than 10us with IRQs disabled!!"); + } + } + ticks = usecs/rtems_configuration_get_microseconds_per_tick(); +#ifndef __rtems_hires_kHz + /* If it's not a macro; do lazy init */ + if ( 0 == __rtems_hires_kHz ) { + uint64_t clicks = rtems_udelay_calibrate(); + __rtems_hires_kHz = (clicks * 1000) / rtems_configuration_get_microseconds_per_tick(); + ticks--; + } +#endif + clicks += (usecs * __rtems_hires_kHz)/1000; + if ( ticks > 0 ) + rtems_task_wake_after(ticks); + + while ( clicks > __read_hires_clicks() ) + /* busy wait */; +} + +/* This doesn't belong here; also, the RTEMS timeout() implementation is buggy: + * if a timeout is added when the networking task is asleep then I believe 'timeout()' + * is unable to schedule a wakeup. Therefore, I implemented the 'callout' facility. + */ +#ifdef UNTESTED +/* Must be executed with the network semaphore held */ +void +rtems_bsdnet_untimeout(timeout_func_t fn, void *arg) +{ +register struct callout *l, *p; + + for ( l = &calltodo; (p=l->c_next); l=p ) { + if ( p->c_func == fn && p->c_arg == arg ) { + register struct callout *n + /* found it */ + if ( (n = p->c_next) && p->c_time > 0 ) { + /* adjust time of following entry */ + n->c_time += p->c_time; + } + /* extract */ + l->c_next = n; + /* return to extract first occurrence; continue + * to extract all + */ +#if 0 + return; +#else + p = l; +#endif + } + } +} +#endif + +struct caldat { + uint64_t t0,t1,t2,t3; + rtems_id thetid; +}; + +#ifndef __rtems_hires_kHz +static void +tickmeas1(rtems_id myself, void *arg) +{ +struct caldat *p = arg; + p->t2 = __read_hires_clicks(); + rtems_event_send(p->thetid, RTEMS_EVENT_0); +} + +static void +tickmeas0(rtems_id myself, void *arg) +{ +struct caldat *p = arg; + p->t1 = __read_hires_clicks(); + rtems_timer_fire_after( myself, 1, tickmeas1, arg ); +} + +/* Calibrate high-resolution timer */ +uint32_t +rtems_udelay_calibrate() +{ +rtems_id timer; +rtems_status_code sc; +rtems_event_set ev; +struct caldat d; + + /* measure a clock tick with the hires timer; + * note that we can't just sleep for 1 tick because + * that results in sleeping for an unknown fraction + * of a tick... + */ + sc = rtems_timer_create( rtems_build_name('h','r','e','s'), &timer ); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to create timer:%i\n", sc); + } + sc = rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &d.thetid); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to read my own TID:%i\n", sc); + } +#ifdef DEBUG + d.t0 = __read_hires_clicks(); +#endif + sc = rtems_timer_fire_after(timer, 1, tickmeas0, &d); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to fire timer:%i\n", sc); + } + sc = rtems_event_receive( RTEMS_EVENT_0, RTEMS_EVENT_ANY | RTEMS_WAIT , RTEMS_NO_TIMEOUT, &ev); +#ifdef DEBUG + d.t3 = __read_hires_clicks(); +#endif + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_panic("Unable to synchronize with timer:%i\n", sc); + } + rtems_timer_delete(timer); +#ifdef DEBUG + printf("Diffs: %llu %llu %llu\n", + d.t3-d.t2, d.t2-d.t1, d.t1-d.t0); +#endif + return d.t2-d.t1; +} +#endif + +#ifdef DEBUG +unsigned +hdiff(unsigned s) +{ +uint64_t now = __read_hires_clicks(); + rtems_task_wake_after(s); + now = __read_hires_clicks() - now; + printf("Diff was %llu clicks\n",now); + return (unsigned)now; +} +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_udelay.h b/bsd_eth_drivers/libbsdport/rtems_udelay.h new file mode 100644 index 0000000..32a3b62 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_udelay.h @@ -0,0 +1,22 @@ +#ifndef RTEMS_UDELAY_Y +#define RTEMS_UDELAY_Y + +#ifdef __cplusplus +extern "C" { +#endif + +/* Delay execution for n microseconds. The current task + * is suspended for multiples of OS 'ticks' and busy-waits + * for fractions thereof. + * The routine panics if requested to delay for more than + * 10us in an ISR or IRQ-disabled section of code. + */ +void rtems_usec_delay(uint32_t usecs); + +#define DELAY(usecs) rtems_usec_delay(usecs) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsd_eth_drivers/libbsdport/rtems_verscheck.h b/bsd_eth_drivers/libbsdport/rtems_verscheck.h new file mode 100644 index 0000000..519f817 --- /dev/null +++ b/bsd_eth_drivers/libbsdport/rtems_verscheck.h @@ -0,0 +1,29 @@ +#ifndef RTEMS_VERSION_CHECKER_H +#define RTEMS_VERSION_CHECKER_H +/* $Id$ Macros to check rtems version dependent API features :-( */ + +#include + +#define RTEMS_REV_LATER_THAN(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ > (re)) \ + ) + +#define RTEMS_REV_AT_LEAST(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ >= (re)) \ + ) + +/* + * unfortunately, (powerpc) libcpu/io.h didn't follow the change from + * unsigned -> uin32_t :--( + */ +#if RTEMS_REV_AT_LEAST(4,8,0) +typedef uint32_t libbsdport_u32_t; +#else +typedef unsigned libbsdport_u32_t; +#endif + +#endif diff --git a/bsd_eth_drivers/libbsdport/sysbus.c b/bsd_eth_drivers/libbsdport/sysbus.c new file mode 100644 index 0000000..fc0ae0b --- /dev/null +++ b/bsd_eth_drivers/libbsdport/sysbus.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#if !RTEMS_REV_AT_LEAST(4,6,99) || !defined(BSP_SHARED_HANDLER_SUPPORT) + +#include + +#else + +static void noop(const rtems_irq_connect_data *unused) {}; +static int noop1(const rtems_irq_connect_data *unused) { return 0;}; + +/* Finally have an ISR arg but the API still sucks.. */ +static int +bspExtInstallSharedISR(int irqLine, void (*isr)(void *), void * uarg, int flags) +{ +rtems_irq_connect_data suck = {0}; + suck.name = irqLine; + suck.hdl = isr; + suck.handle = uarg; + suck.on = noop; + suck.off = noop; + suck.isOn = noop1; + return ! BSP_install_rtems_shared_irq_handler(&suck); +} + +static int +bspExtRemoveSharedISR(int irqLine, void (*isr)(void *), void *uarg) +{ +rtems_irq_connect_data suck = {0}; + suck.name = irqLine; + suck.hdl = isr; + suck.handle = uarg; + suck.on = noop; + suck.off = noop; + suck.isOn = noop1; + return ! BSP_remove_rtems_irq_handler(&suck); +} +#endif + + +struct resource * +bus_alloc_resource_any(device_t dev, int type, int *prid, unsigned flags) +{ +bus_addr_t ba; +int isio; + switch ( type ) { + default: + break; + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + { + libbsdport_u32_t d; + pci_read_config_dword( + dev->bushdr.pci.bus, + dev->bushdr.pci.dev, + dev->bushdr.pci.fun, + *prid, + &d); + ba = d; + isio = (ba & PCI_BASE_ADDRESS_SPACE_IO) ? 1 : 0; + if ( (type == SYS_RES_IOPORT) != (isio != 0) ) + return 0; /* wrong type */ + + return (struct resource *) ba; + } + case SYS_RES_IRQ: + { + uint8_t line; + pci_read_config_byte( + dev->bushdr.pci.bus, + dev->bushdr.pci.dev, + dev->bushdr.pci.fun, + PCI_INTERRUPT_LINE, + &line); + ba = line; + /* MSI not implemented */ + return (struct resource*) ba; + } + } + rtems_panic("bus_alloc_resource_any: unknown/unimplemented resource type %i\n", type); + /* never get here */ + return (struct resource*)0; +} + +struct irq_cookie { + device_t dev; + driver_filter_t handler; + void (*work)(void*); + void *arg; + /* cache methods */ + int (*irq_check_dis)(device_t d); + void (*irq_en) (device_t d); + struct task task; +}; + +static int +sysbus_isr(void *arg) +{ +struct irq_cookie *info = arg; +int rval; +#ifdef DEBUG + printk("Sysbus IRQ\n"); +#endif + /* Check if we have an IRQ pending and disable further interrupts */ + rval = info->irq_check_dis(info->dev); + if ( FILTER_HANDLED == rval ) { + /* enqueue work */ + taskqueue_enqueue(taskqueue_fast, &info->task); + } + return rval; +} + +static void +sysbus_taskfn(void *arg, int pending) +{ +struct irq_cookie *info = arg; + + /* do work */ + info->work(info->arg); + + /* reenable interrupts */ + if ( info->irq_en ) + info->irq_en(info->dev); +} + +int +bus_setup_intr(device_t dev, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep) +{ +int rval; +struct irq_cookie *info = 0; + + if ( filter && handler ) { + rtems_panic("bus_setup_intr for both: filter & handler not implemented\n"); + } + + if ( (flags & INTR_FAST) && filter ) { + rtems_panic("bus_setup_intr for both: filter & INTR_FAST not implemented\n"); + /* handler is a fast handler already */ + filter = (driver_filter_t) handler; + handler = 0; + } + + if ( handler ) { + if ( !dev->drv ) { + device_printf(dev, "bus_setup_intr: device has no driver attached\n"); + return EINVAL; + } else if ( !dev->drv->methods->irq_check_dis ) { + device_printf(dev, "bus_setup_intr: driver has no 'irq_dis' method\n"); + return EINVAL; + } + } + + if ( ! (info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT)) ) + return ENOMEM; + + info->dev = dev; + info->handler = filter; + info->work = handler; + info->arg = arg; + + if ( handler ) { + TASK_INIT(&info->task, 0, sysbus_taskfn, info); + /* make sure taskqueue facility is initialized */ + rtems_taskqueue_initialize(); + /* install our own filter */ + filter = sysbus_isr; + arg = info; + info->irq_check_dis = dev->drv->methods->irq_check_dis; + info->irq_en = dev->drv->methods->irq_en; + } else { + TASK_INIT(&info->task, 0, 0, 0); + } + + rval = bspExtInstallSharedISR((int)r, (void (*)(void*))filter, arg, 0); + + if ( rval ) { + free(info, M_DEVBUF); + return rval; + } + + if ( cookiep ) + *cookiep = info; + return rval; +} + +int +bus_teardown_intr(device_t dev, struct resource *r, void *cookiep) +{ +int rval; +struct irq_cookie *info = cookiep; + rval = bspExtRemoveSharedISR((int)r, (void (*)(void*))info->handler, info->arg); + if ( 0 == rval ) { + if ( info->task.ta_fn ) { + taskqueue_drain(taskqueue_fast, &info->task); + } + free(info, M_DEVBUF); + } + return rval; +} + +bus_space_handle_t +rman_get_bushandle(struct resource *r) +{ +bus_space_handle_t h = (bus_space_handle_t)r; +bus_space_handle_t msk = (PCI_BASE_ADDRESS_SPACE_IO & h) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK; + return h & msk; +} + +bus_space_tag_t +rman_get_bustag(struct resource *r) +{ +bus_space_handle_t h = (bus_space_handle_t)r; + return (PCI_BASE_ADDRESS_SPACE_IO & h) ? bus_space_io : bus_space_mem; +} + +int +bus_dma_tag_create(void *parent, unsigned alignment, unsigned bounds, uint32_t lowadd, uint32_t hiaddr, void (*filter)(void*), void *filterarg, unsigned maxsize, int nsegs, unsigned maxsegsize, unsigned flags, void (*lockfunc)(void*), void *lockarg, bus_dma_tag_t *ptag) +{ +bus_dma_tag_t tag; + if ( filter || lockfunc ) + return ENOTSUP; + if ( ! (tag = malloc(sizeof(*tag), M_DEVBUF, M_NOWAIT)) ) + return ENOMEM; + /* save some information */ + tag->alignment = alignment; + tag->maxsize = maxsize; + tag->maxsegs = nsegs; + *ptag = tag; + return 0; +} + +void +bus_dma_tag_destroy(bus_dma_tag_t tag) +{ + free(tag, M_DEVBUF); +} + +int +bus_dmamem_alloc(bus_dma_tag_t tag, void **p_vaddr, unsigned flags, bus_dmamap_t *p_map) +{ +uintptr_t a; + if ( ! (*p_map = malloc(tag->maxsize + tag->alignment, M_DEVBUF, M_NOWAIT)) ) + return ENOMEM; + a = ((uintptr_t)*p_map + tag->alignment - 1 ) & ~(tag->alignment - 1); + *p_vaddr = (void*)a; + return 0; +} + +void +bus_dmamem_free(bus_dma_tag_t tag, void *vaddr, bus_dmamap_t map) +{ + free(map, M_DEVBUF); +} diff --git a/bsd_eth_drivers/libbsdport/taskqueue.h b/bsd_eth_drivers/libbsdport/taskqueue.h new file mode 100644 index 0000000..d700edf --- /dev/null +++ b/bsd_eth_drivers/libbsdport/taskqueue.h @@ -0,0 +1,69 @@ +#ifndef RTEMS_TASKQUEUE_H +#define RTEMS_TASKQUEUE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct taskqueue; + +typedef void (*task_fn)(void *ctxt, int pending); + +/* forwarded 'ctxt' that was passed to taskqueue_create() */ +typedef void (*tq_enq_fn)(void *ctxt); + +struct task { + struct task *ta_next; + int ta_pending; + int ta_priority; + task_fn ta_fn; + void *ta_fn_arg; +}; + +struct taskqueue * +taskqueue_create(const char *name, int mflags, tq_enq_fn, void *ctxt); + +struct taskqueue * +taskqueue_create_fast(const char *name, int mflags, tq_enq_fn, void *ctxt); + +int +taskqueue_enqueue(struct taskqueue *tq, struct task *ta); + +#define taskqueue_enqueue_fast(_q,_t) taskqueue_enqueue(_q,_t) + +void +taskqueue_thread_enqueue(void *ctxt); + +#define PI_NET 150 +/* Returns 0 on success */ +int +taskqueue_start_threads(struct taskqueue **ptq, int count, int prio, const char *fmt, ...); + +void +taskqueue_drain(struct taskqueue *tq, struct task *ta); + +void +taskqueue_free(struct taskqueue *tq); + +#define TASK_INIT(task, pri, fn, arg) \ + do { \ + (task)->ta_next = 0; \ + (task)->ta_priority = (pri); \ + (task)->ta_pending = 0; \ + (task)->ta_fn = (fn); \ + (task)->ta_fn_arg = (arg); \ + } while (0) + +extern struct taskqueue *taskqueue_fast; + +/* Initialize taskqueue facility [networking must have been initialized already] */ +rtems_id +rtems_taskqueue_initialize(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsd_eth_drivers/links.am b/bsd_eth_drivers/links.am new file mode 100644 index 0000000..22c6e8e --- /dev/null +++ b/bsd_eth_drivers/links.am @@ -0,0 +1,15 @@ +CLOBBER_ADDITIONS = $(LINKS) $(DUMMYHEADERS) + +$(DUMMYHEADERS): + @if [ ! -d `dirname $@` ] ; then mkdir -p `dirname $@`; fi + @touch $@ + +$(LINKS): + @if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@); fi + @ln -s `echo $(dir $@) | sed -e 's%[^/]\+[/]\+%../%g'`$(srcdir)/$(notdir $@) $@ + + +BUILT_SOURCES += $(DUMMYHEADERS) $(LINKS) + +distclean-local: + $(RM) -r $(CLOBBER_ADDITIONS) diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..e7ec2ae --- /dev/null +++ b/config.h.in @@ -0,0 +1,48 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Whether BSP_commandline_string is declared in */ +#undef DECL_BSP_COMMANDLINE_STRING + +/* Define to 1 if you have the `BSP_commandline_string' function. */ +#undef HAVE_BSP_COMMANDLINE_STRING + +/* Whether CEXP is installed */ +#undef HAVE_CEXP + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + + +#ifndef RTEMS_VERSION_LATER_THAN +#define RTEMS_VERSION_LATER_THAN(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ > (re)) \ + ) +#endif +#ifndef RTEMS_VERSION_ATLEAST +#define RTEMS_VERSION_ATLEAST(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ >= (re)) \ + ) +#endif + + +/* Version number of package */ +#undef VERSION diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..e4a5e53 --- /dev/null +++ b/configure.ac @@ -0,0 +1,378 @@ +AC_PREREQ(2.63) + +m4_include(./m4/cvstag.m4) + +AC_INIT(ssrlApps, TILLAC_CVSTAG([$Name$]), ) + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE([foreign 1.10]) +AM_MAINTAINER_MODE + +# Check for critical programs we need for building +TILLAC_RTEMS_CHECK_TOOLS + +# Must add this BEFORE TILLAC_RTEMS_SETUP +# so that the dummy-top 'config.status' also +# knows how to make a config.h +AM_CONFIG_HEADER(config.h) + +TILLAC_RTEMS_SETUP + +AC_SUBST([enable_subdirs]) +AC_SUBST([all_subdirs]) +AC_SUBST([rtems_cpu],[$host_cpu]) +AC_SUBST([rtems_bsp],[$enable_rtemsbsp]) + +# Let this configure and sub-configures search +# the temporary installdir for includes and libraries... +CPPFLAGS="$CPPFLAGS -I`pwd`/data/include" +LDFLAGS="$LDFLAGS -L`pwd`/data/lib" + +dnl This doesn't work -- somehow automake gets confused +dnl (perhaps because we have both, a Makefile and a makefile): +dnl automake doesn't produce a Makefile.in. However, if +dnl we add AC_CONFIG_FILES([Makefile]) then it works... +dnl +dnl my_config_files=Makefile +dnl +AC_CONFIG_FILES([Makefile]) +enable_subdirs= +all_subdirs= + +TILLAC_M4_IF_PRESENT([libbspExt], +[if test -d $srcdir/libbspExt ; then + have_bspext=yes + AC_CONFIG_FILES([libbspExt/Makefile]) + all_subdirs="$all_subdirs libbspExt" + case ${host_cpu} in + powerpc | i386) + enable_subdirs="${enable_subdirs} libbspExt" + AC_DEFINE(HAVE_LIBBSPEXT,1,[Whether we have the libbspExt extension library]) + ;; + *) + ;; + esac +fi]dnl +) + +TILLAC_M4_IF_PRESENT([rtems-gdb-stub], +[if test -d $srcdir/rtems-gdb-stub; then + AC_CONFIG_FILES([rtems-gdb-stub/Makefile]) + all_subdirs="$all_subdirs rtems-gdb-stub" + case ${host_cpu} in + powerpc | i386 | m68k) + enable_subdirs="${enable_subdirs} rtems-gdb-stub" + ;; + *) + ;; + esac +fi]dnl +) + +# Check for PCI support +AC_MSG_NOTICE([Checking for PCI support of your BSP]) +# No use to use a 'CHECKING' message; AC_CHECK_FUNC already reports... +AC_CHECK_FUNC(pci_find_device) + +# BSP_commandline_string is not a function but +# the macro just checks if it can link the symbol +# which is good enough +AC_CHECK_FUNCS([BSP_commandline_string]) +AC_CHECK_DECL([BSP_commandline_string], + [AC_DEFINE([DECL_BSP_COMMANDLINE_STRING],1,[Whether BSP_commandline_string is declared in ])],, + [#include + #include ]) + +TILLAC_M4_IF_PRESENT([telnetd], +[if test -d $srcdir/telnetd ; then + AC_CONFIG_FILES([telnetd/Makefile]) + all_subdirs="${all_subdirs} telnetd" + enable_subdirs="${enable_subdirs} telnetd" + AC_MSG_CHECKING([if bundled rtems_telnetd_initialize() takes 6 args]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], + [[rtems_telnetd_initialize(0,0,0,0,0,0);]])], + [AC_MSG_RESULT([Yes]) + AC_CHECK_LIB([telnetd],[rtems_telnetd_initialize], + [AC_MSG_NOTICE([Using bundled telnetd library]) + have_bundled_telnetd=yes + ], + [AC_MSG_NOTICE([Building unbundled telnetd support]) + have_bundled_telnetd=no + ], + TILLAC_RTEMS_CHECK_LIB_ARGS) + ], + [AC_MSG_NOTICE([No; building unbundled telnetd support])] + ) +fi]dnl +) + +TILLAC_M4_IF_PRESENT([miscUtils], +[if test -d $srcdir/miscUtils ; then + AC_CONFIG_FILES([miscUtils/Makefile]) + enable_subdirs="${enable_subdirs} miscUtils" + all_subdirs="${all_subdirs} miscUtils" +fi]dnl +) + +TILLAC_M4_IF_PRESENT([monitor], +[if test -d $srcdir/monitor ; then + AC_CONFIG_FILES([monitor/Makefile]) + all_subdirs="${all_subdirs} monitor" + if test "${enable_tecla}" = "no" ; then + AC_MSG_NOTICE(['monitor' package disabled because you disabled TECLA -- the monitor would depend on it]) + else + enable_subdirs="${enable_subdirs} monitor" + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([ntpNanoclock], +[if test -d $srcdir/ntpNanoclock ; then + AC_CONFIG_FILES([ntpNanoclock/Makefile]) + all_subdirs="${all_subdirs} ntpNanoclock" + enable_subdirs="${enable_subdirs} ntpNanoclock" +fi]dnl +) + + +if test -d $srcdir/cexp ; then + all_subdirs="$all_subdirs cexp" + enable_subdirs="$enable_subdirs cexp" + AC_CONFIG_SUBDIRS([cexp]) + AC_DEFINE(HAVE_CEXP,1,[Whether CEXP is installed]) + have_cexp=yes +fi + +TILLAC_M4_IF_PRESENT([svgmWatchdog], +[if test -d $srcdir/svgmWatchdog ; then + AC_CONFIG_FILES([svgmWatchdog/Makefile]) + all_subdirs="${all_subdirs} svgmWatchdog" + # Do we have BSP support for our watchdog + AC_MSG_CHECKING([Looking for BSP support for the watchdog]) + if test -f $srcdir/svgmWatchdog/bsp_${enable_rtemsbsp}.c ; then + AC_MSG_RESULT([OK, found BSP support]) + WATCHDOG_BSP_SUPPORT=bsp_${enable_rtemsbsp} + # No BSP support - is this a known BSP with a E500 CPU ? + elif case $enable_rtemsbsp in mvme3100 ) true ;; *) false;; esac ; then + AC_MSG_RESULT([OK, using generic PPC-E500 support]) + WATCHDOG_BSP_SUPPORT=bsp_booke + # No; if it is a i386 board then we hope it has SMIC + elif test "${host_cpu}" = "i386" ; then + AC_MSG_RESULT([MAYBE, using generic SMIC support; hoping your board has SMIC]) + WATCHDOG_BSP_SUPPORT=bsp_smic + else + AC_MSG_RESULT([FAILED. Watchdog not supported on this platform]) + fi + if test "${WATCHDOG_BSP_SUPPORT+set}" = "set" ; then + enable_subdirs="${enable_subdirs} svgmWatchdog" + AC_SUBST([WATCHDOG_BSP_SUPPORT]) + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([coldfUtils], +[if test -d $srcdir/coldfUtils ; then + AC_CONFIG_FILES([coldfUtils/Makefile]) + all_subdirs="${all_subdirs} coldfUtils" + if test "${enable_rtemsbsp}" = "uC5282" ; then + enable_subdirs="${enable_subdirs} coldfUtils" + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([efence], +[if test -d $srcdir/efence ; then + AC_CONFIG_FILES([efence/Makefile]) + all_subdirs="${all_subdirs} efence" + if test "${host_cpu}" = "powerpc" ; then +# should really check if this is a >= 604 PPC + AC_CHECK_HEADER([libcpu/pte121.h], + [enable_subdirs="${enable_subdirs} efence"], + [AC_MSG_NOTICE([No found; not building libefence])]dnl + ) + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([altivec], +[if test -d $srcdir/altivec ; then + AC_CONFIG_FILES([altivec/Makefile]) + all_subdirs="${all_subdirs} altivec" + if test "${host_cpu}" = "powerpc" ; then + AC_MSG_NOTICE([Checking if PPC_CACHE_ALIGNMENT is 32 bytes]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( +[[ +#include +#include +#include +#if PPC_CACHE_ALIGNMENT != 32 +#error "Altivec support assumes cache-line size of 32 bytes" +#endif +]],[[]])], + [AC_MSG_RESULT([Ok]) + enable_subdirs="${enable_subdirs} altivec"], + [AC_MSG_RESULT([No; not building AltiVec extension])]dnl + ) + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([amdeth], +[if test -d $srcdir/amdeth ; then + AC_CONFIG_FILES([amdeth/Makefile]) + all_subdirs="${all_subdirs} amdeth" + if test "$ac_cv_func_pci_find_device" = "yes" ; then + enable_subdirs="${enable_subdirs} amdeth" + else + AC_MSG_NOTICE([Your BSP doesn't seem to have PCI; not building the amdeth driver]) + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([drvLan9118], +[if test -d $srcdir/drvLan9118 ; then + AC_CONFIG_FILES([drvLan9118/Makefile]) + all_subdirs="${all_subdirs} drvLan9118" + case $enable_rtemsbsp in + uC5282 | beatnik) + LANIPBASIC_SUPPORT="${enable_rtemsbsp}" + ;; + *) + if test "$ac_cv_func_pci_find_device" = "yes" ; then + LANIPBASIC_SUPPORT=pci + fi + ;; + esac + if test "${LANIPBASIC_SUPPORT+set}" = "set" ; then + enable_subdirs="${enable_subdirs} drvLan9118" + AC_SUBST(LANIPBASIC_SUPPORT) + else + AC_MSG_NOTICE([lanIpBasic package not supported by this BSP -- not built]) + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([rtemsNfs], +[if test -d $srcdir/rtemsNfs ; then + AC_CONFIG_FILES([rtemsNfs/proto/Makefile]) + AC_CONFIG_FILES([rtemsNfs/Makefile]) + AC_CONFIG_FILES([rtemsNfs/src/Makefile]) + all_subdirs="${all_subdirs} rtemsNfs" +fi]dnl +) + +# looking for bundled NFS; +build_rtems_nfs=no +AC_MSG_CHECKING([whether we should build unbundled NFS]) +# Note the 5th argument; linking would fail because +# the application usually supplies rtems_bsdnet_config. +# In order to link, we create a dummy symbol. +AC_CHECK_LIB([nfs],[nfsMount], + [AC_MSG_NOTICE([Using RTEMS bundled NFS]) + AC_SUBST([NFSLIB],["-lnfs"]) + if test -d $srcdir/rtemsNfs ; then + AC_MSG_NOTICE([Building 'dirutils' only (from unbundled NFS)]) + build_rtems_nfs=dirutils_only + fi], + [if test -d $srcdir/rtemsNfs ; then + AC_MSG_NOTICE([Building unbundled NFS]) + build_rtems_nfs=yes + AC_SUBST([NFSLIB],["-lrtemsNfs -lnfsprot"]) + fi], + TILLAC_RTEMS_CHECK_LIB_ARGS +) +AC_MSG_RESULT([Building unbundled NFS: $build_rtems_nfs]) + +if test ! "$build_rtems_nfs" = "no" ; then + enable_subdirs="${enable_subdirs} rtemsNfs" +fi + +TILLAC_M4_IF_PRESENT([bsd_eth_drivers], +[if test -d $srcdir/bsd_eth_drivers ; then + AC_CONFIG_FILES([bsd_eth_drivers/Makefile]) + AC_CONFIG_FILES([bsd_eth_drivers/libbsdport/Makefile]) + AC_CONFIG_FILES([bsd_eth_drivers/bge/Makefile]) + AC_CONFIG_FILES([bsd_eth_drivers/if_pcn/Makefile]) + AC_CONFIG_FILES([bsd_eth_drivers/if_le/Makefile]) + AC_CONFIG_FILES([bsd_eth_drivers/if_em/Makefile]) + AC_CONFIG_FILES([bsd_eth_drivers/re/Makefile]) + all_subdirs="${all_subdirs} bsd_eth_drivers" + if test "$ac_cv_func_pci_find_device" = "yes" ; then +## if test "${host_cpu}" = i386 && test ! "${have_bspext}" = yes ; then +## AC_MSG_NOTICE([Not building bsd_eth_drivers; on i386 you need libbspExt]) +## else + #FIXME: make these configurable options + ENBL_82542_SUPPORT=NO + ENBL_ICH8LAN_SUPPORT=YES + AC_SUBST([ENBL_82542_SUPPORT]) + AC_SUBST([ENBL_ICH8LAN_SUPPORT]) + enable_subdirs="${enable_subdirs} bsd_eth_drivers" +## fi + fi +fi]dnl +) + +TILLAC_M4_IF_PRESENT([netboot], +[if test -d $srcdir/netboot ; then + AC_CONFIG_FILES([netboot/Makefile]) + all_subdirs="${all_subdirs} netboot" + case "$enable_rtemsbsp" in + svgm|beatnik|uC5282|mvme3100) + if test "${enable_tecla}" = "no" ; then + AC_MSG_NOTICE(['netboot' package disabled because you disabled TECLA -- the monitor would depend on it]) + else + enable_subdirs="${enable_subdirs} netboot" + case "$enable_rtemsbsp" in + svgm) + netboot_compressed=yes + ;; + *) + ;; + esac + fi + ;; + *) + ;; + esac +fi]dnl +) + +system_subdir= +TILLAC_M4_IF_PRESENT([system], +[if test -d $srcdir/system ; then + all_subdirs="$all_subdirs system" + case "$enable_rtemsbsp" in + rce405) + # real application is built somewhere else + ;; + *) + enable_subdirs="$enable_subdirs system" + AC_CONFIG_FILES([ssrlApps.components]) + AC_CONFIG_SUBDIRS([system]) + ;; + esac +fi]dnl +) + +AC_SUBST([system_subdir]) + +# an ugly hack so we can give programs that +# should run on the build host a different +# extension. +# The only way automake does not append $(EXEEXT) +# is if a PROGRAM is configure-substituted +# (see automake doc 'EXEEXT') +AC_SUBST([HOSTPROGRAM],['$(HOSTPROG)$(HOSTEXEEXT)']) +AC_SUBST([installexechostbinprogs],[install-exechostbinPROGRAMS]) + +AM_CONDITIONAL([HAVE_CEXP], [test "$have_cexp" = "yes"]) +AM_CONDITIONAL([BUILD_RTEMS_NFS], [test "$build_rtems_nfs" = "yes"]) +AM_CONDITIONAL([HAVE_BUNDLED_TELNETD], [test "$have_bundled_telnetd" = "yes"]) +AM_CONDITIONAL([NETBOOT_COMPRESSED], [test "$netboot_compressed" = "yes"]) + +AC_OUTPUT diff --git a/m4/config-if-present.m4 b/m4/config-if-present.m4 new file mode 100644 index 0000000..ed402ff --- /dev/null +++ b/m4/config-if-present.m4 @@ -0,0 +1,23 @@ +dnl while we can get autoconf to properly handle nonexisting +dnl directories (e.g., because a sub-package only requires and +dnl distributes a subset of the directories under ssrlApps) +dnl via +dnl if [ -d somedir ] ; then AC_CONFIG_FILES([somedir/Makefile]) ; fi +dnl this doesn't work for automake -- automake doesn't understand +dnl the shell commands. +dnl +dnl The 'TILLAC_M4_IF_PRESENT' macro tests at 'autoconf/automake'-time +dnl if the argument file with '.am' appended exists and expands +dnl $2 if it does. Otherwise, $2 is suppressed. +dnl +dnl USAGE: +dnl +dnl TILLAC_M4_IR_PRESENT( +dnl [mysubdir/Makefile], +dnl [ After expanding this macro 'enable_rtemsbsp' contains a list +# of all BSPs that are installed and -- if 'enable_rtemsbsp' was +# initially set -- which are mentioned in 'enable_rtembsp'. +# +# TILLAC_RTEMS_CHECK_BSPS +# +# NOTE: This macro *modifies* the 'enable_rtemsbsp' variable. +# +AC_DEFUN([TILLAC_RTEMS_CHECK_BSPS], + [AC_REQUIRE([TILLAC_RTEMS_OPTIONS]) + if test ! "${enable_rtemsbsp+set}" = "set" ; then + _tillac_rtems_bsplist="`ls $with_rtems_top/${host_cpu}-${host_os}/ | tr '\n\r' ' '`" + else + _tillac_rtems_bsplist=$enable_rtemsbsp + fi + enable_rtemsbsp= + AC_MSG_CHECKING([Looking for RTEMS BSPs $_tillac_rtems_bsplist]) + for _tillac_rtems_bspcand in $_tillac_rtems_bsplist ; do + if test -d $with_rtems_top/${host_cpu}-${host_os}/$_tillac_rtems_bspcand/lib/include ; then + if test "${enable_rtemsbsp}"xx = xx ; then + enable_rtemsbsp="$_tillac_rtems_bspcand" + else + enable_rtemsbsp="$_tillac_rtems_bspcand $enable_rtemsbsp" + fi + fi + done + if test "$enable_rtemsbsp"xx = "xx" ; then + AC_MSG_ERROR("No BSPs found") + else + AC_MSG_NOTICE([found \'$enable_rtemsbsp\']) + fi]dnl +) diff --git a/m4/rtems-check-libargs.m4 b/m4/rtems-check-libargs.m4 new file mode 100644 index 0000000..27e67e1 --- /dev/null +++ b/m4/rtems-check-libargs.m4 @@ -0,0 +1,9 @@ +# This macro can be provided as a 5th argument +# to AC_CHECK_LIB() so that linking an RTEMS +# application works. Without that, linking would +# fail because the application usually supplies +# rtems_bsdnet_config. +# In order to link, we create a dummy symbol. +AC_DEFUN([TILLAC_RTEMS_CHECK_LIB_ARGS], + [[-Wl,--defsym,rtems_bsdnet_config=0]]dnl +) diff --git a/m4/rtems-checkprog.m4 b/m4/rtems-checkprog.m4 new file mode 100644 index 0000000..700f5b9 --- /dev/null +++ b/m4/rtems-checkprog.m4 @@ -0,0 +1,9 @@ +## Check for a program, similar to AC_CHECK_PROG, but lets +## configure fail if the program is not found +#dnl RTEMS_CHECK_PROG(VARIABLE, PROG-TO-CHECK-FOR, VALUE-IF-FOUND [, VALUE-IF-NOT-FOUND [, PATH [, REJECT]]]) +AC_DEFUN([RTEMS_CHECK_PROG], +[ + AC_CHECK_PROG($1,$2,$3,$4,$5,$6) + AS_IF([test -z "${$1}"], + [AC_MSG_ERROR([program '$2' not found.])]) +]) diff --git a/m4/rtems-checktool.m4 b/m4/rtems-checktool.m4 new file mode 100644 index 0000000..b5e4da1 --- /dev/null +++ b/m4/rtems-checktool.m4 @@ -0,0 +1,11 @@ +## Check for a cross tool, similar to AC_CHECK_TOOL, but do not fall back to +## the un-prefixed version of PROG-TO-CHECK-FOR. +## Also - if tool is not found then produce an error. +#dnl RTEMS_CHECK_TOOL(VARIABLE, PROG-TO-CHECK-FOR[, VALUE-IF-NOT-FOUND [, PATH]]) +AC_DEFUN([RTEMS_CHECK_TOOL], +[ + AS_IF([test "x$build_alias" != "x$host_alias"], + [rtems_tool_prefix=${ac_tool_prefix}]) + RTEMS_CHECK_PROG($1, ${rtems_tool_prefix}$2, ${rtems_tool_prefix}$2, $3, $4) + AC_SUBST($1) +]) diff --git a/m4/rtems-checktop.m4 b/m4/rtems-checktop.m4 new file mode 100644 index 0000000..6a8155b --- /dev/null +++ b/m4/rtems-checktop.m4 @@ -0,0 +1,23 @@ +# Verify that the --with-rtems-top option has been given +# and that the directory it specifies has a subdirectory +# ${with_rtems_top}/${host_cpu}-${host_os} +# +# Throw an error if this test fails. +# +# TILLAC_RTEMS_CHECK_TOP +# +AC_DEFUN([TILLAC_RTEMS_CHECK_TOP], + [AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([TILLAC_RTEMS_OPTIONS]) + if TILLAC_RTEMS_OS_IS_RTEMS ; then + if test ! "${with_rtems_top+set}" = "set" ; then + AC_MSG_ERROR([No RTEMS topdir given; use --with-rtems-top option]) + fi + AC_MSG_CHECKING([Checking RTEMS installation topdir]) + if test ! -d $with_rtems_top/${host_cpu}-${host_os}/ ; then + AC_MSG_ERROR([RTEMS topdir $with_rtems_top/${host_cpu}-${host_os}/ not found]) + fi + AC_MSG_RESULT([OK]) + fi + ]dnl +) diff --git a/m4/rtems-fixup-prefix.m4 b/m4/rtems-fixup-prefix.m4 new file mode 100644 index 0000000..859da2c --- /dev/null +++ b/m4/rtems-fixup-prefix.m4 @@ -0,0 +1,59 @@ +# fixup the 'exec-prefix' and 'includedir' options: +# - if either is given explicitly by the user then do nothing +# - if user says --enable-std-rtems-installdirs then +# prefix -> ${rtems_top} +# exec-prefix -> ${prefix}// +# libdir -> ${exec-prefix}//lib +# includedir -> ${libdir}/include +# +# - if user says nothing then +# +# exec-prefix -> ${prefix}/target/ssrlApps/// +# includedir -> ${exec-prefix}/include +# +AC_DEFUN([TILLAC_RTEMS_FIXUP_PREFIXES], +[ +AC_REQUIRE([TILLAC_RTEMS_OPTIONS]) +if TILLAC_RTEMS_OS_IS_RTEMS ; then +if test "${enable_std_rtems_installdirs}" = "yes" ; then + prefix=${with_rtems_top} + exec_prefix='${prefix}/${host_cpu}-${host_os}/' + libdir='${exec_prefix}/'${enable_rtemsbsp}/lib + if test "$enable_multilib" = "yes" ; then + includedir='${exec_prefix}/include' + else + includedir='${libdir}/include' + fi + ac_configure_args="${ac_configure_args} --prefix='${prefix}'" + ac_configure_args="${ac_configure_args} --exec-prefix='${exec_prefix}'" + ac_configure_args="${ac_configure_args} --libdir='${libdir}'" + ac_configure_args="${ac_configure_args} --includedir='${includedir}'" +else +# should be correct also for multilibbed build (rtems_bsp empty) + if test "${exec_prefix}" = "NONE" ; then + exec_prefix='${prefix}/target/ssrlApps/${host_cpu}-${host_os}/'${enable_rtemsbsp}/ + ac_configure_args="${ac_configure_args} --exec-prefix='${exec_prefix}'" + fi + # Unfortunately we have no way to check if includedir was set by the user + # other than scanning the argument line :-( + tillac_rtems_includedir_set=no + for tillac_rtems_arg in ${ac_configure_args} ; do + case $tillac_rtems_arg in + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc \ + | -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + tillac_rtems_includedir_set=yes; + ;; + *) + ;; + esac + done + + if test "${tillac_rtems_includedir_set}" = "no" ; then + includedir='${exec_prefix}/include' + ac_configure_args="${ac_configure_args} --includedir='${includedir}'" + fi +fi +fi]dnl +) diff --git a/m4/rtems-isrtems.m4 b/m4/rtems-isrtems.m4 new file mode 100644 index 0000000..058be01 --- /dev/null +++ b/m4/rtems-isrtems.m4 @@ -0,0 +1,23 @@ +# Find out if host_os is *rtems*; +# +# Result is exit status, i.e., this macro can e.g., be used +# in a 'if MACRO ; then list; fi' statement. +# +# TILLAC_RTEMS_HOSTOS_IS_RTEMS +AC_DEFUN([TILLAC_RTEMS_HOSTOS_IS_RTEMS], + [AC_REQUIRE([AC_CANONICAL_HOST]) + case "${host_os}" in *rtems* ) : ;; *) false;; esac]dnl +) + + +# Find out if either '--with-rtems-top' was given or +# host_os is *rtems* (or both). +# +# Result is exit status, i.e., this macro can e.g., be used +# in a 'if MACRO ; then list; fi' statement. +# +# TILLAC_RTEMS_OS_IS_RTEMS +AC_DEFUN([TILLAC_RTEMS_OS_IS_RTEMS], + [AC_REQUIRE([AC_CANONICAL_HOST]) + test "${with_rtems_top+set}" = "set" || TILLAC_RTEMS_HOSTOS_IS_RTEMS]dnl +) diff --git a/m4/rtems-makevars.m4 b/m4/rtems-makevars.m4 new file mode 100644 index 0000000..2850dc6 --- /dev/null +++ b/m4/rtems-makevars.m4 @@ -0,0 +1,142 @@ +# This macro saves the BSP-specific variables (such as CC, CXX, CPP, ...) +# into intermediate variables from where they can be restored +# with TILLAC_RTEMS_RESET_MAKEVARS +# +# Note: this macro should not be used directly. It is expanded from +# TILLAC_RTEMS_SETUP +# +# TILLAC_RTEMS_SAVE_MAKEVARS +AC_DEFUN([TILLAC_RTEMS_SAVE_MAKEVARS], + [ + tillac_rtems_cc_orig="$CC" + tillac_rtems_cxx_orig="$CXX" + tillac_rtems_ccas_orig="$CCAS" + tillac_rtems_cpp_orig="$CPP" + tillac_rtems_ldflags_orig="$LDFLAGS" + tillac_rtems_bsp_family_orig="" + tillac_rtems_bsp_insttop_orig=""]dnl +) + +# This macro restores the BSP-specific variables (such as CC, CXX, CPP, ...) +# from intermediate variables that were set by TILLAC_RTEMS_SAVE_MAKEVARS +# +# Note: this macro should not be used directly. It is expanded from +# TILLAC_RTEMS_SETUP +# +# TILLAC_RTEMS_RESET_MAKEVARS +AC_DEFUN([TILLAC_RTEMS_RESET_MAKEVARS], + [ + RTEMS_TILL_MAKEVARS_SET=NO + CC="$tillac_rtems_cc_orig" + CXX="$tillac_rtems_cxx_orig" + CCAS="$tillac_rtems_ccas_orig" + CPP="$tillac_rtems_cpp_orig" + LDFLAGS="$tillac_rtems_ldflags_orig" + RTEMS_BSP_FAMILY="$tillac_rtems_family_orig" + RTEMS_BSP_INSTTOP="$tillac_rtems_insttop_orig"]dnl +) + +# Determine critical, BSP-specific build parameters from the +# RTEMS makefiles. These parameters are cflags, gccspecs, ... +# +# Note: this macro should not be used directly. It is expanded from +# TILLAC_RTEMS_SETUP +# +# TILLAC_RTEMS_MAKEVARS(HOST_SYSTEM, BSP) +AC_DEFUN([TILLAC_RTEMS_MAKEVARS], + [ + AC_MSG_CHECKING([Determining RTEMS Makefile parameters for BSP:]) +dnl DOWNEXT is set in leaf.cfg and we don't include that + if _tillac_rtems_result=`make -s -f - rtems_makevars < and +# +# The macro also, adds the paths listed in the --with-extra-incdirs and +# --with-extra-libdirs options to the cppflags and ldflags, respectively +# (adding -I, -L). If 'LIBSUBDIR' is given then it is appended to +# any of the directories listed in --with-extra-libdirs (if the LIBSUBDIR +# exists) there. +# +# Furthermore, if it is determined that RTEMS was configured for +# a multilibbed cpukit then +# i) -I${with_rtems_top}/${host_cpu}-${host_os}/include +# is added to the cppflags (if the directory exists) +# ii) -B${with_rtems_top}//lib is added to the +# gcc specs so that multilibs are found (workaround for +# a bug in the rtems 4.9.0 makefiles) +# +# Note: this macro should not be used directly. It is expanded from +# TILLAC_RTEMS_SETUP +# +# TILLAC_RTEMS_EXPORT_MAKEVARS([HOST_SYSTEM],[LIBSUBDIR]) +AC_DEFUN([TILLAC_RTEMS_EXPORT_MAKEVARS], + [ + AC_MSG_CHECKING([Checking if RTEMS CC & friends MAKEVARS are already set]) + if test ! "${RTEMS_TILL_MAKEVARS_SET}" = "YES"; then + AC_MSG_RESULT([No (probably a multilibbed build)]) + export RTEMS_TILL_MAKEVARS_SET=YES + # if this is a multilibbed cpukit we need to include + if test -d $with_rtems_top/${host_cpu}-${host_os}/include ; then + tillac_rtems_cppflags="$tillac_rtems_cppflags -I$with_rtems_top/${host_cpu}-${host_os}/include" + # and since the RTEMS (4.9) makefiles seem to be broken + # for multilibbed cpukits (fail to add -B ) we + # do it here + tillac_rtems_gccspecs="$tillac_rtems_gccspecs -B $with_rtems_top/$1/lib" + fi + if test "${with_extra_incdirs+set}" = "set" ; then + for tillac_extra_incs_val in ${with_extra_incdirs} ; do + tillac_rtems_cppflags="$tillac_rtems_cppflags -I$tillac_extra_incs_val" + done + fi + if test "${with_extra_libdirs+set}" = "set" ; then + for tillac_extra_libs_val in ${with_extra_libdirs} ; do + if test -d $tillac_extra_libs_val/$2 ; then + tillac_rtems_ldflags="$tillac_rtems_ldflags -L$tillac_extra_libs_val/$2" + else + tillac_rtems_ldflags="$tillac_rtems_ldflags -L$tillac_extra_libs_val" + fi + done + fi +#export forged CC & friends so that they are used by sub-configures, too + export CC="$CC $tillac_rtems_gccspecs $tillac_rtems_cpu_cflags $tillac_rtems_cppflags" + export CXX="$CXX $tillac_rtems_gccspecs $tillac_rtems_cpu_cflags $tillac_rtems_cppflags" + export CCAS="$CCAS $tillac_rtems_gccspecs $tillac_rtems_cpu_asflags -DASM" + export CPP="$CPP $tillac_rtems_gccspecs $tillac_rtems_cppflags" +# export CFLAGS="$CFLAGS $tillac_rtems_cpu_cflags" +# export CXXFLAGS="$CXXFLAGS $tillac_rtems_cpu_cflags" +# export CCASFLAGS="$CCASFLAGS $tillac_rtems_cpu_asflags -DASM" +# export CPPFLAGS="$CPPFLAGS $tillac_rtems_cppflags" + export LDFLAGS="$LDFLAGS $tillac_rtems_ldflags" + else + AC_MSG_RESULT([yes]) + fi]dnl +) diff --git a/m4/rtems-options.m4 b/m4/rtems-options.m4 new file mode 100644 index 0000000..4e2d484 --- /dev/null +++ b/m4/rtems-options.m4 @@ -0,0 +1,41 @@ +# Declare --enable-rtemsbsp --with-rtems-top options +# +# TILLAC_RTEMS_OPTIONS +AC_DEFUN([TILLAC_RTEMS_OPTIONS], + [AC_ARG_ENABLE(rtemsbsp, + AC_HELP_STRING([--enable-rtemsbsp="bsp1 bsp2 ..."], + [BSPs to include in build (ignore bsps not found in RTEMS installation)]dnl + ) + ) + AC_ARG_WITH(rtems-top, + AC_HELP_STRING([--with-rtems-top=], + [point to RTEMS installation]dnl + ) + ) + AC_ARG_WITH(extra-incdirs, + AC_HELP_STRING([--with-extra-incdirs=], + [point to directories with additional headers]dnl + ) + ) + AC_ARG_WITH(extra-libdirs, + AC_HELP_STRING([--with-extra-libdirs=], + [point to directories with additional libraries]dnl + ) + ) + AC_ARG_WITH(hostbindir, + AC_HELP_STRING([--with-hostbindir=], + [default is /host//bin], + ), + [AC_SUBST([hostbindir],[$with_hostbindir])], + [AC_SUBST([hostbindir],['$(prefix)/host/$(build_alias)/bin'])] + ) + AC_ARG_ENABLE([std-rtems-installdirs], + AC_HELP_STRING([--enable-std-rtems-installdirs], + [install directly into + the RTEMS installation directories; by default a location *outside* + of the standard location is used. If you don't use this option you + can also fine-tune the installation using the usual --prefix, + --exec-prefix, --libdir, --includedir etc. options]dnl + ) + )]dnl +) diff --git a/m4/rtems-setup-recurse.m4 b/m4/rtems-setup-recurse.m4 new file mode 100644 index 0000000..e3f39ec --- /dev/null +++ b/m4/rtems-setup-recurse.m4 @@ -0,0 +1,224 @@ +# This macro assembles a list of RTEMS CPUs and if the list +# contains more than one member then it creates a build subdirectory +# for each CPU architecture, 'chdirs' into the subdirectory and recursively +# configures for the CPU. +# +# Several options affect the behavior of this macro: +# +# --with-rtems-top +# --host +# --enable-rtemsbsp +# +# 1) if --with-rtems-top is NOT given (NOT RTEMS) OR if +# --host=-rtems was given (RTEMS CPU defined by user) +# THEN the macro does nothing. +# +# 2) ELSE (--with-rtems-top given but --host is NOT *rtems* +# i) assemble a list of all '-rtems*' subdirectories +# under ${with_rtems_top} +# +# ii) if --enable-rtemsbsp was given then remove CPU +# architectures not being required by any of the listed +# BSPS. +# +# iii) for each remaining CPU architecture create a subdirectory, +# chdir there and recursively call 'configure' again with +# the original arguments but --host=-rtems appended. +# +# Note: this macro should not be used directly. It is expanded from +# TILLAC_RTEMS_SETUP +# +# TILLAC_RTEMS_CONFIG_CPUS_RECURSIVE +AC_DEFUN([TILLAC_RTEMS_CONFIG_CPUS_RECURSIVE], + [if test ! "${with_rtems_top+set}" = "set" || TILLAC_RTEMS_HOSTOS_IS_RTEMS ; then : ; else + # with_rtems_top is set but host_os is not *rtems*, i.e., + # we have to figure out a list of CPUs/arches that are installed. + AC_MSG_CHECKING([for all installed CPUs/architectures]) + _tillac_rtems_cpulist="`(cd $with_rtems_top; ls -d *-rtems* | tr '\n\r' ' ')`" + AC_MSG_RESULT([Found: $_tillac_rtems_cpulist]) + # if 'enable-rtemsbsp' was given then filter away + # architectures that match none of the BSPs + if test "${enable_rtemsbsp+set}" = "set" ; then + # convert space separated list into ORed (|) pattern + _tillac_rtems_bspfilt=`echo "$enable_rtemsbsp" | sed -e 's/[[ \t]]\+/|/g'` + AC_MSG_NOTICE([Filtering CPU/architecture list against bsps: $_tillac_rtems_bspfilt]) + _tillac_rtems_cpuall="$_tillac_rtems_cpulist" + _tillac_rtems_cpulist="" + AC_MSG_CHECKING([CPU/architectures matching requested BSPs]) + for _tillac_rtems_cpucand in $_tillac_rtems_cpuall ; do + # look for directories which have a 'Makefile.inc' + for _tillac_rtems_bspcand in `(cd $with_rtems_top/$_tillac_rtems_cpucand ; ls */Makefile.inc | tr '\n\r' ' ')` ; do + AC_MSG_NOTICE([testing $_tillac_rtems_bspcand]) + # reduce to bsp name + _tillac_rtems_bspcand=`dirname $_tillac_rtems_bspcand` + if eval "case `echo $_tillac_rtems_bspcand` in $_tillac_rtems_bspfilt) : ;; *) false ;; esac" ; then + # only add candidate to list of cpus if not already there + if test -z "$_tillac_rtems_cpulist" ; then + _tillac_rtems_cpulist="$_tillac_rtems_cpucand" + else + _tillac_rtems_cpufilt=`echo "$_tillac_rtems_cpulist" | sed -e 's/[[ \t]]\+/|/g'` + if eval "case `echo $_tillac_rtems_cpucand` in $_tillac_rtems_cpufilt) false ;; *) : ;; esac" ; then + _tillac_rtems_cpulist="$_tillac_rtems_cpulist $_tillac_rtems_cpucand" + fi + fi + fi + done + done + AC_MSG_RESULT([found: $_tillac_rtems_cpulist]) + fi + if test "$_tillac_rtems_cpulist"xx = "xx" ; then + AC_MSG_ERROR([No RTEMS architectures found]) + fi + # Create directory and configure + for _tillac_rtems_cpucand in $_tillac_rtems_cpulist ; do + if test -d $_tillac_rtems_cpucand || mkdir $_tillac_rtems_cpucand ; then : ; else + AC_MSG_ERROR([Unable to create subdirectory $_tillac_rtems_cpucand]) + fi + TILLAC_RTEMS_TRIM_CONFIG_DIR(_tillac_rtems_config_dir) + # SUB-CONFIGURE + AC_MSG_NOTICE([Running $_tillac_rtems_config_dir/[$]0 "$ac_configure_args --host=$_tillac_rtems_cpucand" in "'$_tillac_rtems_cpucand'" subdir]) + eval \( cd $_tillac_rtems_cpucand \; $SHELL $_tillac_rtems_config_dir/"[$]0" $ac_configure_args --host=$_tillac_rtems_cpucand \) + done + AC_MSG_NOTICE([Creating cpu/arch level makefile]) + AC_SUBST(the_subdirs,[$_tillac_rtems_cpulist]) + AC_SUBST(the_distsub,['$(firstword '"$_tillac_rtems_cpulist"')']) + _tillac_rtems_recursing=yes + false + fi]dnl +) + +# This macro +# +# - removes --enable-rtemsbsp options from the current commandline +# - for each BSP listed in '${enable_rtemsbsp}' +# i) creates a BSP subdirectory in the build tree +# ii) chdirs into the subdirectory +# iii) figures out a few BSP-specific build settings +# (cflags, gcc specs, ...) +# iv) configures for the BSP passing the properties determined +# under iii) along to 'configure' on the commandline. +# Also, --enable-rtemsbsp= is appended to the +# configure commandline. +# +# Note: this macro should not be used directly. It is expanded from +# TILLAC_RTEMS_SETUP +# +# TILLAC_RTEMS_CONFIG_BSPS_RECURSIVE +AC_DEFUN([TILLAC_RTEMS_CONFIG_BSPS_RECURSIVE], + [if test ! "${RTEMS_TILL_MAKEVARS_SET}" = "YES"; then + # strip all --enable-rtemsbsp options from original + # commandline + AC_MSG_NOTICE([Stripping --enable-rtemsbsp option(s) from commandline]) + _tillac_rtems_config_args="" + eval for _tillac_rtems_arg in $ac_configure_args \; do case \$_tillac_rtems_arg in --enable-rtemsbsp\* \) \;\; \*\) _tillac_rtems_config_args=\"\$_tillac_rtems_config_args \'\$_tillac_rtems_arg\'\" \;\; esac done + AC_MSG_NOTICE([Commandline now: $_tillac_rtems_config_args]) + + AC_MSG_NOTICE([Creating BSP subdirectories and sub-configuring]) + TILLAC_RTEMS_SAVE_MAKEVARS + for _tillac_rtems_bsp in $enable_rtemsbsp ; do + if test ! -d $_tillac_rtems_bsp ; then + AC_MSG_CHECKING([Creating $_tillac_rtems_bsp]) + if mkdir $_tillac_rtems_bsp ; then + AC_MSG_RESULT([OK]) + else + AC_MSG_ERROR([Failed]) + fi + fi + TILLAC_RTEMS_TRIM_CONFIG_DIR(_tillac_rtems_config_dir) + TILLAC_RTEMS_RESET_MAKEVARS + TILLAC_RTEMS_MAKEVARS(${host_cpu}-${host_os},$_tillac_rtems_bsp) + tillac_rtems_cppflags="$tillac_rtems_cppflags -I$with_rtems_top/${host_cpu}-${host_os}/$_tillac_rtems_bsp/lib/include" + TILLAC_RTEMS_EXPORT_MAKEVARS(${host_cpu}-${host_os},$_tillac_rtems_bsp) + AC_MSG_NOTICE([Running $_tillac_rtems_config_dir/[$]0 $_tillac_rtems_config_args --enable-rtemsbsp=$_tillac_rtems_bsp in $_tillac_rtems_bsp subdir]) + eval \( cd $_tillac_rtems_bsp \; $SHELL $_tillac_rtems_config_dir/"[$]0" $_tillac_rtems_config_args --enable-rtemsbsp=$_tillac_rtems_bsp \) + done + TILLAC_RTEMS_RESET_MAKEVARS + AC_MSG_NOTICE([Creating toplevel makefile]) + AC_SUBST(the_subdirs,[$enable_rtemsbsp]) + AC_SUBST(the_distsub,['$(firstword '"$enable_rtemsbsp"')']) + fi]dnl +) + +# Grand Master Macro for RTEMS configuration. +# +# This sets up most things for a RTEMS configuration +# for multiple CPU-arches and BSPs. +# +# A package may add the optional (literal) argument +# 'domultilib'. In this case, multilib support is enabled +# and the user may configure with '--enable-multilib'. +# Note that the package must properly support multilibs! +# +# If the host system is not RTEMS (no with-rtems-top given) +# then this macro does *nothing*. +# +# TILLAC_RTEMS_SETUP([domultilib]) +AC_DEFUN([TILLAC_RTEMS_SETUP], + [AC_REQUIRE([TILLAC_RTEMS_OPTIONS])dnl + AM_CONDITIONAL(OS_IS_RTEMS,[TILLAC_RTEMS_OS_IS_RTEMS]) + if TILLAC_RTEMS_CONFIG_CPUS_RECURSIVE ; then + m4_if($1,domultilib, + [TILLAC_RTEMS_MULTILIB([Makefile],[.])], + [AC_REQUIRE([TILLAC_RTEMS_OPTIONS])dnl + if test "${enable_multilib}" = "yes" ; then + AC_MSG_ERROR(["multilibs not supported, sorry"]) + fi]dnl + ) + if TILLAC_RTEMS_OS_IS_RTEMS ; then + TILLAC_RTEMS_CHECK_TOP + AC_ARG_VAR([RTEMS_TILL_MAKEVARS_SET],[Internal use; do NOT set in environment nor on commandline]) + AC_ARG_VAR([DOWNEXT],[extension of downloadable binary (if applicable)]) + AC_ARG_VAR([APPEXEEXT], [extension of linked binary]) + AC_ARG_VAR([RTEMS_BSP_FAMILY],[Internal use; do NOT set in environment nor on commandline]) + AC_ARG_VAR([RTEMS_BSP_INSTTOP],[Internal use; do NOT set in environment nor on commandline]) + if test "$1" = "domultilib" && test "$enable_multilib" = "yes" ; then + if test "${enable_rtemsbsp+set}" = "set" ; then + AC_MSG_ERROR([Cannot --enable-rtemsbsp AND --enable-multilib; build either multilibs or for particular BSP(s)]) + fi + TILLAC_RTEMS_EXPORT_MAKEVARS(${host_cpu}-${host_os},) + else + TILLAC_RTEMS_CHECK_BSPS + fi + if test ! "${RTEMS_TILL_MAKEVARS_SET}" = "YES"; then + TILLAC_RTEMS_CONFIG_BSPS_RECURSIVE(makefile) + _tillac_rtems_recursing=yes + else + TILLAC_RTEMS_FIXUP_PREFIXES +dnl set those in the configure script so that 'configure' uses these settings when trying to compile stuff +dnl AC_SUBST(rtems_gccspecs, [$tillac_rtems_gccspecs]) +dnl AC_SUBST(rtems_cpu_cflags, [$tillac_rtems_cpu_cflags]) +dnl AC_SUBST(rtems_cpu_asflags,["$tillac_rtems_cpu_asflags -DASM"]) +dnl AC_SUBST(rtems_cppflags, [$tillac_rtems_cppflags]) + AC_SUBST(rtems_bsp, [$enable_rtemsbsp]) + AC_MSG_NOTICE([Setting DOWNEXT to .ralf]) + DOWNEXT=.ralf + AC_MSG_NOTICE([Setting APPEXEEXT to .exe]) + APPEXEEXT=.exe + TILLAC_RTEMS_VERSTEST + fi + fi + fi + if test "${_tillac_rtems_recursing}" = "yes" ; then + AC_CONFIG_FILES([makefile:makefile.top.in]) + AC_OUTPUT + exit 0 + false + fi]dnl +) + +dnl m4_syscmd is executed when aclocal is run +m4_syscmd([cat - > makefile.top.am <<'EOF_' +AUTOMAKE_OPTIONS=foreign +SUBDIRS=@the_subdirs@ +# When making a distribution we only want to +# recurse into (any) one single BSP subdir. +DIST_SUBDIRS=@the_distsub@ + +# The dist-hook then removes this extra +# directory level again. +dist-hook: + if test "$(PACKAGE_VERSION)" = "untagged" ; then echo "Need tagged version to cut distribution"; exit 1; fi + cp -frl $(distdir)/$(DIST_SUBDIRS)/* $(distdir) + rm -fr $(distdir)/$(DIST_SUBDIRS) +EOF_ +]) diff --git a/m4/rtems-tools.m4 b/m4/rtems-tools.m4 new file mode 100644 index 0000000..aa4a411 --- /dev/null +++ b/m4/rtems-tools.m4 @@ -0,0 +1,15 @@ +# Check for critical programs we need for building +AC_DEFUN([TILLAC_RTEMS_CHECK_TOOLS], + [AC_PROG_CC + AM_PROG_AS + AC_PROG_CXX + AC_SUBST([GCC]) + AC_PROG_CPP + AC_CHECK_PROGS([HOSTCC], gcc cc) + RTEMS_CHECK_TOOL([AR],ar) + RTEMS_CHECK_TOOL([LD],ld) + RTEMS_CHECK_TOOL([OBJCOPY],objcopy) + RTEMS_CHECK_TOOL([RANLIB],ranlib) + AC_PROG_INSTALL + AC_CHECK_PROG([INSTALL_IF_CHANGE],[install-if-change],[install-if-change],[${INSTALL}])]dnl +) diff --git a/m4/rtems-trim-builddir.m4 b/m4/rtems-trim-builddir.m4 new file mode 100644 index 0000000..ffd94d6 --- /dev/null +++ b/m4/rtems-trim-builddir.m4 @@ -0,0 +1,26 @@ +# Takes one argument +# +# TILLAC_RTEMS_TRIM_CONFIG_DIR(dirvar) +# +# If 'srcdir' is a absolute path (a string +# starting with '/' then set 'dirvar' to the +# empty string; otherwise (srcdir is a relative path) +# set 'dirvar' to '../'. +# +# This macro can be used to find 'srcdir' should +# configure decide to step into a subdirectory inside +# a build tree. +# +AC_DEFUN([TILLAC_RTEMS_TRIM_CONFIG_DIR], + [AC_MSG_NOTICE([Trimming source directory]) + # leave absolute path alone, relative path needs + # to step one level up + case $srcdir in + /* ) + $1= + ;; + *) + $1=../ + ;; + esac]dnl +) diff --git a/m4/rtems-verscheck.m4 b/m4/rtems-verscheck.m4 new file mode 100644 index 0000000..c678f9c --- /dev/null +++ b/m4/rtems-verscheck.m4 @@ -0,0 +1,36 @@ +# Emit two macros +# +# 'RTEMS_VERSION_LATER_THAN(major,minor,revision)' +# +# and +# +# 'RTEMS_VERSION_ATLEAST(major,minor,revision)' +# +# in config.h so applications can test for a particular +# RTEMS version and conditionally deal with API changes +# and other stuff. +# Note that sometimes (mostly during x.y.99 pre-release +# phases) such changes happen w/o an associated change in +# revision number ;-(. +# +#dnl TILLAC_RTEMS_VERSTEST +AC_DEFUN([TILLAC_RTEMS_VERSTEST], + [AH_VERBATIM([RTEMS_VERSION_TEST], + [ +#ifndef RTEMS_VERSION_LATER_THAN +#define RTEMS_VERSION_LATER_THAN(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ > (re)) \ + ) +#endif +#ifndef RTEMS_VERSION_ATLEAST +#define RTEMS_VERSION_ATLEAST(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ >= (re)) \ + ) +#endif + ]dnl + )]dnl +) diff --git a/makefile.top.am b/makefile.top.am new file mode 100644 index 0000000..fd4f9de --- /dev/null +++ b/makefile.top.am @@ -0,0 +1,12 @@ +AUTOMAKE_OPTIONS=foreign +SUBDIRS=@the_subdirs@ +# When making a distribution we only want to +# recurse into (any) one single BSP subdir. +DIST_SUBDIRS=@the_distsub@ + +# The dist-hook then removes this extra +# directory level again. +dist-hook: + if test "$(PACKAGE_VERSION)" = "untagged" ; then echo "Need tagged version to cut distribution"; exit 1; fi + cp -frl $(distdir)/$(DIST_SUBDIRS)/* $(distdir) + rm -fr $(distdir)/$(DIST_SUBDIRS) diff --git a/makefile.top.in b/makefile.top.in new file mode 100644 index 0000000..95cf408 --- /dev/null +++ b/makefile.top.in @@ -0,0 +1,670 @@ +# makefile.top.in generated by automake 1.10.2 from makefile.top.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = . +DIST_COMMON = $(am__configure_deps) $(srcdir)/config.h.in \ + $(srcdir)/makefile.top.am $(srcdir)/makefile.top.in \ + $(top_srcdir)/configure INSTALL config.guess config.sub \ + depcomp install-sh missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/config-if-present.m4 \ + $(top_srcdir)/m4/rtems-bsplist.m4 \ + $(top_srcdir)/m4/rtems-check-libargs.m4 \ + $(top_srcdir)/m4/rtems-checkprog.m4 \ + $(top_srcdir)/m4/rtems-checktool.m4 \ + $(top_srcdir)/m4/rtems-checktop.m4 \ + $(top_srcdir)/m4/rtems-fixup-prefix.m4 \ + $(top_srcdir)/m4/rtems-isrtems.m4 \ + $(top_srcdir)/m4/rtems-makevars.m4 \ + $(top_srcdir)/m4/rtems-options.m4 \ + $(top_srcdir)/m4/rtems-setup-recurse.m4 \ + $(top_srcdir)/m4/rtems-tools.m4 \ + $(top_srcdir)/m4/rtems-trim-builddir.m4 \ + $(top_srcdir)/m4/rtems-verscheck.m4 \ + $(top_srcdir)/./m4/cvstag.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +APPEXEEXT = @APPEXEEXT@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DOWNEXT = @DOWNEXT@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +ENBL_82542_SUPPORT = @ENBL_82542_SUPPORT@ +ENBL_ICH8LAN_SUPPORT = @ENBL_ICH8LAN_SUPPORT@ +EXEEXT = @EXEEXT@ +GCC = @GCC@ +HOSTCC = @HOSTCC@ +HOSTPROGRAM = @HOSTPROGRAM@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_IF_CHANGE = @INSTALL_IF_CHANGE@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NFSLIB = @NFSLIB@ +OBJCOPY = @OBJCOPY@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +RTEMS_BSP_FAMILY = @RTEMS_BSP_FAMILY@ +RTEMS_BSP_INSTTOP = @RTEMS_BSP_INSTTOP@ +RTEMS_TILL_MAKEVARS_SET = @RTEMS_TILL_MAKEVARS_SET@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +all_subdirs = @all_subdirs@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +enable_subdirs = @enable_subdirs@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +hostbindir = @hostbindir@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +installexechostbinprogs = @installexechostbinprogs@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rtems_bsp = @rtems_bsp@ +rtems_cpu = @rtems_cpu@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +system_subdir = @system_subdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +the_distsub = @the_distsub@ +the_subdirs = @the_subdirs@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign +SUBDIRS = @the_subdirs@ +# When making a distribution we only want to +# recurse into (any) one single BSP subdir. +DIST_SUBDIRS = @the_distsub@ +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +am--refresh: + @: +$(srcdir)/makefile.top.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/makefile.top.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \ + cd $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign makefile:makefile.top.in'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign makefile:makefile.top.in +.PRECIOUS: makefile +makefile: $(srcdir)/makefile.top.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d $(distdir) || mkdir $(distdir) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-lzma: distdir + tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lzma*) \ + unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: makefile config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f makefile +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-exec-am: + +install-html: install-html-recursive + +install-info: install-info-recursive + +install-man: + +install-pdf: install-pdf-recursive + +install-ps: install-ps-recursive + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \ + install-strip + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am am--refresh check check-am clean clean-generic \ + ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \ + dist-hook dist-lzma dist-shar dist-tarZ dist-zip distcheck \ + distclean distclean-generic distclean-hdr distclean-tags \ + distcleancheck distdir distuninstallcheck dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ + pdf-am ps ps-am tags tags-recursive uninstall uninstall-am + + +# The dist-hook then removes this extra +# directory level again. +dist-hook: + if test "$(PACKAGE_VERSION)" = "untagged" ; then echo "Need tagged version to cut distribution"; exit 1; fi + cp -frl $(distdir)/$(DIST_SUBDIRS)/* $(distdir) + rm -fr $(distdir)/$(DIST_SUBDIRS) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/rtems-pre.am b/rtems-pre.am new file mode 100644 index 0000000..07d85fa --- /dev/null +++ b/rtems-pre.am @@ -0,0 +1,5 @@ +AM_CPPFLAGS = -Wall -D__BSD_VISIBLE +AM_CFLAGS = -Wall +AM_CXXFLAGS = -Wall +AM_CCASFLAGS = +AM_LDFLAGS = diff --git a/rtems.am b/rtems.am new file mode 100644 index 0000000..8f7bca0 --- /dev/null +++ b/rtems.am @@ -0,0 +1,41 @@ +if OS_IS_RTEMS +RTEMS_BSP = @rtems_bsp@ +# CC += @rtems_gccspecs@ +# AM_CFLAGS += @rtems_cpu_cflags@ +# CXX += @rtems_gccspecs@ +# AM_CXXFLAGS += @rtems_cpu_cflags@ +# CCAS += @rtems_gccspecs@ +# AM_CCASFLAGS += @rtems_cpu_asflags@ +# CPP += @rtems_gccspecs@ +# AM_CPPFLAGS += @rtems_cppflags@ +AM_CPPFLAGS += -I$(top_builddir)/data/include +AM_LDFLAGS += -L$(top_builddir)/data/lib +endif + + +OBJLINK=$(CCLD) -nostdlib -Wl,-r -o $@ +OBJEXEEXT=.obj + +# Where tools that run on the host go +#FIXME: where to put these? +exechostbindir=$(hostbindir) + +include_bspdir=$(includedir)/bsp +include_sysdir=$(includedir)/sys + +preinstall:INSTALL=$(INSTALL_IF_CHANGE) +preinstall:exec_prefix=$(prefix) +preinstall:includedir=$(prefix)/include +preinstall:prefix=$(abs_top_builddir)/data +preinstall:install-data install-exec + +# a rule to link native programs +# +$(HOSTPROG)$(HOSTEXEEXT): $($(HOSTPROG)_OBJECTS) $($(HOSTPROG)_DEPENDENCIES) + $(HOSTCC) -o $@ $($(HOSTPROG)_OBJECTS) $($(HOSTPROG)_LDADD) + +# another hack; if we just enter the install-exechostbinPROGRAMS: +# target on the left then automake will not emit a rule for installation +# :-(. +# As a work-around we let 'configure' fill this in... +@installexechostbinprogs@:EXEEXT=$(HOSTEXEEXT) diff --git a/ssrlApps.components.in b/ssrlApps.components.in new file mode 100644 index 0000000..65e17f9 --- /dev/null +++ b/ssrlApps.components.in @@ -0,0 +1 @@ +ssrlapps_components='@enable_subdirs@' -- cgit v1.2.3