From b7a6d23a0d8d855fe92d573658405e1eedc2356b Mon Sep 17 00:00:00 2001 From: Till Straumann Date: Thu, 3 Dec 2009 16:56:50 +0000 Subject: - importing 'beatnik' BSP from SLAC repository. --- c/src/lib/libbsp/powerpc/beatnik/.cvsignore | 8 + c/src/lib/libbsp/powerpc/beatnik/ChangeLog | 197 + c/src/lib/libbsp/powerpc/beatnik/LICENSE | 52 + c/src/lib/libbsp/powerpc/beatnik/Makefile.am | 245 + c/src/lib/libbsp/powerpc/beatnik/README | 178 + c/src/lib/libbsp/powerpc/beatnik/bsp_specs | 13 + c/src/lib/libbsp/powerpc/beatnik/configure.ac | 40 + c/src/lib/libbsp/powerpc/beatnik/flash/flashcfg.c | 182 + .../lib/libbsp/powerpc/beatnik/include/.cvsignore | 4 + c/src/lib/libbsp/powerpc/beatnik/include/bsp.h | 273 + .../lib/libbsp/powerpc/beatnik/irq/discovery_pic.c | 995 +++ c/src/lib/libbsp/powerpc/beatnik/irq/irq.h | 133 + c/src/lib/libbsp/powerpc/beatnik/irq/irq_init.c | 112 + .../lib/libbsp/powerpc/beatnik/irq/irq_test_app.c | 167 + .../libbsp/powerpc/beatnik/make/custom/beatnik.cfg | 37 + .../lib/libbsp/powerpc/beatnik/marvell/discovery.c | 151 + c/src/lib/libbsp/powerpc/beatnik/marvell/gt.c | 1010 +++ .../lib/libbsp/powerpc/beatnik/marvell/gt_timer.c | 412 ++ .../lib/libbsp/powerpc/beatnik/marvell/gt_timer.h | 134 + c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c.c | 447 ++ .../libbsp/powerpc/beatnik/marvell/gti2c_busdrv.h | 62 + .../lib/libbsp/powerpc/beatnik/marvell/gti2creg.h | 83 + .../lib/libbsp/powerpc/beatnik/marvell/gtintrreg.h | 257 + .../lib/libbsp/powerpc/beatnik/marvell/gtpcireg.h | 964 +++ c/src/lib/libbsp/powerpc/beatnik/marvell/gtreg.h | 854 +++ .../libbsp/powerpc/beatnik/network/if_em/LICENSE | 31 + .../libbsp/powerpc/beatnik/network/if_em/Makefile | 92 + .../libbsp/powerpc/beatnik/network/if_em/README | 332 + .../libbsp/powerpc/beatnik/network/if_em/if_em.c | 3846 ++++++++++++ .../libbsp/powerpc/beatnik/network/if_em/if_em.h | 493 ++ .../powerpc/beatnik/network/if_em/if_em_hw.c | 6635 ++++++++++++++++++++ .../powerpc/beatnik/network/if_em/if_em_hw.h | 2678 ++++++++ .../powerpc/beatnik/network/if_em/if_em_osdep.h | 146 + .../powerpc/beatnik/network/if_em/if_em_pub.h | 23 + .../powerpc/beatnik/network/if_em/if_em_rtems.c | 106 + .../beatnik/network/if_em/rtemscompat_defs.h | 110 + .../libbsp/powerpc/beatnik/network/if_gfe/Makefile | 90 + .../powerpc/beatnik/network/if_gfe/gtethreg.h | 854 +++ .../libbsp/powerpc/beatnik/network/if_gfe/gtvar.h | 170 + .../libbsp/powerpc/beatnik/network/if_gfe/if_gfe.c | 2641 ++++++++ .../powerpc/beatnik/network/if_gfe/if_gfe_pub.h | 31 + .../powerpc/beatnik/network/if_gfe/if_gfe_rtems.c | 130 + .../powerpc/beatnik/network/if_gfe/if_gfevar.h | 225 + .../beatnik/network/if_gfe/rtemscompat_defs.h | 122 + .../powerpc/beatnik/network/if_mve/if_mve_pub.h | 423 ++ .../powerpc/beatnik/network/if_mve/mv643xx_eth.c | 3297 ++++++++++ .../beatnik/network/if_mve/mve_smallbuf_tst.c | 144 + .../powerpc/beatnik/network/if_mve/testing.c | 323 + .../libbsp/powerpc/beatnik/network/porting/LICENSE | 51 + .../beatnik/network/porting/Makefile.template | 84 + .../libbsp/powerpc/beatnik/network/porting/README | 106 + .../beatnik/network/porting/if_xxx.modini.c | 34 + .../powerpc/beatnik/network/porting/if_xxx_rtems.c | 504 ++ .../powerpc/beatnik/network/porting/pcireg.h | 405 ++ .../powerpc/beatnik/network/porting/rtemscompat.h | 459 ++ .../powerpc/beatnik/network/porting/rtemscompat1.h | 219 + .../network/porting/rtemscompat_defs.h.template | 97 + .../powerpc/beatnik/network/support/bsp_attach.c | 468 ++ .../beatnik/network/support/bsp_bsdnet_attach.h | 80 + .../network/support/early_enet_link_status.h | 31 + .../beatnik/network/support/early_link_status.c | 41 + c/src/lib/libbsp/powerpc/beatnik/pci/gt_pci_init.c | 274 + .../lib/libbsp/powerpc/beatnik/pci/motload_fixup.c | 182 + .../lib/libbsp/powerpc/beatnik/pci/pci_io_remap.c | 207 + .../lib/libbsp/powerpc/beatnik/pci/pci_io_remap.h | 66 + c/src/lib/libbsp/powerpc/beatnik/preinstall.am | 188 + .../lib/libbsp/powerpc/beatnik/startup/bspclean.c | 11 + .../lib/libbsp/powerpc/beatnik/startup/bspstart.c | 397 ++ .../lib/libbsp/powerpc/beatnik/startup/i2c_init.c | 132 + c/src/lib/libbsp/powerpc/beatnik/startup/linkcmds | 261 + c/src/lib/libbsp/powerpc/beatnik/startup/reboot.c | 16 + c/src/lib/libbsp/powerpc/beatnik/tod/todcfg.c | 36 + c/src/lib/libbsp/powerpc/beatnik/vme/VMEConfig.h | 112 + c/src/lib/libbsp/powerpc/beatnik/vme/vme_dma.c | 217 + c/src/lib/libbsp/powerpc/beatnik/vme/vmeconfig.c | 305 + 75 files changed, 34935 insertions(+) create mode 100644 c/src/lib/libbsp/powerpc/beatnik/.cvsignore create mode 100644 c/src/lib/libbsp/powerpc/beatnik/ChangeLog create mode 100644 c/src/lib/libbsp/powerpc/beatnik/LICENSE create mode 100644 c/src/lib/libbsp/powerpc/beatnik/Makefile.am create mode 100644 c/src/lib/libbsp/powerpc/beatnik/README create mode 100644 c/src/lib/libbsp/powerpc/beatnik/bsp_specs create mode 100644 c/src/lib/libbsp/powerpc/beatnik/configure.ac create mode 100644 c/src/lib/libbsp/powerpc/beatnik/flash/flashcfg.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/include/.cvsignore create mode 100644 c/src/lib/libbsp/powerpc/beatnik/include/bsp.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/irq/irq.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/irq/irq_init.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/irq/irq_test_app.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/make/custom/beatnik.cfg create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/discovery.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gt.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c_busdrv.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gti2creg.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gtintrreg.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gtpcireg.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/marvell/gtreg.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/LICENSE create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/Makefile create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/README create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_osdep.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_pub.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_rtems.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_em/rtemscompat_defs.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/Makefile create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtethreg.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtvar.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_pub.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_rtems.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfevar.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/rtemscompat_defs.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_mve/if_mve_pub.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mv643xx_eth.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mve_smallbuf_tst.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/if_mve/testing.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/LICENSE create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/Makefile.template create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/README create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx.modini.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx_rtems.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/pcireg.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat1.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat_defs.h.template create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_attach.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_bsdnet_attach.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/support/early_enet_link_status.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/network/support/early_link_status.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/pci/gt_pci_init.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/pci/motload_fixup.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/preinstall.am create mode 100644 c/src/lib/libbsp/powerpc/beatnik/startup/bspclean.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/startup/bspstart.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/startup/i2c_init.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/startup/linkcmds create mode 100644 c/src/lib/libbsp/powerpc/beatnik/startup/reboot.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/tod/todcfg.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/vme/VMEConfig.h create mode 100644 c/src/lib/libbsp/powerpc/beatnik/vme/vme_dma.c create mode 100644 c/src/lib/libbsp/powerpc/beatnik/vme/vmeconfig.c (limited to 'c') diff --git a/c/src/lib/libbsp/powerpc/beatnik/.cvsignore b/c/src/lib/libbsp/powerpc/beatnik/.cvsignore new file mode 100644 index 0000000000..baba64eafa --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/.cvsignore @@ -0,0 +1,8 @@ +aclocal.m4 +autom4te*.cache +config.cache +config.log +config.status +configure +Makefile +Makefile.in diff --git a/c/src/lib/libbsp/powerpc/beatnik/ChangeLog b/c/src/lib/libbsp/powerpc/beatnik/ChangeLog new file mode 100644 index 0000000000..f102855023 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/ChangeLog @@ -0,0 +1,197 @@ +2009-11-06 Till Straumann + + * Makefile.am, irq/discovery_pic.c, irq/irq_init.c: + dump raw_exceptions.rel, raw_exception.h; these have + disappeared. must now be included + instead. + + * Makefile.am: Add 'altivec.rel'. + + * make/custom/beatnik.cfg: Use -mcpu=7400; this enables + AltiVec! + +2009-11-06 Till Straumann + + * beatnik.cfg, make/custom/beatnik.cfg: moved beatnik.cfg + to new make/custom subdir. + +2009-10-20 Till Straumann + + * startup/bspstart.c: leave 'work-space start' and initial + stack alone. These are now handled by the shared framework + and linker script etc. Locate interrupt stack after __rtems_end + and obtain its size from the configuration. + +2009-10-20 Till Straumann + + * network/if_mve/mv643xx_eth.c: made mutex a binary semphore; + simple binary semaphore doesn't support priority inheritance. + This was silently ignored under previous releases but is an + error under 4.10. + +2009-10-20 Till Straumann + + * Makefile.am, bsp_specs, preinstall.am, flash/flashcfg.c, + include/bspopts.h.in, irq/discovery_pic.c, irq/irq_init.c, + marvell/gt_timer.c, marvell/gt_timer.h, marvel/gti2c.c, + network/if_gfe/if_gfe.c, network/if_gfe/if_gfe_rtems.c, + network/if_mve/mv643xx_eth.c, network/support/bsp_attach.c, + pci/gt_pci_init.c, pci/motload_fixup.c, startup/bspstart.c, + startup/i2c_init.c: + Ported to rtems HEAD (to become rtems-4.10). This consisted + mainly of fixing compiler warnings (mostly: adding prototypes + to function declarations and moving extern declarations to + global scope). + + A pecularity: if_gfe.c had to remove 'queue.h' inclusion. + we have two versions of queue.h: one in newlib another one in + rtems - don't know how this is supposed to work... + +2009-10-17 Till Straumann + + * Makefile.am, network/if_mve_mv643xx_eth.c, + network/if_mve_pub.h: Enhanced low-level API allowing + the user to selectively enable/disable/acknowledge + interrupts and to install their own ISR (rather than having + the driver posting an event to a single task). + +2009-10-03 Till Straumann + * network/if_mve/mv643xx_eth.c: + BUGFIX: mbuf leak; consume_rx_mbuf() must release mbuf + if 'len'<=0. + + BUGFIX: Must initialize 'media-word' argument before + calling BSP_mve_media_ioctl() (defines PHY instance). + +2009-06-05 Till Straumann + * network/if_mve/mv643xx_eth.c, network/if_mve/if_mve_pub.h, + Makefile.am: Added MC address reference count and + BSP_mve_mcast_filter_accept_del() to remove a single + entry from the filter. + +2009-06-05 Till Straumann + * network/if_mve/mv643xx_eth.c, network/if_mve/if_mve_pub.h, + Makefile.am: first stab at adding multicast support. + +2009-06-05 Till Straumann + * network/if_gfe/if_gfe.c: + o propagate PROMISC flag to hardware (SIOCSIFFLAGS) + o handle case where IFF_PROMISC is set (and wasn't before) + in gfe_hash_fill() routine. + +2009-06-02 Till Straumann + * network/if_gfe/if_gfe.c: activated and fixed multicast + support. + +2009-06-01 Till Straumann + * network/if_em/if_em.c: activated multicast support. + +2008-10-30 Till Straumann + + MERGED from rtems-4-7-branch: + * Makefile.am, network/if_mve/mv643xx.c, network/if_mve/if_mve_pub.h: + o Exported new low-level driver entry points: + - BSP_mve_ack_link_chg() so that changes in PHY link status can be + propagated to the serial port when handling link-change interrupts. + - BSP_mve_dump_stats() for printing statistics. + o FIXED reading of statistics counters. + o Count interrupts (statistics) in ISR rather than network daemon + (which is only used by the BSD driver). + +2008-10-04 Till Straumann + + * beatnik.cfg: updated to 4.9; removed make-exe + make-cxx-exe commands. Replaced CPU cflags to use + -mpowerpc -D__ppc_generic. + +2008-10-04 Till Straumann + + * startup/linkcmds: increased size of CODE memory + area to 32M. + +2008-05-10 Till Straumann + + * pci/gt_pci_init.c, pci/pci_io_remap.c: fixed 32-bit + types. RTEMS' pci_config access functions now use uint32_t, + earlier versions used unsigned. Both are incompatible, + unfortunately (gcc regards unsigned and unsigned long different + beasts leading to warnings and alias-issues :-() + +2008-05-10 Till Straumann + + * network/porting/rtemscompat1.h, network/porting/rtemscompat.h, + network/porting/if_xxx_rtems.c, network/if_gfe/if_gfe_rtems.c: + Fixed 32-bit types (pci config access, byteorder macros differ + depending on RTEMS version :-(). We now check for version and + use appropriate types (unsigned vs. uint32_t). + Silenced more warnings (ifndef DEBUG_MODULAR the METHODSPTR + is always non-zero; hence I ifdef'ed the affected code snippet). + +2008-03-20 Till Straumann + + * include/bsp.h, startup/bspstart.c: confdefs.h now wants + us to use BSP_INTERRUPT_STACK_SIZE instead of + CONFIGURE_INTERRUPT_STACK_MEMORY. + +2008-03-19 Till Straumann + + * irq/discovery_pic.c: must spare GPP7_0 etc. summary + interrupts in BSP_enable_irq_at_pic() etc. + New 'new-exceptions/bspsupport' code scans all IRQS + and enables or disables depending on the initial config + having a handler connected. This initial disable operation + switched-off the summaries and I had no GPP interrupts... + +2008-01-04 Till Straumann + + * startup/bspstart.c: changed Kate's copyright note + again as requested by her email 1/04/2008. + +2008-01-04 Till Straumann + + * startup/bspstart.c: changed Kate's copyright note + as requested by her email 8/16/2007. + +2007-12-11 Till Straumann + + * irq/discovery_pic.c: don't print warnings if an + invalid irq number is passed to BSP_disable_irq_at_pic(), + BSP_enable_irq_at_pic(). irq_supp.h says we must + silently ignore. + +2007-12-11 Till Straumann + + * Makefile.am: use new irq_bspsupport.rel which was + split out of exc_bspsupport.rel to provide finer-grained + control over what BSPs want to use. + +2007-12-10 Till Straumann + + * Makefile.am, startup/vpd.c, startup/vpd.h: + moved VPD support to ../shared/motorola. + +2007-12-08 Till Straumann + + * Makefile.am: merged shared/vme/vme_universe.c and + shared/vme/vme_universe_dma.c into one file. + Use vme_universe.c, vmeconfig.c from shared area. + +2007-11-30 Till Straumann + + * startup/bspstart: removed _Cpu_table.exceptions_in_RAM. + +2007/11/27 (TS): + - Generalized flash support and moved to shared area (libchip would probably + more appropriate). +2007/10/22 (TS): + - DECREMENTER interrupt is now handled the same way external interrupts are. + It can also be assigned a priority and the handler is executed in priority + order, i.e., it can be preempted by higher-priority interrupts and + is protected from being preempted by lower-priority irqs. +2007/10/08 (TS): + - ChangeLog added + - (Makefile.am) MUST NOT use -msoft-float because this also prevents CR7 + to be set/cleared when calling vararg routines (which may then save/restore + FP args on the stack or do other bad things) :-( + Still don't know how to deal with implicit usage of the FPU by GCC + (problem in ISRs and integer-only tasks). diff --git a/c/src/lib/libbsp/powerpc/beatnik/LICENSE b/c/src/lib/libbsp/powerpc/beatnik/LICENSE new file mode 100644 index 0000000000..2ac95733bd --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/LICENSE @@ -0,0 +1,52 @@ +/* NOTE: The terms described in this LICENSE file apply only to the + * files created by the author (see below). Consult individual + * file headers for more details. Some files were ported from + * netbsd and/or freebsd and are covered by the respective + * file header copyright notices. E.g., the if_em driver is + * covered by it's own network/if_em/LICENSE. + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/Makefile.am b/c/src/lib/libbsp/powerpc/beatnik/Makefile.am new file mode 100644 index 0000000000..5433e2fca5 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/Makefile.am @@ -0,0 +1,245 @@ +## +## $Id$ +## + +ACLOCAL_AMFLAGS = -I ../../../../aclocal + +SUBDIRS = . +CLEANFILES = +DISTCLEANFILES = +noinst_PROGRAMS = +EXTRA_DIST = + + +include $(top_srcdir)/../../../../automake/compile.am +include $(top_srcdir)/../../bsp.am + +include_bspdir = $(includedir)/bsp + +dist_project_lib_DATA = bsp_specs +project_lib_DATA = + +#include +include_HEADERS = include/bsp.h + +nodist_include_HEADERS = include/bspopts.h +nodist_include_bsp_HEADERS = ../../shared/include/bootcard.h +DISTCLEANFILES += include/bspopts.h + +include_bsp_HEADERS = + +#start +EXTRA_DIST += ../../powerpc/shared/start/rtems_crti.S +rtems_crti.$(OBJEXT): ../../powerpc/shared/start/rtems_crti.S + $(CPPASCOMPILE) -DASM -o $@ -c $< +project_lib_DATA += rtems_crti.$(OBJEXT) + + +EXTRA_DIST += ../../powerpc/shared/start/preload.S +preload.$(OBJEXT): ../../powerpc/shared/start/preload.S + $(CPPASCOMPILE) -DASM -o $@ -c $< + +EXTRA_DIST += ../../powerpc/shared/start/vectors_entry.S +vectors_entry.$(OBJEXT): ../../powerpc/shared/start/vectors_entry.S + $(CPPASCOMPILE) -DASM -o $@ -c $< + +EXTRA_DIST += ../../powerpc/shared/start/start.S +start.$(OBJEXT): ../../powerpc/shared/start/start.S + $(CPPASCOMPILE) -DASM -o $@ -c $< + +motld_start.$(OBJEXT): preload.$(OBJEXT) vectors_entry.$(OBJEXT) start.$(OBJEXT) + $(LD) -o $@ -r $^ + +project_lib_DATA += motld_start.$(OBJEXT) + + +#startup + +dist_project_lib_DATA += ../shared/startup/linkcmds + +build_date.c:: + echo 'const char *BSP_build_date="'`date`'";' > $@ + +noinst_LIBRARIES = libbsp.a +libbsp_a_SOURCES = + +libbsp_a_SOURCES += startup/bspstart.c \ + ../shared/motorola/vpd.c startup/reboot.c startup/i2c_init.c build_date.c \ + ../../powerpc/shared/startup/panic.c \ + ../../powerpc/shared/startup/bspgetworkarea.c \ + ../../powerpc/shared/startup/probeMemEnd.c \ + ../../powerpc/shared/startup/pretaskinghook.c \ + ../../powerpc/shared/startup/zerobss.c \ + ../../powerpc/shared/startup/pgtbl_setup.c \ + ../../powerpc/shared/startup/pgtbl_activate.c \ + ../../powerpc/shared/startup/sbrk.c ../../shared/bootcard.c \ + startup/bspclean.c ../../shared/bsplibc.c ../../shared/bsppost.c \ + ../../shared/bsppredriverhook.c \ + ../../shared/gnatinstallhandler.c + +include_bsp_HEADERS += ../shared/motorola/vpd.h + +#pclock +libbsp_a_SOURCES += ../../powerpc/shared/clock/p_clock.c + +#console +include_bsp_HEADERS += ../../powerpc/shared/console/consoleIo.h +include_bsp_HEADERS += ../../powerpc/shared/console/uart.h + +libbsp_a_SOURCES += \ + ../../powerpc/shared/console/uart.c \ + ../../powerpc/shared/console/console.c \ + ../../powerpc/shared/console/consoleIo.h \ + ../../powerpc/shared/console/uart.h + +#irq +include_bsp_HEADERS += irq/irq.h + +libbsp_a_SOURCES += irq/irq_init.c irq/discovery_pic.c + +#marvell +include_bsp_HEADERS += marvell/gtreg.h marvell/gtintrreg.h \ + marvell/gti2creg.h marvell/gti2c_busdrv.h marvell/gt_timer.h \ + marvell/gtpcireg.h + +libbsp_a_SOURCES += marvell/discovery.c marvell/gti2c.c marvell/gt_timer.c + +#flash +include_bsp_HEADERS += ../shared/flash/flashPgm.h +include_bsp_HEADERS += ../shared/flash/flashPgmPvt.h + +libbsp_a_SOURCES += ../shared/flash/flash.c \ + ../shared/flash/intelFlash.c \ + flash/flashcfg.c + +#pci +include_bsp_HEADERS += ../../powerpc/shared/pci/pci.h + +libbsp_a_SOURCES += ../../powerpc/shared/pci/pci.c \ + pci/gt_pci_init.c pci/pci_io_remap.c pci/motload_fixup.c \ + ../../powerpc/shared/pci/pcifinddevice.c + +#vectors +include_bsp_HEADERS += ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/vectors.h +include_bsp_HEADERS += ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/irq_supp.h +include_bsp_HEADERS += ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/ppc_exc_bspsupp.h + +#vme +include_bsp_HEADERS += vme/VMEConfig.h \ + ../../shared/vmeUniverse/vmeUniverse.h \ + ../../shared/vmeUniverse/vmeUniverseDMA.h \ + ../../shared/vmeUniverse/vme_am_defs.h \ + ../../shared/vmeUniverse/vmeTsi148.h \ + ../../shared/vmeUniverse/vmeTsi148DMA.h \ + ../../shared/vmeUniverse/bspVmeDmaList.h \ + ../../shared/vmeUniverse/VME.h \ + ../../shared/vmeUniverse/VMEDMA.h + +libbsp_a_SOURCES += ../shared/vme/vmeconfig.c \ + ../shared/vme/vme_universe.c \ + ../../shared/vmeUniverse/vmeUniverse.c \ + ../../shared/vmeUniverse/vmeTsi148.c \ + ../../shared/vmeUniverse/bspVmeDmaList.c + +#network +if HAS_NETWORKING +include_bsp_HEADERS += network/support/early_enet_link_status.h \ + network/support/bsp_bsdnet_attach.h + +noinst_PROGRAMS += network_support.rel +network_support_rel_SOURCES = network/support/early_link_status.c \ + network/support/bsp_attach.c +network_support_rel_CPPFLAGS = $(AM_CPPFLAGS) +network_support_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS += network/if_mve/if_mve_pub.h + +noinst_PROGRAMS += network_if_mve_tmp.rel +network_if_mve_tmp_rel_SOURCES = network/if_mve/mv643xx_eth.c +network_if_mve_tmp_rel_CPPFLAGS = $(AM_CPPFLAGS) -DDISABLE_DETACHING +network_if_mve_tmp_rel_CFLAGS = $(AM_CFLAGS) +network_if_mve_tmp_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +# remove all unneccessary global symbols to avoid name clashes +# with BSD stuff; +network_if_mve.rel: network_if_mve_tmp.rel + $(OBJCOPY) -G rtems_mve_attach -G rtems_mve_early_link_check_ops \ + -G BSP_mve_ack_irqs -G BSP_mve_disable_irqs \ + -G BSP_mve_enable_irqs -G BSP_mve_init_hw \ + -G BSP_mve_ack_irq_mask -G BSP_mve_disable_irq_mask \ + -G BSP_mve_enable_irq_mask -G BSP_mve_setup_1 \ + -G BSP_mve_read_eaddr -G BSP_mve_send_buf \ + -G BSP_mve_send_buf_raw \ + -G BSP_mve_setup -G BSP_mve_stop_hw \ + -G BSP_mve_swipe_rx -G BSP_mve_swipe_tx \ + -G BSP_mve_detach -G BSP_mve_media_ioctl \ + -G BSP_mve_get_tid \ + -G BSP_mve_dump_stats -G BSP_mve_ack_link_chg \ + -G BSP_mve_mcast_filter_clear \ + -G BSP_mve_mcast_filter_accept_all \ + -G BSP_mve_mcast_filter_accept_add \ + -G BSP_mve_mcast_filter_accept_del \ + -G mveth_serial_ctrl_config_val \ + $^ $@ + +include_bsp_HEADERS += network/if_gfe/if_gfe_pub.h + +noinst_PROGRAMS += network_if_gfe_tmp.rel +network_if_gfe_tmp_rel_SOURCES = network/if_gfe/if_gfe.c network/if_gfe/if_gfe_rtems.c +network_if_gfe_tmp_rel_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(srcdir)/network/porting -I$(srcdir)/network/if_gfe +network_if_gfe_tmp_rel_CFLAGS = -Wno-unused-variable $(AM_CFLAGS) +network_if_gfe_tmp_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +# remove all unneccessary global symbols to avoid name clashes +# with BSD stuff; +network_if_gfe.rel: network_if_gfe_tmp.rel + $(OBJCOPY) -G rtems_gfe_attach -G net_driver_ticks_per_sec \ + -G rtems_gfe_setup -G rtems_gfe_early_link_check_ops \ + $^ $@ + + +include_bsp_HEADERS += network/if_em/if_em_pub.h + +noinst_PROGRAMS += network_if_em_tmp.rel +network_if_em_tmp_rel_SOURCES = network/if_em/if_em.c \ + network/if_em/if_em_hw.c \ + network/if_em/if_em_rtems.c +network_if_em_tmp_rel_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(srcdir)/network/porting -I$(srcdir)/network/if_em +network_if_em_tmp_rel_CFLAGS = -Wno-unused-variable $(AM_CFLAGS) +network_if_em_tmp_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +network_if_em.rel: network_if_em_tmp.rel + $(OBJCOPY) -G rtems_em_attach -G net_driver_ticks_per_sec \ + -G rtems_em_pci_setup -G rtems_em_early_link_check_ops \ + $^ $@ +endif + +# tod +nodist_include_HEADERS += ../../shared/tod.h + +libbsp_a_SOURCES += ../../shared/tod.c tod/todcfg.c + +libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/shared/cpuIdent.rel \ + ../../../libcpu/@RTEMS_CPU@/shared/cache.rel \ + ../../../libcpu/@RTEMS_CPU@/shared/stack.rel \ + ../../../libcpu/@RTEMS_CPU@/@exceptions@/rtems-cpu.rel \ + ../../../libcpu/@RTEMS_CPU@/@exceptions@/exc_bspsupport.rel \ + ../../../libcpu/@RTEMS_CPU@/@exceptions@/irq_bspsupport.rel \ + ../../../libcpu/@RTEMS_CPU@/mpc6xx/clock.rel \ + ../../../libcpu/@RTEMS_CPU@/mpc6xx/mmu.rel \ + ../../../libcpu/@RTEMS_CPU@/mpc6xx/timer.rel \ + ../../../libcpu/@RTEMS_CPU@/mpc6xx/altivec.rel + +if HAS_NETWORKING +libbsp_a_LIBADD += network_support.rel \ + network_if_mve.rel network_if_gfe.rel network_if_em.rel +endif + +all-local: $(PREINSTALL_FILES) $(TMPINSTALL_FILES) + +EXTRA_DIST += README LICENSE ChangeLog + +include $(srcdir)/preinstall.am +include $(top_srcdir)/../../../../automake/local.am diff --git a/c/src/lib/libbsp/powerpc/beatnik/README b/c/src/lib/libbsp/powerpc/beatnik/README new file mode 100644 index 0000000000..699f9a5e9d --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/README @@ -0,0 +1,178 @@ +Some information about this BSP +================================ + +ACKNOWLEDGEMENTS +---------------- +Acknowledgements: + Valuable information was obtained from the following drivers + netbsd: Allegro Networks Inc; Wasabi Systems Inc. + linux: MontaVista, Software, Inc; Chris Zankel, Mark A. Greer. + Matthew Dharm, rabeeh, Manish Lachwani, Ralf Baechle. + rtems: Brookhaven National Laboratory; Shuchen Kate Feng + This BSP also builds on top of the work of others who have contributed + to similar RTEMS (powerpc) BSPs, most notably Eric Valette, Eric Norum + and others. + + In particular, the Author wishes to thank Shuchen Kate Feng (BNL) for many + inspiring discussions and Dayle Kotturi (SLAC) for her contributions, support + and extensive testing. + +LICENSE +------- +See ./LICENSE file. + +Note that not all files that are part of this BSP were written by +me (most notably, the ethernet drivers if_gfe [netbsd port] and +if_em [freebsd port]). Consult individual file headers for copyright +and authorship information. + +BUILD INFO +---------- +(relevant only if you received this BSP unbundled from the RTEMS distribution) + + prepare: + - get up-to date RTEMS release + - untar beatnik.tgz into c/src/lib/libbsp/powerpc + - copy beatnik.cfg into make/custom + - patch c/src/lib/libsp/powerpc/acinclude.ac + - run 'bootstrap' from top directory; make sure RTEMS + autoXXX are found first in your PATH + configure: + - configure with your favorite options. BSP name is 'beatnik' + I recommend passing RTEMS_CFLAGS=-g to 'configure' + +TARGET +------ +Even though this BSP is binary compatible with the MVME5500 it's primary +target was and is the MVME6100 board which in some respects is quite different. +In particular, the discovery chip and the VME bridge exhibit significant +differences. +I am sometimes asked why this BSP provides yet another port of the gfe +and em BSD drivers (which had previously been ported for the mvme5500 +BSP by Shuchen Kate Feng [BNL]). The answer is simply a matter of time: +Once support for the 6100 board was completed I found it easier to use +the set of 'quick-and-dirty' wrappers (found in network/porting) that I had +developed for other projects and to do a new port from scratch using that +framework rather than modifying the mvme5500 BSP's drivers. mvme5500 support was +added to this BSP because we own a few of those boards we occasionally +play with but we don't want to build and support an additional BSP for them. +An important detail -- hardware cache snooping -- was borrowed from +Shuchen Kate Feng's gfe driver port, though. + +HARDWARE SUPPORT +=============== +(some of the headers mentioned below contain more +detailed information) + +NOTE: The BSP supports both, the mvme6100 and the mvme5500 boards. + It detects relevant hardware at run-time. + +CONSOLE: 2 serial devices, UART driver from 'shared' - no surprises + ("/dev/ttyS0", [="/dev/console"], "/dev/ttyS1"). (Only + /dev/ttyS0 is accessible from the front panel.) + +CLOCK: Decrementer, same as other PPC BSPs. (FIXME: a discovery timer + could be used.) + +PIC (interrupt controller) (bsp/irq.h): Marvell hostbridge + does not implement interrupt priorities. The driver supports + priorities in software (masking lower priority lines during + execution of higher priority ISR). I believe the design of the + IRQ subsystem is as efficient as possible with focus on low + latencies. + In addition to the rtems IRQ API, calls are available to + change IRQ priority and to enable/disable interrupts at the PIC. + +EXCEPTIONS: (bspException.h) Routines to install a user callback + for (PPC) exception handling. + +PCI (bsp/pci.h): The BSP hides the fact that there are effectively + two 'root' busses (AKA 'hoses') behind the discovery bridge. + Devices are addressed by bus/slot/function-triples and the PCI + subsystem transparently figures out what hose to use. + In addition to rtems' PCI API, a call is available to scan + all devices executing a user callback on each device. + BSP_pciConfigDump() is a convenience wrapper dumping essential + information (IDs, BAs, IRQ pin/line) to the console or a file. + +MEMORY MAP: CHRP; all addresses (MEM + I/O) read from PCI config. space + are CPU addresses. For sake of portability, drivers should still + use the _IO_BASE, PCI_MEM_BASE, PCI_DRAM_OFFSET constants. + +NVRAM: Address constants are defined in bsp.h + +FLASH (bsp/flashPgm.h): Routines to write flash. Highest level + wrapper writes a file to flash. + NOTE: Writing to flash is disabled by default; + call BSP_flashWriteEnable(). + +I2C (bsp.h, rtems/libi2c.h, libchip/i2c-xxx.h): temp. sensor and eeprom + are available as device files (bsp.h); lower-level interface is + provided by libi2c.h. + NOTE: The I2C devices are not registered and the driver is not + initialized by default. Call BSP_i2c_initialize() to do that; + this will create + /dev/i2c0.vpd-eeprom + /dev/i2c0.usr-eeprom + /dev/i2c0.ds1621 + You can then read the board temperature: + fd = open("/dev/i2c0.ds1621",O_RDONLY) + read(fd,&temp,1) + close(fd); + printf("Board Temp. is %idegC\n",(int)temp); + +VME: (bsp/VME.h, bsp/vme_am_defs.h, bsp/VMEDMA.h). + *always* use VME.h API, if possible; do *not* use chip drivers + (vmeUniverse.h, vmeTsi148.h) directly unless you know what you are + doing (i.e., if you need specific features provided by the particular + chip; currently, both of the mentioned chip drivers expose entry points + that are designed to be compatible). + + VMEConfig.h should not be used by applications as it makes them + dependent on BSP internals. VMEConfig.h is intended to be used + by BSP designers only. + + VME interrupt priorities: the VME bridge(s) do not implement + priorities in hardware. + However, on the 5500/6100 multiple physical interrupt + lines/wires connect the VME bridge to the PIC. Hence, it is possible + to assign the different wires different priorities at the PIC + (see above) and to route VME interrupts to different wires according + to their priority. You need to call driver specific routines + for this (vmeXXXIntRoute()), however (for driver-specific API + consult bsp/vmeUniverse.h, bsp/vmeTsi148.h). + + For VME DMA *always* use the bsp/VMEDMA.h API. DO NOT use + chip-specific features. Applications written using the bsp/VMEDMA.h + API are portable between the UniverseII and the Tsi148. + +HARDWARE TIMERS: (bsp/gt_timer.h). Programmable general-purpose (GPT) and + watchdog timers. Routines are provided to setup, start and stop + GPTs. The setup routine allows for specifying single-shot or periodic + mode and dispatches a user ISR when the GPT expires. + + The watchdog timer - when started - issues a hard-reset of the + board if not 'petted' within a configurable timeout period. + +NETWORK: (bsp/bsp_bsdnet_attach.h). The BSP offers a call to list + all available interfaces (name, description, 'attach'-method) + for the application to make a selection. + Alternatively, there are BSP_auto_network_driver_name and + BSP_auto_enet_attach(), the latter with the capability to configure + the first NIC with a 'live' link status. + All drivers (rewritten 'mve' for the mv64360 NIC (6100) and BSD ports + 'gfe'/'em' (5500)) support the SIOCSIFMEDIA/SIOCGIFMEDIA ioctls + (rtems/rtems_mii_ioctl.h provides helpers to convert strings from/to + control words). + +VPD: (bsp/vpd.h). The board's VPD (vital-product-data such as S/N, + MAC addresses and so forth) can be retrieved. + +BOOTING: BSP has a relocator-header. Clear MSR and jump to the first + instruction in the binary. R3 and R4, if non-null, point to the + start/end of an optional command line string that is copied into + BSP_commandline_string. The BSP is compatible with 'netboot'. + +Have fun. + +-- Till Straumann , 2005-2007. diff --git a/c/src/lib/libbsp/powerpc/beatnik/bsp_specs b/c/src/lib/libbsp/powerpc/beatnik/bsp_specs new file mode 100644 index 0000000000..3c859d1d66 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/bsp_specs @@ -0,0 +1,13 @@ +%rename endfile old_endfile +%rename startfile old_startfile +%rename link old_link + +*startfile: +%{!qrtems: %(old_startfile)} \ +%{!nostdlib: %{qrtems: ecrti%O%s rtems_crti%O%s crtbegin.o%s -e __rtems_entry_point -u __vectors motld_start.o%s}} + +*link: +%{!qrtems: %(old_link)} %{qrtems: -dp -Bstatic} + +*endfile: +%{!qrtems: %(old_endfile)} %{qrtems: crtend.o%s ecrtn.o%s} diff --git a/c/src/lib/libbsp/powerpc/beatnik/configure.ac b/c/src/lib/libbsp/powerpc/beatnik/configure.ac new file mode 100644 index 0000000000..d8f6a3f83c --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/configure.ac @@ -0,0 +1,40 @@ +## Process this file with autoconf to produce a configure script. +## +## configure.ac,v 1.9.2.3 2003/08/11 14:37:22 ralf Exp + +AC_PREREQ(2.59) +AC_INIT([rtems-c-src-lib-libbsp-powerpc-beatnik],[_RTEMS_VERSION],[rtems-bugs@rtems.com]) +AC_CONFIG_SRCDIR([bsp_specs]) +RTEMS_TOP(../../../../../..) + +RTEMS_CANONICAL_TARGET_CPU +AM_INIT_AUTOMAKE([no-define nostdinc foreign 1.9]) +RTEMS_BSP_CONFIGURE + +RTEMS_PROG_CC_FOR_TARGET([-ansi -fasm]) +RTEMS_CANONICALIZE_TOOLS +RTEMS_PROG_CCAS + +RTEMS_CHECK_TOOL([OBJCOPY],[objcopy]) + +RTEMS_CHECK_NETWORKING +AM_CONDITIONAL(HAS_NETWORKING,test "$HAS_NETWORKING" = "yes") + +AS=$CC +AM_PROG_AS + +RTEMS_BSPOPTS_SET([PPC_USE_DATA_CACHE],[*],[1]) +RTEMS_BSPOPTS_HELP([PPC_USE_DATA_CACHE], +[If defined, then the PowerPC specific code in RTEMS will use + data cache instructions to optimize the context switch code. + This code can conflict with debuggers or emulators. It is known + to break the Corelis PowerPC emulator with at least some combinations + of PowerPC 603e revisions and emulator versions. + The BSP actually contains the call that enables this.]) + +# Explicitly list all Makefiles here +AC_CONFIG_FILES([Makefile]) + +RTEMS_PPC_EXCEPTIONS + +AC_OUTPUT diff --git a/c/src/lib/libbsp/powerpc/beatnik/flash/flashcfg.c b/c/src/lib/libbsp/powerpc/beatnik/flash/flashcfg.c new file mode 100644 index 0000000000..3dc76c504d --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/flash/flashcfg.c @@ -0,0 +1,182 @@ +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include +#include +#include + +#define STATIC static + +#include + +/* MVME Board Specifica; board status reg. 2 where write-enable is controlled... */ + +#define SYS_FLASHA_WP (1<<5) +#define SYS_FBOOTB_WP (1<<3) +#define SYS_FBA_WP_HDR (1<<2) +#define SYS_FBOOTB_WP_HDR (1<<1) + +#define SYS_STATUS_2_REG (1) + +/* Forward Declarations */ +STATIC struct bankdesc * +bankcheck(int bank, int quiet); + +static int +flash_wp(int bank, int enbl); + +STATIC uint32_t +read_us_timer(void); + +/* Global Variables */ + +/* motload memory map */ +static struct bankdesc mvme5500Flash[] = { + { 0, 2 }, /* first entry gives number of entries */ + { 0xf2000000, 0x08000000, 0x20000*2, 2, BSP_flash_vendor_intel, 0, 0, 0, }, + { 0xff800000, 0x00800000, 0x20000*2, 2, BSP_flash_vendor_intel, 0, 0, 0, }, +}; + +/* motload memory map */ +static struct bankdesc mvme6100Flash[] = { + { 0, 2 }, /* first entry gives number of entries */ + { 0xf4000000, 0x04000000, 0x20000*2, 2, BSP_flash_vendor_intel, 0, 0, 0, }, + { 0xf8000000, 0x04000000, 0x20000*2, 2, BSP_flash_vendor_intel, 0, 0, 0, }, +}; + +struct flash_bsp_ops BSP_flashBspOps = { + bankcheck : bankcheck, + flash_wp : flash_wp, + read_us_timer: read_us_timer, +}; + +/* set (enbl:1), clear (enbl:0) or query (enbl:-1) write protection + * + * RETURNS 0 on success, nonzero on error. + */ +static int +flash_wp(int bank, int enbl) +{ +BSP_BoardType b; +A8 p; +unsigned char hwp = 0, swp; + + /* validate 'bank' argument */ + if ( !bankcheck( bank, 0 ) ) + return -1; + + switch ( (b=BSP_getBoardType()) ) { + default: + fprintf(stderr,"Unknown board type %i\n",b); + return -1; + + case MVME5500: + /* bit enables both banks; no readback of jumper available */ + p = (A8)(BSP_MV64x60_DEV1_BASE + SYS_STATUS_2_REG); + swp = SYS_FLASHA_WP; + break; + + case MVME6100: + { + + p = (A8)(BSP_MV64x60_DEV1_BASE + SYS_STATUS_2_REG); + if ( 0 == bank ) { + hwp = SYS_FBA_WP_HDR; + swp = SYS_FLASHA_WP; + } else { + hwp = SYS_FBOOTB_WP_HDR; + swp = SYS_FBOOTB_WP; + } + if ( enbl && (*p & hwp) ) { + fprintf(stderr,"HW write protection enabled (jumper)\n"); + return -1; + } + } + break; + } + if ( -1 == enbl ) { + /* query */ + return *p & (swp | hwp); + } else { + if ( enbl ) { + *p |= swp; + } else { + *p &= ~swp;; + } + } + return 0; +} + +/* Lookup bank description in table */ +STATIC struct bankdesc * +bankcheck(int bank, int quiet) +{ +struct bankdesc *b; + switch ( BSP_getBoardType() ) { + case MVME5500: b = mvme5500Flash; break; + case MVME6100: b = mvme6100Flash; break; + default: + fprintf(stderr,"Unknown/unsupported board type\n"); + return 0; + } + if ( bank >= b->size || bank < 0 ) { + if ( !quiet ) + fprintf(stderr,"Invalid flash bank #: %i; (too big)\n", bank); + return 0; + } + return b + bank + 1; +} + +STATIC uint32_t read_us_timer(void) +{ +uint32_t now, mhz; + + /* we burn cycles anyways... */ + mhz = BSP_bus_frequency/BSP_time_base_divisor/1000; + + asm volatile("mftb %0":"=r"(now)); + + return now/mhz; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/include/.cvsignore b/c/src/lib/libbsp/powerpc/beatnik/include/.cvsignore new file mode 100644 index 0000000000..5f1077556d --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/include/.cvsignore @@ -0,0 +1,4 @@ +bspopts.h +bspopts.h.in +stamp-h +stamp-h.in diff --git a/c/src/lib/libbsp/powerpc/beatnik/include/bsp.h b/c/src/lib/libbsp/powerpc/beatnik/include/bsp.h new file mode 100644 index 0000000000..84a3cfce00 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/include/bsp.h @@ -0,0 +1,273 @@ +/* + * bsp.h -- contain BSP API definition. + * + * Copyright (C) 1999 Eric Valette. valette@crf.canon.fr + * + * The license and distribution terms for this file may be + * found in found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * S. Kate Feng 2003-2007 : Modified it to support the mvme5500 BSP. + * + * Modified for the 'beatnik' BSP by T. Straumann, 2005-2007. + * + * bsp.h,v 1.9.4.2 2003/09/04 18:45:20 joel Exp + */ +#ifndef LIBBSP_BEATNIK_BSP_H +#define LIBBSP_BEATNIK_BSP_H + +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* Board type */ +typedef enum { + Unknown = 0, + MVME5500, + MVME6100 +} BSP_BoardType; + +BSP_BoardType +BSP_getBoardType(); + +/* Discovery Version */ + +typedef enum { + unknown = 0, + GT_64260_A, /* Revision 0x10 */ + GT_64260_B, /* Revision 0x20 */ + MV_64360, +} DiscoveryVersion; + +/* Determine the type of discovery chip on this board; info + * is cached and repeated calls just return the cached value. + * + * If a non-zero argument is passed, the routine panics + * (BSP_panic) if no recognized bridge is found; + */ + +DiscoveryVersion +BSP_getDiscoveryVersion(int assertion); + +/* + * confdefs.h overrides for this BSP: + * - termios serial ports (defaults to 1) + * - Interrupt stack space is not minimum if defined. + */ + +#define CONFIGURE_NUMBER_OF_TERMIOS_PORTS 2 +#define BSP_INTERRUPT_STACK_SIZE (16 * 1024) + +/* + * base address definitions for several devices + * + */ +#define BSP_MV64x60_BASE (0xf1000000) +#define BSP_MV64x60_DEV1_BASE (0xf1100000) +#define BSP_UART_IOBASE_COM1 ((BSP_MV64x60_DEV1_BASE)+0x20000) +#define BSP_UART_IOBASE_COM2 ((BSP_MV64x60_DEV1_BASE)+0x21000) +#define BSP_UART_USE_SHARED_IRQS + +#define BSP_NVRAM_BASE_ADDR (0xf1110000) +#define BSP_NVRAM_END_ADDR (0xf1117fff) +#define BSP_NVRAM_RTC_START (0xf1117ff8) + +#define BSP_NVRAM_BOOTPARMS_START (0xf1111000) +#define BSP_NVRAM_BOOTPARMS_END (0xf1111fff) + + +/* This is only active/used during early init. It defines + * the hose0 base for the shared/generic pci code. + * Our own BSP specific pci initialization will then + * override the PCI configuration (see gt_pci_init.c:BSP_pci_initialize) + */ + +#define PCI_CONFIG_ADDR (BSP_MV64x60_BASE + 0xcf8) +#define PCI_CONFIG_DATA (BSP_MV64x60_BASE + 0xcfc) + +/* our wonderful PCI initialization remaps everything to CPU addresses + * - before calling BSP_pci_initialize() this is NOT VALID, however + * and the deprecated inl()/outl() etc won't work! + */ +#define _IO_BASE 0x00000000 +/* wonderful MotLoad has the base address as seen from the CPU programmed into config space :-) */ +#define PCI_MEM_BASE 0 +#define PCI_MEM_BASE_ADJUSTMENT 0 +#define PCI_DRAM_OFFSET 0 + +/* PCI <-> local address mapping - no sophisticated windows + * (i.e., no support for cached regions etc. you read a BAR + * from config space and that's 1:1 where the CPU sees it). + * Our memory is mapped 1:1 to PCI also. + */ +#define BSP_PCI2LOCAL_ADDR(a) ((uint32_t)(a)) +#define BSP_LOCAL2PCI_ADDR(a) ((uint32_t)(a)) + +#define BSP_CONFIG_NUM_PCI_CACHE_SLOTS 32 + +#define BSP_CONSOLE_PORT BSP_UART_COM1 +#define BSP_UART_BAUD_BASE 115200 + +/* I2C Devices */ +/* Note that the i2c addresses stated in the manual are + * left-shifted by one bit. + */ +#define BSP_VPD_I2C_ADDR (0xA8>>1) /* the VPD EEPROM */ +#define BSP_USR_I2C_ADDR (0xAA>>1) /* the user EEPROM */ +#define BSP_THM_I2C_ADDR (0x90>>1) /* the DS1621 temperature sensor & thermostat */ + +#define BSP_I2C_BUS_DESCRIPTOR gt64260_i2c_bus_descriptor + +#define BSP_I2C_BUS0_NAME "/dev/i2c0" + +#define BSP_I2C_VPD_EEPROM_NAME "vpd-eeprom" +#define BSP_I2C_USR_EEPROM_NAME "usr-eeprom" +#define BSP_I2C_DS1621_NAME "ds1621" +#define BSP_I2C_THM_NAME BSP_I2C_DS1621_NAME +#define BSP_I2C_DS1621_RAW_NAME "ds1621-raw" + +#define BSP_I2C_VPD_EEPROM_DEV_NAME (BSP_I2C_BUS0_NAME"."BSP_I2C_VPD_EEPROM_NAME) +#define BSP_I2C_USR_EEPROM_DEV_NAME (BSP_I2C_BUS0_NAME"."BSP_I2C_USR_EEPROM_NAME) +#define BSP_I2C_DS1621_DEV_NAME (BSP_I2C_BUS0_NAME"."BSP_I2C_DS1621_NAME) +#define BSP_I2C_THM_DEV_NAME BSP_I2C_DS1621_DEV_NAME +#define BSP_I2C_DS1621_RAW_DEV_NAME (BSP_I2C_BUS0_NAME"."BSP_I2C_DS1621_RAW_NAME) + + +/* Initialize the I2C driver and register all devices + * RETURNS 0 on success, -1 on error. + * + * Access to the VPD and user EEPROMS as well + * as the ds1621 temperature sensor is possible + * by means of file nodes + * + * /dev/i2c0.vpd-eeprom (read-only) + * /dev/i2c0.usr-eeprom (read-write) + * /dev/i2c0.ds1621 (read-only; one byte: board-temp in degC) + * /dev/i2c0.ds1621-raw (read-write; transfer bytes to/from the ds1621) + */ +int +BSP_i2c_initialize(); + +/* Networking; */ +#include + +/* NOT FOR PUBLIC USE BELOW HERE */ +#define BSP_PCI_HOSE0_MEM_BASE 0x80000000 /* must be aligned to size */ +#define BSP_PCI_HOSE0_MEM_SIZE 0x20000000 + +#define BSP_PCI_HOSE1_MEM_BASE 0xe0000000 + +#define BSP_DEV_AND_PCI_IO_BASE 0xf0000000 +#define BSP_DEV_AND_PCI_IO_SIZE 0x10000000 + +/* maintain coherency between CPU and GT64340 ethernet (& possibly other discovery components) */ +#define BSP_RW_PAGE_ATTRIBUTES TRIV121_ATTR_M + +extern unsigned BSP_pci_hose1_bus_base; + +void BSP_pci_initialize(); + +/* Exception Handling */ + +/* Use a task notepad to attach user exception handler info; + * may be changed by application startup code (EPICS uses 11) + */ +#define BSP_EXCEPTION_NOTEPAD 14 + +#ifndef ASM + +#define outport_byte(port,value) outb(value,port) +#define outport_word(port,value) outw(value,port) +#define outport_long(port,value) outl(value,port) + +#define inport_byte(port,value) (value = inb(port)) +#define inport_word(port,value) (value = inw(port)) +#define inport_long(port,value) (value = inl(port)) +/* + * Vital Board data Start using DATA RESIDUAL + */ +/* + * Total memory using RESIDUAL DATA + */ +extern unsigned int BSP_mem_size; +/* + * Start of the heap + */ +extern unsigned int BSP_heap_start; +/* + * PCI Bus Frequency + */ +extern unsigned int BSP_bus_frequency; +/* + * processor clock frequency + */ +extern unsigned int BSP_processor_frequency; +/* + * Time base divisior (how many tick for 1 second). + */ +extern unsigned int BSP_time_base_divisor; + +extern char BSP_productIdent[20]; +extern char BSP_serialNumber[20]; + +extern char BSP_enetAddr0[7]; +extern char BSP_enetAddr1[7]; + +/* + * The commandline as passed from the bootloader. + */ +extern char *BSP_commandline_string; + + +#define BSP_Convert_decrementer( _value ) \ + ((unsigned long long) ((((unsigned long long)BSP_time_base_divisor) * 1000000ULL) /((unsigned long long) BSP_bus_frequency)) * ((unsigned long long) (_value))) + +extern rtems_configuration_table BSP_Configuration; +extern void BSP_panic(char *s); +extern void bsp_reset(void); +extern int BSP_disconnect_clock_handler (void); +extern int BSP_connect_clock_handler (void); + +/* clear hostbridge errors + * + * enableMCP: whether to enable MCP checkstop / machine check interrupts + * on the hostbridge and in HID0. + * + * NOTE: The 5500 and 6100 boards have NO PHYSICAL CONNECTION + * to MCP so 'enableMCP' will always fail! + * + * quiet : be silent + * + * RETURNS : PCI status (hose 0 in byte 0, host 1 in byte 1) and + * VME bridge status (upper 16 bits). + * Zero if no errors were found. + */ +extern unsigned long _BSP_clear_hostbridge_errors(int enableMCP, int quiet); + +/* clear vme bridge errors and return (bridge-dependent) 16-bit status + * + * quiet : be silent + * + * RETURNS : 0 if there were no errors, non-zero, bridge-dependent + * 16-bit error status on error. + * + */ +extern unsigned short +(*_BSP_clear_vmebridge_errors)(int); + + +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c b/c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c new file mode 100644 index 0000000000..8b0eba27fc --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/irq/discovery_pic.c @@ -0,0 +1,995 @@ +/* $Id$ */ + +/* Interrupt driver + dispatcher for the discovery host controller */ + +/* Author: T. Straumann, 2005-2007 + * + * Acknowledgements: + * Valuable information was obtained from the following drivers + * netbsd: (C) Allegro Networks Inc; Wasabi Systems Inc. + * linux: (C) MontaVista, Software, Inc; Chris Zankel, Mark A. Greer. + * rtems: (C) Brookhaven National Laboratory; K. Feng + * but this implementation is original work by the author. + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* dont change the order (main_lo, main_hi, gpp) which + * matches the interrupt numbers! + */ +#define MAIN_LO_IDX 0 +#define MAIN_HI_IDX 1 +#define GPP_IDX 2 +#define NUM_INTR_REGS 3 + + +#define SYNC() asm volatile("sync") + +/* How many times should the ISR dispatcher check for + * pending interrupts until it decides that something's + * fishy (i.e., a user ISR fails to clear the interrupt + * source) + */ +#define MAX_SPIN_LOOPS 100 + +/* If FASTER is defined, a few obscure I/O statements found in the linux + * driver are removed + */ +#define FASTER + +/* Array helper */ +#define NumberOf(arr) (sizeof(arr)/sizeof((arr)[0])) + + +/* MVME6100 specific; re-define watchdog NMI pin to be a normal output + * so we have a way to raise an interrupt in software (GPP[26] is wired to + * GPP[6] on the MVME6100). + */ +#define MVME6100_IRQ_DEBUG 4 + +#define GPP_WIRED_OUT_BIT_6100 26 /* CAVEAT: this is bit 26 on the 6100 */ +#define GPP_WIRED_OUT_BIT_5500 24 /* CAVEAT: this is bit 24 on the 5500 */ +#define GPP_WIRED_IN_BIT 6 + +/* Ored mask of debugging features to enable */ +#define IRQ_DEBUG_BASIC 1 +/* This is _very_ lowlevel */ +#define IRQ_DEBUG_DISPATCHER 2 +/* Record maximal dispatching latency */ +#define IRQ_DEBUG_MAXLAT 8 /* PPC only */ + +#define IRQ_DEBUG (0 /*|(IRQ_DEBUG_BASIC)*/|(MVME6100_IRQ_DEBUG)|(IRQ_DEBUG_MAXLAT)) + +/********** + * Typedefs + **********/ + +/* Set of the three relevant cause registers */ +typedef volatile unsigned IrqMask[NUM_INTR_REGS]; + +#define REGP(x) ((volatile uint32_t *)(x)) + +/* Information we keep about the PIC */ +typedef struct _Mv64x60PicRec { + /* base address as seen from CPU */ + uintptr_t reg_base; + + /* addresses of 'cause' registers */ + volatile uint32_t *causes[NUM_INTR_REGS]; + + /* addresses of 'mask' registers */ + volatile uint32_t *masks[NUM_INTR_REGS]; + + /* masks for all priorities. If an + * interrupt source has priority X, + * its corresponding bit is set + * (enabled) in mcache[i] for all + * i < X and cleared for i >= X + */ + volatile IrqMask mcache[BSP_IRQ_MAX_PRIO+1]; + + /* Priority we're executing at. + * Thread-level is priority 0, + * ISRs range from 1..MAX_PRIO + */ + volatile rtems_irq_prio current_priority; +} Mv64x60PicRec, *Mv64x60Pic; + +/********** + * Globals + **********/ + + +/* Copy of the configuration */ +static rtems_irq_global_settings theConfig; +/* PIC description */ +static Mv64x60PicRec thePic; + +#if (IRQ_DEBUG) & MVME6100_IRQ_DEBUG +static unsigned long gpp_out_bit = 0; +#endif + +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT +unsigned long discovery_pic_max_dispatching_latency = 0; +#ifdef __PPC__ +static inline unsigned long mftb(void) +{ +unsigned long rval; + asm volatile("mftb %0":"=r"(rval)); + return rval; +} +#else +#define mftb() 0 +#endif +#endif + +/********** + * Functions + **********/ + +/* Debugging helper routines */ +static void pregs(volatile uint32_t **p) +{ +int i; + for (i=NUM_INTR_REGS-1; i>=0; i--) { + printk(" 0x%08x", ld_le32(p[i])); + printk( i ? " --":"\n"); + } +} + +static void pmsks(volatile IrqMask p) +{ +int i; + for (i=NUM_INTR_REGS-1; i>=0; i--) { + printk(" 0x%08x", p[i]); + printk( i ? " --":"\n"); + } +} + +void discovery_dump_picregs(void) +{ + printk(" ..GPP_IRQ. -- ..MAIN_HI. -- ..MAIN_LO.\n"); + printk("Cause:"); pregs(thePic.causes); + printk("Mask: "); pregs(thePic.masks); +} + +/* Small inline helpers */ + +/* return 0 if this PIC is not 'responsible' for a given irq number + * we also 'ignore' the GPP summary bits - these must always remain + * enabled. + */ +static inline int +validIrqNo(rtems_irq_number irq) +{ + return + irq >= BSP_PCI_IRQ_LOWEST_OFFSET + && irq <= BSP_PCI_IRQ_MAX_OFFSET + && ! (IMH_GPP_SUM & (1<<(irq-32))); +} + +/* return 0 if a given priority is outside the valid range */ +static inline int +validPri(rtems_irq_prio pri) +{ + /* silence compiler warning about limited range of type; + * hope it never changes... + */ + return /* pri>=0 && */ pri <=BSP_IRQ_MAX_PRIO; +} + +/* Return position of the most significant bit that is set in 'x' */ +static inline int +__ilog2(unsigned x) +{ + asm volatile("cntlzw %0, %0":"=&r"(x):"0"(x)); + return 31-x; +} + +/* Convert irq number to cause register index + * (array of handles in the PicRec). + * ASSUMES: 'irq' within valid range. + */ +static inline unsigned +irqDiv32(unsigned irq) +{ + return (irq-BSP_PCI_IRQ_LOWEST_OFFSET)>>5; +} + +/* Convert irq number to cause/mask bit number. + * ASSUMES: 'irq' within valid range. + */ + +static inline unsigned +irqMod32(unsigned irq) +{ + return (irq-BSP_PCI_IRQ_LOWEST_OFFSET)&31; +} + +/* NON-ATOMICALLY set/clear bits in a MV64x60 register + * + * register contents at offset 'off' are ANDed with + * complement of the 'clr' mask and ORed with 'set' mask: + * + * *off = (*off & ~clr) | set + * + * ASSUMES: executed from IRQ-disabled section + */ +static inline void +gt_bitmod(unsigned off, unsigned set, unsigned clr) +{ + st_le32(REGP(thePic.reg_base + off), + (ld_le32(REGP(thePic.reg_base+off)) & ~clr) | set); +} + +static inline unsigned +gt_read(unsigned off) +{ + return ld_le32(REGP(thePic.reg_base + off)); +} + +static inline void +gt_write(unsigned off, unsigned val) +{ + st_le32(REGP(thePic.reg_base + off), val); +} + +/* Enable interrupt number 'irq' at the PIC. + * + * Checks for valid arguments but has no way of + * communicating violation; prints to console + * if illegal arguments are given. + * + * This routine may be called from ISR level. + * + * Algorithm: set corresponding bit in masks + * for all priorities lower than the + * target irq's priority and push + * mask for the currently executing + * priority out to the PIC. + */ + +void +BSP_enable_irq_at_pic(rtems_irq_number irq) +{ +unsigned i,j; +unsigned long flags; +volatile uint32_t *p; +uint32_t v,m; + + if ( !validIrqNo(irq) ) { +/* API change - must silently ignore... + printk("BSP_enable_irq_at_pic: Invalid argument (irq #%i)\n",irq); + */ + return; + } + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk("IRQ: Enable #%i;",irq); +#endif + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + /* This is probably a more serious error; don't ignore silently */ + printk("BSP_enable_irq_at_pic: illegal argument\n"); + return; + } + /* compute register pointer and bit mask */ + p = thePic.masks[i]; + m = 1< 0x%08x\n",i,v,ld_le32(p)); + +#endif +} + +/* Disable interrupt number 'irq' at the PIC. + * + * Checks for valid arguments but has no way of + * communicating violation; prints to console + * if illegal arguments are given. + * + * This routine may be called from ISR level. + * + * Algorithm: clear corresponding bit in masks + * for all priorities and push the + * mask for the currently executing + * priority out to the PIC. + */ + +int +BSP_disable_irq_at_pic(rtems_irq_number irq) +{ +unsigned i,j; +unsigned long flags; +volatile uint32_t *p; +uint32_t v,m; +int rval; + + if ( !validIrqNo(irq) ) { +/* API change - must silently ignore... + printk("BSP_disable_irq_at_pic: Invalid argument (irq #%i)\n",irq); + */ + return -1; + } + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk("IRQ: Disable #%i;",irq); +#endif + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + /* This is probably a more serious error; don't ignore silently */ + printk("BSP_enable_irq_at_pic: illegal argument\n"); + return -1; + } + + /* compute register pointer and bit mask */ + p = thePic.masks[i]; + m = (1< 0x%08x\n",i,v,ld_le32(p)); +#endif + + return rval ? 1 : 0; +} + +int +BSP_irq_is_enabled_at_pic(rtems_irq_number irq) +{ +unsigned i; + if ( !validIrqNo(irq) ) { + printk("BSP_irq_is_enabled_at_pic: Invalid argument (irq #%i)\n",irq); + return -1; + } + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + printk("BSP_enable_irq_at_pic: illegal argument\n"); + return -1; + } + return ld_le32(thePic.masks[i]) & (1<=pri + */ +int +BSP_irq_set_priority(rtems_irq_number irq, rtems_irq_prio pri) +{ +unsigned long flags; +volatile uint32_t *p; +uint32_t v,m; +unsigned i,j; + + if ( thePic.current_priority > 0 ) { + printk("BSP_irq_set_priority: must not be called from ISR level\n"); + return -1; + } + + if ( !validPri(pri) ) { + printk("BSP_irq_set_priority: invalid argument (pri #%i)\n",pri); + return -1; + } + + if ( BSP_DECREMENTER != irq ) { + if ( !validIrqNo(irq) ) { + printk("BSP_irq_set_priority: invalid argument (irq #%i)\n",irq); + return -1; + } + + if ( (i=irqDiv32(irq)) > NUM_INTR_REGS ) { + printk("BSP_irq_set_priority: illegal argument (irq #%i not PCI?)\n", irq); + return -1; + } + } + +#if (IRQ_DEBUG) & IRQ_DEBUG_BASIC + printk("IRQ: Set Priority #%i -> %i;",irq,pri); +#endif + + if ( BSP_DECREMENTER == irq ) { + theConfig.irqPrioTbl[irq] = pri; + return 0; + } + + /* compute register pointer and bit mask */ + p = thePic.masks[i]; + m = 1< 0x%08x\n",i,v,ld_le32(p)); +#endif + + return 0; +} + +/* Initialize the PIC; routine needed by BSP framework + * + * RETURNS: NONZERO on SUCCESS, 0 on error! + */ +int +BSP_setup_the_pic(rtems_irq_global_settings* config) +{ +int i; + /* + * Store copy of configuration + */ + theConfig = *config; + + /* check config */ + if ( theConfig.irqNb <= BSP_PCI_IRQ_MAX_OFFSET ) { + printk("BSP_setup_the_pic: FATAL ERROR: configured IRQ table too small???\n"); + return 0; + } + + for ( i=0; i highest priority source is found quickly. It takes at most + * + * BSP_IRQ_MAX_PRIO * ( ~3 reg-only instructions + 2 memory access ) + * + 2 reg-only instructions + 1 I/O + 1 memory access. + * + * + */ + +static unsigned mlc, mhc, gpc; + +static int decrementerPending = 0; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER +int decrementerIrqs = 0; +#endif + +static inline unsigned +find_highest_priority_pending_irq(rtems_irq_prio *ppri) +{ +register int rval = -1; +register rtems_irq_prio *pt = theConfig.irqPrioTbl + BSP_PCI_IRQ_LOWEST_OFFSET; +register rtems_irq_prio pmax = *ppri; +register unsigned cse,ocse; + +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + discovery_dump_picregs(); +#endif + + if ( decrementerPending ) { +/* Don't flood +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Decrementer IRQ pending\n"); +#endif +*/ + if ( theConfig.irqPrioTbl[BSP_DECREMENTER] > pmax ) { + pmax = theConfig.irqPrioTbl[BSP_DECREMENTER]; + rval = BSP_DECREMENTER; + } + } + + mlc = cse = ld_le32(thePic.causes[MAIN_LO_IDX]); +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_LO; cse: 0x%08x, msk 0x%08x\n", cse ,thePic.mcache[pmax][MAIN_LO_IDX]); +#endif + while ( cse &= thePic.mcache[pmax][MAIN_LO_IDX] ) { + rval = __ilog2(cse); + pmax = pt[rval]; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + mhc = cse = ocse = ld_le32(thePic.causes[MAIN_HI_IDX]); +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_HI; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][MAIN_HI_IDX]); +#endif + /* don't look at the GPP summary; only check for 'real' MAIN_HI sources */ + cse &= ~IMH_GPP_SUM; + while ( cse &= thePic.mcache[pmax][MAIN_HI_IDX] ) { + rval = __ilog2(cse) + 32; + pmax = pt[rval]; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + gpc = cse = ld_le32(thePic.causes[GPP_IDX ]); + /* if there were GPP ints, scan the GPP cause now */ + if ( ocse & IMH_GPP_SUM ) { +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("GPP; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][GPP_IDX ]); +#endif + cse &= thePic.mcache[pmax][GPP_IDX ]; + ocse = cse; + while ( cse ) { + rval = __ilog2(cse) + 64; + pmax = pt[rval]; + cse &= thePic.mcache[pmax][GPP_IDX ]; +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } +#ifndef FASTER + /* this doesn't seem to be necessary -- however, the linux people do it... */ + out_le32(thePic.causes[GPP_IDX], ~ocse); +#endif + } +#ifndef FASTER + /* this doesn't seem to be necessary -- however, the linux people do it... */ + (void)in_le32(thePic.causes[GPP_IDX]); +#endif + + *ppri = pmax; + + if ( BSP_DECREMENTER == rval ) { +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + decrementerIrqs++; +#endif + decrementerPending = 0; + } + + return rval; +} + +#if 0 /* TODO: should this be cleaned up ? */ +#define _IRQ_DEBUG IRQ_DEBUG_DISPATCHER +static inline unsigned +ffind_highest_priority_pending_irq(rtems_irq_prio *ppri) +{ +register int rval = -1; +register rtems_irq_prio *pt = theConfig.irqPrioTbl + BSP_PCI_IRQ_LOWEST_OFFSET; +register rtems_irq_prio pmax = *ppri; +register unsigned cse,ocse; + +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + discovery_dump_picregs(); +#endif + + cse = in_le32(thePic.causes[MAIN_LO_IDX]); +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_LO; cse: 0x%08x, msk 0x%08x\n", cse ,thePic.mcache[pmax][MAIN_LO_IDX]); +#endif + while ( cse &= thePic.mcache[pmax][MAIN_LO_IDX] ) { + rval = __ilog2(cse); + pmax = pt[rval]; +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + cse = ocse = in_le32(thePic.causes[MAIN_HI_IDX]); +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("MAIN_HI; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][MAIN_HI_IDX]); +#endif + /* don't look at the GPP summary; only check for 'real' MAIN_HI sources */ + cse &= ~IMH_GPP_SUM; + while ( cse &= thePic.mcache[pmax][MAIN_HI_IDX] ) { + rval = __ilog2(cse) + 32; + pmax = pt[rval]; +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + /* if there were GPP ints, scan the GPP cause now */ + if ( ocse & IMH_GPP_SUM ) { + cse = in_le32(thePic.causes[GPP_IDX ]); +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("GPP; cse: 0x%08x, msk 0x%08x\n", cse, thePic.mcache[pmax][GPP_IDX ]); +#endif + cse &= thePic.mcache[pmax][GPP_IDX ]; + ocse = cse; + while ( cse ) { + rval = __ilog2(cse) + 64; + pmax = pt[rval]; + cse &= thePic.mcache[pmax][GPP_IDX ]; +#if (_IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + printk("Max pri IRQ now %i\n",rval); +#endif + } + /* this doesn't seem to be necessary -- however, the linux people do it... */ + out_le32(thePic.causes[GPP_IDX], ~ocse); + } + /* this doesn't seem to be necessary -- however, the linux people do it... */ + (void)in_le32(thePic.causes[GPP_IDX]); + + *ppri = pmax; + return rval; +} +#endif + + +/* Here's our dispatcher; the BSP framework uses the same one for EE and decrementer + * exceptions... + */ +int C_dispatch_irq_handler (BSP_Exception_frame *frame, unsigned int excNum) +{ +register int irq; +int loop, last_irq; +rtems_irq_prio pri; +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT +unsigned long diff; +#endif + +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT + diff = mftb(); +#endif + + if (excNum == ASM_DEC_VECTOR) { + decrementerPending = 1; + } + + /* Tradeoff: EITHER we loop as long as interrupts are pending + * incurring the overhead of one extra run of the 'find_pending_irq' routine. + * OR we do rely on the handler just being invoked again if multiple + * interrupts are pending. + * + * The first solution gives better worst-case behavior + * the second slightly better average performance. + * --> we go for the first solution. This also enables us to catch + * runaway interrupts, i.e., bad drivers that don't clear interrupts + * at the device. Can be very handy during driver development... + */ + for ( loop=0, last_irq=-1, pri = thePic.current_priority; + (irq=find_highest_priority_pending_irq(&pri)) >=0; + loop++, last_irq = irq ) { + + /* raise priority level and remember current one */ + pri = change_executing_prio_level(pri); + + SYNC(); + +#if (IRQ_DEBUG) & IRQ_DEBUG_MAXLAT + if ( 0 == loop ) { + diff = mftb()-diff; + if ( diff > discovery_pic_max_dispatching_latency ) + discovery_pic_max_dispatching_latency = diff; + } +#endif + +#if (IRQ_DEBUG) & IRQ_DEBUG_DISPATCHER + if ( BSP_DECREMENTER == irq ) { + printk("IRQ: dispatching DECREMENTER\n"); + } else { + int idx = irqDiv32(irq); + printk("IRQ: dispatching #%i; causes[%i]=0x%08x\n", irq, idx, ld_le32(thePic.causes[idx])); + } +#endif + + bsp_irq_dispatch_list( theConfig.irqHdlTbl, irq, theConfig.defaultEntry.hdl ); + + /* restore executing priority level */ + (void)change_executing_prio_level(pri); + + if ( (loop > MAX_SPIN_LOOPS) && (last_irq == irq) ) { + /* try to catch run-away interrupts without disabling a 'legal' one; + * this should never happen with the decrementer (and + * BSP_disable_irq_at_pic(BSP_DECREMENTER) would fail) + */ + printk("Runaway IRQ #%i; disabling\n", irq); + BSP_disable_irq_at_pic(irq); + loop = 0; + } + } + + if (!loop) { + if ( decrementerPending && pri >= theConfig.irqPrioTbl[BSP_DECREMENTER] ) { + /* we cannot mask the decrementer interrupt so it is possible that it + * gets delivered even though it has a lower priority than what we're + * currently executing at. + * In this case, we ignore the zero loop count and return; + * the interrupted instance of C_dispatch_irq_handler() will eventually + * lower the executing priority and catch the 'decrementerPending' flag + * we just set. + */ + } else { + printk("Discovery: Spurious interrupt; causes were gpp: 0x%x, mhc: 0x%x, mlc: 0x%x\n", gpc, mhc, mlc); + printk("Current priority level %i, decrementerPending %i\n", pri, decrementerPending); + { + rtems_irq_prio p=pri; + printk("PIC register dump:\n"); + discovery_dump_picregs(); + printk("Current Priority: %i, found %i\n",pri,find_highest_priority_pending_irq(&p)); + discovery_dump_picregs(); + for (p=0; p<=BSP_IRQ_MAX_PRIO; p++) { + printk("M[%i] :",p);pmsks(thePic.mcache[p]); + } + } + } + } + else if (loop>discovery_pic_max_loops) + discovery_pic_max_loops = loop; + + return 0; +} + + +#if (IRQ_DEBUG) & MVME6100_IRQ_DEBUG +void +discovery_pic_install_debug_irq(void) +{ + switch ( BSP_getBoardType() ) { + case MVME6100: gpp_out_bit = GPP_WIRED_OUT_BIT_6100; break; + case MVME5500: gpp_out_bit = GPP_WIRED_OUT_BIT_5500; break; + default: + gpp_out_bit = 0; break; + break; + } + if ( gpp_out_bit ) { + unsigned mppoff; + switch (gpp_out_bit / 8) { + default: /* silence warning; this is never reached */ + case 0: mppoff = GT_MPP_Control0; break; + case 1: mppoff = GT_MPP_Control1; break; + case 2: mppoff = GT_MPP_Control2; break; + case 3: mppoff = GT_MPP_Control3; break; + } + + /* switch GPP pin allocated to watchdog (value 4) to + * GPP I/O (value 0 ??; have no doc, found out by experimenting) + */ + gt_bitmod(mppoff, 0, (0xf<<(4*(gpp_out_bit % 8)))); + + /* make it an output */ + gt_bitmod(GT_GPP_IO_Control, (1< GPP_WIRED_IN pins + */ +void +discovery_pic_set_debug_irq(int on) +{ +unsigned long flags, clr; + if ( !gpp_out_bit ) { + printk("discovery_pic_set_debug_irq(): unknown wire output\n"); + return; + } + if (on) { + on = 1< by following + * the STREAM API Specification Document link. + * + * The license and distribution terms for this file may be + * found in found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * Modified by T. Straumann for the beatnik BSP, 2005-2007 + * Some information may be based on mvme5500/irq/irq.h by K. Feng. + */ + +#ifndef LIBBSP_POWERPC_MOT_PPC_NEW_IRQ_IRQ_H +#define LIBBSP_POWERPC_MOT_PPC_NEW_IRQ_IRQ_H + +#define BSP_SHARED_HANDLER_SUPPORT 1 +#include +#include + +/* This BSP also passes a pointer to the interrupt frame to the handler. + * The PPC ABI guarantees that this will not mess up handlers written + * without knowledge of this feature. + */ + +typedef void (*BSP_rtems_irq_hdl)(rtems_irq_hdl_param,BSP_Exception_frame*); + + +/* legal priorities are 0 <= priority <= MAX_PRIO; 0 effectively disables the interrupt */ +#define BSP_IRQ_MAX_PRIO 4 +#define BSP_IRQ_MIN_PRIO 1 + +/* Note that priorites are only honoured for 'PCI' interrupt numbers. + * The discovery pic has no support for hardware priorites; hence they + * are handled in software + */ +#define BSP_IRQ_DEFAULT_PRIORITY 2 + + +#define BSP_PCI_IRQ_LOWEST_OFFSET 0 /* IMPLEMENTATION RELIES ON discovery pic INTERRUPTS HAVING NUMBERS 0..95 */ +#define BSP_IRQ_DEV 1 /* device interface interrupt */ +#define BSP_IRQ_DMA 2 /* DMA addres error interrupt (260) */ +#define BSP_IRQ_CPU 3 /* CPU interface interrupt */ +#define BSP_IRQ_IDMA0_1 4 /* IDMA ch. 0..1 complete interrupt (260) */ +#define BSP_IRQ_IDMA2_3 5 /* IDMA ch. 2..3 complete interrupt (260) */ +#define BSP_IRQ_IDMA4_5 6 /* IDMA ch. 4..5 complete interrupt (260) */ +#define BSP_IRQ_IDMA6_7 7 /* IDMA ch. 6..7 complete interrupt (260) */ +#define BSP_IRQ_TIME0_1 8 /* Timer 0..1 interrupt; Timer 0 on 64360 */ +#define BSP_IRQ_TIME2_3 9 /* Timer 2..3 interrupt; Timer 1 on 64360 */ +#define BSP_IRQ_TIME4_5 10 /* Timer 4..5 interrupt; Timer 2 on 64360 */ +#define BSP_IRQ_TIME6_7 11 /* Timer 6..7 interrupt; Timer 3 on 64360 */ +#define BSP_IRQ_PCI0_0 12 /* PCI 0 interrupt 0 summary (PCI 0 interrupt summary on 64360) */ +#define BSP_IRQ_PCI0_1 13 /* PCI 0 interrupt 1 summary (SRAM PAR ERROR on 64360) */ +#define BSP_IRQ_PCI0_2 14 /* PCI 0 interrupt 2 summary */ +#define BSP_IRQ_PCI0_3 15 /* PCI 0 interrupt 3 summary */ +#define BSP_IRQ_PCI1_0 16 /* PCI 1 interrupt 0 summary (PCI 1 interrupt summary on 64360) */ +#define BSP_IRQ_ECC 17 /* ECC error interrupt */ +#define BSP_IRQ_PCI1_1 18 /* PCI 1 interrupt 1 summary */ +#define BSP_IRQ_PCI1_2 19 /* PCI 1 interrupt 2 summary */ +#define BSP_IRQ_PCI1_3 20 /* PCI 1 interrupt 3 summary */ +#define BSP_IRQ_PCI0OUT_LO 21 /* PCI 0 outbound interrupt summary */ +#define BSP_IRQ_PCI0OUT_HI 22 /* PCI 0 outbound interrupt summary */ +#define BSP_IRQ_PCI1OUT_LO 23 /* PCI 1 outbound interrupt summary */ +#define BSP_IRQ_PCI1OUT_HI 24 /* PCI 1 outbound interrupt summary */ +#define BSP_IRQ_PCI0IN_LO 26 /* PCI 0 inbound interrupt summary */ +#define BSP_IRQ_PCI0IN_HI 27 /* PCI 0 inbound interrupt summary */ +#define BSP_IRQ_PCI1IN_LO 28 /* PCI 1 inbound interrupt summary */ +#define BSP_IRQ_PCI1IN_HI 29 /* PCI 1 inbound interrupt summary */ +#define BSP_IRQ_ETH0 (32+0) /* Ethernet controller 0 interrupt */ +#define BSP_IRQ_ETH1 (32+1) /* Ethernet controller 1 interrupt */ +#define BSP_IRQ_ETH2 (32+2) /* Ethernet controller 2 interrupt */ +#define BSP_IRQ_SDMA (32+4) /* SDMA interrupt */ +#define BSP_IRQ_I2C (32+5) /* I2C interrupt */ +#define BSP_IRQ_BRG (32+7) /* Baud Rate Generator interrupt */ +#define BSP_IRQ_MPSC0 (32+8) /* MPSC 0 interrupt */ +#define BSP_IRQ_MPSC1 (32+10) /* MPSC 1 interrupt */ +#define BSP_IRQ_COMM (32+11) /* Comm unit interrupt */ +#define BSP_IRQ_GPP7_0 (32+24) /* GPP[7..0] interrupt summary */ +#define BSP_IRQ_GPP15_8 (32+25) /* GPP[15..8] interrupt summary */ +#define BSP_IRQ_GPP23_16 (32+26) /* GPP[23..16] interrupt summary */ +#define BSP_IRQ_GPP31_24 (32+27) /* GPP[31..24] interrupt summary */ +#define BSP_IRQ_GPP_0 64 + +#define BSP_PCI_IRQ_NUMBER (64+32) +#define BSP_PCI_IRQ_MAX_OFFSET (BSP_PCI_IRQ_LOWEST_OFFSET + BSP_PCI_IRQ_NUMBER - 1) + +#define BSP_PROCESSOR_IRQ_NUMBER 1 +#define BSP_PROCESSOR_IRQ_LOWEST_OFFSET (BSP_PCI_IRQ_MAX_OFFSET+1) +#define BSP_PROCESSOR_IRQ_MAX_OFFSET (BSP_PROCESSOR_IRQ_LOWEST_OFFSET + BSP_PROCESSOR_IRQ_NUMBER - 1) + +/* summary */ + +#define BSP_IRQ_NUMBER (BSP_PCI_IRQ_NUMBER + BSP_PROCESSOR_IRQ_NUMBER) +#define BSP_LOWEST_OFFSET 0 +#define BSP_MAX_OFFSET (BSP_LOWEST_OFFSET + BSP_IRQ_NUMBER - 1) +#define BSP_DECREMENTER BSP_PROCESSOR_IRQ_LOWEST_OFFSET + +#define BSP_UART_COM1_IRQ BSP_IRQ_GPP_0 +#define BSP_UART_COM2_IRQ BSP_IRQ_GPP_0 + +#ifndef ASM + +#ifdef __cplusplus +extern "C" { +#endif + + +#include + +int BSP_irq_is_enabled_at_pic(rtems_irq_number irq); + +/* set priority of an interrupt; must not be called from ISR level */ +int BSP_irq_set_priority(rtems_irq_number irq, rtems_irq_prio pri); + +/* Not for public use */ +void BSP_rtems_irq_mng_init(unsigned cpuId); + +#ifdef __cplusplus +} +#endif + + +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/irq/irq_init.c b/c/src/lib/libbsp/powerpc/beatnik/irq/irq_init.c new file mode 100644 index 0000000000..29499597f8 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/irq/irq_init.c @@ -0,0 +1,112 @@ +/* irq_init.c + * + * This file contains the implementation of rtems initialization + * related to interrupt handling. + * + * CopyRight (C) 1999 valette@crf.canon.fr + * + * Enhanced by Jay Kulpinski + * to make it valid for MVME2300 Motorola boards. + * + * Modified by T. Straumann for the 'beatnik' BSP. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include +#include +#include +#include +#include + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +/* +#define SHOW_ISA_PCI_BRIDGE_SETTINGS +*/ + +extern unsigned int external_exception_vector_prolog_code_size[]; +extern void external_exception_vector_prolog_code(void); +extern unsigned int decrementer_exception_vector_prolog_code_size[]; +extern void decrementer_exception_vector_prolog_code(void); + +/* + * default handler + */ +static void nop_func(void *arg){} + +static rtems_irq_connect_data rtemsIrq[BSP_IRQ_NUMBER]; +static rtems_irq_global_settings initial_config; +static rtems_irq_prio rtemsPrioTbl[BSP_IRQ_NUMBER]; +static rtems_irq_connect_data defaultIrq = { + name: 0, + hdl: nop_func, + handle: 0, + on: 0, + off: 0, + isOn: 0 +}; + + + /* + * This code assumes the exceptions management setup has already + * been done. We just need to replace the exceptions that will + * be handled like interrupt. On mcp750/mpc750 and many PPC processors + * this means the decrementer exception and the external exception. + */ + +void BSP_rtems_irq_mng_init(unsigned cpuId) +{ +int i; + + /* + * First initialize the Interrupt management hardware + */ + + /* + * Initialize Rtems management interrupt table + */ + + /* + * re-init the rtemsIrq table + */ + for (i = BSP_LOWEST_OFFSET; i <= BSP_MAX_OFFSET; i++) { + rtemsIrq[i] = defaultIrq; + rtemsIrq[i].name = i; + rtemsPrioTbl[i] = BSP_IRQ_DEFAULT_PRIORITY; + } + + /* + * Init initial Interrupt management config + */ + initial_config.irqNb = BSP_IRQ_NUMBER; + initial_config.defaultEntry = defaultIrq; + initial_config.irqHdlTbl = rtemsIrq; + initial_config.irqBase = BSP_LOWEST_OFFSET; + initial_config.irqPrioTbl = rtemsPrioTbl; + + if (!BSP_rtems_irq_mngt_set(&initial_config)) { + /* + * put something here that will show the failure... + */ + BSP_panic("Unable to initialize RTEMS interrupt Management!!! System locked\n"); + } + +#ifdef TRACE_IRQ_INIT + printk("RTEMS IRQ management is now operationnal\n"); +#endif +} + diff --git a/c/src/lib/libbsp/powerpc/beatnik/irq/irq_test_app.c b/c/src/lib/libbsp/powerpc/beatnik/irq/irq_test_app.c new file mode 100644 index 0000000000..2a2bfdfb8a --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/irq/irq_test_app.c @@ -0,0 +1,167 @@ +/* Init + * + * This routine is the initialization task for this test program. + * It is called from init_exec and has the responsibility for creating + * and starting the tasks that make up the test. If the time of day + * clock is required for the test, it should also be set to a known + * value by this function. + * + * Input parameters: NONE + * + * Output parameters: NONE + * + * COPYRIGHT (c) 1989-1999. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * OAR init.c Template modified by T. Straumann who provided + * the implementation of this application. + * + * init.c,v 1.12.4.1 2003/09/04 18:46:30 joel Exp + */ + +#define CONFIGURE_INIT + +#include + +/* functions */ +rtems_task Init( + rtems_task_argument argument +); + +/* configuration information */ +#include /* for device driver prototypes */ +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +#define CONFIGURE_MAXIMUM_TASKS 1 +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE +#define CONFIGURE_USE_MINIIMFS_AS_BASE_FILESYSTEM +#include + +#include +#include + +#include +#include + + +void noop(){} +int connected() {return 1;} + +rtems_irq_connect_data blah = { 0, 0, noop, noop, connected }; +extern void discovery_pic_set_debug_irq(); + +#define WIRE_IRQ (BSP_IRQ_GPP_0 + 6) +#define ABRT_IRQ (BSP_IRQ_GPP_0 + 2) + +static volatile int testno=0; +static volatile int wloops =0; +static volatile int aloops =0; + +void wire_hdl() +{ + if ( 1 == testno ) { + BSP_disable_irq_at_pic(WIRE_IRQ); + } + + if ( 2 == testno || 3 == testno ) { + discovery_pic_set_debug_irq(0); + printk("WIRE -- this message should be printed %s\n", 3==testno ? "FIRST":"SECOND"); + } else { + } + + /* assume the driver checks for less than 1000 loops */ + if ( ++wloops > 1000) { + printk("wire IRQ. FAILURE -- driver couldn't catch runaway ISR. Disabling IRQ source.\n"); + discovery_pic_set_debug_irq(0); + BSP_disable_irq_at_pic(WIRE_IRQ); + } + /* + */ +} + +void abrt_hdl() +{ + aloops++; + if ( 2 == testno || 3 == testno ) { + discovery_pic_set_debug_irq(1); + printk("ABRT -- this message should be printed %s\n", 2==testno ? "FIRST":"SECOND"); + } else + printk("ABRT IRQ\n"); + + if ( 1== testno ) { + BSP_enable_irq_at_pic(WIRE_IRQ); + } + BSP_disable_irq_at_pic(ABRT_IRQ); +} + + +rtems_task Init( + rtems_task_argument ignored +) +{ + blah.name = WIRE_IRQ; + blah.hdl = wire_hdl; + if (!BSP_install_rtems_irq_handler(&blah)) { + fprintf(stderr,"installing handler 1 failed\n"); + } + blah.name = ABRT_IRQ; + blah.hdl = abrt_hdl; + if (!BSP_install_rtems_irq_handler(&blah)) { + fprintf(stderr,"installing handler 2 failed\n"); + } + printf("Hello, testing the ISR dispatcher...\n"); + printf(" 1. Trying to catch runaway interrupt\n"); + printf(" If the system freezes, the test failed!\n"); + fflush(stdout); sleep(1); + aloops = wloops = 0; + discovery_pic_set_debug_irq(1); + printf(" >>> %s\n", wloops<1000 ? "SUCCESS" : "FAILED"); + discovery_pic_set_debug_irq(0); + + testno++; + printf(" 2. Testing enabling / disabling interrupt from ISR\n"); + printf(" Hit the ABORT key for this test\n"); + fflush(stdout); sleep(1); + aloops = wloops = 0; + BSP_disable_irq_at_pic(WIRE_IRQ); + BSP_irq_set_priority(ABRT_IRQ,1); + BSP_enable_irq_at_pic(ABRT_IRQ); + discovery_pic_set_debug_irq(1); + while (!aloops) + ; + discovery_pic_set_debug_irq(0); + sleep(2); + printf(" >>> disabling ABRT IRQ from isr %s\n", 1==aloops ? "SUCCESS":"FAILURE"); + printf(" flashing WIRE IRQ from isr %s\n", 1==wloops ? "SUCCESS":"FAILURE"); + + + testno++; + printf(" 3. Testing interrupt priorities\n"); + BSP_irq_set_priority(ABRT_IRQ,2); + BSP_irq_set_priority(WIRE_IRQ,2); + BSP_enable_irq_at_pic(ABRT_IRQ); + BSP_enable_irq_at_pic(WIRE_IRQ); + printf(" Hit the ABORT key for this test\n"); + fflush(stdout); sleep(1); + aloops = wloops = 0; + while (!aloops) + ; + sleep(2); + testno++; + printf(" Now we are raising the priority of the wire interrupt\n"); + BSP_irq_set_priority(WIRE_IRQ,3); + BSP_enable_irq_at_pic(ABRT_IRQ); + printf(" Hit the ABORT key for this test\n"); + fflush(stdout); sleep(1); + aloops = wloops = 0; + while (!aloops) + ; + + sleep(2); + printf( "That's it; we're done...\n" ); + exit( 0 ); +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/make/custom/beatnik.cfg b/c/src/lib/libbsp/powerpc/beatnik/make/custom/beatnik.cfg new file mode 100644 index 0000000000..2729aefc5d --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/make/custom/beatnik.cfg @@ -0,0 +1,37 @@ +# +# Config file for the PowerPC 745x based mvmexxxx +# +# + +include $(RTEMS_ROOT)/make/custom/default.cfg + +RTEMS_CPU=powerpc +RTEMS_CPU_MODEL=mpc7455 +RTEMS_PPC_EXCEPTION_PROCESSING_MODEL=new + +# This is the actual bsp directory used during the build process. +RTEMS_BSP_FAMILY=beatnik + +# This contains the compiler options necessary to select the CPU model +# and (hopefully) optimize for it. +# +CPU_CFLAGS = -mcpu=7400 -D__ppc_generic +#T. Straumann; disable sdata=eabi for now until CEXP supports it -meabi -msdata=eabi + +# optimize flag: typically -0, could use -O4 or -fast +# -O4 is ok for RTEMS +# NOTE: some level of -O may be actually required by inline assembler +#CFLAGS_OPTIMIZE_V=-O4 -fno-keep-inline-functions +CFLAGS_OPTIMIZE_V = -O2 -g + +# debug flags: typically none, but at least -O1 is required due to this +# BSP using inlined code +CFLAGS_DEBUG_V = -O1 -g + +define bsp-post-link + $(default-bsp-post-link) + $(OBJCOPY) -Obinary $@ $(basename $@)$(DOWNEXT) +endef + +# Miscellaneous additions go here +START_BASE = motld_start diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/discovery.c b/c/src/lib/libbsp/powerpc/beatnik/marvell/discovery.c new file mode 100644 index 0000000000..81bbf7b4e0 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/discovery.c @@ -0,0 +1,151 @@ +/* $Id$ */ + +/* + * Acknowledgements: + * Valuable information was obtained from the following drivers + * netbsd: (C) Allegro Networks Inc; Wasabi Systems Inc. + * linux: (C) MontaVista, Software, Inc; Chris Zankel, Mark A. Greer. + * rtems: (C) Brookhaven National Laboratory; K. Feng + * but this implementation is original work by the author. + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include +#include +#include +#include +#include + +#ifndef PCI_VENDOR_ID_MARVELL +#define PCI_VENDOR_ID_MARVELL 0x11ab +#endif + +#ifndef PCI_DEVICE_ID_MARVELL_GT64260 +#define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 +#endif + +#ifndef PCI_DEVICE_ID_MARVELL_MV64360 +#define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 +#endif + +#if 0 +#define MV64x60_PCI0_CONFIG_ADDR (BSP_MV64x60_BASE + 0xcf8) +#define MV64x60_PCI0_CONFIG_DATA (BSP_MV64x60_BASE + 0xcfc) + +/* read from bus/slot/fn 0/0/0 */ +static unsigned long +pci_early_config_read(int offset, int width) +{ + out_be32((unsigned int*) pci.pci_config_addr, + 0x80|(0<<8)|(PCI_DEVFN(0,0)<<16)|((offset&~3)<<24)); + switch (width) { + default: + case 1: + return in_8((unsigned char*)pci.pci_config_data + (offset&3)); + case 2: + return in_le16((unsigned short*)pci.pci_config_data + (offset&3)); + case 4: + return in_le32((unsigned long *)pci.pci_config_data + (offset&3)); + } +} +#endif + +DiscoveryVersion +BSP_getDiscoveryVersion(int assertion) +{ +static DiscoveryVersion rval = unknown; + + if ( unknown ==rval ) { + unsigned char dc; + unsigned short ds; + /* this must work before and after the call to BSP_pciInitialize() -- + * since the host bridge is at 0,0,0 it doesn't matter if the hosed + * access methods are installed or not (as a matter of fact this shouldn't + * matter for any device on hose 0) + */ +printk("config addr is 0x%08x\n", BSP_pci_configuration.pci_config_addr); +printk("config data is 0x%08x\n", BSP_pci_configuration.pci_config_data); + pci_read_config_word(0,0,0,PCI_VENDOR_ID, &ds); + if ( PCI_VENDOR_ID_MARVELL != ds ) { + if ( assertion ) { + printk("Host bridge vendor id: 0x%04x\n",ds); + BSP_panic("Host bridge vendor @ pci(0,0,0) is not MARVELL"); + } + else return unknown; + } + pci_read_config_word(0,0,0,PCI_DEVICE_ID, &ds); + pci_read_config_byte(0,0,0,PCI_REVISION_ID, &dc); + switch (ds) { + case PCI_DEVICE_ID_MARVELL_MV64360: + rval = MV_64360; + break; + + case PCI_DEVICE_ID_MARVELL_GT64260: + switch (dc) { + default: + break; + + case 0x10: + return (rval = GT_64260_A); + + case 0x20: + return (rval = GT_64260_B); + } + + default: + if ( assertion ) { + printk("Marvell device id 0x%04x, revision 0x%02x; check %s:%u\n", + ds, dc, + __FILE__,__LINE__); + BSP_panic("Unknown Marvell bridge or revision@ pci(0,0,0) is not MARVELL"); + } + break; + } + } + + return rval; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gt.c b/c/src/lib/libbsp/powerpc/beatnik/marvell/gt.c new file mode 100644 index 0000000000..c71b7cbdb3 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gt.c @@ -0,0 +1,1010 @@ +/* $NetBSD: gt.c,v 1.5 2003/07/14 15:47:16 lukem Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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. + */ + +/* + * gt.c -- GT system controller driver + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: gt.c,v 1.5 2003/07/14 15:47:16 lukem Exp $"); + +#include "opt_marvell.h" + +#include +#include +#include +#include +#include +#include +#include + +#define _BUS_SPACE_PRIVATE +#define _BUS_DMA_PRIVATE +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef DEBUG +#include /* for Debugger() */ +#endif + +#if ((GT_MPP_WATCHDOG & 0xf0f0f0f0) != 0) +# error /* unqualified: configuration botch! */ +#endif +#if ((GT_MPP_WATCHDOG & GT_MPP_INTERRUPTS) != 0) +# error /* conflict: configuration botch! */ +#endif + +static void gt_comm_intr_enb(struct gt_softc *); +static void gt_devbus_intr_enb(struct gt_softc *); +#ifdef GT_ECC +static void gt_ecc_intr_enb(struct gt_softc *); +#endif + +void gt_init_hostid (struct gt_softc *); +void gt_init_interrupt (struct gt_softc *); +static int gt_comm_intr (void *); + +void gt_watchdog_init(struct gt_softc *); +void gt_watchdog_enable(void); +void gt_watchdog_disable(void); +void gt_watchdog_reset(void); + +extern struct cfdriver gt_cd; + +static int gtfound = 0; + +static struct gt_softc *gt_watchdog_sc = 0; +static int gt_watchdog_state = 0; + +int +gt_cfprint (void *aux, const char *pnp) +{ + struct gt_attach_args *ga = aux; + + if (pnp) { + aprint_normal("%s at %s", ga->ga_name, pnp); + } + + aprint_normal(" unit %d", ga->ga_unit); + return (UNCONF); +} + + +static int +gt_cfsearch(struct device *parent, struct cfdata *cf, void *aux) +{ + struct gt_softc *gt = (struct gt_softc *) parent; + struct gt_attach_args ga; + + ga.ga_name = cf->cf_name; + ga.ga_dmat = gt->gt_dmat; + ga.ga_memt = gt->gt_memt; + ga.ga_memh = gt->gt_memh; + ga.ga_unit = cf->cf_loc[GTCF_UNIT]; + + if (config_match(parent, cf, &ga) > 0) + config_attach(parent, cf, &ga, gt_cfprint); + + return (0); +} + +void +gt_attach_common(struct gt_softc *gt) +{ + uint32_t cpucfg, cpumode, cpumstr; +#ifdef DEBUG + uint32_t loaddr, hiaddr; +#endif + + gtfound = 1; + + cpumode = gt_read(gt, GT_CPU_Mode); + aprint_normal(": id %d", GT_CPUMode_MultiGTID_GET(cpumode)); + if (cpumode & GT_CPUMode_MultiGT) + aprint_normal (" (multi)"); + switch (GT_CPUMode_CPUType_GET(cpumode)) { + case 4: aprint_normal(", 60x bus"); break; + case 5: aprint_normal(", MPX bus"); break; + default: aprint_normal(", %#x(?) bus", GT_CPUMode_CPUType_GET(cpumode)); break; + } + + cpumstr = gt_read(gt, GT_CPU_Master_Ctl); + cpumstr &= ~(GT_CPUMstrCtl_CleanBlock|GT_CPUMstrCtl_FlushBlock); +#if 0 + cpumstr |= GT_CPUMstrCtl_CleanBlock|GT_CPUMstrCtl_FlushBlock; +#endif + gt_write(gt, GT_CPU_Master_Ctl, cpumstr); + + switch (cpumstr & (GT_CPUMstrCtl_CleanBlock|GT_CPUMstrCtl_FlushBlock)) { + case 0: break; + case GT_CPUMstrCtl_CleanBlock: aprint_normal(", snoop=clean"); break; + case GT_CPUMstrCtl_FlushBlock: aprint_normal(", snoop=flush"); break; + case GT_CPUMstrCtl_CleanBlock|GT_CPUMstrCtl_FlushBlock: + aprint_normal(", snoop=clean&flush"); break; + } + aprint_normal(" wdog=%#x,%#x\n", + gt_read(gt, GT_WDOG_Config), + gt_read(gt, GT_WDOG_Value)); + +#if DEBUG + loaddr = GT_LowAddr_GET(gt_read(gt, GT_SCS0_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_SCS0_High_Decode)); + aprint_normal("%s: scs[0]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_SCS1_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_SCS1_High_Decode)); + aprint_normal("%s: scs[1]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_SCS2_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_SCS2_High_Decode)); + aprint_normal("%s: scs[2]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_SCS3_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_SCS3_High_Decode)); + aprint_normal("%s: scs[3]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_CS0_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_CS0_High_Decode)); + aprint_normal("%s: cs[0]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_CS1_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_CS1_High_Decode)); + aprint_normal("%s: cs[1]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_CS2_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_CS2_High_Decode)); + aprint_normal("%s: cs[2]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_CS3_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_CS3_High_Decode)); + aprint_normal("%s: cs[3]=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_BootCS_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_BootCS_High_Decode)); + aprint_normal("%s: bootcs=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI0_IO_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI0_IO_High_Decode)); + aprint_normal("%s: pci0io=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI0_IO_Remap); + aprint_normal("remap=%#010x\n", loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI0_Mem0_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI0_Mem0_High_Decode)); + aprint_normal("%s: pci0mem[0]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI0_Mem0_Remap_Low); + hiaddr = gt_read(gt, GT_PCI0_Mem0_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI0_Mem1_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI0_Mem1_High_Decode)); + aprint_normal("%s: pci0mem[1]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI0_Mem1_Remap_Low); + hiaddr = gt_read(gt, GT_PCI0_Mem1_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI0_Mem2_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI0_Mem2_High_Decode)); + aprint_normal("%s: pci0mem[2]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI0_Mem2_Remap_Low); + hiaddr = gt_read(gt, GT_PCI0_Mem2_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI0_Mem3_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI0_Mem3_High_Decode)); + aprint_normal("%s: pci0mem[3]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI0_Mem3_Remap_Low); + hiaddr = gt_read(gt, GT_PCI0_Mem3_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI1_IO_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI1_IO_High_Decode)); + aprint_normal("%s: pci1io=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI1_IO_Remap); + aprint_normal("remap=%#010x\n", loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI1_Mem0_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI1_Mem0_High_Decode)); + aprint_normal("%s: pci1mem[0]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI1_Mem0_Remap_Low); + hiaddr = gt_read(gt, GT_PCI1_Mem0_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI1_Mem1_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI1_Mem1_High_Decode)); + aprint_normal("%s: pci1mem[1]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI1_Mem1_Remap_Low); + hiaddr = gt_read(gt, GT_PCI1_Mem1_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI1_Mem2_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI1_Mem2_High_Decode)); + aprint_normal("%s: pci1mem[2]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI1_Mem2_Remap_Low); + hiaddr = gt_read(gt, GT_PCI1_Mem2_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_PCI1_Mem3_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_PCI1_Mem3_High_Decode)); + aprint_normal("%s: pci1mem[3]=%#10x-%#10x ", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = gt_read(gt, GT_PCI1_Mem3_Remap_Low); + hiaddr = gt_read(gt, GT_PCI1_Mem3_Remap_High); + aprint_normal("remap=%#010x.%#010x\n", hiaddr, loaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_Internal_Decode)); + aprint_normal("%s: internal=%#10x-%#10x\n", gt->gt_dev.dv_xname, + loaddr, loaddr+256*1024); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_CPU0_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_CPU0_High_Decode)); + aprint_normal("%s: cpu0=%#10x-%#10x\n", gt->gt_dev.dv_xname, loaddr, hiaddr); + + loaddr = GT_LowAddr_GET(gt_read(gt, GT_CPU1_Low_Decode)); + hiaddr = GT_HighAddr_GET(gt_read(gt, GT_CPU1_High_Decode)); + aprint_normal("%s: cpu1=%#10x-%#10x", gt->gt_dev.dv_xname, loaddr, hiaddr); +#endif + + aprint_normal("%s:", gt->gt_dev.dv_xname); + + cpucfg = gt_read(gt, GT_CPU_Cfg); + cpucfg |= GT_CPUCfg_ConfSBDis; /* per errata #46 */ + cpucfg |= GT_CPUCfg_AACKDelay; /* per restriction #18 */ + gt_write(gt, GT_CPU_Cfg, cpucfg); + if (cpucfg & GT_CPUCfg_Pipeline) + aprint_normal(" pipeline"); + if (cpucfg & GT_CPUCfg_AACKDelay) + aprint_normal(" aack-delay"); + if (cpucfg & GT_CPUCfg_RdOOO) + aprint_normal(" read-ooo"); + if (cpucfg & GT_CPUCfg_IOSBDis) + aprint_normal(" io-sb-dis"); + if (cpucfg & GT_CPUCfg_ConfSBDis) + aprint_normal(" conf-sb-dis"); + if (cpucfg & GT_CPUCfg_ClkSync) + aprint_normal(" clk-sync"); + aprint_normal("\n"); + + gt_init_hostid(gt); + + gt_watchdog_init(gt); + + gt_init_interrupt(gt); + +#ifdef GT_ECC + gt_ecc_intr_enb(gt); +#endif + + gt_comm_intr_enb(gt); + gt_devbus_intr_enb(gt); + + gt_watchdog_disable(); + config_search(gt_cfsearch, >->gt_dev, NULL); + gt_watchdog_service(); + gt_watchdog_enable(); +} + +void +gt_init_hostid(struct gt_softc *gt) +{ + + hostid = 1; /* XXX: Used by i2c; needs work -- AKB */ +} + +void +gt_init_interrupt(struct gt_softc *gt) +{ + u_int32_t mppirpts = GT_MPP_INTERRUPTS; /* from config */ + u_int32_t r; + u_int32_t mppbit; + u_int32_t mask; + u_int32_t mppsel; + u_int32_t regoff; + + gt_write(gt, ICR_CIM_LO, 0); + gt_write(gt, ICR_CIM_HI, 0); + + /* + * configure the GPP interrupts: + * - set the configured MPP pins in GPP mode + * - set the configured GPP pins to input, active low, interrupt enbl + */ +#ifdef DEBUG + printf("%s: mpp cfg ", gt->gt_dev.dv_xname); + for (regoff = GT_MPP_Control0; regoff <= GT_MPP_Control3; regoff += 4) + printf("%#x ", gt_read(gt, regoff)); + printf(", mppirpts 0x%x\n", mppirpts); +#endif + mppbit = 0x1; + for (regoff = GT_MPP_Control0; regoff <= GT_MPP_Control3; regoff += 4) { + mask = 0; + for (mppsel = 0xf; mppsel; mppsel <<= 4) { + if (mppirpts & mppbit) + mask |= mppsel; + mppbit <<= 1; + } + if (mask) { + r = gt_read(gt, regoff); + r &= ~mask; + gt_write(gt, regoff, r); + } + } + + r = gt_read(gt, GT_GPP_IO_Control); + r &= ~mppirpts; + gt_write(gt, GT_GPP_IO_Control, r); + + r = gt_read(gt, GT_GPP_Level_Control); + r |= mppirpts; + gt_write(gt, GT_GPP_Level_Control, r); + + r = gt_read(gt, GT_GPP_Interrupt_Mask); + r |= mppirpts; + gt_write(gt, GT_GPP_Interrupt_Mask, r); +} + +uint32_t +gt_read_mpp (void) +{ + return gt_read((struct gt_softc *)gt_cd.cd_devs[0], GT_GPP_Value); +} + +#if 0 +int +gt_bs_extent_init(struct discovery_bus_space *bs, char *name) +{ + u_long start, end; + int i, j, error; + + if (bs->bs_nregion == 0) { + bs->bs_extent = extent_create(name, 0xffffffffUL, 0xffffffffUL, + M_DEVBUF, NULL, 0, EX_NOCOALESCE|EX_WAITOK); + KASSERT(bs->bs_extent != NULL); + return 0; + } + /* + * Find the top and bottoms of this bus space. + */ + start = bs->bs_regions[0].br_start; + end = bs->bs_regions[0].br_end; +#ifdef DEBUG + if (gtpci_debug > 1) + printf("gtpci_bs_extent_init: %s: region %d: %#lx-%#lx\n", + name, 0, bs->bs_regions[0].br_start, + bs->bs_regions[0].br_end); +#endif + for (i = 1; i < bs->bs_nregion; i++) { + if (bs->bs_regions[i].br_start < start) + start = bs->bs_regions[i].br_start; + if (bs->bs_regions[i].br_end > end) + end = bs->bs_regions[i].br_end; +#ifdef DEBUG + if (gtpci_debug > 1) + printf("gtpci_bs_extent_init: %s: region %d:" + " %#lx-%#lx\n", + name, i, bs->bs_regions[i].br_start, + bs->bs_regions[i].br_end); +#endif + } + /* + * Now that we have the top and bottom limits of this + * bus space, create the extent map that will manage this + * space for us. + */ +#ifdef DEBUG + if (gtpci_debug > 1) + printf("gtpci_bs_extent_init: %s: create: %#lx-%#lx\n", + name, start, end); +#endif + bs->bs_extent = extent_create(name, start, end, M_DEVBUF, + NULL, 0, EX_NOCOALESCE|EX_WAITOK); + KASSERT(bs->bs_extent != NULL); + + /* If there was more than one bus space region, then there + * might gaps in between them. Allocate the gap so that + * they will not be legal addresses in the extent. + */ + for (i = 0; i < bs->bs_nregion && bs->bs_nregion > 1; i++) { + /* Initial start is "infinity" and the inital end is + * is the end of this bus region. + */ + start = ~0UL; + end = bs->bs_regions[i].br_end; + /* For each region, if it starts after this region but less + * than the saved start, use its start address. If the start + * address is one past the end address, then we're done + */ + for (j = 0; j < bs->bs_nregion && start > end + 1; j++) { + if (i == j) + continue; + if (bs->bs_regions[j].br_start > end && + bs->bs_regions[j].br_start < start) + start = bs->bs_regions[j].br_start; + } + /* + * If we found a gap, allocate it away. + */ + if (start != ~0UL && start != end + 1) { +#ifdef DEBUG + if (gtpci_debug > 1) + printf("gtpci_bs_extent_init: %s: alloc(hole): %#lx-%#lx\n", + name, end + 1, start - 1); +#endif + error = extent_alloc_region(bs->bs_extent, end + 1, + start - (end + 1), EX_NOWAIT); + KASSERT(error == 0); + } + } + return 1; +} +#endif + +/* + * unknown board, enable everything + */ +# define GT_CommUnitIntr_DFLT GT_CommUnitIntr_S0|GT_CommUnitIntr_S1 \ + |GT_CommUnitIntr_E0|GT_CommUnitIntr_E1 \ + |GT_CommUnitIntr_E2 + +static const char * const gt_comm_subunit_name[8] = { + "ethernet 0", + "ethernet 1", + "ethernet 2", + "(reserved)", + "MPSC 0", + "MPSC 1", + "(reserved)", + "(sel)", +}; + +static int +gt_comm_intr(void *arg) +{ + struct gt_softc *gt = (struct gt_softc *)arg; + u_int32_t cause; + u_int32_t addr; + unsigned int mask; + int i; + + cause = gt_read(gt, GT_CommUnitIntr_Cause); + gt_write(gt, GT_CommUnitIntr_Cause, ~cause); + addr = gt_read(gt, GT_CommUnitIntr_ErrAddr); + + printf("%s: Comm Unit irpt, cause %#x addr %#x\n", + gt->gt_dev.dv_xname, cause, addr); + + cause &= GT_CommUnitIntr_DFLT; + if (cause == 0) + return 0; + + mask = 0x7; + for (i=0; i<7; i++) { + if (cause & mask) { + printf("%s: Comm Unit %s:", gt->gt_dev.dv_xname, + gt_comm_subunit_name[i]); + if (cause & 1) + printf(" AddrMiss"); + if (cause & 2) + printf(" AccProt"); + if (cause & 4) + printf(" WrProt"); + printf("\n"); + } + cause >>= 4; + } + return 1; +} + +/* + * gt_comm_intr_init - enable GT-64260 Comm Unit interrupts + */ +static void +gt_comm_intr_enb(struct gt_softc *gt) +{ + u_int32_t cause; + + cause = gt_read(gt, GT_CommUnitIntr_Cause); + if (cause) + gt_write(gt, GT_CommUnitIntr_Cause, ~cause); + gt_write(gt, GT_CommUnitIntr_Mask, GT_CommUnitIntr_DFLT); + (void)gt_read(gt, GT_CommUnitIntr_ErrAddr); + + intr_establish(IRQ_COMM, IST_LEVEL, IPL_GTERR, gt_comm_intr, gt); + printf("%s: Comm Unit irpt at %d\n", gt->gt_dev.dv_xname, IRQ_COMM); +} + +#ifdef GT_ECC +static char *gt_ecc_intr_str[4] = { + "(none)", + "single bit", + "double bit", + "(reserved)" +}; + +static int +gt_ecc_intr(void *arg) +{ + struct gt_softc *gt = (struct gt_softc *)arg; + u_int32_t addr; + u_int32_t dlo; + u_int32_t dhi; + u_int32_t rec; + u_int32_t calc; + u_int32_t count; + int err; + + count = gt_read(gt, GT_ECC_Count); + dlo = gt_read(gt, GT_ECC_Data_Lo); + dhi = gt_read(gt, GT_ECC_Data_Hi); + rec = gt_read(gt, GT_ECC_Rec); + calc = gt_read(gt, GT_ECC_Calc); + addr = gt_read(gt, GT_ECC_Addr); /* read last! */ + gt_write(gt, GT_ECC_Addr, 0); /* clear irpt */ + + err = addr & 0x3; + + printf("%s: ECC error: %s: " + "addr %#x data %#x.%#x rec %#x calc %#x cnt %#x\n", + gt->gt_dev.dv_xname, gt_ecc_intr_str[err], + addr, dhi, dlo, rec, calc, count); + + if (err == 2) + panic("ecc"); + + return (err == 1); +} + +/* + * gt_ecc_intr_enb - enable GT-64260 ECC interrupts + */ +static void +gt_ecc_intr_enb(struct gt_softc *gt) +{ + u_int32_t ctl; + + ctl = gt_read(gt, GT_ECC_Ctl); + ctl |= 1 << 16; /* XXX 1-bit threshold == 1 */ + gt_write(gt, GT_ECC_Ctl, ctl); + (void)gt_read(gt, GT_ECC_Data_Lo); + (void)gt_read(gt, GT_ECC_Data_Hi); + (void)gt_read(gt, GT_ECC_Rec); + (void)gt_read(gt, GT_ECC_Calc); + (void)gt_read(gt, GT_ECC_Addr); /* read last! */ + gt_write(gt, GT_ECC_Addr, 0); /* clear irpt */ + + intr_establish(IRQ_ECC, IST_LEVEL, IPL_GTERR, gt_ecc_intr, gt); + printf("%s: ECC irpt at %d\n", gt->gt_dev.dv_xname, IRQ_ECC); +} +#endif /* GT_ECC */ + + +#ifndef GT_MPP_WATCHDOG +void +gt_watchdog_init(struct gt_softc *gt) +{ + u_int32_t r; + unsigned int omsr; + + omsr = extintr_disable(); + + printf("%s: watchdog", gt->gt_dev.dv_xname); + + /* + * handle case where firmware started watchdog + */ + r = gt_read(gt, GT_WDOG_Config); + printf(" status %#x,%#x:", + r, gt_read(gt, GT_WDOG_Value)); + if ((r & 0x80000000) != 0) { + gt_watchdog_sc = gt; /* enabled */ + gt_watchdog_state = 1; + printf(" firmware-enabled\n"); + gt_watchdog_service(); + return; + } else { + printf(" firmware-disabled\n"); + } + + extintr_restore(omsr); +} + +#else /* GT_MPP_WATCHDOG */ + +void +gt_watchdog_init(struct gt_softc *gt) +{ + u_int32_t mpp_watchdog = GT_MPP_WATCHDOG; /* from config */ + u_int32_t r; + u_int32_t cfgbits; + u_int32_t mppbits; + u_int32_t mppmask=0; + u_int32_t regoff; + unsigned int omsr; + + printf("%s: watchdog", gt->gt_dev.dv_xname); + + if (mpp_watchdog == 0) { + printf(" not configured\n"); + return; + } + +#if 0 + if (afw_wdog_ctl == 1) { + printf(" admin disabled\n"); + return; + } +#endif + + omsr = extintr_disable(); + + /* + * if firmware started watchdog, we disable and start + * from scratch to get it in a known state. + * + * on GT-64260A we always see 0xffffffff + * in both the GT_WDOG_Config_Enb and GT_WDOG_Value regsiters. + * Use AFW-supplied flag to determine run state. + */ + r = gt_read(gt, GT_WDOG_Config); + if (r != ~0) { + if ((r & GT_WDOG_Config_Enb) != 0) { + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1a | GT_WDOG_Preset_DFLT)); + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1b | GT_WDOG_Preset_DFLT)); + } + } else { +#if 0 + if (afw_wdog_state == 1) { + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1a | GT_WDOG_Preset_DFLT)); + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1b | GT_WDOG_Preset_DFLT)); + } +#endif + } + + /* + * "the watchdog timer can be activated only after + * configuring two MPP pins to act as WDE and WDNMI" + */ + mppbits = 0; + cfgbits = 0x3; + for (regoff = GT_MPP_Control0; regoff <= GT_MPP_Control3; regoff += 4) { + if ((mpp_watchdog & cfgbits) == cfgbits) { + mppbits = 0x99; + mppmask = 0xff; + break; + } + cfgbits <<= 2; + if ((mpp_watchdog & cfgbits) == cfgbits) { + mppbits = 0x9900; + mppmask = 0xff00; + break; + } + cfgbits <<= 6; /* skip unqualified bits */ + } + if (mppbits == 0) { + printf(" config error\n"); + extintr_restore(omsr); + return; + } + + r = gt_read(gt, regoff); + r &= ~mppmask; + r |= mppbits; + gt_write(gt, regoff, r); + printf(" mpp %#x %#x", regoff, mppbits); + + gt_write(gt, GT_WDOG_Value, GT_WDOG_NMI_DFLT); + + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1a | GT_WDOG_Preset_DFLT)); + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1b | GT_WDOG_Preset_DFLT)); + + + r = gt_read(gt, GT_WDOG_Config), + printf(" status %#x,%#x: %s", + r, gt_read(gt, GT_WDOG_Value), + ((r & GT_WDOG_Config_Enb) != 0) ? "enabled" : "botch"); + + if ((r & GT_WDOG_Config_Enb) != 0) { + register_t hid0; + + gt_watchdog_sc = gt; /* enabled */ + gt_watchdog_state = 1; + + /* + * configure EMCP in HID0 in case it's not already set + */ + __asm __volatile("sync":::"memory"); + hid0 = mfspr(SPR_HID0); + if ((hid0 & HID0_EMCP) == 0) { + hid0 |= HID0_EMCP; + __asm __volatile("sync":::"memory"); mtspr(SPR_HID0, hid0); + __asm __volatile("sync":::"memory"); hid0 = mfspr(SPR_HID0); + printf(", EMCP set"); + } + } + printf("\n"); + + extintr_restore(omsr); +} +#endif /* GT_MPP_WATCHDOG */ + +#ifdef DEBUG +u_int32_t hid0_print(void); +u_int32_t +hid0_print() +{ + u_int32_t hid0; + __asm __volatile("sync; mfspr %0,1008;" : "=r"(hid0)::"memory"); + printf("hid0: %#x\n", hid0); + return hid0; +} +#endif + +void +gt_watchdog_enable(void) +{ + struct gt_softc *gt; + unsigned int omsr; + + omsr = extintr_disable(); + gt = gt_watchdog_sc; + if ((gt != NULL) && (gt_watchdog_state == 0)) { + gt_watchdog_state = 1; + + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1a | GT_WDOG_Preset_DFLT)); + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1b | GT_WDOG_Preset_DFLT)); + } + extintr_restore(omsr); +} + +void +gt_watchdog_disable(void) +{ + struct gt_softc *gt; + unsigned int omsr; + + omsr = extintr_disable(); + gt = gt_watchdog_sc; + if ((gt != NULL) && (gt_watchdog_state != 0)) { + gt_watchdog_state = 0; + + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1a | GT_WDOG_Preset_DFLT)); + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1b | GT_WDOG_Preset_DFLT)); + } + extintr_restore(omsr); +} + +#ifdef DEBUG +int inhibit_watchdog_service = 0; +#endif +void +gt_watchdog_service(void) +{ + struct gt_softc *gt = gt_watchdog_sc; + + if ((gt == NULL) || (gt_watchdog_state == 0)) + return; /* not enabled */ +#ifdef DEBUG + if (inhibit_watchdog_service) + return; +#endif + + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl2a | GT_WDOG_Preset_DFLT)); + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl2b | GT_WDOG_Preset_DFLT)); +} + +/* + * gt_watchdog_reset - force a watchdog reset using Preset_VAL=0 + */ +void +gt_watchdog_reset() +{ + struct gt_softc *gt = gt_watchdog_sc; + u_int32_t r; + + (void)extintr_disable(); + r = gt_read(gt, GT_WDOG_Config); + gt_write(gt, GT_WDOG_Config, (GT_WDOG_Config_Ctl1a | 0)); + gt_write(gt, GT_WDOG_Config, (GT_WDOG_Config_Ctl1b | 0)); + if ((r & GT_WDOG_Config_Enb) != 0) { + /* + * was enabled, we just toggled it off, toggle on again + */ + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1a | 0)); + gt_write(gt, GT_WDOG_Config, + (GT_WDOG_Config_Ctl1b | 0)); + } + for(;;); +} + +static int +gt_devbus_intr(void *arg) +{ + struct gt_softc *gt = (struct gt_softc *)arg; + u_int32_t cause; + u_int32_t addr; + + cause = gt_read(gt, GT_DEVBUS_ICAUSE); + addr = gt_read(gt, GT_DEVBUS_ERR_ADDR); + gt_write(gt, GT_DEVBUS_ICAUSE, 0); /* clear irpt */ + + if (cause & GT_DEVBUS_DBurstErr) { + printf("%s: Device Bus error: burst violation", + gt->gt_dev.dv_xname); + if ((cause & GT_DEVBUS_Sel) == 0) + printf(", addr %#x", addr); + printf("\n"); + } + if (cause & GT_DEVBUS_DRdyErr) { + printf("%s: Device Bus error: ready timer expired", + gt->gt_dev.dv_xname); + if ((cause & GT_DEVBUS_Sel) != 0) + printf(", addr %#x\n", addr); + printf("\n"); + } + + return (cause != 0); +} + +/* + * gt_ecc_intr_enb - enable GT-64260 ECC interrupts + */ +static void +gt_devbus_intr_enb(struct gt_softc *gt) +{ + gt_write(gt, GT_DEVBUS_IMASK, + GT_DEVBUS_DBurstErr|GT_DEVBUS_DRdyErr); + (void)gt_read(gt, GT_DEVBUS_ERR_ADDR); /* clear addr */ + gt_write(gt, GT_ECC_Addr, 0); /* clear irpt */ + + intr_establish(IRQ_DEV, IST_LEVEL, IPL_GTERR, gt_devbus_intr, gt); + printf("%s: Device Bus Error irpt at %d\n", + gt->gt_dev.dv_xname, IRQ_DEV); +} + + +int +gt_mii_read( + struct device *child, + struct device *parent, + int phy, + int reg) +{ + struct gt_softc * const gt = (struct gt_softc *) parent; + uint32_t data; + int count = 10000; + + do { + DELAY(10); + data = gt_read(gt, ETH_ESMIR); + } while ((data & ETH_ESMIR_Busy) && count-- > 0); + + if (count == 0) { + printf("%s: mii read for phy %d reg %d busied out\n", + child->dv_xname, phy, reg); + return ETH_ESMIR_Value_GET(data); + } + + gt_write(gt, ETH_ESMIR, ETH_ESMIR_READ(phy, reg)); + + count = 10000; + do { + DELAY(10); + data = gt_read(gt, ETH_ESMIR); + } while ((data & ETH_ESMIR_ReadValid) == 0 && count-- > 0); + + if (count == 0) + printf("%s: mii read for phy %d reg %d timed out\n", + child->dv_xname, phy, reg); +#if defined(GTMIIDEBUG) + printf("%s: mii_read(%d, %d): %#x data %#x\n", + child->dv_xname, phy, reg, + data, ETH_ESMIR_Value_GET(data)); +#endif + return ETH_ESMIR_Value_GET(data); +} + +void +gt_mii_write ( + struct device *child, + struct device *parent, + int phy, int reg, + int value) +{ + struct gt_softc * const gt = (struct gt_softc *) parent; + uint32_t data; + int count = 10000; + + do { + DELAY(10); + data = gt_read(gt, ETH_ESMIR); + } while ((data & ETH_ESMIR_Busy) && count-- > 0); + + if (count == 0) { + printf("%s: mii write for phy %d reg %d busied out (busy)\n", + child->dv_xname, phy, reg); + return; + } + + gt_write(gt, ETH_ESMIR, + ETH_ESMIR_WRITE(phy, reg, value)); + + count = 10000; + do { + DELAY(10); + data = gt_read(gt, ETH_ESMIR); + } while ((data & ETH_ESMIR_Busy) && count-- > 0); + + if (count == 0) + printf("%s: mii write for phy %d reg %d timed out\n", + child->dv_xname, phy, reg); +#if defined(GTMIIDEBUG) + printf("%s: mii_write(%d, %d, %#x)\n", + child->dv_xname, phy, reg, value); +#endif +} + diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.c b/c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.c new file mode 100644 index 0000000000..8cafdebf03 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.c @@ -0,0 +1,412 @@ +/* $Id$ */ + +/* Driver for discovery timers and watchdog */ + +/* + * Acknowledgements: + * Valuable information was obtained from the following drivers + * netbsd: (C) Allegro Networks Inc; Wasabi Systems Inc. + * linux: (C) MontaVista, Software, Inc; Mark A. Greer. + * rtems: (C) Brookhaven National Laboratory; K. Feng + * but this implementation is original work by the author. + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "gt_timer.h" + +#define DEBUG + +static inline uint32_t gt_rd(uint32_t off) +{ + return in_le32( (volatile unsigned *)(BSP_MV64x60_BASE+off) ); +} + +static inline void gt_wr(uint32_t off, uint32_t val) +{ + out_le32( (volatile unsigned *)(BSP_MV64x60_BASE+off), val); +} + +static inline uint32_t gt_timer_bitmod(uint32_t off, uint32_t clr, uint32_t set) +{ + unsigned flags; + uint32_t rval; + rtems_interrupt_disable(flags); + rval = gt_rd( off ); + gt_wr( off, (rval & ~clr) | set ); + rtems_interrupt_enable(flags); + return rval; +} + +#define GT_TIMER_MAX 3 +#define TIMER_ARGCHECK(t) do { if ((t)<0 || (t)>GT_TIMER_MAX) return -1; } while (0) + +static struct { + void (*isr)(void *); + void *arg; +} gt_timer_isrs[GT_TIMER_MAX+1] = {{0},}; + +uint32_t BSP_timer_read(uint32_t timer) +{ + TIMER_ARGCHECK(timer); + return gt_rd(GT_TIMER_0 + (timer<<2)); +} + +int +BSP_timer_start(uint32_t timer, uint32_t period) +{ + TIMER_ARGCHECK(timer); + gt_wr(GT_TIMER_0 + (timer<<2), period); + return 0; +} + +int +BSP_timer_stop(uint32_t timer) +{ + TIMER_ARGCHECK(timer); + /* disable, clear period, re-enable */ + gt_timer_bitmod(GT_TIMER_0_3_Ctl, GT_TIMER_0_Ctl_Enb << (timer<<3), 0); + gt_wr(GT_TIMER_0 + (timer<<2), 0); + gt_timer_bitmod(GT_TIMER_0_3_Ctl, 0, GT_TIMER_0_Ctl_Enb << (timer<<3)); + return 0; +} + +int +BSP_timer_setup(uint32_t timer, void (*isr)(void *arg), void *arg, int reload) +{ + TIMER_ARGCHECK(timer); + if ( isr && gt_timer_isrs[timer].isr ) + return -1; + BSP_timer_stop(timer); + /* mask and clear */ + gt_timer_bitmod(GT_TIMER_0_3_Intr_Msk, GT_TIMER_0_Intr<>= 4 ) { + timer = (iarg & 0xf)-1; + bit = GT_TIMER_0_Intr<=0; i--, arg-=ainc ) { + xx.name = BSP_IRQ_TIME0_1 + i; + xx.handle = (rtems_irq_hdl_param)arg; + if ( !BSP_install_rtems_irq_handler(&xx) ) + return -1; + } + + return 0; +} + +int +BSP_timers_uninstall(void) +{ +rtems_irq_connect_data xx = {0}; +int i; + + xx.hdl = gt_timer_hdl; + xx.on = 0; + xx.off = 0; + xx.isOn = 0; + + for ( i=0; i<= GT_TIMER_MAX; i++ ) { + if ( BSP_timer_setup(i, 0, 0, 0) ) + return -1; + } + + switch (BSP_getDiscoveryVersion(0)) { + case MV_64360: + i = 3; + break; + default: + i = 1; + break; + } + + for ( ; i >= 0; i-- ) { + xx.name = BSP_IRQ_TIME0_1 + i; + BSP_get_current_rtems_irq_handler(&xx); + if ( !BSP_remove_rtems_irq_handler(&xx) ) + return -1; + } + + return 0; +} + +uint32_t +BSP_timer_clock_get(uint32_t timer) +{ + return BSP_bus_frequency; +} + +int BSP_timer_instances(void) +{ + return GT_TIMER_MAX + 1; +} + +#ifdef DEBUG +void BSP_timer_test_isr(void *arg) +{ + printk("TIMER IRQ (user arg 0x%x)\n",arg); +} +#endif + +/* On a 64260A we can't read the status (on/off), apparently + * so we maintain it locally and assume the firmware has + * not enabled the dog initially... + */ +static uint32_t wdog_on = 0x00ffffff; + +static uint32_t rd_wdcnf(void) +{ + uint32_t cnf = gt_rd(GT_WDOG_Config); + /* BSD driver says that on the 64260A we always + * read 0xffffffff so we have to maintain the + * status locally (and hope we get the initial + * value right). + */ + if ( ~0 == cnf ) + cnf = wdog_on; + return cnf; +} + +/* change on/off state assume caller has IRQs disabled */ +static void dog_toggle(uint32_t ctl) +{ + ctl &= ~( GT_WDOG_Config_Ctl1a | GT_WDOG_Config_Ctl1b \ + | GT_WDOG_Config_Ctl2a | GT_WDOG_Config_Ctl2b); + gt_wr(GT_WDOG_Config, ctl | GT_WDOG_Config_Ctl1a); + gt_wr(GT_WDOG_Config, ctl | GT_WDOG_Config_Ctl1b); +} + +static void dog_pet(uint32_t ctl) +{ + ctl &= ~( GT_WDOG_Config_Ctl1a | GT_WDOG_Config_Ctl1b \ + | GT_WDOG_Config_Ctl2a | GT_WDOG_Config_Ctl2b); + gt_wr(GT_WDOG_Config, ctl | GT_WDOG_Config_Ctl2a); + gt_wr(GT_WDOG_Config, ctl | GT_WDOG_Config_Ctl2b); +} + + +/* Enable watchdog and set a timeout (in us) + * a timeout of 0xffffffff selects the old/existing + * timeout. + * + * RETURNS 0 on success + */ +int +BSP_watchdog_enable(uint32_t timeout_us) +{ +unsigned long long x = timeout_us; +unsigned flags; +uint32_t ctl; + + x *= BSP_bus_frequency; + x /= 256; /* there seems to be a prescaler */ + x /= 1000000; /* us/s */ + + if ( x > (1<<24)-1 ) + x = (1<<24)-1; + + if ( 0xffffffff != timeout_us ) + timeout_us = x; + + rtems_interrupt_disable(flags); + + ctl = rd_wdcnf(); + + /* if enabled, disable first */ + if ( GT_WDOG_Config_Enb & ctl ) { + dog_toggle(ctl); + } + if ( 0xffffffff == timeout_us ) { + timeout_us = ctl & ((1<<24)-1); + dog_toggle(ctl); + dog_pet(ctl); + } else { + gt_wr(GT_WDOG_Config, timeout_us | GT_WDOG_Config_Ctl1a); + gt_wr(GT_WDOG_Config, timeout_us | GT_WDOG_Config_Ctl1b); + } + + wdog_on = GT_WDOG_Config_Enb | timeout_us; + + rtems_interrupt_enable(flags); + return 0; +} + +/* Disable watchdog + * RETURNS 0 on success + */ +int BSP_watchdog_disable(void) +{ +unsigned long flags; +uint32_t ctl; + + rtems_interrupt_disable(flags); + + ctl = rd_wdcnf(); + + if ( (GT_WDOG_Config_Enb & ctl) ) { + dog_toggle(ctl); + wdog_on = ctl & ~(GT_WDOG_Config_Enb); + } + + rtems_interrupt_enable(flags); + return 0; +} + +/* Check status -- unfortunately there seems to be no way + * to read the running value... + * + * RETURNS nonzero if enabled/running, zero if disabled/stopped + */ +int BSP_watchdog_status(void) +{ +uint32_t ctl = rd_wdcnf(); + + /* report also the current period */ + return GT_WDOG_Config_Enb & ctl ? ctl : 0; +} + +/* Pet the watchdog (rearm to configured timeout) + * RETURNS: 0 on success, nonzero on failure (watchdog + * currently not running). + */ +int BSP_watchdog_pet(void) +{ +unsigned long flags; + if ( !wdog_on ) + return -1; + rtems_interrupt_disable(flags); + dog_pet(rd_wdcnf()); + rtems_interrupt_enable(flags); + return 0; +} + + +#ifdef DEBUG_MODULAR +int +_cexpModuleFinalize(void *unused) +{ + BSP_watchdog_disable(); + return BSP_timers_uninstall(); +} + +void +_cexpModuleInitialize(void *unused) +{ + BSP_timers_initialize(); +} +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.h b/c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.h new file mode 100644 index 0000000000..f1a83d6bc1 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gt_timer.h @@ -0,0 +1,134 @@ +#ifndef BSP_GT_TIMER_H +#define BSP_GT_TIMER_H +/* $Id$ */ + +/* Support for hardware timers in the discovery bridge */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* Obtain the number of hardware timers present + * The 'timer' argument in the routines below addresses + * one of 0..(BSP_timer_instances()-1) + */ +int BSP_timer_instances(void); + +/* Setup timer but don't start yet; interrupts are enabled if an isr argument is passed + * no interrupts are generated otherwise. + * + * If 'reload' is nonzero then the period is automatically restarted. + * + * RETURNS: 0 on success, nonzero on error (argument error) + * + * NOTE: If an ISR is already connected, it must be removed by passing a NULL isr first. + */ +int BSP_timer_setup(uint32_t timer, void (*isr)(void *arg), void *arg, int reload); + +/* Stop timer; + * + * RETURNS: 0 on success, nonzero on argument error + */ +int BSP_timer_stop(uint32_t timer); + +/* Start timer with 'period' (in ticks) + * + * RETURNS: 0 on success, nonzero on argument error + */ +int BSP_timer_start(uint32_t timer, uint32_t period); + +/* read decrementing timer on the fly + * + * RETURNS: current count in ticks + */ +uint32_t BSP_timer_read(uint32_t timer); + +/* get clock rate in Hz */ +uint32_t BSP_timer_clock_get(uint32_t timer); + +/* Initialize timer facility -- to be used by BSP implementors only + * + * RETURNS: 0 on success, nonzero if ISR wrapper couldn't be installed + */ +int BSP_timers_initialize(void); + +/* WATCHDOG TIMER (resets board if enabled and not 'petted' for + * some time). + */ + +/* Enable watchdog and set a timeout (in us) + * RETURNS 0 on success + */ +int BSP_watchdog_enable(uint32_t timeout_us); + +/* Disable watchdog + * RETURNS 0 on success + */ +int BSP_watchdog_disable(void); + +/* Check status -- unfortunately there seems to be no way + * to read the running value... + * + * RETURNS nonzero if enabled/running, zero if disabled/stopped + */ +int BSP_watchdog_status(void); + +/* Pet the watchdog (rearm to configured timeout) + * RETURNS: 0 on success, nonzero on failure (watchdog + * currently not running). + */ +int BSP_watchdog_pet(void); + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c.c b/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c.c new file mode 100644 index 0000000000..2cc67f3258 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c.c @@ -0,0 +1,447 @@ +/* $NetBSD: gti2c.c,v 1.2 2005/02/27 00:27:21 perry Exp $ */ + +/* + * Copyright (c) 2005 Brocade Communcations, inc. + * All rights reserved. + * + * Written by Matt Thomas for Brocade Communcations, Inc. + * + * 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. The name of Brocade Communications, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BROCADE COMMUNICATIONS, INC. ``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 EITHER BROCADE COMMUNICATIONS, INC. 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. + */ + +/* Fixed many things + ported to RTEMS by Till Straumann, 2005 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define ENABLE_IRQ_AT_PIC_HACK /* workaround for a bad HW bug */ +#undef DEBUG + +#ifndef BSP_IRQ_MIN_PRIO +#define BSP_IRQ_MIN_PRIO 1 +#endif + +struct gti2c_softc { + uint32_t sc_gt; + uint32_t sc_cntl; + int sc_inited; + rtems_id sc_sync; + int sc_irqs; /* statistics */ +}; + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +typedef struct { + rtems_libi2c_bus_t bus_desc; + struct gti2c_softc pvt; +} gti2c_desc_rec, *gti2c_desc; + +STATIC rtems_status_code +gt_i2c_init(rtems_libi2c_bus_t *bh); +STATIC rtems_status_code +gt_i2c_send_start(rtems_libi2c_bus_t *bh); +STATIC rtems_status_code +gt_i2c_send_stop(rtems_libi2c_bus_t *bh); +STATIC rtems_status_code +gt_i2c_send_addr(rtems_libi2c_bus_t *bh, uint32_t addr, int rw); +STATIC int +gt_i2c_read_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len); +STATIC int +gt_i2c_write_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len); + +static rtems_libi2c_bus_ops_t myops = { + init: gt_i2c_init, + send_start: gt_i2c_send_start, + send_stop: gt_i2c_send_stop, + send_addr: gt_i2c_send_addr, + read_bytes: gt_i2c_read_bytes, + write_bytes: gt_i2c_write_bytes, +}; + +static gti2c_desc_rec my_bus_tbl = { + { + ops: &myops, + size: sizeof(my_bus_tbl), + },/* public fields */ + { + sc_gt: BSP_MV64x60_BASE, + sc_cntl: I2C_Control_TWSIEn, + sc_inited: 0, + sc_sync: 0 + } /* our private fields */ +}; + + +static inline uint32_t +gt_read(uint32_t base, uint32_t off) +{ + return in_le32((volatile unsigned*)(base+off)); +} + +static inline void +gt_write(uint32_t base, uint32_t off, uint32_t val) +{ + out_le32((volatile unsigned*)(base+off), val); +} + + +static inline void +disable_irq(struct gti2c_softc *sc) +{ +uint32_t v = gt_read(sc->sc_gt, I2C_REG_Control); + gt_write(sc->sc_gt, I2C_REG_Control, v & ~I2C_Control_IntEn); +} + + +static rtems_status_code +gt_i2c_wait(struct gti2c_softc *sc, uint32_t control, uint32_t desired_status) +{ + uint32_t status; + rtems_status_code rval; + + control |= I2C_Control_IntEn; + + gt_write(sc->sc_gt, I2C_REG_Control, control | sc->sc_cntl); + + if ( sc->sc_inited ) { + +#ifdef ENABLE_IRQ_AT_PIC_HACK + BSP_enable_irq_at_pic(BSP_IRQ_I2C); +#endif + + rval = rtems_semaphore_obtain(sc->sc_sync, RTEMS_WAIT, 100); + + if ( RTEMS_SUCCESSFUL != rval ) + return rval; + } else { + uint32_t then, now; + + /* run in polling mode - useful during init */ + if ( _System_state_Is_up(_System_state_Get()) ) { + printk("WARNING: gti2c running in polled mode -- should initialize properly!\n"); + } + + asm volatile("mftb %0":"=r"(then)); + + do { + asm volatile("mftb %0":"=r"(now)); + /* poll timebase for .2 seconds assuming a bus clock of 100MHz */ + if ( now - then > (uint32_t)100000000/4/5 ) + return RTEMS_TIMEOUT; + } while ( ! (I2C_Control_IFlg & gt_read(sc->sc_gt, I2C_REG_Control)) ); + } + + status = gt_read(sc->sc_gt, I2C_REG_Status); + + if ( status != desired_status && (status!=I2C_Status_ReStarted || desired_status!=I2C_Status_Started) ) + return RTEMS_IO_ERROR; + + return RTEMS_SUCCESSFUL; +} + +static void +gt_i2c_intr(void *arg) +{ +struct gti2c_softc * const sc = &my_bus_tbl.pvt; + uint32_t v; + + v = gt_read(sc->sc_gt, I2C_REG_Control); + if ((v & I2C_Control_IFlg) == 0) { + printk("gt_i2c_intr: IRQ but IFlg not set??\n"); + return; + } + gt_write(sc->sc_gt, I2C_REG_Control, v & ~(I2C_Control_IntEn)); +#if 0 + gt_read(sc->sc_gt, I2C_REG_Control); + asm volatile("sync"); +/* This is how bad it is: after turning off the IntEn bit, the line + * still remains asserted! (shame on you.) + * + * The test below (on MVME6100; the MVME5500 has the same problem + * but the main cause register address is different; substitute + * 0xf100000c for 0xf1000c68 on a 5500). + * + * The skew was 101 TB ticks or ~3us (bus freq 133MHz) which + * really sucks. + * + * Therefore, we must disable the interrupt at the PIC + */ +{unsigned from,to; + asm volatile("mftb %0":"=r"(from)); + while ( in_le32((volatile unsigned*)0xf100000c) & 0x20 ) + ; + asm volatile("mftb %0":"=r"(to)); + printk("I2C IRQ remained asserted for %i TB ticks!\n",to-from); +} +#endif +#ifdef ENABLE_IRQ_AT_PIC_HACK + BSP_disable_irq_at_pic(BSP_IRQ_I2C); +#endif + + sc->sc_irqs++; + + rtems_semaphore_release(sc->sc_sync); +} + +STATIC rtems_status_code +gt_i2c_init(rtems_libi2c_bus_t *bh) +{ +struct gti2c_softc * const sc = &((gti2c_desc)bh)->pvt; +unsigned m,n,N; + + disable_irq(sc); + + /* reset */ + gt_write(sc->sc_gt, I2C_REG_SoftReset, 0); + gt_write(sc->sc_gt, I2C_REG_SlaveAddr, 0); + gt_write(sc->sc_gt, I2C_REG_ExtSlaveAddr, 0); + + /* Set baud rate; I don't know the details + * but have to assume that it has to fit into 7 bits + * (as indicated by some experiment) + */ + n = 0, N=1< 16 ); + + /* n is at least 1 */ + if ( n > 8 ) { + n = 8; m = 16; /* nothing else we can do */ + } + if ( 0 == m ) + m = 1; /* nothing we can do */ + + gt_write(sc->sc_gt, I2C_REG_BaudRate, I2C_BaudRate(m-1, n-1)); + + if ( !sc->sc_inited ) { + + if ( _System_state_Is_up(_System_state_Get()) ) { + rtems_irq_connect_data ii = { + name: BSP_IRQ_I2C, + hdl: gt_i2c_intr, + on: 0, + off: 0, + isOn: 0 + }; + rtems_status_code err; + /* synchronization semaphore */ + err = rtems_semaphore_create( + rtems_build_name('g','i','2','c'), + 0, + RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_LOCAL, + 0, + &sc->sc_sync); + if ( err ) { + sc->sc_sync = 0; + return err; + } + if ( !BSP_install_rtems_irq_handler(&ii) ) { + fprintf(stderr,"Unable to install interrupt handler\n"); + rtems_semaphore_delete(sc->sc_sync); + return RTEMS_INTERNAL_ERROR; + } + BSP_irq_set_priority(BSP_IRQ_I2C, BSP_IRQ_MIN_PRIO); + sc->sc_inited = 1; + } else { + } + } else { + rtems_semaphore_flush(sc->sc_sync); + } + return RTEMS_SUCCESSFUL; +} + +STATIC rtems_status_code +gt_i2c_send_start(rtems_libi2c_bus_t *bh) +{ +struct gti2c_softc * const sc = &((gti2c_desc)bh)->pvt; + + return gt_i2c_wait(sc, I2C_Control_Start, I2C_Status_Started); +} + +STATIC rtems_status_code +gt_i2c_send_stop(rtems_libi2c_bus_t *bh) +{ +struct gti2c_softc * const sc = &((gti2c_desc)bh)->pvt; +uint32_t data; + + data = gt_read(sc->sc_gt, I2C_REG_Status); + if ( I2C_Status_Started == data || I2C_Status_ReStarted == data ) { + /* According to the spec, a void message (start - stop sequence) + * is illegal and indeed, the chip plays bad tricks with us, i.e., + * sometimes it hangs the bus so that it remains idle forever. + * so we have to address someone... + */ + gt_i2c_send_addr(bh, /*just something... */ 8, 1); + data = gt_read(sc->sc_gt, I2C_REG_Status); + } + + if ( I2C_Status_AddrReadAck == data ) { + /* Another thing: spec says that the master generates stop only after + * not acknowledging the last byte. Again, the chip doesn't like + * to be stopped in this condition - hence we just do it the favor + * and read a single byte... + */ + gt_i2c_read_bytes(bh, (unsigned char *)&data, 1); + } + + gt_write(sc->sc_gt, I2C_REG_Control, I2C_Control_Stop | sc->sc_cntl); + + /* should we poll for idle? There seems to be in IRQ when this completes */ + return RTEMS_SUCCESSFUL; +} + +STATIC rtems_status_code +gt_i2c_send_addr(rtems_libi2c_bus_t *bh, uint32_t addr, int rw) +{ +struct gti2c_softc * const sc = &((gti2c_desc)bh)->pvt; +uint32_t data, wanted_status; +uint8_t read_mask = rw ? 1 : 0; +rtems_status_code error; + + if (read_mask) { + wanted_status = I2C_Status_AddrReadAck; + } else { + wanted_status = I2C_Status_AddrWriteAck; + } + /* + * First byte contains whether this xfer is a read or write. + */ + data = read_mask; + if (addr > 0x7f) { + /* + * If this is a 10bit request, the first address byte is + * 0b11110. + */ + data |= 0xf0 | ((addr & 0x300) >> 7); + gt_write(sc->sc_gt, I2C_REG_Data, data); + error = gt_i2c_wait(sc, 0, wanted_status); + if (error) + return error; + /* + * The first address byte has been sent, now to send + * the second one. + */ + if (read_mask) { + wanted_status = I2C_Status_2ndAddrReadAck; + } else { + wanted_status = I2C_Status_2ndAddrWriteAck; + } + data = (uint8_t) addr; + } else { + data |= (addr << 1); + } + + gt_write(sc->sc_gt, I2C_REG_Data, data); + return gt_i2c_wait(sc, 0, wanted_status); +} + +STATIC int +gt_i2c_read_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len) +{ +struct gti2c_softc * const sc = &((gti2c_desc)bh)->pvt; +rtems_status_code error; +register unsigned char *p=buf; + + while ( len-- > 0 ) { + error = gt_i2c_wait( + sc, + len ? I2C_Control_ACK : 0, + len ? I2C_Status_MasterReadAck : I2C_Status_MasterReadNoAck); + if ( error ) { + return -error; + } + *p++ = gt_read(sc->sc_gt, I2C_REG_Data); + } + + return p-buf; +} + +STATIC int +gt_i2c_write_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len) +{ +struct gti2c_softc * const sc = &((gti2c_desc)bh)->pvt; +int rval = 0; +rtems_status_code error; + + while ( len-- > 0 ) { + gt_write(sc->sc_gt, I2C_REG_Data, buf[rval]); + error = gt_i2c_wait(sc, 0, I2C_Status_MasterWriteAck); + if ( error ) { + return -error; + } + rval++; + } + + return rval; +} + +rtems_libi2c_bus_t *gt64260_i2c_bus_descriptor = &my_bus_tbl.bus_desc; + +#ifdef DEBUG_MODULAR + +void +_cexpModuleInitialize(void *arg) +{ + gt_i2c_init(>64260_i2c_bus_descriptor->bus_desc); +} + +int +_cexpModuleFinalize(void * arg) +{ +struct gti2c_softc * const sc = >64260_i2c_bus_descriptor->pvt; + + rtems_irq_connect_data ii = { + name: BSP_IRQ_I2C, + hdl: gt_i2c_intr, + on: noop, + off: noop, + isOn: inoop + }; + + rtems_semaphore_delete(sc->sc_sync); + + return !BSP_remove_rtems_irq_handler(&ii); +} + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c_busdrv.h b/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c_busdrv.h new file mode 100644 index 0000000000..b75e16cdf8 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2c_busdrv.h @@ -0,0 +1,62 @@ +#ifndef GT_64260_BUS_DRIVER_H +#define GT_64260_BUS_DRIVER_H +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#ifdef __cplusplus + extern "C" { +#endif + +#include +#include + +/* for registration with libi2c */ +extern rtems_libi2c_bus_t *gt64260_i2c_bus_descriptor; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2creg.h b/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2creg.h new file mode 100644 index 0000000000..33e566f5bc --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gti2creg.h @@ -0,0 +1,83 @@ +/* $NetBSD: gti2creg.h,v 1.2 2005/02/27 00:27:21 perry Exp $ */ + +/* + * Copyright (c) 2005 Brocade Communcations, inc. + * All rights reserved. + * + * Written by Matt Thomas for Brocade Communcations, Inc. + * + * 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. The name of Brocade Communications, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BROCADE COMMUNICATIONS, INC. ``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 EITHER BROCADE COMMUNICATIONS, INC. 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. + */ + +#ifndef _DEV_MARVELL_GTI2CREG_H_ +#define _DEV_MARVELL_GTI2CREG_H_ + +#define I2C_REG_SlaveAddr 0xc000 +#define I2C_REG_ExtSlaveAddr 0xc010 +#define I2C_REG_Data 0xc004 +#define I2C_REG_Control 0xc008 +#define I2C_REG_Status 0xc00c +#define I2C_REG_BaudRate 0xc00c +#define I2C_REG_SoftReset 0xc01c + +#define I2C_SlaveAddr_GCE 0x0001 /* Act as Slave */ +#define I2C_SlaveAddr_SAddr 0x7E + +#define I2C_Control_ACK 0x04 +#define I2C_Control_IFlg 0x08 +#define I2C_Control_Stop 0x10 +#define I2C_Control_Start 0x20 +#define I2C_Control_TWSIEn 0x40 +#define I2C_Control_IntEn 0x80 + +/* + * F(I2C) = F(Tclk) / ( 10 * (M + 1) * (2^(N+1))) + * For Tclk = 100MHz, M = 4, N = 4: F = 62.5KHz + * For Tclk = 100MHz, M = 13, N = 3: F = 96.2KHz + */ +#define I2C_BaudRate(M, N) (((M) << 3) | (N)) +#define I2C_BaudRate_62_5K I2C_BaudRate(4, 4) +#define I2C_BaudRate_96_2K I2C_BaudRate(13, 3) + +#define I2C_Status_BusError 0x00 /* Bus error */ +#define I2C_Status_Started 0x08 /* Start condition xmitted */ +#define I2C_Status_ReStarted 0x10 /* Repeated start condition xmitted */ +#define I2C_Status_AddrWriteAck 0x18 /* Adr + wr bit xmtd, ack rcvd */ +#define I2C_Status_AddrWriteNoAck 0x20 /* Adr + wr bit xmtd, NO ack rcvd */ +#define I2C_Status_MasterWriteAck 0x28 /* Master xmtd data byte, ack rcvd */ +#define I2C_Status_MasterWriteNoAck 0x30 /* Master xmtd data byte, NO ack rcvd*/ +#define I2C_Status_MasterLostArb 0x38 /* Master lost arbitration during + address or data transfer */ +#define I2C_Status_AddrReadAck 0x40 /* Adr + rd bit xmtd, ack rcvd */ +#define I2C_Status_AddrReadNoAck 0x48 /* Adr + rd bit xmtd, NO ack rcvd */ +#define I2C_Status_MasterReadAck 0x50 /* Master rcvd data bye, ack rcvd */ +#define I2C_Status_MasterReadNoAck 0x58 /* Master rcvd data bye, NO ack rcvd */ +#define I2C_Status_2ndAddrWriteAck 0xd0 /* 2nd adr + wr bit xmid, ack rcvd */ +#define I2C_Status_2ndAddrWriteNoAck 0xd8 /* 2nd adr + wr bit xmid, NO ack rcvd */ +#define I2C_Status_2ndAddrReadAck 0xe0 /* 2nd adr + rd bit xmid, ack rcvd */ +#define I2C_Status_2ndAddrReadNoAck 0xe8 /* 2nd adr + rd bit xmtd, NO ack rcvd */ +#define I2C_Status_Idle 0xf8 /* Idle */ + +#endif /* _DEV_MARVELL_GTI2CREG_H_ */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gtintrreg.h b/c/src/lib/libbsp/powerpc/beatnik/marvell/gtintrreg.h new file mode 100644 index 0000000000..bd3f69514e --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gtintrreg.h @@ -0,0 +1,257 @@ +/* $NetBSD: gtintrreg.h,v 1.3 2005/02/27 00:27:21 perry Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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. + */ + +/* + * gt64260intr.h: defines for GT-64260 system controller interrupts + * + * creation Sun Jan 7 18:05:59 PST 2001 cliff + * + * NOTE: + * Galileo GT-64260 manual bit defines assume Little Endian + * ordering of bits within bytes, i.e. + * bit #0 --> 0x01 + * vs. Motorola Big Endian bit numbering where + * bit #0 --> 0x80 + * Consequently we define bits in Little Endian format and plan + * to swizzle bytes during programmed I/O by using lwbrx/swbrx + * to load/store GT-64260 registers. + */ + + +#ifndef _DISCOVERY_GT64260INTR_H +#define _DISCOVERY_GT64260INTR_H + +#define BIT(n) (1<<(n)) + + +/* + * GT-64260 Interrupt Controller Register Map + */ +#define ICR_260_MIC_LO 0xc18 /* main interrupt cause low */ +#define ICR_260_MIC_HI 0xc68 /* main interrupt cause high */ +#define ICR_260_CIM_LO 0xc1c /* CPU interrupt mask low */ +#define ICR_260_CIM_HI 0xc6c /* CPU interrupt mask high */ +#define ICR_260_CSC 0xc70 /* CPU select cause */ +#define ICR_260_P0IM_LO 0xc24 /* PCI_0 interrupt mask low */ +#define ICR_260_P0IM_HI 0xc64 /* PCI_0 interrupt mask high */ +#define ICR_260_P0SC 0xc74 /* PCI_0 select cause */ +#define ICR_260_P1IM_LO 0xca4 /* PCI_1 interrupt mask low */ +#define ICR_260_P1IM_HI 0xce4 /* PCI_1 interrupt mask high */ +#define ICR_260_P1SC 0xcf4 /* PCI_1 select cause */ +#define ICR_260_CI0M 0xe60 /* CPU int[0] mask */ +#define ICR_260_CI1M 0xe64 /* CPU int[1] mask */ +#define ICR_260_CI2M 0xe68 /* CPU int[2] mask */ +#define ICR_260_CI3M 0xe6c /* CPU int[3] mask */ + +/* + * MV64360 Interrupt Controller Register Map + */ +#define ICR_360_MIC_LO 0x004 /* main interrupt cause low */ +#define ICR_360_MIC_HI 0x00c /* main interrupt cause high */ +#define ICR_360_C0IM_LO 0x014 /* CPU 0 interrupt mask low */ +#define ICR_360_C0IM_HI 0x01c /* CPU 0 interrupt mask high */ +#define ICR_360_C0SC 0x024 /* CPU 0 select cause */ +#define ICR_360_C1IM_LO 0x034 /* CPU 1 interrupt mask low */ +#define ICR_360_C1IM_HI 0x03c /* CPU 1 interrupt mask high */ +#define ICR_360_C1SC 0x044 /* CPU 1 select cause */ +#define ICR_360_I0M_LO 0x014 /* Int 0 mask low */ +#define ICR_360_I0M_HI 0x01c /* Int 0 mask high */ +#define ICR_360_I0SC 0x024 /* Int 0 select cause */ +#define ICR_360_I1M_LO 0x034 /* Int 1 mask low */ +#define ICR_360_I1M_HI 0x03c /* Int 1 mask high */ +#define ICR_360_C1SC 0x044 /* Int 1 select cause */ + + +/* + * IRQs: + * we define IRQs based on bit number in the + * ICU_LEN dimensioned hardware portion of the imask_t bit vector + * which consists of 64 bits of Main Cause and Mask register pairs + * (ICR_MIC_LO, ICR_MIC_HI and ICR_CIM_LO, ICR_CIM_HI) + * as well as 32 bits in GPP registers (see intr.h): + * + * IRQs: + * 31.............................0 63.............................32 + * | | | + * imask_t index: | | | + * | | | | + * ^--------- IM_PIC_LO ----------^ ^------ IM_PIC_HI ------------^ + * | | | + * Bitmasks: | | | + * | | | | + * ^--------- IML_* --------------^ ^------ IMH_* ----------------^ + * | | | + * Registers: | | | + * | | | | + * ^--------- ICR_MIC_LO ---------^ ^------ ICR_MIC_HI -----------^ + * ^--------- ICR_CIM_LO ---------^ ^------ ICR_CIM_HI -----------^ + * + * IRQs: + * 95............................64 127............................96 + * | | | + * imask_t index: | | | + * | | | | + * ^-------- IMASK_GPP ----------^ ^----- IMASK_SOFTINT --------^ + * | | | + * Bitmasks: | | | + * | | | | + * ^--------- GPP_* --------------^ ^------ SIBIT(irq) -----------^ + * | | | + * Registers: | | | + * | | | | + * ^--- GT_GPP_Interrupt_Cause ---^ ^------- (none) -----------^ + * ^--- GT_GPP_Interrupt_Mask ---^ + * + * + * Note that GPP interrupts are summarized in the Main Cause Register. + * + * Some IRQs are "resvered" undefined due to gaps in HW register utilization. + */ +#define IRQ_DEV 1 /* device interface interrupt */ +#define IRQ_DMA 2 /* DMA addres error interrupt */ +#define IRQ_CPU 3 /* CPU interface interrupt */ +#define IRQ_IDMA0_1 4 /* IDMA ch. 0..1 complete interrupt */ +#define IRQ_IDMA2_3 5 /* IDMA ch. 2..3 complete interrupt */ +#define IRQ_IDMA4_5 6 /* IDMA ch. 4..5 complete interrupt */ +#define IRQ_IDMA6_7 7 /* IDMA ch. 6..7 complete interrupt */ +#define IRQ_TIME0_1 8 /* Timer 0..1 interrupt */ +#define IRQ_TIME2_3 9 /* Timer 2..3 interrupt */ +#define IRQ_TIME4_5 10 /* Timer 4..5 interrupt */ +#define IRQ_TIME6_7 11 /* Timer 6..7 interrupt */ +#define IRQ_PCI0_0 12 /* PCI 0 interrupt 0 summary */ +#define IRQ_PCI0_1 13 /* PCI 0 interrupt 1 summary */ +#define IRQ_PCI0_2 14 /* PCI 0 interrupt 2 summary */ +#define IRQ_PCI0_3 15 /* PCI 0 interrupt 3 summary */ +#define IRQ_PCI1_0 16 /* PCI 1 interrupt 0 summary */ +#define IRQ_ECC 17 /* ECC error interrupt */ +#define IRQ_PCI1_1 18 /* PCI 1 interrupt 1 summary */ +#define IRQ_PCI1_2 19 /* PCI 1 interrupt 2 summary */ +#define IRQ_PCI1_3 20 /* PCI 1 interrupt 3 summary */ +#define IRQ_PCI0OUT_LO 21 /* PCI 0 outbound interrupt summary */ +#define IRQ_PCI0OUT_HI 22 /* PCI 0 outbound interrupt summary */ +#define IRQ_PCI1OUT_LO 23 /* PCI 1 outbound interrupt summary */ +#define IRQ_PCI1OUT_HI 24 /* PCI 1 outbound interrupt summary */ +#define IRQ_PCI0IN_LO 26 /* PCI 0 inbound interrupt summary */ +#define IRQ_PCI0IN_HI 27 /* PCI 0 inbound interrupt summary */ +#define IRQ_PCI1IN_LO 28 /* PCI 1 inbound interrupt summary */ +#define IRQ_PCI1IN_HI 29 /* PCI 1 inbound interrupt summary */ +#define IRQ_ETH0 (32+0) /* Ethernet controller 0 interrupt */ +#define IRQ_ETH1 (32+1) /* Ethernet controller 1 interrupt */ +#define IRQ_ETH2 (32+2) /* Ethernet controller 2 interrupt */ +#define IRQ_SDMA (32+4) /* SDMA interrupt */ +#define IRQ_I2C (32+5) /* I2C interrupt */ +#define IRQ_BRG (32+7) /* Baud Rate Generator interrupt */ +#define IRQ_MPSC0 (32+8) /* MPSC 0 interrupt */ +#define IRQ_MPSC1 (32+10) /* MPSC 1 interrupt */ +#define IRQ_COMM (32+11) /* Comm unit interrupt */ +#define IRQ_GPP7_0 (32+24) /* GPP[7..0] interrupt */ +#define IRQ_GPP15_8 (32+25) /* GPP[15..8] interrupt */ +#define IRQ_GPP23_16 (32+26) /* GPP[23..16] interrupt */ +#define IRQ_GPP31_24 (32+27) /* GPP[31..24] interrupt */ + +/* + * low word interrupt mask register bits + */ +#define IML_SUM BIT(0) +#define IML_DEV BIT(IRQ_DEV) +#define IML_DMA BIT(IRQ_DMA) +#define IML_CPU BIT(IRQ_CPU) +#define IML_IDMA0_1 BIT(IRQ_IDMA0_1) +#define IML_IDMA2_3 BIT(IRQ_IDMA2_3) +#define IML_IDMA4_5 BIT(IRQ_IDMA4_5) +#define IML_IDMA6_7 BIT(IRQ_IDMA6_7) +#define IML_TIME0_1 BIT(IRQ_TIME0_1) +#define IML_TIME2_3 BIT(IRQ_TIME2_3) +#define IML_TIME4_5 BIT(IRQ_TIME4_5) +#define IML_TIME6_7 BIT(IRQ_TIME6_7) +#define IML_PCI0_0 BIT(IRQ_PCI0_0) +#define IML_PCI0_1 BIT(IRQ_PCI0_1) +#define IML_PCI0_2 BIT(IRQ_PCI0_2) +#define IML_PCI0_3 BIT(IRQ_PCI0_3) +#define IML_PCI1_0 BIT(IRQ_PCI1_0) +#define IML_ECC BIT(IRQ_ECC) +#define IML_PCI1_1 BIT(IRQ_PCI1_1) +#define IML_PCI1_2 BIT(IRQ_PCI1_2) +#define IML_PCI1_3 BIT(IRQ_PCI1_3) +#define IML_PCI0OUT_LO BIT(IRQ_PCI0OUT_LO) +#define IML_PCI0OUT_HI BIT(IRQ_PCI0OUT_HI) +#define IML_PCI1OUT_LO BIT(IRQ_PCI1OUT_LO) +#define IML_PCI1OUT_HI BIT(IRQ_PCI1OUT_HI) +#define IML_PCI0IN_LO BIT(IRQ_PCI0IN_LO) +#define IML_PCI0IN_HI BIT(IRQ_PCI0IN_HI) +#define IML_PCI1IN_LO BIT(IRQ_PCI1IN_LO) +#define IML_PCI1IN_HI BIT(IRQ_PCI1IN_HI) +#define IML_RES (BIT(25)|BIT(30)|BIT(31)) + +/* + * high word interrupt mask register bits + */ +#define IMH_ETH0 BIT(IRQ_ETH0-32) +#define IMH_ETH1 BIT(IRQ_ETH1-32) +#define IMH_ETH2 BIT(IRQ_ETH2-32) +#define IMH_SDMA BIT(IRQ_SDMA-32) +#define IMH_I2C BIT(IRQ_I2C-32) +#define IMH_BRG BIT(IRQ_BRG-32) +#define IMH_MPSC0 BIT(IRQ_MPSC0-32) +#define IMH_MPSC1 BIT(IRQ_MPSC1-32) +#define IMH_COMM BIT(IRQ_COMM-32) +#define IMH_GPP7_0 BIT(IRQ_GPP7_0-32) +#define IMH_GPP15_8 BIT(IRQ_GPP15_8-32) +#define IMH_GPP23_16 BIT(IRQ_GPP23_16-32) +#define IMH_GPP31_24 BIT(IRQ_GPP31_24-32) +#define IMH_GPP_SUM (IMH_GPP7_0|IMH_GPP15_8|IMH_GPP23_16|IMH_GPP31_24) +#define IMH_RES (BIT(3) |BIT(6) |BIT(9) |BIT(12)|BIT(13)|BIT(14) \ + |BIT(15)|BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20) \ + |BIT(21)|BIT(22)|BIT(23)|BIT(28)|BIT(29)|BIT(30) \ + |BIT(31)) + +/* + * ICR_CSC "Select Cause" register bits + */ +#define CSC_SEL BIT(30) /* HI/LO select */ +#define CSC_STAT BIT(31) /* ? "irq active" : "irq none" */ +#define CSC_CAUSE ~(CSC_SEL|CSC_STAT) + + +/* + * CPU Int[n] Mask bit(s) + */ +#define CPUINT_SEL 0x80000000 /* HI/LO select */ + +#endif /* _DISCOVERY_GT64260INTR_H */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gtpcireg.h b/c/src/lib/libbsp/powerpc/beatnik/marvell/gtpcireg.h new file mode 100644 index 0000000000..d01fc702ac --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gtpcireg.h @@ -0,0 +1,964 @@ +/* $NetBSD: gtpcireg.h,v 1.4 2005/12/11 12:22:16 christos Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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. + */ + +#ifndef _DEV_GTPCIREG_H +#define _DEV_GTPCIREG_H + +#define PCI__BIT(bit) (1U << (bit)) +#define PCI__MASK(bit) (PCI__BIT(bit) - 1) +#define PCI__GEN(bus, off, num) (((off)^((bus) << 7))+((num) << 4)) +#define PCI__EXT(data, bit, len) (((data) >> (bit)) & PCI__MASK(len)) +#define PCI__CLR(data, bit, len) ((data) &= ~(PCI__MASK(len) << (bit))) +#define PCI__INS(bit, new) ((new) << (bit)) + +#define PCI_SYNC_REG(bus) (0xc0 | ((bus) << 3)) + +/* + * Table 185: PCI Slave ADDRess Decoding Register Map + */ +#define PCI_SCS0_BAR_SIZE(bus) PCI__GEN(bus, 0x0c08, 0) +#define PCI_SCS2_BAR_SIZE(bus) PCI__GEN(bus, 0x0c0c, 0) +#define PCI_CS0_BAR_SIZE(bus) PCI__GEN(bus, 0x0c10, 0) +#define PCI_CS3_BAR_SIZE(bus) PCI__GEN(bus, 0x0c14, 0) +#define PCI_SCS1_BAR_SIZE(bus) PCI__GEN(bus, 0x0d08, 0) +#define PCI_SCS3_BAR_SIZE(bus) PCI__GEN(bus, 0x0d0c, 0) +#define PCI_CS1_BAR_SIZE(bus) PCI__GEN(bus, 0x0d10, 0) +#define PCI_BOOTCS_BAR_SIZE(bus) PCI__GEN(bus, 0x0d14, 0) +#define PCI_CS2_BAR_SIZE(bus) PCI__GEN(bus, 0x0d18, 0) +#define PCI_P2P_MEM0_BAR_SIZE(bus) PCI__GEN(bus, 0x0d1c, 0) +#define PCI_P2P_MEM1_BAR_SIZE(bus) PCI__GEN(bus, 0x0d20, 0) +#define PCI_P2P_IO_BAR_SIZE(bus) PCI__GEN(bus, 0x0d24, 0) +#define PCI_CPU_BAR_SIZE(bus) PCI__GEN(bus, 0x0d28, 0) +#define PCI_EXPANSION_ROM_BAR_SIZE(bus) PCI__GEN(bus, 0x0d2c, 0) +#define PCI_DAC_SCS0_BAR_SIZE(bus) PCI__GEN(bus, 0x0e00, 0) +#define PCI_DAC_SCS1_BAR_SIZE(bus) PCI__GEN(bus, 0x0e04, 0) +#define PCI_DAC_SCS2_BAR_SIZE(bus) PCI__GEN(bus, 0x0e08, 0) +#define PCI_DAC_SCS3_BAR_SIZE(bus) PCI__GEN(bus, 0x0e0c, 0) +#define PCI_DAC_CS0_BAR_SIZE(bus) PCI__GEN(bus, 0x0e10, 0) +#define PCI_DAC_CS1_BAR_SIZE(bus) PCI__GEN(bus, 0x0e14, 0) +#define PCI_DAC_CS2_BAR_SIZE(bus) PCI__GEN(bus, 0x0e18, 0) +#define PCI_DAC_CS3_BAR_SIZE(bus) PCI__GEN(bus, 0x0e1c, 0) +#define PCI_DAC_BOOTCS_BAR_SIZE(bus) PCI__GEN(bus, 0x0e20, 0) +#define PCI_DAC_P2P_MEM0_BAR_SIZE(bus) PCI__GEN(bus, 0x0e24, 0) +#define PCI_DAC_P2P_MEM1_BAR_SIZE(bus) PCI__GEN(bus, 0x0e28, 0) +#define PCI_DAC_CPU_BAR_SIZE(bus) PCI__GEN(bus, 0x0e2c, 0) +#define PCI_BASE_ADDR_REGISTERS_ENABLE(bus) PCI__GEN(bus, 0x0c3c, 0) +#define PCI_SCS0_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0c48, 0) +#define PCI_SCS1_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0d48, 0) +#define PCI_SCS2_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0c4c, 0) +#define PCI_SCS3_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0d4c, 0) +#define PCI_CS0_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0c50, 0) +#define PCI_CS1_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0d50, 0) +#define PCI_CS2_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0d58, 0) +#define PCI_CS3_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0c54, 0) +#define PCI_ADDR_DECODE_CONTROL(bus) PCI__GEN(bus, 0x0d3c, 0) +#define PCI_BOOTCS_ADDR_REMAP(bus) PCI__GEN(bus, 0x0d54, 0) +#define PCI_P2P_MEM0_BASE_ADDR_REMAP_LOW(bus) PCI__GEN(bus, 0x0d5c, 0) +#define PCI_P2P_MEM0_BASE_ADDR_REMAP_HIGH(bus) PCI__GEN(bus, 0x0d60, 0) +#define PCI_P2P_MEM1_BASE_ADDR_REMAP_LOW(bus) PCI__GEN(bus, 0x0d64, 0) +#define PCI_P2P_MEM1_BASE_ADDR_REMAP_HIGH(bus) PCI__GEN(bus, 0x0d68, 0) +#define PCI_P2P_IO_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0d6c, 0) +#define PCI_CPU_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0d70, 0) +#define PCI_DAC_SCS0_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f00, 0) +#define PCI_DAC_SCS1_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f04, 0) +#define PCI_DAC_SCS2_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f08, 0) +#define PCI_DAC_SCS3_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f0c, 0) +#define PCI_DAC_CS0_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f10, 0) +#define PCI_DAC_CS1_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f14, 0) +#define PCI_DAC_CS2_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f18, 0) +#define PCI_DAC_CS3_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f1c, 0) +#define PCI_DAC_BOOTCS_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f20, 0) +#define PCI_DAC_P2P_MEM0_BASE_ADDR_REMAP_LOW(bus) PCI__GEN(bus, 0x0f24, 0) +#define PCI_DAC_P2P_MEM0_BASE_ADDR_REMAP_HIGH(bus) PCI__GEN(bus, 0x0f28, 0) +#define PCI_DAC_P2P_MEM1_BASE_ADDR_REMAP_LOW(bus) PCI__GEN(bus, 0x0f2c, 0) +#define PCI_DAC_P2P_MEM1_BASE_ADDR_REMAP_HIGH(bus) PCI__GEN(bus, 0x0f30, 0) +#define PCI_DAC_CPU_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f34, 0) +#define PCI_EXPANSION_ROM_BASE_ADDR_REMAP(bus) PCI__GEN(bus, 0x0f38, 0) + +/* + * Table 186: PCI Control Register Map + */ +#define PCI_COMMAND(bus) PCI__GEN(bus, 0x0c00, 0) +#define PCI_MODE(bus) PCI__GEN(bus, 0x0d00, 0) +#define PCI_TIMEOUT_RETRY(bus) PCI__GEN(bus, 0x0c04, 0) +#define PCI_READ_BUFFER_DISCARD_TIMER(bus) PCI__GEN(bus, 0x0d04, 0) +#define PCI_MSI_TRIGGER_TIMER(bus) PCI__GEN(bus, 0x0c38, 0) +#define PCI_ARBITER_CONTROL(bus) PCI__GEN(bus, 0x1d00, 0) +#define PCI_INTERFACE_XBAR_CONTROL_LOW(bus) PCI__GEN(bus, 0x1d08, 0) +#define PCI_INTERFACE_XBAR_CONTROL_HIGH(bus) PCI__GEN(bus, 0x1d0c, 0) +#define PCI_INTERFACE_XBAR_TIMEOUT(bus) PCI__GEN(bus, 0x1d04, 0) +#define PCI_READ_RESPONSE_XBAR_CONTROL_LOW(bus) PCI__GEN(bus, 0x1d18, 0) +#define PCI_READ_RESPONSE_XBAR_CONTROL_HIGH(bus) PCI__GEN(bus, 0x1d1c, 0) +#define PCI_SYNC_BARRIER(bus) PCI__GEN(bus, 0x1d10, 0) +#define PCI_P2P_CONFIGURATION(bus) PCI__GEN(bus, 0x1d14, 0) +#define PCI_P2P_SWAP_CONTROL(bus) PCI__GEN(bus, 0x1d54, 0) +#define PCI_ACCESS_CONTROL_BASE_LOW(bus, n) PCI__GEN(bus, 0x1e00, n) +#define PCI_ACCESS_CONTROL_BASE_HIGH(bus, n) PCI__GEN(bus, 0x1e04, n) +#define PCI_ACCESS_CONTROL_TOP(bus, n) PCI__GEN(bus, 0x1e08, n) + + +/* + * Table 187: PCI Snoop Control Register Map + */ +#define PCI_SNOOP_CONTROL_BASE_LOW(bus, n) PCI__GEN(bus, 0x1f00, n) +#define PCI_SNOOP_CONTROL_BASE_HIGH(bus, n) PCI__GEN(bus, 0x1f04, n) +#define PCI_SNOOP_CONTROL_TOP(bus, n) PCI__GEN(bus, 0x1f08, n) + +/* + * Table 188: PCI Configuration ACCESS_Register Map + */ +#define PCI_CONFIG_ADDR(bus) PCI__GEN(bus, 0x0cf8, 0) +#define PCI_CONFIG_DATA(bus) PCI__GEN(bus, 0x0cfc, 0) +#define PCI_INTR_ACK(bus) PCI__GEN(bus, 0x0c34, 0) + +/* + * Table 189: PCI ERROR Report Register Map + */ +#define PCI_SERR_MASK(bus) PCI__GEN(bus, 0x0c28, 0) +#define PCI_ERROR_ADDRESS_LOW(bus) PCI__GEN(bus, 0x1d40, 0) +#define PCI_ERROR_ADDRESS_HIGH(bus) PCI__GEN(bus, 0x1d44, 0) +#define PCI_ERROR_DATA_LOW(bus) PCI__GEN(bus, 0x1d48, 0) +#define PCI_ERROR_DATA_HIGH(bus) PCI__GEN(bus, 0x1d4c, 0) +#define PCI_ERROR_COMMAND(bus) PCI__GEN(bus, 0x1d50, 0) +#define PCI_ERROR_CAUSE(bus) PCI__GEN(bus, 0x1d58, 0) +#define PCI_ERROR_MASK(bus) PCI__GEN(bus, 0x1d5c, 0) + + + +/* + * Table 223: PCI Base Address Registers Enable + * If a bit is clear, the BAR is enabled. If set, disabled. The GT64260] + * prevents disabling both memory mapped and I/O mapped BARs (bits 9 and 10 + * cannot simultaneously be set to 1). + */ +#define PCI_BARE_SCS0En PCI__BIT(0) /* SCS[0]* BAR Enable */ +#define PCI_BARE_SCS1En PCI__BIT(1) /* SCS[1]* BAR Enable */ +#define PCI_BARE_SCS2En PCI__BIT(2) /* SCS[2]* BAR Enable */ +#define PCI_BARE_SCS3En PCI__BIT(3) /* SCS[3]* BAR Enable */ +#define PCI_BARE_CS0En PCI__BIT(4) /* CS[0]* BAR Enable */ +#define PCI_BARE_CS1En PCI__BIT(5) /* CS[1]* BAR Enable */ +#define PCI_BARE_CS2En PCI__BIT(6) /* CS[2]* BAR Enable */ +#define PCI_BARE_CS3En PCI__BIT(7) /* CS[3]* BAR Enable */ +#define PCI_BARE_BootCSEn PCI__BIT(8) /* BootCS* BAR Enable */ +#define PCI_BARE_IntMemEn PCI__BIT(9) /* Memory Mapped Internal + * Registers BAR Enable */ +#define PCI_BARE_IntIOEn PCI__BIT(10) /* I/O Mapped Internal + * Registers BAR Enable */ +#define PCI_BARE_P2PMem0En PCI__BIT(11) /* P2P Mem0 BAR Enable */ +#define PCI_BARE_P2PMem1En PCI__BIT(12) /* P2P Mem1 BAR Enable */ +#define PCI_BARE_P2PIOEn PCI__BIT(13) /* P2P IO BAR Enable */ +#define PCI_BARE_CPUEn PCI__BIT(14) /* CPU BAR Enable */ +#define PCI_BARE_DSCS0En PCI__BIT(15) /* DAC SCS[0]* BAR Enable */ +#define PCI_BARE_DSCS1En PCI__BIT(16) /* DAC SCS[1]* BAR Enable */ +#define PCI_BARE_DSCS2En PCI__BIT(17) /* DAC SCS[2]* BAR Enable */ +#define PCI_BARE_DSCS3En PCI__BIT(18) /* DAC SCS[3]* BAR Enable */ +#define PCI_BARE_DCS0En PCI__BIT(19) /* DAC CS[0]* BAR Enable */ +#define PCI_BARE_DCS1En PCI__BIT(20) /* DAC CS[1]* BAR Enable */ +#define PCI_BARE_DCS2En PCI__BIT(21) /* DAC CS[2]* BAR Enable */ +#define PCI_BARE_DCS3En PCI__BIT(22) /* DAC CS[3]* BAR Enable */ +#define PCI_BARE_DBootCSEn PCI__BIT(23) /* DAC BootCS* BAR Enable */ +#define PCI_BARE_DP2PMem0En PCI__BIT(24) /* DAC P2P Mem0 BAR Enable */ +#define PCI_BARE_DP2PMem1En PCI__BIT(25) /* DAC P2P Mem1 BAR Enable */ +#define PCI_BARE_DCPUEn PCI__BIT(26) /* DAC CPU BAR Enable */ + +/* + * Table 254: PCI Address Decode Control + * Bits 7:4 and 31:25 are reserved + * 00:00 RemapWrDis Address Remap Registers Write Disable + * 0: Writes to a BAR result in updating the + * corresponding remap register with the BAR's + * new value. + * 1: Writes to a BAR have no affect on the + * corresponding Remap register value. + * 01:01 ExpRomDev Expansion ROM Device (0: CS[3]; 1: BootCS) + * 02:02 VPDDev VPD Device (0: CS[3]; 1: BootCS) + * 03:03 MsgAcc Messaging registers access + * 0: Messaging unit registers are accessible on + * lowest 4Kbyte of SCS[0] BAR space. + * 1: Messaging unit registers are only accessible + * as part of the GT64260 internal space. + * 07:04 Reserved + * 24:08 VPDHighAddr VPD High Address bits + * [31:15] of VPD the address. + * 31:25 Reserved + */ +#define PCI_ADC_RemapWrDis PCI__BIT(0) +#define PCI_ADC_ExpRomDev PCI__BIT(1) +#define PCI_ADC_VPDDev PCI__BIT(2) +#define PCI_ADC_MsgAcc PCI__BIT(3) +#define PCI_ADC_VPDHighAddr_GET(v) PCI__EXT(v, 8, 16) + + +/* + * Table 255: PCI Command + * 00:00 MByteSwap PCI Master Byte Swap + * NOTE: GT-64120 and GT-64130 compatible. + * When set to 0, the GTO64260 PCI master swaps the bytes + * of the incoming and outgoing PCI data (swap the 8 bytes + * of a longword). + * 01:01 Reserved + * 02:02 Reserved Must be 0. + * 03:03 Reserved + * 04:04 MWrCom PCI Master Write Combine Enable + * When set to 1, write combining is enabled. + * 05:05 MRdCom PCI Master Read Combine Enable + * When set to 1, read combining is enabled. + * 06:06 MWrTrig PCI Master Write Trigger + * 0: Accesses the PCI bus only when the whole burst is + * written into the master write buffer. + * 1: Accesses the PCI bus when the first data is written + * into the master write buffer. + * 07:07 MRdTrig PCI Master Read Trigger + * 0: Returns read data to the initiating unit only when + * the whole burst is written into master read buffer. + * 1: Returns read data to the initiating unit when the + * first read data is written into master read buffer. + * 08:08 MRdLine PCI Master Memory Read Line Enable + * (0: Disable; 1: Enable) + * 09:09 MRdMul PCI Master Memory Read Multiple Enable + * (0: Disable; 1: Enable) + * 10:10 MWordSwap PCI Master Word Swap + * NOTE: GT-64120 and GT-64130 compatible. + * When set to 1, the GT64260 PCI master swaps the 32-bit + * words of the incoming and outgoing PCI data. + * 11:11 SWordSwap PCI Slave Word Swap + * NOTE: GT-64120 and GT-64130 compatible. + * When set to 1, the GT64260 PCI slave swaps the 32-bit + * words of the incoming and outgoing PCI data. + * 12:12 IntBusCtl PCI Interface Unit Internal Bus Control + * NOTE: Reserved for Galileo Technology usage + * 0: Enable internal bus sharing between master and + * slave interfaces. + * 1: Disable internal bus sharing between master and + * slave interfaces. + * 13:13 SBDis PCI Slave Sync Barrier Disable + * When set to 1, the PCI configuration read transaction + * will stop act as sync barrier transaction. + * 14:14 Reserved Must be 0 + * 15:15 MReq64 PCI Master REQ64* Enable (0: Disable; 1: Enable) + * 16:16 SByteSwap PCI Slave Byte Swap + * NOTE: GT-64120 and GT-64130 compatible. + * When set to 0, the GT64260 PCI slave swaps the bytes of + * the incoming and outgoing PCI data (swap the 8 bytes of + * a long-word). + * 17:17 MDACEn PCI Master DAC Enable + * 0: Disable (The PCI master never drives the DAC cycle) + * 1: Enable (In case the upper 32-bit address is not 0, + * the PCI master drives the DAC cycle) + * 18:18 M64Allign PCI Master REQ64* assertion on non-aligned + * 0: Disable (The master asserts REQ64* only if + * the address is 64-bit aligned) + * 1: Enable (The master asserts REQ64* even if + * the address is not 64-bit aligned) + * 19:19 PErrProp Parity/ECC Errors Propagation Enable + * 0: Disable (The PCI interface always drives + * correct parity on the PAR signal) + * 1: Enable (In case of slave read bad ECC from + * SDRAM, or master write with bad parity/ECC + * indication from the initiator, the PCI interface + * drives bad parity on the PAR signal) + * 20:20 SSwapEn PCI Slave Swap Enable + * NOTE: Even if the SSwapEn bit is set to 1 and + * the PCI address does not match any of the + * Access Control registers, slave data swapping + * works according to SByteSwap and SWordSwap bits. + * 0: PCI slave data swapping is determined via + * SByteSwap and SWordSwap bits (bits 16 and 11), + * as in the GT-64120/130. + * 1: PCI slave data swapping is determined via PCISwap + * bits [25:24] in the PCI Access Control registers. + * 21:21 MSwapEn PCI Master Swap Enable + * 0: PCI master data swapping is determined via + * MByteSwap and MWordSwap bits (bits 0 and 10), + * as in the GT-64120/130. + * 1: PCI master data swapping is determined via + * PCISwap bits in CPU to PCI Address Decoding + * registers. + * 22:22 MIntSwapEn PCI Master Configuration Transactions Data Swap Enable + * NOTE: Reserved for Galileo Technology usage. + * 0: Disable (The PCI master configuration transaction + * to the PCI bus is always in Little Endian convention) + * 1: Enable (The PCI master configuration transaction to + * the PCI bus is determined according to the setting + * of MSwapEn bit) + * 23:23 LBEn PCI Loop Back Enable + * NOTE: Reserved for Galileo Technology usage. + * 0: Disable (The PCI slave does not respond to + * transactions initiated by the PCI master) + * 1: Enable (The PCI slave does respond to + * transactions initiated by the PCI master, + * if targeted to the slave (address match) + * 26:24 SIntSwap PCI Slave data swap control on PCI accesses to the + * GT64260 internal and configuration registers. + * Bits encoding are the same as bits[26:24] in PCI Access + * Control registers. + * 27:27 Reserved Must be 0. + * 31:28 Reserved Read only. + */ +#define PCI_CMD_MByteSwap PCI__BIT(0) +#define PCI_CMD_MBZ0_2 PCI__BIT(2) +#define PCI_CMD_MWrCom PCI__BIT(4) +#define PCI_CMD_MRdCom PCI__BIT(5) +#define PCI_CMD_MWrTrig PCI__BIT(6) +#define PCI_CMD_MRdTrig PCI__BIT(7) +#define PCI_CMD_MRdLine PCI__BIT(8) +#define PCI_CMD_MRdMul PCI__BIT(9) +#define PCI_CMD_MWordSwap PCI__BIT(10) +#define PCI_CMD_SWordSwap PCI__BIT(11) +#define PCI_CMD_IntBusCtl PCI__BIT(12) +#define PCI_CMD_SBDis PCI__BIT(13) +#define PCI_CMD_MBZ0_14 PCI__BIT(14) +#define PCI_CMD_MReq64 PCI__BIT(15) +#define PCI_CMD_SByteSwap PCI__BIT(16) +#define PCI_CMD_MDCAEn PCI__BIT(17) +#define PCI_CMD_M64Allign PCI__BIT(18) +#define PCI_CMD_PErrProp PCI__BIT(19) +#define PCI_CMD_SSwapEn PCI__BIT(20) +#define PCI_CMD_MSwapEn PCI__BIT(21) +#define PCI_CMD_MIntSwapEn PCI__BIT(22) +#define PCI_CMD_LBEn PCI__BIT(23) +#define PCI_CMD_SIntSwap_GET(v) PCI__EXT(v, 24, 3) +#define PCI_CMD_MBZ0_27 PCI__BIT(27) + + +/* + * Table 256: PCI Mode + * 00:00 PciID PCI Interface ID -- Read Only (PCI_0: 0x0; PCI_1: 0x1) + * 01:01 Reserved + * 02:02 Pci64 64-bit PCI Interface -- Read Only + * When set to 1, the PCI interface is configured to a + * 64 bit interface. + * 07:03 Reserved + * 08:08 ExpRom Expansion ROM Enable -- Read Only from PCI + * When set to 1, the expansion ROM BAR is enabled. + * 09:09 VPD VPD Enable -- Read Only from PCI + * When set to 1, VPD is supported. + * 10:10 MSI MSI Enable -- Read Only from PCI + * When set to 1, MSI is supported. + * 11:11 PMG Power Management Enable -- Read Only from PCI + * When set to 1, PMG is supported. + * 12:12 HotSwap CompactPCI Hot Swap Enable -- Read Only from PCI + * When set to 1, HotSwap is supported. + * 13:13 BIST BIST Enable -- Read only from PCI + * If set to 1, BIST is enabled. + * 30:14 Reserved + * 31:31 PRst PCI Interface Reset Indication -- Read Only + * Set to 0 as long as the RST* pin is asserted. + */ +#define PCI_MODE_PciID_GET(v) PCI__EXT(v, 0, 1) +#define PCI_MODE_Pci64 PCI__BIT(2) +#define PCI_MODE_ExpRom PCI__BIT(8) +#define PCI_MODE_VPD PCI__BIT(9) +#define PCI_MODE_MSI PCI__BIT(10) +#define PCI_MODE_PMG PCI__BIT(11) +#define PCI_MODE_HotSwap PCI__BIT(12) +#define PCI_MODE_BIST PCI__BIT(13) +#define PCI_MODE_PRst PCI__BIT(31) + +/* + * Table 257: PCI Timeout and Retry + * 07:00 Timeout0 Specifies the number of PClk cycles the GT64260 slave + * holds the PCI bus before terminating a transaction + * with RETRY. + * 15:08 Timeout1 Specifies the number of PClk cycles the GT64260 slave + * holds the PCI bus before terminating a transaction + * with DISCONNECT. + * 23:16 RetryCtr Retry Counter + * Specifies the number of retries of the GT64260 Master. + * The GT64260 generates an interrupt when this timer + * expires. A 0x00 value means a retry forever. + * 31:24 Reserved + */ +#define PCI_TMORTRY_Timeout0_GET(v) PCI__EXT(v, 0, 8) +#define PCI_TMORTRY_Timeout1_GET(v) PCI__EXT(v, 8, 8) +#define PCI_TMORTRY_RetryCtr_GET(v) PCI__EXT(v, 16, 8) + + +/* + * Table 258: PCI Read Buffer Discard Timer + * 15:00 Timer Specifies the number of PClk cycles the GT64260 + * slave keeps an non-accessed read buffers (non com- + * pleted delayed read) before invalidating the buffer. + * 23:16 RdBufEn Slave Read Buffers Enable + * Each bit corresponds to one of the eight read buffers. + * If set to 1, buffer is enabled. + * 31:24 Reserved + */ +#define PCI_RdBufDisTmr_Timer_GET(v) PCI__EXT(v, 0, 16) +#define PCI_RdBufDisTmr_RdBufEn_GET(v) PCI__EXT(v, 16, 8) +#define PCI_RdBufDisTmr_RdBufEn0(v) PCI__BIT(16) +#define PCI_RdBufDisTmr_RdBufEn1(v) PCI__BIT(17) +#define PCI_RdBufDisTmr_RdBufEn2(v) PCI__BIT(18) +#define PCI_RdBufDisTmr_RdBufEn3(v) PCI__BIT(19) +#define PCI_RdBufDisTmr_RdBufEn4(v) PCI__BIT(20) +#define PCI_RdBufDisTmr_RdBufEn5(v) PCI__BIT(21) +#define PCI_RdBufDisTmr_RdBufEn6(v) PCI__BIT(22) +#define PCI_RdBufDisTmr_RdBufEn7(v) PCI__BIT(23) + +/* + * Table 259: MSI Trigger Timer + * 15:00 Timer Specifies the number of TClk cycles between consecutive + * MSI requests. + * 31:16 Reserved + */ +#define PCI_MSITrigger_Timer_GET(v) PCI__EXT(v, 0, 16) + +/* + * Table 260: PCI Arbiter Control + * NOTE: If HPPV (bits [28:21]) is set to 0 and PAEn is set to 1, + * priority scheme is reversed. This means that high priority + * requests are granted if no low priority request is pending. + * 00:00 Reserved Must be 0. 0x0 + * 01:01 BDEn Broken Detection Enable + * If set to 1, broken master detection is enabled. A mas- + * ter is said to be broken if it fails to respond to grant + * assertion within a window specified in BV (bits [6:3]). + * 02:02 PAEn Priority Arbitration Enable + * 0: Low priority requests are granted only when no high + * priority request is pending + * 1: Weighted round robin arbitration is performed + * between high priority and low priority groups. + * 06:03 BV Broken Value + * This value sets the maximum number of cycles that the + * arbiter waits for a PCI master to respond to its grant + * assertion. If a PCI master fails to assert FRAME* within + * this time, the PCI arbiter aborts the transaction and + * performs a new arbitration cycle and a maskable + * interrupt is generated. Must be greater than 0. + * NOTE: The PCI arbiter waits for the current + * transaction to end before starting to + * count the wait-for-broken cycles. + * Must be greater than 1 for masters that performs address + * stepping (such as the GTO 64260 PCI master), since they + * require GNT* assertion for two cycles. + * 13:07 P[6:0] Priority + * These bits assign priority levels to the requests + * connected to the PCI arbiter. When a PM bit is set to + * 1, priority of the associated request is high. The + * mapping between P[6:0] bits and the request/grant pairs + * are as follows: + * P[0]: internal PCI master P[1]: external REQ0/GNT0 + * P[2]: external REQ1/GNT1 P[3]: external REQ2/GNT2 + * P[4]: external REQ3/GNT3 P[5]: external REQ4/GNT4 + * P[6]: external REQ5/GNT5 + * 20:14 PD[6:0] Parking Disable + * Use these bits to disable parking on any of the PCI + * masters. When a PD bit is set to 1, parking on the + * associated PCI master is disabled. + * NOTE: The arbiter parks on the last master granted + * unless disabled through the PD bit. Also, if + * PD bits are all 1, the PCI arbiter parks on + * the internal PCI master. + * 28:21 HPPV High Priority Preset Value + * This is the preset value of the high priority counter + * (High_cnt). This counter decrements each time a high + * priority request is granted. When the counter reaches + * zero, it reloads with this preset value. The counter + * reloads when a low priority request is granted. + * 30:29 Reserved + * 31:31 EN Enable + * Setting this bit to 1 enables operation of the arbiter. + */ +#define PCI_ARBCTL_MBZ0_0 PCI__BIT(0) +#define PCI_ARBCTL_BDEn PCI__BIT(1) +#define PCI_ARBCTL_PAEn PCI__BIT(2) +#define PCI_ARBCTL_BV_GET(v) PCI__EXT(v, 3, 4) +#define PCI_ARBCTL_P_GET(v) PCI__EXT(v, 7, 7) +#define PCI_ARBCTL_PD_GET(v) PCI__EXT(v, 14, 7) +#define PCI_ARBCTL_HPPV_GET(v) PCI__EXT(v, 21, 7) +#define PCI_ARBCTL_EN PCI__BIT(31) + +#define PCI_ARBPRI_IntPci PCI__BIT(0) +#define PCI_ARBPRI_ExtReqGnt0 PCI__BIT(1) +#define PCI_ARBPRI_ExtReqGnt1 PCI__BIT(2) +#define PCI_ARBPRI_EXtReqGnt2 PCI__BIT(3) +#define PCI_ARBPRI_EXtReqGnt3 PCI__BIT(4) +#define PCI_ARBPRI_EXtReqGnt4 PCI__BIT(5) +#define PCI_ARBPRI_EXtReqGnt5 PCI__BIT(6) + +/* + * Table 261: PCI Interface Crossbar Control (Low) + * 03:00 Arb0 Slice 0 of PCI master pizza arbiter. + * 07:04 Arb1 Slice 1 of PCI master pizza arbiter. + * 11:08 Arb2 Slice 2 of PCI master pizza arbiter. + * 15:12 Arb3 Slice 3 of PCI master pizza arbiter. + * 19:16 Arb4 Slice 4 of PCI master pizza arbiter. + * 23:20 Arb5 Slice 5 of PCI master pizza arbiter. + * 27:24 Arb6 Slice 6 of PCI master pizza arbiter. + * 31:28 Arb7 Slice 7 of PCI master pizza arbiter. + */ +#define PCI_IFXBRCTL_GET_SLICE(v, n) PCI__EXT(v, (n) * 4, 4) +#define PCI_IFXBRCTL_SET_SLICE(v, n, s) ((void)(PCI__CLR(v, (n)*4, 4),\ + (v) |= PCI__INS((n)*4, s))) + +/* + * Table 262: PCI Interface Crossbar Control (High) + * 03:00 Arb8 Slice 8 of PCI master pizza arbiter. + * 07:04 Arb9 Slice 9 of PCI master pizza arbiter. + * 11:08 Arb10 Slice 10 of PCI master pizza arbiter. + * 15:12 Arb11 Slice 11 of PCI master pizza arbiter. + * 19:16 Arb12 Slice 12 of PCI master pizza arbiter. + * 23:20 Arb13 Slice 13 of PCI master pizza arbiter. + * 27:24 Arb14 Slice 14 of PCI master pizza arbiter. + * 31:28 Arb15 Slice 15 of PCI master pizza arbiter. + */ +#define PCI_IFXBRCH_GET_SLICE(v, n) PCI__EXT(v, ((n) - 8) * 4, 4) +#define PCI_IFXBRCH_SET_SLICE(v, n, s) ((void)(PCI__CLR(v, ((n)*-8)4, 4),\ + (v) |= PCI__INS(((n)-8)*4, s))) + +/* + * Table 263: PCI Interface Crossbar Timeout + (NOTE: Reserved for Galileo Technology usage.) + * 07:00 Timeout Crossbar Arbiter Timeout Preset Value + * 15:08 Reserved + * 16:16 TimeoutEn Crossbar Arbiter Timer Enable (1: Disable) + * 31:17 Reserved + */ +#define PCI_IFXBRTMO_Timeout_GET(v) PCI__EXT(v, 0, 8) +#define PCI_IFXBRTMO_TimeoutEn PCI__BIT(16) + +/* + * Table 264: PCI Read Response Crossbar Control (Low) + * 03:00 Arb0 Slice 0 of PCI slave pizza arbiter. + * 07:04 Arb1 Slice 1 of PCI slave pizza arbiter. + * 11:08 Arb2 Slice 2 of PCI slave pizza arbiter. + * 15:12 Arb3 Slice 3 of PCI slave pizza arbiter. + * 19:16 Arb4 Slice 4 of PCI slave pizza arbiter. + * 23:20 Arb5 Slice 5 of PCI slave pizza arbiter. + * 27:24 Arb6 Slice 6 of PCI slave pizza arbiter. + * 31:28 Arb7 Slice 7 of PCI slave pizza arbiter. + */ +#define PCI_RRXBRCL_GET_SLICE(v, n) PCI__EXT(v, (n) * 4, 4) +#define PCI_RRXBRCL_SET_SLICE(v, n, s) ((void)(PCI__CLR(v, (n)*4, 4),\ + (v) |= PCI__INS((n)*4, s))) + + +/* + * Table 265: PCI Read Response Crossbar Control (High) + * 03:00 Arb8 Slice 8 of PCI slave pizza arbiter. + * 07:04 Arb9 Slice 9 of PCI slave pizza arbiter. + * 11:08 Arb10 Slice 10 of PCI slave pizza arbiter. + * 15:12 Arb11 Slice 11 of PCI slave pizza arbiter. + * 19:16 Arb12 Slice 12 of PCI slave pizza arbiter. + * 23:20 Arb13 Slice 13 of PCI slave pizza arbiter. + * 27:24 Arb14 Slice 14 of PCI slave pizza arbiter. + * 31:28 Arb15 Slice 15 of PCI slave pizza arbiter. + */ +#define PCI_RRXBRCH_GET_SLICE(v, n) PCI__EXT(v, ((n) - 8) * 4, 4) +#define PCI_RRXBRCH_SET_SLICE(v, n, s) ((void)(PCI__CLR(v, ((n)*-8)4, 4),\ + (v) |= PCI__INS(((n)-8)*4, s))) + +/* + * Table 266: PCI Sync Barrier Virtual Register + * 31:0 SyncReg Sync Barrier Virtual Register + * PCI read from this register results in PCI slave sync barrier + * action. The returned data is un-deterministic. Read Only. + */ + +/* + * Table 267: PCI P2P Configuration + * 07:00 2ndBusL Secondary PCI Interface Bus Range Lower Boundary + * 15:08 2ndBusH Secondary PCI Interface Bus Range Upper Boundary + * 23:16 BusNum The PCI bus number to which the PCI interface + * is connected. + * 28:24 DevNum The PCI interface's device number. + * 31:29 Reserved Reserved. + */ +#define PCI_P2PCFG_2ndBusL_GET(v) PCI__EXT(v, 0, 8) +#define PCI_P2PCFG_2ndBusH_GET(v) PCI__EXT(v, 8, 8) +#define PCI_P2PCFG_BusNum_GET(v) PCI__EXT(v, 16, 8) +#define PCI_P2PCFG_DevNum_GET(v) PCI__EXT(v, 24, 5) + +/* + * Table 268: PCI P2P Swap Control + * 02:00 M0Sw P2P Mem0 BAR Swap Control + * 03:03 M0Req64 P2P Mem0 BAR Force REQ64 + * 06:04 M1Sw P2P Mem1 BAR Swap Control + * 07:07 M1Req64 P2P Mem1 BAR Force REQ64 + * 10:08 DM0Sw P2P DAC Mem0 BAR Swap Control + * 11:11 DM0Req64 P2P DAC Mem0 BAR Force REQ64 + * 14:12 DM1Sw P2P DAC Mem1 BAR Swap Control + * 15:15 DM1Req64 P2P DAC Mem1 BAR Force REQ64 + * 18:16 IOSw P2P I/O BAR Swap Control + * 19:19 Reserved + * 22:20 CfgSw P2P Configuration Swap Control + * 31:19 Reserved + */ +#define PCI_P2PSWAP_M0Sw_GET(v) PCI__EXT(v, 0, 3) +#define PCI_P2PSWAP_M0Req64 PCI__BIT(3) +#define PCI_P2PSWAP_M1Sw_GET(v) PCI__EXT(v, 4, 3) +#define PCI_P2PSWAP_M1Req64 PCI__BIT(7) +#define PCI_P2PSWAP_DM0Sw_GET(v) PCI__EXT(v, 8, 3) +#define PCI_P2PSWAP_DM0Req64 PCI__BIT(11) +#define PCI_P2PSWAP_DM1Sw_GET(v) PCI__EXT(v, 12, 3) +#define PCI_P2PSWAP_DM1Req64 PCI__BIT(15) +#define PCI_P2PSWAP_CfgSw_GET(v) PCI__EXT(v, 20, 3) + + + +/* + * Table 269: PCI Access Control Base (Low) + * 11:00 Addr Base Address Corresponds to address bits[31:20]. + * 12:12 PrefetchEn Read Prefetch Enable + * 0: Prefetch disabled (The PCI slave reads single words) + * 1: Prefetch enabled. + * 14:14 Reserved Must be 0 + * 15:15 Reserved + * 16:16 RdPrefetch PCI Read Aggressive Prefetch Enable; 0: Disable; + * 1: Enable (The PCI slave prefetches two + * bursts in advance) + * 17:17 RdLinePrefetch PCI Read Line Aggressive Prefetch Enable; 0: Disable; + * 1: Enable (PCI slave prefetch two bursts in advance) + * 18:18 RdMulPrefetch PCI Read Multiple Aggressive Prefetch Enable + * 0: Disable; 1: Enable (PCI slave prefetch two bursts in + * advance) + * 19:19 Reserved + * 21:20 MBurst PCI Max Burst + * Specifies the maximum burst size for a single transac- + * tion between a PCI slave and the other interfaces + * 00 - 4 64-bit words + * 01 - 8 64-bit words + * 10 - 16 64-bit words + * 11 - Reserved + * 23:22 Reserved + * 25:24 PCISwap Data Swap Control + * 00 - Byte Swap + * 01 - No swapping + * 10 - Both byte and word swap + * 11 - Word swap + * 26:26 Reserved Must be 0 + * 27:27 Reserved + * 28:28 AccProt Access Protect (0: PCI access is allowed; 1; Region is + not accessible from PCI) + * 29:29 WrProt Write Protect (0: PCI write is allowed; 1: Region is + * not writeable from PCI) + * 31:30 Reserved + */ +#define PCI_ACCCTLBASEL_Addr_GET(v) PCI__EXT(v, 0, 12) +#define PCI_ACCCTLBASEL_PrefetchEn PCI__BIT(12) +#define PCI_ACCCTLBASEL_MBZ0_14 PCI__BIT(14) +#define PCI_ACCCTLBASEL_RdPrefetch PCI__BIT(16) +#define PCI_ACCCTLBASEL_RdLinePrefetch PCI__BIT(17) +#define PCI_ACCCTLBASEL_RdMulPrefetch PCI__BIT(18) +#define PCI_ACCCTLBASEL_WBurst PCI__EXT(v, 20, 2) +#define PCI_ACCCTLBASEL_WBurst_8_QW PCI__INS(20, PCI_WBURST_8_QW) +#define PCI_ACCCTLBASEL_PCISwap PCI__EXT(v, 24, 2) +#define PCI_ACCCTLBASEL_PCISwap_NoSwap PCI__INS(24, PCI_PCISWAP_NoSwap) +#define PCI_ACCCTLBASEL_MBZ0_26 PCI__BIT(26) +#define PCI_ACCCTLBASEL_AccProt PCI__BIT(28) +#define PCI_ACCCTLBASEL_WrProt PCI__BIT(29) + +#define PCI_WBURST_4_QW 0x00 +#define PCI_WBURST_8_QW 0x01 +#define PCI_WBURST_16_QW 0x02 +#define PCI_WBURST_Reserved 0x04 + +#define PCI_PCISWAP_ByteSwap 0x00 +#define PCI_PCISWAP_NoSwap 0x01 +#define PCI_PCISWAP_ByteWordSwap 0x02 +#define PCI_PCISWAP_WordSwap 0x04 + +/* + * Table 293: PCI Snoop Control Base (Low) + * 11:00 Addr Base Address Corresponds to address bits[31:20]. + * 13:12 Snoop Snoop Type + * 31:14 Reserved + */ +#define PCI_SNOOPCTL_ADDR(v) PCI__EXT(v, 0, 12) +#define PCI_SNOOPCTL_TYPE(v) PCI__EXT(v, 12, 2) + +#define PCI_SNOOP_None 0 /* no snoop */ +#define PCI_SNOOP_WT 1 /* Snoop to WT region */ +#define PCI_SNOOP_WB 2 /* Snoop to WB region */ + + +/* + * Table 305: PCI Configuration Address + * + * 07:02 RegNum Register number. + * 10:08 FunctNum Function number. + * 15:11 DevNum Device number. + * 23:16 BusNum Bus number. + * 31:31 ConfigEn When set, an access to the Configuration Data + * register is translated into a Configuration + * or Special cycle on the PCI bus. + */ +#define PCI_CFG_MAKE_TAG(bus, dev, fun, reg) (PCI__BIT(31)|\ + PCI__INS(16, (bus))|\ + PCI__INS(11, (dev))|\ + PCI__INS( 8, (fun))|\ + PCI__INS( 0, (reg))) +#define PCI_CFG_GET_BUSNO(tag) PCI__EXT(tag, 16, 8) +#define PCI_CFG_GET_DEVNO(tag) PCI__EXT(tag, 11, 5) +#define PCI_CFG_GET_FUNCNO(tag) PCI__EXT(tag, 8, 3) +#define PCI_CFG_GET_REGNO(tag) PCI__EXT(tag, 0, 8) + +/* + * Table 306: PCI Configuration Data + * + * 31:00 ConfigData The data is transferred to/from the PCI bus when + * the CPU accesses this register and the ConfigEn + * bit in the Configuration Address register is set + * + * A CPU access to this register causes the GT64260 to perform a Configuration + * or Special cycle on the PCI bus. + */ + + +/* + * Table 307: PCI Interrupt Acknowledge (This register is READ ONLY) + * 31:00 IntAck A CPU read access to this register forces an + * interrupt acknowledge cycle on the PCI bus. + */ + + +/* + * Table 308: PCI SERR* Mask + * + * NOTE: The GT64260 asserts SERR* only if SERR* is enabled via the PCI Status + * and Command register. + * If the corresponding bit is set, then asserts SERR* upon ... + */ +#define PCI_SERRMSK_SAPerr PCI__BIT(0) /* PCI slave detection of bad + * address parity. */ +#define PCI_SERRMSK_SWrPerr PCI__BIT(1) /* PCI slave detection of bad + * write data parity. */ +#define PCI_SERRMSK_SRdPerr PCI__BIT(2) /* a PERR* response to read + * data driven by the PCI + * slave. */ +#define PCI_SERRMSK_MAPerr PCI__BIT(4) /* a PERR* response to an + * address driven by the PCI + * master. */ +#define PCI_SERRMSK_MWrPerr PCI__BIT(5) /* a PERR* response to write + * data driven by the PCI + * master. */ +#define PCI_SERRMSK_MRdPerr PCI__BIT(6) /* bad data parity detection + * during a PCI master read + * transaction. */ +#define PCI_SERRMSK_MMabort PCI__BIT(8) /* a PCI master generation of + * master abort. */ +#define PCI_SERRMSK_MTabort PCI__BIT(9) /* a PCI master detection of + * target abort. */ +#define PCI_SERRMSK_MRetry PCI__BIT(11) /* a PCI master reaching retry + * counter limit. */ +#define PCI_SERRMSK_SMabort PCI__BIT(16) /* a PCI slave detection of + * master abort. */ +#define PCI_SERRMSK_STabort PCI__BIT(17) /* a PCI slave termination of + * a transaction with Target + * Abort. */ +#define PCI_SERRMSK_SAccProt PCI__BIT(18) /* a PCI slave access protect + * violation. */ +#define PCI_SERRMSK_SWrProt PCI__BIT(19) /* a PCI slave write protect + * violation. */ +#define PCI_SERRMSK_SRdBuf PCI__BIT(20) /* the PCI slave's read buffer, + * discard timer expires */ +#define PCI_SERRMSK_Arb PCI__BIT(21) /* the internal PCI arbiter + * detection of a broken PCI + * master. */ + +#define PCI_SERRMSK_ALL_ERRS \ + (PCI_SERRMSK_SAPerr|PCI_SERRMSK_SWrPerr|PCI_SERRMSK_SRdPerr \ + |PCI_SERRMSK_MAPerr|PCI_SERRMSK_MWrPerr|PCI_SERRMSK_MRdPerr \ + |PCI_SERRMSK_MMabort|PCI_SERRMSK_MTabort|PCI_SERRMSK_MRetry \ + |PCI_SERRMSK_SMabort|PCI_SERRMSK_STabort|PCI_SERRMSK_SAccProt \ + |PCI_SERRMSK_SWrProt|PCI_SERRMSK_SRdBuf|PCI_SERRMSK_Arb) + + + +/* + * Table 309: PCI Error Address (Low) -- Read Only. + * 31:00 ErrAddr PCI address bits [31:0] are latched upon an error + * condition. Upon address latch, no new addresses can + * be registered (due to additional error condition) until + * the register is being read. + */ + + + +/* + * Table 310: PCI Error Address (High) Applicable only when running DAC cycles. + * 31:00 ErrAddr PCI address bits [63:32] are latched upon + * error condition. + * + * NOTE: Upon data sample, no new data is latched until the PCI Error Low + * Address register is read. This means that PCI Error Low Address + * register must bethe last register read by the interrupt handler. + */ + +/* + * Table 311: PCI Error Data (Low) + * 31:00 ErrData PCI data bits [31:00] are latched upon error condition. + */ + +/* + * Table 312: PCI Error Data (High) Applicable only when running + * 64-bit cycles. + * 31:00 ErrData PCI data bits [63:32] are latched upon error condition. + */ + +/* + * Table 313: PCI Error Command + * 03:00 ErrCmd PCI command is latched upon error condition. + * 07:04 Reserved + * 15:08 ErrBE PCI byte enable is latched upon error condition. + * 16:16 ErrPAR PCI PAR is latched upon error condition. + * 17:17 ErrPAR64 PCI PAR64 is latched upon error condition. + * Applicable only when running 64-bit cycles. + * 31:18 Reserved + * NOTE: Upon data sample, no new data is latched until the PCI Error Low + * Address register is read. This means that PCI Error Low Address register + * must be the last register read by the interrupt handler. + */ +#define PCI_ERRCMD_Cmd_GET(v) PCI__EXT(v, 0, 4) +#define PCI_ERRCMD_ByteEn_GET(v) PCI__EXT(v, 8, 8) +#define PCI_ERRCMD_PAR PCI__BIT(16) +#define PCI_ERRCMD_PAR64 PCI__BIT(17) + +/* + * Table 314: PCI Interrupt Cause + * 1. All bits are Clear Only. A cause bit set upon error event occurrence. + * A write of 0 clears the bit. A write of 1 has no affect. + * 2. PCI Interrupt bits are organized in four groups: + * bits[ 7: 0] for address and data parity errors, + * bits[15: 8] for PCI master transaction failure (possible external + * target problem), + * bits[23:16] for slave response failure (possible external master problem), + * bits[26:24] for external PCI events that require CPU handle. + */ +#define PCI_IC_SAPerr PCI__BIT(0) /* The PCI slave detected + * bad address parity. */ +#define PCI_IC_SWrPerr PCI__BIT(1) /* The PCI slave detected + * bad write data parity. */ +#define PCI_IC_SRdPerr PCI__BIT(2) /* PERR* response to read + * data driven by PCI slave. */ +#define PCI_IC_MAPerr PCI__BIT(4) /* PERR* response to address + * driven by the PCI master. */ +#define PCI_IC_MWrPerr PCI__BIT(5) /* PERR* response to write data + * driven by the PCI master. */ +#define PCI_IC_MRdPerr PCI__BIT(6) /* Bad data parity detected + * during the PCI master read + * transaction. */ +#define PCI_IC_MMabort PCI__BIT(8) /* The PCI master generated + * master abort. */ +#define PCI_IC_MTabort PCI__BIT(9) /* The PCI master detected + * target abort. */ +#define PCI_IC_MMasterEn PCI__BIT(10) /* An attempt to generate a PCI + * transaction while master is + * not enabled. */ +#define PCI_IC_MRetry PCI__BIT(11) /* The PCI master reached + * retry counter limit. */ +#define PCI_IC_SMabort PCI__BIT(16) /* The PCI slave detects an il- + * legal master termination. */ +#define PCI_IC_STabort PCI__BIT(17) /* The PCI slave terminates a + * transaction with Target + * Abort. */ +#define PCI_IC_SAccProt PCI__BIT(18) /* A PCI slave access protect + * violation. */ +#define PCI_IC_SWrProt PCI__BIT(19) /* A PCI slave write protect + * violation. */ +#define PCI_IC_SRdBuf PCI__BIT(20) /* A PCI slave read buffer + * discard timer expired. */ +#define PCI_IC_Arb PCI__BIT(21) /* Internal PCI arbiter detec- + * tion of a broken master. */ +#define PCI_IC_BIST PCI__BIT(24) /* PCI BIST Interrupt */ +#define PCI_IC_PMG PCI__BIT(25) /* PCI Power Management + * Interrupt */ +#define PCI_IC_PRST PCI__BIT(26) /* PCI Reset Assert */ + +/* +31:27 Sel Specifies the error event currently being reported in the +Error Address, Error Data, and Error Command registers. +*/ +#define PCI_IC_SEL_GET(v) PCI__EXT((v), 27, 5) +#define PCI_IC_SEL_SAPerr 0x00 +#define PCI_IC_SEL_SWrPerr 0x01 +#define PCI_IC_SEL_SRdPerr 0x02 +#define PCI_IC_SEL_MAPerr 0x04 +#define PCI_IC_SEL_MWrPerr 0x05 +#define PCI_IC_SEL_MRdPerr 0x06 +#define PCI_IC_SEL_MMabort 0x08 +#define PCI_IC_SEL_MTabort 0x09 +#define PCI_IC_SEL_MMasterEn 0x0a +#define PCI_IC_SEL_MRetry 0x0b +#define PCI_IC_SEL_SMabort 0x10 +#define PCI_IC_SEL_STabort 0x11 +#define PCI_IC_SEL_SAccProt 0x12 +#define PCI_IC_SEL_SWrProt 0x13 +#define PCI_IC_SEL_SRdBuf 0x14 +#define PCI_IC_SEL_Arb 0x15 +#define PCI_IC_SEL_BIST 0x18 +#define PCI_IC_SEL_PMG 0x19 +#define PCI_IC_SEL_PRST 0x1a + +#define PCI_IC_SEL_Strings { \ + "SAPerr", "SWrPerr", "SRdPerr", "Rsvd#03", \ + "MAPerr", "MWrPerr", "MRdPerr", "Rsvd#07", \ + "MMabort", "MTabort", "MMasterEn", "MRetry", \ + "Rsvd#0c", "Rsvd#0d", "Rsvd#0e", "Rsvd#0f", \ + "SMabort", "STabort", "SAccProt", "SWrProt", \ + "SRdBuf", "Arb", "Rsvd#16", "Rsvd#17", \ + "BIST", "PMG", "PRST", "Rsvd#1b", \ + "Rsvd#1c", "Rsvd#1d", "Rsvd#1e", "Rsvd#1f" } + +/* + * Table 315: PCI Error Mask + * If the corresponding bit is 1, that interrupt is enabled + * Bits 3, 7, 12:15, 22:23, 27:31 are reserved. + */ +#define PCI_ERRMASK_SAPErr PCI__BIT(0) +#define PCI_ERRMASK_SWrPErr PCI__BIT(1) +#define PCI_ERRMASK_SRdPErr PCI__BIT(2) +#define PCI_ERRMASK_MAPErr PCI__BIT(4) +#define PCI_ERRMASK_MWRPErr PCI__BIT(5) +#define PCI_ERRMASK_MRDPErr PCI__BIT(6) +#define PCI_ERRMASK_MMAbort PCI__BIT(8) +#define PCI_ERRMASK_MTAbort PCI__BIT(9) +#define PCI_ERRMASK_MMasterEn PCI__BIT(10) +#define PCI_ERRMASK_MRetry PCI__BIT(11) +#define PCI_ERRMASK_SMAbort PCI__BIT(16) +#define PCI_ERRMASK_STAbort PCI__BIT(17) +#define PCI_ERRMASK_SAccProt PCI__BIT(18) +#define PCI_ERRMASK_SWrProt PCI__BIT(19) +#define PCI_ERRMASK_SRdBuf PCI__BIT(20) +#define PCI_ERRMASK_Arb PCI__BIT(21) +#define PCI_ERRMASK_BIST PCI__BIT(24) +#define PCI_ERRMASK_PMG PCI__BIT(25) +#define PCI_ERRMASK_PRST PCI__BIT(26) + +#endif /* _DEV_GTPCIREG_H_ */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/marvell/gtreg.h b/c/src/lib/libbsp/powerpc/beatnik/marvell/gtreg.h new file mode 100644 index 0000000000..a6c87e2047 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/marvell/gtreg.h @@ -0,0 +1,854 @@ +/* $NetBSD: gtreg.h,v 1.2 2005/02/27 00:27:21 perry Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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. + */ + +#ifndef _DISCOVERY_DEV_GTREG_H_ +#define _DISCOVERY_DEV_GTREG_H_ + + +#define GT__BIT(bit) (1U << (bit)) +#define GT__MASK(bit) (GT__BIT(bit) - 1) +#define GT__EXT(data, bit, len) (((data) >> (bit)) & GT__MASK(len)) +#define GT__CLR(data, bit, len) ((data) &= ~(GT__MASK(len) << (bit))) +#define GT__INS(new, bit) ((new) << (bit)) + + +/* + * Table 30: CPU Address Decode Register Map + */ +#define GT_SCS0_Low_Decode 0x0008 +#define GT_SCS0_High_Decode 0x0010 +#define GT_SCS1_Low_Decode 0x0208 +#define GT_SCS1_High_Decode 0x0210 +#define GT_SCS2_Low_Decode 0x0018 +#define GT_SCS2_High_Decode 0x0020 +#define GT_SCS3_Low_Decode 0x0218 +#define GT_SCS3_High_Decode 0x0220 +#define GT_CS0_Low_Decode 0x0028 +#define GT_CS0_High_Decode 0x0030 +#define GT_CS1_Low_Decode 0x0228 +#define GT_CS1_High_Decode 0x0230 +#define GT_CS2_Low_Decode 0x0248 +#define GT_CS2_High_Decode 0x0250 +#define GT_CS3_Low_Decode 0x0038 +#define GT_CS3_High_Decode 0x0040 +#define GT_BootCS_Low_Decode 0x0238 +#define GT_BootCS_High_Decode 0x0240 +#define GT_PCI0_IO_Low_Decode 0x0048 +#define GT_PCI0_IO_High_Decode 0x0050 +#define GT_PCI0_Mem0_Low_Decode 0x0058 +#define GT_PCI0_Mem0_High_Decode 0x0060 +#define GT_PCI0_Mem1_Low_Decode 0x0080 +#define GT_PCI0_Mem1_High_Decode 0x0088 +#define GT_PCI0_Mem2_Low_Decode 0x0258 +#define GT_PCI0_Mem2_High_Decode 0x0260 +#define GT_PCI0_Mem3_Low_Decode 0x0280 +#define GT_PCI0_Mem3_High_Decode 0x0288 +#define GT_PCI1_IO_Low_Decode 0x0090 +#define GT_PCI1_IO_High_Decode 0x0098 +#define GT_PCI1_Mem0_Low_Decode 0x00a0 +#define GT_PCI1_Mem0_High_Decode 0x00a8 +#define GT_PCI1_Mem1_Low_Decode 0x00b0 +#define GT_PCI1_Mem1_High_Decode 0x00b8 +#define GT_PCI1_Mem2_Low_Decode 0x02a0 +#define GT_PCI1_Mem2_High_Decode 0x02a8 +#define GT_PCI1_Mem3_Low_Decode 0x02b0 +#define GT_PCI1_Mem3_High_Decode 0x02b8 +#define GT_Internal_Decode 0x0068 +#define GT_CPU0_Low_Decode 0x0290 +#define GT_CPU0_High_Decode 0x0298 +#define GT_CPU1_Low_Decode 0x02c0 +#define GT_CPU1_High_Decode 0x02c8 +/* ts, 2005/8: it seems that these are implicitely written + * when setting the 'Low_Decode' regs... + */ +#define GT_PCI0_IO_Remap 0x00f0 +#define GT_PCI0_Mem0_Remap_Low 0x00f8 +#define GT_PCI0_Mem0_Remap_High 0x0320 +#define GT_PCI0_Mem1_Remap_Low 0x0100 +#define GT_PCI0_Mem1_Remap_High 0x0328 +#define GT_PCI0_Mem2_Remap_Low 0x02f8 +#define GT_PCI0_Mem2_Remap_High 0x0330 +#define GT_PCI0_Mem3_Remap_Low 0x0300 +#define GT_PCI0_Mem3_Remap_High 0x0338 +#define GT_PCI1_IO_Remap 0x0108 +#define GT_PCI1_Mem0_Remap_Low 0x0110 +#define GT_PCI1_Mem0_Remap_High 0x0340 +#define GT_PCI1_Mem1_Remap_Low 0x0118 +#define GT_PCI1_Mem1_Remap_High 0x0348 +#define GT_PCI1_Mem2_Remap_Low 0x0310 +#define GT_PCI1_Mem2_Remap_High 0x0350 +#define GT_PCI1_Mem3_Remap_Low 0x0318 +#define GT_PCI1_Mem3_Remap_High 0x0358 + + +/* + * Table 31: CPU Control Register Map + */ +#define GT_CPU_Cfg 0x0000 +#define GT_CPU_Mode 0x0120 +#define GT_CPU_Master_Ctl 0x0160 +#define GT_CPU_If_Xbar_Ctl_Low 0x0150 +#define GT_CPU_If_Xbar_Ctl_High 0x0158 +#define GT_CPU_If_Xbar_Timeout 0x0168 +#define GT_260_CPU_Rd_Rsp_Xbar_Ctl_Low 0x0170 +#define GT_260_CPU_Rd_Rsp_Xbar_Ctl_High 0x0178 + +/* + * Table 32: CPU Sync Barrier Register Map + */ +#define GT_260_PCI_Sync_Barrier(bus) (0x00c0 | ((bus) << 3)) +#define GT_260_PCI0_Sync_Barrier 0x00c0 +#define GT_260_PCI1_Sync_Barrier 0x00c8 + +/* + * Table 33: CPU Access Protection Register Map + */ +#define GT_Protect_Low_0 0x0180 +#define GT_Protect_High_0 0x0188 +#define GT_Protect_Low_1 0x0190 +#define GT_Protect_High_1 0x0198 +#define GT_Protect_Low_2 0x01a0 +#define GT_Protect_High_2 0x01a8 +#define GT_Protect_Low_3 0x01b0 +#define GT_Protect_High_3 0x01b8 +#define GT_260_Protect_Low_4 0x01c0 +#define GT_260_Protect_High_4 0x01c8 +#define GT_260_Protect_Low_5 0x01d0 +#define GT_260_Protect_High_5 0x01d8 +#define GT_260_Protect_Low_6 0x01e0 +#define GT_260_Protect_High_6 0x01e8 +#define GT_260_Protect_Low_7 0x01f0 +#define GT_260_Protect_High_7 0x01f8 + +/* + * Table 34: Snoop Control Register Map + */ +#define GT_260_Snoop_Base_0 0x0380 +#define GT_260_Snoop_Top_0 0x0388 +#define GT_260_Snoop_Base_1 0x0390 +#define GT_260_Snoop_Top_1 0x0398 +#define GT_260_Snoop_Base_2 0x03a0 +#define GT_260_Snoop_Top_2 0x03a8 +#define GT_260_Snoop_Base_3 0x03b0 +#define GT_260_Snoop_Top_3 0x03b8 + +/* + * Table 35: CPU Error Report Register Map + */ +#define GT_CPU_Error_Address_Low 0x0070 +#define GT_CPU_Error_Address_High 0x0078 +#define GT_CPU_Error_Data_Low 0x0128 +#define GT_CPU_Error_Data_High 0x0130 +#define GT_CPU_Error_Parity 0x0138 +#define GT_CPU_Error_Cause 0x0140 +#define GT_CPU_Error_Mask 0x0148 + +#define GT_DecodeAddr_SET(g, r, v) \ + do { \ + gt_read((g), GT_Internal_Decode); \ + gt_write((g), (r), ((v) & 0xfff00000) >> 20); \ + while ((gt_read((g), (r)) & 0xfff) != ((v) >> 20)); \ + } while (0) + +#define GT_LowAddr_GET(v) (GT__EXT((v), 0, 12) << 20) +#define GT_HighAddr_GET(v) ((GT__EXT((v), 0, 12) << 20) | 0xfffff) + +#define GT_MPP_Control0 0xf000 +#define GT_MPP_Control1 0xf004 +#define GT_MPP_Control2 0xf008 +#define GT_MPP_Control3 0xf00c + +#define GT_GPP_IO_Control 0xf100 +#define GT_GPP_Level_Control 0xf110 +#define GT_GPP_Value 0xf104 +#define GT_GPP_Interrupt_Cause 0xf108 +#define GT_GPP_Interrupt_Mask 0xf10c +/* + * Table 36: SCS[0]* Low Decode Address, Offset: 0x008 + * Table 38: SCS[1]* Low Decode Address, Offset: 0x208 + * Table 40: SCS[2]* Low Decode Address, Offset: 0x018 + * Table 42: SCS[3]* Low Decode Address, Offset: 0x218 + * Table 44: CS[0]* Low Decode Address, Offset: 0x028 + * Table 46: CS[1]* Low Decode Address, Offset: 0x228 + * Table 48: CS[2]* Low Decode Address, Offset: 0x248 + * Table 50: CS[3]* Low Decode Address, Offset: 0x038 + * Table 52: BootCS* Low Decode Address, Offset: 0x238 + * Table 75: CPU 0 Low Decode Address, Offset: 0x290 + * Table 77: CPU 1 Low Decode Address, Offset: 0x2c0 + * + * 11:00 LowAddr SCS[0] Base Address + * 31:12 Reserved Must be 0. + */ + +/* + * Table 37: SCS[0]* High Decode Address, Offset: 0x010 + * Table 39: SCS[1]* High Decode Address, Offset: 0x210 + * Table 41: SCS[2]* High Decode Address, Offset: 0x020 + * Table 43: SCS[3]* High Decode Address, Offset: 0x220 + * Table 45: CS[0]* High Decode Address, Offset: 0x030 + * Table 47: CS[1]* High Decode Address, Offset: 0x230 + * Table 49: CS[2]* High Decode Address, Offset: 0x250 + * Table 51: CS[3]* High Decode Address, Offset: 0x040 + * Table 53: BootCS* High Decode Address, Offset: 0x240 + * Table 76: CPU 0 High Decode Address, Offset: 0x298 + * Table 78: CPU 1 High Decode Address, Offset: 0x2c8 + * + * 11:00 HighAddr SCS[0] Top Address + * 31:12 Reserved + */ + +/* + * Table 54: PCI_0 I/O Low Decode Address, Offset: 0x048 + * Table 56: PCI_0 Memory 0 Low Decode Address, Offset: 0x058 + * Table 58: PCI_0 Memory 1 Low Decode Address, Offset: 0x080 + * Table 60: PCI_0 Memory 2 Low Decode Address, Offset: 0x258 + * Table 62: PCI_0 Memory 3 Low Decode Address, Offset: 0x280 + * Table 64: PCI_1 I/O Low Decode Address, Offset: 0x090 + * Table 66: PCI_1 Memory 0 Low Decode Address, Offset: 0x0a0 + * Table 68: PCI_1 Memory 1 Low Decode Address, Offset: 0x0b0 + * Table 70: PCI_1 Memory 2 Low Decode Address, Offset: 0x2a0 + * Table 72: PCI_1 Memory 3 Low Decode Address, Offset: 0x2b0 + * + * 11:00 LowAddr PCI IO/Memory Space Base Address + * 23:12 Reserved + * 26:24 PCISwap PCI Master Data Swap Control (0: Byte Swap; + * 1: No swapping; 2: Both byte and word swap; + * 3: Word swap; 4..7: Reserved) + * 27:27 PCIReq64 PCI master REQ64* policy (Relevant only when + * configured to 64-bit PCI bus and not I/O) + * 0: Assert s REQ64* only when transaction + * is longer than 64-bits. + * 1: Always assert REQ64*. + * 31:28 Reserved + */ +#define GT_PCISwap_GET(v) GT__EXT((v), 24, 3) +#define GT_PCISwap_ByteSwap 0 +#define GT_PCISwap_NoSwap 1 +#define GT_PCISwap_ByteWordSwap 2 +#define GT_PCISwap_WordSwap 3 +#define GT_PCI_LowDecode_PCIReq64 GT__BIT(27) + +/* + * Table 55: PCI_0 I/O High Decode Address, Offset: 0x050 + * Table 57: PCI_0 Memory 0 High Decode Address, Offset: 0x060 + * Table 59: PCI_0 Memory 1 High Decode Address, Offset: 0x088 + * Table 61: PCI_0 Memory 2 High Decode Address, Offset: 0x260 + * Table 63: PCI_0 Memory 3 High Decode Address, Offset: 0x288 + * Table 65: PCI_1 I/O High Decode Address, Offset: 0x098 + * Table 67: PCI_1 Memory 0 High Decode Address, Offset: 0x0a8 + * Table 69: PCI_1 Memory 1 High Decode Address, Offset: 0x0b8 + * Table 71: PCI_1 Memory 2 High Decode Address, Offset: 0x2a8 + * Table 73: PCI_1 Memory 3 High Decode Address, Offset: 0x2b8 + * + * 11:00 HighAddr PCI_0 I/O Space Top Address + * 31:12 Reserved + */ + +/* + * Table 74: Internal Space Decode, Offset: 0x068 + * 15:00 IntDecode GT64260 Internal Space Base Address + * 23:16 Reserved + * 26:24 PCISwap Same as PCI_0 Memory 0 Low Decode Address. + * NOTE: Reserved for Galileo Technology usage. + * Relevant only for PCI master configuration + * transactions on the PCI bus. + * 31:27 Reserved + */ + +/* + * Table 79: PCI_0 I/O Address Remap, Offset: 0x0f0 + * Table 80: PCI_0 Memory 0 Address Remap Low, Offset: 0x0f8 + * Table 82: PCI_0 Memory 1 Address Remap Low, Offset: 0x100 + * Table 84: PCI_0 Memory 2 Address Remap Low, Offset: 0x2f8 + * Table 86: PCI_0 Memory 3 Address Remap Low, Offset: 0x300 + * Table 88: PCI_1 I/O Address Remap, Offset: 0x108 + * Table 89: PCI_1 Memory 0 Address Remap Low, Offset: 0x110 + * Table 91: PCI_1 Memory 1 Address Remap Low, Offset: 0x118 + * Table 93: PCI_1 Memory 2 Address Remap Low, Offset: 0x310 + * Table 95: PCI_1 Memory 3 Address Remap Low, Offset: 0x318 + * + * 11:00 Remap PCI IO/Memory Space Address Remap (31:20) + * 31:12 Reserved + */ + +/* + * Table 81: PCI_0 Memory 0 Address Remap High, Offset: 0x320 + * Table 83: PCI_0 Memory 1 Address Remap High, Offset: 0x328 + * Table 85: PCI_0 Memory 2 Address Remap High, Offset: 0x330 + * Table 87: PCI_0 Memory 3 Address Remap High, Offset: 0x338 + * Table 90: PCI_1 Memory 0 Address Remap High, Offset: 0x340 + * Table 92: PCI_1 Memory 1 Address Remap High, Offset: 0x348 + * Table 94: PCI_1 Memory 2 Address Remap High, Offset: 0x350 + * Table 96: PCI_1 Memory 3 Address Remap High, Offset: 0x358 + * + * 31:00 Remap PCI Memory Address Remap (high 32 bits) + */ + +/* + * Table 97: CPU Configuration, Offset: 0x000 + * 07:00 NoMatchCnt CPU Address Miss Counter + * 08:08 NoMatchCntEn CPU Address Miss Counter Enable + * NOTE: Relevant only if multi-GT is enabled. + * (0: Disabled; 1: Enabled) + * 09:09 NoMatchCntExt CPU address miss counter MSB + * 10:10 Reserved + * 11:11 AACKDelay Address Acknowledge Delay + * 0: AACK* is asserted one cycle after TS*. + * 1: AACK* is asserted two cycles after TS*. + * 12:12 Endianess Must be 0 + * NOTE: The GT64260 does not support the PowerPC + * Little Endian convention + * 13:13 Pipeline Pipeline Enable + * 0: Disabled. The GT64260 will not respond with + * AACK* to a new CPU transaction, before the + * previous transaction data phase completes. + * 1: Enabled. + * 14:14 Reserved + * 15:15 TADelay Transfer Acknowledge Delay + * 0: TA* is asserted one cycle after AACK* + * 1: TA* is asserted two cycles after AACK* + * 16:16 RdOOO Read Out of Order Completion + * 0: Not Supported, Data is always returned in + * order (DTI[0-2] is always driven + * 1: Supported + * 17:17 StopRetry Relevant only if PCI Retry is enabled + * 0: Keep Retry all PCI transactions targeted + * to the GT64260. + * 1: Stop Retry of PCI transactions. + * 18:18 MultiGTDec Multi-GT Address Decode + * 0: Normal address decoding + * 1: Multi-GT address decoding + * 19:19 DPValid CPU DP[0-7] Connection. CPU write parity ... + * 0: is not checked. (Not connected) + * 1: is checked (Connected) + * 21:20 Reserved + * 22:22 PErrProp Parity Error Propagation + * 0: GT64260 always drives good parity on + * DP[0-7] during CPU reads. + * 1: GT64260 drives bad parity on DP[0-7] in case + * the read response from the target interface + * comes with erroneous data indication + * (e.g. ECC error from SDRAM interface). + * 25:23 Reserved + * 26:26 APValid CPU AP[0-3] Connection. CPU address parity ... + * 0: is not checked. (Not connected) + * 1: is checked (Connected) + * 27:27 RemapWrDis Address Remap Registers Write Control + * 0: Write to Low Address decode register. + * Results in writing of the corresponding + * Remap register. + * 1: Write to Low Address decode register. No + * affect on the corresponding Remap register. + * 28:28 ConfSBDis Configuration Read Sync Barrier Disable + * 0: enabled; 1: disabled + * 29:29 IOSBDis I/O Read Sync Barrier Disable + * 0: enabled; 1: disabled + * 30:30 ClkSync Clocks Synchronization + * 0: The CPU interface is running with SysClk, + * which is asynchronous to TClk. + * 1: The CPU interface is running with TClk. + * 31:31 Reserved + */ +#define GT_CPUCfg_NoMatchCnt_GET(v) GT__EXT((v), 0, 8) +#define GT_CPUCfg_NoMatchCntEn GT__BIT( 9) +#define GT_CPUCfg_NoMatchCntExt GT__BIT(10) +#define GT_CPUCfg_AACKDelay GT__BIT(11) +#define GT_CPUCfg_Endianess GT__BIT(12) +#define GT_CPUCfg_Pipeline GT__BIT(13) +#define GT_CPUCfg_TADelay GT__BIT(15) +#define GT_CPUCfg_RdOOO GT__BIT(16) +#define GT_CPUCfg_StopRetry GT__BIT(17) +#define GT_CPUCfg_MultiGTDec GT__BIT(18) +#define GT_CPUCfg_DPValid GT__BIT(19) +#define GT_CPUCfg_PErrProp GT__BIT(22) +#define GT_CPUCfg_APValid GT__BIT(26) +#define GT_CPUCfg_RemapWrDis GT__BIT(27) +#define GT_CPUCfg_ConfSBDis GT__BIT(28) +#define GT_CPUCfg_IOSBDis GT__BIT(29) +#define GT_CPUCfg_ClkSync GT__BIT(30) + +/* + * Table 98: CPU Mode, Offset: 0x120, Read only + * 01:00 MultiGTID Multi-GT ID + * Represents the ID to which the GT64260 responds + * to during a multi-GT address decoding period. + * 02:02 MultiGT (0: Single; 1: Multiple) GT configuration + * 03:03 RetryEn (0: Don't; 1: Do) Retry PCI transactions + * 07:04 CPUType + * 0x0-0x3: Reserved + * 0x4: 64-bit PowerPC CPU, 60x bus + * 0x5: 64-bit PowerPC CPU, MPX bus + * 0x6-0xf: Reserved + * 31:08 Reserved + */ +#define GT_CPUMode_MultiGTID_GET(v) GT__EXT(v, 0, 2) +#define GT_CPUMode_MultiGT GT__BIT(2) +#define GT_CPUMode_RetryEn GT__BIT(3) +#define GT_CPUMode_CPUType_GET(v) GT__EXT(v, 4, 4) + +/* + * Table 99: CPU Master Control, Offset: 0x160 + * 07:00 Reserved + * 08:08 IntArb CPU Bus Internal Arbiter Enable + * NOTE: Only relevant to 60x bus mode. When + * running MPX bus, the GT64260 internal + * arbiter must be used. + * 0: Disabled. External arbiter is required. + * 1: Enabled. Use the GT64260 CPU bus arbiter. + * 09:09 IntBusCtl CPU Interface Unit Internal Bus Control + * NOTE: This bit must be set to 1. It is reserved + * for Galileo Technology usage. + * 0: Enable internal bus sharing between master + * and slave interfaces. + * 1: Disable internal bus sharing between master + * and slave interfaces. + * 10:10 MWrTrig Master Write Transaction Trigger + * 0: With first valid write data + * 1: With last valid write data + * 11:11 MRdTrig Master Read Response Trigger + * 0: With first valid read data + * 1: With last valid read data + * 12:12 CleanBlock Clean Block Snoop Transaction Support + * 0: CPU does not support clean block (603e,750) + * 1: CPU supports clean block (604e,G4) + * 13:13 FlushBlock Flush Block Snoop Transaction Support + * 0: CPU does not support flush block (603e,750) + * 1: CPU supports flush block (604e,G4) + * 31:14 Reserved + */ +#define GT_CPUMstrCtl_IntArb GT__BIT(8) +#define GT_CPUMstrCtl_IntBusCtl GT__BIT(9) +#define GT_CPUMstrCtl_MWrTrig GT__BIT(10) +#define GT_CPUMstrCtl_MRdTrig GT__BIT(11) +#define GT_CPUMstrCtl_CleanBlock GT__BIT(12) +#define GT_CPUMstrCtl_FlushBlock GT__BIT(13) + +#define GT_ArbSlice_SDRAM 0x0 /* SDRAM interface snoop request */ +#define GT_ArbSlice_DEVICE 0x1 /* Device request */ +#define GT_ArbSlice_NULL 0x2 /* NULL request */ +#define GT_ArbSlice_PCI0 0x3 /* PCI_0 access */ +#define GT_ArbSlice_PCI1 0x4 /* PCI_1 access */ +#define GT_ArbSlice_COMM 0x5 /* Comm unit access */ +#define GT_ArbSlice_IDMA0123 0x6 /* IDMA channels 0/1/2/3 access */ +#define GT_ArbSlice_IDMA4567 0x7 /* IDMA channels 4/5/6/7 access */ + /* 0x8-0xf: Reserved */ + +/* Pass in the slice number (from 0..16) as 'n' + */ +#define GT_XbarCtl_GET_ArbSlice(v, n) GT__EXT((v), (((n) & 7)*4, 4) + +/* + * Table 100: CPU Interface Crossbar Control Low, Offset: 0x150 + * 03:00 Arb0 Slice 0 of CPU Master pizza Arbiter + * 07:04 Arb1 Slice 1 of CPU Master pizza Arbiter + * 11:08 Arb2 Slice 2 of CPU Master pizza Arbiter + * 15:12 Arb3 Slice 3 of CPU Master pizza Arbiter + * 19:16 Arb4 Slice 4 of CPU Master pizza Arbiter + * 23:20 Arb5 Slice 5 of CPU Master pizza Arbiter + * 27:24 Arb6 Slice 6 of CPU Master pizza Arbiter + * 31:28 Arb7 Slice 7 of CPU Master pizza Arbiter + */ + +/* + * Table 101: CPU Interface Crossbar Control High, Offset: 0x158 + * 03:00 Arb8 Slice 8 of CPU Master pizza Arbiter + * 07:04 Arb9 Slice 9 of CPU Master pizza Arbiter + * 11:08 Arb10 Slice 10 of CPU Master pizza Arbiter + * 15:12 Arb11 Slice 11 of CPU Master pizza Arbiter + * 19:16 Arb12 Slice 12 of CPU Master pizza Arbiter + * 23:20 Arb13 Slice 13 of CPU Master pizza Arbiter + * 27:24 Arb14 Slice 14 of CPU Master pizza Arbiter + * 31:28 Arb15 Slice 15 of CPU Master pizza Arbiter + */ + +/* + * Table 102: CPU Interface Crossbar Timeout, Offset: 0x168 + * NOTE: Reserved for Galileo Technology usage. + * 07:00 Timeout Crossbar Arbiter Timeout Preset Value + * 15:08 Reserved + * 16:16 TimeoutEn Crossbar Arbiter Timer Enable + * (0: Enable; 1: Disable) + * 31:17 Reserved + */ + +/* + * Table 103: CPU Read Response Crossbar Control Low, Offset: 0x170 + * 03:00 Arb0 Slice 0 of CPU Slave pizza Arbiter + * 07:04 Arb1 Slice 1 of CPU Slave pizza Arbiter + * 11:08 Arb2 Slice 2 of CPU Slave pizza Arbiter + * 15:12 Arb3 Slice 3 of CPU Slave pizza Arbiter + * 19:16 Arb4 Slice 4 of CPU Slave pizza Arbiter + * 23:20 Arb5 Slice 5 of CPU Slave pizza Arbiter + * 27:24 Arb6 Slice 6 of CPU Slave pizza Arbiter + * 31:28 Arb7 Slice 7 of CPU Slave pizza Arbiter + */ +/* + * Table 104: CPU Read Response Crossbar Control High, Offset: 0x178 + * 03:00 Arb8 Slice 8 of CPU Slave pizza Arbiter + * 07:04 Arb9 Slice 9 of CPU Slave pizza Arbiter + * 11:08 Arb10 Slice 10 of CPU Slave pizza Arbiter + * 15:12 Arb11 Slice 11 of CPU Slave pizza Arbiter + * 19:16 Arb12 Slice 12 of CPU Slave pizza Arbiter + * 23:20 Arb13 Slice 13 of CPU Slave pizza Arbiter + * 27:24 Arb14 Slice 14 of CPU Slave pizza Arbiter + * 31:28 Arb15 Slice 15 of CPU Slave pizza Arbiter + */ + +/* + * Table 105: PCI_0 Sync Barrier Virtual Register, Offset: 0x0c0 + * Table 106: PCI_1 Sync Barrier Virtual Register, Offset: 0x0c8 + * NOTE: The read data is random and should be ignored. + * 31:00 SyncBarrier A CPU read from this register creates a + * synchronization barrier cycle. + */ + +/* + * Table 107: CPU Protect Address 0 Low, Offset: 0x180 + * Table 109: CPU Protect Address 1 Low, Offset: 0x190 + * Table 111: CPU Protect Address 2 Low, Offset: 0x1a0 + * Table 113: CPU Protect Address 3 Low, Offset: 0x1b0 + * Table 115: CPU Protect Address 4 Low, Offset: 0x1c0 + * Table 117: CPU Protect Address 5 Low, Offset: 0x1d0 + * Table 119: CPU Protect Address 6 Low, Offset: 0x1e0 + * Table 121: CPU Protect Address 7 Low, Offset: 0x1f0 + * + * 11:00 LowAddr CPU Protect Region Base Address + * Corresponds to address bits[31:20]. + * 15:12 Reserved. Must be 0 + * 16:16 AccProtect CPU Access Protect + * Access is (0: allowed; 1: forbidden) + * 17:17 WrProtect CPU Write Protect + * Writes are (0: allowed; 1: forbidden) + * 18:18 CacheProtect CPU caching protect. Caching (block read) + * is (0: allowed; 1: forbidden) + * 31:19 Reserved + */ +#define GT_CPU_AccProtect GT__BIT(16) +#define GT_CPU_WrProtect GT__BIT(17) +#define GT_CPU_CacheProtect GT__BIT(18) + +/* + * Table 108: CPU Protect Address 0 High, Offset: 0x188 + * Table 110: CPU Protect Address 1 High, Offset: 0x198 + * Table 112: CPU Protect Address 2 High, Offset: 0x1a8 + * Table 114: CPU Protect Address 3 High, Offset: 0x1b8 + * Table 116: CPU Protect Address 4 High, Offset: 0x1c8 + * Table 118: CPU Protect Address 5 High, Offset: 0x1d8 + * Table 120: CPU Protect Address 6 High, Offset: 0x1e8 + * Table 122: CPU Protect Address 7 High, Offset: 0x1f8 + * + * 11:00 HighAddr CPU Protect Region Top Address + * Corresponds to address bits[31:20] + * 31:12 Reserved + */ + +/* + * Table 123: Snoop Base Address 0, Offset: 0x380 + * Table 125: Snoop Base Address 1, Offset: 0x390 + * Table 127: Snoop Base Address 2, Offset: 0x3a0 + * Table 129: Snoop Base Address 3, Offset: 0x3b0 + * + * 11:00 LowAddr Snoop Region Base Address [31:20] + * 15:12 Reserved Must be 0. + * 17:16 Snoop Snoop Type + * 0x0: No Snoop + * 0x1: Snoop to WT region + * 0x2: Snoop to WB region + * 0x3: Reserved + * 31:18 Reserved + */ +#define GT_Snoop_GET(v) GT__EXT((v), 16, 2) +#define GT_Snoop_INS(v) GT__INS((v), 16) +#define GT_Snoop_None 0 +#define GT_Snoop_WT 1 +#define GT_Snoop_WB 2 + + +/* + * Table 124: Snoop Top Address 0, Offset: 0x388 + * Table 126: Snoop Top Address 1, Offset: 0x398 + * Table 128: Snoop Top Address 2, Offset: 0x3a8 + * Table 130: Snoop Top Address 3, Offset: 0x3b8 + * 11:00 HighAddr Snoop Region Top Address [31:20] + * 31:12 Reserved + */ + + +/* + * Table 131: CPU Error Address Low, Offset: 0x070, Read Only. + * In case of multiple errors, only the first one is latched. New error + * report latching is enabled only after the CPU Error Address Low register + * is being read. + * 31:00 ErrAddr Latched address bits [31:0] of a CPU + * transaction in case of: + * o illegal address (failed address decoding) + * o access protection violation + * o bad data parity + * o bad address parity + * Upon address latch, no new address are + * registered (due to additional error condition), + * until the register is being read. + */ + +/* + * Table 132: CPU Error Address High, Offset: 0x078, Read Only. + * Once data is latched, no new data can be registered (due to additional + * error condition), until CPU Error Low Address is being read (which + * implies, it should be the last being read by the interrupt handler). + * 03:00 Reserved + * 07:04 ErrPar Latched address parity bits in case + * of bad CPU address parity detection. + * 31:08 Reserved + */ +#define GT_CPUErrorAddrHigh_ErrPar_GET(v) GT__EXT((v), 4, 4) + +/* + * Table 133: CPU Error Data Low, Offset: 0x128, Read only. + * 31:00 PErrData Latched data bits [31:0] in case of bad data + * parity sampled on write transactions or on + * master read transactions. + */ + +/* + * Table 134: CPU Error Data High, Offset: 0x130, Read only. + * 31:00 PErrData Latched data bits [63:32] in case of bad data + * parity sampled on write transactions or on + * master read transactions. + */ + +/* + * Table 135: CPU Error Parity, Offset: 0x138, Read only. + * 07:00 PErrPar Latched data parity bus in case of bad data + * parity sampled on write transactions or on + * master read transactions. + * 31:10 Reserved + */ +#define GT_CPUErrorParity_PErrPar_GET(v) GT__EXT((v), 0, 8) + +/* + * Table 136: CPU Error Cause, Offset: 0x140 + * Bits[7:0] are clear only. A cause bit is set upon an error condition + * occurrence. Write a 0 value to clear the bit. Writing a 1 value has + * no affect. + * 00:00 AddrOut CPU Address Out of Range + * 01:01 AddrPErr Bad Address Parity Detected + * 02:02 TTErr Transfer Type Violation. + * The CPU attempts to burst (read or write) to an + * internal register. + * 03:03 AccErr Access to a Protected Region + * 04:04 WrErr Write to a Write Protected Region + * 05:05 CacheErr Read from a Caching protected region + * 06:06 WrDataPErr Bad Write Data Parity Detected + * 07:07 RdDataPErr Bad Read Data Parity Detected + * 26:08 Reserved + * 31:27 Sel Specifies the error event currently being + * reported in Error Address, Error Data, and + * Error Parity registers. + * 0x0: AddrOut + * 0x1: AddrPErr + * 0x2: TTErr + * 0x3: AccErr + * 0x4: WrErr + * 0x5: CacheErr + * 0x6: WrDataPErr + * 0x7: RdDataPErr + * 0x8-0x1f: Reserved + */ +#define GT_CPUError_AddrOut GT__BIT(GT_CPUError_Sel_AddrOut) +#define GT_CPUError_AddrPErr GT__BIT(GT_CPUError_Sel_AddrPErr) +#define GT_CPUError_TTErr GT__BIT(GT_CPUError_Sel_TTErr) +#define GT_CPUError_AccErr GT__BIT(GT_CPUError_Sel_AccErr) +#define GT_CPUError_WrErr GT__BIT(GT_CPUError_Sel_WrPErr) +#define GT_CPUError_CacheErr GT__BIT(GT_CPUError_Sel_CachePErr) +#define GT_CPUError_WrDataPErr GT__BIT(GT_CPUError_Sel_WrDataPErr) +#define GT_CPUError_RdDataPErr GT__BIT(GT_CPUError_Sel_RdDataPErr) + +#define GT_CPUError_Sel_AddrOut 0 +#define GT_CPUError_Sel_AddrPErr 1 +#define GT_CPUError_Sel_TTErr 2 +#define GT_CPUError_Sel_AccErr 3 +#define GT_CPUError_Sel_WrErr 4 +#define GT_CPUError_Sel_CacheErr 5 +#define GT_CPUError_Sel_WrDataPErr 6 +#define GT_CPUError_Sel_RdDataPErr 7 + +#define GT_CPUError_Sel_GET(v) GT__EXT((v), 27, 5) + +/* + * Table 137: CPU Error Mask, Offset: 0x148 + * 00:00 AddrOut If set to 1, enables AddrOut interrupt. + * 01:01 AddrPErr If set to 1, enables AddrPErr interrupt. + * 02:02 TTErr If set to 1, enables TTErr interrupt. + * 03:03 AccErr If set to 1, enables AccErr interrupt. + * 04:04 WrErr If set to 1, enables WrErr interrupt. + * 05:05 CacheErr If set to 1, enables CacheErr interrupt. + * 06:06 WrDataPErr If set to 1, enables WrDataPErr interrupt. + * 07:07 RdDataPErr If set to 1, enables RdDataPErr interrupt. + * 31:08 Reserved + */ + +/* + * Comm Unit Interrupt registers + */ + +/* Comm Unit Arbiter Control */ +#define GT_CommUnitArb_Ctrl 0xf300 +/* GPP IRQs level vs. edge sensitivity */ +#define GT_CommUnitArb_Ctrl_GPP_Ints_Level_Sensitive (1<<10) + +#define GT_CommUnitIntr_Cause 0xf310 +#define GT_CommUnitIntr_Mask 0xf314 +#define GT_CommUnitIntr_ErrAddr 0xf318 + +#define GT_CommUnitIntr_E0 0x00000007 +#define GT_CommUnitIntr_E1 0x00000070 +#define GT_CommUnitIntr_E2 0x00000700 +#define GT_CommUnitIntr_S0 0x00070000 +#define GT_CommUnitIntr_S1 0x00700000 +#define GT_CommUnitIntr_Sel 0x70000000 + +/* + * SDRAM Error Report (ECC) Registers + */ +#define GT_260_ECC_Data_Lo 0x484 /* latched Error Data (low) */ +#define GT_260_ECC_Data_Hi 0x480 /* latched Error Data (high) */ +#define GT_260_ECC_Addr 0x490 /* latched Error Address */ +#define GT_260_ECC_Rec 0x488 /* latched ECC code from SDRAM */ +#define GT_260_ECC_Calc 0x48c /* latched ECC code from SDRAM */ +#define GT_260_ECC_Ctl 0x494 /* ECC Control */ +#define GT_260_ECC_Count 0x498 /* ECC 1-bit error count */ + +/* Timer/Counter Registers (t. straumann) + */ +#define GT_TIMER_0 0x0850 /* preset / running value */ +#define GT_TIMER_1 0x0854 +#define GT_TIMER_2 0x0858 +#define GT_TIMER_3 0x085c + +#define GT_TIMER_0_3_Ctl 0x0864 + +#define GT_TIMER_0_Ctl_Enb 0x00000001 /* enable timer */ +#define GT_TIMER_0_Ctl_Rld 0x00000002 /* reload after expiration */ +#define GT_TIMER_1_Ctl_Enb 0x00000100 /* enable timer */ +#define GT_TIMER_1_Ctl_Rld 0x00000200 /* reload after expiration */ +#define GT_TIMER_2_Ctl_Enb 0x00010000 /* enable timer */ +#define GT_TIMER_2_Ctl_Rld 0x00020000 /* reload after expiration */ +#define GT_TIMER_3_Ctl_Enb 0x01000000 /* enable timer */ +#define GT_TIMER_3_Ctl_Rld 0x02000000 /* reload after expiration */ + +#define GT_TIMER_0_3_Intr_Cse 0x0868 +#define GT_TIMER_0_Intr 0x00000001 +#define GT_TIMER_1_Intr 0x00000002 +#define GT_TIMER_2_Intr 0x00000004 +#define GT_TIMER_3_Intr 0x00000008 +#define GT_TIMER_Intr_Smry 0x80000000 /* Interrupt Summary */ + +#define GT_TIMER_0_3_Intr_Msk 0x086c + +/* + * Watchdog Registers + */ +#define GT_WDOG_Config 0xb410 +#define GT_WDOG_Value 0xb414 +#define GT_WDOG_Value_NMI GT__MASK(24) +#define GT_WDOG_Config_Preset GT__MASK(24) +#define GT_WDOG_Config_Ctl1a GT__BIT(24) +#define GT_WDOG_Config_Ctl1b GT__BIT(25) +#define GT_WDOG_Config_Ctl2a GT__BIT(26) +#define GT_WDOG_Config_Ctl2b GT__BIT(27) +#define GT_WDOG_Config_Enb GT__BIT(31) + +#define GT_WDOG_NMI_DFLT (GT__MASK(24) & GT_WDOG_Value_NMI) +#define GT_WDOG_Preset_DFLT (GT__MASK(22) & GT_WDOG_Config_Preset) + +/* + * Device Bus Interrupts + */ +#define GT_DEVBUS_ICAUSE 0x4d0 /* Device Interrupt Cause */ +#define GT_DEVBUS_IMASK 0x4d4 /* Device Interrupt Mask */ +#define GT_DEVBUS_ERR_ADDR 0x4d8 /* Device Error Address */ + +/* + * bit defines for GT_DEVBUS_ICAUSE, GT_DEVBUS_IMASK + */ +#define GT_DEVBUS_DBurstErr GT__BIT(0) +#define GT_DEVBUS_DRdyErr GT__BIT(1) +#define GT_DEVBUS_Sel GT__BIT(27) +#define GT_DEVBUS_RES ~(GT_DEVBUS_DBurstErr|GT_DEVBUS_DRdyErr|GT_DEVBUS_Sel) + +/* MV64360 */ +/* Enable individual CPU windows by *clearing* respective bits + * in MV_64360_BASE_ADDR_DISBL + * + * Bit ordering is: + * + * SDRAM_CS_0..3 (1<<0..3) + * DEV_CS_0..3 (1<<4..7) + * BOOT_CS_0..3 (1<<8) + * PCI_0_IO (1<<9) + * PCI_0_MEM_0..3 (1<<10..13) + * PCI_1_IO (1<<14) + * PCI_1_MEM_0..3 (1<<15..18) + * INTERNAL_SRAM (1<<19) + * MV64x60_REGS (1<<20) + */ +#define MV_64360_BASE_ADDR_DISBL (0x278) + +/* Internal SRAM */ +#define MV_64360_SRAM_BASE (0x268) +#define MV_64360_SRAM_CTRL (0x380) +/* Control register bits */ +#define MV_64360_SRAM_CacheWb GT__BIT(1) +/* default setup used by linux, motload (uses 90 instead of b0), ... + * Comments say: + * - parity enabled, + * - parity error propagation + * - arbitration not parked for CPU only + * - other bits are reserved + */ +#define MV_64360_SRAM_Ctl_Setup (0x001600b0) + +#define MV_64360_SRAM_TEST_MODE (0x3f4) +#define MV_64340_SRAM_ERR_CAUSE (0x388) +#define MV_64340_SRAM_ERR_ADDR (0x390) +#define MV_64340_SRAM_ERR_ADDR_HI (0X3f8) +#define MV_64340_SRAM_ERR_DATA_LO (0x398) +#define MV_64340_SRAM_ERR_DATA_HI (0x3a0) +#define MV_64340_SRAM_ERR_DATA_PARITY (0x3a8) + +#endif /* !_DISCOVERY_DEV_GTREG_H */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/LICENSE b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/LICENSE new file mode 100644 index 0000000000..ba90c4a817 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/LICENSE @@ -0,0 +1,31 @@ +$FreeBSD: /repoman/r/ncvs/src/sys/dev/em/LICENSE,v 1.3 2005/01/06 01:42:38 imp Exp $ +/*- +Copyright (c) 2001-2003, 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. +*/ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/Makefile b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/Makefile new file mode 100644 index 0000000000..af3d788b53 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/Makefile @@ -0,0 +1,92 @@ +# +# Makefile.lib,v 1.5 2000/06/12 15:00:14 joel Exp +# +# Templates/Makefile.lib +# Template library Makefile +# + +LIBNAME=libif_em.a # xxx- your library names goes here +#PGMS=${ARCH}/if_em.obj +LIB=${ARCH}/${LIBNAME} + +# C and C++ source names, if any, go here -- minus the .c or .cc +C_PIECES=if_em_hw if_em if_em_rtems +#C_PIECES=if_em_hw if_em if_em_rtems if_em.modini +MODOBJS=$(ARCH)/if_em.o $(ARCH)/if_em_hw.o $(ARCH)/if_em.modini.o +C_FILES=$(C_PIECES:%=%.c) +C_O_FILES=$(C_PIECES:%=${ARCH}/%.o) + +CC_PIECES= +CC_FILES=$(CC_PIECES:%=%.cc) +CC_O_FILES=$(CC_PIECES:%=${ARCH}/%.o) + +H_FILES= + +# Assembly source names, if any, go here -- minus the .S +S_PIECES= +S_FILES=$(S_PIECES:%=%.S) +S_O_FILES=$(S_FILES:%.S=${ARCH}/%.o) + +SRCS=$(C_FILES) $(CC_FILES) $(H_FILES) $(S_FILES) +OBJS=$(C_O_FILES) $(CC_O_FILES) $(S_O_FILES) + +include $(RTEMS_MAKEFILE_PATH)/Makefile.inc + +include $(RTEMS_CUSTOM) +include $(RTEMS_ROOT)/make/lib.cfg + +# +# Add local stuff here using += +# + +DEFINES += -DHAVE_LIBBSPEXT +CPPFLAGS += -I. -Ilibchip -Iporting +CFLAGS += -Wno-unused-variable -msoft-float + +# +# Add your list of files to delete here. The config files +# already know how to delete some stuff, so you may want +# to just run 'make clean' first to see what gets missed. +# 'make clobber' already includes 'make clean' +# + +CLEAN_ADDITIONS += +CLOBBER_ADDITIONS += + +all: ${ARCH} $(SRCS) $(LIB) ${PGMS} + +# doesn't work if we define this just after OBJS= :-( + +# must be after inclusion of RTEMS_CUSTOM + +$(LIB): OBJS=$(filter-out %.modini.o,$(OBJS)) + +$(LIB): ${OBJS} + $(make-library) + +#How to make a relocatable object +$(filter %.obj, $(PGMS)): $(MODOBJS) + $(make-obj) + +ifndef RTEMS_SITE_INSTALLDIR +RTEMS_SITE_INSTALLDIR = $(PROJECT_RELEASE) +endif + +${RTEMS_SITE_INSTALLDIR}/include \ +${RTEMS_SITE_INSTALLDIR}/lib \ +${RTEMS_SITE_INSTALLDIR}/bin \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/include \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/lib \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/bin : + test -d $@ || mkdir -p $@ + +# Install the library, appending _g or _p as appropriate. +# for include files, just use $(INSTALL_CHANGE) +# +# NOTES: +# - BSP specific libraries, headers etc. should be installed to +# $RTEMS_SITE_INSTALLDIR)/$(RTEMS_BSP)/lib +# + +install: all $(RTEMS_SITE_INSTALLDIR)/lib + $(INSTALL_VARIANT) -m 644 ${LIB} ${RTEMS_SITE_INSTALLDIR}/lib diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/README b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/README new file mode 100644 index 0000000000..b4eef8dbb7 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/README @@ -0,0 +1,332 @@ +$FreeBSD: /repoman/r/ncvs/src/sys/dev/em/README,v 1.10 2005/07/11 02:33:25 delphij Exp $ +FreeBSD* Driver for the Intel(R) PRO/1000 Family of Adapters +============================================================ + +March 18, 2005 + + +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, version 2.1.x, for the Intel(R) +PRO/1000 Family of Adapters. This driver has been developed for use with +FreeBSD, version 5.x. + +For questions related to hardware requirements, refer to the documentation +supplied with your Intel PRO/1000 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/adapter/pro100/21397.htm + + +For the latest Intel network drivers for FreeBSD, see: + +http://appsr.intel.com/scripts-df/support_intel.asp + + +NOTE: Mobile adapters are not fully supported. + + +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 xvfz 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 in 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: + + cd em-x.x.x/src + + cp if_em* /usr/src/sys/dev/em + + cp Makefile.kernel /usr/src/sys/modules/em/Makefile + + Edit the /usr/src/sys/conf/files.i386 file, and add the following lines only if + they don't already exist: + + dev/em/if_em.c optional em + + dev/em/if_em_hw.c optional em + + Remove the following lines from the /usr/src/sys/conf/files.i386 file, + if they exist: + + dev/em/if_em_fxhw.c optional em + dev/em/if_em_phy.c optional 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. + + +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 increase the MTU + beyond 1500 bytes. + + NOTES: Only enable Jumbo Frames if your network infrastructure supports + them. + + The Jumbo Frames setting on the switch must be set to at least + 22 bytes larger than that of the MTU. + + The Intel PRO/1000 PM Network Connection does not support jumbo + frames. + + + The Jumbo Frames MTU range for Intel Adapters is 1500 to 16114. The default + MTU range is 1500. To modify the setting, enter the following: + + ifconfig em mtu 9000 + + To confirm the MTU used between two specific devices, use: + + route get + + 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 vlan10 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: + + ifconfig destroy + + Polling + ------- + NOTES: DEVICE POLLING is only valid for non-SMP kernels. + + The driver has to be compiled into the kernel for DEVICE POLLING to be + enabled in the driver. + + 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: + sysctl kern.polling.enable=1 to turn polling on + Use: + sysctl kern.polling.enable=0 to turn polling 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. + +Known Limitations +================= + + There are known performance issues with this driver when running UDP traffic + with Jumbo Frames. + + 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 uncomment the #define EM_MASTER_SLAVE + from within the comments. For example, change from: + + /* #define EM_MASTER_SLAVE 2 */ + 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/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.c new file mode 100644 index 0000000000..749a4877a4 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.c @@ -0,0 +1,3846 @@ +/************************************************************************** + +Copyright (c) 2001-2005, 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: /repoman/r/ncvs/src/sys/dev/em/if_em.c,v 1.67 2005/08/03 00:18:29 rwatson Exp $*/ +#ifndef __rtems__ +#include +#else +#include +#include +#include +#include +#endif + +/********************************************************************* + * Set this to one to display debug statistics + *********************************************************************/ +int em_display_debug_stats = 0; + +/********************************************************************* + * Linked list of board private structures for all NICs found + *********************************************************************/ + +struct adapter *em_adapter_list = NULL; + + +/********************************************************************* + * Driver version + *********************************************************************/ + +char em_driver_version[] = "2.1.7"; + + +/********************************************************************* + * PCI Device ID Table + * + * Used by probe to select devices to load on + * Last field stores an index into em_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}, + + { 0x8086, E1000_DEV_ID_82542, PCI_ANY_ID, PCI_ANY_ID, 0}, + + { 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_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_82573E, PCI_ANY_ID, PCI_ANY_ID, 0}, + { 0x8086, E1000_DEV_ID_82573E_IAMT, 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); +#if !defined(__rtems__) || defined(DEBUG_MODULAR) +static int em_detach(device_t); +#endif +#ifndef __rtems__ +static int em_shutdown(device_t); +#endif +static void em_intr(void *); +static void em_start(struct ifnet *); +#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 ifnet *); +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 *); +#ifndef __rtems__ +static int em_media_change(struct ifnet *); +#else +static int em_media_change(struct ifnet *ifp, struct rtems_ifmedia *ifm); +#endif +static void em_identify_hardware(struct adapter *); +static int em_allocate_pci_resources(struct adapter *); +#ifndef __rtems__ +static void em_free_pci_resources(struct adapter *); +static void em_local_timer(void *); +#endif +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_clean_transmit_interrupts(struct adapter *); +static int em_allocate_receive_structures(struct adapter *); +static int em_allocate_transmit_structures(struct adapter *); +static void em_process_receive_interrupts(struct adapter *, int); +#ifndef __rtems__ +static void em_receive_checksum(struct adapter *, + struct em_rx_desc *, + struct mbuf *); +static void em_transmit_checksum_setup(struct adapter *, + struct mbuf *, + u_int32_t *, + u_int32_t *); +#endif +static void em_set_promisc(struct adapter *); +static void em_disable_promisc(struct adapter *); +static void em_set_multi(struct adapter *); +static void em_print_hw_stats(struct adapter *); +static void em_print_link_status(struct adapter *); +static int em_get_buf(int i, struct adapter *, + struct mbuf *); +#ifndef __rtems__ +static void em_enable_vlans(struct adapter *); +static void em_disable_vlans(struct adapter *); +#endif +static int em_encap(struct adapter *, struct mbuf **); +#ifndef __rtems__ +static void em_smartspeed(struct adapter *); +#endif +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 *); +#ifndef __rtems__ +static void em_82547_move_tail(void *arg); +#endif +static void em_82547_move_tail_locked(struct adapter *); +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 *); +#endif +static int em_is_valid_ether_addr(u_int8_t *); +#ifndef __rtems__ +static int em_sysctl_stats(SYSCTL_HANDLER_ARGS); +static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS); +#endif +static u_int32_t em_fill_descriptors (u_int64_t address, + u_int32_t length, + PDESC_ARRAY desc_array); +#ifndef __rtems__ +static int em_sysctl_int_delay(SYSCTL_HANDLER_ARGS); +static void em_add_int_delay_sysctl(struct adapter *, const char *, + const char *, struct em_int_delay_info *, + int, int); +#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), + {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 +net_drv_tbl_t METHODS = { + n_probe : em_probe, + n_attach : em_attach, +#ifdef DEBUG_MODULAR + n_detach : em_detach, +#else + n_detach: 0, +#endif + n_intr : em_intr, +}; +#endif + +/********************************************************************* + * Tunable default values. + *********************************************************************/ + +#define E1000_TICKS_TO_USECS(ticks) ((1024 * (ticks) + 500) / 1000) +#define E1000_USECS_TO_TICKS(usecs) ((1000 * (usecs) + 512) / 1024) + +#ifndef __rtems__ +static int em_tx_int_delay_dflt = E1000_TICKS_TO_USECS(EM_TIDV); +static int em_rx_int_delay_dflt = E1000_TICKS_TO_USECS(EM_RDTR); +static int em_tx_abs_int_delay_dflt = E1000_TICKS_TO_USECS(EM_TADV); +static int em_rx_abs_int_delay_dflt = E1000_TICKS_TO_USECS(EM_RADV); + +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); +#endif + +/********************************************************************* + * 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) +{ + em_vendor_info_t *ent; + + u_int16_t pci_vendor_id = 0; + u_int16_t pci_device_id = 0; + u_int16_t pci_subvendor_id = 0; + u_int16_t pci_subdevice_id = 0; + char adapter_name[60]; + + 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, Version - %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; + + INIT_DEBUGOUT("em_attach: begin"); + + /* Allocate, clear, and link in our adapter structure */ + if (!(adapter = device_get_softc(dev))) { + printf("em: adapter structure allocation failed\n"); + return(ENOMEM); + } +#ifndef __rtems__ + bzero(adapter, sizeof(struct adapter )); +#else + /* softc structure is maintained outside of this + * and the osdep already contains vital fields (memory address) + */ +#endif + adapter->dev = dev; + adapter->osdep.dev = dev; + adapter->unit = device_get_unit(dev); + EM_LOCK_INIT(adapter, device_get_nameunit(dev)); + + if (em_adapter_list != NULL) + em_adapter_list->prev = adapter; + adapter->next = em_adapter_list; + em_adapter_list = adapter; + +#ifndef __rtems__ + /* 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, + (void *)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, + (void *)adapter, 0, + em_sysctl_stats, "I", "Statistics"); +#endif + + callout_init(&adapter->timer, CALLOUT_MPSAFE); + callout_init(&adapter->tx_fifo_timer, CALLOUT_MPSAFE); + + /* Determine hardware revision */ + em_identify_hardware(adapter); + +#ifndef __rtems__ + /* 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_REG_OFFSET(&adapter->hw, 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_REG_OFFSET(&adapter->hw, TIDV), em_tx_int_delay_dflt); + if (adapter->hw.mac_type >= em_82540) { + em_add_int_delay_sysctl(adapter, "rx_abs_int_delay", + "receive interrupt delay limit in usecs", + &adapter->rx_abs_int_delay, + E1000_REG_OFFSET(&adapter->hw, 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_REG_OFFSET(&adapter->hw, TADV), + em_tx_abs_int_delay_dflt); + } +#endif + + /* Parameters (to be read from user) */ + adapter->num_tx_desc = EM_MAX_TXD; + adapter->num_rx_desc = EM_MAX_RXD; +#ifdef __rtems__ + if ( dev->d_ifconfig->rbuf_count > 0 ) { + adapter->num_rx_desc = dev->d_ifconfig->rbuf_count; + } + if ( adapter->num_rx_desc < 80 ) + adapter->num_rx_desc = 80; + if ( adapter->num_rx_desc > 256 ) + adapter->num_rx_desc = 256; + if ( dev->d_ifconfig->xbuf_count > 0 ) { + adapter->num_tx_desc = dev->d_ifconfig->xbuf_count; + } + if ( adapter->num_tx_desc < 80 ) + adapter->num_tx_desc = 80; + if ( adapter->num_tx_desc > 256 ) + adapter->num_tx_desc = 256; + adapter->tx_cleanup_threshold = adapter->num_tx_desc/8; +#endif + adapter->hw.autoneg = DO_AUTO_NEG; + adapter->hw.wait_autoneg_complete = WAIT_FOR_AUTO_NEG_DEFAULT; + adapter->hw.autoneg_advertised = AUTONEG_ADV_DEFAULT; + adapter->hw.tbi_compatibility_en = TRUE; + adapter->rx_buffer_len = EM_RXBUFFER_2048; + + /* + * These parameters control the automatic generation(Tx) and + * response(Rx) to Ethernet PAUSE frames. + */ + adapter->hw.fc_high_water = FC_DEFAULT_HI_THRESH; + adapter->hw.fc_low_water = FC_DEFAULT_LO_THRESH; + adapter->hw.fc_pause_time = FC_DEFAULT_TX_TIMER; + adapter->hw.fc_send_xon = TRUE; + adapter->hw.fc = em_fc_full; + + adapter->hw.phy_init_script = 1; + adapter->hw.phy_reset_disable = FALSE; + +#ifndef EM_MASTER_SLAVE + adapter->hw.master_slave = em_ms_hw_default; +#else + adapter->hw.master_slave = EM_MASTER_SLAVE; +#endif + /* + * Set the max frame size assuming standard ethernet + * sized frames + */ + adapter->hw.max_frame_size = + ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN; + + adapter->hw.min_frame_size = + MINIMUM_ETHERNET_PACKET_SIZE + ETHER_CRC_LEN; + + /* + * This controls when hardware reports transmit completion + * status. + */ + adapter->hw.report_tx_early = 1; + + + if (em_allocate_pci_resources(adapter)) { + printf("em%d: Allocation of PCI resources failed\n", + adapter->unit); + error = ENXIO; + goto err_pci; + } + + + /* Initialize eeprom parameters */ + em_init_eeprom_params(&adapter->hw); + + tsize = EM_ROUNDUP(adapter->num_tx_desc * + sizeof(struct em_tx_desc), 4096); + + /* Allocate Transmit Descriptor ring */ + if (em_dma_malloc(adapter, tsize, &adapter->txdma, BUS_DMA_NOWAIT)) { + printf("em%d: Unable to allocate tx_desc memory\n", + adapter->unit); + error = ENOMEM; + goto err_tx_desc; + } + adapter->tx_desc_base = (struct em_tx_desc *) adapter->txdma.dma_vaddr; + + rsize = EM_ROUNDUP(adapter->num_rx_desc * + sizeof(struct em_rx_desc), 4096); + + /* Allocate Receive Descriptor ring */ + if (em_dma_malloc(adapter, rsize, &adapter->rxdma, BUS_DMA_NOWAIT)) { + printf("em%d: Unable to allocate rx_desc memory\n", + adapter->unit); + error = ENOMEM; + goto err_rx_desc; + } + adapter->rx_desc_base = (struct em_rx_desc *) adapter->rxdma.dma_vaddr; + + /* Initialize the hardware */ + if (em_hardware_init(adapter)) { + printf("em%d: Unable to initialize the hardware\n", + adapter->unit); + error = EIO; + goto err_hw_init; + } + + /* Copy the permanent MAC address out of the EEPROM */ + if (em_read_mac_addr(&adapter->hw) < 0) { + printf("em%d: EEPROM read error while reading mac address\n", + adapter->unit); + error = EIO; + goto err_mac_addr; + } + +#ifdef __rtems__ + /* if the configuration has not set a mac address, copy the permanent + * address from the device to the arpcom struct. + */ + { + int i; + for ( i=0; iarpcom.ac_enaddr[i] ) + break; + } + if ( i >= ETHER_ADDR_LEN ) { + /* all nulls */ + bcopy(adapter->hw.mac_addr, adapter->arpcom.ac_enaddr, + ETHER_ADDR_LEN); + } + } +#endif + + if (!em_is_valid_ether_addr(adapter->hw.mac_addr)) { + printf("em%d: Invalid mac address\n", adapter->unit); + error = EIO; + goto err_mac_addr; + } + + /* Setup OS specific network interface */ + em_setup_interface(dev, adapter); + + /* Initialize statistics */ + em_clear_hw_cntrs(&adapter->hw); + em_update_stats_counters(adapter); + adapter->hw.get_link_status = 1; +#ifndef __rtems__ + em_check_for_link(&adapter->hw); +#else + /* first check during hw init usually fails - probably we need to wait longer; + * could take a while till the link is up, depends on the partner? + * in any case, rather than waiting here we just proceed... + */ + em_check_for_link(&adapter->hw); + /* em_check_for_link doesn't update 'link_active' + * -- they usually call em_print_link_status() right + * after check_for_link, so let's repeat this + * algorithm here. + */ + em_print_link_status(adapter); +#endif + + /* Print the link status */ + if (adapter->link_active == 1) { + em_get_speed_and_duplex(&adapter->hw, &adapter->link_speed, + &adapter->link_duplex); + printf("em%d: Speed:%d Mbps Duplex:%s\n", + adapter->unit, + adapter->link_speed, + adapter->link_duplex == FULL_DUPLEX ? "Full" : "Half"); + } else + printf("em%d: Speed:N/A Duplex:N/A\n", adapter->unit); + + /* Identify 82544 on PCIX */ + em_get_bus_info(&adapter->hw); + if(adapter->hw.bus_type == em_bus_type_pcix && + adapter->hw.mac_type == em_82544) { + adapter->pcix_82544 = TRUE; + } + else { + adapter->pcix_82544 = FALSE; + } + INIT_DEBUGOUT("em_attach: end"); + return(0); + +err_mac_addr: +err_hw_init: + em_dma_free(adapter, &adapter->rxdma); +err_rx_desc: + em_dma_free(adapter, &adapter->txdma); +err_tx_desc: +err_pci: +#ifndef __rtems__ + em_free_pci_resources(adapter); +#endif + 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 + *********************************************************************/ + +#if !defined(__rtems__) || defined(DEBUG_MODULAR) + +static int +em_detach(device_t dev) +{ + struct adapter * adapter = device_get_softc(dev); + struct ifnet *ifp = &adapter->arpcom.ac_if; + + INIT_DEBUGOUT("em_detach: begin"); + + EM_LOCK(adapter); + adapter->in_detach = 1; + em_stop(adapter); + em_phy_hw_reset(&adapter->hw); + EM_UNLOCK(adapter); +#ifndef __rtems__ +#if __FreeBSD_version < 500000 + ether_ifdetach(adapter->ifp, ETHER_BPF_SUPPORTED); +#else + ether_ifdetach(adapter->ifp); + if_free(ifp); +#endif + em_free_pci_resources(adapter); + bus_generic_detach(dev); +#else + ether_ifdetach(ifp); +#endif + + /* 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; + } + + /* Remove from the adapter list */ + if (em_adapter_list == adapter) + em_adapter_list = adapter->next; + if (adapter->next != NULL) + adapter->next->prev = adapter->prev; + if (adapter->prev != NULL) + adapter->prev->next = adapter->next; + + EM_LOCK_DESTROY(adapter); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + + return(0); +} + +#endif + +#ifndef __rtems__ +/********************************************************************* + * + * Shutdown entry point + * + **********************************************************************/ + +static int +em_shutdown(device_t dev) +{ + struct adapter *adapter = device_get_softc(dev); + EM_LOCK(adapter); + em_stop(adapter); + EM_UNLOCK(adapter); + return(0); +} +#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 mbuf *m_head; + struct adapter *adapter = ifp->if_softc; + + mtx_assert(&adapter->mtx, MA_OWNED); + + 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; + + /* + * em_encap() can modify our pointer, and or make it NULL on + * failure. In that event, we can't requeue. + */ + if (em_encap(adapter, &m_head)) { + if (m_head == NULL) + break; + ifp->if_flags |= IFF_OACTIVE; + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + break; + } + + /* Send a copy of the frame to the BPF listener */ +#if __FreeBSD_version < 500000 && !defined(__rtems__) + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); +#else + BPF_MTAP(ifp, m_head); +#endif + + /* Set timeout in case hardware has problems transmitting */ + ifp->if_timer = EM_TX_TIMEOUT; + + } + return; +} + +static void +em_start(struct ifnet *ifp) +{ + struct adapter *adapter __attribute__((unused)) = ifp->if_softc; + + EM_LOCK(adapter); + em_start_locked(ifp); + EM_UNLOCK(adapter); + return; +} + +/********************************************************************* + * Ioctl entry point + * + * em_ioctl is called when the user wants to configure the + * interface. + * + * return 0 on success, positive on failure + **********************************************************************/ + +#ifndef __rtems__ +static int +em_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +#else +static int +em_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data) +#endif +{ +#ifndef __rtems__ + int mask, reinit, error = 0; +#else + int error = 0; +#endif + struct ifreq *ifr = (struct ifreq *) data; + struct adapter * adapter = ifp->if_softc; + + if (adapter->in_detach) return(error); + + switch (command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCxIFADDR (Get/Set Interface Addr)"); + ether_ioctl(ifp, command, data); + break; + case SIOCSIFMTU: + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)"); + if (ifr->ifr_mtu > MAX_JUMBO_FRAME_SIZE - ETHER_HDR_LEN || \ + /* 82573 does not support jumbo frames */ + (adapter->hw.mac_type == em_82573 && ifr->ifr_mtu > ETHERMTU) ) { + error = EINVAL; + } else { + EM_LOCK(adapter); + ifp->if_mtu = ifr->ifr_mtu; + adapter->hw.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_flags & IFF_RUNNING)) { + em_init_locked(adapter); + } + + em_disable_promisc(adapter); + em_set_promisc(adapter); + } else { + if (ifp->if_flags & IFF_RUNNING) { + em_stop(adapter); + } + } + EM_UNLOCK(adapter); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: +#ifdef __rtems__ + if ( (error = ( SIOCADDMULTI == command ? + ether_addmulti( ifr, (struct arpcom*)ifp ) : + ether_delmulti( ifr, (struct arpcom*)ifp ) ) ) ) { + if ( ENETRESET == error ) + error = 0; + else + break; + } +#endif + IOCTL_DEBUGOUT("ioctl rcv'd: SIOC(ADD|DEL)MULTI"); + if (ifp->if_flags & IFF_RUNNING) { + EM_LOCK(adapter); + em_disable_intr(adapter); + em_set_multi(adapter); + if (adapter->hw.mac_type == em_82542_rev2_0) { + em_initialize_receive_unit(adapter); + } +#ifdef DEVICE_POLLING + if (!(ifp->if_flags & IFF_POLLING)) +#endif + em_enable_intr(adapter); + EM_UNLOCK(adapter); + } + break; +#ifndef __rtems__ + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCxIFMEDIA (Get/Set Interface Media)"); + error = ifmedia_ioctl(ifp, ifr, &adapter->media, command); + break; +#else + case SIOCSIFMEDIA: + { + struct rtems_ifmedia mhack; + mhack.ifm_media = ifr->ifr_media; + error = em_media_change(ifp, &mhack); + } + break; + case SIOCGIFMEDIA: + { + struct ifmediareq ifmr; + em_media_status(ifp, &ifmr); + ifr->ifr_media = ifmr.ifm_active; + /* add-in rtems flags */ + if ( adapter->link_active ) + ifr->ifr_media |= IFM_LINK_OK; + if ( !adapter->hw.autoneg ) + ifr->ifr_media |= IFM_ANEG_DIS; + error = 0; + } + break; +#endif +#ifndef __rtems__ + case SIOCSIFCAP: + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFCAP (Set Capabilities)"); + reinit = 0; + mask = ifr->ifr_reqcap ^ ifp->if_capenable; + if (mask & IFCAP_POLLING) + ifp->if_capenable ^= IFCAP_POLLING; + if (mask & IFCAP_HWCSUM) { + ifp->if_capenable ^= IFCAP_HWCSUM; + reinit = 1; + } + if (mask & IFCAP_VLAN_HWTAGGING) { + ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; + reinit = 1; + } + if (reinit && (ifp->if_flags & IFF_RUNNING)) + em_init(adapter); + break; +#endif + +#ifdef __rtems__ + case SIO_RTEMS_SHOW_STATS: + em_print_hw_stats(adapter); + error = 0; + break; +#endif + + default: + IOCTL_DEBUGOUT1("ioctl received: UNKNOWN (0x%x)", (int)command); + error = EINVAL; + } + + return(error); +} + +/********************************************************************* + * Watchdog entry point + * + * This routine is called whenever hardware quits transmitting. + * + **********************************************************************/ + +static void +em_watchdog(struct ifnet *ifp) +{ + struct adapter * adapter; + adapter = ifp->if_softc; + + /* If we are in this routine because of pause frames, then + * don't reset the hardware. + */ + if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_TXOFF) { + ifp->if_timer = EM_TX_TIMEOUT; + return; + } + + if (em_check_for_link(&adapter->hw)) + printf("em%d: watchdog timeout -- resetting\n", adapter->unit); + + ifp->if_flags &= ~IFF_RUNNING; + + em_init(adapter); + + ifp->if_oerrors++; + return; +} + +/********************************************************************* + * 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; + + uint32_t pba; + ifp = &adapter->arpcom.ac_if; + + INIT_DEBUGOUT("em_init: begin"); + + mtx_assert(&adapter->mtx, MA_OWNED); + + 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. + */ + if(adapter->hw.mac_type < em_82547) { + /* Total FIFO is 64K */ + if(adapter->rx_buffer_len > EM_RXBUFFER_8192) + pba = E1000_PBA_40K; /* 40K for Rx, 24K for Tx */ + else + pba = E1000_PBA_48K; /* 48K for Rx, 16K for Tx */ + } else { + /* Total FIFO is 40K */ + if(adapter->hw.max_frame_size > EM_RXBUFFER_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; + } + INIT_DEBUGOUT1("em_init: pba=%" PRId32 "K",pba); + E1000_WRITE_REG(&adapter->hw, PBA, pba); + + /* Get the latest mac address, User can use a LAA */ + bcopy(adapter->arpcom.ac_enaddr, adapter->hw.mac_addr, + ETHER_ADDR_LEN); + + /* Initialize the hardware */ + if (em_hardware_init(adapter)) { + printf("em%d: Unable to initialize the hardware\n", + adapter->unit); + return; + } + +#ifndef __rtems__ + if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) + em_enable_vlans(adapter); +#endif + + /* Prepare transmit descriptors and buffers */ + if (em_setup_transmit_structures(adapter)) { + printf("em%d: Could not setup transmit structures\n", + adapter->unit); + 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)) { + printf("em%d: Could not setup receive structures\n", + adapter->unit); + em_stop(adapter); + return; + } + em_initialize_receive_unit(adapter); + + /* Don't loose promiscuous settings */ + em_set_promisc(adapter); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + +#ifndef __rtems__ + if (adapter->hw.mac_type >= em_82543) { + if (ifp->if_capenable & IFCAP_TXCSUM) + ifp->if_hwassist = EM_CHECKSUM_FEATURES; + else + ifp->if_hwassist = 0; + } +#endif + + callout_reset(&adapter->timer, hz, em_local_timer, adapter); + em_clear_hw_cntrs(&adapter->hw); +#ifdef DEVICE_POLLING + /* + * Only enable interrupts if we are not polling, make sure + * they are off otherwise. + */ + if (ifp->if_flags & IFF_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; + + return; +} + +static void +em_init(void *arg) +{ + struct adapter * adapter = arg; + + EM_LOCK(adapter); + em_init_locked(adapter); + EM_UNLOCK(adapter); + return; +} + + +#ifdef DEVICE_POLLING +static poll_handler_t em_poll; + +static void +em_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct adapter *adapter = ifp->if_softc; + u_int32_t reg_icr; + + mtx_assert(&adapter->mtx, MA_OWNED); + + if (!(ifp->if_capenable & IFCAP_POLLING)) { + ether_poll_deregister(ifp); + cmd = POLL_DEREGISTER; + } + if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ + em_enable_intr(adapter); + return; + } + if (cmd == POLL_AND_CHECK_STATUS) { + reg_icr = E1000_READ_REG(&adapter->hw, ICR); + if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { + callout_stop(&adapter->timer); + adapter->hw.get_link_status = 1; + em_check_for_link(&adapter->hw); + em_print_link_status(adapter); + callout_reset(&adapter->timer, hz, em_local_timer, adapter); + } + } + if (ifp->if_flags & IFF_RUNNING) { + em_process_receive_interrupts(adapter, count); + em_clean_transmit_interrupts(adapter); + } + + if (ifp->if_flags & IFF_RUNNING && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp); +} + +static void +em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct adapter *adapter = ifp->if_softc; + + EM_LOCK(adapter); + em_poll_locked(ifp, cmd, count); + EM_UNLOCK(adapter); +} +#endif /* DEVICE_POLLING */ + +/********************************************************************* + * + * Interrupt Service routine + * + **********************************************************************/ +static void +em_intr(void *arg) +{ + u_int32_t loop_cnt = EM_MAX_INTR; + u_int32_t reg_icr; + struct ifnet *ifp; + struct adapter *adapter = arg; + + EM_LOCK(adapter); + + ifp = &adapter->arpcom.ac_if; + +#ifdef DEVICE_POLLING + if (ifp->if_flags & IFF_POLLING) { + EM_UNLOCK(adapter); + return; + } + + if ((ifp->if_capenable & IFCAP_POLLING) && + ether_poll_register(em_poll, ifp)) { + em_disable_intr(adapter); + em_poll_locked(ifp, 0, 1); + EM_UNLOCK(adapter); + return; + } +#endif /* DEVICE_POLLING */ + + reg_icr = E1000_READ_REG(&adapter->hw, ICR); + if (!reg_icr) { + EM_UNLOCK(adapter); + return; + } + + /* Link status change */ + if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) { + callout_stop(&adapter->timer); + adapter->hw.get_link_status = 1; + em_check_for_link(&adapter->hw); + em_print_link_status(adapter); + callout_reset(&adapter->timer, hz, em_local_timer, adapter); + } + + while (loop_cnt > 0) { + if (ifp->if_flags & IFF_RUNNING) { + em_process_receive_interrupts(adapter, -1); + em_clean_transmit_interrupts(adapter); + } + loop_cnt--; + } + + if (ifp->if_flags & IFF_RUNNING && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + em_start_locked(ifp); + + EM_UNLOCK(adapter); + return; +} + + + +/********************************************************************* + * + * 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; + + INIT_DEBUGOUT("em_media_status: begin"); + + em_check_for_link(&adapter->hw); + if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU) { + if (adapter->link_active == 0) { + em_get_speed_and_duplex(&adapter->hw, + &adapter->link_speed, + &adapter->link_duplex); + adapter->link_active = 1; + } + } else { + if (adapter->link_active == 1) { + adapter->link_speed = 0; + adapter->link_duplex = 0; + adapter->link_active = 0; + } + } + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (!adapter->link_active) + return; + + ifmr->ifm_status |= IFM_ACTIVE; + + if (adapter->hw.media_type == em_media_type_fiber) { + ifmr->ifm_active |= IFM_1000_SX | 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: +#if __FreeBSD_version < 500000 && !defined(__rtems__) + ifmr->ifm_active |= IFM_1000_TX; +#else + ifmr->ifm_active |= IFM_1000_T; +#endif + break; + } + if (adapter->link_duplex == FULL_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + } + return; +} + +/********************************************************************* + * + * Media Ioctl callback + * + * This routine is called when the user changes speed/duplex using + * media/mediopt option with ifconfig. + * + **********************************************************************/ +static int +#ifndef __rtems__ +em_media_change(struct ifnet *ifp) +#else +em_media_change(struct ifnet *ifp, struct rtems_ifmedia *ifm) +#endif +{ + struct adapter * adapter = ifp->if_softc; +#ifndef __rtems__ + struct ifmedia *ifm = &adapter->media; +#endif + + INIT_DEBUGOUT("em_media_change: begin"); + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return(EINVAL); + + switch (IFM_SUBTYPE(ifm->ifm_media)) { + case IFM_AUTO: + adapter->hw.autoneg = DO_AUTO_NEG; + adapter->hw.autoneg_advertised = AUTONEG_ADV_DEFAULT; + break; + case IFM_1000_SX: +#if __FreeBSD_version < 500000 && !defined(__rtems__) + case IFM_1000_TX: +#else + case IFM_1000_T: +#endif + adapter->hw.autoneg = DO_AUTO_NEG; + adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; + break; + case IFM_100_TX: + adapter->hw.autoneg = FALSE; + adapter->hw.autoneg_advertised = 0; + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) + adapter->hw.forced_speed_duplex = em_100_full; + else + adapter->hw.forced_speed_duplex = em_100_half; + break; + case IFM_10_T: + adapter->hw.autoneg = FALSE; + adapter->hw.autoneg_advertised = 0; + if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) + adapter->hw.forced_speed_duplex = em_10_full; + else + adapter->hw.forced_speed_duplex = em_10_half; + break; + default: + printf("em%d: Unsupported media type\n", adapter->unit); + } + + /* As the speed/duplex settings my have changed we need to + * reset the PHY. + */ + adapter->hw.phy_reset_disable = FALSE; + + em_init(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) +{ + u_int32_t txd_upper; + u_int32_t txd_lower, txd_used = 0, txd_saved = 0; + int i, j, error; + u_int64_t address; + + struct mbuf *m_head; + + /* For 82544 Workaround */ + DESC_ARRAY desc_array; + u_int32_t array_elements; + u_int32_t counter; + +#ifndef __rtems__ +#if __FreeBSD_version < 500000 + struct ifvlan *ifv = NULL; +#else + struct m_tag *mtag; +#endif +#endif + bus_dma_segment_t segs[EM_MAX_SCATTER]; +#ifndef __rtems__ + bus_dmamap_t map; +#endif + int nsegs; + struct em_buffer *tx_buffer = NULL; + struct em_tx_desc *current_tx_desc = NULL; +#ifndef __rtems__ + struct ifnet *ifp = &adapter->arpcom.ac_if; +#endif + + m_head = *m_headp; + + /* + * Force a cleanup if number of TX descriptors + * available hits the threshold + */ + if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) { + em_clean_transmit_interrupts(adapter); + if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) { + adapter->no_tx_desc_avail1++; + return(ENOBUFS); + } + } + +#ifndef __rtems__ + /* + * Map the packet for DMA. + */ + if (bus_dmamap_create(adapter->txtag, BUS_DMA_NOWAIT, &map)) { + adapter->no_tx_map_avail++; + return (ENOMEM); + } + error = bus_dmamap_load_mbuf_sg(adapter->txtag, map, m_head, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + adapter->no_tx_dma_setup++; + bus_dmamap_destroy(adapter->txtag, map); + return (error); + } +#else + error = 0; + { + struct mbuf *m; + for ( m=m_head, nsegs=0; m; m=m->m_next, nsegs++ ) { + if ( nsegs >= sizeof(segs)/sizeof(segs[0]) ) { + error = -1; + break; + } + segs[nsegs].ds_addr = mtod(m, unsigned); + segs[nsegs].ds_len = m->m_len; + } + } +#endif + KASSERT(nsegs != 0, ("em_encap: empty packet")); + + if (nsegs > adapter->num_tx_desc_avail) { + adapter->no_tx_desc_avail2++; + bus_dmamap_destroy(adapter->txtag, map); + return (ENOBUFS); + } + + +#ifndef __rtems__ + if (ifp->if_hwassist > 0) { + em_transmit_checksum_setup(adapter, m_head, + &txd_upper, &txd_lower); + } else +#endif + txd_upper = txd_lower = 0; + + +#ifndef __rtems__ + /* Find out if we are in vlan mode */ +#if __FreeBSD_version < 500000 + if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) && + m_head->m_pkthdr.rcvif != NULL && + m_head->m_pkthdr.rcvif->if_type == IFT_L2VLAN) + ifv = m_head->m_pkthdr.rcvif->if_softc; +#else + mtag = VLAN_OUTPUT_TAG(ifp, m_head); +#endif + + /* + * When operating in promiscuous mode, hardware encapsulation for + * packets is disabled. This means we have to add the vlan + * encapsulation in the driver, since it will have come down from the + * VLAN layer with a tag instead of a VLAN header. + */ + if (mtag != NULL && adapter->em_insert_vlan_header) { + struct ether_vlan_header *evl; + struct ether_header eh; + + m_head = m_pullup(m_head, sizeof(eh)); + if (m_head == NULL) { + *m_headp = NULL; + bus_dmamap_destroy(adapter->txtag, map); + return (ENOBUFS); + } + eh = *mtod(m_head, struct ether_header *); + M_PREPEND(m_head, sizeof(*evl), M_DONTWAIT); + if (m_head == NULL) { + *m_headp = NULL; + bus_dmamap_destroy(adapter->txtag, map); + return (ENOBUFS); + } + m_head = m_pullup(m_head, sizeof(*evl)); + if (m_head == NULL) { + *m_headp = NULL; + bus_dmamap_destroy(adapter->txtag, map); + return (ENOBUFS); + } + evl = mtod(m_head, struct ether_vlan_header *); + bcopy(&eh, evl, sizeof(*evl)); + evl->evl_proto = evl->evl_encap_proto; + evl->evl_encap_proto = htons(ETHERTYPE_VLAN); + evl->evl_tag = htons(VLAN_TAG_VALUE(mtag)); + m_tag_delete(m_head, mtag); + mtag = NULL; + *m_headp = m_head; + } +#endif + + i = adapter->next_avail_tx_desc; + if (adapter->pcix_82544) { + txd_saved = i; + txd_used = 0; + } + for (j = 0; j < nsegs; j++) { + /* If adapter is 82544 and on PCIX bus */ + if(adapter->pcix_82544) { + array_elements = 0; + address = htole64(segs[j].ds_addr); + /* + * Check the Address and Length combination and + * split the data accordingly + */ + array_elements = em_fill_descriptors(address, + htole32(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_destroy(adapter->txtag, map); + return (ENOBUFS); + } + tx_buffer = &adapter->tx_buffer_area[i]; + current_tx_desc = &adapter->tx_desc_base[i]; + current_tx_desc->buffer_addr = htole64( + desc_array.descriptor[counter].address); + current_tx_desc->lower.data = htole32( + (adapter->txd_cmd | txd_lower | + (u_int16_t)desc_array.descriptor[counter].length)); + current_tx_desc->upper.data = htole32((txd_upper)); + if (++i == adapter->num_tx_desc) + i = 0; + + tx_buffer->m_head = NULL; + txd_used++; + } + } else { + tx_buffer = &adapter->tx_buffer_area[i]; + current_tx_desc = &adapter->tx_desc_base[i]; + + current_tx_desc->buffer_addr = htole64(segs[j].ds_addr); + current_tx_desc->lower.data = htole32( + adapter->txd_cmd | txd_lower | segs[j].ds_len); + current_tx_desc->upper.data = htole32(txd_upper); + + if (++i == adapter->num_tx_desc) + i = 0; + + tx_buffer->m_head = NULL; + } + } + + adapter->next_avail_tx_desc = i; + if (adapter->pcix_82544) { + adapter->num_tx_desc_avail -= txd_used; + } + else { + adapter->num_tx_desc_avail -= nsegs; + } + +#ifndef __rtems__ +#if __FreeBSD_version < 500000 + if (ifv != NULL) { + /* Set the vlan id */ + current_tx_desc->upper.fields.special = htole16(ifv->ifv_tag); +#else + if (mtag != NULL) { + /* Set the vlan id */ + current_tx_desc->upper.fields.special = htole16(VLAN_TAG_VALUE(mtag)); +#endif + + /* Tell hardware to add tag */ + current_tx_desc->lower.data |= htole32(E1000_TXD_CMD_VLE); + } +#endif + + tx_buffer->m_head = m_head; +#ifndef __rtems__ + tx_buffer->map = map; +#endif + bus_dmamap_sync(adapter->txtag, map, BUS_DMASYNC_PREWRITE); + + /* + * Last Descriptor of Packet needs End Of Packet (EOP) + */ + current_tx_desc->lower.data |= htole32(E1000_TXD_CMD_EOP); + + /* + * 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 == em_82547 && + adapter->link_duplex == HALF_DUPLEX) { + em_82547_move_tail_locked(adapter); + } else { + E1000_WRITE_REG(&adapter->hw, TDT, i); + if (adapter->hw.mac_type == em_82547) { + em_82547_update_fifo_head(adapter, m_head->m_pkthdr.len); + } + } + + 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_locked(struct adapter *adapter) +{ + uint16_t hw_tdt; + uint16_t sw_tdt; + struct em_tx_desc *tx_desc; + uint16_t length = 0; + boolean_t eop = 0; + + EM_LOCK_ASSERT(adapter); + + hw_tdt = E1000_READ_REG(&adapter->hw, 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, TDT, hw_tdt); + em_82547_update_fifo_head(adapter, length); + length = 0; + } + } + return; +} + +#ifndef __rtems__ +static void +em_82547_move_tail(void *arg) +{ + struct adapter *adapter = arg; + + EM_LOCK(adapter); + em_82547_move_tail_locked(adapter); + EM_UNLOCK(adapter); +} +#endif + +static int +em_82547_fifo_workaround(struct adapter *adapter, int len) +{ + int fifo_space, fifo_pkt_len; + + fifo_pkt_len = EM_ROUNDUP(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 = EM_ROUNDUP(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; + } + + return; +} + + +static int +em_82547_tx_fifo_reset(struct adapter *adapter) +{ + uint32_t tctl; + + if ( (E1000_READ_REG(&adapter->hw, TDT) == + E1000_READ_REG(&adapter->hw, TDH)) && + (E1000_READ_REG(&adapter->hw, TDFT) == + E1000_READ_REG(&adapter->hw, TDFH)) && + (E1000_READ_REG(&adapter->hw, TDFTS) == + E1000_READ_REG(&adapter->hw, TDFHS)) && + (E1000_READ_REG(&adapter->hw, TDFPC) == 0)) { + + /* Disable TX unit */ + tctl = E1000_READ_REG(&adapter->hw, TCTL); + E1000_WRITE_REG(&adapter->hw, TCTL, tctl & ~E1000_TCTL_EN); + + /* Reset FIFO pointers */ + E1000_WRITE_REG(&adapter->hw, TDFT, adapter->tx_head_addr); + E1000_WRITE_REG(&adapter->hw, TDFH, adapter->tx_head_addr); + E1000_WRITE_REG(&adapter->hw, TDFTS, adapter->tx_head_addr); + E1000_WRITE_REG(&adapter->hw, TDFHS, adapter->tx_head_addr); + + /* Re-enable TX unit */ + E1000_WRITE_REG(&adapter->hw, 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) +{ + + u_int32_t reg_rctl; + struct ifnet *ifp = &adapter->arpcom.ac_if; + + reg_rctl = E1000_READ_REG(&adapter->hw, RCTL); + + if (ifp->if_flags & IFF_PROMISC) { + reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); + E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); +#ifndef __rtems__ + /* Disable VLAN stripping in promiscous mode + * This enables bridging of vlan tagged frames to occur + * and also allows vlan tags to be seen in tcpdump + */ + if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) + em_disable_vlans(adapter); + adapter->em_insert_vlan_header = 1; +#endif + } else if (ifp->if_flags & IFF_ALLMULTI) { + reg_rctl |= E1000_RCTL_MPE; + reg_rctl &= ~E1000_RCTL_UPE; + E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); +#ifndef __rtems__ + adapter->em_insert_vlan_header = 0; + } else + adapter->em_insert_vlan_header = 0; +#else + } +#endif + + return; +} + +static void +em_disable_promisc(struct adapter * adapter) +{ + u_int32_t reg_rctl; +#ifndef __rtems__ + struct ifnet *ifp = adapter->ifp; +#endif + + reg_rctl = E1000_READ_REG(&adapter->hw, RCTL); + + reg_rctl &= (~E1000_RCTL_UPE); + reg_rctl &= (~E1000_RCTL_MPE); + E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); + +#ifndef __rtems__ + if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) + em_enable_vlans(adapter); + adapter->em_insert_vlan_header = 0; +#endif + + return; +} + + +/********************************************************************* + * Multicast Update + * + * This routine is called whenever multicast address list is updated. + * + **********************************************************************/ + +static void +em_set_multi(struct adapter * adapter) +{ + u_int32_t reg_rctl = 0; + u_int8_t mta[MAX_NUM_MULTICAST_ADDRESSES * ETH_LENGTH_OF_ADDRESS]; +#ifndef __rtems__ + struct ifmultiaddr *ifma; +#endif + int mcnt = 0; + struct ifnet *ifp = &adapter->arpcom.ac_if; + + IOCTL_DEBUGOUT("em_set_multi: begin"); + + if (adapter->hw.mac_type == em_82542_rev2_0) { + reg_rctl = E1000_READ_REG(&adapter->hw, RCTL); + if (adapter->hw.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) { + em_pci_clear_mwi(&adapter->hw); + } + reg_rctl |= E1000_RCTL_RST; + E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); + msec_delay(5); + } + +#ifndef __rtems__ + IF_ADDR_LOCK(ifp); +#if __FreeBSD_version < 500000 + LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { +#else + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { +#endif + 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_LENGTH_OF_ADDRESS], ETH_LENGTH_OF_ADDRESS); + mcnt++; + } + IF_ADDR_UNLOCK(ifp); +#else + { + /* Don't know how to handle address ranges - we warn and ignore + * for now... + */ + 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/ignoring\n"); + goto skiptonext; + } else { + bcopy(enm->enm_addrlo, &mta[mcnt * ETHER_ADDR_LEN], ETHER_ADDR_LEN); + } + mcnt++; +skiptonext: + ETHER_NEXT_MULTI( step, enm ); + } + } +#endif + + if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) { + reg_rctl = E1000_READ_REG(&adapter->hw, RCTL); + reg_rctl |= E1000_RCTL_MPE; + E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); + } else + em_mc_addr_list_update(&adapter->hw, mta, mcnt, 0, 1); + + if (adapter->hw.mac_type == em_82542_rev2_0) { + reg_rctl = E1000_READ_REG(&adapter->hw, RCTL); + reg_rctl &= ~E1000_RCTL_RST; + E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); + msec_delay(5); + if (adapter->hw.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) { + em_pci_set_mwi(&adapter->hw); + } + } + + return; +} + +#ifndef __rtems__ +/********************************************************************* + * Timer routine + * + * This routine checks for link status and updates statistics. + * + **********************************************************************/ + +static void +em_local_timer(void *arg) +{ + struct ifnet *ifp; + struct adapter * adapter = arg; + ifp = &adapter->arpcom.ac_if; + + EM_LOCK(adapter); + + em_check_for_link(&adapter->hw); + em_print_link_status(adapter); + em_update_stats_counters(adapter); + if (em_display_debug_stats && ifp->if_flags & IFF_RUNNING) { + em_print_hw_stats(adapter); + } + em_smartspeed(adapter); + + callout_reset(&adapter->timer, hz, em_local_timer, adapter); + + EM_UNLOCK(adapter); + return; +} +#endif + +static void +em_print_link_status(struct adapter * adapter) +{ +#ifndef __rtems__ + struct ifnet *ifp = &adapter->arpcom.ac_if; +#endif + + if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU) { + if (adapter->link_active == 0) { + em_get_speed_and_duplex(&adapter->hw, + &adapter->link_speed, + &adapter->link_duplex); + if (bootverbose) + printf("em%d: Link is up %d Mbps %s\n", + adapter->unit, + adapter->link_speed, + ((adapter->link_duplex == FULL_DUPLEX) ? + "Full Duplex" : "Half Duplex")); + adapter->link_active = 1; + adapter->smartspeed = 0; +#ifndef __rtems__ + if_link_state_change(ifp, LINK_STATE_UP); +#endif + } + } else { + if (adapter->link_active == 1) { + adapter->link_speed = 0; + adapter->link_duplex = 0; + if (bootverbose) + printf("em%d: Link is Down\n", adapter->unit); + adapter->link_active = 0; +#ifndef __rtems__ + if_link_state_change(ifp, LINK_STATE_UP); + if_link_state_change(ifp, LINK_STATE_DOWN); +#endif + } + } + + return; +} + +/********************************************************************* + * + * 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 ifnet *ifp; + struct adapter * adapter = arg; + ifp = &adapter->arpcom.ac_if; + + mtx_assert(&adapter->mtx, MA_OWNED); + + INIT_DEBUGOUT("em_stop: begin"); +#ifdef DEVICE_POLLING + ether_poll_deregister(ifp); +#endif + em_disable_intr(adapter); + em_reset_hw(&adapter->hw); + 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_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + return; +} + + +/********************************************************************* + * + * 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.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); + if (!((adapter->hw.pci_cmd_word & PCIM_CMD_BUSMASTEREN) && + (adapter->hw.pci_cmd_word & PCIM_CMD_MEMEN))) { + printf("em%d: Memory Access and/or Bus Master bits were not set!\n", + adapter->unit); + adapter->hw.pci_cmd_word |= + (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); + pci_write_config(dev, PCIR_COMMAND, adapter->hw.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_id = pci_read_config(dev, PCIR_SUBDEV_0, 2); + + /* Identify the MAC */ + if (em_set_mac_type(&adapter->hw)) + printf("em%d: Unknown MAC Type\n", adapter->unit); + + if(adapter->hw.mac_type == em_82541 || + adapter->hw.mac_type == em_82541_rev_2 || + adapter->hw.mac_type == em_82547 || + adapter->hw.mac_type == em_82547_rev_2) + adapter->hw.phy_init_script = TRUE; + + return; +} + +static int +em_allocate_pci_resources(struct adapter * adapter) +{ + int i, val, rid; + device_t dev = adapter->dev; + + rid = EM_MMBA; + +#ifndef __rtems__ + adapter->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (!(adapter->res_memory)) { + printf("em%d: Unable to allocate bus resource: memory\n", + adapter->unit); + 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); +#endif + + adapter->hw.hw_addr = (uint8_t *)&adapter->osdep.mem_bus_space_handle; + + + if (adapter->hw.mac_type > em_82543) { + /* Figure our where our IO BAR is ? */ + rid = EM_MMBA; + for (i = 0; i < 5; i++) { + val = pci_read_config(dev, rid, 4); + if (val & 0x00000001) { +#ifndef __rtems__ + adapter->io_rid = rid; +#endif + break; + } + rid += 4; + } + +#ifndef __rtems__ + adapter->res_ioport = bus_alloc_resource_any(dev, + SYS_RES_IOPORT, + &adapter->io_rid, + RF_ACTIVE); + if (!(adapter->res_ioport)) { + printf("em%d: Unable to allocate bus resource: ioport\n", + adapter->unit); + return(ENXIO); + } + + adapter->hw.io_base = + rman_get_start(adapter->res_ioport); +#else + adapter->hw.io_base = val & PCI_BASE_ADDRESS_IO_MASK; +#endif + } + +#ifndef __rtems__ + rid = 0x0; + adapter->res_interrupt = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | + RF_ACTIVE); + if (!(adapter->res_interrupt)) { + printf("em%d: Unable to allocate bus resource: interrupt\n", + adapter->unit); + return(ENXIO); + } + if (bus_setup_intr(dev, adapter->res_interrupt, + INTR_TYPE_NET | INTR_MPSAFE, + (void (*)(void *)) em_intr, adapter, + &adapter->int_handler_tag)) { + printf("em%d: Error registering interrupt handler!\n", + adapter->unit); + return(ENXIO); + } +#endif + + adapter->hw.back = &adapter->osdep; + + return(0); +} + +#ifndef __rtems__ +static void +em_free_pci_resources(struct adapter * adapter) +{ + device_t dev = adapter->dev; + + if (adapter->res_interrupt != NULL) { + bus_teardown_intr(dev, adapter->res_interrupt, + adapter->int_handler_tag); + bus_release_resource(dev, SYS_RES_IRQ, 0, + adapter->res_interrupt); + } + if (adapter->res_memory != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, EM_MMBA, + adapter->res_memory); + } + + if (adapter->res_ioport != NULL) { + bus_release_resource(dev, SYS_RES_IOPORT, adapter->io_rid, + adapter->res_ioport); + } + return; +} +#endif + +/********************************************************************* + * + * Initialize the hardware to a configuration as specified by the + * adapter structure. The controller is reset, the EEPROM is + * verified, the MAC address is set, then the shared initialization + * routines are called. + * + **********************************************************************/ +static int +em_hardware_init(struct adapter * adapter) +{ + INIT_DEBUGOUT("em_hardware_init: begin"); + /* Issue a global reset */ + em_reset_hw(&adapter->hw); + + /* When hardware is reset, fifo_head is also reset */ + adapter->tx_fifo_head = 0; + + /* Make sure we have a good EEPROM before we read from it */ + if (em_validate_eeprom_checksum(&adapter->hw) < 0) { + printf("em%d: The EEPROM Checksum Is Not Valid\n", + adapter->unit); + return(EIO); + } + + if (em_read_part_num(&adapter->hw, &(adapter->part_num)) < 0) { + printf("em%d: EEPROM read error while reading part number\n", + adapter->unit); + return(EIO); + } + + if (em_init_hw(&adapter->hw) < 0) { + printf("em%d: Hardware Initialization Failed", + adapter->unit); + return(EIO); + } + + em_check_for_link(&adapter->hw); + if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU) + adapter->link_active = 1; + else + adapter->link_active = 0; + + if (adapter->link_active) { + em_get_speed_and_duplex(&adapter->hw, + &adapter->link_speed, + &adapter->link_duplex); + } else { + adapter->link_speed = 0; + adapter->link_duplex = 0; + } + + return(0); +} + +/********************************************************************* + * + * Setup networking device structure and register an interface. + * + **********************************************************************/ +static void +em_setup_interface(device_t dev, struct adapter * adapter) +{ + struct ifnet *ifp = &device_get_softc(dev)->arpcom.ac_if; + INIT_DEBUGOUT("em_setup_interface: begin"); + + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_mtu = ETHERMTU; + ifp->if_baudrate = 1000000000; + ifp->if_init = em_init; + ifp->if_softc = adapter; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; +#ifdef __rtems__ + ifp->if_output = ether_output; +#endif + ifp->if_ioctl = em_ioctl; + ifp->if_start = em_start; + ifp->if_watchdog = em_watchdog; +#ifndef __rtems__ + 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); +#else + ifp->if_snd.ifq_maxlen = adapter->num_tx_desc - 1; +#endif + +#ifndef __rtems__ +#if __FreeBSD_version < 500000 + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); +#else + ether_ifattach(ifp, adapter->hw.mac_addr); +#endif +#else + if ( !ifp->if_addrlist ) /* reattach hack */ + { + if_attach(ifp); + ether_ifattach(ifp); + } +#endif + +#ifndef __rtems__ + ifp->if_capabilities = ifp->if_capenable = 0; + + if (adapter->hw.mac_type >= em_82543) { + ifp->if_capabilities |= IFCAP_HWCSUM; + ifp->if_capenable |= IFCAP_HWCSUM; + } + + /* + * Tell the upper layer(s) we support long frames. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); +#if __FreeBSD_version >= 500000 + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; + ifp->if_capenable |= IFCAP_VLAN_MTU; +#endif + +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; + ifp->if_capenable |= IFCAP_POLLING; +#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 == em_media_type_fiber) { + ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_SX | IFM_FDX, + 0, NULL); + ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_SX, + 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 __FreeBSD_version < 500000 + ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_TX | IFM_FDX, + 0, NULL); + ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_TX, 0, NULL); +#else + ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_T | IFM_FDX, + 0, NULL); + ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_T, 0, NULL); +#endif + } + ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO); +#endif + + return; +} + +#ifndef __rtems__ +/********************************************************************* + * + * 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 != em_phy_igp) || + !adapter->hw.autoneg || !(adapter->hw.autoneg_advertised & ADVERTISE_1000_FULL)) + return; + + if(adapter->smartspeed == 0) { + /* If Master/Slave config fault is asserted twice, + * we assume back-to-back */ + em_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp); + if(!(phy_tmp & SR_1000T_MS_CONFIG_FAULT)) return; + em_read_phy_reg(&adapter->hw, PHY_1000T_STATUS, &phy_tmp); + if(phy_tmp & SR_1000T_MS_CONFIG_FAULT) { + em_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, + &phy_tmp); + if(phy_tmp & CR_1000T_MS_ENABLE) { + phy_tmp &= ~CR_1000T_MS_ENABLE; + em_write_phy_reg(&adapter->hw, + PHY_1000T_CTRL, phy_tmp); + adapter->smartspeed++; + if(adapter->hw.autoneg && + !em_phy_setup_autoneg(&adapter->hw) && + !em_read_phy_reg(&adapter->hw, PHY_CTRL, + &phy_tmp)) { + phy_tmp |= (MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + em_write_phy_reg(&adapter->hw, + PHY_CTRL, phy_tmp); + } + } + } + return; + } else if(adapter->smartspeed == EM_SMARTSPEED_DOWNSHIFT) { + /* If still no link, perhaps using 2/3 pair cable */ + em_read_phy_reg(&adapter->hw, PHY_1000T_CTRL, &phy_tmp); + phy_tmp |= CR_1000T_MS_ENABLE; + em_write_phy_reg(&adapter->hw, PHY_1000T_CTRL, phy_tmp); + if(adapter->hw.autoneg && + !em_phy_setup_autoneg(&adapter->hw) && + !em_read_phy_reg(&adapter->hw, PHY_CTRL, &phy_tmp)) { + phy_tmp |= (MII_CR_AUTO_NEG_EN | + MII_CR_RESTART_AUTO_NEG); + em_write_phy_reg(&adapter->hw, PHY_CTRL, phy_tmp); + } + } + /* Restart process after EM_SMARTSPEED_MAX iterations */ + if(adapter->smartspeed++ == EM_SMARTSPEED_MAX) + adapter->smartspeed = 0; + + return; +} + + +/* + * 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->ds_addr; + return; +} +#endif + +static int +em_dma_malloc(struct adapter *adapter, bus_size_t size, + struct em_dma_alloc *dma, int mapflags) +{ + int r; + +#ifndef __rtems__ + r = bus_dma_tag_create(NULL, /* parent */ + PAGE_SIZE, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + size, /* maxsize */ + 1, /* nsegments */ + size, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &dma->dma_tag); + if (r != 0) { + printf("em%d: em_dma_malloc: bus_dma_tag_create failed; " + "error %u\n", adapter->unit, r); + goto fail_0; + } + + r = bus_dmamem_alloc(dma->dma_tag, (void**) &dma->dma_vaddr, + BUS_DMA_NOWAIT, &dma->dma_map); +#else + if ( (dma->malloc_base = malloc( size + PAGE_SIZE, M_DEVBUF, M_NOWAIT )) ) { + r = 0; + dma->dma_vaddr = (caddr_t)_DO_ALIGN(dma->malloc_base, PAGE_SIZE); + } else { + r = -1; + } +#endif + if (r != 0) { +#ifndef __rtems__ + printf("em%d: em_dma_malloc: bus_dmammem_alloc failed; " + "size %ju, error %d\n", adapter->unit, + (uintmax_t)size, r); +#else + printf("em%d: em_dma_malloc: bus_dmammem_alloc failed; " + "size %u, error %d\n", adapter->unit, + size, r); +#endif + goto fail_2; + } + +#ifndef __rtems__ + r = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, + size, + em_dmamap_cb, + &dma->dma_paddr, + mapflags | BUS_DMA_NOWAIT); +#else + dma->dma_paddr = kvtop(dma->dma_vaddr); +#endif + if (r != 0) { + printf("em%d: em_dma_malloc: bus_dmamap_load failed; " + "error %u\n", adapter->unit, r); + goto fail_3; + } + +#ifndef __rtems__ + dma->dma_size = size; +#endif + return (0); + +fail_3: + bus_dmamap_unload(dma->dma_tag, dma->dma_map); +fail_2: +#ifndef __rtems__ + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); +#else + free(dma->malloc_base, M_DEVBUF); + dma->dma_vaddr = dma->malloc_base = 0; + dma->dma_paddr = 0; +#endif + bus_dma_tag_destroy(dma->dma_tag); +#ifndef __rtems__ +fail_0: + dma->dma_map = NULL; + dma->dma_tag = NULL; +#endif + return (r); +} + +static void +em_dma_free(struct adapter *adapter, struct em_dma_alloc *dma) +{ + bus_dmamap_unload(dma->dma_tag, dma->dma_map); +#ifndef __rtems__ + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); +#else + free(dma->malloc_base, M_DEVBUF); + dma->dma_vaddr = dma->malloc_base = 0; + dma->dma_paddr = 0; +#endif + bus_dma_tag_destroy(dma->dma_tag); +} + + +/********************************************************************* + * + * Allocate memory for tx_buffer structures. The tx_buffer stores all + * the information needed to transmit a packet on the wire. + * + **********************************************************************/ +static int +em_allocate_transmit_structures(struct adapter * adapter) +{ + if (!(adapter->tx_buffer_area = + (struct em_buffer *) malloc(sizeof(struct em_buffer) * + adapter->num_tx_desc, M_DEVBUF, + M_NOWAIT))) { + printf("em%d: Unable to allocate tx_buffer memory\n", + adapter->unit); + return ENOMEM; + } + + bzero(adapter->tx_buffer_area, + sizeof(struct em_buffer) * adapter->num_tx_desc); + + return 0; +} + +/********************************************************************* + * + * Allocate and initialize transmit structures. + * + **********************************************************************/ +static int +em_setup_transmit_structures(struct adapter * adapter) +{ +#ifndef __rtems__ + /* + * Setup DMA descriptor areas. + */ + if (bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES * 8, /* maxsize */ + EM_MAX_SCATTER, /* nsegments */ + MCLBYTES * 8, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &adapter->txtag)) { + printf("em%d: Unable to allocate TX DMA tag\n", adapter->unit); + return (ENOMEM); + } +#endif + + if (em_allocate_transmit_structures(adapter)) + return (ENOMEM); + + bzero((void *) adapter->tx_desc_base, + (sizeof(struct em_tx_desc)) * adapter->num_tx_desc); + + adapter->next_avail_tx_desc = 0; + adapter->oldest_used_tx_desc = 0; + + /* Set number of descriptors available */ + adapter->num_tx_desc_avail = adapter->num_tx_desc; + + /* Set checksum context */ + adapter->active_checksum_context = OFFLOAD_NONE; + + return (0); +} + +/********************************************************************* + * + * Enable transmit unit. + * + **********************************************************************/ +static void +em_initialize_transmit_unit(struct adapter * adapter) +{ + u_int32_t reg_tctl; + u_int32_t reg_tipg = 0; + u_int64_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, TDBAL, (u_int32_t)bus_addr); + E1000_WRITE_REG(&adapter->hw, TDBAH, (u_int32_t)(bus_addr >> 32)); + E1000_WRITE_REG(&adapter->hw, TDLEN, + adapter->num_tx_desc * + sizeof(struct em_tx_desc)); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + E1000_WRITE_REG(&adapter->hw, TDH, 0); + E1000_WRITE_REG(&adapter->hw, TDT, 0); + + + HW_DEBUGOUT2("Base = %x, Length = %x\n", + E1000_READ_REG(&adapter->hw, TDBAL), + E1000_READ_REG(&adapter->hw, TDLEN)); + + /* Set the default values for the Tx Inter Packet Gap timer */ + switch (adapter->hw.mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + reg_tipg = DEFAULT_82542_TIPG_IPGT; + reg_tipg |= DEFAULT_82542_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; + reg_tipg |= DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; + break; + default: + if (adapter->hw.media_type == em_media_type_fiber) + reg_tipg = DEFAULT_82543_TIPG_IPGT_FIBER; + else + reg_tipg = DEFAULT_82543_TIPG_IPGT_COPPER; + reg_tipg |= DEFAULT_82543_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; + reg_tipg |= DEFAULT_82543_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; + } + + E1000_WRITE_REG(&adapter->hw, TIPG, reg_tipg); + E1000_WRITE_REG(&adapter->hw, TIDV, adapter->tx_int_delay.value); + if(adapter->hw.mac_type >= em_82540) + E1000_WRITE_REG(&adapter->hw, TADV, + adapter->tx_abs_int_delay.value); + + /* Program the Transmit Control Register */ + reg_tctl = E1000_TCTL_PSP | E1000_TCTL_EN | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); + if (adapter->hw.mac_type >= em_82573) + reg_tctl |= E1000_TCTL_MULR; + if (adapter->link_duplex == 1) { + reg_tctl |= E1000_FDX_COLLISION_DISTANCE << E1000_COLD_SHIFT; + } else { + reg_tctl |= E1000_HDX_COLLISION_DISTANCE << E1000_COLD_SHIFT; + } + E1000_WRITE_REG(&adapter->hw, TCTL, reg_tctl); + + /* Setup Transmit Descriptor Settings for this adapter */ + adapter->txd_cmd = E1000_TXD_CMD_IFCS | E1000_TXD_CMD_RS; + + if (adapter->tx_int_delay.value > 0) + adapter->txd_cmd |= E1000_TXD_CMD_IDE; + + return; +} + +/********************************************************************* + * + * 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_unload(adapter->txtag, tx_buffer->map); + bus_dmamap_destroy(adapter->txtag, tx_buffer->map); + m_freem(tx_buffer->m_head); + } + tx_buffer->m_head = NULL; + } + } + if (adapter->tx_buffer_area != NULL) { + free(adapter->tx_buffer_area, M_DEVBUF); + adapter->tx_buffer_area = NULL; + } +#ifndef __rtems__ + if (adapter->txtag != NULL) { + bus_dma_tag_destroy(adapter->txtag); + adapter->txtag = NULL; + } +#endif + return; +} + +#ifndef __rtems__ +/********************************************************************* + * + * The offload context needs to be set when we transfer the first + * packet of a particular protocol (TCP/UDP). We change the + * context only if the protocol type changes. + * + **********************************************************************/ +static void +em_transmit_checksum_setup(struct adapter * adapter, + struct mbuf *mp, + u_int32_t *txd_upper, + u_int32_t *txd_lower) +{ + struct em_context_desc *TXD; + struct em_buffer *tx_buffer; + int curr_txd; + + if (mp->m_pkthdr.csum_flags) { + + if (mp->m_pkthdr.csum_flags & CSUM_TCP) { + *txd_upper = E1000_TXD_POPTS_TXSM << 8; + *txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D; + if (adapter->active_checksum_context == OFFLOAD_TCP_IP) + return; + else + adapter->active_checksum_context = OFFLOAD_TCP_IP; + + } else if (mp->m_pkthdr.csum_flags & CSUM_UDP) { + *txd_upper = E1000_TXD_POPTS_TXSM << 8; + *txd_lower = E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D; + if (adapter->active_checksum_context == OFFLOAD_UDP_IP) + return; + else + adapter->active_checksum_context = OFFLOAD_UDP_IP; + } else { + *txd_upper = 0; + *txd_lower = 0; + return; + } + } else { + *txd_upper = 0; + *txd_lower = 0; + return; + } + + /* If we reach this point, the checksum offload context + * needs to be reset. + */ + curr_txd = adapter->next_avail_tx_desc; + tx_buffer = &adapter->tx_buffer_area[curr_txd]; + TXD = (struct em_context_desc *) &adapter->tx_desc_base[curr_txd]; + + TXD->lower_setup.ip_fields.ipcss = ETHER_HDR_LEN; + TXD->lower_setup.ip_fields.ipcso = + ETHER_HDR_LEN + offsetof(struct ip, ip_sum); + TXD->lower_setup.ip_fields.ipcse = + htole16(ETHER_HDR_LEN + sizeof(struct ip) - 1); + + TXD->upper_setup.tcp_fields.tucss = + ETHER_HDR_LEN + sizeof(struct ip); + TXD->upper_setup.tcp_fields.tucse = htole16(0); + + if (adapter->active_checksum_context == OFFLOAD_TCP_IP) { + TXD->upper_setup.tcp_fields.tucso = + ETHER_HDR_LEN + sizeof(struct ip) + + offsetof(struct tcphdr, th_sum); + } else if (adapter->active_checksum_context == OFFLOAD_UDP_IP) { + TXD->upper_setup.tcp_fields.tucso = + ETHER_HDR_LEN + sizeof(struct ip) + + offsetof(struct udphdr, uh_sum); + } + + TXD->tcp_seg_setup.data = htole32(0); + TXD->cmd_and_length = htole32(adapter->txd_cmd | E1000_TXD_CMD_DEXT); + + tx_buffer->m_head = NULL; + + if (++curr_txd == adapter->num_tx_desc) + curr_txd = 0; + + adapter->num_tx_desc_avail--; + adapter->next_avail_tx_desc = curr_txd; + + return; +} +#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_clean_transmit_interrupts(struct adapter * adapter) +{ + int i, num_avail; + struct em_buffer *tx_buffer; + struct em_tx_desc *tx_desc; + struct ifnet *ifp = &adapter->arpcom.ac_if; + + mtx_assert(&adapter->mtx, MA_OWNED); + + if (adapter->num_tx_desc_avail == adapter->num_tx_desc) + return; + + num_avail = adapter->num_tx_desc_avail; + i = adapter->oldest_used_tx_desc; + + tx_buffer = &adapter->tx_buffer_area[i]; + tx_desc = &adapter->tx_desc_base[i]; + + bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, + BUS_DMASYNC_POSTREAD); + while (tx_desc->upper.fields.status & E1000_TXD_STAT_DD) { + + tx_desc->upper.data = 0; + num_avail++; + + if (tx_buffer->m_head) { + ifp->if_opackets++; + bus_dmamap_unload(adapter->txtag, tx_buffer->map); + bus_dmamap_destroy(adapter->txtag, tx_buffer->map); + + m_freem(tx_buffer->m_head); + tx_buffer->m_head = NULL; + } + + if (++i == adapter->num_tx_desc) + i = 0; + + tx_buffer = &adapter->tx_buffer_area[i]; + tx_desc = &adapter->tx_desc_base[i]; + } + bus_dmamap_sync(adapter->txdma.dma_tag, adapter->txdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + adapter->oldest_used_tx_desc = i; + + /* + * If we have enough room, clear IFF_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_flags &= ~IFF_OACTIVE; + if (num_avail == adapter->num_tx_desc) + ifp->if_timer = 0; + else if (num_avail == adapter->num_tx_desc_avail) + ifp->if_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(int i, struct adapter *adapter, + struct mbuf *nmp) +{ + register struct mbuf *mp = nmp; + struct em_buffer *rx_buffer; + struct ifnet *ifp; + bus_addr_t paddr; +#ifndef __rtems__ + int error; +#endif + + ifp = &adapter->arpcom.ac_if; + + if (mp == NULL) { +#ifndef __rtems__ + mp = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); +#else + MGETHDR(mp, M_DONTWAIT, MT_DATA); + if ( mp ) { + MCLGET( mp, M_DONTWAIT ); + if ( !(mp->m_flags & M_EXT) ) { + m_freem(mp); + mp = 0; + } + } +#endif + if (mp == NULL) { + adapter->mbuf_cluster_failed++; + return(ENOBUFS); + } + mp->m_len = mp->m_pkthdr.len = MCLBYTES; + } else { + mp->m_len = mp->m_pkthdr.len = MCLBYTES; + mp->m_data = mp->m_ext.ext_buf; + mp->m_next = NULL; + } + + if (ifp->if_mtu <= ETHERMTU) { + m_adj(mp, ETHER_ALIGN); + } + + rx_buffer = &adapter->rx_buffer_area[i]; + +#ifndef __rtems__ + /* + * Using memory from the mbuf cluster pool, invoke the + * bus_dma machinery to arrange the memory mapping. + */ + error = bus_dmamap_load(adapter->rxtag, rx_buffer->map, + mtod(mp, void *), mp->m_len, + em_dmamap_cb, &paddr, 0); + if (error) { + m_free(mp); + return(error); + } +#else + paddr = kvtop(mtod(mp, void*)); +#endif + + rx_buffer->m_head = mp; + adapter->rx_desc_base[i].buffer_addr = htole64(paddr); + bus_dmamap_sync(adapter->rxtag, rx_buffer->map, BUS_DMASYNC_PREREAD); + + 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) +{ + int i, error; +#ifndef __rtems__ + struct em_buffer *rx_buffer; +#endif + + if (!(adapter->rx_buffer_area = + (struct em_buffer *) malloc(sizeof(struct em_buffer) * + adapter->num_rx_desc, M_DEVBUF, + M_NOWAIT))) { + printf("em%d: Unable to allocate rx_buffer memory\n", + adapter->unit); + return(ENOMEM); + } + + bzero(adapter->rx_buffer_area, + sizeof(struct em_buffer) * adapter->num_rx_desc); + +#ifndef __rtems__ + error = bus_dma_tag_create(NULL, /* parent */ + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, /* maxsize */ + 1, /* nsegments */ + MCLBYTES, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &adapter->rxtag); + if (error != 0) { + printf("em%d: em_allocate_receive_structures: " + "bus_dma_tag_create failed; error %u\n", + adapter->unit, error); + goto fail_0; + } + 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 != 0) { + printf("em%d: em_allocate_receive_structures: " + "bus_dmamap_create failed; error %u\n", + adapter->unit, error); + goto fail_1; + } + } + +#else + error = 0; +#endif + + for (i = 0; i < adapter->num_rx_desc; i++) { + error = em_get_buf(i, adapter, NULL); + if (error != 0) { + adapter->rx_buffer_area[i].m_head = NULL; + adapter->rx_desc_base[i].buffer_addr = 0; + return(error); + } + } + bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + return(0); + +#ifndef __rtems__ +fail_1: + bus_dma_tag_destroy(adapter->rxtag); +fail_0: + adapter->rxtag = NULL; +#endif + free(adapter->rx_buffer_area, M_DEVBUF); + adapter->rx_buffer_area = NULL; + return (error); +} + +/********************************************************************* + * + * Allocate and initialize receive structures. + * + **********************************************************************/ +static int +em_setup_receive_structures(struct adapter * adapter) +{ + bzero((void *) adapter->rx_desc_base, + (sizeof(struct em_rx_desc)) * adapter->num_rx_desc); + + if (em_allocate_receive_structures(adapter)) + return ENOMEM; + + /* 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) +{ + u_int32_t reg_rctl; +#ifndef __rtems__ + u_int32_t reg_rxcsum; +#endif + struct ifnet *ifp; + u_int64_t bus_addr; + + INIT_DEBUGOUT("em_initialize_receive_unit: begin"); + ifp = &adapter->arpcom.ac_if; + + /* Make sure receives are disabled while setting up the descriptor ring */ + E1000_WRITE_REG(&adapter->hw, RCTL, 0); + + /* Set the Receive Delay Timer Register */ + E1000_WRITE_REG(&adapter->hw, RDTR, + adapter->rx_int_delay.value | E1000_RDT_FPDB); + + if(adapter->hw.mac_type >= em_82540) { + E1000_WRITE_REG(&adapter->hw, 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, ITR, DEFAULT_ITR); + } + + /* Setup the Base and Length of the Rx Descriptor Ring */ + bus_addr = adapter->rxdma.dma_paddr; + E1000_WRITE_REG(&adapter->hw, RDBAL, (u_int32_t)bus_addr); + E1000_WRITE_REG(&adapter->hw, RDBAH, (u_int32_t)(bus_addr >> 32)); + E1000_WRITE_REG(&adapter->hw, RDLEN, adapter->num_rx_desc * + sizeof(struct em_rx_desc)); + + /* Setup the HW Rx Head and Tail Descriptor Pointers */ + E1000_WRITE_REG(&adapter->hw, RDH, 0); + E1000_WRITE_REG(&adapter->hw, RDT, adapter->num_rx_desc - 1); + + /* Setup the Receive Control Register */ + reg_rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | + E1000_RCTL_RDMTS_HALF | + (adapter->hw.mc_filter_type << E1000_RCTL_MO_SHIFT); + + if (adapter->hw.tbi_compatibility_on == TRUE) + reg_rctl |= E1000_RCTL_SBP; + + + switch (adapter->rx_buffer_len) { + default: + case EM_RXBUFFER_2048: + reg_rctl |= E1000_RCTL_SZ_2048; + break; + case EM_RXBUFFER_4096: + reg_rctl |= E1000_RCTL_SZ_4096 | E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + case EM_RXBUFFER_8192: + reg_rctl |= E1000_RCTL_SZ_8192 | E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + case EM_RXBUFFER_16384: + reg_rctl |= E1000_RCTL_SZ_16384 | E1000_RCTL_BSEX | E1000_RCTL_LPE; + break; + } + + if (ifp->if_mtu > ETHERMTU) + reg_rctl |= E1000_RCTL_LPE; + +#ifndef __rtems__ + /* Enable 82543 Receive Checksum Offload for TCP and UDP */ + if ((adapter->hw.mac_type >= em_82543) && + (ifp->if_capenable & IFCAP_RXCSUM)) { + reg_rxcsum = E1000_READ_REG(&adapter->hw, RXCSUM); + reg_rxcsum |= (E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL); + E1000_WRITE_REG(&adapter->hw, RXCSUM, reg_rxcsum); + } +#endif + + /* Enable Receives */ + E1000_WRITE_REG(&adapter->hw, RCTL, reg_rctl); + + 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_buffer_area != NULL) { + rx_buffer = adapter->rx_buffer_area; + for (i = 0; i < adapter->num_rx_desc; i++, rx_buffer++) { +#ifndef __rtems__ + if (rx_buffer->map != NULL) { + bus_dmamap_unload(adapter->rxtag, rx_buffer->map); + bus_dmamap_destroy(adapter->rxtag, rx_buffer->map); + } +#endif + if (rx_buffer->m_head != NULL) + m_freem(rx_buffer->m_head); + rx_buffer->m_head = NULL; + } + } + if (adapter->rx_buffer_area != NULL) { + free(adapter->rx_buffer_area, M_DEVBUF); + adapter->rx_buffer_area = NULL; + } +#ifndef __rtems__ + if (adapter->rxtag != NULL) { + bus_dma_tag_destroy(adapter->rxtag); + adapter->rxtag = NULL; + } +#endif + return; +} + +/********************************************************************* + * + * 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 void +em_process_receive_interrupts(struct adapter * adapter, int count) +{ + struct ifnet *ifp; + struct mbuf *mp; +#if __FreeBSD_version < 500000 + struct ether_header *eh; +#endif + u_int8_t accept_frame = 0; + u_int8_t eop = 0; + u_int16_t len, desc_len, prev_len_adj; + int i; + + /* Pointer to the receive descriptor being examined. */ + struct em_rx_desc *current_desc; + + mtx_assert(&adapter->mtx, MA_OWNED); + + ifp = &adapter->arpcom.ac_if; + 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; + } + + while ((current_desc->status & E1000_RXD_STAT_DD) && (count != 0)) { + + mp = adapter->rx_buffer_area[i].m_head; + 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); + if (current_desc->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) { + u_int8_t last_byte; + u_int32_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, current_desc->status, + current_desc->errors, + pkt_len, last_byte)) { + em_tbi_adjust_stats(&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(i, adapter, NULL) == ENOBUFS) { + adapter->dropped_pkts++; + em_get_buf(i, adapter, mp); + if (adapter->fmp != NULL) + m_freem(adapter->fmp); + adapter->fmp = NULL; + adapter->lmp = NULL; + break; + } + + /* 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++; + +#if __FreeBSD_version < 500000 + eh = mtod(adapter->fmp, struct ether_header *); + /* Remove ethernet header from mbuf */ + m_adj(adapter->fmp, sizeof(struct ether_header)); +#ifndef __rtems__ + em_receive_checksum(adapter, current_desc, + adapter->fmp); + if (current_desc->status & E1000_RXD_STAT_VP) + VLAN_INPUT_TAG(eh, adapter->fmp, + (current_desc->special & + E1000_RXD_SPC_VLAN_MASK)); + else +#endif + ether_input(ifp, eh, adapter->fmp); +#else + + em_receive_checksum(adapter, current_desc, + adapter->fmp); + if (current_desc->status & E1000_RXD_STAT_VP) + VLAN_INPUT_TAG(ifp, adapter->fmp, + (current_desc->special & + E1000_RXD_SPC_VLAN_MASK), + adapter->fmp = NULL); + + if (adapter->fmp != NULL) { + EM_UNLOCK(adapter); + (*ifp->if_input)(ifp, adapter->fmp); + EM_LOCK(adapter); + } +#endif + adapter->fmp = NULL; + adapter->lmp = NULL; + } + } else { + adapter->dropped_pkts++; + em_get_buf(i, adapter, mp); + if (adapter->fmp != NULL) + m_freem(adapter->fmp); + adapter->fmp = NULL; + adapter->lmp = NULL; + } + + /* Zero out the receive descriptors status */ + current_desc->status = 0; + + /* Advance the E1000's Receive Queue #0 "Tail Pointer". */ + E1000_WRITE_REG(&adapter->hw, RDT, i); + + /* Advance our pointers to the next descriptor */ + if (++i == adapter->num_rx_desc) { + i = 0; + current_desc = adapter->rx_desc_base; + } else + current_desc++; + } + bus_dmamap_sync(adapter->rxdma.dma_tag, adapter->rxdma.dma_map, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + adapter->next_rx_desc_to_check = i; + return; +} + +#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 em_rx_desc *rx_desc, + struct mbuf *mp) +{ + /* 82543 or newer only */ + if ((adapter->hw.mac_type < em_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); + } + } + + return; +} + + +static void +em_enable_vlans(struct adapter *adapter) +{ + uint32_t ctrl; + + E1000_WRITE_REG(&adapter->hw, VET, ETHERTYPE_VLAN); + + ctrl = E1000_READ_REG(&adapter->hw, CTRL); + ctrl |= E1000_CTRL_VME; + E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + + return; +} + +static void +em_disable_vlans(struct adapter *adapter) +{ + uint32_t ctrl; + + ctrl = E1000_READ_REG(&adapter->hw, CTRL); + ctrl &= ~E1000_CTRL_VME; + E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + + return; +} +#endif + +static void +em_enable_intr(struct adapter * adapter) +{ + E1000_WRITE_REG(&adapter->hw, IMS, (IMS_ENABLE_MASK)); + return; +} + +static void +em_disable_intr(struct adapter *adapter) +{ + /* + * The first version of 82542 had an errata where when link was forced it + * would stay up even up even if the cable was disconnected. Sequence errors + * were used to detect the disconnect and then the driver would unforce the link. + * This code in the in the ISR. For this to work correctly the Sequence error + * interrupt had to be enabled all the time. + */ + + if (adapter->hw.mac_type == em_82542_rev2_0) + E1000_WRITE_REG(&adapter->hw, IMC, + (0xffffffff & ~E1000_IMC_RXSEQ)); + else + E1000_WRITE_REG(&adapter->hw, IMC, + 0xffffffff); + return; +} + +static int +em_is_valid_ether_addr(u_int8_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); +} + +void +em_write_pci_cfg(struct em_hw *hw, + uint32_t reg, + uint16_t *value) +{ + pci_write_config(((struct em_osdep *)hw->back)->dev, reg, + *value, 2); +} + +void +em_read_pci_cfg(struct em_hw *hw, uint32_t reg, + uint16_t *value) +{ + *value = pci_read_config(((struct em_osdep *)hw->back)->dev, + reg, 2); + return; +} + +void +em_pci_set_mwi(struct em_hw *hw) +{ + pci_write_config(((struct em_osdep *)hw->back)->dev, + PCIR_COMMAND, + (hw->pci_cmd_word | CMD_MEM_WRT_INVALIDATE), 2); + return; +} + +void +em_pci_clear_mwi(struct em_hw *hw) +{ + pci_write_config(((struct em_osdep *)hw->back)->dev, + PCIR_COMMAND, + (hw->pci_cmd_word & ~CMD_MEM_WRT_INVALIDATE), 2); + return; +} + +uint32_t +em_io_read(struct em_hw *hw, unsigned long port) +{ + return(inl(port)); +} + +void +em_io_write(struct em_hw *hw, unsigned long port, uint32_t value) +{ +#ifndef __rtems__ + outl(port, value); +#else + /* everybody else has this the other way round! */ + outl(value, port); +#endif + 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 u_int32_t +em_fill_descriptors (u_int64_t address, + u_int32_t length, + PDESC_ARRAY desc_array) +{ + /* Since issue is sensitive to length and address.*/ + /* Let us first check the address...*/ + u_int32_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 = (u_int32_t)((((u_int32_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 == em_media_type_copper || + (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU)) { + adapter->stats.symerrs += E1000_READ_REG(&adapter->hw, SYMERRS); + adapter->stats.sec += E1000_READ_REG(&adapter->hw, SEC); + } + adapter->stats.crcerrs += E1000_READ_REG(&adapter->hw, CRCERRS); + adapter->stats.mpc += E1000_READ_REG(&adapter->hw, MPC); + adapter->stats.scc += E1000_READ_REG(&adapter->hw, SCC); + adapter->stats.ecol += E1000_READ_REG(&adapter->hw, ECOL); + + adapter->stats.mcc += E1000_READ_REG(&adapter->hw, MCC); + adapter->stats.latecol += E1000_READ_REG(&adapter->hw, LATECOL); + adapter->stats.colc += E1000_READ_REG(&adapter->hw, COLC); + adapter->stats.dc += E1000_READ_REG(&adapter->hw, DC); + adapter->stats.rlec += E1000_READ_REG(&adapter->hw, RLEC); + adapter->stats.xonrxc += E1000_READ_REG(&adapter->hw, XONRXC); + adapter->stats.xontxc += E1000_READ_REG(&adapter->hw, XONTXC); + adapter->stats.xoffrxc += E1000_READ_REG(&adapter->hw, XOFFRXC); + adapter->stats.xofftxc += E1000_READ_REG(&adapter->hw, XOFFTXC); + adapter->stats.fcruc += E1000_READ_REG(&adapter->hw, FCRUC); + adapter->stats.prc64 += E1000_READ_REG(&adapter->hw, PRC64); + adapter->stats.prc127 += E1000_READ_REG(&adapter->hw, PRC127); + adapter->stats.prc255 += E1000_READ_REG(&adapter->hw, PRC255); + adapter->stats.prc511 += E1000_READ_REG(&adapter->hw, PRC511); + adapter->stats.prc1023 += E1000_READ_REG(&adapter->hw, PRC1023); + adapter->stats.prc1522 += E1000_READ_REG(&adapter->hw, PRC1522); + adapter->stats.gprc += E1000_READ_REG(&adapter->hw, GPRC); + adapter->stats.bprc += E1000_READ_REG(&adapter->hw, BPRC); + adapter->stats.mprc += E1000_READ_REG(&adapter->hw, MPRC); + adapter->stats.gptc += E1000_READ_REG(&adapter->hw, 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, GORCL); + adapter->stats.gorch += E1000_READ_REG(&adapter->hw, GORCH); + adapter->stats.gotcl += E1000_READ_REG(&adapter->hw, GOTCL); + adapter->stats.gotch += E1000_READ_REG(&adapter->hw, GOTCH); + + adapter->stats.rnbc += E1000_READ_REG(&adapter->hw, RNBC); + adapter->stats.ruc += E1000_READ_REG(&adapter->hw, RUC); + adapter->stats.rfc += E1000_READ_REG(&adapter->hw, RFC); + adapter->stats.roc += E1000_READ_REG(&adapter->hw, ROC); + adapter->stats.rjc += E1000_READ_REG(&adapter->hw, RJC); + + adapter->stats.torl += E1000_READ_REG(&adapter->hw, TORL); + adapter->stats.torh += E1000_READ_REG(&adapter->hw, TORH); + adapter->stats.totl += E1000_READ_REG(&adapter->hw, TOTL); + adapter->stats.toth += E1000_READ_REG(&adapter->hw, TOTH); + + adapter->stats.tpr += E1000_READ_REG(&adapter->hw, TPR); + adapter->stats.tpt += E1000_READ_REG(&adapter->hw, TPT); + adapter->stats.ptc64 += E1000_READ_REG(&adapter->hw, PTC64); + adapter->stats.ptc127 += E1000_READ_REG(&adapter->hw, PTC127); + adapter->stats.ptc255 += E1000_READ_REG(&adapter->hw, PTC255); + adapter->stats.ptc511 += E1000_READ_REG(&adapter->hw, PTC511); + adapter->stats.ptc1023 += E1000_READ_REG(&adapter->hw, PTC1023); + adapter->stats.ptc1522 += E1000_READ_REG(&adapter->hw, PTC1522); + adapter->stats.mptc += E1000_READ_REG(&adapter->hw, MPTC); + adapter->stats.bptc += E1000_READ_REG(&adapter->hw, BPTC); + + if (adapter->hw.mac_type >= em_82543) { + adapter->stats.algnerrc += + E1000_READ_REG(&adapter->hw, ALGNERRC); + adapter->stats.rxerrc += + E1000_READ_REG(&adapter->hw, RXERRC); + adapter->stats.tncrs += + E1000_READ_REG(&adapter->hw, TNCRS); + adapter->stats.cexterr += + E1000_READ_REG(&adapter->hw, CEXTERR); + adapter->stats.tsctc += + E1000_READ_REG(&adapter->hw, TSCTC); + adapter->stats.tsctfc += + E1000_READ_REG(&adapter->hw, TSCTFC); + } + ifp = &adapter->arpcom.ac_if; + + /* Fill out the OS statistics structure */ + ifp->if_ibytes = adapter->stats.gorcl; + ifp->if_obytes = adapter->stats.gotcl; + ifp->if_imcasts = adapter->stats.mprc; + 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.rlec + + adapter->stats.mpc + adapter->stats.cexterr; + + /* Tx Errors */ + ifp->if_oerrors = adapter->stats.ecol + adapter->stats.latecol; + +} + +#ifndef __rtems__ +/********************************************************************** + * + * 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. + * + **********************************************************************/ +static void +em_print_debug_info(struct adapter *adapter) +{ + int unit = adapter->unit; + uint8_t *hw_addr = adapter->hw.hw_addr; + + printf("em%d: Adapter hardware address = %p \n", unit, hw_addr); + printf("em%d:CTRL = 0x%x\n", unit, + E1000_READ_REG(&adapter->hw, CTRL)); + printf("em%d:RCTL = 0x%x PS=(0x8402)\n", unit, + E1000_READ_REG(&adapter->hw, RCTL)); + printf("em%d:tx_int_delay = %d, tx_abs_int_delay = %d\n", unit, + E1000_READ_REG(&adapter->hw, TIDV), + E1000_READ_REG(&adapter->hw, TADV)); + printf("em%d:rx_int_delay = %d, rx_abs_int_delay = %d\n", unit, + E1000_READ_REG(&adapter->hw, RDTR), + E1000_READ_REG(&adapter->hw, RADV)); + printf("em%d: fifo workaround = %lld, fifo_reset = %lld\n", unit, + (long long)adapter->tx_fifo_wrk_cnt, + (long long)adapter->tx_fifo_reset_cnt); + printf("em%d: hw tdh = %d, hw tdt = %d\n", unit, + E1000_READ_REG(&adapter->hw, TDH), + E1000_READ_REG(&adapter->hw, TDT)); + printf("em%d: Num Tx descriptors avail = %d\n", unit, + adapter->num_tx_desc_avail); + printf("em%d: Tx Descriptors not avail1 = %ld\n", unit, + adapter->no_tx_desc_avail1); + printf("em%d: Tx Descriptors not avail2 = %ld\n", unit, + adapter->no_tx_desc_avail2); + printf("em%d: Std mbuf failed = %ld\n", unit, + adapter->mbuf_alloc_failed); + printf("em%d: Std mbuf cluster failed = %ld\n", unit, + adapter->mbuf_cluster_failed); + printf("em%d: Driver dropped packets = %ld\n", unit, + adapter->dropped_pkts); + + return; +} +#endif + +static void +em_print_hw_stats(struct adapter *adapter) +{ + int unit = adapter->unit; + + printf("em%d: Excessive collisions = %lld\n", unit, + (long long)adapter->stats.ecol); + printf("em%d: Symbol errors = %lld\n", unit, + (long long)adapter->stats.symerrs); + printf("em%d: Sequence errors = %lld\n", unit, + (long long)adapter->stats.sec); + printf("em%d: Defer count = %lld\n", unit, + (long long)adapter->stats.dc); + + printf("em%d: Missed Packets = %lld\n", unit, + (long long)adapter->stats.mpc); + printf("em%d: Receive No Buffers = %lld\n", unit, + (long long)adapter->stats.rnbc); + printf("em%d: Receive length errors = %lld\n", unit, + (long long)adapter->stats.rlec); + printf("em%d: Receive errors = %lld\n", unit, + (long long)adapter->stats.rxerrc); + printf("em%d: Crc errors = %lld\n", unit, + (long long)adapter->stats.crcerrs); + printf("em%d: Alignment errors = %lld\n", unit, + (long long)adapter->stats.algnerrc); + printf("em%d: Carrier extension errors = %lld\n", unit, + (long long)adapter->stats.cexterr); + + printf("em%d: XON Rcvd = %lld\n", unit, + (long long)adapter->stats.xonrxc); + printf("em%d: XON Xmtd = %lld\n", unit, + (long long)adapter->stats.xontxc); + printf("em%d: XOFF Rcvd = %lld\n", unit, + (long long)adapter->stats.xoffrxc); + printf("em%d: XOFF Xmtd = %lld\n", unit, + (long long)adapter->stats.xofftxc); + + printf("em%d: Good Packets Rcvd = %lld\n", unit, + (long long)adapter->stats.gprc); + printf("em%d: Good Packets Xmtd = %lld\n", unit, + (long long)adapter->stats.gptc); + + return; +} + +#ifndef __rtems__ +static int +em_sysctl_debug_info(SYSCTL_HANDLER_ARGS) +{ + int error; + int result; + struct adapter *adapter; + + 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) +{ + int error; + int result; + struct adapter *adapter; + + 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; + u_int32_t regval; + int error; + int usecs; + int ticks; + int s; + + info = (struct em_int_delay_info *)arg1; + adapter = info->adapter; + usecs = info->value; + error = sysctl_handle_int(oidp, &usecs, 0, req); + if (error != 0 || req->newptr == NULL) + return error; + if (usecs < 0 || usecs > E1000_TICKS_TO_USECS(65535)) + return EINVAL; + info->value = usecs; + ticks = E1000_USECS_TO_TICKS(usecs); + + s = splimp(); + regval = E1000_READ_OFFSET(&adapter->hw, info->offset); + regval = (regval & ~0xffff) | (ticks & 0xffff); + /* Handle a few special cases. */ + switch (info->offset) { + case E1000_RDTR: + case E1000_82542_RDTR: + regval |= E1000_RDT_FPDB; + break; + case E1000_TIDV: + case E1000_82542_TIDV: + if (ticks == 0) { + adapter->txd_cmd &= ~E1000_TXD_CMD_IDE; + /* Don't write 0 into the TIDV register. */ + regval++; + } else + adapter->txd_cmd |= E1000_TXD_CMD_IDE; + break; + } + E1000_WRITE_OFFSET(&adapter->hw, info->offset, regval); + splx(s); + return 0; +} + +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); +} +#endif + +#ifdef __rtems__ +/* Initialize bare minimals so we can check the phy link status */ +int +em_hw_early_init(device_t dev) +{ +struct adapter *adapter = device_get_softc(dev); + adapter->dev = dev; + adapter->osdep.dev = dev; + em_identify_hardware(adapter); + return em_allocate_pci_resources(adapter); +} +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.h new file mode 100644 index 0000000000..560c682581 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em.h @@ -0,0 +1,493 @@ +/************************************************************************** + +Copyright (c) 2001-2005, 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: /repoman/r/ncvs/src/sys/dev/em/if_em.h,v 1.31 2005/05/26 23:32:02 tackerman Exp $*/ + +#ifndef _EM_H_DEFINED_ +#define _EM_H_DEFINED_ + + +#include +#include +#include +#include +#include +#include +#include +#ifndef __rtems__ +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#ifndef __rtems__ + +#include +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#ifndef __rtems__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif + +#ifndef __rtems__ +#include +#include +#include "opt_bdg.h" + + +#include +#else +#include +#endif + +/* Tunables */ + +/* + * EM_MAX_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. + */ +#define EM_MAX_TXD 256 + +/* + * EM_MAX_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. + * + */ +#define EM_MAX_RXD 80 + +/* + * 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 maximum no of times the driver will loop + * in the isr. + * Minimum Value = 1 + */ +#define EM_MAX_INTR 3 + +/* + * Inform the stack about transmit checksum offload capabilities. + */ +#define EM_CHECKSUM_FEATURES (CSUM_TCP | CSUM_UDP) + +/* + * 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. + */ +#ifndef __rtems__ +#define EM_TX_CLEANUP_THRESHOLD EM_MAX_TXD / 8 +#else +#define EM_TX_CLEANUP_THRESHOLD (adapter->tx_cleanup_threshold) +#endif + +/* + * 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 + +/* + * EM_MASTER_SLAVE is only defined to enable a workaround for a known compatibility issue + * with 82541/82547 devices and some switches. See the "Known Limitations" section of + * the README file for a complete description and a list of affected switches. + * + * 0 = Hardware default + * 1 = Master mode + * 2 = Slave mode + * 3 = Auto master/slave + */ +/* #define EM_MASTER_SLAVE 2 */ + +/* Tunables -- End */ + +#define AUTONEG_ADV_DEFAULT (ADVERTISE_10_HALF | ADVERTISE_10_FULL | \ + ADVERTISE_100_HALF | ADVERTISE_100_FULL | \ + ADVERTISE_1000_FULL) + +#define EM_VENDOR_ID 0x8086 +#define EM_MMBA 0x0010 /* Mem base address */ +#define EM_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1)) + +#define EM_JUMBO_PBA 0x00000028 +#define EM_DEFAULT_PBA 0x00000030 +#define EM_SMARTSPEED_DOWNSHIFT 3 +#define EM_SMARTSPEED_MAX 15 + + +#define MAX_NUM_MULTICAST_ADDRESSES 128 +#define PCI_ANY_ID (~0U) +#define ETHER_ALIGN 2 + +/* 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) + + +/* Supported RX Buffer Sizes */ +#define EM_RXBUFFER_2048 2048 +#define EM_RXBUFFER_4096 4096 +#define EM_RXBUFFER_8192 8192 +#define EM_RXBUFFER_16384 16384 + +#define EM_MAX_SCATTER 64 + +/* ****************************************************************************** + * 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 { + struct mbuf *m_head; +#ifndef __rtems__ + bus_dmamap_t map; /* bus_dma map for packet */ +#endif +}; + +/* + * Bus dma allocation structure used by + * em_dma_malloc and em_dma_free. + */ +struct em_dma_alloc { + bus_addr_t dma_paddr; /* 64bit in descriptors */ +#ifndef __rtems__ + caddr_t dma_vaddr; + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_dma_segment_t dma_seg; + bus_size_t dma_size; + int dma_nseg; +#else + caddr_t dma_vaddr; + caddr_t malloc_base; +#endif +}; + +typedef enum _XSUM_CONTEXT_T { + OFFLOAD_NONE, + OFFLOAD_TCP_IP, + OFFLOAD_UDP_IP +} XSUM_CONTEXT_T; + +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 */ +}; + +/* For 82544 PCIX Workaround */ +typedef struct _ADDRESS_LENGTH_PAIR +{ + u_int64_t address; + u_int32_t length; +} ADDRESS_LENGTH_PAIR, *PADDRESS_LENGTH_PAIR; + +typedef struct _DESCRIPTOR_PAIR +{ + ADDRESS_LENGTH_PAIR descriptor[4]; + u_int32_t elements; +} DESC_ARRAY, *PDESC_ARRAY; + +/* Our adapter structure */ +struct adapter { + struct arpcom interface_data; + struct adapter *next; + struct adapter *prev; + struct em_hw hw; + + /* FreeBSD operating-system-specific structures */ + struct em_osdep osdep; +#ifndef __rtems__ + struct device *dev; + struct resource *res_memory; + struct resource *res_ioport; + struct resource *res_interrupt; + void *int_handler_tag; + struct ifmedia media; + struct callout timer; + struct callout tx_fifo_timer; + int io_rid; + struct ifmedia media; +#endif + u_int8_t unit; +#ifndef __rtems__ + struct mtx mtx; + int em_insert_vlan_header; +#else + device_t dev; + unsigned char irq_no; + unsigned char b,d,f; + rtems_id tid; +#endif + + /* Info about the board itself */ +#ifndef __rtems__ + u_int32_t part_num; +#else + uint32_t part_num; +#endif + u_int8_t link_active; + u_int16_t link_speed; + u_int16_t link_duplex; + u_int32_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; + + XSUM_CONTEXT_T active_checksum_context; + + /* + * 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 em_tx_desc *tx_desc_base; + u_int32_t next_avail_tx_desc; + u_int32_t oldest_used_tx_desc; + volatile u_int16_t num_tx_desc_avail; + u_int16_t num_tx_desc; + u_int32_t txd_cmd; + struct em_buffer *tx_buffer_area; +#ifndef __rtems__ + bus_dma_tag_t txtag; /* dma tag for tx */ +#endif +#ifdef __rtems__ + u_int16_t tx_cleanup_threshold; +#endif + + /* + * 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 em_rx_desc *rx_desc_base; + u_int32_t next_rx_desc_to_check; + u_int16_t num_rx_desc; + u_int32_t rx_buffer_len; + struct em_buffer *rx_buffer_area; +#ifndef __rtems__ + bus_dma_tag_t rxtag; +#endif + + /* Jumbo frame */ + 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; + + /* 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 + + u_int32_t tx_fifo_size; + u_int32_t tx_fifo_head; + u_int32_t tx_fifo_head_addr; + u_int64_t tx_fifo_reset_cnt; + u_int64_t tx_fifo_wrk_cnt; + u_int32_t tx_head_addr; + + /* For 82544 PCIX Workaround */ + boolean_t pcix_82544; + boolean_t in_detach; + + struct em_hw_stats stats; +}; + +#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) + +#ifdef __rtems__ +/* Initialize bare minimals so we can check the phy link status; + * 'rtems_em_pci_setup()' must have been run on the device already! + */ +int +em_hw_early_init(device_t dev); +#endif + + +#endif /* _EM_H_DEFINED_ */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.c new file mode 100644 index 0000000000..56f7224055 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.c @@ -0,0 +1,6635 @@ +/******************************************************************************* + + Copyright (c) 2001-2005, 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. + +*******************************************************************************/ + +/* if_em_hw.c + * Shared functions for accessing and configuring the MAC + */ + +#include +#ifdef __rtems__ +#include +#include +#else +__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/dev/em/if_em_hw.c,v 1.16 2005/05/26 23:32:02 tackerman Exp $"); + +#include +#endif + +static int32_t em_set_phy_type(struct em_hw *hw); +static void em_phy_init_script(struct em_hw *hw); +static int32_t em_setup_copper_link(struct em_hw *hw); +static int32_t em_setup_fiber_serdes_link(struct em_hw *hw); +static int32_t em_adjust_serdes_amplitude(struct em_hw *hw); +static int32_t em_phy_force_speed_duplex(struct em_hw *hw); +static int32_t em_config_mac_to_phy(struct em_hw *hw); +static void em_raise_mdi_clk(struct em_hw *hw, uint32_t *ctrl); +static void em_lower_mdi_clk(struct em_hw *hw, uint32_t *ctrl); +static void em_shift_out_mdi_bits(struct em_hw *hw, uint32_t data, + uint16_t count); +static uint16_t em_shift_in_mdi_bits(struct em_hw *hw); +static int32_t em_phy_reset_dsp(struct em_hw *hw); +static int32_t em_write_eeprom_spi(struct em_hw *hw, uint16_t offset, + uint16_t words, uint16_t *data); +static int32_t em_write_eeprom_microwire(struct em_hw *hw, + uint16_t offset, uint16_t words, + uint16_t *data); +static int32_t em_spi_eeprom_ready(struct em_hw *hw); +static void em_raise_ee_clk(struct em_hw *hw, uint32_t *eecd); +static void em_lower_ee_clk(struct em_hw *hw, uint32_t *eecd); +static void em_shift_out_ee_bits(struct em_hw *hw, uint16_t data, + uint16_t count); +static int32_t em_write_phy_reg_ex(struct em_hw *hw, uint32_t reg_addr, + uint16_t phy_data); +static int32_t em_read_phy_reg_ex(struct em_hw *hw,uint32_t reg_addr, + uint16_t *phy_data); +static uint16_t em_shift_in_ee_bits(struct em_hw *hw, uint16_t count); +static int32_t em_acquire_eeprom(struct em_hw *hw); +static void em_release_eeprom(struct em_hw *hw); +static void em_standby_eeprom(struct em_hw *hw); +static int32_t em_set_vco_speed(struct em_hw *hw); +static int32_t em_polarity_reversal_workaround(struct em_hw *hw); +static int32_t em_set_phy_mode(struct em_hw *hw); +static int32_t em_host_if_read_cookie(struct em_hw *hw, uint8_t *buffer); +static uint8_t em_calculate_mng_checksum(char *buffer, uint32_t length); + +/* IGP cable length table */ +static const +uint16_t em_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = + { 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}; + +static const +uint16_t em_igp_2_cable_length_table[IGP02E1000_AGC_LENGTH_TABLE_SIZE] = + { 8, 13, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, + 22, 24, 27, 30, 32, 35, 37, 40, 42, 44, 47, 49, 51, 54, 56, 58, + 32, 35, 38, 41, 44, 47, 50, 53, 55, 58, 61, 63, 66, 69, 71, 74, + 43, 47, 51, 54, 58, 61, 64, 67, 71, 74, 77, 80, 82, 85, 88, 90, + 57, 62, 66, 70, 74, 77, 81, 85, 88, 91, 94, 97, 100, 103, 106, 108, + 73, 78, 82, 87, 91, 95, 98, 102, 105, 109, 112, 114, 117, 119, 122, 124, + 91, 96, 101, 105, 109, 113, 116, 119, 122, 125, 127, 128, 128, 128, 128, 128, + 108, 113, 117, 121, 124, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}; + + +/****************************************************************************** + * Set the phy type member in the hw struct. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_set_phy_type(struct em_hw *hw) +{ + DEBUGFUNC("em_set_phy_type"); + + if(hw->mac_type == em_undefined) + return -E1000_ERR_PHY_TYPE; + + switch(hw->phy_id) { + case M88E1000_E_PHY_ID: + case M88E1000_I_PHY_ID: + case M88E1011_I_PHY_ID: + case M88E1111_I_PHY_ID: + hw->phy_type = em_phy_m88; + break; + case IGP01E1000_I_PHY_ID: + if(hw->mac_type == em_82541 || + hw->mac_type == em_82541_rev_2 || + hw->mac_type == em_82547 || + hw->mac_type == em_82547_rev_2) { + hw->phy_type = em_phy_igp; + break; + } + /* Fall Through */ + default: + /* Should never have loaded on this device */ + hw->phy_type = em_phy_undefined; + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * IGP phy init script - initializes the GbE PHY + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +em_phy_init_script(struct em_hw *hw) +{ + uint32_t ret_val; + uint16_t phy_saved_data; + + DEBUGFUNC("em_phy_init_script"); + + if(hw->phy_init_script) { + msec_delay(20); + + /* Save off the current value of register 0x2F5B to be restored at + * the end of this routine. */ + ret_val = em_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + /* Disabled the PHY transmitter */ + em_write_phy_reg(hw, 0x2F5B, 0x0003); + + msec_delay(20); + + em_write_phy_reg(hw,0x0000,0x0140); + + msec_delay(5); + + switch(hw->mac_type) { + case em_82541: + case em_82547: + em_write_phy_reg(hw, 0x1F95, 0x0001); + + em_write_phy_reg(hw, 0x1F71, 0xBD21); + + em_write_phy_reg(hw, 0x1F79, 0x0018); + + em_write_phy_reg(hw, 0x1F30, 0x1600); + + em_write_phy_reg(hw, 0x1F31, 0x0014); + + em_write_phy_reg(hw, 0x1F32, 0x161C); + + em_write_phy_reg(hw, 0x1F94, 0x0003); + + em_write_phy_reg(hw, 0x1F96, 0x003F); + + em_write_phy_reg(hw, 0x2010, 0x0008); + break; + + case em_82541_rev_2: + case em_82547_rev_2: + em_write_phy_reg(hw, 0x1F73, 0x0099); + break; + default: + break; + } + + em_write_phy_reg(hw, 0x0000, 0x3300); + + msec_delay(20); + + /* Now enable the transmitter */ + em_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if(hw->mac_type == em_82547) { + uint16_t fused, fine, coarse; + + /* Move to analog registers page */ + em_read_phy_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused); + + if(!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) { + em_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); + + em_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_CONTROL, fused); + em_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS, + IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL); + } + } + } +} + +/****************************************************************************** + * Set the mac type member in the hw struct. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_set_mac_type(struct em_hw *hw) +{ + DEBUGFUNC("em_set_mac_type"); + + switch (hw->device_id) { + case E1000_DEV_ID_82542: + switch (hw->revision_id) { + case E1000_82542_2_0_REV_ID: + hw->mac_type = em_82542_rev2_0; + break; + case E1000_82542_2_1_REV_ID: + hw->mac_type = em_82542_rev2_1; + break; + default: + /* Invalid 82542 revision ID */ + return -E1000_ERR_MAC_TYPE; + } + break; + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + hw->mac_type = em_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: + hw->mac_type = em_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: + hw->mac_type = em_82540; + break; + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + hw->mac_type = em_82545; + break; + case E1000_DEV_ID_82545GM_COPPER: + case E1000_DEV_ID_82545GM_FIBER: + case E1000_DEV_ID_82545GM_SERDES: + hw->mac_type = em_82545_rev_3; + break; + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546EB_QUAD_COPPER: + hw->mac_type = em_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: + hw->mac_type = em_82546_rev_3; + break; + case E1000_DEV_ID_82541EI: + case E1000_DEV_ID_82541ER_LOM: + case E1000_DEV_ID_82541EI_MOBILE: + hw->mac_type = em_82541; + break; + case E1000_DEV_ID_82541ER: + case E1000_DEV_ID_82541GI: + case E1000_DEV_ID_82541GI_LF: + case E1000_DEV_ID_82541GI_MOBILE: + hw->mac_type = em_82541_rev_2; + break; + case E1000_DEV_ID_82547EI: + case E1000_DEV_ID_82547EI_MOBILE: + hw->mac_type = em_82547; + break; + case E1000_DEV_ID_82547GI: + hw->mac_type = em_82547_rev_2; + break; + case E1000_DEV_ID_82573E: + case E1000_DEV_ID_82573E_IAMT: + hw->mac_type = em_82573; + break; + default: + /* Should never have loaded on this device */ + return -E1000_ERR_MAC_TYPE; + } + + switch(hw->mac_type) { + case em_82573: + hw->eeprom_semaphore_present = TRUE; + /* fall through */ + case em_82541: + case em_82547: + case em_82541_rev_2: + case em_82547_rev_2: + hw->asf_firmware_present = TRUE; + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * Set media type and TBI compatibility. + * + * hw - Struct containing variables accessed by shared code + * **************************************************************************/ +void +em_set_media_type(struct em_hw *hw) +{ + uint32_t status; + + DEBUGFUNC("em_set_media_type"); + + if(hw->mac_type != em_82543) { + /* tbi_compatibility is only valid on 82543 */ + hw->tbi_compatibility_en = FALSE; + } + + switch (hw->device_id) { + case E1000_DEV_ID_82545GM_SERDES: + case E1000_DEV_ID_82546GB_SERDES: + hw->media_type = em_media_type_internal_serdes; + break; + default: + if(hw->mac_type >= em_82543) { + status = E1000_READ_REG(hw, STATUS); + if(status & E1000_STATUS_TBIMODE) { + hw->media_type = em_media_type_fiber; + /* tbi_compatibility not valid on fiber */ + hw->tbi_compatibility_en = FALSE; + } else { + hw->media_type = em_media_type_copper; + } + } else { + /* This is an 82542 (fiber only) */ + hw->media_type = em_media_type_fiber; + } + } +} + +/****************************************************************************** + * Reset the transmit and receive units; mask and clear all interrupts. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_reset_hw(struct em_hw *hw) +{ + uint32_t ctrl; + uint32_t ctrl_ext; + uint32_t icr; + uint32_t manc; + uint32_t led_ctrl; + uint32_t timeout; + uint32_t extcnf_ctrl; + int32_t ret_val; + + DEBUGFUNC("em_reset_hw"); + + /* For 82542 (rev 2.0), disable MWI before issuing a device reset */ + if(hw->mac_type == em_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + em_pci_clear_mwi(hw); + } + + if(hw->bus_type == em_bus_type_pci_express) { + /* Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + if(em_disable_pciex_master(hw) != E1000_SUCCESS) { + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + } + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, 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, RCTL, 0); + E1000_WRITE_REG(hw, TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ + hw->tbi_compatibility_on = FALSE; + + /* Delay to allow any outstanding PCI transactions to complete before + * resetting the device + */ + msec_delay(10); + + ctrl = E1000_READ_REG(hw, CTRL); + + /* Must reset the PHY before resetting the MAC */ + if((hw->mac_type == em_82541) || (hw->mac_type == em_82547)) { + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST)); + msec_delay(5); + } + + /* Must acquire the MDIO ownership before MAC reset. + * Ownership defaults to firmware after a reset. */ + if(hw->mac_type == em_82573) { + timeout = 10; + + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; + + do { + E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + + if(extcnf_ctrl & E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP) + break; + else + extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; + + msec_delay(2); + timeout--; + } while(timeout); + } + + /* Issue a global reset to the MAC. This will reset the chip's + * transmit, receive, DMA, and link units. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + DEBUGOUT("Issuing a global reset to MAC\n"); + + switch(hw->mac_type) { + case em_82544: + case em_82540: + case em_82545: +#ifndef __arm__ + case em_82546: +#endif + case em_82541: + case em_82541_rev_2: + /* These controllers can't ack the 64-bit write when issuing the + * reset, so use IO-mapping as a workaround to issue the reset */ + E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST)); + break; + case em_82545_rev_3: + case em_82546_rev_3: + /* Reset is performed on a shadow of the control register */ + E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST)); + break; + default: + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + break; + } + + /* After MAC reset, force reload of EEPROM to restore power-on settings to + * device. Later controllers reload the EEPROM automatically, so just wait + * for reload to complete. + */ + switch(hw->mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + case em_82543: + case em_82544: + /* Wait for reset to complete */ + usec_delay(10); + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + /* Wait for EEPROM reload */ + msec_delay(2); + break; + case em_82541: + case em_82541_rev_2: + case em_82547: + case em_82547_rev_2: + /* Wait for EEPROM reload */ + msec_delay(20); + break; + case em_82573: + usec_delay(10); + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + /* fall through */ + ret_val = em_get_auto_rd_done(hw); + if(ret_val) + /* We don't want to continue accessing MAC registers. */ + return ret_val; + break; + default: + /* Wait for EEPROM reload (it happens automatically) */ + msec_delay(5); + break; + } + + /* Disable HW ARPs on ASF enabled adapters */ + if(hw->mac_type >= em_82540 && hw->mac_type <= em_82547_rev_2) { + manc = E1000_READ_REG(hw, MANC); + manc &= ~(E1000_MANC_ARP_EN); + E1000_WRITE_REG(hw, MANC, manc); + } + + if((hw->mac_type == em_82541) || (hw->mac_type == em_82547)) { + em_phy_init_script(hw); + + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, IMC, 0xffffffff); + + /* Clear any pending interrupt events. */ + icr = E1000_READ_REG(hw, ICR); + + /* If MWI was previously enabled, reenable it. */ + if(hw->mac_type == em_82542_rev2_0) { + if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) + em_pci_set_mwi(hw); + } +#ifdef __rtems__ + msec_delay(100); +#endif + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Performs basic configuration of the adapter. + * + * hw - Struct containing variables accessed by shared code + * + * Assumes that the controller has previously been reset and is in a + * post-reset uninitialized state. Initializes the receive address registers, + * multicast table, and VLAN filter table. Calls routines to setup link + * configuration and flow control settings. Clears all on-chip counters. Leaves + * the transmit and receive units disabled and uninitialized. + *****************************************************************************/ +int32_t +em_init_hw(struct em_hw *hw) +{ + uint32_t ctrl; + uint32_t i; + int32_t ret_val; + uint16_t pcix_cmd_word; + uint16_t pcix_stat_hi_word; + uint16_t cmd_mmrbc; + uint16_t stat_mmrbc; + uint32_t mta_size; + + DEBUGFUNC("em_init_hw"); + + /* Initialize Identification LED */ + ret_val = em_id_led_init(hw); + if(ret_val) { + DEBUGOUT("Error Initializing Identification LED\n"); + return ret_val; + } + + /* Set the media type and TBI compatibility */ + em_set_media_type(hw); + + /* Disabling VLAN filtering. */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + if (hw->mac_type < em_82545_rev_3) + E1000_WRITE_REG(hw, VET, 0); + em_clear_vfta(hw); + + /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ + if(hw->mac_type == em_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + em_pci_clear_mwi(hw); + E1000_WRITE_REG(hw, RCTL, E1000_RCTL_RST); + E1000_WRITE_FLUSH(hw); + msec_delay(5); + } + + /* Setup the receive address. This involves initializing all of the Receive + * Address Registers (RARs 0 - 15). + */ + em_init_rx_addrs(hw); + + /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ + if(hw->mac_type == em_82542_rev2_0) { + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_FLUSH(hw); + msec_delay(1); + if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE) + em_pci_set_mwi(hw); + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + mta_size = E1000_MC_TBL_SIZE; + for(i = 0; i < mta_size; i++) + E1000_WRITE_REG_ARRAY(hw, 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. Valid only on + * 82542 and 82543 silicon. + */ + if(hw->dma_fairness && hw->mac_type <= em_82543) { + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR); + } + + switch(hw->mac_type) { + case em_82545_rev_3: + case em_82546_rev_3: + break; + default: + /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ + if(hw->bus_type == em_bus_type_pcix) { + em_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word); + em_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, + &pcix_stat_hi_word); + cmd_mmrbc = (pcix_cmd_word & 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_word &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + em_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, + &pcix_cmd_word); + } + } + break; + } + + /* Call a subroutine to configure the link and setup flow control. */ + ret_val = em_setup_link(hw); + + /* Set the transmit descriptor write-back policy */ + if(hw->mac_type > em_82544) { + ctrl = E1000_READ_REG(hw, TXDCTL); + ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; + switch (hw->mac_type) { + default: + break; + case em_82573: + ctrl |= E1000_TXDCTL_COUNT_DESC; + break; + } + E1000_WRITE_REG(hw, TXDCTL, ctrl); + } + + if (hw->mac_type == em_82573) { + em_enable_tx_pkt_filtering(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. + */ + em_clear_hw_cntrs(hw); + + return ret_val; +} + +/****************************************************************************** + * Adjust SERDES output amplitude based on EEPROM setting. + * + * hw - Struct containing variables accessed by shared code. + *****************************************************************************/ +static int32_t +em_adjust_serdes_amplitude(struct em_hw *hw) +{ + uint16_t eeprom_data; + int32_t ret_val; + + DEBUGFUNC("em_adjust_serdes_amplitude"); + + if(hw->media_type != em_media_type_internal_serdes) + return E1000_SUCCESS; + + switch(hw->mac_type) { + case em_82545_rev_3: + case em_82546_rev_3: + break; + default: + return E1000_SUCCESS; + } + + ret_val = em_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, &eeprom_data); + if (ret_val) { + return ret_val; + } + + if(eeprom_data != EEPROM_RESERVED_WORD) { + /* Adjust SERDES output amplitude only. */ + eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, eeprom_data); + if(ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Configures flow control and link settings. + * + * hw - Struct containing variables accessed by shared code + * + * Determines which flow control settings to use. Calls the apropriate media- + * specific link configuration function. Configures the flow control settings. + * 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. + *****************************************************************************/ +int32_t +em_setup_link(struct em_hw *hw) +{ + uint32_t ctrl_ext; + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC("em_setup_link"); + + /* 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. + */ + if(em_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data)) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + + if(hw->fc == em_fc_default) { + if((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0) + hw->fc = em_fc_none; + else if((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == + EEPROM_WORD0F_ASM_DIR) + hw->fc = em_fc_tx_pause; + else + hw->fc = em_fc_full; + } + + /* 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. + */ + if(hw->mac_type == em_82542_rev2_0) + hw->fc &= (~em_fc_tx_pause); + + if((hw->mac_type < em_82543) && (hw->report_tx_early == 1)) + hw->fc &= (~em_fc_rx_pause); + + hw->original_fc = hw->fc; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", hw->fc); + + /* Take the 4 bits from EEPROM word 0x0F 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 em_setup_pcs_link() + * or em_phy_setup() is called. + */ + if(hw->mac_type == em_82543) { + ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << + SWDPIO__EXT_SHIFT); + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + /* Call the necessary subroutine to configure the link. */ + ret_val = (hw->media_type == em_media_type_copper) ? + em_setup_copper_link(hw) : + em_setup_fiber_serdes_link(hw); + + /* 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, FCAL, FLOW_CONTROL_ADDRESS_LOW); + E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); + + E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time); + + /* 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 in not enabled, then these + * registers will be set to 0. + */ + if(!(hw->fc & em_fc_tx_pause)) { + E1000_WRITE_REG(hw, FCRTL, 0); + E1000_WRITE_REG(hw, FCRTH, 0); + } else { + /* We need to set up the Receive Threshold high and low water marks + * as well as (optionally) enabling the transmission of XON frames. + */ + if(hw->fc_send_xon) { + E1000_WRITE_REG(hw, FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE)); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } else { + E1000_WRITE_REG(hw, FCRTL, hw->fc_low_water); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } + } + return ret_val; +} + +/****************************************************************************** + * Sets up link for a fiber based or serdes based adapter + * + * hw - Struct containing variables accessed by shared code + * + * Manipulates Physical Coding Sublayer functions in order to configure + * link. Assumes the hardware has been previously reset and the transmitter + * and receiver are not enabled. + *****************************************************************************/ +static int32_t +em_setup_fiber_serdes_link(struct em_hw *hw) +{ + uint32_t ctrl; + uint32_t status; + uint32_t txcw = 0; + uint32_t i; + uint32_t signal = 0; + int32_t ret_val; + + DEBUGFUNC("em_setup_fiber_serdes_link"); + + /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal. This applies to fiber media only. + * If we're on serdes media, adjust the output amplitude to value set in + * the EEPROM. + */ + ctrl = E1000_READ_REG(hw, CTRL); + if(hw->media_type == em_media_type_fiber) + signal = (hw->mac_type > em_82544) ? E1000_CTRL_SWDPIN1 : 0; + + ret_val = em_adjust_serdes_amplitude(hw); + if(ret_val) + return ret_val; + + /* Take the link out of reset */ + ctrl &= ~(E1000_CTRL_LRST); + + /* Adjust VCO speed to improve BER performance */ + ret_val = em_set_vco_speed(hw); + if(ret_val) + return ret_val; + + em_config_collision_dist(hw); + + /* 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 Tranmsit + * 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 (hw->fc) { + case em_fc_none: + /* Flow control is completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case em_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 em_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 em_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"); + return -E1000_ERR_CONFIG; + break; + } + + /* 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-neogtiation 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, TXCW, txcw); + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + hw->txcw = txcw; + msec_delay(1); + + /* If we have a signal (the cable is plugged in) 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 internal serdes, we just assume a signal is present, then poll. + */ + if(hw->media_type == em_media_type_internal_serdes || + (E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) { + DEBUGOUT("Looking for Link\n"); + for(i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { + msec_delay(10); + status = E1000_READ_REG(hw, STATUS); + if(status & E1000_STATUS_LU) break; + } + if(i == (LINK_UP_TIMEOUT / 10)) { + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + hw->autoneg_failed = 1; + /* AutoNeg failed to achieve a link, so we'll call + * em_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 = em_check_for_link(hw); + if(ret_val) { + DEBUGOUT("Error while checking for link\n"); + return ret_val; + } + hw->autoneg_failed = 0; + } else { + hw->autoneg_failed = 0; + DEBUGOUT("Valid Link Found\n"); + } + } else { + DEBUGOUT("No Signal Detected\n"); + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Make sure we have a valid PHY and change PHY mode before link setup. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +em_copper_link_preconfig(struct em_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_copper_link_preconfig"); + + ctrl = E1000_READ_REG(hw, CTRL); + /* 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 > em_82543) { + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, CTRL, ctrl); + } else { + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU); + E1000_WRITE_REG(hw, CTRL, ctrl); + ret_val = em_phy_hw_reset(hw); + if(ret_val) + return ret_val; + } + + /* Make sure we have a valid PHY */ + ret_val = em_detect_gig_phy(hw); + if(ret_val) { + DEBUGOUT("Error, did not detect valid phy.\n"); + return ret_val; + } + DEBUGOUT1("Phy ID = %x \n", hw->phy_id); + + /* Set PHY to class A mode (if necessary) */ + ret_val = em_set_phy_mode(hw); + if(ret_val) + return ret_val; + + if((hw->mac_type == em_82545_rev_3) || + (hw->mac_type == em_82546_rev_3)) { + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + phy_data |= 0x00000008; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + } + + if(hw->mac_type <= em_82543 || + hw->mac_type == em_82541 || hw->mac_type == em_82547 || + hw->mac_type == em_82541_rev_2 || hw->mac_type == em_82547_rev_2) + hw->phy_reset_disable = FALSE; + + return E1000_SUCCESS; +} + + +/******************************************************************** +* Copper link setup for em_phy_igp series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +em_copper_link_igp_setup(struct em_hw *hw) +{ + uint32_t led_ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_copper_link_igp_setup"); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + ret_val = em_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + /* Wait 10ms for MAC to configure PHY from eeprom settings */ + msec_delay(15); + + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + + /* disable lplu d3 during driver init */ + ret_val = em_set_d3_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D3\n"); + return ret_val; + } + + /* disable lplu d0 during driver init */ + ret_val = em_set_d0_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D0\n"); + return ret_val; + } + /* Configure mdi-mdix settings */ + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + if ((hw->mac_type == em_82541) || (hw->mac_type == em_82547)) { + hw->dsp_config_state = em_dsp_config_disabled; + /* Force MDI for earlier revs of the IGP PHY */ + phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX | IGP01E1000_PSCR_FORCE_MDI_MDIX); + hw->mdix = 1; + + } else { + hw->dsp_config_state = em_dsp_config_enabled; + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (hw->mdix) { + case 1: + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + phy_data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + } + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if(ret_val) + return ret_val; + + /* set auto-master slave resolution settings */ + if(hw->autoneg) { + em_ms_type phy_ms_setting = hw->master_slave; + + if(hw->ffe_config_state == em_ffe_config_active) + hw->ffe_config_state = em_ffe_config_enabled; + + if(hw->dsp_config_state == em_dsp_config_activated) + hw->dsp_config_state = em_dsp_config_enabled; + + /* when autonegotiation advertisment is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. */ + if(hw->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if(ret_val) + return ret_val; + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = em_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if(ret_val) + return ret_val; + /* Set auto Master/Slave resolution process */ + ret_val = em_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if(ret_val) + return ret_val; + phy_data &= ~CR_1000T_MS_ENABLE; + ret_val = em_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if(ret_val) + return ret_val; + } + + ret_val = em_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if(ret_val) + return ret_val; + + /* load defaults for future use */ + hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + em_ms_force_master : + em_ms_force_slave) : + em_ms_auto; + + switch (phy_ms_setting) { + case em_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case em_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case em_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + default: + break; + } + ret_val = em_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if(ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + + +/******************************************************************** +* Copper link setup for em_phy_m88 series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +em_copper_link_mgp_setup(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_copper_link_mgp_setup"); + + if(hw->phy_reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) + return ret_val; + + 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 (hw->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(hw->disable_polarity_correction == 1) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if(ret_val) + return ret_val; + + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = em_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if(ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if (hw->phy_revision < M88E1011_I_REV_4) { + /* 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 = em_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if(ret_val) + return ret_val; + } + + /* SW Reset the PHY so all changes take effect */ + ret_val = em_phy_reset(hw); + if(ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/******************************************************************** +* Setup auto-negotiation and flow control advertisements, +* and then perform auto-negotiation. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +em_copper_link_autoneg(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_copper_link_autoneg"); + + /* Perform some bounds checking on the hw->autoneg_advertised + * parameter. If this variable is zero, then set it to the default. + */ + hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if(hw->autoneg_advertised == 0) + hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = em_phy_setup_autoneg(hw); + if(ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; + } + 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 = em_read_phy_reg(hw, PHY_CTRL, &phy_data); + if(ret_val) + return ret_val; + + phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = em_write_phy_reg(hw, PHY_CTRL, phy_data); + if(ret_val) + return ret_val; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if(hw->wait_autoneg_complete) { + ret_val = em_wait_autoneg(hw); + if(ret_val) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + return ret_val; + } + } + + hw->get_link_status = TRUE; + + return E1000_SUCCESS; +} + + +/****************************************************************************** +* Config the MAC and the PHY after link is up. +* 1) Set up the MAC to the current PHY speed/duplex +* if we are on 82543. If we +* are on newer silicon, we only need to configure +* collision distance in the Transmit Control Register. +* 2) Set up flow control on the MAC to that established with +* the link partner. +* 3) Config DSP to improve Gigabit link quality for some PHY revisions. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +em_copper_link_postconfig(struct em_hw *hw) +{ + int32_t ret_val; + DEBUGFUNC("em_copper_link_postconfig"); + + if(hw->mac_type >= em_82544) { + em_config_collision_dist(hw); + } else { + ret_val = em_config_mac_to_phy(hw); + if(ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + ret_val = em_config_fc_after_link_up(hw); + if(ret_val) { + DEBUGOUT("Error Configuring Flow Control\n"); + return ret_val; + } + + /* Config DSP to improve Giga link quality */ + if(hw->phy_type == em_phy_igp) { + ret_val = em_config_dsp_after_link_change(hw, TRUE); + if(ret_val) { + DEBUGOUT("Error Configuring DSP after link up\n"); + return ret_val; + } + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Detects which PHY is present and setup the speed and duplex +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +em_setup_copper_link(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t i; + uint16_t phy_data; + + DEBUGFUNC("em_setup_copper_link"); + + /* Check if it is a valid PHY and set PHY mode if necessary. */ + ret_val = em_copper_link_preconfig(hw); + if(ret_val) + return ret_val; + + if (hw->phy_type == em_phy_igp || + hw->phy_type == em_phy_igp_2) { + ret_val = em_copper_link_igp_setup(hw); + if(ret_val) + return ret_val; + } else if (hw->phy_type == em_phy_m88) { + ret_val = em_copper_link_mgp_setup(hw); + if(ret_val) + return ret_val; + } + + if(hw->autoneg) { + /* Setup autoneg and flow control advertisement + * and perform autonegotiation */ + ret_val = em_copper_link_autoneg(hw); + if(ret_val) + return ret_val; + } else { + /* PHY will be set to 10H, 10F, 100H,or 100F + * depending on value from forced_speed_duplex. */ + DEBUGOUT("Forcing speed and duplex\n"); + ret_val = em_phy_force_speed_duplex(hw); + if(ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + return ret_val; + } + } + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + for(i = 0; i < 10; i++) { + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + + if(phy_data & MII_SR_LINK_STATUS) { + /* Config the MAC and PHY after link is up */ + ret_val = em_copper_link_postconfig(hw); + if(ret_val) + return ret_val; + + DEBUGOUT("Valid link established!!!\n"); + return E1000_SUCCESS; + } + usec_delay(10); + } + + DEBUGOUT("Unable to establish link!!!\n"); + return E1000_SUCCESS; +} + +/****************************************************************************** +* Configures PHY autoneg and flow control advertisement settings +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +em_phy_setup_autoneg(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t mii_autoneg_adv_reg; + uint16_t mii_1000t_ctrl_reg; + + DEBUGFUNC("em_phy_setup_autoneg"); + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = em_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if(ret_val) + return ret_val; + + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = em_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); + if(ret_val) + return ret_val; + + /* 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 &= ~REG4_SPEED_MASK; + mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK; + + DEBUGOUT1("autoneg_advertised %x\n", hw->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if(hw->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(hw->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(hw->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(hw->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(hw->autoneg_advertised & ADVERTISE_1000_HALF) { + DEBUGOUT("Advertise 1000mb Half duplex requested, request denied!\n"); + } + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if(hw->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->fc) { + case em_fc_none: /* 0 */ + /* 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 em_fc_rx_pause: /* 1 */ + /* 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 em_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 em_fc_tx_pause: /* 2 */ + /* 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 em_fc_full: /* 3 */ + /* 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"); + return -E1000_ERR_CONFIG; + } + + ret_val = em_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if(ret_val) + return ret_val; + + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + ret_val = em_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); + if(ret_val) + return ret_val; + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Force PHY speed and duplex settings to hw->forced_speed_duplex +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +em_phy_force_speed_duplex(struct em_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t mii_ctrl_reg; + uint16_t mii_status_reg; + uint16_t phy_data; + uint16_t i; + + DEBUGFUNC("em_phy_force_speed_duplex"); + + /* Turn off Flow control if we are forcing speed and duplex. */ + hw->fc = em_fc_none; + + DEBUGOUT1("hw->fc = %d\n", hw->fc); + + /* Read the Device Control Register. */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Set the bits to Force Speed and Duplex in the Device Ctrl Reg. */ + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(DEVICE_SPEED_MASK); + + /* Clear the Auto Speed Detect Enable bit. */ + ctrl &= ~E1000_CTRL_ASDE; + + /* Read the MII Control Register. */ + ret_val = em_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg); + if(ret_val) + return ret_val; + + /* We need to disable autoneg in order to force link and duplex. */ + + mii_ctrl_reg &= ~MII_CR_AUTO_NEG_EN; + + /* Are we forcing Full or Half Duplex? */ + if(hw->forced_speed_duplex == em_100_full || + hw->forced_speed_duplex == em_10_full) { + /* We want to force full duplex so we SET the full duplex bits in the + * Device and MII Control Registers. + */ + ctrl |= E1000_CTRL_FD; + mii_ctrl_reg |= MII_CR_FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + /* We want to force half duplex so we CLEAR the full duplex bits in + * the Device and MII Control Registers. + */ + ctrl &= ~E1000_CTRL_FD; + mii_ctrl_reg &= ~MII_CR_FULL_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } + + /* Are we forcing 100Mbps??? */ + if(hw->forced_speed_duplex == em_100_full || + hw->forced_speed_duplex == em_100_half) { + /* Set the 100Mb bit and turn off the 1000Mb and 10Mb bits. */ + ctrl |= E1000_CTRL_SPD_100; + mii_ctrl_reg |= MII_CR_SPEED_100; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10); + DEBUGOUT("Forcing 100mb "); + } else { + /* Set the 10Mb bit and turn off the 1000Mb and 100Mb bits. */ + ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + mii_ctrl_reg |= MII_CR_SPEED_10; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + DEBUGOUT("Forcing 10mb "); + } + + em_config_collision_dist(hw); + + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(hw, CTRL, ctrl); + + if (hw->phy_type == em_phy_m88) { + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) + return ret_val; + + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + * forced whenever speed are duplex are forced. + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if(ret_val) + return ret_val; + + DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data); + + /* Need to reset the PHY or these changes will be ignored */ + mii_ctrl_reg |= MII_CR_RESET; + } else { + /* Clear Auto-Crossover to force MDI manually. IGP requires MDI + * forced whenever speed or duplex are forced. + */ + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if(ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if(ret_val) + return ret_val; + } + + /* Write back the modified PHY MII control register. */ + ret_val = em_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg); + if(ret_val) + return ret_val; + + usec_delay(1); + + /* The wait_autoneg_complete flag may be a little misleading here. + * Since we are forcing speed and duplex, Auto-Neg is not enabled. + * But we do want to delay for a period while forcing only so we + * don't generate false No Link messages. So we will wait here + * only if the user has set wait_autoneg_complete to 1, which is + * the default. + */ + if(hw->wait_autoneg_complete) { + /* We will wait for autoneg to complete. */ + DEBUGOUT("Waiting for forced speed/duplex link.\n"); + mii_status_reg = 0; + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for(i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg Complete bit + * to be set. + */ + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + if(mii_status_reg & MII_SR_LINK_STATUS) break; + msec_delay(100); + } + if((i == 0) && + (hw->phy_type == em_phy_m88)) { + /* We didn't get link. Reset the DSP and wait again for link. */ + ret_val = em_phy_reset_dsp(hw); + if(ret_val) { + DEBUGOUT("Error Resetting PHY DSP\n"); + return ret_val; + } + } + /* This loop will early-out if the link condition has been met. */ + for(i = PHY_FORCE_TIME; i > 0; i--) { + if(mii_status_reg & MII_SR_LINK_STATUS) break; + msec_delay(100); + /* Read the MII Status Register and wait for Auto-Neg Complete bit + * to be set. + */ + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + } + } + + if (hw->phy_type == em_phy_m88) { + /* Because we reset the PHY above, we need to re-force TX_CLK in the + * Extended PHY Specific Control Register to 25MHz clock. This value + * defaults back to a 2.5MHz clock when the PHY is reset. + */ + ret_val = em_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if(ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + ret_val = em_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if(ret_val) + return ret_val; + + /* In addition, because of the s/w reset above, we need to enable CRS on + * TX. This must be set for both full and half duplex operation. + */ + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if(ret_val) + return ret_val; + + if((hw->mac_type == em_82544 || hw->mac_type == em_82543) && + (!hw->autoneg) && + (hw->forced_speed_duplex == em_10_full || + hw->forced_speed_duplex == em_10_half)) { + ret_val = em_polarity_reversal_workaround(hw); + if(ret_val) + return ret_val; + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Sets the collision distance in the Transmit Control register +* +* hw - Struct containing variables accessed by shared code +* +* Link should have been established previously. Reads the speed and duplex +* information from the Device Status register. +******************************************************************************/ +void +em_config_collision_dist(struct em_hw *hw) +{ + uint32_t tctl; + + DEBUGFUNC("em_config_collision_dist"); + + tctl = E1000_READ_REG(hw, TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/****************************************************************************** +* Sets MAC speed and duplex settings to reflect the those in the PHY +* +* hw - Struct containing variables accessed by shared code +* mii_reg - data to write to the MII control register +* +* The contents of the PHY register containing the needed information need to +* be passed in. +******************************************************************************/ +static int32_t +em_config_mac_to_phy(struct em_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_config_mac_to_phy"); + + /* 82544 or newer MAC, Auto Speed Detection takes care of + * MAC speed/duplex configuration.*/ + if (hw->mac_type >= em_82544) + return E1000_SUCCESS; + + /* Read the Device Control Register and set the bits to Force Speed + * and Duplex. + */ + ctrl = E1000_READ_REG(hw, 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 = em_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if(ret_val) + return ret_val; + + if(phy_data & M88E1000_PSSR_DPLX) + ctrl |= E1000_CTRL_FD; + else + ctrl &= ~E1000_CTRL_FD; + + em_config_collision_dist(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; + + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(hw, CTRL, ctrl); + return E1000_SUCCESS; +} + +/****************************************************************************** + * Forces the MAC's flow control settings. + * + * hw - Struct containing variables accessed by shared code + * + * 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. + *****************************************************************************/ +int32_t +em_force_mac_fc(struct em_hw *hw) +{ + uint32_t ctrl; + + DEBUGFUNC("em_force_mac_fc"); + + /* Get the current configuration of the Device Control Register */ + ctrl = E1000_READ_REG(hw, 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 "hw->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. + */ + + switch (hw->fc) { + case em_fc_none: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case em_fc_rx_pause: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case em_fc_tx_pause: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case em_fc_full: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + /* Disable TX Flow Control for 82542 (rev 2.0) */ + if(hw->mac_type == em_82542_rev2_0) + ctrl &= (~E1000_CTRL_TFCE); + + E1000_WRITE_REG(hw, CTRL, ctrl); + return E1000_SUCCESS; +} + +/****************************************************************************** + * Configures flow control settings after link is established + * + * hw - Struct containing variables accessed by shared code + * + * Should be called immediately after a valid link has been established. + * Forces MAC flow control settings if link was forced. When in MII/GMII mode + * and autonegotiation is enabled, the MAC flow control settings will be set + * based on the flow control negotiated by the PHY. In TBI mode, the TFCE + * and RFCE bits will be automaticaly set to the negotiated flow control mode. + *****************************************************************************/ +int32_t +em_config_fc_after_link_up(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t mii_status_reg; + uint16_t mii_nway_adv_reg; + uint16_t mii_nway_lp_ability_reg; + uint16_t speed; + uint16_t duplex; + + DEBUGFUNC("em_config_fc_after_link_up"); + + /* 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(((hw->media_type == em_media_type_fiber) && (hw->autoneg_failed)) || + ((hw->media_type == em_media_type_internal_serdes) && (hw->autoneg_failed)) || + ((hw->media_type == em_media_type_copper) && (!hw->autoneg))) { + ret_val = em_force_mac_fc(hw); + if(ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + /* 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 == em_media_type_copper) && hw->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 = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + if(mii_status_reg & MII_SR_AUTONEG_COMPLETE) { + /* 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 = em_read_phy_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg); + if(ret_val) + return ret_val; + ret_val = em_read_phy_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg); + if(ret_val) + return ret_val; + + /* 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 | em_fc_none + * 0 | 1 | 0 | DC | em_fc_none + * 0 | 1 | 1 | 0 | em_fc_none + * 0 | 1 | 1 | 1 | em_fc_tx_pause + * 1 | 0 | 0 | DC | em_fc_none + * 1 | DC | 1 | DC | em_fc_full + * 1 | 1 | 0 | 0 | em_fc_none + * 1 | 1 | 0 | 1 | em_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 | em_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(hw->original_fc == em_fc_full) { + hw->fc = em_fc_full; + DEBUGOUT("Flow Control = FULL.\r\n"); + } else { + hw->fc = em_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 | em_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)) { + hw->fc = em_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 | em_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)) { + hw->fc = em_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((hw->original_fc == em_fc_none || + hw->original_fc == em_fc_tx_pause) || + hw->fc_strict_ieee) { + hw->fc = em_fc_none; + DEBUGOUT("Flow Control = NONE.\r\n"); + } else { + hw->fc = em_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 = em_get_speed_and_duplex(hw, &speed, &duplex); + if(ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + + if(duplex == HALF_DUPLEX) + hw->fc = em_fc_none; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = em_force_mac_fc(hw); + if(ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } else { + DEBUGOUT("Copper PHY and Auto Neg has not completed.\r\n"); + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Checks to see if the link status of the hardware has changed. + * + * hw - Struct containing variables accessed by shared code + * + * Called by any function that needs to check the link status of the adapter. + *****************************************************************************/ +int32_t +em_check_for_link(struct em_hw *hw) +{ + uint32_t rxcw = 0; + uint32_t ctrl; + uint32_t status; + uint32_t rctl; + uint32_t icr; + uint32_t signal = 0; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_check_for_link"); + + ctrl = E1000_READ_REG(hw, CTRL); + status = E1000_READ_REG(hw, STATUS); + + /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal. This applies to fiber media only. + */ + if((hw->media_type == em_media_type_fiber) || + (hw->media_type == em_media_type_internal_serdes)) { + rxcw = E1000_READ_REG(hw, RXCW); + + if(hw->media_type == em_media_type_fiber) { + signal = (hw->mac_type > em_82544) ? E1000_CTRL_SWDPIN1 : 0; + if(status & E1000_STATUS_LU) + hw->get_link_status = FALSE; + } + } + + /* If we have a copper PHY then 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 will be set if we + * receive a Link Status Change interrupt or we have Rx Sequence + * Errors. + */ + if((hw->media_type == em_media_type_copper) && hw->get_link_status) { + /* 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. + * Read the register twice since the link bit is sticky. + */ + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + + if(phy_data & MII_SR_LINK_STATUS) { + hw->get_link_status = FALSE; + /* Check if there was DownShift, must be checked immediately after + * link-up */ + em_check_downshift(hw); + + /* If we are on 82544 or 82543 silicon and speed/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 + * happen due to the execution of this workaround. + */ + + if((hw->mac_type == em_82544 || hw->mac_type == em_82543) && + (!hw->autoneg) && + (hw->forced_speed_duplex == em_10_full || + hw->forced_speed_duplex == em_10_half)) { + E1000_WRITE_REG(hw, IMC, 0xffffffff); + ret_val = em_polarity_reversal_workaround(hw); + icr = E1000_READ_REG(hw, ICR); + E1000_WRITE_REG(hw, ICS, (icr & ~E1000_ICS_LSC)); + E1000_WRITE_REG(hw, IMS, IMS_ENABLE_MASK); + } + + } else { + /* No link detected */ + em_config_dsp_after_link_change(hw, FALSE); + return 0; + } + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if(!hw->autoneg) return -E1000_ERR_CONFIG; + + /* optimize the dsp settings for the igp phy */ + em_config_dsp_after_link_change(hw, TRUE); + + /* 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(hw->mac_type >= em_82544) + em_config_collision_dist(hw); + else { + ret_val = em_config_mac_to_phy(hw); + if(ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + + /* 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 = em_config_fc_after_link_up(hw); + if(ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + + /* 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(hw->tbi_compatibility_en) { + uint16_t speed, duplex; + em_get_speed_and_duplex(hw, &speed, &duplex); + if(speed != SPEED_1000) { + /* If link speed is not set to gigabit speed, we do not need + * to enable TBI compatibility. + */ + if(hw->tbi_compatibility_on) { + /* If we previously were in the mode, turn it off. */ + rctl = E1000_READ_REG(hw, RCTL); + rctl &= ~E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + hw->tbi_compatibility_on = FALSE; + } + } 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(!hw->tbi_compatibility_on) { + hw->tbi_compatibility_on = TRUE; + rctl = E1000_READ_REG(hw, RCTL); + rctl |= E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + } + } + } + } + /* 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. + */ + else if((((hw->media_type == em_media_type_fiber) && + ((ctrl & E1000_CTRL_SWDPIN1) == signal)) || + (hw->media_type == em_media_type_internal_serdes)) && + (!(status & E1000_STATUS_LU)) && + (!(rxcw & E1000_RXCW_C))) { + if(hw->autoneg_failed == 0) { + hw->autoneg_failed = 1; + return 0; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\r\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, TXCW, (hw->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = em_config_fc_after_link_up(hw); + if(ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } + /* 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. + */ + else if(((hw->media_type == em_media_type_fiber) || + (hw->media_type == em_media_type_internal_serdes)) && + (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\r\n"); + E1000_WRITE_REG(hw, TXCW, hw->txcw); + E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU)); + + hw->serdes_link_down = FALSE; + } + /* If we force link for non-auto-negotiation switch, check link status + * based on MAC synchronization for internal serdes media type. + */ + else if((hw->media_type == em_media_type_internal_serdes) && + !(E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) { + /* SYNCH bit and IV bit are sticky. */ + usec_delay(10); + if(E1000_RXCW_SYNCH & E1000_READ_REG(hw, RXCW)) { + if(!(rxcw & E1000_RXCW_IV)) { + hw->serdes_link_down = FALSE; + DEBUGOUT("SERDES: Link is up.\n"); + } + } else { + hw->serdes_link_down = TRUE; + DEBUGOUT("SERDES: Link is down.\n"); + } + } + if((hw->media_type == em_media_type_internal_serdes) && + (E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) { + hw->serdes_link_down = !(E1000_STATUS_LU & E1000_READ_REG(hw, STATUS)); + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + *****************************************************************************/ +int32_t +em_get_speed_and_duplex(struct em_hw *hw, + uint16_t *speed, + uint16_t *duplex) +{ + uint32_t status; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_get_speed_and_duplex"); + + if(hw->mac_type >= em_82543) { + status = E1000_READ_REG(hw, 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\r\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT(" Half Duplex\r\n"); + } + } else { + DEBUGOUT("1000 Mbs, Full Duplex\r\n"); + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + } + + /* 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. + */ + if(hw->phy_type == em_phy_igp && hw->speed_downgraded) { + ret_val = em_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data); + if(ret_val) + return ret_val; + + if(!(phy_data & NWAY_ER_LP_NWAY_CAPS)) + *duplex = HALF_DUPLEX; + else { + ret_val = em_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data); + if(ret_val) + return ret_val; + if((*speed == SPEED_100 && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) || + (*speed == SPEED_10 && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) + *duplex = HALF_DUPLEX; + } + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Blocks until autoneg completes or times out (~4.5 seconds) +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +em_wait_autoneg(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t i; + uint16_t phy_data; + + DEBUGFUNC("em_wait_autoneg"); + DEBUGOUT("Waiting for Auto-Neg to complete.\n"); + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for(i = PHY_AUTO_NEG_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + if(phy_data & MII_SR_AUTONEG_COMPLETE) { + return E1000_SUCCESS; + } + msec_delay(100); + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Raises the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +em_raise_mdi_clk(struct em_hw *hw, + uint32_t *ctrl) +{ + /* Raise the clock input to the Management Data Clock (by setting the MDC + * bit), and then delay 10 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + usec_delay(10); +} + +/****************************************************************************** +* Lowers the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +em_lower_mdi_clk(struct em_hw *hw, + uint32_t *ctrl) +{ + /* Lower the clock input to the Management Data Clock (by clearing the MDC + * bit), and then delay 10 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + usec_delay(10); +} + +/****************************************************************************** +* Shifts data bits out to the PHY +* +* hw - Struct containing variables accessed by shared code +* data - Data to send out to the PHY +* count - Number of bits to shift out +* +* Bits are shifted out in MSB to LSB order. +******************************************************************************/ +static void +em_shift_out_mdi_bits(struct em_hw *hw, + uint32_t data, + uint16_t count) +{ + uint32_t ctrl; + uint32_t 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, 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, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + usec_delay(10); + + em_raise_mdi_clk(hw, &ctrl); + em_lower_mdi_clk(hw, &ctrl); + + mask = mask >> 1; + } +} + +/****************************************************************************** +* Shifts data bits in from the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Bits are shifted in in MSB to LSB order. +******************************************************************************/ +static uint16_t +em_shift_in_mdi_bits(struct em_hw *hw) +{ + uint32_t ctrl; + uint16_t data = 0; + uint8_t 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, 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, 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. + */ + em_raise_mdi_clk(hw, &ctrl); + em_lower_mdi_clk(hw, &ctrl); + + for(data = 0, i = 0; i < 16; i++) { + data = data << 1; + em_raise_mdi_clk(hw, &ctrl); + ctrl = E1000_READ_REG(hw, CTRL); + /* Check to see if we shifted in a "1". */ + if(ctrl & E1000_CTRL_MDIO) data |= 1; + em_lower_mdi_clk(hw, &ctrl); + } + + em_raise_mdi_clk(hw, &ctrl); + em_lower_mdi_clk(hw, &ctrl); + + return data; +} + +/***************************************************************************** +* Reads the value from a PHY register, if the value is on a specific non zero +* page, sets the page first. +* hw - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to read +******************************************************************************/ +int32_t +em_read_phy_reg(struct em_hw *hw, + uint32_t reg_addr, + uint16_t *phy_data) +{ + uint32_t ret_val; + + DEBUGFUNC("em_read_phy_reg"); + + if((hw->phy_type == em_phy_igp || + hw->phy_type == em_phy_igp_2) && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret_val = em_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr); + if(ret_val) { + return ret_val; + } + } + + ret_val = em_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, + phy_data); + + return ret_val; +} + +int32_t +em_read_phy_reg_ex(struct em_hw *hw, + uint32_t reg_addr, + uint16_t *phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + DEBUGFUNC("em_read_phy_reg_ex"); + + if(reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if(hw->mac_type > em_82543) { + /* Set up Op-code, Phy Address, and register address in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + E1000_WRITE_REG(hw, 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, MDIC); + if(mdic & E1000_MDIC_READY) break; + } + if(!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Read did not complete\n"); + return -E1000_ERR_PHY; + } + if(mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + *phy_data = (uint16_t) mdic; + } else { + /* 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. + */ + em_shift_out_mdi_bits(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 + * em_shift_out_mdi_bits routine five different times. The format of + * a 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 = ((reg_addr) | (phy_addr << 5) | + (PHY_OP_READ << 10) | (PHY_SOF << 12)); + + em_shift_out_mdi_bits(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. + */ + *phy_data = em_shift_in_mdi_bits(hw); + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Writes a value to a PHY register +* +* hw - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to write +* data - data to write to the PHY +******************************************************************************/ +int32_t +em_write_phy_reg(struct em_hw *hw, + uint32_t reg_addr, + uint16_t phy_data) +{ + uint32_t ret_val; + + DEBUGFUNC("em_write_phy_reg"); + + if((hw->phy_type == em_phy_igp || + hw->phy_type == em_phy_igp_2) && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret_val = em_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr); + if(ret_val) { + return ret_val; + } + } + + ret_val = em_write_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, + phy_data); + + return ret_val; +} + +int32_t +em_write_phy_reg_ex(struct em_hw *hw, + uint32_t reg_addr, + uint16_t phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + DEBUGFUNC("em_write_phy_reg_ex"); + + if(reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if(hw->mac_type > em_82543) { + /* Set up Op-code, Phy Address, register address, and data intended + * for the PHY register in the MDI Control register. The MAC will take + * care of interfacing with the PHY to send the desired data. + */ + mdic = (((uint32_t) phy_data) | + (reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + E1000_WRITE_REG(hw, MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for(i = 0; i < 640; i++) { + usec_delay(5); + mdic = E1000_READ_REG(hw, MDIC); + if(mdic & E1000_MDIC_READY) break; + } + if(!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + return -E1000_ERR_PHY; + } + } else { + /* 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. + */ + em_shift_out_mdi_bits(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 + * em_shift_out_mdi_bits routine for each field in the command. The + * format of a MII write instruction is as follows: + * . + */ + mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | + (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); + mdic <<= 16; + mdic |= (uint32_t) phy_data; + + em_shift_out_mdi_bits(hw, mdic, 32); + } + + return E1000_SUCCESS; +} + + +/****************************************************************************** +* Returns the PHY to the power-on reset state +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +em_phy_hw_reset(struct em_hw *hw) +{ + uint32_t ctrl, ctrl_ext; + uint32_t led_ctrl; + int32_t ret_val; + + DEBUGFUNC("em_phy_hw_reset"); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = em_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + DEBUGOUT("Resetting Phy...\n"); + + if(hw->mac_type > em_82543) { + /* Read the device control register and assert the E1000_CTRL_PHY_RST + * bit. Then, take it out of reset. + */ + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + msec_delay(10); + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + } else { + /* Read the Extended Device Control Register, assert the PHY_RESET_DIR + * bit to put the PHY into reset. Then, take it out of reset. + */ + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; + ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + msec_delay(10); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + usec_delay(150); + + if((hw->mac_type == em_82541) || (hw->mac_type == em_82547)) { + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* Wait for FW to finish PHY configuration. */ + ret_val = em_get_phy_cfg_done(hw); + + return ret_val; +} + +/****************************************************************************** +* Resets the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Sets bit 15 of the MII Control regiser +******************************************************************************/ +int32_t +em_phy_reset(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_phy_reset"); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = em_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + switch (hw->mac_type) { + case em_82541_rev_2: + ret_val = em_phy_hw_reset(hw); + if(ret_val) + return ret_val; + break; + default: + ret_val = em_read_phy_reg(hw, PHY_CTRL, &phy_data); + if(ret_val) + return ret_val; + + phy_data |= MII_CR_RESET; + ret_val = em_write_phy_reg(hw, PHY_CTRL, phy_data); + if(ret_val) + return ret_val; + + usec_delay(1); + break; + } + + if(hw->phy_type == em_phy_igp || hw->phy_type == em_phy_igp_2) + em_phy_init_script(hw); + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Probes the expected PHY address for known PHY IDs +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +em_detect_gig_phy(struct em_hw *hw) +{ + int32_t phy_init_status, ret_val; + uint16_t phy_id_high, phy_id_low; + boolean_t match = FALSE; + + DEBUGFUNC("em_detect_gig_phy"); + + /* Read the PHY ID Registers to identify which PHY is onboard. */ + ret_val = em_read_phy_reg(hw, PHY_ID1, &phy_id_high); + if(ret_val) + return ret_val; + + hw->phy_id = (uint32_t) (phy_id_high << 16); + usec_delay(20); + ret_val = em_read_phy_reg(hw, PHY_ID2, &phy_id_low); + if(ret_val) + return ret_val; + + hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK); + hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK; + + switch(hw->mac_type) { + case em_82543: + if(hw->phy_id == M88E1000_E_PHY_ID) match = TRUE; + break; + case em_82544: + if(hw->phy_id == M88E1000_I_PHY_ID) match = TRUE; + break; + case em_82540: + case em_82545: + case em_82545_rev_3: + case em_82546: + case em_82546_rev_3: + if(hw->phy_id == M88E1011_I_PHY_ID) match = TRUE; + break; + case em_82541: + case em_82541_rev_2: + case em_82547: + case em_82547_rev_2: + if(hw->phy_id == IGP01E1000_I_PHY_ID) match = TRUE; + break; + case em_82573: + if(hw->phy_id == M88E1111_I_PHY_ID) match = TRUE; + break; + default: + DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type); + return -E1000_ERR_CONFIG; + } + phy_init_status = em_set_phy_type(hw); + + if ((match) && (phy_init_status == E1000_SUCCESS)) { + DEBUGOUT1("PHY ID 0x%X detected\n", hw->phy_id); + return E1000_SUCCESS; + } + DEBUGOUT1("Invalid PHY ID 0x%X\n", hw->phy_id); + return -E1000_ERR_PHY; +} + +/****************************************************************************** +* Resets the PHY's DSP +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +em_phy_reset_dsp(struct em_hw *hw) +{ + int32_t ret_val; + DEBUGFUNC("em_phy_reset_dsp"); + + do { + ret_val = em_write_phy_reg(hw, 29, 0x001d); + if(ret_val) break; + ret_val = em_write_phy_reg(hw, 30, 0x00c1); + if(ret_val) break; + ret_val = em_write_phy_reg(hw, 30, 0x0000); + if(ret_val) break; + ret_val = E1000_SUCCESS; + } while(0); + + return ret_val; +} + +/****************************************************************************** +* Get PHY information from various PHY registers for igp PHY only. +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +int32_t +em_phy_igp_get_info(struct em_hw *hw, + struct em_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data, polarity, min_length, max_length, average; + + DEBUGFUNC("em_phy_igp_get_info"); + + /* The downshift status is checked only once, after link is established, + * and it stored in the hw->speed_downgraded parameter. */ + phy_info->downshift = (em_downshift)hw->speed_downgraded; + + /* IGP01E1000 does not need to support it. */ + phy_info->extended_10bt_distance = em_10bt_ext_dist_enable_normal; + + /* IGP01E1000 always correct polarity reversal */ + phy_info->polarity_correction = em_polarity_reversal_enabled; + + /* Check polarity status */ + ret_val = em_check_polarity(hw, &polarity); + if(ret_val) + return ret_val; + + phy_info->cable_polarity = polarity; + + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data); + if(ret_val) + return ret_val; + + phy_info->mdix_mode = (phy_data & IGP01E1000_PSSR_MDIX) >> + IGP01E1000_PSSR_MDIX_SHIFT; + + if((phy_data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + /* Local/Remote Receiver Information are only valid at 1000 Mbps */ + ret_val = em_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if(ret_val) + return ret_val; + + phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >> + SR_1000T_LOCAL_RX_STATUS_SHIFT; + phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >> + SR_1000T_REMOTE_RX_STATUS_SHIFT; + + /* Get cable length */ + ret_val = em_get_cable_length(hw, &min_length, &max_length); + if(ret_val) + return ret_val; + + /* Translate to old method */ + average = (max_length + min_length) / 2; + + if(average <= em_igp_cable_length_50) + phy_info->cable_length = em_cable_length_50; + else if(average <= em_igp_cable_length_80) + phy_info->cable_length = em_cable_length_50_80; + else if(average <= em_igp_cable_length_110) + phy_info->cable_length = em_cable_length_80_110; + else if(average <= em_igp_cable_length_140) + phy_info->cable_length = em_cable_length_110_140; + else + phy_info->cable_length = em_cable_length_140; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Get PHY information from various PHY registers fot m88 PHY only. +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +int32_t +em_phy_m88_get_info(struct em_hw *hw, + struct em_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data, polarity; + + DEBUGFUNC("em_phy_m88_get_info"); + + /* The downshift status is checked only once, after link is established, + * and it stored in the hw->speed_downgraded parameter. */ + phy_info->downshift = (em_downshift)hw->speed_downgraded; + + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) + return ret_val; + + phy_info->extended_10bt_distance = + (phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >> + M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT; + phy_info->polarity_correction = + (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >> + M88E1000_PSCR_POLARITY_REVERSAL_SHIFT; + + /* Check polarity status */ + ret_val = em_check_polarity(hw, &polarity); + if(ret_val) + return ret_val; + phy_info->cable_polarity = polarity; + + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if(ret_val) + return ret_val; + + phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) >> + M88E1000_PSSR_MDIX_SHIFT; + + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + /* Cable Length Estimation and Local/Remote Receiver Information + * are only valid at 1000 Mbps. + */ + phy_info->cable_length = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT); + + ret_val = em_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if(ret_val) + return ret_val; + + phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >> + SR_1000T_LOCAL_RX_STATUS_SHIFT; + + phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >> + SR_1000T_REMOTE_RX_STATUS_SHIFT; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Get PHY information from various PHY registers +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +int32_t +em_phy_get_info(struct em_hw *hw, + struct em_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_phy_get_info"); + + phy_info->cable_length = em_cable_length_undefined; + phy_info->extended_10bt_distance = em_10bt_ext_dist_enable_undefined; + phy_info->cable_polarity = em_rev_polarity_undefined; + phy_info->downshift = em_downshift_undefined; + phy_info->polarity_correction = em_polarity_reversal_undefined; + phy_info->mdix_mode = em_auto_x_mode_undefined; + phy_info->local_rx = em_1000t_rx_status_undefined; + phy_info->remote_rx = em_1000t_rx_status_undefined; + + if(hw->media_type != em_media_type_copper) { + DEBUGOUT("PHY info is only valid for copper media\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) + return ret_val; + + if((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) { + DEBUGOUT("PHY info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + if(hw->phy_type == em_phy_igp || + hw->phy_type == em_phy_igp_2) + return em_phy_igp_get_info(hw, phy_info); + else + return em_phy_m88_get_info(hw, phy_info); +} + +int32_t +em_validate_mdi_setting(struct em_hw *hw) +{ + DEBUGFUNC("em_validate_mdi_settings"); + + if(!hw->autoneg && (hw->mdix == 0 || hw->mdix == 3)) { + DEBUGOUT("Invalid MDI setting detected\n"); + hw->mdix = 1; + return -E1000_ERR_CONFIG; + } + return E1000_SUCCESS; +} + + +/****************************************************************************** + * Sets up eeprom variables in the hw struct. Must be called after mac_type + * is configured. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_init_eeprom_params(struct em_hw *hw) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd = E1000_READ_REG(hw, EECD); + int32_t ret_val = E1000_SUCCESS; + uint16_t eeprom_size; + + DEBUGFUNC("em_init_eeprom_params"); + + switch (hw->mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + case em_82543: + case em_82544: + eeprom->type = em_eeprom_microwire; + eeprom->word_size = 64; + eeprom->opcode_bits = 3; + eeprom->address_bits = 6; + eeprom->delay_usec = 50; + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case em_82540: + case em_82545: + case em_82545_rev_3: + case em_82546: + case em_82546_rev_3: + eeprom->type = em_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if(eecd & E1000_EECD_SIZE) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case em_82541: + case em_82541_rev_2: + case em_82547: + case em_82547_rev_2: + if (eecd & E1000_EECD_TYPE) { + eeprom->type = em_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + } else { + eeprom->type = em_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case em_82573: + eeprom->type = em_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = TRUE; + eeprom->use_eewr = TRUE; + if(em_is_onboard_nvm_eeprom(hw) == FALSE) { + eeprom->type = em_eeprom_flash; + eeprom->word_size = 2048; + + /* Ensure that the Autonomous FLASH update bit is cleared due to + * Flash update issue on parts which use a FLASH for NVM. */ + eecd &= ~E1000_EECD_AUPDEN; + E1000_WRITE_REG(hw, EECD, eecd); + } + break; + default: + break; + } + + if (eeprom->type == em_eeprom_spi) { + /* eeprom_size will be an enum [0..8] that maps to eeprom sizes 128B to + * 32KB (incremented by powers of 2). + */ + if(hw->mac_type <= em_82547_rev_2) { + /* Set to default value for initial eeprom read. */ + eeprom->word_size = 64; + ret_val = em_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size); + if(ret_val) + return ret_val; + eeprom_size = (eeprom_size & EEPROM_SIZE_MASK) >> EEPROM_SIZE_SHIFT; + /* 256B eeprom size was not supported in earlier hardware, so we + * bump eeprom_size up one to ensure that "1" (which maps to 256B) + * is never the result used in the shifting logic below. */ + if(eeprom_size) + eeprom_size++; + } else { + eeprom_size = (uint16_t)((eecd & E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + } + + eeprom->word_size = 1 << (eeprom_size + EEPROM_WORD_SIZE_SHIFT); + } + return ret_val; +} + +/****************************************************************************** + * Raises the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +static void +em_raise_ee_clk(struct em_hw *hw, + uint32_t *eecd) +{ + /* Raise the clock input to the EEPROM (by setting the SK bit), and then + * wait microseconds. + */ + *eecd = *eecd | E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->eeprom.delay_usec); +} + +/****************************************************************************** + * Lowers the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +static void +em_lower_ee_clk(struct em_hw *hw, + uint32_t *eecd) +{ + /* Lower the clock input to the EEPROM (by clearing the SK bit), and then + * wait 50 microseconds. + */ + *eecd = *eecd & ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->eeprom.delay_usec); +} + +/****************************************************************************** + * Shift data bits out to the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * data - data to send to the EEPROM + * count - number of bits to shift out + *****************************************************************************/ +static void +em_shift_out_ee_bits(struct em_hw *hw, + uint16_t data, + uint16_t count) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + uint32_t mask; + + /* We need to shift "count" bits out to the EEPROM. So, 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. + */ + mask = 0x01 << (count - 1); + eecd = E1000_READ_REG(hw, EECD); + if (eeprom->type == em_eeprom_microwire) { + eecd &= ~E1000_EECD_DO; + } else if (eeprom->type == em_eeprom_spi) { + eecd |= E1000_EECD_DO; + } + do { + /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1", + * and then raising and then lowering the clock (the SK bit controls + * the clock input to the EEPROM). A "0" is shifted out to the EEPROM + * by setting "DI" to "0" and then raising and then lowering the clock. + */ + eecd &= ~E1000_EECD_DI; + + if(data & mask) + eecd |= E1000_EECD_DI; + + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + + usec_delay(eeprom->delay_usec); + + em_raise_ee_clk(hw, &eecd); + em_lower_ee_clk(hw, &eecd); + + mask = mask >> 1; + + } while(mask); + + /* We leave the "DI" bit set to "0" when we leave this routine. */ + eecd &= ~E1000_EECD_DI; + E1000_WRITE_REG(hw, EECD, eecd); +} + +/****************************************************************************** + * Shift data bits in from the EEPROM + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static uint16_t +em_shift_in_ee_bits(struct em_hw *hw, + uint16_t count) +{ + uint32_t eecd; + uint32_t i; + uint16_t data; + + /* 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 "DO" bit. During this "shifting in" process the "DI" bit should + * always be clear. + */ + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for(i = 0; i < count; i++) { + data = data << 1; + em_raise_ee_clk(hw, &eecd); + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DI); + if(eecd & E1000_EECD_DO) + data |= 1; + + em_lower_ee_clk(hw, &eecd); + } + + return data; +} + +/****************************************************************************** + * Prepares EEPROM for access + * + * hw - Struct containing variables accessed by shared code + * + * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This + * function should be called before issuing a command to the EEPROM. + *****************************************************************************/ +static int32_t +em_acquire_eeprom(struct em_hw *hw) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd, i=0; + + DEBUGFUNC("em_acquire_eeprom"); + + if(em_get_hw_eeprom_semaphore(hw)) + return -E1000_ERR_EEPROM; + + eecd = E1000_READ_REG(hw, EECD); + + if (hw->mac_type != em_82573) { + /* Request EEPROM Access */ + if(hw->mac_type > em_82544) { + eecd |= E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + eecd = E1000_READ_REG(hw, EECD); + while((!(eecd & E1000_EECD_GNT)) && + (i < E1000_EEPROM_GRANT_ATTEMPTS)) { + i++; + usec_delay(5); + eecd = E1000_READ_REG(hw, EECD); + } + if(!(eecd & E1000_EECD_GNT)) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + DEBUGOUT("Could not acquire EEPROM grant\n"); + return -E1000_ERR_EEPROM; + } + } + } + + /* Setup EEPROM for Read/Write */ + + if (eeprom->type == em_eeprom_microwire) { + /* Clear SK and DI */ + eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + + /* Set CS */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + } else if (eeprom->type == em_eeprom_spi) { + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + usec_delay(1); + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Returns EEPROM to a "standby" state + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +em_standby_eeprom(struct em_hw *hw) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + + eecd = E1000_READ_REG(hw, EECD); + + if(eeprom->type == em_eeprom_microwire) { + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(eeprom->delay_usec); + + /* Clock high */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(eeprom->delay_usec); + + /* Select EEPROM */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(eeprom->delay_usec); + + /* Clock low */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(eeprom->delay_usec); + } else if(eeprom->type == em_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(eeprom->delay_usec); + eecd &= ~E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(eeprom->delay_usec); + } +} + +/****************************************************************************** + * Terminates a command by inverting the EEPROM's chip select pin + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +em_release_eeprom(struct em_hw *hw) +{ + uint32_t eecd; + + DEBUGFUNC("em_release_eeprom"); + + eecd = E1000_READ_REG(hw, EECD); + + if (hw->eeprom.type == em_eeprom_spi) { + eecd |= E1000_EECD_CS; /* Pull CS high */ + eecd &= ~E1000_EECD_SK; /* Lower SCK */ + + E1000_WRITE_REG(hw, EECD, eecd); + + usec_delay(hw->eeprom.delay_usec); + } else if(hw->eeprom.type == em_eeprom_microwire) { + /* cleanup eeprom */ + + /* CS on Microwire is active-high */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); + + E1000_WRITE_REG(hw, EECD, eecd); + + /* Rising edge of clock */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->eeprom.delay_usec); + + /* Falling edge of clock */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->eeprom.delay_usec); + } + + /* Stop requesting EEPROM access */ + if(hw->mac_type > em_82544) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + } + + em_put_hw_eeprom_semaphore(hw); +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_spi_eeprom_ready(struct em_hw *hw) +{ + uint16_t retry_count = 0; + uint8_t spi_stat_reg; + + DEBUGFUNC("em_spi_eeprom_ready"); + + /* 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 + * 5 milliseconds, then error out. + */ + retry_count = 0; + do { + em_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI, + hw->eeprom.opcode_bits); + spi_stat_reg = (uint8_t)em_shift_in_ee_bits(hw, 8); + if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI)) + break; + + usec_delay(5); + retry_count += 5; + + em_standby_eeprom(hw); + } while(retry_count < EEPROM_MAX_RETRY_SPI); + + /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and + * only 0-5mSec on 5V devices) + */ + if(retry_count >= EEPROM_MAX_RETRY_SPI) { + DEBUGOUT("SPI EEPROM Status error\n"); + return -E1000_ERR_EEPROM; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +int32_t +em_read_eeprom(struct em_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + uint32_t i = 0; + int32_t ret_val; + + DEBUGFUNC("em_read_eeprom"); + + /* A check for invalid values: offset too large, too many words, and not + * enough words. + */ + if((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) || + (words == 0)) { + DEBUGOUT("\"words\" parameter out of bounds\n"); + return -E1000_ERR_EEPROM; + } + + /* FLASH reads without acquiring the semaphore are safe in 82573-based + * controllers. + */ + if ((em_is_onboard_nvm_eeprom(hw) == TRUE) || + (hw->mac_type != em_82573)) { + /* Prepare the EEPROM for reading */ + if(em_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + } + + if(eeprom->use_eerd == TRUE) { + ret_val = em_read_eeprom_eerd(hw, offset, words, data); + if ((em_is_onboard_nvm_eeprom(hw) == TRUE) || + (hw->mac_type != em_82573)) + em_release_eeprom(hw); + return ret_val; + } + + if(eeprom->type == em_eeprom_spi) { + uint16_t word_in; + uint8_t read_opcode = EEPROM_READ_OPCODE_SPI; + + if(em_spi_eeprom_ready(hw)) { + em_release_eeprom(hw); + return -E1000_ERR_EEPROM; + } + + em_standby_eeprom(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the opcode */ + if((eeprom->address_bits == 8) && (offset >= 128)) + read_opcode |= EEPROM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + em_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits); + em_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits); + + /* Read the data. The address of the eeprom internally increments with + * each byte (spi) being read, saving on the overhead of eeprom setup + * and tear-down. The address counter will roll over if reading beyond + * the size of the eeprom, thus allowing the entire memory to be read + * starting from any offset. */ + for (i = 0; i < words; i++) { + word_in = em_shift_in_ee_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + } else if(eeprom->type == em_eeprom_microwire) { + for (i = 0; i < words; i++) { + /* Send the READ command (opcode + addr) */ + em_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE, + eeprom->opcode_bits); + em_shift_out_ee_bits(hw, (uint16_t)(offset + i), + eeprom->address_bits); + + /* Read the data. For microwire, each word requires the overhead + * of eeprom setup and tear-down. */ + data[i] = em_shift_in_ee_bits(hw, 16); + em_standby_eeprom(hw); + } + } + + /* End this read operation */ + em_release_eeprom(hw); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM using the EERD register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +int32_t +em_read_eeprom_eerd(struct em_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + uint32_t i, eerd = 0; + int32_t error = 0; + + for (i = 0; i < words; i++) { + eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) + + E1000_EEPROM_RW_REG_START; + + E1000_WRITE_REG(hw, EERD, eerd); + error = em_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ); + + if(error) { + break; + } + data[i] = (E1000_READ_REG(hw, EERD) >> E1000_EEPROM_RW_REG_DATA); + + } + + return error; +} + +/****************************************************************************** + * Writes a 16 bit word from the EEPROM using the EEWR register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +int32_t +em_write_eeprom_eewr(struct em_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + uint32_t register_value = 0; + uint32_t i = 0; + int32_t error = 0; + + for (i = 0; i < words; i++) { + register_value = (data[i] << E1000_EEPROM_RW_REG_DATA) | + ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) | + E1000_EEPROM_RW_REG_START; + + error = em_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE); + if(error) { + break; + } + + E1000_WRITE_REG(hw, EEWR, register_value); + + error = em_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE); + + if(error) { + break; + } + } + + return error; +} + +/****************************************************************************** + * Polls the status bit (bit 1) of the EERD to determine when the read is done. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_poll_eerd_eewr_done(struct em_hw *hw, int eerd) +{ + uint32_t attempts = 100000; + uint32_t i, reg = 0; + int32_t done = E1000_ERR_EEPROM; + + for(i = 0; i < attempts; i++) { + if(eerd == E1000_EEPROM_POLL_READ) + reg = E1000_READ_REG(hw, EERD); + else + reg = E1000_READ_REG(hw, EEWR); + + if(reg & E1000_EEPROM_RW_REG_DONE) { + done = E1000_SUCCESS; + break; + } + usec_delay(5); + } + + return done; +} + +/*************************************************************************** +* Description: Determines if the onboard NVM is FLASH or EEPROM. +* +* hw - Struct containing variables accessed by shared code +****************************************************************************/ +boolean_t +em_is_onboard_nvm_eeprom(struct em_hw *hw) +{ + uint32_t eecd = 0; + + if(hw->mac_type == em_82573) { + eecd = E1000_READ_REG(hw, EECD); + + /* Isolate bits 15 & 16 */ + eecd = ((eecd >> 15) & 0x03); + + /* If both bits are set, device is Flash type */ + if(eecd == 0x03) { + return FALSE; + } + } + return TRUE; +} + +/****************************************************************************** + * Verifies that the EEPROM has a valid checksum + * + * hw - Struct containing variables accessed by shared code + * + * Reads the first 64 16 bit words of the EEPROM and sums the values read. + * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is + * valid. + *****************************************************************************/ +int32_t +em_validate_eeprom_checksum(struct em_hw *hw) +{ + uint16_t checksum = 0; + uint16_t i, eeprom_data; + + DEBUGFUNC("em_validate_eeprom_checksum"); + + if ((hw->mac_type == em_82573) && + (em_is_onboard_nvm_eeprom(hw) == FALSE)) { + /* Check bit 4 of word 10h. If it is 0, firmware is done updating + * 10h-12h. Checksum may need to be fixed. */ + em_read_eeprom(hw, 0x10, 1, &eeprom_data); + if ((eeprom_data & 0x10) == 0) { + /* 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. */ + em_read_eeprom(hw, 0x23, 1, &eeprom_data); + if ((eeprom_data & 0x8000) == 0) { + eeprom_data |= 0x8000; + em_write_eeprom(hw, 0x23, 1, &eeprom_data); + em_update_eeprom_checksum(hw); + } + } + } + + for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { + if(em_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + checksum += eeprom_data; + } + + if(checksum == (uint16_t) EEPROM_SUM) + return E1000_SUCCESS; + else { + DEBUGOUT("EEPROM Checksum Invalid\n"); + return -E1000_ERR_EEPROM; + } +} + +/****************************************************************************** + * Calculates the EEPROM checksum and writes it to the EEPROM + * + * hw - Struct containing variables accessed by shared code + * + * Sums the first 63 16 bit words of the EEPROM. Subtracts the sum from 0xBABA. + * Writes the difference to word offset 63 of the EEPROM. + *****************************************************************************/ +int32_t +em_update_eeprom_checksum(struct em_hw *hw) +{ + uint16_t checksum = 0; + uint16_t i, eeprom_data; + + DEBUGFUNC("em_update_eeprom_checksum"); + + for(i = 0; i < EEPROM_CHECKSUM_REG; i++) { + if(em_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + checksum += eeprom_data; + } + checksum = (uint16_t) EEPROM_SUM - checksum; + if(em_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) { + DEBUGOUT("EEPROM Write Error\n"); + return -E1000_ERR_EEPROM; + } else if (hw->eeprom.type == em_eeprom_flash) { + em_commit_shadow_ram(hw); + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Parent function for writing words to the different EEPROM types. + * + * hw - Struct containing variables accessed by shared code + * offset - offset within the EEPROM to be written to + * words - number of words to write + * data - 16 bit word to be written to the EEPROM + * + * If em_update_eeprom_checksum is not called after this function, the + * EEPROM will most likely contain an invalid checksum. + *****************************************************************************/ +int32_t +em_write_eeprom(struct em_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + int32_t status = 0; + + DEBUGFUNC("em_write_eeprom"); + + /* A check for invalid values: offset too large, too many words, and not + * enough words. + */ + if((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) || + (words == 0)) { + DEBUGOUT("\"words\" parameter out of bounds\n"); + return -E1000_ERR_EEPROM; + } + + /* 82573 reads only through eerd */ + if(eeprom->use_eewr == TRUE) + return em_write_eeprom_eewr(hw, offset, words, data); + + /* Prepare the EEPROM for writing */ + if (em_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + + if(eeprom->type == em_eeprom_microwire) { + status = em_write_eeprom_microwire(hw, offset, words, data); + } else { + status = em_write_eeprom_spi(hw, offset, words, data); + msec_delay(10); + } + + /* Done with writing */ + em_release_eeprom(hw); + + return status; +} + +/****************************************************************************** + * Writes a 16 bit word to a given offset in an SPI EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset within the EEPROM to be written to + * words - number of words to write + * data - pointer to array of 8 bit words to be written to the EEPROM + * + *****************************************************************************/ +int32_t +em_write_eeprom_spi(struct em_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + uint16_t widx = 0; + + DEBUGFUNC("em_write_eeprom_spi"); + + while (widx < words) { + uint8_t write_opcode = EEPROM_WRITE_OPCODE_SPI; + + if(em_spi_eeprom_ready(hw)) return -E1000_ERR_EEPROM; + + em_standby_eeprom(hw); + + /* Send the WRITE ENABLE command (8 bit opcode ) */ + em_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI, + eeprom->opcode_bits); + + em_standby_eeprom(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the opcode */ + if((eeprom->address_bits == 8) && (offset >= 128)) + write_opcode |= EEPROM_A8_OPCODE_SPI; + + /* Send the Write command (8-bit opcode + addr) */ + em_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits); + + em_shift_out_ee_bits(hw, (uint16_t)((offset + widx)*2), + eeprom->address_bits); + + /* Send the data */ + + /* Loop to allow for up to whole page write (32 bytes) of eeprom */ + while (widx < words) { + uint16_t word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); + em_shift_out_ee_bits(hw, word_out, 16); + widx++; + + /* Some larger eeprom sizes are capable of a 32-byte PAGE WRITE + * operation, while the smaller eeproms are capable of an 8-byte + * PAGE WRITE operation. Break the inner loop to pass new address + */ + if((((offset + widx)*2) % eeprom->page_size) == 0) { + em_standby_eeprom(hw); + break; + } + } + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Writes a 16 bit word to a given offset in a Microwire EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset within the EEPROM to be written to + * words - number of words to write + * data - pointer to array of 16 bit words to be written to the EEPROM + * + *****************************************************************************/ +int32_t +em_write_eeprom_microwire(struct em_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct em_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + uint16_t words_written = 0; + uint16_t i = 0; + + DEBUGFUNC("em_write_eeprom_microwire"); + + /* Send the write enable command to the EEPROM (3-bit opcode plus + * 6/8-bit dummy address beginning with 11). It's less work to include + * the 11 of the dummy address as part of the opcode than it is to shift + * it over the correct number of bits for the address. This puts the + * EEPROM into write/erase mode. + */ + em_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE, + (uint16_t)(eeprom->opcode_bits + 2)); + + em_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2)); + + /* Prepare the EEPROM */ + em_standby_eeprom(hw); + + while (words_written < words) { + /* Send the Write command (3-bit opcode + addr) */ + em_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE, + eeprom->opcode_bits); + + em_shift_out_ee_bits(hw, (uint16_t)(offset + words_written), + eeprom->address_bits); + + /* Send the data */ + em_shift_out_ee_bits(hw, data[words_written], 16); + + /* Toggle the CS line. This in effect tells the EEPROM to execute + * the previous command. + */ + em_standby_eeprom(hw); + + /* Read DO repeatedly until it is high (equal to '1'). The EEPROM will + * signal that the command has been completed by raising the DO signal. + * If DO does not go high in 10 milliseconds, then error out. + */ + for(i = 0; i < 200; i++) { + eecd = E1000_READ_REG(hw, EECD); + if(eecd & E1000_EECD_DO) break; + usec_delay(50); + } + if(i == 200) { + DEBUGOUT("EEPROM Write did not complete\n"); + return -E1000_ERR_EEPROM; + } + + /* Recover from write */ + em_standby_eeprom(hw); + + words_written++; + } + + /* Send the write disable command to the EEPROM (3-bit opcode plus + * 6/8-bit dummy address beginning with 10). It's less work to include + * the 10 of the dummy address as part of the opcode than it is to shift + * it over the correct number of bits for the address. This takes the + * EEPROM out of write/erase mode. + */ + em_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE, + (uint16_t)(eeprom->opcode_bits + 2)); + + em_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2)); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Flushes the cached eeprom to NVM. This is done by saving the modified values + * in the eeprom cache and the non modified values in the currently active bank + * to the new bank. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +int32_t +em_commit_shadow_ram(struct em_hw *hw) +{ + uint32_t attempts = 100000; + uint32_t eecd = 0; + uint32_t flop = 0; + uint32_t i = 0; + int32_t error = E1000_SUCCESS; + + /* The flop register will be used to determine if flash type is STM */ + flop = E1000_READ_REG(hw, FLOP); + + if (hw->mac_type == em_82573) { + for (i=0; i < attempts; i++) { + eecd = E1000_READ_REG(hw, EECD); + if ((eecd & E1000_EECD_FLUPD) == 0) { + break; + } + usec_delay(5); + } + + if (i == attempts) { + return -E1000_ERR_EEPROM; + } + + /* If STM opcode located in bits 15:8 of flop, reset firmware */ + if ((flop & 0xFF00) == E1000_STM_OPCODE) { + E1000_WRITE_REG(hw, HICR, E1000_HICR_FW_RESET); + } + + /* Perform the flash update */ + E1000_WRITE_REG(hw, EECD, eecd | E1000_EECD_FLUPD); + + for (i=0; i < attempts; i++) { + eecd = E1000_READ_REG(hw, EECD); + if ((eecd & E1000_EECD_FLUPD) == 0) { + break; + } + usec_delay(5); + } + + if (i == attempts) { + return -E1000_ERR_EEPROM; + } + } + + return error; +} + +/****************************************************************************** + * Reads the adapter's part number from the EEPROM + * + * hw - Struct containing variables accessed by shared code + * part_num - Adapter's part number + *****************************************************************************/ +int32_t +em_read_part_num(struct em_hw *hw, + uint32_t *part_num) +{ + uint16_t offset = EEPROM_PBA_BYTE_1; + uint16_t eeprom_data; + + DEBUGFUNC("em_read_part_num"); + + /* Get word 0 from EEPROM */ + if(em_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + /* Save word 0 in upper half of part_num */ + *part_num = (uint32_t) (eeprom_data << 16); + + /* Get word 1 from EEPROM */ + if(em_read_eeprom(hw, ++offset, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + /* Save word 1 in lower half of part_num */ + *part_num |= eeprom_data; + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the + * second function of dual function devices + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_read_mac_addr(struct em_hw * hw) +{ + uint16_t offset; + uint16_t eeprom_data, i; + + DEBUGFUNC("em_read_mac_addr"); + + for(i = 0; i < NODE_ADDRESS_SIZE; i += 2) { + offset = i >> 1; + if(em_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + hw->perm_mac_addr[i] = (uint8_t) (eeprom_data & 0x00FF); + hw->perm_mac_addr[i+1] = (uint8_t) (eeprom_data >> 8); + } + if(((hw->mac_type == em_82546) || (hw->mac_type == em_82546_rev_3)) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) + hw->perm_mac_addr[5] ^= 0x01; + + for(i = 0; i < NODE_ADDRESS_SIZE; i++) + hw->mac_addr[i] = hw->perm_mac_addr[i]; + return E1000_SUCCESS; +} + +/****************************************************************************** + * Initializes receive address filters. + * + * hw - Struct containing variables accessed by shared code + * + * Places the MAC address in receive address register 0 and clears the rest + * of the receive addresss registers. Clears the multicast table. Assumes + * the receiver is in reset when the routine is called. + *****************************************************************************/ +void +em_init_rx_addrs(struct em_hw *hw) +{ + uint32_t i; + uint32_t rar_num; + + DEBUGFUNC("em_init_rx_addrs"); + + /* Setup the receive address. */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + + em_rar_set(hw, hw->mac_addr, 0); + + rar_num = E1000_RAR_ENTRIES; + /* Zero out the other 15 receive addresses. */ + DEBUGOUT("Clearing RAR[1-15]\n"); + for(i = 1; i < rar_num; i++) { + E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + } +} + +/****************************************************************************** + * Updates the MAC's list of multicast addresses. + * + * hw - Struct containing variables accessed by shared code + * mc_addr_list - the list of new multicast addresses + * mc_addr_count - number of addresses + * pad - number of bytes between addresses in the list + * rar_used_count - offset where to start adding mc addresses into the RAR's + * + * The given list replaces any existing list. Clears the last 15 receive + * address registers and the multicast table. Uses receive address registers + * for the first 15 multicast addresses, and hashes the rest into the + * multicast table. + *****************************************************************************/ +void +em_mc_addr_list_update(struct em_hw *hw, + uint8_t *mc_addr_list, + uint32_t mc_addr_count, + uint32_t pad, + uint32_t rar_used_count) +{ + uint32_t hash_value; + uint32_t i; + uint32_t num_rar_entry; + uint32_t num_mta_entry; + + DEBUGFUNC("em_mc_addr_list_update"); + + /* Set the new number of MC addresses that we are being requested to use. */ + hw->num_mc_addrs = mc_addr_count; + + /* Clear RAR[1-15] */ + DEBUGOUT(" Clearing RAR[1-15]\n"); + num_rar_entry = E1000_RAR_ENTRIES; + for(i = rar_used_count; i < num_rar_entry; i++) { + E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + } + + /* Clear the MTA */ + DEBUGOUT(" Clearing MTA\n"); + num_mta_entry = E1000_NUM_MTA_REGISTERS; + for(i = 0; i < num_mta_entry; i++) { + E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + } + + /* Add the new addresses */ + for(i = 0; i < mc_addr_count; i++) { + DEBUGOUT(" Adding the multicast addresses:\n"); + DEBUGOUT7(" MC Addr #%d =%.2X %.2X %.2X %.2X %.2X %.2X\n", i, + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad)], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 1], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 2], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 3], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 4], + mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 5]); + + hash_value = em_hash_mc_addr(hw, + mc_addr_list + + (i * (ETH_LENGTH_OF_ADDRESS + pad))); + + DEBUGOUT1(" Hash value = 0x%03X\n", hash_value); + + /* Place this multicast address in the RAR if there is room, * + * else put it in the MTA + */ + if (rar_used_count < num_rar_entry) { + em_rar_set(hw, + mc_addr_list + (i * (ETH_LENGTH_OF_ADDRESS + pad)), + rar_used_count); + rar_used_count++; + } else { + em_mta_set(hw, hash_value); + } + } + DEBUGOUT("MC Update Complete\n"); +} + +/****************************************************************************** + * Hashes an address to determine its location in the multicast table + * + * hw - Struct containing variables accessed by shared code + * mc_addr - the multicast address to hash + *****************************************************************************/ +uint32_t +em_hash_mc_addr(struct em_hw *hw, + uint8_t *mc_addr) +{ + uint32_t hash_value = 0; + + /* The portion of the address that is used for the hash table is + * determined by the mc_filter_type setting. + */ + switch (hw->mc_filter_type) { + /* [0] [1] [2] [3] [4] [5] + * 01 AA 00 12 34 56 + * LSB MSB + */ + case 0: + /* [47:36] i.e. 0x563 for above example address */ + hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4)); + break; + case 1: + /* [46:35] i.e. 0xAC6 for above example address */ + hash_value = ((mc_addr[4] >> 3) | (((uint16_t) mc_addr[5]) << 5)); + break; + case 2: + /* [45:34] i.e. 0x5D8 for above example address */ + hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6)); + break; + case 3: + /* [43:32] i.e. 0x634 for above example address */ + hash_value = ((mc_addr[4]) | (((uint16_t) mc_addr[5]) << 8)); + break; + } + + hash_value &= 0xFFF; + + return hash_value; +} + +/****************************************************************************** + * Sets the bit in the multicast table corresponding to the hash value. + * + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + *****************************************************************************/ +void +em_mta_set(struct em_hw *hw, + uint32_t hash_value) +{ + uint32_t hash_bit, hash_reg; + uint32_t mta; + uint32_t temp; + + /* The MTA is a register array of 128 32-bit registers. + * It is treated like an array of 4096 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 register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 5) & 0x7F; + hash_bit = hash_value & 0x1F; + + mta = E1000_READ_REG_ARRAY(hw, MTA, hash_reg); + + mta |= (1 << hash_bit); + + /* 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 == em_82544) && ((hash_reg & 0x1) == 1)) { + temp = E1000_READ_REG_ARRAY(hw, MTA, (hash_reg - 1)); + E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); + E1000_WRITE_REG_ARRAY(hw, MTA, (hash_reg - 1), temp); + } else { + E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); + } +} + +/****************************************************************************** + * Puts an ethernet address into a receive address register. + * + * hw - Struct containing variables accessed by shared code + * addr - Address to put into receive address register + * index - Receive address register to write + *****************************************************************************/ +void +em_rar_set(struct em_hw *hw, + uint8_t *addr, + uint32_t index) +{ + uint32_t rar_low, rar_high; + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((uint32_t) addr[0] | + ((uint32_t) addr[1] << 8) | + ((uint32_t) addr[2] << 16) | ((uint32_t) addr[3] << 24)); + + rar_high = ((uint32_t) addr[4] | ((uint32_t) addr[5] << 8) | E1000_RAH_AV); + + E1000_WRITE_REG_ARRAY(hw, RA, (index << 1), rar_low); + E1000_WRITE_REG_ARRAY(hw, RA, ((index << 1) + 1), rar_high); +} + +/****************************************************************************** + * Writes a value to the specified offset in the VLAN filter table. + * + * hw - Struct containing variables accessed by shared code + * offset - Offset in VLAN filer table to write + * value - Value to write into VLAN filter table + *****************************************************************************/ +void +em_write_vfta(struct em_hw *hw, + uint32_t offset, + uint32_t value) +{ + uint32_t temp; + + if((hw->mac_type == em_82544) && ((offset & 0x1) == 1)) { + temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1)); + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + E1000_WRITE_REG_ARRAY(hw, VFTA, (offset - 1), temp); + } else { + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + } +} + +/****************************************************************************** + * Clears the VLAN filer table + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void +em_clear_vfta(struct em_hw *hw) +{ + uint32_t offset; + uint32_t vfta_value = 0; + uint32_t vfta_offset = 0; + uint32_t vfta_bit_in_reg = 0; + + if (hw->mac_type == em_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, VFTA, offset, vfta_value); + } +} + +int32_t +em_id_led_init(struct em_hw * hw) +{ + uint32_t ledctl; + const uint32_t ledctl_mask = 0x000000FF; + const uint32_t ledctl_on = E1000_LEDCTL_MODE_LED_ON; + const uint32_t ledctl_off = E1000_LEDCTL_MODE_LED_OFF; + uint16_t eeprom_data, i, temp; + const uint16_t led_mask = 0x0F; + + DEBUGFUNC("em_id_led_init"); + + if(hw->mac_type < em_82540) { + /* Nothing to do */ + return E1000_SUCCESS; + } + + ledctl = E1000_READ_REG(hw, LEDCTL); + hw->ledctl_default = ledctl; + hw->ledctl_mode1 = hw->ledctl_default; + hw->ledctl_mode2 = hw->ledctl_default; + + if(em_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + if((eeprom_data== ID_LED_RESERVED_0000) || + (eeprom_data == ID_LED_RESERVED_FFFF)) eeprom_data = ID_LED_DEFAULT; + for(i = 0; i < 4; i++) { + temp = (eeprom_data >> (i << 2)) & led_mask; + switch(temp) { + case ID_LED_ON1_DEF2: + case ID_LED_ON1_ON2: + case ID_LED_ON1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode1 |= ledctl_on << (i << 3); + break; + case ID_LED_OFF1_DEF2: + case ID_LED_OFF1_ON2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->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: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_on << (i << 3); + break; + case ID_LED_DEF1_OFF2: + case ID_LED_ON1_OFF2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Prepares SW controlable LED for use and saves the current state of the LED. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_setup_led(struct em_hw *hw) +{ + uint32_t ledctl; + int32_t ret_val = E1000_SUCCESS; + + DEBUGFUNC("em_setup_led"); + + switch(hw->mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + case em_82543: + case em_82544: + /* No setup necessary */ + break; + case em_82541: + case em_82547: + case em_82541_rev_2: + case em_82547_rev_2: + /* Turn off PHY Smart Power Down (if enabled) */ + ret_val = em_read_phy_reg(hw, IGP01E1000_GMII_FIFO, + &hw->phy_spd_default); + if(ret_val) + return ret_val; + ret_val = em_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + (uint16_t)(hw->phy_spd_default & + ~IGP01E1000_GMII_SPD)); + if(ret_val) + return ret_val; + /* Fall Through */ + default: + if(hw->media_type == em_media_type_fiber) { + ledctl = E1000_READ_REG(hw, LEDCTL); + /* Save current LEDCTL settings */ + hw->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, LEDCTL, ledctl); + } else if(hw->media_type == em_media_type_copper) + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); + break; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Restores the saved state of the SW controlable LED. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_cleanup_led(struct em_hw *hw) +{ + int32_t ret_val = E1000_SUCCESS; + + DEBUGFUNC("em_cleanup_led"); + + switch(hw->mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + case em_82543: + case em_82544: + /* No cleanup necessary */ + break; + case em_82541: + case em_82547: + case em_82541_rev_2: + case em_82547_rev_2: + /* Turn on PHY Smart Power Down (if previously enabled) */ + ret_val = em_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + hw->phy_spd_default); + if(ret_val) + return ret_val; + /* Fall Through */ + default: + /* Restore LEDCTL settings */ + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default); + break; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Turns on the software controllable LED + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_led_on(struct em_hw *hw) +{ + uint32_t ctrl = E1000_READ_REG(hw, CTRL); + + DEBUGFUNC("em_led_on"); + + switch(hw->mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + case em_82543: + /* Set SW Defineable Pin 0 to turn on the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + break; + case em_82544: + if(hw->media_type == em_media_type_fiber) { + /* Set SW Defineable Pin 0 to turn on the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + /* Clear SW Defineable Pin 0 to turn on the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + break; + default: + if(hw->media_type == em_media_type_fiber) { + /* Clear SW Defineable Pin 0 to turn on the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else if(hw->media_type == em_media_type_copper) { + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2); + return E1000_SUCCESS; + } + break; + } + + E1000_WRITE_REG(hw, CTRL, ctrl); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Turns off the software controllable LED + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +em_led_off(struct em_hw *hw) +{ + uint32_t ctrl = E1000_READ_REG(hw, CTRL); + + DEBUGFUNC("em_led_off"); + + switch(hw->mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + case em_82543: + /* Clear SW Defineable Pin 0 to turn off the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + break; + case em_82544: + if(hw->media_type == em_media_type_fiber) { + /* Clear SW Defineable Pin 0 to turn off the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + /* Set SW Defineable Pin 0 to turn off the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + break; + default: + if(hw->media_type == em_media_type_fiber) { + /* Set SW Defineable Pin 0 to turn off the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else if(hw->media_type == em_media_type_copper) { + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); + return E1000_SUCCESS; + } + break; + } + + E1000_WRITE_REG(hw, CTRL, ctrl); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Clears all hardware statistics counters. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void +em_clear_hw_cntrs(struct em_hw *hw) +{ + volatile uint32_t temp; + + temp = E1000_READ_REG(hw, CRCERRS); + temp = E1000_READ_REG(hw, SYMERRS); + temp = E1000_READ_REG(hw, MPC); + temp = E1000_READ_REG(hw, SCC); + temp = E1000_READ_REG(hw, ECOL); + temp = E1000_READ_REG(hw, MCC); + temp = E1000_READ_REG(hw, LATECOL); + temp = E1000_READ_REG(hw, COLC); + temp = E1000_READ_REG(hw, DC); + temp = E1000_READ_REG(hw, SEC); + temp = E1000_READ_REG(hw, RLEC); + temp = E1000_READ_REG(hw, XONRXC); + temp = E1000_READ_REG(hw, XONTXC); + temp = E1000_READ_REG(hw, XOFFRXC); + temp = E1000_READ_REG(hw, XOFFTXC); + temp = E1000_READ_REG(hw, FCRUC); + temp = E1000_READ_REG(hw, PRC64); + temp = E1000_READ_REG(hw, PRC127); + temp = E1000_READ_REG(hw, PRC255); + temp = E1000_READ_REG(hw, PRC511); + temp = E1000_READ_REG(hw, PRC1023); + temp = E1000_READ_REG(hw, PRC1522); + temp = E1000_READ_REG(hw, GPRC); + temp = E1000_READ_REG(hw, BPRC); + temp = E1000_READ_REG(hw, MPRC); + temp = E1000_READ_REG(hw, GPTC); + temp = E1000_READ_REG(hw, GORCL); + temp = E1000_READ_REG(hw, GORCH); + temp = E1000_READ_REG(hw, GOTCL); + temp = E1000_READ_REG(hw, GOTCH); + temp = E1000_READ_REG(hw, RNBC); + temp = E1000_READ_REG(hw, RUC); + temp = E1000_READ_REG(hw, RFC); + temp = E1000_READ_REG(hw, ROC); + temp = E1000_READ_REG(hw, RJC); + temp = E1000_READ_REG(hw, TORL); + temp = E1000_READ_REG(hw, TORH); + temp = E1000_READ_REG(hw, TOTL); + temp = E1000_READ_REG(hw, TOTH); + temp = E1000_READ_REG(hw, TPR); + temp = E1000_READ_REG(hw, TPT); + temp = E1000_READ_REG(hw, PTC64); + temp = E1000_READ_REG(hw, PTC127); + temp = E1000_READ_REG(hw, PTC255); + temp = E1000_READ_REG(hw, PTC511); + temp = E1000_READ_REG(hw, PTC1023); + temp = E1000_READ_REG(hw, PTC1522); + temp = E1000_READ_REG(hw, MPTC); + temp = E1000_READ_REG(hw, BPTC); + + if(hw->mac_type < em_82543) return; + + temp = E1000_READ_REG(hw, ALGNERRC); + temp = E1000_READ_REG(hw, RXERRC); + temp = E1000_READ_REG(hw, TNCRS); + temp = E1000_READ_REG(hw, CEXTERR); + temp = E1000_READ_REG(hw, TSCTC); + temp = E1000_READ_REG(hw, TSCTFC); + + if(hw->mac_type <= em_82544) return; + + temp = E1000_READ_REG(hw, MGTPRC); + temp = E1000_READ_REG(hw, MGTPDC); + temp = E1000_READ_REG(hw, MGTPTC); + + if(hw->mac_type <= em_82547_rev_2) return; + + temp = E1000_READ_REG(hw, IAC); + temp = E1000_READ_REG(hw, ICRXOC); + temp = E1000_READ_REG(hw, ICRXPTC); + temp = E1000_READ_REG(hw, ICRXATC); + temp = E1000_READ_REG(hw, ICTXPTC); + temp = E1000_READ_REG(hw, ICTXATC); + temp = E1000_READ_REG(hw, ICTXQEC); + temp = E1000_READ_REG(hw, ICTXQMTC); + temp = E1000_READ_REG(hw, ICRXDMTC); + +} + +/****************************************************************************** + * Resets Adaptive IFS to its default state. + * + * hw - Struct containing variables accessed by shared code + * + * Call this after em_init_hw. You may override the IFS defaults by setting + * hw->ifs_params_forced to TRUE. However, you must initialize hw-> + * current_ifs_val, ifs_min_val, ifs_max_val, ifs_step_size, and ifs_ratio + * before calling this function. + *****************************************************************************/ +void +em_reset_adaptive(struct em_hw *hw) +{ + DEBUGFUNC("em_reset_adaptive"); + + if(hw->adaptive_ifs) { + if(!hw->ifs_params_forced) { + hw->current_ifs_val = 0; + hw->ifs_min_val = IFS_MIN; + hw->ifs_max_val = IFS_MAX; + hw->ifs_step_size = IFS_STEP; + hw->ifs_ratio = IFS_RATIO; + } + hw->in_ifs_mode = FALSE; + E1000_WRITE_REG(hw, AIT, 0); + } else { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + } +} + +/****************************************************************************** + * Called during the callback/watchdog routine to update IFS value based on + * the ratio of transmits to collisions. + * + * hw - Struct containing variables accessed by shared code + * tx_packets - Number of transmits since last callback + * total_collisions - Number of collisions since last callback + *****************************************************************************/ +void +em_update_adaptive(struct em_hw *hw) +{ + DEBUGFUNC("em_update_adaptive"); + + if(hw->adaptive_ifs) { + if((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) { + if(hw->tx_packet_delta > MIN_NUM_XMITS) { + hw->in_ifs_mode = TRUE; + if(hw->current_ifs_val < hw->ifs_max_val) { + if(hw->current_ifs_val == 0) + hw->current_ifs_val = hw->ifs_min_val; + else + hw->current_ifs_val += hw->ifs_step_size; + E1000_WRITE_REG(hw, AIT, hw->current_ifs_val); + } + } + } else { + if(hw->in_ifs_mode && (hw->tx_packet_delta <= MIN_NUM_XMITS)) { + hw->current_ifs_val = 0; + hw->in_ifs_mode = FALSE; + E1000_WRITE_REG(hw, AIT, 0); + } + } + } else { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + } +} + +/****************************************************************************** + * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT + * + * hw - Struct containing variables accessed by shared code + * frame_len - The length of the frame in question + * mac_addr - The Ethernet destination address of the frame in question + *****************************************************************************/ +void +em_tbi_adjust_stats(struct em_hw *hw, + struct em_hw_stats *stats, + uint32_t frame_len, + uint8_t *mac_addr) +{ + uint64_t carry_bit; + + /* 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] == (uint8_t) 0xff) && (mac_addr[1] == (uint8_t) 0xff)) + /* Broadcast packet */ + stats->bprc++; + else if(*mac_addr & 0x01) + /* Multicast packet */ + stats->mprc++; + + if(frame_len == hw->max_frame_size) { + /* In this case, the hardware has overcounted the number of + * oversize frames. + */ + if(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++; + } +} + +/****************************************************************************** + * Gets the current PCI bus type, speed, and width of the hardware + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void +em_get_bus_info(struct em_hw *hw) +{ + uint32_t status; + + switch (hw->mac_type) { + case em_82542_rev2_0: + case em_82542_rev2_1: + hw->bus_type = em_bus_type_unknown; + hw->bus_speed = em_bus_speed_unknown; + hw->bus_width = em_bus_width_unknown; + break; + case em_82573: + hw->bus_type = em_bus_type_pci_express; + hw->bus_speed = em_bus_speed_2500; + hw->bus_width = em_bus_width_pciex_4; + break; + default: + status = E1000_READ_REG(hw, STATUS); + hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? + em_bus_type_pcix : em_bus_type_pci; + + if(hw->device_id == E1000_DEV_ID_82546EB_QUAD_COPPER) { + hw->bus_speed = (hw->bus_type == em_bus_type_pci) ? + em_bus_speed_66 : em_bus_speed_120; + } else if(hw->bus_type == em_bus_type_pci) { + hw->bus_speed = (status & E1000_STATUS_PCI66) ? + em_bus_speed_66 : em_bus_speed_33; + } else { + switch (status & E1000_STATUS_PCIX_SPEED) { + case E1000_STATUS_PCIX_SPEED_66: + hw->bus_speed = em_bus_speed_66; + break; + case E1000_STATUS_PCIX_SPEED_100: + hw->bus_speed = em_bus_speed_100; + break; + case E1000_STATUS_PCIX_SPEED_133: + hw->bus_speed = em_bus_speed_133; + break; + default: + hw->bus_speed = em_bus_speed_reserved; + break; + } + } + hw->bus_width = (status & E1000_STATUS_BUS64) ? + em_bus_width_64 : em_bus_width_32; + break; + } +} +/****************************************************************************** + * Reads a value from one of the devices registers using port I/O (as opposed + * memory mapped I/O). Only 82544 and newer devices support port I/O. + * + * hw - Struct containing variables accessed by shared code + * offset - offset to read from + *****************************************************************************/ +uint32_t +em_read_reg_io(struct em_hw *hw, + uint32_t offset) +{ + unsigned long io_addr = hw->io_base; + unsigned long io_data = hw->io_base + 4; + + em_io_write(hw, io_addr, offset); + return em_io_read(hw, io_data); +} + +/****************************************************************************** + * Writes a value to one of the devices registers using port I/O (as opposed to + * memory mapped I/O). Only 82544 and newer devices support port I/O. + * + * hw - Struct containing variables accessed by shared code + * offset - offset to write to + * value - value to write + *****************************************************************************/ +void +em_write_reg_io(struct em_hw *hw, + uint32_t offset, + uint32_t value) +{ + unsigned long io_addr = hw->io_base; + unsigned long io_data = hw->io_base + 4; + + em_io_write(hw, io_addr, offset); + em_io_write(hw, io_data, value); +} + + +/****************************************************************************** + * Estimates the cable length. + * + * hw - Struct containing variables accessed by shared code + * min_length - The estimated minimum length + * max_length - The estimated maximum length + * + * returns: - E1000_ERR_XXX + * E1000_SUCCESS + * + * This function always returns a ranged length (minimum & maximum). + * So for M88 phy's, this function interprets the one value returned from the + * register to the minimum and maximum range. + * For IGP phy's, the function calculates the range by the AGC registers. + *****************************************************************************/ +int32_t +em_get_cable_length(struct em_hw *hw, + uint16_t *min_length, + uint16_t *max_length) +{ + int32_t ret_val; + uint16_t agc_value = 0; + uint16_t cur_agc, min_agc = IGP01E1000_AGC_LENGTH_TABLE_SIZE; + uint16_t i, phy_data; + uint16_t cable_length; + + DEBUGFUNC("em_get_cable_length"); + + *min_length = *max_length = 0; + + /* Use old method for Phy older than IGP */ + if(hw->phy_type == em_phy_m88) { + + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) + return ret_val; + cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT; + + /* Convert the enum value to ranged values */ + switch (cable_length) { + case em_cable_length_50: + *min_length = 0; + *max_length = em_igp_cable_length_50; + break; + case em_cable_length_50_80: + *min_length = em_igp_cable_length_50; + *max_length = em_igp_cable_length_80; + break; + case em_cable_length_80_110: + *min_length = em_igp_cable_length_80; + *max_length = em_igp_cable_length_110; + break; + case em_cable_length_110_140: + *min_length = em_igp_cable_length_110; + *max_length = em_igp_cable_length_140; + break; + case em_cable_length_140: + *min_length = em_igp_cable_length_140; + *max_length = em_igp_cable_length_170; + break; + default: + return -E1000_ERR_PHY; + break; + } + } else if(hw->phy_type == em_phy_igp) { /* For IGP PHY */ + uint16_t agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = + {IGP01E1000_PHY_AGC_A, + IGP01E1000_PHY_AGC_B, + IGP01E1000_PHY_AGC_C, + IGP01E1000_PHY_AGC_D}; + /* Read the AGC registers for all channels */ + for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + + ret_val = em_read_phy_reg(hw, agc_reg_array[i], &phy_data); + if(ret_val) + return ret_val; + + cur_agc = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT; + + /* Array bound check. */ + if((cur_agc >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) || + (cur_agc == 0)) + return -E1000_ERR_PHY; + + agc_value += cur_agc; + + /* Update minimal AGC value. */ + if(min_agc > cur_agc) + min_agc = cur_agc; + } + + /* Remove the minimal AGC result for length < 50m */ + if(agc_value < IGP01E1000_PHY_CHANNEL_NUM * em_igp_cable_length_50) { + agc_value -= min_agc; + + /* Get the average length of the remaining 3 channels */ + agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1); + } else { + /* Get the average length of all the 4 channels. */ + agc_value /= IGP01E1000_PHY_CHANNEL_NUM; + } + + /* Set the range of the calculated length. */ + *min_length = ((em_igp_cable_length_table[agc_value] - + IGP01E1000_AGC_RANGE) > 0) ? + (em_igp_cable_length_table[agc_value] - + IGP01E1000_AGC_RANGE) : 0; + *max_length = em_igp_cable_length_table[agc_value] + + IGP01E1000_AGC_RANGE; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Check the cable polarity + * + * hw - Struct containing variables accessed by shared code + * polarity - output parameter : 0 - Polarity is not reversed + * 1 - Polarity is reversed. + * + * returns: - E1000_ERR_XXX + * E1000_SUCCESS + * + * For phy's older then IGP, this function simply reads the polarity bit in the + * Phy Status register. For IGP phy's, this bit is valid only if link speed is + * 10 Mbps. If the link speed is 100 Mbps there is no polarity so this bit will + * return 0. If the link speed is 1000 Mbps the polarity status is in the + * IGP01E1000_PHY_PCS_INIT_REG. + *****************************************************************************/ +int32_t +em_check_polarity(struct em_hw *hw, + uint16_t *polarity) +{ + int32_t ret_val; + uint16_t phy_data; +#ifdef __rtems__ + *polarity = 0; /* keep compiler happy */ +#endif + + DEBUGFUNC("em_check_polarity"); + + if(hw->phy_type == em_phy_m88) { + /* return the Polarity bit in the Status register. */ + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) + return ret_val; + *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >> + M88E1000_PSSR_REV_POLARITY_SHIFT; + } else if(hw->phy_type == em_phy_igp || + hw->phy_type == em_phy_igp_2) { + /* Read the Status register to check the speed */ + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, + &phy_data); + if(ret_val) + return ret_val; + + /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to + * find the polarity status */ + if((phy_data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + + /* Read the GIG initialization PCS register (0x00B4) */ + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG, + &phy_data); + if(ret_val) + return ret_val; + + /* Check the polarity bits */ + *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ? 1 : 0; + } else { + /* For 10 Mbps, read the polarity bit in the status register. (for + * 100 Mbps this bit is always 0) */ + *polarity = phy_data & IGP01E1000_PSSR_POLARITY_REVERSED; + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Check if Downshift occured + * + * hw - Struct containing variables accessed by shared code + * downshift - output parameter : 0 - No Downshift ocured. + * 1 - Downshift ocured. + * + * returns: - E1000_ERR_XXX + * E1000_SUCCESS + * + * For phy's older then IGP, this function reads the Downshift bit in the Phy + * Specific Status register. For IGP phy's, it reads the Downgrade bit in the + * Link Health register. In IGP this bit is latched high, so the driver must + * read it immediately after link is established. + *****************************************************************************/ +int32_t +em_check_downshift(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("em_check_downshift"); + + if(hw->phy_type == em_phy_igp || + hw->phy_type == em_phy_igp_2) { + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, + &phy_data); + if(ret_val) + return ret_val; + + hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0; + } else if(hw->phy_type == em_phy_m88) { + ret_val = em_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) + return ret_val; + + hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> + M88E1000_PSSR_DOWNSHIFT_SHIFT; + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a + * gigabit link is achieved to improve link quality. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +int32_t +em_config_dsp_after_link_change(struct em_hw *hw, + boolean_t link_up) +{ + int32_t ret_val; + uint16_t phy_data, phy_saved_data, speed, duplex, i; + uint16_t 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}; + uint16_t min_length, max_length; + + DEBUGFUNC("em_config_dsp_after_link_change"); + + if(hw->phy_type != em_phy_igp) + return E1000_SUCCESS; + + if(link_up) { + ret_val = em_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) { + + em_get_cable_length(hw, &min_length, &max_length); + + if((hw->dsp_config_state == em_dsp_config_enabled) && + min_length >= em_igp_cable_length_50) { + + for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = em_read_phy_reg(hw, dsp_reg_array[i], + &phy_data); + if(ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; + + ret_val = em_write_phy_reg(hw, dsp_reg_array[i], + phy_data); + if(ret_val) + return ret_val; + } + hw->dsp_config_state = em_dsp_config_activated; + } + + if((hw->ffe_config_state == em_ffe_config_enabled) && + (min_length < em_igp_cable_length_50)) { + + uint16_t ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20; + uint32_t idle_errs = 0; + + /* clear previous idle error counts */ + ret_val = em_read_phy_reg(hw, PHY_1000T_STATUS, + &phy_data); + if(ret_val) + return ret_val; + + for(i = 0; i < ffe_idle_err_timeout; i++) { + usec_delay(1000); + ret_val = em_read_phy_reg(hw, PHY_1000T_STATUS, + &phy_data); + if(ret_val) + return ret_val; + + idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT); + if(idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) { + hw->ffe_config_state = em_ffe_config_active; + + ret_val = em_write_phy_reg(hw, + IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_CM_CP); + if(ret_val) + return ret_val; + break; + } + + if(idle_errs) + ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_100; + } + } + } + } else { + if(hw->dsp_config_state == em_dsp_config_activated) { + /* Save off the current value of register 0x2F5B to be restored at + * the end of the routines. */ + ret_val = em_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + if(ret_val) + return ret_val; + + /* Disable the PHY transmitter */ + ret_val = em_write_phy_reg(hw, 0x2F5B, 0x0003); + + if(ret_val) + return ret_val; + + msec_delay_irq(20); + + ret_val = em_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if(ret_val) + return ret_val; + for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = em_read_phy_reg(hw, dsp_reg_array[i], &phy_data); + if(ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; + phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS; + + ret_val = em_write_phy_reg(hw,dsp_reg_array[i], phy_data); + if(ret_val) + return ret_val; + } + + ret_val = em_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if(ret_val) + return ret_val; + + msec_delay_irq(20); + + /* Now enable the transmitter */ + ret_val = em_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if(ret_val) + return ret_val; + + hw->dsp_config_state = em_dsp_config_enabled; + } + + if(hw->ffe_config_state == em_ffe_config_active) { + /* Save off the current value of register 0x2F5B to be restored at + * the end of the routines. */ + ret_val = em_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + if(ret_val) + return ret_val; + + /* Disable the PHY transmitter */ + ret_val = em_write_phy_reg(hw, 0x2F5B, 0x0003); + + if(ret_val) + return ret_val; + + msec_delay_irq(20); + + ret_val = em_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if(ret_val) + return ret_val; + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_DEFAULT); + if(ret_val) + return ret_val; + + ret_val = em_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if(ret_val) + return ret_val; + + msec_delay_irq(20); + + /* Now enable the transmitter */ + ret_val = em_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if(ret_val) + return ret_val; + + hw->ffe_config_state = em_ffe_config_enabled; + } + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * Set PHY to class A mode + * 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. + * + * hw - Struct containing variables accessed by shared code + ****************************************************************************/ +static int32_t +em_set_phy_mode(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC("em_set_phy_mode"); + + if((hw->mac_type == em_82545_rev_3) && + (hw->media_type == em_media_type_copper)) { + ret_val = em_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, &eeprom_data); + if(ret_val) { + return ret_val; + } + + if((eeprom_data != EEPROM_RESERVED_WORD) && + (eeprom_data & EEPROM_PHY_CLASS_A)) { + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x000B); + if(ret_val) + return ret_val; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x8104); + if(ret_val) + return ret_val; + + hw->phy_reset_disable = FALSE; + } + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +int32_t +em_set_d3_lplu_state(struct em_hw *hw, + boolean_t active) +{ + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC("em_set_d3_lplu_state"); + + if(hw->phy_type != em_phy_igp && hw->phy_type != em_phy_igp_2) + return E1000_SUCCESS; + + /* During driver activity LPLU should not be used or it will attain link + * from the lowest speeds starting from 10Mbps. The capability is used for + * Dx transitions and states */ + if(hw->mac_type == em_82541_rev_2 || hw->mac_type == em_82547_rev_2) { + ret_val = em_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data); + if(ret_val) + return ret_val; + } else { + ret_val = em_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); + if(ret_val) + return ret_val; + } + + if(!active) { + if(hw->mac_type == em_82541_rev_2 || + hw->mac_type == em_82547_rev_2) { + phy_data &= ~IGP01E1000_GMII_FLEX_SPD; + ret_val = em_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); + if(ret_val) + return ret_val; + } else { + phy_data &= ~IGP02E1000_PM_D3_LPLU; + ret_val = em_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + phy_data); + if (ret_val) + return ret_val; + } + + /* 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 (hw->smart_speed == em_smart_speed_on) { + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if(ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if(ret_val) + return ret_val; + } else if (hw->smart_speed == em_smart_speed_off) { + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if(ret_val) + return ret_val; + } + + } else if((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL ) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { + + if(hw->mac_type == em_82541_rev_2 || + hw->mac_type == em_82547_rev_2) { + phy_data |= IGP01E1000_GMII_FLEX_SPD; + ret_val = em_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); + if(ret_val) + return ret_val; + } else { + phy_data |= IGP02E1000_PM_D3_LPLU; + ret_val = em_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + phy_data); + if (ret_val) + return ret_val; + } + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if(ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); + if(ret_val) + return ret_val; + + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu d0 state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +int32_t +em_set_d0_lplu_state(struct em_hw *hw, + boolean_t active) +{ + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC("em_set_d0_lplu_state"); + + if(hw->mac_type <= em_82547_rev_2) + return E1000_SUCCESS; + + ret_val = em_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); + if(ret_val) + return ret_val; + + if (!active) { + phy_data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = em_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + + /* 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 (hw->smart_speed == em_smart_speed_on) { + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if(ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if(ret_val) + return ret_val; + } else if (hw->smart_speed == em_smart_speed_off) { + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if(ret_val) + return ret_val; + } + + + } else { + + phy_data |= IGP02E1000_PM_D0_LPLU; + ret_val = em_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = em_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if(ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = em_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); + if(ret_val) + return ret_val; + + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Change VCO speed register to improve Bit Error Rate performance of SERDES. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +em_set_vco_speed(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t default_page = 0; + uint16_t phy_data; + + DEBUGFUNC("em_set_vco_speed"); + + switch(hw->mac_type) { + case em_82545_rev_3: + case em_82546_rev_3: + break; + default: + return E1000_SUCCESS; + } + + /* Set PHY register 30, page 5, bit 8 to 0 */ + + ret_val = em_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, &default_page); + if(ret_val) + return ret_val; + + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005); + if(ret_val) + return ret_val; + + ret_val = em_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if(ret_val) + return ret_val; + + phy_data &= ~M88E1000_PHY_VCO_REG_BIT8; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if(ret_val) + return ret_val; + + /* Set PHY register 30, page 4, bit 11 to 1 */ + + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004); + if(ret_val) + return ret_val; + + ret_val = em_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if(ret_val) + return ret_val; + + phy_data |= M88E1000_PHY_VCO_REG_BIT11; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if(ret_val) + return ret_val; + + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, default_page); + if(ret_val) + return ret_val; + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function reads the cookie from ARC ram. + * + * returns: - E1000_SUCCESS . + ****************************************************************************/ +int32_t +em_host_if_read_cookie(struct em_hw * hw, uint8_t *buffer) +{ + uint8_t i; + uint32_t offset = E1000_MNG_DHCP_COOKIE_OFFSET; + uint8_t length = E1000_MNG_DHCP_COOKIE_LENGTH; + + length = (length >> 2); + offset = (offset >> 2); + + for (i = 0; i < length; i++) { + *((uint32_t *) buffer + i) = + E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset + i); + } + return E1000_SUCCESS; +} + + +/***************************************************************************** + * 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. + * + * returns: - E1000_ERR_HOST_INTERFACE_COMMAND in case if is not ready or + * timeout + * - E1000_SUCCESS for success. + ****************************************************************************/ +int32_t +em_mng_enable_host_if(struct em_hw * hw) +{ + uint32_t hicr; + uint8_t i; + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, HICR); + if ((hicr & E1000_HICR_EN) == 0) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + /* check the previous command is completed */ + for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay_irq(1); + } + + if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) { + DEBUGOUT("Previous command timeout failed .\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * 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. + * + * returns - E1000_SUCCESS for success. + ****************************************************************************/ +int32_t +em_mng_host_if_write(struct em_hw * hw, uint8_t *buffer, + uint16_t length, uint16_t offset, uint8_t *sum) +{ + uint8_t *tmp; + uint8_t *bufptr = buffer; + uint32_t data; + uint16_t remaining, i, j, prev_bytes; + + /* sum = only sum of the data and it is not checksum */ + + if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) { + return -E1000_ERR_PARAM; + } + + tmp = (uint8_t *)&data; + prev_bytes = offset & 0x3; + offset &= 0xFFFC; + offset >>= 2; + + if (prev_bytes) { + data = E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset); + for (j = prev_bytes; j < sizeof(uint32_t); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset, data); + length -= j - prev_bytes; + offset++; + } + + remaining = length & 0x3; + length -= remaining; + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant command block into the + * ram area. */ + for (i = 0; i < length; i++) { + for (j = 0; j < sizeof(uint32_t); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data); + } + if (remaining) { + for (j = 0; j < sizeof(uint32_t); j++) { + if (j < remaining) + *(tmp + j) = *bufptr++; + else + *(tmp + j) = 0; + + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data); + } + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function writes the command header after does the checksum calculation. + * + * returns - E1000_SUCCESS for success. + ****************************************************************************/ +int32_t +em_mng_write_cmd_header(struct em_hw * hw, + struct em_host_mng_command_header * hdr) +{ + uint16_t i; + uint8_t sum; + uint8_t *buffer; + + /* Write the whole command header structure which includes sum of + * the buffer */ + + uint16_t length = sizeof(struct em_host_mng_command_header); + + sum = hdr->checksum; + hdr->checksum = 0; + + buffer = (uint8_t *) hdr; + i = length; + while(i--) + sum += buffer[i]; + + hdr->checksum = 0 - sum; + + length >>= 2; + /* The device driver writes the relevant command block into the ram area. */ + for (i = 0; i < length; i++) + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, i, *((uint32_t *) hdr + i)); + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function indicates to ARC that a new command is pending which completes + * one write operation by the driver. + * + * returns - E1000_SUCCESS for success. + ****************************************************************************/ +int32_t +em_mng_write_commit( + struct em_hw * hw) +{ + uint32_t hicr; + + hicr = E1000_READ_REG(hw, HICR); + /* Setting this bit tells the ARC that a new command is pending. */ + E1000_WRITE_REG(hw, HICR, hicr | E1000_HICR_C); + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function checks the mode of the firmware. + * + * returns - TRUE when the mode is IAMT or FALSE. + ****************************************************************************/ +boolean_t +em_check_mng_mode( + struct em_hw *hw) +{ + uint32_t fwsm; + + fwsm = E1000_READ_REG(hw, FWSM); + + if((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return TRUE; + + return FALSE; +} + + +/***************************************************************************** + * This function writes the dhcp info . + ****************************************************************************/ +int32_t +em_mng_write_dhcp_info(struct em_hw * hw, uint8_t *buffer, + uint16_t length) +{ + int32_t ret_val; + struct em_host_mng_command_header hdr; + + hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD; + hdr.command_length = length; + hdr.reserved1 = 0; + hdr.reserved2 = 0; + hdr.checksum = 0; + + ret_val = em_mng_enable_host_if(hw); + if (ret_val == E1000_SUCCESS) { + ret_val = em_mng_host_if_write(hw, buffer, length, sizeof(hdr), + &(hdr.checksum)); + if (ret_val == E1000_SUCCESS) { + ret_val = em_mng_write_cmd_header(hw, &hdr); + if (ret_val == E1000_SUCCESS) + ret_val = em_mng_write_commit(hw); + } + } + return ret_val; +} + + +/***************************************************************************** + * This function calculates the checksum. + * + * returns - checksum of buffer contents. + ****************************************************************************/ +uint8_t +em_calculate_mng_checksum(char *buffer, uint32_t length) +{ + uint8_t sum = 0; + uint32_t i; + + if (!buffer) + return 0; + + for (i=0; i < length; i++) + sum += buffer[i]; + + return (uint8_t) (0 - sum); +} + +/***************************************************************************** + * This function checks whether tx pkt filtering needs to be enabled or not. + * + * returns - TRUE for packet filtering or FALSE. + ****************************************************************************/ +boolean_t +em_enable_tx_pkt_filtering(struct em_hw *hw) +{ + /* called in init as well as watchdog timer functions */ + + int32_t ret_val, checksum; + boolean_t tx_filter = FALSE; + struct em_host_mng_dhcp_cookie *hdr = &(hw->mng_cookie); + uint8_t *buffer = (uint8_t *) &(hw->mng_cookie); + + if (em_check_mng_mode(hw)) { + ret_val = em_mng_enable_host_if(hw); + if (ret_val == E1000_SUCCESS) { + ret_val = em_host_if_read_cookie(hw, buffer); + if (ret_val == E1000_SUCCESS) { + checksum = hdr->checksum; + hdr->checksum = 0; + if ((hdr->signature == E1000_IAMT_SIGNATURE) && + checksum == em_calculate_mng_checksum((char *)buffer, + E1000_MNG_DHCP_COOKIE_LENGTH)) { + if (hdr->status & + E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT) + tx_filter = TRUE; + } else + tx_filter = TRUE; + } else + tx_filter = TRUE; + } + } + + hw->tx_pkt_filtering = tx_filter; + return tx_filter; +} + +/****************************************************************************** + * Verifies the hardware needs to allow ARPs to be processed by the host + * + * hw - Struct containing variables accessed by shared code + * + * returns: - TRUE/FALSE + * + *****************************************************************************/ +uint32_t +em_enable_mng_pass_thru(struct em_hw *hw) +{ + uint32_t manc; + uint32_t fwsm, factps; + + if (hw->asf_firmware_present) { + manc = E1000_READ_REG(hw, MANC); + + if (!(manc & E1000_MANC_RCV_TCO_EN) || + !(manc & E1000_MANC_EN_MAC_ADDR_FILTER)) + return FALSE; + if (em_arc_subsystem_valid(hw) == TRUE) { + fwsm = E1000_READ_REG(hw, FWSM); + factps = E1000_READ_REG(hw, FACTPS); + + if (((fwsm & E1000_FWSM_MODE_MASK) == + (em_mng_mode_pt << E1000_FWSM_MODE_SHIFT)) && + (factps & E1000_FACTPS_MNGCG)) + return TRUE; + } else + if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN)) + return TRUE; + } + return FALSE; +} + +static int32_t +em_polarity_reversal_workaround(struct em_hw *hw) +{ + int32_t ret_val; + uint16_t mii_status_reg; + uint16_t i; + + /* Polarity reversal workaround for forced 10F/10H links. */ + + /* Disable the transmitter on the PHY */ + + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if(ret_val) + return ret_val; + ret_val = em_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFFF); + if(ret_val) + return ret_val; + + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if(ret_val) + return ret_val; + + /* This loop will early-out if the NO link condition has been met. */ + for(i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Link Status bit + * to be clear. + */ + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + 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 th transmitter on the PHY */ + + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if(ret_val) + return ret_val; + msec_delay_irq(50); + ret_val = em_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0); + if(ret_val) + return ret_val; + msec_delay_irq(50); + ret_val = em_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00); + if(ret_val) + return ret_val; + msec_delay_irq(50); + ret_val = em_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000); + if(ret_val) + return ret_val; + + ret_val = em_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if(ret_val) + return ret_val; + + /* This loop will early-out if the link condition has been met. */ + for(i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Link Status bit + * to be set. + */ + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + ret_val = em_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) + return ret_val; + + if(mii_status_reg & MII_SR_LINK_STATUS) break; + msec_delay_irq(100); + } + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Disables PCI-Express master access. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - none. + * + ***************************************************************************/ +void +em_set_pci_express_master_disable(struct em_hw *hw) +{ + uint32_t ctrl; + + DEBUGFUNC("em_set_pci_express_master_disable"); + + if (hw->bus_type != em_bus_type_pci_express) + return; + + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; + E1000_WRITE_REG(hw, CTRL, ctrl); +} + +/*************************************************************************** + * + * Enables PCI-Express master access. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - none. + * + ***************************************************************************/ +void +em_enable_pciex_master(struct em_hw *hw) +{ + uint32_t ctrl; + + DEBUGFUNC("em_enable_pciex_master"); + + if (hw->bus_type != em_bus_type_pci_express) + return; + + ctrl = E1000_READ_REG(hw, CTRL); + ctrl &= ~E1000_CTRL_GIO_MASTER_DISABLE; + E1000_WRITE_REG(hw, CTRL, ctrl); +} + +/******************************************************************************* + * + * Disables PCI-Express master access and verifies there are no pending requests + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_MASTER_REQUESTS_PENDING if master disable bit hasn't + * caused the master requests to be disabled. + * E1000_SUCCESS master requests disabled. + * + ******************************************************************************/ +int32_t +em_disable_pciex_master(struct em_hw *hw) +{ + int32_t timeout = MASTER_DISABLE_TIMEOUT; /* 80ms */ + + DEBUGFUNC("em_disable_pciex_master"); + + if (hw->bus_type != em_bus_type_pci_express) + return E1000_SUCCESS; + + em_set_pci_express_master_disable(hw); + + while(timeout) { + if(!(E1000_READ_REG(hw, STATUS) & E1000_STATUS_GIO_MASTER_ENABLE)) + break; + else + usec_delay(100); + timeout--; + } + + if(!timeout) { + DEBUGOUT("Master requests are pending.\n"); + return -E1000_ERR_MASTER_REQUESTS_PENDING; + } + + return E1000_SUCCESS; +} + +/******************************************************************************* + * + * Check for EEPROM Auto Read bit done. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to reset MAC + * E1000_SUCCESS at any other case. + * + ******************************************************************************/ +int32_t +em_get_auto_rd_done(struct em_hw *hw) +{ + int32_t timeout = AUTO_READ_DONE_TIMEOUT; + + DEBUGFUNC("em_get_auto_rd_done"); + + switch (hw->mac_type) { + default: + msec_delay(5); + break; + case em_82573: + while(timeout) { + if (E1000_READ_REG(hw, EECD) & E1000_EECD_AUTO_RD) break; + else msec_delay(1); + timeout--; + } + + if(!timeout) { + DEBUGOUT("Auto read by HW from EEPROM has not completed.\n"); + return -E1000_ERR_RESET; + } + break; + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * Checks if the PHY configuration is done + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to reset MAC + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +int32_t +em_get_phy_cfg_done(struct em_hw *hw) +{ + DEBUGFUNC("em_get_phy_cfg_done"); + + /* Simply wait for 10ms */ + msec_delay(10); + + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Using the combination of SMBI and SWESMBI semaphore bits when resetting + * adapter or Eeprom access. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_EEPROM if fail to access EEPROM. + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +int32_t +em_get_hw_eeprom_semaphore(struct em_hw *hw) +{ + int32_t timeout; + uint32_t swsm; + + DEBUGFUNC("em_get_hw_eeprom_semaphore"); + + if(!hw->eeprom_semaphore_present) + return E1000_SUCCESS; + + + /* Get the FW semaphore. */ + timeout = hw->eeprom.word_size + 1; + while(timeout) { + swsm = E1000_READ_REG(hw, SWSM); + swsm |= E1000_SWSM_SWESMBI; + E1000_WRITE_REG(hw, SWSM, swsm); + /* if we managed to set the bit we got the semaphore. */ + swsm = E1000_READ_REG(hw, SWSM); + if(swsm & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + timeout--; + } + + if(!timeout) { + /* Release semaphores */ + em_put_hw_eeprom_semaphore(hw); + DEBUGOUT("Driver can't access the Eeprom - SWESMBI bit is set.\n"); + return -E1000_ERR_EEPROM; + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * This function clears HW semaphore bits. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - None. + * + ***************************************************************************/ +void +em_put_hw_eeprom_semaphore(struct em_hw *hw) +{ + uint32_t swsm; + + DEBUGFUNC("em_put_hw_eeprom_semaphore"); + + if(!hw->eeprom_semaphore_present) + return; + + swsm = E1000_READ_REG(hw, SWSM); + /* Release both semaphores. */ + swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); + E1000_WRITE_REG(hw, SWSM, swsm); +} + +/****************************************************************************** + * Checks if PHY reset is blocked due to SOL/IDER session, for example. + * Returning E1000_BLK_PHY_RESET isn't necessarily an error. But it's up to + * the caller to figure out how to deal with it. + * + * hw - Struct containing variables accessed by shared code + * + * returns: - E1000_BLK_PHY_RESET + * E1000_SUCCESS + * + *****************************************************************************/ +int32_t +em_check_phy_reset_block(struct em_hw *hw) +{ + uint32_t manc = 0; + if(hw->mac_type > em_82547_rev_2) + manc = E1000_READ_REG(hw, MANC); + return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? + E1000_BLK_PHY_RESET : E1000_SUCCESS; +} + +uint8_t +em_arc_subsystem_valid(struct em_hw *hw) +{ + uint32_t fwsm; + + /* On 8257x silicon, registers in the range of 0x8800 - 0x8FFC + * may not be provided a DMA clock when no manageability features are + * enabled. We do not want to perform any reads/writes to these registers + * if this is the case. We read FWSM to determine the manageability mode. + */ + switch (hw->mac_type) { + case em_82573: + fwsm = E1000_READ_REG(hw, FWSM); + if((fwsm & E1000_FWSM_MODE_MASK) != 0) + return TRUE; + break; + default: + break; + } + return FALSE; +} + + + diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.h new file mode 100644 index 0000000000..b043bcba67 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_hw.h @@ -0,0 +1,2678 @@ +/******************************************************************************* + + Copyright (c) 2001-2005, 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: /repoman/r/ncvs/src/sys/dev/em/if_em_hw.h,v 1.15 2005/05/26 23:32:02 tackerman Exp $*/ +/* if_em_hw.h + * Structures, enums, and macros for the MAC + */ + +#ifndef _EM_HW_H_ +#define _EM_HW_H_ + +#ifndef __rtems__ +#include +#else +#include +#endif + + +/* Forward declarations of structures used by the shared code */ +struct em_hw; +struct em_hw_stats; + +/* Enumerated types specific to the e1000 hardware */ +/* Media Access Controlers */ +typedef enum { + em_undefined = 0, + em_82542_rev2_0, + em_82542_rev2_1, + em_82543, + em_82544, + em_82540, + em_82545, + em_82545_rev_3, + em_82546, + em_82546_rev_3, + em_82541, + em_82541_rev_2, + em_82547, + em_82547_rev_2, + em_82573, + em_num_macs +} em_mac_type; + +typedef enum { + em_eeprom_uninitialized = 0, + em_eeprom_spi, + em_eeprom_microwire, + em_eeprom_flash, + em_num_eeprom_types +} em_eeprom_type; + +/* Media Types */ +typedef enum { + em_media_type_copper = 0, + em_media_type_fiber = 1, + em_media_type_internal_serdes = 2, + em_num_media_types +} em_media_type; + +typedef enum { + em_10_half = 0, + em_10_full = 1, + em_100_half = 2, + em_100_full = 3 +} em_speed_duplex_type; + +/* Flow Control Settings */ +typedef enum { + em_fc_none = 0, + em_fc_rx_pause = 1, + em_fc_tx_pause = 2, + em_fc_full = 3, + em_fc_default = 0xFF +} em_fc_type; + +/* PCI bus types */ +typedef enum { + em_bus_type_unknown = 0, + em_bus_type_pci, + em_bus_type_pcix, + em_bus_type_pci_express, + em_bus_type_reserved +} em_bus_type; + +/* PCI bus speeds */ +typedef enum { + em_bus_speed_unknown = 0, + em_bus_speed_33, + em_bus_speed_66, + em_bus_speed_100, + em_bus_speed_120, + em_bus_speed_133, + em_bus_speed_2500, + em_bus_speed_reserved +} em_bus_speed; + +/* PCI bus widths */ +typedef enum { + em_bus_width_unknown = 0, + em_bus_width_32, + em_bus_width_64, + em_bus_width_pciex_1, + em_bus_width_pciex_4, + em_bus_width_reserved +} em_bus_width; + +/* PHY status info structure and supporting enums */ +typedef enum { + em_cable_length_50 = 0, + em_cable_length_50_80, + em_cable_length_80_110, + em_cable_length_110_140, + em_cable_length_140, + em_cable_length_undefined = 0xFF +} em_cable_length; + +typedef enum { + em_igp_cable_length_10 = 10, + em_igp_cable_length_20 = 20, + em_igp_cable_length_30 = 30, + em_igp_cable_length_40 = 40, + em_igp_cable_length_50 = 50, + em_igp_cable_length_60 = 60, + em_igp_cable_length_70 = 70, + em_igp_cable_length_80 = 80, + em_igp_cable_length_90 = 90, + em_igp_cable_length_100 = 100, + em_igp_cable_length_110 = 110, + em_igp_cable_length_120 = 120, + em_igp_cable_length_130 = 130, + em_igp_cable_length_140 = 140, + em_igp_cable_length_150 = 150, + em_igp_cable_length_160 = 160, + em_igp_cable_length_170 = 170, + em_igp_cable_length_180 = 180 +} em_igp_cable_length; + +typedef enum { + em_10bt_ext_dist_enable_normal = 0, + em_10bt_ext_dist_enable_lower, + em_10bt_ext_dist_enable_undefined = 0xFF +} em_10bt_ext_dist_enable; + +typedef enum { + em_rev_polarity_normal = 0, + em_rev_polarity_reversed, + em_rev_polarity_undefined = 0xFF +} em_rev_polarity; + +typedef enum { + em_downshift_normal = 0, + em_downshift_activated, + em_downshift_undefined = 0xFF +} em_downshift; + +typedef enum { + em_smart_speed_default = 0, + em_smart_speed_on, + em_smart_speed_off +} em_smart_speed; + +typedef enum { + em_polarity_reversal_enabled = 0, + em_polarity_reversal_disabled, + em_polarity_reversal_undefined = 0xFF +} em_polarity_reversal; + +typedef enum { + em_auto_x_mode_manual_mdi = 0, + em_auto_x_mode_manual_mdix, + em_auto_x_mode_auto1, + em_auto_x_mode_auto2, + em_auto_x_mode_undefined = 0xFF +} em_auto_x_mode; + +typedef enum { + em_1000t_rx_status_not_ok = 0, + em_1000t_rx_status_ok, + em_1000t_rx_status_undefined = 0xFF +} em_1000t_rx_status; + +typedef enum { + em_phy_m88 = 0, + em_phy_igp, + em_phy_igp_2, + em_phy_undefined = 0xFF +} em_phy_type; + +typedef enum { + em_ms_hw_default = 0, + em_ms_force_master, + em_ms_force_slave, + em_ms_auto +} em_ms_type; + +typedef enum { + em_ffe_config_enabled = 0, + em_ffe_config_active, + em_ffe_config_blocked +} em_ffe_config; + +typedef enum { + em_dsp_config_disabled = 0, + em_dsp_config_enabled, + em_dsp_config_activated, + em_dsp_config_undefined = 0xFF +} em_dsp_config; + +struct em_phy_info { + em_cable_length cable_length; + em_10bt_ext_dist_enable extended_10bt_distance; + em_rev_polarity cable_polarity; + em_downshift downshift; + em_polarity_reversal polarity_correction; + em_auto_x_mode mdix_mode; + em_1000t_rx_status local_rx; + em_1000t_rx_status remote_rx; +}; + +struct em_phy_stats { + uint32_t idle_errors; + uint32_t receive_errors; +}; + +struct em_eeprom_info { + em_eeprom_type type; + uint16_t word_size; + uint16_t opcode_bits; + uint16_t address_bits; + uint16_t delay_usec; + uint16_t page_size; + boolean_t use_eerd; + boolean_t use_eewr; +}; + +/* Flex ASF Information */ +#define E1000_HOST_IF_MAX_SIZE 2048 + +typedef enum { + em_byte_align = 0, + em_word_align = 1, + em_dword_align = 2 +} em_align_type; + + + +/* Error Codes */ +#define E1000_SUCCESS 0 +#define E1000_ERR_EEPROM 1 +#define E1000_ERR_PHY 2 +#define E1000_ERR_CONFIG 3 +#define E1000_ERR_PARAM 4 +#define E1000_ERR_MAC_TYPE 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 + +/* Function prototypes */ +/* Initialization */ +int32_t em_reset_hw(struct em_hw *hw); +int32_t em_init_hw(struct em_hw *hw); +int32_t em_id_led_init(struct em_hw * hw); +int32_t em_set_mac_type(struct em_hw *hw); +void em_set_media_type(struct em_hw *hw); + +/* Link Configuration */ +int32_t em_setup_link(struct em_hw *hw); +int32_t em_phy_setup_autoneg(struct em_hw *hw); +void em_config_collision_dist(struct em_hw *hw); +int32_t em_config_fc_after_link_up(struct em_hw *hw); +int32_t em_check_for_link(struct em_hw *hw); +int32_t em_get_speed_and_duplex(struct em_hw *hw, uint16_t * speed, uint16_t * duplex); +int32_t em_wait_autoneg(struct em_hw *hw); +int32_t em_force_mac_fc(struct em_hw *hw); + +/* PHY */ +int32_t em_read_phy_reg(struct em_hw *hw, uint32_t reg_addr, uint16_t *phy_data); +int32_t em_write_phy_reg(struct em_hw *hw, uint32_t reg_addr, uint16_t data); +int32_t em_phy_hw_reset(struct em_hw *hw); +int32_t em_phy_reset(struct em_hw *hw); +int32_t em_detect_gig_phy(struct em_hw *hw); +int32_t em_phy_get_info(struct em_hw *hw, struct em_phy_info *phy_info); +int32_t em_phy_m88_get_info(struct em_hw *hw, struct em_phy_info *phy_info); +int32_t em_phy_igp_get_info(struct em_hw *hw, struct em_phy_info *phy_info); +int32_t em_get_cable_length(struct em_hw *hw, uint16_t *min_length, uint16_t *max_length); +int32_t em_check_polarity(struct em_hw *hw, uint16_t *polarity); +int32_t em_check_downshift(struct em_hw *hw); +int32_t em_validate_mdi_setting(struct em_hw *hw); + +/* EEPROM Functions */ +int32_t em_init_eeprom_params(struct em_hw *hw); +boolean_t em_is_onboard_nvm_eeprom(struct em_hw *hw); +int32_t em_read_eeprom_eerd(struct em_hw *hw, uint16_t offset, uint16_t words, uint16_t *data); +int32_t em_write_eeprom_eewr(struct em_hw *hw, uint16_t offset, uint16_t words, uint16_t *data); +int32_t em_poll_eerd_eewr_done(struct em_hw *hw, int eerd); + +/* MNG HOST IF functions */ +uint32_t em_enable_mng_pass_thru(struct em_hw *hw); + +#define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64 +#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 /* Host Interface data length */ + +#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 /* Time in ms to process MNG command */ +#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 /* Cookie offset */ +#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 /* Cookie length */ +#define E1000_MNG_IAMT_MODE 0x3 +#define E1000_IAMT_SIGNATURE 0x544D4149 /* Intel(R) Active Management Technology signature */ + +#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT 0x1 /* DHCP parsing enabled */ +#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT 0x2 /* DHCP parsing enabled */ +#define E1000_VFTA_ENTRY_SHIFT 0x5 +#define E1000_VFTA_ENTRY_MASK 0x7F +#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F + +struct em_host_mng_command_header { + uint8_t command_id; + uint8_t checksum; + uint16_t reserved1; + uint16_t reserved2; + uint16_t command_length; +}; + +struct em_host_mng_command_info { + struct em_host_mng_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ + uint8_t command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; /* Command data can length 0..0x658*/ +}; +#ifdef __BIG_ENDIAN +struct em_host_mng_dhcp_cookie{ + uint32_t signature; + uint16_t vlan_id; + uint8_t reserved0; + uint8_t status; + uint32_t reserved1; + uint8_t checksum; + uint8_t reserved3; + uint16_t reserved2; +}; +#else +struct em_host_mng_dhcp_cookie{ + uint32_t signature; + uint8_t status; + uint8_t reserved0; + uint16_t vlan_id; + uint32_t reserved1; + uint16_t reserved2; + uint8_t reserved3; + uint8_t checksum; +}; +#endif + +int32_t em_mng_write_dhcp_info(struct em_hw *hw, uint8_t *buffer, + uint16_t length); +boolean_t em_check_mng_mode(struct em_hw *hw); +boolean_t em_enable_tx_pkt_filtering(struct em_hw *hw); +int32_t em_mng_enable_host_if(struct em_hw *hw); +int32_t em_mng_host_if_write(struct em_hw *hw, uint8_t *buffer, + uint16_t length, uint16_t offset, uint8_t *sum); +int32_t em_mng_write_cmd_header(struct em_hw* hw, + struct em_host_mng_command_header* hdr); + +int32_t em_mng_write_commit(struct em_hw *hw); + +int32_t em_read_eeprom(struct em_hw *hw, uint16_t reg, uint16_t words, uint16_t *data); +int32_t em_validate_eeprom_checksum(struct em_hw *hw); +int32_t em_update_eeprom_checksum(struct em_hw *hw); +int32_t em_write_eeprom(struct em_hw *hw, uint16_t reg, uint16_t words, uint16_t *data); +int32_t em_read_part_num(struct em_hw *hw, uint32_t * part_num); +int32_t em_read_mac_addr(struct em_hw * hw); +int32_t em_swfw_sync_acquire(struct em_hw *hw, uint16_t mask); +void em_swfw_sync_release(struct em_hw *hw, uint16_t mask); + +/* Filters (multicast, vlan, receive) */ +void em_init_rx_addrs(struct em_hw *hw); +void em_mc_addr_list_update(struct em_hw *hw, uint8_t * mc_addr_list, uint32_t mc_addr_count, uint32_t pad, uint32_t rar_used_count); +uint32_t em_hash_mc_addr(struct em_hw *hw, uint8_t * mc_addr); +void em_mta_set(struct em_hw *hw, uint32_t hash_value); +void em_rar_set(struct em_hw *hw, uint8_t * mc_addr, uint32_t rar_index); +void em_write_vfta(struct em_hw *hw, uint32_t offset, uint32_t value); +void em_clear_vfta(struct em_hw *hw); + +/* LED functions */ +int32_t em_setup_led(struct em_hw *hw); +int32_t em_cleanup_led(struct em_hw *hw); +int32_t em_led_on(struct em_hw *hw); +int32_t em_led_off(struct em_hw *hw); + +/* Adaptive IFS Functions */ + +/* Everything else */ +void em_clear_hw_cntrs(struct em_hw *hw); +void em_reset_adaptive(struct em_hw *hw); +void em_update_adaptive(struct em_hw *hw); +void em_tbi_adjust_stats(struct em_hw *hw, struct em_hw_stats *stats, uint32_t frame_len, uint8_t * mac_addr); +void em_get_bus_info(struct em_hw *hw); +void em_pci_set_mwi(struct em_hw *hw); +void em_pci_clear_mwi(struct em_hw *hw); +void em_read_pci_cfg(struct em_hw *hw, uint32_t reg, uint16_t * value); +void em_write_pci_cfg(struct em_hw *hw, uint32_t reg, uint16_t * value); +/* Port I/O is only supported on 82544 and newer */ +uint32_t em_io_read(struct em_hw *hw, unsigned long port); +uint32_t em_read_reg_io(struct em_hw *hw, uint32_t offset); +void em_io_write(struct em_hw *hw, unsigned long port, uint32_t value); +void em_write_reg_io(struct em_hw *hw, uint32_t offset, uint32_t value); +int32_t em_config_dsp_after_link_change(struct em_hw *hw, boolean_t link_up); +int32_t em_set_d3_lplu_state(struct em_hw *hw, boolean_t active); +int32_t em_set_d0_lplu_state(struct em_hw *hw, boolean_t active); +void em_set_pci_express_master_disable(struct em_hw *hw); +void em_enable_pciex_master(struct em_hw *hw); +int32_t em_disable_pciex_master(struct em_hw *hw); +int32_t em_get_auto_rd_done(struct em_hw *hw); +int32_t em_get_phy_cfg_done(struct em_hw *hw); +int32_t em_get_software_semaphore(struct em_hw *hw); +void em_release_software_semaphore(struct em_hw *hw); +int32_t em_check_phy_reset_block(struct em_hw *hw); +int32_t em_get_hw_eeprom_semaphore(struct em_hw *hw); +void em_put_hw_eeprom_semaphore(struct em_hw *hw); +int32_t em_commit_shadow_ram(struct em_hw *hw); +uint8_t em_arc_subsystem_valid(struct em_hw *hw); + +#define E1000_READ_REG_IO(a, reg) \ + em_read_reg_io((a), E1000_##reg) +#define E1000_WRITE_REG_IO(a, reg, val) \ + em_write_reg_io((a), E1000_##reg, val) + +/* PCI Device IDs */ +#define E1000_DEV_ID_82542 0x1000 +#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_82541ER_LOM 0x1014 +#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_82541EI 0x1013 +#define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER 0x1078 +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#define E1000_DEV_ID_82541GI_LF 0x107C +#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_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A +#define E1000_DEV_ID_82573E 0x108B +#define E1000_DEV_ID_82573E_IAMT 0x108C + +#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 + +#define NODE_ADDRESS_SIZE 6 +#define ETH_LENGTH_OF_ADDRESS 6 + +/* MAC decode size is 128K - This is the size of BAR0 */ +#define MAC_DECODE_SIZE (128 * 1024) + +#define E1000_82542_2_0_REV_ID 2 +#define E1000_82542_2_1_REV_ID 3 +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 +#define E1000_REVISION_3 3 + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +/* The sizes (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* With FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */ +#define ETHERNET_FCS_SIZE 4 +#define MAXIMUM_ETHERNET_PACKET_SIZE \ + (MAXIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define MINIMUM_ETHERNET_PACKET_SIZE \ + (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define CRC_LENGTH ETHERNET_FCS_SIZE +#define MAX_JUMBO_FRAME_SIZE 0x3F00 + + +/* 802.1q VLAN Packet Sizes */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */ + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ +#define ETHERNET_IP_TYPE 0x0800 /* IP packets */ +#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */ + +/* Packet Header defines */ +#define IP_PROTOCOL_TCP 6 +#define IP_PROTOCOL_UDP 0x11 + +/* 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) + + +/* Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. We + * reserve one of these spots for our directed address, allowing us room for + * E1000_RAR_ENTRIES - 1 multicast addresses. + */ +#define E1000_RAR_ENTRIES 15 + +#define MIN_NUMBER_OF_DESCRIPTORS 8 +#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8 + +/* Receive Descriptor */ +struct em_rx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + uint16_t length; /* Length of data DMAed into data buffer */ + uint16_t csum; /* Packet checksum */ + uint8_t status; /* Descriptor status */ + uint8_t errors; /* Descriptor Errors */ + uint16_t special; +}; + +/* Receive Descriptor - Extended */ +union em_rx_desc_extended { + struct { + uint64_t buffer_addr; + uint64_t reserved; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length; + uint16_t vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 +/* Receive Descriptor - Packet Split */ +union em_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + uint64_t buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length0; /* length of buffer 0 */ + uint16_t vlan; /* VLAN tag */ + } middle; + struct { + uint16_t header_status; + uint16_t length[3]; /* length of buffers 1-3 */ + } upper; + uint64_t reserved; + } wb; /* writeback */ +}; + +/* 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_IPIDV 0x200 /* IP identification valid */ +#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ +#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 + +#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 +#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF + +/* 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) + +/* Transmit Descriptor */ +struct em_tx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + } fields; + } upper; +}; + +/* 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 */ + +/* Offload Context Descriptor */ +struct em_context_desc { + union { + uint32_t ip_config; + struct { + uint8_t ipcss; /* IP checksum start */ + uint8_t ipcso; /* IP checksum offset */ + uint16_t ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + uint32_t tcp_config; + struct { + uint8_t tucss; /* TCP checksum start */ + uint8_t tucso; /* TCP checksum offset */ + uint16_t tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + uint32_t cmd_and_length; /* */ + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t hdr_len; /* Header length */ + uint16_t mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct em_data_desc { + uint64_t buffer_addr; /* Address of the descriptor's buffer address */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t typ_len_ext; /* */ + uint8_t cmd; /* */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t popts; /* Packet Options */ + uint16_t special; /* */ + } fields; + } upper; +}; + +/* Filters */ +#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ +#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + + +/* Receive Address Register */ +struct em_rar { + volatile uint32_t low; /* receive address low */ + volatile uint32_t high; /* receive address high */ +}; + +/* Number of entries in the Multicast Table Array (MTA). */ +#define E1000_NUM_MTA_REGISTERS 128 + +/* IPv4 Address Table Entry */ +struct em_ipv4_at_entry { + volatile uint32_t ipv4_addr; /* IP Address (RW) */ + volatile uint32_t reserved; +}; + +/* Four wakeup IP addresses are supported */ +#define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4 +#define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX +#define E1000_IP6AT_SIZE 1 + +/* IPv6 Address Table Entry */ +struct em_ipv6_at_entry { + volatile uint8_t ipv6_addr[16]; +}; + +/* Flexible Filter Length Table Entry */ +struct em_fflt_entry { + volatile uint32_t length; /* Flexible Filter Length (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Mask Table Entry */ +struct em_ffmt_entry { + volatile uint32_t mask; /* Flexible Filter Mask (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Value Table Entry */ +struct em_ffvt_entry { + volatile uint32_t value; /* Flexible Filter Value (RW) */ + volatile uint32_t reserved; +}; + +/* 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 + +/* Register Set. (82543, 82544) + * + * Registers are defined to be 32 bits and should be accessed as 32 bit values. + * These registers are physically located on the NIC, but are mapped into the + * host memory address space. + * + * RW - register is both readable and writable + * RO - register is read only + * WO - register is write only + * R/clr - register is read only and is cleared when read + * A - register array + */ +#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_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - 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_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_TCTL 0x00400 /* 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_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_FLASH_UPDATES 1000 +#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_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_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_RXDCTL 0x02828 /* RX Descriptor Control - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - 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_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_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 Descripotr 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_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_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 0x4100 /* Interrupt Assertion Count */ +#define E1000_ICRXPTC 0x4104 /* Interrupt Cause Rx Packet Timer Expire Count */ +#define E1000_ICRXATC 0x4108 /* Interrupt Cause Rx Absolute Timer Expire Count */ +#define E1000_ICTXPTC 0x410C /* Interrupt Cause Tx Packet Timer Expire Count */ +#define E1000_ICTXATC 0x4110 /* Interrupt Cause Tx Absolute Timer Expire Count */ +#define E1000_ICTXQEC 0x4118 /* Interrupt Cause Tx Queue Empty Count */ +#define E1000_ICTXQMTC 0x411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ +#define E1000_ICRXDMTC 0x4120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ +#define E1000_ICRXOC 0x4124 /* Interrupt Cause Receiver Overrun Count */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#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_VFTA 0x05600 /* VLAN Filter Table Array - 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_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_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Inteface Control */ +/* Register Set (82542) + * + * Some of the 82542 registers are located at different offsets than they are + * in more current versions of the 8254x. Despite the difference in location, + * the registers function in the same manner. + */ +#define E1000_82542_CTRL E1000_CTRL +#define E1000_82542_CTRL_DUP E1000_CTRL_DUP +#define E1000_82542_STATUS E1000_STATUS +#define E1000_82542_EECD E1000_EECD +#define E1000_82542_EERD E1000_EERD +#define E1000_82542_CTRL_EXT E1000_CTRL_EXT +#define E1000_82542_FLA E1000_FLA +#define E1000_82542_MDIC E1000_MDIC +#define E1000_82542_FCAL E1000_FCAL +#define E1000_82542_FCAH E1000_FCAH +#define E1000_82542_FCT E1000_FCT +#define E1000_82542_VET E1000_VET +#define E1000_82542_RA 0x00040 +#define E1000_82542_ICR E1000_ICR +#define E1000_82542_ITR E1000_ITR +#define E1000_82542_ICS E1000_ICS +#define E1000_82542_IMS E1000_IMS +#define E1000_82542_IMC E1000_IMC +#define E1000_82542_RCTL E1000_RCTL +#define E1000_82542_RDTR 0x00108 +#define E1000_82542_RDBAL 0x00110 +#define E1000_82542_RDBAH 0x00114 +#define E1000_82542_RDLEN 0x00118 +#define E1000_82542_RDH 0x00120 +#define E1000_82542_RDT 0x00128 +#define E1000_82542_FCRTH 0x00160 +#define E1000_82542_FCRTL 0x00168 +#define E1000_82542_FCTTV E1000_FCTTV +#define E1000_82542_TXCW E1000_TXCW +#define E1000_82542_RXCW E1000_RXCW +#define E1000_82542_MTA 0x00200 +#define E1000_82542_TCTL E1000_TCTL +#define E1000_82542_TIPG E1000_TIPG +#define E1000_82542_TDBAL 0x00420 +#define E1000_82542_TDBAH 0x00424 +#define E1000_82542_TDLEN 0x00428 +#define E1000_82542_TDH 0x00430 +#define E1000_82542_TDT 0x00438 +#define E1000_82542_TIDV 0x00440 +#define E1000_82542_TBT E1000_TBT +#define E1000_82542_AIT E1000_AIT +#define E1000_82542_VFTA 0x00600 +#define E1000_82542_LEDCTL E1000_LEDCTL +#define E1000_82542_PBA E1000_PBA +#define E1000_82542_PBS E1000_PBS +#define E1000_82542_EEMNGCTL E1000_EEMNGCTL +#define E1000_82542_EEARBC E1000_EEARBC +#define E1000_82542_FLASHT E1000_FLASHT +#define E1000_82542_EEWR E1000_EEWR +#define E1000_82542_FLSWCTL E1000_FLSWCTL +#define E1000_82542_FLSWDATA E1000_FLSWDATA +#define E1000_82542_FLSWCNT E1000_FLSWCNT +#define E1000_82542_FLOP E1000_FLOP +#define E1000_82542_EXTCNF_CTRL E1000_EXTCNF_CTRL +#define E1000_82542_EXTCNF_SIZE E1000_EXTCNF_SIZE +#define E1000_82542_ERT E1000_ERT +#define E1000_82542_RXDCTL E1000_RXDCTL +#define E1000_82542_RADV E1000_RADV +#define E1000_82542_RSRPD E1000_RSRPD +#define E1000_82542_TXDMAC E1000_TXDMAC +#define E1000_82542_TDFHS E1000_TDFHS +#define E1000_82542_TDFTS E1000_TDFTS +#define E1000_82542_TDFPC E1000_TDFPC +#define E1000_82542_TXDCTL E1000_TXDCTL +#define E1000_82542_TADV E1000_TADV +#define E1000_82542_TSPMT E1000_TSPMT +#define E1000_82542_CRCERRS E1000_CRCERRS +#define E1000_82542_ALGNERRC E1000_ALGNERRC +#define E1000_82542_SYMERRS E1000_SYMERRS +#define E1000_82542_RXERRC E1000_RXERRC +#define E1000_82542_MPC E1000_MPC +#define E1000_82542_SCC E1000_SCC +#define E1000_82542_ECOL E1000_ECOL +#define E1000_82542_MCC E1000_MCC +#define E1000_82542_LATECOL E1000_LATECOL +#define E1000_82542_COLC E1000_COLC +#define E1000_82542_DC E1000_DC +#define E1000_82542_TNCRS E1000_TNCRS +#define E1000_82542_SEC E1000_SEC +#define E1000_82542_CEXTERR E1000_CEXTERR +#define E1000_82542_RLEC E1000_RLEC +#define E1000_82542_XONRXC E1000_XONRXC +#define E1000_82542_XONTXC E1000_XONTXC +#define E1000_82542_XOFFRXC E1000_XOFFRXC +#define E1000_82542_XOFFTXC E1000_XOFFTXC +#define E1000_82542_FCRUC E1000_FCRUC +#define E1000_82542_PRC64 E1000_PRC64 +#define E1000_82542_PRC127 E1000_PRC127 +#define E1000_82542_PRC255 E1000_PRC255 +#define E1000_82542_PRC511 E1000_PRC511 +#define E1000_82542_PRC1023 E1000_PRC1023 +#define E1000_82542_PRC1522 E1000_PRC1522 +#define E1000_82542_GPRC E1000_GPRC +#define E1000_82542_BPRC E1000_BPRC +#define E1000_82542_MPRC E1000_MPRC +#define E1000_82542_GPTC E1000_GPTC +#define E1000_82542_GORCL E1000_GORCL +#define E1000_82542_GORCH E1000_GORCH +#define E1000_82542_GOTCL E1000_GOTCL +#define E1000_82542_GOTCH E1000_GOTCH +#define E1000_82542_RNBC E1000_RNBC +#define E1000_82542_RUC E1000_RUC +#define E1000_82542_RFC E1000_RFC +#define E1000_82542_ROC E1000_ROC +#define E1000_82542_RJC E1000_RJC +#define E1000_82542_MGTPRC E1000_MGTPRC +#define E1000_82542_MGTPDC E1000_MGTPDC +#define E1000_82542_MGTPTC E1000_MGTPTC +#define E1000_82542_TORL E1000_TORL +#define E1000_82542_TORH E1000_TORH +#define E1000_82542_TOTL E1000_TOTL +#define E1000_82542_TOTH E1000_TOTH +#define E1000_82542_TPR E1000_TPR +#define E1000_82542_TPT E1000_TPT +#define E1000_82542_PTC64 E1000_PTC64 +#define E1000_82542_PTC127 E1000_PTC127 +#define E1000_82542_PTC255 E1000_PTC255 +#define E1000_82542_PTC511 E1000_PTC511 +#define E1000_82542_PTC1023 E1000_PTC1023 +#define E1000_82542_PTC1522 E1000_PTC1522 +#define E1000_82542_MPTC E1000_MPTC +#define E1000_82542_BPTC E1000_BPTC +#define E1000_82542_TSCTC E1000_TSCTC +#define E1000_82542_TSCTFC E1000_TSCTFC +#define E1000_82542_RXCSUM E1000_RXCSUM +#define E1000_82542_WUC E1000_WUC +#define E1000_82542_WUFC E1000_WUFC +#define E1000_82542_WUS E1000_WUS +#define E1000_82542_MANC E1000_MANC +#define E1000_82542_IPAV E1000_IPAV +#define E1000_82542_IP4AT E1000_IP4AT +#define E1000_82542_IP6AT E1000_IP6AT +#define E1000_82542_WUPL E1000_WUPL +#define E1000_82542_WUPM E1000_WUPM +#define E1000_82542_FFLT E1000_FFLT +#define E1000_82542_TDFH 0x08010 +#define E1000_82542_TDFT 0x08018 +#define E1000_82542_FFMT E1000_FFMT +#define E1000_82542_FFVT E1000_FFVT +#define E1000_82542_HOST_IF E1000_HOST_IF +#define E1000_82542_IAM E1000_IAM +#define E1000_82542_EEMNGCTL E1000_EEMNGCTL +#define E1000_82542_PSRCTL E1000_PSRCTL +#define E1000_82542_RAID E1000_RAID +#define E1000_82542_TARC0 E1000_TARC0 +#define E1000_82542_TDBAL1 E1000_TDBAL1 +#define E1000_82542_TDBAH1 E1000_TDBAH1 +#define E1000_82542_TDLEN1 E1000_TDLEN1 +#define E1000_82542_TDH1 E1000_TDH1 +#define E1000_82542_TDT1 E1000_TDT1 +#define E1000_82542_TXDCTL1 E1000_TXDCTL1 +#define E1000_82542_TARC1 E1000_TARC1 +#define E1000_82542_RFCTL E1000_RFCTL +#define E1000_82542_GCR E1000_GCR +#define E1000_82542_GSCL_1 E1000_GSCL_1 +#define E1000_82542_GSCL_2 E1000_GSCL_2 +#define E1000_82542_GSCL_3 E1000_GSCL_3 +#define E1000_82542_GSCL_4 E1000_GSCL_4 +#define E1000_82542_FACTPS E1000_FACTPS +#define E1000_82542_SWSM E1000_SWSM +#define E1000_82542_FWSM E1000_FWSM +#define E1000_82542_FFLT_DBG E1000_FFLT_DBG +#define E1000_82542_IAC E1000_IAC +#define E1000_82542_ICRXPTC E1000_ICRXPTC +#define E1000_82542_ICRXATC E1000_ICRXATC +#define E1000_82542_ICTXPTC E1000_ICTXPTC +#define E1000_82542_ICTXATC E1000_ICTXATC +#define E1000_82542_ICTXQEC E1000_ICTXQEC +#define E1000_82542_ICTXQMTC E1000_ICTXQMTC +#define E1000_82542_ICRXDMTC E1000_ICRXDMTC +#define E1000_82542_ICRXOC E1000_ICRXOC +#define E1000_82542_HICR E1000_HICR + +/* Statistics counters collected by the MAC */ +struct em_hw_stats { + uint64_t crcerrs; + uint64_t algnerrc; + uint64_t symerrs; + uint64_t rxerrc; + uint64_t mpc; + uint64_t scc; + uint64_t ecol; + uint64_t mcc; + uint64_t latecol; + uint64_t colc; + uint64_t dc; + uint64_t tncrs; + uint64_t sec; + uint64_t cexterr; + uint64_t rlec; + uint64_t xonrxc; + uint64_t xontxc; + uint64_t xoffrxc; + uint64_t xofftxc; + uint64_t fcruc; + uint64_t prc64; + uint64_t prc127; + uint64_t prc255; + uint64_t prc511; + uint64_t prc1023; + uint64_t prc1522; + uint64_t gprc; + uint64_t bprc; + uint64_t mprc; + uint64_t gptc; + uint64_t gorcl; + uint64_t gorch; + uint64_t gotcl; + uint64_t gotch; + uint64_t rnbc; + uint64_t ruc; + uint64_t rfc; + uint64_t roc; + uint64_t rjc; + uint64_t mgprc; + uint64_t mgpdc; + uint64_t mgptc; + uint64_t torl; + uint64_t torh; + uint64_t totl; + uint64_t toth; + uint64_t tpr; + uint64_t tpt; + uint64_t ptc64; + uint64_t ptc127; + uint64_t ptc255; + uint64_t ptc511; + uint64_t ptc1023; + uint64_t ptc1522; + uint64_t mptc; + uint64_t bptc; + uint64_t tsctc; + uint64_t tsctfc; + uint64_t iac; + uint64_t icrxptc; + uint64_t icrxatc; + uint64_t ictxptc; + uint64_t ictxatc; + uint64_t ictxqec; + uint64_t ictxqmtc; + uint64_t icrxdmtc; + uint64_t icrxoc; +}; + +/* Structure containing variables used by the shared code (em_hw.c) */ +struct em_hw { + uint8_t *hw_addr; + uint8_t *flash_address; + em_mac_type mac_type; + em_phy_type phy_type; + uint32_t phy_init_script; + em_media_type media_type; + void *back; + em_fc_type fc; + em_bus_speed bus_speed; + em_bus_width bus_width; + em_bus_type bus_type; + struct em_eeprom_info eeprom; + em_ms_type master_slave; + em_ms_type original_master_slave; + em_ffe_config ffe_config_state; + uint32_t asf_firmware_present; + uint32_t eeprom_semaphore_present; + unsigned long io_base; + uint32_t phy_id; + uint32_t phy_revision; + uint32_t phy_addr; + uint32_t original_fc; + uint32_t txcw; + uint32_t autoneg_failed; + uint32_t max_frame_size; + uint32_t min_frame_size; + uint32_t mc_filter_type; + uint32_t num_mc_addrs; + uint32_t collision_delta; + uint32_t tx_packet_delta; + uint32_t ledctl_default; + uint32_t ledctl_mode1; + uint32_t ledctl_mode2; + boolean_t tx_pkt_filtering; + struct em_host_mng_dhcp_cookie mng_cookie; + uint16_t phy_spd_default; + uint16_t autoneg_advertised; + uint16_t pci_cmd_word; + uint16_t fc_high_water; + uint16_t fc_low_water; + uint16_t fc_pause_time; + uint16_t current_ifs_val; + uint16_t ifs_min_val; + uint16_t ifs_max_val; + uint16_t ifs_step_size; + uint16_t ifs_ratio; + uint16_t device_id; + uint16_t vendor_id; + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; + uint8_t revision_id; + uint8_t autoneg; + uint8_t mdix; + uint8_t forced_speed_duplex; + uint8_t wait_autoneg_complete; + uint8_t dma_fairness; + uint8_t mac_addr[NODE_ADDRESS_SIZE]; + uint8_t perm_mac_addr[NODE_ADDRESS_SIZE]; + boolean_t disable_polarity_correction; + boolean_t speed_downgraded; + em_smart_speed smart_speed; + em_dsp_config dsp_config_state; + boolean_t get_link_status; + boolean_t serdes_link_down; + boolean_t tbi_compatibility_en; + boolean_t tbi_compatibility_on; + boolean_t phy_reset_disable; + boolean_t fc_send_xon; + boolean_t fc_strict_ieee; + boolean_t report_tx_early; + boolean_t adaptive_ifs; + boolean_t ifs_params_forced; + boolean_t in_ifs_mode; + boolean_t mng_reg_access_disabled; +}; + + +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ +#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */ +#define E1000_EEPROM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */ +#define E1000_EEPROM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ +#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ +/* Register Bit Masks */ +/* 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_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ +#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 */ + +/* 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_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 */ + +/* 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 */ + +/* EEPROM/Flash Control */ +#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ +#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ +#define E1000_EECD_DO 0x00000008 /* EEPROM 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 /* EEPROM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ +#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ +#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type + * (0-small, 1-large) */ +#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ +#ifndef E1000_EEPROM_GRANT_ATTEMPTS +#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ +#endif +#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom 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_STM_OPCODE 0xDB00 +#define E1000_HICR_FW_RESET 0xC0 + +/* EEPROM Read */ +#define E1000_EERD_START 0x00000001 /* Start Read */ +#define E1000_EERD_DONE 0x00000010 /* Read Done */ +#define E1000_EERD_ADDR_SHIFT 8 +#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */ +#define E1000_EERD_DATA_SHIFT 16 +#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */ + +/* SPI EEPROM Status Register */ +#define EEPROM_STATUS_RDY_SPI 0x01 +#define EEPROM_STATUS_WEN_SPI 0x02 +#define EEPROM_STATUS_BP0_SPI 0x04 +#define EEPROM_STATUS_BP1_SPI 0x08 +#define EEPROM_STATUS_WPEN_SPI 0x80 + +/* 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_LINK_MODE_MASK 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 +#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_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */ +#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */ + +/* 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 + +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_BLINK_RATE 0x0000020 +#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 0x0002000 +#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_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 + +/* Receive Address */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ + +/* 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 */ + +/* 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 */ + +/* 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 */ + +/* Interrupt Mask Clear */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMC_SRPD E1000_ICR_SRPD +#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ + +/* 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 */ + +/* Receive Descriptor */ +#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */ +#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */ +#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */ +#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */ +#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */ + +/* 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 */ + +/* 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 + +/* Receive Descriptor Control */ +#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */ +#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */ +#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */ +#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */ + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x000000FF /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x0000FF00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x00FF0000 /* 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_COUNT_DESC 0x00400000 /* Enable the counting of desc. + still to be processed. */ + +/* 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 */ + +/* 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 */ + +/* 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_IPPCSE 0x00001000 /* IP payload checksum enable */ +#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */ + + +/* 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 0x00000001 /* Link Status Changed */ +#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */ +#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */ +#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */ +#define E1000_WUS_BC 0x00000010 /* Broadcast Received */ +#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */ +#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */ +#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */ +#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */ +#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */ +#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */ +#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */ +#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* 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_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 */ + +/* 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 */ + +/* FW Semaphore Register */ +#define E1000_FWSM_MODE_MASK 0x0000000E /* FW mode */ +#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */ + +/* FFLT Debug Register */ +#define E1000_FFLT_DBG_INVC 0x00100000 /* Invalid /C/ code handling */ + +typedef enum { + em_mng_mode_none = 0, + em_mng_mode_asf, + em_mng_mode_pt, + em_mng_mode_ipmi, + em_mng_mode_host_interface_only +} em_mng_mode; + +/* Host Inteface Control Register */ +#define E1000_HICR_EN 0x00000001 /* Enable Bit - RO */ +#define E1000_HICR_C 0x00000002 /* Driver sets this bit when done + * to put command in RAM */ +#define E1000_HICR_SV 0x00000004 /* Status Validity */ +#define E1000_HICR_FWR 0x00000080 /* FW reset. Set by the Host */ + +/* Host Interface Command Interface - Address range 0x8800-0x8EFF */ +#define E1000_HI_MAX_DATA_LENGTH 252 /* Host Interface data length */ +#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Number of bytes in range */ +#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Number of dwords in range */ +#define E1000_HI_COMMAND_TIMEOUT 500 /* Time in ms to process HI command */ + +struct em_host_command_header { + uint8_t command_id; + uint8_t command_length; + uint8_t command_options; /* I/F bits for command, status for return */ + uint8_t checksum; +}; +struct em_host_command_info { + struct em_host_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ + uint8_t command_data[E1000_HI_MAX_DATA_LENGTH]; /* Command data can length 0..252 */ +}; + +/* Host SMB register #0 */ +#define E1000_HSMC0R_CLKIN 0x00000001 /* SMB Clock in */ +#define E1000_HSMC0R_DATAIN 0x00000002 /* SMB Data in */ +#define E1000_HSMC0R_DATAOUT 0x00000004 /* SMB Data out */ +#define E1000_HSMC0R_CLKOUT 0x00000008 /* SMB Clock out */ + +/* Host SMB register #1 */ +#define E1000_HSMC1R_CLKIN E1000_HSMC0R_CLKIN +#define E1000_HSMC1R_DATAIN E1000_HSMC0R_DATAIN +#define E1000_HSMC1R_DATAOUT E1000_HSMC0R_DATAOUT +#define E1000_HSMC1R_CLKOUT E1000_HSMC0R_CLKOUT + +/* FW Status Register */ +#define E1000_FWSTS_FWS_MASK 0x000000FF /* FW Status */ + +/* Wake Up Packet Length */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ + +#define E1000_MDALIGN 4096 + +#define E1000_GCR_BEM32 0x00400000 +/* Function Active and Power State to MNG */ +#define E1000_FACTPS_FUNC0_POWER_STATE_MASK 0x00000003 +#define E1000_FACTPS_LAN0_VALID 0x00000004 +#define E1000_FACTPS_FUNC0_AUX_EN 0x00000008 +#define E1000_FACTPS_FUNC1_POWER_STATE_MASK 0x000000C0 +#define E1000_FACTPS_FUNC1_POWER_STATE_SHIFT 6 +#define E1000_FACTPS_LAN1_VALID 0x00000100 +#define E1000_FACTPS_FUNC1_AUX_EN 0x00000200 +#define E1000_FACTPS_FUNC2_POWER_STATE_MASK 0x00003000 +#define E1000_FACTPS_FUNC2_POWER_STATE_SHIFT 12 +#define E1000_FACTPS_IDE_ENABLE 0x00004000 +#define E1000_FACTPS_FUNC2_AUX_EN 0x00008000 +#define E1000_FACTPS_FUNC3_POWER_STATE_MASK 0x000C0000 +#define E1000_FACTPS_FUNC3_POWER_STATE_SHIFT 18 +#define E1000_FACTPS_SP_ENABLE 0x00100000 +#define E1000_FACTPS_FUNC3_AUX_EN 0x00200000 +#define E1000_FACTPS_FUNC4_POWER_STATE_MASK 0x03000000 +#define E1000_FACTPS_FUNC4_POWER_STATE_SHIFT 24 +#define E1000_FACTPS_IPMI_ENABLE 0x04000000 +#define E1000_FACTPS_FUNC4_AUX_EN 0x08000000 +#define E1000_FACTPS_MNGCG 0x20000000 +#define E1000_FACTPS_LAN_FUNC_SEL 0x40000000 +#define E1000_FACTPS_PM_STATE_CHANGED 0x80000000 + +/* EEPROM Commands - Microwire */ +#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ +#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ +#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ +#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ + +/* EEPROM Commands - SPI */ +#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */ +#define EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Enable latch */ +#define EEPROM_WRDI_OPCODE_SPI 0x04 /* EEPROM reset Write Enable latch */ +#define EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status register */ +#define EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status register */ +#define EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */ +#define EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */ +#define EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */ + +/* EEPROM Size definitions */ +#define EEPROM_WORD_SIZE_SHIFT 6 +#define EEPROM_SIZE_SHIFT 10 +#define EEPROM_SIZE_MASK 0x1C00 + +/* EEPROM Word Offsets */ +#define EEPROM_COMPAT 0x0003 +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ +#define EEPROM_PHY_CLASS_WORD 0x0007 +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 +#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 +#define EEPROM_CFG 0x0012 +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +/* 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 + + +/* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */ +#define EEPROM_SERDES_AMPLITUDE_MASK 0x000F + +/* Mask bit for PHY class in Word 7 of the EEPROM */ +#define EEPROM_PHY_CLASS_A 0x8000 + +/* Mask bits for fields in Word 0x0a of the EEPROM */ +#define EEPROM_WORD0A_ILOS 0x0010 +#define EEPROM_WORD0A_SWDPIO 0x01E0 +#define EEPROM_WORD0A_LRST 0x0200 +#define EEPROM_WORD0A_FD 0x0400 +#define EEPROM_WORD0A_66MHZ 0x0800 + +/* Mask bits for fields in Word 0x0f of the EEPROM */ +#define EEPROM_WORD0F_PAUSE_MASK 0x3000 +#define EEPROM_WORD0F_PAUSE 0x1000 +#define EEPROM_WORD0F_ASM_DIR 0x2000 +#define EEPROM_WORD0F_ANE 0x0800 +#define EEPROM_WORD0F_SWPDIO_EXT 0x00F0 + +/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ +#define EEPROM_SUM 0xBABA + +/* EEPROM Map defines (WORD OFFSETS)*/ +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_PBA_BYTE_1 8 + +#define EEPROM_RESERVED_WORD 0xFFFF + +/* EEPROM Map Sizes (Byte Counts) */ +#define PBA_SIZE 4 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 15 +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 64 +#define E1000_FDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_HDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_COLD_SHIFT 12 + +/* Number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Default values for the transmit IPG register */ +#define DEFAULT_82542_TIPG_IPGT 10 +#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 + +#define DEFAULT_82542_TIPG_IPGR1 2 +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#define DEFAULT_82542_TIPG_IPGR2 10 +#define DEFAULT_82543_TIPG_IPGR2 6 +#define E1000_TIPG_IPGR2_SHIFT 20 + +#define E1000_TXDMAC_DPP 0x00000001 + +/* Adaptive IFS defines */ +#define TX_THRESHOLD_START 8 +#define TX_THRESHOLD_INCREMENT 10 +#define TX_THRESHOLD_DECREMENT 1 +#define TX_THRESHOLD_STOP 190 +#define TX_THRESHOLD_DISABLE 0 +#define TX_THRESHOLD_TIMER_MS 10000 +#define MIN_NUM_XMITS 1000 +#define IFS_MAX 80 +#define IFS_STEP 10 +#define IFS_MIN 40 +#define IFS_RATIO 4 + +/* Extended Configuration Control and Size */ +#define E1000_EXTCNF_CTRL_PCIE_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_PHY_WRITE_ENABLE 0x00000002 +#define E1000_EXTCNF_CTRL_D_UD_ENABLE 0x00000004 +#define E1000_EXTCNF_CTRL_D_UD_LATENCY 0x00000008 +#define E1000_EXTCNF_CTRL_D_UD_OWNER 0x00000010 +#define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020 +#define E1000_EXTCNF_CTRL_MDIO_HW_OWNERSHIP 0x00000040 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER 0x1FFF0000 + +#define E1000_EXTCNF_SIZE_EXT_PHY_LENGTH 0x000000FF +#define E1000_EXTCNF_SIZE_EXT_DOCK_LENGTH 0x0000FF00 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH 0x00FF0000 + +/* PBA constants */ +#define E1000_PBA_12K 0x000C /* 12KB, default Rx allocation */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_22K 0x0016 +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_30K 0x001E +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* The historical defaults for the flow control values are given below. */ +#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */ +#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ +#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ + +/* PCIX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA + +#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 + + +/* Number of bits required to shift right the "pause" bits from the + * EEPROM (bits 13:12) to the "pause" (bits 8:7) field in the TXCW register. + */ +#define PAUSE_SHIFT 5 + +/* Number of bits required to shift left the "SWDPIO" bits from the + * EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field in the CTRL register. + */ +#define SWDPIO_SHIFT 17 + +/* Number of bits required to shift left the "SWDPIO_EXT" bits from the + * EEPROM word F (bits 7:4) to the bits 11:8 of The Extended CTRL register. + */ +#define SWDPIO__EXT_SHIFT 4 + +/* Number of bits required to shift left the "ILOS" bit from the EEPROM + * (bit 4) to the "ILOS" (bit 7) field in the CTRL register. + */ +#define ILOS_SHIFT 3 + + +#define RECEIVE_BUFFER_ALIGN_SIZE (256) + +/* Number of milliseconds we wait for auto-negotiation to complete */ +#define LINK_UP_TIMEOUT 500 + +/* Number of 100 microseconds we wait for PCI Express master disable */ +#define MASTER_DISABLE_TIMEOUT 800 +/* Number of milliseconds we wait for Eeprom auto read bit done after MAC reset */ +#define AUTO_READ_DONE_TIMEOUT 10 +/* Number of milliseconds we wait for PHY configuration done after MAC reset */ +#define PHY_CFG_TIMEOUT 40 + +#define E1000_TX_BUFFER_SIZE ((uint32_t)1514) + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +/* TBI_ACCEPT macro definition: + * + * This macro requires: + * adapter = a pointer to struct em_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; + * em_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = FALSE; + * } + * ... + */ + +#define TBI_ACCEPT(adapter, status, errors, length, last_byte) \ + ((adapter)->tbi_compatibility_on && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + (((status) & E1000_RXD_STAT_VP) ? \ + (((length) > ((adapter)->min_frame_size - VLAN_TAG_SIZE)) && \ + ((length) <= ((adapter)->max_frame_size + 1))) : \ + (((length) > (adapter)->min_frame_size) && \ + ((length) <= ((adapter)->max_frame_size + VLAN_TAG_SIZE + 1))))) + + +/* Structures, enums, and macros for the PHY */ + +/* 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 + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CTRL 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 */ + +/* 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 */ + +#define IGP01E1000_IEEE_REGS_PAGE 0x0000 +#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300 +#define IGP01E1000_IEEE_FORCE_GIGA 0x0140 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */ +#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */ +#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */ + +/* IGP01E1000 AGC Registers - stores the cable length values*/ +#define IGP01E1000_PHY_AGC_A 0x1172 +#define IGP01E1000_PHY_AGC_B 0x1272 +#define IGP01E1000_PHY_AGC_C 0x1472 +#define IGP01E1000_PHY_AGC_D 0x1872 + +/* IGP02E1000 AGC Registers for cable length values */ +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 + +/* IGP01E1000 DSP Reset Register */ +#define IGP01E1000_PHY_DSP_RESET 0x1F33 +#define IGP01E1000_PHY_DSP_SET 0x1F71 +#define IGP01E1000_PHY_DSP_FFE 0x1F35 + +#define IGP01E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_CHANNEL_NUM 4 + +#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_ANALOG_TX_STATE 0x2890 +#define IGP01E1000_PHY_ANALOG_CLASS_A 0x2000 +#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE 0x0004 +#define IGP01E1000_PHY_DSP_FFE_CM_CP 0x0069 + +#define IGP01E1000_PHY_DSP_FFE_DEFAULT 0x002A +/* IGP01E1000 PCS Initialization register - stores the polarity status when + * speed = 1000 Mbps. */ +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_PCS_CTRL_REG 0x00B5 + +#define IGP01E1000_ANALOG_REGS_PAGE 0x20C0 + +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF /*Registers that are equal on all pages*/ +/* 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 */ + +/* 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 */ + +/* Next Page TX Register */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 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_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 + +/* Extended Status Register */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* 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_10BT_EXT_DIST_ENABLE 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 */ + +#define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5 +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 + +/* 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_REV_POLARITY_SHIFT 1 +#define M88E1000_PSSR_DOWNSHIFT_SHIFT 5 +#define M88E1000_PSSR_MDIX_SHIFT 6 +#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 */ + +/* IGP01E1000 Specific Port Config Register - R/W */ +#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010 +#define IGP01E1000_PSCFR_PRE_EN 0x0020 +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 +#define IGP01E1000_PSCFR_DISABLE_TPLOOPBACK 0x0100 +#define IGP01E1000_PSCFR_DISABLE_JABBER 0x0400 +#define IGP01E1000_PSCFR_DISABLE_TRANSMIT 0x2000 + +/* IGP01E1000 Specific Port Status Register - R/O */ +#define IGP01E1000_PSSR_AUTONEG_FAILED 0x0001 /* RO LH SC */ +#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002 +#define IGP01E1000_PSSR_CABLE_LENGTH 0x007C +#define IGP01E1000_PSSR_FULL_DUPLEX 0x0200 +#define IGP01E1000_PSSR_LINK_UP 0x0400 +#define IGP01E1000_PSSR_MDIX 0x0800 +#define IGP01E1000_PSSR_SPEED_MASK 0xC000 /* speed bits mask */ +#define IGP01E1000_PSSR_SPEED_10MBPS 0x4000 +#define IGP01E1000_PSSR_SPEED_100MBPS 0x8000 +#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000 +#define IGP01E1000_PSSR_CABLE_LENGTH_SHIFT 0x0002 /* shift right 2 */ +#define IGP01E1000_PSSR_MDIX_SHIFT 0x000B /* shift right 11 */ + +/* IGP01E1000 Specific Port Control Register - R/W */ +#define IGP01E1000_PSCR_TP_LOOPBACK 0x0010 +#define IGP01E1000_PSCR_CORRECT_NC_SCMBLR 0x0200 +#define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400 +#define IGP01E1000_PSCR_FLIP_CHIP 0x0800 +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */ + +/* IGP01E1000 Specific Port Link Health Register */ +#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 +#define IGP01E1000_PLHR_GIG_SCRAMBLER_ERROR 0x4000 +#define IGP01E1000_PLHR_MASTER_FAULT 0x2000 +#define IGP01E1000_PLHR_MASTER_RESOLUTION 0x1000 +#define IGP01E1000_PLHR_GIG_REM_RCVR_NOK 0x0800 /* LH */ +#define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW 0x0400 /* LH */ +#define IGP01E1000_PLHR_DATA_ERR_1 0x0200 /* LH */ +#define IGP01E1000_PLHR_DATA_ERR_0 0x0100 +#define IGP01E1000_PLHR_AUTONEG_FAULT 0x0040 +#define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0010 +#define IGP01E1000_PLHR_VALID_CHANNEL_D 0x0008 +#define IGP01E1000_PLHR_VALID_CHANNEL_C 0x0004 +#define IGP01E1000_PLHR_VALID_CHANNEL_B 0x0002 +#define IGP01E1000_PLHR_VALID_CHANNEL_A 0x0001 + +/* IGP01E1000 Channel Quality Register */ +#define IGP01E1000_MSE_CHANNEL_D 0x000F +#define IGP01E1000_MSE_CHANNEL_C 0x00F0 +#define IGP01E1000_MSE_CHANNEL_B 0x0F00 +#define IGP01E1000_MSE_CHANNEL_A 0xF000 + +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* Enable LPLU in non-D0a modes */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* Enable LPLU in D0a mode */ + +/* IGP01E1000 DSP reset macros */ +#define DSP_RESET_ENABLE 0x0 +#define DSP_RESET_DISABLE 0x2 +#define E1000_MAX_DSP_RESETS 10 + +/* IGP01E1000 & IGP02E1000 AGC Registers */ + +#define IGP01E1000_AGC_LENGTH_SHIFT 7 /* Coarse - 13:11, Fine - 10:7 */ +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Coarse - 15:13, Fine - 12:9 */ + +/* IGP02E1000 AGC Register Length 9-bit mask */ +#define IGP02E1000_AGC_LENGTH_MASK 0x7F + +/* 7 bits (3 Coarse + 4 Fine) --> 128 optional values */ +#define IGP01E1000_AGC_LENGTH_TABLE_SIZE 128 +#define IGP02E1000_AGC_LENGTH_TABLE_SIZE 128 + +/* The precision error of the cable length is +/- 10 meters */ +#define IGP01E1000_AGC_RANGE 10 +#define IGP02E1000_AGC_RANGE 10 + +/* IGP01E1000 PCS Initialization register */ +/* bits 3:6 in the PCS registers stores the channels polarity */ +#define IGP01E1000_PHY_POLARITY_MASK 0x0078 + +/* IGP01E1000 GMII FIFO Register */ +#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed + * on Link-Up */ +#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */ + +/* IGP01E1000 Analog Register */ +#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1 +#define IGP01E1000_ANALOG_FUSE_STATUS 0x20D0 +#define IGP01E1000_ANALOG_FUSE_CONTROL 0x20DC +#define IGP01E1000_ANALOG_FUSE_BYPASS 0x20DE + +#define IGP01E1000_ANALOG_FUSE_POLY_MASK 0xF000 +#define IGP01E1000_ANALOG_FUSE_FINE_MASK 0x0F80 +#define IGP01E1000_ANALOG_FUSE_COARSE_MASK 0x0070 +#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED 0x0100 +#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002 + +#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 + + +/* 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 M88E1000_12_PHY_ID M88E1000_E_PHY_ID +#define M88E1000_14_PHY_ID M88E1000_E_PHY_ID +#define M88E1011_I_REV_4 0x04 +#define M88E1111_I_PHY_ID 0x01410CC0 +#define L1LXT971A_PHY_ID 0x001378E0 + +/* Miscellaneous PHY bit definitions. */ +#define PHY_PREAMBLE 0xFFFFFFFF +#define PHY_SOF 0x01 +#define PHY_OP_READ 0x02 +#define PHY_OP_WRITE 0x01 +#define PHY_TURNAROUND 0x02 +#define PHY_PREAMBLE_SIZE 32 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 +#define E1000_PHY_ADDRESS 0x01 +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */ +#define REG4_SPEED_MASK 0x01E0 +#define REG9_SPEED_MASK 0x0300 +#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 +#define ADVERTISE_1000_FULL 0x0020 +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ +#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/ +#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/ + +#endif /* _EM_HW_H_ */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_osdep.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_osdep.h new file mode 100644 index 0000000000..0b62e8bed3 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_osdep.h @@ -0,0 +1,146 @@ +/************************************************************************** + +Copyright (c) 2001-2005, 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: /repoman/r/ncvs/src/sys/dev/em/if_em_osdep.h,v 1.14 2005/05/26 23:32:02 tackerman Exp $*/ + +#ifndef _RTEMS_OS_H_ +#define _RTEMS_OS_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Eventually, we should include this +#include +*/ +#define IFM_LINK_OK IFM_FLAG0 +#define IFM_ANEG_DIS IFM_FLAG1 + +#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)) + +#define MSGOUT(S, A, B) printf(S "\n", A, B) +#define DEBUGFUNC(F) DEBUGOUT(F); +#if DBG + #define DEBUGOUT(S) printf(S "\n") + #define DEBUGOUT1(S,A) printf(S "\n",A) + #define DEBUGOUT2(S,A,B) printf(S "\n",A,B) + #define DEBUGOUT3(S,A,B,C) printf(S "\n",A,B,C) + #define bootverbose (1) + #define DEBUGOUT7(S,A,B,C,D,E,F,G) printf(S "\n",A,B,C,D,E,F,G) +#else + #define DEBUGOUT(S) + #define DEBUGOUT1(S,A) + #define DEBUGOUT2(S,A,B) + #define DEBUGOUT3(S,A,B,C) + #define bootverbose (0) + #define DEBUGOUT7(S,A,B,C,D,E,F,G) +#endif + +#define CMD_MEM_WRT_INVALIDATE 0x0010 /* BIT_4 */ +#define PCI_COMMAND_REGISTER PCIR_COMMAND + +struct em_osdep +{ + unsigned mem_bus_space_handle; + device_t dev; +}; + +struct rtems_ifmedia { + int ifm_media; +}; + +#define E1000_WRITE_FLUSH(hw) E1000_READ_REG(hw, STATUS) + +/* Read from an absolute offset in the adapter's memory space */ +#define E1000_READ_OFFSET(hw, offset) \ + bus_space_read_4( ((struct em_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct em_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 em_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct em_osdep *)(hw)->back)->mem_bus_space_handle, \ + offset, \ + value) + +/* Convert a register name to its offset in the adapter's memory space */ +#define E1000_REG_OFFSET(hw, reg) \ + ((hw)->mac_type >= em_82543 ? E1000_##reg : E1000_82542_##reg) + +#define E1000_READ_REG(hw, reg) \ + E1000_READ_OFFSET(hw, E1000_REG_OFFSET(hw, reg)) + +#define E1000_WRITE_REG(hw, reg, value) \ + E1000_WRITE_OFFSET(hw, E1000_REG_OFFSET(hw, reg), value) + +#define E1000_READ_REG_ARRAY(hw, reg, index) \ + E1000_READ_OFFSET(hw, E1000_REG_OFFSET(hw, reg) + ((index) << 2)) + +#define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY + +#define E1000_WRITE_REG_ARRAY(hw, reg, index, value) \ + E1000_WRITE_OFFSET(hw, E1000_REG_OFFSET(hw, reg) + ((index) << 2), value) + +#define E1000_WRITE_REG_ARRAY_BYTE(hw, reg, index, value) \ + bus_space_write_1( ((struct em_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct em_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REG_OFFSET(hw, reg) + (index), \ + value) + +#define E1000_WRITE_REG_ARRAY_WORD(hw, reg, index, value) \ + bus_space_write_2( ((struct em_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct em_osdep *)(hw)->back)->mem_bus_space_handle, \ + E1000_REG_OFFSET(hw, reg) + (index), \ + value) + +#define E1000_WRITE_REG_ARRAY_DWORD(hw, reg, index, value) \ + E1000_WRITE_OFFSET(hw, E1000_REG_OFFSET(hw, reg) + ((index) << 2), value) + +#endif /* _FREEBSD_OS_H_ */ + diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_pub.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_pub.h new file mode 100644 index 0000000000..66c9d3d3d2 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_pub.h @@ -0,0 +1,23 @@ +/* $Id$ */ +#ifndef RTEMS_BSDNET_IF_EM_PUBLIC_SYMBOLS_H +#define RTEMS_BSDNET_IF_EM_PUBLIC_SYMBOLS_H + +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +extern int rtems_em_attach(struct rtems_bsdnet_ifconfig *, int); +extern int rtems_em_pci_setup(int); +extern rtems_bsdnet_early_link_check_ops rtems_em_early_link_check_ops; + +#ifdef __cplusplus + } +#endif + +#endif + + diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_rtems.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_rtems.c new file mode 100644 index 0000000000..306c2abc05 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/if_em_rtems.c @@ -0,0 +1,106 @@ +/* $Id$ */ +#include "if_xxx_rtems.c" +#include +#include + +/* Provide a routine to check link status early, + * i.e., before the network is really running. + * In case someone wants to decide whether to use/configure + * this interface at all :-) + * + * NOTE: this routine tries to enable autonegotiation! + * + * unit: unit number starting with 1 (usual BSDNET convention) + * + * RETURNS: Phy status register contents (1<<2 means link up). + * or -1 on error. + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +static int +em_early_init(int idx) +{ + if ( idx < 0 || idx >= NETDRIVER_SLOTS ) + return -1; + return em_hw_early_init(&the_em_devs[idx]); +} + +static int +em_early_read_phy(int idx, unsigned reg) +{ +unsigned short data; + if ( idx < 0 || idx >= NETDRIVER_SLOTS ) + return -1; + /* Bizarre - I always have to read PHY_STATUS twice until a good link + * status is read + */ + if ( em_read_phy_reg(&the_em_devs[idx].d_softc.hw, reg, &data) ) + return -1; + if ( PHY_STATUS == reg ) { + /* read again */ + if ( em_read_phy_reg(&the_em_devs[idx].d_softc.hw, PHY_STATUS, &data) ) + return -1; + } + return data; +} + +static int +em_early_write_phy(int idx, unsigned reg, unsigned val) +{ + if ( idx < 0 || idx >= NETDRIVER_SLOTS ) + return -1; + return em_write_phy_reg(&the_em_devs[idx].d_softc.hw, reg, val); +} + +rtems_bsdnet_early_link_check_ops +rtems_em_early_link_check_ops = { + init: em_early_init, + read_phy: em_early_read_phy, + write_phy: em_early_write_phy, + name: NETDRIVER, + num_slots: NETDRIVER_SLOTS +}; diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_em/rtemscompat_defs.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/rtemscompat_defs.h new file mode 100644 index 0000000000..50b51e6d40 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_em/rtemscompat_defs.h @@ -0,0 +1,110 @@ +#ifndef RTEMS_COMPAT_DEFS_H +#define RTEMS_COMPAT_DEFS_H + +/* Number of device instances the driver should support + * - may be limited to 1 depending on IRQ API + * (braindamaged PC586 and powerpc) + */ +#define NETDRIVER_SLOTS 1 + +/* String name to print with error messages */ +#define NETDRIVER "em" +/* Name snippet used to make global symbols unique to this driver */ +#define NETDRIVER_PREFIX em + +#define adapter em_softc +#define interface_data arpcom + +/* Define according to endianness of the *ethernet*chip* + * (not the CPU - most probably are LE) + * This must be either NET_CHIP_LE or NET_CHIP_BE + */ + +#define NET_CHIP_LE +#undef NET_CHIP_BE + +/* Define either NET_CHIP_MEM_IO or NET_CHIP_PORT_IO, + * depending whether the CPU sees it in memory address space + * or (e.g. x86) uses special I/O instructions. + */ +#define NET_CHIP_MEM_IO +#undef NET_CHIP_PORT_IO + +/* The name of the hijacked 'bus handle' field in the softc + * structure. We use this field to store the chip's base address. + */ +#define NET_SOFTC_BHANDLE_FIELD osdep.mem_bus_space_handle + +/* define the names of the 'if_XXXreg.h' and 'if_XXXvar.h' headers + * (only if present, i.e., if the BSDNET driver has no respective + * header, leave this undefined). + * + */ +#define IF_REG_HEADER +#undef IF_VAR_HEADER + +/* define if a pci device */ +#define NETDRIVER_PCI + +/* Macros to disable and enable interrupts, respectively. + * The 'disable' macro is expanded in the ISR, the 'enable' + * macro is expanded in the driver task. + * The global network semaphore usually provides mutex + * protection of the device registers. + * Special care must be taken when coding the 'disable' macro, + * however to MAKE SURE THERE ARE NO OTHER SIDE EFFECTS such + * as: + * - macro must not clear any status flags + * - macro must save/restore any context information + * (e.g., a address register pointer or a bank switch register) + * + * ARGUMENT: the macro arg is a pointer to the driver's 'softc' structure + */ + +#define NET_ENABLE_IRQS(sc) do { \ + E1000_WRITE_REG(&sc->hw, IMS, (IMS_ENABLE_MASK)); \ + } while (0) + +#define NET_DISABLE_IRQS(sc) do { \ + E1000_WRITE_REG(&sc->hw, IMC, 0xffffffff); \ + } while (0) + +#define KASSERT(a...) do {} while (0) + +/* dmamap stuff; these are defined just to work with the current version + * of this driver and the implementation must be carefully checked if + * a newer version is merged.! + * + * The more cumbersome routines have been commented in the source, the + * simpler ones are defined to be NOOPs here so the source gets less + * cluttered... + * + * ASSUMPTIONS: + * + * -> dmamap_sync cannot sync caches; assume we have HW snooping + * + */ + +typedef unsigned bus_size_t; +typedef unsigned bus_addr_t; + +typedef struct { + unsigned ds_addr; + unsigned ds_len; +} bus_dma_segment_t; + +#define bus_dma_tag_destroy(args...) do {} while(0) + +#define bus_dmamap_destroy(args...) do {} while(0) + +#define bus_dmamap_unload(args...) do {} while (0) + +#ifdef __PPC__ +#define bus_dmamap_sync(args...) do { asm volatile("sync":::"memory"); } while (0) +#else +#define bus_dmamap_sync(args...) do {} while (0) +#endif + +#define BUS_DMA_NOWAIT 0xdeadbeef /* unused */ + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/Makefile b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/Makefile new file mode 100644 index 0000000000..a2329f5e98 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/Makefile @@ -0,0 +1,90 @@ +# +# Makefile.lib,v 1.5 2000/06/12 15:00:14 joel Exp +# +# Templates/Makefile.lib +# Template library Makefile +# + +LIBNAME=libif_gfe.a +LIB=${ARCH}/${LIBNAME} + +# C and C++ source names, if any, go here -- minus the .c or .cc +C_PIECES=if_gfe if_gfe_rtems if_gfe.modini +PGMS = $(ARCH)/if_gfe.obj +MODOBJS = $(OBJS) +C_FILES=$(C_PIECES:%=%.c) +C_O_FILES=$(C_PIECES:%=${ARCH}/%.o) + +CC_PIECES= +CC_FILES=$(CC_PIECES:%=%.cc) +CC_O_FILES=$(CC_PIECES:%=${ARCH}/%.o) + +H_FILES= + +# Assembly source names, if any, go here -- minus the .S +S_PIECES= +S_FILES=$(S_PIECES:%=%.S) +S_O_FILES=$(S_FILES:%.S=${ARCH}/%.o) + +SRCS=$(C_FILES) $(CC_FILES) $(H_FILES) $(S_FILES) +OBJS=$(C_O_FILES) $(CC_O_FILES) $(S_O_FILES) + +include $(RTEMS_MAKEFILE_PATH)/Makefile.inc + +include $(RTEMS_CUSTOM) +include $(RTEMS_ROOT)/make/lib.cfg + +# +# Add local stuff here using += +# + +DEFINES += -DDEBUG_MODULAR +#-DDEBUG + +CPPFLAGS += -I. -Ilibchip -Iporting +# bsdnet newproc generated daemon is non-FP; +# prevent optimizer from generating FP instructions +CFLAGS += -Wno-unused-variable -msoft-float + +# +# Add your list of files to delete here. The config files +# already know how to delete some stuff, so you may want +# to just run 'make clean' first to see what gets missed. +# 'make clobber' already includes 'make clean' +# + +CLEAN_ADDITIONS += +CLOBBER_ADDITIONS += + +all: ${ARCH} $(SRCS) $(LIB) $(PGMS) + +$(LIB): ${OBJS} + $(make-library) + +#How to make a relocatable object +$(filter %.obj, $(PGMS)): $(MODOBJS) + $(make-obj) + +ifndef RTEMS_SITE_INSTALLDIR +RTEMS_SITE_INSTALLDIR = $(PROJECT_RELEASE) +endif + +${RTEMS_SITE_INSTALLDIR}/include \ +${RTEMS_SITE_INSTALLDIR}/lib \ +${RTEMS_SITE_INSTALLDIR}/bin \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/include \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/lib \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/bin : + test -d $@ || mkdir -p $@ + +# Install the library, appending _g or _p as appropriate. +# for include files, just use $(INSTALL_CHANGE) +# +# NOTES: +# - BSP specific libraries, headers etc. should be installed to +# $RTEMS_SITE_INSTALLDIR)/$(RTEMS_BSP)/lib +# + +install: all $(RTEMS_SITE_INSTALLDIR)/lib + $(INSTALL_VARIANT) -m 644 ${LIB} ${RTEMS_SITE_INSTALLDIR}/lib + $(INSTALL_VARIANT) -m 644 ${PGMS} ${RTEMS_SITE_INSTALLDIR}/bin diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtethreg.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtethreg.h new file mode 100644 index 0000000000..d571eecd34 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtethreg.h @@ -0,0 +1,854 @@ +/* $NetBSD: gtethreg.h,v 1.2.10.1 2005/04/29 11:28:55 kent Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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. + */ + +#ifndef _DEV_GTETHREG_H_ +#define _DEV_GTETHREG_H_ + +#define ETH__BIT(bit) (1U << (bit)) +#define ETH__LLBIT(bit) (1LLU << (bit)) +#define ETH__MASK(bit) (ETH__BIT(bit) - 1) +#define ETH__LLMASK(bit) (ETH__LLBIT(bit) - 1) +#define ETH__GEN(n, off) (0x2400+((n) << 10)+(ETH__ ## off)) +#define ETH__EXT(data, bit, len) (((data) >> (bit)) & ETH__MASK(len)) +#define ETH__LLEXT(data, bit, len) (((data) >> (bit)) & ETH__LLMASK(len)) +#define ETH__CLR(data, bit, len) ((data) &= ~(ETH__MASK(len) << (bit))) +#define ETH__INS(new, bit) ((new) << (bit)) +#define ETH__LLINS(new, bit) ((uint64_t)(new) << (bit)) + +/* + * Descriptors used for both receive & transmit data. Note that the descriptor + * must start on a 4LW boundary. Since the GT accesses the descriptor as + * two 64-bit quantities, we must present them 32bit quantities in the right + * order based on endianess. + */ + +struct gt_eth_desc { +#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN + u_int32_t ed_lencnt; /* length is hi 16 bits; count (rx) is lo 16 */ + u_int32_t ed_cmdsts; /* command (hi16)/status (lo16) bits */ + u_int32_t ed_nxtptr; /* next descriptor (must be 4LW aligned) */ + u_int32_t ed_bufptr; /* pointer to packet buffer */ +#endif +#if defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN + u_int32_t ed_cmdsts; /* command (hi16)/status (lo16) bits */ + u_int32_t ed_lencnt; /* length is hi 16 bits; count (rx) is lo 16 */ + u_int32_t ed_bufptr; /* pointer to packet buffer */ + u_int32_t ed_nxtptr; /* next descriptor (must be 4LW aligned) */ +#endif +}; + +/* Table 578: Ethernet TX Descriptor - Command/Status word + * All bits except F, EI, AM, O are only valid if TX_CMD_L is also set, + * otherwise should be 0 (tx). + */ +#define TX_STS_LC ETH__BIT(5) /* Late Collision */ +#define TX_STS_UR ETH__BIT(6) /* Underrun error */ +#define TX_STS_RL ETH__BIT(8) /* Retransmit Limit (excession coll) */ +#define TX_STS_COL ETH__BIT(9) /* Collision Occurred */ +#define TX_STS_RC(v) ETH__GETBITS(v, 10, 4) /* Retransmit Count */ +#define TX_STS_ES ETH__BIT(15) /* Error Summary (LC|UR|RL) */ +#define TX_CMD_L ETH__BIT(16) /* Last - End Of Packet */ +#define TX_CMD_F ETH__BIT(17) /* First - Start Of Packet */ +#define TX_CMD_P ETH__BIT(18) /* Pad Packet */ +#define TX_CMD_GC ETH__BIT(22) /* Generate CRC */ +#define TX_CMD_EI ETH__BIT(23) /* Enable Interrupt */ +#define TX_CMD_AM ETH__BIT(30) /* Auto Mode */ +#define TX_CMD_O ETH__BIT(31) /* Ownership (1=GT 0=CPU) */ + +#define TX_CMD_FIRST (TX_CMD_F|TX_CMD_O) +#define TX_CMD_LAST (TX_CMD_L|TX_CMD_GC|TX_CMD_P|TX_CMD_O) + +/* Table 582: Ethernet RX Descriptor - Command/Status Word + * All bits except F, EI, AM, O are only valid if RX_CMD_L is also set, + * otherwise should be ignored (rx). + */ +#define RX_STS_CE ETH__BIT(0) /* CRC Error */ +#define RX_STS_COL ETH__BIT(1) /* Collision sensed during reception */ +#define RX_STS_LC ETH__BIT(5) /* Late Collision (Reserved) */ +#define RX_STS_OR ETH__BIT(6) /* Overrun Error */ +#define RX_STS_MFL ETH__BIT(7) /* Max Frame Len Error */ +#define RX_STS_SF ETH__BIT(8) /* Short Frame Error (< 64 bytes) */ +#define RX_STS_FT ETH__BIT(11) /* Frame Type (1 = 802.3) */ +#define RX_STS_M ETH__BIT(12) /* Missed Frame */ +#define RX_STS_HE ETH__BIT(13) /* Hash Expired (manual match) */ +#define RX_STS_IGMP ETH__BIT(14) /* IGMP Packet */ +#define RX_STS_ES ETH__BIT(15) /* Error Summary (CE|COL|LC|OR|MFL|SF) */ +#define RX_CMD_L ETH__BIT(16) /* Last - End Of Packet */ +#define RX_CMD_F ETH__BIT(17) /* First - Start Of Packet */ +#define RX_CMD_EI ETH__BIT(23) /* Enable Interrupt */ +#define RX_CMD_AM ETH__BIT(30) /* Auto Mode */ +#define RX_CMD_O ETH__BIT(31) /* Ownership (1=GT 0=CPU) */ + +/* Table 586: Hash Table Entry Fields + */ +#define HSH_V ETH__LLBIT(0) /* Entry is valid */ +#define HSH_S ETH__LLBIT(1) /* Skip this entry */ +#define HSH_RD ETH__LLBIT(2) /* Receive(1) / Discard (0) */ +#define HSH_R ETH__LLBIT(2) /* Receive(1) */ +#define HSH_PRIO_GET(v) ETH__LLEXT(v, 51, 2) +#define HSH_PRIO_INS(v) ETH__LLINS(v, 51) +#define HSH_ADDR_MASK 0x7fffff8LLU +#define HSH_LIMIT 12 + + +#define ETH_EPAR 0x2000 /* PHY Address Register */ +#define ETH_ESMIR 0x2010 /* SMI Register */ + +#define ETH_BASE_ETH0 0x2400 /* Ethernet0 Register Base */ +#define ETH_BASE_ETH1 0x2800 /* Ethernet1 Register Base */ +#define ETH_BASE_ETH2 0x2c00 /* Ethernet2 Register Base */ +#define ETH_SIZE 0x0400 /* Register Space */ + +#define ETH__EBASE 0x0000 /* Base of Registers */ +#define ETH__EPCR 0x0000 /* Port Config. Register */ +#define ETH__EPCXR 0x0008 /* Port Config. Extend Reg */ +#define ETH__EPCMR 0x0010 /* Port Command Register */ +#define ETH__EPSR 0x0018 /* Port Status Register */ +#define ETH__ESPR 0x0020 /* Port Serial Parameters Reg */ +#define ETH__EHTPR 0x0028 /* Port Hash Table Pointer Reg*/ +#define ETH__EFCSAL 0x0030 /* Flow Control Src Addr Low */ +#define ETH__EFCSAH 0x0038 /* Flow Control Src Addr High */ +#define ETH__ESDCR 0x0040 /* SDMA Configuration Reg */ +#define ETH__ESDCMR 0x0048 /* SDMA Command Register */ +#define ETH__EICR 0x0050 /* Interrupt Cause Register */ +#define ETH__EIMR 0x0058 /* Interrupt Mask Register */ +#define ETH__EFRDP0 0x0080 /* First Rx Desc Pointer 0 */ +#define ETH__EFRDP1 0x0084 /* First Rx Desc Pointer 1 */ +#define ETH__EFRDP2 0x0088 /* First Rx Desc Pointer 2 */ +#define ETH__EFRDP3 0x008c /* First Rx Desc Pointer 3 */ +#define ETH__ECRDP0 0x00a0 /* Current Rx Desc Pointer 0 */ +#define ETH__ECRDP1 0x00a4 /* Current Rx Desc Pointer 1 */ +#define ETH__ECRDP2 0x00a8 /* Current Rx Desc Pointer 2 */ +#define ETH__ECRDP3 0x00ac /* Current Rx Desc Pointer 3 */ +#define ETH__ECTDP0 0x00e0 /* Current Tx Desc Pointer 0 */ +#define ETH__ECTDP1 0x00e4 /* Current Tx Desc Pointer 1 */ +#define ETH__EDSCP2P0L 0x0060 /* IP Differentiated Services + CodePoint to Priority0 low */ +#define ETH__EDSCP2P0H 0x0064 /* IP Differentiated Services + CodePoint to Priority0 high*/ +#define ETH__EDSCP2P1L 0x0068 /* IP Differentiated Services + CodePoint to Priority1 low */ +#define ETH__EDSCP2P1H 0x006c /* IP Differentiated Services + CodePoint to Priority1 high*/ +#define ETH__EVPT2P 0x0068 /* VLAN Prio. Tag to Priority */ +#define ETH__EMIBCTRS 0x0100 /* MIB Counters */ + +#define ETH_BASE(n) ETH__GEN(n, EBASE) +#define ETH_EPCR(n) ETH__GEN(n, EPCR) /* Port Config. Register */ +#define ETH_EPCXR(n) ETH__GEN(n, EPCXR) /* Port Config. Extend Reg */ +#define ETH_EPCMR(n) ETH__GEN(n, EPCMR) /* Port Command Register */ +#define ETH_EPSR(n) ETH__GEN(n, EPSR) /* Port Status Register */ +#define ETH_ESPR(n) ETH__GEN(n, ESPR) /* Port Serial Parameters Reg */ +#define ETH_EHTPR(n) ETH__GEN(n, EHPTR) /* Port Hash Table Pointer Reg*/ +#define ETH_EFCSAL(n) ETH__GEN(n, EFCSAL) /* Flow Control Src Addr Low */ +#define ETH_EFCSAH(n) ETH__GEN(n, EFCSAH) /* Flow Control Src Addr High */ +#define ETH_ESDCR(n) ETH__GEN(n, ESDCR) /* SDMA Configuration Reg */ +#define ETH_ESDCMR(n) ETH__GEN(n, ESDCMR) /* SDMA Command Register */ +#define ETH_EICR(n) ETH__GEN(n, EICR) /* Interrupt Cause Register */ +#define ETH_EIMR(n) ETH__GEN(n, EIMR) /* Interrupt Mask Register */ +#define ETH_EFRDP0(n) ETH__GEN(n, EFRDP0) /* First Rx Desc Pointer 0 */ +#define ETH_EFRDP1(n) ETH__GEN(n, EFRDP1) /* First Rx Desc Pointer 1 */ +#define ETH_EFRDP2(n) ETH__GEN(n, EFRDP2) /* First Rx Desc Pointer 2 */ +#define ETH_EFRDP3(n) ETH__GEN(n, EFRDP3) /* First Rx Desc Pointer 3 */ +#define ETH_ECRDP0(n) ETH__GEN(n, ECRDP0) /* Current Rx Desc Pointer 0 */ +#define ETH_ECRDP1(n) ETH__GEN(n, ECRDP1) /* Current Rx Desc Pointer 1 */ +#define ETH_ECRDP2(n) ETH__GEN(n, ECRDP2) /* Current Rx Desc Pointer 2 */ +#define ETH_ECRDP3(n) ETH__GEN(n, ECRDP3) /* Current Rx Desc Pointer 3 */ +#define ETH_ECTDP0(n) ETH__GEN(n, ECTDP0) /* Current Tx Desc Pointer 0 */ +#define ETH_ECTDP1(n) ETH__GEN(n, ECTDP1) /* Current Tx Desc Pointer 1 */ +#define ETH_EDSCP2P0L(n) ETH__GEN(n, EDSCP2P0L) /* IP Differentiated Services + CodePoint to Priority0 low */ +#define ETH_EDSCP2P0H(n) ETH__GEN(n, EDSCP2P0H) /* IP Differentiated Services + CodePoint to Priority0 high*/ +#define ETH_EDSCP2P1L(n) ETH__GEN(n, EDSCP2P1L) /* IP Differentiated Services + CodePoint to Priority1 low */ +#define ETH_EDSCP2P1H(n) ETH__GEN(n, EDSCP1P1H) /* IP Differentiated Services + CodePoint to Priority1 high*/ +#define ETH_EVPT2P(n) ETH__GEN(n, EVPT2P) /* VLAN Prio. Tag to Priority */ +#define ETH_EMIBCTRS(n) ETH__GEN(n, EMIBCTRS) /* MIB Counters */ + +#define ETH_EPAR_PhyAD_GET(v, n) (((v) >> ((n) * 5)) & 0x1f) + +#define ETH_ESMIR_READ(phy, reg) (ETH__INS(phy, 16)|\ + ETH__INS(reg, 21)|\ + ETH_ESMIR_ReadOpcode) +#define ETH_ESMIR_WRITE(phy, reg, val) (ETH__INS(phy, 16)|\ + ETH__INS(reg, 21)|\ + ETH__INS(val, 0)|\ + ETH_ESMIR_WriteOpcode) +#define ETH_ESMIR_Value_GET(v) ETH__EXT(v, 0, 16) +#define ETH_ESMIR_WriteOpcode 0 +#define ETH_ESMIR_ReadOpcode ETH__BIT(26) +#define ETH_ESMIR_ReadValid ETH__BIT(27) +#define ETH_ESMIR_Busy ETH__BIT(28) + +/* + * Table 597: Port Configuration Register (PCR) + * 00:00 PM Promiscuous mode + * 0: Normal mode (Frames are only received if the + * destination address is found in the hash + * table) + * 1: Promiscuous mode (Frames are received + * regardless of their destination address. + * Errored frames are discarded unless the Port + * Configuration register's PBF bit is set) + * 01:01 RBM Reject Broadcast Mode + * 0: Receive broadcast address + * 1: Reject frames with broadcast address + * Overridden by the promiscuous mode. + * 02:02 PBF Pass Bad Frames + * (0: Normal mode, 1: Pass bad Frames) + * The Ethernet receiver passes to the CPU errored + * frames (like fragments and collided packets) + * that are normally rejected. + * NOTE: Frames are only passed if they + * successfully pass address filtering. + * 06:03 Reserved + * 07:07 EN Enable (0: Disabled, 1: Enable) + * When enabled, the ethernet port is ready to + * transmit/receive. + * 09:08 LPBK Loop Back Mode + * 00: Normal mode + * 01: Internal loop back mode (TX data is looped + * back to the RX lines. No transition is seen + * on the interface pins) + * 10: External loop back mode (TX data is looped + * back to the RX lines and also transmitted + * out to the MII interface pins) + * 11: Reserved + * 10:10 FC Force Collision + * 0: Normal mode. + * 1: Force Collision on any TX frame. + * For RXM test (in Loopback mode). + * 11:11 Reserved. + * 12:12 HS Hash Size + * 0: 8K address filtering + * (256KB of memory space required). + * 1: 512 address filtering + * ( 16KB of memory space required). + * 13:13 HM Hash Mode (0: Hash Func. 0; 1: Hash Func. 1) + * 14:14 HDM Hash Default Mode + * 0: Discard addresses not found in address table + * 1: Pass addresses not found in address table + * 15:15 HD Duplex Mode (0: Half Duplex, 1: Full Duplex) + * NOTE: Valid only when auto-negotiation for + * duplex mode is disabled. + * 30:16 Reserved + * 31:31 ACCS Accelerate Slot Time + * (0: Normal mode, 1: Reserved) + */ +#define ETH_EPCR_PM ETH__BIT(0) +#define ETH_EPCR_RBM ETH__BIT(1) +#define ETH_EPCR_PBF ETH__BIT(2) +#define ETH_EPCR_EN ETH__BIT(7) +#define ETH_EPCR_LPBK_GET(v) ETH__BIT(v, 8, 2) +#define ETH_EPCR_LPBK_Normal 0 +#define ETH_EPCR_LPBK_Internal 1 +#define ETH_EPCR_LPBK_External 2 +#define ETH_EPCR_FC ETH__BIT(10) + +#define ETH_EPCR_HS ETH__BIT(12) +#define ETH_EPCR_HS_8K 0 +#define ETH_EPCR_HS_512 ETH_EPCR_HS + +#define ETH_EPCR_HM ETH__BIT(13) +#define ETH_EPCR_HM_0 0 +#define ETH_EPCR_HM_1 ETH_EPCR_HM + +#define ETH_EPCR_HDM ETH__BIT(14) +#define ETH_EPCR_HDM_Discard 0 +#define ETH_EPCR_HDM_Pass ETH_EPCR_HDM + +#define ETH_EPCR_HD_Half 0 +#define ETH_EPCR_HD_Full ETH_EPCR_HD_Full + +#define ETH_EPCR_ACCS ETH__BIT(31) + + + +/* + * Table 598: Port Configuration Extend Register (PCXR) + * 00:00 IGMP IGMP Packets Capture Enable + * 0: IGMP packets are treated as normal Multicast + * packets. + * 1: IGMP packets on IPv4/Ipv6 over Ethernet/802.3 + * are trapped and sent to high priority RX + * queue. + * 01:01 SPAN Spanning Tree Packets Capture Enable + * 0: BPDU (Bridge Protocol Data Unit) packets are + * treated as normal Multicast packets. + * 1: BPDU packets are trapped and sent to high + * priority RX queue. + * 02:02 PAR Partition Enable (0: Normal, 1: Partition) + * When more than 61 collisions occur while + * transmitting, the port enters Partition mode. + * It waits for the first good packet from the + * wire and then goes back to Normal mode. Under + * Partition mode it continues transmitting, but + * it does not receive. + * 05:03 PRIOtx Priority weight in the round-robin between high + * and low priority TX queues. + * 000: 1 pkt from HIGH, 1 pkt from LOW. + * 001: 2 pkt from HIGH, 1 pkt from LOW. + * 010: 4 pkt from HIGH, 1 pkt from LOW. + * 011: 6 pkt from HIGH, 1 pkt from LOW. + * 100: 8 pkt from HIGH, 1 pkt from LOW. + * 101: 10 pkt from HIGH, 1 pkt from LOW. + * 110: 12 pkt from HIGH, 1 pkt from LOW. + * 111: All pkt from HIGH, 0 pkt from LOW. LOW is + * served only if HIGH is empty. + * NOTE: If the HIGH queue is emptied before + * finishing the count, the count is reset + * until the next first HIGH comes in. + * 07:06 PRIOrx Default Priority for Packets Received on this + * Port (00: Lowest priority, 11: Highest priority) + * 08:08 PRIOrx_Override Override Priority for Packets Received on this + * Port (0: Do not override, 1: Override with + * field) + * 09:09 DPLXen Enable Auto-negotiation for Duplex Mode + * (0: Enable, 1: Disable) + * 11:10 FCTLen Enable Auto-negotiation for 802.3x Flow-control + * 0: Enable; When enabled, 1 is written (through + * SMI access) to the PHY's register 4 bit 10 + * to advertise flow-control capability. + * 1: Disable; Only enables flow control after the + * PHY address is set by the CPU. When changing + * the PHY address the flow control + * auto-negotiation must be disabled. + * 11:11 FLP Force Link Pass + * (0: Force Link Pass, 1: Do NOT Force Link pass) + * 12:12 FCTL 802.3x Flow-Control Mode (0: Enable, 1: Disable) + * NOTE: Only valid when auto negotiation for flow + * control is disabled. + * 13:13 Reserved + * 15:14 MFL Max Frame Length + * Maximum packet allowed for reception (including + * CRC): 00: 1518 bytes, 01: 1536 bytes, + * 10: 2048 bytes, 11: 64K bytes + * 16:16 MIBclrMode MIB Counters Clear Mode (0: Clear, 1: No effect) + * 17:17 MIBctrMode Reserved. (MBZ) + * 18:18 Speed Port Speed (0: 10Mbit/Sec, 1: 100Mbit/Sec) + * NOTE: Only valid if SpeedEn bit is set. + * 19:19 SpeedEn Enable Auto-negotiation for Speed + * (0: Enable, 1: Disable) + * 20:20 RMIIen RMII enable + * 0: Port functions as MII port + * 1: Port functions as RMII port + * 21:21 DSCPen DSCP enable + * 0: IP DSCP field decoding is disabled. + * 1: IP DSCP field decoding is enabled. + * 31:22 Reserved + */ +#define ETH_EPCXR_IGMP ETH__BIT(0) +#define ETH_EPCXR_SPAN ETH__BIT(1) +#define ETH_EPCXR_PAR ETH__BIT(2) +#define ETH_EPCXR_PRIOtx_GET(v) ETH__EXT(v, 3, 3) +#define ETH_EPCXR_PRIOrx_GET(v) ETH__EXT(v, 3, 3) +#define ETH_EPCXR_PRIOrx_Override ETH__BIT(8) +#define ETH_EPCXR_DLPXen ETH__BIT(9) +#define ETH_EPCXR_FCTLen ETH__BIT(10) +#define ETH_EPCXR_FLP ETH__BIT(11) +#define ETH_EPCXR_FCTL ETH__BIT(12) +#define ETH_EPCXR_MFL_GET(v) ETH__EXT(v, 14, 2) +#define ETH_EPCXR_MFL_1518 0 +#define ETH_EPCXR_MFL_1536 1 +#define ETH_EPCXR_MFL_2084 2 +#define ETH_EPCXR_MFL_64K 3 +#define ETH_EPCXR_MIBclrMode ETH__BIT(16) +#define ETH_EPCXR_MIBctrMode ETH__BIT(17) +#define ETH_EPCXR_Speed ETH__BIT(18) +#define ETH_EPCXR_SpeedEn ETH__BIT(19) +#define ETH_EPCXR_RMIIEn ETH__BIT(20) +#define ETH_EPCXR_DSCPEn ETH__BIT(21) + + + +/* + * Table 599: Port Command Register (PCMR) + * 14:00 Reserved + * 15:15 FJ Force Jam / Flow Control + * When in half-duplex mode, the CPU uses this bit + * to force collisions on the Ethernet segment. + * When the CPU recognizes that it is going to run + * out of receive buffers, it can force the + * transmitter to send jam frames, forcing + * collisions on the wire. To allow transmission + * on the Ethernet segment, the CPU must clear the + * FJ bit when more resources are available. When + * in full-duplex and flow-control is enabled, this + * bit causes the port's transmitter to send + * flow-control PAUSE packets. The CPU must reset + * this bit when more resources are available. + * 31:16 Reserved + */ + +#define ETH_EPCMR_FJ ETH__BIT(15) + + +/* + * Table 600: Port Status Register (PSR) -- Read Only + * 00:00 Speed Indicates Port Speed (0: 10Mbs, 1: 100Mbs) + * 01:01 Duplex Indicates Port Duplex Mode (0: Half, 1: Full) + * 02:02 Fctl Indicates Flow-control Mode + * (0: enabled, 1: disabled) + * 03:03 Link Indicates Link Status (0: down, 1: up) + * 04:04 Pause Indicates that the port is in flow-control + * disabled state. This bit is set when an IEEE + * 802.3x flow-control PAUSE (XOFF) packet is + * received (assuming that flow-control is + * enabled and the port is in full-duplex mode). + * Reset when XON is received, or when the XOFF + * timer has expired. + * 05:05 TxLow Tx Low Priority Status + * Indicates the status of the low priority + * transmit queue: (0: Stopped, 1: Running) + * 06:06 TxHigh Tx High Priority Status + * Indicates the status of the high priority + * transmit queue: (0: Stopped, 1: Running) + * 07:07 TXinProg TX in Progress + * Indicates that the port's transmitter is in an + * active transmission state. + * 31:08 Reserved + */ +#define ETH_EPSR_Speed ETH__BIT(0) +#define ETH_EPSR_Duplex ETH__BIT(1) +#define ETH_EPSR_Fctl ETH__BIT(2) +#define ETH_EPSR_Link ETH__BIT(3) +#define ETH_EPSR_Pause ETH__BIT(4) +#define ETH_EPSR_TxLow ETH__BIT(5) +#define ETH_EPSR_TxHigh ETH__BIT(6) +#define ETH_EPSR_TXinProg ETH__BIT(7) + + +/* + * Table 601: Serial Parameters Register (SPR) + * 01:00 JAM_LENGTH Two bits to determine the JAM Length + * (in Backpressure) as follows: + * 00 = 12K bit-times + * 01 = 24K bit-times + * 10 = 32K bit-times + * 11 = 48K bit-times + * 06:02 JAM_IPG Five bits to determine the JAM IPG. + * The step is four bit-times. The value may vary + * between 4 bit time to 124. + * 11:07 IPG_JAM_TO_DATA Five bits to determine the IPG JAM to DATA. + * The step is four bit-times. The value may vary + * between 4 bit time to 124. + * 16:12 IPG_DATA Inter-Packet Gap (IPG) + * The step is four bit-times. The value may vary + * between 12 bit time to 124. + * NOTE: These bits may be changed only when the + * Ethernet ports is disabled. + * 21:17 Data_Blind Data Blinder + * The number of nibbles from the beginning of the + * IPG, in which the IPG counter is restarted when + * detecting a carrier activity. Following this + * value, the port enters the Data Blinder zone and + * does not reset the IPG counter. This ensures + * fair access to the medium. + * The default is 10 hex (64 bit times - 2/3 of the + * default IPG). The step is 4 bit-times. Valid + * range is 3 to 1F hex nibbles. + * NOTE: These bits may be only changed when the + * Ethernet port is disabled. + * 22:22 Limit4 The number of consecutive packet collisions that + * occur before the collision counter is reset. + * 0: The port resets its collision counter after + * 16 consecutive retransmit trials and + * restarts the Backoff algorithm. + * 1: The port resets its collision counter and + * restarts the Backoff algorithm after 4 + * consecutive transmit trials. + * 31:23 Reserved + */ +#define ETH_ESPR_JAM_LENGTH_GET(v) ETH__EXT(v, 0, 2) +#define ETH_ESPR_JAM_IPG_GET(v) ETH__EXT(v, 2, 5) +#define ETH_ESPR_IPG_JAM_TO_DATA_GET(v) ETH__EXT(v, 7, 5) +#define ETH_ESPR_IPG_DATA_GET(v) ETH__EXT(v, 12, 5) +#define ETH_ESPR_Data_Bilnd_GET(v) ETH__EXT(v, 17, 5) +#define ETH_ESPR_Limit4(v) ETH__BIT(22) + +/* + * Table 602: Hash Table Pointer Register (HTPR) + * 31:00 HTP 32-bit pointer to the address table. + * Bits [2:0] must be set to zero. + */ + +/* + * Table 603: Flow Control Source Address Low (FCSAL) + * 15:0 SA[15:0] Source Address + * The least significant bits of the source + * address for the port. This address is used for + * Flow Control. + * 31:16 Reserved + */ + +/* + * Table 604: Flow Control Source Address High (FCSAH) + * 31:0 SA[47:16] Source Address + * The most significant bits of the source address + * for the port. This address is used for Flow + * Control. + */ + + +/* + * Table 605: SDMA Configuration Register (SDCR) + * 01:00 Reserved + * 05:02 RC Retransmit Count + * Sets the maximum number of retransmits per + * packet. After executing retransmit for RC + * times, the TX SDMA closes the descriptor with a + * Retransmit Limit error indication and processes + * the next packet. When RC is set to 0, the + * number of retransmits is unlimited. In this + * case, the retransmit process is only terminated + * if CPU issues an Abort command. + * 06:06 BLMR Big/Little Endian Receive Mode + * The DMA supports Big or Little Endian + * configurations on a per channel basis. The BLMR + * bit only affects data transfer to memory. + * 0: Big Endian + * 1: Little Endian + * 07:07 BLMT Big/Little Endian Transmit Mode + * The DMA supports Big or Little Endian + * configurations on a per channel basis. The BLMT + * bit only affects data transfer from memory. + * 0: Big Endian + * 1: Little Endian + * 08:08 POVR PCI Override + * When set, causes the SDMA to direct all its + * accesses in PCI_0 direction and overrides + * normal address decoding process. + * 09:09 RIFB Receive Interrupt on Frame Boundaries + * When set, the SDMA Rx generates interrupts only + * on frame boundaries (i.e. after writing the + * frame status to the descriptor). + * 11:10 Reserved + * 13:12 BSZ Burst Size + * Sets the maximum burst size for SDMA + * transactions: + * 00: Burst is limited to 1 64bit words. + * 01: Burst is limited to 2 64bit words. + * 10: Burst is limited to 4 64bit words. + * 11: Burst is limited to 8 64bit words. + * 31:14 Reserved + */ +#define ETH_ESDCR_RC_GET(v) ETH__EXT(v, 2, 4) +#define ETH_ESDCR_BLMR ETH__BIT(6) +#define ETH_ESDCR_BLMT ETH__BIT(7) +#define ETH_ESDCR_POVR ETH__BIT(8) +#define ETH_ESDCR_RIFB ETH__BIT(9) +#define ETH_ESDCR_BSZ_GET(v) ETH__EXT(v, 12, 2) +#define ETH_ESDCR_BSZ_SET(v, n) (ETH__CLR(v, 12, 2),\ + (v) |= ETH__INS(n, 12)) +#define ETH_ESDCR_BSZ_1 0 +#define ETH_ESDCR_BSZ_2 1 +#define ETH_ESDCR_BSZ_4 2 +#define ETH_ESDCR_BSZ_8 3 + +#define ETH_ESDCR_BSZ_Strings { "1 64-bit word", "2 64-bit words", \ + "4 64-bit words", "8 64-bit words" } + +/* + * Table 606: SDMA Command Register (SDCMR) + * 06:00 Reserved + * 07:07 ERD Enable RX DMA. + * Set to 1 by the CPU to cause the SDMA to start + * a receive process. Cleared when the CPU issues + * an Abort Receive command. + * 14:08 Reserved + * 15:15 AR Abort Receive + * Set to 1 by the CPU to abort a receive SDMA + * operation. When the AR bit is set, the SDMA + * aborts its current operation and moves to IDLE. + * No descriptor is closed. The AR bit is cleared + * upon entering IDLE. After setting the AR bit, + * the CPU must poll the bit to verify that the + * abort sequence is completed. + * 16:16 STDH Stop TX High + * Set to 1 by the CPU to stop the transmission + * process from the high priority queue at the end + * of the current frame. An interrupt is generated + * when the stop command has been executed. + * Writing 1 to STDH resets TXDH bit. + * Writing 0 to this bit has no effect. + * 17:17 STDL Stop TX Low + * Set to 1 by the CPU to stop the transmission + * process from the low priority queue at the end + * of the current frame. An interrupt is generated + * when the stop command has been executed. + * Writing 1 to STDL resets TXDL bit. + * Writing 0 to this bit has no effect. + * 22:18 Reserved + * 23:23 TXDH Start Tx High + * Set to 1 by the CPU to cause the SDMA to fetch + * the first descriptor and start a transmit + * process from the high priority Tx queue. + * Writing 1 to TXDH resets STDH bit. + * Writing 0 to this bit has no effect. + * 24:24 TXDL Start Tx Low + * Set to 1 by the CPU to cause the SDMA to fetch + * the first descriptor and start a transmit + * process from the low priority Tx queue. + * Writing 1 to TXDL resets STDL bit. + * Writing 0 to this bit has no effect. + * 30:25 Reserved + * 31:31 AT Abort Transmit + * Set to 1 by the CPU to abort a transmit DMA + * operation. When the AT bit is set, the SDMA + * aborts its current operation and moves to IDLE. + * No descriptor is closed. Cleared upon entering + * IDLE. After setting AT bit, the CPU must poll + * it in order to verify that the abort sequence + * is completed. + */ +#define ETH_ESDCMR_ERD ETH__BIT(7) +#define ETH_ESDCMR_AR ETH__BIT(15) +#define ETH_ESDCMR_STDH ETH__BIT(16) +#define ETH_ESDCMR_STDL ETH__BIT(17) +#define ETH_ESDCMR_TXDH ETH__BIT(23) +#define ETH_ESDCMR_TXDL ETH__BIT(24) +#define ETH_ESDCMR_AT ETH__BIT(31) + +/* + * Table 607: Interrupt Cause Register (ICR) + * 00:00 RxBuffer Rx Buffer Return + * Indicates an Rx buffer returned to CPU ownership + * or that the port finished reception of a Rx + * frame in either priority queues. + * NOTE: In order to get a Rx Buffer return per + * priority queue, use bit 19:16. This bit is + * set upon closing any Rx descriptor which + * has its EI bit set. To limit the + * interrupts to frame (rather than buffer) + * boundaries, the user must set SDMA + * Configuration register's RIFB bit. When + * the RIFB bit is set, an interrupt + * generates only upon closing the first + * descriptor of a received packet, if this + * descriptor has it EI bit set. + * 01:01 Reserved + * 02:02 TxBufferHigh Tx Buffer for High priority Queue + * Indicates a Tx buffer returned to CPU ownership + * or that the port finished transmission of a Tx + * frame. + * NOTE: This bit is set upon closing any Tx + * descriptor which has its EI bit set. To + * limit the interrupts to frame (rather than + * buffer) boundaries, the user must set EI + * only in the last descriptor. + * 03:03 TxBufferLow Tx Buffer for Low Priority Queue + * Indicates a Tx buffer returned to CPU ownership + * or that the port finished transmission of a Tx + * frame. + * NOTE: This bit is set upon closing any Tx + * descriptor which has its EI bit set. To + * limit the interrupts to frame (rather than + * buffer) boundaries, the user must set EI + * only in the last descriptor. + * 05:04 Reserved + * 06:06 TxEndHigh Tx End for High Priority Queue + * Indicates that the Tx DMA stopped processing the + * high priority queue after stop command, or that + * it reached the end of the high priority + * descriptor chain. + * 07:07 TxEndLow Tx End for Low Priority Queue + * Indicates that the Tx DMA stopped processing the + * low priority queue after stop command, or that + * it reached the end of the low priority + * descriptor chain. + * 08:08 RxError Rx Resource Error + * Indicates a Rx resource error event in one of + * the priority queues. + * NOTE: To get a Rx Resource Error Indication per + * priority queue, use bit 23:20. + * 09:09 Reserved + * 10:10 TxErrorHigh Tx Resource Error for High Priority Queue + * Indicates a Tx resource error event during + * packet transmission from the high priority queue + * 11:11 TxErrorLow Tx Resource Error for Low Priority Queue + * Indicates a Tx resource error event during + * packet transmission from the low priority queue + * 12:12 RxOVR Rx Overrun + * Indicates an overrun event that occurred during + * reception of a packet. + * 13:13 TxUdr Tx Underrun + * Indicates an underrun event that occurred during + * transmission of packet from either queue. + * 15:14 Reserved + * 16:16 RxBuffer-Queue[0] Rx Buffer Return in Priority Queue[0] + * Indicates a Rx buffer returned to CPU ownership + * or that the port completed reception of a Rx + * frame in a receive priority queue[0] + * 17:17 RxBuffer-Queue[1] Rx Buffer Return in Priority Queue[1] + * Indicates a Rx buffer returned to CPU ownership + * or that the port completed reception of a Rx + * frame in a receive priority queue[1]. + * 18:18 RxBuffer-Queue[2] Rx Buffer Return in Priority Queue[2] + * Indicates a Rx buffer returned to CPU ownership + * or that the port completed reception of a Rx + * frame in a receive priority queue[2]. + * 19:19 RxBuffer-Queue[3] Rx Buffer Return in Priority Queue[3] + * Indicates a Rx buffer returned to CPU ownership + * or that the port completed reception of a Rx + * frame in a receive priority queue[3]. + * 20:20 RxError-Queue[0] Rx Resource Error in Priority Queue[0] + * Indicates a Rx resource error event in receive + * priority queue[0]. + * 21:21 RxError-Queue[1] Rx Resource Error in Priority Queue[1] + * Indicates a Rx resource error event in receive + * priority queue[1]. + * 22:22 RxError-Queue[2] Rx Resource Error in Priority Queue[2] + * Indicates a Rx resource error event in receive + * priority queue[2]. + * 23:23 RxError-Queue[3] Rx Resource Error in Priority Queue[3] + * Indicates a Rx resource error event in receive + * priority queue[3]. + * 27:24 Reserved + * 28:29 MIIPhySTC MII PHY Status Change + * Indicates a status change reported by the PHY + * connected to this port. Set when the MII + * management interface block identifies a change + * in PHY's register 1. + * 29:29 SMIdone SMI Command Done + * Indicates that the SMI completed a MII + * management command (either read or write) that + * was initiated by the CPU writing to the SMI + * register. + * 30:30 Reserved + * 31:31 EtherIntSum Ethernet Interrupt Summary + * This bit is a logical OR of the (unmasked) bits + * [30:04] in the Interrupt Cause register. + */ + +#define ETH_IR_RxBuffer ETH__BIT(0) +#define ETH_IR_TxBufferHigh ETH__BIT(2) +#define ETH_IR_TxBufferLow ETH__BIT(3) +#define ETH_IR_TxEndHigh ETH__BIT(6) +#define ETH_IR_TxEndLow ETH__BIT(7) +#define ETH_IR_RxError ETH__BIT(8) +#define ETH_IR_TxErrorHigh ETH__BIT(10) +#define ETH_IR_TxErrorLow ETH__BIT(11) +#define ETH_IR_RxOVR ETH__BIT(12) +#define ETH_IR_TxUdr ETH__BIT(13) +#define ETH_IR_RxBuffer_0 ETH__BIT(16) +#define ETH_IR_RxBuffer_1 ETH__BIT(17) +#define ETH_IR_RxBuffer_2 ETH__BIT(18) +#define ETH_IR_RxBuffer_3 ETH__BIT(19) +#define ETH_IR_RxBuffer_GET(v) ETH__EXT(v, 16, 4) +#define ETH_IR_RxError_0 ETH__BIT(20) +#define ETH_IR_RxError_1 ETH__BIT(21) +#define ETH_IR_RxError_2 ETH__BIT(22) +#define ETH_IR_RxError_3 ETH__BIT(23) +#define ETH_IR_RxError_GET(v) ETH__EXT(v, 20, 4) +#define ETH_IR_RxBits (ETH_IR_RxBuffer_0|\ + ETH_IR_RxBuffer_1|\ + ETH_IR_RxBuffer_2|\ + ETH_IR_RxBuffer_3|\ + ETH_IR_RxError_0|\ + ETH_IR_RxError_1|\ + ETH_IR_RxError_2|\ + ETH_IR_RxError_3) +#define ETH_IR_MIIPhySTC ETH__BIT(28) +#define ETH_IR_SMIdone ETH__BIT(29) +#define ETH_IR_EtherIntSum ETH__BIT(31) +#define ETH_IR_Summary ETH__BIT(31) + +/* + * Table 608: Interrupt Mask Register (IMR) + * 31:00 Various Mask bits for the Interrupt Cause register. + */ + +/* + * Table 609: IP Differentiated Services CodePoint to Priority0 low (DSCP2P0L), + * 31:00 Priority0_low The LSB priority bits for DSCP[31:0] entries. + */ + +/* + * Table 610: IP Differentiated Services CodePoint to Priority0 high (DSCP2P0H) + * 31:00 Priority0_high The LSB priority bits for DSCP[63:32] entries. + */ + +/* + * Table 611: IP Differentiated Services CodePoint to Priority1 low (DSCP2P1L) + * 31:00 Priority1_low The MSB priority bits for DSCP[31:0] entries. + */ + +/* + * Table 612: IP Differentiated Services CodePoint to Priority1 high (DSCP2P1H) + * 31:00 Priority1_high The MSB priority bit for DSCP[63:32] entries. + */ + +/* + * Table 613: VLAN Priority Tag to Priority (VPT2P) + * 07:00 Priority0 The LSB priority bits for VLAN Priority[7:0] + * entries. + * 15:08 Priority1 The MSB priority bits for VLAN Priority[7:0] + * entries. + * 31:16 Reserved + */ + +/* Address control registers -- these are offsets from the GT base + * and NOT from the ethernet port base. + */ +#define ETH_ACTL_0_LO 0xf200 + +/* Enable hardware cache snooping; + * Copyright Shuchen K. Feng , 2004 + */ + +/* Ethernet address control (Low) snoop bits */ +#define RxBSnoopEn ETH__BIT(6) /* Rx buffer snoop enable,1=enable*/ +#define TxBSnoopEn ETH__BIT(14) /* Tx buffer snoop enable */ +#define RxDSnoopEn ETH__BIT(22) /* Rx descriptor snoop enable */ +#define TxDSnoopEn ETH__BIT(30) /* Tx descriptor snoop enable */ + +#define ETH_ACTL_0_HI 0xf204 +/* Ethernet address control (High), snoop bits */ +#define HashSnoopEn ETH__BIT(6) /* Hash Table snoop enable */ + + +#define ETH_ACTL_1_LO 0xf220 +#define ETH_ACTL_1_HI 0xf224 +#define ETH_ACTL_2_LO 0xf240 +#define ETH_ACTL_2_HI 0xf244 + + +#endif /* _DEV_GTETHREG_H_ */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtvar.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtvar.h new file mode 100644 index 0000000000..00d72bfa3a --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/gtvar.h @@ -0,0 +1,170 @@ +/* $NetBSD: gtvar.h,v 1.7.4.1 2005/04/29 11:28:56 kent Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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. + */ + +/* + * gtvar.h -- placeholder for GT system controller driver + */ +#ifndef _DISCOVERY_DEV_GTVAR_H_ +#define _DISCOVERY_DEV_GTVAR_H_ + +#include + +struct gt_softc { +#ifndef __rtems__ + struct device gt_dev; + bus_dma_tag_t gt_dmat; + bus_space_tag_t gt_memt; /* the GT itself */ + bus_space_tag_t gt_pci0_memt; /* PCI0 mem space */ + bus_space_tag_t gt_pci0_iot; /* PCI0 i/o space */ + boolean_t gt_pci0_host; /* We're host on PCI0 if TRUE */ + bus_space_tag_t gt_pci1_memt; /* PCI1 mem space */ + bus_space_tag_t gt_pci1_iot; /* PCI1 i/o space */ + boolean_t gt_pci1_host; /* We're host on PCI1 if TRUE */ + + bus_space_handle_t gt_memh; /* to access the GT registers */ +#else + unsigned gt_memh; +#endif + int gt_childmask; /* what children are present */ +}; + +#define GT_CHILDOK(gt, ga, cd, pos, max) \ + (((ga)->ga_unit) < (max) && \ + !((gt)->gt_childmask & (1 << (((ga)->ga_unit) + (pos)))) && \ + !strcmp((ga)->ga_name, (cd)->cd_name)) + +#define GT_MPSCOK(gt, ga, cd) GT_CHILDOK((gt), (ga), (cd), 0, 2) +#define GT_PCIOK(gt, ga, cd) GT_CHILDOK((gt), (ga), (cd), 2, 2) +#define GT_ETHEROK(gt, ga, cd) GT_CHILDOK((gt), (ga), (cd), 4, 3) +#define GT_OBIOOK(gt, ga, cd) GT_CHILDOK((gt), (ga), (cd), 7, 5) +#define GT_I2COK(gt, ga, cd) GT_CHILDOK((gt), (ga), (cd), 12, 1) + +#define GT_CHILDFOUND(gt, ga, pos) \ + ((void)(((gt)->gt_childmask |= (1 << (((ga)->ga_unit) + (pos)))))) + +#define GT_MPSCFOUND(gt, ga) GT_CHILDFOUND((gt), (ga), 0) +#define GT_PCIFOUND(gt, ga) GT_CHILDFOUND((gt), (ga), 2) +#define GT_ETHERFOUND(gt, ga) GT_CHILDFOUND((gt), (ga), 4) +#define GT_OBIOFOUND(gt, ga) GT_CHILDFOUND((gt), (ga), 7) +#define GT_I2CFOUND(gt, ga) GT_CHILDFOUND((gt), (ga), 12) + +#ifndef __rtems__ +struct gt_attach_args { + const char *ga_name; /* class name of device */ + bus_dma_tag_t ga_dmat; /* dma tag */ + bus_space_tag_t ga_memt; /* GT bus space tag */ + bus_space_handle_t ga_memh; /* GT bus space handle */ + int ga_unit; /* instance of ga_name */ +}; + +struct obio_attach_args { + const char *oa_name; /* call name of device */ + bus_space_tag_t oa_memt; /* bus space tag */ + bus_addr_t oa_offset; /* offset (absolute) to device */ + bus_size_t oa_size; /* size (strided) of device */ + int oa_irq; /* irq */ +}; +#endif + +#ifdef _KERNEL +#ifndef __rtems__ +#include "locators.h" +#endif + +#ifdef DEBUG +extern int gtpci_debug; +#endif + +/* + * Locators for GT private devices, as specified to config. + */ +#define GT_UNK_UNIT GTCF_UNIT_DEFAULT /* wcarded 'function' */ + +#define OBIO_UNK_OFFSET OBIOCF_OFFSET_DEFAULT /* wcarded 'offset' */ + +#define OBIO_UNK_SIZE OBIOCF_SIZE_DEFAULT /* wcarded 'size' */ + +#define OBIO_UNK_IRQ OBIOCF_IRQ_DEFAULT /* wcarded 'irq' */ + +void gt_attach_common(struct gt_softc *); +uint32_t gt_read_mpp(void); +int gt_cfprint(void *, const char *); + +#ifndef __rtems__ +/* int gt_bs_extent_init(struct discovery_bus_space *, char *); AKB */ +int gt_mii_read(struct device *, struct device *, int, int); +void gt_mii_write(struct device *, struct device *, int, int, int); +int gtget_macaddr(struct gt_softc *,int, char *); + +void gt_watchdog_service(void); +bus_addr_t gt_dma_phys_to_bus_mem(bus_dma_tag_t, bus_addr_t); +bus_addr_t gt_dma_bus_mem_to_phys(bus_dma_tag_t, bus_addr_t); + +#define gt_read(gt,o) \ + bus_space_read_4((gt)->gt_memt, (gt)->gt_memh, (o)) +#define gt_write(gt,o,v) \ + bus_space_write_4((gt)->gt_memt, (gt)->gt_memh, (o), (v)) +#else +#endif + +#if defined(__powerpc__) +static __inline volatile int +atomic_add(volatile int *p, int v) +{ + int rv; + int rtmp; + + __asm __volatile( + "1: lwarx %0,0,%3\n" + " add %1,%4,%0\n" + " stwcx. %1,0,%3\n" + " bne- 1b\n" + " sync" + : "=&r"(rv), "=&r"(rtmp), "=m"(*p) + : "r"(p), "r"(v), "m"(*p) + : "cc"); + + return rv; +} + +#endif /* __powerpc__ */ + +#endif /* _KERNEL */ + +#endif /* _DISCOVERY_DEV_GTVAR_H_ */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe.c new file mode 100644 index 0000000000..47ceb98182 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe.c @@ -0,0 +1,2641 @@ +/* $NetBSD: if_gfe.c,v 1.13.8.1 2005/04/29 11:28:56 kent Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * All rights reserved. + * + * Copyright 2004: Enable hardware cache snooping. Kate Feng + * + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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_gfe.c -- GT ethernet MAC driver + */ + +/* Enable hardware cache snooping; + * Copyright Shuchen K. Feng , 2004 + */ + +#ifdef __rtems__ +#include +#include +#include +#include +#endif + +#include +#ifndef __rtems__ +__KERNEL_RCSID(0, "$NetBSD: if_gfe.c,v 1.13.8.1 2005/04/29 11:28:56 kent Exp $"); + +#include "opt_inet.h" +#include "bpfilter.h" +#endif + +#include +#include +#ifndef __rtems__ +#include +#include +#endif + +#ifndef __rtems__ +#include + +#include +#include +#endif +#include +#include +#include +#include + +#ifndef __rtems__ +#include +#endif + +#include +#include +#include +#ifndef __rtems__ +#include +#else +#include +#include +#include +#include +#endif + +#ifdef INET +#include +#ifndef __rtems__ +#include +#endif +#endif +#if NBPFILTER > 0 +#include +#endif + +#ifndef __rtems__ +#include + +#include +#include + +#include +#include +#else +#include +#include +#include "gtethreg.h" + +#include "gtvar.h" +#include "if_gfevar.h" +#include +#define ether_sprintf ether_sprintf_macro +#endif + +#define GE_READ(sc, reg) \ + bus_space_read_4((sc)->sc_gt_memt, (sc)->sc_memh, ETH__ ## reg) +#define GE_WRITE(sc, reg, v) \ + bus_space_write_4((sc)->sc_gt_memt, (sc)->sc_memh, ETH__ ## reg, (v)) + +#define GT_READ(sc, reg) \ + bus_space_read_4((sc)->sc_gt_memt, (sc)->sc_gt_memh, reg) +#define GT_WRITE(sc, reg, v) \ + bus_space_write_4((sc)->sc_gt_memt, (sc)->sc_gt_memh, reg, (v)) + +#define GE_DEBUG +#if 0 +#define GE_NOHASH +#define GE_NORX +#endif + +#ifdef GE_DEBUG +#define GE_DPRINTF(sc, a) do \ + if ((sc)->sc_ec.ec_if.if_flags & IFF_DEBUG) \ + printf a; \ + while (0) +#define GE_FUNC_ENTER(sc, func) GE_DPRINTF(sc, ("[" func)) +#define GE_FUNC_EXIT(sc, str) GE_DPRINTF(sc, (str "]")) +#else +#define GE_DPRINTF(sc, a) do { } while (0) +#define GE_FUNC_ENTER(sc, func) do { } while (0) +#define GE_FUNC_EXIT(sc, str) do { } while (0) +#endif +enum gfe_whack_op { + GE_WHACK_START, GE_WHACK_RESTART, + GE_WHACK_CHANGE, GE_WHACK_STOP +}; + +enum gfe_hash_op { + GE_HASH_ADD, GE_HASH_REMOVE, +}; + + +#if 1 +#define htogt32(a) htobe32(a) +#define gt32toh(a) be32toh(a) +#else +#define htogt32(a) htole32(a) +#define gt32toh(a) le32toh(a) +#endif + +#ifdef __rtems__ +#define htobe32 htonl +#define be32toh ntohl +#endif + +#define GE_RXDSYNC(sc, rxq, n, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (rxq)->rxq_desc_mem.gdm_map, \ + (n) * sizeof((rxq)->rxq_descs[0]), sizeof((rxq)->rxq_descs[0]), \ + (ops)) +#define GE_RXDPRESYNC(sc, rxq, n) \ + GE_RXDSYNC(sc, rxq, n, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE) +#define GE_RXDPOSTSYNC(sc, rxq, n) \ + GE_RXDSYNC(sc, rxq, n, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE) + +#define GE_TXDSYNC(sc, txq, n, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (txq)->txq_desc_mem.gdm_map, \ + (n) * sizeof((txq)->txq_descs[0]), sizeof((txq)->txq_descs[0]), \ + (ops)) +#define GE_TXDPRESYNC(sc, txq, n) \ + GE_TXDSYNC(sc, txq, n, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE) +#define GE_TXDPOSTSYNC(sc, txq, n) \ + GE_TXDSYNC(sc, txq, n, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE) + +#define STATIC + +#ifndef __rtems__ +STATIC int gfe_match (struct device *, struct cfdata *, void *); +STATIC void gfe_attach (struct device *, struct device *, void *); +#else +STATIC int gfe_probe (device_t); +STATIC int gfe_attach (device_t); +STATIC void gfe_init (void*); +#endif + +STATIC int gfe_dmamem_alloc(struct gfe_softc *, struct gfe_dmamem *, int, + size_t, int); +STATIC void gfe_dmamem_free(struct gfe_softc *, struct gfe_dmamem *); + +#ifndef __rtems__ +STATIC int gfe_ifioctl (struct ifnet *, u_long, caddr_t); +#else +STATIC int gfe_ifioctl (struct ifnet *, ioctl_command_t, caddr_t); +#endif +STATIC void gfe_ifstart (struct ifnet *); +STATIC void gfe_ifwatchdog (struct ifnet *); + +#ifndef __rtems__ +STATIC int gfe_mii_mediachange (struct ifnet *); +STATIC void gfe_mii_mediastatus (struct ifnet *, struct ifmediareq *); +STATIC int gfe_mii_read (struct device *, int, int); +STATIC void gfe_mii_write (struct device *, int, int, int); +STATIC void gfe_mii_statchg (struct device *); +#endif + +STATIC void gfe_tick(void *arg); + +STATIC void gfe_tx_restart(void *); +STATIC int gfe_tx_enqueue(struct gfe_softc *, enum gfe_txprio); +STATIC uint32_t gfe_tx_done(struct gfe_softc *, enum gfe_txprio, uint32_t); +STATIC void gfe_tx_cleanup(struct gfe_softc *, enum gfe_txprio, int); +STATIC int gfe_tx_txqalloc(struct gfe_softc *, enum gfe_txprio); +STATIC int gfe_tx_start(struct gfe_softc *, enum gfe_txprio); +STATIC void gfe_tx_stop(struct gfe_softc *, enum gfe_whack_op); + +STATIC void gfe_rx_cleanup(struct gfe_softc *, enum gfe_rxprio); +STATIC void gfe_rx_get(struct gfe_softc *, enum gfe_rxprio); +STATIC int gfe_rx_prime(struct gfe_softc *); +STATIC uint32_t gfe_rx_process(struct gfe_softc *, uint32_t, uint32_t); +STATIC int gfe_rx_rxqalloc(struct gfe_softc *, enum gfe_rxprio); +STATIC int gfe_rx_rxqinit(struct gfe_softc *, enum gfe_rxprio); +STATIC void gfe_rx_stop(struct gfe_softc *, enum gfe_whack_op); + +STATIC int gfe_intr(void *); + +STATIC int gfe_whack(struct gfe_softc *, enum gfe_whack_op); + +STATIC int gfe_hash_compute(struct gfe_softc *, const uint8_t [ETHER_ADDR_LEN]); +STATIC int gfe_hash_entry_op(struct gfe_softc *, enum gfe_hash_op, + enum gfe_rxprio, const uint8_t [ETHER_ADDR_LEN]); +#ifndef __rtems__ +STATIC int gfe_hash_multichg(struct ethercom *, const struct ether_multi *, + u_long); +#endif +STATIC int gfe_hash_fill(struct gfe_softc *); +STATIC int gfe_hash_alloc(struct gfe_softc *); + +#ifndef __rtems__ +/* Linkup to the rest of the kernel */ +CFATTACH_DECL(gfe, sizeof(struct gfe_softc), + gfe_match, gfe_attach, NULL, NULL); +#else +net_drv_tbl_t METHODS = { + n_probe : gfe_probe, + n_attach : gfe_attach, + n_detach : 0, + n_intr : (void (*)(void*))gfe_intr, +}; + +int +gfe_mii_read(int phy, void *arg, unsigned reg, uint32_t *pval); +int +gfe_mii_write(int phy, void *arg, unsigned reg, uint32_t val); + +struct rtems_mdio_info +gfe_mdio_access = { + mdio_r: gfe_mii_read, + mdio_w: gfe_mii_write, + has_gmii: 0 +}; + +#endif + +extern struct cfdriver gfe_cd; + +#ifndef __rtems__ +int +gfe_match(struct device *parent, struct cfdata *cf, void *aux) +{ + struct gt_softc *gt = (struct gt_softc *) parent; + struct gt_attach_args *ga = aux; + uint8_t enaddr[6]; + + if (!GT_ETHEROK(gt, ga, &gfe_cd)) + return 0; + + if (gtget_macaddr(gt, ga->ga_unit, enaddr) < 0) + return 0; + + if (enaddr[0] == 0 && enaddr[1] == 0 && enaddr[2] == 0 && + enaddr[3] == 0 && enaddr[4] == 0 && enaddr[5] == 0) + return 0; + + return 1; +} +#else +int +gfe_probe(device_t dev) +{ + switch ( BSP_getDiscoveryVersion(0) ) { + case GT_64260_A: + case GT_64260_B: + return 0; + default: + break; + } + return -1; +} + +void +gfe_init(void *arg) +{ +struct gfe_softc *sc = arg; + if ( sc->sc_ec.ec_if.if_flags & IFF_RUNNING ) + gfe_whack(sc, GE_WHACK_RESTART); + else + gfe_whack(sc, GE_WHACK_START); +} +#endif + +/* + * Attach this instance, and then all the sub-devices + */ +#ifndef __rtems__ +void +gfe_attach(struct device *parent, struct device *self, void *aux) +#else +int +gfe_attach(device_t dev) +#endif +{ +#ifndef __rtems__ + struct gt_attach_args * const ga = aux; + struct gt_softc * const gt = (struct gt_softc *) parent; + struct gfe_softc * const sc = (struct gfe_softc *) self; +#else + struct gfe_softc * const sc = device_get_softc(dev); +#endif + struct ifnet * const ifp = &sc->sc_ec.ec_if; + uint32_t data; + uint8_t enaddr[6]; + int phyaddr; + uint32_t sdcr; + int error; +#ifdef __rtems__ + SPRINTFVARDECL; +#endif + +#ifndef __rtems__ + GT_ETHERFOUND(gt, ga); + + sc->sc_gt_memt = ga->ga_memt; + sc->sc_gt_memh = ga->ga_memh; + sc->sc_dmat = ga->ga_dmat; + sc->sc_macno = ga->ga_unit; + + if (bus_space_subregion(sc->sc_gt_memt, sc->sc_gt_memh, + ETH_BASE(sc->sc_macno), ETH_SIZE, &sc->sc_memh)) { + aprint_error(": failed to map registers\n"); + } + + callout_init(&sc->sc_co); +#else + /* sc_macno, irq_no and sc_gt_memh must be filled in by 'setup' */ + + /* make ring sizes even numbers so that we have always multiple + * cache lines (paranoia) + */ + if ( (sc->num_rxdesc = dev->d_ifconfig->rbuf_count) & 1 ) + sc->num_rxdesc++; + if ( 0 == sc->num_rxdesc ) + sc->num_rxdesc = 64; + + if ( (sc->num_txdesc = dev->d_ifconfig->xbuf_count) & 1 ) + sc->num_txdesc++; + if ( 0 == sc->num_txdesc ) + sc->num_txdesc = 256; + + /* Enable hardware cache snooping; + * Copyright Shuchen K. Feng , 2004 + */ + /* regs are eth0: 0xf200/0xf204, eth1 0xf220/0xf224, eth2: 0xf240/0xf244 */ + { + uint32_t v; + v = GT_READ(sc, ETH_ACTL_0_LO + (sc->sc_macno<<5)); + v |= RxBSnoopEn|TxBSnoopEn|RxDSnoopEn|TxDSnoopEn; + GT_WRITE(sc, ETH_ACTL_0_LO + (sc->sc_macno<<5), v); + + v = GT_READ(sc, ETH_ACTL_0_HI + (sc->sc_macno<<5)); + v |= HashSnoopEn; + GT_WRITE(sc, ETH_ACTL_0_HI + (sc->sc_macno<<5), v); + } + +#endif + + data = bus_space_read_4(sc->sc_gt_memt, sc->sc_gt_memh, ETH_EPAR); +#ifdef __rtems__ + sc->sc_phyaddr = +#endif + phyaddr = ETH_EPAR_PhyAD_GET(data, sc->sc_macno); + +#ifndef __rtems__ + gtget_macaddr(gt, sc->sc_macno, enaddr); +#else + memset( enaddr, 0, ETHER_ADDR_LEN ); + if ( !memcmp(enaddr, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN) ) { + aprint_error(": MAC address not set (pass to rtems_gfe_setup())\n"); + return -1; + } + /* mac address needs to be provided by 'setup' */ + memcpy(enaddr, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); +#endif + + sc->sc_pcr = GE_READ(sc, EPCR); + sc->sc_pcxr = GE_READ(sc, EPCXR); + sc->sc_intrmask = GE_READ(sc, EIMR) | ETH_IR_MIIPhySTC; + + aprint_normal(": address %s", ether_sprintf(enaddr)); + +#if defined(DEBUG) + aprint_normal(", pcr %#x, pcxr %#x", sc->sc_pcr, sc->sc_pcxr); +#endif + + sc->sc_pcxr &= ~ETH_EPCXR_PRIOrx_Override; +#ifndef __rtems__ + if (sc->sc_dev.dv_cfdata->cf_flags & 1) { + aprint_normal(", phy %d (rmii)", phyaddr); + sc->sc_pcxr |= ETH_EPCXR_RMIIEn; + } else +#endif + { + aprint_normal(", phy %d (mii)", phyaddr); + sc->sc_pcxr &= ~ETH_EPCXR_RMIIEn; + } +#ifndef __rtems__ + if (sc->sc_dev.dv_cfdata->cf_flags & 2) + sc->sc_flags |= GE_NOFREE; +#endif + sc->sc_pcxr &= ~(3 << 14); + sc->sc_pcxr |= (ETH_EPCXR_MFL_1536 << 14); + + if (sc->sc_pcr & ETH_EPCR_EN) { + int tries = 1000; + /* + * Abort transmitter and receiver and wait for them to quiese + */ + GE_WRITE(sc, ESDCMR, ETH_ESDCMR_AR|ETH_ESDCMR_AT); + do { + delay(100); + } while (tries-- > 0 && (GE_READ(sc, ESDCMR) & (ETH_ESDCMR_AR|ETH_ESDCMR_AT))); + } + + sc->sc_pcr &= ~(ETH_EPCR_EN | ETH_EPCR_RBM | ETH_EPCR_PM | ETH_EPCR_PBF); + +#if defined(DEBUG) + aprint_normal(", pcr %#x, pcxr %#x", sc->sc_pcr, sc->sc_pcxr); +#endif + + /* + * Now turn off the GT. If it didn't quiese, too ***ing bad. + */ + GE_WRITE(sc, EPCR, sc->sc_pcr); +#ifndef __rtems__ + GE_WRITE(sc, EIMR, sc->sc_intrmask); +#else + GE_WRITE(sc, EICR, 0); + GE_WRITE(sc, EIMR, 0); +#endif + sdcr = GE_READ(sc, ESDCR); + ETH_ESDCR_BSZ_SET(sdcr, ETH_ESDCR_BSZ_4); + sdcr |= ETH_ESDCR_RIFB; + GE_WRITE(sc, ESDCR, sdcr); + sc->sc_max_frame_length = 1536; + + aprint_normal("\n"); +#ifndef __rtems__ + sc->sc_mii.mii_ifp = ifp; + sc->sc_mii.mii_readreg = gfe_mii_read; + sc->sc_mii.mii_writereg = gfe_mii_write; + sc->sc_mii.mii_statchg = gfe_mii_statchg; + + ifmedia_init(&sc->sc_mii.mii_media, 0, gfe_mii_mediachange, + gfe_mii_mediastatus); + + mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, phyaddr, + MII_OFFSET_ANY, MIIF_NOISOLATE); + if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL); + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); + } else { + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); + } + + strcpy(ifp->if_xname, sc->sc_dev.dv_xname); +#else + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_mtu = ETHERMTU; + ifp->if_output = ether_output; + ifp->if_init = gfe_init; + ifp->if_snd.ifq_maxlen = GE_TXDESC_MAX - 1; + ifp->if_baudrate = 10000000; +#endif + ifp->if_softc = sc; + /* ifp->if_mowner = &sc->sc_mowner; */ + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; +#if 0 + ifp->if_flags |= IFF_DEBUG; +#endif + ifp->if_ioctl = gfe_ifioctl; + ifp->if_start = gfe_ifstart; + ifp->if_watchdog = gfe_ifwatchdog; + + if (sc->sc_flags & GE_NOFREE) { + error = gfe_rx_rxqalloc(sc, GE_RXPRIO_HI); + if (!error) + error = gfe_rx_rxqalloc(sc, GE_RXPRIO_MEDHI); + if (!error) + error = gfe_rx_rxqalloc(sc, GE_RXPRIO_MEDLO); + if (!error) + error = gfe_rx_rxqalloc(sc, GE_RXPRIO_LO); + if (!error) + error = gfe_tx_txqalloc(sc, GE_TXPRIO_HI); + if (!error) + error = gfe_hash_alloc(sc); + if (error) + aprint_error( + "%s: failed to allocate resources: %d\n", + ifp->if_xname, error); + } + + if_attach(ifp); +#ifndef __rtems__ + ether_ifattach(ifp, enaddr); +#else + ether_ifattach(ifp); +#endif +#if NBPFILTER > 0 + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +#if NRND > 0 + rnd_attach_source(&sc->sc_rnd_source, self->dv_xname, RND_TYPE_NET, 0); +#endif +#ifndef __rtems__ + intr_establish(IRQ_ETH0 + sc->sc_macno, IST_LEVEL, IPL_NET, + gfe_intr, sc); +#else + return 0; +#endif +} + +int +gfe_dmamem_alloc(struct gfe_softc *sc, struct gfe_dmamem *gdm, int maxsegs, + size_t size, int flags) +{ + int error = 0; + GE_FUNC_ENTER(sc, "gfe_dmamem_alloc"); + + KASSERT(gdm->gdm_kva == NULL); + gdm->gdm_size = size; + gdm->gdm_maxsegs = maxsegs; + +#ifndef __rtems__ + error = bus_dmamem_alloc(sc->sc_dmat, gdm->gdm_size, PAGE_SIZE, + gdm->gdm_size, gdm->gdm_segs, gdm->gdm_maxsegs, &gdm->gdm_nsegs, + BUS_DMA_NOWAIT); + if (error) + goto fail; + + error = bus_dmamem_map(sc->sc_dmat, gdm->gdm_segs, gdm->gdm_nsegs, + gdm->gdm_size, &gdm->gdm_kva, flags | BUS_DMA_NOWAIT); + if (error) + goto fail; + + error = bus_dmamap_create(sc->sc_dmat, gdm->gdm_size, gdm->gdm_nsegs, + gdm->gdm_size, 0, BUS_DMA_ALLOCNOW|BUS_DMA_NOWAIT, &gdm->gdm_map); + if (error) + goto fail; + + error = bus_dmamap_load(sc->sc_dmat, gdm->gdm_map, gdm->gdm_kva, + gdm->gdm_size, NULL, BUS_DMA_NOWAIT); + if (error) + goto fail; +#else + gdm->gdm_segs[0].ds_len = size; + + /* FIXME: probably we can relax the alignment */ + if ( ! ( gdm->gdm_unaligned_buf = malloc( size + PAGE_SIZE - 1, M_DEVBUF, M_NOWAIT ) ) ) + goto fail; + + gdm->gdm_map = gdm; + gdm->gdm_nsegs = 1; + gdm->gdm_kva = (caddr_t)(gdm->gdm_segs[0].ds_addr = _DO_ALIGN(gdm->gdm_unaligned_buf, PAGE_SIZE)); +#endif + + /* invalidate from cache */ + bus_dmamap_sync(sc->sc_dmat, gdm->gdm_map, 0, gdm->gdm_size, + BUS_DMASYNC_PREREAD); +fail: + if (error) { + gfe_dmamem_free(sc, gdm); + GE_DPRINTF(sc, (":err=%d", error)); + } + GE_DPRINTF(sc, (":kva=%p/%#x,map=%p,nsegs=%d,pa=%" PRIx32 "/%" PRIx32, + gdm->gdm_kva, gdm->gdm_size, gdm->gdm_map, gdm->gdm_map->dm_nsegs, + gdm->gdm_map->dm_segs->ds_addr, gdm->gdm_map->dm_segs->ds_len)); + GE_FUNC_EXIT(sc, ""); + return error; +} + +void +gfe_dmamem_free(struct gfe_softc *sc, struct gfe_dmamem *gdm) +{ + GE_FUNC_ENTER(sc, "gfe_dmamem_free"); +#ifndef __rtems__ + if (gdm->gdm_map) + bus_dmamap_destroy(sc->sc_dmat, gdm->gdm_map); + if (gdm->gdm_kva) + bus_dmamem_unmap(sc->sc_dmat, gdm->gdm_kva, gdm->gdm_size); + if (gdm->gdm_nsegs > 0) + bus_dmamem_free(sc->sc_dmat, gdm->gdm_segs, gdm->gdm_nsegs); +#else + if (gdm->gdm_nsegs > 0) + free(gdm->gdm_unaligned_buf, M_DEVBUF); +#endif + gdm->gdm_map = NULL; + gdm->gdm_kva = NULL; + gdm->gdm_nsegs = 0; + GE_FUNC_EXIT(sc, ""); +} + +#ifndef __rtems__ +int +gfe_ifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +#else +int +gfe_ifioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +#endif +{ + struct gfe_softc * const sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; +#ifndef __rtems__ + struct ifaddr *ifa = (struct ifaddr *) data; +#endif + int s, error = 0; + + GE_FUNC_ENTER(sc, "gfe_ifioctl"); + s = splnet(); + + switch (cmd) { +#ifndef __rtems__ + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + error = gfe_whack(sc, GE_WHACK_START); + if (error == 0) + arp_ifinit(ifp, ifa); + break; +#endif + default: + error = gfe_whack(sc, GE_WHACK_START); + break; + } + break; +#endif + + case SIOCSIFFLAGS: + if ((sc->sc_ec.ec_if.if_flags & IFF_PROMISC) == 0) + sc->sc_pcr &= ~ETH_EPCR_PM; + else + sc->sc_pcr |= ETH_EPCR_PM; + switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { + case IFF_UP|IFF_RUNNING:/* active->active, update */ + error = gfe_whack(sc, GE_WHACK_CHANGE); + break; + case IFF_RUNNING: /* not up, so we stop */ + error = gfe_whack(sc, GE_WHACK_STOP); + break; + case IFF_UP: /* not running, so we start */ + error = gfe_whack(sc, GE_WHACK_START); + break; + case 0: /* idle->idle: do nothing */ + break; + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) + ? ether_addmulti(ifr, &sc->sc_ec) + : ether_delmulti(ifr, &sc->sc_ec); + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) +#if !defined(__rtems__) + error = gfe_whack(sc, GE_WHACK_CHANGE); +#else + /* doing GE_WHACK_CHANGE seems wrong - that + * doesn't do anything to the hash table. + * Therefore we perform a stop/start sequence. + */ + { + error = gfe_whack(sc, GE_WHACK_STOP); + if ( error ) + break; + error = gfe_whack(sc, GE_WHACK_START); + } +#endif + else + error = 0; + } + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) { + error = EINVAL; + break; + } + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: +#ifndef __rtems__ + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); +#else + error = rtems_mii_ioctl(&gfe_mdio_access, sc, cmd, &ifr->ifr_media); +#endif + break; + + default: +#ifndef __rtems__ + error = EINVAL; +#else + error = ether_ioctl(ifp, cmd, data); +#endif + break; + } + splx(s); + GE_FUNC_EXIT(sc, ""); + return error; +} + +void +gfe_ifstart(struct ifnet *ifp) +{ + struct gfe_softc * const sc = ifp->if_softc; + struct mbuf *m; + + GE_FUNC_ENTER(sc, "gfe_ifstart"); + + if ((ifp->if_flags & IFF_RUNNING) == 0) { + GE_FUNC_EXIT(sc, "$"); + return; + } + + for (;;) { + IF_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + GE_FUNC_EXIT(sc, ""); + return; + } + + /* + * No space in the pending queue? try later. + */ + if (IF_QFULL(&sc->sc_txq[GE_TXPRIO_HI].txq_pendq)) + break; + + /* + * Try to enqueue a mbuf to the device. If that fails, we + * can always try to map the next mbuf. + */ + IF_ENQUEUE(&sc->sc_txq[GE_TXPRIO_HI].txq_pendq, m); + GE_DPRINTF(sc, (">")); +#ifndef GE_NOTX + (void) gfe_tx_enqueue(sc, GE_TXPRIO_HI); +#endif + } + + /* + * Attempt to queue the mbuf for send failed. + */ + IF_PREPEND(&ifp->if_snd, m); + ifp->if_flags |= IFF_OACTIVE; + GE_FUNC_EXIT(sc, "%%"); +} + +void +gfe_ifwatchdog(struct ifnet *ifp) +{ + struct gfe_softc * const sc = ifp->if_softc; + struct gfe_txqueue * const txq = &sc->sc_txq[GE_TXPRIO_HI]; + + GE_FUNC_ENTER(sc, "gfe_ifwatchdog"); + printf("%s: device timeout", sc->sc_dev.dv_xname); + if (ifp->if_flags & IFF_RUNNING) { + uint32_t curtxdnum = (bus_space_read_4(sc->sc_gt_memt, sc->sc_gt_memh, txq->txq_ectdp) - txq->txq_desc_busaddr) / sizeof(txq->txq_descs[0]); + GE_TXDPOSTSYNC(sc, txq, txq->txq_fi); + GE_TXDPOSTSYNC(sc, txq, curtxdnum); + printf(" (fi=%d(%#x),lo=%d,cur=%" PRId32 "(%#x),icm=%#x) ", + txq->txq_fi, txq->txq_descs[txq->txq_fi].ed_cmdsts, + txq->txq_lo, curtxdnum, txq->txq_descs[curtxdnum].ed_cmdsts, + GE_READ(sc, EICR)); + GE_TXDPRESYNC(sc, txq, txq->txq_fi); + GE_TXDPRESYNC(sc, txq, curtxdnum); + } + printf("\n"); + ifp->if_oerrors++; + (void) gfe_whack(sc, GE_WHACK_RESTART); + GE_FUNC_EXIT(sc, ""); +} + +#ifdef __rtems__ +static struct mbuf * +gfe_newbuf(struct mbuf *m) +{ + if ( !m ) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if ( !m ) + return 0; + MCLGET(m, M_DONTWAIT); + if ( !(M_EXT & m->m_flags) ) { + m_freem(m); + return 0; + } + } else { + m->m_data = m->m_ext.ext_buf; + } + m->m_len = m->m_pkthdr.len = MCLBYTES; +#if 0 + m_adj(m, 2); /* so payload is 16-byte aligned */ +#endif + return m; +} +#endif + +int +gfe_rx_rxqalloc(struct gfe_softc *sc, enum gfe_rxprio rxprio) +{ + struct gfe_rxqueue * const rxq = &sc->sc_rxq[rxprio]; + int error; + + GE_FUNC_ENTER(sc, "gfe_rx_rxqalloc"); + GE_DPRINTF(sc, ("(%d)", rxprio)); + + error = gfe_dmamem_alloc(sc, &rxq->rxq_desc_mem, 1, + GE_RXDESC_MEMSIZE, BUS_DMA_NOCACHE); + if (error) { + GE_FUNC_EXIT(sc, "!!"); + return error; + } + +#ifndef __rtems__ + error = gfe_dmamem_alloc(sc, &rxq->rxq_buf_mem, GE_RXBUF_NSEGS, + GE_RXBUF_MEMSIZE, 0); +#else + if ( ! (rxq->rxq_bufs = malloc( sizeof(*rxq->rxq_bufs) * GE_RXDESC_MAX, M_DEVBUF, M_NOWAIT ) ) ) { + error = -1; + } else { + int i; + for ( i = 0; irxq_bufs[i] = gfe_newbuf(0)) ) { + fprintf(stderr,"gfe: Not enough mbuf clusters to initialize RX ring!\n"); + while (--i >=0 ) { + m_freem(rxq->rxq_bufs[i]); + } + free(rxq->rxq_bufs, M_DEVBUF); + rxq->rxq_bufs = 0; + error = -1; + break; + } + } + } +#endif + if (error) { + GE_FUNC_EXIT(sc, "!!!"); + return error; + } + GE_FUNC_EXIT(sc, ""); + return error; +} + +int +gfe_rx_rxqinit(struct gfe_softc *sc, enum gfe_rxprio rxprio) +{ + struct gfe_rxqueue * const rxq = &sc->sc_rxq[rxprio]; + volatile struct gt_eth_desc *rxd; +#ifndef __rtems__ + const bus_dma_segment_t *ds; +#endif + int idx; + bus_addr_t nxtaddr; +#ifndef __rtems__ + bus_size_t boff; +#endif + + GE_FUNC_ENTER(sc, "gfe_rx_rxqinit"); + GE_DPRINTF(sc, ("(%d)", rxprio)); + + if ((sc->sc_flags & GE_NOFREE) == 0) { + int error = gfe_rx_rxqalloc(sc, rxprio); + if (error) { + GE_FUNC_EXIT(sc, "!"); + return error; + } + } else { + KASSERT(rxq->rxq_desc_mem.gdm_kva != NULL); +#ifndef __rtems__ + KASSERT(rxq->rxq_buf_mem.gdm_kva != NULL); +#else + KASSERT(rxq->rxq_bufs != NULL); +#endif + } + + memset(rxq->rxq_desc_mem.gdm_kva, 0, GE_RXDESC_MEMSIZE); + + rxq->rxq_descs = + (volatile struct gt_eth_desc *) rxq->rxq_desc_mem.gdm_kva; + rxq->rxq_desc_busaddr = rxq->rxq_desc_mem.gdm_map->dm_segs[0].ds_addr; +#ifndef __rtems__ + rxq->rxq_bufs = (struct gfe_rxbuf *) rxq->rxq_buf_mem.gdm_kva; +#endif + rxq->rxq_fi = 0; + rxq->rxq_active = GE_RXDESC_MAX; + for (idx = 0, rxd = rxq->rxq_descs, +#ifndef __rtems__ + boff = 0, ds = rxq->rxq_buf_mem.gdm_map->dm_segs, +#endif + nxtaddr = rxq->rxq_desc_busaddr + sizeof(*rxd); + idx < GE_RXDESC_MAX; + idx++, rxd++, nxtaddr += sizeof(*rxd)) { +#ifndef __rtems__ + rxd->ed_lencnt = htogt32(GE_RXBUF_SIZE << 16); +#else + rxd->ed_lencnt = htogt32(MCLBYTES << 16); +#endif + rxd->ed_cmdsts = htogt32(RX_CMD_F|RX_CMD_L|RX_CMD_O|RX_CMD_EI); +#ifndef __rtems__ + rxd->ed_bufptr = htogt32(ds->ds_addr + boff); +#else + rxd->ed_bufptr = htogt32(mtod(rxq->rxq_bufs[idx], uint32_t)); +#endif + /* + * update the nxtptr to point to the next txd. + */ + if (idx == GE_RXDESC_MAX - 1) + nxtaddr = rxq->rxq_desc_busaddr; + rxd->ed_nxtptr = htogt32(nxtaddr); +#ifndef __rtems__ + boff += GE_RXBUF_SIZE; + if (boff == ds->ds_len) { + ds++; + boff = 0; + } +#endif + } + bus_dmamap_sync(sc->sc_dmat, rxq->rxq_desc_mem.gdm_map, 0, + rxq->rxq_desc_mem.gdm_map->dm_mapsize, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); +#ifndef __rtems__ + bus_dmamap_sync(sc->sc_dmat, rxq->rxq_buf_mem.gdm_map, 0, + rxq->rxq_buf_mem.gdm_map->dm_mapsize, + BUS_DMASYNC_PREREAD); +#else + /* FIXME: we leave this call in here so compilation fails + * if bus_dmamap_sync() is ever fleshed-out to implement + * software cache coherency... + */ + bus_dmamap_sync(sc->sc_dmat, rxq->rxq_buf_mem.gdm_map, 0, + rxq->rxq_buf_mem.gdm_map->dm_mapsize, + BUS_DMASYNC_PREREAD); +#endif + + rxq->rxq_intrbits = ETH_IR_RxBuffer|ETH_IR_RxError; + switch (rxprio) { + case GE_RXPRIO_HI: + rxq->rxq_intrbits |= ETH_IR_RxBuffer_3|ETH_IR_RxError_3; + rxq->rxq_efrdp = ETH_EFRDP3(sc->sc_macno); + rxq->rxq_ecrdp = ETH_ECRDP3(sc->sc_macno); + break; + case GE_RXPRIO_MEDHI: + rxq->rxq_intrbits |= ETH_IR_RxBuffer_2|ETH_IR_RxError_2; + rxq->rxq_efrdp = ETH_EFRDP2(sc->sc_macno); + rxq->rxq_ecrdp = ETH_ECRDP2(sc->sc_macno); + break; + case GE_RXPRIO_MEDLO: + rxq->rxq_intrbits |= ETH_IR_RxBuffer_1|ETH_IR_RxError_1; + rxq->rxq_efrdp = ETH_EFRDP1(sc->sc_macno); + rxq->rxq_ecrdp = ETH_ECRDP1(sc->sc_macno); + break; + case GE_RXPRIO_LO: + rxq->rxq_intrbits |= ETH_IR_RxBuffer_0|ETH_IR_RxError_0; + rxq->rxq_efrdp = ETH_EFRDP0(sc->sc_macno); + rxq->rxq_ecrdp = ETH_ECRDP0(sc->sc_macno); + break; + } + GE_FUNC_EXIT(sc, ""); + return 0; +} + +void +gfe_rx_get(struct gfe_softc *sc, enum gfe_rxprio rxprio) +{ + struct ifnet * const ifp = &sc->sc_ec.ec_if; + struct gfe_rxqueue * const rxq = &sc->sc_rxq[rxprio]; +#ifndef __rtems__ + struct mbuf *m = rxq->rxq_curpkt; +#else + struct mbuf *m; +#endif + + GE_FUNC_ENTER(sc, "gfe_rx_get"); + GE_DPRINTF(sc, ("(%d)", rxprio)); + + while (rxq->rxq_active > 0) { + volatile struct gt_eth_desc *rxd = &rxq->rxq_descs[rxq->rxq_fi]; +#ifndef __rtems__ + struct gfe_rxbuf *rxb = &rxq->rxq_bufs[rxq->rxq_fi]; +#else + struct mbuf **rxb = &rxq->rxq_bufs[rxq->rxq_fi]; +#endif + const struct ether_header *eh; + unsigned int cmdsts; + size_t buflen; + + GE_RXDPOSTSYNC(sc, rxq, rxq->rxq_fi); + cmdsts = gt32toh(rxd->ed_cmdsts); + GE_DPRINTF(sc, (":%d=%#x", rxq->rxq_fi, cmdsts)); + rxq->rxq_cmdsts = cmdsts; + /* + * Sometimes the GE "forgets" to reset the ownership bit. + * But if the length has been rewritten, the packet is ours + * so pretend the O bit is set. + */ + buflen = gt32toh(rxd->ed_lencnt) & 0xffff; + if ((cmdsts & RX_CMD_O) && buflen == 0) { + GE_RXDPRESYNC(sc, rxq, rxq->rxq_fi); + break; + } + + /* + * If this is not a single buffer packet with no errors + * or for some reason it's bigger than our frame size, + * ignore it and go to the next packet. + */ + if ((cmdsts & (RX_CMD_F|RX_CMD_L|RX_STS_ES)) != + (RX_CMD_F|RX_CMD_L) || + buflen > sc->sc_max_frame_length) { + GE_DPRINTF(sc, ("!")); + --rxq->rxq_active; + ifp->if_ipackets++; + ifp->if_ierrors++; + + *rxb = gfe_newbuf(*rxb); + goto give_it_back; + } + + /* CRC is included with the packet; trim it off. */ + buflen -= ETHER_CRC_LEN; + +#ifndef __rtems__ + if (m == NULL) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + GE_DPRINTF(sc, ("?")); + break; + } + } + if ((m->m_flags & M_EXT) == 0 && buflen > MHLEN - 2) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + GE_DPRINTF(sc, ("?")); + break; + } + } + m->m_data += 2; + m->m_len = 0; + m->m_pkthdr.len = 0; + m->m_pkthdr.rcvif = ifp; +#else + if ( ! (m=gfe_newbuf(0)) ) { + /* recycle old buffer */ + *rxb = gfe_newbuf(*rxb); + goto give_it_back; + } + /* swap mbufs */ + { + struct mbuf *tmp = *rxb; + *rxb = m; + m = tmp; + rxd->ed_bufptr = htogt32(mtod(*rxb, uint32_t)); + } +#endif + rxq->rxq_cmdsts = cmdsts; + --rxq->rxq_active; + +#ifdef __rtems__ + /* FIXME: we leave this call in here so compilation fails + * if bus_dmamap_sync() is ever fleshed-out to implement + * software cache coherency... + */ + bus_dmamap_sync(sc->sc_dmat, rxq->rxq_buf_mem.gdm_map, + rxq->rxq_fi * sizeof(*rxb), buflen, BUS_DMASYNC_POSTREAD); +#else + bus_dmamap_sync(sc->sc_dmat, rxq->rxq_buf_mem.gdm_map, + rxq->rxq_fi * sizeof(*rxb), buflen, BUS_DMASYNC_POSTREAD); + + KASSERT(m->m_len == 0 && m->m_pkthdr.len == 0); + memcpy(m->m_data + m->m_len, rxb->rb_data, buflen); +#endif + + m->m_len = buflen; + m->m_pkthdr.len = buflen; + + ifp->if_ipackets++; +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m); +#endif + + eh = (const struct ether_header *) m->m_data; + if ((ifp->if_flags & IFF_PROMISC) || + (rxq->rxq_cmdsts & RX_STS_M) == 0 || + (rxq->rxq_cmdsts & RX_STS_HE) || + (eh->ether_dhost[0] & 1) != 0 || + memcmp(eh->ether_dhost, +#ifndef __rtems__ + LLADDR(ifp->if_sadl), +#else + sc->sc_ec.ac_enaddr, +#endif + ETHER_ADDR_LEN) == 0) { +#ifndef __rtems__ + (*ifp->if_input)(ifp, m); + m = NULL; +#else + DO_ETHER_INPUT_SKIPPING_ETHER_HEADER(ifp,m); +#endif + GE_DPRINTF(sc, (">")); + } else { +#ifndef __rtems__ + m->m_len = 0; + m->m_pkthdr.len = 0; +#else + m_freem(m); +#endif + GE_DPRINTF(sc, ("+")); + } + rxq->rxq_cmdsts = 0; + + give_it_back: + rxd->ed_lencnt &= ~0xffff; /* zero out length */ + rxd->ed_cmdsts = htogt32(RX_CMD_F|RX_CMD_L|RX_CMD_O|RX_CMD_EI); +#if 0 + GE_DPRINTF(sc, ("([%d]->%08lx.%08lx.%08lx.%08lx)", + rxq->rxq_fi, + ((unsigned long *)rxd)[0], ((unsigned long *)rxd)[1], + ((unsigned long *)rxd)[2], ((unsigned long *)rxd)[3])); +#endif + GE_RXDPRESYNC(sc, rxq, rxq->rxq_fi); + if (++rxq->rxq_fi == GE_RXDESC_MAX) + rxq->rxq_fi = 0; + rxq->rxq_active++; + } +#ifndef __rtems__ + rxq->rxq_curpkt = m; +#endif + GE_FUNC_EXIT(sc, ""); +} + +uint32_t +gfe_rx_process(struct gfe_softc *sc, uint32_t cause, uint32_t intrmask) +{ + struct ifnet * const ifp = &sc->sc_ec.ec_if; + struct gfe_rxqueue *rxq; + uint32_t rxbits; +#define RXPRIO_DECODER 0xffffaa50 + GE_FUNC_ENTER(sc, "gfe_rx_process"); + + rxbits = ETH_IR_RxBuffer_GET(cause); + while (rxbits) { + enum gfe_rxprio rxprio = (RXPRIO_DECODER >> (rxbits * 2)) & 3; + GE_DPRINTF(sc, ("%1" PRIx32, rxbits)); + rxbits &= ~(1 << rxprio); + gfe_rx_get(sc, rxprio); + } + + rxbits = ETH_IR_RxError_GET(cause); + while (rxbits) { + enum gfe_rxprio rxprio = (RXPRIO_DECODER >> (rxbits * 2)) & 3; + uint32_t masks[(GE_RXDESC_MAX + 31) / 32]; + int idx; + rxbits &= ~(1 << rxprio); + rxq = &sc->sc_rxq[rxprio]; + sc->sc_idlemask |= (rxq->rxq_intrbits & ETH_IR_RxBits); + intrmask &= ~(rxq->rxq_intrbits & ETH_IR_RxBits); + if ((sc->sc_tickflags & GE_TICK_RX_RESTART) == 0) { + sc->sc_tickflags |= GE_TICK_RX_RESTART; + callout_reset(&sc->sc_co, 1, gfe_tick, sc); + } + ifp->if_ierrors++; + GE_DPRINTF(sc, ("%s: rx queue %d filled at %u\n", + sc->sc_dev.dv_xname, rxprio, rxq->rxq_fi)); + memset(masks, 0, sizeof(masks)); + bus_dmamap_sync(sc->sc_dmat, rxq->rxq_desc_mem.gdm_map, + 0, rxq->rxq_desc_mem.gdm_size, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + for (idx = 0; idx < GE_RXDESC_MAX; idx++) { + volatile struct gt_eth_desc *rxd = &rxq->rxq_descs[idx]; + + if (RX_CMD_O & gt32toh(rxd->ed_cmdsts)) + masks[idx/32] |= 1 << (idx & 31); + } + bus_dmamap_sync(sc->sc_dmat, rxq->rxq_desc_mem.gdm_map, + 0, rxq->rxq_desc_mem.gdm_size, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); +#if defined(DEBUG) + printf("%s: rx queue %d filled at %u=%#x(%#x/%#x)\n", + sc->sc_dev.dv_xname, rxprio, rxq->rxq_fi, + rxq->rxq_cmdsts, masks[0], masks[1]); +#endif + } + if ((intrmask & ETH_IR_RxBits) == 0) + intrmask &= ~(ETH_IR_RxBuffer|ETH_IR_RxError); + + GE_FUNC_EXIT(sc, ""); + return intrmask; +} + +int +gfe_rx_prime(struct gfe_softc *sc) +{ + struct gfe_rxqueue *rxq; + int error; + + GE_FUNC_ENTER(sc, "gfe_rx_prime"); + + error = gfe_rx_rxqinit(sc, GE_RXPRIO_HI); + if (error) + goto bail; + rxq = &sc->sc_rxq[GE_RXPRIO_HI]; + if ((sc->sc_flags & GE_RXACTIVE) == 0) { + GE_WRITE(sc, EFRDP3, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP3, rxq->rxq_desc_busaddr); + } + sc->sc_intrmask |= rxq->rxq_intrbits; + + error = gfe_rx_rxqinit(sc, GE_RXPRIO_MEDHI); + if (error) + goto bail; + if ((sc->sc_flags & GE_RXACTIVE) == 0) { + rxq = &sc->sc_rxq[GE_RXPRIO_MEDHI]; + GE_WRITE(sc, EFRDP2, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP2, rxq->rxq_desc_busaddr); + sc->sc_intrmask |= rxq->rxq_intrbits; + } + + error = gfe_rx_rxqinit(sc, GE_RXPRIO_MEDLO); + if (error) + goto bail; + if ((sc->sc_flags & GE_RXACTIVE) == 0) { + rxq = &sc->sc_rxq[GE_RXPRIO_MEDLO]; + GE_WRITE(sc, EFRDP1, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP1, rxq->rxq_desc_busaddr); + sc->sc_intrmask |= rxq->rxq_intrbits; + } + + error = gfe_rx_rxqinit(sc, GE_RXPRIO_LO); + if (error) + goto bail; + if ((sc->sc_flags & GE_RXACTIVE) == 0) { + rxq = &sc->sc_rxq[GE_RXPRIO_LO]; + GE_WRITE(sc, EFRDP0, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP0, rxq->rxq_desc_busaddr); + sc->sc_intrmask |= rxq->rxq_intrbits; + } + + bail: + GE_FUNC_EXIT(sc, ""); + return error; +} + +void +gfe_rx_cleanup(struct gfe_softc *sc, enum gfe_rxprio rxprio) +{ + struct gfe_rxqueue *rxq = &sc->sc_rxq[rxprio]; + GE_FUNC_ENTER(sc, "gfe_rx_cleanup"); + if (rxq == NULL) { + GE_FUNC_EXIT(sc, ""); + return; + } + +#ifndef __rtems__ + if (rxq->rxq_curpkt) + m_freem(rxq->rxq_curpkt); +#endif + if ((sc->sc_flags & GE_NOFREE) == 0) { + gfe_dmamem_free(sc, &rxq->rxq_desc_mem); +#ifndef __rtems__ + gfe_dmamem_free(sc, &rxq->rxq_buf_mem); +#else + if ( rxq->rxq_bufs ) { + int i; + for ( i=0; irxq_bufs[i] ) { + m_freem(rxq->rxq_bufs[i]); + } + } + free(rxq->rxq_bufs, M_DEVBUF); + } +#endif + } + GE_FUNC_EXIT(sc, ""); +} + +void +gfe_rx_stop(struct gfe_softc *sc, enum gfe_whack_op op) +{ + GE_FUNC_ENTER(sc, "gfe_rx_stop"); + sc->sc_flags &= ~GE_RXACTIVE; + sc->sc_idlemask &= ~(ETH_IR_RxBits|ETH_IR_RxBuffer|ETH_IR_RxError); + sc->sc_intrmask &= ~(ETH_IR_RxBits|ETH_IR_RxBuffer|ETH_IR_RxError); + GE_WRITE(sc, EIMR, sc->sc_intrmask); + GE_WRITE(sc, ESDCMR, ETH_ESDCMR_AR); + do { + delay(10); + } while (GE_READ(sc, ESDCMR) & ETH_ESDCMR_AR); + gfe_rx_cleanup(sc, GE_RXPRIO_HI); + gfe_rx_cleanup(sc, GE_RXPRIO_MEDHI); + gfe_rx_cleanup(sc, GE_RXPRIO_MEDLO); + gfe_rx_cleanup(sc, GE_RXPRIO_LO); + GE_FUNC_EXIT(sc, ""); +} + +void +gfe_tick(void *arg) +{ + struct gfe_softc * const sc = arg; + uint32_t intrmask; + unsigned int tickflags; + int s; + + GE_FUNC_ENTER(sc, "gfe_tick"); + + s = splnet(); + + tickflags = sc->sc_tickflags; + sc->sc_tickflags = 0; + intrmask = sc->sc_intrmask; + if (tickflags & GE_TICK_TX_IFSTART) + gfe_ifstart(&sc->sc_ec.ec_if); + if (tickflags & GE_TICK_RX_RESTART) { + intrmask |= sc->sc_idlemask; + if (sc->sc_idlemask & (ETH_IR_RxBuffer_3|ETH_IR_RxError_3)) { + struct gfe_rxqueue *rxq = &sc->sc_rxq[GE_RXPRIO_HI]; + rxq->rxq_fi = 0; + GE_WRITE(sc, EFRDP3, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP3, rxq->rxq_desc_busaddr); + } + if (sc->sc_idlemask & (ETH_IR_RxBuffer_2|ETH_IR_RxError_2)) { + struct gfe_rxqueue *rxq = &sc->sc_rxq[GE_RXPRIO_MEDHI]; + rxq->rxq_fi = 0; + GE_WRITE(sc, EFRDP2, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP2, rxq->rxq_desc_busaddr); + } + if (sc->sc_idlemask & (ETH_IR_RxBuffer_1|ETH_IR_RxError_1)) { + struct gfe_rxqueue *rxq = &sc->sc_rxq[GE_RXPRIO_MEDLO]; + rxq->rxq_fi = 0; + GE_WRITE(sc, EFRDP1, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP1, rxq->rxq_desc_busaddr); + } + if (sc->sc_idlemask & (ETH_IR_RxBuffer_0|ETH_IR_RxError_0)) { + struct gfe_rxqueue *rxq = &sc->sc_rxq[GE_RXPRIO_LO]; + rxq->rxq_fi = 0; + GE_WRITE(sc, EFRDP0, rxq->rxq_desc_busaddr); + GE_WRITE(sc, ECRDP0, rxq->rxq_desc_busaddr); + } + sc->sc_idlemask = 0; + } + if (intrmask != sc->sc_intrmask) { + sc->sc_intrmask = intrmask; + GE_WRITE(sc, EIMR, sc->sc_intrmask); + } + gfe_intr(sc); + splx(s); + + GE_FUNC_EXIT(sc, ""); +} + +static int +gfe_free_slots(struct gfe_softc *sc, struct gfe_txqueue *const txq) +{ + struct ifnet * const ifp = &sc->sc_ec.ec_if; +#ifndef __rtems__ + const int dcache_line_size = curcpu()->ci_ci.dcache_line_size; +#endif + int got = 0; + int fi = txq->txq_fi; + volatile struct gt_eth_desc *txd = &txq->txq_descs[fi]; + uint32_t cmdsts; +#ifndef __rtems__ + size_t pktlen; +#endif + + GE_FUNC_ENTER(sc, "gfe_free_slots"); + +#ifdef __rtems__ + do { +#endif + GE_TXDPOSTSYNC(sc, txq, fi); + if ((cmdsts = gt32toh(txd->ed_cmdsts)) & TX_CMD_O) { + int nextin; + + if (txq->txq_nactive == 1) { + GE_TXDPRESYNC(sc, txq, fi); + GE_FUNC_EXIT(sc, ""); + return -1; + } + /* + * Sometimes the Discovery forgets to update the + * ownership bit in the descriptor. See if we own the + * descriptor after it (since we know we've turned + * that to the Discovery and if we own it now then the + * Discovery gave it back). If we do, we know the + * Discovery gave back this one but forgot to mark it + * as ours. + */ + nextin = fi + 1; + if (nextin == GE_TXDESC_MAX) + nextin = 0; + GE_TXDPOSTSYNC(sc, txq, nextin); + if (gt32toh(txq->txq_descs[nextin].ed_cmdsts) & TX_CMD_O) { + GE_TXDPRESYNC(sc, txq, fi); + GE_TXDPRESYNC(sc, txq, nextin); + GE_FUNC_EXIT(sc, ""); + return -1; + } +#ifdef DEBUG + printf("%s: gfe_free_slots: transmitter resynced at %d\n", + sc->sc_dev.dv_xname, fi); +#endif + } + got++; +#ifdef __rtems__ + txd++; + fi++; + } while ( ! ( TX_CMD_LAST & cmdsts ) ); + + { struct mbuf *m; + IF_DEQUEUE(&txq->txq_sentq, m); + m_freem(m); + } +#endif +#if 0 + GE_DPRINTF(sc, ("([%d]<-%08lx.%08lx.%08lx.%08lx)", + txq->txq_lo, + ((unsigned long *)txd)[0], ((unsigned long *)txd)[1], + ((unsigned long *)txd)[2], ((unsigned long *)txd)[3])); +#endif + GE_DPRINTF(sc, ("(%d)", fi)); + txq->txq_fi = fi; + if ( txq->txq_fi >= GE_TXDESC_MAX) + txq->txq_fi -= GE_TXDESC_MAX; +#ifndef __rtems__ + txq->txq_inptr = gt32toh(txd->ed_bufptr) - txq->txq_buf_busaddr; + pktlen = (gt32toh(txd->ed_lencnt) >> 16) & 0xffff; + bus_dmamap_sync(sc->sc_dmat, txq->txq_buf_mem.gdm_map, + txq->txq_inptr, pktlen, BUS_DMASYNC_POSTWRITE); + txq->txq_inptr += roundup(pktlen, dcache_line_size); +#endif + + /* statistics */ + ifp->if_opackets++; +#ifdef __rtems__ + /* FIXME: should we check errors on every fragment? */ +#endif + if (cmdsts & TX_STS_ES) + ifp->if_oerrors++; + + /* txd->ed_bufptr = 0; */ + + txq->txq_nactive -= got; + + GE_FUNC_EXIT(sc, ""); + + return got; +} + +#ifndef __rtems__ +int +gfe_tx_enqueue(struct gfe_softc *sc, enum gfe_txprio txprio) +{ +#ifndef __rtems__ + const int dcache_line_size = curcpu()->ci_ci.dcache_line_size; +#else +#ifndef PPC_CACHE_ALIGNMENT +#error "Unknown cache alignment for your CPU" +#endif + const int dcache_line_size = PPC_CACHE_ALIGNMENT; +#endif + struct ifnet * const ifp = &sc->sc_ec.ec_if; + struct gfe_txqueue * const txq = &sc->sc_txq[txprio]; + volatile struct gt_eth_desc * const txd = &txq->txq_descs[txq->txq_lo]; + uint32_t intrmask = sc->sc_intrmask; + size_t buflen; + struct mbuf *m; + + GE_FUNC_ENTER(sc, "gfe_tx_enqueue"); + + /* + * Anything in the pending queue to enqueue? if not, punt. Likewise + * if the txq is not yet created. + * otherwise grab its dmamap. + */ + if (txq == NULL || (m = txq->txq_pendq.ifq_head) == NULL) { + GE_FUNC_EXIT(sc, "-"); + return 0; + } + + /* + * Have we [over]consumed our limit of descriptors? + * Do we have enough free descriptors? + */ + if (GE_TXDESC_MAX == txq->txq_nactive + 2) { + if ( gfe_free_slots(sc, txq) <= 0 ) + return 0; + } + + buflen = roundup(m->m_pkthdr.len, dcache_line_size); + + /* + * If this packet would wrap around the end of the buffer, reset back + * to the beginning. + */ + if (txq->txq_outptr + buflen > GE_TXBUF_SIZE) { + txq->txq_ei_gapcount += GE_TXBUF_SIZE - txq->txq_outptr; + txq->txq_outptr = 0; + } + + /* + * Make sure the output packet doesn't run over the beginning of + * what we've already given the GT. + */ + if (txq->txq_nactive > 0 && txq->txq_outptr <= txq->txq_inptr && + txq->txq_outptr + buflen > txq->txq_inptr) { + intrmask |= txq->txq_intrbits & + (ETH_IR_TxBufferHigh|ETH_IR_TxBufferLow); + if (sc->sc_intrmask != intrmask) { + sc->sc_intrmask = intrmask; + GE_WRITE(sc, EIMR, sc->sc_intrmask); + } + GE_FUNC_EXIT(sc, "#"); + return 0; + } + + /* + * The end-of-list descriptor we put on last time is the starting point + * for this packet. The GT is supposed to terminate list processing on + * a NULL nxtptr but that currently is broken so a CPU-owned descriptor + * must terminate the list. + */ + intrmask = sc->sc_intrmask; + + m_copydata(m, 0, m->m_pkthdr.len, + txq->txq_buf_mem.gdm_kva + txq->txq_outptr); + bus_dmamap_sync(sc->sc_dmat, txq->txq_buf_mem.gdm_map, + txq->txq_outptr, buflen, BUS_DMASYNC_PREWRITE); + txd->ed_bufptr = htogt32(txq->txq_buf_busaddr + txq->txq_outptr); + txd->ed_lencnt = htogt32(m->m_pkthdr.len << 16); + GE_TXDPRESYNC(sc, txq, txq->txq_lo); + + /* + * Request a buffer interrupt every 2/3 of the way thru the transmit + * buffer. + */ + txq->txq_ei_gapcount += buflen; + if (txq->txq_ei_gapcount > 2 * GE_TXBUF_SIZE / 3) { + txd->ed_cmdsts = htogt32(TX_CMD_FIRST|TX_CMD_LAST|TX_CMD_EI); + txq->txq_ei_gapcount = 0; + } else { + txd->ed_cmdsts = htogt32(TX_CMD_FIRST|TX_CMD_LAST); + } +#if 0 + GE_DPRINTF(sc, ("([%d]->%08lx.%08lx.%08lx.%08lx)", txq->txq_lo, + ((unsigned long *)txd)[0], ((unsigned long *)txd)[1], + ((unsigned long *)txd)[2], ((unsigned long *)txd)[3])); +#endif + GE_TXDPRESYNC(sc, txq, txq->txq_lo); + + txq->txq_outptr += buflen; + /* + * Tell the SDMA engine to "Fetch!" + */ + GE_WRITE(sc, ESDCMR, + txq->txq_esdcmrbits & (ETH_ESDCMR_TXDH|ETH_ESDCMR_TXDL)); + + GE_DPRINTF(sc, ("(%d)", txq->txq_lo)); + + /* + * Update the last out appropriately. + */ + txq->txq_nactive++; + if (++txq->txq_lo == GE_TXDESC_MAX) + txq->txq_lo = 0; + + /* + * Move mbuf from the pending queue to the snd queue. + */ + IF_DEQUEUE(&txq->txq_pendq, m); +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m); +#endif + m_freem(m); + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * Since we have put an item into the packet queue, we now want + * an interrupt when the transmit queue finishes processing the + * list. But only update the mask if needs changing. + */ + intrmask |= txq->txq_intrbits & (ETH_IR_TxEndHigh|ETH_IR_TxEndLow); + if (sc->sc_intrmask != intrmask) { + sc->sc_intrmask = intrmask; + GE_WRITE(sc, EIMR, sc->sc_intrmask); + } + if (ifp->if_timer == 0) + ifp->if_timer = 5; + GE_FUNC_EXIT(sc, "*"); + return 1; +} + +#else + +#ifdef __PPC__ +static inline void membarrier(void) +{ + asm volatile("sync":::"memory"); +} +#else +#error "memory synchronization for your CPU not implemented" +#endif + + +void +gfe_assign_desc(volatile struct gt_eth_desc *const d, struct mbuf *m, uint32_t flags) +{ + d->ed_cmdsts = htogt32(flags | TX_CMD_GC | TX_CMD_P); + d->ed_bufptr = htogt32(mtod(m, uint32_t)); + d->ed_lencnt = htogt32(m->m_len << 16); +} + +int +gfe_tx_enqueue(struct gfe_softc *sc, enum gfe_txprio txprio) +{ + struct ifnet * const ifp = &sc->sc_ec.ec_if; + struct gfe_txqueue * const txq = &sc->sc_txq[txprio]; + volatile struct gt_eth_desc * const txd = &txq->txq_descs[txq->txq_lo]; +#define NEXT_TXD(d) ((d)+1 < &txq->txq_descs[GE_TXDESC_MAX] ? (d)+1 : txq->txq_descs) + volatile struct gt_eth_desc *l,*d; + uint32_t intrmask = sc->sc_intrmask; + struct mbuf *m_head,*m,*m1; + int avail, used; + + GE_FUNC_ENTER(sc, "gfe_tx_enqueue"); + + /* + * Anything in the pending queue to enqueue? if not, punt. Likewise + * if the txq is not yet created. + * otherwise grab its dmamap. + */ + if (txq == NULL || (m_head = txq->txq_pendq.ifq_head) == NULL) { + GE_FUNC_EXIT(sc, "-"); + return 0; + } + + /* find 1st mbuf with actual data; m_head is not NULL at this point */ + for ( m1=m_head; 0 == m1->m_len; ) { + if ( ! (m1=m1->m_next) ) { + /* nothing to send */ + IF_DEQUEUE(&txq->txq_pendq, m_head); + m_freem(m_head); + return 0; + } + } + + avail = GE_TXDESC_MAX - 1 - txq->txq_nactive; + + if ( avail < 1 && (avail += gfe_free_slots(sc, txq)) < 1 ) + return 0; + + avail--; + + l = txd; + d = NEXT_TXD(txd); + + for ( m=m1->m_next, used = 1; m; m=m->m_next ) { + if ( 0 == m->m_len ) + continue; /* skip empty mbufs */ + + if ( avail < 1 && (avail += gfe_free_slots(sc, txq)) < 1 ) { + /* not enough descriptors; cleanup */ + for ( l = NEXT_TXD(txd); l!=d; l = NEXT_TXD(l) ) { + l->ed_cmdsts = 0; + avail++; + } + avail++; + if ( used >= GE_TXDESC_MAX-1 ) + panic("mbuf chain (#%i) longer than TX ring (#%i); configuration error!", + used, GE_TXDESC_MAX-1); + return 0; + } + used++; + avail--; + + /* fill this slot */ + gfe_assign_desc(d, m, TX_CMD_O); + + bus_dmamap_sync(sc->sc_dmat, /* TODO */, + mtod(m, uint32_t), m->m_len, BUS_DMASYNC_PREWRITE); + + l = d; + d = NEXT_TXD(d); + + GE_TXDPRESYNC(sc, txq, l - txq->txq_descs); + } + + /* fill first slot */ + gfe_assign_desc(txd, m1, TX_CMD_F); + + bus_dmamap_sync(sc->sc_dmat, /* TODO */, + mtod(m1, uint32_t), m1->m_len, BUS_DMASYNC_PREWRITE); + + /* tag last slot; this covers where 1st = last */ + l->ed_cmdsts |= htonl(TX_CMD_L | TX_CMD_EI); + + GE_TXDPRESYNC(sc, txq, l - txq->txq_descs); + + /* + * The end-of-list descriptor we put on last time is the starting point + * for this packet. The GT is supposed to terminate list processing on + * a NULL nxtptr but that currently is broken so a CPU-owned descriptor + * must terminate the list. + */ + d = NEXT_TXD(l); + + out_be32(&d->ed_cmdsts,0); + + GE_TXDPRESYNC(sc, txq, d - txq->txq_descs); + + membarrier(); + + /* turn over the whole chain by flipping the ownership of the first desc */ + txd->ed_cmdsts |= htonl(TX_CMD_O); + + GE_TXDPRESYNC(sc, txq, txq->txq_lo); + + + intrmask = sc->sc_intrmask; + +#if 0 + GE_DPRINTF(sc, ("([%d]->%08lx.%08lx.%08lx.%08lx)", txq->txq_lo, + ((unsigned long *)txd)[0], ((unsigned long *)txd)[1], + ((unsigned long *)txd)[2], ((unsigned long *)txd)[3])); +#endif + + membarrier(); + + /* + * Tell the SDMA engine to "Fetch!" + */ + GE_WRITE(sc, ESDCMR, + txq->txq_esdcmrbits & (ETH_ESDCMR_TXDH|ETH_ESDCMR_TXDL)); + + GE_DPRINTF(sc, ("(%d)", txq->txq_lo)); + + /* + * Update the last out appropriately. + */ + txq->txq_nactive += used; + txq->txq_lo += used; + if ( txq->txq_lo >= GE_TXDESC_MAX ) + txq->txq_lo -= GE_TXDESC_MAX; + + /* + * Move mbuf from the pending queue to the snd queue. + */ + IF_DEQUEUE(&txq->txq_pendq, m_head); + + IF_ENQUEUE(&txq->txq_sentq, m_head); + +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m_head); +#endif + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * Since we have put an item into the packet queue, we now want + * an interrupt when the transmit queue finishes processing the + * list. But only update the mask if needs changing. + */ + intrmask |= txq->txq_intrbits & (ETH_IR_TxEndHigh|ETH_IR_TxEndLow); + if (sc->sc_intrmask != intrmask) { + sc->sc_intrmask = intrmask; + GE_WRITE(sc, EIMR, sc->sc_intrmask); + } + if (ifp->if_timer == 0) + ifp->if_timer = 5; + GE_FUNC_EXIT(sc, "*"); + return 1; +} +#endif + +uint32_t +gfe_tx_done(struct gfe_softc *sc, enum gfe_txprio txprio, uint32_t intrmask) +{ + struct gfe_txqueue * const txq = &sc->sc_txq[txprio]; + struct ifnet * const ifp = &sc->sc_ec.ec_if; + + GE_FUNC_ENTER(sc, "gfe_tx_done"); + + if (txq == NULL) { + GE_FUNC_EXIT(sc, ""); + return intrmask; + } + + while (txq->txq_nactive > 0) { + if ( gfe_free_slots(sc, txq) < 0 ) + return intrmask; + ifp->if_timer = 5; + } + if (txq->txq_nactive != 0) + panic("%s: transmit fifo%d empty but active count (%d) not 0!", + sc->sc_dev.dv_xname, txprio, txq->txq_nactive); + ifp->if_timer = 0; + intrmask &= ~(txq->txq_intrbits & (ETH_IR_TxEndHigh|ETH_IR_TxEndLow)); + intrmask &= ~(txq->txq_intrbits & (ETH_IR_TxBufferHigh|ETH_IR_TxBufferLow)); + GE_FUNC_EXIT(sc, ""); + return intrmask; +} + +int +gfe_tx_txqalloc(struct gfe_softc *sc, enum gfe_txprio txprio) +{ + struct gfe_txqueue * const txq = &sc->sc_txq[txprio]; + int error; + + GE_FUNC_ENTER(sc, "gfe_tx_txqalloc"); + + error = gfe_dmamem_alloc(sc, &txq->txq_desc_mem, 1, + GE_TXDESC_MEMSIZE, BUS_DMA_NOCACHE); + if (error) { + GE_FUNC_EXIT(sc, ""); + return error; + } +#ifndef __rtems__ + error = gfe_dmamem_alloc(sc, &txq->txq_buf_mem, 1, GE_TXBUF_SIZE, 0); + if (error) { + gfe_dmamem_free(sc, &txq->txq_desc_mem); + GE_FUNC_EXIT(sc, ""); + return error; + } +#endif + GE_FUNC_EXIT(sc, ""); + return 0; +} + +int +gfe_tx_start(struct gfe_softc *sc, enum gfe_txprio txprio) +{ + struct gfe_txqueue * const txq = &sc->sc_txq[txprio]; + volatile struct gt_eth_desc *txd; + unsigned int i; + bus_addr_t addr; + + GE_FUNC_ENTER(sc, "gfe_tx_start"); + + sc->sc_intrmask &= ~(ETH_IR_TxEndHigh|ETH_IR_TxBufferHigh| + ETH_IR_TxEndLow |ETH_IR_TxBufferLow); + + if (sc->sc_flags & GE_NOFREE) { + KASSERT(txq->txq_desc_mem.gdm_kva != NULL); +#ifndef __rtems__ + KASSERT(txq->txq_buf_mem.gdm_kva != NULL); +#endif + } else { + int error = gfe_tx_txqalloc(sc, txprio); + if (error) { + GE_FUNC_EXIT(sc, "!"); + return error; + } + } + + txq->txq_descs = + (volatile struct gt_eth_desc *) txq->txq_desc_mem.gdm_kva; + txq->txq_desc_busaddr = txq->txq_desc_mem.gdm_map->dm_segs[0].ds_addr; +#ifndef __rtems__ + txq->txq_buf_busaddr = txq->txq_buf_mem.gdm_map->dm_segs[0].ds_addr; +#else + /* never used */ + memset(&txq->txq_pendq,0,sizeof(txq->txq_pendq)); + memset(&txq->txq_sentq,0,sizeof(txq->txq_sentq)); + txq->txq_sentq.ifq_maxlen = 100000; +#endif + + txq->txq_pendq.ifq_maxlen = 10; +#ifndef __rtems__ + txq->txq_ei_gapcount = 0; +#endif + txq->txq_nactive = 0; + txq->txq_fi = 0; + txq->txq_lo = 0; +#ifndef __rtems__ + txq->txq_ei_gapcount = 0; + txq->txq_inptr = GE_TXBUF_SIZE; + txq->txq_outptr = 0; +#endif + for (i = 0, txd = txq->txq_descs, + addr = txq->txq_desc_busaddr + sizeof(*txd); + i < GE_TXDESC_MAX - 1; + i++, txd++, addr += sizeof(*txd)) { + /* + * update the nxtptr to point to the next txd. + */ + txd->ed_cmdsts = 0; + txd->ed_nxtptr = htogt32(addr); + } + txq->txq_descs[GE_TXDESC_MAX-1].ed_nxtptr = + htogt32(txq->txq_desc_busaddr); + bus_dmamap_sync(sc->sc_dmat, txq->txq_desc_mem.gdm_map, 0, + GE_TXDESC_MEMSIZE, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + switch (txprio) { + case GE_TXPRIO_HI: + txq->txq_intrbits = ETH_IR_TxEndHigh|ETH_IR_TxBufferHigh; + txq->txq_esdcmrbits = ETH_ESDCMR_TXDH; + txq->txq_epsrbits = ETH_EPSR_TxHigh; + txq->txq_ectdp = ETH_ECTDP1(sc->sc_macno); + GE_WRITE(sc, ECTDP1, txq->txq_desc_busaddr); + break; + + case GE_TXPRIO_LO: + txq->txq_intrbits = ETH_IR_TxEndLow|ETH_IR_TxBufferLow; + txq->txq_esdcmrbits = ETH_ESDCMR_TXDL; + txq->txq_epsrbits = ETH_EPSR_TxLow; + txq->txq_ectdp = ETH_ECTDP0(sc->sc_macno); + GE_WRITE(sc, ECTDP0, txq->txq_desc_busaddr); + break; + + case GE_TXPRIO_NONE: + break; + } +#if 0 + GE_DPRINTF(sc, ("(ectdp=%#x", txq->txq_ectdp)); + gt_write(sc->sc_dev.dv_parent, txq->txq_ectdp, txq->txq_desc_busaddr); + GE_DPRINTF(sc, (")")); +#endif + + /* + * If we are restarting, there may be packets in the pending queue + * waiting to be enqueued. Try enqueuing packets from both priority + * queues until the pending queue is empty or there no room for them + * on the device. + */ + while (gfe_tx_enqueue(sc, txprio)) + continue; + + GE_FUNC_EXIT(sc, ""); + return 0; +} + +void +gfe_tx_cleanup(struct gfe_softc *sc, enum gfe_txprio txprio, int flush) +{ + struct gfe_txqueue * const txq = &sc->sc_txq[txprio]; + + GE_FUNC_ENTER(sc, "gfe_tx_cleanup"); + if (txq == NULL) { + GE_FUNC_EXIT(sc, ""); + return; + } + + if (!flush) { + GE_FUNC_EXIT(sc, ""); + return; + } + +#ifdef __rtems__ + /* reclaim mbufs that were never sent */ + { + struct mbuf *m; + while ( txq->txq_sentq.ifq_head ) { + IF_DEQUEUE(&txq->txq_sentq, m); + m_freem(m); + } + } +#endif + + if ((sc->sc_flags & GE_NOFREE) == 0) { + gfe_dmamem_free(sc, &txq->txq_desc_mem); +#ifndef __rtems__ + gfe_dmamem_free(sc, &txq->txq_buf_mem); +#endif + } + GE_FUNC_EXIT(sc, "-F"); +} + +void +gfe_tx_stop(struct gfe_softc *sc, enum gfe_whack_op op) +{ + GE_FUNC_ENTER(sc, "gfe_tx_stop"); + + GE_WRITE(sc, ESDCMR, ETH_ESDCMR_STDH|ETH_ESDCMR_STDL); + + sc->sc_intrmask = gfe_tx_done(sc, GE_TXPRIO_HI, sc->sc_intrmask); + sc->sc_intrmask = gfe_tx_done(sc, GE_TXPRIO_LO, sc->sc_intrmask); + sc->sc_intrmask &= ~(ETH_IR_TxEndHigh|ETH_IR_TxBufferHigh| + ETH_IR_TxEndLow |ETH_IR_TxBufferLow); + + gfe_tx_cleanup(sc, GE_TXPRIO_HI, op == GE_WHACK_STOP); + gfe_tx_cleanup(sc, GE_TXPRIO_LO, op == GE_WHACK_STOP); + + sc->sc_ec.ec_if.if_timer = 0; + GE_FUNC_EXIT(sc, ""); +} + +int +gfe_intr(void *arg) +{ + struct gfe_softc * const sc = arg; + uint32_t cause; + uint32_t intrmask = sc->sc_intrmask; + int claim = 0; + int cnt; + + GE_FUNC_ENTER(sc, "gfe_intr"); + + for (cnt = 0; cnt < 4; cnt++) { + if (sc->sc_intrmask != intrmask) { + sc->sc_intrmask = intrmask; + GE_WRITE(sc, EIMR, sc->sc_intrmask); + } + cause = GE_READ(sc, EICR); + cause &= sc->sc_intrmask; + GE_DPRINTF(sc, (".%#" PRIx32, cause)); + if (cause == 0) + break; + + claim = 1; + + GE_WRITE(sc, EICR, ~cause); +#ifndef GE_NORX + if (cause & (ETH_IR_RxBuffer|ETH_IR_RxError)) + intrmask = gfe_rx_process(sc, cause, intrmask); +#endif + +#ifndef GE_NOTX + if (cause & (ETH_IR_TxBufferHigh|ETH_IR_TxEndHigh)) + intrmask = gfe_tx_done(sc, GE_TXPRIO_HI, intrmask); + if (cause & (ETH_IR_TxBufferLow|ETH_IR_TxEndLow)) + intrmask = gfe_tx_done(sc, GE_TXPRIO_LO, intrmask); +#endif + if (cause & ETH_IR_MIIPhySTC) { + sc->sc_flags |= GE_PHYSTSCHG; + /* intrmask &= ~ETH_IR_MIIPhySTC; */ + } + } + + while (gfe_tx_enqueue(sc, GE_TXPRIO_HI)) + continue; + while (gfe_tx_enqueue(sc, GE_TXPRIO_LO)) + continue; + + GE_FUNC_EXIT(sc, ""); + return claim; +} + +#ifndef __rtems__ +int +gfe_mii_mediachange (struct ifnet *ifp) +{ + struct gfe_softc *sc = ifp->if_softc; + + if (ifp->if_flags & IFF_UP) + mii_mediachg(&sc->sc_mii); + + return (0); +} +void +gfe_mii_mediastatus (struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct gfe_softc *sc = ifp->if_softc; + + if (sc->sc_flags & GE_PHYSTSCHG) { + sc->sc_flags &= ~GE_PHYSTSCHG; + mii_pollstat(&sc->sc_mii); + } + ifmr->ifm_status = sc->sc_mii.mii_media_status; + ifmr->ifm_active = sc->sc_mii.mii_media_active; +} + +int +gfe_mii_read (struct device *self, int phy, int reg) +{ + return gt_mii_read(self, self->dv_parent, phy, reg); +} + +void +gfe_mii_write (struct device *self, int phy, int reg, int value) +{ + gt_mii_write(self, self->dv_parent, phy, reg, value); +} + +void +gfe_mii_statchg (struct device *self) +{ + /* struct gfe_softc *sc = (struct gfe_softc *) self; */ + /* do nothing? */ +} + +#else +int +gfe_mii_read(int phy, void *arg, unsigned reg, uint32_t *pval) +{ + struct gfe_softc *sc = arg; + uint32_t data; + int count = 10000; + + if ( 0 != phy ) + return -1; /* invalid index */ + + phy = sc->sc_phyaddr; + + do { + DELAY(10); + data = GT_READ(sc, ETH_ESMIR); + } while ((data & ETH_ESMIR_Busy) && count-- > 0); + + if (count == 0) { + fprintf(stderr,"%s: mii read for phy %d reg %d busied out\n", + sc->sc_dev.dv_xname, phy, reg); + *pval = ETH_ESMIR_Value_GET(data); + return -1; + } + + GT_WRITE(sc, ETH_ESMIR, ETH_ESMIR_READ(phy, reg)); + + count = 10000; + do { + DELAY(10); + data = GT_READ(sc, ETH_ESMIR); + } while ((data & ETH_ESMIR_ReadValid) == 0 && count-- > 0); + + if (count == 0) + printf("%s: mii read for phy %d reg %d timed out\n", + sc->sc_dev.dv_xname, phy, reg); +#if defined(GTMIIDEBUG) + printf("%s: mii_read(%d, %d): %#x data %#x\n", + sc->sc_dev.dv_xname, phy, reg, + data, ETH_ESMIR_Value_GET(data)); +#endif + *pval = ETH_ESMIR_Value_GET(data); + return 0; +} + +int +gfe_mii_write(int phy, void *arg, unsigned reg, uint32_t value) +{ + struct gfe_softc *sc = arg; + uint32_t data; + int count = 10000; + + if ( 0 != phy ) + return -1; /* invalid index */ + + phy = sc->sc_phyaddr; + + do { + DELAY(10); + data = GT_READ(sc, ETH_ESMIR); + } while ((data & ETH_ESMIR_Busy) && count-- > 0); + + if (count == 0) { + fprintf(stderr, "%s: mii write for phy %d reg %d busied out (busy)\n", + sc->sc_dev.dv_xname, phy, reg); + return -1; + } + + GT_WRITE(sc, ETH_ESMIR, + ETH_ESMIR_WRITE(phy, reg, value)); + + count = 10000; + do { + DELAY(10); + data = GT_READ(sc, ETH_ESMIR); + } while ((data & ETH_ESMIR_Busy) && count-- > 0); + + if (count == 0) + printf("%s: mii write for phy %d reg %d timed out\n", + sc->sc_dev.dv_xname, phy, reg); +#if defined(GTMIIDEBUG) + printf("%s: mii_write(%d, %d, %#x)\n", + sc->sc_dev.dv_xname, phy, reg, value); +#endif + return 0; +} + +#endif +int +gfe_whack(struct gfe_softc *sc, enum gfe_whack_op op) +{ + int error = 0; + GE_FUNC_ENTER(sc, "gfe_whack"); + + switch (op) { + case GE_WHACK_RESTART: +#ifndef GE_NOTX + gfe_tx_stop(sc, op); +#endif + /* sc->sc_ec.ec_if.if_flags &= ~IFF_RUNNING; */ + /* FALLTHROUGH */ + case GE_WHACK_START: +#ifndef GE_NOHASH + if (error == 0 && sc->sc_hashtable == NULL) { + error = gfe_hash_alloc(sc); + if (error) + break; + } + if (op != GE_WHACK_RESTART) + gfe_hash_fill(sc); +#endif +#ifndef GE_NORX + if (op != GE_WHACK_RESTART) { + error = gfe_rx_prime(sc); + if (error) + break; + } +#endif +#ifndef GE_NOTX + error = gfe_tx_start(sc, GE_TXPRIO_HI); + if (error) + break; +#endif + sc->sc_ec.ec_if.if_flags |= IFF_RUNNING; + GE_WRITE(sc, EPCR, sc->sc_pcr | ETH_EPCR_EN); + GE_WRITE(sc, EPCXR, sc->sc_pcxr); + GE_WRITE(sc, EICR, 0); + GE_WRITE(sc, EIMR, sc->sc_intrmask); +#ifndef GE_NOHASH + GE_WRITE(sc, EHTPR, sc->sc_hash_mem.gdm_map->dm_segs->ds_addr); +#endif +#ifndef GE_NORX + GE_WRITE(sc, ESDCMR, ETH_ESDCMR_ERD); + sc->sc_flags |= GE_RXACTIVE; +#endif + /* FALLTHROUGH */ + case GE_WHACK_CHANGE: + GE_DPRINTF(sc, ("(pcr=%#x,imr=%#x)", + GE_READ(sc, EPCR), GE_READ(sc, EIMR))); + GE_WRITE(sc, EPCR, sc->sc_pcr | ETH_EPCR_EN); + GE_WRITE(sc, EIMR, sc->sc_intrmask); + gfe_ifstart(&sc->sc_ec.ec_if); + GE_DPRINTF(sc, ("(ectdp0=%#x, ectdp1=%#x)", + GE_READ(sc, ECTDP0), GE_READ(sc, ECTDP1))); + GE_FUNC_EXIT(sc, ""); + return error; + case GE_WHACK_STOP: + break; + } + +#ifdef GE_DEBUG + if (error) + GE_DPRINTF(sc, (" failed: %d\n", error)); +#endif + GE_WRITE(sc, EPCR, sc->sc_pcr); + GE_WRITE(sc, EIMR, 0); + sc->sc_ec.ec_if.if_flags &= ~IFF_RUNNING; +#ifndef GE_NOTX + gfe_tx_stop(sc, GE_WHACK_STOP); +#endif +#ifndef GE_NORX + gfe_rx_stop(sc, GE_WHACK_STOP); +#endif +#ifndef GE_NOHASH + if ((sc->sc_flags & GE_NOFREE) == 0) { + gfe_dmamem_free(sc, &sc->sc_hash_mem); + sc->sc_hashtable = NULL; + } +#endif + + GE_FUNC_EXIT(sc, ""); + return error; +} + +int +gfe_hash_compute(struct gfe_softc *sc, const uint8_t eaddr[ETHER_ADDR_LEN]) +{ + uint32_t w0, add0, add1; + uint32_t result; +#ifdef __rtems__ + SPRINTFVARDECL; +#endif + + GE_FUNC_ENTER(sc, "gfe_hash_compute"); + add0 = ((uint32_t) eaddr[5] << 0) | + ((uint32_t) eaddr[4] << 8) | + ((uint32_t) eaddr[3] << 16); + + add0 = ((add0 & 0x00f0f0f0) >> 4) | ((add0 & 0x000f0f0f) << 4); + add0 = ((add0 & 0x00cccccc) >> 2) | ((add0 & 0x00333333) << 2); + add0 = ((add0 & 0x00aaaaaa) >> 1) | ((add0 & 0x00555555) << 1); + + add1 = ((uint32_t) eaddr[2] << 0) | + ((uint32_t) eaddr[1] << 8) | + ((uint32_t) eaddr[0] << 16); + + add1 = ((add1 & 0x00f0f0f0) >> 4) | ((add1 & 0x000f0f0f) << 4); + add1 = ((add1 & 0x00cccccc) >> 2) | ((add1 & 0x00333333) << 2); + add1 = ((add1 & 0x00aaaaaa) >> 1) | ((add1 & 0x00555555) << 1); + + GE_DPRINTF(sc, ("%s=", ether_sprintf(eaddr))); + /* + * hashResult is the 15 bits Hash entry address. + * ethernetADD is a 48 bit number, which is derived from the Ethernet + * MAC address, by nibble swapping in every byte (i.e MAC address + * of 0x123456789abc translates to ethernetADD of 0x21436587a9cb). + */ + + if ((sc->sc_pcr & ETH_EPCR_HM) == 0) { + /* + * hashResult[14:0] = hashFunc0(ethernetADD[47:0]) + * + * hashFunc0 calculates the hashResult in the following manner: + * hashResult[ 8:0] = ethernetADD[14:8,1,0] + * XOR ethernetADD[23:15] XOR ethernetADD[32:24] + */ + result = (add0 & 3) | ((add0 >> 6) & ~3); + result ^= (add0 >> 15) ^ (add1 >> 0); + result &= 0x1ff; + /* + * hashResult[14:9] = ethernetADD[7:2] + */ + result |= (add0 & ~3) << 7; /* excess bits will be masked */ + GE_DPRINTF(sc, ("0(%#"PRIx32")", result & 0x7fff)); + } else { +#define TRIBITFLIP 073516240 /* yes its in octal */ + /* + * hashResult[14:0] = hashFunc1(ethernetADD[47:0]) + * + * hashFunc1 calculates the hashResult in the following manner: + * hashResult[08:00] = ethernetADD[06:14] + * XOR ethernetADD[15:23] XOR ethernetADD[24:32] + */ + w0 = ((add0 >> 6) ^ (add0 >> 15) ^ (add1)) & 0x1ff; + /* + * Now bitswap those 9 bits + */ + result = 0; + result |= ((TRIBITFLIP >> (((w0 >> 0) & 7) * 3)) & 7) << 6; + result |= ((TRIBITFLIP >> (((w0 >> 3) & 7) * 3)) & 7) << 3; + result |= ((TRIBITFLIP >> (((w0 >> 6) & 7) * 3)) & 7) << 0; + + /* + * hashResult[14:09] = ethernetADD[00:05] + */ + result |= ((TRIBITFLIP >> (((add0 >> 0) & 7) * 3)) & 7) << 12; + result |= ((TRIBITFLIP >> (((add0 >> 3) & 7) * 3)) & 7) << 9; + GE_DPRINTF(sc, ("1(%#"PRIx32")", result)); + } + GE_FUNC_EXIT(sc, ""); + return result & ((sc->sc_pcr & ETH_EPCR_HS_512) ? 0x7ff : 0x7fff); +} + +int +gfe_hash_entry_op(struct gfe_softc *sc, enum gfe_hash_op op, + enum gfe_rxprio prio, const uint8_t eaddr[ETHER_ADDR_LEN]) +{ + uint64_t he; + uint64_t *maybe_he_p = NULL; + int limit; + int hash; + int maybe_hash = 0; + + GE_FUNC_ENTER(sc, "gfe_hash_entry_op"); + + hash = gfe_hash_compute(sc, eaddr); + + if (sc->sc_hashtable == NULL) { + panic("%s:%d: hashtable == NULL!", sc->sc_dev.dv_xname, + __LINE__); + } + + /* + * Assume we are going to insert so create the hash entry we + * are going to insert. We also use it to match entries we + * will be removing. + */ + he = ((uint64_t) eaddr[5] << 43) | + ((uint64_t) eaddr[4] << 35) | + ((uint64_t) eaddr[3] << 27) | + ((uint64_t) eaddr[2] << 19) | + ((uint64_t) eaddr[1] << 11) | + ((uint64_t) eaddr[0] << 3) | + HSH_PRIO_INS(prio) | HSH_V | HSH_R; + + /* + * The GT will search upto 12 entries for a hit, so we must mimic that. + */ + hash &= sc->sc_hashmask / sizeof(he); + for (limit = HSH_LIMIT; limit > 0 ; --limit) { + /* + * Does the GT wrap at the end, stop at the, or overrun the + * end? Assume it wraps for now. Stash a copy of the + * current hash entry. + */ + uint64_t *he_p = &sc->sc_hashtable[hash]; + uint64_t thishe = *he_p; + + /* + * If the hash entry isn't valid, that break the chain. And + * this entry a good candidate for reuse. + */ + if ((thishe & HSH_V) == 0) { + maybe_he_p = he_p; + break; + } + + /* + * If the hash entry has the same address we are looking for + * then ... if we are removing and the skip bit is set, its + * already been removed. if are adding and the skip bit is + * clear, then its already added. In either return EBUSY + * indicating the op has already been done. Otherwise flip + * the skip bit and return 0. + */ + if (((he ^ thishe) & HSH_ADDR_MASK) == 0) { + if (((op == GE_HASH_REMOVE) && (thishe & HSH_S)) || + ((op == GE_HASH_ADD) && (thishe & HSH_S) == 0)) + return EBUSY; + *he_p = thishe ^ HSH_S; + bus_dmamap_sync(sc->sc_dmat, sc->sc_hash_mem.gdm_map, + hash * sizeof(he), sizeof(he), + BUS_DMASYNC_PREWRITE); + GE_FUNC_EXIT(sc, "^"); + return 0; + } + + /* + * If we haven't found a slot for the entry and this entry + * is currently being skipped, return this entry. + */ + if (maybe_he_p == NULL && (thishe & HSH_S)) { + maybe_he_p = he_p; + maybe_hash = hash; + } + + hash = (hash + 1) & (sc->sc_hashmask / sizeof(he)); + } + + /* + * If we got here, then there was no entry to remove. + */ + if (op == GE_HASH_REMOVE) { + GE_FUNC_EXIT(sc, "?"); + return ENOENT; + } + + /* + * If we couldn't find a slot, return an error. + */ + if (maybe_he_p == NULL) { + GE_FUNC_EXIT(sc, "!"); + return ENOSPC; + } + + /* Update the entry. + */ + *maybe_he_p = he; + bus_dmamap_sync(sc->sc_dmat, sc->sc_hash_mem.gdm_map, + maybe_hash * sizeof(he), sizeof(he), BUS_DMASYNC_PREWRITE); + GE_FUNC_EXIT(sc, "+"); + return 0; +} + +#ifndef __rtems__ +int +gfe_hash_multichg(struct ethercom *ec, const struct ether_multi *enm, u_long cmd) +{ + struct gfe_softc * const sc = ec->ec_if.if_softc; + int error; + enum gfe_hash_op op; + enum gfe_rxprio prio; +#ifdef __rtems__ + SPRINTFVARDECL; +#endif + + GE_FUNC_ENTER(sc, "hash_multichg"); + /* + * Is this a wildcard entry? If so and its being removed, recompute. + */ + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) != 0) { + if (cmd == SIOCDELMULTI) { + GE_FUNC_EXIT(sc, ""); + return ENETRESET; + } + + /* + * Switch in + */ + sc->sc_flags |= GE_ALLMULTI; + if ((sc->sc_pcr & ETH_EPCR_PM) == 0) { + sc->sc_pcr |= ETH_EPCR_PM; + GE_WRITE(sc, EPCR, sc->sc_pcr); + GE_FUNC_EXIT(sc, ""); + return 0; + } + GE_FUNC_EXIT(sc, ""); + return ENETRESET; + } + + prio = GE_RXPRIO_MEDLO; + op = (cmd == SIOCDELMULTI ? GE_HASH_REMOVE : GE_HASH_ADD); + + if (sc->sc_hashtable == NULL) { + GE_FUNC_EXIT(sc, ""); + return 0; + } + + error = gfe_hash_entry_op(sc, op, prio, enm->enm_addrlo); + if (error == EBUSY) { + printf("%s: multichg: tried to %s %s again\n", + sc->sc_dev.dv_xname, + cmd == SIOCDELMULTI ? "remove" : "add", + ether_sprintf(enm->enm_addrlo)); + GE_FUNC_EXIT(sc, ""); + return 0; + } + + if (error == ENOENT) { + printf("%s: multichg: failed to remove %s: not in table\n", + sc->sc_dev.dv_xname, + ether_sprintf(enm->enm_addrlo)); + GE_FUNC_EXIT(sc, ""); + return 0; + } + + if (error == ENOSPC) { + printf("%s: multichg: failed to add %s: no space; regenerating table\n", + sc->sc_dev.dv_xname, + ether_sprintf(enm->enm_addrlo)); + GE_FUNC_EXIT(sc, ""); + return ENETRESET; + } + GE_DPRINTF(sc, ("%s: multichg: %s: %s succeeded\n", + sc->sc_dev.dv_xname, + cmd == SIOCDELMULTI ? "remove" : "add", + ether_sprintf(enm->enm_addrlo))); + GE_FUNC_EXIT(sc, ""); + return 0; +} +#endif + +int +gfe_hash_fill(struct gfe_softc *sc) +{ + struct ether_multistep step; + struct ether_multi *enm; + int error; + + GE_FUNC_ENTER(sc, "gfe_hash_fill"); + +#ifndef __rtems__ + error = gfe_hash_entry_op(sc, GE_HASH_ADD, GE_RXPRIO_HI, + LLADDR(sc->sc_ec.ec_if.if_sadl)); +#else + error = gfe_hash_entry_op(sc, GE_HASH_ADD, GE_RXPRIO_HI, sc->sc_ec.ac_enaddr); +#endif + if (error) { + GE_FUNC_EXIT(sc, "!"); + return error; + } + + sc->sc_flags &= ~GE_ALLMULTI; + if ((sc->sc_ec.ec_if.if_flags & IFF_PROMISC) == 0) + sc->sc_pcr &= ~ETH_EPCR_PM; + else + sc->sc_pcr |= ETH_EPCR_PM; + ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); + while (enm != NULL) { + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + sc->sc_flags |= GE_ALLMULTI; + sc->sc_pcr |= ETH_EPCR_PM; + } else { + error = gfe_hash_entry_op(sc, GE_HASH_ADD, + GE_RXPRIO_MEDLO, enm->enm_addrlo); + if (error == ENOSPC) + break; + } + ETHER_NEXT_MULTI(step, enm); + } + + GE_FUNC_EXIT(sc, ""); + return error; +} + +int +gfe_hash_alloc(struct gfe_softc *sc) +{ + int error; + GE_FUNC_ENTER(sc, "gfe_hash_alloc"); + sc->sc_hashmask = (sc->sc_pcr & ETH_EPCR_HS_512 ? 16 : 256)*1024 - 1; + error = gfe_dmamem_alloc(sc, &sc->sc_hash_mem, 1, sc->sc_hashmask + 1, + BUS_DMA_NOCACHE); + if (error) { + printf("%s: failed to allocate %d bytes for hash table: %d\n", + sc->sc_dev.dv_xname, sc->sc_hashmask + 1, error); + GE_FUNC_EXIT(sc, ""); + return error; + } + sc->sc_hashtable = (uint64_t *) sc->sc_hash_mem.gdm_kva; + memset(sc->sc_hashtable, 0, sc->sc_hashmask + 1); + bus_dmamap_sync(sc->sc_dmat, sc->sc_hash_mem.gdm_map, + 0, sc->sc_hashmask + 1, BUS_DMASYNC_PREWRITE); + GE_FUNC_EXIT(sc, ""); + return 0; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_pub.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_pub.h new file mode 100644 index 0000000000..8988f88a5c --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_pub.h @@ -0,0 +1,31 @@ +/* $Id$ */ +#ifndef RTEMS_BSDNET_IF_GFE_PUBLIC_SYMBOLS_H +#define RTEMS_BSDNET_IF_GFE_PUBLIC_SYMBOLS_H + +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +extern int +rtems_gfe_attach(struct rtems_bsdnet_ifconfig *, int); + + +/* enet_addr must be 6 bytes long */ +int +rtems_gfe_setup(int unit, char *enet_addr, uint32_t base_addr); + +extern rtems_bsdnet_early_link_check_ops +rtems_gfe_early_link_check_ops; + +#ifdef __cplusplus + } +#endif + +#endif + + diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_rtems.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_rtems.c new file mode 100644 index 0000000000..069e0d9a89 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfe_rtems.c @@ -0,0 +1,130 @@ +/* $Id$ */ + +/* Author: T. Straumann ; see ../../LICENSE */ +#include "rtemscompat.h" +#include "gtethreg.h" + +#include +#include +#include + +/* from if_gfe.c */ +#define GE_READ(sc, reg) \ + bus_space_read_4((sc)->sc_gt_memt, (sc)->sc_memh, ETH__ ## reg) +#define GE_WRITE(sc, reg, v) \ + bus_space_write_4((sc)->sc_gt_memt, (sc)->sc_memh, ETH__ ## reg, (v)) + +#define GT_READ(sc, reg) \ + bus_space_read_4((sc)->sc_gt_memt, (sc)->sc_gt_memh, reg) +#define GT_WRITE(sc, reg, v) \ + bus_space_write_4((sc)->sc_gt_memt, (sc)->sc_gt_memh, reg, (v)) + +#include "if_xxx_rtems.c" + +#include +#include + +int +NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_setup)( + int unit, + char *ea, + uint32_t base_addr) +{ +struct NET_SOFTC *sc; + + if ( !ea ) { + fprintf(stderr,"Station address argument missing\n"); + return 0; + } + + if ( !(sc=net_drv_check_unit(unit)) ) { + fprintf(stderr,"Bad unit number -- (not enought driver slots?)\n"); + return 0; + } + + unit--; + +#ifdef DEBUG_MODULAR + if ( !METHODSPTR ) { + fprintf(stderr,"Methods not set -- module not loaded?\n"); + return 0; + } +#endif + + if ( !base_addr ) { +#ifdef BSP_MV64x60_BASE + base_addr = BSP_MV64x60_BASE; +#else + fprintf(stderr,"Missing GT64260 base address\n"); + return 0; +#endif + } + sc->sc_gt_memh = base_addr; + /* must set this as well to indicate that the device is set up */ + sc->NET_SOFTC_BHANDLE_FIELD = base_addr + 0x2400 + (unit<<10); + sc->sc_macno = unit; + memcpy( sc->arpcom.ac_enaddr, ea, ETHER_ADDR_LEN); + + if ( 0 == METHODSPTR->n_probe(&THEDEVS[unit]) ) { + printf(NETDRIVER": Unit %i set up\n", unit + 1); + sc->irq_no = BSP_IRQ_ETH0 + unit; + return 1; + } + return 0; +} + +static int +gfe_early_init(int idx) +{ +struct gfe_softc *sc; +uint32_t d; + + if ( idx < 0 || idx >= NETDRIVER_SLOTS ) + return -1; + + sc = device_get_softc(&the_gfe_devs[idx]); + d = bus_space_read_4(sc->sc_gt_memt, sc->sc_gt_memh, ETH_EPAR); + + sc->sc_phyaddr = ETH_EPAR_PhyAD_GET(d, sc->sc_macno); + sc->sc_dev.dv_xname = NETDRIVER; + return 0; +} + +static int +gfe_early_read_phy(int idx, unsigned reg) +{ +uint32_t rval; +struct gfe_softc *sc; + + if ( idx < 0 || idx >= NETDRIVER_SLOTS ) + return -1; + + sc = device_get_softc(&the_gfe_devs[idx]); + + if ( gfe_mii_read( 0, sc, reg, &rval) ) + return -1; + return rval & 0xffff; +} + + +static int +gfe_early_write_phy(int idx, unsigned reg, unsigned val) +{ +struct gfe_softc *sc; + + if ( idx < 0 || idx >= NETDRIVER_SLOTS ) + return -1; + + sc = device_get_softc(&the_gfe_devs[idx]); + + return gfe_mii_write( 0, sc, reg, val); +} + +rtems_bsdnet_early_link_check_ops +rtems_gfe_early_link_check_ops = { + init: gfe_early_init, + read_phy: gfe_early_read_phy, + write_phy: gfe_early_write_phy, + name: NETDRIVER, + num_slots: NETDRIVER_SLOTS +}; diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfevar.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfevar.h new file mode 100644 index 0000000000..cbb9609cf8 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/if_gfevar.h @@ -0,0 +1,225 @@ +#ifndef IF_GFEVAR_H +#define IF_GFEVAR_H +/* $NetBSD: if_gfevar.h,v 1.4.10.1 2005/04/29 11:28:56 kent Exp $ */ + +/* + * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc. + * 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 for the NetBSD Project by + * Allegro Networks, Inc., and Wasabi Systems, Inc. + * 4. The name of Allegro Networks, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * 5. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND + * WASABI SYSTEMS, INC. ``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 EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC. + * 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. + */ + +/* NOTE: GE_RXDESC_MAX * 16 <= GE_RXDESC_MEMSIZE */ +/* NOTE: the driver needs 4*GE_RXDESC_MAX mbuf clusters (4 queues) */ +#ifndef __rtems__ +#define GE_RXDESC_MEMSIZE (1 * PAGE_SIZE) +#define GE_RXDESC_MAX 64 +#define GE_RXBUF_SIZE 2048 +#define GE_RXBUF_MEMSIZE (GE_RXDESC_MAX*GE_RXBUF_SIZE) +#else +#define GE_RXDESC_MEMSIZE (GE_RXDESC_MAX * sizeof(struct gt_eth_desc)) +#define GE_RXDESC_MAX (sc->num_rxdesc) +#define GE_RXBUF_MEMSIZE 0 +#endif + +#define GE_RXBUF_NSEGS ((GE_RXBUF_MEMSIZE/PAGE_SIZE)+1) +#define GE_DMSEG_MAX (GE_RXBUF_NSEGS) + +struct gfe_dmamem { + bus_dmamap_t gdm_map; /* dmamem'ed memory */ +#ifdef __rtems__ + void *gdm_unaligned_buf; +#endif + caddr_t gdm_kva; /* kva of tx memory */ + int gdm_nsegs; /* # of segment in gdm_segs */ + int gdm_maxsegs; /* maximum # of segments allowed */ + size_t gdm_size; /* size of memory region */ + bus_dma_segment_t gdm_segs[GE_DMSEG_MAX]; /* dma segment of tx memory */ +}; + +/* With a 4096 page size, we get 256 descriptors per page. + */ +#ifndef __rtems__ +#define GE_TXDESC_MEMSIZE (1 * PAGE_SIZE) +#define GE_TXDESC_MAX (GE_TXDESC_MEMSIZE / 16) +#define GE_TXBUF_SIZE (4 * PAGE_SIZE) +#else +#define GE_TXDESC_MEMSIZE (sc->num_txdesc * sizeof(struct gt_eth_desc)) +#define GE_TXDESC_MAX (sc->num_txdesc) +#endif + +struct gfe_txqueue { + struct ifqueue txq_pendq; /* these are ready to go to the GT */ + struct ifqueue txq_sentq; + struct gfe_dmamem txq_desc_mem; /* transmit descriptor memory */ +#ifndef __rtems__ + struct gfe_dmamem txq_buf_mem; /* transmit buffer memory */ +#endif + unsigned int txq_lo; /* next to be given to GT */ + unsigned int txq_fi; /* next to be returned to CPU */ +#ifndef __rtems__ + unsigned int txq_ei_gapcount; /* counter until next EI */ +#endif + unsigned int txq_nactive; /* number of active descriptors */ +#ifndef __rtems__ + unsigned int txq_outptr; /* where to put next transmit packet */ + unsigned int txq_inptr; /* start of 1st queued tx packet */ +#endif + uint32_t txq_intrbits; /* bits to write to EIMR */ + uint32_t txq_esdcmrbits; /* bits to write to ESDCMR */ + uint32_t txq_epsrbits; /* bits to test with EPSR */ + volatile struct gt_eth_desc *txq_descs; /* ptr to tx descriptors */ + bus_addr_t txq_ectdp; /* offset to cur. tx desc ptr reg */ + bus_addr_t txq_desc_busaddr; /* bus addr of tx descriptors */ +#ifndef __rtems__ + bus_addr_t txq_buf_busaddr; /* bus addr of tx buffers */ +#endif +}; + +/* With a 4096 page size, we get 256 descriptors per page. We want 1024 + * which will give us about 8ms of 64 byte packets (2ms for each priority + * queue). + */ + +#ifndef __rtems__ +struct gfe_rxbuf { + uint8_t rb_data[GE_RXBUF_SIZE]; +}; +#endif + +struct gfe_rxqueue { + struct gfe_dmamem rxq_desc_mem; /* receive descriptor memory */ +#ifndef __rtems__ + struct gfe_dmamem rxq_buf_mem; /* receive buffer memory */ + struct mbuf *rxq_curpkt; /* mbuf for current packet */ +#endif + volatile struct gt_eth_desc *rxq_descs; +#ifndef __rtems__ + struct gfe_rxbuf *rxq_bufs; +#else + struct mbuf **rxq_bufs; +#endif + unsigned int rxq_fi; /* next to be returned to CPU */ + unsigned int rxq_active; /* # of descriptors given to GT */ + uint32_t rxq_intrbits; /* bits to write to EIMR */ + bus_addr_t rxq_desc_busaddr; /* bus addr of rx descriptors */ + uint32_t rxq_cmdsts; /* save cmdsts from first descriptor */ + bus_size_t rxq_efrdp; + bus_size_t rxq_ecrdp; +}; + +enum gfe_txprio { + GE_TXPRIO_HI=1, + GE_TXPRIO_LO=0, + GE_TXPRIO_NONE=2 +}; +enum gfe_rxprio { + GE_RXPRIO_HI=3, + GE_RXPRIO_MEDHI=2, + GE_RXPRIO_MEDLO=1, + GE_RXPRIO_LO=0 +}; + +#ifdef __rtems__ +#define sc_ec arpcom +#define ec_if ac_if +#define sc_dev arpcom +#define dv_xname ac_if.if_name +#endif + +struct gfe_softc { +#ifndef __rtems__ + struct device sc_dev; /* must be first */ + struct ethercom sc_ec; /* common ethernet glue */ + struct callout sc_co; /* resource recovery */ + mii_data_t sc_mii; /* mii interface */ + + /* + * + */ + bus_space_tag_t sc_gt_memt; + bus_space_handle_t sc_gt_memh; + bus_space_handle_t sc_memh; /* subregion for ethernet */ + bus_dma_tag_t sc_dmat; +#else + struct arpcom sc_ec; + unsigned sc_gt_memh; + unsigned sc_memh; + unsigned char irq_no; + rtems_id tid; + int sc_phyaddr; + int num_rxdesc, num_txdesc; +#endif + int sc_macno; /* which mac? 0, 1, or 2 */ + + unsigned int sc_tickflags; +#define GE_TICK_TX_IFSTART 0x0001 +#define GE_TICK_RX_RESTART 0x0002 + unsigned int sc_flags; +#define GE_ALLMULTI 0x0001 +#define GE_PHYSTSCHG 0x0002 +#define GE_RXACTIVE 0x0004 +#define GE_NOFREE 0x0008 /* Don't free on disable */ + uint32_t sc_pcr; /* current EPCR value */ + uint32_t sc_pcxr; /* current EPCXR value */ + uint32_t sc_intrmask; /* current EIMR value */ + uint32_t sc_idlemask; /* suspended EIMR bits */ + size_t sc_max_frame_length; /* maximum frame length */ + + /* + * Hash table related members + */ + struct gfe_dmamem sc_hash_mem; /* dma'ble hash table */ + uint64_t *sc_hashtable; + unsigned int sc_hashmask; /* 0x1ff or 0x1fff */ + + /* + * Transmit related members + */ + struct gfe_txqueue sc_txq[2]; /* High & Low transmit queues */ + + /* + * Receive related members + */ + struct gfe_rxqueue sc_rxq[4]; /* Hi/MedHi/MedLo/Lo receive queues */ +}; + +#ifdef __rtems__ +int +gfe_mii_read(int phy, void *arg, unsigned reg, uint32_t *pval); + +int +gfe_mii_write(int phy, void *arg, unsigned reg, uint32_t value); +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/rtemscompat_defs.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/rtemscompat_defs.h new file mode 100644 index 0000000000..8234681b1b --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_gfe/rtemscompat_defs.h @@ -0,0 +1,122 @@ +#ifndef RTEMS_COMPAT_DEFS_H +#define RTEMS_COMPAT_DEFS_H + +/* Number of device instances the driver should support + * - may be limited to 1 depending on IRQ API + * (braindamaged PC586 and powerpc) + */ +#define NETDRIVER_SLOTS 1 +/* String name to print with error messages */ +#define NETDRIVER "gfe" +/* Name snippet used to make global symbols unique to this driver */ +#define NETDRIVER_PREFIX gfe + +/* Define according to endianness of the *ethernet*chip* + * (not the CPU - most probably are LE) + * This must be either NET_CHIP_LE or NET_CHIP_BE + */ + +#define NET_CHIP_LE +#undef NET_CHIP_BE + +/* Define either NET_CHIP_MEM_IO or NET_CHIP_PORT_IO, + * depending whether the CPU sees it in memory address space + * or (e.g. x86) uses special I/O instructions. + */ +#define NET_CHIP_MEM_IO +#undef NET_CHIP_PORT_IO + +/* The name of the hijacked 'bus handle' field in the softc + * structure. We use this field to store the chip's base address. + */ +#define NET_SOFTC_BHANDLE_FIELD sc_memh + +/* define the names of the 'if_XXXreg.h' and 'if_XXXvar.h' headers + * (only if present, i.e., if the BSDNET driver has no respective + * header, leave this undefined). + * + */ +#undef IF_REG_HEADER +#define IF_VAR_HEADER + +/* define if a pci device */ +/* +#define NETDRIVER_PCI +*/ +#undef NETDRIVER_PCI + +/* Macros to disable and enable interrupts, respectively. + * The 'disable' macro is expanded in the ISR, the 'enable' + * macro is expanded in the driver task. + * The global network semaphore usually provides mutex + * protection of the device registers. + * Special care must be taken when coding the 'disable' macro, + * however to MAKE SURE THERE ARE NO OTHER SIDE EFFECTS such + * as: + * - macro must not clear any status flags + * - macro must save/restore any context information + * (e.g., a address register pointer or a bank switch register) + * + * ARGUMENT: the macro arg is a pointer to the driver's 'softc' structure + */ + +#define NET_DISABLE_IRQS(sc) GE_WRITE(sc, EIMR, 0) +#define NET_ENABLE_IRQS(sc) GE_WRITE(sc, EIMR, sc->sc_intrmask) + +/* Driver may provide a macro/function to copy the hardware address + * from the device into 'softc.arpcom'. + * If this is undefined, the driver must to the copy itself. + * Preferrably, it should check soft.arpcom.ac_enaddr for all + * zeros and leave it alone if it is nonzero, i.e., write it + * to the hardware. +#define NET_READ_MAC_ADDR(sc) + */ + +typedef struct { + uint32_t ds_addr; + uint32_t ds_len; +} bus_dma_segment_t; + +#define dm_segs gdm_segs +#define dm_nsegs gdm_nsegs +typedef struct gfe_dmamem *bus_dmamap_t; + +typedef uint32_t bus_addr_t; +typedef uint32_t bus_size_t; + +typedef struct device blah; + +#define BUS_DMA_NOCACHE 0xdeadbeef + +#ifdef __PPC__ +#define bus_dmamap_sync(args...) do { asm volatile("sync":::"memory"); } while(0) +#else +#error "Dont' know how to sync memory on your CPU" +#endif + +int ether_sprintf_r(const unsigned char *enaddr, char *buf, int len); + +/* we have it although we're not ansi */ +int snprintf(char *, size_t, const char *,...); + +#include + +/* declare in every routine using ether_sprintf */ +#define SPRINTFVARDECL char rtems_sprintf_local_buf[3*6] /* ethernet string */ + +#define ether_sprintf_macro(a) \ + (snprintf(rtems_sprintf_local_buf, \ + sizeof(rtems_sprintf_local_buf), \ + "%02X:%02X:%02X:%02X:%02X:%02X", \ + a[0],a[1],a[2],a[3],a[4],a[5]) ? \ + rtems_sprintf_local_buf : 0 \ + ) + + +#define aprint_normal(args...) printf(args) +#define aprint_error(args...) fprintf(stderr,args) + +#define delay(arg) DELAY(arg) + +#define KASSERT(a...) do {} while (0) +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/if_mve_pub.h b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/if_mve_pub.h new file mode 100644 index 0000000000..876c3fefdf --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/if_mve_pub.h @@ -0,0 +1,423 @@ +/* $Id$ */ +#ifndef RTEMS_BSDNET_IF_MVE_PUBLIC_SYMBOLS_H +#define RTEMS_BSDNET_IF_MVE_PUBLIC_SYMBOLS_H + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +extern int rtems_mve_attach(struct rtems_bsdnet_ifconfig *, int); +extern rtems_bsdnet_early_link_check_ops rtems_mve_early_link_check_ops; + +/* Low-level Driver API. + * This provides driver access to applications that want to use e.g., the second + * ethernet interface w/o running the BSD TCP/IP stack. + */ + +/* Opaque handle */ +struct mveth_private; + +/* Direct assignment of MVE flags to user API relies on irqs and x-irqs not overlapping */ +#define BSP_MVE_IRQ_RX (1<<2) +#define BSP_MVE_IRQ_TX (1<<0) +#define BSP_MVE_IRQ_LINK (1<<16) + +/* Setup an interface. + * Allocates resources for descriptor rings and sets up the driver software structure. + * + * Arguments: + * unit: + * interface # (1..2). The interface must not be attached to BSD. + * + * driver_tid: + * ISR posts RTEMS event # ('unit' - 1) to task with ID 'driver_tid' and disables interrupts + * from this interface. + * + * void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred): + * Pointer to user-supplied callback to release a buffer that had been sent + * by BSP_mve_send_buf() earlier. The callback is passed 'cleanup_txbuf_arg' + * and a flag indicating whether the send had been successful. + * The driver no longer accesses 'user_buf' after invoking this callback. + * CONTEXT: This callback is executed either by BSP_mve_swipe_tx() or + * BSP_mve_send_buf(), BSP_mve_init_hw(), BSP_mve_stop_hw() (the latter + * ones calling BSP_mve_swipe_tx()). + * void *cleanup_txbuf_arg: + * Closure argument that is passed on to 'cleanup_txbuf()' callback; + * + * void *(*alloc_rxbuf)(int *p_size, unsigned long *p_data_addr), + * Pointer to user-supplied callback to allocate a buffer for subsequent + * insertion into the RX ring by the driver. + * RETURNS: opaque handle to the buffer (which may be a more complex object + * such as an 'mbuf'). The handle is not used by the driver directly + * but passed back to the 'consume_rxbuf()' callback. + * Size of the available data area and pointer to buffer's data area + * in '*psize' and '*p_data_area', respectively. + * If no buffer is available, this routine should return NULL in which + * case the driver drops the last packet and re-uses the last buffer + * instead of handing it out to 'consume_rxbuf()'. + * CONTEXT: Called when initializing the RX ring (BSP_mve_init_hw()) or when + * swiping it (BSP_mve_swipe_rx()). + * + * + * void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len); + * Pointer to user-supplied callback to pass a received buffer back to + * the user. The driver no longer accesses the buffer after invoking this + * callback (with 'len'>0, see below). 'user_buf' is the buffer handle + * previously generated by 'alloc_rxbuf()'. + * The callback is passed 'cleanup_rxbuf_arg' and a 'len' + * argument giving the number of bytes that were received. + * 'len' may be <=0 in which case the 'user_buf' argument is NULL. + * 'len' == 0 means that the last 'alloc_rxbuf()' had failed, + * 'len' < 0 indicates a receiver error. In both cases, the last packet + * was dropped/missed and the last buffer will be re-used by the driver. + * NOTE: the data are 'prefixed' with two bytes, i.e., the ethernet packet header + * is stored at offset 2 in the buffer's data area. Also, the FCS (4 bytes) + * is appended. 'len' accounts for both. + * CONTEXT: Called from BSP_mve_swipe_rx(). + * void *cleanup_rxbuf_arg: + * Closure argument that is passed on to 'consume_rxbuf()' callback; + * + * rx_ring_size, tx_ring_size: + * How many big to make the RX and TX descriptor rings. Note that the sizes + * may be 0 in which case a reasonable default will be used. + * If either ring size is < 0 then the RX or TX will be disabled. + * Note that it is illegal in this case to use BSP_mve_swipe_rx() or + * BSP_mve_swipe_tx(), respectively. + * + * irq_mask: + * Interrupts to enable. OR of flags from above. + * + */ +struct mveth_private * +BSP_mve_setup( + int unit, + rtems_id driver_tid, + void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +); + +/* + * Alternate 'setup' routine allowing the user to install an ISR rather + * than a task ID. + * All parameters (other than 'isr' / 'isr_arg') and the return value + * are identical to the BSP_mve_setup() entry point. + */ +struct mveth_private * +BSP_mve_setup_1( + int unit, + void (*isr)(void *isr_arg), + void *isr_arg, + void (*cleanup_txbuf)(void *user_buf, void *cleanup_txbuf_arg, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *consume_rxbuf_arg, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +); + + +/* + * Initialize interface hardware + * + * 'mp' handle obtained by from BSP_mve_setup(). + * 'promisc' whether to set promiscuous flag. + * 'enaddr' pointer to six bytes with MAC address. Read + * from the device if NULL. + * + * Note: Multicast filters are cleared by this routine. + * However, in promiscuous mode the mcast filters + * are programmed to accept all multicast frames. + */ +void +BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr); + +/* + * Clear multicast hash filter. No multicast frames are accepted + * after executing this routine (unless the hardware was initialized + * in 'promiscuous' mode). + */ +void +BSP_mve_mcast_filter_clear(struct mveth_private *mp); + +/* + * Program multicast filter to accept all multicast frames + */ +void +BSP_mve_mcast_filter_accept_all(struct mveth_private *mp); + +/* + * Add a MAC address to the multicast filter. + * Existing entries are not changed but note that + * the filter is imperfect, i.e., multiple MAC addresses + * may alias to a single filter entry. Hence software + * filtering must still be performed. + * + * If a higher-level driver implements IP multicasting + * then multiple IP addresses may alias to the same MAC + * address. This driver maintains a 'reference-count' + * which is incremented every time the same MAC-address + * is passed to this routine; the address is only removed + * from the filter if BSP_mve_mcast_filter_accept_del() + * is called the same number of times (or by BSP_mve_mcast_filter_clear). + */ +void +BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr); + +/* + * Remove a MAC address from the multicast filter. + * This routine decrements the reference count of the given + * MAC-address and removes it from the filter once the + * count reaches zero. + */ +void +BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr); + +/* + * Shutdown hardware and clean out the rings + */ +void +BSP_mve_stop_hw(struct mveth_private *mp); + +/* calls BSP_mve_stop_hw(), releases all resources and marks the interface + * as unused. + * RETURNS 0 on success, nonzero on failure. + * NOTE: the handle MUST NOT be used after successful execution of this + * routine. + */ +int +BSP_mve_detach(struct mveth_private *mp); + +/* + * Enqueue a mbuf chain or a raw data buffer for transmission; + * RETURN: #bytes sent or -1 if there are not enough free descriptors + * + * If 'len' is <=0 then 'm_head' is assumed to point to a mbuf chain. + * OTOH, a raw data packet (or a different type of buffer) + * may be send (non-BSD driver) by pointing data_p to the start of + * the data and passing 'len' > 0. + * 'm_head' is passed back to the 'cleanup_txbuf()' callback. + * + * Comments: software cache-flushing incurs a penalty if the + * packet cannot be queued since it is flushed anyways. + * The algorithm is slightly more efficient in the normal + * case, though. + * + * RETURNS: # bytes enqueued to device for transmission or -1 if no + * space in the TX ring was available. + */ +int +BSP_mve_send_buf(struct mveth_private *mp, void *m_head, void *data_p, int len); + +/* Descriptor scavenger; cleanup the TX ring, passing all buffers + * that have been sent to the cleanup_tx() callback. + * This routine is called from BSP_mve_send_buf(), BSP_mve_init_hw(), + * BSP_mve_stop_hw(). + * + * RETURNS: number of buffers processed. + */ +int +BSP_mve_swipe_tx(struct mveth_private *mp); + +/* Retrieve all received buffers from the RX ring, replacing them + * by fresh ones (obtained from the alloc_rxbuf() callback). The + * received buffers are passed to consume_rxbuf(). + * + * RETURNS: number of buffers processed. + */ +int +BSP_mve_swipe_rx(struct mveth_private *mp); + +/* read ethernet address from hw to buffer */ +void +BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *eaddr); + +/* read/write media word. + * 'cmd': can be SIOCGIFMEDIA, SIOCSIFMEDIA, 0 or 1. The latter + * are aliased to the former for convenience. + * 'parg': pointer to media word. + * + * RETURNS: 0 on success, nonzero on error + * + * NOTE: This routine is thread-safe. + */ +int +BSP_mve_media_ioctl(struct mveth_private *mp, int cmd, int *parg); + +/* Interrupt related routines */ + +/* Note: the BSP_mve_enable/disable/ack_irqs() entry points + * are deprecated. + * The newer API where the user passes a mask allows + * for more selective control. + */ + +/* Enable all supported interrupts at device */ +void +BSP_mve_enable_irqs(struct mveth_private *mp); + +/* Disable all supported interrupts at device */ +void +BSP_mve_disable_irqs(struct mveth_private *mp); + +/* Acknowledge (and clear) all supported interrupts. + * RETURNS: interrupts that were raised. + */ +uint32_t +BSP_mve_ack_irqs(struct mveth_private *mp); + +/* Enable interrupts included in 'mask' (leaving + * already enabled interrupts on). If the mask + * includes bits that were not passed to the 'setup' + * routine then the behavior is undefined. + */ +void +BSP_mve_enable_irq_mask(struct mveth_private *mp, uint32_t irq_mask); + +/* Disable interrupts included in 'mask' (leaving + * other ones that are currently enabled on). If the + * mask includes bits that were not passed to the 'setup' + * routine then the behavior is undefined. + * + * RETURNS: Bitmask of interrupts that were enabled upon entry + * into this routine. This can be used to restore the + * previous state. + */ +uint32_t +BSP_mve_disable_irq_mask(struct mveth_private *mp, uint32_t irq_mask); + +/* Acknowledge and clear selected interrupts. + * + * RETURNS: All pending interrupts. + * + * NOTE: Only pending interrupts contained in 'mask' + * are cleared. Others are left pending. + * + * This routine can be used to check for pending + * interrupts (pass mask == 0) or to clear all + * interrupts (pass mask == -1). + */ +uint32_t +BSP_mve_ack_irq_mask(struct mveth_private *mp, uint32_t mask); + +/* If the PHY link status changes then some + * internal settings in the ethernet controller's + * serial port need to be updated to match the + * PHY settings. Use this routine to perform the + * necessary steps after a link change has been + * detected. + * + * RETURNS: 0 on success, -1 if the PHY state + * could not be determined. + * + * The current state of the media as read + * by BSP_mve_media_ioctl() is returned in + * *pmedia. + * + * NOTE: This routine calls BSP_mve_media_ioctl(). + */ +int +BSP_mve_ack_link_chg(struct mveth_private *mp, int *pmedia); + +/* Retrieve the driver daemon TID that was passed to + * BSP_mve_setup(). + */ + +rtems_id +BSP_mve_get_tid(struct mveth_private *mp); + +/* Dump statistics to file (stdout if NULL) + * + * NOTE: this routine is not thread safe + */ +void +BSP_mve_dump_stats(struct mveth_private *mp, FILE *f); + +/* + * + * Example driver task loop (note: no synchronization of + * buffer access shown!). + * RTEMS_EVENTx = 0,1 or 2 depending on IF unit. + * + * / * setup (obtain handle) and initialize hw here * / + * + * do { + * / * ISR disables IRQs and posts event * / + * rtems_event_receive( RTEMS_EVENTx, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &evs ); + * irqs = BSP_mve_ack_irqs(handle); + * if ( irqs & BSP_MVE_IRQ_TX ) { + * BSP_mve_swipe_tx(handle); / * cleanup_txbuf() callback executed * / + * } + * if ( irqs & BSP_MVE_IRQ_RX ) { + * BSP_mve_swipe_rx(handle); / * alloc_rxbuf() and consume_rxbuf() executed * / + * } + * if ( irqs & BSP_MVE_IRQ_LINK ) { + * / * update serial port settings from current link status * / + * BSP_mve_ack_link_chg(handle, 0); + * } + * BSP_mve_enable_irqs(handle); + * } while (1); + * + */ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mv643xx_eth.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mv643xx_eth.c new file mode 100644 index 0000000000..e9e1779574 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mv643xx_eth.c @@ -0,0 +1,3297 @@ +/* $Id$ */ + +/* RTEMS driver for the mv643xx gigabit ethernet chip */ + +/* Acknowledgement: + * + * Valuable information for developing this driver was obtained + * from the linux open-source driver mv643xx_eth.c which was written + * by the following people and organizations: + * + * Matthew Dharm + * rabeeh@galileo.co.il + * PMC-Sierra, Inc., Manish Lachwani + * Ralf Baechle + * MontaVista Software, Inc., Dale Farnsworth + * Steven J. Hill / + * + * Note however, that in spite of the identical name of this file + * (and some of the symbols used herein) this file provides a + * new implementation and is the original work by the author. + */ + +/* + * Authorship + * ---------- + * This software (mv643xx ethernet driver for RTEMS) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mv643xx ethernet driver for RTEMS' was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +/* + * NOTE: Some register (e.g., the SMI register) are SHARED among the + * three devices. Concurrent access protection is provided by + * the global networking semaphore. + * If other drivers are running on a subset of IFs then proper + * locking of all shared registers must be implemented! + * + * Some things I learned about this hardware can be found + * further down... + */ + +#ifndef KERNEL +#define KERNEL +#endif +#ifndef _KERNEL +#define _KERNEL +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Not so nice; would be more elegant not to depend on C library but the + * RTEMS-specific ioctl for dumping statistics needs stdio anyways. + */ + +/*#define NDEBUG effectively removes all assertions + * If defining NDEBUG, MAKE SURE assert() EXPRESSION HAVE NO SIDE_EFFECTS!! + * This driver DOES have side-effects, so DONT DEFINE NDEBUG + * Performance-critical assertions are removed by undefining MVETH_TESTING. + */ + +#undef NDEBUG +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* CONFIGURABLE PARAMETERS */ + +/* Enable Hardware Snooping; if this is disabled (undefined), + * cache coherency is maintained by software. + */ +#undef ENABLE_HW_SNOOPING + +/* Compile-time debugging features */ + +/* Enable paranoia assertions and checks; reduce # of descriptors to minimum for stressing */ +#undef MVETH_TESTING + +/* Enable debugging messages and some support routines (dump rings etc.) */ +#undef MVETH_DEBUG + +#ifndef DISABLE_DETACHING /* can override from Makefile */ +/* Hack for driver development; rtems bsdnet doesn't implement detaching an interface :-( + * but this hack allows us to unload/reload the driver module which makes development + * a lot less painful. + */ +#define MVETH_DETACH_HACK +#endif + +/* Ring sizes */ + +#ifdef MVETH_TESTING + +/* hard and small defaults */ +#undef MV643XX_RX_RING_SIZE +#define MV643XX_RX_RING_SIZE 2 +#undef MV643XX_TX_RING_SIZE +#define MV643XX_TX_RING_SIZE 4 + +#else /* MVETH_TESTING */ + +/* Define default ring sizes, allow override from bsp.h, Makefile,... and from ifcfg->rbuf_count/xbuf_count */ + +#ifndef MV643XX_RX_RING_SIZE +#define MV643XX_RX_RING_SIZE 40 /* attached buffers are always 2k clusters, i.e., this + * driver - with a configured ring size of 40 - constantly + * locks 80k of cluster memory - your app config better + * provides enough space! + */ +#endif + +#ifndef MV643XX_TX_RING_SIZE +/* NOTE: tx ring size MUST be > max. # of fragments / mbufs in a chain; + * in 'TESTING' mode, special code is compiled in to repackage + * chains that are longer than the ring size. Normally, this is + * disabled for sake of speed. + * I observed chains of >17 entries regularly! + * + * Also, TX_NUM_TAG_SLOTS (1) must be left empty as a marker, hence + * the ring size must be > max. #frags + 1. + */ +#define MV643XX_TX_RING_SIZE 200 /* these are smaller fragments and not occupied when + * the driver is idle. + */ +#endif + +#endif /* MVETH_TESTING */ + +/* How many instances to we support (bsp.h could override) */ +#ifndef MV643XXETH_NUM_DRIVER_SLOTS +#define MV643XXETH_NUM_DRIVER_SLOTS 2 +#endif + +#define TX_NUM_TAG_SLOTS 1 /* leave room for tag; must not be 0 */ + +/* This is REAL; chip reads from 64-bit down-aligned buffer + * if the buffer size is < 8 !!! for buffer sizes 8 and upwards + * alignment is not an issue. This was verified using the + * 'mve_smallbuf_test.c' + */ +#define ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM + +/* Chip register configuration values */ +#define MVETH_PORT_CONFIG_VAL (0 \ + | MV643XX_ETH_DFLT_RX_Q(0) \ + | MV643XX_ETH_DFLT_RX_ARP_Q(0) \ + | MV643XX_ETH_DFLT_RX_TCP_Q(0) \ + | MV643XX_ETH_DFLT_RX_UDP_Q(0) \ + | MV643XX_ETH_DFLT_RX_BPDU_Q(0) \ + ) + + +#define MVETH_PORT_XTEND_CONFIG_VAL 0 + +#ifdef OLDCONFIGVAL +#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \ + | MV643XX_ETH_FORCE_LINK_PASS \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \ + | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \ + | MV643XX_ETH_BIT9_UNKNOWN \ + | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \ + | MV643XX_ETH_SC_MAX_RX_1552 \ + | MV643XX_ETH_SET_FULL_DUPLEX \ + | MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD \ + ) +#endif +/* If we enable autoneg (duplex, speed, ...) then it seems + * that the chip automatically updates link settings + * (correct link settings are reflected in PORT_STATUS_R). + * However, when we disable aneg in the PHY then things + * can get messed up and the port doesn't work anymore. + * => we follow the linux driver in disabling all aneg + * in the serial config reg. and manually updating the + * speed & duplex bits when the phy link status changes. + * FIXME: don't know what to do about pause/flow-ctrl. + * It is best to just use ANEG anyways!!! + */ +#define MVETH_SERIAL_CTRL_CONFIG_VAL (0 \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX \ + | MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL \ + | MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL \ + | MV643XX_ETH_BIT9_UNKNOWN \ + | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE \ + | MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII \ + | MV643XX_ETH_SC_MAX_RX_1552 \ + ) + +#define MVETH_SERIAL_CTRL_CONFIG_MSK (0 \ + | MV643XX_ETH_SERIAL_PORT_ENBL \ + | MV643XX_ETH_FORCE_LINK_PASS \ + | MV643XX_ETH_SC_MAX_RX_MASK \ + ) + + +#ifdef __PPC__ +#define MVETH_SDMA_CONFIG_VAL (0 \ + | MV643XX_ETH_RX_BURST_SZ_4_64BIT \ + | MV643XX_ETH_TX_BURST_SZ_4_64BIT \ + ) +#else +#define MVETH_SDMA_CONFIG_VAL (0 \ + | MV643XX_ETH_RX_BURST_SZ_16_64BIT \ + | MV643XX_ETH_TX_BURST_SZ_16_64BIT \ + ) +#endif + +/* minimal frame size we accept */ +#define MVETH_MIN_FRAMSZ_CONFIG_VAL 40 + +/* END OF CONFIGURABLE SECTION */ + +/* + * Here's stuff I learned about this chip: + * + * + * RX interrupt flags: + * + * broadcast packet RX: 0x00000005 + * last buf: 0x00000c05 + * overrun: 0x00000c00 + * unicast packet RX: 0x00000005 + * bad CRC received: 0x00000005 + * + * clearing 0x00000004 -> clears 0x00000001 + * clearing 0x00000400 -> clears 0x00000800 + * + * --> 0x0801 are probably some sort of summary bits. + * + * TX interrupt flags: + * + * broadcast packet in 1 buf: xcause: 0x00000001 (cause 0x00080000) + * into disconn. link: " " + * + * in some cases, I observed xcause: 0x00000101 (reason for 0x100 unknown + * but the linux driver accepts it also). + * + * + * Here a few more ugly things about this piece of hardware I learned + * (painfully, painfully; spending many many hours & nights :-() + * + * a) Especially in the case of 'chained' descriptors, the DMA keeps + * clobbering 'cmd_sts' long after it cleared the OWNership flag!!! + * Only after the whole chain is processed (OWN cleared on the + * last descriptor) it is safe to change cmd_sts. + * However, in the case of hardware snooping I found that the + * last descriptor in chain has its cmd_sts still clobbered *after* + * checking ownership!, I.e., + * if ( ! OWN & cmd_sts ) { + * cmd_sts = 0; + * } + * --> sometimes, cmd_sts is STILL != 0 here + * + * b) Sometimes, the OWNership flag is *not cleared*. + * + * c) Weird things happen if the chip finds a descriptor with 'OWN' + * still set (i.e., not properly loaded), i.e., corrupted packets + * are sent [with OK checksum since the chip calculates it]. + * + * Combine a+b+c and we end up with a real mess. + * + * The fact that the chip doesn't reliably reset OWN and that OTOH, + * it can't be reliably reset by the driver and still, the chip needs + * it for proper communication doesn't make things easy... + * + * Here the basic workarounds: + * + * - In addition to check OWN, the scavenger compares the "currently + * served desc" register to the descriptor it tries to recover and + * ignores OWN if they do not match. Hope this is OK. + * Otherwise, we could scan the list of used descriptors and proceed + * recycling descriptors if we find a !OWNed one behind the target... + * + * - Always keep an empty slot around to mark the end of the list of + * jobs. The driver clears the descriptor ahead when enqueueing a new + * packet. + */ + +#define DRVNAME "mve" +#define MAX_NUM_SLOTS 3 + +#if MV643XXETH_NUM_DRIVER_SLOTS > MAX_NUM_SLOTS +#error "mv643xxeth: only MAX_NUM_SLOTS supported" +#endif + +#ifdef NDEBUG +#error "Driver uses assert() statements with side-effects; MUST NOT define NDEBUG" +#endif + +#ifdef MVETH_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +#define TX_AVAILABLE_RING_SIZE(mp) ((mp)->xbuf_count - (TX_NUM_TAG_SLOTS)) + +/* macros for ring alignment; proper alignment is a hardware req; . */ + +#ifdef ENABLE_HW_SNOOPING + +#define RING_ALIGNMENT 16 +/* rx buffers must be 64-bit aligned (chip requirement) */ +#define RX_BUF_ALIGNMENT 8 + +#else /* ENABLE_HW_SNOOPING */ + +/* Software cache management */ + +#ifndef __PPC__ +#error "Dont' know how to deal with cache on this CPU architecture" +#endif + +/* Ring entries are 32 bytes; coherency-critical chunks are 16 -> software coherency + * management works for cache line sizes of 16 and 32 bytes only. If the line size + * is bigger, the descriptors could be padded... + */ +#if PPC_CACHE_ALIGMENT != 16 && PPC_CACHE_ALIGNMENT != 32 +#error "Cache line size must be 16 or 32" +#else +#define RING_ALIGNMENT PPC_CACHE_ALIGNMENT +#define RX_BUF_ALIGNMENT PPC_CACHE_ALIGNMENT +#endif + +#endif /* ENABLE_HW_SNOOPING */ + + +/* HELPER MACROS */ + +/* Align base to alignment 'a' */ +#define _ALIGN(b, a) ((((uint32_t)(b)) + (a)-1) & (~((a)-1))) + +#define NOOP() do {} while(0) + +/* Function like macros */ +#define MV_READ(off) \ + ld_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off))) +#define MV_WRITE(off, data) \ + st_le32((volatile uint32_t *)(BSP_MV64x60_BASE + (off)), ((unsigned)data)) + + +/* ENET window mapped 1:1 to CPU addresses by our BSP/MotLoad + * -- if this is changed, we should think about caching the 'next' and 'buf' pointers. + */ +#define CPUADDR2ENET(a) ((Dma_addr_t)(a)) +#define ENET2CPUADDR(a) (a) + +#if 1 /* Whether to automatically try to reclaim descriptors when enqueueing new packets */ +#define MVETH_CLEAN_ON_SEND(mp) (BSP_mve_swipe_tx(mp)) +#else +#define MVETH_CLEAN_ON_SEND(mp) (-1) +#endif + +#define NEXT_TXD(d) (d)->next +#define NEXT_RXD(d) (d)->next + +/* REGISTER AND DESCRIPTOR OFFSET AND BIT DEFINITIONS */ + +/* Descriptor Definitions */ +/* Rx descriptor */ +#define RDESC_ERROR (1<< 0) /* Error summary */ + +/* Error code (bit 1&2) is only valid if summary bit is set */ +#define RDESC_CRC_ERROR ( 1) +#define RDESC_OVERRUN_ERROR ( 3) +#define RDESC_MAX_FRAMELENGTH_ERROR ( 5) +#define RDESC_RESOURCE_ERROR ( 7) + +#define RDESC_LAST (1<<26) /* Last Descriptor */ +#define RDESC_FRST (1<<27) /* First Descriptor */ +#define RDESC_INT_ENA (1<<29) /* Enable Interrupts */ +#define RDESC_DMA_OWNED (1<<31) + +/* Tx descriptor */ +#define TDESC_ERROR (1<< 0) /* Error summary */ +#define TDESC_ZERO_PAD (1<<19) +#define TDESC_LAST (1<<20) /* Last Descriptor */ +#define TDESC_FRST (1<<21) /* First Descriptor */ +#define TDESC_GEN_CRC (1<<22) +#define TDESC_INT_ENA (1<<23) /* Enable Interrupts */ +#define TDESC_DMA_OWNED (1<<31) + + + +/* Register Definitions */ +#define MV643XX_ETH_PHY_ADDR_R (0x2000) +#define MV643XX_ETH_SMI_R (0x2004) +#define MV643XX_ETH_SMI_BUSY (1<<28) +#define MV643XX_ETH_SMI_VALID (1<<27) +#define MV643XX_ETH_SMI_OP_WR (0<<26) +#define MV643XX_ETH_SMI_OP_RD (1<<26) + +#define MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port) (0x2448 + ((port)<<10)) +#define MV643XX_ETH_TX_START(queue) (0x0001<<(queue)) +#define MV643XX_ETH_TX_STOP(queue) (0x0100<<(queue)) +#define MV643XX_ETH_TX_START_M(queues) ((queues)&0xff) +#define MV643XX_ETH_TX_STOP_M(queues) (((queues)&0xff)<<8) +#define MV643XX_ETH_TX_STOP_ALL (0xff00) +#define MV643XX_ETH_TX_ANY_RUNNING (0x00ff) + +#define MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(port) (0x2680 + ((port)<<10)) +#define MV643XX_ETH_RX_START(queue) (0x0001<<(queue)) +#define MV643XX_ETH_RX_STOP(queue) (0x0100<<(queue)) +#define MV643XX_ETH_RX_STOP_ALL (0xff00) +#define MV643XX_ETH_RX_ANY_RUNNING (0x00ff) + +#define MV643XX_ETH_CURRENT_SERVED_TX_DESC(port) (0x2684 + ((port)<<10)) + +/* The chip puts the ethernet header at offset 2 into the buffer so + * that the payload is aligned + */ +#define ETH_RX_OFFSET 2 +#define ETH_CRC_LEN 4 /* strip FCS at end of packet */ + + +#define MV643XX_ETH_INTERRUPT_CAUSE_R(port) (0x2460 + ((port)<<10)) +/* not fully understood; RX seems to raise 0x0005 or 0x0c05 if last buffer is filled and 0x0c00 + * if there are no buffers + */ +#define MV643XX_ETH_ALL_IRQS (0x0007ffff) +#define MV643XX_ETH_KNOWN_IRQS (0x00000c05) +#define MV643XX_ETH_IRQ_EXT_ENA (1<<1) +#define MV643XX_ETH_IRQ_RX_DONE (1<<2) +#define MV643XX_ETH_IRQ_RX_NO_DESC (1<<10) + +#define MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(port) (0x2464 + ((port)<<10)) +/* not fully understood; TX seems to raise 0x0001 and link change is 0x00010000 + * if there are no buffers + */ +#define MV643XX_ETH_ALL_EXT_IRQS (0x0011ffff) +#define MV643XX_ETH_KNOWN_EXT_IRQS (0x00010101) +#define MV643XX_ETH_EXT_IRQ_TX_DONE (1<<0) +#define MV643XX_ETH_EXT_IRQ_LINK_CHG (1<<16) +#define MV643XX_ETH_INTERRUPT_ENBL_R(port) (0x2468 + ((port)<<10)) +#define MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(port) (0x246c + ((port)<<10)) + +/* port configuration */ +#define MV643XX_ETH_PORT_CONFIG_R(port) (0x2400 + ((port)<<10)) +#define MV643XX_ETH_UNICAST_PROMISC_MODE (1<<0) +#define MV643XX_ETH_DFLT_RX_Q(q) ((q)<<1) +#define MV643XX_ETH_DFLT_RX_ARP_Q(q) ((q)<<4) +#define MV643XX_ETH_REJ_BCAST_IF_NOT_IP_OR_ARP (1<<7) +#define MV643XX_ETH_REJ_BCAST_IF_IP (1<<8) +#define MV643XX_ETH_REJ_BCAST_IF_ARP (1<<9) +#define MV643XX_ETH_TX_AM_NO_UPDATE_ERR_SUMMARY (1<<12) +#define MV643XX_ETH_CAPTURE_TCP_FRAMES_ENBL (1<<14) +#define MV643XX_ETH_CAPTURE_UDP_FRAMES_ENBL (1<<15) +#define MV643XX_ETH_DFLT_RX_TCP_Q(q) ((q)<<16) +#define MV643XX_ETH_DFLT_RX_UDP_Q(q) ((q)<<19) +#define MV643XX_ETH_DFLT_RX_BPDU_Q(q) ((q)<<22) + + + +#define MV643XX_ETH_PORT_CONFIG_XTEND_R(port) (0x2404 + ((port)<<10)) +#define MV643XX_ETH_CLASSIFY_ENBL (1<<0) +#define MV643XX_ETH_SPAN_BPDU_PACKETS_AS_NORMAL (0<<1) +#define MV643XX_ETH_SPAN_BPDU_PACKETS_2_Q7 (1<<1) +#define MV643XX_ETH_PARTITION_DISBL (0<<2) +#define MV643XX_ETH_PARTITION_ENBL (1<<2) + +#define MV643XX_ETH_SDMA_CONFIG_R(port) (0x241c + ((port)<<10)) +#define MV643XX_ETH_SDMA_RIFB (1<<0) +#define MV643XX_ETH_RX_BURST_SZ_1_64BIT (0<<1) +#define MV643XX_ETH_RX_BURST_SZ_2_64BIT (1<<1) +#define MV643XX_ETH_RX_BURST_SZ_4_64BIT (2<<1) +#define MV643XX_ETH_RX_BURST_SZ_8_64BIT (3<<1) +#define MV643XX_ETH_RX_BURST_SZ_16_64BIT (4<<1) +#define MV643XX_ETH_SMDA_BLM_RX_NO_SWAP (1<<4) +#define MV643XX_ETH_SMDA_BLM_TX_NO_SWAP (1<<5) +#define MV643XX_ETH_SMDA_DESC_BYTE_SWAP (1<<6) +#define MV643XX_ETH_TX_BURST_SZ_1_64BIT (0<<22) +#define MV643XX_ETH_TX_BURST_SZ_2_64BIT (1<<22) +#define MV643XX_ETH_TX_BURST_SZ_4_64BIT (2<<22) +#define MV643XX_ETH_TX_BURST_SZ_8_64BIT (3<<22) +#define MV643XX_ETH_TX_BURST_SZ_16_64BIT (4<<22) + +#define MV643XX_ETH_RX_MIN_FRAME_SIZE_R(port) (0x247c + ((port)<<10)) + + +#define MV643XX_ETH_SERIAL_CONTROL_R(port) (0x243c + ((port)<<10)) +#define MV643XX_ETH_SERIAL_PORT_ENBL (1<<0) /* Enable serial port */ +#define MV643XX_ETH_FORCE_LINK_PASS (1<<1) +#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLEX (1<<2) +#define MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOWCTL (1<<3) +#define MV643XX_ETH_ADVERTISE_SYMMETRIC_FLOWCTL (1<<4) +#define MV643XX_ETH_FORCE_FC_MODE_TX_PAUSE_DIS (1<<5) +#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX (1<<7) +#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR (1<<8) +#define MV643XX_ETH_BIT9_UNKNOWN (1<<9) /* unknown purpose; linux sets this */ +#define MV643XX_ETH_FORCE_LINK_FAIL_DISABLE (1<<10) +#define MV643XX_ETH_RETRANSMIT_FOREVER (1<<11) /* limit to 16 attempts if clear */ +#define MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII (1<<13) +#define MV643XX_ETH_DTE_ADV_1 (1<<14) +#define MV643XX_ETH_AUTO_NEG_BYPASS_ENBL (1<<15) +#define MV643XX_ETH_RESTART_AUTO_NEG (1<<16) +#define MV643XX_ETH_SC_MAX_RX_1518 (0<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_1522 (1<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_1552 (2<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9022 (3<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9192 (4<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_9700 (5<<17) /* Limit RX packet size */ +#define MV643XX_ETH_SC_MAX_RX_MASK (7<<17) /* bitmask */ +#define MV643XX_ETH_SET_EXT_LOOPBACK (1<<20) +#define MV643XX_ETH_SET_FULL_DUPLEX (1<<21) +#define MV643XX_ETH_ENBL_FLOWCTL_TX_RX_IN_FD (1<<22) /* enable flow ctrl on rx and tx in full-duplex */ +#define MV643XX_ETH_SET_GMII_SPEED_1000 (1<<23) /* 10/100 if clear */ +#define MV643XX_ETH_SET_MII_SPEED_100 (1<<24) /* 10 if clear */ + +#define MV643XX_ETH_PORT_STATUS_R(port) (0x2444 + ((port)<<10)) + +#define MV643XX_ETH_PORT_STATUS_MODE_10_BIT (1<<0) +#define MV643XX_ETH_PORT_STATUS_LINK_UP (1<<1) +#define MV643XX_ETH_PORT_STATUS_FDX (1<<2) +#define MV643XX_ETH_PORT_STATUS_FC (1<<3) +#define MV643XX_ETH_PORT_STATUS_1000 (1<<4) +#define MV643XX_ETH_PORT_STATUS_100 (1<<5) +/* PSR bit 6 unknown */ +#define MV643XX_ETH_PORT_STATUS_TX_IN_PROGRESS (1<<7) +#define MV643XX_ETH_PORT_STATUS_ANEG_BYPASSED (1<<8) +#define MV643XX_ETH_PORT_STATUS_PARTITION (1<<9) +#define MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY (1<<10) + +#define MV643XX_ETH_MIB_COUNTERS(port) (0x3000 + ((port)<<7)) +#define MV643XX_ETH_NUM_MIB_COUNTERS 32 + +#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO (0) +#define MV643XX_ETH_MIB_GOOD_OCTS_RCVD_HI (1<<2) +#define MV643XX_ETH_MIB_BAD_OCTS_RCVD (2<<2) +#define MV643XX_ETH_MIB_INTERNAL_MAC_TX_ERR (3<<2) +#define MV643XX_ETH_MIB_GOOD_FRAMES_RCVD (4<<2) +#define MV643XX_ETH_MIB_BAD_FRAMES_RCVD (5<<2) +#define MV643XX_ETH_MIB_BCAST_FRAMES_RCVD (6<<2) +#define MV643XX_ETH_MIB_MCAST_FRAMES_RCVD (7<<2) +#define MV643XX_ETH_MIB_FRAMES_64_OCTS (8<<2) +#define MV643XX_ETH_MIB_FRAMES_65_127_OCTS (9<<2) +#define MV643XX_ETH_MIB_FRAMES_128_255_OCTS (10<<2) +#define MV643XX_ETH_MIB_FRAMES_256_511_OCTS (11<<2) +#define MV643XX_ETH_MIB_FRAMES_512_1023_OCTS (12<<2) +#define MV643XX_ETH_MIB_FRAMES_1024_MAX_OCTS (13<<2) +#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO (14<<2) +#define MV643XX_ETH_MIB_GOOD_OCTS_SENT_HI (15<<2) +#define MV643XX_ETH_MIB_GOOD_FRAMES_SENT (16<<2) +#define MV643XX_ETH_MIB_EXCESSIVE_COLL (17<<2) +#define MV643XX_ETH_MIB_MCAST_FRAMES_SENT (18<<2) +#define MV643XX_ETH_MIB_BCAST_FRAMES_SENT (19<<2) +#define MV643XX_ETH_MIB_UNREC_MAC_CTRL_RCVD (20<<2) +#define MV643XX_ETH_MIB_FC_SENT (21<<2) +#define MV643XX_ETH_MIB_GOOD_FC_RCVD (22<<2) +#define MV643XX_ETH_MIB_BAD_FC_RCVD (23<<2) +#define MV643XX_ETH_MIB_UNDERSIZE_RCVD (24<<2) +#define MV643XX_ETH_MIB_FRAGMENTS_RCVD (25<<2) +#define MV643XX_ETH_MIB_OVERSIZE_RCVD (26<<2) +#define MV643XX_ETH_MIB_JABBER_RCVD (27<<2) +#define MV643XX_ETH_MIB_MAC_RX_ERR (28<<2) +#define MV643XX_ETH_MIB_BAD_CRC_EVENT (29<<2) +#define MV643XX_ETH_MIB_COLL (30<<2) +#define MV643XX_ETH_MIB_LATE_COLL (31<<2) + +#define MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(port) (0x3400+((port)<<10)) +#define MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(port) (0x3500+((port)<<10)) +#define MV643XX_ETH_DA_FILTER_UNICAST_TBL(port) (0x3600+((port)<<10)) +#define MV643XX_ETH_NUM_MCAST_ENTRIES 64 +#define MV643XX_ETH_NUM_UNICAST_ENTRIES 4 + +#define MV643XX_ETH_BAR_0 (0x2200) +#define MV643XX_ETH_SIZE_R_0 (0x2204) +#define MV643XX_ETH_BAR_1 (0x2208) +#define MV643XX_ETH_SIZE_R_1 (0x220c) +#define MV643XX_ETH_BAR_2 (0x2210) +#define MV643XX_ETH_SIZE_R_2 (0x2214) +#define MV643XX_ETH_BAR_3 (0x2218) +#define MV643XX_ETH_SIZE_R_3 (0x221c) +#define MV643XX_ETH_BAR_4 (0x2220) +#define MV643XX_ETH_SIZE_R_4 (0x2224) +#define MV643XX_ETH_BAR_5 (0x2228) +#define MV643XX_ETH_SIZE_R_5 (0x222c) +#define MV643XX_ETH_NUM_BARS 6 + +/* Bits in the BAR reg to program cache snooping */ +#define MV64360_ENET2MEM_SNOOP_NONE 0x0000 +#define MV64360_ENET2MEM_SNOOP_WT 0x1000 +#define MV64360_ENET2MEM_SNOOP_WB 0x2000 +#define MV64360_ENET2MEM_SNOOP_MSK 0x3000 + + +#define MV643XX_ETH_BAR_ENBL_R (0x2290) +#define MV643XX_ETH_BAR_DISABLE(bar) (1<<(bar)) +#define MV643XX_ETH_BAR_DISBL_ALL 0x3f + +#define MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(port) (0x260c+((port)<<10)) +#define MV643XX_ETH_RX_Q1_CURRENT_DESC_PTR(port) (0x261c+((port)<<10)) +#define MV643XX_ETH_RX_Q2_CURRENT_DESC_PTR(port) (0x262c+((port)<<10)) +#define MV643XX_ETH_RX_Q3_CURRENT_DESC_PTR(port) (0x263c+((port)<<10)) +#define MV643XX_ETH_RX_Q4_CURRENT_DESC_PTR(port) (0x264c+((port)<<10)) +#define MV643XX_ETH_RX_Q5_CURRENT_DESC_PTR(port) (0x265c+((port)<<10)) +#define MV643XX_ETH_RX_Q6_CURRENT_DESC_PTR(port) (0x266c+((port)<<10)) +#define MV643XX_ETH_RX_Q7_CURRENT_DESC_PTR(port) (0x267c+((port)<<10)) + +#define MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(port) (0x26c0+((port)<<10)) +#define MV643XX_ETH_TX_Q1_CURRENT_DESC_PTR(port) (0x26c4+((port)<<10)) +#define MV643XX_ETH_TX_Q2_CURRENT_DESC_PTR(port) (0x26c8+((port)<<10)) +#define MV643XX_ETH_TX_Q3_CURRENT_DESC_PTR(port) (0x26cc+((port)<<10)) +#define MV643XX_ETH_TX_Q4_CURRENT_DESC_PTR(port) (0x26d0+((port)<<10)) +#define MV643XX_ETH_TX_Q5_CURRENT_DESC_PTR(port) (0x26d4+((port)<<10)) +#define MV643XX_ETH_TX_Q6_CURRENT_DESC_PTR(port) (0x26d8+((port)<<10)) +#define MV643XX_ETH_TX_Q7_CURRENT_DESC_PTR(port) (0x26dc+((port)<<10)) + +#define MV643XX_ETH_MAC_ADDR_LO(port) (0x2414+((port)<<10)) +#define MV643XX_ETH_MAC_ADDR_HI(port) (0x2418+((port)<<10)) + +/* TYPE DEFINITIONS */ + +/* just to make the purpose explicit; vars of this + * type may need CPU-dependent address translation, + * endian conversion etc. + */ +typedef uint32_t Dma_addr_t; + +typedef volatile struct mveth_rx_desc { +#ifndef __BIG_ENDIAN__ +#error "descriptor declaration not implemented for little endian machines" +#endif + uint16_t byte_cnt; + uint16_t buf_size; + uint32_t cmd_sts; /* control and status */ + Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */ + Dma_addr_t buf_ptr; + /* fields below here are not used by the chip */ + void *u_buf; /* user buffer */ + volatile struct mveth_rx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */ + uint32_t pad[2]; +} MvEthRxDescRec, *MvEthRxDesc +__attribute__(( aligned(RING_ALIGNMENT) )); + +typedef volatile struct mveth_tx_desc { +#ifndef __BIG_ENDIAN__ +#error "descriptor declaration not implemented for little endian machines" +#endif + uint16_t byte_cnt; + uint16_t l4i_chk; + uint32_t cmd_sts; /* control and status */ + Dma_addr_t next_desc_ptr; /* next descriptor (as seen from DMA) */ + Dma_addr_t buf_ptr; + /* fields below here are not used by the chip */ + uint32_t workaround[2]; /* use this space to work around the 8byte problem (is this real?) */ + void *u_buf; /* user buffer */ + volatile struct mveth_tx_desc *next; /* next descriptor (CPU address; next_desc_ptr is a DMA address) */ +} MvEthTxDescRec, *MvEthTxDesc +__attribute__(( aligned(RING_ALIGNMENT) )); + +/* Assume there are never more then 64k aliasing entries */ +typedef uint16_t Mc_Refcnt[MV643XX_ETH_NUM_MCAST_ENTRIES*4]; + +/* driver private data and bsdnet interface structure */ +struct mveth_private { + MvEthRxDesc rx_ring; /* pointers to aligned ring area */ + MvEthTxDesc tx_ring; /* pointers to aligned ring area */ + MvEthRxDesc ring_area; /* allocated ring area */ + int rbuf_count, xbuf_count; /* saved ring sizes from ifconfig */ + int port_num; + int phy; + MvEthRxDesc d_rx_t; /* tail of the RX ring; next received packet */ + MvEthTxDesc d_tx_t, d_tx_h; + uint32_t rx_desc_dma, tx_desc_dma; /* ring address as seen by DMA; (1:1 on this BSP) */ + int avail; + void (*isr)(void*); + void *isr_arg; + /* Callbacks to handle buffers */ + void (*cleanup_txbuf)(void*, void*, int); /* callback to cleanup TX buffer */ + void *cleanup_txbuf_arg; + void *(*alloc_rxbuf)(int *psize, uintptr_t *paddr); /* allocate RX buffer */ + void (*consume_rxbuf)(void*, void*, int); /* callback to consume RX buffer */ + void *consume_rxbuf_arg; + rtems_id tid; + uint32_t irq_mask; /* IRQs we use */ + uint32_t xirq_mask; + int promisc; + struct { + unsigned irqs; + unsigned maxchain; + unsigned repack; + unsigned packet; + unsigned odrops; /* no counter in core code */ + struct { + uint64_t good_octs_rcvd; /* 64-bit */ + uint32_t bad_octs_rcvd; + uint32_t internal_mac_tx_err; + uint32_t good_frames_rcvd; + uint32_t bad_frames_rcvd; + uint32_t bcast_frames_rcvd; + uint32_t mcast_frames_rcvd; + uint32_t frames_64_octs; + uint32_t frames_65_127_octs; + uint32_t frames_128_255_octs; + uint32_t frames_256_511_octs; + uint32_t frames_512_1023_octs; + uint32_t frames_1024_max_octs; + uint64_t good_octs_sent; /* 64-bit */ + uint32_t good_frames_sent; + uint32_t excessive_coll; + uint32_t mcast_frames_sent; + uint32_t bcast_frames_sent; + uint32_t unrec_mac_ctrl_rcvd; + uint32_t fc_sent; + uint32_t good_fc_rcvd; + uint32_t bad_fc_rcvd; + uint32_t undersize_rcvd; + uint32_t fragments_rcvd; + uint32_t oversize_rcvd; + uint32_t jabber_rcvd; + uint32_t mac_rx_err; + uint32_t bad_crc_event; + uint32_t coll; + uint32_t late_coll; + } mib; + } stats; + struct { + Mc_Refcnt specl, other; + } mc_refcnt; +}; + +/* stuff needed for bsdnet support */ +struct mveth_bsdsupp { + int oif_flags; /* old / cached if_flags */ +}; + +struct mveth_softc { + struct arpcom arpcom; + struct mveth_bsdsupp bsd; + struct mveth_private pvt; +}; + +/* GLOBAL VARIABLES */ +#ifdef MVETH_DEBUG_TX_DUMP +int mveth_tx_dump = 0; +#endif + +/* THE array of driver/bsdnet structs */ + +/* If detaching/module unloading is enabled, the main driver data + * structure must remain in memory; hence it must reside in its own + * 'dummy' module... + */ +#ifdef MVETH_DETACH_HACK +extern +#else +STATIC +#endif +struct mveth_softc theMvEths[MV643XXETH_NUM_DRIVER_SLOTS] +#ifndef MVETH_DETACH_HACK += {{{{0}},}} +#endif +; + +/* daemon task id */ +STATIC rtems_id mveth_tid = 0; +/* register access protection mutex */ +STATIC rtems_id mveth_mtx = 0; +#define REGLOCK() do { \ + if ( RTEMS_SUCCESSFUL != rtems_semaphore_obtain(mveth_mtx, RTEMS_WAIT, RTEMS_NO_TIMEOUT) ) \ + rtems_panic(DRVNAME": unable to lock register protection mutex"); \ + } while (0) +#define REGUNLOCK() rtems_semaphore_release(mveth_mtx) + +/* Format strings for statistics messages */ +static const char *mibfmt[] = { + " GOOD_OCTS_RCVD: %"PRIu64"\n", + 0, + " BAD_OCTS_RCVD: %"PRIu32"\n", + " INTERNAL_MAC_TX_ERR: %"PRIu32"\n", + " GOOD_FRAMES_RCVD: %"PRIu32"\n", + " BAD_FRAMES_RCVD: %"PRIu32"\n", + " BCAST_FRAMES_RCVD: %"PRIu32"\n", + " MCAST_FRAMES_RCVD: %"PRIu32"\n", + " FRAMES_64_OCTS: %"PRIu32"\n", + " FRAMES_65_127_OCTS: %"PRIu32"\n", + " FRAMES_128_255_OCTS: %"PRIu32"\n", + " FRAMES_256_511_OCTS: %"PRIu32"\n", + " FRAMES_512_1023_OCTS:%"PRIu32"\n", + " FRAMES_1024_MAX_OCTS:%"PRIu32"\n", + " GOOD_OCTS_SENT: %"PRIu64"\n", + 0, + " GOOD_FRAMES_SENT: %"PRIu32"\n", + " EXCESSIVE_COLL: %"PRIu32"\n", + " MCAST_FRAMES_SENT: %"PRIu32"\n", + " BCAST_FRAMES_SENT: %"PRIu32"\n", + " UNREC_MAC_CTRL_RCVD: %"PRIu32"\n", + " FC_SENT: %"PRIu32"\n", + " GOOD_FC_RCVD: %"PRIu32"\n", + " BAD_FC_RCVD: %"PRIu32"\n", + " UNDERSIZE_RCVD: %"PRIu32"\n", + " FRAGMENTS_RCVD: %"PRIu32"\n", + " OVERSIZE_RCVD: %"PRIu32"\n", + " JABBER_RCVD: %"PRIu32"\n", + " MAC_RX_ERR: %"PRIu32"\n", + " BAD_CRC_EVENT: %"PRIu32"\n", + " COLL: %"PRIu32"\n", + " LATE_COLL: %"PRIu32"\n", +}; + +/* Interrupt Handler Connection */ + +/* forward decls + implementation for IRQ API funcs */ + +static void mveth_isr(rtems_irq_hdl_param unit); +static void mveth_isr_1(rtems_irq_hdl_param unit); +static void noop(const rtems_irq_connect_data *unused) {} +static int noop1(const rtems_irq_connect_data *unused) { return 0; } + +static rtems_irq_connect_data irq_data[MAX_NUM_SLOTS] = { + { + BSP_IRQ_ETH0, + 0, + (rtems_irq_hdl_param)0, + noop, + noop, + noop1 + }, + { + BSP_IRQ_ETH1, + 0, + (rtems_irq_hdl_param)1, + noop, + noop, + noop1 + }, + { + BSP_IRQ_ETH2, + 0, + (rtems_irq_hdl_param)2, + noop, + noop, + noop1 + }, +}; + +/* MII Ioctl Interface */ + +STATIC unsigned +mveth_mii_read(struct mveth_private *mp, unsigned addr); + +STATIC unsigned +mveth_mii_write(struct mveth_private *mp, unsigned addr, unsigned v); + + +/* mdio / mii interface wrappers for rtems_mii_ioctl API */ + +static int mveth_mdio_r(int phy, void *uarg, unsigned reg, uint32_t *pval) +{ + if ( phy > 1 ) + return -1; + + *pval = mveth_mii_read(uarg, reg); + return 0; +} + +static int mveth_mdio_w(int phy, void *uarg, unsigned reg, uint32_t val) +{ + if ( phy > 1 ) + return -1; + mveth_mii_write(uarg, reg, val); + return 0; +} + +static struct rtems_mdio_info mveth_mdio = { + mdio_r: mveth_mdio_r, + mdio_w: mveth_mdio_w, + has_gmii: 1, +}; + +/* LOW LEVEL SUPPORT ROUTINES */ + +/* Software Cache Coherency */ +#ifndef ENABLE_HW_SNOOPING +#ifndef __PPC__ +#error "Software cache coherency maintenance is not implemented for your CPU architecture" +#endif + +static inline unsigned INVAL_DESC(volatile void *d) +{ +typedef const char cache_line[PPC_CACHE_ALIGNMENT]; + asm volatile("dcbi 0, %1":"=m"(*(cache_line*)d):"r"(d)); + return (unsigned)d; /* so this can be used in comma expression */ +} + +static inline void FLUSH_DESC(volatile void *d) +{ +typedef const char cache_line[PPC_CACHE_ALIGNMENT]; + asm volatile("dcbf 0, %0"::"r"(d),"m"(*(cache_line*)d)); +} + +static inline void FLUSH_BARRIER(void) +{ + asm volatile("eieio"); +} + +/* RX buffers are always cache-line aligned + * ASSUMPTIONS: + * - 'addr' is cache aligned + * - len is a multiple >0 of cache lines + */ +static inline void INVAL_BUF(register uintptr_t addr, register int len) +{ +typedef char maxbuf[2048]; /* more than an ethernet packet */ + do { + len -= RX_BUF_ALIGNMENT; + asm volatile("dcbi %0, %1"::"b"(addr),"r"(len)); + } while (len > 0); + asm volatile("":"=m"(*(maxbuf*)addr)); +} + +/* Flushing TX buffers is a little bit trickier; we don't really know their + * alignment but *assume* adjacent addresses are covering 'ordinary' memory + * so that flushing them does no harm! + */ +static inline void FLUSH_BUF(register uintptr_t addr, register int len) +{ +typedef char maxbuf[2048]; /* more than an ethernet packet */ + asm volatile("":::"memory"); + len = _ALIGN(len, RX_BUF_ALIGNMENT); + do { + asm volatile("dcbf %0, %1"::"b"(addr),"r"(len)); + len -= RX_BUF_ALIGNMENT; + } while ( len >= 0 ); +} + +#else /* hardware snooping enabled */ + +/* inline this to silence compiler warnings */ +static inline int INVAL_DESC(volatile void *d) +{ return 0; } + +#define FLUSH_DESC(d) NOOP() +#define INVAL_BUF(b,l) NOOP() +#define FLUSH_BUF(b,l) NOOP() +#define FLUSH_BARRIER() NOOP() + +#endif /* cache coherency support */ + +/* Synchronize memory access */ +#ifdef __PPC__ +static inline void membarrier(void) +{ + asm volatile("sync":::"memory"); +} +#else +#error "memory barrier instruction not defined (yet) for this CPU" +#endif + +/* Enable and disable interrupts at the device */ +static inline void +mveth_enable_irqs(struct mveth_private *mp, uint32_t mask) +{ +rtems_interrupt_level l; +uint32_t val; + rtems_interrupt_disable(l); + + val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num)); + val = (val | mask | MV643XX_ETH_IRQ_EXT_ENA) & mp->irq_mask; + + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), val); + + val = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num)); + val = (val | mask) & mp->xirq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), val); + + rtems_interrupt_enable(l); +} + +static inline uint32_t +mveth_disable_irqs(struct mveth_private *mp, uint32_t mask) +{ +rtems_interrupt_level l; +uint32_t val,xval,tmp; + rtems_interrupt_disable(l); + + val = MV_READ(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num)); + tmp = ( (val & ~mask) | MV643XX_ETH_IRQ_EXT_ENA ) & mp->irq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), tmp); + + xval = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num)); + tmp = (xval & ~mask) & mp->xirq_mask; + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), tmp); + + rtems_interrupt_enable(l); + + return (val | xval); +} + +/* This should be safe even w/o turning off interrupts if multiple + * threads ack different bits in the cause register (and ignore + * other ones) since writing 'ones' into the cause register doesn't + * 'stick'. + */ + +static inline uint32_t +mveth_ack_irqs(struct mveth_private *mp, uint32_t mask) +{ +register uint32_t x,xe,p; + + p = mp->port_num; + /* Get cause */ + x = MV_READ(MV643XX_ETH_INTERRUPT_CAUSE_R(p)); + + /* Ack interrupts filtering the ones we're interested in */ + + /* Note: EXT_IRQ bit clears by itself if EXT interrupts are cleared */ + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(p), ~ (x & mp->irq_mask & mask)); + + /* linux driver tests 1<<1 as a summary bit for extended interrupts; + * the mv64360 seems to use 1<<19 for that purpose; for the moment, + * I just check both. + * Update: link status irq (1<<16 in xe) doesn't set (1<<19) in x! + */ + if ( 1 /* x & 2 */ ) + { + xe = MV_READ(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p)); + + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(p), ~ (xe & mp->xirq_mask & mask)); + } else { + xe = 0; + } +#ifdef MVETH_TESTING + if ( ((x & MV643XX_ETH_ALL_IRQS) & ~MV643XX_ETH_KNOWN_IRQS) + || ((xe & MV643XX_ETH_ALL_EXT_IRQS) & ~MV643XX_ETH_KNOWN_EXT_IRQS) ) { + fprintf(stderr, "Unknown IRQs detected; leaving all disabled for debugging:\n"); + fprintf(stderr, "Cause reg was 0x%08x, ext cause 0x%08x\n", x, xe); + mp->irq_mask = 0; + mp->xirq_mask = 0; + } +#endif + /* luckily, the extended and 'normal' interrupts we use don't overlap so + * we can just OR them into a single word + */ + return (xe & mp->xirq_mask) | (x & mp->irq_mask); +} + +static void mveth_isr(rtems_irq_hdl_param arg) +{ +unsigned unit = (unsigned)arg; + mveth_disable_irqs(&theMvEths[unit].pvt, -1); + theMvEths[unit].pvt.stats.irqs++; + rtems_event_send( theMvEths[unit].pvt.tid, 1<stats.irqs++; + mp->isr(mp->isr_arg); +} + +static void +mveth_clear_mib_counters(struct mveth_private *mp) +{ +register int i; +register uint32_t b; + /* reading the counters resets them */ + b = MV643XX_ETH_MIB_COUNTERS(mp->port_num); + for (i=0; i< MV643XX_ETH_NUM_MIB_COUNTERS; i++, b+=4) + (void)MV_READ(b); +} + +/* Reading a MIB register also clears it. Hence we read the lo + * register first, then the hi one. Correct reading is guaranteed since + * the 'lo' register cannot overflow after it is read since it had + * been reset to 0. + */ +static unsigned long long +read_long_mib_counter(int port_num, int idx) +{ +unsigned long lo; +unsigned long long hi; + lo = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); + idx++; + hi = MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); + return (hi<<32) | lo; +} + +static inline unsigned long +read_mib_counter(int port_num, int idx) +{ + return MV_READ(MV643XX_ETH_MIB_COUNTERS(port_num)+(idx<<2)); +} + + +/* write ethernet address from buffer to hardware (need to change unicast filter after this) */ +static void +mveth_write_eaddr(struct mveth_private *mp, unsigned char *eaddr) +{ +int i; +uint32_t x; + + /* build hi word */ + for (i=4,x=0; i; i--, eaddr++) { + x = (x<<8) | *eaddr; + } + MV_WRITE(MV643XX_ETH_MAC_ADDR_HI(mp->port_num), x); + + /* build lo word */ + for (i=2,x=0; i; i--, eaddr++) { + x = (x<<8) | *eaddr; + } + MV_WRITE(MV643XX_ETH_MAC_ADDR_LO(mp->port_num), x); +} + +/* PHY/MII Interface + * + * Read/write a PHY register; + * + * NOTE: The SMI register is shared among the three devices. + * Protection is provided by the global networking semaphore. + * If non-bsd drivers are running on a subset of IFs proper + * locking of all shared registers must be implemented! + */ +STATIC unsigned +mveth_mii_read(struct mveth_private *mp, unsigned addr) +{ +unsigned v; +unsigned wc = 0; + + addr &= 0x1f; + + /* wait until not busy */ + do { + v = MV_READ(MV643XX_ETH_SMI_R); + wc++; + } while ( MV643XX_ETH_SMI_BUSY & v ); + + MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (mp->phy<<16) | MV643XX_ETH_SMI_OP_RD ); + + do { + v = MV_READ(MV643XX_ETH_SMI_R); + wc++; + } while ( MV643XX_ETH_SMI_BUSY & v ); + + if (wc>0xffff) + wc = 0xffff; + return (wc<<16) | (v & 0xffff); +} + +STATIC unsigned +mveth_mii_write(struct mveth_private *mp, unsigned addr, unsigned v) +{ +unsigned wc = 0; + + addr &= 0x1f; + v &= 0xffff; + + /* busywait is ugly but not preventing ISRs or high priority tasks from + * preempting us + */ + + /* wait until not busy */ + while ( MV643XX_ETH_SMI_BUSY & MV_READ(MV643XX_ETH_SMI_R) ) + wc++ /* wait */; + + MV_WRITE(MV643XX_ETH_SMI_R, (addr <<21 ) | (mp->phy<<16) | MV643XX_ETH_SMI_OP_WR | v ); + + return wc; +} + +/* MID-LAYER SUPPORT ROUTINES */ + +/* Stop TX and wait for the command queues to stop and the fifo to drain */ +static uint32_t +mveth_stop_tx(int port) +{ +uint32_t active_q; + + active_q = (MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) & MV643XX_ETH_TX_ANY_RUNNING); + + if ( active_q ) { + /* Halt TX and wait for activity to stop */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port), MV643XX_ETH_TX_STOP_ALL); + while ( MV643XX_ETH_TX_ANY_RUNNING & MV_READ(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(port)) ) + /* poll-wait */; + /* Wait for Tx FIFO to drain */ + while ( ! (MV643XX_ETH_PORT_STATUS_R(port) & MV643XX_ETH_PORT_STATUS_TX_FIFO_EMPTY) ) + /* poll-wait */; + } + + return active_q; +} + +/* update serial port settings from current link status */ +static void +mveth_update_serial_port(struct mveth_private *mp, int media) +{ +int port = mp->port_num; +uint32_t old, new; + + new = old = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(port)); + + /* mask speed and duplex settings */ + new &= ~( MV643XX_ETH_SET_GMII_SPEED_1000 + | MV643XX_ETH_SET_MII_SPEED_100 + | MV643XX_ETH_SET_FULL_DUPLEX ); + + if ( IFM_FDX & media ) + new |= MV643XX_ETH_SET_FULL_DUPLEX; + + switch ( IFM_SUBTYPE(media) ) { + default: /* treat as 10 */ + break; + case IFM_100_TX: + new |= MV643XX_ETH_SET_MII_SPEED_100; + break; + case IFM_1000_T: + new |= MV643XX_ETH_SET_GMII_SPEED_1000; + break; + } + + if ( new != old ) { + if ( ! (MV643XX_ETH_SERIAL_PORT_ENBL & new) ) { + /* just write */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + } else { + uint32_t were_running; + + were_running = mveth_stop_tx(port); + + old &= ~MV643XX_ETH_SERIAL_PORT_ENBL; + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), old); + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + /* linux driver writes twice... */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(port), new); + + if ( were_running ) { + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + } + } + } +} + +/* Clear multicast filters */ +void +BSP_mve_mcast_filter_clear(struct mveth_private *mp) +{ +int i; +register uint32_t s,o; +uint32_t v = mp->promisc ? 0x01010101 : 0x00000000; + s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + for (i=0; imc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) { + mp->mc_refcnt.specl[i] = 0; + mp->mc_refcnt.other[i] = 0; + } +} + +void +BSP_mve_mcast_filter_accept_all(struct mveth_private *mp) +{ +int i; +register uint32_t s,o; + s = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + o = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + for (i=0; imc_refcnt.specl)/sizeof(mp->mc_refcnt.specl[0]); i++) { + mp->mc_refcnt.specl[i]++; + mp->mc_refcnt.other[i]++; + } + } +} + +static void add_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt) +{ +uint32_t val; +uint32_t slot = hash & 0xfc; + + if ( 0 == (*refcnt)[hash]++ ) { + val = MV_READ(off+slot) | ( 1 << ((hash&3)<<3) ); + MV_WRITE(off+slot, val); + } +} + +static void del_entry(uint32_t off, uint8_t hash, Mc_Refcnt *refcnt) +{ +uint32_t val; +uint32_t slot = hash & 0xfc; + + if ( (*refcnt)[hash] > 0 && 0 == --(*refcnt)[hash] ) { + val = MV_READ(off+slot) & ~( 1 << ((hash&3)<<3) ); + MV_WRITE(off+slot, val); + } +} + +void +BSP_mve_mcast_filter_accept_add(struct mveth_private *mp, unsigned char *enaddr) +{ +uint32_t hash; +static const char spec[]={0x01,0x00,0x5e,0x00,0x00}; +static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff}; +uint32_t tabl; +Mc_Refcnt *refcnt; + + if ( ! (0x01 & enaddr[0]) ) { + /* not a multicast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) { + /* broadcast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) { + hash = enaddr[5]; + tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.specl; + } else { + uint32_t test, mask; + int i; + /* algorithm used by linux driver */ + for ( hash=0, i=0; i<6; i++ ) { + hash = (hash ^ enaddr[i]) << 8; + for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) { + if ( hash & test ) + hash ^= mask; + } + } + tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.other; + } + add_entry(tabl, hash, refcnt); +} + +void +BSP_mve_mcast_filter_accept_del(struct mveth_private *mp, unsigned char *enaddr) +{ +uint32_t hash; +static const char spec[]={0x01,0x00,0x5e,0x00,0x00}; +static const char bcst[]={0xff,0xff,0xff,0xff,0xff,0xff}; +uint32_t tabl; +Mc_Refcnt *refcnt; + + if ( ! (0x01 & enaddr[0]) ) { + /* not a multicast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, bcst, sizeof(bcst)) ) { + /* broadcast address; ignore */ + return; + } + + if ( 0 == memcmp(enaddr, spec, sizeof(spec)) ) { + hash = enaddr[5]; + tabl = MV643XX_ETH_DA_FILTER_SPECL_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.specl; + } else { + uint32_t test, mask; + int i; + /* algorithm used by linux driver */ + for ( hash=0, i=0; i<6; i++ ) { + hash = (hash ^ enaddr[i]) << 8; + for ( test=0x8000, mask=0x8380; test>0x0080; test>>=1, mask>>=1 ) { + if ( hash & test ) + hash ^= mask; + } + } + tabl = MV643XX_ETH_DA_FILTER_OTHER_MCAST_TBL(mp->port_num); + refcnt = &mp->mc_refcnt.other; + } + del_entry(tabl, hash, refcnt); +} + +/* Clear all address filters (multi- and unicast) */ +static void +mveth_clear_addr_filters(struct mveth_private *mp) +{ +register int i; +register uint32_t u; + u = MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num); + for (i=0; iport_num) + slot); + if ( accept ) { + val |= 0x01 << bit; + } else { + val &= 0x0e << bit; + } + MV_WRITE(MV643XX_ETH_DA_FILTER_UNICAST_TBL(mp->port_num) + slot, val); +} + +#if defined( ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM ) && 0 +/* Currently unused; small unaligned buffers seem to be rare + * so we just use memcpy()... + */ + +/* memcpy for 0..7 bytes; arranged so that gcc + * optimizes for powerpc... + */ + +static inline void memcpy8(void *to, void *fr, unsigned x) +{ +register uint8_t *d = to, *s = fro; + + d+=l; s+=l; + if ( l & 1 ) { + *--d=*--s; + } + if ( l & 2 ) { + /* pre-decrementing causes gcc to use auto-decrementing + * PPC instructions (lhzu rx, -2(ry)) + */ + d-=2; s-=2; + /* use memcpy; don't cast to short -- accessing + * misaligned data as short is not portable + * (but it works on PPC). + */ + __builtin_memcpy(d,s,2); + } + if ( l & 4 ) { + d-=4; s-=4; + /* see above */ + __builtin_memcpy(d,s,4); + } +} +#endif + +/* Assign values (buffer + user data) to a tx descriptor slot */ +static int +mveth_assign_desc(MvEthTxDesc d, struct mbuf *m, unsigned long extra) +{ +int rval = (d->byte_cnt = m->m_len); + +#ifdef MVETH_TESTING + assert( !d->mb ); + assert( m->m_len ); +#endif + + /* set CRC on all descriptors; seems to be necessary */ + d->cmd_sts = extra | (TDESC_GEN_CRC | TDESC_ZERO_PAD); + +#ifdef ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM + /* The buffer must be 64bit aligned if the payload is <8 (??) */ + if ( rval < 8 && ((mtod(m, uintptr_t)) & 7) ) { + d->buf_ptr = CPUADDR2ENET( d->workaround ); + memcpy((void*)d->workaround, mtod(m, void*), rval); + } else +#endif + { + d->buf_ptr = CPUADDR2ENET( mtod(m, unsigned long) ); + } + d->l4i_chk = 0; + return rval; +} + +static int +mveth_assign_desc_raw(MvEthTxDesc d, void *buf, int len, unsigned long extra) +{ +int rval = (d->byte_cnt = len); + +#ifdef MVETH_TESTING + assert( !d->u_buf ); + assert( len ); +#endif + + /* set CRC on all descriptors; seems to be necessary */ + d->cmd_sts = extra | (TDESC_GEN_CRC | TDESC_ZERO_PAD); + +#ifdef ENABLE_TX_WORKAROUND_8_BYTE_PROBLEM + /* The buffer must be 64bit aligned if the payload is <8 (??) */ + if ( rval < 8 && ( ((uintptr_t)buf) & 7) ) { + d->buf_ptr = CPUADDR2ENET( d->workaround ); + memcpy((void*)d->workaround, buf, rval); + } else +#endif + { + d->buf_ptr = CPUADDR2ENET( (unsigned long)buf ); + } + d->l4i_chk = 0; + return rval; +} + +/* + * Ring Initialization + * + * ENDIAN ASSUMPTION: DMA engine matches CPU endianness (???) + * + * Linux driver discriminates __LITTLE and __BIG endian for re-arranging + * the u16 fields in the descriptor structs. However, no endian conversion + * is done on the individual fields (SDMA byte swapping is disabled on LE). + */ + +STATIC int +mveth_init_rx_desc_ring(struct mveth_private *mp) +{ +int i,sz; +MvEthRxDesc d; +uintptr_t baddr; + + memset((void*)mp->rx_ring, 0, sizeof(*mp->rx_ring)*mp->rbuf_count); + + mp->rx_desc_dma = CPUADDR2ENET(mp->rx_ring); + + for ( i=0, d = mp->rx_ring; irbuf_count; i++, d++ ) { + d->u_buf = mp->alloc_rxbuf(&sz, &baddr); + assert( d->u_buf ); + +#ifndef ENABLE_HW_SNOOPING + /* could reduce the area to max. ethernet packet size */ + INVAL_BUF(baddr, sz); +#endif + + d->buf_size = sz; + d->byte_cnt = 0; + d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA; + d->next = mp->rx_ring + (i+1) % mp->rbuf_count; + + d->buf_ptr = CPUADDR2ENET( baddr ); + d->next_desc_ptr = CPUADDR2ENET(d->next); + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + mp->d_rx_t = mp->rx_ring; + + /* point the chip to the start of the ring */ + MV_WRITE(MV643XX_ETH_RX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->rx_desc_dma); + + + return i; +} + +STATIC int +mveth_init_tx_desc_ring(struct mveth_private *mp) +{ +int i; +MvEthTxDesc d; + + memset((void*)mp->tx_ring, 0, sizeof(*mp->tx_ring)*mp->xbuf_count); + + /* DMA and CPU live in the same address space (rtems) */ + mp->tx_desc_dma = CPUADDR2ENET(mp->tx_ring); + mp->avail = TX_AVAILABLE_RING_SIZE(mp); + + for ( i=0, d=mp->tx_ring; ixbuf_count; i++,d++ ) { + d->l4i_chk = 0; + d->byte_cnt = 0; + d->cmd_sts = 0; + d->buf_ptr = 0; + + d->next = mp->tx_ring + (i+1) % mp->xbuf_count; + d->next_desc_ptr = CPUADDR2ENET(d->next); + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + mp->d_tx_h = mp->d_tx_t = mp->tx_ring; + + /* point the chip to the start of the ring */ + MV_WRITE(MV643XX_ETH_TX_Q0_CURRENT_DESC_PTR(mp->port_num),mp->tx_desc_dma); + + return i; +} + +/* PUBLIC LOW-LEVEL DRIVER ACCESS */ + +static struct mveth_private * +mve_setup_internal( + int unit, + rtems_id tid, + void (*isr)(void*isr_arg), + void *isr_arg, + void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *closure, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +) + +{ +struct mveth_private *mp; +struct ifnet *ifp; +int InstallISRSuccessful; + + if ( unit <= 0 || unit > MV643XXETH_NUM_DRIVER_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, MV643XXETH_NUM_DRIVER_SLOTS); + return 0; + } + ifp = &theMvEths[unit-1].arpcom.ac_if; + if ( ifp->if_init ) { + if ( ifp->if_init ) { + printk(DRVNAME": instance %i already attached.\n", unit); + return 0; + } + } + + if ( rx_ring_size < 0 && tx_ring_size < 0 ) + return 0; + + if ( MV_64360 != BSP_getDiscoveryVersion(0) ) { + printk(DRVNAME": not mv64360 chip\n"); + return 0; + } + + /* lazy init of mutex (non thread-safe! - we assume 1st initialization is single-threaded) */ + if ( ! mveth_mtx ) { + rtems_status_code sc; + sc = rtems_semaphore_create( + rtems_build_name('m','v','e','X'), + 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY | RTEMS_DEFAULT_ATTRIBUTES, + 0, + &mveth_mtx); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,DRVNAME": creating mutex\n"); + rtems_panic("unable to proceed\n"); + } + } + + mp = &theMvEths[unit-1].pvt; + + memset(mp, 0, sizeof(*mp)); + + mp->port_num = unit-1; + mp->phy = (MV_READ(MV643XX_ETH_PHY_ADDR_R) >> (5*mp->port_num)) & 0x1f; + + mp->tid = tid; + mp->isr = isr; + mp->isr_arg = isr_arg; + + mp->cleanup_txbuf = cleanup_txbuf; + mp->cleanup_txbuf_arg = cleanup_txbuf_arg; + mp->alloc_rxbuf = alloc_rxbuf; + mp->consume_rxbuf = consume_rxbuf; + mp->consume_rxbuf_arg = consume_rxbuf_arg; + + mp->rbuf_count = rx_ring_size ? rx_ring_size : MV643XX_RX_RING_SIZE; + mp->xbuf_count = tx_ring_size ? tx_ring_size : MV643XX_TX_RING_SIZE; + + if ( mp->xbuf_count > 0 ) + mp->xbuf_count += TX_NUM_TAG_SLOTS; + + if ( mp->rbuf_count < 0 ) + mp->rbuf_count = 0; + if ( mp->xbuf_count < 0 ) + mp->xbuf_count = 0; + + /* allocate ring area; add 1 entry -- room for alignment */ + assert( !mp->ring_area ); + mp->ring_area = malloc( + sizeof(*mp->ring_area) * + (mp->rbuf_count + mp->xbuf_count + 1), + M_DEVBUF, + M_WAIT ); + assert( mp->ring_area ); + + BSP_mve_stop_hw(mp); + + if ( irq_mask ) { + irq_data[mp->port_num].hdl = tid ? mveth_isr : mveth_isr_1; + InstallISRSuccessful = BSP_install_rtems_irq_handler( &irq_data[mp->port_num] ); + assert( InstallISRSuccessful ); + } + + /* mark as used */ + ifp->if_init = (void*)(-1); + + if ( rx_ring_size < 0 ) + irq_mask &= ~ MV643XX_ETH_IRQ_RX_DONE; + if ( tx_ring_size < 0 ) + irq_mask &= ~ MV643XX_ETH_EXT_IRQ_TX_DONE; + + mp->irq_mask = (irq_mask & MV643XX_ETH_IRQ_RX_DONE); + if ( (irq_mask &= (MV643XX_ETH_EXT_IRQ_TX_DONE | MV643XX_ETH_EXT_IRQ_LINK_CHG)) ) { + mp->irq_mask |= MV643XX_ETH_IRQ_EXT_ENA; + mp->xirq_mask = irq_mask; + } else { + mp->xirq_mask = 0; + } + + return mp; +} + +struct mveth_private * +BSP_mve_setup( + int unit, + rtems_id tid, + void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *closure, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +) +{ + if ( irq_mask && 0 == tid ) { + printk(DRVNAME": must supply a TID if irq_msk not zero\n"); + return 0; + } + + return mve_setup_internal( + unit, + tid, + 0, 0, + cleanup_txbuf, cleanup_txbuf_arg, + alloc_rxbuf, + consume_rxbuf, consume_rxbuf_arg, + rx_ring_size, tx_ring_size, + irq_mask); +} + +struct mveth_private * +BSP_mve_setup_1( + int unit, + void (*isr)(void *isr_arg), + void *isr_arg, + void (*cleanup_txbuf)(void *user_buf, void *closure, int error_on_tx_occurred), + void *cleanup_txbuf_arg, + void *(*alloc_rxbuf)(int *p_size, uintptr_t *p_data_addr), + void (*consume_rxbuf)(void *user_buf, void *closure, int len), + void *consume_rxbuf_arg, + int rx_ring_size, + int tx_ring_size, + int irq_mask +) +{ + if ( irq_mask && 0 == isr ) { + printk(DRVNAME": must supply an ISR if irq_msk not zero\n"); + return 0; + } + + return mve_setup_internal( + unit, + 0, + isr, isr_arg, + cleanup_txbuf, cleanup_txbuf_arg, + alloc_rxbuf, + consume_rxbuf, consume_rxbuf_arg, + rx_ring_size, tx_ring_size, + irq_mask); +} + +rtems_id +BSP_mve_get_tid(struct mveth_private *mp) +{ + return mp->tid; +} + +int +BSP_mve_detach(struct mveth_private *mp) +{ +int unit = mp->port_num; + BSP_mve_stop_hw(mp); + if ( mp->irq_mask || mp->xirq_mask ) { + if ( !BSP_remove_rtems_irq_handler( &irq_data[mp->port_num] ) ) + return -1; + } + free( (void*)mp->ring_area, M_DEVBUF ); + memset(mp, 0, sizeof(*mp)); + __asm__ __volatile__("":::"memory"); + /* mark as unused */ + theMvEths[unit].arpcom.ac_if.if_init = 0; + return 0; +} + +/* MAIN RX-TX ROUTINES + * + * BSP_mve_swipe_tx(): descriptor scavenger; releases mbufs + * BSP_mve_send_buf(): xfer mbufs from IF to chip + * BSP_mve_swipe_rx(): enqueue received mbufs to interface + * allocate new ones and yield them to the + * chip. + */ + +/* clean up the TX ring freeing up buffers */ +int +BSP_mve_swipe_tx(struct mveth_private *mp) +{ +int rval = 0; +register MvEthTxDesc d; + + for ( d = mp->d_tx_t; d->buf_ptr; d = NEXT_TXD(d) ) { + + INVAL_DESC(d); + + if ( (TDESC_DMA_OWNED & d->cmd_sts) + && (uint32_t)d == MV_READ(MV643XX_ETH_CURRENT_SERVED_TX_DESC(mp->port_num)) ) + break; + + /* d->u_buf is only set on the last descriptor in a chain; + * we only count errors in the last descriptor; + */ + if ( d->u_buf ) { + mp->cleanup_txbuf(d->u_buf, mp->cleanup_txbuf_arg, (d->cmd_sts & TDESC_ERROR) ? 1 : 0); + d->u_buf = 0; + } + + d->buf_ptr = 0; + + rval++; + } + mp->d_tx_t = d; + mp->avail += rval; + + return rval; +} + +/* allocate a new cluster and copy an existing chain there; + * old chain is released... + */ +static struct mbuf * +repackage_chain(struct mbuf *m_head) +{ +struct mbuf *m; + MGETHDR(m, M_DONTWAIT, MT_DATA); + + if ( !m ) { + goto bail; + } + + MCLGET(m, M_DONTWAIT); + + if ( !(M_EXT & m->m_flags) ) { + m_freem(m); + m = 0; + goto bail; + } + + m_copydata(m_head, 0, MCLBYTES, mtod(m, caddr_t)); + m->m_pkthdr.len = m->m_len = m_head->m_pkthdr.len; + +bail: + m_freem(m_head); + return m; +} + +/* Enqueue a mbuf chain or a raw data buffer for transmission; + * RETURN: #bytes sent or -1 if there are not enough descriptors + * + * If 'len' is <=0 then 'm_head' is assumed to point to a mbuf chain. + * OTOH, a raw data packet may be send (non-BSD driver) by pointing + * m_head to the start of the data and passing 'len' > 0. + * + * Comments: software cache-flushing incurs a penalty if the + * packet cannot be queued since it is flushed anyways. + * The algorithm is slightly more efficient in the normal + * case, though. + */ +int +BSP_mve_send_buf(struct mveth_private *mp, void *m_head, void *data_p, int len) +{ +int rval; +register MvEthTxDesc l,d,h; +register struct mbuf *m1; +int nmbs; +int ismbuf = (len <= 0); + +/* Only way to get here is when we discover that the mbuf chain + * is too long for the tx ring + */ +startover: + + rval = 0; + +#ifdef MVETH_TESTING + assert(m_head); +#endif + + /* if no descriptor is available; try to wipe the queue */ + if ( (mp->avail < 1) && MVETH_CLEAN_ON_SEND(mp)<=0 ) { + return -1; + } + + h = mp->d_tx_h; + +#ifdef MVETH_TESTING + assert( !h->buf_ptr ); + assert( !h->mb ); +#endif + + if ( ! (m1 = m_head) ) + return 0; + + if ( ismbuf ) { + /* find first mbuf with actual data */ + while ( 0 == m1->m_len ) { + if ( ! (m1 = m1->m_next) ) { + /* end reached and still no data to send ?? */ + m_freem(m_head); + return 0; + } + } + } + + /* Don't use the first descriptor yet because BSP_mve_swipe_tx() + * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we + * start with the second mbuf and fill the first descriptor + * last. + */ + + l = h; + d = NEXT_TXD(h); + + mp->avail--; + + nmbs = 1; + if ( ismbuf ) { + register struct mbuf *m; + for ( m=m1->m_next; m; m=m->m_next ) { + if ( 0 == m->m_len ) + continue; /* skip empty mbufs */ + + nmbs++; + + if ( mp->avail < 1 && MVETH_CLEAN_ON_SEND(mp)<=0 ) { + /* not enough descriptors; cleanup... + * the first slot was never used, so we start + * at mp->d_tx_h->next; + */ + for ( l = NEXT_TXD(h); l!=d; l=NEXT_TXD(l) ) { +#ifdef MVETH_TESTING + assert( l->mb == 0 ); +#endif + l->buf_ptr = 0; + l->cmd_sts = 0; + mp->avail++; + } + mp->avail++; + if ( nmbs > TX_AVAILABLE_RING_SIZE(mp) ) { + /* this chain will never fit into the ring */ + if ( nmbs > mp->stats.maxchain ) + mp->stats.maxchain = nmbs; + mp->stats.repack++; + if ( ! (m_head = repackage_chain(m_head)) ) { + /* no cluster available */ + mp->stats.odrops++; + return 0; + } + goto startover; + } + return -1; + } + + mp->avail--; + +#ifdef MVETH_TESTING + assert( d != h ); + assert( !d->buf_ptr ); +#endif + + /* fill this slot */ + rval += mveth_assign_desc(d, m, TDESC_DMA_OWNED); + + FLUSH_BUF(mtod(m, uint32_t), m->m_len); + + l = d; + d = NEXT_TXD(d); + + FLUSH_DESC(l); + } + + /* fill first slot - don't release to DMA yet */ + rval += mveth_assign_desc(h, m1, TDESC_FRST); + + + FLUSH_BUF(mtod(m1, uint32_t), m1->m_len); + + } else { + /* fill first slot with raw buffer - don't release to DMA yet */ + rval += mveth_assign_desc_raw(h, data_p, len, TDESC_FRST); + + FLUSH_BUF( (uint32_t)data_p, len); + } + + /* tag last slot; this covers the case where 1st==last */ + l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA; + /* mbuf goes into last desc */ + l->u_buf = m_head; + + + FLUSH_DESC(l); + + /* Tag end; make sure chip doesn't try to read ahead of here! */ + l->next->cmd_sts = 0; + FLUSH_DESC(l->next); + +#ifdef MVETH_DEBUG_TX_DUMP + if ( (mveth_tx_dump & (1<port_num)) ) { + int ll,kk; + if ( ismbuf ) { + struct mbuf *m; + for ( kk=0, m=m_head; m; m=m->m_next) { + for ( ll=0; llm_len; ll++ ) { + printf("%02X ",*(mtod(m,char*) + ll)); + if ( ((++kk)&0xf) == 0 ) + printf("\n"); + } + } + } else { + for ( ll=0; llcmd_sts |= TDESC_DMA_OWNED; + + FLUSH_DESC(h); + + membarrier(); + + /* notify the device */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + + /* Update softc */ + mp->stats.packet++; + if ( nmbs > mp->stats.maxchain ) + mp->stats.maxchain = nmbs; + + /* remember new head */ + mp->d_tx_h = d; + + return rval; /* #bytes sent */ +} + +int +BSP_mve_send_buf_raw( + struct mveth_private *mp, + void *head_p, + int h_len, + void *data_p, + int d_len) +{ +int rval; +register MvEthTxDesc l,d,h; +int needed; +void *frst_buf; +int frst_len; + + rval = 0; + +#ifdef MVETH_TESTING + assert(header || data); +#endif + + needed = head_p && data_p ? 2 : 1; + + /* if no descriptor is available; try to wipe the queue */ + if ( ( mp->avail < needed ) + && ( MVETH_CLEAN_ON_SEND(mp) <= 0 || mp->avail < needed ) ) { + return -1; + } + + h = mp->d_tx_h; + +#ifdef MVETH_TESTING + assert( !h->buf_ptr ); + assert( !h->mb ); +#endif + + /* find the 'first' user buffer */ + if ( (frst_buf = head_p) ) { + frst_len = h_len; + } else { + frst_buf = data_p; + frst_len = d_len; + } + + /* Don't use the first descriptor yet because BSP_mve_swipe_tx() + * needs mp->d_tx_h->buf_ptr == NULL as a marker. Hence, we + * start with the second (optional) slot and fill the first + * descriptor last. + */ + + l = h; + d = NEXT_TXD(h); + + mp->avail--; + + if ( needed > 1 ) { + mp->avail--; +#ifdef MVETH_TESTING + assert( d != h ); + assert( !d->buf_ptr ); +#endif + rval += mveth_assign_desc_raw(d, data_p, d_len, TDESC_DMA_OWNED); + FLUSH_BUF( (uint32_t)data_p, d_len ); + d->u_buf = data_p; + + l = d; + d = NEXT_TXD(d); + + FLUSH_DESC(l); + } + + /* fill first slot with raw buffer - don't release to DMA yet */ + rval += mveth_assign_desc_raw(h, frst_buf, frst_len, TDESC_FRST); + + FLUSH_BUF( (uint32_t)frst_buf, frst_len); + + /* tag last slot; this covers the case where 1st==last */ + l->cmd_sts |= TDESC_LAST | TDESC_INT_ENA; + + /* first buffer of 'chain' goes into last desc */ + l->u_buf = frst_buf; + + FLUSH_DESC(l); + + /* Tag end; make sure chip doesn't try to read ahead of here! */ + l->next->cmd_sts = 0; + FLUSH_DESC(l->next); + + membarrier(); + + /* turn over the whole chain by flipping ownership of the first desc */ + h->cmd_sts |= TDESC_DMA_OWNED; + + FLUSH_DESC(h); + + membarrier(); + + /* notify the device */ + MV_WRITE(MV643XX_ETH_TRANSMIT_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_TX_START(0)); + + /* Update softc */ + mp->stats.packet++; + if ( needed > mp->stats.maxchain ) + mp->stats.maxchain = needed; + + /* remember new head */ + mp->d_tx_h = d; + + return rval; /* #bytes sent */ +} + +/* send received buffers upwards and replace them + * with freshly allocated ones; + * ASSUMPTION: buffer length NEVER changes and is set + * when the ring is initialized. + * TS 20060727: not sure if this assumption is still necessary - I believe it isn't. + */ + +int +BSP_mve_swipe_rx(struct mveth_private *mp) +{ +int rval = 0, err; +register MvEthRxDesc d; +void *newbuf; +int sz; +uintptr_t baddr; + + for ( d = mp->d_rx_t; ! (INVAL_DESC(d), (RDESC_DMA_OWNED & d->cmd_sts)); d=NEXT_RXD(d) ) { + +#ifdef MVETH_TESTING + assert(d->u_buf); +#endif + + err = (RDESC_ERROR & d->cmd_sts); + + if ( err || !(newbuf = mp->alloc_rxbuf(&sz, &baddr)) ) { + /* drop packet and recycle buffer */ + newbuf = d->u_buf; + mp->consume_rxbuf(0, mp->consume_rxbuf_arg, err ? -1 : 0); + } else { +#ifdef MVETH_TESTING + assert( d->byte_cnt > 0 ); +#endif + mp->consume_rxbuf(d->u_buf, mp->consume_rxbuf_arg, d->byte_cnt); + +#ifndef ENABLE_HW_SNOOPING + /* could reduce the area to max. ethernet packet size */ + INVAL_BUF(baddr, sz); +#endif + d->u_buf = newbuf; + d->buf_ptr = CPUADDR2ENET(baddr); + d->buf_size = sz; + FLUSH_DESC(d); + } + + membarrier(); + + d->cmd_sts = RDESC_DMA_OWNED | RDESC_INT_ENA; + + FLUSH_DESC(d); + + rval++; + } + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0)); + mp->d_rx_t = d; + return rval; +} + +/* Stop hardware and clean out the rings */ +void +BSP_mve_stop_hw(struct mveth_private *mp) +{ +MvEthTxDesc d; +MvEthRxDesc r; +int i; + + mveth_disable_irqs(mp, -1); + + mveth_stop_tx(mp->port_num); + + /* cleanup TX rings */ + if (mp->d_tx_t) { /* maybe ring isn't initialized yet */ + for ( i=0, d=mp->tx_ring; ixbuf_count; i++, d++ ) { + /* should be safe to clear ownership */ + d->cmd_sts &= ~TDESC_DMA_OWNED; + FLUSH_DESC(d); + } + FLUSH_BARRIER(); + + BSP_mve_swipe_tx(mp); + +#ifdef MVETH_TESTING + assert( mp->d_tx_h == mp->d_tx_t ); + for ( i=0, d=mp->tx_ring; ixbuf_count; i++, d++ ) { + assert( !d->buf_ptr ); + } +#endif + } + + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_STOP_ALL); + while ( MV643XX_ETH_RX_ANY_RUNNING & MV_READ(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num)) ) + /* poll-wait */; + + /* stop serial port */ + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), + MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)) + & ~( MV643XX_ETH_SERIAL_PORT_ENBL | MV643XX_ETH_FORCE_LINK_FAIL_DISABLE | MV643XX_ETH_FORCE_LINK_PASS) + ); + + /* clear pending interrupts */ + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0); + + /* cleanup RX rings */ + if ( mp->rx_ring ) { + for ( i=0, r=mp->rx_ring; irbuf_count; i++, r++ ) { + /* should be OK to clear ownership flag */ + r->cmd_sts = 0; + FLUSH_DESC(r); + mp->consume_rxbuf(r->u_buf, mp->consume_rxbuf_arg, 0); + r->u_buf = 0; + } + FLUSH_BARRIER(); + } + + +} + +uint32_t mveth_serial_ctrl_config_val = MVETH_SERIAL_CTRL_CONFIG_VAL; + +/* Fire up the low-level driver + * + * - make sure hardware is halted + * - enable cache snooping + * - clear address filters + * - clear mib counters + * - reset phy + * - initialize (or reinitialize) descriptor rings + * - check that the firmware has set up a reasonable mac address. + * - generate unicast filter entry for our mac address + * - write register config values to the chip + * - start hardware (serial port and SDMA) + */ + +void +BSP_mve_init_hw(struct mveth_private *mp, int promisc, unsigned char *enaddr) +{ +int i; +uint32_t v; +static int inited = 0; + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Entering BSP_mve_init_hw()\n", mp->port_num+1); +#endif + + /* since enable/disable IRQ routine only operate on select bitsets + * we must make sure everything is masked initially. + */ + MV_WRITE(MV643XX_ETH_INTERRUPT_ENBL_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_ENBL_R(mp->port_num), 0); + + BSP_mve_stop_hw(mp); + + memset(&mp->stats, 0, sizeof(mp->stats)); + + mp->promisc = promisc; + + /* MotLoad has cache snooping disabled on the ENET2MEM windows. + * Some comments in (linux) indicate that there are errata + * which cause problems which would be a real bummer. + * We try it anyways... + */ + if ( !inited ) { + unsigned long disbl, bar; + inited = 1; /* FIXME: non-thread safe lazy init */ + disbl = MV_READ(MV643XX_ETH_BAR_ENBL_R); + /* disable all 6 windows */ + MV_WRITE(MV643XX_ETH_BAR_ENBL_R, MV643XX_ETH_BAR_DISBL_ALL); + /* set WB snooping on enabled bars */ + for ( i=0; irbuf_count > 0 ) { + mp->rx_ring = (MvEthRxDesc)_ALIGN(mp->ring_area, RING_ALIGNMENT); + mveth_init_rx_desc_ring(mp); + } + + if ( mp->xbuf_count > 0 ) { + mp->tx_ring = (MvEthTxDesc)mp->rx_ring + mp->rbuf_count; + mveth_init_tx_desc_ring(mp); + } + + if ( enaddr ) { + /* set ethernet address from arpcom struct */ +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Writing MAC addr ", mp->port_num+1); + for (i=5; i>=0; i--) { + printk("%02X%c", enaddr[i], i?':':'\n'); + } +#endif + mveth_write_eaddr(mp, enaddr); + } + + /* set mac address and unicast filter */ + + { + uint32_t machi, maclo; + maclo = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num)); + machi = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num)); + /* ASSUME: firmware has set the mac address for us + * - if assertion fails, we have to do more work... + */ + assert( maclo && machi && maclo != 0xffffffff && machi != 0xffffffff ); + mveth_ucfilter(mp, maclo&0xff, 1/* accept */); + } + + /* port, serial and sdma configuration */ + v = MVETH_PORT_CONFIG_VAL; + if ( promisc ) { + /* multicast filters were already set up to + * accept everything (mveth_clear_addr_filters()) + */ + v |= MV643XX_ETH_UNICAST_PROMISC_MODE; + } else { + v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE; + } + MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(mp->port_num), + v); + MV_WRITE(MV643XX_ETH_PORT_CONFIG_XTEND_R(mp->port_num), + MVETH_PORT_XTEND_CONFIG_VAL); + + v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)); + v &= ~(MVETH_SERIAL_CTRL_CONFIG_MSK); + v |= mveth_serial_ctrl_config_val; + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), v); + + i = IFM_MAKEWORD(0, 0, 0, 0); + if ( 0 == BSP_mve_media_ioctl(mp, SIOCGIFMEDIA, &i) ) { + if ( (IFM_LINK_OK & i) ) { + mveth_update_serial_port(mp, i); + } + } + + /* enable serial port */ + v = MV_READ(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num)); + MV_WRITE(MV643XX_ETH_SERIAL_CONTROL_R(mp->port_num), + v | MV643XX_ETH_SERIAL_PORT_ENBL); + +#ifndef __BIG_ENDIAN__ +#error "byte swapping needs to be disabled for little endian machines" +#endif + MV_WRITE(MV643XX_ETH_SDMA_CONFIG_R(mp->port_num), MVETH_SDMA_CONFIG_VAL); + + /* allow short frames */ + MV_WRITE(MV643XX_ETH_RX_MIN_FRAME_SIZE_R(mp->port_num), MVETH_MIN_FRAMSZ_CONFIG_VAL); + + MV_WRITE(MV643XX_ETH_INTERRUPT_CAUSE_R(mp->port_num), 0); + MV_WRITE(MV643XX_ETH_INTERRUPT_EXTEND_CAUSE_R(mp->port_num), 0); + /* TODO: set irq coalescing */ + + /* enable Rx */ + if ( mp->rbuf_count > 0 ) { + MV_WRITE(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_R(mp->port_num), MV643XX_ETH_RX_START(0)); + } + + mveth_enable_irqs(mp, -1); + +#ifdef MVETH_DEBUG + printk(DRVNAME"%i: Leaving BSP_mve_init_hw()\n", mp->port_num+1); +#endif +} + +/* read ethernet address from hw to buffer */ +void +BSP_mve_read_eaddr(struct mveth_private *mp, unsigned char *oeaddr) +{ +int i; +uint32_t x; +unsigned char buf[6], *eaddr; + + eaddr = oeaddr ? oeaddr : buf; + + eaddr += 5; + x = MV_READ(MV643XX_ETH_MAC_ADDR_LO(mp->port_num)); + + /* lo word */ + for (i=2; i; i--, eaddr--) { + *eaddr = (unsigned char)(x & 0xff); + x>>=8; + } + + x = MV_READ(MV643XX_ETH_MAC_ADDR_HI(mp->port_num)); + /* hi word */ + for (i=4; i; i--, eaddr--) { + *eaddr = (unsigned char)(x & 0xff); + x>>=8; + } + + if ( !oeaddr ) { + printf("%02X",buf[0]); + for (i=1; ipvt); + sc->arpcom.ac_if.if_timer = 0; +} + +/* allocate a mbuf for RX with a properly aligned data buffer + * RETURNS 0 if allocation fails + */ +static void * +alloc_mbuf_rx(int *psz, uintptr_t *paddr) +{ +struct mbuf *m; +unsigned long l,o; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if ( !m ) + return 0; + MCLGET(m, M_DONTWAIT); + if ( ! (m->m_flags & M_EXT) ) { + m_freem(m); + return 0; + } + + o = mtod(m, unsigned long); + l = _ALIGN(o, RX_BUF_ALIGNMENT) - o; + + /* align start of buffer */ + m->m_data += l; + + /* reduced length */ + l = MCLBYTES - l; + + m->m_len = m->m_pkthdr.len = l; + *psz = m->m_len; + *paddr = mtod(m, uintptr_t); + + return (void*) m; +} + +static void consume_rx_mbuf(void *buf, void *arg, int len) +{ +struct ifnet *ifp = arg; +struct mbuf *m = buf; + + if ( len <= 0 ) { + ifp->if_iqdrops++; + if ( len < 0 ) { + ifp->if_ierrors++; + } + if ( m ) + m_freem(m); + } else { + struct ether_header *eh; + + eh = (struct ether_header *)(mtod(m, unsigned long) + ETH_RX_OFFSET); + m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header) - ETH_RX_OFFSET - ETH_CRC_LEN; + m->m_data += sizeof(struct ether_header) + ETH_RX_OFFSET; + m->m_pkthdr.rcvif = ifp; + + ifp->if_ipackets++; + ifp->if_ibytes += m->m_pkthdr.len; + + if (0) { + /* Low-level debugging */ + int i; + for (i=0; i<13; i++) { + printf("%02X:",((char*)eh)[i]); + } + printf("%02X\n",((char*)eh)[i]); + for (i=0; im_len; i++) { + if ( !(i&15) ) + printf("\n"); + printf("0x%02x ",mtod(m,char*)[i]); + } + printf("\n"); + } + + if (0) { + /* Low-level debugging/testing without bsd stack */ + m_freem(m); + } else { + /* send buffer upwards */ + ether_input(ifp, eh, m); + } + } +} + +static void release_tx_mbuf(void *buf, void *arg, int err) +{ +struct ifnet *ifp = arg; +struct mbuf *mb = buf; + + if ( err ) { + ifp->if_oerrors++; + } else { + ifp->if_opackets++; + } + ifp->if_obytes += mb->m_pkthdr.len; + m_freem(mb); +} + +static void +dump_update_stats(struct mveth_private *mp, FILE *f) +{ +int p = mp->port_num; +int idx; +uint32_t v; + + if ( !f ) + f = stdout; + + fprintf(f, DRVNAME"%i Statistics:\n", mp->port_num + 1); + fprintf(f, " # IRQS: %i\n", mp->stats.irqs); + fprintf(f, " Max. mbuf chain length: %i\n", mp->stats.maxchain); + fprintf(f, " # repacketed: %i\n", mp->stats.repack); + fprintf(f, " # packets: %i\n", mp->stats.packet); + fprintf(f, "MIB Counters:\n"); + for ( idx = MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2; + idx < MV643XX_ETH_NUM_MIB_COUNTERS; + idx++ ) { + switch ( idx ) { + case MV643XX_ETH_MIB_GOOD_OCTS_RCVD_LO>>2: + mp->stats.mib.good_octs_rcvd += read_long_mib_counter(p, idx); + fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_rcvd); + idx++; + break; + + case MV643XX_ETH_MIB_GOOD_OCTS_SENT_LO>>2: + mp->stats.mib.good_octs_sent += read_long_mib_counter(p, idx); + fprintf(f, mibfmt[idx], mp->stats.mib.good_octs_sent); + idx++; + break; + + default: + v = ((uint32_t*)&mp->stats.mib)[idx] += read_mib_counter(p, idx); + fprintf(f, mibfmt[idx], v); + break; + } + } + fprintf(f, "\n"); +} + +void +BSP_mve_dump_stats(struct mveth_private *mp, FILE *f) +{ + dump_update_stats(mp, f); +} + +/* BSDNET DRIVER CALLBACKS */ + +static void +mveth_init(void *arg) +{ +struct mveth_softc *sc = arg; +struct ifnet *ifp = &sc->arpcom.ac_if; +int media; + + BSP_mve_init_hw(&sc->pvt, ifp->if_flags & IFF_PROMISC, sc->arpcom.ac_enaddr); + + media = IFM_MAKEWORD(0, 0, 0, 0); + if ( 0 == BSP_mve_media_ioctl(&sc->pvt, SIOCGIFMEDIA, &media) ) { + if ( (IFM_LINK_OK & media) ) { + ifp->if_flags &= ~IFF_OACTIVE; + } else { + ifp->if_flags |= IFF_OACTIVE; + } + } + + /* if promiscuous then there is no need to change */ + if ( ! (ifp->if_flags & IFF_PROMISC) ) + mveth_set_filters(ifp); + + ifp->if_flags |= IFF_RUNNING; + sc->arpcom.ac_if.if_timer = 0; +} + +/* bsdnet driver entry to start transmission */ +static void +mveth_start(struct ifnet *ifp) +{ +struct mveth_softc *sc = ifp->if_softc; +struct mbuf *m = 0; + + while ( ifp->if_snd.ifq_head ) { + IF_DEQUEUE( &ifp->if_snd, m ); + if ( BSP_mve_send_buf(&sc->pvt, m, 0, 0) < 0 ) { + IF_PREPEND( &ifp->if_snd, m); + ifp->if_flags |= IFF_OACTIVE; + break; + } + /* need to do this really only once + * but it's cheaper this way. + */ + ifp->if_timer = 2*IFNET_SLOWHZ; + } +} + +/* bsdnet driver entry; */ +static void +mveth_watchdog(struct ifnet *ifp) +{ +struct mveth_softc *sc = ifp->if_softc; + + ifp->if_oerrors++; + printk(DRVNAME"%i: watchdog timeout; resetting\n", ifp->if_unit); + + mveth_init(sc); + mveth_start(ifp); +} + +static void +mveth_set_filters(struct ifnet *ifp) +{ +struct mveth_softc *sc = ifp->if_softc; +uint32_t v; + + v = MV_READ(MV643XX_ETH_PORT_CONFIG_R(sc->pvt.port_num)); + if ( ifp->if_flags & IFF_PROMISC ) + v |= MV643XX_ETH_UNICAST_PROMISC_MODE; + else + v &= ~MV643XX_ETH_UNICAST_PROMISC_MODE; + MV_WRITE(MV643XX_ETH_PORT_CONFIG_R(sc->pvt.port_num), v); + + if ( ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI) ) { + BSP_mve_mcast_filter_accept_all(&sc->pvt); + } else { + struct ether_multi *enm; + struct ether_multistep step; + + BSP_mve_mcast_filter_clear( &sc->pvt ); + + ETHER_FIRST_MULTI(step, (struct arpcom *)ifp, enm); + + while ( enm ) { + if ( memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) ) + assert( !"Should never get here; IFF_ALLMULTI should be set!" ); + + BSP_mve_mcast_filter_accept_add(&sc->pvt, enm->enm_addrlo); + + ETHER_NEXT_MULTI(step, enm); + } + } +} + +/* bsdnet driver ioctl entry */ +static int +mveth_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ +struct mveth_softc *sc = ifp->if_softc; +struct ifreq *ifr = (struct ifreq *)data; +int error = 0; +int f; + + switch ( cmd ) { + case SIOCSIFFLAGS: + f = ifp->if_flags; + if ( f & IFF_UP ) { + if ( ! ( f & IFF_RUNNING ) ) { + mveth_init(sc); + } else { + if ( (f & IFF_PROMISC) != (sc->bsd.oif_flags & IFF_PROMISC) ) { + /* Note: in all other scenarios the 'promisc' flag + * in the low-level driver [which affects the way + * the multicast filter is setup: accept none vs. + * accept all in promisc mode] is eventually + * set when the IF is brought up... + */ + sc->pvt.promisc = (f & IFF_PROMISC); + + mveth_set_filters(ifp); + } + /* FIXME: other flag changes are ignored/unimplemented */ + } + } else { + if ( f & IFF_RUNNING ) { + mveth_stop(sc); + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + } + } + sc->bsd.oif_flags = ifp->if_flags; + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = BSP_mve_media_ioctl(&sc->pvt, cmd, &ifr->ifr_media); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) + ? ether_addmulti(ifr, &sc->arpcom) + : ether_delmulti(ifr, &sc->arpcom); + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) { + mveth_set_filters(ifp); + } + error = 0; + } + break; + + + break; + + case SIO_RTEMS_SHOW_STATS: + dump_update_stats(&sc->pvt, stdout); + break; + + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return error; +} + +/* DRIVER TASK */ + +/* Daemon task does all the 'interrupt' work */ +static void mveth_daemon(void *arg) +{ +struct mveth_softc *sc; +struct ifnet *ifp; +rtems_event_set evs; + for (;;) { + rtems_bsdnet_event_receive( 7, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &evs ); + evs &= 7; + for ( sc = theMvEths; evs; evs>>=1, sc++ ) { + if ( (evs & 1) ) { + register uint32_t x; + + ifp = &sc->arpcom.ac_if; + + if ( !(ifp->if_flags & IFF_UP) ) { + mveth_stop(sc); + ifp->if_flags &= ~(IFF_UP|IFF_RUNNING); + continue; + } + + if ( !(ifp->if_flags & IFF_RUNNING) ) { + /* event could have been pending at the time hw was stopped; + * just ignore... + */ + continue; + } + + x = mveth_ack_irqs(&sc->pvt, -1); + + if ( MV643XX_ETH_EXT_IRQ_LINK_CHG & x ) { + /* phy status changed */ + int media; + + if ( 0 == BSP_mve_ack_link_chg(&sc->pvt, &media) ) { + if ( IFM_LINK_OK & media ) { + ifp->if_flags &= ~IFF_OACTIVE; + mveth_start(ifp); + } else { + /* stop sending */ + ifp->if_flags |= IFF_OACTIVE; + } + } + } + /* free tx chain */ + if ( (MV643XX_ETH_EXT_IRQ_TX_DONE & x) && BSP_mve_swipe_tx(&sc->pvt) ) { + ifp->if_flags &= ~IFF_OACTIVE; + if ( TX_AVAILABLE_RING_SIZE(&sc->pvt) == sc->pvt.avail ) + ifp->if_timer = 0; + mveth_start(ifp); + } + if ( (MV643XX_ETH_IRQ_RX_DONE & x) ) + BSP_mve_swipe_rx(&sc->pvt); + + mveth_enable_irqs(&sc->pvt, -1); + } + } + } +} + +#ifdef MVETH_DETACH_HACK +static int mveth_detach(struct mveth_softc *sc); +#endif + + +/* PUBLIC RTEMS BSDNET ATTACH FUNCTION */ +int +rtems_mve_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) +{ +char *unitName; +int unit,i,cfgUnits; +struct mveth_softc *sc; +struct ifnet *ifp; + + unit = rtems_bsdnet_parse_driver_name(ifcfg, &unitName); + if ( unit <= 0 || unit > MV643XXETH_NUM_DRIVER_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, MV643XXETH_NUM_DRIVER_SLOTS); + return 1; + } + + sc = &theMvEths[unit-1]; + ifp = &sc->arpcom.ac_if; + sc->pvt.port_num = unit-1; + sc->pvt.phy = (MV_READ(MV643XX_ETH_PHY_ADDR_R) >> (5*sc->pvt.port_num)) & 0x1f; + + if ( attaching ) { + if ( ifp->if_init ) { + printk(DRVNAME": instance %i already attached.\n", unit); + return -1; + } + + for ( i=cfgUnits = 0; irbuf_count, + ifcfg->xbuf_count, + BSP_MVE_IRQ_TX | BSP_MVE_IRQ_RX | BSP_MVE_IRQ_LINK) ) { + return -1; + } + + if ( nmbclusters < sc->pvt.rbuf_count * cfgUnits + 60 /* arbitrary */ ) { + printk(DRVNAME"%i: (mv643xx ethernet) Your application has not enough mbuf clusters\n", unit); + printk( " configured for this driver.\n"); + return -1; + } + + if ( ifcfg->hardware_address ) { + memcpy(sc->arpcom.ac_enaddr, ifcfg->hardware_address, ETHER_ADDR_LEN); + } else { + /* read back from hardware assuming that MotLoad already had set it up */ + BSP_mve_read_eaddr(&sc->pvt, sc->arpcom.ac_enaddr); + } + + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = unitName; + + ifp->if_mtu = ifcfg->mtu ? ifcfg->mtu : ETHERMTU; + + ifp->if_init = mveth_init; + ifp->if_ioctl = mveth_ioctl; + ifp->if_start = mveth_start; + ifp->if_output = ether_output; + /* + * While nonzero, the 'if->if_timer' is decremented + * (by the networking code) at a rate of IFNET_SLOWHZ (1hz) and 'if_watchdog' + * is called when it expires. + * If either of those fields is 0 the feature is disabled. + */ + ifp->if_watchdog = mveth_watchdog; + ifp->if_timer = 0; + + sc->bsd.oif_flags = /* ... */ + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; + + /* + * if unset, this set to 10Mbps by ether_ifattach; seems to be unused by bsdnet stack; + * could be updated along with phy speed, though... + ifp->if_baudrate = 10000000; + */ + + /* NOTE: ether_output drops packets if ifq_len >= ifq_maxlen + * but this is the packet count, not the fragment count! + ifp->if_snd.ifq_maxlen = sc->pvt.xbuf_count; + */ + ifp->if_snd.ifq_maxlen = ifqmaxlen; + +#ifdef MVETH_DETACH_HACK + if ( !ifp->if_addrlist ) /* do only the first time [reattach hack] */ +#endif + { + if_attach(ifp); + ether_ifattach(ifp); + } + + } else { +#ifdef MVETH_DETACH_HACK + if ( !ifp->if_init ) { + printk(DRVNAME": instance %i not attached.\n", unit); + return -1; + } + return mveth_detach(sc); +#else + printk(DRVNAME": interface detaching not implemented\n"); + return -1; +#endif + } + + return 0; +} + +/* EARLY PHY ACCESS */ +static int +mveth_early_init(int idx) +{ + if ( idx < 0 || idx >= MV643XXETH_NUM_DRIVER_SLOTS ) + return -1; + + /* determine the phy */ + theMvEths[idx].pvt.phy = (MV_READ(MV643XX_ETH_PHY_ADDR_R) >> (5*idx)) & 0x1f; + return 0; +} + +static int +mveth_early_read_phy(int idx, unsigned reg) +{ +int rval; + + if ( idx < 0 || idx >= MV643XXETH_NUM_DRIVER_SLOTS ) + return -1; + + rval = mveth_mii_read(&theMvEths[idx].pvt, reg); + return rval < 0 ? rval : rval & 0xffff; +} + +static int +mveth_early_write_phy(int idx, unsigned reg, unsigned val) +{ + if ( idx < 0 || idx >= MV643XXETH_NUM_DRIVER_SLOTS ) + return -1; + + mveth_mii_write(&theMvEths[idx].pvt, reg, val); + return 0; +} + +rtems_bsdnet_early_link_check_ops +rtems_mve_early_link_check_ops = { + init: mveth_early_init, + read_phy: mveth_early_read_phy, + write_phy: mveth_early_write_phy, + name: DRVNAME, + num_slots: MAX_NUM_SLOTS +}; + +/* DEBUGGING */ + +#ifdef MVETH_DEBUG +/* Display/dump descriptor rings */ + +int +mveth_dring(struct mveth_softc *sc) +{ +int i; +if (1) { +MvEthRxDesc pr; +printf("RX:\n"); + + for (i=0, pr=sc->pvt.rx_ring; ipvt.rbuf_count; i++, pr++) { +#ifndef ENABLE_HW_SNOOPING + /* can't just invalidate the descriptor - if it contains + * data that hasn't been flushed yet, we create an inconsistency... + */ + rtems_bsdnet_semaphore_obtain(); + INVAL_DESC(pr); +#endif + printf("cnt: 0x%04x, size: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x\n", + pr->byte_cnt, pr->buf_size, pr->cmd_sts, (uint32_t)pr->next_desc_ptr, pr->buf_ptr); + +#ifndef ENABLE_HW_SNOOPING + rtems_bsdnet_semaphore_release(); +#endif + } +} +if (1) { +MvEthTxDesc pt; +printf("TX:\n"); + for (i=0, pt=sc->pvt.tx_ring; ipvt.xbuf_count; i++, pt++) { +#ifndef ENABLE_HW_SNOOPING + rtems_bsdnet_semaphore_obtain(); + INVAL_DESC(pt); +#endif + printf("cnt: 0x%04x, stat: 0x%08x, next: 0x%08x, buf: 0x%08x, mb: 0x%08x\n", + pt->byte_cnt, pt->cmd_sts, (uint32_t)pt->next_desc_ptr, pt->buf_ptr, + (uint32_t)pt->mb); + +#ifndef ENABLE_HW_SNOOPING + rtems_bsdnet_semaphore_release(); +#endif + } +} + return 0; +} + +#endif + +/* DETACH HACK DETAILS */ + +#ifdef MVETH_DETACH_HACK +int +_cexpModuleFinalize(void *mh) +{ +int i; + for ( i=0; iif_flags = 0; + ifp->if_ioctl = 0; + ifp->if_start = 0; + ifp->if_watchdog = 0; + ifp->if_init = 0; +} + +static int +mveth_detach(struct mveth_softc *sc) +{ +struct ifnet *ifp = &sc->arpcom.ac_if; + if ( ifp->if_init ) { + if ( ifp->if_flags & (IFF_UP | IFF_RUNNING) ) { + printf(DRVNAME"%i: refuse to detach; interface still up\n",sc->pvt.port_num+1); + return -1; + } + mveth_stop(sc); +/* not implemented in BSDnet/RTEMS (yet) but declared in header */ +#define ether_ifdetach ether_ifdetach_pvt + ether_ifdetach(ifp); + } + free( (void*)sc->pvt.ring_area, M_DEVBUF ); + sc->pvt.ring_area = 0; + sc->pvt.tx_ring = 0; + sc->pvt.rx_ring = 0; + sc->pvt.d_tx_t = sc->pvt.d_tx_h = 0; + sc->pvt.d_rx_t = 0; + sc->pvt.avail = 0; + /* may fail if ISR was not installed yet */ + BSP_remove_rtems_irq_handler( &irq_data[sc->pvt.port_num] ); + return 0; +} + +#ifdef MVETH_DEBUG +struct rtems_bsdnet_ifconfig mveth_dbg_config = { + name: DRVNAME"1", + attach: rtems_mve_attach, + ip_address: "192.168.2.10", /* not used by rtems_bsdnet_attach */ + ip_netmask: "255.255.255.0", /* not used by rtems_bsdnet_attach */ + hardware_address: 0, /* (void *) */ + ignore_broadcast: 0, /* TODO driver should honour this */ + mtu: 0, + rbuf_count: 0, /* TODO driver should honour this */ + xbuf_count: 0, /* TODO driver should honour this */ +}; +#endif +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mve_smallbuf_tst.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mve_smallbuf_tst.c new file mode 100644 index 0000000000..62469f736c --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/mve_smallbuf_tst.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include + +/* Demo for the mv64360 ethernet quirk: + * + * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + * $$ buffer segments < 8 bytes must be aligned $$ + * $$ to 8 bytes but larger segments are not $$ + * $$ sensitive to alignment. $$ + * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + * + * How to use: + * + * Init MVE driver on (unused) unit 2: + * + * mve = mvtst_init(2) + * + * data = { 1,2,3,4,5,6,7,8,9,0xa,0xb, ... } + * + * Alloc 2-element mbuf chain (1st holds an + * ethernet header which is > 8bytes so we can't + * test this with only 1 mbuf. The 2nd mbuf holds + * a small fragment of data). + * + * mb = mvtst_getbuf(mve) + * + * Copy data into aligned area inside 2nd mbuf, + * (so that you can see if the chip starts using + * the aligned area rather than the unaligned + * buffer pointer). Point mbuf's data pointer + * at 'off'set from the aligned area: + * + * mvtst_putbuf(mb, data, len, offset) + * + * Send chain off: + * + * BSP_mve_send_buf(mve, mb, 0, 0) + * + * Watch raw data: + * + * tcpdump -XX -vv -s0 ether host + * + * E.g, if offset = 1, len = 2 then we would like + * to see + * + * GOOD: + * < 14 header bytes > 0x02, 0x03 + + * but if the chip starts DMA at aligned address + * we see instead + * BAD: + * < 14 header bytes > 0x01, 0x02 + */ + +static inline void *rmalloc(size_t l) { return malloc(l); } +static inline void rfree(void *p) { return free(p); } + +#define _KERNEL +#include + +static void +cleanup_buf(void *u_b, void *closure, int error) +{ +rtems_bsdnet_semaphore_obtain(); + m_freem((struct mbuf*)u_b); +rtems_bsdnet_semaphore_release(); +} + +struct mbuf *mvtst_getbuf(struct mveth_private *mp) +{ +struct mbuf *m,*n; + + if ( !mp ) { + printf("need driver ptr arg\n"); + return 0; + } +rtems_bsdnet_semaphore_obtain(); + MGETHDR(m, M_DONTWAIT, MT_DATA); + MGET(n, M_DONTWAIT, MT_DATA); + m->m_next = n; +rtems_bsdnet_semaphore_release(); + /* Ethernet header */ + memset( mtod(m, unsigned char*), 0xff, 6); + BSP_mve_read_eaddr(mp, mtod(m, unsigned char*) + 6); + /* Arbitrary; setting to IP but we don't bother + * to setup a real IP header. We just watch the + * raw packet contents... + */ + mtod(m, unsigned char*)[12] = 0x08; + mtod(m, unsigned char*)[13] = 0x00; + m->m_pkthdr.len = m->m_len = 14; + n->m_len = 0; + return m; +} + +int +mvtst_putbuf(struct mbuf *m, void *data, int len, int off) +{ +int i; + if ( m ) { + m->m_pkthdr.len += len; + if ( ( m= m->m_next ) ) { + m->m_len = len; + memcpy(mtod(m, void*), data, 32); + m->m_data += off; + printf("m.dat: 0x%08x, m.data: 0x%08x\n", m->m_dat, m->m_data); + for ( i=0; i< 16; i++ ) { + printf(" %02x,",mtod(m, unsigned char*)[i]); + } + printf("\n"); + } + } + + return 0; +} + +static void *alloc_rxbuf(int *p_size, unsigned long *paddr) +{ + return *(void**)paddr = rmalloc((*p_size = 1800)); +} + +static void consume_buf(void *buf, void *closure, int len) +{ + rfree(buf); +} + +void * +mvtst_init(int unit) +{ +struct mveth_private *mp; + mp = BSP_mve_setup( + unit, 0, + cleanup_buf, 0, + alloc_rxbuf, + consume_buf, 0, + 10, 10, + 0); + if ( mp ) + BSP_mve_init_hw(mp, 0, 0); + return mp; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/testing.c b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/testing.c new file mode 100644 index 0000000000..71090a3aa0 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/if_mve/testing.c @@ -0,0 +1,323 @@ +#ifndef KERNEL +#define KERNEL +#endif + +#include +#include +#include +#include + +#include "mv64340_eth_ll.h" + +#include +#include + +#include +#include + +#define RX_SPACING 1 +#define TX_SPACING 1 + +#define RX_RING_SIZE (MV64340_RX_QUEUE_SIZE*RX_SPACING) +#define TX_RING_SIZE (MV64340_TX_QUEUE_SIZE*TX_SPACING) + + +struct eth_rx_desc rx_ring[RX_RING_SIZE] __attribute__((aligned(32))); +struct eth_rx_desc rx_ring[RX_RING_SIZE] = {{0},}; + +struct eth_tx_desc tx_ring[TX_RING_SIZE] __attribute__((aligned(32))); +struct eth_tx_desc tx_ring[TX_RING_SIZE] = {{0},}; + +/* packet buffers */ +char rx_buf[MV64340_RX_QUEUE_SIZE][2048] __attribute__((aligned(8))); +char rx_buf[MV64340_RX_QUEUE_SIZE][2048]; + +char tx_buf[MV64340_RX_QUEUE_SIZE][2048] __attribute__((aligned(8))); +char tx_buf[MV64340_RX_QUEUE_SIZE][2048]; + +char BcHeader[22] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* dst */ + 0x00, 0x01, 0xaf, 0x13, 0xb5, 0x3e, /* src */ + 00, 00, /* len */ + 0xAA, /* dsap */ + 0xAA, /* ssap */ + 0x03, /* ctrl */ + 0x08, 0x00, 0x56, /* snap_org [stanford] */ + 0x80, 0x5b, /* snap_type (stanford kernel) */ +}; + +struct mv64340_private mveth = { + port_num: 0, + port_mac_addr: {0x00,0x01,0xAF,0x13,0xB5,0x3C}, + /* port_config .. tx_resource_err are set by port_init */ + 0 +}; + +struct pkt_info p0,p1; + +static inline void rx_stopq(int port) +{ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(port), 0x0000ff00); +} + +static inline void tx_stopq(int port) +{ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(port), 0x0000ff00); +} + +#define MV64360_ENET2MEM_SNOOP_NONE 0x0000 +#define MV64360_ENET2MEM_SNOOP_WT 0x1000 +#define MV64360_ENET2MEM_SNOOP_WB 0x2000 + +#if 0 +int +mveth_init(struct mv64340_private *mp) +{ +int i; + mp->p_rx_desc_area = rx_ring; + mp->p_tx_desc_area = tx_ring; + + rx_stopq(mp->port_num); + tx_stopq(mp->port_num); + + /* MotLoad has cache snooping disabled on the ENET2MEM windows. + * Some comments in (linux) indicate that there are errata + * which cause problems which is a real bummer. + * We try it anyways... + */ + { + unsigned long disbl, bar; + disbl = MV_READ(MV64340_ETH_BASE_ADDR_ENABLE_REG); + /* disable all 6 windows */ + MV_WRITE(MV64340_ETH_BASE_ADDR_ENABLE_REG, 0x3f); + /* set WB snooping */ + for ( i=0; i<6*8; i+=8 ) { + if ( (bar = MV_READ(MV64340_ETH_BAR_0 + i)) && MV_READ(MV64340_ETH_SIZE_REG_0 + i) ) { + MV_WRITE(MV64340_ETH_BAR_0 + i, bar | MV64360_ENET2MEM_SNOOP_WB); + /* read back to flush fifo [linux comment] */ + (void)MV_READ(MV64340_ETH_BAR_0 + i); + } + } + /* restore/re-enable */ + MV_WRITE(MV64340_ETH_BASE_ADDR_ENABLE_REG, disbl); + } + + eth_port_init(mp); + + sleep(1); + + mveth_init_tx_desc_ring(mp); + mveth_init_rx_desc_ring(mp); +#if 0 + for ( i = 0; im_flags & M_EXT) ) { + m_freem(m); + emsg="Unable to allocate cluster\n"; + goto bail; + } + p = mtod(m, char *); + l = 0; + switch (nbufs) { + case 3: + default: + emsg="nbufs arg must be 1..3\n"; + goto bail; + + case 1: + l += sizeof(BcHeader); + memcpy(p, &BcHeader, sizeof(BcHeader)); + p += sizeof(BcHeader); + + case 2: + memcpy(p,data,len); + l += len; + m->m_len = m->m_pkthdr.len = l; + if ( 2 == nbufs ) { + M_PREPEND(m, sizeof (BcHeader), M_WAIT); + if (!m) { + emsg = "Unable to prepend\n"; + goto bail; + } + p = mtod(m, char*); + memcpy(p,&BcHeader,sizeof(BcHeader)); + l += sizeof(BcHeader); + } + break; + } + *(short*)(mtod(m, char*) + 12) = htons(l-14); + rval = mveth_send_mbuf(mp,m); + +bail: + rtems_bsdnet_semaphore_release(); + if (emsg) + printf(emsg); + +#if 0 + /* + * Add local net header. If no space in first mbuf, + * allocate another. + */ + M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT); + if (m == 0) + senderr(ENOBUFS); + eh = mtod(m, struct ether_header *); + (void)memcpy(&eh->ether_type, &type, + sizeof(eh->ether_type)); + (void)memcpy(eh->ether_dhost, edst, sizeof (edst)); + (void)memcpy(eh->ether_shost, ac->ac_enaddr, + sizeof(eh->ether_shost)); +#endif + return rval; +} + +int +mveth_protected(int (*p)(struct mv64340_private*), struct mv64340_private *mp) +{ +int rval; + rtems_bsdnet_semaphore_obtain(); + rval = p(mp); + rtems_bsdnet_semaphore_release(); + return rval; +} + +int +mveth_rx(struct mv64340_private *mp) +{ +extern int mveth_swipe_rx(); + return mveth_protected(mveth_swipe_rx,mp); +} + +int +mveth_reclaim(struct mv64340_private *mp) +{ +extern int mveth_swipe_tx(); + return mveth_protected(mveth_swipe_tx,mp); +} + + +int preth(FILE *f, char *p) +{ +int i; + for (i=0; i<4; i++) + fprintf(f,"%02X:",p[i]); + fprintf(f,"%02X",p[i]); + return 6; +} + +char *errcode2str(st) +{ +char *rval; + switch(st) { + case ETH_OK: + rval = "OK"; + break; + case ETH_ERROR: + rval = "Fundamental error."; + break; + case ETH_RETRY: + rval = "Could not process request. Try later."; + break; + case ETH_END_OF_JOB: + rval = "Ring has nothing to process."; + break; + case ETH_QUEUE_FULL: + rval = "Ring resource error."; + break; + case ETH_QUEUE_LAST_RESOURCE: + rval = "Ring resources about to exhaust."; + break; + default: + rval = "UNKNOWN"; break; + } + return rval; +} + + +#if 0 +int +mveth_rx(struct mv64340_private *mp) +{ +int st; +struct pkt_info p; + if ( ETH_OK != (st=eth_port_receive(mp, &p)) ) { + fprintf(stderr,"receive: %s\n", errcode2str(st)); + return -1; + } + printf("%i bytes received from ", p.byte_cnt); + preth(stdout,(char*)p.buf_ptr+6); + printf(" (desc. stat: 0x%08x)\n", p.cmd_sts); + + p.byte_cnt = sizeof(rx_buf[0]); + p.buf_ptr -= RX_BUF_OFFSET; + if ( ETH_OK != (st=eth_rx_return_buff(mp,&p) ) ) { + fprintf(stderr,"returning buffer: %s\n", errcode2str(st)); + return -1; + } + return 0; +} +#endif + +int +dring() +{ +int i; +if (1) { +struct eth_rx_desc *pr; +printf("RX:\n"); + for (i=0, pr=rx_ring; ibyte_cnt, pr->buf_size, pr->cmd_sts, pr->next_desc_ptr, pr->buf_ptr); + } +} +if (1) { +struct eth_tx_desc *pt; +printf("TX:\n"); + for (i=0, pt=tx_ring; ibyte_cnt, pt->cmd_sts, pt->next_desc_ptr, pt->buf_ptr); + } +} + return 0; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/LICENSE b/c/src/lib/libbsp/powerpc/beatnik/network/porting/LICENSE new file mode 100644 index 0000000000..62b91ab03d --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/LICENSE @@ -0,0 +1,51 @@ +/* NOTE: The terms described in this LICENSE file apply only to the + * files created by the author (see below). Consult individual + * file headers for more details. Some files were ported from + * netbsd and/or freebsd and are covered by the respective + * file header copyright notices. + */ + +/* + * Authorship + * ---------- + * This software ('RTEMS-portability wrappers for BSD network drivers') was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'RTEMS-portability wrappers for BSD network drivers' software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/Makefile.template b/c/src/lib/libbsp/powerpc/beatnik/network/porting/Makefile.template new file mode 100644 index 0000000000..3c5d6e3e33 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/Makefile.template @@ -0,0 +1,84 @@ +# +# Makefile.lib,v 1.5 2000/06/12 15:00:14 joel Exp +# +# Copyright: Till Straumann , 2005; +# License: see LICENSE file. +# +# Templates/Makefile.lib +# Template library Makefile +# + +LIBNAME=libif_XXX.a # XXX- your library names goes here +LIB=${ARCH}/${LIBNAME} + +# C and C++ source names, if any, go here -- minus the .c or .cc +C_PIECES=if_XXX if_XXX_rtems +C_FILES=$(C_PIECES:%=%.c) +C_O_FILES=$(C_PIECES:%=${ARCH}/%.o) + +CC_PIECES= +CC_FILES=$(CC_PIECES:%=%.cc) +CC_O_FILES=$(CC_PIECES:%=${ARCH}/%.o) + +H_FILES= + +# Assembly source names, if any, go here -- minus the .S +S_PIECES= +S_FILES=$(S_PIECES:%=%.S) +S_O_FILES=$(S_FILES:%.S=${ARCH}/%.o) + +SRCS=$(C_FILES) $(CC_FILES) $(H_FILES) $(S_FILES) +OBJS=$(C_O_FILES) $(CC_O_FILES) $(S_O_FILES) + +include $(RTEMS_MAKEFILE_PATH)/Makefile.inc + +include $(RTEMS_CUSTOM) +include $(RTEMS_ROOT)/make/lib.cfg + +# +# Add local stuff here using += +# + +#DEFINES += -DHAVE_LIBBSPEXT -DDEBUG_MODULAR +CPPFLAGS += -I. -Ilibchip -Iporting +# bsdnet newproc generated daemon is non-FP; +# prevent optimizer from generating FP instructions +CFLAGS += -Wno-unused-variable -msoft-float + +# +# Add your list of files to delete here. The config files +# already know how to delete some stuff, so you may want +# to just run 'make clean' first to see what gets missed. +# 'make clobber' already includes 'make clean' +# + +CLEAN_ADDITIONS += +CLOBBER_ADDITIONS += + +all: ${ARCH} $(SRCS) $(LIB) + +$(LIB): ${OBJS} + $(make-library) + +ifndef RTEMS_SITE_INSTALLDIR +RTEMS_SITE_INSTALLDIR = $(PROJECT_RELEASE) +endif + +${RTEMS_SITE_INSTALLDIR}/include \ +${RTEMS_SITE_INSTALLDIR}/lib \ +${RTEMS_SITE_INSTALLDIR}/bin \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/include \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/lib \ +${RTEMS_SITE_INSTALLDIR}/$(RTEMS_BSP)/bin : + test -d $@ || mkdir -p $@ + +# Install the library, appending _g or _p as appropriate. +# for include files, just use $(INSTALL_CHANGE) +# +# NOTES: +# - BSP specific libraries, headers etc. should be installed to +# $RTEMS_SITE_INSTALLDIR)/$(RTEMS_BSP)/lib +# + +install: all $(RTEMS_SITE_INSTALLDIR)/lib + $(INSTALL_VARIANT) -m 644 ${LIB} ${RTEMS_SITE_INSTALLDIR}/lib diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/README b/c/src/lib/libbsp/powerpc/beatnik/network/porting/README new file mode 100644 index 0000000000..57a5690e9e --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/README @@ -0,0 +1,106 @@ +Templates to help porting freebsd networking drivers +to rtems (focus on i386 and powerpc) using a 'quick and dirty' +approach. +This is not an elegant piece of software -- be warned. + +/* Copyright: Till Straumann , 2005; + * License: see LICENSE file. + */ + +Release info: $Name$ + +Usage: + + A obtain the freebsd driver source. It usually is made + up of a + if_XXX.c -- core driver + if_XXXreg.h -- core header + if_XXXvar.h -- some have it, some don't + if_XXX_.c -- glue to integrate the core + driver with different environments + (such as pccard, pci, ...). There + are several of these. + + Note that you might have to get an older version + as some structures and interfaces may have undergone + significant changes since the bsd/networking version that + was ported to rtems. + + B Copy the Makefile and rtemscompat_defs.h templates to the + driver source dir and edit them. + + C Edit if_XXXreg.h and comment all unneeded fields from the + 'softc' structure declaration with + + #ifndef __rtems__ + #endif + + In particular, the bushandle field (as defined in rtemscompat_defs.h) + above, see comments in the template) must be re-declared: + + #ifndef __rtems__ + bus_space_handle_t XXX_bhandle; + #else + unsigned XXX_bhandle; + unsigned char irq_no; + unsigned char b,d,f; /* PCI tuple; needed for PCI only */ + rtems_id tid; /* driver task id */ + #endif + + Later, the compilation attempts will help identifying + fields that need to be removed. + + I like the #ifdef __rtems__ construct as it minimizes changes + to the source thus making merging updated driver versions easier. + + D Edit if_XXX.c; at the very top, include the lines + + #ifdef __rtems__ + #include + #endif + + use the #ifndef __rtems__ #endif construct to comment + unneeded / unsupported inclusion of headers and code pieces. + + - inclusion of net/if_media.h must usually be mapped to + libchip/if_media.h + + comment all vm, machine, bus, mii etc. related headers. + + - replace inclusion of if_XXXreg.h by + + #include "if_XXXreg.h" + #include + + - work through the if_XXX.c and if_XXXreg.h files commenting + stuff until if_XXX.c compiles. This might involve hacking + the helper headers. + + - pay attention to endian issues; things may need to be fixed! + + - at the top where the freebsd 'methods' and the like are + commented, add a definition of the driver methods for RTEMS: + + #ifdef __rtems__ + net_drv_tbl_t METHODS = { + n_probe : XXX_probe, + n_attach : XXX_attach, + n_detach : XXX_detach, /* optional; */ + n_intr : XXX_intr, /* freebsd ISR; executed from daemon under RTEMS */ + }; + #endif + + - make sure all the if_xxx methods are set; in particular, + set + sc->if_output = ether_output; + + - on input: + you can use DO_ETHER_INPUT_SKIPPING_ETHER_HEADER() macro + -- if you don't MAKE SURE THE RECEIVING INTERFACE IS SET + in the mbuf packet header!!! + + E create 'rtems__setup()' to probe for devices and + set the softc struct's base address, interrupt line and + bus/dev/fun triple (PCI only). + For PCI devices, a generic setup routine already comes with + porting/if_xxx_rtems.c -> nothing needs to be written! diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx.modini.c b/c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx.modini.c new file mode 100644 index 0000000000..1abad7dc22 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx.modini.c @@ -0,0 +1,34 @@ +#include +#include + +/* CEXP module initialization/finalization */ + +/* Copyright: Till Straumann , 2005; + * License: see LICENSE file. + */ + +void +_cexpModuleInitialize(void *unused) +{ +extern void NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringup)(char *); + METHODSPTR = &METHODS; +/* +#ifdef DEBUG + NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringup)("192.168.2.13/255.255.255.0"); +#endif +*/ +} + +int +_cexpModuleFinalize(void *unused) +{ +#ifdef DEBUG +extern int NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringdown)(); + if (NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringdown)()) + return -1; + METHODSPTR = 0; + return 0; +#else + return -1; +#endif +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx_rtems.c b/c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx_rtems.c new file mode 100644 index 0000000000..0c7d2d9eba --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/if_xxx_rtems.c @@ -0,0 +1,504 @@ +/* $Id$ */ +#include + +/* Template for driver task, setup and attach routines. To be instantiated + * by defining the relevant symbols in header files. + */ + +/* Copyright: Till Straumann , 2005; + * License: see LICENSE file. + */ + +/* $Name$ */ + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#ifdef IF_REG_HEADER +#include IF_REG_HEADER +#endif +#ifdef IF_VAR_HEADER +#include IF_VAR_HEADER +#endif + +#include + +#define EX_EVENT RTEMS_EVENT_1 +#undef IRQ_AT_8259 + +NETDEV_DECL = { /*[0]:*/{ /* softc: */ { /* arpcom: */{ /* ac_if: */ { 0 }}}}}; + +static void net_daemon(void *arg); + +#ifdef HAVE_LIBBSPEXT +#include +static void the_net_isr(void *); +#else +static void noop(const rtems_irq_connect_data *unused) {} +static int noop1(const rtems_irq_connect_data *unused) { return 0;} +#if ISMINVERSION(4,6,99) +static void the_net_isr(rtems_irq_hdl_param); +#else +static void the_net_isr(); +#if NETDRIVER_SLOTS > 1 +#error only one instance supported (stupid IRQ API) +#else +static struct NET_SOFTC *thesc; +#endif +#endif +#endif + +#if defined(NETDRIVER_PCI) +/* Public setup routine for PCI devices; + * TODO: currently doesn't work for subsystem vendor/id , i.e. + * devices behind a standard PCI interface... + */ +int +NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_pci_setup)(int inst); +#endif + +static unsigned +NET_EMBEMB(,NETDRIVER_PREFIX,_net_driver_ticks_per_sec) = 0; + +/* other drivers may already have this created */ +extern unsigned net_driver_ticks_per_sec +__attribute__((weak, alias(NET_STRSTR(NETDRIVER_PREFIX)"_net_driver_ticks_per_sec") )); + +#ifdef DEBUG_MODULAR +net_drv_tbl_t * volatile METHODSPTR = 0; +#endif + + +int +NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_attach) + (struct rtems_bsdnet_ifconfig *config, int attaching) +{ + int error = 0; + device_t dev = net_dev_get(config); + struct NET_SOFTC *sc; + struct ifnet *ifp; +#ifndef HAVE_LIBBSPEXT + rtems_irq_connect_data irq_data = { + 0, + the_net_isr, +#if ISMINVERSION(4,6,99) + 0, +#endif + noop, + noop, + noop1 }; +#endif + + if ( !dev ) + return 1; + + if ( !dev->d_softc.NET_SOFTC_BHANDLE_FIELD ) { +#if defined(NETDRIVER_PCI) + device_printf(dev,NETDRIVER" unit not configured; executing setup..."); + /* setup should really be performed prior to attaching. + * Wipe the device; setup and re-obtain the device... + */ + memset(dev,0,sizeof(*dev)); + error = NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_pci_setup)(-1); + /* re-obtain the device */ + dev = net_dev_get(config); + if ( !dev ) { + printk("Unable to re-assign device structure???\n"); + return 1; + } + if (error <= 0) { + device_printf(dev,NETDRIVER" FAILED; unable to attach interface, sorry\n"); + return 1; + } + device_printf(dev,"success\n"); +#else + device_printf(dev,NETDRIVER" unit not configured; use 'rtems_"NETDRIVER"_setup()'\n"); + return 1; +#endif + } + + if ( !net_driver_ticks_per_sec ) + rtems_clock_get( RTEMS_CLOCK_GET_TICKS_PER_SECOND, &net_driver_ticks_per_sec ); + + sc = device_get_softc( dev ); + ifp = &sc->arpcom.ac_if; + +#ifdef DEBUG_MODULAR + if (!METHODSPTR) { + device_printf(dev,NETDRIVER": method pointer not set\n"); + return -1; + } +#endif + + if ( attaching ) { + if ( ifp->if_init ) { + device_printf(dev,NETDRIVER" Driver already attached.\n"); + return -1; + } + if ( config->hardware_address ) { + /* use configured MAC address */ + memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } else { +#ifdef NET_READ_MAC_ADDR + NET_READ_MAC_ADDR(sc); +#endif + } + if ( METHODSPTR->n_attach(dev) ) { + device_printf(dev,NETDRIVER"_attach() failed\n"); + return -1; + } + } else { + if ( !ifp->if_init ) { + device_printf(dev,NETDRIVER" Driver not attached.\n"); + return -1; + } + if ( METHODSPTR->n_detach ) { + if ( METHODSPTR->n_detach(dev) ) { + device_printf(dev,NETDRIVER"_detach() failed\n"); + return -1; + } + } else { + device_printf(dev,NETDRIVER"_detach() not implemented\n"); + return -1; + } + } + + + if ( !sc->tid ) + sc->tid = rtems_bsdnet_newproc(NETDRIVER"d", 4096, net_daemon, sc); + + if (attaching) { +#ifdef DEBUG + printf("Installing IRQ # %i\n",sc->irq_no); +#endif +#ifdef HAVE_LIBBSPEXT + if ( bspExtInstallSharedISR(sc->irq_no, the_net_isr, sc, 0) ) +#else + /* BSP dependent :-( */ + irq_data.name = sc->irq_no; +#if ISMINVERSION(4,6,99) + irq_data.handle = (rtems_irq_hdl_param)sc; +#else + thesc = sc; +#endif + if ( ! BSP_install_rtems_irq_handler( &irq_data ) ) +#endif + { + fprintf(stderr,NETDRIVER": unable to install ISR\n"); + error = -1; + } + } else { + if ( sc->irq_no ) { +#ifdef DEBUG + printf("Removing IRQ # %i\n",sc->irq_no); +#endif +#ifdef HAVE_LIBBSPEXT + if (bspExtRemoveSharedISR(sc->irq_no, the_net_isr, sc)) +#else + /* BSP dependent :-( */ + irq_data.name = sc->irq_no; +#if ISMINVERSION(4,6,99) + irq_data.handle = (rtems_irq_hdl_param)sc; +#endif + if ( ! BSP_remove_rtems_irq_handler( &irq_data ) ) +#endif + { + fprintf(stderr,NETDRIVER": unable to uninstall ISR\n"); + error = -1; + } + } + } + return error; +} + +static void +the_net_isr( +#ifdef HAVE_LIBBSPEXT +void *thesc +#elif ISMINVERSION(4,6,99) +rtems_irq_hdl_param thesc +#endif +) +{ +struct NET_SOFTC *sc = thesc; + + /* disable interrupts */ + NET_DISABLE_IRQS(sc); + + rtems_event_send( sc->tid, EX_EVENT ); +} + +static void net_daemon(void *arg) +{ +struct NET_SOFTC *sc = arg; +rtems_event_set evs; + + for (;;) { + rtems_bsdnet_event_receive( + EX_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &evs); + + METHODSPTR->n_intr(sc); + + /* re-enable interrupts */ + NET_ENABLE_IRQS(sc); + } +} + +static struct NET_SOFTC * +net_drv_check_unit(int unit) +{ + unit--; + if ( unit < 0 || unit >= NETDRIVER_SLOTS ) { + fprintf(stderr,"Invalid unit # %i (not in %i..%i)\n", unit+1, 1, NETDRIVER_SLOTS); + return 0; + } + + if ( THEDEVS[unit].d_name ) { + fprintf(stderr,"Unit %i already set up\n", unit+1); + return 0; + } + + memset( &THEDEVS[unit], 0, sizeof(THEDEVS[0]) ); + + return &THEDEVS[unit].d_softc; +} + +struct rtems_bsdnet_ifconfig NET_EMBEMB(NETDRIVER_PREFIX,_dbg,_config) = { + NETDRIVER"1", + NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_attach), + 0 +}; + +#ifdef DEBUG +void +NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringup)(char *ipaddr) +{ +short flags; +struct sockaddr_in addr; +char *mask; + + + if (!ipaddr) { + printf("Need an ip[/mask] argument (dot notation)\n"); + return; + } + + ipaddr = strdup(ipaddr); + + if ( (mask = strchr(ipaddr,'/')) ) { + *mask++=0; + } else { + mask = "255.255.255.0"; + } + +#if defined(NETDRIVER_PCI) + /* this fails if already setup */ + NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_pci_setup)(-1); +#endif + rtems_bsdnet_attach(&NET_EMBEMB(NETDRIVER_PREFIX,_dbg,_config)); + + flags = IFF_UP /*| IFF_PROMISC*/; + if ( rtems_bsdnet_ifconfig(NETDRIVER"1",SIOCSIFFLAGS,&flags) < 0 ) { + printf("Can't bring '"NETDRIVER"1' up\n"); + goto cleanup; + } + memset(&addr,0,sizeof(addr)); + addr.sin_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(mask); + if ( rtems_bsdnet_ifconfig(NETDRIVER"1",SIOCSIFNETMASK,&addr) < 0 ) { + printf("Unable to set netmask on '"NETDRIVER"1'\n"); + goto cleanup; + } + addr.sin_addr.s_addr = inet_addr(ipaddr); + if ( rtems_bsdnet_ifconfig(NETDRIVER"1",SIOCSIFADDR,&addr) < 0 ) { + printf("Unable to set address on '"NETDRIVER"1'\n"); + goto cleanup; + } +cleanup: + the_real_free (ipaddr); +} + +int +NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_bringdown)() +{ +short flags; + flags = 0; + if ( rtems_bsdnet_ifconfig(NETDRIVER"1",SIOCSIFFLAGS,&flags) < 0 ) { + printf("Can't bring '"NETDRIVER"1' down\n"); + return -1; + } + + rtems_bsdnet_detach(&NET_EMBEMB(NETDRIVER_PREFIX,_dbg,_config)); + return 0; +} +#endif + + +#if defined(NETDRIVER_PCI) && !defined(NETDRIVER_OWN_SETUP) +/* Public setup routine for PCI devices; + * TODO: currently doesn't work for subsystem vendor/id , i.e. + * devices behind a standard PCI interface... + * passing 'inst' > only sets-up the 'inst'th card; passing + * 'inst' == 0 sets-up all matching cards. + */ +int +NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_pci_setup)(int inst) +{ +unsigned b,d,f,i,isio,unit; +rtemscompat_32_t base; +unsigned short cmd,id; +unsigned char h; +struct NET_SOFTC *sc; +unsigned try[] = { PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, 0 }; + +#ifdef DEBUG_MODULAR + if ( !METHODSPTR ) { + fprintf(stderr,NETDRIVER": Methods pointer not set\n"); + return -1; + } +#endif + + /* 0 can be reached when looking for the desired instance */ + if ( 0 == inst ) + inst = -1; + +#ifdef HAVE_LIBBSPEXT + /* make sure it's initialized */ + bspExtInit(); +#endif + + /* scan PCI for supported devices */ + for ( b=0, sc=0, unit=0; bb = b; + sc->d = d; + sc->f = f; + + for ( i=0, base=0, isio=0; try[i]; i++ ) { + pci_read_config_dword(b,d,f,try[i],&base); + if ( base ) { + if ( (isio = (PCI_BASE_ADDRESS_SPACE_IO == (base & PCI_BASE_ADDRESS_SPACE )) ) ) { +#ifdef NET_CHIP_PORT_IO + base &= PCI_BASE_ADDRESS_IO_MASK; + sc->NET_SOFTC_BHANDLE_FIELD = PCI_IO_2LOCAL(base,b); +#ifdef DEBUG + printf("Found PCI I/O Base 0x%08x\n", (unsigned)base); +#endif +#else + base = 0; + continue; +#endif + } else { +#ifdef NET_CHIP_MEM_IO + base &= PCI_BASE_ADDRESS_MEM_MASK; + sc->NET_SOFTC_BHANDLE_FIELD = PCI2LOCAL(base,b); +#ifdef DEBUG + printf("Found PCI MEM Base 0x%08x\n", (unsigned)base); +#endif +#else + base = 0; + continue; +#endif + } + break; + } + } + if ( !base ) { +#ifdef DEBUG + fprintf(stderr, NETDRIVER": (warning) Neither PCI base address 0 nor 1 are configured; skipping bus %i, slot %i, fn %i...\n",b,d,f); +#endif + continue; + } + + if ( 0 == METHODSPTR->n_probe(&THEDEVS[unit]) && (inst < 0 || !--inst) ) { + pci_read_config_word(b,d,f,PCI_COMMAND,&cmd); + pci_write_config_word(b,d,f,PCI_COMMAND, + cmd | (isio ? PCI_COMMAND_IO : PCI_COMMAND_MEMORY) | PCI_COMMAND_MASTER ); + pci_read_config_byte(b,d,f,PCI_INTERRUPT_LINE,&sc->irq_no); + printf(NETDRIVER": card found @PCI[%s] 0x%08x (local 0x%08x), IRQ %i\n", + (isio ? "io" : "mem"), (unsigned)base, sc->NET_SOFTC_BHANDLE_FIELD, sc->irq_no); + + sc = 0; /* need to allocate a new slot */ + unit++; + + if ( 0 == inst ) { + /* found desired instance */ + goto terminated; + } + } + } + } + } + +terminated: + return unit; +} +#else + +/* simple skeleton +int +NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_setup)( + int unit, + void *base_addr, + int irq_no) +{ +struct NET_SOFTC *sc; + if ( !(sc=net_drv_check_unit(unit)) ) { + fprintf(stderr,"Bad unit number -- (not enought driver slots?)\n"); + return 0; + } + sc->NET_SOFTC_BHANDLE_FIELD = base_addr; + if ( 0 == METHODSPTR->n_probe(&THEDEVS[unit-1]) ) { + sc->irq_no = irq_no; + printf(NETDRIVER": Unit %i set up\n", unit); + return unit; + } + return 0; +} +*/ + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/pcireg.h b/c/src/lib/libbsp/powerpc/beatnik/network/porting/pcireg.h new file mode 100644 index 0000000000..8487f5b8f6 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/pcireg.h @@ -0,0 +1,405 @@ +/*- + * Copyright (c) 1997, Stefan Esser + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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: /repoman/r/ncvs/src/sys/dev/pci/pcireg.h,v 1.39.4.3 2005/04/02 05:03:34 jmg Exp $ + * + */ + +/* + * PCIM_xxx: mask to locate subfield in register + * PCIR_xxx: config register offset + * PCIC_xxx: device class + * PCIS_xxx: device subclass + * PCIP_xxx: device programming interface + * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices) + * PCID_xxx: device ID + * PCIY_xxx: capability identification number + */ + +/* some PCI bus constants */ + +#define PCI_BUSMAX 255 +#define PCI_SLOTMAX 31 +#define PCI_FUNCMAX 7 +#define PCI_REGMAX 255 +#define PCI_MAXHDRTYPE 2 + +/* PCI config header registers for all devices */ + +#define PCIR_DEVVENDOR 0x00 +#define PCIR_VENDOR 0x00 +#define PCIR_DEVICE 0x02 +#define PCIR_COMMAND 0x04 +#define PCIM_CMD_PORTEN 0x0001 +#define PCIM_CMD_MEMEN 0x0002 +#define PCIM_CMD_BUSMASTEREN 0x0004 +#define PCIM_CMD_SPECIALEN 0x0008 +#define PCIM_CMD_MWRICEN 0x0010 +#define PCIM_CMD_PERRESPEN 0x0040 +#define PCIM_CMD_SERRESPEN 0x0100 +#define PCIM_CMD_BACKTOBACK 0x0200 +#define PCIR_STATUS 0x06 +#define PCIM_STATUS_CAPPRESENT 0x0010 +#define PCIM_STATUS_66CAPABLE 0x0020 +#define PCIM_STATUS_BACKTOBACK 0x0080 +#define PCIM_STATUS_PERRREPORT 0x0100 +#define PCIM_STATUS_SEL_FAST 0x0000 +#define PCIM_STATUS_SEL_MEDIMUM 0x0200 +#define PCIM_STATUS_SEL_SLOW 0x0400 +#define PCIM_STATUS_SEL_MASK 0x0600 +#define PCIM_STATUS_STABORT 0x0800 +#define PCIM_STATUS_RTABORT 0x1000 +#define PCIM_STATUS_RMABORT 0x2000 +#define PCIM_STATUS_SERR 0x4000 +#define PCIM_STATUS_PERR 0x8000 +#define PCIR_REVID 0x08 +#define PCIR_PROGIF 0x09 +#define PCIR_SUBCLASS 0x0a +#define PCIR_CLASS 0x0b +#define PCIR_CACHELNSZ 0x0c +#define PCIR_LATTIMER 0x0d +#define PCIR_HDRTYPE 0x0e +#ifndef BURN_BRIDGES +#define PCIR_HEADERTYPE PCIR_HDRTYPE +#endif +#define PCIM_HDRTYPE 0x7f +#define PCIM_HDRTYPE_NORMAL 0x00 +#define PCIM_HDRTYPE_BRIDGE 0x01 +#define PCIM_HDRTYPE_CARDBUS 0x02 +#define PCIM_MFDEV 0x80 +#define PCIR_BIST 0x0f + +/* Capability Identification Numbers */ + +#define PCIY_PMG 0x01 /* PCI Power Management */ +#define PCIY_AGP 0x02 /* AGP */ +#define PCIY_VPD 0x03 /* Vital Product Data */ +#define PCIY_SLOTID 0x04 /* Slot Identification */ +#define PCIY_MSI 0x05 /* Message Signaled Interrupts */ +#define PCIY_CHSWP 0x06 /* CompactPCI Hot Swap */ +#define PCIY_PCIX 0x07 /* PCI-X */ +#define PCIY_HT 0x08 /* HyperTransport */ +#define PCIY_VENDOR 0x09 /* Vendor Unique */ +#define PCIY_DEBUG 0x0a /* Debug port */ +#define PCIY_CRES 0x0b /* CompactPCI central resource control */ +#define PCIY_HOTPLUG 0x0c /* PCI Hot-Plug */ +#define PCIY_AGP8X 0x0e /* AGP 8x */ +#define PCIY_SECDEV 0x0f /* Secure Device */ +#define PCIY_EXPRESS 0x10 /* PCI Express */ +#define PCIY_MSIX 0x11 /* MSI-X */ + +/* config registers for header type 0 devices */ + +#define PCIR_BARS 0x10 +#define PCIR_BAR(x) (PCIR_BARS + (x) * 4) +#ifndef BURN_BRIDGES +#define PCIR_MAPS PCIR_BARS +#endif +#define PCIR_CARDBUSCIS 0x28 +#define PCIR_SUBVEND_0 0x2c +#define PCIR_SUBDEV_0 0x2e +#define PCIR_BIOS 0x30 +#define PCIM_BIOS_ENABLE 0x01 +#define PCIR_CAP_PTR 0x34 +#define PCIR_INTLINE 0x3c +#define PCIR_INTPIN 0x3d +#define PCIR_MINGNT 0x3e +#define PCIR_MAXLAT 0x3f + +/* config registers for header type 1 (PCI-to-PCI bridge) devices */ + +#define PCIR_SECSTAT_1 0x1e + +#define PCIR_PRIBUS_1 0x18 +#define PCIR_SECBUS_1 0x19 +#define PCIR_SUBBUS_1 0x1a +#define PCIR_SECLAT_1 0x1b + +#define PCIR_IOBASEL_1 0x1c +#define PCIR_IOLIMITL_1 0x1d +#define PCIR_IOBASEH_1 0x30 +#define PCIR_IOLIMITH_1 0x32 +#define PCIM_BRIO_16 0x0 +#define PCIM_BRIO_32 0x1 +#define PCIM_BRIO_MASK 0xf + +#define PCIR_MEMBASE_1 0x20 +#define PCIR_MEMLIMIT_1 0x22 + +#define PCIR_PMBASEL_1 0x24 +#define PCIR_PMLIMITL_1 0x26 +#define PCIR_PMBASEH_1 0x28 +#define PCIR_PMLIMITH_1 0x2c + +#define PCIR_BRIDGECTL_1 0x3e + +#define PCIR_SUBVEND_1 0x34 +#define PCIR_SUBDEV_1 0x36 + +/* config registers for header type 2 (CardBus) devices */ + +#define PCIR_SECSTAT_2 0x16 + +#define PCIR_PRIBUS_2 0x18 +#define PCIR_SECBUS_2 0x19 +#define PCIR_SUBBUS_2 0x1a +#define PCIR_SECLAT_2 0x1b + +#define PCIR_MEMBASE0_2 0x1c +#define PCIR_MEMLIMIT0_2 0x20 +#define PCIR_MEMBASE1_2 0x24 +#define PCIR_MEMLIMIT1_2 0x28 +#define PCIR_IOBASE0_2 0x2c +#define PCIR_IOLIMIT0_2 0x30 +#define PCIR_IOBASE1_2 0x34 +#define PCIR_IOLIMIT1_2 0x38 + +#define PCIR_BRIDGECTL_2 0x3e + +#define PCIR_SUBVEND_2 0x40 +#define PCIR_SUBDEV_2 0x42 + +#define PCIR_PCCARDIF_2 0x44 + +/* PCI device class, subclass and programming interface definitions */ + +#define PCIC_OLD 0x00 +#define PCIS_OLD_NONVGA 0x00 +#define PCIS_OLD_VGA 0x01 + +#define PCIC_STORAGE 0x01 +#define PCIS_STORAGE_SCSI 0x00 +#define PCIS_STORAGE_IDE 0x01 +#define PCIP_STORAGE_IDE_MODEPRIM 0x01 +#define PCIP_STORAGE_IDE_PROGINDPRIM 0x02 +#define PCIP_STORAGE_IDE_MODESEC 0x04 +#define PCIP_STORAGE_IDE_PROGINDSEC 0x08 +#define PCIP_STORAGE_IDE_MASTERDEV 0x80 +#define PCIS_STORAGE_FLOPPY 0x02 +#define PCIS_STORAGE_IPI 0x03 +#define PCIS_STORAGE_RAID 0x04 +#define PCIS_STORAGE_OTHER 0x80 + +#define PCIC_NETWORK 0x02 +#define PCIS_NETWORK_ETHERNET 0x00 +#define PCIS_NETWORK_TOKENRING 0x01 +#define PCIS_NETWORK_FDDI 0x02 +#define PCIS_NETWORK_ATM 0x03 +#define PCIS_NETWORK_ISDN 0x04 +#define PCIS_NETWORK_OTHER 0x80 + +#define PCIC_DISPLAY 0x03 +#define PCIS_DISPLAY_VGA 0x00 +#define PCIS_DISPLAY_XGA 0x01 +#define PCIS_DISPLAY_3D 0x02 +#define PCIS_DISPLAY_OTHER 0x80 + +#define PCIC_MULTIMEDIA 0x04 +#define PCIS_MULTIMEDIA_VIDEO 0x00 +#define PCIS_MULTIMEDIA_AUDIO 0x01 +#define PCIS_MULTIMEDIA_TELE 0x02 +#define PCIS_MULTIMEDIA_OTHER 0x80 + +#define PCIC_MEMORY 0x05 +#define PCIS_MEMORY_RAM 0x00 +#define PCIS_MEMORY_FLASH 0x01 +#define PCIS_MEMORY_OTHER 0x80 + +#define PCIC_BRIDGE 0x06 +#define PCIS_BRIDGE_HOST 0x00 +#define PCIS_BRIDGE_ISA 0x01 +#define PCIS_BRIDGE_EISA 0x02 +#define PCIS_BRIDGE_MCA 0x03 +#define PCIS_BRIDGE_PCI 0x04 +#define PCIS_BRIDGE_PCMCIA 0x05 +#define PCIS_BRIDGE_NUBUS 0x06 +#define PCIS_BRIDGE_CARDBUS 0x07 +#define PCIS_BRIDGE_RACEWAY 0x08 +#define PCIS_BRIDGE_OTHER 0x80 + +#define PCIC_SIMPLECOMM 0x07 +#define PCIS_SIMPLECOMM_UART 0x00 +#define PCIP_SIMPLECOMM_UART_16550A 0x02 +#define PCIS_SIMPLECOMM_PAR 0x01 +#define PCIS_SIMPLECOMM_MULSER 0x02 +#define PCIS_SIMPLECOMM_MODEM 0x03 +#define PCIS_SIMPLECOMM_OTHER 0x80 + +#define PCIC_BASEPERIPH 0x08 +#define PCIS_BASEPERIPH_PIC 0x00 +#define PCIS_BASEPERIPH_DMA 0x01 +#define PCIS_BASEPERIPH_TIMER 0x02 +#define PCIS_BASEPERIPH_RTC 0x03 +#define PCIS_BASEPERIPH_PCIHOT 0x04 +#define PCIS_BASEPERIPH_OTHER 0x80 + +#define PCIC_INPUTDEV 0x09 +#define PCIS_INPUTDEV_KEYBOARD 0x00 +#define PCIS_INPUTDEV_DIGITIZER 0x01 +#define PCIS_INPUTDEV_MOUSE 0x02 +#define PCIS_INPUTDEV_SCANNER 0x03 +#define PCIS_INPUTDEV_GAMEPORT 0x04 +#define PCIS_INPUTDEV_OTHER 0x80 + +#define PCIC_DOCKING 0x0a +#define PCIS_DOCKING_GENERIC 0x00 +#define PCIS_DOCKING_OTHER 0x80 + +#define PCIC_PROCESSOR 0x0b +#define PCIS_PROCESSOR_386 0x00 +#define PCIS_PROCESSOR_486 0x01 +#define PCIS_PROCESSOR_PENTIUM 0x02 +#define PCIS_PROCESSOR_ALPHA 0x10 +#define PCIS_PROCESSOR_POWERPC 0x20 +#define PCIS_PROCESSOR_MIPS 0x30 +#define PCIS_PROCESSOR_COPROC 0x40 + +#define PCIC_SERIALBUS 0x0c +#define PCIS_SERIALBUS_FW 0x00 +#define PCIS_SERIALBUS_ACCESS 0x01 +#define PCIS_SERIALBUS_SSA 0x02 +#define PCIS_SERIALBUS_USB 0x03 +#define PCIP_SERIALBUS_USB_UHCI 0x00 +#define PCIP_SERIALBUS_USB_OHCI 0x10 +#define PCIP_SERIALBUS_USB_EHCI 0x20 +#define PCIS_SERIALBUS_FC 0x04 +#define PCIS_SERIALBUS_SMBUS 0x05 + +#define PCIC_WIRELESS 0x0d +#define PCIS_WIRELESS_IRDA 0x00 +#define PCIS_WIRELESS_IR 0x01 +#define PCIS_WIRELESS_RF 0x10 +#define PCIS_WIRELESS_OTHER 0x80 + +#define PCIC_INTELLIIO 0x0e +#define PCIS_INTELLIIO_I2O 0x00 + +#define PCIC_SATCOM 0x0f +#define PCIS_SATCOM_TV 0x01 +#define PCIS_SATCOM_AUDIO 0x02 +#define PCIS_SATCOM_VOICE 0x03 +#define PCIS_SATCOM_DATA 0x04 + +#define PCIC_CRYPTO 0x10 +#define PCIS_CRYPTO_NETCOMP 0x00 +#define PCIS_CRYPTO_ENTERTAIN 0x10 +#define PCIS_CRYPTO_OTHER 0x80 + +#define PCIC_DASP 0x11 +#define PCIS_DASP_DPIO 0x00 +#define PCIS_DASP_OTHER 0x80 + +#define PCIC_OTHER 0xff + +/* PCI power manangement */ + +#define PCIR_POWER_CAP 0x2 +#define PCIM_PCAP_SPEC 0x0007 +#define PCIM_PCAP_PMEREQCLK 0x0008 +#define PCIM_PCAP_PMEREQPWR 0x0010 +#define PCIM_PCAP_DEVSPECINIT 0x0020 +#define PCIM_PCAP_DYNCLOCK 0x0040 +#define PCIM_PCAP_SECCLOCK 0x00c0 +#define PCIM_PCAP_CLOCKMASK 0x00c0 +#define PCIM_PCAP_REQFULLCLOCK 0x0100 +#define PCIM_PCAP_D1SUPP 0x0200 +#define PCIM_PCAP_D2SUPP 0x0400 +#define PCIM_PCAP_D0PME 0x1000 +#define PCIM_PCAP_D1PME 0x2000 +#define PCIM_PCAP_D2PME 0x4000 + +#define PCIR_POWER_STATUS 0x4 +#define PCIM_PSTAT_D0 0x0000 +#define PCIM_PSTAT_D1 0x0001 +#define PCIM_PSTAT_D2 0x0002 +#define PCIM_PSTAT_D3 0x0003 +#define PCIM_PSTAT_DMASK 0x0003 +#define PCIM_PSTAT_REPENABLE 0x0010 +#define PCIM_PSTAT_PMEENABLE 0x0100 +#define PCIM_PSTAT_D0POWER 0x0000 +#define PCIM_PSTAT_D1POWER 0x0200 +#define PCIM_PSTAT_D2POWER 0x0400 +#define PCIM_PSTAT_D3POWER 0x0600 +#define PCIM_PSTAT_D0HEAT 0x0800 +#define PCIM_PSTAT_D1HEAT 0x1000 +#define PCIM_PSTAT_D2HEAT 0x1200 +#define PCIM_PSTAT_D3HEAT 0x1400 +#define PCIM_PSTAT_DATAUNKN 0x0000 +#define PCIM_PSTAT_DATADIV10 0x2000 +#define PCIM_PSTAT_DATADIV100 0x4000 +#define PCIM_PSTAT_DATADIV1000 0x6000 +#define PCIM_PSTAT_DATADIVMASK 0x6000 +#define PCIM_PSTAT_PME 0x8000 + +#define PCIR_POWER_PMCSR 0x6 +#define PCIM_PMCSR_DCLOCK 0x10 +#define PCIM_PMCSR_B2SUPP 0x20 +#define PCIM_BMCSR_B3SUPP 0x40 +#define PCIM_BMCSR_BPCE 0x80 + +#define PCIR_POWER_DATA 0x7 + +/* PCI Message Signalled Interrupts (MSI) */ +#define PCIR_MSI_CTRL 0x2 +#define PCIM_MSICTRL_VECTOR 0x0100 +#define PCIM_MSICTRL_64BIT 0x0080 +#define PCIM_MSICTRL_MME_MASK 0x0070 +#define PCIM_MSICTRL_MME_1 0x0000 +#define PCIM_MSICTRL_MME_2 0x0010 +#define PCIM_MSICTRL_MME_4 0x0020 +#define PCIM_MSICTRL_MME_8 0x0030 +#define PCIM_MSICTRL_MME_16 0x0040 +#define PCIM_MSICTRL_MME_32 0x0050 +#define PCIM_MSICTRL_MMC_MASK 0x000E +#define PCIM_MSICTRL_MMC_1 0x0000 +#define PCIM_MSICTRL_MMC_2 0x0002 +#define PCIM_MSICTRL_MMC_4 0x0004 +#define PCIM_MSICTRL_MMC_8 0x0006 +#define PCIM_MSICTRL_MMC_16 0x0008 +#define PCIM_MSICTRL_MMC_32 0x000A +#define PCIM_MSICTRL_MSI_ENABLE 0x0001 +#define PCIR_MSI_ADDR 0x4 +#define PCIR_MSI_ADDR_HIGH 0x8 +#define PCIR_MSI_DATA 0x8 +#define PCIR_MSI_DATA_64BIT 0xc +#define PCIR_MSI_MASK 0x10 +#define PCIR_MSI_PENDING 0x14 + +/* PCI-X definitions */ +#define PCIXR_COMMAND 0x96 +#define PCIXR_DEVADDR 0x98 +#define PCIXM_DEVADDR_FNUM 0x0003 /* Function Number */ +#define PCIXM_DEVADDR_DNUM 0x00F8 /* Device Number */ +#define PCIXM_DEVADDR_BNUM 0xFF00 /* Bus Number */ +#define PCIXR_STATUS 0x9A +#define PCIXM_STATUS_64BIT 0x0001 /* Active 64bit connection to device. */ +#define PCIXM_STATUS_133CAP 0x0002 /* Device is 133MHz capable */ +#define PCIXM_STATUS_SCDISC 0x0004 /* Split Completion Discarded */ +#define PCIXM_STATUS_UNEXPSC 0x0008 /* Unexpected Split Completion */ +#define PCIXM_STATUS_CMPLEXDEV 0x0010 /* Device Complexity (set == bridge) */ +#define PCIXM_STATUS_MAXMRDBC 0x0060 /* Maximum Burst Read Count */ +#define PCIXM_STATUS_MAXSPLITS 0x0380 /* Maximum Split Transactions */ +#define PCIXM_STATUS_MAXCRDS 0x1C00 /* Maximum Cumulative Read Size */ +#define PCIXM_STATUS_RCVDSCEM 0x2000 /* Received a Split Comp w/Error msg */ diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat.h b/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat.h new file mode 100644 index 0000000000..ea1c251261 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat.h @@ -0,0 +1,459 @@ +#ifndef RTEMS_COMPAT_BSD_NET_H +#define RTEMS_COMPAT_BSD_NET_H + +/* BSD -> rtems wrappers; stuff that must be defined + * prior to including most BSD headers + */ + +/* Copyright: Till Straumann , 2005; + * License: see LICENSE file. + */ + +#include +#include +#include + +#include + +/* Check for RTEMS version; true if >= ma.mi.re */ +#define ISMINVERSION(ma,mi,re) \ + ( __RTEMS_MAJOR__ > (ma) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ > (mi)) \ + || (__RTEMS_MAJOR__ == (ma) && __RTEMS_MINOR__ == (mi) && __RTEMS_REVISION__ >= (re)) \ + ) + +/* 'align' ARG is evaluated more than once */ +#define _DO_ALIGN(addr, align) (((uint32_t)(addr) + (align) - 1) & ~((align)-1)) + + +/* malloc/free are redefined :-( */ +static inline void *the_real_malloc(size_t n) +{ + return malloc(n); +} + +static inline void the_real_free(void *p) +{ + return free(p); +} + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ +#include +#include +#ifdef __i386__ +#include +#elif defined(__PPC__) +#include +#else +#error "dunno what IO ops to use on this architecture" +#endif +#include + +#include "rtemscompat_defs.h" + +#define NET_EMB(x,y,z) x ## y ## z +#define NET_EMBEMB(x,y,z) NET_EMB(x,y,z) + +#define NET_STR(arg) #arg +#define NET_STRSTR(arg) NET_STR(arg) + +#define NET_SOFTC NET_EMBEMB(,NETDRIVER_PREFIX,_softc) + +#define METHODS NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_methods) +extern struct _net_drv_tbl METHODS; + +#ifdef DEBUG_MODULAR +#define METHODSPTR NET_EMBEMB(rtems_,NETDRIVER_PREFIX,_methods_p) +extern struct _net_drv_tbl *volatile METHODSPTR; +#else +#define METHODSPTR (&METHODS) +#endif + +#if defined(__LITTLE_ENDIAN__) || (__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; } +#elif defined(__BIG_ENDIAN__) +#ifdef __PPC__ +#include + +/* Different RTEMS versions use different types + * for 32-bit (unsigned vs. unsigned long which + * always cause gcc warnings and possible alias + * violations, sigh). + */ + +#if ISMINVERSION(4,8,0) +typedef uint32_t rtemscompat_32_t; +#else +typedef unsigned rtemscompat_32_t; +#endif + +static inline uint16_t +htole16(uint16_t v) +{ +uint16_t rval; + st_le16(&rval,v); + return rval; +} + +static inline uint16_t +le16toh(uint16_t v) +{ + return ld_le16((unsigned short*)&v); +} + +static inline uint32_t +htole32(uint32_t v) +{ +rtemscompat_32_t rval; + st_le32(&rval,v); + return rval; +} + +static inline uint32_t +le32toh(uint32_t v) +{ +rtemscompat_32_t vv = v; + return ld_le32(&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 { + rtemscompat_32_t tmp[2]; + uint64_t rval; +} u; + + st_le32( &u.tmp[0], (unsigned)(v&0xffffffff) ); + st_le32( &u.tmp[1], (unsigned)((v>>32)&0xffffffff) ); + + return u.rval; +} + +#else +#error "need htoleXX() implementation for this CPU arch" +#endif + +#else +#error "Unknown CPU endianness" +#endif + + + +#ifdef __PPC__ +#define _out_byte(a,v) out_8((volatile unsigned char*)(a),(v)) +#define _inp_byte(a) in_8((volatile unsigned char*)(a)) +#ifdef NET_CHIP_LE +#define _out_word(a,v) out_le16((volatile unsigned short*)(a),(v)) +#define _out_long(a,v) out_le32((volatile unsigned *)(a),(v)) +#define _inp_word(a) in_le16((volatile unsigned short*)(a)) +#define _inp_long(a) in_le32((volatile unsigned *)(a)) +#elif defined(NET_CHIP_BE) +#define _out_word(a,v) out_be16((volatile unsigned short*)(a),(v)) +#define _out_long(a,v) out_be32((volatile unsigned *)(a),(v)) +#define _inp_word(a) in_be16((volatile unsigned short*)(a)) +#define _inp_long(a) in_be32((volatile unsigned *)(a)) +#else +#error rtemscompat_defs.h must define either NET_CHIP_LE or NET_CHIP_BE +#endif +static inline void wrle32(unsigned *a, unsigned v) +{ + asm volatile("stwbrx %1,0,%2":"=m"(*a):"r"(v),"r"(a)); +} +static inline unsigned rdle32(unsigned *a) +{ + asm volatile("lwbrx %0,0,%0":"=r"(a):"0"(a),"m"(*a)); + return (unsigned)a; +} +static inline void orle32(unsigned *a,unsigned v) { wrle32(a, rdle32(a) | v); } +static inline void anle32(unsigned *a,unsigned v) { wrle32(a, rdle32(a) & v); } + +static inline void wrle16(unsigned short *a, unsigned short v) +{ + asm volatile("sthbrx %1,0,%2":"=m"(*a):"r"(v),"r"(a)); +} +static inline unsigned short rdle16(unsigned short *a) +{ + asm volatile("lhbrx %0,0,%0":"=r"(a):"0"(a),"m"(*a)); + return (unsigned short)(unsigned)a; +} +static inline void orle16(unsigned short *a,unsigned short v) { wrle16(a, rdle16(a) | v); } +static inline void anle16(unsigned short *a,unsigned short v) { wrle16(a, rdle16(a) & v); } +#endif + +#ifdef __i386__ +#ifdef NET_CHIP_BE +#error dunno how to output BE data +#endif + +static inline void wrle32(volatile unsigned *p, unsigned v) { *p = v; } +static inline void orle32(volatile unsigned *p, unsigned v) { *p |= v; } +static inline void anle32(volatile unsigned *p, unsigned v) { *p &= v; } +static inline unsigned rdle32(volatile unsigned *p) { return *p; } + +static inline void wrle16(volatile unsigned short *p, unsigned short v) { *p = v; } +static inline void orle16(volatile unsigned short *p, unsigned short v) { *p |= v; } +static inline void anle16(volatile unsigned short *p, unsigned short v) { *p &= v; } +static inline unsigned short rdle16(volatile unsigned short *p) { return *p; } + +#ifdef NET_CHIP_MEM_IO + +#ifdef __i386__ +static inline void _out_byte(unsigned a, unsigned char v) { *(volatile unsigned char*)a = v; } +static inline unsigned char _inp_byte(unsigned a) { return *(volatile unsigned char*)a; } +#ifdef NET_CHIP_LE +static inline void _out_word(unsigned a, unsigned short v) { *(volatile unsigned short*)a = v; } +static inline unsigned short _inp_word(unsigned a) { return *(volatile unsigned short*)a; } +static inline void _out_long(unsigned a, unsigned v) { *(volatile unsigned *)a = v; } +static inline unsigned _inp_long(unsigned a) { return *(volatile unsigned *)a; } +#elif defined(NET_CHIP_BE) +#error "BE memory IO not implemented for i386 yet" +#else +#error rtemscompat_defs.h must define either NET_CHIP_LE or NET_CHIP_BE +#endif +#else + +#error "Memory IO not implemented for this CPU architecture yet" + +#endif +#elif defined(NET_CHIP_PORT_IO) +#define _out_byte(addr,val) outport_byte((addr),(val)) +#define _out_word(addr,val) outport_word((addr),(val)) +#define _out_long(addr,val) outport_long((addr),(val)) + +static inline u_int8_t _inp_byte(volatile unsigned char *a) +{ +register u_int8_t rval; + inport_byte((unsigned short)(unsigned)a,rval); + return rval; +} +static inline u_int16_t _inp_word(volatile unsigned short *a) +{ +register u_int16_t rval; + inport_word((unsigned short)(unsigned)a,rval); + return rval; +} +static inline u_int32_t _inp_long(volatile unsigned *a) +{ +register u_int32_t rval; + inport_long((unsigned short)(unsigned)a,rval); + return rval; +} +#else +#error either NET_CHIP_MEM_IO or NET_CHIP_PORT_IO must be defined +#endif +#endif + +#ifndef __FBSDID +#define __FBSDID(arg) +#endif + +#define _KERNEL + +#define device_printf(device,format,args...) printk(format,## args) + +static inline u_int8_t bus_space_do_read_1(u_long handle, unsigned reg) +{ + return _inp_byte((volatile unsigned char*)((handle)+(reg))); +} + +static inline u_int16_t bus_space_do_read_2(u_long handle, unsigned reg) +{ + return _inp_word((volatile unsigned short*)((handle)+(reg))); +} + +static inline u_int32_t bus_space_do_read_4(u_long handle, unsigned reg) +{ + return _inp_long((volatile unsigned *)((handle)+(reg))); +} + +#define bus_space_read_1(tag,handle,reg) bus_space_do_read_1((handle),(reg)) +#define bus_space_read_2(tag,handle,reg) bus_space_do_read_2((handle),(reg)) +#define bus_space_read_4(tag,handle,reg) bus_space_do_read_4((handle),(reg)) + +static inline void bus_space_do_write_multi_1(u_long handle, unsigned reg, unsigned char *addr, int cnt) +{ + int i; for (i=0; i 10000 ) \ + rtems_task_wake_after((((n)*net_driver_ticks_per_sec)/1000000) + 1); \ + else \ + rtems_bsp_delay(n); \ + } while (0) +#else +#warning "Have no good usec delay implementation" +#define DELAY(n) do { \ + rtems_task_wake_after((((n)*net_driver_ticks_per_sec)/1000000) + 1); \ + } while (0) +#endif + + +#define IRQ_LOCKED(code) \ + do { unsigned long _xtre_irq_flags; \ + rtems_interrupt_disable(_xtre_irq_flags); \ + do { code } while(0); \ + rtems_interrupt_enable(_xtre_irq_flags); \ + } while (0) + +typedef struct _netdev_t *device_t; +typedef void (driver_intr_t)(void *); + +#define if_xname if_name + +/* need to replace those with LOCAL2PCI() and make sure the bus handle is initialized + * (on most BSPs we get away with PCI_DRAM_OFFSET [no bus handle needed at all] + */ +#ifndef PCI_DRAM_OFFSET +#define PCI_DRAM_OFFSET 0 +#endif + +#ifndef PCI_MEM_BASE +#define PCI_MEM_BASE 0 +#endif + +#define kvtop(a) ((unsigned long)(a) + PCI_DRAM_OFFSET) +#define vtophys(a) ((unsigned long)(a) + PCI_DRAM_OFFSET) + +#define PCI2LOCAL(a,bus) ((unsigned long)(a) + PCI_MEM_BASE) + +#ifdef PCI0_IO_BASE /* special mvme5500 hack :-( */ +#define PCI_IO_2LOCAL(a,bus) ((unsigned long)(a) + PCI0_IO_BASE) +#elif defined(PCI_IO_BASE) +#define PCI_IO_2LOCAL(a,bus) ((unsigned long)(a) + PCI_IO_BASE) +#elif defined(_IO_BASE) +#define PCI_IO_2LOCAL(a,bus) ((unsigned long)(a) + _IO_BASE) +#else +#warning "Unable to determine base address of PCI IO space; using ZERO" +#define PCI_IO_2LOCAL(a,bus) ((unsigned long)(a)) +#endif + +#define if_printf(if,fmt,args...) printf("%s:"fmt,(if)->if_name,args) + +#ifndef BUS_PROBE_DEFAULT +#define BUS_PROBE_DEFAULT 0 +#endif + +static inline 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; +} + +static inline void +contigfree(void *ptr, size_t size, int type) +{ + rtems_bsdnet_free( *(void**)((unsigned)ptr + size), type); +} + +/* callout stuff */ +#define callout_init(args...) do {} while (0); +#define callout_reset(args...) do {} while (0); +#define callout_stop(args...) do {} while (0); + +#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 DO_ETHER_INPUT_SKIPPING_ETHER_HEADER(ifp,m) \ + { struct ether_header *eh; \ + eh = mtod(m, struct ether_header*); \ + m->m_data += sizeof(struct ether_header); \ + m->m_len -= sizeof(struct ether_header); \ + m->m_pkthdr.len -= sizeof(struct ether_header); \ + m->m_pkthdr.rcvif = ifp; \ + ether_input(ifp, eh, m); \ + } while (0) + + +#ifndef __KERNEL_RCSID +#define __KERNEL_RCSID(arg...) +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat1.h b/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat1.h new file mode 100644 index 0000000000..0795fd30d0 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat1.h @@ -0,0 +1,219 @@ +#ifndef RTEMS_COMPAT1_BSD_NET_H +#define RTEMS_COMPAT1_BSD_NET_H + +/* BSD -> RTEMS conversion wrappers; stuff that must be defined + * after most BSD headers are included. + */ + +#include +#include + +/* Copyright: Till Straumann , 2005; + * License: see LICENSE file. + */ + +typedef struct _netdev_t { + struct NET_SOFTC d_softc; /* MUST BE FIRST FIELD */ + char *d_name; + char *d_desc; + int d_unit; + int flags; + /* pointer to ifconfig only valid during excution of + * the n_attach/n_detach methods (see below) + */ + struct rtems_bsdnet_ifconfig *d_ifconfig; +} netdev_t; + +#define THEDEVS NET_EMBEMB(the_,NETDRIVER_PREFIX,_devs) +#define NETDEV_DECL netdev_t THEDEVS[NETDRIVER_SLOTS] + +extern NETDEV_DECL; + +typedef struct _net_drv_tbl { + int (*n_probe)(device_t); + int (*n_attach)(device_t); + int (*n_detach)(device_t); + void (*n_intr)(void *); +} net_drv_tbl_t; + +static inline netdev_t * +net_dev_get(struct rtems_bsdnet_ifconfig *config) +{ + int unitNo; + char *unitName; + + unitNo = rtems_bsdnet_parse_driver_name(config, &unitName); + if ( unitNo < 0 ) + return 0; + + if ( unitNo <=0 || unitNo > NETDRIVER_SLOTS ) { + device_printf(dev, "Bad "NETDRIVER" unit number.\n"); + return 0; + } + + if ( THEDEVS[unitNo-1].d_unit && THEDEVS[unitNo-1].d_unit != unitNo ) { + device_printf(dev, "Unit # mismatch !!??\n"); + return 0; + } + + THEDEVS[unitNo-1].d_unit = unitNo; + THEDEVS[unitNo-1].d_name = unitName; + THEDEVS[unitNo-1].d_ifconfig = config; + + return &THEDEVS[unitNo - 1]; +} + +/* kludge; that's why softc needs to be first */ +static inline netdev_t * +softc_get_device(struct NET_SOFTC *sc) +{ + return (netdev_t *)sc; +} + +static inline struct NET_SOFTC * +device_get_softc(netdev_t *dev) +{ return &dev->d_softc; } + +static inline int +device_get_unit(netdev_t *dev) +{ return dev->d_unit; } + +static inline char * +device_get_name(netdev_t *dev) +{ return dev->d_name; } + +static inline void +if_initname(struct ifnet *ifp, char *name, int unit) +{ + ifp->if_name = name; + ifp->if_unit = unit; +} + +static inline void +device_set_desc(netdev_t *dev, char *str) +{ + dev->d_desc = str; +} + +static inline void +device_set_desc_copy(netdev_t *dev, char *str) +{ + dev->d_desc = strdup(str); +} + + +static inline int +device_is_attached(netdev_t *dev) +{ + return dev->d_softc.arpcom.ac_if.if_addrlist && dev->d_softc.arpcom.ac_if.if_init; +} + +#ifdef NETDRIVER_PCI +#include NETDRIVER_PCI +#include + +static inline unsigned +pci_read_config(device_t dev, unsigned addr, unsigned width) +{ +rtemscompat_32_t d; +unsigned short s; +unsigned char b; +struct NET_SOFTC *sc = device_get_softc(dev); + switch (width) { + case 1: pci_read_config_byte(sc->b, sc->d, sc->f, addr, &b); + return b; + case 2: pci_read_config_word(sc->b, sc->d, sc->f, addr, &s); + return s; + case 4: pci_read_config_dword(sc->b, sc->d, sc->f, addr, &d); + return d; + default: + break; + } + return 0xdeadbeef; +} + +static inline void +pci_write_config(device_t dev, unsigned addr, unsigned width, unsigned val) +{ +struct NET_SOFTC *sc = device_get_softc(dev); + switch (width) { + case 1: pci_write_config_byte(sc->b, sc->d, sc->f, addr, val); + return ; + case 2: pci_write_config_word(sc->b, sc->d, sc->f, addr, val); + return ; + case 4: pci_write_config_dword(sc->b, sc->d, sc->f, addr, val); + return ; + default: + break; + } +} + + +static inline unsigned short +pci_get_vendor(device_t dev) +{ + return pci_read_config(dev, PCIR_VENDOR, 2); +} + +static inline unsigned short +pci_get_device(device_t dev) +{ + return pci_read_config(dev, PCIR_DEVICE, 2); +} + +static inline unsigned short +pci_get_subvendor(device_t dev) +{ + return pci_read_config(dev, PCIR_SUBVEND_0, 2); +} + +static inline unsigned short +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, + PCIR_COMMAND, + 2, + pci_read_config(dev, PCIR_COMMAND, 2) + | PCIM_CMD_BUSMASTEREN); +} + +#define mtx_init(a,b,c,d) do {} while(0) +#define mtx_initialized(ma) (1) +#define mtx_destroy(ma) do {} while(0) +#define mtx_lock(a) do {} while(0) +#define mtx_unlock(a) do {} while(0) +#define mtx_assert(a,b) do {} while(0) + +#define callout_handle_init(x) do {} while (0) +#define untimeout(a...) do {} while (0) + +#if !ISMINVERSION(4,6,99) +#define pci_bus_count BusCountPCI +#endif + +#endif + +/* 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... + */ +static inline 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; +} + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat_defs.h.template b/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat_defs.h.template new file mode 100644 index 0000000000..5dc8d1efff --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/porting/rtemscompat_defs.h.template @@ -0,0 +1,97 @@ +#ifndef RTEMS_COMPAT_DEFS_H +#define RTEMS_COMPAT_DEFS_H + +/* Symbol definitions for a particular driver */ + +/* Copyright: Till Straumann , 2005; + * License: see LICENSE file. + */ + +/* Number of device instances the driver should support + * - may be limited to 1 depending on IRQ API + * (braindamaged PC586 and powerpc) + */ +#define NETDRIVER_SLOTS 1 +/* String name to print with error messages */ +#define NETDRIVER "PCN" +/* Name snippet used to make global symbols unique to this driver */ +#define NETDRIVER_PREFIX pcn + +/* Define according to endianness of the *ethernet*chip* + * (not the CPU - most probably are LE) + * This must be either NET_CHIP_LE or NET_CHIP_BE + */ + +#define NET_CHIP_LE +#undef NET_CHIP_BE + +/* Define either NET_CHIP_MEM_IO or NET_CHIP_PORT_IO, + * depending whether the CPU sees it in memory address space + * or (e.g. x86) uses special I/O instructions. + */ +#define NET_CHIP_MEM_IO +#undef NET_CHIP_PORT_IO + +/* The name of the hijacked 'bus handle' field in the softc + * structure. We use this field to store the chip's base address. + */ +#define NET_SOFTC_BHANDLE_FIELD pcn_bhandle + +/* define the names of the 'if_XXXreg.h' and 'if_XXXvar.h' headers + * (only if present, i.e., if the BSDNET driver has no respective + * header, leave this undefined). + * + */ +#undef IF_REG_HEADER +#undef IF_VAR_HEADER + +/* define if a pci device */ +#define NETDRIVER_PCI + +/* Macros to disable and enable interrupts, respectively. + * The 'disable' macro is expanded in the ISR, the 'enable' + * macro is expanded in the driver task. + * The global network semaphore usually provides mutex + * protection of the device registers. + * Special care must be taken when coding the 'disable' macro, + * however to MAKE SURE THERE ARE NO OTHER SIDE EFFECTS such + * as: + * - macro must not clear any status flags + * - macro must save/restore any context information + * (e.g., a address register pointer or a bank switch register) + * + * ARGUMENT: the macro arg is a pointer to the driver's 'softc' structure + */ + +/* Here EXAMPLES for the pcnet chip which addresses registers indirectly + * through a 'address-pointer' (RAP) and 'data-port' (RDP) register pair: +#define NET_DISABLE_IRQS(sc) do { \ + unsigned rap = CSR_READ_4((sc),PCN_IO32_RAP); \ + unsigned val; \ + CSR_WRITE_4((sc),PCN_IO32_RAP,PCN_CSR_CSR); \ + val = CSR_READ_4((sc),PCN_IO32_RDP); \ + CSR_WRITE_4((sc), PCN_IO32_RDP, val & ~(CSR0_INT_STATUS_MASK | PCN_CSR_INTEN)); \ + CSR_WRITE_4((sc), PCN_IO32_RAP, rap); \ + } while (0) + +#define NET_ENABLE_IRQS(sc) do { \ + unsigned flags,val; \ + rtems_interrupt_disable(flags); \ + CSR_WRITE_4((sc),PCN_IO32_RAP,PCN_CSR_CSR); \ + val = CSR_READ_4((sc),PCN_IO32_RDP); \ + CSR_WRITE_4((sc), PCN_IO32_RDP, (val & ~CSR0_INT_STATUS_MASK) | PCN_CSR_INTEN); \ + rtems_interrupt_enable(flags); \ + } while (0) +*/ + +/* Driver may provide a macro/function to copy the hardware address + * from the device into 'softc.arpcom'. + * If this is undefined, the driver must to the copy itself. + * Preferrably, it should check soft.arpcom.ac_enaddr for all + * zeros and leave it alone if it is nonzero, i.e., write it + * to the hardware. +#define NET_READ_MAC_ADDR(sc) + */ + +#define KASSERT(a...) do {} while (0) +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_attach.c b/c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_attach.c new file mode 100644 index 0000000000..8dae0cae3a --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_attach.c @@ -0,0 +1,468 @@ +/* $Id$ */ + +/* BSP specific wrapper for rtems_bsdnet_attach(). This wrapper + * dispatches to the correct driver attach routine depending on + * the board type, boot parameters, link status etc. + * + * Also, it performs board-specific setup of driver parameters + * (such as ethernet address, base addresses and the like) + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#define IS_6100() (MVME6100 == BSP_getBoardType()) +#define IS_5500() (MVME5500 == BSP_getBoardType()) + +static int bsp_gfe_attach(struct rtems_bsdnet_ifconfig *, int); +static int mvme5500_em_attach (struct rtems_bsdnet_ifconfig *, int); +static int mvme5500_em_find_onboard_unit(void); + +char BSP_auto_network_driver_name[20] = {0,}; + +static BSP_NetIFDescRec mvme6100_netifs[] = { + { + name: "mve1", + description: "MV64360 built-in 10/100/1000 Ethernet 1", + attach_fn: rtems_mve_attach + }, + { + name: "mve2", + description: "MV64360 built-in 10/100/1000 Ethernet 2", + attach_fn: rtems_mve_attach + }, + { + name: 0, + } +}; + +static BSP_NetIFDescRec mvme5500_netifs[] = { + { + name: "em1", + description: "Intel 82544 on-board 10/100/1000 Ethernet (port 1)", + attach_fn: mvme5500_em_attach, + }, + { + name: "gfe1", + description: "GT64260 built-in 10/100 Ethernet (port 2)", + attach_fn: bsp_gfe_attach, + }, + { + name: 0, + } +}; + + +/* wrap the 'gfe' and 'em' attach functions. + * RATIONALE: 'rtems_gfe_attach()' and 'rtems_em_attach()' are + * *chip* specific functions. However, they require + * some *board* specific parameter setup prior to + * being attached which is what these wrappers do... + */ + +static unsigned em_info[]; + +static int scan4irqLine(int bus, int dev, int fn, void *uarg) +{ +unsigned char byte; +unsigned short word; +int i; + + /* count the number of 82544 we find */ + pci_read_config_word(bus, dev, fn, PCI_VENDOR_ID, &word); + if ( 0x8086 != word ) + return 0; + + pci_read_config_word(bus, dev, fn, PCI_DEVICE_ID, &word); + for ( i = 0; em_info[i]; i++ ) { + if ( em_info[i] == word ) + break; + } + + if ( !em_info[i] ) + return 0; + + /* found a candidate; bump unit number */ + (*(int *)uarg)++; + + pci_read_config_byte(bus, dev, fn, PCI_INTERRUPT_LINE, &byte); + + /* On the mvme5500 the 82544 is hooked to GPP IRQ 20 */ + + return ( BSP_IRQ_GPP_0 + 20 == byte ) ? 1 : 0; +} + +/* Setup only once */ +static void +onboard_em_setup_once(void) +{ +static char done = 0; + + /* If scanning didn't do anything, passing 0 will setup all interfaces */ + if ( !done + && rtems_em_pci_setup( mvme5500_em_find_onboard_unit() > 0 ) ) { + done=1; + } +} + +static void +onboard_gfe_setup_once(void) +{ +static char done = 0; + + /* must setup HW address -- note that the label on the + * board indicates that the gfe is the second interface + * but motLoad etc. interprets the order actually + * the other way round... + */ + if ( !done + && rtems_gfe_setup( 1, BSP_enetAddr0, BSP_MV64x60_BASE ) > 0 ) { + done=1; + } +} + + +/* Find the unit number of the on-board 82544 (even if there is another one + * plugged into PMC... + * + * RETURNS: unit # (>0) or zero if nothing at all was found. (New board rev. + * with the 82544 hooked to a different IRQ line, new PCI device ID, + * ...) + */ +static int +mvme5500_em_find_onboard_unit(void) +{ +int unit = 0; +void *h; + /* Determine the on-board 'em' adapter; we know it's interrupt line :-) */ + for ( h=0; (h=BSP_pciScan(h, scan4irqLine, &unit)); ) + /* nothing else to do */; + return h ? unit : 0; +} + +static int +mvme5500_em_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) +{ + if ( attaching ) { + onboard_em_setup_once(); + + /* Make sure there is no conflict in MAC address -- see below + * why we 'swap' the addresses. (We actually don't but swap the + * order of the interfaces so they match the label.) + */ + if ( !ifcfg->hardware_address ) + ifcfg->hardware_address = BSP_enetAddr1; + } + + return rtems_em_attach(ifcfg, attaching); +} + +static int +bsp_gfe_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) +{ + if ( attaching ) { + onboard_gfe_setup_once(); + } + return rtems_gfe_attach(ifcfg, attaching); +} + +BSP_NetIFDesc +BSP_availableNetIFs(void) +{ + if ( IS_6100() ) + return mvme6100_netifs; + if ( IS_5500() ) + return mvme5500_netifs; + + fprintf(stderr,"BSP_availableNetIFs() -- productIdent not set? unable to identify board type\n"); + + return 0; +} + +typedef int (*read_op_t)(int,unsigned); +typedef int (*write_op_t)(int,unsigned,unsigned); + +struct poll_job { + read_op_t rdop; + write_op_t wrop; + int unit; +}; + +static int +check_phys(struct poll_job *job) +{ +struct poll_job *j; +int rval = -2; + for ( j=job; j->rdop; j++ ) { + unsigned w; + w = j->rdop(j->unit, 1/*status*/); + if ( 0x04 & w ) /* active link */ + return j-job; + if ( !(0x20 & w) ) /* aneg not done */ + rval = -1; + } + return rval; +} + +/* check a number of phys + * RETURNS: -1 if at least one is still busy + * -2 if all are terminated but no link is found + * >=0 index of first IF with a live link + */ +static int +poll_phys(struct poll_job *job) +{ +int rval; +struct poll_job *j = job; +int retry = 4; + + /* see if we already have a link */ + if ( (rval=check_phys(job)) < 0 ) { + /* no - start negotiation */ + for ( j = job; j->rdop; j++ ) { + j->wrop(j->unit, 0/* ctrl */, 0x1200 /* start aneg */); + } + + do { + sleep(1); + } while ( -1 == (rval = check_phys(job)) && retry-- ); + } + + return rval; +} + + +/* note that detaching is not supported by the current version of the BSD stack */ +int +BSP_auto_enet_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) +{ +int i = -1; +BSP_NetIFDesc d = BSP_availableNetIFs(); +struct poll_job job[3]; + + if ( !d ) + return -1; + + /* If they pass a name, find the attach fn */ + if ( ifcfg->name && RTEMS_BSP_NETWORK_DRIVER_NAME != ifcfg->name && attaching ) { + for (i = 0; d[i].name; i++ ) { + if ( !strcmp(d[i].name, ifcfg->name) ) { + ifcfg->attach = d[i].attach_fn; + break; + } + } + if ( !d[i].name ) { + fprintf(stderr,"WARNING: have no '%s' interface - using default\n", ifcfg->name); + ifcfg->name = 0; + } + } + + if ( !ifcfg->name || ifcfg->name == RTEMS_BSP_NETWORK_DRIVER_NAME ) { + /* automatically choose and attach an interface */ + if ( RTEMS_BSP_NETWORK_DRIVER_NAME[0] ) { + fprintf(stderr, + "Configuration error: 'auto' network if already chosen (%s)\n", + RTEMS_BSP_NETWORK_DRIVER_NAME); + return -1; + } + if ( IS_6100() ) { +#define ops rtems_mve_early_link_check_ops + assert(ops.num_slots >= 2); + ops.init(0); + ops.init(1); + job[0].rdop = ops.read_phy; + job[0].wrop = ops.write_phy; + job[0].unit = 0; + job[1].rdop = ops.read_phy; + job[1].wrop = ops.write_phy; + job[1].unit = 1; +#undef ops + } else if ( IS_5500() ) { +#define opsgfe rtems_gfe_early_link_check_ops +#define opsem rtems_em_early_link_check_ops + assert(opsgfe.num_slots >= 1); + onboard_gfe_setup_once(); + opsgfe.init(0); + assert(opsem.num_slots >= 1); + onboard_em_setup_once(); + opsem.init(0); + job[0].rdop = opsem.read_phy; + job[0].wrop = opsem.write_phy; + job[0].unit = 0; + job[1].rdop = opsgfe.read_phy; + job[1].wrop = opsgfe.write_phy; + job[1].unit = 0; +#undef opsgfe +#undef opsem + } + job[2].rdop = 0; /* tag end */ + i = poll_phys(job); + if ( i >= 0 ) { + printf("L"); + } else { + i = 0; + printf("No l"); + } + printf("ink detected; attaching %s\n",d[i].name); + + /* set attach function and IF name */ + ifcfg->attach = d[i].attach_fn; + ifcfg->name = RTEMS_BSP_NETWORK_DRIVER_NAME; + strcpy(RTEMS_BSP_NETWORK_DRIVER_NAME, d[i].name); + } + return ifcfg->attach(ifcfg, attaching); +} + +/* from 'em' */ + +/* PCI Device IDs */ +#define E1000_DEV_ID_82542 0x1000 +#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_82541ER_LOM 0x1014 +#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_82541EI 0x1013 +#define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER 0x1078 +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#define E1000_DEV_ID_82541GI_LF 0x107C +#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_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A +#define E1000_DEV_ID_82573E 0x108B +#define E1000_DEV_ID_82573E_IAMT 0x108C + +#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 + +static unsigned em_info[] = +{ + /* Intel(R) PRO/1000 Network Connection */ + E1000_DEV_ID_82540EM, + E1000_DEV_ID_82540EM_LOM, + E1000_DEV_ID_82540EP, + E1000_DEV_ID_82540EP_LOM, + E1000_DEV_ID_82540EP_LP, + + E1000_DEV_ID_82541EI, + E1000_DEV_ID_82541ER, + E1000_DEV_ID_82541ER_LOM, + E1000_DEV_ID_82541EI_MOBILE, + E1000_DEV_ID_82541GI, + E1000_DEV_ID_82541GI_LF, + E1000_DEV_ID_82541GI_MOBILE, + + E1000_DEV_ID_82542, + + E1000_DEV_ID_82543GC_FIBER, + E1000_DEV_ID_82543GC_COPPER, + + E1000_DEV_ID_82544EI_COPPER, + E1000_DEV_ID_82544EI_FIBER, + E1000_DEV_ID_82544GC_COPPER, + E1000_DEV_ID_82544GC_LOM, + + E1000_DEV_ID_82545EM_COPPER, + E1000_DEV_ID_82545EM_FIBER, + E1000_DEV_ID_82545GM_COPPER, + E1000_DEV_ID_82545GM_FIBER, + E1000_DEV_ID_82545GM_SERDES, + + E1000_DEV_ID_82546EB_COPPER, + E1000_DEV_ID_82546EB_FIBER, + E1000_DEV_ID_82546EB_QUAD_COPPER, + E1000_DEV_ID_82546GB_COPPER, + E1000_DEV_ID_82546GB_FIBER, + E1000_DEV_ID_82546GB_SERDES, + E1000_DEV_ID_82546GB_PCIE, + E1000_DEV_ID_82546GB_QUAD_COPPER, + + E1000_DEV_ID_82547EI, + E1000_DEV_ID_82547EI_MOBILE, + E1000_DEV_ID_82547GI, + + E1000_DEV_ID_82573E, + E1000_DEV_ID_82573E_IAMT, + + /* required last entry */ + 0, +}; diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_bsdnet_attach.h b/c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_bsdnet_attach.h new file mode 100644 index 0000000000..fa7d16b5b5 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/support/bsp_bsdnet_attach.h @@ -0,0 +1,80 @@ +/* $Id$ */ +#ifndef BSP_BSDNET_ATTACH_INFO_H +#define BSP_BSDNET_ATTACH_INFO_H + +/* Author: Till Straumann, 2005; see ../../LICENSE */ + +/* Rationale: traditionally, BSPs only supported a single networking interface + * the BSP defined RTEMS_NETWORK_DRIVER_NAME & friends macros + * for applications to use. + * If more than one interface is present, this simple approach is + * not enough. + * Hence, this BSP exports a routine declaring all available interfaces + * so the application can make a choice. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* Fwd. decl just in case */ +struct rtems_bsdnet_ifconfig; + +typedef struct { + /* name of the interface */ + const char *name; + /* optional description (to be used by chooser 'help' function etc.) */ + const char *description; + /* driver 'attach' function */ + int (*attach_fn)(struct rtems_bsdnet_ifconfig*, int); +} BSP_NetIFDescRec, *BSP_NetIFDesc; + +/* Return a pointer to the (static) list of network interface descriptions + * of this board. + * + * NOTES: A NULL value is returned if e.g., the board type cannot be determined + * or for other reasons. + * The 'description' field is optional, i.e., may be NULL. + * The list is terminated by an element with a NULL name field. + * The interfaces are listed in the order they are labelled. + */ + +BSP_NetIFDesc +BSP_availableNetIFs(); + +/* Define this macro so applications can conditionally compile this API */ +#define BSP_HAS_MULTIPLE_NETIFS(x) BSP_availableNetIFs() + +/* Legacy macro; applications should use BSP_Available_NetIfs() to choose + * an interface and attach function. + */ +extern char BSP_auto_network_driver_name[20]; +#define RTEMS_BSP_NETWORK_DRIVER_NAME BSP_auto_network_driver_name + +#define RTEMS_BSP_NETWORK_DRIVER_ATTACH BSP_auto_enet_attach + +/* This routine checks the name field passed in the 'ifconfig'. + * If the name is NULL or points to the BSP_auto_network_driver_name + * array, the routine checks all interfaces for an active link and + * attaches the first alive one. + * It also updates 'ifconfig' to reflect the chosen interface's name + * and attach function. + * + * If another name is passed in, the routine scans + * the available interfaces for that name and uses it, if found. + * Eventually, a default interface is chosen (provided that + * the board type is successfully detected). + * + * Note that only ONE interface chained into rtems_bsdnet_config + * may use the "auto" name. + * + */ + +int +BSP_auto_enet_attach(struct rtems_bsdnet_ifconfig *ifconfig, int attaching); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/support/early_enet_link_status.h b/c/src/lib/libbsp/powerpc/beatnik/network/support/early_enet_link_status.h new file mode 100644 index 0000000000..7a6543c281 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/support/early_enet_link_status.h @@ -0,0 +1,31 @@ +/*$Id$*/ +#ifndef BSP_EARLY_ENET_LINK_STATUS_H +#define BSP_EARLY_ENET_LINK_STATUS_H + +/* Determine link status of ethernet device before network is initialized */ + +/* T. Straumann, 2005; see ../../LICENSE */ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct { + int (*init)(int idx); /* perform enough initialization to access (default) phy */ + int (*read_phy)(int idx, unsigned reg); + int (*write_phy)(int idx, unsigned reg, unsigned val); + const char *name; /* driver name */ + unsigned char num_slots; /* max number of supported devices */ + unsigned char initialized; /* must be initialized to 0; */ +} rtems_bsdnet_early_link_check_ops; + +int +BSP_early_check_link_status(int unit, rtems_bsdnet_early_link_check_ops *ops); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/network/support/early_link_status.c b/c/src/lib/libbsp/powerpc/beatnik/network/support/early_link_status.c new file mode 100644 index 0000000000..be76cdeaa0 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/network/support/early_link_status.c @@ -0,0 +1,41 @@ +/* $Id$ */ +#include +#include +#include + +/* T. Straumann, 2005; see ../../LICENSE */ + +static const char *ename = ": rtems_em_early_check_link_status() - "; + +int +BSP_early_check_link_status(int unit, rtems_bsdnet_early_link_check_ops *ops) +{ +int status; + + unit--; + if ( unit < 0 || unit >= ops->num_slots ) { + printk("%s%sinvalid unit # %i (not in %i..%i)\n", + ops->name, ename, unit+1, 1, ops->num_slots); + return -1; + } + + if ( !ops->initialized ) { + if ( ops->init(unit) ) { + printk("%s%sFAILURE to init hardware\n",ops->name, ename); + return -1; + } + /* Start autoneg */ + if ( ops->write_phy(unit, 0, 0x1200) ) { + printk("%s%sFAILURE to start autonegotiation\n",ops->name, ename); + return -1; + } + /* Dont wait here; the caller can do this on various interfaces + * and wait herself. + */ + ops->initialized = 1; + } + if ( (status = ops->read_phy(unit, 1)) < 0 ) { + printk("%s%sFAILURE to read phy status\n", ops->name, ename); + } + return status; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/pci/gt_pci_init.c b/c/src/lib/libbsp/powerpc/beatnik/pci/gt_pci_init.c new file mode 100644 index 0000000000..7acb240d72 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/pci/gt_pci_init.c @@ -0,0 +1,274 @@ +/* $Id$ */ + +/* PCI configuration space access */ + +/* + * Acknowledgements: + * Valuable information was obtained from the following drivers + * netbsd: (C) Allegro Networks Inc; Wasabi Systems Inc. + * linux: (C) MontaVista, Software, Inc; Chris Zankel, Mark A. Greer. + * rtems: (C) Brookhaven National Laboratory; K. Feng + */ + +/* + * Original file header of libbsp/shared/pci.c where this file is based upon. + * + * Copyright (C) 1999 valette@crf.canon.fr + * + * This code is heavily inspired by the public specification of STREAM V2 + * that can be found at : + * + * by following + * the STREAM API Specification Document link. + * + * The license and distribution terms for this file may be + * found in found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + * + * Till Straumann, , 1/2002 + * - separated bridge detection code out of this file + */ + +#include +#include +#include +#include +#include +#include + +/* set to max so we get early access to hose 0 */ +unsigned BSP_pci_hose1_bus_base = (unsigned)-1; + +#define MV64x60_PCI0_CONFIG_ADDR (BSP_MV64x60_BASE + 0xcf8) +#define MV64x60_PCI0_CONFIG_DATA (BSP_MV64x60_BASE + 0xcfc) +#define MV64x60_PCI1_CONFIG_ADDR (BSP_MV64x60_BASE + 0xc78) +#define MV64x60_PCI1_CONFIG_DATA (BSP_MV64x60_BASE + 0xc7c) + +#define PCI_BUS2HOSE(bus) (bus 255 ) { + BSP_panic("Too many PCI busses in the system"); + } + /* readjust total number */ + ucMaxPCIBus+=BSP_pci_hose1_bus_base; + + /* install new access functions that can hide the hoses */ + BSP_pci_configuration.pci_config_addr = (volatile unsigned char *)0xdeadbeef; + BSP_pci_configuration.pci_config_data = (volatile unsigned char *)0xdeadbeef; + BSP_pci_configuration.pci_functions = &pci_hosed_indirect_functions; +} + +#define PCI_ERR_BITS 0xf900 +#define PCI_STATUS_OK(x) (!((x)&PCI_ERR_BITS)) + +/* For now, just clear errors in the PCI status reg. + * + * Returns: (for diagnostic purposes) + * original settings (i.e. before applying the clearing + * sequence) + * (pci_status(hose_1)&0xff00) | ((pci_status(hose_2)>>8)&0xff) + */ + +static unsigned long clear_hose_errors(int bus, int quiet) +{ +unsigned long rval; +uint16_t pcistat; +int count; +int hose = PCI_BUS2HOSE(bus); + + /* read error status for info return */ + pci_read_config_word(bus,0,0,PCI_STATUS,&pcistat); + rval = pcistat; + + count=10; + do { + /* clear error reporting registers */ + + /* clear PCI status register */ + pci_write_config_word(bus,0,0,PCI_STATUS, PCI_ERR_BITS); + + /* read new status */ + pci_read_config_word(bus,0,0,PCI_STATUS, &pcistat); + + } while ( ! PCI_STATUS_OK(pcistat) && count-- ); + + if ( !PCI_STATUS_OK(rval) && !quiet) { + printk("Cleared PCI errors at discovery (hose %i): pci_stat was 0x%04x\n", hose, rval); + } + if ( !PCI_STATUS_OK(pcistat) ) { + printk("Unable to clear PCI errors at discovery (hose %i) still 0x%04x after 10 attempts\n",hose, pcistat); + } + return rval; +} + +unsigned short +(*_BSP_clear_vmebridge_errors)(int) = 0; + +unsigned long +_BSP_clear_hostbridge_errors(int enableMCP, int quiet) +{ +unsigned long rval; + + /* MCP is not connected */ + if ( enableMCP ) + return -1; + + rval = (clear_hose_errors(0, quiet) & PCI_ERR_BITS)>>8; + rval |= clear_hose_errors(BSP_pci_hose1_bus_base, quiet) & PCI_ERR_BITS; + + /* Tsi148 doesn't propagate VME bus errors to PCI status reg. */ + if ( _BSP_clear_vmebridge_errors ) + rval |= _BSP_clear_vmebridge_errors(quiet)<<16; + + return rval; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/pci/motload_fixup.c b/c/src/lib/libbsp/powerpc/beatnik/pci/motload_fixup.c new file mode 100644 index 0000000000..93746e6d0e --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/pci/motload_fixup.c @@ -0,0 +1,182 @@ +/* $Id$ */ + +/* remap the zero-based PCI IO spaces of both hoses to a single + * address space + * + * This must be called AFTER to BSP_pci_initialize() + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#include +#include +#include +#include +#include +#include +#include +#include "pci_io_remap.h" + +static int +fixup_irq_line(int bus, int slot, int fun, void *uarg) +{ +unsigned char line; + pci_read_config_byte( bus, slot, fun, PCI_INTERRUPT_LINE, &line); + if ( line < BSP_IRQ_GPP_0 ) { + pci_write_config_byte( bus, slot, fun, PCI_INTERRUPT_LINE, line + BSP_IRQ_GPP_0 ); + } + + return 0; +} + +void BSP_motload_pci_fixup(void) +{ +uint32_t b0,b1,r0,r1,lim,dis; + + /* MotLoad on the mvme5500 and mvme6100 configures the PCI + * busses nicely, i.e., the values read from the memory address + * space BARs by means of PCI config cycles directly reflect the + * CPU memory map. Thus, the presence of two hoses is already hidden. + * + * Unfortunately, all PCI I/O addresses are 'zero-based' i.e., + * a hose-specific base address would have to be added to + * the values read from config space. + * + * We fix this here so I/O BARs also reflect the CPU memory map. + * + * Furthermore, the mvme5500 uses + * f000.0000 + * ..f07f.ffff for PCI-0 / hose0 + * + * and + * + * f080.0000 + * ..f0ff.0000 for PCI-1 / hose 0 + * + * whereas the mvme6100 does it the other way round... + */ + + b0 = in_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI0_IO_Low_Decode) ); + b1 = in_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI1_IO_Low_Decode) ); + + r0 = in_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI0_IO_Remap) ); + r1 = in_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI1_IO_Remap) ); + + switch ( BSP_getDiscoveryVersion(0) ) { + case MV_64360: + /* In case of the MV64360 the 'limit' is actually a 'size'! + * Disable by setting special bits in the 'BAR disable reg'. + */ + dis = in_le32( (volatile unsigned*)(BSP_MV64x60_BASE + MV_64360_BASE_ADDR_DISBL) ); + /* disable PCI0 I/O and PCI1 I/O */ + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + MV_64360_BASE_ADDR_DISBL), dis | (1<<9) | (1<<14) ); + /* remap busses on hose 0; if the remap register was already set, assume + * that someone else [such as the bootloader] already performed the fixup + */ + if ( (b0 & 0xffff) && 0 == (r0 & 0xffff) ) { + rtems_pci_io_remap( 0, BSP_pci_hose1_bus_base, (b0 & 0xffff)<<16 ); + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI0_IO_Remap), (b0 & 0xffff) ); + } + + /* remap busses on hose 1 */ + if ( (b1 & 0xffff) && 0 == (r1 & 0xffff) ) { + rtems_pci_io_remap( BSP_pci_hose1_bus_base, pci_bus_count(), (b1 & 0xffff)<<16 ); + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI1_IO_Remap), (b1 & 0xffff) ); + } + + /* re-enable */ + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + MV_64360_BASE_ADDR_DISBL), dis ); + break; + + case GT_64260_A: + case GT_64260_B: + + if ( (b0 & 0xfff) && 0 == (r0 & 0xfff) ) { /* base are only 12 bits */ + /* switch window off by setting the limit < base */ + lim = in_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI0_IO_High_Decode) ); + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI0_IO_High_Decode), 0 ); + /* remap busses on hose 0 */ + rtems_pci_io_remap( 0, BSP_pci_hose1_bus_base, (b0 & 0xfff)<<20 ); + + /* BTW: it seems that writing the base register also copies the + * value into the 'remap' register automatically (??) + */ + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI0_IO_Remap), (b0 & 0xfff) ); + + /* re-enable */ + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI0_IO_High_Decode), lim ); + } + + if ( (b1 & 0xfff) && 0 == (r1 & 0xfff) ) { /* base are only 12 bits */ + /* switch window off by setting the limit < base */ + lim = in_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI1_IO_High_Decode) ); + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI1_IO_High_Decode), 0 ); + + /* remap busses on hose 1 */ + rtems_pci_io_remap( BSP_pci_hose1_bus_base, pci_bus_count(), (b1 & 0xfff)<<20 ); + + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI1_IO_Remap), (b1 & 0xfff) ); + + /* re-enable */ + out_le32( (volatile unsigned*)(BSP_MV64x60_BASE + GT_PCI1_IO_High_Decode), lim ); + } + break; + + default: + BSP_panic("Unknown discovery version; switch in file: "__FILE__" not implemented (yet)"); + break; /* never get here */ + } + + /* Fixup the IRQ lines; the mvme6100 maps them nicely into our scheme, i.e., GPP + * interrupts start at 64 upwards + * + * The mvme5500 is apparently initialized differently :-(. GPP interrupts start at 0 + * Since all PCI interrupts are wired to GPP we simply check for a value < 64 and + * reprogram the interrupt line register. + */ + BSP_pciScan(0, fixup_irq_line, 0); +} + + diff --git a/c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.c b/c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.c new file mode 100644 index 0000000000..cb25c5b7e4 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.c @@ -0,0 +1,207 @@ +/* $Id$ */ + +/* Adjust a PCI bus range's I/O address space by adding an offset */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include +#include +#include +#include + +#ifndef PCI_MULTI_FUN +#define PCI_MULTI_FUN 0x80 +#endif + +#ifndef PCI_HEADER_TYPE_MSK +#define PCI_HEADER_TYPE_MSK 0x7f +#endif + +/* Reconfigure all I/O base address registers for a range of PCI busses + * (from and including 'bus_from' up to and not including 'bus_to'). + * adding an offset. This involves adjusting the base and limit registers + * of PCI-PCI bridges, too. + * + * RESTRICTIONS: 'offset' must be 4k aligned (PCI req.); no argument check + * on the bus numbers is done. + * + * RETURNS: 0 on success and a number > 0 indicating the number of + * non-32bit bridges found where the offset couldn't be added. + * Devices behind such a bridge are not accessible through I/O + * and should probably be switched off (not done by this code). + */ + +int +rtems_pci_io_remap(int bus_from, int bus_to, uint32_t offset) +{ +int rval = 0; + +int bus, dev, fun, maxf; +int bar, numBars = 0; + +uint8_t b; +uint16_t s; +uint32_t d; +unsigned int bas, lim; + + if ( offset & ((1<<12)-1) ) { + BSP_panic("rtems_pci_io_remap(): offset must be 4k aligned"); + return -1; + } + + + for ( bus=bus_from; bus < bus_to; bus++ ) { + for ( dev = 0; dev 0xffff || lim > 0xffff ) { + printk("PCI I/O range type 1 (16bit) bridge (@%i/%i/%i) found:\n", bus, dev, fun); + printk("WARNING: base (0x%08x) or limit (0x%08x) exceed 16-bit;\n", bas, lim); + printk(" devices behind this bridge are NOT accessible!\n"); + + /* FIXME: should we disable devices behind this bridge ? */ + bas = lim = 0; + } + break; + + case PCI_IO_RANGE_TYPE_32: + break; + } + + b = (uint8_t)((bas>>8) & PCI_IO_RANGE_MASK); + pci_write_config_byte( bus, dev, fun, PCI_IO_BASE, b ); + + s = (uint16_t)((bas>>16)&0xffff); + pci_write_config_word( bus, dev, fun, PCI_IO_BASE_UPPER16, s); + + b = (uint8_t)((lim>>8) & PCI_IO_RANGE_MASK); + pci_write_config_byte( bus, dev, fun, PCI_IO_LIMIT, b ); + s = (uint16_t)((lim>>16)&0xffff); + pci_write_config_word( bus, dev, fun, PCI_IO_LIMIT_UPPER16, s ); + } + } + } + } + return rval; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.h b/c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.h new file mode 100644 index 0000000000..533519a2ae --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/pci/pci_io_remap.h @@ -0,0 +1,66 @@ +#ifndef RTEMS_PCI_IO_REMAP_H +#define RTEMS_PCI_IO_REMAP_H +/* Reconfigure all I/O base address registers for a range of PCI busses + * (from and including 'bus_from' up to and not including 'bus_to'). + * adding an offset. This involves adjusting the base and limit registers + * of PCI-PCI bridges, too. + * + * RESTRICTIONS: 'offset' must be 4k aligned (PCI req.); no argument check + * on the bus numbers is done. + * + * RETURNS: 0 on success and a number > 0 indicating the number of + * non-32bit bridges found where the offset couldn't be added. + * Devices behind such a bridge are not accessible through I/O + * and should probably be switched off (not done by this code). + */ + +int +rtems_pci_io_remap(int bus_from, int bus_to, uint32_t offset); + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#endif + diff --git a/c/src/lib/libbsp/powerpc/beatnik/preinstall.am b/c/src/lib/libbsp/powerpc/beatnik/preinstall.am new file mode 100644 index 0000000000..51a83f2ded --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/preinstall.am @@ -0,0 +1,188 @@ +## Automatically generated by ampolish3 - Do not edit + +if AMPOLISH3 +$(srcdir)/preinstall.am: Makefile.am + $(AMPOLISH3) $(srcdir)/Makefile.am > $(srcdir)/preinstall.am +endif + +PREINSTALL_DIRS = +DISTCLEANFILES += $(PREINSTALL_DIRS) + +all-local: $(TMPINSTALL_FILES) + +TMPINSTALL_FILES = +CLEANFILES += $(TMPINSTALL_FILES) + +all-am: $(PREINSTALL_FILES) + +PREINSTALL_FILES = +CLEANFILES += $(PREINSTALL_FILES) + +$(PROJECT_LIB)/$(dirstamp): + @$(MKDIR_P) $(PROJECT_LIB) + @: > $(PROJECT_LIB)/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_LIB)/$(dirstamp) + +$(PROJECT_INCLUDE)/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE) + @: > $(PROJECT_INCLUDE)/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/$(dirstamp) + +$(PROJECT_INCLUDE)/bsp/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/bsp + @: > $(PROJECT_INCLUDE)/bsp/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/bsp/$(dirstamp) + +$(PROJECT_LIB)/bsp_specs: bsp_specs $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/bsp_specs +PREINSTALL_FILES += $(PROJECT_LIB)/bsp_specs + +$(PROJECT_INCLUDE)/bsp.h: include/bsp.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp.h + +$(PROJECT_INCLUDE)/bspopts.h: include/bspopts.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bspopts.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bspopts.h + +$(PROJECT_INCLUDE)/bsp/bootcard.h: ../../shared/include/bootcard.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/bootcard.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/bootcard.h + +$(PROJECT_LIB)/rtems_crti.$(OBJEXT): rtems_crti.$(OBJEXT) $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/rtems_crti.$(OBJEXT) +TMPINSTALL_FILES += $(PROJECT_LIB)/rtems_crti.$(OBJEXT) + +$(PROJECT_LIB)/motld_start.$(OBJEXT): motld_start.$(OBJEXT) $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/motld_start.$(OBJEXT) +TMPINSTALL_FILES += $(PROJECT_LIB)/motld_start.$(OBJEXT) + +$(PROJECT_LIB)/linkcmds: ../shared/startup/linkcmds $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds +PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds + +$(PROJECT_INCLUDE)/bsp/vpd.h: ../shared/motorola/vpd.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vpd.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vpd.h + +$(PROJECT_INCLUDE)/bsp/consoleIo.h: ../../powerpc/shared/console/consoleIo.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/consoleIo.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/consoleIo.h + +$(PROJECT_INCLUDE)/bsp/uart.h: ../../powerpc/shared/console/uart.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/uart.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/uart.h + +$(PROJECT_INCLUDE)/bsp/irq.h: irq/irq.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq.h + +$(PROJECT_INCLUDE)/bsp/gtreg.h: marvell/gtreg.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gtreg.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gtreg.h + +$(PROJECT_INCLUDE)/bsp/gtintrreg.h: marvell/gtintrreg.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gtintrreg.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gtintrreg.h + +$(PROJECT_INCLUDE)/bsp/gti2creg.h: marvell/gti2creg.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gti2creg.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gti2creg.h + +$(PROJECT_INCLUDE)/bsp/gti2c_busdrv.h: marvell/gti2c_busdrv.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gti2c_busdrv.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gti2c_busdrv.h + +$(PROJECT_INCLUDE)/bsp/gt_timer.h: marvell/gt_timer.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gt_timer.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gt_timer.h + +$(PROJECT_INCLUDE)/bsp/gtpcireg.h: marvell/gtpcireg.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gtpcireg.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gtpcireg.h + +$(PROJECT_INCLUDE)/bsp/flashPgm.h: ../shared/flash/flashPgm.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/flashPgm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/flashPgm.h + +$(PROJECT_INCLUDE)/bsp/flashPgmPvt.h: ../shared/flash/flashPgmPvt.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/flashPgmPvt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/flashPgmPvt.h + +$(PROJECT_INCLUDE)/bsp/pci.h: ../../powerpc/shared/pci/pci.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/pci.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/pci.h + +$(PROJECT_INCLUDE)/bsp/vectors.h: ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/vectors.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vectors.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vectors.h + +$(PROJECT_INCLUDE)/bsp/irq_supp.h: ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/irq_supp.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq_supp.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq_supp.h + +$(PROJECT_INCLUDE)/bsp/ppc_exc_bspsupp.h: ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/ppc_exc_bspsupp.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/ppc_exc_bspsupp.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/ppc_exc_bspsupp.h + +$(PROJECT_INCLUDE)/bsp/VMEConfig.h: vme/VMEConfig.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/VMEConfig.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/VMEConfig.h + +$(PROJECT_INCLUDE)/bsp/vmeUniverse.h: ../../shared/vmeUniverse/vmeUniverse.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vmeUniverse.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vmeUniverse.h + +$(PROJECT_INCLUDE)/bsp/vmeUniverseDMA.h: ../../shared/vmeUniverse/vmeUniverseDMA.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vmeUniverseDMA.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vmeUniverseDMA.h + +$(PROJECT_INCLUDE)/bsp/vme_am_defs.h: ../../shared/vmeUniverse/vme_am_defs.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vme_am_defs.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vme_am_defs.h + +$(PROJECT_INCLUDE)/bsp/vmeTsi148.h: ../../shared/vmeUniverse/vmeTsi148.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vmeTsi148.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vmeTsi148.h + +$(PROJECT_INCLUDE)/bsp/vmeTsi148DMA.h: ../../shared/vmeUniverse/vmeTsi148DMA.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/vmeTsi148DMA.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/vmeTsi148DMA.h + +$(PROJECT_INCLUDE)/bsp/bspVmeDmaList.h: ../../shared/vmeUniverse/bspVmeDmaList.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/bspVmeDmaList.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/bspVmeDmaList.h + +$(PROJECT_INCLUDE)/bsp/VME.h: ../../shared/vmeUniverse/VME.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/VME.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/VME.h + +$(PROJECT_INCLUDE)/bsp/VMEDMA.h: ../../shared/vmeUniverse/VMEDMA.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/VMEDMA.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/VMEDMA.h + +if HAS_NETWORKING +$(PROJECT_INCLUDE)/bsp/early_enet_link_status.h: network/support/early_enet_link_status.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/early_enet_link_status.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/early_enet_link_status.h + +$(PROJECT_INCLUDE)/bsp/bsp_bsdnet_attach.h: network/support/bsp_bsdnet_attach.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/bsp_bsdnet_attach.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/bsp_bsdnet_attach.h + +$(PROJECT_INCLUDE)/bsp/if_mve_pub.h: network/if_mve/if_mve_pub.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/if_mve_pub.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/if_mve_pub.h + +$(PROJECT_INCLUDE)/bsp/if_gfe_pub.h: network/if_gfe/if_gfe_pub.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/if_gfe_pub.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/if_gfe_pub.h + +$(PROJECT_INCLUDE)/bsp/if_em_pub.h: network/if_em/if_em_pub.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/if_em_pub.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/if_em_pub.h +endif +$(PROJECT_INCLUDE)/tod.h: ../../shared/tod.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tod.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/tod.h + diff --git a/c/src/lib/libbsp/powerpc/beatnik/startup/bspclean.c b/c/src/lib/libbsp/powerpc/beatnik/startup/bspclean.c new file mode 100644 index 0000000000..746a48fbde --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/startup/bspclean.c @@ -0,0 +1,11 @@ +#include +#include + +void bsp_cleanup(void) +{ + /* We can't go back to MotLoad since we blew it's memory area + * and vectors. Just pull the reset line... + */ + printk("bsp_cleanup(): RTEMS terminated -- no way back to MotLoad so I reset the card\n"); + bsp_reset(); +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/startup/bspstart.c b/c/src/lib/libbsp/powerpc/beatnik/startup/bspstart.c new file mode 100644 index 0000000000..d88476a3f1 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/startup/bspstart.c @@ -0,0 +1,397 @@ +/* + * This routine starts the application. It includes application, + * board, and monitor specific initialization and configuration. + * The generic CPU dependent initialization has been performed + * before this routine is invoked. + * + * COPYRIGHT (c) 1989-1998. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.OARcorp.com/rtems/license.html. + * + * Modified to support the MCP750. + * Modifications Copyright (C) 1999 Eric Valette. valette@crf.canon.fr + * + * Modified to support the Synergy VGM & Motorola PowerPC boards. + * (C) by Till Straumann, , 2002, 2004, 2005 + * + * Modified to support the mvme5500 BSP + * (C) by Kate Feng , 2003, 2004 + * under the contract DE-AC02-98CH10886 with the Deaprtment of Energy + * + * T. Straumann: 2005-2007; stolen again for 'beatnik'... + */ +#include +#include +#include + +#include +#include +#include +#include +#include +/*#include */ +#include /* registers.h is included here */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* for RTEMS_VERSION :-( I dont like the preassembled string */ +#include + +#ifdef __RTEMS_APPLICATION__ +#undef __RTEMS_APPLICATION__ +#endif + +#define SHOW_MORE_INIT_SETTINGS + +BSP_output_char_function_type BSP_output_char = BSP_output_char_via_serial; + +extern char *BSP_build_date; +extern void bsp_cleanup(void); +extern Triv121PgTbl BSP_pgtbl_setup(unsigned int *); +extern void BSP_pgtbl_activate(Triv121PgTbl); +extern void BSP_motload_pci_fixup(void); + +extern unsigned long __rtems_end[]; + +/* We really shouldn't use these since MMUoff also sets IP; + * nevertheless, during early init I don't care for now + */ +extern void MMUoff(void); +extern void MMUon(void); + +extern uint32_t probeMemoryEnd(void); + +SPR_RW(SPRG0) +SPR_RW(SPRG1) +SPR_RO(HID1) + +/* Table of PLL multipliers for 7455/7457: +01000 2 00010 7.5 00000 11.5 00001 17 +10000 3 11000 8 10111 12 00101 18 +10100 4 01100 8.5 11111 12.5 00111 20 +10110 5 01111 9 01011 13 01001 21 +10010 5.5 01110 9.5 11100 13.5 01101 24 +11010 6 10101 10 11001 14 11101 28 +01010 6.5 10001 10.5 00011 15 00110 bypass +00100 7 10011 11 11011 16 11110 off +*/ + +/* Sorted according to CFG bits and multiplied by 2 it looks + * like this (note that this is in sequential order, not + * tabulated as above) + */ +signed char mpc7450PllMultByTwo[32] = { +23, 34, 15, 30, +14, 36, 2/*bypass*/, 40, +4, 42, 13, 26, +17, 48, 19, 18, +6, 21, 11, 22, +8, 20, 10, 24, +16, 28, 12, 32, +27, 56, 0/*off*/, 25, +}; + +uint32_t bsp_clicks_per_usec = 0; + +/* + * Total memory using probing. + */ +unsigned int BSP_mem_size; + +/* + * PCI Bus Frequency + */ +unsigned int BSP_bus_frequency = 0xdeadbeef; +/* + * processor clock frequency + */ +unsigned int BSP_processor_frequency = 0xdeadbeef; + +/* + * Time base divisior (bus freq / TB clock) + */ +unsigned int BSP_time_base_divisor = 4000; /* most 604+ CPUs seem to use this */ + +/* Board identification string */ +char BSP_productIdent[20] = {0}; +char BSP_serialNumber[20] = {0}; + +/* VPD appends an extra char -- what for ? */ +char BSP_enetAddr0[7] = {0}; +char BSP_enetAddr1[7] = {0}; + +/* + * The original table from the application and our copy of it with + * some changes. + */ + +extern rtems_configuration_table Configuration; + +char *rtems_progname; + +/* + * Use the shared implementations of the following routines + */ + +extern void bsp_pretasking_hook(void); + +#define CMDLINE_BUF_SIZE 2048 + +static char cmdline_buf[CMDLINE_BUF_SIZE]; +char *BSP_commandline_string = cmdline_buf; + +/* this routine is called early and must be safe with a not properly + * aligned stack + */ +char * +save_boot_params(void *r3, void *r4, void* r5, char *cmdline_start, char *cmdline_end) +{ +int i=cmdline_end-cmdline_start; + if ( i >= CMDLINE_BUF_SIZE ) + i = CMDLINE_BUF_SIZE-1; + else if ( i < 0 ) + i = 0; + memmove(cmdline_buf, cmdline_start, i); + cmdline_buf[i]=0; + return cmdline_buf; +} + +static BSP_BoardType board_type = Unknown; + +BSP_BoardType +BSP_getBoardType( void ) +{ + return board_type; +} + +/* + * bsp_start + * + * This routine does the bulk of the system initialization. + */ + +void bsp_start( void ) +{ + unsigned char *stack; + char *chpt; + uint32_t intrStackStart; + uint32_t intrStackSize; + + Triv121PgTbl pt=0; + + VpdBufRec vpdData [] = { + { key: ProductIdent, instance: 0, buf: BSP_productIdent, buflen: sizeof(BSP_productIdent) - 1 }, + { key: SerialNumber, instance: 0, buf: BSP_serialNumber, buflen: sizeof(BSP_serialNumber) - 1 }, + { key: CpuClockHz, instance: 0, buf: &BSP_processor_frequency, buflen: sizeof(BSP_processor_frequency) }, + { key: BusClockHz, instance: 0, buf: &BSP_bus_frequency, buflen: sizeof(BSP_bus_frequency) }, + { key: EthernetAddr, instance: 0, buf: BSP_enetAddr0, buflen: sizeof(BSP_enetAddr0) }, + { key: EthernetAddr, instance: 1, buf: BSP_enetAddr1, buflen: sizeof(BSP_enetAddr1) }, + VPD_END + }; + + /* T. Straumann: 4/2005 + * + * Need to map the system registers early, so we can printk... + * (otherwise we silently die) + */ + /* map the PCI 0, 1 Domain I/O space, GT64260B registers + * and the reserved area so that the size is the power of 2. + */ + setdbat(7, BSP_DEV_AND_PCI_IO_BASE, BSP_DEV_AND_PCI_IO_BASE, BSP_DEV_AND_PCI_IO_SIZE, IO_PAGE); + + /* Intersperse messages with actions to help locate problems */ + printk("-----------------------------------------\n"); + + /* + * Get CPU identification dynamically. Note that the get_ppc_cpu_type() & friends functions + * store the result in global variables so that it can be used latter... + * This also verifies that we run on a known CPU. + */ + get_ppc_cpu_type(); + get_ppc_cpu_revision(); + + /* Make sure we detect a known host bridge */ + BSP_getDiscoveryVersion(/* assert detection */ 1); + + printk("Welcome to %s ($Name$)\n", _RTEMS_version ); + + /* Leave all caches as MotLoad left them. Seems to be fine */ + + /* + * the initial stack has aready been set to this value in start.S + * so there is no need to set it in r1 again... It is just for info + * so that it can be printed without accessing R1. + */ + asm volatile("mr %0, 1":"=r"(stack)); + + /* tag the bottom (T. Straumann 6/36/2001 ) */ + + *((uint32_t *)stack) = 0; + + /* + * Initialize the interrupt related settings + * SPRG0 = interrupt nesting level count + * SPRG1 = software managed IRQ stack + * + * This could be done latter (e.g in IRQ_INIT) but it helps to understand + * some settings below... + */ + intrStackStart = (uint32_t)__rtems_end; + intrStackSize = rtems_configuration_get_interrupt_stack_size(); + + + /* + * Initialize default raw exception handlers. See vectors/vectors_init.c + */ + ppc_exc_initialize( + PPC_INTERRUPT_DISABLE_MASK_DEFAULT, + intrStackStart, + intrStackSize + ); + + printk("CPU: %s\n", get_ppc_cpu_type_name(current_ppc_cpu)); + + /* + * Initialize RTEMS IRQ system + */ + BSP_rtems_irq_mng_init(0); + + printk("Build Date: %s\n",BSP_build_date); + + BSP_vpdRetrieveFields(vpdData); + + if ( !strncmp(BSP_productIdent,"MVME5500",8) ) + board_type = MVME5500; + else if ( !strncmp(BSP_productIdent,"MVME6100",8) ) + board_type = MVME6100; + + printk("Board Type: %s (S/N %s)\n", + BSP_productIdent[0] ? BSP_productIdent : "n/a", + BSP_serialNumber[0] ? BSP_serialNumber : "n/a"); + + if ( 0xdeadbeef == BSP_bus_frequency ) { + BSP_bus_frequency = 133333333; + printk("Bus Clock Freq NOT FOUND in VPD; using %10u Hz\n", + BSP_bus_frequency); + } else { + printk("Bus Clock Freq: %10u Hz\n", + BSP_bus_frequency); + } + + if ( 0xdeadbeef == BSP_processor_frequency ) { + BSP_processor_frequency = BSP_bus_frequency/2; + BSP_processor_frequency *= mpc7450PllMultByTwo[ (_read_HID1() >> (31-19)) & 31 ]; + } + printk("CPU Clock Freq: %10u Hz\n", BSP_processor_frequency); + + /* probe real memory size; if it's more than 256M we can't currently access it + * since at this point only BAT-0 maps 0..256M + */ + BSP_mem_size = probeMemoryEnd(); + + if ( (chpt = strstr(BSP_commandline_string,"MEMSZ=")) ) { + char *endp; + uint32_t sz; + chpt+=6 /* strlen("MEMSZ=") */; + sz = strtoul(chpt, &endp, 0); + if ( endp != chpt ) + BSP_mem_size = sz; + } + + printk("Memory: %10u bytes\n", BSP_mem_size); + + if ( BSP_mem_size > 0x10000000 ) { + uint32_t s; + if ( BSP_mem_size > 0x80000000 ) { + BSP_mem_size = 0x80000000; + printk("Memory clipped to 0x%08x for now, sorry\n", BSP_mem_size); + } + for ( s = 0x20000000; s < BSP_mem_size ; s<<=1) + ; + MMUoff(); + /* since it's currently in use we must first surrender it */ + setdbat(0, 0, 0, 0, 0); + setdbat(0, 0, 0, s, _PAGE_RW); + MMUon(); + } + + printk("-----------------------------------------\n"); + + /* Maybe not setup yet because of the warning message */ + + /* Allocate and set up the page table mappings + * This is only available on >604 CPUs. + * + * NOTE: This setup routine may modify the available memory + * size. It is essential to call it before + * calculating the workspace etc. + */ + pt = BSP_pgtbl_setup(&BSP_mem_size); + if (!pt) + printk("WARNING: unable to setup page tables.\n"); + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Now BSP_mem_size = 0x%x\n",BSP_mem_size); +#endif + + /* + * Set up our hooks + * Make sure libc_init is done before drivers initialized so that + * they can use atexit() + */ + + bsp_clicks_per_usec = BSP_bus_frequency/(BSP_time_base_divisor * 1000); + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Configuration.work_space_size = %x\n", Configuration.work_space_size); +#endif + + /* Activate the page table mappings only after + * initializing interrupts because the irq_mng_init() + * routine needs to modify the text + */ + if ( pt ) { +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Page table setup finished; will activate it NOW...\n"); +#endif + BSP_pgtbl_activate(pt); + } + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Going to start PCI buses scanning and initialization\n"); +#endif + BSP_pci_initialize(); + + /* need to tweak the motload setup */ + BSP_motload_pci_fixup(); + + /* map 512M, 256 for PCI 256 for VME */ + setdbat(5,BSP_PCI_HOSE0_MEM_BASE, BSP_PCI_HOSE0_MEM_BASE, BSP_PCI_HOSE0_MEM_SIZE, IO_PAGE); + setdbat(6,BSP_PCI_HOSE1_MEM_BASE, BSP_PCI_HOSE1_MEM_BASE, 0x10000000, IO_PAGE); + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Number of PCI buses found is : %d\n", pci_bus_count()); +#endif + + /* + * Initialize hardware timer facility (not used by BSP itself) + * Needs PCI to identify discovery version... + */ + BSP_timers_initialize(); + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("MSR %x \n", _read_MSR()); + printk("Exit from bspstart\n"); +#endif +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/startup/i2c_init.c b/c/src/lib/libbsp/powerpc/beatnik/startup/i2c_init.c new file mode 100644 index 0000000000..a91d456cf8 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/startup/i2c_init.c @@ -0,0 +1,132 @@ +/* $Id$ */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Register i2c bus driver & devices */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +int +BSP_i2c_initialize( void ) +{ +int busno; + /* Initialize the library */ + if ( rtems_libi2c_initialize() ) { + fprintf(stderr,"Initializing I2C library failed\n"); + return -1; + } + + /* Register our bus driver */ + if ( (busno=rtems_libi2c_register_bus( + BSP_I2C_BUS0_NAME, + BSP_I2C_BUS_DESCRIPTOR) ) < 0 ) { + perror("Registering gt64260 i2c bus driver"); + return -1; + } + + /* Now register higher level drivers; note that + * the i2c address in the manual is actually left-shifted + * by one bit, i.e., as it would go on the bus. + */ + + /* Use read-only driver for VPD */ + if ( rtems_libi2c_register_drv( + BSP_I2C_VPD_EEPROM_NAME, + i2c_2b_eeprom_ro_driver_descriptor, + busno, + BSP_VPD_I2C_ADDR) < 0 ) { + perror("Registering i2c VPD eeprom driver failed"); + return -1; + } + + /* Use read-write driver for user eeprom -- you still might + * have to disable HW write-protection on your board. + */ + if ( rtems_libi2c_register_drv( + BSP_I2C_USR_EEPROM_NAME, + i2c_2b_eeprom_driver_descriptor, + busno, + BSP_USR_I2C_ADDR) < 0 ) { + perror("Registering i2c USR eeprom driver failed"); + return -1; + } + + /* The thermostat */ + if ( rtems_libi2c_register_drv( + BSP_I2C_DS1621_NAME, + i2c_ds1621_driver_descriptor, + busno, + BSP_THM_I2C_ADDR) < 0 ) { + perror("Registering i2c ds1621 temp sensor. driver failed"); + return -1; + } + + /* Finally, as an example, register raw access to the + * ds1621. The driver above just reads the 8 msb of the + * temperature but doesn't support anything else. Using + * the raw device node you can write/read individual + * control bytes yourself and e.g., program the thermostat... + */ + + if ( mknod( + BSP_I2C_DS1621_RAW_DEV_NAME, + 0666 | S_IFCHR, + rtems_filesystem_make_dev_t(rtems_libi2c_major, + RTEMS_LIBI2C_MAKE_MINOR(busno,BSP_THM_I2C_ADDR))) ) { + perror("Creating device node for raw ds1621 access failed"); + return -1; + } + printf("I2C devices registered\n"); + return 0; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/startup/linkcmds b/c/src/lib/libbsp/powerpc/beatnik/startup/linkcmds new file mode 100644 index 0000000000..a186b6dcae --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/startup/linkcmds @@ -0,0 +1,261 @@ +OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", + "elf32-powerpc") +OUTPUT_ARCH(powerpc) +ENTRY(_start) +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +PROVIDE (__stack = 0); + + +MEMORY { + BOTTOM : ORIGIN = 0x00, LENGTH = 0x80 + MAILBOX : ORIGIN = 0x80, LENGTH = 0x80 /* RESERVED */ + VECTORS : ORIGIN = 0x100 , LENGTH = 0x3000 - 0x100 + CODE : ORIGIN = 0x3000 , LENGTH = 32M-0x3000 +} +SECTIONS +{ + /* discard the 'shared/vector.S' entry point section */ + /DISCARD/ : + { + *(.entry_point_section) + } + + + .vectors : + { + /* should be the first thing... */ + *(.ppc_preloader_section) + + /* + * This section is used only if NO_DYNAMIC_EXCEPTION_VECTOR_INSTALL + * is defined in vectors/vectors.S + * *(.vectors) + * We actually RELY on dynamic vector installation since we need + * this space for the preloader... + */ + } > VECTORS + + + /* START OF THE LOADED IMAGE (parts moved by the preloader) */ + .image_start : + { + __rtems_start = ABSOLUTE(.); + } > CODE + + /* Read-only sections, merged into text segment: */ + .interp : { *(.interp) } > CODE + .hash : { *(.hash) } > CODE + .dynsym : { *(.dynsym) } > CODE + .dynstr : { *(.dynstr) } > CODE + .gnu.version : { *(.gnu.version) } > CODE + .gnu.version_d : { *(.gnu.version_d) } > CODE + .gnu.version_r : { *(.gnu.version_r) } > CODE + .rela.text : + { *(.rela.text) *(.rela.gnu.linkonce.t*) } > CODE + .rela.data : + { *(.rela.data) *(.rela.gnu.linkonce.d*) } > CODE + .rela.rodata : + { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } > CODE + .rela.got : { *(.rela.got) } > CODE + .rela.got1 : { *(.rela.got1) } > CODE + .rela.got2 : { *(.rela.got2) } > CODE + .rela.ctors : { *(.rela.ctors) } > CODE + .rela.dtors : { *(.rela.dtors) } > CODE + .rela.init : { *(.rela.init) } > CODE + .rela.fini : { *(.rela.fini) } > CODE + .rela.bss : { *(.rela.bss) } > CODE + .rela.plt : { *(.rela.plt) } > CODE + .rela.sdata : { *(.rela.sdata) } > CODE + .rela.sbss : { *(.rela.sbss) } > CODE + .rela.sdata2 : { *(.rela.sdata2) } > CODE + .rela.sbss2 : { *(.rela.sbss2) } > CODE + + .init : { *(.init) } >CODE + + .text : + { + *(.text*) + + /* + * Special FreeBSD sysctl sections. + */ + . = ALIGN (16); + __start_set_sysctl_set = .; + *(set_sysctl_*); + __stop_set_sysctl_set = ABSOLUTE(.); + *(set_domain_*); + *(set_pseudo_*); + + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } > CODE + + .fini : { _fini = .; *(.fini) } >CODE + .rodata : { *(.rodata*) *(.gnu.linkonce.r*) } > CODE + .rodata1 : { *(.rodata1) } > CODE +_SDA2_BASE_ = __SDATA2_START__ + 0x8000; + .sdata2 : { *(.sdata2) *(.gnu.linkonce.s2.*) } > CODE + .sbss2 : { + PROVIDE (__sbss2_start = .); + *(.sbss2*) *(.gnu.linkonce.sb2.*) + /* avoid empty sdata2/sbss2 area because __eabi wouldn't set up r2 + * (IMPORTANT if run-time loading is involved) + */ + . += 1 ; + PROVIDE (__sbss2_end = .); + } > CODE + .eh_frame : { *.(eh_frame) } >CODE + _etext = .; + PROVIDE (etext = .); + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. It would + be more correct to do this: + . = ALIGN(0x40000) + (ALIGN(8) & (0x40000 - 1)); + The current expression does not correctly handle the case of a + text segment ending precisely at the end of a page; it causes the + data segment to skip a page. The above expression does not have + this problem, but it will currently (2/95) cause BFD to allocate + a single segment, combining both text and data, for this case. + This will prevent the text segment from being shared among + multiple executions of the program; I think that is more + important than losing a page of the virtual address space (note + that no actual memory is lost; the page which is skipped can not + be referenced). */ + .data ALIGN(0x1000) : + { + PROVIDE(__DATA_START__ = ABSOLUTE(.) ); + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } > CODE + .data1 : { *(.data1) } > CODE + PROVIDE (__EXCEPT_START__ = .); + .gcc_except_table : { *(.gcc_except_table) } > CODE + PROVIDE (__EXCEPT_END__ = .); + .got1 : { *(.got1) } > CODE + .dynamic : { *(.dynamic) } > CODE + /* Put .ctors and .dtors next to the .got2 section, so that the pointers + get relocated with -mrelocatable. Also put in the .fixup pointers. + The current compiler no longer needs this, but keep it around for 2.7.2 */ + PROVIDE (_GOT2_START_ = .); + .got2 : { *(.got2) } > CODE +/* + PROVIDE (__CTOR_LIST__ = .); + .ctors : { *(.ctors) } > CODE + PROVIDE (__CTOR_END__ = .); +*/ + .ctors : + { + KEEP(*crtbegin.o(.ctors)) + KEEP(*(EXCLUDE_FILE(*crtend.o) .ctors)) + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + } > CODE + .dtors : + { + KEEP(*crtbegin.o(.dtors)) + KEEP(*(EXCLUDE_FILE(*crtend.o) .dtors)) + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + } > CODE +/* + PROVIDE (__DTOR_LIST__ = .); + .dtors : { *(.dtors) } > CODE + PROVIDE (__DTOR_END__ = .); +*/ + PROVIDE (_FIXUP_START_ = .); + .fixup : { *(.fixup) } > CODE + PROVIDE (_FIXUP_END_ = .); + PROVIDE (_GOT2_END_ = .); + PROVIDE (_GOT_START_ = .); + .got : { *(.got) } > CODE + .got.plt : { *(.got.plt) } > CODE + PROVIDE (_GOT_END_ = .); + + .jcr : { KEEP (*(.jcr)) } > CODE + + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ +_SDA_BASE_ = __SDATA_START__ + 0x8000; + .sdata : { *(.sdata*) *(.gnu.linkonce.s.*) } > CODE + _edata = .; + PROVIDE (edata = .); +/* END OF THE LOADED IMAGE (parts moved by the preloader) */ +/* BELOW THIS POINT, NO LOADABLE ITEMS MUST APPEAR */ + .sbss : + { + PROVIDE (__sbss_start = ABSOLUTE(.)); + *(.sbss) *(.sbss.*) *(.gnu.linkonce.sb.*) + *(.scommon) + *(.dynsbss) + /* avoid empty sdata/sbss area because __eabi wouldn't set up r13 + * (IMPORTANT if run-time loading is involved) + */ + . += 1 ; + PROVIDE (__sbss_end = ABSOLUTE(.)); + } > CODE + .plt : { *(.plt) } > CODE + .bss : + { + PROVIDE (__bss_start = ABSOLUTE(.)); + *(.dynbss) + *(.bss*) *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } > CODE + /* proper alignment for SYSV stack + * (init stack is allocated just after __rtems_end + */ + . = ALIGN(16); + _end = . ; + __rtems_end = . ; + PROVIDE (end = .); + /DISCARD/ : + { + *(.comment) + } + + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/startup/reboot.c b/c/src/lib/libbsp/powerpc/beatnik/startup/reboot.c new file mode 100644 index 0000000000..0e6af195f4 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/startup/reboot.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include + +void bsp_reset() +{ + + printk("Printing a stack trace for your convenience :-)\n"); + CPU_print_stack(); + + printk("RTEMS terminated; Rebooting ...\n"); + /* Mvme5500 board reset : 2004 S. Kate Feng */ + out_8((volatile unsigned char*) (BSP_MV64x60_DEV1_BASE +2), 0x80); +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/tod/todcfg.c b/c/src/lib/libbsp/powerpc/beatnik/tod/todcfg.c new file mode 100644 index 0000000000..10f123b27f --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/tod/todcfg.c @@ -0,0 +1,36 @@ +/* + * This file contains the RTC driver table for Motorola shared BSPs. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#include +#include +#include + +/* The following table configures the RTC drivers used in this BSP */ +rtc_tbl RTC_Table[] = { + { + "/dev/rtc", /* sDeviceName */ + RTC_M48T08, /* deviceType */ + &m48t08_fns, /* pDeviceFns */ + rtc_probe, /* deviceProbe */ + NULL, /* pDeviceParams */ + BSP_NVRAM_RTC_START, /* ulCtrlPort1 */ + 0x00, /* ulDataPort */ + m48t08_get_register, /* getRegister */ + m48t08_set_register /* setRegister */ + } +}; + +/* Some information used by the RTC driver */ + +#define NUM_RTCS (sizeof(RTC_Table)/sizeof(rtc_tbl)) + +size_t RTC_Count = NUM_RTCS; + +rtems_device_minor_number RTC_Minor; diff --git a/c/src/lib/libbsp/powerpc/beatnik/vme/VMEConfig.h b/c/src/lib/libbsp/powerpc/beatnik/vme/VMEConfig.h new file mode 100644 index 0000000000..5d3364473e --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/vme/VMEConfig.h @@ -0,0 +1,112 @@ +#ifndef RTEMS_BSP_VME_CONFIG_H +#define RTEMS_BSP_VME_CONFIG_H +/* $Id$ */ + +/* BSP specific address space configuration parameters */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#define _VME_DRIVER_TSI148 +#define _VME_DRIVER_UNIVERSE + +/* + * NOTE: the BSP (startup/bspstart.c) uses + * hardcoded window lengths that match this + * layout when setting BATs: + */ +#define _VME_A32_WIN0_ON_PCI 0x90000000 +/* If _VME_CSR_ON_PCI is defined then the A32 window is reduced to accommodate + * CSR for space. + */ +#define _VME_CSR_ON_PCI 0x9e000000 +#define _VME_A24_ON_PCI 0x9f000000 +#define _VME_A16_ON_PCI 0x9fff0000 + +/* start of the A32 window on the VME bus + * TODO: this should perhaps be a configuration option + */ +#define _VME_A32_WIN0_ON_VME 0x20000000 + +/* if _VME_DRAM_OFFSET is defined, the BSP + * will map our RAM onto the VME bus, starting + * at _VME_DRAM_OFFSET + */ +#define _VME_DRAM_OFFSET 0x90000000 + +#define BSP_VME_INSTALL_IRQ_MGR(err) \ + do { \ + err = -1; \ + switch (BSP_getBoardType()) { \ + case MVME6100: \ + err = theOps->install_irq_mgr( \ + VMETSI148_IRQ_MGR_FLAG_SHARED, \ + 0, BSP_IRQ_GPP_0 + 20, \ + 1, BSP_IRQ_GPP_0 + 21, \ + 2, BSP_IRQ_GPP_0 + 22, \ + 3, BSP_IRQ_GPP_0 + 23, \ + -1); \ + break; \ + \ + case MVME5500: \ + err = theOps->install_irq_mgr( \ + VMEUNIVERSE_IRQ_MGR_FLAG_SHARED | \ + VMEUNIVERSE_IRQ_MGR_FLAG_PW_WORKAROUND, \ + 0, BSP_IRQ_GPP_0 + 12, \ + 1, BSP_IRQ_GPP_0 + 13, \ + 2, BSP_IRQ_GPP_0 + 14, \ + 3, BSP_IRQ_GPP_0 + 15, \ + -1); \ + break; \ + \ + default: \ + printk("WARNING: unknown board; "); \ + break; \ + } \ + if ( err ) \ + printk("VME interrupt manager NOT INSTALLED (error: %i)\n", err); \ + } while (0) + +#endif diff --git a/c/src/lib/libbsp/powerpc/beatnik/vme/vme_dma.c b/c/src/lib/libbsp/powerpc/beatnik/vme/vme_dma.c new file mode 100644 index 0000000000..368bf4c844 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/vme/vme_dma.c @@ -0,0 +1,217 @@ +/* $Id$ */ + +/* Setup/glue to attach VME DMA driver to the beatnik BSP */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct DmaOpsRec_ { + int (*setup)(int, uint32_t, uint32_t, void *); + int (*start)(int, uint32_t, uint32_t, uint32_t); + uint32_t (*status)(int); + VMEDmaListClass listClass; +} DmaOpsRec, *DmaOps; + +static DmaOpsRec universeOps = { + vmeUniverseDmaSetup, + vmeUniverseDmaStart, + vmeUniverseDmaStatus, + &vmeUniverseDmaListClass, +}; + +static DmaOpsRec tsiOps = { + vmeTsi148DmaSetup, + vmeTsi148DmaStart, + vmeTsi148DmaStatus, + &vmeTsi148DmaListClass, +}; + +static int setup(int a, uint32_t b, uint32_t c, void *d); +static int start(int a, uint32_t b, uint32_t c, uint32_t d); +static uint32_t status(int a); + +static DmaOpsRec jumpstartOps = { + setup, + start, + status, + 0 +}; + +static DmaOps dmaOps = &jumpstartOps; + +static DmaOps selectOps() +{ + return (MVME6100 != BSP_getBoardType()) ? + &universeOps : &tsiOps; +} + +static int +setup(int a, uint32_t b, uint32_t c, void *d) +{ + return (dmaOps=selectOps())->setup(a,b,c,d); +} + +static int +start(int a, uint32_t b, uint32_t c, uint32_t d) +{ + return (dmaOps=selectOps())->start(a,b,c,d); +} + +static uint32_t +status(int a) +{ + return (dmaOps=selectOps())->status(a); +} + + +int +BSP_VMEDmaSetup(int channel, uint32_t bus_mode, uint32_t xfer_mode, void *custom_setup) +{ + return dmaOps->setup(channel, bus_mode, xfer_mode, custom_setup); +} + +int +BSP_VMEDmaStart(int channel, uint32_t pci_addr, uint32_t vme_addr, uint32_t n_bytes) +{ + return dmaOps->start(channel, pci_addr, vme_addr, n_bytes); +} + +uint32_t +BSP_VMEDmaStatus(int channel) +{ + return dmaOps->status(channel); +} + +BSP_VMEDmaListDescriptor +BSP_VMEDmaListDescriptorSetup( + BSP_VMEDmaListDescriptor d, + uint32_t attr_mask, + uint32_t xfer_mode, + uint32_t pci_addr, + uint32_t vme_addr, + uint32_t n_bytes) +{ +VMEDmaListClass pc; + if ( !d ) { + if ( ! (pc = dmaOps->listClass) ) { + pc = (dmaOps = selectOps())->listClass; + } + return BSP_VMEDmaListDescriptorNewTool( + pc, + attr_mask, + xfer_mode, + pci_addr, + vme_addr, + n_bytes); + + } + return BSP_VMEDmaListDescriptorSetupTool(d, attr_mask, xfer_mode, pci_addr, vme_addr, n_bytes); +} + +int +BSP_VMEDmaListStart(int channel, BSP_VMEDmaListDescriptor list) +{ + return BSP_VMEDmaListDescriptorStartTool(0, channel, list); +} + +/* NOT thread safe! */ +int +BSP_VMEDmaInstallISR(int channel, BSP_VMEDmaIRQCallback cb, void *usr_arg) +{ +int vec; +BSP_VME_ISR_t curr; +void *carg; + + if ( MVME6100 != BSP_getBoardType() ) { + if ( channel != 0 ) + return -1; + + vec = UNIV_DMA_INT_VEC; + + } else { + if ( channel < 0 || channel > 1 ) + return -1; + + vec = (channel ? TSI_DMA1_INT_VEC : TSI_DMA_INT_VEC ); + } + + curr = BSP_getVME_isr(vec, &carg); + + if ( cb && curr ) { + /* IRQ currently in use */ + return -1; + } + + if ( !cb && !curr ) { + /* Allow uninstall if no handler is currently installed; + * just make sure IRQ is disabled + */ + BSP_disableVME_int_lvl(vec); + return 0; + } + + if ( cb ) { + if ( BSP_installVME_isr(vec, (BSP_VME_ISR_t)cb, usr_arg) ) + return -4; + BSP_enableVME_int_lvl(vec); + } else { + BSP_disableVME_int_lvl(vec); + if ( BSP_removeVME_isr(vec, curr, carg) ) + return -4; + } + return 0; +} diff --git a/c/src/lib/libbsp/powerpc/beatnik/vme/vmeconfig.c b/c/src/lib/libbsp/powerpc/beatnik/vme/vmeconfig.c new file mode 100644 index 0000000000..577ef8011b --- /dev/null +++ b/c/src/lib/libbsp/powerpc/beatnik/vme/vmeconfig.c @@ -0,0 +1,305 @@ +/* $Id$ */ + +/* Standard VME bridge configuration for MVME5500, MVME6100 */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include +#include +#include +#include +#include +#include +#include +#define _VME_TSI148_DECLARE_SHOW_ROUTINES +#include +#include + +/* Use a weak alias for the VME configuration. + * This permits individual applications to override + * this routine. + * They may even create an 'empty' + * + * void BSP_vme_config(void) {} + * + * which will avoid linking in the Universe driver + * at all :-). + */ + +void BSP_vme_config(void) __attribute__ (( weak, alias("__BSP_default_vme_config") )); + +typedef struct { + int (*xlate_adrs)(int, int, unsigned long, unsigned long, unsigned long *); + int (*install_isr)(unsigned long, BSP_VME_ISR_t, void *); + int (*remove_isr)(unsigned long, BSP_VME_ISR_t, void *); + BSP_VME_ISR_t (*get_isr)(unsigned long vector, void **); + int (*enable_int_lvl)(unsigned int); + int (*disable_int_lvl)(unsigned int); + int (*outbound_p_cfg)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); + int (*inbound_p_cfg) (unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); + void (*outbound_p_show)(FILE*); + void (*inbound_p_show) (FILE*); + void (*reset_bus)(void); + int (*install_irq_mgr)(int, int, int, ...); +} VMEOpsRec, *VMEOps; + +static VMEOpsRec uniOpsRec = { + xlate_adrs: vmeUniverseXlateAddr, + install_isr: vmeUniverseInstallISR, + remove_isr: vmeUniverseRemoveISR, + get_isr: vmeUniverseISRGet, + enable_int_lvl: vmeUniverseIntEnable, + disable_int_lvl: vmeUniverseIntDisable, + outbound_p_cfg: vmeUniverseMasterPortCfg, + inbound_p_cfg: vmeUniverseSlavePortCfg, + outbound_p_show: vmeUniverseMasterPortsShow, + inbound_p_show: vmeUniverseSlavePortsShow, + reset_bus: vmeUniverseResetBus, + install_irq_mgr: vmeUniverseInstallIrqMgrAlt, +}; + +static VMEOpsRec tsiOpsRec = { + xlate_adrs: vmeTsi148XlateAddr, + install_isr: vmeTsi148InstallISR, + remove_isr: vmeTsi148RemoveISR, + get_isr: vmeTsi148ISRGet, + enable_int_lvl: vmeTsi148IntEnable, + disable_int_lvl: vmeTsi148IntDisable, + outbound_p_cfg: vmeTsi148OutboundPortCfg, + inbound_p_cfg: vmeTsi148InboundPortCfg, + outbound_p_show: vmeTsi148OutboundPortsShow, + inbound_p_show: vmeTsi148InboundPortsShow, + reset_bus: vmeTsi148ResetBus, + install_irq_mgr: vmeTsi148InstallIrqMgrAlt, +}; + +static VMEOps theOps = 0; + +int +BSP_vme2local_adrs(unsigned long am, unsigned long vmeaddr, unsigned long *plocaladdr) +{ +int rval=theOps->xlate_adrs(1,0,am,vmeaddr,plocaladdr); + *plocaladdr+=PCI_MEM_BASE; + return rval; +} + +int +BSP_local2vme_adrs(unsigned long am, unsigned long localaddr, unsigned long *pvmeaddr) +{ + return theOps->xlate_adrs(0, 0, am,localaddr+PCI_DRAM_OFFSET,pvmeaddr); +} + +int +BSP_installVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *arg) +{ + return theOps->install_isr(vector, handler, arg); +} + +int +BSP_removeVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *arg) +{ + return theOps->remove_isr(vector, handler, arg); +} + +/* retrieve the currently installed ISR for a given vector */ +BSP_VME_ISR_t +BSP_getVME_isr(unsigned long vector, void **parg) +{ + return theOps->get_isr(vector, parg); +} + +int +BSP_enableVME_int_lvl(unsigned int level) +{ + return theOps->enable_int_lvl(level); +} + +int +BSP_disableVME_int_lvl(unsigned int level) +{ + return theOps->disable_int_lvl(level); +} + +int +BSP_VMEOutboundPortCfg( + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long pci_address, + unsigned long size) +{ + return theOps->outbound_p_cfg(port, address_space, vme_address, pci_address, size); +} + +int +BSP_VMEInboundPortCfg( + unsigned long port, + unsigned long address_space, + unsigned long vme_address, + unsigned long pci_address, + unsigned long size) +{ + return theOps->inbound_p_cfg(port, address_space, vme_address, pci_address, size); +} + +void +BSP_VMEOutboundPortsShow(FILE *f) +{ + theOps->outbound_p_show(f); +} + +void +BSP_VMEInboundPortsShow(FILE *f) +{ + theOps->inbound_p_show(f); +} + +void +BSP_VMEResetBus(void) +{ + theOps->reset_bus(); +} + +static unsigned short +tsi_clear_errors(int quiet) +{ +unsigned long v; +unsigned short rval; + v = vmeTsi148ClearVMEBusErrors(0); + + /* return bits 8..23 of VEAT; set bit 15 to make sure rval is nonzero on error */ + rval = v ? ((v>>8) & 0xffff) | (1<<15) : 0; + return rval; +} + +void +__BSP_default_vme_config(void) +{ +int err = 1; + if ( 0 == vmeUniverseInit() ) { + theOps = &uniOpsRec; + vmeUniverseReset(); + } else if ( 0 == vmeTsi148Init() ) { + theOps = &tsiOpsRec; + vmeTsi148Reset(); + _BSP_clear_vmebridge_errors = tsi_clear_errors; + } else + return; /* no VME bridge found chip */ + + /* map VME address ranges */ + BSP_VMEOutboundPortCfg( + 0, + VME_AM_EXT_SUP_DATA, + _VME_A32_WIN0_ON_VME, + _VME_A32_WIN0_ON_PCI, + 0x0e000000 + ); + BSP_VMEOutboundPortCfg( + 1, + VME_AM_STD_SUP_DATA, + 0x00000000, + _VME_A24_ON_PCI, + 0x00ff0000); + BSP_VMEOutboundPortCfg( + 2, + VME_AM_SUP_SHORT_IO, + 0x00000000, + _VME_A16_ON_PCI, + 0x00010000); + +#ifdef _VME_CSR_ON_PCI + /* Map VME64 CSR */ + BSP_VMEOutboundPortCfg( + 7, + VME_AM_CSR, + 0, + _VME_CSR_ON_PCI, + 0x01000000); +#endif + +#ifdef _VME_DRAM_OFFSET + /* map our memory to VME */ + BSP_VMEInboundPortCfg( + 0, + VME_AM_EXT_SUP_DATA | VME_AM_IS_MEMORY, + _VME_DRAM_OFFSET, + PCI_DRAM_OFFSET, + BSP_mem_size); +#endif + + /* stdio is not yet initialized; the driver will revert to printk */ + BSP_VMEOutboundPortsShow(0); + BSP_VMEInboundPortsShow(0); + + switch (BSP_getBoardType()) { + case MVME6100: + err = theOps->install_irq_mgr( + VMETSI148_IRQ_MGR_FLAG_SHARED, + 0, BSP_IRQ_GPP_0 + 20, + 1, BSP_IRQ_GPP_0 + 21, + 2, BSP_IRQ_GPP_0 + 22, + 3, BSP_IRQ_GPP_0 + 23, + -1); + break; + + case MVME5500: + err = theOps->install_irq_mgr( + VMEUNIVERSE_IRQ_MGR_FLAG_SHARED | + VMEUNIVERSE_IRQ_MGR_FLAG_PW_WORKAROUND, + 0, BSP_IRQ_GPP_0 + 12, + 1, BSP_IRQ_GPP_0 + 13, + 2, BSP_IRQ_GPP_0 + 14, + 3, BSP_IRQ_GPP_0 + 15, + -1); + break; + + default: + printk("WARNING: unknown board; "); + break; + } + if ( err ) + printk("VME interrupt manager NOT INSTALLED (error: %i)\n", err); +} -- cgit v1.2.3