From b599faa1bb56dd5909be179ca56ad74937906823 Mon Sep 17 00:00:00 2001 From: Till Straumann Date: Fri, 14 Dec 2007 06:30:15 +0000 Subject: - imported MVME3100 BSP (from SLAC repository) --- c/src/lib/libbsp/powerpc/mvme3100/.cvsignore | 8 + c/src/lib/libbsp/powerpc/mvme3100/ChangeLog | 1 + c/src/lib/libbsp/powerpc/mvme3100/KNOWN_PROBLEMS | 77 + c/src/lib/libbsp/powerpc/mvme3100/LICENSE | 49 + c/src/lib/libbsp/powerpc/mvme3100/Makefile.am | 184 ++ c/src/lib/libbsp/powerpc/mvme3100/README | 129 + c/src/lib/libbsp/powerpc/mvme3100/bsp_specs | 15 + c/src/lib/libbsp/powerpc/mvme3100/configure.ac | 43 + c/src/lib/libbsp/powerpc/mvme3100/flash/flashcfg.c | 118 + c/src/lib/libbsp/powerpc/mvme3100/i2c/i2c_init.c | 187 ++ .../lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c.c | 452 +++ .../powerpc/mvme3100/i2c/mpc8540_i2c_busdrv.h | 64 + c/src/lib/libbsp/powerpc/mvme3100/include/bsp.h | 329 +++ .../libbsp/powerpc/mvme3100/include/bspopts.h.in | 23 + c/src/lib/libbsp/powerpc/mvme3100/irq/irq.h | 126 + c/src/lib/libbsp/powerpc/mvme3100/irq/irq_init.c | 140 + .../libbsp/powerpc/mvme3100/network/if_tsec_pub.h | 346 +++ c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c | 2920 ++++++++++++++++++++ .../powerpc/mvme3100/pci/detect_host_bridge.c | 115 + c/src/lib/libbsp/powerpc/mvme3100/preinstall.am | 143 + c/src/lib/libbsp/powerpc/mvme3100/start/start.S | 67 + .../lib/libbsp/powerpc/mvme3100/startup/bspstart.c | 480 ++++ c/src/lib/libbsp/powerpc/mvme3100/startup/misc.c | 133 + c/src/lib/libbsp/powerpc/mvme3100/tod/todcfg.c | 28 + c/src/lib/libbsp/powerpc/mvme3100/vme/VMEConfig.h | 116 + 25 files changed, 6293 insertions(+) create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/.cvsignore create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/ChangeLog create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/KNOWN_PROBLEMS create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/LICENSE create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/Makefile.am create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/README create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/bsp_specs create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/configure.ac create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/flash/flashcfg.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/i2c/i2c_init.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c_busdrv.h create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/include/bsp.h create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/include/bspopts.h.in create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/irq/irq.h create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/irq/irq_init.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/network/if_tsec_pub.h create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/pci/detect_host_bridge.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/preinstall.am create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/start/start.S create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/startup/bspstart.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/startup/misc.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/tod/todcfg.c create mode 100644 c/src/lib/libbsp/powerpc/mvme3100/vme/VMEConfig.h diff --git a/c/src/lib/libbsp/powerpc/mvme3100/.cvsignore b/c/src/lib/libbsp/powerpc/mvme3100/.cvsignore new file mode 100644 index 0000000000..849c802af6 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/.cvsignore @@ -0,0 +1,8 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +config.cache +config.log +config.status +configure diff --git a/c/src/lib/libbsp/powerpc/mvme3100/ChangeLog b/c/src/lib/libbsp/powerpc/mvme3100/ChangeLog new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/ChangeLog @@ -0,0 +1 @@ + diff --git a/c/src/lib/libbsp/powerpc/mvme3100/KNOWN_PROBLEMS b/c/src/lib/libbsp/powerpc/mvme3100/KNOWN_PROBLEMS new file mode 100644 index 0000000000..2178c43206 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/KNOWN_PROBLEMS @@ -0,0 +1,77 @@ +I have observed what seem to be strange +initialization problems with the ethernet +driver: + +I usually configure RTEMS networking by +BOOTP (the problem has nothing to do with +BOOTP but I just want to describe my +environment). Sometimes (it can actually +happen quite frequently, like 1 out of 4 +attempts but since yesterday when I decided +to hunt this down more systematically +the problem seems to have gone - typical!) +networking fails to initialize properly: + +BOOTP requests are sent (to the MAC), +TX interrupts occur and the TX MIB +counters increment - i.e., everything +seems normal but no data can be seen on +the wire. Also, even though we are on +a quite busy network, the receiver +doesn't see anything, i.e., 0 RX +interrupts, RX MIB counters for broadcast +packets remain steady at zero etc. +In brief, everyting seems normal at the +MAC and higher layers but no connection +to the wire seems to be established. + +Some further tests reveal (system under +test is in the 'bad' state): + 1 communication with the BCM5461 PHY + is normal. Registers can be read/written + and everything seems normal. In particular, + the link status is reported OK: disconnect + the cable and MII - BMSR bit 1<<2 is clear, + reconnect the cable and BMSR[2] is set. + Restart autoneg, the link goes and comes + back after a short while. + 2 setting the loopback bit in the TSEC's + MACCFG1 register correctly feeds packets + back into the RX, RX MIB counters now + increment and indicate data flow. + There are RX interrupts and all indicates + (I haven't actually looked at RX packet + data) that the RX would work normally. + After switching MACCFG1[LOOP_BACK] off + no RX traffic can be seen anymore. + 3 resetting the PHY (BMCR = 0x8000) and/or + restarting autoneg (BMCR = 0x1200) seems + to perform the desired action (registers + take on expected values) but still no luck + with communication all the way through + to the wire. + +Especially point 2 seems to indicate that +the problem is likely to be between the +wire and the MAC somewhere but re-setting +the PHY doesn't change things. Analysis is +much complicated by the fact that there +is no documentation on the BCM5461 chip +available. + +Noteworthy is also that if the system +initializes OK then it continues to work +normally; if initialization fails then +only resetting the board and restarting +helps. + +I wanted to test if it makes a difference +if MotLoad used the chip prior to RTEMS +being booted (in case MotLoad did some +magic step during initialization) but +before I could really test this the +problem went away. + +Big Mystery... + +12/12/2007, T.S. diff --git a/c/src/lib/libbsp/powerpc/mvme3100/LICENSE b/c/src/lib/libbsp/powerpc/mvme3100/LICENSE new file mode 100644 index 0000000000..25a6abc81c --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/LICENSE @@ -0,0 +1,49 @@ +/* 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. + */ + +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was + * created by Till Straumann , 2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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/mvme3100/Makefile.am b/c/src/lib/libbsp/powerpc/mvme3100/Makefile.am new file mode 100644 index 0000000000..8f4035a5b9 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/Makefile.am @@ -0,0 +1,184 @@ +## +## $Id$ +## + +ACLOCAL_AMFLAGS = -I ../../../../aclocal + +# wrapup is the one that actually builds and installs the library +# from the individual .rel files built in other directories +SUBDIRS = . + +include $(top_srcdir)/../../../../automake/compile.am +include $(top_srcdir)/../../bsp.am + +dist_project_lib_DATA = bsp_specs + +include_HEADERS = include/bsp.h + +nodist_include_HEADERS = include/bspopts.h +DISTCLEANFILES = include/bspopts.h + +noinst_PROGRAMS = + +include_bspdir = $(includedir)/bsp + +include_HEADERS += ../../shared/include/coverhd.h +include_HEADERS += ../../shared/tod.h + +project_lib_DATA = + +EXTRA_DIST = ./start/start.S +start.$(OBJEXT): ./start/start.S + $(CPPASCOMPILE) -o $@ -c $< + +EXTRA_DIST += ../beatnik/start/ssrl/preload.S +preload.$(OBJEXT): ../beatnik/start/ssrl/preload.S + $(CPPASCOMPILE) -DASM -o $@ -c $< + +motld_start.$(OBJEXT): preload.$(OBJEXT) start.$(OBJEXT) + $(LD) -o $@ -r $^ + +project_lib_DATA += motld_start.$(OBJEXT) + +EXTRA_DIST += ../../powerpc/shared/start/rtems_crti.S +rtems_crti.$(OBJEXT): ../../powerpc/shared/start/rtems_crti.S + $(CPPASCOMPILE) -o $@ -c $< +project_lib_DATA += rtems_crti.$(OBJEXT) + +dist_project_lib_DATA += ../shared/startup/linkcmds + +build_date.c:: + echo 'const char *BSP_build_date="'`date`'";' > $@ + +noinst_PROGRAMS += startup.rel +startup_rel_SOURCES = ./startup/bspstart.c build_date.c \ + ./startup/misc.c \ + ../../powerpc/shared/startup/pretaskinghook.c \ + ../../powerpc/shared/startup/zerobss.c \ + ../../powerpc/shared/startup/sbrk.c ../../shared/bootcard.c \ + ../../shared/bspclean.c ../../shared/bsplibc.c ../../shared/bsppost.c \ + ../../shared/gnatinstallhandler.c +startup_rel_CPPFLAGS = $(AM_CPPFLAGS) +startup_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +noinst_PROGRAMS += tod.rel +tod_rel_SOURCES = ../../shared/tod.c tod/todcfg.c +tod_rel_CPPFLAGS = $(AM_CPPFLAGS) +tod_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +noinst_PROGRAMS += pclock.rel +pclock_rel_SOURCES = ../../powerpc/shared/clock/p_clock.c +pclock_rel_CPPFLAGS = $(AM_CPPFLAGS) +pclock_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS = ../../powerpc/shared/console/uart.h +noinst_PROGRAMS += console.rel +console_rel_SOURCES = ../../powerpc/shared/console/uart.c \ + ../../powerpc/shared/console/console.c \ + ../../powerpc/shared/console/consoleIo.h \ + ../../powerpc/shared/console/uart.h +console_rel_CPPFLAGS = $(AM_CPPFLAGS) $(console_CPPFLAGS) +console_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS += ./irq/irq.h + +noinst_PROGRAMS += irq.rel +irq_rel_SOURCES = ./irq/irq_init.c ../../powerpc/shared/irq/openpic_i8259_irq.c \ + ../../powerpc/shared/irq/irq.h +irq_rel_CPPFLAGS = $(AM_CPPFLAGS) +irq_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + + +include_bsp_HEADERS += ../../powerpc/shared/openpic/openpic.h + +noinst_PROGRAMS += openpic.rel +openpic_rel_SOURCES = ../../powerpc/shared/openpic/openpic.h \ + ../../powerpc/shared/openpic/openpic.c \ + ../../powerpc/shared/openpic/openpic.h + +openpic_rel_CPPFLAGS = $(AM_CPPFLAGS) +openpic_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS += ../../powerpc/shared/pci/pci.h + +noinst_PROGRAMS += pci.rel +pci_rel_SOURCES = ../../powerpc/shared/pci/pci.c \ + ./pci/detect_host_bridge.c \ + ../../powerpc/shared/pci/generic_clear_hberrs.c \ + ../../powerpc/shared/pci/pcifinddevice.c ../../powerpc/shared/pci/pci.h +pci_rel_CPPFLAGS = $(AM_CPPFLAGS) +pci_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS += ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/vectors.h +include_bsp_HEADERS += ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/ppc_exc_bspsupp.h +include_bsp_HEADERS += ../../../libcpu/@RTEMS_CPU@/@exceptions@/bspsupport/irq_supp.h + +include_bsp_HEADERS += ./vme/VMEConfig.h \ + ../../shared/vmeUniverse/vmeTsi148.h \ + ../../shared/vmeUniverse/vme_am_defs.h \ + ../../shared/vmeUniverse/VME.h \ + ../../shared/vmeUniverse/vmeTsi148DMA.h\ + ../../shared/vmeUniverse/bspVmeDmaList.h\ + ../../shared/vmeUniverse/VMEDMA.h + +noinst_PROGRAMS += vme.rel +vme_rel_SOURCES = ../../shared/vmeUniverse/vmeTsi148.c \ + ../../shared/vmeUniverse/bspVmeDmaList.c \ + ../../shared/vmeUniverse/vmeTsi148.h \ + ../../shared/vmeUniverse/vme_am_defs.h \ + ../../shared/vmeUniverse/VME.h \ + ../../powerpc/shared/vme/vmeconfig.c \ + ../../powerpc/shared/vme/vme_universe.c + +vme_rel_CPPFLAGS = $(AM_CPPFLAGS) +vme_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS += ../shared/flash/flashPgm.h \ + ../shared/flash/flashPgmPvt.h + +noinst_PROGRAMS += flash.rel +flash_rel_SOURCES = ../shared/flash/flash.c \ + ../shared/flash/spansionFlash.c \ + ./flash/flashcfg.c + +flash_rel_CPPFLAGS = $(AM_CPPFLAGS) +flash_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + + +include_bsp_HEADERS += i2c/mpc8540_i2c_busdrv.h +noinst_PROGRAMS += i2c.rel +i2c_rel_SOURCES = i2c/mpc8540_i2c.c i2c/i2c_init.c +i2c_rel_CPPFLAGS = $(AM_CPPFLAGS) +i2c_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS += ../shared/motorola/vpd.h +noinst_PROGRAMS += vpd.rel +vpd_rel_SOURCES = ../shared/motorola/vpd.c +vpd_rel_CPPFLAGS = $(AM_CPPFLAGS) +vpd_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + +include_bsp_HEADERS += network/if_tsec_pub.h +noinst_PROGRAMS += network.rel +network_rel_SOURCES = network/tsec.c +network_rel_CPPFLAGS = $(AM_CPPFLAGS) +network_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + + +noinst_LIBRARIES = libbsp.a +libbsp_a_SOURCES = +libbsp_a_LIBADD = startup.rel pclock.rel console.rel openpic.rel \ + pci.rel irq.rel i2c.rel tod.rel vpd.rel network.rel vme.rel flash.rel + +libbsp_a_LIBADD += ../../../libcpu/@RTEMS_CPU@/shared/cpuIdent.rel \ + ../../../libcpu/@RTEMS_CPU@/shared/stack.rel \ + ../../../libcpu/@RTEMS_CPU@/e500/clock.rel \ + ../../../libcpu/@RTEMS_CPU@/e500/timer.rel \ + ../../../libcpu/@RTEMS_CPU@/@exceptions@/rtems-cpu.rel \ + ../../../libcpu/@RTEMS_CPU@/@exceptions@/raw_exception.rel \ + ../../../libcpu/@RTEMS_CPU@/@exceptions@/exc_bspsupport.rel \ + ../../../libcpu/@RTEMS_CPU@/@exceptions@/irq_bspsupport.rel + +EXTRA_DIST += LICENSE ChangeLog README KNOWN_PROBLEMS + +include $(srcdir)/preinstall.am +include $(top_srcdir)/../../../../automake/local.am diff --git a/c/src/lib/libbsp/powerpc/mvme3100/README b/c/src/lib/libbsp/powerpc/mvme3100/README new file mode 100644 index 0000000000..5fff588ea9 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/README @@ -0,0 +1,129 @@ +Some information about this BSP +================================ + +ACKNOWLEDGEMENTS +---------------- +Acknowledgements: + + Valuable information was obtained from the following drivers + + linux: (BCM54xx) Maciej W. Rozycki, Amy Fong. + + 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. + + This BSP was produced by the Stanford Linear Accelerator Center, + Stanford University under contract with the US Department of Energy. + +LICENSE +------- +See ./LICENSE file. + +Note that not all files that are part of this BSP were written by +myself. Consult individual file headers for copyright +and authorship information. + +HARDWARE SUPPORT +=============== +(some of the headers mentioned below contain more +detailed information) + +NOTE: The BSP supports the mvme3100 board. + +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 openpic timer + could be used.) The bookE decrementer is slightly different + from the classic PPC decrementer but the differences are + hidden from the user. + +PIC (interrupt controller) (bsp/irq.h): OpenPIC integrated with + the MPC8540. (see also: bsp/openpic.h). + +PCI (bsp/pci.h): + 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: MotLoad; 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: No NVRAM. + +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, eeprom + and real-time clock (RTC) are available as device files (bsp.h); + lower-level interface is provided by libi2c.h. + + Available i2c devices are: + + /dev/i2c0.vpd-eeprom + /dev/i2c0.usr-eeprom + /dev/i2c0.usr1-eeprom + /dev/i2c0.ds1621 + /dev/i2c0.ds1621-raw + /dev/i2c0.ds1375-raw + + You can e.g., 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 driver + (vmeTsi148.h) directly unless you know what you are + doing (i.e., if you need specific features provided by the particular + chip) + + 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 3100 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 bsp/openpic.h) 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/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/openpic.h). Programmable general-purpose + 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. + +NETWORK: (bsp/if_tsec_pub.h). In addition to the standard bsdnet + 'attach' function the driver offers a low-level API that + can be used to implement alternate communication links + which are totally decoupled from BSDNET. + + Consult 'KNOWN_PROBLEMS'. + +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 , 2007. diff --git a/c/src/lib/libbsp/powerpc/mvme3100/bsp_specs b/c/src/lib/libbsp/powerpc/mvme3100/bsp_specs new file mode 100644 index 0000000000..0fff686076 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/bsp_specs @@ -0,0 +1,15 @@ +%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 \ +%{!qrtems_debug: motld_start.o%s} \ +%{qrtems_debug: motld_start_g.o%s}}} + +*link: +%{!qrtems: %(old_link)} %{qrtems: -Qy -dp -Bstatic -e __rtems_entry_point -u __vectors} + +*endfile: +%{!qrtems: %(old_endfile)} %{qrtems: crtend.o%s ecrtn.o%s} diff --git a/c/src/lib/libbsp/powerpc/mvme3100/configure.ac b/c/src/lib/libbsp/powerpc/mvme3100/configure.ac new file mode 100644 index 0000000000..f62e62f1bd --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/configure.ac @@ -0,0 +1,43 @@ +## Process this file with autoconf to produce a configure script. +## +## $Id$ + +AC_PREREQ(2.60) +AC_INIT([rtems-c-src-lib-libbsp-powerpc-mvme3100],[_RTEMS_VERSION],[http://www.rtems.org/bugzilla]) +AC_CONFIG_SRCDIR([bsp_specs]) +RTEMS_TOP(../../../../../..) + +RTEMS_CANONICAL_TARGET_CPU +AM_INIT_AUTOMAKE([no-define nostdinc foreign 1.10]) +RTEMS_BSP_CONFIGURE + +RTEMS_PROG_CC_FOR_TARGET([-ansi -fasm]) +RTEMS_CANONICALIZE_TOOLS +RTEMS_CHECK_TOOL(NM,nm,no) +RTEMS_PROG_CCAS + +RTEMS_CHECK_NETWORKING +AM_CONDITIONAL(HAS_NETWORKING,test "$HAS_NETWORKING" = "yes") + +AS=$CC +AM_PROG_AS + +dnl AC_PATH_PROG([AMPOLISH3],[ampolish3],[]) +dnl AM_CONDITIONAL([AMPOLISH3],[test x"$USE_MAINTAINER_MODE" = x"yes" \ +dnl && test -n "$AMPOLISH3"]) + +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/mvme3100/flash/flashcfg.c b/c/src/lib/libbsp/powerpc/mvme3100/flash/flashcfg.c new file mode 100644 index 0000000000..217282d026 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/flash/flashcfg.c @@ -0,0 +1,118 @@ +/* $Id$ */ + +/* BSP-specific bits of flash programmer support */ + +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was created by + * + * Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 + +SPR_RO(TBRL) + +#define STATIC static + +static struct bankdesc mvme3100Flash[] = { + /* + * Bank is populated from the top; make max_size negative to + * indicate this + */ + { 0xf8000000, 0, - 0x08000000, 2, BSP_flash_vendor_spansion, 0, 0, 0 }, +}; + +STATIC struct bankdesc * +bankcheck(int bank, int quiet) +{ + if ( bank ) { + if ( !quiet ) + fprintf(stderr,"Invalid flash bank #%i\n",bank); + return 0; + } + return &mvme3100Flash[bank]; +} + +STATIC int +flash_wp(int bank, int enbl) +{ +uint8_t mask = enbl < 0 ? 0 : BSP_MVME3100_FLASH_CSR_F_WP_SW; +uint8_t val; + + if ( bank != 0 ) { + fprintf(stderr,"Invalid flash bank #%i\n",bank); + return -1; + } + + if ( enbl ) + val = BSP_setSysReg( BSP_MVME3100_FLASH_CSR, mask ); + else + val = BSP_clrSysReg( BSP_MVME3100_FLASH_CSR, mask ); + + if ( BSP_MVME3100_FLASH_CSR_F_WP_HW & val ) { + fprintf(stderr,"Flash: hardware write-protection engaged (switch)\n"); + return -1; + } + if ( enbl < 0 ) + return val & (BSP_MVME3100_FLASH_CSR_F_WP_HW | BSP_MVME3100_FLASH_CSR_F_WP_SW ); + return 0; +} + +STATIC uint32_t +read_us_timer() +{ +uint32_t mhz = BSP_bus_frequency/BSP_time_base_divisor/1000; + + return _read_TBRL()/mhz; +} + +/* BSP ops (detect banks, handle write-protection on board) */ +struct flash_bsp_ops BSP_flashBspOps = { + bankcheck: bankcheck, + flash_wp: flash_wp, + read_us_timer: read_us_timer, +}; diff --git a/c/src/lib/libbsp/powerpc/mvme3100/i2c/i2c_init.c b/c/src/lib/libbsp/powerpc/mvme3100/i2c/i2c_init.c new file mode 100644 index 0000000000..61eae8b1b7 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/i2c/i2c_init.c @@ -0,0 +1,187 @@ +/* $Id$ */ + +/* Register i2c bus driver & devices */ + +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was created by + * + * Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 + +static void +safe_printf (const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if ( _System_state_Is_up( _System_state_Get() ) ) + vfprintf( stderr, fmt, ap ); + else + vprintk( fmt, ap ); + va_end(ap); +} + +static void +safe_perror(const char *s) +{ + safe_printf("%s :%s\n", s, strerror(errno)); +} + + +int +BSP_i2c_initialize() +{ +int busno, succ = 0; + + /* Initialize the library */ + if ( rtems_libi2c_initialize() ) { + safe_printf("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 ) { + safe_perror("Registering mpc8540 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 ) { + safe_perror("Registering i2c VPD eeprom driver failed"); + } else { + succ++; + } + + /* 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_USR0_I2C_ADDR) < 0 ) { + safe_perror("Registering i2c 1st USR eeprom driver failed"); + } else { + succ++; + } + + /* 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_USR1_EEPROM_NAME, + i2c_2b_eeprom_driver_descriptor, + busno, + BSP_USR1_I2C_ADDR) < 0 ) { + safe_perror("Registering i2c 2nd USR eeprom driver failed"); + } else { + succ++; + } + + /* The thermostat */ + if ( rtems_libi2c_register_drv( + BSP_I2C_DS1621_NAME, + i2c_ds1621_driver_descriptor, + busno, + BSP_THM_I2C_ADDR) < 0 ) { + safe_perror("Registering i2c ds1621 temp sensor. driver failed"); + } else { + succ++; + } + + /* 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))) ) { + safe_perror("Creating device node for raw ds1621 (temp. sensor) access failed"); + } else { + succ++; + } + + /* Raw access to RTC */ + if ( mknod( + BSP_I2C_DS1375_RAW_DEV_NAME, + 0666 | S_IFCHR, + rtems_filesystem_make_dev_t(rtems_libi2c_major, + RTEMS_LIBI2C_MAKE_MINOR(busno,BSP_RTC_I2C_ADDR))) ) { + safe_perror("Creating device node for raw ds1375 (rtc) access failed"); + } else { + succ++; + } + + safe_printf("%i I2C devices registered\n", succ); + return 0; +} diff --git a/c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c.c b/c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c.c new file mode 100644 index 0000000000..7512ef5354 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c.c @@ -0,0 +1,452 @@ +/* I2C bus driver for mpc8540-based boards */ + +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was created by + * + * Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 + */ + +/* Note: We maintain base address, IRQ etc. statically and + * globally. We don't bother creating driver-specific + * data or using the bus handle but simply assume + * this is the only 8540/i2c bus in the system. + * Proper support for multiple instances would not + * be very hard to add but I don't see the point... + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mpc8540_i2c_busdrv.h" + +#define STATIC + +/* I2C controller register definitions */ +#define I2CADR 0x3000 +#define I2CFDR 0x3004 +#define I2CCR 0x3008 +#define I2CCR_MEN (1<<(7-0)) +#define I2CCR_MIEN (1<<(7-1)) +#define I2CCR_MSTA (1<<(7-2)) +#define I2CCR_MTX (1<<(7-3)) +#define I2CCR_TXAK (1<<(7-4)) +#define I2CCR_RSTA (1<<(7-5)) +#define I2CCR_BCST (1<<(7-7)) +#define I2CSR 0x300c +#define I2CSR_MCF (1<<(7-0)) +#define I2CSR_MAAS (1<<(7-1)) +#define I2CSR_MBB (1<<(7-2)) +#define I2CSR_MAL (1<<(7-3)) +#define I2CSR_BCSTM (1<<(7-4)) +#define I2CSR_SRW (1<<(7-5)) +#define I2CSR_MIF (1<<(7-6)) +#define I2CSR_RXAK (1<<(7-7)) +#define I2CDR 0x3010 +#define I2CDFSRR 0x3014 + +SPR_RO(TBRL) + +/********* Global Variables **********/ + +/* + * Semaphore for synchronizing accessing task + * with the (slow) hardware operation. + * Task takes semaphore and blocks, ISR releases. + */ +static rtems_id syncsem = 0; + +static inline int ok_to_block() +{ + return syncsem && _System_state_Is_up( _System_state_Get() ); +} + +/* + * Wild guess for 0.2 s; this timeout is effective + * in polling mode; during early init we don't know + * the system clock rate yet - it's one of the things + * we have to read from VPD -- via i2c. + */ + +static uint32_t poll_timeout = 333333333/8/5; + +/********* Primitives ****************/ + +static inline uint8_t +i2c_rd(unsigned reg) +{ + return in_8( (volatile uint8_t *)(BSP_8540_CCSR_BASE + reg) ); +} + +static inline void +i2c_wr(unsigned reg, uint8_t val) +{ + out_8( (volatile uint8_t *)(BSP_8540_CCSR_BASE + reg), val ); +} + +static inline void +i2c_set(unsigned reg, uint8_t val) +{ + i2c_wr( reg, i2c_rd( reg ) | val ); +} + +static inline void +i2c_clr(unsigned reg, uint8_t val) +{ + i2c_wr( reg, i2c_rd( reg ) & ~val ); +} + +/********* Helper Routines ***********/ + +/* Synchronize (wait) for a condition on the + * i2c bus. Wait for START or STOP to be complete + * or wait for a byte-transfer. + * The latter is much slower (9 bit times vs. 1/2 + * in the former cases). + * + * If the system is up (and we may block) then + * this routine attempts to block the current + * task rather than busy-waiting. + * + * NOTE: waiting for START/STOP always requires + * polling. + */ + +/* wait until i2c status reg AND mask == cond */ +static rtems_status_code +i2c_wait( uint8_t msk, uint8_t cond ) +{ +uint32_t then; +rtems_status_code sc; +static int warn = 0; + + if ( I2CSR_MIF == msk && ok_to_block() ) { + /* block on semaphore only if system is up and sema initialized */ + sc = rtems_semaphore_obtain( syncsem, RTEMS_WAIT, 100 ); + if ( RTEMS_SUCCESSFUL != sc ) + return sc; + } else { + /* system not up (no SEMA yet ) or waiting on something other + * than MIF + */ + if ( I2CSR_MIF == msk && _System_state_Is_up( _System_state_Get() ) ) { + if ( warn < 8 || ! (warn & 0x1f) ) + printk("WARNING: i2c bus driver running in polled mode -- should initialize properly!\n"); + warn++; + } + + then = _read_TBRL(); + do { + /* poll for .2 seconds */ + if ( (_read_TBRL() - then) > poll_timeout ) + return RTEMS_TIMEOUT; + } while ( (msk & i2c_rd( I2CSR )) != cond ); + } + + return RTEMS_SUCCESSFUL; +} + +/* + * multi-byte transfer + * - set transfer direction (master read or master write) + * - transfer byte + * - wait/synchronize + * - check for ACK + * + * RETURNS: number of bytes transferred or negative error code. + */ + +STATIC int +i2c_xfer(int rw, uint8_t *buf, int len) +{ +int i; +rtems_status_code sc; + + if ( rw ) { + i2c_clr( I2CCR, I2CCR_MTX ); + } else { + i2c_set( I2CCR, I2CCR_MTX ); + } + + for ( i = 0; i< len; i++ ) { + i2c_clr( I2CSR, I2CSR_MIF ); + /* Enable interrupts if necessary */ + if ( ok_to_block() ) + i2c_set( I2CCR, I2CCR_MIEN ); + if ( rw ) { + buf[i] = i2c_rd( I2CDR ); + } else { + i2c_wr( I2CDR, buf[i] ); + } + if ( RTEMS_SUCCESSFUL != (sc = i2c_wait( I2CSR_MIF, I2CSR_MIF )) ) + return -sc; + if ( (I2CSR_RXAK & i2c_rd( I2CSR )) ) { + /* NO ACK */ + return -RTEMS_IO_ERROR; + } + } + + return i; +} + +/* + * This bus controller gives us lagging data, i.e., + * when we read a byte from the data reg then that + * issues a read cycle on the bus and gives us the + * byte from the *previous* read cycle :-( + * + * This makes it impossible to properly terminate + * a read transaction w/o knowing ahead of time + * how many bytes are going to be read (API decouples + * 'START'/'STOP' from 'READ') since we would have to + * set TXAK when reading the next-to-last byte + * (i.e., when the last byte is read on the i2c bus). + * + * Hence, (if we are reading) we must do a dummy + * read-cycle here -- hopefully + * that has no side-effects! (i.e., EEPROM drivers should + * reposition file pointers after issuing STOP) + * + */ + +static void +rd1byte_noack() +{ +uint8_t dum; +uint8_t ccr; + + /* If we are in reading state then read one more + * byte w/o acknowledge + */ + + ccr = i2c_rd (I2CCR ); + + if ( ! ( I2CCR_MTX & ccr ) ) { + i2c_wr( I2CCR, ccr | I2CCR_TXAK ); + i2c_xfer(1, &dum, 1); + /* restore original TXAK bit setting */ + i2c_clr( I2CCR, (I2CCR_TXAK & ccr) ); + } +} + + +/********* ISR ***********************/ + +static void i2c_isr(rtems_irq_hdl_param arg) +{ + /* disable irq */ + i2c_clr( I2CCR, I2CCR_MIEN ); + /* release task */ + rtems_semaphore_release( syncsem ); +} + +/********* IIC Bus Driver Ops ********/ + +STATIC rtems_status_code +i2c_init(rtems_libi2c_bus_t *bh) +{ +rtems_status_code sc; + + /* compute more accurate timeout */ + if ( BSP_bus_frequency && BSP_time_base_divisor ) + poll_timeout = BSP_bus_frequency/BSP_time_base_divisor*1000/5; + + i2c_clr( I2CCR, I2CCR_MEN ); + i2c_set( I2CCR, I2CCR_MEN ); + + i2c_wr( I2CADR, 0 ); + + /* leave motload settings for divisor and filter registers */ + + if ( SYSTEM_STATE_BEFORE_MULTITASKING <= _System_state_Get() && !syncsem ) { + sc = rtems_semaphore_create( + rtems_build_name('i','2','c','b'), + 0, + RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_LOCAL, + 0, + &syncsem); + if ( RTEMS_SUCCESSFUL == sc ) { + rtems_irq_connect_data xxx; + xxx.name = BSP_I2C_IRQ; + xxx.on = 0; + xxx.off = 0; + xxx.isOn = 0; + xxx.hdl = i2c_isr; + xxx.handle = 0; + if ( ! BSP_install_rtems_irq_handler( &xxx ) ) { + printk("Unable to install i2c ISR -- falling back to polling mode\n"); + rtems_semaphore_delete( syncsem ); + /* fall back to polling mode */ + syncsem = 0; + } + } else { + syncsem = 0; + } + } + + return RTEMS_SUCCESSFUL; +} + +STATIC rtems_status_code +i2c_start(rtems_libi2c_bus_t *bh) +{ +uint8_t v; +rtems_status_code sc = RTEMS_SUCCESSFUL; + + v = i2c_rd( I2CCR ); + if ( I2CCR_MSTA & v ) { + /* RESTART */ + rd1byte_noack(); + v |= I2CCR_RSTA; + } else { + v |= I2CCR_MSTA; + } + i2c_wr( I2CCR, v ); + + /* On MBB we can only poll-wait (no IRQ is generated) + * and this is also much faster than reading a byte + * (1/2-bit time) so the overhead of an IRQ may not + * be justified. + * OTOH, we can put this off into the 'send_addr' routine + * + + sc = i2c_wait( I2CSR_MBB, I2CSR_MBB ); + */ + + return sc; +} + +STATIC rtems_status_code +i2c_stop(rtems_libi2c_bus_t *bh) +{ + rd1byte_noack(); + + /* STOP */ + i2c_clr( I2CCR, I2CCR_TXAK | I2CCR_MSTA ); + + /* FIXME: should we really spend 1/2 bit-time polling + * or should we just go ahead and hope noone + * else will get a chance to do something to + * the bus until the STOP completes? + */ + return i2c_wait( I2CSR_MBB, 0 ); +} + +STATIC rtems_status_code +i2c_send_addr(rtems_libi2c_bus_t *bh, uint32_t addr, int rw) +{ +uint8_t buf[2]; +int l = 0; +uint8_t read_mask = rw ? 1 : 0; +rtems_status_code sc; + + /* Make sure we are started; (i2c_start() didn't bother to wait + * so we do it here - some time already has expired. + */ + sc = i2c_wait( I2CSR_MBB, I2CSR_MBB ); + + if ( RTEMS_SUCCESSFUL != sc ) + return sc; + + if ( addr > 0x7f ) { + /* 10-bit request; 1st address byte is 0b11110 */ + buf[l] = 0xf0 | ((addr >> 7) & 0x06) | read_mask; + read_mask = 0; + l++; + buf[l] = addr & 0xff; + } else { + buf[l] = (addr << 1) | read_mask; + l++; + } + + /* + * After sending a an address for reading we must + * read a dummy byte (this actually clocks the first real + * byte on the i2c bus and makes it available in the + * data register so that the first 'read_bytes' operation + * obtains the byte we clock in here [and starts clocking + * the second byte]) to overcome the pipeline + * delay in the hardware (I don't like this design) :-(. + */ + sc = i2c_xfer( 0, buf, l ); + if ( rw && l == sc ) { + sc = i2c_xfer( 1, buf, 1 ); + } + return sc >=0 ? RTEMS_SUCCESSFUL : -sc; +} + +STATIC int +i2c_read_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len) +{ + return i2c_xfer( 1, buf, len ); +} + +STATIC int +i2c_write_bytes(rtems_libi2c_bus_t *bh, unsigned char *buf, int len) +{ + return i2c_xfer( 0, buf, len ); +} + +/********* Driver Glue Vars **********/ + +static rtems_libi2c_bus_ops_t myops = { + init: i2c_init, + send_start: i2c_start, + send_stop: i2c_stop, + send_addr: i2c_send_addr, + read_bytes: i2c_read_bytes, + write_bytes: i2c_write_bytes, +}; + +static rtems_libi2c_bus_t my_bus_tbl = { + ops: &myops, + size: sizeof(my_bus_tbl), +}; + +/********* Global Driver Handle ******/ + +rtems_libi2c_bus_t *mpc8540_i2c_bus_descriptor = &my_bus_tbl; diff --git a/c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c_busdrv.h b/c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c_busdrv.h new file mode 100644 index 0000000000..96921c19d8 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/i2c/mpc8540_i2c_busdrv.h @@ -0,0 +1,64 @@ +#ifndef MPC8540_I2C_BUS_DRIVER_H +#define MPC8540_I2C_BUS_DRIVER_H + +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was created by + * + * Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 *mpc8540_i2c_bus_descriptor; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/mvme3100/include/bsp.h b/c/src/lib/libbsp/powerpc/mvme3100/include/bsp.h new file mode 100644 index 0000000000..e36d307e59 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/include/bsp.h @@ -0,0 +1,329 @@ +/* + * 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. + * + * Adapted for the mvme3100 BSP by T. Straumann, 2007. + * + * $Id$ + */ +#ifndef _BSP_H +#define _BSP_H + +#include + +#include +#include +#include +#include + +/* + * 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 CONFIGURE_INTERRUPT_STACK_MEMORY (16 * 1024) + +/* + * diagram illustrating the role of the configuration + * constants + * PCI_MEM_WIN0: CPU starting addr where PCI memory space is visible + * PCI_MEM_BASE: CPU address of PCI mem addr. zero. (regardless of this + * address being 'visible' or not!). + * _VME_A32_WIN0_ON_PCI: PCI starting addr of the 1st window to VME + * _VME_A32_WIN0_ON_VME: VME address of that same window + * + * AFAIK, only PreP boards have a non-zero PCI_MEM_BASE (i.e., an offset between + * CPU and PCI addresses). The mvme2300 'ppcbug' firmware configures the PCI + * bus using PCI base addresses! I.e., drivers need to add PCI_MEM_BASE to + * the base address read from PCI config.space in order to translate that + * into a CPU address. + * + * NOTE: VME addresses should NEVER be translated using these constants! + * they are strictly for BSP internal use. Drivers etc. should use + * the translation routines int VME.h (BSP_vme2local_adrs/BSP_local2vme_adrs). + * + * CPU ADDR PCI_ADDR VME ADDR + * + * 00000000 XXXXXXXX XXXXXXXX + * ^ ^ ........ + * | | + * | | e.g., RAM XXXXXXXX + * | | 00000000 + * | | ......... ^ + * | | (possible offset | + * | | between pci and XXXXXXXX | ...... + * | | cpu addresses) | + * | v | + * | PCI_MEM_BASE -------------> 00000000 --------------- | + * | ........ ........ ^ | + * | invisible | | + * | ........ from CPU | | + * v | | + * PCI_MEM_WIN0 ============= first visible PCI addr | | + * | | + * pci devices pci window | | + * visible here v v + * mapped by ========== _VME_A32_WIN0_ON_PCI ======= _VME_A32_WIN0_ON_VME + * vme window + * VME devices hostbridge mapped by + * visible here universe + * ===================================================== + * + */ + +/* fundamental addresses for BSP (CHRPxxx and PREPxxx are from libcpu/io.h) */ +#define _IO_BASE 0xe0000000 /* Motload's PCI IO base */ +#define _ISA_MEM_BASE CHRP_ISA_MEM_BASE +/* address of our ram on the PCI bus */ +#define PCI_DRAM_OFFSET CHRP_PCI_DRAM_OFFSET +/* offset of pci memory as seen from the CPU */ +#define PCI_MEM_BASE 0 +/* where (in CPU addr. space) does the PCI window start */ +#define PCI_MEM_WIN0 0x80000000 + +/* + * Base address definitions for several devices + */ + +#define BSP_OPEN_PIC_BASE_OFFSET 0x40000 +#define BSP_OPEN_PIC_BIG_ENDIAN + +#define BSP_8540_CCSR_BASE (0xe1000000) + +#define BSP_UART_IOBASE_COM1 (BSP_8540_CCSR_BASE+0x4500) +#define BSP_UART_IOBASE_COM2 (BSP_8540_CCSR_BASE+0x4600) +#define PCI_CONFIG_ADDR (BSP_8540_CCSR_BASE+0x8000) +#define PCI_CONFIG_DATA (BSP_8540_CCSR_BASE+0x8004) +#define PCI_CONFIG_WR_ADDR( addr, val ) out_be32((unsigned int*)(addr), (val)) + +#define BSP_CONSOLE_PORT BSP_UART_COM1 +#define BSP_UART_BAUD_BASE (-9600) /* use existing divisor to determine clock rate */ +#define BSP_UART_USE_SHARED_IRQS + +#define BSP_MVME3100_IRQ_DETECT_REG ((volatile uint8_t *)0xe2000007) + +/* 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_USR0_I2C_ADDR (0xA4>>1) /* the 1st user EEPROM */ +#define BSP_USR1_I2C_ADDR (0xA6>>1) /* the 2nd user EEPROM */ +#define BSP_THM_I2C_ADDR (0x90>>1) /* the DS1621 temperature sensor & thermostat */ +#define BSP_RTC_I2C_ADDR (0xD0>>1) /* the DS1375 wall-clock */ + +#define BSP_I2C_BUS_DESCRIPTOR mpc8540_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_USR1_EEPROM_NAME "usr1-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_DS1375_RAW_NAME "ds1375-raw" +#define BSP_I2C_RTC_RAW_NAME BSP_I2C_DS1375_RAW_NAME + +#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_USR1_EEPROM_DEV_NAME (BSP_I2C_BUS0_NAME"."BSP_I2C_USR1_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) +#define BSP_I2C_DS1375_RAW_DEV_NAME (BSP_I2C_BUS0_NAME"."BSP_I2C_DS1375_RAW_NAME) + +/* Definitions useful for bootloader (netboot); where to find + * boot/'environment' parameters. + */ +#define BSP_EEPROM_BOOTPARMS_NAME BSP_I2C_USR1_EEPROM_DEV_NAME +#define BSP_EEPROM_BOOTPARMS_SIZE 1024 +#define BSP_EEPROM_BOOTPARMS_OFFSET 0 +#define BSP_BOOTPARMS_WRITE_ENABLE() do { BSP_eeprom_write_enable(); } while (0) +#define BSP_BOOTPARMS_WRITE_DISABLE() do { BSP_eeprom_write_protect();} while (0) + + +#ifdef __cplusplus +extern "C" { +#endif +/* 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.usr1-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) + * /dev/i2c0.ds1375-raw (read-write; transfer bytes to/from the ds1375) + * + */ +int +BSP_i2c_initialize(); + +/* Misc utility definitions and routines */ + +void +rtemsReboot(); + +/* System Control Register */ +#define BSP_MVME3100_SYS_CR ((volatile uint8_t *)0xe2000001) +#define BSP_MVME3100_SYS_CR_RESET_MSK (7<<5) +#define BSP_MVME3100_SYS_CR_RESET (5<<5) +#define BSP_MVME3100_SYS_CR_EEPROM_WP (1<<1) +#define BSP_MVME3100_SYS_CR_TSTAT_MSK (1<<0) + +/* LED support */ +#define BSP_MVME3100_SYS_IND_REG ((volatile uint8_t *)0xe2000002) +#define BSP_LED_BRD_FAIL (1<<0) +#define BSP_LED_USR1 (1<<1) +#define BSP_LED_USR2 (1<<2) +#define BSP_LED_USR3 (1<<3) + +/* Flash CSR */ +#define BSP_MVME3100_FLASH_CSR ((volatile uint8_t *)0xe2000003) +#define BSP_MVME3100_FLASH_CSR_FLASH_RDY (1<<0) +#define BSP_MVME3100_FLASH_CSR_FBT_BLK_SEL (1<<1) +#define BSP_MVME3100_FLASH_CSR_F_WP_HW (1<<2) +#define BSP_MVME3100_FLASH_CSR_F_WP_SW (1<<3) +#define BSP_MVME3100_FLASH_CSR_MAP_SEL (1<<4) + +/* Phy interrupt detect */ +#define BSP_MVME3100_IRQ_DETECT_REG ((volatile uint8_t *)0xe2000007) + +/* Atomically set bits in a sys-register; The bits set in 'mask' + * are set in the register others; are left unmodified. + * + * RETURNS: old state. + * + * NOTE : since BSP_setSysReg( reg, 0 ) does not make + * any changes this call may be used + * to read the current status w/o modifying it. + */ +uint8_t +BSP_setSysReg(volatile uint8_t *r, uint8_t mask); + +/* Atomically clear bits in a sys-register; The bits set in 'mask' + * are cleared in the register; others are left unmodified. + * + * RETURNS: old state. + * + * NOTE : since BSP_clrSysReg( reg, 0 ) does not make + * any changes this call may be used + * to read the current status w/o modifying it. + */ + +uint8_t +BSP_clrSysReg(volatile uint8_t *r, uint8_t mask); + +/* Convenience wrappers around BSP_setSysReg()/BSP_clrSysReg() */ + +/* Set write-protection for all EEPROM devices + * RETURNS: old status + */ +uint8_t +BSP_eeprom_write_protect(); + +/* Disengage write-protection for all EEPROM devices + * RETURNS: old status + */ +uint8_t +BSP_eeprom_write_enable(); + +/* Set LEDs that have their bit set in the mask + * + * RETURNS: old status. + * + * NOTE : since BSP_setLEDs( 0 ) does not make + * any changes this call may be used + * to read the current status w/o modifying it. + */ +uint8_t +BSP_setLEDs(uint8_t mask); + +/* Clear LEDs that have their bit set in the mask + * + * RETURNS: old status + * + * NOTE: : see above (BSP_setLEDs) + */ +uint8_t +BSP_clrLEDs(uint8_t mask); + +#if 0 +#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)) +#endif + +/* + * 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; + +#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 rtemsReboot(void); +/* extern int printk(const char *, ...) __attribute__((format(printf, 1, 2))); */ +extern int BSP_disconnect_clock_handler (void); +extern int BSP_connect_clock_handler (void); + +/* clear hostbridge errors + * + * NOTE: The routine returns always (-1) if 'enableMCP==1' + * [semantics needed by libbspExt] if the MCP input is not wired. + * It returns and clears the error bits of the PCI status register. + * MCP support is disabled because: + * a) the 2100 has no raven chip + * b) the raven (2300) would raise machine check interrupts + * on PCI config space access to empty slots. + */ +extern unsigned long _BSP_clear_hostbridge_errors(int enableMCP, int quiet); +extern void BSP_motload_pci_fixup(); + +struct rtems_bsdnet_ifconfig; + +int +rtems_tsec_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching); + +#define RTEMS_BSP_NETWORK_DRIVER_NAME "tse1" +#define RTEMS_BSP_NETWORK_DRIVER_ATTACH rtems_tsec_attach + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/mvme3100/include/bspopts.h.in b/c/src/lib/libbsp/powerpc/mvme3100/include/bspopts.h.in new file mode 100644 index 0000000000..2678efef45 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/include/bspopts.h.in @@ -0,0 +1,23 @@ +/* include/bspopts.h.in. Generated from configure.ac by autoheader. */ + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* 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. */ +#undef PPC_USE_DATA_CACHE diff --git a/c/src/lib/libbsp/powerpc/mvme3100/irq/irq.h b/c/src/lib/libbsp/powerpc/mvme3100/irq/irq.h new file mode 100644 index 0000000000..d7002527e9 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/irq/irq.h @@ -0,0 +1,126 @@ +/* irq.h + * + * This include file describe the data structure and the functions implemented + * by RTEMS to write interrupt handlers. + * + * Copyright (C) 1999 valette@crf.canon.fr + * + * This code is heavilly 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. + * + * Adapted for the mvme3100 BSP by T. Straumann, 2007. + * + * $Id$ + */ + +#ifndef BSP_POWERPC_IRQ_H +#define BSP_POWERPC_IRQ_H + +#define BSP_SHARED_HANDLER_SUPPORT 1 +#include + +#ifndef ASM + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * rtems_irq_number Definitions + */ + +/* Must pad number of external sources to 16 because + * of the layout of vector/priority registers in the + * 8540's openpic where there is a gap between + * registers corresponding to external and core sources. + */ +#define BSP_EXT_IRQ_NUMBER (16) +#define BSP_CORE_IRQ_NUMBER (32) + +/* openpic glue code from shared/irq assigns priorities and configures + * initial ISRs for BSP_PCI_IRQ_NUMBER entries (plus ISA stuff on legacy + * boards). Hence PCI_IRQ_NUMBER must also cover the internal sources + * even though they have nothing to do with PCI. + */ +#define BSP_PCI_IRQ_NUMBER (BSP_EXT_IRQ_NUMBER + BSP_CORE_IRQ_NUMBER) +#define BSP_PCI_IRQ_LOWEST_OFFSET (0) +#define BSP_PCI_IRQ_MAX_OFFSET (BSP_PCI_IRQ_LOWEST_OFFSET + BSP_PCI_IRQ_NUMBER - 1) + +#define BSP_CORE_IRQ_LOWEST_OFFSET (BSP_PCI_IRQ_LOWEST_OFFSET + BSP_EXT_IRQ_NUMBER) +#define BSP_CORE_IRQ_MAX_OFFSET (BSP_CORE_IRQ_LOWEST_OFFSET + BSP_CORE_IRQ_NUMBER - 1) + +/* + * PowerPC exceptions handled as interrupt where an RTEMS managed interrupt + * handler might be connected + */ +#define BSP_PROCESSOR_IRQ_NUMBER (1) +#define BSP_PROCESSOR_IRQ_LOWEST_OFFSET (BSP_CORE_IRQ_MAX_OFFSET + 1) +#define BSP_PROCESSOR_IRQ_MAX_OFFSET (BSP_PROCESSOR_IRQ_LOWEST_OFFSET + BSP_PROCESSOR_IRQ_NUMBER - 1) +/* Misc vectors for OPENPIC irqs (IPI, timers) + */ +#define BSP_MISC_IRQ_NUMBER (8) +#define BSP_MISC_IRQ_LOWEST_OFFSET (BSP_PROCESSOR_IRQ_MAX_OFFSET + 1) +#define BSP_MISC_IRQ_MAX_OFFSET (BSP_MISC_IRQ_LOWEST_OFFSET + BSP_MISC_IRQ_NUMBER - 1) +/* + * Summary + */ +#define BSP_IRQ_NUMBER (BSP_MISC_IRQ_MAX_OFFSET + 1) +#define BSP_LOWEST_OFFSET (BSP_PCI_IRQ_LOWEST_OFFSET) +#define BSP_MAX_OFFSET (BSP_MISC_IRQ_MAX_OFFSET) + +/* + * Some PCI IRQ symbolic name definition + */ +#define BSP_PCI_IRQ0 (BSP_PCI_IRQ_LOWEST_OFFSET) + +#define BSP_VME0_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 0) +#define BSP_VME1_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 1) +#define BSP_VME2_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 2) +#define BSP_VME3_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 3) + +#define BSP_ABORT_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 8) +#define BSP_TEMP_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 9) +#define BSP_PHY_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 10) +#define BSP_RTC_IRQ (BSP_PCI_IRQ_LOWEST_OFFSET + 11) + +/* Weird - they provide 3 different IRQ lines per ethernet controller + * but only one shared line for 2 UARTs ??? + */ +#define BSP_UART_COM1_IRQ (BSP_CORE_IRQ_LOWEST_OFFSET + 26) +#define BSP_UART_COM2_IRQ (BSP_CORE_IRQ_LOWEST_OFFSET + 26) +#define BSP_I2C_IRQ (BSP_CORE_IRQ_LOWEST_OFFSET + 27) + +/* + * Some internal (CORE) name definitions + */ +/* Ethernet (FEC) */ +#define BSP_CORE_IRQ_FEC (BSP_CORE_IRQ_LOWEST_OFFSET + 25) +/* i2c controller */ +#define BSP_CORE_IRQ_I2C (BSP_CORE_IRQ_LOWEST_OFFSET + 27) + +/* + * Some Processor execption handled as RTEMS IRQ symbolic name definition + */ +#define BSP_DECREMENTER (BSP_PROCESSOR_IRQ_LOWEST_OFFSET) + +/*-------------------------------------------------------------------------+ +| Function Prototypes. ++--------------------------------------------------------------------------*/ + +extern void BSP_rtems_irq_mng_init(unsigned cpuId); + +#include + +#ifdef __cplusplus +}; +#endif + +#endif +#endif diff --git a/c/src/lib/libbsp/powerpc/mvme3100/irq/irq_init.c b/c/src/lib/libbsp/powerpc/mvme3100/irq/irq_init.c new file mode 100644 index 0000000000..9b474899b7 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/irq/irq_init.c @@ -0,0 +1,140 @@ +/* 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. + * + * Till Straumann , 12/20/2001: + * Use the new interface to openpic_init + * + * Adapted for the mvme3100 BSP by T. Straumann, 2007. + * + * 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 +#include +#include + +static void nop_func() +{ + printk("Unhandled IRQ\n"); +} + +static rtems_irq_connect_data rtemsIrq[BSP_IRQ_NUMBER]; +static rtems_irq_global_settings initial_config; +static rtems_irq_connect_data defaultIrq = { + /* vectorIdex, hdl , handle , on , off , isOn */ + 0, nop_func , NULL , 0 , 0 , 0 +}; + +static rtems_irq_prio irqPrioTable[BSP_IRQ_NUMBER]={ + /* + * actual priorities for interrupt : + * 0 means that only current interrupt is masked + * 255 means all other interrupts are masked + */ + /* + * PCI Interrupts + */ + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* for raven prio 0 means unactive... */ + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* for raven prio 0 means unactive... */ + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* for raven prio 0 means unactive... */ + /* + * Processor exceptions handled as interrupts + */ + 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) +{ + /* We should really have a way to find the number of sources + * the driver will use so that the size of the polarity-array + * matches the driver's idea of it. + */ + unsigned char pol[56]; + int i; + + /* Note: The openpic driver initializes only as many + * 'pic-external' interrupt sources as reported + * by the feature register. + * The 8540's openpic supports 12 core-external + * and 23 core-internal (both of these groups + * are external to the PIC, i.e., 'pic-external') + * interrupts but between the corresponding + * banks of vector/priority registers there is + * a gap leaving space for 4 (unsupported) irqs. + * The driver, not knowing of this gap, would + * initialize the 12 core-external sources + * followed by 4 unsupported sources and 19 + * core-internal sources thus leaving the last + * four core-internal sources uninitialized. + * Luckily, the feature register reports + * too many sources: + * - the 4 IPI plus 4 timer plus 4 messaging + * sources are included with the count + * - there are unused core-internal sources 24..32 + * which are also supported by the pic + * bringing the reported number of sources to + * a count of 56 (12+32+4+4+4) which is enough + * so that all pic-external sources are covered + * and initialized. + * + * NOTE: All core-internal sources are active-high. + * The manual says that setting the polarity + * to 'low/0' will disable the interrupt but + * I found this not to be true: on the device + * I tested the interrupt was asserted hard. + */ + + /* core-external sources on the mvme3100 are active-low, + * core-internal sources are active high. + */ + for (i=0; i, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Opaque driver handle */ +struct tsec_private; + +/********** Low-level Driver API ****************/ + +/* + * This API provides driver access to applications that + * want to use e.g., the second ethernet interface + * independently from the BSD TCP/IP stack. E.g., for + * raw ethernet packet communication... + */ + +/* + * 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 already. + * + * 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_tsec_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_tsec_swipe_tx() or + * BSP_tsec_send_buf(), BSP_tsec_init_hw(), BSP_tsec_stop_hw() (the latter + * ones calling BSP_tsec_swipe_tx()). + * void *cleanup_txbuf_arg: + * Closure argument that is passed on to 'cleanup_txbuf()' callback; + * + * void *(*alloc_rxbuf)(int *p_size, uintptr_t *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_tsec_init_hw()) or when + * swiping it (BSP_tsec_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_tsec_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_tsec_swipe_rx() or + * BSP_tsec_swipe_tx(), respectively. + * + * irq_mask: + * Interrupts to enable. OR of flags from above. + * + */ +struct tsec_private * +BSP_tsec_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 +); + +/* + * Descriptor scavenger; cleanup the TX ring, passing all buffers + * that have been sent to the cleanup_tx() callback. + * This routine is called from BSP_tsec_send_buf(), BSP_tsec_init_hw(), + * BSP_tsec_stop_hw(). + * + * RETURNS: number of buffers processed. + */ + +int +BSP_tsec_swipe_tx(struct tsec_private *mp); + + +/* + * Reset statistics counters. + */ +void +BSP_tsec_reset_stats(struct tsec_private *mp); + +/* + * Initialize interface hardware + * + * 'mp' handle obtained by from BSP_tsec_setup(). + * 'promisc' whether to set promiscuous flag. + * 'enaddr' pointer to six bytes with MAC address. Read + * from the device if NULL. + */ +void +BSP_tsec_init_hw(struct tsec_private *mp, int promisc, unsigned char *enaddr); + +/* + * Dump statistics to FILE 'f'. If NULL, stdout is used. + */ +void +BSP_tsec_dump_stats(struct tsec_private *mp, FILE *f); + +/* + * Shutdown hardware and clean out the rings + */ +void +BSP_tsec_stop_hw(struct tsec_private *mp); + +/* + * calls BSP_tsec_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_tsec_detach(struct tsec_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 sent (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_tsec_send_buf(struct tsec_private *mp, void *m_head, void *data_p, int len); + +/* + * 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_tsec_swipe_rx(struct tsec_private *mp); + +/* read ethernet address from hw to buffer */ +void +BSP_tsec_read_eaddr(struct tsec_private *mp, unsigned char *eaddr); + +/* Read MII register */ +uint32_t +BSP_tsec_mdio_rd(struct tsec_private *mp, unsigned reg); + +/* Write MII register */ +int +BSP_tsec_mdio_wr(struct tsec_private *mp, unsigned reg, uint32_t val); + +/* + * 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 + */ +int +BSP_tsec_media_ioctl(struct tsec_private *mp, int cmd, int *parg); + +/* Interrupt related routines */ + +/* + * When it comes to interrupts the chip has two rather + * annoying features: + * 1 once an IRQ is pending, clearing the IMASK does not + * de-assert the interrupt line. + * 2 the chip has three physical interrupt lines even though + * all events are reported in a single register. Rather + * useless; we must hook 3 ISRs w/o any real benefit. + * In fact, it makes our life a bit more difficult: + * + * Hence, for (1) we would have to mask interrupts at the PIC + * but to re-enable them we would have to do that three times + * because of (2). + * + * Therefore, we take the following approach: + * + * ISR masks all interrupts on the TSEC, acks/clears them + * and stores the acked irqs in the device struct where + * it is picked up by BSP_tsec_ack_irqs(). + * Since all interrupts are disabled until the daemon + * re-enables them after calling BSP_tsec_ack_irqs() + * no interrupts are lost. + * + * BUT: NO isr (including PHY isrs) MUST INTERRUPT ANY + * OTHER ONE, i.e., they all must have the same + * priority. Otherwise, integrity of the cached + * irq_pending variable may be compromised. + */ + +/* Enable interrupts at device */ +void +BSP_tsec_enable_irqs(struct tsec_private *mp); + +/* Disable interrupts at device */ +void +BSP_tsec_disable_irqs(struct tsec_private *mp); + +/* + * Acknowledge (and clear) interrupts. + * RETURNS: interrupts that were raised. + */ +uint32_t +BSP_tsec_ack_irqs(struct tsec_private *mp); + +/* Retrieve the driver daemon TID that was passed to + * BSP_tsec_setup(). + */ + +rtems_id +BSP_tsec_get_tid(struct tsec_private *mp); + +struct tsec_private * +BSP_tsec_getp(unsigned index); + +/* + * + * 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_tsec_ack_irqs(handle); + * if ( irqs & BSP_TSEC_IRQ_TX ) { + * BSP_tsec_swipe_tx(handle); / * cleanup_txbuf() callback executed * / + * } + * if ( irqs & BSP_TSEC_IRQ_RX ) { + * BSP_tsec_swipe_rx(handle); / * alloc_rxbuf() and consume_rxbuf() executed * / + * } + * BSP_tsec_enable_irqs(handle); + * } while (1); + * + */ + +/* PUBLIC RTEMS BSDNET ATTACH FUNCTION */ +struct rtems_bsdnet_ifconfig; + +int +rtems_tsec_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c b/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c new file mode 100644 index 0000000000..ffff01c850 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c @@ -0,0 +1,2920 @@ +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was created by + * + * Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 + +#ifndef KERNEL +#define KERNEL +#endif +#ifndef _KERNEL +#define _KERNEL +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define STATIC +#define PARANOIA +#undef DEBUG + + +#ifdef TEST_MII_TIMING + +#include + +SPR_RO(TBRL) + +static inline uint32_t tb_rd() +{ + return _read_TBRL(); +} +#endif + +struct tsec_private; + +/* Forward declarations */ +static void +phy_init_irq( int install, struct tsec_private *mp, void (*tsec_lisr)(rtems_irq_hdl_param) ); + +static void +phy_en_irq(struct tsec_private *mp); + +static void +phy_en_irq_at_phy(struct tsec_private *mp); + +static void +phy_dis_irq(struct tsec_private *mp); + +static void +phy_dis_irq_at_phy(struct tsec_private *mp); + +static int +phy_irq_pending(struct tsec_private *mp); + +static uint32_t +phy_ack_irq(struct tsec_private *mp); + +#if defined(PARANOIA) || defined(DEBUG) +void tsec_dump_tring(struct tsec_private *mp); +void tsec_dump_rring(struct tsec_private *mp); +#endif + +#ifdef DEBUG +#ifdef TSEC_RX_RING_SIZE +#undef TSEC_RX_RING_SIZE +#endif +#define TSEC_RX_RING_SIZE 4 + +#ifdef TSEC_TX_RING_SIZE +#undef TSEC_TX_RING_SIZE +#endif +#define TSEC_TX_RING_SIZE 2 +#else +#ifndef TSEC_RX_RING_SIZE +#define TSEC_RX_RING_SIZE 40 +#endif +#ifndef TSEC_TX_RING_SIZE +#define TSEC_TX_RING_SIZE 200 +#endif +#endif + +/********** Helper Macros and Definitions ******/ + +/* + * Align 'p' up to a multiple of 'a' which must be + * a power of two. Result is cast to (uintptr_t) + */ +#define ALIGNTO(p,a) ((((uintptr_t)(p)) + (a) - 1) & ~((a)-1)) + + +/* + * Not obvious from the doc: RX buffers apparently must be 32-byte + * aligned :-(; TX buffers have no alignment requirement. + * I found this by testing, T.S, 11/2007 + */ +#define RX_BUF_ALIGNMENT 32 + +/* + * Alignment req. for buffer descriptors (BDs) + */ +#define BD_ALIGNMENT 8 + + +#define ETH_RX_OFFSET 0 +#define ETH_CRC_LEN 0 + +#define CPU2BUS_ADDR(x) (x) +#define BUS2CPU_ADDR(x) (x) + +/* + * Whether to automatically try to reclaim descriptors + * when enqueueing new packets + */ +#if 1 +#define TSEC_CLEAN_ON_SEND(mp) (BSP_tsec_swipe_tx(mp)) +#else +#define TSEC_CLEAN_ON_SEND(mp) (-1) +#endif + +#define TX_AVAILABLE_RING_SIZE(mp) (mp)->tx_ring_size + +#define DRVNAME "tsec" + +/* + * Event(s) posted by ISRs to driver task + */ +#define EV_PER_UNIT 1 + +#define TSEC_ETH_EVENT( unit ) ( 1 << (EV_PER_UNIT * (unit) )) +#if EV_PER_UNIT > 1 +#define TSEC_PHY_EVENT( unit ) ( 1 << (EV_PER_UNIT * (unit) + 1)) +#endif + +#define EV_IS_ETH(ev) ( (ev) & 1 ) +#if EV_PER_UNIT > 1 +#define EV_IS_PHY(ev) ( (ev) & 2 ) +#endif + +#define EV_IS_ANY(ev) ( (ev) & ((1<flags ); +} + +static inline void bd_wrfl(TSEC_BD *bd, uint16_t v) +{ + st_be16( &bd->flags, v ); +} + +static inline void bd_setfl(TSEC_BD *bd, uint16_t v) +{ + bd_wrfl(bd, bd_rdfl(bd) | v ); +} + +static inline void bd_clrfl(TSEC_BD *bd, uint16_t v) +{ + bd_wrfl(bd, bd_rdfl(bd) & ~v ); +} + +static inline void bd_cslfl(TSEC_BD *bd, uint16_t s, uint16_t c) +{ + bd_wrfl( bd, ( bd_rdfl(bd) & ~c ) | s ); +} + +static inline uint32_t bd_rdbuf(TSEC_BD *bd) +{ + return BUS2CPU_ADDR( ld_be32( &bd->buf ) ); +} + +static inline void bd_wrbuf(TSEC_BD *bd, uint32_t addr) +{ + st_be32( &bd->buf, CPU2BUS_ADDR(addr) ); +} + +/* BD bit definitions */ + +#define TSEC_TXBD_R ((uint16_t)(1<<(15- 0))) +#define TSEC_TXBD_PAD_CRC ((uint16_t)(1<<(15- 1))) +#define TSEC_TXBD_W ((uint16_t)(1<<(15- 2))) +#define TSEC_TXBD_I ((uint16_t)(1<<(15- 3))) +#define TSEC_TXBD_L ((uint16_t)(1<<(15- 4))) +#define TSEC_TXBD_TC ((uint16_t)(1<<(15- 5))) +#define TSEC_TXBD_DEF ((uint16_t)(1<<(15- 6))) +#define TSEC_TXBD_TO1 ((uint16_t)(1<<(15- 7))) +#define TSEC_TXBD_HFE_LC ((uint16_t)(1<<(15- 8))) +#define TSEC_TXBD_RL ((uint16_t)(1<<(15- 9))) +#define TSEC_TXBD_RC(x) ((uint16_t)(((x)>>2)&0xf)) +#define TSEC_TXBD_UN ((uint16_t)(1<<(15-14))) +#define TSEC_TXBD_TXTRUNC ((uint16_t)(1<<(15-15))) +#define TSEC_TXBD_ERRS (TSEC_TXBD_RL | TSEC_TXBD_UN | TSEC_TXBD_TXTRUNC) + +#define TSEC_RXBD_E ((uint16_t)(1<<(15- 0))) +#define TSEC_RXBD_RO1 ((uint16_t)(1<<(15- 1))) +#define TSEC_RXBD_W ((uint16_t)(1<<(15- 2))) +#define TSEC_RXBD_I ((uint16_t)(1<<(15- 3))) +#define TSEC_RXBD_L ((uint16_t)(1<<(15- 4))) +#define TSEC_RXBD_F ((uint16_t)(1<<(15- 5))) +#define TSEC_RXBD_M ((uint16_t)(1<<(15- 7))) +#define TSEC_RXBD_BC ((uint16_t)(1<<(15- 8))) +#define TSEC_RXBD_MC ((uint16_t)(1<<(15- 9))) +#define TSEC_RXBD_LG ((uint16_t)(1<<(15-10))) +#define TSEC_RXBD_NO ((uint16_t)(1<<(15-11))) +#define TSEC_RXBD_SH ((uint16_t)(1<<(15-12))) +#define TSEC_RXBD_CR ((uint16_t)(1<<(15-13))) +#define TSEC_RXBD_OV ((uint16_t)(1<<(15-14))) +#define TSEC_RXBD_TR ((uint16_t)(1<<(15-15))) + +#define TSEC_RXBD_ERROR \ + (TSEC_RXBD_LG | TSEC_RXBD_NO | TSEC_RXBD_SH | TSEC_RXBD_CR | TSEC_RXBD_OV | TSEC_RXBD_TR ) + +/* Driver 'private' data */ + +struct tsec_private { + FEC_Enet_Base base; /* Controller base address */ + FEC_Enet_Base phy_base; /* Phy base address (not necessarily identical + * with controller base address); + * e.g., phy attached to 2nd controller may be + * connected to mii bus of 1st controller. + */ + unsigned phy; /* Phy address on mii bus */ + unsigned unit; /* Driver instance (one-based */ + int isfec; /* Set if a FEC (not TSEC) controller */ + struct tsec_softc *sc; /* Pointer to BSD driver struct */ + TSEC_BD *ring_area; /* Not necessarily aligned */ + TSEC_BD *tx_ring; /* Aligned array of TX BDs */ + void **tx_ring_user; /* Array of user pointers (1 per BD) */ + unsigned tx_ring_size; + unsigned tx_head; /* first 'dirty' BD; chip is working on */ + unsigned tx_tail; /* BDs between head and tail */ + unsigned tx_avail; /* Number of available/free TX BDs */ + TSEC_BD *rx_ring; /* Aligned array of RX BDs */ + void **rx_ring_user; /* Array of user pointers (1 per BD) */ + unsigned rx_tail; /* Where we left off scanning for full bufs */ + unsigned rx_ring_size; + void (*cleanup_txbuf) /* Callback to cleanup TX ring */ + (void*, void*, int); + void *cleanup_txbuf_arg; + void *(*alloc_rxbuf) /* Callback for allocating RX buffer */ + (int *psize, uintptr_t *paddr); + void (*consume_rxbuf) /* callback to consume RX buffer */ + (void*, void*, int); + void *consume_rxbuf_arg; + rtems_id tid; /* driver task ID */ + uint32_t irq_mask; + uint32_t irq_pending; + rtems_event_set event; /* Task synchronization events */ + struct { /* Statistics */ + unsigned xirqs; + unsigned rirqs; + unsigned eirqs; + unsigned lirqs; + unsigned maxchain; + unsigned packet; + unsigned odrops; + unsigned repack; + } stats; +}; + +#define NEXT_TXI(mp, i) (((i)+1) < (mp)->tx_ring_size ? (i)+1 : 0 ) +#define NEXT_RXI(mp, i) (((i)+1) < (mp)->rx_ring_size ? (i)+1 : 0 ) + +/* Stuff needed for bsdnet support */ +struct tsec_bsdsupp { + int oif_flags; /* old / cached if_flags */ +}; + +/* bsdnet driver data */ +struct tsec_softc { + struct arpcom arpcom; + struct tsec_bsdsupp bsd; + struct tsec_private pvt; +}; + +/* BSP glue information */ +typedef struct tsec_bsp_config { + uint32_t base; + int xirq, rirq, eirq; + uint32_t phy_base; + int phy_addr; +} TsecBspConfig; + +/********** Global Variables ********************/ + +/* You may override base addresses + * externally - but you must + * then also define TSEC_NUM_DRIVER_SLOTS. + */ +#ifndef TSEC_CONFIG + +static TsecBspConfig tsec_config[] = +{ + { + base: BSP_8540_CCSR_BASE + 0x24000, + xirq: BSP_CORE_IRQ_LOWEST_OFFSET + 13, + rirq: BSP_CORE_IRQ_LOWEST_OFFSET + 14, + eirq: BSP_CORE_IRQ_LOWEST_OFFSET + 18, + phy_base: BSP_8540_CCSR_BASE + 0x24000, + phy_addr: 1, + }, + { + base: BSP_8540_CCSR_BASE + 0x25000, + xirq: BSP_CORE_IRQ_LOWEST_OFFSET + 19, + rirq: BSP_CORE_IRQ_LOWEST_OFFSET + 20, + eirq: BSP_CORE_IRQ_LOWEST_OFFSET + 23, + /* all PHYs are on the 1st adapter's mii bus */ + phy_base: BSP_8540_CCSR_BASE + 0x24000, + phy_addr: 2, + }, +}; + +#define TSEC_CONFIG tsec_config + +#endif + +#ifndef TSEC_NUM_DRIVER_SLOTS +#define TSEC_NUM_DRIVER_SLOTS (sizeof(TSEC_CONFIG)/sizeof(TSEC_CONFIG[0])) +#endif + +/* Driver data structs */ +STATIC struct tsec_softc theTsecEths[TSEC_NUM_DRIVER_SLOTS] = { {{{0}}} }; + +/* Bsdnet driver task ID; since the BSD stack is single-threaded + * there is no point having multiple tasks. A single + * task handling all adapters (attached to BSD stack) + * is good enough. + * Note that an adapter might well be used independently + * from the BSD stack (use the low-level driver interface) + * and be serviced by a separate task. + */ +STATIC rtems_id tsec_tid = 0; + +/* If we anticipate using adapters independently + * from the BSD stack AND if all PHYs are on a single + * adapter's MII bus THEN we must mutex-protect + * that MII bus. + * If not all of these conditions hold then you + * may define TSEC_CONFIG_NO_PHY_REGLOCK and + * avoid the creation and use of a mutex. + */ +#ifndef TSEC_CONFIG_NO_PHY_REGLOCK +/* + * PHY register access protection mutex; + * multiple instances of tsec hardware + * may share e.g., the first tsec's registers + * for accessing the mii bus where all PHYs + * may be connected. If we would only deal + * with BSD networking then using the normal + * networking semaphore would be OK. However, + * we want to support standalone drivers and + * therefore might require a separate lock. + */ +STATIC rtems_id tsec_mtx = 0; +#define REGLOCK() do { \ + if ( RTEMS_SUCCESSFUL != rtems_semaphore_obtain(tsec_mtx, RTEMS_WAIT, RTEMS_NO_TIMEOUT) ) \ + rtems_panic(DRVNAME": unable to lock phy register protection mutex"); \ + } while (0) +#define REGUNLOCK() rtems_semaphore_release(tsec_mtx) +#else +#define REGLOCK() do { } while (0) +#define REGUNLOCK() do { } while (0) +#endif + +static void tsec_xisr(rtems_irq_hdl_param arg); +static void tsec_risr(rtems_irq_hdl_param arg); +static void tsec_eisr(rtems_irq_hdl_param arg); +static void tsec_lisr(rtems_irq_hdl_param arg); + +static void noop(const rtems_irq_connect_data *unused) { } +static int nopf(const rtems_irq_connect_data *unused) { return -1; } + +/********** Low-level Driver API ****************/ + +/* + * This API provides driver access to applications that + * want to use e.g., the second ethernet interface + * independently from the BSD TCP/IP stack. E.g., for + * raw ethernet packet communication... + */ + +/* + * Descriptor scavenger; cleanup the TX ring, passing all buffers + * that have been sent to the cleanup_tx() callback. + * This routine is called from BSP_tsec_send_buf(), BSP_tsec_init_hw(), + * BSP_tsec_stop_hw(). + * + * RETURNS: number of buffers processed. + */ + +int +BSP_tsec_swipe_tx(struct tsec_private *mp) +{ +int rval = 0; +int i; +TSEC_BD *bd; +uint16_t flags; +void *u; + +#if DEBUG > 2 +printf("Swipe TX entering:\n"); +tsec_dump_tring(mp); +#endif + + for ( i = mp->tx_head; bd_rdbuf( (bd = &mp->tx_ring[i]) ); i = NEXT_TXI(mp, i) ) { + + flags = bd_rdfl( bd ); + if ( (TSEC_TXBD_R & flags) ) { + /* nothing more to clean */ + break; + } + + /* tx_ring_user[i] is only set on the last descriptor in a chain; + * we only count errors in the last descriptor; + */ + if ( (u=mp->tx_ring_user[i]) ) { + mp->cleanup_txbuf(u, mp->cleanup_txbuf_arg, (flags & TSEC_TXBD_ERRS)); + mp->tx_ring_user[i] = 0; + } + + bd_wrbuf( bd, 0 ); + + mp->tx_avail++; + + rval++; + } + mp->tx_head = i; + +#if DEBUG > 2 +tsec_dump_tring(mp); +printf("Swipe TX leaving\n"); +#endif + + return rval; +} + + +/* + * Reset the controller and bring into a known state; + * all interrupts are off + */ +STATIC void +tsec_reset_hw(struct tsec_private *mp) +{ +FEC_Enet_Base b = mp->base; + + /* Make sure all interrupts are off */ + fec_wr(b, TSEC_IMASK, TSEC_IMASK_NONE); + +#ifndef TSEC_CONFIG_NO_PHY_REGLOCK + /* don't bother disabling irqs in the PHY if this is + * called before the mutex is created; + * the PHY ISR is not hooked yet and there can be no + * interrupts... + */ + if ( tsec_mtx ) +#endif + phy_dis_irq_at_phy( mp ); + + /* Follow the manual resetting the chip */ + + /* Do graceful stop (if not in stop condition already) */ + if ( ! (TSEC_DMACTRL_GTS & fec_rd(b, TSEC_DMACTRL)) ) { + /* Make sure GTSC is clear */ + fec_wr(b, TSEC_IEVENT, TSEC_IEVENT_GTSC); + fec_set(b, TSEC_DMACTRL, TSEC_DMACTRL_GTS); + while ( ! (TSEC_IEVENT_GTSC & fec_rd(b, TSEC_IEVENT)) ) + /* wait */; + } + + /* Clear RX/TX enable in MAC */ + fec_clr(b, TSEC_MACCFG1, TSEC_MACCFG1_RX_EN | TSEC_MACCFG1_TX_EN); + + /* wait for > 8ms */ + rtems_task_wake_after(1); + + /* set GRS if not already stopped */ + if ( ! (TSEC_DMACTRL_GRS & fec_rd(b, TSEC_DMACTRL)) ) { + /* Make sure GRSC is clear */ + fec_wr(b, TSEC_IEVENT, TSEC_IEVENT_GRSC); + fec_set(b, TSEC_DMACTRL, TSEC_DMACTRL_GRS); + while ( ! (TSEC_IEVENT_GRSC & fec_rd(b, TSEC_IEVENT)) ) + /* wait */; + } + + fec_set(b, TSEC_MACCFG1, TSEC_MACCFG1_SOFT_RESET); + fec_clr(b, TSEC_MACCFG1, TSEC_MACCFG1_SOFT_RESET); + + /* clear all irqs */ + fec_wr (b, TSEC_IEVENT, TSEC_IEVENT_ALL); +} + +/* Helper to hook/unhook interrupts */ + +static void +install_remove_isrs(int install, struct tsec_private *mp, uint32_t irq_mask) +{ + rtems_irq_connect_data xxx; + int installed = 0; + int line; + int unit = mp->unit; + + xxx.on = noop; + xxx.off = noop; + xxx.isOn = nopf; + xxx.handle = mp; + + if ( irq_mask & TSEC_TXIRQ ) { + xxx.name = TSEC_CONFIG[unit-1].xirq; + xxx.hdl = tsec_xisr; + if ( ! (install ? + BSP_install_rtems_irq_handler( &xxx ) : + BSP_remove_rtems_irq_handler( &xxx ) ) ) { + rtems_panic(DRVNAME": Unable to install TX ISR\n"); + } + installed++; + } + + if ( (irq_mask & TSEC_RXIRQ) ) { + if ( (line = TSEC_CONFIG[unit-1].rirq) < 0 && ! installed ) { + /* have no dedicated RX IRQ line; install TX ISR if not already done */ + line = TSEC_CONFIG[unit-1].xirq; + } + xxx.name = line; + xxx.hdl = tsec_risr; + if ( ! (install ? + BSP_install_rtems_irq_handler( &xxx ) : + BSP_remove_rtems_irq_handler( &xxx ) ) ) { + rtems_panic(DRVNAME": Unable to install RX ISR\n"); + } + installed++; + } + + if ( (line = TSEC_CONFIG[unit-1].eirq) < 0 && ! installed ) { + /* have no dedicated RX IRQ line; install TX ISR if not already done */ + line = TSEC_CONFIG[unit-1].xirq; + } + xxx.name = line; + xxx.hdl = tsec_eisr; + if ( ! (install ? + BSP_install_rtems_irq_handler( &xxx ) : + BSP_remove_rtems_irq_handler( &xxx ) ) ) { + rtems_panic(DRVNAME": Unable to install ERR ISR\n"); + } + + if ( irq_mask & TSEC_LINK_INTR ) { + phy_init_irq( install, mp, tsec_lisr ); + } +} + +/* + * 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 already. + * + * 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_tsec_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_tsec_swipe_tx() or + * BSP_tsec_send_buf(), BSP_tsec_init_hw(), BSP_tsec_stop_hw() (the latter + * ones calling BSP_tsec_swipe_tx()). + * void *cleanup_txbuf_arg: + * Closure argument that is passed on to 'cleanup_txbuf()' callback; + * + * void *(*alloc_rxbuf)(int *p_size, uintptr_t *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_tsec_init_hw()) or when + * swiping it (BSP_tsec_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_tsec_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_tsec_swipe_rx() or + * BSP_tsec_swipe_tx(), respectively. + * + * irq_mask: + * Interrupts to enable. OR of flags from above. + * + */ +struct tsec_private * +BSP_tsec_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 +) +{ +struct tsec_private *mp; +int i; +struct ifnet *ifp; + + if ( unit <= 0 || unit > TSEC_NUM_DRIVER_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, TSEC_NUM_DRIVER_SLOTS); + return 0; + } + + ifp = &theTsecEths[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; + + mp = &theTsecEths[unit - 1].pvt; + + memset(mp, 0, sizeof(*mp)); + + mp->sc = &theTsecEths[unit - 1]; + mp->unit = unit; + + mp->base = (FEC_Enet_Base)TSEC_CONFIG[unit-1].base; + mp->phy_base = (FEC_Enet_Base)TSEC_CONFIG[unit-1].phy_base; + mp->phy = TSEC_CONFIG[unit-1].phy_addr; + mp->tid = driver_tid; + /* use odd event flags for link status IRQ */ + mp->event = TSEC_ETH_EVENT((unit-1)); + + 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; + + /* stop hw prior to setting ring-size to anything nonzero + * so that the rings are not swept. + */ + BSP_tsec_stop_hw(mp); + + if ( 0 == rx_ring_size ) + rx_ring_size = TSEC_RX_RING_SIZE; + if ( 0 == tx_ring_size ) + tx_ring_size = TSEC_TX_RING_SIZE; + + mp->rx_ring_size = rx_ring_size < 0 ? 0 : rx_ring_size; + mp->tx_ring_size = tx_ring_size < 0 ? 0 : tx_ring_size; + + /* allocate ring area; add 1 entry -- room for alignment */ + assert( !mp->ring_area ); + mp->ring_area = malloc( + sizeof(*mp->ring_area) * + (mp->rx_ring_size + mp->tx_ring_size + 1), + M_DEVBUF, + M_WAIT ); + assert( mp->ring_area ); + + mp->tx_ring_user = malloc( sizeof(*mp->tx_ring_user) * + (mp->rx_ring_size + mp->tx_ring_size), + M_DEVBUF, + M_WAIT ); + assert( mp->tx_ring_user ); + + mp->rx_ring_user = mp->tx_ring_user + mp->tx_ring_size; + + /* Initialize TX ring */ + mp->tx_ring = (TSEC_BD *) ALIGNTO(mp->ring_area,BD_ALIGNMENT); + + mp->rx_ring = mp->tx_ring + mp->tx_ring_size; + + for ( i=0; itx_ring_size; i++ ) { + bd_wrbuf( &mp->tx_ring[i], 0 ); + bd_wrfl( &mp->tx_ring[i], TSEC_TXBD_I ); + mp->tx_ring_user[i] = 0; + } + /* set wrap-around flag on last BD */ + if ( mp->tx_ring_size ) + bd_setfl( &mp->tx_ring[i-1], TSEC_TXBD_W ); + + mp->tx_tail = mp->tx_head = 0; + mp->tx_avail = mp->tx_ring_size; + + /* Initialize RX ring (buffers are allocated later) */ + for ( i=0; irx_ring_size; i++ ) { + bd_wrbuf( &mp->rx_ring[i], 0 ); + bd_wrfl( &mp->rx_ring[i], TSEC_RXBD_I ); + mp->rx_ring_user[i] = 0; + } + /* set wrap-around flag on last BD */ + if ( mp->rx_ring_size ) + bd_setfl( &mp->rx_ring[i-1], TSEC_RXBD_W ); + + if ( irq_mask ) { + if ( rx_ring_size == 0 ) + irq_mask &= ~ TSEC_RXIRQ; + if ( tx_ring_size == 0 ) + irq_mask &= ~ TSEC_TXIRQ; + } + +#ifndef TSEC_CONFIG_NO_PHY_REGLOCK + /* lazy init of mutex (non thread-safe! - we assume initialization + * of 1st IF is single-threaded) + */ + if ( ! tsec_mtx ) { + rtems_status_code sc; + sc = rtems_semaphore_create( + rtems_build_name('t','s','e','X'), + 1, + RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY | RTEMS_DEFAULT_ATTRIBUTES, + 0, + &tsec_mtx); + if ( RTEMS_SUCCESSFUL != sc ) { + rtems_error(sc,DRVNAME": creating mutex\n"); + rtems_panic("unable to proceed\n"); + } + } +#endif + + if ( irq_mask ) { + install_remove_isrs( 1, mp, irq_mask ); + } + + mp->irq_mask = irq_mask; + + /* mark as used */ + ifp->if_init = (void*)(-1); + + return mp; +} + +void +BSP_tsec_reset_stats(struct tsec_private *mp) +{ +FEC_Enet_Base b = mp->base; +int i; + memset( &mp->stats, 0, sizeof( mp->stats ) ); + if ( mp->isfec ) + return; + for ( i=TSEC_TR64; i<=TSEC_TFRG; i+=4 ) + fec_wr( b, i, 0 ); + +} + +/* + * retrieve media status from the PHY + * and set duplex mode in MACCFG2 based + * on the result. + * + * RETURNS: media word (or -1 if BSP_tsec_media_ioctl() fails) + */ +static int +mac_set_duplex(struct tsec_private *mp) +{ +int media = 0; + + if ( 0 == BSP_tsec_media_ioctl(mp, SIOCGIFMEDIA, &media)) { + if ( IFM_LINK_OK & media ) { + /* update duplex setting in MACCFG2 */ + if ( IFM_FDX & media ) { + fec_set( mp->base, TSEC_MACCFG2, TSEC_MACCFG2_FD ); + } else { + fec_clr( mp->base, TSEC_MACCFG2, TSEC_MACCFG2_FD ); + } + } + return media; + } + return -1; +} + +/* + * Initialize interface hardware + * + * 'mp' handle obtained by from BSP_tsec_setup(). + * 'promisc' whether to set promiscuous flag. + * 'enaddr' pointer to six bytes with MAC address. Read + * from the device if NULL. + */ +void +BSP_tsec_init_hw(struct tsec_private *mp, int promisc, unsigned char *enaddr) +{ +FEC_Enet_Base b = mp->base; +unsigned i; +uint32_t v; +int sz; +rtems_interrupt_level l; + + BSP_tsec_stop_hw(mp); + +#ifdef PARANOIA + assert( mp->tx_avail == mp->tx_ring_size ); + assert( mp->tx_head == mp->tx_tail ); + for ( i=0; itx_ring_size; i++ ) { + assert( mp->tx_ring_user[i] == 0 ); + } +#endif + + /* make sure RX ring is filled */ + for ( i=0; irx_ring_size; i++ ) { + uintptr_t data_area; + if ( ! (mp->rx_ring_user[i] = mp->alloc_rxbuf( &sz, &data_area)) ) { + rtems_panic(DRVNAME": unable to fill RX ring"); + } + if ( data_area & (RX_BUF_ALIGNMENT-1) ) + rtems_panic(DRVNAME": RX buffers must be %i-byte aligned", RX_BUF_ALIGNMENT); + + bd_wrbuf( &mp->rx_ring[i], data_area ); + st_be16 ( &mp->rx_ring[i].len, sz ); + bd_setfl( &mp->rx_ring[i], TSEC_RXBD_E | TSEC_RXBD_I ); + } + + mp->tx_tail = mp->tx_head = 0; + + mp->rx_tail = 0; + + /* tell chip what the ring areas are */ + fec_wr( b, TSEC_TBASE, (uint32_t)mp->tx_ring ); + fec_wr( b, TSEC_RBASE, (uint32_t)mp->rx_ring ); + + /* clear and disable IRQs */ + fec_wr( b, TSEC_IEVENT, TSEC_IEVENT_ALL ); + fec_wr( b, TSEC_IMASK, TSEC_IMASK_NONE ); + + /* bring other regs. into a known state */ + fec_wr( b, TSEC_EDIS, 0 ); + + if ( !mp->isfec ) + fec_wr( b, TSEC_ECNTRL, TSEC_ECNTRL_CLRCNT | TSEC_ECNTRL_STEN ); + + fec_wr( b, TSEC_MINFLR, 64 ); + fec_wr( b, TSEC_PTV, 0 ); + + v = TSEC_DMACTRL_WWR; + + if ( mp->tx_ring_size ) + v |= TSEC_DMACTRL_TDSEN | TSEC_DMACTRL_TBDSEN | TSEC_DMACTRL_WOP; + + fec_wr( b, TSEC_DMACTRL, v ); + + fec_wr( b, TSEC_FIFO_PAUSE_CTRL, 0 ); + fec_wr( b, TSEC_FIFO_TX_THR, 256 ); + fec_wr( b, TSEC_FIFO_TX_STARVE, 128 ); + fec_wr( b, TSEC_FIFO_TX_STARVE_SHUTOFF, 256 ); + fec_wr( b, TSEC_TCTRL, 0 ); + if ( !mp->isfec ) { + /* FIXME: use IRQ coalescing ? not sure how to + * set the timer (bad if it depends on the speed + * setting). + */ + fec_wr( b, TSEC_TXIC, 0); + } + fec_wr( b, TSEC_OSTBD, 0 ); + fec_wr( b, TSEC_RCTRL, (promisc ? TSEC_RCTRL_PROM : 0) ); + fec_wr( b, TSEC_RSTAT, TSEC_RSTAT_QHLT ); + if ( !mp->isfec ) { + /* FIXME: use IRQ coalescing ? not sure how to + * set the timer (bad if it depends on the speed + * setting). + */ + fec_wr( b, TSEC_RXIC, 0 ); + } + fec_wr( b, TSEC_MRBLR, sz & ~63 ); + + /* Reset config. as per manual */ + fec_wr( b, TSEC_IPGIFG, 0x40605060 ); + fec_wr( b, TSEC_HAFDUP, 0x00a1f037 ); + fec_wr( b, TSEC_MAXFRM, 1536 ); + + if ( enaddr ) { + union { + uint32_t u; + uint16_t s[2]; + uint8_t c[4]; + } x; + fec_wr( b, TSEC_MACSTNADDR1, ld_le32( (volatile uint32_t*)(enaddr + 2) ) ); + x.s[0] = ld_le16( (volatile uint16_t *)(enaddr) ); + fec_wr( b, TSEC_MACSTNADDR2, x.u ); + } + + for ( i=0; i<8*4; i+=4 ) { + fec_wr( b, TSEC_IADDR0 + i, 0 ); + fec_wr( b, TSEC_GADDR0 + i, 0 ); + } + + BSP_tsec_reset_stats(mp); + + fec_wr( b, TSEC_ATTR, (TSEC_ATTR_RDSEN | TSEC_ATTR_RBDSEN) ); + fec_wr( b, TSEC_ATTRELI, 0 ); + + /* The interface type is probably board dependent; leave alone... + v = mp->isfec ? TSEC_MACCFG2_IF_MODE_MII : TSEC_MACCFG2_IF_MODE_GMII; + */ + + fec_clr( b, TSEC_MACCFG2, + TSEC_MACCFG2_PREAMBLE_15 + | TSEC_MACCFG2_HUGE_FRAME + | TSEC_MACCFG2_LENGTH_CHECK ); + + fec_set( b, TSEC_MACCFG2, + TSEC_MACCFG2_PREAMBLE_7 + | TSEC_MACCFG2_PAD_CRC ); + + mac_set_duplex( mp ); + + v = 0; + if ( mp->rx_ring_size ) { + v |= TSEC_MACCFG1_RX_EN; + } + if ( mp->tx_ring_size ) { + v |= TSEC_MACCFG1_TX_EN; + } + + fec_wr( b, TSEC_MACCFG1, v); + + /* The following sequency (FWIW) ensures that + * + * - PHY and TSEC interrupts are enabled atomically + * - IRQS are not globally off while accessing the PHY + * (slow MII) + */ + + /* disable PHY irq at PIC (fast) */ + phy_dis_irq( mp ); + /* enable PHY irq (MII operation, slow) */ + phy_en_irq_at_phy (mp ); + + /* globally disable */ + rtems_interrupt_disable( l ); + + /* enable TSEC IRQs */ + fec_wr( mp->base, TSEC_IMASK, mp->irq_mask ); + /* enable PHY irq at PIC */ + phy_en_irq( mp ); + + /* globally reenable */ + rtems_interrupt_enable( l ); +} + +void +BSP_tsec_dump_stats(struct tsec_private *mp, FILE *f) +{ +FEC_Enet_Base b; + + if ( !mp ) + mp = &theTsecEths[0].pvt; + + if ( ! f ) + f = stdout; + + fprintf(f, DRVNAME"%i Statistics:\n", mp->unit); + + b = mp->base; + + fprintf(f, "TX IRQS: %u\n", mp->stats.xirqs); + fprintf(f, "RX IRQS: %u\n", mp->stats.rirqs); + fprintf(f, "ERR IRQS: %u\n", mp->stats.eirqs); + fprintf(f, "LNK IRQS: %u\n", mp->stats.lirqs); + fprintf(f, "maxchain: %u\n", mp->stats.maxchain); + fprintf(f, "xpackets: %u\n", mp->stats.packet); + fprintf(f, "odrops: %u\n", mp->stats.odrops); + fprintf(f, "repack: %u\n", mp->stats.repack); + + if ( mp->isfec ) { + fprintf(f,"FEC has no HW counters\n"); + return; + } + + fprintf(f,"TSEC MIB counters (modulo 2^32):\n"); + + fprintf(f,"RX bytes %"PRIu32"\n", fec_rd( b, TSEC_RBYT )); + fprintf(f,"RX pkts %"PRIu32"\n", fec_rd( b, TSEC_RPKT )); + fprintf(f,"RX FCS errs %"PRIu32"\n", fec_rd( b, TSEC_RFCS )); + fprintf(f,"RX mcst pkts %"PRIu32"\n", fec_rd( b, TSEC_RMCA )); + fprintf(f,"RX bcst pkts %"PRIu32"\n", fec_rd( b, TSEC_RBCA )); + fprintf(f,"RX pse frms %"PRIu32"\n", fec_rd( b, TSEC_RXPF )); + fprintf(f,"RX drop %"PRIu32"\n", fec_rd( b, TSEC_RDRP )); + fprintf(f,"TX bytes %"PRIu32"\n", fec_rd( b, TSEC_TBYT )); + fprintf(f,"TX pkts %"PRIu32"\n", fec_rd( b, TSEC_TPKT )); + fprintf(f,"TX FCS errs %"PRIu32"\n", fec_rd( b, TSEC_TFCS )); + fprintf(f,"TX mcst pkts %"PRIu32"\n", fec_rd( b, TSEC_TMCA )); + fprintf(f,"TX bcst pkts %"PRIu32"\n", fec_rd( b, TSEC_TBCA )); + fprintf(f,"TX pse frms %"PRIu32"\n", fec_rd( b, TSEC_TXPF )); + fprintf(f,"TX drop %"PRIu32"\n", fec_rd( b, TSEC_TDRP )); + fprintf(f,"TX coll %"PRIu32"\n", fec_rd( b, TSEC_TSCL )); + fprintf(f,"TX mcoll %"PRIu32"\n", fec_rd( b, TSEC_TMCL )); + fprintf(f,"TX late coll %"PRIu32"\n", fec_rd( b, TSEC_TLCL )); + fprintf(f,"TX exc coll %"PRIu32"\n", fec_rd( b, TSEC_TXCL )); + fprintf(f,"TX defer %"PRIu32"\n", fec_rd( b, TSEC_TDFR )); + fprintf(f,"TX exc defer %"PRIu32"\n", fec_rd( b, TSEC_TEDF )); + fprintf(f,"TX oversz %"PRIu32"\n", fec_rd( b, TSEC_TOVR )); + fprintf(f,"TX undersz %"PRIu32"\n", fec_rd( b, TSEC_TUND )); +} + +/* + * Shutdown hardware and clean out the rings + */ +void +BSP_tsec_stop_hw(struct tsec_private *mp) +{ +unsigned i; + /* stop and reset hardware */ + tsec_reset_hw( mp ); + + if ( mp->tx_ring_size ) { + /* should be OK to clear all ownership flags */ + for ( i=0; itx_ring_size; i++ ) { + bd_clrfl( &mp->tx_ring[i], TSEC_TXBD_R ); + } + BSP_tsec_swipe_tx(mp); +#if DEBUG > 0 + tsec_dump_tring(mp); + fflush(stderr); fflush(stdout); +#endif +#ifdef PARANOIA + assert( mp->tx_avail == mp->tx_ring_size ); + assert( mp->tx_head == mp->tx_tail ); + for ( i=0; itx_ring_size; i++ ) { + assert( !bd_rdbuf( & mp->tx_ring[i] ) ); + assert( !mp->tx_ring_user[i] ); + } +#endif + } + + if ( mp->rx_ring_size ) { + for ( i=0; irx_ring_size; i++ ) { + bd_clrfl( &mp->rx_ring[i], TSEC_RXBD_E ); + bd_wrbuf( &mp->rx_ring[i], 0 ); + if ( mp->rx_ring_user[i] ) + mp->consume_rxbuf( mp->rx_ring_user[i], mp->consume_rxbuf_arg, 0 ); + mp->rx_ring_user[i] = 0; + } + } +} + +/* + * calls BSP_tsec_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_tsec_detach(struct tsec_private *mp) +{ + + if ( ! mp || !mp->sc || ! mp->sc->arpcom.ac_if.if_init ) { + fprintf(stderr,"Unit not setup -- programming error!\n"); + return -1; + } + + BSP_tsec_stop_hw(mp); + + install_remove_isrs( 0, mp, mp->irq_mask ); + + free( (void*)mp->ring_area, M_DEVBUF ); + free( (void*)mp->tx_ring_user, M_DEVBUF ); + memset(mp, 0, sizeof(*mp)); + __asm__ __volatile__("":::"memory"); + + /* mark as unused */ + mp->sc->arpcom.ac_if.if_init = 0; + + return 0; +} + +/* + * 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 sent (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. + */ + +#if 0 +#define NEXT_TXD(mp, bd) ((bd_rdfl( bd ) & TSEC_TXBD_W) ? mp->tx_ring : (bd + 1)) +#endif + +/* + * 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; +} + +static inline unsigned +tsec_assign_desc( TSEC_BD *bd, uint32_t buf, unsigned len, uint32_t flags) +{ + st_be16 ( &bd->len, (uint16_t)len ); + bd_wrbuf( bd, buf ); + bd_cslfl( bd, flags, TSEC_TXBD_R | TSEC_TXBD_L ); + return len; +} + + +int +BSP_tsec_send_buf(struct tsec_private *mp, void *m_head, void *data_p, int len) +{ +int rval; +register TSEC_BD *bd; +register unsigned l,d,t; +register struct mbuf *m1; +int nmbs; +int ismbuf = (len <= 0); + +#if DEBUG > 2 + printf("send entering...\n"); + tsec_dump_tring(mp); +#endif +/* Only way to get here is when we discover that the mbuf chain + * is too long for the tx ring + */ +startover: + + rval = 0; + +#ifdef PARANOIA + assert(m_head); +#endif + + /* if no descriptor is available; try to wipe the queue */ + if ( (mp->tx_avail < 1) && TSEC_CLEAN_ON_SEND(mp)<=0 ) { + return -1; + } + + t = mp->tx_tail; + +#ifdef PARANOIA + assert( ! bd_rdbuf( &mp->tx_ring[t] ) ); + assert( ! mp->tx_ring_user[t] ); +#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_tsec_swipe_tx() + * needs bd->buf == NULL as a marker. Hence, we + * start with the second mbuf and fill the first descriptor + * last. + */ + + l = t; + d = NEXT_TXI(mp,t); + + mp->tx_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->tx_avail < 1 && TSEC_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_TXI(mp, t); l!=d; l=NEXT_TXI(mp, l) ) { + bd = & mp->tx_ring[l]; +#ifdef PARANOIA + assert( mp->tx_ring_user[l] == 0 ); +#endif + bd_wrbuf( bd, 0 ); + bd_clrfl( bd, TSEC_TXBD_R | TSEC_TXBD_L ); + + mp->tx_avail++; + } + mp->tx_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++; +#ifdef PARANOIA + printf("send return 0\n"); + tsec_dump_tring(mp); +#endif + return 0; + } +#ifdef PARANOIA + printf("repackaged; start over\n"); +#endif + goto startover; + } +#ifdef PARANOIA + printf("send return -1\n"); + tsec_dump_tring(mp); +#endif + return -1; + } + + mp->tx_avail--; + +#ifdef PARANOIA + assert( ! mp->tx_ring_user[d] ); + if ( d == t ) { + tsec_dump_tring(mp); + printf("l %i, d %i, t %i, nmbs %i\n", l,d,t, nmbs); + } else + assert( d != t ); + assert( ! bd_rdbuf( &mp->tx_ring[d] ) ); +#endif + + /* fill this slot */ + rval += tsec_assign_desc( &mp->tx_ring[d], mtod(m, uint32_t), m->m_len, TSEC_TXBD_R); + + FLUSH_BUF(mtod(m, uint32_t), m->m_len); + + l = d; + d = NEXT_TXI(mp, d); + } + + /* fill first slot - don't release to DMA yet */ + rval += tsec_assign_desc( &mp->tx_ring[t], mtod(m1, uint32_t), m1->m_len, 0); + + + FLUSH_BUF(mtod(m1, uint32_t), m1->m_len); + + } else { + /* fill first slot with raw buffer - don't release to DMA yet */ + rval += tsec_assign_desc( &mp->tx_ring[t], (uint32_t)data_p, len, 0); + + FLUSH_BUF( (uint32_t)data_p, len); + } + + /* tag last slot; this covers the case where 1st==last */ + bd_setfl( &mp->tx_ring[l], TSEC_TXBD_L ); + + /* mbuf goes into last desc */ + mp->tx_ring_user[l] = m_head; + + membarrier(); + + /* turn over the whole chain by flipping ownership of the first desc */ + bd_setfl( &mp->tx_ring[t], TSEC_TXBD_R ); + + membarrier(); + +#if DEBUG > 2 + printf("send return (l=%i, t=%i, d=%i) %i\n", l, t, d, rval); + tsec_dump_tring(mp); +#endif + + /* notify the device */ + fec_wr( mp->base, TSEC_TSTAT, TSEC_TSTAT_THLT ); + + /* Update softc */ + mp->stats.packet++; + if ( nmbs > mp->stats.maxchain ) + mp->stats.maxchain = nmbs; + + /* remember new tail */ + mp->tx_tail = d; + + return rval; /* #bytes sent */ +} + +/* + * 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_tsec_swipe_rx(struct tsec_private *mp) +{ +int rval = 0, err; +unsigned i; +uint16_t flags; +TSEC_BD *bd; +void *newbuf; +int sz; +uint16_t len; +uintptr_t baddr; + + i = mp->rx_tail; + bd = mp->rx_ring + i; + flags = bd_rdfl( bd ); + + while ( ! (TSEC_RXBD_E & flags) ) { + + /* err is not valid if not qualified by TSEC_RXBD_L */ + if ( ( err = (TSEC_RXBD_ERROR & flags) ) ) { + /* make sure error is < 0 */ + err |= 0xffff0000; + /* pass 'L' flag out so they can verify... */ + err |= (flags & TSEC_RXBD_L); + } + +#ifdef PARANOIA + assert( flags & TSEC_RXBD_L ); + assert( mp->rx_ring_user[i] ); +#endif + + if ( err || !(newbuf = mp->alloc_rxbuf(&sz, &baddr)) ) { + /* drop packet and recycle buffer */ + newbuf = mp->rx_ring_user[i]; + mp->consume_rxbuf( 0, mp->consume_rxbuf_arg, err ); + } else { + len = ld_be16( &bd->len ); + +#ifdef PARANOIA + assert( 0 == (baddr & (RX_BUF_ALIGNMENT-1)) ); + assert( len > 0 ); +#endif + + mp->consume_rxbuf( mp->rx_ring_user[i], mp->consume_rxbuf_arg, len ); + + mp->rx_ring_user[i] = newbuf; + st_be16( &bd->len, sz ); + bd_wrbuf( bd, baddr ); + } + + RTEMS_COMPILER_MEMORY_BARRIER(); + + bd_wrfl( &mp->rx_ring[i], (flags & ~TSEC_RXBD_ERROR) | TSEC_RXBD_E ); + + rval++; + + i = NEXT_RXI( mp, i ); + bd = mp->rx_ring + i; + flags = bd_rdfl( bd ); + } + + fec_wr( mp->base, TSEC_RSTAT, TSEC_RSTAT_QHLT ); + + mp->rx_tail = i; + return rval; +} + +/* read ethernet address from hw to buffer */ +void +BSP_tsec_read_eaddr(struct tsec_private *mp, unsigned char *eaddr) +{ +union { + uint32_t u; + uint16_t s[2]; + uint8_t c[4]; +} x; + st_le32( (volatile uint32_t *)(eaddr+2), fec_rd(mp->base, TSEC_MACSTNADDR1) ); + x.u = fec_rd(mp->base, TSEC_MACSTNADDR2); + st_le16( (volatile uint16_t *)(eaddr), x.s[0]); +} + +/* mdio / mii interface wrappers for rtems_mii_ioctl API */ + +/* + * Busy-wait -- this can take a while: I measured ~550 timebase-ticks + * @333333333Hz, TB divisor is 8 -> 13us + */ +static inline void mii_wait(FEC_Enet_Base b) +{ + while ( (TSEC_MIIMIND_BUSY & fec_rd( b, TSEC_MIIMIND )) ) + ; +} + +/* MII operations are rather slow :-( */ +static int +tsec_mdio_rd(int phyidx, void *uarg, unsigned reg, uint32_t *pval) +{ +uint32_t v; +#ifdef TEST_MII_TIMING +uint32_t t; +#endif +struct tsec_private *mp = uarg; +FEC_Enet_Base b = mp->phy_base; + + if ( phyidx != 0 ) + return -1; /* only one phy supported for now */ + + /* write phy and register address */ + fec_wr( b, TSEC_MIIMADD, TSEC_MIIMADD_ADDR(mp->phy,reg) ); + + /* clear READ bit */ + v = fec_rd( b, TSEC_MIIMCOM ); + fec_wr( b, TSEC_MIIMCOM, v & ~TSEC_MIIMCOM_READ ); + +#ifdef TEST_MII_TIMING + t = tb_rd(); +#endif + + /* trigger READ operation by READ-bit 0-->1 transition */ + fec_wr( b, TSEC_MIIMCOM, v | TSEC_MIIMCOM_READ ); + + /* (busy) wait for completion */ + + mii_wait( b ); + + /* Ugly workaround: I observed that the link status + * is not correctly reported when the link changes to + * a good status - a failed link is reported until + * we read twice :-( + */ + if ( MII_BMSR == reg ) { + /* trigger a second read operation */ + fec_wr( b, TSEC_MIIMCOM, v & ~TSEC_MIIMCOM_READ ); + fec_wr( b, TSEC_MIIMCOM, v | TSEC_MIIMCOM_READ ); + mii_wait( b ); + } + +#ifdef TEST_MII_TIMING + t = tb_rd() - t; + printf("Reading MII took %"PRIi32"\n", t); +#endif + /* return result */ + *pval = fec_rd( b, TSEC_MIIMSTAT ) & 0xffff; + return 0; +} + +STATIC int +tsec_mdio_wr(int phyidx, void *uarg, unsigned reg, uint32_t val) +{ +#ifdef TEST_MII_TIMING +uint32_t t; +#endif +struct tsec_private *mp = uarg; +FEC_Enet_Base b = mp->phy_base; + + if ( phyidx != 0 ) + return -1; /* only one phy supported for now */ + +#ifdef TEST_MII_TIMING + t = tb_rd(); +#endif + + fec_wr( b, TSEC_MIIMADD, TSEC_MIIMADD_ADDR(mp->phy,reg) ); + fec_wr( b, TSEC_MIIMCON, val & 0xffff ); + + mii_wait( b ); + +#ifdef TEST_MII_TIMING + t = tb_rd() - t; + printf("Writing MII took %"PRIi32"\n", t); +#endif + + return 0; +} + +/* Public, locked versions */ +uint32_t +BSP_tsec_mdio_rd(struct tsec_private *mp, unsigned reg) +{ +uint32_t val, rval; + + REGLOCK(); + rval = tsec_mdio_rd(0, mp, reg, &val ) ? -1 : val; + REGUNLOCK(); + + return rval; +} + +int +BSP_tsec_mdio_wr(struct tsec_private *mp, unsigned reg, uint32_t val) +{ +int rval; + + REGLOCK(); + rval = tsec_mdio_wr(0, mp, reg, val); + REGUNLOCK(); + + return rval; +} + +static struct rtems_mdio_info tsec_mdio = { + mdio_r: tsec_mdio_rd, + mdio_w: tsec_mdio_wr, + has_gmii: 1, +}; + + +/* + * 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 + */ +int +BSP_tsec_media_ioctl(struct tsec_private *mp, int cmd, int *parg) +{ +int rval; + /* alias cmd == 0,1 for convenience when calling from shell */ + switch ( cmd ) { + case 0: cmd = SIOCGIFMEDIA; + break; + case 1: cmd = SIOCSIFMEDIA; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + break; + default: return -1; + } + REGLOCK(); + tsec_mdio.has_gmii = mp->isfec ? 0 : 1; + rval = rtems_mii_ioctl(&tsec_mdio, mp, cmd, parg); + REGUNLOCK(); + return rval; +} + +/* Interrupt related routines */ + +/* + * When it comes to interrupts the chip has two rather + * annoying features: + * 1 once an IRQ is pending, clearing the IMASK does not + * de-assert the interrupt line. + * 2 the chip has three physical interrupt lines even though + * all events are reported in a single register. Rather + * useless; we must hook 3 ISRs w/o any real benefit. + * In fact, it makes our life a bit more difficult: + * + * Hence, for (1) we would have to mask interrupts at the PIC + * but to re-enable them we would have to do that three times + * because of (2). + * + * Therefore, we take the following approach: + * + * ISR masks all interrupts on the TSEC, acks/clears them + * and stores the acked irqs in the device struct where + * it is picked up by BSP_tsec_ack_irqs(). + * Since all interrupts are disabled until the daemon + * re-enables them after calling BSP_tsec_ack_irqs() + * no interrupts are lost. + * + * BUT: NO isr (including PHY isrs) MUST INTERRUPT ANY + * OTHER ONE, i.e., they all must have the same + * priority. Otherwise, integrity of the cached + * irq_pending variable may be compromised. + */ + +static inline void +tsec_dis_irqs( struct tsec_private *mp) +{ + phy_dis_irq( mp ); + fec_wr( mp->base, TSEC_IMASK, TSEC_IMASK_NONE ); +} + +static inline uint32_t +tsec_dis_clr_irqs(struct tsec_private *mp) +{ +uint32_t rval; +FEC_Enet_Base b = mp->base; + tsec_dis_irqs( mp ); + rval = fec_rd( b, TSEC_IEVENT); + fec_wr( b, TSEC_IEVENT, rval ); + /* Make sure we mask out the link intr */ + return rval & ~TSEC_LINK_INTR; +} + +/* + * We have 3 different ISRs just so we can count + * interrupt types independently... + */ + +static void tsec_xisr(rtems_irq_hdl_param arg) +{ +struct tsec_private *mp = (struct tsec_private *)arg; + + mp->irq_pending |= tsec_dis_clr_irqs( mp ); + + mp->stats.xirqs++; + + rtems_event_send( mp->tid, mp->event ); +} + +static void tsec_risr(rtems_irq_hdl_param arg) +{ +struct tsec_private *mp = (struct tsec_private *)arg; + + mp->irq_pending |= tsec_dis_clr_irqs( mp ); + + mp->stats.rirqs++; + + rtems_event_send( mp->tid, mp->event ); +} + +static void tsec_eisr(rtems_irq_hdl_param arg) +{ +struct tsec_private *mp = (struct tsec_private *)arg; + + mp->irq_pending |= tsec_dis_clr_irqs( mp ); + + mp->stats.eirqs++; + + rtems_event_send( mp->tid, mp->event ); +} + +static void tsec_lisr(rtems_irq_hdl_param arg) +{ +struct tsec_private *mp = (struct tsec_private *)arg; + + if ( phy_irq_pending( mp ) ) { + + tsec_dis_irqs( mp ); + + mp->irq_pending |= TSEC_LINK_INTR; + + mp->stats.lirqs++; + + rtems_event_send( mp->tid, mp->event ); + } +} + +/* Enable interrupts at device */ +void +BSP_tsec_enable_irqs(struct tsec_private *mp) +{ +rtems_interrupt_level l; + rtems_interrupt_disable( l ); + fec_wr( mp->base, TSEC_IMASK, mp->irq_mask ); + phy_en_irq( mp ); + rtems_interrupt_enable( l ); +} + +/* Disable interrupts at device */ +void +BSP_tsec_disable_irqs(struct tsec_private *mp) +{ +rtems_interrupt_level l; + rtems_interrupt_disable( l ); + tsec_dis_irqs( mp ); + rtems_interrupt_enable( l ); +} + +/* + * Acknowledge (and clear) interrupts. + * RETURNS: interrupts that were raised. + */ +uint32_t +BSP_tsec_ack_irqs(struct tsec_private *mp) +{ +uint32_t rval; + + /* no need to disable interrupts because + * this should only be called after receiving + * a RTEMS event posted by the ISR which + * already shut off interrupts. + */ + rval = mp->irq_pending; + mp->irq_pending = 0; + + if ( (rval & TSEC_LINK_INTR) ) { + /* interacting with the PHY is slow so + * we do it only if we have to... + */ + phy_ack_irq( mp ); + } + + return rval & mp->irq_mask; +} + +/* Retrieve the driver daemon TID that was passed to + * BSP_tsec_setup(). + */ + +rtems_id +BSP_tsec_get_tid(struct tsec_private *mp) +{ + return mp->tid; +} + +struct tsec_private * +BSP_tsec_getp(unsigned index) +{ + if ( index >= TSEC_NUM_DRIVER_SLOTS ) + return 0; + return & theTsecEths[index].pvt; +} + +/* + * + * 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_tsec_ack_irqs(handle); + * if ( irqs & BSP_TSEC_IRQ_TX ) { + * BSP_tsec_swipe_tx(handle); / * cleanup_txbuf() callback executed * / + * } + * if ( irqs & BSP_TSEC_IRQ_RX ) { + * BSP_tsec_swipe_rx(handle); / * alloc_rxbuf() and consume_rxbuf() executed * / + * } + * BSP_tsec_enable_irqs(handle); + * } while (1); + * + */ + +/* BSDNET SUPPORT/GLUE ROUTINES */ + +STATIC void +tsec_stop(struct tsec_softc *sc) +{ + BSP_tsec_stop_hw(&sc->pvt); + 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 = ALIGNTO(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, unsigned long); + + return (void*) m; +} + +static void consume_rx_mbuf(void *buf, void *arg, int len) +{ +struct ifnet *ifp = arg; + if ( len <= 0 ) { + ifp->if_iqdrops++; + if ( len < 0 ) { + ifp->if_ierrors++; + } + /* caller recycles mbuf */ + } else { + struct ether_header *eh; + struct mbuf *m = buf; + + 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; + + /* send buffer upwards */ + 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 + 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); +} + +/* BSDNET DRIVER CALLBACKS */ + +static void +tsec_init(void *arg) +{ +struct tsec_softc *sc = arg; +struct ifnet *ifp = &sc->arpcom.ac_if; + BSP_tsec_init_hw(&sc->pvt, ifp->if_flags & IFF_PROMISC, sc->arpcom.ac_enaddr); + ifp->if_flags |= IFF_RUNNING; + sc->arpcom.ac_if.if_timer = 0; +} + +/* bsdnet driver entry to start transmission */ +static void +tsec_start(struct ifnet *ifp) +{ +struct tsec_softc *sc = ifp->if_softc; +struct mbuf *m = 0; + + while ( ifp->if_snd.ifq_head ) { + IF_DEQUEUE( &ifp->if_snd, m ); + if ( BSP_tsec_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 +tsec_watchdog(struct ifnet *ifp) +{ +struct tsec_softc *sc = ifp->if_softc; + + ifp->if_oerrors++; + printk(DRVNAME"%i: watchdog timeout; resetting\n", ifp->if_unit); + + tsec_init(sc); + tsec_start(ifp); +} + +/* bsdnet driver ioctl entry */ +static int +tsec_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data) +{ +struct tsec_softc *sc = ifp->if_softc; +struct ifreq *ifr = (struct ifreq *)data; +#if 0 +uint32_t v; +#endif +int error = 0; +int f; + + switch ( cmd ) { + case SIOCSIFFLAGS: + f = ifp->if_flags; + if ( f & IFF_UP ) { + if ( ! ( f & IFF_RUNNING ) ) { + tsec_init(sc); + } else { + if ( (f & IFF_PROMISC) != (sc->bsd.oif_flags & IFF_PROMISC) ) { + /* Hmm - the manual says we must change the RCTRL + * register only after a reset or if DMACTRL[GRS] + * is cleared which is the normal operating + * condition. I hope this is legal ?? + */ + if ( (f & IFF_PROMISC) ) { + fec_set( sc->pvt.base, TSEC_RCTRL, TSEC_RCTRL_PROM ); + } else { + fec_clr( sc->pvt.base, TSEC_RCTRL, TSEC_RCTRL_PROM ); + } + } + /* FIXME: other flag changes are ignored/unimplemented */ + } + } else { + if ( f & IFF_RUNNING ) { + tsec_stop(sc); + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + } + } + sc->bsd.oif_flags = ifp->if_flags; + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = BSP_tsec_media_ioctl(&sc->pvt, cmd, &ifr->ifr_media); + break; + +/* + * TODO + * + * case SIOCADDMULTI: + * case SIOCDELMULTI: + * + * break; + */ + + + case SIO_RTEMS_SHOW_STATS: + BSP_tsec_dump_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 tsec_daemon(void *arg) +{ +struct tsec_softc *sc; +struct ifnet *ifp; +rtems_event_set evs; + for (;;) { + rtems_bsdnet_event_receive( EV_MSK, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &evs ); + evs &= EV_MSK; + for ( sc = theTsecEths; evs; evs>>=EV_PER_UNIT, sc++ ) { + if ( EV_IS_ANY(evs) ) { + + register uint32_t x; + + ifp = &sc->arpcom.ac_if; + + if ( !(ifp->if_flags & IFF_UP) ) { + tsec_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 = BSP_tsec_ack_irqs(&sc->pvt); + + if ( TSEC_LINK_INTR & x ) { + /* phy status changed */ + int media; + +#ifdef DEBUG + printf("LINK INTR\n"); +#endif + if ( -1 != (media = mac_set_duplex( &sc->pvt )) ) { +#ifdef DEBUG + rtems_ifmedia2str( media, 0, 0 ); + printf("\n"); +#endif + if ( IFM_LINK_OK & media ) { + ifp->if_flags &= ~IFF_OACTIVE; + tsec_start(ifp); + } else { + /* stop sending */ + ifp->if_flags |= IFF_OACTIVE; + } + } + } + + /* free tx chain */ + if ( (TSEC_TXIRQ & x) && BSP_tsec_swipe_tx(&sc->pvt) ) { + ifp->if_flags &= ~IFF_OACTIVE; + if ( TX_AVAILABLE_RING_SIZE(&sc->pvt) == sc->pvt.tx_avail ) + ifp->if_timer = 0; + tsec_start(ifp); + } + if ( (TSEC_RXIRQ & x) ) + BSP_tsec_swipe_rx(&sc->pvt); + + BSP_tsec_enable_irqs(&sc->pvt); + } + } + } +} + +/* PUBLIC RTEMS BSDNET ATTACH FUNCTION */ +int +rtems_tsec_attach(struct rtems_bsdnet_ifconfig *ifcfg, int attaching) +{ +char *unitName; +int unit,i,cfgUnits; +struct tsec_softc *sc; +struct ifnet *ifp; + + unit = rtems_bsdnet_parse_driver_name(ifcfg, &unitName); + if ( unit <= 0 || unit > TSEC_NUM_DRIVER_SLOTS ) { + printk(DRVNAME": Bad unit number %i; must be 1..%i\n", unit, TSEC_NUM_DRIVER_SLOTS); + return 1; + } + + sc = &theTsecEths[unit-1]; + ifp = &sc->arpcom.ac_if; + + 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, + TSEC_RXIRQ | TSEC_TXIRQ | TSEC_LINK_INTR) ) { + return -1; + } + + if ( nmbclusters < sc->pvt.rx_ring_size * cfgUnits + 60 /* arbitrary */ ) { + printk(DRVNAME"%i: (tsec 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_tsec_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 = tsec_init; + ifp->if_ioctl = tsec_ioctl; + ifp->if_start = tsec_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 = tsec_watchdog; + ifp->if_timer = 0; + + sc->bsd.oif_flags = /* ... */ + ifp->if_flags = IFF_BROADCAST | 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.tx_ring_size; + */ + ifp->if_snd.ifq_maxlen = ifqmaxlen; + +#ifdef TSEC_DETACH_HACK + if ( !ifp->if_addrlist ) /* do only the first time [reattach hack] */ +#endif + { + if_attach(ifp); + ether_ifattach(ifp); + } + + } else { +#ifdef TSEC_DETACH_HACK + if ( !ifp->if_init ) { + printk(DRVNAME": instance %i not attached.\n", unit); + return -1; + } + return tsec_detach(sc); +#else + printk(DRVNAME": interface detaching not implemented\n"); + return -1; +#endif + } + + return 0; +} + +/* PHY stuff should really not be in this driver but separate :-( + * However, I don't have time right now to implement clean + * boundaries: + * - PHY driver should only know about the PHY + * - TSEC driver only provides MII access and knows + * how to deal with a PHY interrupt. + * - BSP knows how interrupts are routed. E.g., the MVME3100 + * shares a single IRQ line among 3 PHYs (for the three ports) + * and provides a special 'on-board' register for determining + * what PHY raised an interrupt w/o the need to do any MII IO. + * Integrating all these bits in a clean way is not as easy as + * just hacking away, sorry... + */ + +/* + * Broadcom 54xx PHY register definitions. Unfriendly Broadcom doesn't + * release specs for their products :-( -- valuable info comes from + * the linux driver by + * Maciej W. Rozycki + * Amy Fong + */ + +#define BCM54xx_GBCR 0x09 /* gigabit control */ +#define BCM54xx_GBCR_FD (1<<9) /* full-duplex cap. */ + +#define BCM54xx_ECR 0x10 /* extended control */ +#define BCM54xx_ECR_IM (1<<12) /* IRQ mask */ +#define BCM54xx_ECR_IF (1<<12) /* IRQ force */ + +#define BCM54xx_ESR 0x11 /* extended status */ +#define BCM54xx_ESR_IRQ (1<<12) /* IRQ pending */ + +#define BCM54xx_AUXCR 0x18 /* AUX control */ +#define BCM54xx_AUXCR_PWR10BASET (1<<2) + +#define BCM54xx_AUXST 0x19 /* AUX status */ +#define BCM54xx_AUXST_LNKMM (7<<8) /* link mode mask */ + +/* link mode (linux' syngem_phy.c helped here...) + * + * 0: no link + * 1: 10BT half + * 2: 10BT full + * 3: 100BT half + * 4: 100BT half + * 5: 100BT full + * 6: 1000BT full + * 7: 1000BT full + */ + +#define BCM54xx_ISR 0x1a /* IRQ status */ +#define BCM54xx_IMR 0x1b /* IRQ mask */ +#define BCM54xx_IRQ_CRC (1<< 0) /* CRC error */ +#define BCM54xx_IRQ_LNK (1<< 1) /* LINK status chg. */ +#define BCM54xx_IRQ_SPD (1<< 2) /* SPEED change */ +#define BCM54xx_IRQ_DUP (1<< 3) /* LINK status chg. */ +#define BCM54xx_IRQ_LRS (1<< 4) /* Lcl. RX status chg.*/ +#define BCM54xx_IRQ_RRS (1<< 5) /* Rem. RX status chg.*/ +#define BCM54xx_IRQ_SSE (1<< 6) /* Scrambler sync err */ +#define BCM54xx_IRQ_UHCD (1<< 7) /* Unsupp. HCD neg. */ +#define BCM54xx_IRQ_NHCD (1<< 8) /* No HCD */ +#define BCM54xx_IRQ_HCDL (1<< 9) /* No HCD Link */ +#define BCM54xx_IRQ_ANPR (1<<10) /* Aneg. pg. req. */ +#define BCM54xx_IRQ_LC (1<<11) /* All ctrs. < 128 */ +#define BCM54xx_IRQ_HC (1<<12) /* Ctr > 32768 */ +#define BCM54xx_IRQ_MDIX (1<<13) /* MDIX status chg. */ +#define BCM54xx_IRQ_PSERR (1<<14) /* Pair swap error */ + +#define PHY_IRQS ( BCM54xx_IRQ_LNK | BCM54xx_IRQ_SPD | BCM54xx_IRQ_DUP ) + + +static void +phy_en_irq_at_phy( struct tsec_private *mp ) +{ +uint32_t ecr; + + REGLOCK(); + tsec_mdio_rd( 0, mp, BCM54xx_ECR, &ecr ); + ecr &= ~BCM54xx_ECR_IM; + tsec_mdio_wr( 0, mp, BCM54xx_ECR, ecr ); + REGUNLOCK(); +} + +static void +phy_dis_irq_at_phy( struct tsec_private *mp ) +{ +uint32_t ecr; + + REGLOCK(); + tsec_mdio_rd( 0, mp, BCM54xx_ECR, &ecr ); + ecr |= BCM54xx_ECR_IM; + tsec_mdio_wr( 0, mp, BCM54xx_ECR, ecr ); + REGUNLOCK(); +} + +static void +phy_init_irq( int install, struct tsec_private *mp, void (*isr)(rtems_irq_hdl_param) ) +{ +uint32_t v; +rtems_irq_connect_data xxx; + + xxx.on = noop; + xxx.off = noop; + xxx.isOn = nopf; + xxx.name = BSP_PHY_IRQ; + xxx.handle = mp; + xxx.hdl = isr; + + phy_dis_irq_at_phy( mp ); + + REGLOCK(); + /* Select IRQs we want */ + tsec_mdio_wr( 0, mp, BCM54xx_IMR, ~ PHY_IRQS ); + /* clear pending irqs */ + tsec_mdio_rd( 0, mp, BCM54xx_ISR, &v ); + REGUNLOCK(); + + /* install shared ISR */ + if ( ! (install ? + BSP_install_rtems_shared_irq_handler( &xxx ) : + BSP_remove_rtems_irq_handler( &xxx )) ) { + rtems_panic( "Unable to %s shared irq handler (PHY)\n", install ? "install" : "remove" ); + } +} + +/* Because on the MVME3100 multiple PHYs (belonging to different + * TSEC instances) share a single interrupt line and we want + * to disable interrupts at the PIC rather than in the individual + * PHYs (because access to those is slow) we must implement + * nesting... + */ +STATIC int phy_irq_dis_level = 0; + +/* assume 'en_irq' / 'dis_irq' cannot be interrupted. + * Either because they are called from an ISR (all + * tsec + phy isrs must have the same priority) or + * from a IRQ-protected section of code + */ +static void +phy_en_irq(struct tsec_private *mp) +{ + if ( ! ( --phy_irq_dis_level ) ) { + BSP_enable_irq_at_pic( BSP_PHY_IRQ ); + } +} + + +static void +phy_dis_irq(struct tsec_private *mp) +{ + if ( !(phy_irq_dis_level++) ) { + BSP_disable_irq_at_pic( BSP_PHY_IRQ ); + } +} + +static int +phy_irq_pending(struct tsec_private *mp) +{ + /* MVME3100 speciality: we can check for a pending + * PHY IRQ w/o having to access the MII bus :-) + */ + return in_8( BSP_MVME3100_IRQ_DETECT_REG ) & (1 << (mp->unit - 1)); +} + +static uint32_t +phy_ack_irq(struct tsec_private *mp) +{ +uint32_t v; + + REGLOCK(); + tsec_mdio_rd( 0, mp, BCM54xx_ISR, &v ); + REGUNLOCK(); + +#ifdef DEBUG + printf("phy_ack_irq: 0x%08"PRIx32"\n", v); +#endif + + return v; +} + +#if defined(PARANOIA) || defined(DEBUG) + +static void dumpbd(TSEC_BD *bd) +{ + printf("Flags 0x%04"PRIx16", len 0x%04"PRIx16", buf 0x%08"PRIx32"\n", + bd_rdfl( bd ), ld_be16( &bd->len ), bd_rdbuf( bd ) ); +} + +void tsec_dump_rring(struct tsec_private *mp) +{ +int i; +TSEC_BD *bd; + if ( !mp ) { + printf("Neet tsec_private * arg\n"); + return; + } + for ( i=0; irx_ring_size; i++ ) { + bd = &mp->rx_ring[i]; + printf("[%i]: ", i); + dumpbd(bd); + } +} + +void tsec_dump_tring(struct tsec_private *mp) +{ +int i; +TSEC_BD *bd; + if ( !mp ) { + printf("Neet tsec_private * arg\n"); + return; + } + for ( i=0; itx_ring_size; i++ ) { + bd = &mp->tx_ring[i]; + printf("[%i]: ", i); + dumpbd(bd); + } + printf("Avail: %i; Head %i; Tail %i\n", mp->tx_avail, mp->tx_head, mp->tx_tail); +} +#endif + + +#ifdef DEBUG + +#undef free +#undef malloc + +#include + +void cleanup_txbuf_test(void *u, void *a, int err) +{ + printf("cleanup_txbuf_test (releasing buf 0x%8p)\n", u); + free(u); + if ( err ) + printf("cleanup_txbuf_test: an error was detected\n"); +} + +void *alloc_rxbuf_test(int *p_size, uintptr_t *p_data_addr) +{ +void *rval; + + *p_size = 1600; + rval = malloc( *p_size + RX_BUF_ALIGNMENT ); + *p_data_addr = (uintptr_t)ALIGNTO(rval,RX_BUF_ALIGNMENT); + + /* PRIxPTR is still broken :-( */ + printf("alloc_rxxbuf_test: allocated %i bytes @0x%8p/0x%08"PRIx32"\n", + *p_size, rval, (uint32_t)*p_data_addr); + + return rval; +} + +void consume_rxbuf_test(void *user_buf, void *consume_rxbuf_arg, int len) +{ +int i; +uintptr_t d = (uintptr_t)ALIGNTO(user_buf,RX_BUF_ALIGNMENT); + + /* PRIxPTR is still broken */ + printf("consuming rx buf 0x%8p (data@ 0x%08"PRIx32")\n",user_buf, (uint32_t)d); + if ( len > 32 ) + len = 32; + if ( len < 0 ) + printf("consume_rxbuf_test: ERROR occurred: 0x%x\n", len); + else { + printf("RX:"); + for ( i=0; i, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 + +/* Motload configures PCI interrupts to start at 16 and up but + * we'd rather have them starting at 0. + * Use this callback to fix them up. + */ +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_EXT_IRQ_NUMBER ) { + pci_write_config_byte( bus, slot, fun, PCI_INTERRUPT_LINE, line - BSP_EXT_IRQ_NUMBER ); + } + + return 0; +} + +void BSP_motload_pci_fixup() +{ + BSP_pciScan(0, fixup_irq_line, 0); +} + +void detect_host_bridge() +{ + OpenPIC = (volatile struct OpenPIC *) (BSP_8540_CCSR_BASE + BSP_OPEN_PIC_BASE_OFFSET); +} + +static int +dump_dev_cb( + int bus, + int dev, + int fun, + void *uarg +) +{ + uint16_t vi,di; + uint16_t cd,st; + uint32_t b1,b2; + uint8_t il,ip; + + pci_read_config_word (bus, dev, fun, PCI_VENDOR_ID, &vi); + pci_read_config_word (bus, dev, fun, PCI_DEVICE_ID, &di); + pci_read_config_word (bus, dev, fun, PCI_COMMAND, &cd); + pci_read_config_word (bus, dev, fun, PCI_STATUS, &st); + pci_read_config_dword(bus, dev, fun, PCI_BASE_ADDRESS_0, &b1); + pci_read_config_dword(bus, dev, fun, PCI_BASE_ADDRESS_1, &b2); + pci_read_config_byte (bus, dev, fun, PCI_INTERRUPT_LINE, &il); + pci_read_config_byte (bus, dev, fun, PCI_INTERRUPT_PIN, &ip); + + printk("%3d:0x%02x:%d 0x%04x-0x%04x: 0x%04x 0x%04x 0x%08x 0x%08x %d -> %3d (=0x%02x)\n", + bus, dev, fun, vi, di, cd, st, b1, b2, ip, il, il); + return 0; +} + +void +BSP_pciConfigDump_early() +{ + printk("BUS:SLOT:FUN VENDOR-DEV_ID: COMMAND STATUS BASE_ADDR0 BASE_ADDR1 IRQ_PIN -> IRQ_LINE\n"); + BSP_pciScan(0, dump_dev_cb, 0); +} diff --git a/c/src/lib/libbsp/powerpc/mvme3100/preinstall.am b/c/src/lib/libbsp/powerpc/mvme3100/preinstall.am new file mode 100644 index 0000000000..76a8db09dd --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/preinstall.am @@ -0,0 +1,143 @@ +## 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_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/$(dirstamp): + @$(MKDIR_P) $(PROJECT_INCLUDE)/bsp + @: > $(PROJECT_INCLUDE)/bsp/$(dirstamp) +PREINSTALL_DIRS += $(PROJECT_INCLUDE)/bsp/$(dirstamp) + +$(PROJECT_INCLUDE)/coverhd.h: ../../shared/include/coverhd.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/coverhd.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/coverhd.h + +$(PROJECT_INCLUDE)/tod.h: ../../shared/tod.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tod.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/tod.h + +$(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)/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)/linkcmds: ../shared/startup/linkcmds $(PROJECT_LIB)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_LIB)/linkcmds +PREINSTALL_FILES += $(PROJECT_LIB)/linkcmds + +$(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/openpic.h: ../../powerpc/shared/openpic/openpic.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/openpic.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/openpic.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/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/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/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/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/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/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/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/VMEDMA.h: ../../shared/vmeUniverse/VMEDMA.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/VMEDMA.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/VMEDMA.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/mpc8540_i2c_busdrv.h: i2c/mpc8540_i2c_busdrv.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/mpc8540_i2c_busdrv.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/mpc8540_i2c_busdrv.h + +$(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/if_tsec_pub.h: network/if_tsec_pub.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/if_tsec_pub.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/if_tsec_pub.h + diff --git a/c/src/lib/libbsp/powerpc/mvme3100/start/start.S b/c/src/lib/libbsp/powerpc/mvme3100/start/start.S new file mode 100644 index 0000000000..cab9261c0a --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/start/start.S @@ -0,0 +1,67 @@ +/* + * start.S : RTEMS entry point + * + * 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. + * + * Modified for mvme3100 by T. Straumann, 2007. + * + * $Id$ + * + */ + +#include +#include +#include + +#include + +#define SYNC \ + sync; \ + isync + +#define KERNELBASE 0x0 + + .text + .globl __rtems_entry_point + .type __rtems_entry_point,@function +__rtems_entry_point: + mr r31,r3 + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + + /* Use MotLoad's TLB setup for now; caches are on already */ + bl __eabi /* setup EABI and SYSV environment */ + bl zero_bss + /* + * restore original args + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl save_boot_params + /* + * stack = &__rtems_end + 4096 + */ + addis r9,r0, __rtems_end+(4096-PPC_MINIMUM_STACK_FRAME_SIZE)@ha + addi r9,r9, __rtems_end+(4096-PPC_MINIMUM_STACK_FRAME_SIZE)@l + /* align down to 16-bytes */ + li r5, (CPU_STACK_ALIGNMENT - 1) + andc r1, r9, r5 + /* + * We are now in a environment that is totally independent from + * bootloader setup. + */ + lis r5,environ@ha + la r5,environ@l(r5) /* environp */ + li r4, 0 /* argv */ + li r3, 0 /* argc */ + bl boot_card + /* point of no return: reset board here ? */ diff --git a/c/src/lib/libbsp/powerpc/mvme3100/startup/bspstart.c b/c/src/lib/libbsp/powerpc/mvme3100/startup/bspstart.c new file mode 100644 index 0000000000..5fb1481715 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/startup/bspstart.c @@ -0,0 +1,480 @@ +/* + * 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.rtems.com/license/LICENSE. + * + * Modified to support the MCP750. + * Modifications Copyright (C) 1999 Eric Valette. valette@crf.canon.fr + * + * Modified for mvme3100 by T. Straumann + * + * $Id$ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHOW_MORE_INIT_SETTINGS +#undef DEBUG + +#ifdef DEBUG +#define STATIC +#else +#define STATIC static +#endif + +extern unsigned long __rtems_end[]; +extern void bsp_cleanup(void); +extern void BSP_vme_config(); + +SPR_RW(SPRG0) +SPR_RW(SPRG1) + +/* + * Copy Additional boot param passed by boot loader + */ +#define CMDLINE_BUF_SIZE 2048 + +static char cmdline_buf[CMDLINE_BUF_SIZE] = {0}; +char *BSP_commandline_string = cmdline_buf; + +extern const char *BSP_build_date; + +/* + * Vital Board data Start using DATA RESIDUAL + */ +uint32_t bsp_clicks_per_usec = 0; +/* + * Total memory using RESIDUAL DATA + */ +unsigned int BSP_mem_size = 0; +/* + * Where the heap starts; is used by bsp_pretasking_hook; + */ +unsigned int BSP_heap_start = 0; +/* + * PCI Bus Frequency + */ +unsigned int BSP_pci_bus_frequency = 0xdeadbeef; +/* + * PPC Bus Frequency + */ +unsigned int BSP_bus_frequency = 0; +/* + * processor clock frequency + */ +unsigned int BSP_processor_frequency = 0; +/* + * Time base divisior (how many tick for 1 second). + */ +unsigned int BSP_time_base_divisor = 8000; /* if external RTC clock unused (HID0) */ + +/* 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}; +char BSP_enetAddr2[7] = {0}; + +static void +prether(char *b, int idx) +{ +int i; + printk("Ethernet %i %02X", idx, *b++); + for ( i=0; i<5; i++ ) + printk(":%02X",*b++); + printk("\n"); +} + +/* + * system init stack and soft ir stack size + */ +#define INIT_STACK_SIZE 0x1000 +#define INTR_STACK_SIZE rtems_configuration_get_interrupt_stack_size() + +BSP_output_char_function_type BSP_output_char = BSP_output_char_via_serial; + +void BSP_panic(char *s) +{ + printk("\n%s PANIC %s\n",_RTEMS_version, s); + __asm__ __volatile ("sc"); +} + +void _BSP_Fatal_error(unsigned int v) +{ + printk("\n%s PANIC ERROR %x\n",_RTEMS_version, v); + __asm__ __volatile ("sc"); +} + +/* + * 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 + */ + +void save_boot_params(void* r3, void *r4, void* r5, char *additional_boot_options) +{ + + strncpy(cmdline_buf, additional_boot_options, CMDLINE_BUF_SIZE); + cmdline_buf[CMDLINE_BUF_SIZE - 1] ='\0'; +} + +#define CS_CONFIG_CS_EN (1<<31) +#define CS_BNDS_SA(x) ((((uint32_t)(x))>>(31-15)) & 0xff) +#define CS_BNDS_EA(x) ((((uint32_t)(x))>>(31-31)) & 0xff) + +static inline uint32_t +_ccsr_rd32(uint32_t off) +{ + return in_be32( (volatile unsigned *)(BSP_8540_CCSR_BASE + off) ); +} + +static inline void +_ccsr_wr32(uint32_t off, uint32_t val) +{ + out_be32( (volatile unsigned *)(BSP_8540_CCSR_BASE + off), val ); +} + + +STATIC uint32_t +BSP_get_mem_size() +{ +int i; +uint32_t cs_bnds, cs_config; +uint32_t memsz=0; +uint32_t v; + + for ( cs_bnds = 0x2000, cs_config=0x2080, i=0; i<4; i++, cs_bnds+=8, cs_config+=4 ) { + if ( CS_CONFIG_CS_EN & _ccsr_rd32( cs_config ) ) { + v = _ccsr_rd32( cs_bnds ); + + memsz += CS_BNDS_EA(v) - CS_BNDS_SA(v) + 1; + } + } + return memsz << 24; +} + +STATIC void +BSP_calc_freqs() +{ +uint32_t porpllsr = _ccsr_rd32( 0xe0000 ); +unsigned plat_ratio = (porpllsr >> (31-30)) & 0x1f; +unsigned e500_ratio = (porpllsr >> (31-15)) & 0x3f; + + switch ( plat_ratio ) { + case 2: case 3: case 4: case 5: case 6: + case 8: case 9: case 10: case 12: case 16: + /* supported ratios */ + BSP_bus_frequency = BSP_pci_bus_frequency * plat_ratio; + break; + + default: + BSP_panic("Unknown PLL sys-clock ratio; something's wrong here"); + } + + switch ( e500_ratio ) { + case 4: case 5: case 6: case 7: + BSP_processor_frequency = (BSP_pci_bus_frequency * e500_ratio) >> 1; + break; + + default: + BSP_panic("Unknown PLL e500-clock ratio; something's wrong here"); + } + + printk("Core Complex Bus (CCB) Clock Freq: %10u Hz\n", BSP_bus_frequency); + printk("CPU Clock Freq: %10u Hz\n", BSP_processor_frequency); +} + +void +bsp_predriver_hook(void) +{ + /* Some drivers (RTC) may need i2c */ + BSP_i2c_initialize(); +} + +/* + * bsp_start + * + * This routine does the bulk of the system initialization. + */ + +void bsp_start( void ) +{ +unsigned char *stack; +register uint32_t intrStack; +register uint32_t *intrStackPtr; +unsigned char *work_space_start; +char *chpt; +ppc_cpu_id_t myCpu; +ppc_cpu_revision_t myCpuRevision; + +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: BusClockHz, instance: 0, buf: &BSP_pci_bus_frequency, buflen: sizeof(BSP_pci_bus_frequency) }, + { key: EthernetAddr, instance: 0, buf: BSP_enetAddr0, buflen: sizeof(BSP_enetAddr0) }, + { key: EthernetAddr, instance: 1, buf: BSP_enetAddr1, buflen: sizeof(BSP_enetAddr1) }, + { key: EthernetAddr, instance: 2, buf: BSP_enetAddr2, buflen: sizeof(BSP_enetAddr2) }, + VPD_END +}; + + /* Intersperse messages with actions to help locate problems */ + printk("-----------------------------------------\n"); + + /* + * Get CPU identification dynamically. Note that the get_ppc_cpu_type() + * function store the result in global variables so that it can be used + * later... + */ + myCpu = get_ppc_cpu_type(); + myCpuRevision = get_ppc_cpu_revision(); + + printk("Welcome to %s\n", _RTEMS_version); + printk("BSP: %s, CVS Release ($Name$)\n", "mvme3100"); + + /* + * 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)); +#if 0 + stack = ((unsigned char*) __rtems_end) + + INIT_STACK_SIZE - PPC_MINIMUM_STACK_FRAME_SIZE; +#endif + + /* tag the bottom */ + *((uint32_t*)stack) = 0; + + /* + * Initialize the interrupt related settings + * SPRG1 = software managed IRQ stack + * + * This could be done later (e.g in IRQ_INIT) but it helps to understand + * some settings below... + */ + BSP_heap_start = ((uint32_t) __rtems_end) + INIT_STACK_SIZE + INTR_STACK_SIZE; + + /* reserve space for the marker/tag frame */ + intrStack = BSP_heap_start - PPC_MINIMUM_STACK_FRAME_SIZE; + + /* make sure it's properly aligned */ + intrStack &= ~(CPU_STACK_ALIGNMENT-1); + + /* tag the bottom (T. Straumann 6/36/2001 ) */ + intrStackPtr = (uint32_t*) intrStack; + *intrStackPtr = 0; + + _write_SPRG1(intrStack); + + /* signal them that we have fixed PR288 - eventually, this should go away */ + _write_SPRG0(PPC_BSP_HAS_FIXED_PR288); + + /* + * Initialize default raw exception handlers. See vectors/vectors_init.c + */ + initialize_exceptions(); + + printk("CPU 0x%x - rev 0x%x\n", myCpu, myCpuRevision); + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Additionnal boot options are %s\n", BSP_commandline_string); + printk("Initial system stack at %x\n", stack); + printk("Software IRQ stack at %x\n", intrStack); +#endif + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Going to start PCI buses scanning and initialization\n"); +#endif + + printk("Build Date: %s\n",BSP_build_date); + + BSP_vpdRetrieveFields( vpdData ); + + printk("Board Type: %s (S/N %s)\n", + BSP_productIdent[0] ? BSP_productIdent : "n/a", + BSP_serialNumber[0] ? BSP_serialNumber : "n/a"); + + printk("External (=PCI Bus) Clock Freq "); + if ( 0xdeadbeef == BSP_pci_bus_frequency ) { + BSP_pci_bus_frequency = 66666666; + printk(" NOT FOUND in VPD; using %10u Hz\n", + BSP_pci_bus_frequency); + } else { + printk(": %10u Hz\n", + BSP_pci_bus_frequency); + } + + /* Calculate CPU and CCB bus freqs */ + BSP_calc_freqs(); + + pci_initialize(); + + prether(BSP_enetAddr0, 0); + prether(BSP_enetAddr1, 1); + prether(BSP_enetAddr2, 2); + + /* need to tweak the motload setup */ + BSP_motload_pci_fixup(); + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Number of PCI buses found is : %d\n", pci_bus_count()); + { + void BSP_pciConfigDump_early(); + BSP_pciConfigDump_early(); + } +#endif + +#ifdef TEST_RAW_EXCEPTION_CODE + printk("Testing exception handling Part 1\n"); + /* + * Cause a software exception + */ + __asm__ __volatile ("sc"); + /* + * Check we can still catch exceptions and return coorectly. + */ + printk("Testing exception handling Part 2\n"); + __asm__ __volatile ("sc"); + + /* + * Somehow doing the above seems to clobber SPRG0 on the mvme2100. It + * is probably a not so subtle hint that you do not want to use PPCBug + * once RTEMS is up and running. Anyway, we still needs to indicate + * that we have fixed PR288. Eventually, this should go away. + */ + _write_SPRG0(PPC_BSP_HAS_FIXED_PR288); +#endif + + BSP_mem_size = BSP_get_mem_size(); + + 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); + + BSP_bus_frequency = 333333333; + BSP_processor_frequency = 833333333; + BSP_time_base_divisor = 8000; /* if external RTC clock unused (HID0) */ + + /* clear hostbridge errors but leave MCP disabled - + * PCI config space scanning code will trip otherwise :-( + */ + _BSP_clear_hostbridge_errors(0 /* enableMCP */, 0/*quiet*/); + + /* + * Set up our hooks + * Make sure libc_init is done before drivers initialized so that + * they can use atexit() + */ + +#if 0 + Cpu_table.interrupt_stack_size = CONFIGURE_INTERRUPT_STACK_MEMORY; +/* FIXME */ +#endif + 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 + + work_space_start = + (unsigned char *)BSP_mem_size - Configuration.work_space_size; + + if ( work_space_start <= + ((unsigned char *)__rtems_end) + INIT_STACK_SIZE + INTR_STACK_SIZE) { + printk( "bspstart: Not enough RAM!!!\n" ); + bsp_cleanup(); + } + + Configuration.work_space_start = work_space_start; + + /* + * Initalize RTEMS IRQ system + */ + BSP_rtems_irq_mng_init(0); + + if (1) { + int i; + unsigned msr,tcr; + asm volatile("mfmsr %0":"=r"(msr)); + asm volatile("mftcr %0":"=r"(tcr)); + printk("MSR is 0x%08x, TCR 0x%08x\n",msr,tcr); + asm volatile("mttcr %0"::"r"(0)); + if (0) { + asm volatile("mtmsr %0"::"r"(msr|0x8000)); + for (i=0; i<12; i++) + BSP_enable_irq_at_pic(i); + printk("IRQS enabled\n"); + } + } + + if (0) { + extern unsigned ppc_exc_lock_std, ppc_exc_gpr3_std; + unsigned x; + asm volatile("mfivpr %0":"=r"(x)); + printk("IVPR: 0x%08x\n",x); + asm volatile("mfivor8 %0":"=r"(x)); + printk("IVOR8: 0x%08x\n",x); + printk("0x%08x\n",*(unsigned *)0xc00); + printk("0x%08x\n",*(unsigned *)0xc04); + printk("0x%08x\n",*(unsigned *)0xc08); + printk("0x%08x\n\n\n",*(unsigned *)0xc0c); + if (0) { + *(unsigned *)0xc08 = 0x4c000064; + asm volatile("dcbf 0, %0; icbi 0, %0; sync; isync"::"r"(0xc00)); + } + + printk("0x%08x\n", ppc_exc_lock_std); + printk("0x%08x\n", ppc_exc_gpr3_std); + + asm volatile("sc"); + + printk("0x%08x\n", ppc_exc_lock_std); + printk("0x%08x\n", ppc_exc_gpr3_std); + } + + printk("-----------------------------------------\n"); + +#ifdef SHOW_MORE_INIT_SETTINGS + printk("Exit from bspstart\n"); +#endif + +} diff --git a/c/src/lib/libbsp/powerpc/mvme3100/startup/misc.c b/c/src/lib/libbsp/powerpc/mvme3100/startup/misc.c new file mode 100644 index 0000000000..98ab3eb07b --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/startup/misc.c @@ -0,0 +1,133 @@ +/* $Id$ */ + +/* Miscellaneous small BSP routines; reboot, board CSR, ... */ + +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was created by + * + * Till Straumann , 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' 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 + +void +rtemsReboot() +{ +uint8_t v; + /* + * AFAIK, the hardest reset available; cleared + * some errors a VME-bus reset wouldn't (hung + * i2c bus)... + */ + v = in_8( BSP_MVME3100_SYS_CR ); + v &= ~BSP_MVME3100_SYS_CR_RESET_MSK; + v |= BSP_MVME3100_SYS_CR_RESET; + out_8( BSP_MVME3100_SYS_CR, v ); +} + +uint8_t +BSP_setSysReg(volatile uint8_t *r, uint8_t mask) +{ +uint8_t v; +rtems_interrupt_level l; + + if ( !mask ) + return in_8( r ); + + rtems_interrupt_disable(l); + v = in_8( r ); + if ( mask ) { + out_8( r, v | mask ); + } + rtems_interrupt_enable(l); + return v; +} + +uint8_t +BSP_clrSysReg(volatile uint8_t *r, uint8_t mask) +{ +uint8_t v; +rtems_interrupt_level l; + + if ( !mask ) + return in_8( r ); + + rtems_interrupt_disable(l); + v = in_8( r ); + if ( mask ) { + out_8( r, v & ~mask ); + } + rtems_interrupt_enable(l); + return v; +} + +uint8_t +BSP_setLEDs(uint8_t mask) +{ + return BSP_setSysReg( BSP_MVME3100_SYS_IND_REG, mask ); +} + +uint8_t +BSP_clrLEDs(uint8_t mask) +{ + return BSP_clrSysReg( BSP_MVME3100_SYS_IND_REG, mask ); +} + +uint8_t +BSP_eeprom_write_protect() +{ +uint8_t m = BSP_MVME3100_SYS_CR_EEPROM_WP; +volatile uint8_t *r = BSP_MVME3100_SYS_CR; + + return m & BSP_setSysReg( r, m ); +} + +uint8_t +BSP_eeprom_write_enable() +{ +uint8_t m = BSP_MVME3100_SYS_CR_EEPROM_WP; +volatile uint8_t *r = BSP_MVME3100_SYS_CR; + + return m & BSP_clrSysReg( r, m ); +} diff --git a/c/src/lib/libbsp/powerpc/mvme3100/tod/todcfg.c b/c/src/lib/libbsp/powerpc/mvme3100/tod/todcfg.c new file mode 100644 index 0000000000..7d29377c21 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/tod/todcfg.c @@ -0,0 +1,28 @@ +/* + * This file contains the RTC driver table for the MVME3100 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. + * + * Modified for mvme3100 by T. Straumann, 2007 + * + * $Id$ + */ + +#include +#include +#include + +/* The following table configures the RTC drivers used in this BSP */ +rtc_tbl RTC_Table[] = { + DS1375_RTC_TBL_ENTRY(BSP_I2C_DS1375_RAW_DEV_NAME), +}; + +/* 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/mvme3100/vme/VMEConfig.h b/c/src/lib/libbsp/powerpc/mvme3100/vme/VMEConfig.h new file mode 100644 index 0000000000..3366efab41 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/mvme3100/vme/VMEConfig.h @@ -0,0 +1,116 @@ +#ifndef RTEMS_BSP_VME_CONFIG_H +#define RTEMS_BSP_VME_CONFIG_H +/* $Id$ */ + +/* mvme3100 BSP specific address space configuration parameters */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann , 2002..2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This 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 + */ + +/* + * NOTE: the BSP (startup/bspstart.c) uses + * hardcoded window lengths that match this + * layout: + */ + +#define _VME_A32_WIN0_ON_PCI 0xc0000000 +#define _VME_CSR_ON_PCI 0xce000000 +#define _VME_A24_ON_PCI 0xcf000000 +#define _VME_A16_ON_PCI 0xcfff0000 + +/* start of the A32 window on the VME bus + * TODO: this should perhaps be a run-time configuration option + */ +#define _VME_A32_WIN0_ON_VME 0x20000000 + +/* if _VME_DRAM_OFFSET is defined, the BSP + * will map the board RAM onto the VME bus, starting + * at _VME_DRAM_OFFSET + */ +#define _VME_DRAM_OFFSET 0xc0000000 + +/* If your BSP requires a non-standard way to configure + * the VME interrupt manager then define the symbol + * + * BSP_VME_INSTALL_IRQ_MGR + * + * to a proper instruction sequence that installs the + * universe interrupt manager. This requires knowledge + * of the wiring between the universe and the PIC (main + * interrupt controller), i.e., which IRQ 'pins' of the + * universe are wired to which 'lines'/inputs at the PIC. + * (consult vmeUniverse.h for more information). + * + * When installing the universe IRQ manager it is also + * possible to specify whether it should try to share + * PIC interrupts with other sources. This might not + * be supported by all BSPs (but the unverse driver + * recognizes that). + * + * If BSP_VME_INSTALL_IRQ_MGR is undefined then + * the default algorithm is used (vme_universe.c): + * + * This default setup uses only a single wire. It reads + * the PIC 'line' from PCI configuration space and assumes + * this to be wired to the first (LIRQ0) IRQ input at the + * universe. The default setup tries to use interrupt + * sharing. + */ +#define BSP_VME_INSTALL_IRQ_MGR(err) \ + do { \ + err = vmeTsi148InstallIrqMgrAlt(\ + VMETSI148_IRQ_MGR_FLAG_SHARED, /* use shared IRQs */ \ + 0, BSP_VME0_IRQ, \ + 1, BSP_VME1_IRQ, \ + 2, BSP_VME2_IRQ, \ + 3, BSP_VME3_IRQ, \ + -1 /* terminate list */ \ + ); \ + } while (0) + +/* This BSP uses the Tsi148 Driver */ +#define _VME_DRIVER_TSI148 + +#endif -- cgit v1.2.3