diff options
author | Daniel Hellstrom <daniel@gaisler.com> | 2015-02-23 13:02:39 +0100 |
---|---|---|
committer | Daniel Hellstrom <daniel@gaisler.com> | 2015-04-17 01:10:17 +0200 |
commit | 3bb41226e0941b86d58ecb97f7d292677de573c8 (patch) | |
tree | 907aa270343f7c6d1bc08bf73288fb9b10da6197 /c | |
parent | LEON: added network device configuration helper function (diff) | |
download | rtems-3bb41226e0941b86d58ecb97f7d292677de573c8.tar.bz2 |
LEON: added new drivers to the LEON2/LEON3 BSPs
Most drivers use the Driver Manager for device probing, they
work on AMBA-over-PCI systems if PCI is big-endian.
New APIs:
* GPIO Library, interfaced to GRGPIO
* GENIRQ, Generic interrupt service implementation helper
New GRLIB Drivers:
* ACTEL 1553 RT, user interface is similar to 1553 BRM driver
* GR1553 (1553 BC, RT and BM core)
* AHBSTAT (AHB error status core)
* GRADCDAC (Core interfacing to ADC/DAC hardware)
* GRGPIO (GPIO port accessed from GPIO Library)
* MCTRL (Memory controller settings configuration)
* GRETH (10/100/1000 Ethernet driver using Driver manager)
* GRPWM (Pulse Width Modulation core)
* SPICTRL (SPI master interface)
* GRSPW_ROUTER (SpaceWire Router AMBA configuration interface)
* GRCTM (SpaceCraft on-board Time Management core)
* SPWCUC (Time distribution over SpaceWire)
* GRTC (SpaceCraft up-link Tele core)
* GRTM (SpaceCraft down-link Tele Metry core)
GR712RC ASIC specific interfaces:
* GRASCS
* CANMUX (select between OCCAN and SATCAN)
* SATCAN
* SLINK
Diffstat (limited to 'c')
55 files changed, 21513 insertions, 5 deletions
diff --git a/c/src/lib/libbsp/sparc/Makefile.am b/c/src/lib/libbsp/sparc/Makefile.am index cbd2a14bc0..13411ccf5a 100644 --- a/c/src/lib/libbsp/sparc/Makefile.am +++ b/c/src/lib/libbsp/sparc/Makefile.am @@ -12,11 +12,15 @@ EXTRA_DIST += shared/start/start.S # Interrupt EXTRA_DIST += shared/irq/irq-shared.c EXTRA_DIST += shared/irq/bsp_isr_handler.c +EXTRA_DIST += shared/irq/genirq.c +EXTRA_DIST += shared/include/genirq.h # AMBA Plug&Play bus +EXTRA_DIST += shared/include/ahbstat.h EXTRA_DIST += shared/include/ambapp.h EXTRA_DIST += shared/include/ambapp_ids.h EXTRA_DIST += shared/include/grlib.h +EXTRA_DIST += shared/amba/ahbstat.c EXTRA_DIST += shared/amba/ambapp.c EXTRA_DIST += shared/amba/ambapp_alloc.c EXTRA_DIST += shared/amba/ambapp_count.c @@ -58,9 +62,11 @@ EXTRA_DIST += shared/pci/gr_tmtc_1553.c # DEBUG EXTRA_DIST += shared/include/debug_defs.h -# SpaceWire (GRSPW) +# SpaceWire EXTRA_DIST += shared/spw/grspw.c +EXTRA_DIST += shared/spw/grspw_router.c EXTRA_DIST += shared/include/grspw.h +EXTRA_DIST += shared/include/grspw_router.h # UART EXTRA_DIST += shared/uart/cons.c @@ -75,14 +81,58 @@ EXTRA_DIST += shared/can/grcan.c EXTRA_DIST += shared/include/occan.h EXTRA_DIST += shared/include/grcan.h +# MEM +EXTRA_DIST += shared/mem/mctrl.c + # MIL-STD-B1553 (Core1553BRM) EXTRA_DIST += shared/1553/b1553brm.c +EXTRA_DIST += shared/1553/b1553rt.c EXTRA_DIST += shared/include/b1553brm.h +EXTRA_DIST += shared/include/b1553rt.h + +# MIL-STD-B1553 (GR1553B) +EXTRA_DIST += shared/1553/gr1553b.c +EXTRA_DIST += shared/1553/gr1553bc.c +EXTRA_DIST += shared/1553/gr1553bm.c +EXTRA_DIST += shared/1553/gr1553rt.c +EXTRA_DIST += shared/include/gr1553b.h +EXTRA_DIST += shared/include/gr1553bc.h +EXTRA_DIST += shared/include/gr1553bc_list.h +EXTRA_DIST += shared/include/gr1553bm.h +EXTRA_DIST += shared/include/gr1553rt.h # I2C-master (I2CMST) EXTRA_DIST += shared/i2c/i2cmst.c EXTRA_DIST += shared/include/i2cmst.h +# SPI +EXTRA_DIST += shared/spi/spictrl.c +EXTRA_DIST += shared/include/spictrl.h + +# TIME +EXTRA_DIST += shared/time/spwcuc.c +EXTRA_DIST += shared/time/grctm.c +EXTRA_DIST += shared/include/spwcuc.h +EXTRA_DIST += shared/include/grctm.h + +# GPIO +EXTRA_DIST += shared/gpio/grgpio.c +EXTRA_DIST += shared/gpio/gpiolib.c +EXTRA_DIST += shared/include/grgpio.h +EXTRA_DIST += shared/include/gpiolib.h + +# PWM +EXTRA_DIST += shared/pwm/grpwm.c +EXTRA_DIST += shared/include/grpwm.h + +# ADC and DAC +EXTRA_DIST += shared/analog/gradcdac.c +EXTRA_DIST += shared/include/gradcdac.h + +# GRETH +EXTRA_DIST += shared/net/greth.c +EXTRA_DIST += shared/include/greth.h + # Network configuration EXTRA_DIST += shared/net/network_interface_add.c EXTRA_DIST += shared/include/network_interface_add.h @@ -96,6 +146,19 @@ EXTRA_DIST += shared/drvmgr/leon2_amba_bus.c EXTRA_DIST += shared/include/drvmgr/ambapp_bus_grlib.h EXTRA_DIST += shared/include/drvmgr/ambapp_bus.h EXTRA_DIST += shared/include/drvmgr/leon2_amba_bus.h +# GR712 +EXTRA_DIST += shared/ascs/grascs.c +EXTRA_DIST += shared/include/grascs.h +EXTRA_DIST += shared/can/satcan.c +EXTRA_DIST += shared/include/satcan.h +EXTRA_DIST += shared/slink/grslink.c +EXTRA_DIST += shared/include/grslink.h + +# TMTC +EXTRA_DIST += shared/tmtc/grtc.c +EXTRA_DIST += shared/include/grtc.h +EXTRA_DIST += shared/tmtc/grtm.c +EXTRA_DIST += shared/include/grtm.h include $(top_srcdir)/../../../automake/subdirs.am include $(top_srcdir)/../../../automake/local.am diff --git a/c/src/lib/libbsp/sparc/leon2/Makefile.am b/c/src/lib/libbsp/sparc/leon2/Makefile.am index 3f29d46c46..dba192395b 100644 --- a/c/src/lib/libbsp/sparc/leon2/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon2/Makefile.am @@ -58,6 +58,8 @@ libbsp_a_SOURCES += console/console.c console/debugputs.c libbsp_a_SOURCES += clock/ckinit.c libbsp_a_SOURCES += ../../shared/clockdrv_shell.h # IRQ +include_HEADERS += ../../sparc/shared/include/genirq.h +libbsp_a_SOURCES += ../../sparc/shared/irq/genirq.c include_bsp_HEADERS += \ ../../shared/include/irq-generic.h \ ../../shared/include/irq-info.h \ @@ -76,6 +78,7 @@ libbsp_a_SOURCES += \ include_HEADERS += ../../sparc/shared/include/ambapp.h include_HEADERS += ../../sparc/shared/include/ambapp_ids.h include_HEADERS += ../../sparc/shared/include/grlib.h +include_HEADERS += ../../sparc/shared/include/ahbstat.h libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_alloc.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_count.c @@ -86,6 +89,7 @@ libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_parent.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_old.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_names.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_show.c +libbsp_a_SOURCES += ../../sparc/shared/amba/ahbstat.c # Clock Driver and Timer Library include_HEADERS += ../../sparc/shared/include/tlib.h @@ -114,7 +118,20 @@ libbsp_a_SOURCES += ../../sparc/shared/pci/gr_rasta_tmtc.c # B1553BRM include_HEADERS += ../../sparc/shared/include/b1553brm.h +include_HEADERS += ../../sparc/shared/include/b1553rt.h libbsp_a_SOURCES += ../../sparc/shared/1553/b1553brm.c +libbsp_a_SOURCES += ../../sparc/shared/1553/b1553rt.c + +# GR1553B +include_HEADERS += ../../sparc/shared/include/gr1553b.h +include_HEADERS += ../../sparc/shared/include/gr1553bc.h +include_HEADERS += ../../sparc/shared/include/gr1553bc_list.h +include_HEADERS += ../../sparc/shared/include/gr1553bm.h +include_HEADERS += ../../sparc/shared/include/gr1553rt.h +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553b.c +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553bc.c +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553bm.c +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553rt.c # CAN include_HEADERS += ../../sparc/shared/include/occan.h @@ -124,7 +141,9 @@ libbsp_a_SOURCES += ../../sparc/shared/can/occan.c \ # SpaceWire include_HEADERS += ../../sparc/shared/include/grspw.h +include_HEADERS += ../../sparc/shared/include/grspw_router.h libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw_router.c # UART (RAW) include_HEADERS += ../../sparc/shared/include/apbuart.h @@ -134,9 +153,42 @@ libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c include_HEADERS += ../../sparc/shared/include/i2cmst.h libbsp_a_SOURCES += ../../sparc/shared/i2c/i2cmst.c +# SPI +include_HEADERS += ../../sparc/shared/include/spictrl.h +libbsp_a_SOURCES += ../../sparc/shared/spi/spictrl.c + +# TIME +include_HEADERS += ../../sparc/shared/include/spwcuc.h +include_HEADERS += ../../sparc/shared/include/grctm.h +libbsp_a_SOURCES += ../../sparc/shared/time/spwcuc.c +libbsp_a_SOURCES += ../../sparc/shared/time/grctm.c + +# GPIO +include_HEADERS += ../../sparc/shared/include/grgpio.h +include_HEADERS += ../../sparc/shared/include/gpiolib.h +libbsp_a_SOURCES += ../../sparc/shared/gpio/grgpio.c +libbsp_a_SOURCES += ../../sparc/shared/gpio/gpiolib.c + +# PWM +include_HEADERS += ../../sparc/shared/include/grpwm.h +libbsp_a_SOURCES += ../../sparc/shared/pwm/grpwm.c + +# ADC and DAC +include_HEADERS += ../../sparc/shared/include/gradcdac.h +libbsp_a_SOURCES += ../../sparc/shared/analog/gradcdac.c + +# Memory controllers +libbsp_a_SOURCES += ../../sparc/shared/mem/mctrl.c + # timer libbsp_a_SOURCES += timer/timer.c +# TM/TC +include_HEADERS += ../../sparc/shared/include/grtc.h +include_HEADERS += ../../sparc/shared/include/grtm.h +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtc.c +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm.c + # Driver Manager include_drvmgrdir = $(includedir)/drvmgr include_drvmgr_HEADERS = ../../sparc/shared/include/drvmgr/ambapp_bus.h @@ -166,6 +218,14 @@ leon_open_eth_rel_CPPFLAGS += -D__INSIDE_RTEMS_BSD_TCPIP_STACK__ leon_open_eth_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) endif +if HAS_NETWORKING +noinst_PROGRAMS += leon_greth.rel +leon_greth_rel_SOURCES = ../../sparc/shared/net/greth.c +include_HEADERS += ../../sparc/shared/include/greth.h +leon_greth_rel_CPPFLAGS = $(AM_CPPFLAGS) +leon_greth_rel_CPPFLAGS += -D__INSIDE_RTEMS_BSD_TCPIP_STACK__ +leon_greth_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) + # BSP Network configuration include_HEADERS += ../../sparc/shared/include/network_interface_add.h libbsp_a_SOURCES += ../../sparc/shared/net/network_interface_add.c @@ -178,6 +238,7 @@ libbsp_a_LIBADD = \ ../../../libcpu/@RTEMS_CPU@/syscall.rel if HAS_NETWORKING +libbsp_a_LIBADD += leon_greth.rel libbsp_a_LIBADD += leon_open_eth.rel libbsp_a_LIBADD += leon_smc91111.rel endif diff --git a/c/src/lib/libbsp/sparc/leon2/include/bsp.h b/c/src/lib/libbsp/sparc/leon2/include/bsp.h index d669d8910e..29e316947c 100644 --- a/c/src/lib/libbsp/sparc/leon2/include/bsp.h +++ b/c/src/lib/libbsp/sparc/leon2/include/bsp.h @@ -227,6 +227,7 @@ int cchip1_register(void); */ #define AMBAPPBUS_INFO_AVAIL /* AMBAPP Bus driver */ #define GPTIMER_INFO_AVAIL /* GPTIMER Timer driver */ +#define GRETH_INFO_AVAIL /* GRETH Ethernet driver */ #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/leon2/preinstall.am b/c/src/lib/libbsp/sparc/leon2/preinstall.am index 11dae63b6f..cfcd389033 100644 --- a/c/src/lib/libbsp/sparc/leon2/preinstall.am +++ b/c/src/lib/libbsp/sparc/leon2/preinstall.am @@ -81,6 +81,10 @@ $(PROJECT_INCLUDE)/bsp/gnatcommon.h: ../shared/include/gnatcommon.h $(PROJECT_IN $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gnatcommon.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gnatcommon.h +$(PROJECT_INCLUDE)/genirq.h: ../../sparc/shared/include/genirq.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/genirq.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/genirq.h + $(PROJECT_INCLUDE)/bsp/irq-generic.h: ../../shared/include/irq-generic.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq-generic.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq-generic.h @@ -105,6 +109,10 @@ $(PROJECT_INCLUDE)/grlib.h: ../../sparc/shared/include/grlib.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grlib.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grlib.h +$(PROJECT_INCLUDE)/ahbstat.h: ../../sparc/shared/include/ahbstat.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/ahbstat.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/ahbstat.h + $(PROJECT_INCLUDE)/tlib.h: ../../sparc/shared/include/tlib.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tlib.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/tlib.h @@ -133,6 +141,30 @@ $(PROJECT_INCLUDE)/b1553brm.h: ../../sparc/shared/include/b1553brm.h $(PROJECT_I $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553brm.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553brm.h +$(PROJECT_INCLUDE)/b1553rt.h: ../../sparc/shared/include/b1553rt.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553rt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553rt.h + +$(PROJECT_INCLUDE)/gr1553b.h: ../../sparc/shared/include/gr1553b.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553b.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553b.h + +$(PROJECT_INCLUDE)/gr1553bc.h: ../../sparc/shared/include/gr1553bc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553bc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553bc.h + +$(PROJECT_INCLUDE)/gr1553bc_list.h: ../../sparc/shared/include/gr1553bc_list.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553bc_list.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553bc_list.h + +$(PROJECT_INCLUDE)/gr1553bm.h: ../../sparc/shared/include/gr1553bm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553bm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553bm.h + +$(PROJECT_INCLUDE)/gr1553rt.h: ../../sparc/shared/include/gr1553rt.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553rt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553rt.h + $(PROJECT_INCLUDE)/occan.h: ../../sparc/shared/include/occan.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/occan.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/occan.h @@ -145,6 +177,10 @@ $(PROJECT_INCLUDE)/grspw.h: ../../sparc/shared/include/grspw.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw.h +$(PROJECT_INCLUDE)/grspw_router.h: ../../sparc/shared/include/grspw_router.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_router.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_router.h + $(PROJECT_INCLUDE)/apbuart.h: ../../sparc/shared/include/apbuart.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart.h @@ -153,6 +189,42 @@ $(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLU $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i2cmst.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/i2cmst.h +$(PROJECT_INCLUDE)/spictrl.h: ../../sparc/shared/include/spictrl.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/spictrl.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/spictrl.h + +$(PROJECT_INCLUDE)/spwcuc.h: ../../sparc/shared/include/spwcuc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/spwcuc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/spwcuc.h + +$(PROJECT_INCLUDE)/grctm.h: ../../sparc/shared/include/grctm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grctm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grctm.h + +$(PROJECT_INCLUDE)/grgpio.h: ../../sparc/shared/include/grgpio.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grgpio.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grgpio.h + +$(PROJECT_INCLUDE)/gpiolib.h: ../../sparc/shared/include/gpiolib.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gpiolib.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gpiolib.h + +$(PROJECT_INCLUDE)/grpwm.h: ../../sparc/shared/include/grpwm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grpwm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grpwm.h + +$(PROJECT_INCLUDE)/gradcdac.h: ../../sparc/shared/include/gradcdac.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gradcdac.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gradcdac.h + +$(PROJECT_INCLUDE)/grtc.h: ../../sparc/shared/include/grtc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grtc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grtc.h + +$(PROJECT_INCLUDE)/grtm.h: ../../sparc/shared/include/grtm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grtm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grtm.h + $(PROJECT_INCLUDE)/drvmgr/$(dirstamp): @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) @@ -166,6 +238,11 @@ $(PROJECT_INCLUDE)/drvmgr/leon2_amba_bus.h: ../../sparc/shared/include/drvmgr/le $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/leon2_amba_bus.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/leon2_amba_bus.h +if HAS_NETWORKING +$(PROJECT_INCLUDE)/greth.h: ../../sparc/shared/include/greth.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/greth.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/greth.h + $(PROJECT_INCLUDE)/network_interface_add.h: ../../sparc/shared/include/network_interface_add.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/network_interface_add.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/network_interface_add.h diff --git a/c/src/lib/libbsp/sparc/leon3/Makefile.am b/c/src/lib/libbsp/sparc/leon3/Makefile.am index dae3ecc22d..4517ee3914 100644 --- a/c/src/lib/libbsp/sparc/leon3/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon3/Makefile.am @@ -55,6 +55,7 @@ include_HEADERS += include/amba.h include_HEADERS += ../../sparc/shared/include/ambapp.h include_HEADERS += ../../sparc/shared/include/ambapp_ids.h include_HEADERS += ../../sparc/shared/include/grlib.h +include_HEADERS += ../../sparc/shared/include/ahbstat.h libbsp_a_SOURCES += amba/amba.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_alloc.c @@ -66,6 +67,7 @@ libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_parent.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_old.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_names.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_show.c +libbsp_a_SOURCES += ../../sparc/shared/amba/ahbstat.c # Clock Driver and Timer Library include_HEADERS += ../../sparc/shared/include/tlib.h @@ -86,12 +88,14 @@ include_HEADERS += ../../sparc/shared/include/cons.h libbsp_a_SOURCES += console/printk_support.c # IRQ +include_HEADERS += ../../sparc/shared/include/genirq.h include_bsp_HEADERS += \ ../../shared/include/irq-generic.h \ ../../shared/include/irq-info.h \ include/bsp/irq.h libbsp_a_SOURCES += \ startup/eirq.c \ + ../../sparc/shared/irq/genirq.c \ ../../sparc/shared/irq/irq-shared.c \ ../../sparc/shared/irq/bsp_isr_handler.c \ ../../shared/src/irq-default-handler.c \ @@ -124,7 +128,20 @@ libbsp_a_SOURCES += ../../sparc/shared/pci/gr_tmtc_1553.c # B1553BRM include_HEADERS += ../../sparc/shared/include/b1553brm.h +include_HEADERS += ../../sparc/shared/include/b1553rt.h libbsp_a_SOURCES += ../../sparc/shared/1553/b1553brm.c +libbsp_a_SOURCES += ../../sparc/shared/1553/b1553rt.c + +# GR1553B +include_HEADERS += ../../sparc/shared/include/gr1553b.h +include_HEADERS += ../../sparc/shared/include/gr1553bc.h +include_HEADERS += ../../sparc/shared/include/gr1553bc_list.h +include_HEADERS += ../../sparc/shared/include/gr1553bm.h +include_HEADERS += ../../sparc/shared/include/gr1553rt.h +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553b.c +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553bc.c +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553bm.c +libbsp_a_SOURCES += ../../sparc/shared/1553/gr1553rt.c # CAN include_HEADERS += ../../sparc/shared/include/occan.h \ @@ -134,7 +151,9 @@ libbsp_a_SOURCES += ../../sparc/shared/can/occan.c \ # SpaceWire include_HEADERS += ../../sparc/shared/include/grspw.h +include_HEADERS += ../../sparc/shared/include/grspw_router.h libbsp_a_SOURCES += ../../sparc/shared/spw/grspw.c +libbsp_a_SOURCES += ../../sparc/shared/spw/grspw_router.c # UART include_HEADERS += ../../sparc/shared/include/apbuart.h \ @@ -146,6 +165,33 @@ libbsp_a_SOURCES += ../../sparc/shared/uart/apbuart.c \ include_HEADERS += ../../sparc/shared/include/i2cmst.h libbsp_a_SOURCES += ../../sparc/shared/i2c/i2cmst.c +# SPI +include_HEADERS += ../../sparc/shared/include/spictrl.h +libbsp_a_SOURCES += ../../sparc/shared/spi/spictrl.c + +# TIME +include_HEADERS += ../../sparc/shared/include/spwcuc.h +include_HEADERS += ../../sparc/shared/include/grctm.h +libbsp_a_SOURCES += ../../sparc/shared/time/spwcuc.c +libbsp_a_SOURCES += ../../sparc/shared/time/grctm.c + +# GPIO +include_HEADERS += ../../sparc/shared/include/grgpio.h +include_HEADERS += ../../sparc/shared/include/gpiolib.h +libbsp_a_SOURCES += ../../sparc/shared/gpio/grgpio.c +libbsp_a_SOURCES += ../../sparc/shared/gpio/gpiolib.c + +# PWM +include_HEADERS += ../../sparc/shared/include/grpwm.h +libbsp_a_SOURCES += ../../sparc/shared/pwm/grpwm.c + +# ADC and DAC +include_HEADERS += ../../sparc/shared/include/gradcdac.h +libbsp_a_SOURCES += ../../sparc/shared/analog/gradcdac.c + +# Memory controllers +libbsp_a_SOURCES += ../../sparc/shared/mem/mctrl.c + # timer libbsp_a_SOURCES += timer/timer.c libbsp_a_SOURCES += timer/watchdog.c @@ -156,6 +202,22 @@ libbsp_a_SOURCES += ../../../libcpu/shared/src/cache_manager.c libbsp_a_SOURCES += include/cache_.h libbsp_a_CPPFLAGS = -I$(srcdir)/include +# GR712 +include_HEADERS += ../../sparc/shared/include/grascs.h +include_HEADERS += ../../sparc/shared/include/satcan.h +include_HEADERS += ../../sparc/shared/include/canmux.h +include_HEADERS += ../../sparc/shared/include/grslink.h +libbsp_a_SOURCES += ../../sparc/shared/ascs/grascs.c +libbsp_a_SOURCES += ../../sparc/shared/can/satcan.c +libbsp_a_SOURCES += ../../sparc/shared/can/canmux.c +libbsp_a_SOURCES += ../../sparc/shared/slink/grslink.c + +# TM/TC +include_HEADERS += ../../sparc/shared/include/grtc.h +include_HEADERS += ../../sparc/shared/include/grtm.h +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtc.c +libbsp_a_SOURCES += ../../sparc/shared/tmtc/grtm.c + # Driver Manager include_drvmgrdir = $(includedir)/drvmgr include_drvmgr_HEADERS = ../../sparc/shared/include/drvmgr/ambapp_bus_grlib.h @@ -185,7 +247,9 @@ endif if HAS_NETWORKING noinst_PROGRAMS += leon_greth.rel -leon_greth_rel_SOURCES = leon_greth/leon_greth.c +libbsp_a_SOURCES += leon_greth/leon_greth.c +leon_greth_rel_SOURCES = ../../sparc/shared/net/greth.c +include_HEADERS += ../../sparc/shared/include/greth.h leon_greth_rel_CPPFLAGS = $(AM_CPPFLAGS) leon_greth_rel_CPPFLAGS += -D__INSIDE_RTEMS_BSD_TCPIP_STACK__ leon_greth_rel_LDFLAGS = $(RTEMS_RELLDFLAGS) diff --git a/c/src/lib/libbsp/sparc/leon3/include/bsp.h b/c/src/lib/libbsp/sparc/leon3/include/bsp.h index d302418774..0be33a9110 100644 --- a/c/src/lib/libbsp/sparc/leon3/include/bsp.h +++ b/c/src/lib/libbsp/sparc/leon3/include/bsp.h @@ -257,6 +257,7 @@ extern const unsigned char LEON3_irq_to_cpu[32]; #define AMBAPPBUS_INFO_AVAIL /* AMBAPP Bus driver */ #define APBUART_INFO_AVAIL /* APBUART Console driver */ #define GPTIMER_INFO_AVAIL /* GPTIMER Timer driver */ +#define GRETH_INFO_AVAIL /* GRETH Ethernet driver */ #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/leon3/preinstall.am b/c/src/lib/libbsp/sparc/leon3/preinstall.am index 41f7d6de8c..29fd095187 100644 --- a/c/src/lib/libbsp/sparc/leon3/preinstall.am +++ b/c/src/lib/libbsp/sparc/leon3/preinstall.am @@ -105,6 +105,10 @@ $(PROJECT_INCLUDE)/grlib.h: ../../sparc/shared/include/grlib.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grlib.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grlib.h +$(PROJECT_INCLUDE)/ahbstat.h: ../../sparc/shared/include/ahbstat.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/ahbstat.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/ahbstat.h + $(PROJECT_INCLUDE)/tlib.h: ../../sparc/shared/include/tlib.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tlib.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/tlib.h @@ -113,6 +117,10 @@ $(PROJECT_INCLUDE)/cons.h: ../../sparc/shared/include/cons.h $(PROJECT_INCLUDE)/ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/cons.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/cons.h +$(PROJECT_INCLUDE)/genirq.h: ../../sparc/shared/include/genirq.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/genirq.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/genirq.h + $(PROJECT_INCLUDE)/bsp/irq-generic.h: ../../shared/include/irq-generic.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq-generic.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq-generic.h @@ -153,6 +161,30 @@ $(PROJECT_INCLUDE)/b1553brm.h: ../../sparc/shared/include/b1553brm.h $(PROJECT_I $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553brm.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553brm.h +$(PROJECT_INCLUDE)/b1553rt.h: ../../sparc/shared/include/b1553rt.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/b1553rt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/b1553rt.h + +$(PROJECT_INCLUDE)/gr1553b.h: ../../sparc/shared/include/gr1553b.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553b.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553b.h + +$(PROJECT_INCLUDE)/gr1553bc.h: ../../sparc/shared/include/gr1553bc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553bc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553bc.h + +$(PROJECT_INCLUDE)/gr1553bc_list.h: ../../sparc/shared/include/gr1553bc_list.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553bc_list.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553bc_list.h + +$(PROJECT_INCLUDE)/gr1553bm.h: ../../sparc/shared/include/gr1553bm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553bm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553bm.h + +$(PROJECT_INCLUDE)/gr1553rt.h: ../../sparc/shared/include/gr1553rt.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gr1553rt.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gr1553rt.h + $(PROJECT_INCLUDE)/occan.h: ../../sparc/shared/include/occan.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/occan.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/occan.h @@ -165,6 +197,10 @@ $(PROJECT_INCLUDE)/grspw.h: ../../sparc/shared/include/grspw.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw.h +$(PROJECT_INCLUDE)/grspw_router.h: ../../sparc/shared/include/grspw_router.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grspw_router.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grspw_router.h + $(PROJECT_INCLUDE)/apbuart.h: ../../sparc/shared/include/apbuart.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/apbuart.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/apbuart.h @@ -177,10 +213,62 @@ $(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLU $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i2cmst.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/i2cmst.h +$(PROJECT_INCLUDE)/spictrl.h: ../../sparc/shared/include/spictrl.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/spictrl.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/spictrl.h + +$(PROJECT_INCLUDE)/spwcuc.h: ../../sparc/shared/include/spwcuc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/spwcuc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/spwcuc.h + +$(PROJECT_INCLUDE)/grctm.h: ../../sparc/shared/include/grctm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grctm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grctm.h + +$(PROJECT_INCLUDE)/grgpio.h: ../../sparc/shared/include/grgpio.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grgpio.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grgpio.h + +$(PROJECT_INCLUDE)/gpiolib.h: ../../sparc/shared/include/gpiolib.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gpiolib.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gpiolib.h + +$(PROJECT_INCLUDE)/grpwm.h: ../../sparc/shared/include/grpwm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grpwm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grpwm.h + +$(PROJECT_INCLUDE)/gradcdac.h: ../../sparc/shared/include/gradcdac.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/gradcdac.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/gradcdac.h + $(PROJECT_INCLUDE)/watchdog.h: include/watchdog.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/watchdog.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/watchdog.h +$(PROJECT_INCLUDE)/grascs.h: ../../sparc/shared/include/grascs.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grascs.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grascs.h + +$(PROJECT_INCLUDE)/satcan.h: ../../sparc/shared/include/satcan.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/satcan.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/satcan.h + +$(PROJECT_INCLUDE)/canmux.h: ../../sparc/shared/include/canmux.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/canmux.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/canmux.h + +$(PROJECT_INCLUDE)/grslink.h: ../../sparc/shared/include/grslink.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grslink.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grslink.h + +$(PROJECT_INCLUDE)/grtc.h: ../../sparc/shared/include/grtc.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grtc.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grtc.h + +$(PROJECT_INCLUDE)/grtm.h: ../../sparc/shared/include/grtm.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grtm.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/grtm.h + $(PROJECT_INCLUDE)/drvmgr/$(dirstamp): @$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr @: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp) @@ -195,6 +283,11 @@ $(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h: ../../sparc/shared/include/drvmgr/ambapp PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/ambapp_bus.h if HAS_NETWORKING +$(PROJECT_INCLUDE)/greth.h: ../../sparc/shared/include/greth.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/greth.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/greth.h +endif +if HAS_NETWORKING $(PROJECT_INCLUDE)/network_interface_add.h: ../../sparc/shared/include/network_interface_add.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/network_interface_add.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/network_interface_add.h diff --git a/c/src/lib/libbsp/sparc/shared/1553/b1553rt.c b/c/src/lib/libbsp/sparc/shared/1553/b1553rt.c new file mode 100644 index 0000000000..3dfb40309c --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/b1553rt.c @@ -0,0 +1,859 @@ +/* + * B1553RT driver implmenetation + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <rtems/bspIo.h> + +#include <drvmgr/drvmgr.h> +#include <b1553rt.h> +#include <ambapp.h> +#include <drvmgr/ambapp_bus.h> + +/* Uncomment for debug output */ +/*#define DEBUG 1*/ + +/* + #define FUNCDEBUG 1*/ +/*#undef DEBUG*/ +#undef FUNCDEBUG + +/* EVENT_QUEUE_SIZE sets the size of the event queue + */ +#define EVENT_QUEUE_SIZE 1024 + + +#define INDEX(x) ( x&(EVENT_QUEUE_SIZE-1) ) + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#ifdef FUNCDEBUG +#define FUNCDBG(x...) printk(x) +#else +#define FUNCDBG(x...) +#endif + +#define READ_DMA(address) _READ16((unsigned int)address) + +static __inline__ unsigned short _READ16(unsigned int addr) { + unsigned short tmp; + asm(" lduha [%1]1, %0 " + : "=r"(tmp) + : "r"(addr) + ); + return tmp; +} + +static rtems_device_driver rt_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define RT_DRIVER_TABLE_ENTRY { rt_initialize, rt_open, rt_close, rt_read, rt_write, rt_control } + +static rtems_driver_address_table b1553rt_driver = RT_DRIVER_TABLE_ENTRY; + +typedef struct { + + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + + struct rt_reg *regs; + unsigned int ctrl_copy; /* Local copy of config register */ + + unsigned int cfg_freq; + + unsigned int memarea_base; + unsigned int memarea_base_remote; + + volatile unsigned short *mem; + + /* Received events waiting to be read */ + struct rt_msg *rt_event; + unsigned int head, tail; + + int rx_blocking; + + rtems_id rx_sem, tx_sem, dev_sem; + int minor; + int irqno; + +#ifdef DEBUG + unsigned int log[EVENT_QUEUE_SIZE*4]; + unsigned int log_i; +#endif + + unsigned int status; + rtems_id event_id; /* event that may be signalled upon errors, needs to be set through ioctl command RT_SET_EVENTID */ + +} rt_priv; + +static void b1553rt_interrupt(void *arg); +static rtems_device_driver rt_init(rt_priv *rt); + +#define OFS(ofs) (((unsigned int)&ofs & 0x1ffff)>>1) + +static int b1553rt_driver_io_registered = 0; +static rtems_device_major_number b1553rt_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +int b1553rt_register_io(rtems_device_major_number *m); +int b1553rt_device_init(rt_priv *pDev); + +int b1553rt_init2(struct drvmgr_dev *dev); +int b1553rt_init3(struct drvmgr_dev *dev); +int b1553rt_remove(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops b1553rt_ops = +{ + .init = {NULL, b1553rt_init2, b1553rt_init3, NULL}, + .remove = b1553rt_remove, + .info = NULL +}; + +struct amba_dev_id b1553rt_ids[] = +{ + {VENDOR_GAISLER, GAISLER_B1553RT}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info b1553rt_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_B1553RT_ID, /* Driver ID */ + "B1553RT_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &b1553rt_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + + }, + &b1553rt_ids[0] +}; + +void b1553rt_register_drv (void) +{ + DBG("Registering B1553RT driver\n"); + drvmgr_drv_register(&b1553rt_drv_info.general); +} + +int b1553rt_init2(struct drvmgr_dev *dev) +{ + rt_priv *priv; + + DBG("B1553RT[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(rt_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + +int b1553rt_init3(struct drvmgr_dev *dev) +{ + rt_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( b1553rt_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( b1553rt_register_io(&b1553rt_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + b1553rt_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + if ( b1553rt_device_init(priv) ) { + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/b1553rt%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sb1553rt%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, b1553rt_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +int b1553rt_remove(struct drvmgr_dev *dev) +{ + /* Stop more tasks to open driver */ + + /* Throw out all tasks using this driver */ + + /* Unregister I/O node */ + + /* Unregister and disable Interrupt */ + + /* Free device memory */ + + /* Return sucessfully */ + + return DRVMGR_FAIL; +} + +/******************* Driver Implementation ***********************/ + +int b1553rt_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &b1553rt_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("B1553RT driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("B1553RT rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("B1553RT rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("B1553RT rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("B1553RT rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +int b1553rt_device_init(rt_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + unsigned int mem; + unsigned int sys_freq_hz; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)pDev->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + pDev->irqno = pnpinfo->irq; + pDev->regs = (struct rt_reg *)pnpinfo->apb_slv->start; + pDev->minor = pDev->dev->minor_drv; + +#ifdef DEBUG + pDev->log_i = 0; + memset(pDev->log,0,sizeof(pDev->log)); + printf("LOG: 0x%x\n", &pDev->log[0]); + printf("LOG_I: 0x%x\n", &pDev->log_i); +#endif + + /* Get memory configuration from bus resources */ + value = drvmgr_dev_key_get(pDev->dev, "dmaBaseAdr", KEY_TYPE_POINTER); + if (value) + mem = (unsigned int)value->ptr; + + if (value && (mem & 1)) { + /* Remote address, address as RT looks at it. */ + + /* Translate the base address into an address that the the CPU can understand */ + pDev->memarea_base = mem & ~1; + drvmgr_translate_check(pDev->dev, DMAMEM_TO_CPU, + (void *)pDev->memarea_base_remote, + (void **)&pDev->memarea_base, + 4 * 1024); + } else { + if (!value) { + /* Use dynamically allocated memory, + * 4k DMA memory + 4k for alignment + */ + mem = (char *)malloc(4 * 1024 * 2); + if ( !mem ){ + printk("RT: Failed to allocate HW memory\n\r"); + return -1; + } + /* align memory to 4k boundary */ + pDev->memarea_base = (mem + 0xfff) & ~0xfff; + } else { + pDev->memarea_base = mem; + } + + /* Translate the base address into an address that the RT core can understand */ + drvmgr_translate_check(pDev->dev, CPUMEM_TO_DMA, + (void *)pDev->memarea_base, + (void **)&pDev->memarea_base_remote, + 4 * 1024); + } + + /* clear the used memory */ + memset((char *)pDev->memarea_base, 0, 4 * 1024); + + /* Set base address of all descriptors */ + pDev->memarea_base = (unsigned int)mem; + pDev->mem = (volatile unsigned short *)pDev->memarea_base; + + pDev->rt_event = NULL; + + /* The RT is always clocked at the same frequency as the bus + * If the frequency doesnt match it is defaulted to 24MHz, + * user can always override it. + */ + pDev->cfg_freq = RT_FREQ_24MHZ; + + /* Get frequency in Hz */ + if ( drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &sys_freq_hz) == 0 ) { + if ( sys_freq_hz == 20000000 ) { + pDev->cfg_freq = RT_FREQ_20MHZ; + } else if ( sys_freq_hz == 16000000 ) { + pDev->cfg_freq = RT_FREQ_16MHZ; + } else if ( sys_freq_hz == 12000000 ) { + pDev->cfg_freq = RT_FREQ_12MHZ; + } + } + + value = drvmgr_dev_key_get(pDev->dev, "coreFreq", KEY_TYPE_INT); + if ( value ) { + pDev->cfg_freq = value->i & RT_FREQ_MASK; + } + + /* RX Semaphore created with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('R', 'T', '0', '0' + pDev->minor), + 0, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->rx_sem) != RTEMS_SUCCESSFUL ) { + printk("RT: Failed to create rx semaphore\n"); + return RTEMS_INTERNAL_ERROR; + } + + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('R', 'T', '0', '0' + pDev->minor), + 1, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->dev_sem) != RTEMS_SUCCESSFUL ){ + printk("RT: Failed to create device semaphore\n"); + return RTEMS_INTERNAL_ERROR; + } + + /* Default to RT-mode */ + rt_init(pDev); + + return 0; +} + +static int odd_parity(unsigned int data) +{ + unsigned int i=0; + + while(data) + { + i++; + data &= (data - 1); + } + + return !(i&1); +} + +static void start_operation(rt_priv *rt) +{ + +} + +static void stop_operation(rt_priv *rt) +{ + +} + +static void set_extmdata_en(rt_priv *rt, int extmdata) +{ + if ( extmdata ) + extmdata = 1; + rt->ctrl_copy = (rt->ctrl_copy & ~(1<<16)) | (extmdata<<16); + rt->regs->ctrl = rt->ctrl_copy; +} + +static void set_vector_word(rt_priv *rt, unsigned short vword) +{ + rt->regs->vword = vword; +} + +/* Set clock speed */ +static void set_clkspd(rt_priv *rt, int spd) +{ + rt->ctrl_copy = (rt->ctrl_copy & ~0xC0) | (spd<<6); + rt->regs->ctrl = rt->ctrl_copy; + asm volatile("nop"::); + rt->regs->ctrl = rt->ctrl_copy | (1<<20); +} + +static void set_rtaddr(rt_priv *rt, int addr) +{ + rt->ctrl_copy = (rt->ctrl_copy & ~0x3F00) | (addr << 8) | (odd_parity(addr)<<13); + rt->regs->ctrl = rt->ctrl_copy; +} + +static void set_broadcast_en(rt_priv *rt, int data) +{ + rt->ctrl_copy = (rt->ctrl_copy & ~0x40000) | (data<<18); + rt->regs->ctrl = rt->ctrl_copy; +} + +static rtems_device_driver rt_init(rt_priv *rt) +{ + rt->rx_blocking = 1; + + if ( rt->rt_event ) + free(rt->rt_event); + rt->rt_event = NULL; + + rt->rt_event = (struct rt_msg *) malloc(EVENT_QUEUE_SIZE*sizeof(struct rt_msg)); + + if (rt->rt_event == NULL) { + DBG("RT driver failed to allocated memory."); + return RTEMS_NO_MEMORY; + } + + rt->ctrl_copy = rt->regs->ctrl & 0x3F00; /* Keep rtaddr and rtaddrp */ + rt->ctrl_copy |= 0x3C0D0; /* broadcast disabled, extmdata=1, writetsw = writecmd = 1 */ + rt->regs->ctrl = rt->ctrl_copy; + + /* Set Clock speed */ + set_clkspd(rt, rt->cfg_freq); + + rt->regs->addr = rt->memarea_base_remote; + rt->regs->ipm = 0x70000; /* Enable RT RX, MEM Failure and AHB Error interrupts */ + + DBG("B1553RT DMA_AREA: 0x%x\n", (unsigned int)rt->mem); + + return RTEMS_SUCCESSFUL; +} + + +static rtems_device_driver rt_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver rt_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { + rt_priv *rt; + struct drvmgr_dev *dev; + + FUNCDBG("rt_open\n"); + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + if (rtems_semaphore_obtain(rt->dev_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL) { + DBG("rt_open: resource in use\n"); + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + + /* Set defaults */ + rt->event_id = 0; + + start_operation(rt); + + /* Register interrupt routine */ + if (drvmgr_interrupt_register(rt->dev, 0, "b1553rt", b1553rt_interrupt, rt)) { + rtems_semaphore_release(rt->dev_sem); + return -1; + } + + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver rt_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rt_priv *rt; + struct drvmgr_dev *dev; + + FUNCDBG("rt_close"); + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + drvmgr_interrupt_unregister(rt->dev, 0, b1553rt_interrupt, rt); + + stop_operation(rt); + rtems_semaphore_release(rt->dev_sem); + + return RTEMS_SUCCESSFUL; +} + +static int get_messages(rt_priv *rt, void *buf, unsigned int msg_count) +{ + + struct rt_msg *dest = (struct rt_msg *) buf; + int count = 0; + + if (rt->head == rt->tail) { + return 0; + } + + do { + + DBG("rt read - head: %d, tail: %d\n", rt->head, rt->tail); + dest[count++] = rt->rt_event[INDEX(rt->tail++)]; + + } while (rt->head != rt->tail && count < msg_count); + + return count; + +} +static rtems_device_driver rt_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_rw_args_t *rw_args; + int count = 0; + rt_priv *rt; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + rw_args = (rtems_libio_rw_args_t *) arg; + + FUNCDBG("rt_read [%i,%i]: buf: 0x%x, len: %i\n",major, minor, (unsigned int)rw_args->buffer, rw_args->count); + + while ( (count = get_messages(rt,rw_args->buffer, rw_args->count)) == 0 ) { + + if (rt->rx_blocking) { + rtems_semaphore_obtain(rt->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + } else { + /* Translates to EBUSY */ + return RTEMS_RESOURCE_IN_USE; + } + } + + rw_args->bytes_moved = count; + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver rt_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_rw_args_t *rw_args; + struct rt_msg *source; + rt_priv *rt; + struct drvmgr_dev *dev; + unsigned int descriptor, suba, wc; + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + rw_args = (rtems_libio_rw_args_t *) arg; + + if ( rw_args->count != 1 ) { + return RTEMS_INVALID_NAME; + } + + source = (struct rt_msg *) rw_args->buffer; + + descriptor = source[0].desc & 0x7F; + suba = descriptor-32; + wc = source[0].miw >> 11; + wc = wc ? wc : 32; + + FUNCDBG("rt_write [%i,%i]: buf: 0x%x\n",major, minor, (unsigned int)rw_args->buffer); + + memcpy((void *)&rt->mem[0x400 + suba*32], &source[0].data[0], wc*2); + + rw_args->bytes_moved = 1; + + return RTEMS_SUCCESSFUL; + +} + +static rtems_device_driver rt_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; + unsigned int *data = ioarg->buffer; + + rt_priv *rt; + struct drvmgr_dev *dev; + + FUNCDBG("rt_control[%d]: [%i,%i]\n", minor, major, minor); + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + if (!ioarg) { + DBG("rt_control: invalid argument\n"); + return RTEMS_INVALID_NAME; + } + + ioarg->ioctl_return = 0; + switch (ioarg->command) { + + case RT_SET_ADDR: + set_rtaddr(rt, data[0]); + break; + + case RT_SET_BCE: + set_broadcast_en(rt, data[0]); + break; + + case RT_SET_VECTORW: + set_vector_word(rt, data[0]); + break; + + case RT_SET_EXTMDATA: + set_extmdata_en(rt, data[0]); + break; + + case RT_RX_BLOCK: + rt->rx_blocking = data[0]; + break; + + case RT_CLR_STATUS: + rt->status = 0; + break; + + case RT_GET_STATUS: /* copy status */ + if ( !ioarg->buffer ) + return RTEMS_INVALID_NAME; + + *(unsigned int *)ioarg->buffer = rt->status; + break; + + case RT_SET_EVENTID: + rt->event_id = (rtems_id)ioarg->buffer; + break; + + default: + return RTEMS_NOT_IMPLEMENTED; + } + + return RTEMS_SUCCESSFUL; +} + +static void b1553rt_interrupt(void *arg) +{ + rt_priv *rt = arg; + unsigned short descriptor; + int signal_event=0, wake_rx_task=0; + unsigned int event_status=0; + unsigned int wc, irqv, cmd, tsw, suba, tx, miw, i; + unsigned int ipend; + + #define SET_ERROR_DESCRIPTOR(descriptor) (event_status = (event_status & 0x0000ffff) | descriptor<<16) + ipend = rt->regs->ipm; + + if (ipend == 0) { + /* IRQ mask has been cleared, we must have been reset */ + /* Restore ctrl registers */ + rt->regs->ctrl = rt->ctrl_copy; + rt->regs->addr = rt->memarea_base_remote; + rt->regs->ipm = 0x70000; + /* Send reset mode code event */ + if (rt->head - rt->tail != EVENT_QUEUE_SIZE) { + miw = (8<<11); + descriptor = 64 + 32 + 8; + rt->rt_event[INDEX(rt->head)].miw = miw; + rt->rt_event[INDEX(rt->head)].time = 0; + rt->rt_event[INDEX(rt->head)].desc = descriptor; + rt->head++; + } + } + + if ( ipend & 0x1 ) { + /* RT IRQ */ + if (rt->head - rt->tail != EVENT_QUEUE_SIZE) { + + irqv = rt->regs->irq; + cmd = irqv >> 7; + wc = cmd & 0x1F; /* word count / mode code */ + suba = irqv & 0x1F; /* sub address (0-31) */ + tx = (irqv >> 5) & 1; + + /* read status word */ + tsw = READ_DMA(&rt->mem[tx*0x3E0+suba]); + + /* Build Message Information Word (B1553BRM-style) */ + miw = (wc<<11) | (tsw&RT_TSW_BUS)>>4 | !(tsw&RT_TSW_OK)>>7 | (tsw&RT_TSW_ILL)>>5 | + (tsw&RT_TSW_PAR)>>5 | (tsw&RT_TSW_MAN)>>7; + + descriptor = (tx << 5) | suba; + + /* Mode codes */ + if (suba == 0 || suba == 31) { + descriptor = 64 + (tx*32) + wc; + } + + /* Data received or transmitted */ + if (descriptor < 64) { + wc = wc ? wc : 32; /* wc = 0 means 32 words transmitted */ + } + /* RX Mode code */ + else if (descriptor < 96) { + wc = (wc>>4); + } + /* TX Mode code */ + else if (descriptor < 128) { + wc = (wc>>4); + } + + /* Copy to event queue */ + rt->rt_event[INDEX(rt->head)].miw = miw; + rt->rt_event[INDEX(rt->head)].time = 0; + + for (i = 0; i < wc; i++) { + rt->rt_event[INDEX(rt->head)].data[i] = READ_DMA(&rt->mem[tx*0x400 + suba*32 + i]); + } + rt->rt_event[INDEX(rt->head)].desc = descriptor; + rt->head++; + + + /* Handle errors */ + if ( tsw & RT_TSW_ILL){ + FUNCDBG("RT: RT_ILLCMD\n\r"); + rt->status |= RT_ILLCMD_IRQ; + event_status |= RT_ILLCMD_IRQ; + SET_ERROR_DESCRIPTOR(descriptor); + signal_event=1; + } + + if ( !(tsw & RT_TSW_OK) ) { + FUNCDBG("RT: RT_MERR_IRQ\n\r"); + rt->status |= RT_MERR_IRQ; + event_status |= RT_MERR_IRQ; + SET_ERROR_DESCRIPTOR(descriptor); + signal_event=1; + } + + } + else { + /* Indicate overrun */ + rt->rt_event[INDEX(rt->head)].desc |= 0x8000; + } + } + + if ( ipend & 0x2 ) { + /* Memory failure IRQ */ + FUNCDBG("B1553RT: Memory failure\n"); + event_status |= RT_DMAF_IRQ; + signal_event=1; + } + + if ( ipend & 0x4 ) { + /* AHB Error */ + FUNCDBG("B1553RT: AHB ERROR\n"); + event_status |= RT_DMAF_IRQ; + signal_event=1; + } + +#ifdef DEBUG + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = descriptor; + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = cmd; + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = miw; + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = tsw; +#endif + + wake_rx_task = 1; + + /* Wake any blocked rx thread only on receive interrupts */ + if ( wake_rx_task ) { + rtems_semaphore_release(rt->rx_sem); + } + + /* Copy current mask to status mask */ + if ( event_status ) { + if ( event_status & 0xffff0000 ) + rt->status &= 0x0000ffff; + rt->status |= event_status; + } + + /* signal event once */ + if ( signal_event && (rt->event_id != 0) ) { + rtems_event_send(rt->event_id, event_status); + } + +} + +void b1553rt_print_dev(struct drvmgr_dev *dev, int options) +{ + rt_priv *pDev = dev->priv; + struct amba_dev_info *devinfo; + + devinfo = (struct amba_dev_info *)pDev->dev->businfo; + + /* Print */ + printf("--- B1553RT[%d] %s ---\n", pDev->minor, pDev->devName); + printf(" REGS: 0x%x\n", (unsigned int)pDev->regs); + printf(" IRQ: %d\n", pDev->irqno); + +} + +void b1553rt_print(int options) +{ + struct amba_drv_info *drv = &b1553rt_drv_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + b1553rt_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553b.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553b.c new file mode 100644 index 0000000000..26d7b400c3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553b.c @@ -0,0 +1,305 @@ +/* GR1553B driver, used by BC, RT and/or BM driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <stdlib.h> +#include <drvmgr/ambapp_bus.h> + +#include <gr1553b.h> + +/* Driver Manager interface for BC, RT, BM, BRM, BC-BM and RT-BM */ + +#define GR1553B_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553B_READ_REG(adr) (*(volatile uint32_t *)(adr)) + +#define FEAT_BC 0x1 +#define FEAT_RT 0x2 +#define FEAT_BM 0x4 + +#define ALLOC_BC 0x1 +#define ALLOC_RT 0x2 +#define ALLOC_BM 0x4 + +struct gr1553_device { + struct drvmgr_dev *dev; + int features; + int alloc; +}; + +struct gr1553_device_feature { + struct gr1553_device_feature *next; + struct gr1553_device *dev; + int minor; +}; + +/* Device lists */ +struct gr1553_device_feature *gr1553_bm_root = NULL; +struct gr1553_device_feature *gr1553_rt_root = NULL; +struct gr1553_device_feature *gr1553_bc_root = NULL; + +/* Driver registered */ +int gr1553_driver_registerd = 0; + +/* Add 'feat' to linked list pointed to by 'root'. A minor is also assigned. */ +void gr1553_list_add + ( + struct gr1553_device_feature **root, + struct gr1553_device_feature *feat + ) +{ + int minor; + struct gr1553_device_feature *curr; + + if ( *root == NULL ) { + *root = feat; + feat->next = NULL; + feat->minor = 0; + return; + } + + minor = 0; +retry_new_minor: + curr = *root; + while ( curr->next ) { + if ( curr->minor == minor ) { + minor++; + goto retry_new_minor; + } + curr = curr->next; + } + + feat->next = NULL; + feat->minor = minor; + curr->next = feat; +} + +struct gr1553_device_feature *gr1553_list_find + ( + struct gr1553_device_feature *root, + int minor + ) +{ + struct gr1553_device_feature *curr = root; + while ( curr ) { + if ( curr->minor == minor ) { + return curr; + } + curr = curr->next; + } + return NULL; +} + +struct drvmgr_dev **gr1553_bc_open(int minor) +{ + struct gr1553_device_feature *feat; + + feat = gr1553_list_find(gr1553_bc_root, minor); + if ( feat == NULL ) + return NULL; + + /* Only possible to allocate is RT and BC is free, + * this is beacuse it is not possible to use the + * RT and the BC at the same time. + */ + if ( feat->dev->alloc & (ALLOC_BC|ALLOC_RT) ) + return NULL; + + /* Alloc BC device */ + feat->dev->alloc |= ALLOC_BC; + + return &feat->dev->dev; +} + +void gr1553_bc_close(struct drvmgr_dev **dev) +{ + struct gr1553_device *d = (struct gr1553_device *)dev; + + d->alloc &= ~ALLOC_BC; +} + +struct drvmgr_dev **gr1553_rt_open(int minor) +{ + struct gr1553_device_feature *feat; + + feat = gr1553_list_find(gr1553_rt_root, minor); + if ( feat == NULL ) + return NULL; + + /* Only possible to allocate is RT and BC is free, + * this is beacuse it is not possible to use the + * RT and the BC at the same time. + */ + if ( feat->dev->alloc & (ALLOC_BC|ALLOC_RT) ) + return NULL; + + /* Alloc RT device */ + feat->dev->alloc |= ALLOC_RT; + + return &feat->dev->dev; +} + +void gr1553_rt_close(struct drvmgr_dev **dev) +{ + struct gr1553_device *d = (struct gr1553_device *)dev; + + d->alloc &= ~ALLOC_RT; +} + +struct drvmgr_dev **gr1553_bm_open(int minor) +{ + struct gr1553_device_feature *feat; + + feat = gr1553_list_find(gr1553_bm_root, minor); + if ( feat == NULL ) + return NULL; + + /* Only possible to allocate is RT and BC is free, + * this is beacuse it is not possible to use the + * RT and the BC at the same time. + */ + if ( feat->dev->alloc & ALLOC_BM ) + return NULL; + + /* Alloc BM device */ + feat->dev->alloc |= ALLOC_BM; + + return &feat->dev->dev; +} + +void gr1553_bm_close(struct drvmgr_dev **dev) +{ + struct gr1553_device *d = (struct gr1553_device *)dev; + + d->alloc &= ~ALLOC_BM; +} + +int gr1553_init2(struct drvmgr_dev *dev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + struct gr1553b_regs *regs; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return DRVMGR_FAIL; + } + pnpinfo = &ambadev->info; + regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start; + + /* Stop IRQ */ + GR1553B_WRITE_REG(®s->imask, 0); + GR1553B_WRITE_REG(®s->irq, 0xffffffff); + /* Stop BC if not already stopped (just in case) */ + GR1553B_WRITE_REG(®s->bc_ctrl, 0x15520204); + /* Stop RT rx (just in case) */ + GR1553B_WRITE_REG(®s->rt_cfg, 0x15530000); + /* Stop BM logging (just in case) */ + GR1553B_WRITE_REG(®s->bm_ctrl, 0); + + return DRVMGR_OK; +} + +/* Register the different functionalities that the + * core supports. + */ +int gr1553_init3(struct drvmgr_dev *dev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + struct gr1553_device *priv; + struct gr1553_device_feature *feat; + struct gr1553b_regs *regs; + + priv = malloc(sizeof(struct gr1553_device)); + if ( priv == NULL ) + return DRVMGR_NOMEM; + priv->dev = dev; + priv->alloc = 0; + priv->features = 0; + dev->priv = NULL; /* Let higher level driver handle this */ + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return DRVMGR_FAIL; + } + pnpinfo = &ambadev->info; + regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start; + + if ( GR1553B_READ_REG(®s->bm_stat) & GR1553B_BM_STAT_BMSUP ) { + priv->features |= FEAT_BM; + feat = malloc(sizeof(struct gr1553_device_feature)); + feat->dev = priv; + /* Init Minor and Next */ + gr1553_list_add(&gr1553_bm_root, feat); + } + + if ( GR1553B_READ_REG(®s->bc_stat) & GR1553B_BC_STAT_BCSUP ) { + priv->features |= FEAT_BC; + feat = malloc(sizeof(struct gr1553_device_feature)); + feat->dev = priv; + /* Init Minor and Next */ + gr1553_list_add(&gr1553_bc_root, feat); + } + + if ( GR1553B_READ_REG(®s->rt_stat) & GR1553B_RT_STAT_RTSUP ) { + priv->features |= FEAT_RT; + feat = malloc(sizeof(struct gr1553_device_feature)); + feat->dev = priv; + /* Init Minor and Next */ + gr1553_list_add(&gr1553_rt_root, feat); + } + + return DRVMGR_OK; +} + +struct drvmgr_drv_ops gr1553_ops = +{ + {NULL, gr1553_init2, gr1553_init3, NULL}, + NULL, + NULL +}; + +struct amba_dev_id gr1553_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GR1553B}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info gr1553_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GR1553B_ID,/* Driver ID */ + "GR1553_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &gr1553_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gr1553_ids[0] +}; + +/* Multiple drivers may call this function. The drivers that depends on + * this driver: + * - BM driver + * - BC driver + * - RT driver + */ +void gr1553_register(void) +{ + if ( gr1553_driver_registerd == 0 ) { + gr1553_driver_registerd = 1; + drvmgr_drv_register(&gr1553_drv_info.general); + } +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c new file mode 100644 index 0000000000..4133200709 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c @@ -0,0 +1,1674 @@ +/* GR1553B BC driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#include <gr1553b.h> +#include <gr1553bc.h> + +#define GR1553BC_WRITE_MEM(adr, val) *(volatile uint32_t *)(adr) = (uint32_t)(val) +#define GR1553BC_READ_MEM(adr) (*(volatile uint32_t *)(adr)) + +#define GR1553BC_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (uint32_t)(val) +#define GR1553BC_READ_REG(adr) (*(volatile uint32_t *)(adr)) + +#ifndef IRQ_GLOBAL_PREPARE + #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level +#endif + +#ifndef IRQ_GLOBAL_DISABLE + #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level) +#endif + +#ifndef IRQ_GLOBAL_ENABLE + #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level) +#endif + +/* Needed by list for data pinter and BD translation */ +struct gr1553bc_priv { + struct drvmgr_dev **pdev; + struct gr1553b_regs *regs; + struct gr1553bc_list *list; + struct gr1553bc_list *alist; + int started; + + /* IRQ log management */ + void *irq_log_p; + uint32_t *irq_log_base; + uint32_t *irq_log_curr; + uint32_t *irq_log_end; + uint32_t *irq_log_base_hw; + + /* Standard IRQ handler function */ + bcirq_func_t irq_func; + void *irq_data; +}; + + +/*************** LIST HANDLING ROUTINES ***************/ + +/* This marks that the jump is a jump to next Minor. + * It is important that it sets one of the two LSB + * so that we can separate it from a JUMP-IRQ function, + * function pointers must be aligned to 4bytes. + * + * This marker is used to optimize the INDICATION process, + * from a descriptor pointer we can step to next Jump that + * has this MARKER set, then we know that the MID is stored + * there. + * + * The marker is limited to 1 byte. + */ +#define NEXT_MINOR_MARKER 0x01 + +/* To separate ASYNC list from SYNC list we mark them differently, but with + * LSB always set. This can be used to get the list the descriptor is a part + * of. + */ +#define NEXT_MINOR_MARKER_ASYNC 0x80 + +struct gr1553bc_list_cfg gr1553bc_def_cfg = +{ + .rt_timeout = + { + 20, 20, 20, 20, + 20, 20, 20, 20, + 20, 20, 20, 20, + 20, 20, 20, 20, + 20, 20, 20, 20, + 20, 20, 20, 20, + 20, 20, 20, 20, + 20, 20, 20 + }, + .bc_timeout = 30, + .tropt_irq_on_err = 0, + .tropt_pause_on_err = 0, + .async_list = 0, +}; + +int gr1553bc_list_alloc(struct gr1553bc_list **list, int max_major) +{ + int size; + struct gr1553bc_list *l; + + size = sizeof(struct gr1553bc_list) + max_major * sizeof(void *); + l = malloc(size); + if ( l == NULL ) + return -1; + memset(l, 0, size); + + l->major_cnt = max_major; + *list = l; + + /* Set default options: + * - RT timeout tolerance 20us + * - Global transfer options used when generating transfer descriptors + * - No BC device, note that this only works when no translation is + * required + */ + if ( gr1553bc_list_config(l, &gr1553bc_def_cfg, NULL) ) { + free(l); + return -1; + } + + return 0; +} + +void gr1553bc_list_free(struct gr1553bc_list *list) +{ + gr1553bc_list_table_free(list); + free(list); +} + +int gr1553bc_list_config + ( + struct gr1553bc_list *list, + struct gr1553bc_list_cfg *cfg, + void *bc + ) +{ + int timeout, i, tropts; + + /* RT Time Tolerances */ + for (i=0; i<31; i++) { + /* 0=14us, 1=18us ... 0xf=74us + * round upwards: 15us will be 18us + */ + timeout = ((cfg->rt_timeout[i] + 1) - 14) / 4; + if ( (timeout > 0xf) || (timeout < 0) ) + return -1; + list->rt_timeout[i] = timeout; + } + timeout = ((cfg->bc_timeout + 1) - 14) / 4; + if ( timeout > 0xf ) + return -1; + list->rt_timeout[i] = timeout; + + /* Transfer descriptor generation options */ + tropts = 0; + if ( cfg->tropt_irq_on_err ) + tropts |= 1<<28; + if ( cfg->tropt_pause_on_err ) + tropts |= 1<<26; + list->tropts = tropts; + + list->async_list = cfg->async_list; + list->bc = bc; + + return 0; +} + +void gr1553bc_list_link_major( + struct gr1553bc_major *major, + struct gr1553bc_major *next + ) +{ + if ( major ) { + major->next = next; + if ( next ) { + major->minors[major->cfg->minor_cnt-1]->next = + next->minors[0]; + } else { + major->minors[major->cfg->minor_cnt-1]->next = NULL; + } + } +} + +int gr1553bc_list_set_major( + struct gr1553bc_list *list, + struct gr1553bc_major *major, + int no) +{ + struct gr1553bc_major *prev, *next; + + if ( no >= list->major_cnt ) + return -1; + + list->majors[no] = major; + + /* Link previous Major frame with this one */ + if ( no > 0 ) { + prev = list->majors[no-1]; + } else { + /* First Major is linked with last major */ + prev = list->majors[list->major_cnt-1]; + } + + /* Link to next Major if not the last one and if there is + * a next major + */ + if ( no == list->major_cnt-1 ) { + /* The last major, assume that it is connected with the first */ + next = list->majors[0]; + } else { + next = list->majors[no+1]; + } + + /* Link previous frame to jump into this */ + gr1553bc_list_link_major(prev, major); + + /* Link This frame to jump into the next */ + gr1553bc_list_link_major(major, next); + + return 0; +} + +/* Translate Descriptor address from CPU-address to Hardware Address */ +static inline union gr1553bc_bd *gr1553bc_bd_cpu2hw + ( + struct gr1553bc_list *list, + union gr1553bc_bd *bd + ) +{ + return (union gr1553bc_bd *)(((unsigned int)bd - list->table_cpu) + + list->table_hw); +} + +/* Translate Descriptor address from HW-address to CPU Address */ +static inline union gr1553bc_bd *gr1553bc_bd_hw2cpu + ( + struct gr1553bc_list *list, + union gr1553bc_bd *bd + ) +{ + return (union gr1553bc_bd *)(((unsigned int)bd - list->table_hw) + + list->table_cpu); +} + +int gr1553bc_minor_table_size(struct gr1553bc_minor *minor) +{ + struct gr1553bc_minor_cfg *mincfg = minor->cfg; + int slot_cnt; + + /* SLOTS + JUMP */ + slot_cnt = mincfg->slot_cnt + 1; + if ( mincfg->timeslot ) { + /* time management requires 1 extra slot */ + slot_cnt++; + } + + return slot_cnt * GR1553BC_BD_SIZE; +} + +int gr1553bc_list_table_size(struct gr1553bc_list *list) +{ + struct gr1553bc_major *major; + int i, j, minor_cnt, size; + + size = 0; + for (i=0; i<list->major_cnt; i++) { + major = list->majors[i]; + minor_cnt = major->cfg->minor_cnt; + for (j=0; j<minor_cnt; j++) { + /* 128-bit Alignment required by HW */ + size += (GR1553BC_BD_ALIGN - + (size & (GR1553BC_BD_ALIGN-1))) & + ~(GR1553BC_BD_ALIGN-1); + + /* Size required by descriptors */ + size += gr1553bc_minor_table_size(major->minors[j]); + } + } + + return size; +} + +int gr1553bc_list_table_alloc + ( + struct gr1553bc_list *list, + void *bdtab_custom + ) +{ + struct gr1553bc_major *major; + int i, j, minor_cnt, size; + unsigned int table; + struct gr1553bc_priv *bcpriv = list->bc; + + /* Free previous allocated descriptor table */ + gr1553bc_list_table_free(list); + + /* Remember user's settings for uninitialization */ + list->_table_custom = bdtab_custom; + + /* Get Size required for descriptors */ + size = gr1553bc_list_table_size(list); + + if ((unsigned int)bdtab_custom & 0x1) { + /* Address given in Hardware accessible address, we + * convert it into CPU-accessible address. + */ + list->table_hw = (unsigned int)bdtab_custom & ~0x1; + list->_table = bdtab_custom; + drvmgr_translate_check( + *bcpriv->pdev, + DMAMEM_TO_CPU, + (void *)list->table_hw, + (void **)&list->table_cpu, + size); + } else { + if (bdtab_custom == NULL) { + /* Allocate descriptors */ + list->_table = malloc(size + (GR1553BC_BD_ALIGN-1)); + if ( list->_table == NULL ) + return -1; + } else { + /* Custom address, given in CPU-accessible address */ + list->_table = bdtab_custom; + } + /* 128-bit Alignment required by HW */ + list->table_cpu = + (((unsigned int)list->_table + (GR1553BC_BD_ALIGN-1)) & + ~(GR1553BC_BD_ALIGN-1)); + + /* We got CPU accessible descriptor table address, now we + * translate that into an address that the Hardware can + * understand + */ + if (bcpriv) { + drvmgr_translate_check( + *bcpriv->pdev, + CPUMEM_TO_DMA, + (void *)list->table_cpu, + (void **)&list->table_hw, + size + ); + } else { + list->table_hw = list->table_cpu; + } + } + + /* Write End-Of-List all over the descriptor table here, + * For debugging/safety? + */ + + /* Assign descriptors to all minor frames. The addresses is + * CPU-accessible addresses. + */ + table = list->table_cpu; + for (i=0; i<list->major_cnt; i++) { + major = list->majors[i]; + minor_cnt = major->cfg->minor_cnt; + for (j=0; j<minor_cnt; j++) { + /* 128-bit Alignment required by HW */ + table = (table + (GR1553BC_BD_ALIGN-1)) & + ~(GR1553BC_BD_ALIGN-1); + major->minors[j]->bds = (union gr1553bc_bd *)table; + + /* Calc size required by descriptors */ + table += gr1553bc_minor_table_size(major->minors[j]); + } + } + + return 0; +} + +void gr1553bc_list_table_free(struct gr1553bc_list *list) +{ + if ( (list->_table_custom == NULL) && list->_table ) { + free(list->_table); + } + list->_table = NULL; + list->_table_custom = NULL; + list->table_cpu = 0; + list->table_hw = 0; +} + +/* Init descriptor table provided by each minor frame, + * we link them together using unconditional JUMP. + */ +int gr1553bc_list_table_build(struct gr1553bc_list *list) +{ + struct gr1553bc_major *major; + struct gr1553bc_minor *minor; + struct gr1553bc_minor_cfg *mincfg; + int i, j, k, minor_cnt, marker; + union gr1553bc_bd *bds, *hwbd; + + marker = NEXT_MINOR_MARKER; + if ( list->async_list ) + marker |= NEXT_MINOR_MARKER_ASYNC; + + /* Create Major linking */ + for (i=0; i<list->major_cnt; i++) { + major = list->majors[i]; + minor_cnt = major->cfg->minor_cnt; + for (j=0; j<minor_cnt; j++) { + minor = major->minors[j]; + mincfg = minor->cfg; + bds = minor->bds; + + /* BD[0..SLOTCNT-1] = message slots + * BD[SLOTCNT+0] = END + * BD[SLOTCNT+1] = JUMP + * + * or if no optional time slot handling: + * + * BD[0..SLOTCNT-1] = message slots + * BD[SLOTCNT] = JUMP + */ + + /* BD[0..SLOTCNT-1] */ + for (k=0; k<mincfg->slot_cnt; k++) { + gr1553bc_bd_tr_init( + &bds[k].tr, + GR1553BC_TR_DUMMY_0, + GR1553BC_TR_DUMMY_1, + 0, + 0); + } + + /* BD[SLOTCNT] (OPTIONAL) + * If a minor frame is configured to be executed in + * certain time (given a time slot), this descriptor + * sums up all unused time. The time slot is + * decremented when messages are inserted into the + * minor frame and increased when messages are removed. + */ + if ( mincfg->timeslot > 0 ) { + gr1553bc_bd_tr_init( + &bds[k].tr, + GR1553BC_TR_DUMMY_0 | (mincfg->timeslot >> 2), + GR1553BC_TR_DUMMY_1, + 0, + 0); + k++; + } + + /* Last descriptor is a jump to next minor frame, to a + * synchronization point. If chain ends here, the list + * is marked with a "end-of-list" marker. + * + */ + if ( minor->next ) { + /* Translate CPU address of BD into HW address */ + hwbd = gr1553bc_bd_cpu2hw( + list, + &minor->next->bds[0] + ); + gr1553bc_bd_init( + &bds[k], + 0xf, + GR1553BC_UNCOND_JMP, + (uint32_t)hwbd, + ((GR1553BC_ID(i,j,k) << 8) | marker), + 0 + ); + } else { + gr1553bc_bd_init( + &bds[k], + 0xf, + GR1553BC_TR_EOL, + 0, + ((GR1553BC_ID(i,j,k) << 8) | marker), + 0); + } + } + } + + return 0; +} + +void gr1553bc_bd_init( + union gr1553bc_bd *bd, + unsigned int flags, + uint32_t word0, + uint32_t word1, + uint32_t word2, + uint32_t word3 + ) +{ + struct gr1553bc_bd_raw *raw = &bd->raw; + + if ( flags & 0x1 ) { + if ( (flags & KEEP_TIMESLOT) && + ((word0 & GR1553BC_BD_TYPE) == 0) ) { + /* Don't touch timeslot previously allocated */ + word0 &= ~GR1553BC_TR_TIME; + word0 |= GR1553BC_READ_MEM(&raw->words[0]) & + GR1553BC_TR_TIME; + } + GR1553BC_WRITE_MEM(&raw->words[0], word0); + } + if ( flags & 0x2 ) + GR1553BC_WRITE_MEM(&raw->words[1], word1); + if ( flags & 0x4 ) + GR1553BC_WRITE_MEM(&raw->words[2], word2); + if ( flags & 0x8 ) + GR1553BC_WRITE_MEM(&raw->words[3], word3); +} + +/* Alloc a Major frame according to the configuration structure */ +int gr1553bc_major_alloc_skel + ( + struct gr1553bc_major **major, + struct gr1553bc_major_cfg *cfg + ) +{ + struct gr1553bc_major *maj; + struct gr1553bc_minor *minor; + int size, i; + + if ( (cfg == NULL) || (major == NULL) || (cfg->minor_cnt <= 0) ) + return -1; + + /* Allocate Major Frame description, but no descriptors */ + size = sizeof(struct gr1553bc_major) + cfg->minor_cnt * + (sizeof(struct gr1553bc_minor) + sizeof(void *)); + maj = (struct gr1553bc_major *)malloc(size); + if ( maj == NULL ) + return -1; + + maj->cfg = cfg; + maj->next = NULL; + + /* Create links between minor frames, and from minor frames + * to configuration structure. + */ + minor = (struct gr1553bc_minor *)&maj->minors[cfg->minor_cnt]; + for (i=0; i<cfg->minor_cnt; i++, minor++) { + maj->minors[i] = minor; + minor->next = minor + 1; + minor->cfg = &cfg->minor_cfgs[i]; + minor->alloc = 0; + minor->bds = NULL; + } + /* last Minor should point to next Major frame's first minor, + * we do that somewhere else. + */ + (minor - 1)->next = NULL; + + *major = maj; + + return 0; +} + +struct gr1553bc_major *gr1553bc_major_from_id + ( + struct gr1553bc_list *list, + int mid + ) +{ + int major_no; + + /* Find Minor Frame from MID */ + major_no = GR1553BC_MAJID_FROM_ID(mid); + + if ( major_no >= list->major_cnt ) + return NULL; + return list->majors[major_no]; +} + +struct gr1553bc_minor *gr1553bc_minor_from_id + ( + struct gr1553bc_list *list, + int mid + ) +{ + int minor_no; + struct gr1553bc_major *major; + + /* Get Major from ID */ + major = gr1553bc_major_from_id(list, mid); + if ( major == NULL ) + return NULL; + + /* Find Minor Frame from MID */ + minor_no = GR1553BC_MINID_FROM_ID(mid); + + if ( minor_no >= major->cfg->minor_cnt ) + return NULL; + return major->minors[minor_no]; +} + +union gr1553bc_bd *gr1553bc_slot_bd + ( + struct gr1553bc_list *list, + int mid + ) +{ + struct gr1553bc_minor *minor; + int slot_no; + + /*** look up BD ***/ + + /* Get minor */ + minor = gr1553bc_minor_from_id(list, mid); + if ( minor == NULL ) + return NULL; + + /* Get Slot */ + slot_no = GR1553BC_SLOTID_FROM_ID(mid); + if ( slot_no >= 0xff ) + slot_no = 0; + + /* Get BD address */ + return &minor->bds[slot_no]; +} + +int gr1553bc_minor_first_avail(struct gr1553bc_minor *minor) +{ + int slot_num; + uint32_t alloc; + + alloc = minor->alloc; + if ( alloc == 0xffffffff ) { + /* No free */ + return -1; + } + slot_num = 0; + while ( alloc & 1 ) { + alloc = alloc >> 1; + slot_num++; + } + if ( slot_num >= minor->cfg->slot_cnt ) { + /* no free */ + return -1; + } + return slot_num; +} + +int gr1553bc_slot_alloc( + struct gr1553bc_list *list, + int *mid, + int timeslot, + union gr1553bc_bd **bd + ) +{ + struct gr1553bc_minor *minor = gr1553bc_minor_from_id(list, *mid); + + return gr1553bc_slot_alloc2(minor, mid, timeslot, bd); +} + +/* Same as gr1553bc_slot_alloc but identifies a minor instead of list. + * The major/minor part of MID is ignored. + */ +int gr1553bc_slot_alloc2( + struct gr1553bc_minor *minor, + int *mid, + int timeslot, + union gr1553bc_bd **bd + ) +{ + int slot_no; + uint32_t set0; + int timefree; + struct gr1553bc_bd_tr *trbd; + struct gr1553bc_minor_cfg *mincfg; + + if ( minor == NULL ) + return -1; + + mincfg = minor->cfg; + + /* Find first free slot if not a certain slot is requested */ + slot_no = GR1553BC_SLOTID_FROM_ID(*mid); + if ( slot_no == 0xff ) { + slot_no = gr1553bc_minor_first_avail(minor); + if ( slot_no < 0 ) + return -1; + } else { + /* Allocate a certain slot, check that it is free */ + if ( slot_no >= mincfg->slot_cnt ) + return -1; + if ( (1<<slot_no) & minor->alloc ) + return -1; + } + + /* Ok, we got our slot. Lets allocate time for slot if requested by user + * and time management is enabled for this Minor Frame. + */ + if ( timeslot > 0 ) { + /* Make timeslot on a 4us boundary (time resolution of core) */ + timeslot = (timeslot + 0x3) >> 2; + + if ( mincfg->timeslot ) { + /* Subtract requested time from free time */ + trbd = &minor->bds[mincfg->slot_cnt].tr; + set0 = GR1553BC_READ_MEM(&trbd->settings[0]); + timefree = set0 & GR1553BC_TR_TIME; + if ( timefree < timeslot ) { + /* Not enough time left to schedule slot in minor */ + return -1; + } + /* Store back the time left */ + timefree -= timeslot; + set0 = (set0 & ~GR1553BC_TR_TIME) | timefree; + GR1553BC_WRITE_MEM(&trbd->settings[0], set0); + /* Note: at the moment the minor frame can be executed faster + * than expected, we hurry up writing requested + * descriptor. + */ + } + } + + /* Make the allocated descriptor be an empty slot with the + * timeslot requested. + */ + trbd = &minor->bds[slot_no].tr; + gr1553bc_bd_tr_init( + trbd, + GR1553BC_TR_DUMMY_0 | timeslot, + GR1553BC_TR_DUMMY_1, + 0, + 0); + + /* Allocate slot */ + minor->alloc |= 1<<slot_no; + + if ( bd ) + *bd = (union gr1553bc_bd *)trbd; + *mid = GR1553BC_ID_SET_SLOT(*mid, slot_no); + + return 0; +} + +/* Return time slot freed (if time is managed by driver), negative on error */ +int gr1553bc_slot_free(struct gr1553bc_list *list, int mid) +{ + struct gr1553bc_minor *minor = gr1553bc_minor_from_id(list, mid); + + return gr1553bc_slot_free2(minor, mid); +} + +/* Return time slot freed (if time is managed by driver), negative on error */ +int gr1553bc_slot_free2(struct gr1553bc_minor *minor, int mid) +{ + union gr1553bc_bd *bd; + struct gr1553bc_bd_tr *endbd; + struct gr1553bc_minor_cfg *mincfg; + int slot_no, timeslot, timefree; + uint32_t word0, set0; + + if ( minor == NULL ) + return -1; + + slot_no = GR1553BC_SLOTID_FROM_ID(mid); + + if ( (minor->alloc & (1<<slot_no)) == 0 ) + return -1; + + bd = &minor->bds[slot_no]; + + /* If the driver handles time for this minor frame, return + * time if previuosly requested. + */ + timeslot = 0; + mincfg = minor->cfg; + if ( mincfg->timeslot > 0 ) { + /* Find out if message slot had time allocated */ + word0 = GR1553BC_READ_MEM(&bd->raw.words[0]); + if ( word0 & GR1553BC_BD_TYPE ) { + /* Condition ==> no time slot allocated */ + } else { + /* Transfer descriptor, may have time slot */ + timeslot = word0 & GR1553BC_TR_TIME; + if ( timeslot > 0 ) { + /* Return previously allocated time to END + * TIME descriptor. + */ + endbd = &minor->bds[mincfg->slot_cnt].tr; + set0 = GR1553BC_READ_MEM(&endbd->settings[0]); + timefree = set0 & GR1553BC_TR_TIME; + timefree += timeslot; + set0 = (set0 & ~GR1553BC_TR_TIME) | timefree; + GR1553BC_WRITE_MEM(&endbd->settings[0], set0); + /* Note: at the moment the minor frame can be + * executed slower than expected, the + * timeslot is at two locations. + */ + } + } + } + + /* Make slot an empty message */ + gr1553bc_bd_tr_init( + &bd->tr, + GR1553BC_TR_DUMMY_0, + GR1553BC_TR_DUMMY_1, + 0, + 0); + + /* unallocate descriptor */ + minor->alloc &= ~(1<<slot_no); + + /* Return time freed in microseconds */ + return timeslot << 2; +} + +int gr1553bc_list_freetime(struct gr1553bc_list *list, int mid) +{ + struct gr1553bc_minor *minor = gr1553bc_minor_from_id(list, mid); + + return gr1553bc_minor_freetime(minor); +} + +int gr1553bc_minor_freetime(struct gr1553bc_minor *minor) +{ + struct gr1553bc_bd_tr *endbd; + struct gr1553bc_minor_cfg *mincfg; + int timefree; + uint32_t set0; + + if ( minor == NULL ) + return -1; + + /* If the driver handles time for this minor frame, return + * time if previuosly requested. + */ + timefree = 0; + mincfg = minor->cfg; + if ( mincfg->timeslot > 0 ) { + /* Return previously allocated time to END + * TIME descriptor. + */ + endbd = &minor->bds[mincfg->slot_cnt].tr; + set0 = GR1553BC_READ_MEM(&endbd->settings[0]); + timefree = (set0 & GR1553BC_TR_TIME) << 2; + } + + /* Return time freed */ + return timefree; +} + +int gr1553bc_slot_raw + ( + struct gr1553bc_list *list, + int mid, + unsigned int flags, + uint32_t word0, + uint32_t word1, + uint32_t word2, + uint32_t word3 + ) +{ + struct gr1553bc_minor *minor; + union gr1553bc_bd *bd; + int slot_no; + + minor = gr1553bc_minor_from_id(list, mid); + if ( minor == NULL ) + return -1; + + /* Get Slot */ + slot_no = GR1553BC_SLOTID_FROM_ID(mid); + if ( slot_no >= minor->cfg->slot_cnt ) { + return -1; + } + + /* Get descriptor */ + bd = &minor->bds[slot_no]; + + /* Build empty descriptor. */ + gr1553bc_bd_init( + bd, + flags, + word0, + word1, + word2, + word3); + + return 0; +} + +/* Create unconditional IRQ customly defined location + * The IRQ is disabled, enable it with gr1553bc_slot_irq_enable(). + */ +int gr1553bc_slot_irq_prepare + ( + struct gr1553bc_list *list, + int mid, + bcirq_func_t func, + void *data + ) +{ + union gr1553bc_bd *bd; + int slot_no, to_mid; + + /* Build unconditional IRQ descriptor. The padding is used + * for identifying the MINOR frame and function and custom data. + * + * The IRQ is disabled at first, a unconditional jump to next + * descriptor in table. + */ + + /* Get BD address of jump destination */ + slot_no = GR1553BC_SLOTID_FROM_ID(mid); + to_mid = GR1553BC_ID_SET_SLOT(mid, slot_no + 1); + bd = gr1553bc_slot_bd(list, to_mid); + if ( bd == NULL ) + return -1; + bd = gr1553bc_bd_cpu2hw(list, bd); + + return gr1553bc_slot_raw( + list, + mid, + 0xF, + GR1553BC_UNCOND_JMP, + (uint32_t)bd, + (uint32_t)func, + (uint32_t)data + ); +} + +/* Enable previously prepared unconditional IRQ */ +int gr1553bc_slot_irq_enable(struct gr1553bc_list *list, int mid) +{ + /* Leave word1..3 untouched: + * 1. Unconditional Jump address + * 2. Function + * 3. Custom Data + * + * Since only one bit is changed in word0 (Condition word), + * no hardware/software races will exist ==> it is safe + * to enable/disable IRQ at any time independent of where + * hardware is in table. + */ + return gr1553bc_slot_raw( + list, + mid, + 0x1, /* change only WORD0 */ + GR1553BC_UNCOND_IRQ, + 0, + 0, + 0); +} + +/* Disable unconditional IRQ point, changed to unconditional JUMP + * to descriptor following. + * After disabling it it can be enabled again, or freed. + */ +int gr1553bc_slot_irq_disable(struct gr1553bc_list *list, int mid) +{ + return gr1553bc_slot_raw( + list, + mid, + 0x1, /* change only WORD0, JUMP address already set */ + GR1553BC_UNCOND_JMP, + 0, + 0, + 0); +} + +int gr1553bc_slot_empty(struct gr1553bc_list *list, int mid) +{ + return gr1553bc_slot_raw( + list, + mid, + 0xF | KEEP_TIMESLOT, + GR1553BC_TR_DUMMY_0, + GR1553BC_TR_DUMMY_1, + 0, + 0); +} + +int gr1553bc_slot_exttrig(struct gr1553bc_list *list, int mid) +{ + return gr1553bc_slot_raw( + list, + mid, + 0xF | KEEP_TIMESLOT, + GR1553BC_TR_DUMMY_0 | GR1553BC_TR_EXTTRIG, + GR1553BC_TR_DUMMY_1, + 0, + 0); +} + +int gr1553bc_slot_jump + ( + struct gr1553bc_list *list, + int mid, + uint32_t condition, + int to_mid + ) +{ + union gr1553bc_bd *bd; + + /* Get BD address */ + bd = gr1553bc_slot_bd(list, to_mid); + if ( bd == NULL ) + return -1; + /* Convert into an address that the HW understand */ + bd = gr1553bc_bd_cpu2hw(list, bd); + + return gr1553bc_slot_raw( + list, + mid, + 0xF, + condition, + (uint32_t)bd, + 0, + 0); +} + +int gr1553bc_slot_transfer( + struct gr1553bc_list *list, + int mid, + int options, + int tt, + uint16_t *dptr) +{ + uint32_t set0, set1; + union gr1553bc_bd *bd; + int rx_rtadr, tx_rtadr, timeout; + + /* Get BD address */ + bd = gr1553bc_slot_bd(list, mid); + if ( bd == NULL ) + return -1; + + /* Translate Data pointer from CPU-local to 1553-core accessible + * address if user wants that. This may be useful for AMBA-over-PCI + * cores. + */ + if ( (unsigned int)dptr & 0x1 ) { + struct gr1553bc_priv *bcpriv = list->bc; + + drvmgr_translate( + *bcpriv->pdev, + CPUMEM_TO_DMA, + (void *)((unsigned int)dptr & ~0x1), + (void **)&dptr); + } + + /* It is assumed that the descriptor has already been initialized + * as a empty slot (Dummy bit set), so to avoid races the dummy + * bit is cleared last. + * + * If we knew that the write would do a burst (for example over SpW) + * it would be safe to write in order. + */ + + /* Preserve timeslot */ + set0 = GR1553BC_READ_MEM(&bd->tr.settings[0]); + set0 &= GR1553BC_TR_TIME; + set0 |= options & 0x61f00000; + set0 |= list->tropts; /* Global options */ + + /* Set transfer type, bus and let RT tolerance table descide + * responce tolerance. + * + * If a destination address is specified the longest timeout + * tolerance is taken. + */ + rx_rtadr = (tt >> 22) & 0x1f; + tx_rtadr = (tt >> 12) & 0x1f; + if ( (tx_rtadr != 0x1f) && + (list->rt_timeout[rx_rtadr] < list->rt_timeout[tx_rtadr]) ) { + timeout = list->rt_timeout[tx_rtadr]; + } else { + timeout = list->rt_timeout[rx_rtadr]; + } + set1 = ((timeout & 0xf) << 27) | (tt & 0x27ffffff) | ((options & 0x3)<<30); + + GR1553BC_WRITE_MEM(&bd->tr.settings[0], set0); + GR1553BC_WRITE_MEM(&bd->tr.dptr, (uint32_t)dptr); + /* Write UNUSED BIT, when cleared it Indicates that BC has written it */ + GR1553BC_WRITE_MEM(&bd->tr.status, 0x80000000); + GR1553BC_WRITE_MEM(&bd->tr.settings[1], set1); + + return 0; +} + +int gr1553bc_slot_update + ( + struct gr1553bc_list *list, + int mid, + uint16_t *dptr, + unsigned int *stat + ) +{ + union gr1553bc_bd *bd; + unsigned int status; + unsigned int dataptr = (unsigned int)dptr; + + /* Get BD address */ + bd = gr1553bc_slot_bd(list, mid); + if ( bd == NULL ) + return -1; + + /* Write new Data Pointer if needed */ + if ( dataptr ) { + struct gr1553bc_priv *bcpriv = list->bc; + + /* Translate Data pointer from CPU-local to 1553-core accessible + * address if user wants that. This may be useful for AMBA-over-PCI + * cores. + */ + if ( dataptr & 0x1 ) { + drvmgr_translate( + *bcpriv->pdev, + CPUMEM_TO_DMA, + (void *)(dataptr & ~0x1), + (void **)&dptr + ); + } + + /* Update Data Pointer */ + GR1553BC_WRITE_MEM(&bd->tr.dptr, dataptr); + } + + /* Get status of transfer descriptor */ + if ( stat ) { + status = *stat; + *stat = GR1553BC_READ_MEM(&bd->tr.status); + if ( status ) { + /* Clear status fields user selects, then + * or bit31 if user wants that. The bit31 + * may be used to indicate if the BC has + * performed the access. + */ + status = (*stat & (status & 0xffffff)) | + (status & (1<<31)); + GR1553BC_WRITE_MEM(&bd->tr.status, status); + } + } + + return 0; +} + +int gr1553bc_slot_dummy( + struct gr1553bc_list *list, + int mid, + unsigned int *dummy) +{ + union gr1553bc_bd *bd; + unsigned int set1, new_set1; + + /* Get BD address */ + bd = gr1553bc_slot_bd(list, mid); + if ( bd == NULL ) + return -1; + /* Update the Dummy Bit */ + set1 = GR1553BC_READ_MEM(&bd->tr.settings[1]); + new_set1 = (set1 & ~GR1553BC_TR_DUMMY_1) | (*dummy & GR1553BC_TR_DUMMY_1); + GR1553BC_WRITE_MEM(&bd->tr.settings[1], new_set1); + + *dummy = set1; + + return 0; +} + +/* Find MID from Descriptor pointer */ +int gr1553bc_mid_from_bd( + union gr1553bc_bd *bd, + int *mid, + int *async + ) +{ + int i, bdmid, slot_no; + uint32_t word0, word2; + + /* Find Jump to next Minor Frame or End-Of-List, + * at those locations we have stored a MID + * + * GR1553BC_SLOT_MAX+2 = Worst case, BD is max distance from jump + * descriptor. 2=END and Jump descriptors. + */ + for (i=0; i<GR1553BC_SLOT_MAX+2; i++) { + word0 = GR1553BC_READ_MEM(&bd->raw.words[0]); + if ( word0 & GR1553BC_BD_TYPE ) { + if ( word0 == GR1553BC_UNCOND_JMP ) { + /* May be a unconditional IRQ set by user. In + * that case the function is stored in WORD3, + * functions must be aligned to 4 byte boudary. + */ + word2 = GR1553BC_READ_MEM(&bd->raw.words[2]); + if ( word2 & NEXT_MINOR_MARKER ) { + goto found_mid; + } + } else if ( word0 == GR1553BC_TR_EOL ) { + /* End-Of-List, does contain a MID */ + word2 = GR1553BC_READ_MEM(&bd->raw.words[2]); + goto found_mid; + } + } + bd++; + } + + return -1; + +found_mid: + /* Get MID of JUMP descriptor */ + bdmid = word2 >> 8; + /* Subtract distance from JUMP descriptor to find MID + * of requested BD. + */ + slot_no = GR1553BC_SLOTID_FROM_ID(bdmid); + slot_no -= i; + bdmid = GR1553BC_ID_SET_SLOT(bdmid, slot_no); + + if ( mid ) + *mid = bdmid; + + /* Determine which list BD belongs to: async or sync */ + if ( async ) + *async = word2 & NEXT_MINOR_MARKER_ASYNC; + + return 0; +} + +/*************** END OF LIST HANDLING ROUTINES ***************/ + +/*************** DEVICE HANDLING ROUTINES ***************/ + +void gr1553bc_device_init(struct gr1553bc_priv *priv); +void gr1553bc_device_uninit(struct gr1553bc_priv *priv); +void gr1553bc_isr(void *data); + +/*** GR1553BC driver ***/ + +void gr1553bc_register(void) +{ + /* The BC driver rely on the GR1553B Driver */ + gr1553_register(); +} + +void gr1553bc_isr_std(union gr1553bc_bd *bd, void *data) +{ + /* Do nothing */ +} + +/* Take a GR1553BC hardware device identified by minor. + * A pointer is returned that is used internally by the GR1553BC + * driver, it is used as an input paramter 'bc' to all other + * functions that manipulate the hardware. + */ +void *gr1553bc_open(int minor) +{ + struct drvmgr_dev **pdev = NULL; + struct gr1553bc_priv *priv = NULL; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + void *irq_log_p = NULL; + + /* Allocate requested device */ + pdev = gr1553_bc_open(minor); + if ( pdev == NULL ) + goto fail; + + irq_log_p = malloc(GR1553BC_IRQLOG_SIZE*2); + if ( irq_log_p == NULL ) + goto fail; + + priv = malloc(sizeof(struct gr1553bc_priv)); + if ( priv == NULL ) + goto fail; + memset(priv, 0, sizeof(struct gr1553bc_priv)); + + /* Init BC device */ + priv->pdev = pdev; + (*pdev)->priv = priv; + priv->irq_log_p = irq_log_p; + priv->started = 0; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)(*pdev)->businfo; + pnpinfo = &ambadev->info; + priv->regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start; + + gr1553bc_device_init(priv); + + /* Register ISR handler (unmask at IRQ controller) */ + if ( drvmgr_interrupt_register(*priv->pdev, 0, "gr1553bc", + gr1553bc_isr, priv) ) { + goto fail; + } + + return priv; + +fail: + if ( pdev ) + gr1553_bc_close(pdev); + if ( irq_log_p ) + free(irq_log_p); + if ( priv ) + free(priv); + return NULL; +} + +void gr1553bc_close(void *bc) +{ + struct gr1553bc_priv *priv = bc; + + /* Stop Hardware */ + gr1553bc_stop(bc, 0x3); + + gr1553bc_device_uninit(priv); + + /* Remove interrupt handler (mask IRQ at IRQ controller) */ + drvmgr_interrupt_unregister(*priv->pdev, 0, gr1553bc_isr, priv); + + /* Free device */ + gr1553_bc_close(priv->pdev); + free(priv->irq_log_p); + free(priv); +} + +/* Return Current Minor frame number */ +int gr1553bc_indication(void *bc, int async, int *mid) +{ + struct gr1553bc_priv *priv = bc; + union gr1553bc_bd *bd; + + /* Get current descriptor pointer */ + if ( async ) { + bd = (union gr1553bc_bd *) + GR1553BC_READ_REG(&priv->regs->bc_aslot); + bd = gr1553bc_bd_hw2cpu(priv->alist, bd); + } else { + bd = (union gr1553bc_bd *) + GR1553BC_READ_REG(&priv->regs->bc_slot); + bd = gr1553bc_bd_hw2cpu(priv->list, bd); + } + + return gr1553bc_mid_from_bd(bd, mid, NULL); +} + +/* Start major frame processing, wait for TimerManager tick or start directly */ +int gr1553bc_start(void *bc, struct gr1553bc_list *list, struct gr1553bc_list *list_async) +{ + struct gr1553bc_priv *priv = bc; + union gr1553bc_bd *bd = NULL, *bd_async = NULL; + uint32_t ctrl, irqmask; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( (list == NULL) && (list_async == NULL) ) + return 0; + + /* Find first descriptor in list, the descriptor + * first to be executed. + */ + ctrl = GR1553BC_KEY; + if ( list ) { + bd = gr1553bc_slot_bd(list, GR1553BC_ID(0,0,0)); + if ( bd == NULL ) + return -1; + bd = gr1553bc_bd_cpu2hw(list, bd); + ctrl |= GR1553B_BC_ACT_SCSRT; + } + if ( list_async ) { + bd_async = gr1553bc_slot_bd(list_async, GR1553BC_ID(0,0,0)); + if ( bd_async == NULL ) + return -1; + bd_async = gr1553bc_bd_cpu2hw(list_async, bd_async); + ctrl |= GR1553B_BC_ACT_ASSRT; + } + + /* Do "hot-swapping" of lists */ + IRQ_GLOBAL_DISABLE(oldLevel); + if ( list ) { + priv->list = list; + GR1553BC_WRITE_REG(&priv->regs->bc_bd, (uint32_t)bd); + } + if ( list_async ) { + priv->alist = list_async; + GR1553BC_WRITE_REG(&priv->regs->bc_abd, (uint32_t)bd_async); + } + + /* If not enabled before, we enable it now. */ + GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl); + + /* Enable IRQ */ + if ( priv->started == 0 ) { + priv->started = 1; + irqmask = GR1553BC_READ_REG(&priv->regs->imask); + irqmask |= GR1553B_IRQEN_BCEVE|GR1553B_IRQEN_BCDE|GR1553B_IRQEN_BCWKE; + GR1553BC_WRITE_REG(&priv->regs->imask, irqmask); + } + + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +/* Pause GR1553 BC transfers */ +int gr1553bc_pause(void *bc) +{ + struct gr1553bc_priv *priv = bc; + uint32_t ctrl; + IRQ_GLOBAL_PREPARE(oldLevel); + + /* Do "hot-swapping" of lists */ + IRQ_GLOBAL_DISABLE(oldLevel); + ctrl = GR1553BC_KEY | GR1553B_BC_ACT_SCSUS; + GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl); + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +/* Restart GR1553 BC transfers, after being paused */ +int gr1553bc_restart(void *bc) +{ + struct gr1553bc_priv *priv = bc; + uint32_t ctrl; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + ctrl = GR1553BC_KEY | GR1553B_BC_ACT_SCSRT; + GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl); + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +/* Stop BC transmission */ +int gr1553bc_stop(void *bc, int options) +{ + struct gr1553bc_priv *priv = bc; + uint32_t ctrl; + IRQ_GLOBAL_PREPARE(oldLevel); + + ctrl = GR1553BC_KEY; + if ( options & 0x1 ) + ctrl |= GR1553B_BC_ACT_SCSTP; + if ( options & 0x2 ) + ctrl |= GR1553B_BC_ACT_ASSTP; + + IRQ_GLOBAL_DISABLE(oldLevel); + GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl); + priv->started = 0; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +/* Reset software and BC hardware into a known "unused/init" state */ +void gr1553bc_device_init(struct gr1553bc_priv *priv) +{ +/* RESET HARDWARE REGISTERS */ + /* Stop BC if not already stopped */ + GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204); + + /* Since RT can not be used at the same time as BC, we stop + * RT rx, it should already be stopped... + */ + GR1553BC_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY); + + /* Clear some registers */ + GR1553BC_WRITE_REG(&priv->regs->bc_bd, 0); + GR1553BC_WRITE_REG(&priv->regs->bc_abd, 0); + GR1553BC_WRITE_REG(&priv->regs->bc_timer, 0); + GR1553BC_WRITE_REG(&priv->regs->bc_wake, 0); + GR1553BC_WRITE_REG(&priv->regs->bc_irqptr, 0); + GR1553BC_WRITE_REG(&priv->regs->bc_busmsk, 0); + +/* PUT SOFTWARE INTO INITIAL STATE */ + priv->list = NULL; + priv->alist = NULL; + + priv->irq_log_base = (uint32_t *) + (((uint32_t)priv->irq_log_p + (GR1553BC_IRQLOG_SIZE-1)) & + ~(GR1553BC_IRQLOG_SIZE-1)); + /* Translate into a hardware accessible address */ + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->irq_log_base, + (void **)&priv->irq_log_base_hw, + GR1553BC_IRQLOG_SIZE); + priv->irq_log_curr = priv->irq_log_base; + priv->irq_log_end = &priv->irq_log_base[GR1553BC_IRQLOG_CNT-1]; + priv->irq_func = gr1553bc_isr_std; + priv->irq_data = NULL; + + GR1553BC_WRITE_REG(&priv->regs->bc_irqptr,(uint32_t)priv->irq_log_base_hw); +} + +void gr1553bc_device_uninit(struct gr1553bc_priv *priv) +{ + uint32_t irqmask; + + /* Stop BC if not already stopped */ + GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204); + + /* Since RT can not be used at the same time as BC, we stop + * RT rx, it should already be stopped... + */ + GR1553BC_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY); + + /* Turn off IRQ generation */ + irqmask=GR1553BC_READ_REG(&priv->regs->imask); + irqmask&=~(GR1553B_IRQEN_BCEVE|GR1553B_IRQEN_BCDE|GR1553B_IRQEN_BCWKE); + GR1553BC_WRITE_REG(&priv->regs->irq, irqmask); +} + +/* Interrupt handler */ +void gr1553bc_isr(void *arg) +{ + struct gr1553bc_priv *priv = arg; + uint32_t *curr, *pos, word0, word2; + union gr1553bc_bd *bd; + bcirq_func_t func; + void *data; + int handled, irq; + + /* Did core make IRQ */ + irq = GR1553BC_READ_REG(&priv->regs->irq); + irq &= (GR1553B_IRQEN_BCEVE|GR1553B_IRQEN_BCDE|GR1553B_IRQEN_BCWKE); + if ( irq == 0 ) + return; /* Shared IRQ: some one else may have caused the IRQ */ + + /* Clear handled IRQs */ + GR1553BC_WRITE_REG(&priv->regs->irq, irq); + + /* DMA error. This IRQ does not affect the IRQ log. + * We let standard IRQ handle handle it. + */ + if ( irq & GR1553B_IRQEN_BCDE ) { + priv->irq_func(NULL, priv->irq_data); + } + + /* Get current posistion in hardware */ + pos = (uint32_t *)GR1553BC_READ_REG(&priv->regs->bc_irqptr); + /* Convertin into CPU address */ + pos = priv->irq_log_base + + ((unsigned int)pos - (unsigned int)priv->irq_log_base_hw)/4; + + /* Step in IRQ log until we reach the end. */ + handled = 0; + curr = priv->irq_log_curr; + while ( curr != pos ) { + bd = (union gr1553bc_bd *)(GR1553BC_READ_MEM(curr) & ~1); + GR1553BC_WRITE_MEM(curr, 0x2); /* Mark Handled */ + + /* Convert Descriptor in IRQ log into CPU address. In order + * to convert we must know which list the descriptor belongs + * to, we compare the address of the bd to the ASYNC list + * descriptor table area. + */ + if ( priv->alist && ((unsigned int)bd>=priv->alist->table_hw) && + ((unsigned int)bd < + (priv->alist->table_hw + priv->alist->table_size))) { + /* BD in async list */ + bd = gr1553bc_bd_hw2cpu(priv->alist, bd); + } else { + /* BD in sync list */ + bd = gr1553bc_bd_hw2cpu(priv->list, bd); + } + + /* Handle Descriptor that cased IRQ + * + * If someone have inserted an IRQ descriptor and tied + * that to a custom function we call that function, otherwise + * we let the standard IRQ handle handle it. + */ + word0 = GR1553BC_READ_MEM(&bd->raw.words[0]); + if ( word0 == GR1553BC_UNCOND_IRQ ) { + word2 = GR1553BC_READ_MEM(&bd->raw.words[2]); + if ( (word2 & 0x3) == 0 ) { + func = (bcirq_func_t)(word2 & ~0x3); + data = (void *) + GR1553BC_READ_MEM(&bd->raw.words[3]); + func(bd, data); + handled = 1; + } + } + + if ( handled == 0 ) { + /* Let standard IRQ handle handle it */ + priv->irq_func(bd, priv->irq_data); + } else { + handled = 0; + } + + /* Increment to next entry in IRQ LOG */ + if ( curr == priv->irq_log_end ) + curr = priv->irq_log_base; + else + curr++; + } + priv->irq_log_curr = curr; +} + +int gr1553bc_irq_setup + ( + void *bc, + bcirq_func_t func, + void *data + ) +{ + struct gr1553bc_priv *priv = bc; + + if ( func == NULL ) + priv->irq_func = gr1553bc_isr_std; + else + priv->irq_func = func; + priv->irq_data = data; + + return 0; +} + +void gr1553bc_ext_trig(void *bc, int trig) +{ + struct gr1553bc_priv *priv = bc; + unsigned int trigger; + + if ( trig ) + trigger = GR1553B_BC_ACT_SETT; + else + trigger = GR1553B_BC_ACT_CLRT; + + GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | trigger); +} + +void gr1553bc_status(void *bc, struct gr1553bc_status *status) +{ + struct gr1553bc_priv *priv = bc; + + status->status = GR1553BC_READ_REG(&priv->regs->bc_stat); + status->time = GR1553BC_READ_REG(&priv->regs->bc_timer); +} + +/*** DEBUGGING HELP FUNCTIONS ***/ + +#include <stdio.h> + +void gr1553bc_show_list(struct gr1553bc_list *list, int options) +{ + struct gr1553bc_major *major; + struct gr1553bc_minor *minor; + int i, j, minor_cnt, timefree; + + printf("LIST\n"); + printf(" major cnt: %d\n", list->major_cnt); + for (i=0; i<32; i++) { + printf(" RT[%d] timeout: %d\n", i, 14+(list->rt_timeout[i]*4)); + } + + for (i=0; i<list->major_cnt; i++) { + major = list->majors[i]; + minor_cnt = major->cfg->minor_cnt; + printf(" MAJOR[%d]\n", i); + printf(" minor count: %d\n", minor_cnt); + + for (j=0; j<minor_cnt; j++) { + minor = major->minors[j]; + + printf(" MINOR[%d]\n", j); + printf(" bd: 0x%08x (HW:0x%08x)\n", + (unsigned int)&minor->bds[0], + (unsigned int)gr1553bc_bd_cpu2hw(list, + &minor->bds[0])); + printf(" slot cnt: %d\n", minor->cfg->slot_cnt); + if ( minor->cfg->timeslot ) { + timefree = gr1553bc_minor_freetime(minor); + printf(" timefree: %d\n", timefree); + printf(" timetotal: %d\n", + minor->cfg->timeslot); + } else { + printf(" no time mgr\n"); + } + } + } +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c new file mode 100644 index 0000000000..1ce731ec43 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c @@ -0,0 +1,519 @@ +/* GR1553B BM driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#include <gr1553b.h> +#include <gr1553bm.h> + + +#define GR1553BM_WRITE_MEM(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553BM_READ_MEM(adr) (*(volatile uint32_t *)(adr)) + +#define GR1553BM_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553BM_READ_REG(adr) (*(volatile uint32_t *)(adr)) + +#ifndef IRQ_GLOBAL_PREPARE + #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level +#endif + +#ifndef IRQ_GLOBAL_DISABLE + #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level) +#endif + +#ifndef IRQ_GLOBAL_ENABLE + #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level) +#endif + +struct gr1553bm_priv { + struct drvmgr_dev **pdev; + struct gr1553b_regs *regs; + + void *buffer; + unsigned int buffer_base_hw; + unsigned int buffer_base; + unsigned int buffer_end; + unsigned int buffer_size; + unsigned int read_pos; + int started; + struct gr1553bm_config cfg; + + /* Time updated by IRQ when 24-bit Time counter overflows */ + volatile uint64_t time; +}; + +void gr1553bm_isr(void *data); + +/* Default Driver configuration */ +struct gr1553bm_config gr1553bm_default_config = +{ + /* Highest resolution, use Time overflow IRQ to track */ + .time_resolution = 0, + .time_ovf_irq = 1, + + /* No filtering, log all */ + .filt_error_options = GR1553BM_ERROPTS_ALL, + .filt_rtadr = 0xffffffff, + .filt_subadr = 0xffffffff, + .filt_mc = 0x0007ffff, + + /* 128Kbyte dynamically allocated buffer. */ + .buffer_size = 128*1024, + .buffer_custom = NULL, +}; + +void gr1553bm_register(void) +{ + /* The BM driver rely on the GR1553B Driver */ + gr1553_register(); +} + +static void gr1553bm_hw_start(struct gr1553bm_priv *priv) +{ + IRQ_GLOBAL_PREPARE(oldLevel); + + /* Enable IRQ source and mark running state */ + IRQ_GLOBAL_DISABLE(oldLevel); + + priv->started = 1; + + /* Clear old IRQ flags */ + priv->regs->irq = GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF; + + /* Unmask IRQ sources */ + if ( priv->cfg.time_ovf_irq ) { + priv->regs->imask |= GR1553B_IRQEN_BMDE | GR1553B_IRQEN_BMTOE; + } else { + priv->regs->imask |= GR1553B_IRQEN_BMDE; + } + + /* Start logging */ + priv->regs->bm_ctrl = + (priv->cfg.filt_error_options & + (GR1553B_BM_CTRL_MANL|GR1553B_BM_CTRL_UDWL|GR1553B_BM_CTRL_IMCL)) + | GR1553B_BM_CTRL_BMEN; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +static void gr1553bm_hw_stop(struct gr1553bm_priv *priv) +{ + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + + /* Stop Logging */ + priv->regs->bm_ctrl = 0; + + /* Stop IRQ source */ + priv->regs->imask &= ~(GR1553B_IRQEN_BMDE|GR1553B_IRQEN_BMTOE); + + /* Clear IRQ flags */ + priv->regs->irq = GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF; + + priv->started = 0; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +/* Open device by number */ +void *gr1553bm_open(int minor) +{ + struct drvmgr_dev **pdev = NULL; + struct gr1553bm_priv *priv = NULL; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + + /* Allocate requested device */ + pdev = gr1553_bm_open(minor); + if ( pdev == NULL ) + goto fail; + + priv = malloc(sizeof(struct gr1553bm_priv)); + if ( priv == NULL ) + goto fail; + memset(priv, 0, sizeof(struct gr1553bm_priv)); + + /* Init BC device */ + priv->pdev = pdev; + (*pdev)->priv = priv; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)(*pdev)->businfo; + pnpinfo = &ambadev->info; + priv->regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start; + + /* Start with default configuration */ + priv->cfg = gr1553bm_default_config; + + /* Unmask IRQs */ + gr1553bm_hw_stop(priv); + + return priv; + +fail: + if ( pdev ) + gr1553_bm_close(pdev); + if ( priv ) + free(priv); + return NULL; +} + +/* Close previously */ +void gr1553bm_close(void *bm) +{ + struct gr1553bm_priv *priv = bm; + + if ( priv->started ) { + gr1553bm_stop(bm); + } + + if ( (priv->cfg.buffer_custom == NULL) && priv->buffer ) + free(priv->buffer); + + gr1553_bm_close(priv->pdev); + free(priv); +} + +/* Configure the BM driver */ +int gr1553bm_config(void *bm, struct gr1553bm_config *cfg) +{ + struct gr1553bm_priv *priv = bm; + + if ( priv->started ) + return -1; + + /* Check Config validity? */ +/*#warning IMPLEMENT.*/ + + /* Free old buffer if dynamically allocated */ + if ( (priv->cfg.buffer_custom == NULL) && priv->buffer ) { + free(priv->buffer); + priv->buffer = NULL; + } + priv->buffer_size = cfg->buffer_size & ~0x7; /* on 8 byte bounadry */ + if ((unsigned int)cfg->buffer_custom & 1) { + /* Custom Address Given in Remote address. We need + * to convert it intoTranslate into Hardware a + * hardware accessible address + */ + priv->buffer_base_hw = (unsigned int)cfg->buffer_custom & ~1; + priv->buffer = cfg->buffer_custom; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->buffer_base_hw, + (void **)&priv->buffer_base, + priv->buffer_size); + } else { + if (cfg->buffer_custom == NULL) { + /* Allocate new buffer dynamically */ + priv->buffer = malloc(priv->buffer_size + 8); + if (priv->buffer == NULL) + return -1; + } else { + /* Address given in CPU accessible address, no + * translation required. + */ + priv->buffer = cfg->buffer_custom; + } + /* Align to 16 bytes */ + priv->buffer_base = ((unsigned int)priv->buffer + (8-1)) & + ~(8-1); + /* Translate address of buffer base into address that Hardware must + * use to access the buffer. + */ + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->buffer_base, + (void **)&priv->buffer_base_hw, + priv->buffer_size); + + } + + /* Copy valid config */ + priv->cfg = *cfg; + + return 0; +} + +/* Start logging */ +int gr1553bm_start(void *bm) +{ + struct gr1553bm_priv *priv = bm; + + if ( priv->started ) + return -1; + if ( priv->buffer == NULL ) + return -2; + + /* Start at Time = 0 */ + priv->regs->bm_ttag = + priv->cfg.time_resolution << GR1553B_BM_TTAG_RES_BIT; + + /* Configure Filters */ + priv->regs->bm_adr = priv->cfg.filt_rtadr; + priv->regs->bm_subadr = priv->cfg.filt_subadr; + priv->regs->bm_mc = priv->cfg.filt_mc; + + /* Set up buffer */ + priv->regs->bm_start = priv->buffer_base_hw; + priv->regs->bm_end = priv->buffer_base_hw + priv->cfg.buffer_size - 4; + priv->regs->bm_pos = priv->buffer_base_hw; + priv->read_pos = priv->buffer_base; + priv->buffer_end = priv->buffer_base + priv->cfg.buffer_size; + + /* Register ISR handler and unmask IRQ source at IRQ controller */ + if (drvmgr_interrupt_register(*priv->pdev, 0, "gr1553bm", gr1553bm_isr, priv)) + return -3; + + /* Start hardware and set priv->started */ + gr1553bm_hw_start(priv); + + return 0; +} + +/* Stop logging */ +void gr1553bm_stop(void *bm) +{ + struct gr1553bm_priv *priv = bm; + + /* Stop Hardware */ + gr1553bm_hw_stop(priv); + + /* At this point the hardware must be stopped and IRQ + * sources unmasked. + */ + + /* Unregister ISR handler and unmask 1553 IRQ source at IRQ ctrl */ + drvmgr_interrupt_unregister(*priv->pdev, 0, gr1553bm_isr, priv); +} + +int gr1553bm_started(void *bm) +{ + return ((struct gr1553bm_priv *)bm)->started; +} + +/* Get 64-bit 1553 Time. + * + * Update software time counters and return the current time. + */ +void gr1553bm_time(void *bm, uint64_t *time) +{ + struct gr1553bm_priv *priv = bm; + unsigned int hwtime, hwtime2; + +resample: + if ( priv->started && (priv->cfg.time_ovf_irq == 0) ) { + /* Update Time overflow counter. The carry bit from Time counter + * is located in IRQ Flag. + * + * When IRQ is not used this function must be called often + * enough to avoid that the Time overflows and the carry + * bit is already set. The frequency depends on the Time + * resolution. + */ + if ( priv->regs->irq & GR1553B_IRQ_BMTOF ) { + /* Clear carry bit */ + priv->regs->irq = GR1553B_IRQ_BMTOF; + priv->time += (GR1553B_BM_TTAG_VAL + 1); + } + } + + /* Report current Time, even if stopped */ + hwtime = priv->regs->bm_ttag & GR1553B_BM_TTAG_VAL; + if ( time ) + *time = priv->time | hwtime; + + if ( priv->cfg.time_ovf_irq ) { + /* Detect wrap around */ + hwtime2 = priv->regs->bm_ttag & GR1553B_BM_TTAG_VAL; + if ( hwtime > hwtime2 ) { + /* priv->time and hwtime may be out of sync if + * IRQ updated priv->time just after bm_ttag was read + * here, we resample if we detect inconsistancy. + */ + goto resample; + } + } +} + +/* Number of entries available in DMA buffer */ +int gr1553bm_available(void *bm, int *nentries) +{ + struct gr1553bm_priv *priv = bm; + unsigned int top, bot, pos; + + if ( !priv->started ) + return -1; + + /* Get BM posistion in log */ + pos = priv->regs->bm_pos; + + /* Convert into CPU accessible address */ + pos = priv->buffer_base + (pos - priv->buffer_base_hw); + + if ( pos >= priv->read_pos ) { + top = (pos - priv->read_pos)/sizeof(struct gr1553bm_entry); + bot = 0; + } else { + top = (priv->buffer_end - priv->read_pos)/sizeof(struct gr1553bm_entry); + bot = (pos - priv->buffer_base)/sizeof(struct gr1553bm_entry); + } + + if ( nentries ) + *nentries = top+bot; + + return 0; +} + +/* Read a maximum number of entries from LOG buffer. */ +int gr1553bm_read(void *bm, struct gr1553bm_entry *dst, int *max) +{ + struct gr1553bm_priv *priv = bm; + unsigned int dest, pos, left, newPos, len; + unsigned int topAdr, botAdr, topLen, botLen; + + if ( !priv || !priv->started ) + return -1; + + left = *max; + pos = priv->regs->bm_pos & ~0x7; + + /* Convert into CPU accessible address */ + pos = priv->buffer_base + (pos - priv->buffer_base_hw); + + if ( (pos == priv->read_pos) || (left < 1) ) { + /* No data available */ + *max = 0; + return 0; + } + newPos = 0; + + /* Addresses and lengths of BM log buffer */ + if ( pos >= priv->read_pos ) { + /* Read Top only */ + topAdr = priv->read_pos; + botAdr = 0; + topLen = (pos - priv->read_pos)/sizeof(struct gr1553bm_entry); + botLen = 0; + } else { + /* Read Top and Bottom */ + topAdr = priv->read_pos; + botAdr = priv->buffer_base; + topLen = (priv->buffer_end - priv->read_pos)/sizeof(struct gr1553bm_entry); + botLen = (pos - priv->buffer_base)/sizeof(struct gr1553bm_entry); + } + + dest = (unsigned int)dst; + if ( topLen > 0 ) { + /* Copy from top area first */ + if ( topLen > left ) { + len = left; + left = 0; + } else { + len = topLen; + left -= topLen; + } + newPos = topAdr + (len * sizeof(struct gr1553bm_entry)); + if ( newPos >= priv->buffer_end ) + newPos -= priv->buffer_size; + if ( priv->cfg.copy_func ) { + dest += priv->cfg.copy_func( + dest, /*Optional Destination*/ + (void *)topAdr, /* DMA start address */ + len, /* Number of entries */ + priv->cfg.copy_func_arg /* Custom ARG */ + ); + } else { + memcpy( (void *)dest, + (void *)topAdr, + len * sizeof(struct gr1553bm_entry)); + dest += len * sizeof(struct gr1553bm_entry); + } + } + + if ( (botLen > 0) && (left > 0) ) { + /* Copy bottom area last */ + if ( botLen > left ) { + len = left; + left = 0; + } else { + len = botLen; + left -= botLen; + } + newPos = botAdr + (len * sizeof(struct gr1553bm_entry)); + + if ( priv->cfg.copy_func ) { + priv->cfg.copy_func( + dest, /*Optional Destination*/ + (void *)botAdr, /* DMA start address */ + len, /* Number of entries */ + priv->cfg.copy_func_arg /* Custom ARG */ + ); + } else { + memcpy( (void *)dest, + (void *)botAdr, + len * sizeof(struct gr1553bm_entry)); + } + } + + /* Remember last read posistion in buffer */ + /*printf("New pos: 0x%08x (0x%08x), %d\n", newPos, priv->read_pos, *max - left);*/ + priv->read_pos = newPos; + + /* Return number of entries read */ + *max = *max - left; + + return 0; +} + +/* Note: This is a shared interrupt handler, with BC/RT driver + * we must determine the cause of IRQ before handling it. + */ +void gr1553bm_isr(void *data) +{ + struct gr1553bm_priv *priv = data; + uint32_t irqflag; + + /* Get Causes */ + irqflag = priv->regs->irq & (GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF); + + /* Check spurious IRQs */ + if ( (irqflag == 0) || (priv->started == 0) ) + return; + + if ( (irqflag & GR1553B_IRQ_BMTOF) && priv->cfg.time_ovf_irq ) { + /* 1553 Time Over flow. Time is 24-bits */ + priv->time += (GR1553B_BM_TTAG_VAL + 1); + + /* Clear cause handled */ + priv->regs->irq = GR1553B_IRQ_BMTOF; + } + + if ( irqflag & GR1553B_IRQ_BMD ) { + /* BM DMA ERROR. Fatal error, we stop BM hardware and let + * user take care of it. From now on all calls will result + * in an error because the BM is stopped (priv->started=0). + */ + + /* Clear cause handled */ + priv->regs->irq = GR1553B_IRQ_BMD; + + if ( priv->cfg.dma_error_isr ) + priv->cfg.dma_error_isr(data, priv->cfg.dma_error_arg); + + gr1553bm_hw_stop(priv); + } +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c new file mode 100644 index 0000000000..ff05ce5d73 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c @@ -0,0 +1,1256 @@ +/* GR1553B RT driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <rtems.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <gr1553b.h> +#include <gr1553rt.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#define GR1553RT_WRITE_MEM(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553RT_READ_MEM(adr) (*(volatile uint32_t *)(adr)) + +#define GR1553RT_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553RT_READ_REG(adr) (*(volatile uint32_t *)(adr)) + +#ifndef IRQ_GLOBAL_PREPARE + #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level +#endif + +#ifndef IRQ_GLOBAL_DISABLE + #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level) +#endif + +#ifndef IRQ_GLOBAL_ENABLE + #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level) +#endif + +/* Software representation of one hardware descriptor */ +struct gr1553rt_sw_bd { + unsigned short this_next;/* Next entry or this entry. 0xffff: no next */ + unsigned char listid; /* ListID of List the descriptor is attached */ + char unused; +} __attribute__((packed)); + +/* Software description of a subaddress */ +struct gr1553rt_subadr { + /* RX LIST */ + unsigned char rxlistid; + /* TX LIST */ + unsigned char txlistid; +}; + +struct gr1553rt_irqerr { + gr1553rt_irqerr_t func; + void *data; +}; + +struct gr1553rt_irqmc { + gr1553rt_irqmc_t func; + void *data; +}; + +struct gr1553rt_irq { + gr1553rt_irq_t func; + void *data; +}; + +struct gr1553rt_priv { + /* Pointer to Hardware registers */ + struct gr1553b_regs *regs; + + /* Software State */ + int started; + struct gr1553rt_cfg cfg; + + /* Handle to GR1553B RT device layer */ + struct drvmgr_dev **pdev; + + /* Each Index represents one RT Subaddress. 31 = Broadcast */ + struct gr1553rt_subadr subadrs[32]; + + /* Pointer to array of Software's description of a hardware + * descriptor. + */ +#if (RTBD_MAX == 0) + struct gr1553rt_sw_bd *swbds; +#else + struct gr1553rt_sw_bd swbds[RTBD_MAX]; +#endif + + /* List of Free descriptors */ + unsigned short swbd_free; + int swbd_free_cnt; + + /* Hardware SubAddress descriptors given for CPU and Hardware */ + void *satab_buffer; + struct gr1553rt_sa *sas_cpu; /* Translated for CPU */ + struct gr1553rt_sa *sas_hw; /* Translated for Hardware */ + + /* Hardware descriptors address given for CPU and hardware */ + void *bd_buffer; + int bds_cnt; /* Number of descriptors */ + struct gr1553rt_bd *bds_cpu; /* Translated for CPU */ + struct gr1553rt_bd *bds_hw; /* Translated for Hardware */ + + + /* Event Log buffer in */ + void *evlog_buffer; + unsigned int *evlog_cpu_next; /* Next LOG entry to be handled */ + unsigned int *evlog_cpu_base; /* First Entry in LOG */ + unsigned int *evlog_cpu_end; /* Last+1 Entry in LOG */ + unsigned int *evlog_hw_base; /* Translated for Hardware */ + + /* Each Index represents a LIST ID */ + struct gr1553rt_list *lists[RTLISTID_MAX]; + + /* IRQ handlers, one per SUBADDRESS */ + struct gr1553rt_irq irq_rx[32]; + struct gr1553rt_irq irq_tx[32]; + + /* ISR called when an ERROR IRQ is received */ + struct gr1553rt_irqerr irq_err; + + /* ISR called when an Mode Code is received */ + struct gr1553rt_irqmc irq_mc; +}; + +void gr1553rt_sw_init(struct gr1553rt_priv *priv); +void gr1553rt_sw_free(struct gr1553rt_priv *priv); +int gr1553rt_sw_alloc(struct gr1553rt_priv *priv); + +/* Assign and ID to the list. An LIST ID is needed before scheduling list + * on an RT subaddress. + * + * Only 64 lists can be registered at a time on the same device. + */ +int gr1553rt_list_reg(struct gr1553rt_list *list) +{ + struct gr1553rt_priv *priv = list->rt; + int i; + + /* Find first free list ID */ + for ( i=0; i<RTLISTID_MAX; i++) { + if ( priv->lists[i] == NULL ) { + priv->lists[i] = list; + list->listid = i; + return i; + } + } + + /* No available LIST IDs */ + list->listid = -1; + + return -1; +} + +/* Unregister List from device */ +void gr1553rt_list_unreg(struct gr1553rt_list *list) +{ + struct gr1553rt_priv *priv = list->rt; + + priv->lists[list->listid] = NULL; + list->listid = -1; +} + +static int gr1553rt_bdid(void *rt, struct gr1553rt_sw_bd *bd) +{ + struct gr1553rt_priv *priv = rt; + + unsigned short index; + + /* Get Index of Software BD */ + index = ((unsigned int)bd - (unsigned int)&priv->swbds[0]) / + sizeof(struct gr1553rt_sw_bd); + + return index; +} + +void gr1553rt_bd_alloc_init(void *rt, int count) +{ + struct gr1553rt_priv *priv = rt; + int i; + + for (i=0; i<count-1; i++) { + priv->swbds[i].this_next = i+1; + } + priv->swbds[count-1].this_next = 0xffff; + priv->swbd_free = 0; + priv->swbd_free_cnt = count; +} + +/* Allocate a Chain of descriptors */ +int gr1553rt_bd_alloc(void *rt, struct gr1553rt_sw_bd **bd, int cnt) +{ + struct gr1553rt_priv *priv = rt; + struct gr1553rt_sw_bd *curr; + int i; + + if ((priv->swbd_free_cnt < cnt) || (cnt <= 0)) { + *bd = NULL; + return -1; + } + + *bd = &priv->swbds[priv->swbd_free]; + for (i=0; i<cnt; i++) { + if ( i == 0) { + curr = &priv->swbds[priv->swbd_free]; + } else { + curr = &priv->swbds[curr->this_next]; + } + if ( curr->this_next == 0xffff ) { + *bd = NULL; + return -1; + } + } + priv->swbd_free = curr->this_next; + priv->swbd_free_cnt -= cnt; + curr->this_next = 0xffff; /* Mark end of chain on last entry */ + + return 0; +} + +void gr1553rt_bd_free(void *rt, struct gr1553rt_sw_bd *bd) +{ + struct gr1553rt_priv *priv = rt; + unsigned short index; + + /* Get Index of Software BD */ + index = gr1553rt_bdid(priv, bd); + + /* Insert first in list */ + bd->this_next = priv->swbd_free; + priv->swbd_free = index; + priv->swbd_free_cnt++; +} + +int gr1553rt_list_init + ( + void *rt, + struct gr1553rt_list **plist, + struct gr1553rt_list_cfg *cfg + ) +{ + struct gr1553rt_priv *priv = rt; + int i, size; + struct gr1553rt_sw_bd *swbd; + unsigned short index; + struct gr1553rt_list *list; + + /* The user may provide a pre allocated LIST, or + * let the driver handle allocation by using malloc() + * + * If the IN/OUT plist argument points to NULL a list + * dynamically allocated here. + */ + list = *plist; + if ( list == NULL ) { + /* Dynamically allocate LIST */ + size = offsetof(struct gr1553rt_list, bds) + + (cfg->bd_cnt * sizeof(unsigned short)); + list = (struct gr1553rt_list *)malloc(size); + if ( list == NULL ) + return -1; + *plist = list; + } + + list->rt = rt; + list->subadr = -1; + list->listid = gr1553rt_list_reg(list); + if ( list->listid == -1 ) + return -2; /* Too many lists */ + list->cfg = cfg; + list->bd_cnt = cfg->bd_cnt; + + /* Allocate all BDs needed by list */ + if ( gr1553rt_bd_alloc(rt, &swbd, list->bd_cnt) ) { + return -3; /* Too few descriptors */ + } + + /* Get ID/INDEX of Software BDs */ + index = gr1553rt_bdid(rt, swbd); + list->bds[0] = index; + for (i=1; i<list->bd_cnt; i++) { + list->bds[i] = priv->swbds[list->bds[i-1]].this_next; + } + + /* Now that the next pointer has fullfilled it's job and not + * needed anymore, we use it as list entry pointer instead. + * The this_next pointer is a list entry number. + */ + for (i=0; i<list->bd_cnt; i++) { + priv->swbds[list->bds[i]].this_next = i; + } + + return 0; +} + +int gr1553rt_bd_init( + struct gr1553rt_list *list, + unsigned short entry_no, + unsigned int flags, + uint16_t *dptr, + unsigned short next + ) +{ + struct gr1553rt_priv *priv; + unsigned short bdid; + struct gr1553rt_bd *bd; + unsigned int nextbd, dataptr; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( entry_no >= list->bd_cnt ) + return -1; + + /* Find Descriptor */ + bdid = list->bds[entry_no]; + priv = list->rt; + bd = &priv->bds_cpu[bdid]; + + if ( next == 0xfffe ) { + next = entry_no + 1; + if ( next >= list->bd_cnt ) + next = 0; + } + + /* Find next descriptor in address space that the + * Hardware understand. + */ + if ( next >= 0xffff ) { + nextbd = 0x3; /* End of list */ + } else if ( next >= list->bd_cnt ) { + return -1; + } else { + bdid = list->bds[next]; + nextbd = (unsigned int)&priv->bds_hw[bdid]; + } + + dataptr = (unsigned int)dptr; + if ( dataptr & 1 ) { + /* Translate address from CPU-local into remote */ + dataptr &= ~1; + drvmgr_translate( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)dataptr, + (void **)&dataptr + ); + } + + /* Get current status, and clear */ + IRQ_GLOBAL_DISABLE(oldLevel); + bd->ctrl = flags & GR1553RT_BD_FLAGS_IRQEN; + bd->dptr = (unsigned int)dptr; + bd->next = nextbd; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_bd_update( + struct gr1553rt_list *list, + int entry_no, + unsigned int *status, + uint16_t **dptr + ) +{ + struct gr1553rt_priv *priv; + unsigned short bdid; + struct gr1553rt_bd *bd; + unsigned int tmp, dataptr; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( entry_no >= list->bd_cnt ) + return -1; + + /* Find Descriptor */ + bdid = list->bds[entry_no]; + priv = list->rt; + bd = &priv->bds_cpu[bdid]; + + /* Prepare translation if needed */ + if ( dptr && (dataptr=(unsigned int)*dptr) ) { + if ( dataptr & 1 ) { + /* Translate address from CPU-local into remote. May + * be used when RT core is accessed over the PCI bus. + */ + dataptr &= ~1; + drvmgr_translate( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)dataptr, + (void **)&dataptr + ); + } + } + + /* Get current status, and clear */ + IRQ_GLOBAL_DISABLE(oldLevel); + /* READ/WRITE Status/Control word */ + if ( status ) { + tmp = bd->ctrl; + if ( *status ) { + bd->ctrl = *status; + } + *status = tmp; + } + /* READ/WRITE Data-Pointer word */ + if ( dptr ) { + tmp = bd->dptr; + if ( dataptr ) { + bd->dptr = dataptr; + } + *dptr = (uint16_t *)tmp; + } + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_irq_err + ( + void *rt, + gr1553rt_irqerr_t func, + void *data + ) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + priv->irq_err.func = func; + priv->irq_err.data = data; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_irq_mc + ( + void *rt, + gr1553rt_irqmc_t func, + void *data + ) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + priv->irq_mc.func = func; + priv->irq_mc.data = data; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_irq_sa + ( + void *rt, + int subadr, + int tx, + gr1553rt_irq_t func, + void *data + ) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + if ( tx ) { + priv->irq_tx[subadr].func = func; + priv->irq_tx[subadr].data = data; + } else { + priv->irq_rx[subadr].func = func; + priv->irq_rx[subadr].data = data; + } + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +/* GR1553-RT Interrupt Service Routine */ +void gr1553rt_isr(void *data) +{ + struct gr1553rt_priv *priv = data; + unsigned int firstirq, lastpos; + int index; + unsigned int *last, *curr, entry, hwbd; + int type, samc, mcode, subadr; + int listid; + struct gr1553rt_irq *isr; + struct gr1553rt_irqerr *isrerr; + struct gr1553rt_irqmc *isrmc; + unsigned int irq; + + /* Ack IRQ before reading current write pointer, but after + * reading current IRQ pointer. This is because RT_EVIRQ + * may be updated after we ACK the IRQ source. + */ + irq = priv->regs->irq & + (GR1553B_IRQ_RTTE|GR1553B_IRQ_RTD|GR1553B_IRQ_RTEV); + if ( irq == 0 ) + return; + + firstirq = priv->regs->rt_evirq; + priv->regs->irq = irq; + lastpos = priv->regs->rt_evlog; + + /* Quit if nothing has been added to the log */ + if ( lastpos == firstirq ) + return; + + if ( irq & (GR1553B_IRQ_RTTE|GR1553B_IRQ_RTD) ) { + isrerr = &priv->irq_err; + if ( isrerr->func ) { + isrerr->func(irq, isrerr->data); + } + + /* Stop Hardware and enter non-started mode. This will + * make all future calls to driver result in an error. + */ + gr1553rt_stop(priv); + } + + /* Step between first log entry causing an IRQ to last + * entry. Each entry that has caused an IRQ will be handled + * by calling user-defined function. + * + * We convert hardware addresses into CPU accessable addresses + * first. + */ + index = (firstirq - (unsigned int)priv->evlog_hw_base) / + sizeof(unsigned int); + curr = priv->evlog_cpu_base + index; + index = (lastpos - (unsigned int)priv->evlog_hw_base) / + sizeof(unsigned int); + last = priv->evlog_cpu_base + index; + + do { + /* Process one entry */ + entry = *curr; + + if ( entry & 0x80000000 ) { + /* Entry caused IRQ */ + type = (entry >> 29) & 0x3; + samc = (entry >> 24) & 0x1f; + if ( (type & 0x2) == 0 ) { + /* Transmit/Receive Data */ + subadr = samc; + if ( type ) { + /* Receive */ + listid = priv->subadrs[subadr].rxlistid; + hwbd = priv->sas_cpu[subadr].rxptr; + isr = &priv->irq_rx[subadr]; + } else { + /* Transmit */ + listid = priv->subadrs[subadr].txlistid; + hwbd = priv->sas_cpu[subadr].txptr; + isr = &priv->irq_tx[subadr]; + } + + index = ((unsigned int)hwbd - (unsigned int) + priv->bds_hw)/sizeof(struct gr1553rt_bd); + + /* Call user ISR of RX/TX transfer */ + if ( isr->func ) { + isr->func( + priv->lists[listid], + entry, + priv->swbds[index].this_next, + isr->data + ); + } + } else if ( type == 0x2) { + /* Modecode */ + mcode = samc; + isrmc = &priv->irq_mc; + + /* Call user ISR of ModeCodes RX/TX */ + if ( isrmc->func ) { + isrmc->func( + mcode, + entry, + isrmc->data + ); + } + } else { + /* ERROR OF SOME KIND, EVLOG OVERWRITTEN? */ + exit(-1); + } + } + + /* Calc next entry posistion */ + curr++; + if ( curr == priv->evlog_cpu_end ) + curr = priv->evlog_cpu_base; + + } while ( curr != last ); +} + +int gr1553rt_indication(void *rt, int subadr, int *txeno, int *rxeno) +{ + struct gr1553rt_priv *priv = rt; + struct gr1553rt_sa *sa; + unsigned int bd, index; + + /* Sub address valid */ + if ( (subadr < 0) || (subadr > 31) ) + return -1; + + /* Get SubAddress Descriptor address as accessed from CPU */ + sa = &priv->sas_cpu[subadr]; + + /* Indication of TX descriptor? */ + if ( txeno ) { + bd = sa->txptr; + /* Get Index of Hardware BD */ + index = ((unsigned int)bd - (unsigned int)&priv->bds_hw[0]) / + sizeof(struct gr1553rt_bd); + *txeno = priv->swbds[index].this_next; + } + + /* Indication of RX descriptor? */ + if ( rxeno ) { + bd = sa->rxptr; + /* Get Index of Hardware BD */ + index = ((unsigned int)bd - (unsigned int)&priv->bds_hw[0]) / + sizeof(struct gr1553rt_bd); + *rxeno = priv->swbds[index].this_next; + } + + return 0; +} + +#if 0 +int gr1553rt_bd_irq( + struct gr1553rt_list *list, + unsigned short entry_no, + void (*func)(struct gr1553rt_list *list, int entry, void *data), + void *data + ) +{ + struct gr1553rt_priv *priv = list->rt; + int irqid; + int ret; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( entry_no == 0xffff ) { + /* Default interrupt for all list entries without + * assigned IRQ function. + */ + list->irqs[0].func = func; + list->irqs[0].data = data; + return 0; + } + + if ( entry_no >= list->bd_cnt ) { + return -1; + } + + bdid = list->bds[entry_no]; + irqid = priv->swbds[bdid].irqid; + + ret = 0; + IRQ_GLOBAL_DISABLE(oldLevel); + if ( (irqid != 0) && (func == 0) ) { + /* Unassign IRQ function */ + list->irqs[irqid].func = NULL; + list->irqs[irqid].data = NULL; + irqid = 0; /* Disable IRQ (default handler) */ + } else if ( priv->swbds[bdid].irqid != 0 ) { + /* reassign IRQ function */ + list->irqs[irqid].func = func; + list->irqs[irqid].data = data; + } else { + /* Find free IRQ spot. If no free irqid=0 (general IRQ) */ + ret = -1; + for (i=0; i<list->cfg->maxirq; i++) { + if ( list->irqs[i].func == NULL ) { + irqid = i; + list->irqs[i].func = func; + list->irqs[i].data = data; + ret = 0; + break; + } + } + } + priv->swbds[bdid].irqid = irqid; + IRQ_GLOBAL_ENABLE(oldLevel); + + return ret; +} +#endif + +void gr1553rt_hw_stop(struct gr1553rt_priv *priv); + +void gr1553rt_register(void) +{ + /* The RT driver rely on the GR1553B Driver */ + gr1553_register(); +} + +void *gr1553rt_open(int minor) +{ + struct drvmgr_dev **pdev = NULL; + struct gr1553rt_priv *priv = NULL; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + + /* Allocate requested device */ + pdev = gr1553_rt_open(minor); + if ( pdev == NULL ) + goto fail; + + priv = malloc(sizeof(struct gr1553rt_priv)); + if ( priv == NULL ) + goto fail; + memset(priv, 0, sizeof(struct gr1553rt_priv)); + + /* Assign a device private to RT device */ + priv->pdev = pdev; + (*pdev)->priv = priv; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)(*pdev)->businfo; + pnpinfo = &ambadev->info; + priv->regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start; + + /* Start with default configuration */ + /*priv->cfg = gr1553rt_default_config;*/ + + /* Unmask IRQs and so */ + gr1553rt_hw_stop(priv); + + /* Register ISR handler. hardware mask IRQ, so it is safe to unmask + * at IRQ controller. + */ + if (drvmgr_interrupt_register(*priv->pdev, 0, "gr1553rt", gr1553rt_isr, priv)) + goto fail; + + return priv; + +fail: + if ( pdev ) + gr1553_rt_close(pdev); + if ( priv ) + free(priv); + return NULL; +} + +void gr1553rt_close(void *rt) +{ + struct gr1553rt_priv *priv = rt; + + if ( priv->started ) { + gr1553rt_stop(priv); + } + + /* Remove ISR handler */ + drvmgr_interrupt_unregister(*priv->pdev, 0, gr1553rt_isr, priv); + + /* Free dynamically allocated buffers if any */ + gr1553rt_sw_free(priv); + + /* Return RT/BC device */ + gr1553_rt_close(priv->pdev); +} + +/* Stop Hardware and disable IRQ */ +void gr1553rt_hw_stop(struct gr1553rt_priv *priv) +{ + uint32_t irqmask; + + /* Disable RT */ + GR1553RT_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY); + + /* Stop BC if not already stopped: BC can not be used simultaneously + * as the RT anyway + */ + GR1553RT_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204); + + /* Turn off RT IRQ generation */ + irqmask=GR1553RT_READ_REG(&priv->regs->imask); + irqmask&=~(GR1553B_IRQEN_RTEVE|GR1553B_IRQEN_RTDE); + GR1553RT_WRITE_REG(&priv->regs->irq, irqmask); +} + +/* Free dynamically allocated buffers, if any */ +void gr1553rt_sw_free(struct gr1553rt_priv *priv) +{ + /* Event log */ + if ( (priv->cfg.evlog_buffer == NULL) && priv->evlog_buffer ) { + free(priv->evlog_buffer); + priv->evlog_buffer = NULL; + } + + /* RX/TX Descriptors */ + if ( (priv->cfg.bd_buffer == NULL) && priv->bd_buffer ) { + free(priv->bd_buffer); + priv->bd_buffer = NULL; + } + +#if (RTBD_MAX == 0) + if ( priv->swbds ) { + free(priv->swbds); + priv->swbds = NULL; + } +#endif + + /* Sub address table */ + if ( (priv->cfg.satab_buffer == NULL) && priv->satab_buffer ) { + free(priv->satab_buffer); + priv->satab_buffer = NULL; + } +} + +/* Free dynamically allocated buffers, if any */ +int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) +{ + int size; + + /* Allocate Event log */ + if ((unsigned int)priv->cfg.evlog_buffer & 1) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + priv->evlog_hw_base = (unsigned int *) + ((unsigned int)priv->cfg.evlog_buffer & ~0x1); + priv->evlog_buffer = priv->cfg.evlog_buffer; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->evlog_hw_base, + (void **)&priv->evlog_cpu_base, + priv->cfg.evlog_size + ); + } else { + if (priv->cfg.evlog_buffer == NULL) { + priv->evlog_buffer = malloc(priv->cfg.evlog_size * 2); + if (priv->evlog_buffer == NULL) + return -1; + } else { + /* Addess already CPU-LOCAL */ + priv->evlog_buffer = priv->cfg.evlog_buffer; + } + /* Align to SIZE bytes boundary */ + priv->evlog_cpu_base = (unsigned int *) + (((unsigned int)priv->evlog_buffer + + (priv->cfg.evlog_size-1)) & ~(priv->cfg.evlog_size-1)); + + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->evlog_cpu_base, + (void **)&priv->evlog_hw_base, + priv->cfg.evlog_size + ); + } + priv->evlog_cpu_end = priv->evlog_cpu_base + + priv->cfg.evlog_size/sizeof(unsigned int *); + + /* Allocate Transfer Descriptors */ + priv->bds_cnt = priv->cfg.bd_count; + size = priv->bds_cnt * sizeof(struct gr1553rt_bd); + if ((unsigned int)priv->cfg.bd_buffer & 1) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + priv->bds_hw = (unsigned int)priv->cfg.bd_buffer & ~0x1; + priv->bd_buffer = priv->cfg.bd_buffer; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->bds_hw, + (void **)&priv->bds_cpu, + size + ); + } else { + if ( priv->cfg.bd_buffer == NULL ) { + priv->bd_buffer = malloc(size + 0xf); + if (priv->bd_buffer == NULL) + return -1; + } else { + /* Addess already CPU-LOCAL */ + priv->bd_buffer = priv->cfg.bd_buffer; + } + /* Align to 16 bytes boundary */ + priv->bds_cpu = (struct gr1553rt_bd *) + (((unsigned int)priv->bd_buffer + 0xf) & ~0xf); + + /* Translate from CPU address to hardware address */ + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->bds_cpu, + (void **)&priv->bds_hw, + size + ); + } + +#if (RTBD_MAX == 0) + /* Allocate software description of */ + priv->swbds = malloc(priv->cfg.bd_count * sizeof(struct gr1553rt_sw_bd)); + if ( priv->swbds == NULL ) { + return -1; + } +#endif + + /* Allocate Sub address table */ + if ((unsigned int)priv->cfg.satab_buffer & 1) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + priv->sas_hw = (unsigned int)priv->cfg.satab_buffer & ~0x1; + priv->satab_buffer = priv->cfg.satab_buffer; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->sas_hw, + (void **)&priv->sas_cpu, + 16 * 32); + } else { + if (priv->cfg.satab_buffer == NULL) { + priv->satab_buffer = malloc((16 * 32) * 2); + if (priv->satab_buffer == NULL) + return -1; + } else { + /* Addess already CPU-LOCAL */ + priv->satab_buffer = priv->cfg.satab_buffer; + } + /* Align to 512 bytes boundary */ + priv->sas_cpu = (struct gr1553rt_sa *) + (((unsigned int)priv->satab_buffer + 0x1ff) & + ~0x1ff); + + /* Translate Address from CPU-LOCAL to HARDWARE (REMOTE) */ + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->sas_cpu, + (void **)&priv->sas_hw, + 16 * 32); + } + + return 0; +} + +void gr1553rt_sw_init(struct gr1553rt_priv *priv) +{ + int i; + + /* Clear Sub Address table */ + memset(priv->sas_cpu, 0, 512); + + /* Clear Transfer descriptors */ + memset(priv->bds_cpu, 0, priv->bds_cnt * 16); + + /* Clear the Event log */ + memset(priv->evlog_cpu_base, 0, priv->cfg.evlog_size); + + /* Init descriptor allocation algorithm */ + gr1553rt_bd_alloc_init(priv, priv->bds_cnt); + + /* Init table used to convert from sub address to list. + * Currently non assigned. + */ + for (i=0; i<32; i++) { + priv->subadrs[i].rxlistid = 0xff; + priv->subadrs[i].txlistid = 0xff; + } + + /* Clear all previous IRQ handlers */ + for (i=0; i<32; i++) { + priv->irq_rx[i].func = NULL; + priv->irq_tx[i].data = NULL; + } + priv->irq_err.func = NULL; + priv->irq_err.data = NULL; + priv->irq_mc.func = NULL; + priv->irq_mc.data = NULL; + + /* Clear LIST to LISTID table */ + for (i=0; i<RTLISTID_MAX; i++) { + priv->lists[i] = NULL; + } +} + +int gr1553rt_config(void *rt, struct gr1553rt_cfg *cfg) +{ + struct gr1553rt_priv *priv = rt; + + if ( priv->started ) + return -1; + + /*** Free dynamically allocated buffers ***/ + + gr1553rt_sw_free(priv); + + /*** Check new config ***/ + if ( cfg->rtaddress > 30 ) + return -1; + if ( (cfg->evlog_size & (cfg->evlog_size-1)) != 0) + return -1; /* SIZE: Not aligned to a power of 2 */ + if ( ((unsigned int)priv->cfg.evlog_buffer & (cfg->evlog_size-1)) != 0 ) + return -1; /* Buffer: Not aligned to size */ +#if (RTBD_MAX > 0) + if ( cfg->bd_count > RTBD_MAX ) + return -1; +#endif + + /*** Make new config current ***/ + priv->cfg = *cfg; + + /*** Adapt to new config ***/ + + if ( gr1553rt_sw_alloc(priv) != 0 ) + return -1; + + gr1553rt_sw_init(priv); + + return 0; +} + +int gr1553rt_start(void *rt) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( priv->started ) + return -1; + + /*** Initialize software Pointers and stuff ***/ + + if ( !priv->satab_buffer || !priv->bd_buffer || !priv->evlog_buffer ) + return -2; + + priv->evlog_cpu_next = priv->evlog_cpu_base; + + /*** Initialize Registers ***/ + + /* Subaddress table base */ + priv->regs->rt_tab = (unsigned int)priv->sas_hw; + + /* Mode code configuration */ + priv->regs->rt_mcctrl = priv->cfg.modecode; + + /* RT Time Tag resolution */ + priv->regs->rt_ttag = priv->cfg.time_res << 16; + + /* Event LOG base and size */ + priv->regs->rt_evsz = ~(priv->cfg.evlog_size - 1); + priv->regs->rt_evlog = (unsigned int)priv->evlog_hw_base; + priv->regs->rt_evirq = 0; + + /* Clear and old IRQ flag and Enable IRQ */ + IRQ_GLOBAL_DISABLE(oldLevel); + priv->regs->irq = GR1553B_IRQ_RTEV|GR1553B_IRQ_RTD|GR1553B_IRQ_RTTE; + priv->regs->imask |= GR1553B_IRQEN_RTEVE | GR1553B_IRQEN_RTDE | + GR1553B_IRQEN_RTTEE; + + /* Enable and Set RT address */ + priv->regs->rt_cfg = GR1553RT_KEY | + (priv->cfg.rtaddress << GR1553B_RT_CFG_RTADDR_BIT) | + GR1553B_RT_CFG_RTEN; + + /* Tell software RT is started */ + priv->started = 1; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +void gr1553rt_stop(void *rt) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + + /* Stop Hardware */ + gr1553rt_hw_stop(priv); + + /* Software state */ + priv->started = 0; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +void gr1553rt_sa_schedule( + void *rt, + int subadr, + int tx, + struct gr1553rt_list *list + ) +{ + struct gr1553rt_priv *priv = rt; + unsigned short bdid; + struct gr1553rt_bd *bd; + + if ( !list || (list->listid == -1) ) + return; + + /* Get Hardware address of first descriptor in list */ + bdid = list->bds[0]; + if ( bdid == 0xffff ) + return; + bd = &priv->bds_hw[bdid]; + + list->subadr = subadr; + + /* Update Sub address table */ + if ( tx ) { + list->subadr |= 0x100; + priv->subadrs[subadr].txlistid = list->listid; + priv->sas_cpu[subadr].txptr = (unsigned int)bd; + } else { + priv->subadrs[subadr].rxlistid = list->listid; + priv->sas_cpu[subadr].rxptr = (unsigned int)bd; + } +} + +void gr1553rt_sa_setopts( + void *rt, + int subadr, + unsigned int mask, + unsigned int options + ) +{ + struct gr1553rt_priv *priv = rt; + unsigned int ctrl; + + if ( (subadr > 31) || (priv->sas_cpu == NULL) ) + return; + + ctrl = priv->sas_cpu[subadr].ctrl; + priv->sas_cpu[subadr].ctrl = (ctrl & ~mask) | options; +} + +void gr1553rt_set_vecword(void *rt, unsigned int mask, unsigned int words) +{ + struct gr1553rt_priv *priv = rt; + unsigned int vword; + + if ( mask == 0 ) + return; + + vword = priv->regs->rt_statw; + + priv->regs->rt_statw = (vword & ~mask) | (words & mask); +} + +void gr1553rt_set_bussts(void *rt, unsigned int mask, unsigned int sts) +{ + struct gr1553rt_priv *priv = rt; + unsigned int stat; + + stat = priv->regs->rt_stat2; + priv->regs->rt_stat2 = (stat & ~mask) | (mask & sts); +} + +void gr1553rt_status(void *rt, struct gr1553rt_status *status) +{ + struct gr1553rt_priv *priv = rt; + struct gr1553b_regs *regs = priv->regs; + unsigned int tmp; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + status->status = regs->rt_stat; + status->bus_status = regs->rt_stat2; + + tmp = regs->rt_sync; + status->synctime = tmp >> 16; + status->syncword = tmp & 0xffff; + + tmp = regs->rt_ttag; + status->time_res = tmp >> 16; + status->time = tmp & 0xffff; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +void gr1553rt_list_sa(struct gr1553rt_list *list, int *subadr, int *tx) +{ + int sa, trt; + + if ( list->subadr == -1 ) { + sa = -1; + trt = -1; + } else { + sa = list->subadr & 0xff; + trt = (list->subadr & 0x100) >> 8; + } + + if ( subadr ) + *subadr = sa; + if ( tx ) + *tx = trt; +} + +int gr1553rt_evlog_read(void *rt, unsigned int *dst, int max) +{ + struct gr1553rt_priv *priv = rt; + int cnt, top, bot, left; + unsigned int *hwpos; + + /* Get address of hardware's current working entry */ + hwpos = (unsigned int *)priv->regs->rt_evlog; + + /* Convert into CPU address */ + hwpos = (unsigned int *) + ((unsigned int)hwpos - (unsigned int)priv->evlog_hw_base + + (unsigned int)priv->evlog_cpu_base); + + if ( priv->evlog_cpu_next == hwpos ) + return 0; + + if ( priv->evlog_cpu_next > hwpos ) { + top = (unsigned int)priv->evlog_cpu_end - + (unsigned int)priv->evlog_cpu_next; + bot = (unsigned int)hwpos - (unsigned int)priv->evlog_cpu_base; + } else { + top = (unsigned int)hwpos - (unsigned int)priv->evlog_cpu_next; + bot = 0; + } + top = top / 4; + bot = bot / 4; + + left = max; + if ( top > 0 ) { + if ( top > left ) { + cnt = left; + } else { + cnt = top; + } + memcpy(dst, priv->evlog_cpu_next, cnt*4); + dst += cnt; + left -= cnt; + } + + if ( (bot > 0) && (left > 0) ) { + if ( bot > left ) { + cnt = left; + } else { + cnt = bot; + } + memcpy(dst, priv->evlog_cpu_base, cnt*4); + left -= cnt; + } + + cnt = max - left; + priv->evlog_cpu_next += cnt; + if ( priv->evlog_cpu_next >= priv->evlog_cpu_end ) { + priv->evlog_cpu_next = (unsigned int *) + ((unsigned int)priv->evlog_cpu_base + + ((unsigned int)priv->evlog_cpu_next - + (unsigned int)priv->evlog_cpu_end )); + } + + return max - left; +} diff --git a/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c b/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c new file mode 100644 index 0000000000..0fe96cd26f --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/amba/ahbstat.c @@ -0,0 +1,206 @@ +/* AHB Status register driver + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <stdint.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#include <ahbstat.h> + +void ahbstat_isr(void *arg); + +/* AHB fail interrupt callback to user. This function is declared weak so that + * the user can define a function pointer variable containing the address + * responsible for handling errors + * + * minor Index of AHBSTAT hardware + * regs Register address of AHBSTAT + * status AHBSTAT status register at IRQ + * failing_address AHBSTAT Failing address register at IRQ + * + * * User return + * 0: print error onto terminal with printk and reenable AHBSTAT + * 1: just re-enable AHBSTAT + * 2: just print error + * 3: do nothing, let user do custom handling + */ +int (*ahbstat_error)( + int minor, + struct ahbstat_regs *regs, + uint32_t status, + uint32_t failing_address + ) __attribute__((weak)) = NULL; + +#define AHBSTAT_STS_CE_BIT 9 +#define AHBSTAT_STS_NE_BIT 8 +#define AHBSTAT_STS_HW_BIT 7 +#define AHBSTAT_STS_HM_BIT 3 +#define AHBSTAT_STS_HS_BIT 0 + +#define AHBSTAT_STS_CE (1 << AHBSTAT_STS_CE_BIT) +#define AHBSTAT_STS_NE (1 << AHBSTAT_STS_NE_BIT) +#define AHBSTAT_STS_HW (1 << AHBSTAT_STS_HW_BIT) +#define AHBSTAT_STS_HM (0xf << AHBSTAT_STS_HM_BIT) +#define AHBSTAT_STS_HS (0x7 << AHBSTAT_STS_HS_BIT) + +struct ahbstat_priv { + struct drvmgr_dev *dev; + struct ahbstat_regs *regs; + int minor; + uint32_t last_status; + uint32_t last_address; +}; + +int ahbstat_init2(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops ahbstat_ops = +{ + .init = {NULL, ahbstat_init2, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id ahbstat_ids[] = +{ + {VENDOR_GAISLER, GAISLER_AHBSTAT}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info ahbstat_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_AHBSTAT_ID,/* Driver ID */ + "AHBSTAT_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &ahbstat_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct ahbstat_priv), + }, + &ahbstat_ids[0] +}; + +void ahbstat_register_drv (void) +{ + drvmgr_drv_register(&ahbstat_drv_info.general); +} + +int ahbstat_init2(struct drvmgr_dev *dev) +{ + struct ahbstat_priv *priv; + struct amba_dev_info *ambadev; + + priv = dev->priv; + if (!priv) + return DRVMGR_NOMEM; + priv->dev = dev; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if (ambadev == NULL) + return DRVMGR_FAIL; + priv->regs = (struct ahbstat_regs *)ambadev->info.apb_slv->start; + priv->minor = dev->minor_drv; + + /* Initialize hardware */ + priv->regs->status = 0; + + /* Install IRQ handler */ + drvmgr_interrupt_register(dev, 0, "ahbstat", ahbstat_isr, priv); + + return DRVMGR_OK; +} + +void ahbstat_isr(void *arg) +{ + struct ahbstat_priv *priv = arg; + uint32_t fadr, status; + int rc; + + /* Get hardware status */ + status = priv->regs->status; + if ((status & AHBSTAT_STS_NE) == 0) + return; + + /* IRQ generated by AHBSTAT core... handle it here */ + + /* Get Failing address */ + fadr = priv->regs->failing; + + priv->last_status = status; + priv->last_address = fadr; + + /* Let user handle error, default to print the error and reenable HW + * + * User return + * 0: print error and reenable AHBSTAT + * 1: just reenable AHBSTAT + * 2: just print error reenable + * 3: do nothing + */ + rc = 0; + if (ahbstat_error != NULL) + rc = ahbstat_error(priv->minor, priv->regs, status, fadr); + + if ((rc & 0x1) == 0) { + printk("\n### AHBSTAT: %s %s error of size %lu by master %d" + " at 0x%08lx\n", + status & AHBSTAT_STS_CE ? "single" : "non-correctable", + status & AHBSTAT_STS_HW ? "write" : "read", + (status & AHBSTAT_STS_HS) >> AHBSTAT_STS_HS_BIT, + (status & AHBSTAT_STS_HM) >> AHBSTAT_STS_HM_BIT, + fadr); + } + + if ((rc & 0x2) == 0) { + /* Trigger new interrupts */ + priv->regs->status = 0; + } +} + +/* Get Last received AHB Error + * + * Return + * 0: No error received + * 1: Error Received, last status and address stored into argument pointers + * -1: No such AHBSTAT device + */ +int ahbstat_last_error(int minor, uint32_t *status, uint32_t *address) +{ + struct drvmgr_dev *dev; + struct ahbstat_priv *priv; + + if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) { + return -1; + } + priv = (struct ahbstat_priv *)dev->priv; + + *status = priv->last_status; + *address = priv->last_address; + + return (priv->last_status & AHBSTAT_STS_NE) >> AHBSTAT_STS_NE_BIT; +} + +/* Get AHBSTAT registers address from minor. NULL returned if no such device */ +struct ahbstat_regs *ahbstat_get_regs(int minor) +{ + struct drvmgr_dev *dev; + struct ahbstat_priv *priv; + + if (drvmgr_get_dev(&ahbstat_drv_info.general, minor, &dev)) { + return NULL; + } + priv = (struct ahbstat_priv *)dev->priv; + + return priv->regs; +} diff --git a/c/src/lib/libbsp/sparc/shared/analog/gradcdac.c b/c/src/lib/libbsp/sparc/shared/analog/gradcdac.c new file mode 100644 index 0000000000..4b8e636666 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/analog/gradcdac.c @@ -0,0 +1,578 @@ +/* ADC / DAC (GRADCDAC) interface implementation + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <rtems.h> +#include <stdlib.h> +#include <stdio.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <gradcdac.h> + +/****************** DEBUG Definitions ********************/ +#define DBG_IOCTRL 1 +#define DBG_TX 2 +#define DBG_RX 4 + +#define DEBUG_FLAGS (DBG_IOCTRL | DBG_RX | DBG_TX ) +/* Uncomment for debug output */ +/* +#define DEBUG +#define DEBUGFUNCS +*/ +#include <debug_defs.h> + +struct gradcdac_priv { + struct gradcdac_regs *regs; /* Must be first */ + struct drvmgr_dev *dev; + char devName[48]; + + unsigned int freq; + int irqno; + int minor; + + void (*isr_adc)(void *cookie, void *arg); + void (*isr_dac)(void *cookie, void *arg); + void *isr_adc_arg; + void *isr_dac_arg; + + int open; +}; + +/* Global variables */ + +/* Print Info routines */ +void gradcdac_print(void *cookie); + +int gradcdac_init2(struct drvmgr_dev *dev); +int gradcdac_init3(struct drvmgr_dev *dev); +int gradcadc_device_init(struct gradcdac_priv *pDev); +void gradcdac_adc_interrupt(void *arg); +void gradcdac_dac_interrupt(void *arg); + +struct drvmgr_drv_ops gradcdac_ops = +{ + .init = {NULL, gradcdac_init2, gradcdac_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id gradcdac_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRADCDAC}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info gradcdac_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRADCDAC_ID, /* Driver ID */ + "GRADCDAC_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &gradcdac_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gradcdac_ids[0] +}; + +void gradcdac_register_drv (void) +{ + DBG("Registering GRADCDAC driver\n"); + drvmgr_drv_register(&gradcdac_drv_info.general); +} + +int gradcdac_init2(struct drvmgr_dev *dev) +{ + struct gradcdac_priv *priv; + + DBG("GRADCDAC[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + priv = dev->priv = malloc(sizeof(struct gradcdac_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + + +int gradcdac_init3(struct drvmgr_dev *dev) +{ + struct gradcdac_priv *priv = dev->priv; + char prefix[32]; + + if ( !priv ) + return DRVMGR_FAIL; + + if ( gradcadc_device_init(priv) ) { + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/gradcdac%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sgradcdac%d", prefix, dev->minor_bus); + } + + return DRVMGR_OK; +} + +void gradcdac_print_dev(struct gradcdac_priv *pDev) +{ + printf("======= GRADCDAC %p =======\n", pDev->regs); + printf(" Minor: %d\n", pDev->minor); + printf(" Dev Name: %s\n", pDev->devName); + printf(" RegBase: %p\n", pDev->regs); + printf(" IRQ: %d and %d\n", pDev->irqno, pDev->irqno+1); + printf(" Core Freq: %d kHz\n", pDev->freq / 1000); + printf(" Opened: %s\n", pDev->open ? "YES" : "NO"); + + printf(" CONFIG: 0x%x\n", pDev->regs->config); + printf(" STATUS: 0x%x\n", pDev->regs->status); +} + +void gradcdac_print(void *cookie) +{ + struct drvmgr_dev *dev; + struct gradcdac_priv *pDev; + + if ( cookie ) { + gradcdac_print_dev(cookie); + return; + } + + /* Show all */ + dev = gradcdac_drv_info.general.dev; + while (dev) { + pDev = (struct gradcdac_priv *)dev->priv; + gradcdac_print_dev(pDev); + dev = dev->next_in_drv; + } +} + +void gradcdac_hw_reset(struct gradcdac_regs *regs) +{ + /* Reset core */ + regs->config = 0; + regs->adrdir = 0; + regs->adrout = 0; + regs->data_dir = 0; + regs->data_out = 0; +} + +/* Device initialization called once on startup */ +int gradcadc_device_init(struct gradcdac_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)pDev->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + pDev->irqno = pnpinfo->irq; + pDev->regs = (struct gradcdac_regs *)pnpinfo->apb_slv->start; + pDev->minor = pDev->dev->minor_drv; + + /* Reset Hardware before attaching IRQ handler */ + gradcdac_hw_reset(pDev->regs); + + pDev->open = 0; + + /* Get frequency in Hz */ + if ( drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &pDev->freq) ) { + return -1; + } + + DBG("GRADCDAC frequency: %d Hz\n", pDev->freq); + + return 0; +} + +void gradcdac_dac_interrupt(void *arg) +{ + struct gradcdac_priv *pDev = arg; + if ( pDev->isr_dac ) + pDev->isr_dac(pDev, pDev->isr_dac_arg); +} + +void gradcdac_adc_interrupt(void *arg) +{ + struct gradcdac_priv *pDev = arg; + if ( pDev->isr_adc ) + pDev->isr_adc(pDev, pDev->isr_adc_arg); +} + +void *gradcdac_open(char *devname) +{ + struct gradcdac_priv *pDev; + struct drvmgr_dev *dev; + + /* Find device by name */ + dev = gradcdac_drv_info.general.dev; + while ( dev ) { + pDev = (struct gradcdac_priv *)dev->priv; + if ( pDev ) { + if ( strncmp(pDev->devName, devname, sizeof(pDev->devName)) == 0 ) { + /* Found matching device name */ + break; + } + } + dev = dev->next_in_drv; + } + + if ( !dev ) + return NULL; + + /* is device busy/taken? */ + if ( pDev->open ) + return NULL; + + /* Mark device taken */ + pDev->open = 1; + + return pDev; +} + +void gradcdac_set_config(void *cookie, struct gradcdac_config *cfg) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int config=0; + + config = (cfg->dac_ws<<GRADCDAC_CFG_DACWS_BIT)&GRADCDAC_CFG_DACWS; + + if ( cfg->wr_pol ) + config |= GRADCDAC_CFG_WRPOL; + + config |= (cfg->dac_dw<<GRADCDAC_CFG_DACDW_BIT)&GRADCDAC_CFG_DACDW; + + config |= (cfg->adc_ws<<GRADCDAC_CFG_ADCWS_BIT)&GRADCDAC_CFG_ADCWS; + + if ( cfg->rc_pol ) + config |= GRADCDAC_CFG_RCPOL; + + config |= (cfg->cs_mode<<GRADCDAC_CFG_CSMODE_BIT)&GRADCDAC_CFG_CSMODE; + + if ( cfg->cs_pol ) + config |= GRADCDAC_CFG_CSPOL; + + if ( cfg->ready_mode ) + config |= GRADCDAC_CFG_RDYMODE; + + if ( cfg->ready_pol ) + config |= GRADCDAC_CFG_RDYPOL; + + if ( cfg->trigg_pol ) + config |= GRADCDAC_CFG_TRIGPOL; + + config |= (cfg->trigg_mode<<GRADCDAC_CFG_TRIGMODE_BIT)&GRADCDAC_CFG_TRIGMODE; + + config |= (cfg->adc_dw<<GRADCDAC_CFG_ADCDW_BIT)&GRADCDAC_CFG_ADCDW; + + /* Write config */ + pDev->regs->config = config; +} + +void gradcdac_get_config(void *cookie, struct gradcdac_config *cfg) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int config; + + if ( !cfg ) + return; + + /* Get config */ + config = pDev->regs->config; + + cfg->dac_ws = (config&GRADCDAC_CFG_DACWS)>>GRADCDAC_CFG_DACWS_BIT; + + cfg->wr_pol = (config&GRADCDAC_CFG_WRPOL)>>GRADCDAC_CFG_WRPOL_BIT; + + cfg->dac_dw = (config&GRADCDAC_CFG_DACDW)>>GRADCDAC_CFG_DACDW_BIT; + + cfg->adc_ws = (config&GRADCDAC_CFG_ADCWS)>>GRADCDAC_CFG_ADCWS_BIT; + + cfg->rc_pol = (config&GRADCDAC_CFG_RCPOL)>>GRADCDAC_CFG_RCPOL_BIT; + + cfg->cs_mode = (config&GRADCDAC_CFG_CSMODE)>>GRADCDAC_CFG_CSMODE_BIT; + + cfg->cs_pol = (config&GRADCDAC_CFG_CSPOL)>>GRADCDAC_CFG_CSPOL_BIT; + + cfg->ready_mode = (config&GRADCDAC_CFG_RDYMODE)>>GRADCDAC_CFG_RDYMODE_BIT; + + cfg->ready_pol = (config&GRADCDAC_CFG_RDYPOL)>>GRADCDAC_CFG_RDYPOL_BIT; + + cfg->trigg_pol = (config&GRADCDAC_CFG_TRIGPOL)>>GRADCDAC_CFG_TRIGPOL_BIT; + + cfg->trigg_mode = (config&GRADCDAC_CFG_TRIGMODE)>>GRADCDAC_CFG_TRIGMODE_BIT; + + cfg->adc_dw = (config&GRADCDAC_CFG_ADCDW)>>GRADCDAC_CFG_ADCDW_BIT; +} + +void gradcdac_set_cfg(void *cookie, unsigned int config) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->config = config; +} + +unsigned int gradcdac_get_cfg(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->config; +} + +unsigned int gradcdac_get_status(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->status; +} + +/* Install IRQ handler for ADC and/or DAC interrupt. + * The installed IRQ handler(ISR) must read the status + * register to clear the pending interrupt avoiding multiple + * entries to the ISR caused by the same IRQ. + * + * \param adc 1=ADC interrupt, 2=ADC interrupt, 3=ADC and DAC interrupt + * \param isr Interrupt service routine called when IRQ is fired + * \param arg custom argument passed to ISR when called. + */ +int gradcdac_install_irq_handler(void *cookie, int adc, void (*isr)(void *cookie, void *arg), void *arg) +{ + struct gradcdac_priv *pDev = cookie; + + if ( (adc > 3) || !adc ) + return -1; + + if ( adc & GRADCDAC_ISR_ADC ){ + pDev->isr_adc_arg = arg; + pDev->isr_adc = isr; + drvmgr_interrupt_register(pDev->dev, GRADCDAC_IRQ_ADC, "gradcdac_adc", gradcdac_adc_interrupt, pDev); + } + + if ( adc & GRADCDAC_ISR_DAC ){ + pDev->isr_dac_arg = arg; + pDev->isr_dac = isr; + drvmgr_interrupt_register(pDev->dev, GRADCDAC_IRQ_DAC, "gradcdac_dac", gradcdac_dac_interrupt, pDev); + } + + return 0; +} + +void gradcdac_uninstall_irq_handler(void *cookie, int adc) +{ + struct gradcdac_priv *pDev = cookie; + + if ( (adc > 3) || !adc ) + return; + + if ( adc & GRADCDAC_ISR_ADC ){ + drvmgr_interrupt_unregister(pDev->dev, GRADCDAC_IRQ_ADC, gradcdac_adc_interrupt, pDev); + pDev->isr_adc = NULL; + pDev->isr_adc_arg = NULL; + } + + if ( adc & GRADCDAC_ISR_DAC ){ + drvmgr_interrupt_unregister(pDev->dev, GRADCDAC_IRQ_DAC, gradcdac_dac_interrupt, pDev); + pDev->isr_dac = NULL; + pDev->isr_dac_arg = NULL; + } +} + +/* Make the ADC circuitry initialize a analogue to digital + * conversion. The result can be read out by gradcdac_adc_convert_try + * or gradcdac_adc_convert. + */ +void gradcdac_adc_convert_start(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + + /* Write to ADC Data Input register to start a conversion */ + pDev->regs->adc_din = 0; +} + +/* Tries to read the conversion result. If the circuitry is busy + * converting the function return a non-zero value, if the conversion + * has successfully finished the function return zero. + * + * \param digital_value the resulting converted value is placed here + * \return zero = ADC conversion complete, digital_value contain current conversion result + * positive = ADC busy, digital_value contain previous conversion result + * negative = Conversion request failed. + */ +int gradcdac_adc_convert_try(void *cookie, unsigned short *digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status; + + status = pDev->regs->status; + + if ( digital_value ){ + *digital_value = pDev->regs->adc_din; + } + + if ( gradcdac_ADC_isOngoing(status) ) + return 1; + + if ( gradcdac_ADC_isCompleted(status) ) + return 0; + + /* Failure */ + return -1; +} + +/* Waits until the ADC circuity has finished a digital to analogue + * conversion. The Waiting is implemented as a busy loop utilizing + * 100% CPU load. + */ +int gradcdac_adc_convert(void *cookie, unsigned short *digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status; + + do { + status=gradcdac_get_status(pDev); + }while ( gradcdac_ADC_isOngoing(status) ); + + if ( digital_value ) + *digital_value = pDev->regs->adc_din; + + if ( gradcdac_ADC_isCompleted(status) ) + return 0; + + return -1; +} + +/* Try to make the DAC circuitry initialize a digital to analogue + * conversion. If the circuitry is busy by a previous conversion + * the function return a non-zero value, if the conversion is + * successfully initialized the function return zero. + */ +int gradcdac_dac_convert_try(void *cookie, unsigned short digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status = pDev->regs->status; + + if ( gradcdac_DAC_isOngoing(status) ) + return -1; + + /* Force a new conversion */ + pDev->regs->dac_dout = digital_value; + + /* Return success */ + return 0; +} + +/* Initializes a digital to analogue conversion by waiting until + * previous conversions is finished before proceeding with the + * conversion. The Waiting is implemented as a busy loop utilizing + * 100% CPU load. + */ +void gradcdac_dac_convert(void *cookie, unsigned short digital_value) +{ + struct gradcdac_priv *pDev = cookie; + unsigned int status; + + do { + status = gradcdac_get_status(pDev); + }while( gradcdac_DAC_isOngoing(status) ); + + pDev->regs->dac_dout = digital_value; +} + +unsigned int gradcdac_get_adrinput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->adrin; +} + +void gradcdac_set_adrinput(void *cookie, unsigned int input) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->adrin = input; +} + +unsigned int gradcdac_get_adroutput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->adrout; +} + +void gradcdac_set_adroutput(void *cookie, unsigned int output) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->adrout = output; +} + +unsigned int gradcdac_get_adrdir(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->adrdir; +} + +void gradcdac_set_adrdir(void *cookie, unsigned int dir) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->adrdir = dir; +} + +unsigned int gradcdac_get_datainput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->data_in; +} + +void gradcdac_set_datainput(void *cookie, unsigned int input) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->data_in = input; +} + +unsigned int gradcdac_get_dataoutput(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->data_out; +} + +void gradcdac_set_dataoutput(void *cookie, unsigned int output) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->data_out = output; +} + +unsigned int gradcdac_get_datadir(void *cookie) +{ + struct gradcdac_priv *pDev = cookie; + return pDev->regs->data_dir; +} + +void gradcdac_set_datadir(void *cookie, unsigned int dir) +{ + struct gradcdac_priv *pDev = cookie; + pDev->regs->data_dir = dir; +} diff --git a/c/src/lib/libbsp/sparc/shared/ascs/grascs.c b/c/src/lib/libbsp/sparc/shared/ascs/grascs.c new file mode 100644 index 0000000000..a2019703d1 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/ascs/grascs.c @@ -0,0 +1,615 @@ +/* This file contains the GRASCS RTEMS driver + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <stdlib.h> +#include <bsp.h> +#include <ambapp.h> +#include <grascs.h> + +#ifndef GAISLER_ASCS +#define GAISLER_ASCS 0x043 +#endif + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +typedef struct { + volatile unsigned int cmd; + volatile unsigned int clk; + volatile unsigned int sts; + volatile unsigned int tcd; + volatile unsigned int tmd; +} GRASCS_regs; + +typedef struct { + unsigned char tmconf; + unsigned char usconf; + unsigned char nslaves; + unsigned char dbits; + int clkfreq; +} GRASCS_caps; + +typedef struct { + GRASCS_regs *regs; /* Pointer to core registers */ + GRASCS_caps *caps; /* Pointer to capability struct */ + rtems_id tcsem1, tcsem2; + rtems_id tmsem1, tmsem2; + volatile char running; + int tcptr; + int tmptr; + int tcwords; + int tmwords; +} GRASCS_cfg; + +static GRASCS_cfg *cfg = NULL; + +/*------------------------------------*/ +/* Start of internal helper functions */ +/*------------------------------------*/ + +/* Function: ASCS_getaddr + Arguments: base: Core's register base address + irq: Core's irq + Return values: 0 if successful, -1 if core is not found + Description: Assigns core's register base address and + irq to arguments. Uses AMBA plug and play to find the + core. +*/ +static int ASCS_get_addr(int *base, int *irq) { + + struct ambapp_apb_info core; + + if(ambapp_find_apbslv(&ambapp_plb, VENDOR_GAISLER, GAISLER_ASCS, &core) == 1) { + *base = core.start; + *irq = core.irq; + DBG("ASCS_get_addr: Registerd ASCS core at 0x%x with irq %i\n",core.start, core.irq); + return 0; + } + DBG("ASCS_get_addr: Failed to detect core\n"); + return -1; +} + +/* Function: ASCS_calc_clkreg + Arguments: sysfreq: System clock frequency in kHz + etrfreq: ETR frequency in Hz + Return values: Value of core's CLK-register + Description: Calculates value of core's CLK-register. See + GRASCS IP core documentation for details. +*/ +static int ASCS_calc_clkreg(int sysfreq, int etrfreq) { + + if(cfg->caps->usconf) + return 1000000/etrfreq; + else + return sysfreq*1000/etrfreq; +} + +/* Function: ASCS_get_sysfreq + Arguments: - + Return values: System clock frequency in kHz, -1 if failed + Description: Uses AMBA plug and play to lookup system frequency +*/ +static int ASCS_get_sysfreq(void) { + + struct ambapp_apb_info gpt; + struct gptimer_regs *tregs; + int tmp; + + if(ambapp_find_apbslv(&ambapp_plb, VENDOR_GAISLER, GAISLER_GPTIMER, &gpt) == 1) { + tregs = (struct gptimer_regs *) gpt.start; + tmp = (tregs->scaler_reload + 1)*1000; + DBG("ASCS_get_sysfreq: Detected system frequency %i kHz\n",tmp); + if((tmp < GRASCS_MIN_SFREQ) || (tmp > GRASCS_MAX_SFREQ)) { + DBG("ASCS_get_sysfreq: System frequency is invalid for ASCS core\n"); + return -1; + } + else + return (tregs->scaler_reload + 1)*1000; + } + DBG("ASCS_get_sysfreq: Failed to detect system frequency\n"); + return -1; +} + +/* Function: ASCS_irqhandler + Arguments: v: not used + Return values: - + Description: Determines the source of the interrupt, clears the + appropriate bits in the core's STS register and releases + the associated semaphore +*/ +static rtems_isr ASCS_irqhandler(rtems_vector_number v) { + + if(cfg->regs->sts & GRASCS_STS_TCDONE) { + /* Clear TC done bit */ + cfg->regs->sts |= GRASCS_STS_TCDONE; + + if(--cfg->tcwords == 0) + /* No more TCs to perform right now */ + rtems_semaphore_release(cfg->tcsem2); + else { + /* Block not sent yet, start next TC */ + if(cfg->caps->dbits == 8) { + cfg->tcptr++; + cfg->regs->tcd = *((unsigned char*)cfg->tcptr); + } + else if(cfg->caps->dbits == 16) { + cfg->tcptr += 2; + cfg->regs->tcd = *((unsigned short int*)cfg->tcptr); + } + else { + cfg->tcptr += 4; + cfg->regs->tcd = *((unsigned int*)cfg->tcptr); + } + } + } + + if(cfg->regs->sts & GRASCS_STS_TMDONE) { + /* Clear TM done bit */ + cfg->regs->sts |= GRASCS_STS_TMDONE; + + /* Store received data */ + if(cfg->caps->dbits == 8) { + *((unsigned char*)cfg->tmptr) = (unsigned char)(cfg->regs->tmd & 0xFF); + cfg->tmptr++; + } + else if(cfg->caps->dbits == 16) { + *((unsigned short int*)cfg->tmptr) = (unsigned short int)(cfg->regs->tmd & 0xFFFF); + cfg->tmptr += 2; + } + else { + *((unsigned int*)cfg->tmptr) = cfg->regs->tmd; + cfg->tmptr += 4; + } + + if(--cfg->tmwords == 0) + /* No more TMs to perform right now */ + rtems_semaphore_release(cfg->tmsem2); + else + /* Block not received yet, start next TM */ + cfg->regs->cmd |= GRASCS_CMD_SENDTM; + } +} + +/*---------------------------*/ +/* Start of driver interface */ +/*---------------------------*/ + +/* Function: ASCS_init + Arguments: - + Return values: 0 if successful, -1 if unsuccessful + Description: Initializes the ASCS core +*/ +int ASCS_init(void) { + + int base, irq, tmp; + + DBG("ASCS_init: Starting initialization of ASCS core\n"); + + /* Allocate memory for config, status and capability struct */ + if((cfg = (GRASCS_cfg*)malloc(sizeof(GRASCS_cfg))) == NULL) { + DBG("ASCS_init: Could not allocate memory for cfg struc\n"); + return -1; + } + + if((cfg->caps = (GRASCS_caps*)calloc(1,sizeof(GRASCS_caps))) == NULL) { + DBG("ASCS_init: Could not allocate memory for caps struc\n"); + goto init_error1; + } + + /* Create semaphores for blocking ASCS_TC/TM functions */ + if(rtems_semaphore_create(rtems_build_name('A','S','C','0'),1, + (RTEMS_FIFO|RTEMS_BINARY_SEMAPHORE| + RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL| + RTEMS_NO_PRIORITY_CEILING), 0, + &cfg->tcsem1) != RTEMS_SUCCESSFUL) { + DBG("ASCS_init: Failed to create semaphore ASC0\n"); + goto init_error2; + } + if(rtems_semaphore_create(rtems_build_name('A','S','C','1'),1, + (RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE| + RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL| + RTEMS_NO_PRIORITY_CEILING), 0, + &cfg->tmsem1) != RTEMS_SUCCESSFUL) { + DBG("ASCS_init: Failed to create semaphore ASC1\n"); + goto init_error2; + } + if(rtems_semaphore_create(rtems_build_name('A','S','C','2'),0, + (RTEMS_FIFO|RTEMS_BINARY_SEMAPHORE| + RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL| + RTEMS_NO_PRIORITY_CEILING), 0, + &cfg->tcsem2) != RTEMS_SUCCESSFUL) { + DBG("ASCS_init: Failed to create semaphore ASC2\n"); + goto init_error2; + } + if(rtems_semaphore_create(rtems_build_name('A','S','C','3'),0, + (RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE| + RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL| + RTEMS_NO_PRIORITY_CEILING), 0, + &cfg->tmsem2) != RTEMS_SUCCESSFUL) { + DBG("ASCS_init: Failed to create semaphore ASC3\n"); + goto init_error2; + } + + /* Set pointer to core registers */ + if(ASCS_get_addr(&base, &irq) == -1) + goto init_error2; + + cfg->regs = (GRASCS_regs*)base; + + /* Read core capabilities */ + tmp = cfg->regs->sts; + cfg->caps->dbits = ((tmp >> GRASCS_STS_DBITS_BITS) & 0x1F) + 1; + cfg->caps->nslaves = ((tmp >> GRASCS_STS_NSLAVES_BITS) & 0xF) + 1; + cfg->caps->tmconf = (tmp >> GRASCS_STS_TMCONF_BITS) & 0x1; + cfg->caps->usconf = (tmp >> GRASCS_STS_USCONF_BITS) & 0x1; + + /* Reset and configure core */ + cfg->running = 0; + cfg->regs->cmd |= GRASCS_CMD_RESET; + if((tmp = ASCS_get_sysfreq()) == -1) + goto init_error2; + cfg->caps->clkfreq = tmp; + while(ASCS_iface_status()) + ; + cfg->regs->clk = ASCS_calc_clkreg(tmp, GRASCS_DEFAULT_ETRFREQ); + cfg->regs->cmd = GRASCS_CMD_US1C; + cfg->regs->cmd |= (tmp/1000 << GRASCS_CMD_US1_BITS) | GRASCS_CMD_US1C | + GRASCS_CMD_TCDONE | GRASCS_CMD_TMDONE; + + /* Register interrupt routine */ + set_vector(ASCS_irqhandler,irq+0x10,2); + + return 0; + + init_error2: + free(cfg->caps); + init_error1: + free(cfg); + return -1; +} + +/* Function: ASCS_input_select + Arguments: slave: The number of the slave that is active, + numbered from 0-15 + Return values: 0 if successful, -GRASCS_ERROR_CAPFAULT if slave value + is negative or too big, -GRASCS_ERROR_TRANSACTIVE if + a TM is active. + Description: Sets the slave_sel bits in the core's CMD register. + they are used to choose which slave the core listens + to when performing a TM. The bits can't be set + during a TM, and the function will in such a case fail. +*/ +int ASCS_input_select(int slave) { + + if((slave < 0) || (slave > cfg->caps->nslaves)) { + /* Slave number is negative or too big */ + DBG("ASCS_input_select: Wrong slave number\n"); + return -GRASCS_ERROR_CAPFAULT; + } + + if(rtems_semaphore_obtain(cfg->tmsem1,RTEMS_NO_WAIT,RTEMS_NO_TIMEOUT) != + RTEMS_SUCCESSFUL) { + /* Can't change active slave during a TM */ + DBG("ASCS_input_select: Transaction active\n"); + return -GRASCS_ERROR_TRANSACTIVE; + } + + cfg->regs->cmd = ((cfg->regs->cmd &= ~GRASCS_CMD_SLAVESEL) | + (slave << GRASCS_CMD_SLAVESEL_BITS)); + + rtems_semaphore_release(cfg->tmsem1); + return 0; +} + +/* Function: ASCS_etr_select + Arguments: src: The source of the ETR signal, valid values are + 0-GRASCS_MAX_TMS (0 = internal source, 1-GRASCS_MAX_TMS = + external time markers 1-GRASCS_MAX_TMS). + freq: ETR frequency in Hz. Valid values are + GRASCS_MIN_ETRFREQ-GRASCS_MAX_ETRFREQ + Return values: 0 if successful, -GRASCS_ERROR_CAPFAULT if src or freq values + are invalid, -GRASCS_ERROR_STARTSTOP if synchronization interface + isn't stopped. + Description: Changes the source for the ETR signal. The frequency of source signal + is assumed to be the same as the frequency of the freq input +*/ +int ASCS_etr_select(int etr, int freq) { + + if((etr < 0) || (etr > GRASCS_MAX_TMS) || ((cfg->caps->tmconf == 0) && (etr > 0)) || + (freq < GRASCS_MIN_ETRFREQ) || (freq > GRASCS_MAX_ETRFREQ)) { + /* ETR source value or frequency is invalid */ + DBG("ASCS_etr_select: Wrong etr src number or wrong frequency\n"); + return -GRASCS_ERROR_CAPFAULT; + } + + if(cfg->regs->sts & GRASCS_STS_ERUNNING) { + /* Synchronization interface is running */ + DBG("ASCS_etr_select: Synch interface is running\n"); + return -GRASCS_ERROR_STARTSTOP; + } + + cfg->regs->clk = ASCS_calc_clkreg(cfg->caps->clkfreq,freq); + cfg->regs->cmd = ((cfg->regs->cmd &= ~GRASCS_CMD_ETRCTRL) | + (etr << GRASCS_CMD_ETRCTRL_BITS)); + + return 0; +} + +/* Function: ASCS_start + Arguments: - + Return values: - + Description: Enables the serial interface. +*/ +void ASCS_start(void) { + + /* Set register and internal status to running */ + cfg->regs->cmd |= GRASCS_CMD_STARTSTOP; + cfg->running = 1; +} + +/* Function: ASCS_stop + Arguments: - + Return values: - + Description: Disables the serial interface. This function will + block until possible calls to TC_send(_block) and + TM_recv(_block) has returned in order to be sure + that started transactions will be performed. +*/ +void ASCS_stop(void) { + + /* Set internal status to stopped */ + cfg->running = 0; + + /* Obtain semaphores to avoid possible situation where a + TC_send(_block) or TM_recv(_block) is aborted and driver is + waiting forever for an interrupt */ + rtems_semaphore_obtain(cfg->tcsem1,RTEMS_WAIT,RTEMS_NO_TIMEOUT); + rtems_semaphore_obtain(cfg->tmsem1,RTEMS_WAIT,RTEMS_NO_TIMEOUT); + + /* Change actual register value */ + cfg->regs->cmd &= ~GRASCS_CMD_STARTSTOP; + + /* Release the semaphores */ + rtems_semaphore_release(cfg->tcsem1); + rtems_semaphore_release(cfg->tmsem1); +} + +/* Function: ASCS_iface_status + Arguments: - + Return values: 0 if both serial interface and synch interface is stopped, + 1 if serial interface is running buth synch interface is + stopped, 2 if serial interface is stopped but synch interface + is running, 3 if both serial and synch interface is running + Description: Reads the core's STS register and reports the status of the + serial and synch interfaces +*/ +int ASCS_iface_status(void) { + + return ((cfg->regs->sts & 0x3) & (0x2 | cfg->running)); +} + +/* Function: ASCS_TC_send + Arguments: word: Pointer to a word that should be sent + Return values: 0 on success + -GRASCS_ERROR_STARTSTOP if serial interface is stopped, + -GRASCS_ERROR_TRANSACTIVE if another TC is in progress. + Description: Start a TC and sends the data that word points to. +*/ +int ASCS_TC_send(int *word) { + + int retval; + + if(rtems_semaphore_obtain(cfg->tcsem1,RTEMS_NO_WAIT,RTEMS_NO_TIMEOUT) != + RTEMS_SUCCESSFUL) { + /* Can't start a TC_send if another TC_send of TC_send_block is + in progress */ + DBG("ASCS_TC_send: Could not obtain semaphore, transcation probably in progress\n"); + return -GRASCS_ERROR_TRANSACTIVE; + } + + if(!cfg->running) { + /* Can't start a TC if serial interface isn't started */ + DBG("ASCS_TC_send: Serial interface is not started\n"); + retval = -GRASCS_ERROR_STARTSTOP; + } + else { + /* Start the transfer */ + cfg->tcwords = 1; + if(cfg->caps->dbits == 8) + cfg->regs->tcd = *((unsigned char*)word); + else if(cfg->caps->dbits == 16) + cfg->regs->tcd = *((unsigned short int*)((int)word & ~1)); + else + cfg->regs->tcd = *((unsigned int*)((int)word & ~3)); + + /* Wait until transfer is complete */ + rtems_semaphore_obtain(cfg->tcsem2,RTEMS_WAIT,RTEMS_NO_TIMEOUT); + retval = 0; + } + + rtems_semaphore_release(cfg->tcsem1); + + return retval; +} + +/* Function: ASCS_TC_send_block + Arguments: block: Pointer to the start of a datablock that + should be sent. + ntrans: Number of transfers needed to transfer + the block. + Return values: 0 if successfull, -GRASCS_ERROR_STARTSTOP if TC + couldn't be started because serial interface is + stopped, -GRASCS_ERROR_TRANSACTIVE if TC couldn't + be started because another TC isn't done yet. + Description: Starts ntrans TCs and sends the data that starts at the + address that block points to. The size of each + transaction will vary depending on whether the core is + configured for 8, 16, or 32 bits data transfers. +*/ +int ASCS_TC_send_block(int *block, int ntrans) { + + int retval; + + if(rtems_semaphore_obtain(cfg->tcsem1,RTEMS_NO_WAIT,RTEMS_NO_TIMEOUT) != + RTEMS_SUCCESSFUL) { + /* Can't start a TC_send_block if another TC_send of TC_send_block is + in progress */ + DBG("ASCS_TC_send_block: Could not obtain semaphore, transcation probably in progress\n"); + return -GRASCS_ERROR_TRANSACTIVE; + } + + if(!cfg->running) { + /* Can't start a TC if serial interface isn't started */ + DBG("ASCS_TC_send_block: Serial interface is not started\n"); + retval = -GRASCS_ERROR_STARTSTOP; + } + else { + /* Start the first transfer */ + cfg->tcwords = ntrans; + if(cfg->caps->dbits == 8) { + cfg->tcptr = (int)block; + cfg->regs->tcd = *((unsigned char*)cfg->tcptr); + } + else if(cfg->caps->dbits == 16) { + cfg->tcptr = (int)block & ~1; + cfg->regs->tcd = *((unsigned short int*)cfg->tcptr); + } + else { + cfg->tcptr = (int)block & ~3; + cfg->regs->tcd = *((unsigned int*)cfg->tcptr); + } + + /* Wait until all transfers are complete */ + rtems_semaphore_obtain(cfg->tcsem2,RTEMS_WAIT,RTEMS_NO_TIMEOUT); + retval = 0; + } + + rtems_semaphore_release(cfg->tcsem1); + + return retval; +} + +/* Function: ASCS_TC_sync_start + Arguments: - + Return values: - + Description: Starts synchronization interface. Might + be delayed if a TM is in progress. SW can poll + ASCS_iface_status() to find out when synch interface is + started. First ETR pulse can be delay up to one ETR + period depending on the source of the ETR and + activity on the TM line. +*/ +void ASCS_TC_sync_start(void) { + + cfg->regs->cmd |= GRASCS_CMD_ESTARTSTOP; +} + +/* Function: ASCS_TC_sync_stop + Arguments: - + Return values: - + Description: Stops the synchronization interface. Might + be delayed for 1 us if a ETR pulse is being generated. SW + can determine when synch interface has stopped by polling + ASCS_iface_status(). +*/ +void ASCS_TC_sync_stop(void) { + + cfg->regs->cmd &= ~GRASCS_CMD_ESTARTSTOP; +} + +/* Function: ASCS_TM_recv + Arguments: word: Pointer to where the received word should be + placed + Return values: 0 if successful, -GRASCS_ERROR_STARTSTOP if serial + interface isn't started, -GRASCS_ERROR_TRANSACTIVE + if another TM is in progress + Description: Starts a TM and stores the incoming data in word. +*/ +int ASCS_TM_recv(int *word) { + + int retval; + + if(rtems_semaphore_obtain(cfg->tmsem1,RTEMS_NO_WAIT,RTEMS_NO_TIMEOUT) != + RTEMS_SUCCESSFUL) { + /* Can't start a TM_recv if another TM_recv of TM_recv_block is + in progress */ + DBG("ASCS_TM_recv: Could not obtain semaphore, transaction probably in progress\n"); + return -GRASCS_ERROR_TRANSACTIVE; + } + + if(!cfg->running) { + /* Can't start a TM if serial interface isn't started */ + DBG("ASCS_TM_recv: Serial interface is not started\n"); + retval = -GRASCS_ERROR_STARTSTOP; + } + else { + /* Start transfer */ + cfg->tmwords = 1; + cfg->tmptr = (int)word; + cfg->regs->cmd |= GRASCS_CMD_SENDTM; + + /* Wait until transfer finishes */ + rtems_semaphore_obtain(cfg->tmsem2,RTEMS_WAIT,RTEMS_NO_TIMEOUT); + retval = 0; + } + + rtems_semaphore_release(cfg->tmsem1); + + return retval; +} + +/* Function: ASCS_TM_recv_block + Arguments: block: Pointer to where the received datablock + should be stored. + ntrans: Number of transfers needed to transfer + the block. + Return values: 0 if successful, -GRASCS_ERROR_STARTSTOP if serial + interface isn't started, -GRASCS_ERROR_TRANSACTIVE if + a performed TM hasn't been processed yet + Description: Starts ntrans TMs and stores the data at the address + that block points to. The size of each transaction + will vary depending on whether the core is + configured for 8, 16, or 32 bits data transfers. +*/ +int ASCS_TM_recv_block(int *block, int ntrans) { + + int retval; + + if(rtems_semaphore_obtain(cfg->tmsem1,RTEMS_NO_WAIT,RTEMS_NO_TIMEOUT) != + RTEMS_SUCCESSFUL) { + /* Can't start a TM_recv_block if another TM_recv of TM_recv_block is + in progress */ + DBG("ASCS_TM_recv_block: Could not obtain semaphore, transaction probably in progress\n"); + return -GRASCS_ERROR_TRANSACTIVE; + } + + if(!cfg->running) { + /* Can't start a TM if serial interface isn't started */ + DBG("ASCS_TM_recv_block: Serial interface is not started\n"); + retval = -GRASCS_ERROR_STARTSTOP; + } + else { + /* Start transfer */ + cfg->tmwords = ntrans; + cfg->tmptr = (int)block; + cfg->regs->cmd |= GRASCS_CMD_SENDTM; + + /* Wait until transfer finishes */ + rtems_semaphore_obtain(cfg->tmsem2,RTEMS_WAIT,RTEMS_NO_TIMEOUT); + retval = 0; + } + + rtems_semaphore_release(cfg->tmsem1); + + return retval; +} diff --git a/c/src/lib/libbsp/sparc/shared/can/canmux.c b/c/src/lib/libbsp/sparc/shared/can/canmux.c new file mode 100644 index 0000000000..0884d911f5 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/can/canmux.c @@ -0,0 +1,197 @@ +/* + * CAN_MUX driver. Present in GR712RC. + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <rtems/libio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <bsp.h> +#include <rtems/bspIo.h> /* printk */ + +#include <canmux.h> +#include <ambapp.h> + +#ifndef GAISLER_CANMUX +#define GAISLER_CANMUX 0x081 +#endif + +#if !defined(CANMUX_DEVNAME) + #undef CANMUX_DEVNAME + #define CANMUX_DEVNAME "/dev/canmux" +#endif + +/* Enable debug output? */ +/* #define DEBUG */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define BUSA_SELECT (1 << 0) +#define BUSB_SELECT (1 << 1) + +struct canmux_priv { + volatile unsigned int *muxreg; + rtems_id devsem; + int open; +}; + +static struct canmux_priv *priv; + +static rtems_device_driver canmux_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver canmux_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver canmux_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver canmux_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver canmux_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver canmux_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg); + + +static rtems_device_driver canmux_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t*)arg; + + DBG("CAN_MUX: IOCTL %d\n\r", ioarg->command); + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case CANMUX_IOC_BUSA_SATCAN: *priv->muxreg &= ~BUSA_SELECT; break; + case CANMUX_IOC_BUSA_OCCAN1: *priv->muxreg |= BUSA_SELECT; break; + case CANMUX_IOC_BUSB_SATCAN: *priv->muxreg &= ~BUSB_SELECT; break; + case CANMUX_IOC_BUSB_OCCAN2: *priv->muxreg |= BUSB_SELECT; break; + default: return RTEMS_NOT_DEFINED; + } + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver canmux_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t*)arg; + + rw_args->bytes_moved = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver canmux_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t*)arg; + + rw_args->bytes_moved = 0; + + return RTEMS_SUCCESSFUL; +} + + +static rtems_device_driver canmux_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + DBG("CAN_MUX: Closing %d\n\r",minor); + + priv->open = 0; + return RTEMS_SUCCESSFUL; +} + + +static rtems_device_driver canmux_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + DBG("CAN_MUX: Opening %d\n\r",minor); + + rtems_semaphore_obtain(priv->devsem,RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (priv->open) { + rtems_semaphore_release(priv->devsem); + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + priv->open = 1; + rtems_semaphore_release(priv->devsem); + + DBG("CAN_MUX: Opening %d success\n\r",minor); + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver canmux_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct ambapp_apb_info d; + char fs_name[20]; + rtems_status_code status; + + DBG("CAN_MUX: Initialize..\n\r"); + + strcpy(fs_name, CANMUX_DEVNAME); + + /* Find core and initialize register pointer */ + if (!ambapp_find_apbslv(&ambapp_plb, VENDOR_GAISLER, GAISLER_CANMUX, &d)) { + printk("CAN_MUX: Failed to find CAN_MUX core\n\r"); + return -1; + } + + status = rtems_io_register_name(fs_name, major, minor); + if (RTEMS_SUCCESSFUL != status) + rtems_fatal_error_occurred(status); + + /* Create private structure */ + if ((priv = malloc(sizeof(struct canmux_priv))) == NULL) { + printk("CAN_MUX driver could not allocate memory for priv structure\n\r"); + return -1; + } + + priv->muxreg = (unsigned int*)d.start; + + status = rtems_semaphore_create( + rtems_build_name('M', 'd', 'v', '0'), + 1, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->devsem); + if (status != RTEMS_SUCCESSFUL) { + printk("CAN_MUX: Failed to create dev semaphore (%d)\n\r", status); + free(priv); + return RTEMS_UNSATISFIED; + } + + priv->open = 0; + + return RTEMS_SUCCESSFUL; +} + + +#define CANMUX_DRIVER_TABLE_ENTRY { canmux_initialize, canmux_open, canmux_close, canmux_read, canmux_write, canmux_ioctl } + +static rtems_driver_address_table canmux_driver = CANMUX_DRIVER_TABLE_ENTRY; + +int canmux_register(void) +{ + rtems_status_code r; + rtems_device_major_number m; + + DBG("CAN_MUX: canmux_register called\n\r"); + + if ((r = rtems_io_register_driver(0, &canmux_driver, &m)) == RTEMS_SUCCESSFUL) { + DBG("CAN_MUX driver successfully registered, major: %d\n\r", m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("CAN_MUX rtems_io_register_driver failed: RTEMS_TOO_MANY\n\r"); break; + case RTEMS_INVALID_NUMBER: + printk("CAN_MUX rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n\r"); break; + case RTEMS_RESOURCE_IN_USE: + printk("CAN_MUX rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n\r"); break; + default: + printk("CAN_MUX rtems_io_register_driver failed\n\r"); + } + return 1; + } + + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/can/satcan.c b/c/src/lib/libbsp/sparc/shared/can/satcan.c new file mode 100644 index 0000000000..36c6bb9f5d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/can/satcan.c @@ -0,0 +1,714 @@ +/* + * SatCAN FPGA driver + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <rtems/libio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <bsp.h> +#include <rtems/bspIo.h> /* printk */ + +#include <satcan.h> +#include <ambapp.h> + +#ifndef GAISLER_SATCAN +#define GAISLER_SATCAN 0x080 +#endif + +#if !defined(SATCAN_DEVNAME) + #undef SATCAN_DEVNAME + #define SATCAN_DEVNAME "/dev/satcan" +#endif + +/* Enable debug output? */ +/* #define DEBUG */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + + +/* Defines related to DMA */ +#define ALIGN_2KMEM 32*1024 +#define ALIGN_8KMEM 128*1024 + +#define OFFSET_2K_LOW_POS 15 +#define OFFSET_8K_LOW_POS 17 + +#define DMA_2K_DATA_SELECT (1 << 14) +#define DMA_8K_DATA_SELECT (1 << 16) + +#define DMA_2K_DATA_OFFSET 16*1024 +#define DMA_8K_DATA_OFFSET 64*1024 + +/* Core register structures and defines */ + +/* Indexes to SatCAN registers in satcan array are declared in satcan.h*/ +/* Fields for some of the SatCAN FPGA registers */ + +/* CmdReg0 */ +#define CAN_TODn_Int_sel (1 << 5) + +/* CmdReg1 */ +#define Sel_2k_8kN (1 << 0) + +/* Read FIFO */ +#define FIFO_Full (1 << 8) +#define FIFO_Empty (1 << 9) + +/* DMA Ch_Enable */ +#define DMA_AutoInitDmaTx (1 << 3) +#define DMA_EnTx2 (1 << 2) +#define DMA_EnTx1 (1 << 1) +#define DMA_EnRx (1 << 0) + +/* SatCAN wrapper register fields */ +#define CTRL_BT_P 9 +#define CTRL_NODENO_P 5 +#define CTRL_DIS (1 << 2) +#define CTRL_DPS_P 1 +#define CTRL_RST (1 << 0) + +#define IRQ_AHB (1 << 8) +#define IRQ_PPS (1 << 7) +#define IRQ_M5 (1 << 6) +#define IRQ_M4 (1 << 5) +#define IRQ_M3 (1 << 4) +#define IRQ_M2 (1 << 3) +#define IRQ_M1 (1 << 2) +#define IRQ_SYNC (1 << 1) +#define IRQ_CAN (1 << 0) + +#define MSK_AHB (1 << 8) +#define MSK_PPS (1 << 7) +#define MSK_M5 (1 << 6) +#define MSK_M4 (1 << 5) +#define MSK_M3 (1 << 4) +#define MSK_M2 (1 << 3) +#define MSK_M1 (1 << 2) +#define MSK_SYNC (1 << 1) +#define MSK_CAN (1 << 0) + + + +struct satcan_regs { + volatile unsigned int satcan[32]; + volatile unsigned int ctrl; + volatile unsigned int irqpend; + volatile unsigned int irqmask; + volatile unsigned int membase; +}; + + +struct satcan_priv { + /* config */ + void *dmaptr; + unsigned char *alptr; + satcan_config *cfg; + + /* driver state */ + rtems_id devsem; + rtems_id txsem; + int open; + int txactive; + int dmaen; + int doff; + rtems_interval timeout; + int dmamode; +}; + +static struct satcan_regs *regs; +static struct satcan_priv *priv; + +static rtems_device_driver satcan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver satcan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver satcan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver satcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver satcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver satcan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg); + + +/* + * almalloc: allocate memory area of size sz aligned on sz boundary + * alptr: Utilized to return aligned pointer + * ptr: Unaligned pointer + * sz: Size of memory area + */ +static void almalloc(unsigned char **alptr, void **ptr, int sz) +{ + *ptr = calloc(1,2*sz); + *alptr = (unsigned char *) (((int)*ptr+sz) & ~(sz-1)); +} + +static rtems_isr satcan_interrupt_handler(rtems_vector_number v) +{ + unsigned int irq; + unsigned int fifo; + + irq = regs->irqpend; + + if (irq & IRQ_AHB && priv->cfg->ahb_irq_callback) { + priv->cfg->ahb_irq_callback(); + } + if (irq & IRQ_PPS && priv->cfg->pps_irq_callback) { + priv->cfg->pps_irq_callback(); + } + if (irq & IRQ_M5 && priv->cfg->m5_irq_callback) { + priv->cfg->m5_irq_callback(); + } + if (irq & IRQ_M4 && priv->cfg->m4_irq_callback) { + priv->cfg->m4_irq_callback(); + } + if (irq & IRQ_M3 && priv->cfg->m3_irq_callback) { + priv->cfg->m3_irq_callback(); + } + if (irq & IRQ_M2 && priv->cfg->m2_irq_callback) { + priv->cfg->m2_irq_callback(); + } + if (irq & IRQ_M1 && priv->cfg->m1_irq_callback) { + priv->cfg->m1_irq_callback(); + } + if (irq & IRQ_SYNC && priv->cfg->sync_irq_callback) { + priv->cfg->sync_irq_callback(); + } + if (irq & IRQ_CAN) { + fifo = regs->satcan[SATCAN_FIFO]; + if (!(fifo & FIFO_Empty) && priv->txactive && + (((fifo & 0xff) == SATCAN_IRQ_EOD1) || ((fifo & 0xff) == SATCAN_IRQ_EOD2))) { + rtems_semaphore_release(priv->txsem); + } + if (priv->cfg->can_irq_callback) + priv->cfg->can_irq_callback(fifo); + } +} + + + +static rtems_device_driver satcan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t*)arg; + int *value; + rtems_interval *timeout; + satcan_regmod *regmod; + + DBG("SatCAN: IOCTL %d\n\r", ioarg->command); + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case SATCAN_IOC_DMA_2K: + DBG("SatCAN: ioctl: setting 2K DMA mode\n\r"); + free(priv->dmaptr); + almalloc(&priv->alptr, &priv->dmaptr, ALIGN_2KMEM); + if (priv->dmaptr == NULL) { + printk("SatCAN: Failed to allocate DMA memory\n\r"); + return RTEMS_NO_MEMORY; + } + + regs->membase = (unsigned int)priv->alptr; + regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> OFFSET_2K_LOW_POS; + regs->satcan[SATCAN_CMD1] = regs->satcan[SATCAN_CMD1] | Sel_2k_8kN; + break; + + case SATCAN_IOC_DMA_8K: + DBG("SatCAN: ioctl: setting 8K DMA mode\n\r"); + free(priv->dmaptr); + almalloc(&priv->alptr, &priv->dmaptr, ALIGN_8KMEM); + if (priv->dmaptr == NULL) { + printk("SatCAN: Failed to allocate DMA memory\n\r"); + return RTEMS_NO_MEMORY; + } + + regs->membase = (unsigned int)priv->alptr; + regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> OFFSET_8K_LOW_POS; + regs->satcan[SATCAN_CMD1] = regs->satcan[SATCAN_CMD1] & ~Sel_2k_8kN; + break; + + case SATCAN_IOC_GET_REG: + /* Get regmod structure from argument */ + regmod = (satcan_regmod*)ioarg->buffer; + DBG("SatCAN: ioctl: getting register %d\n\r", regmod->reg); + if (regmod->reg < 0) + return RTEMS_INVALID_NAME; + else if (regmod->reg <= SATCAN_FILTER_STOP) + regmod->val = regs->satcan[regmod->reg]; + else if (regmod->reg == SATCAN_WCTRL) + regmod->val = regs->ctrl; + else if (regmod->reg == SATCAN_WIPEND) + regmod->val = regs->irqpend; + else if (regmod->reg == SATCAN_WIMASK) + regmod->val = regs->irqmask; + else if (regmod->reg == SATCAN_WAHBADDR) + regmod->val = regs->membase; + else + return RTEMS_INVALID_NAME; + break; + + case SATCAN_IOC_SET_REG: + /* Get regmod structure from argument */ + regmod = (satcan_regmod*)ioarg->buffer; + DBG("SatCAN: ioctl: setting register %d, value %x\n\r", + regmod->reg, regmod->val); + if (regmod->reg < 0) + return RTEMS_INVALID_NAME; + else if (regmod->reg <= SATCAN_FILTER_STOP) + regs->satcan[regmod->reg] = regmod->val; + else if (regmod->reg == SATCAN_WCTRL) + regs->ctrl = regmod->val; + else if (regmod->reg == SATCAN_WIPEND) + regs->irqpend = regmod->val; + else if (regmod->reg == SATCAN_WIMASK) + regs->irqmask = regmod->val; + else if (regmod->reg == SATCAN_WAHBADDR) + regs->membase = regmod->val; + else + return RTEMS_INVALID_NAME; + break; + + case SATCAN_IOC_OR_REG: + /* Get regmod structure from argument */ + regmod = (satcan_regmod*)ioarg->buffer; + DBG("SatCAN: ioctl: or:ing register %d, with value %x\n\r", + regmod->reg, regmod->val); + if (regmod->reg < 0) + return RTEMS_INVALID_NAME; + else if (regmod->reg <= SATCAN_FILTER_STOP) + regs->satcan[regmod->reg] |= regmod->val; + else if (regmod->reg == SATCAN_WCTRL) + regs->ctrl |= regmod->val; + else if (regmod->reg == SATCAN_WIPEND) + regs->irqpend |= regmod->val; + else if (regmod->reg == SATCAN_WIMASK) + regs->irqmask |= regmod->val; + else if (regmod->reg == SATCAN_WAHBADDR) + regs->membase |= regmod->val; + else + return RTEMS_INVALID_NAME; + break; + + case SATCAN_IOC_AND_REG: + /* Get regmod structure from argument */ + regmod = (satcan_regmod*)ioarg->buffer; + DBG("SatCAN: ioctl: masking register %d, with value %x\n\r", + regmod->reg, regmod->val); + if (regmod->reg < 0) + return RTEMS_INVALID_NAME; + else if (regmod->reg <= SATCAN_FILTER_STOP) + regs->satcan[regmod->reg] &= regmod->val; + else if (regmod->reg == SATCAN_WCTRL) + regs->ctrl &= regmod->val; + else if (regmod->reg == SATCAN_WIPEND) + regs->irqpend &= regmod->val; + else if (regmod->reg == SATCAN_WIMASK) + regs->irqmask &= regmod->val; + else if (regmod->reg == SATCAN_WAHBADDR) + regs->membase &= regmod->val; + else + return RTEMS_INVALID_NAME; + break; + + case SATCAN_IOC_EN_TX1_DIS_TX2: + priv->dmaen = SATCAN_DMA_ENABLE_TX1; + break; + + case SATCAN_IOC_EN_TX2_DIS_TX1: + priv->dmaen = SATCAN_DMA_ENABLE_TX2; + break; + + case SATCAN_IOC_GET_DMA_MODE: + value = (int*)ioarg->buffer; + *value = priv->dmamode; + break; + + case SATCAN_IOC_SET_DMA_MODE: + value = (int*)ioarg->buffer; + if (*value != SATCAN_DMA_MODE_USER && *value != SATCAN_DMA_MODE_SYSTEM) { + DBG("SatCAN: ioctl: invalid DMA mode\n\r"); + return RTEMS_INVALID_NAME; + } + priv->dmamode = *value; + break; + + case SATCAN_IOC_ACTIVATE_DMA: + if (priv->dmamode != SATCAN_DMA_MODE_USER) { + DBG("SatCAN: ioctl: ACTIVATE_DMA: not in user mode\n\r"); + return RTEMS_INVALID_NAME; + } + value = (int*)ioarg->buffer; + if (*value != SATCAN_DMA_ENABLE_TX1 && *value != SATCAN_DMA_ENABLE_TX2) { + DBG("SatCAN: ioctl: ACTIVATE_DMA: Illegal channel\n\r"); + return RTEMS_INVALID_NAME; + } + regs->satcan[SATCAN_DMA] |= *value << 1; + break; + + case SATCAN_IOC_DEACTIVATE_DMA: + if (priv->dmamode != SATCAN_DMA_MODE_USER) { + DBG("SatCAN: ioctl: DEACTIVATE_DMA: not in user mode\n\r"); + return RTEMS_INVALID_NAME; + } + value = (int*)ioarg->buffer; + if (*value != SATCAN_DMA_ENABLE_TX1 && *value != SATCAN_DMA_ENABLE_TX2) { + DBG("SatCAN: ioctl: DEACTIVATE_DMA: Illegal channel\n\r"); + return RTEMS_INVALID_NAME; + } + regs->satcan[SATCAN_DMA] &= ~(*value << 1); + break; + + case SATCAN_IOC_GET_DOFFSET: + value = (int*)ioarg->buffer; + *value = priv->doff; + break; + + case SATCAN_IOC_SET_DOFFSET: + value = (int*)ioarg->buffer; + priv->doff = *value; + break; + + case SATCAN_IOC_GET_TIMEOUT: + timeout = (rtems_interval*)ioarg->buffer; + *timeout = priv->timeout; + break; + + case SATCAN_IOC_SET_TIMEOUT: + timeout = (rtems_interval*)ioarg->buffer; + priv->timeout = *timeout; + break; + + default: + return RTEMS_NOT_DEFINED; + } + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver satcan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + int i; + int doff; + int msgindex; + int messages; + rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg; + satcan_msg *msgs; + rtems_status_code status; + + DBG("SatCAN: Writing %d bytes from %p\n\r",rw_args->count,rw_args->buffer); + + if ((rw_args->count < sizeof(satcan_msg)) || (!rw_args->buffer)) { + DBG("SatCAN: write: returning EINVAL\n\r"); + return RTEMS_INVALID_NAME; /* EINVAL */ + } + + messages = rw_args->count / sizeof(satcan_msg); + msgs = (satcan_msg*)rw_args->buffer; + + /* Check that size matches any number of satcan_msg */ + if (rw_args->count % sizeof(satcan_msg)) { + DBG("SatCAN: write: count can not be evenly divided with satcan_msg size\n\r"); + return RTEMS_INVALID_NAME; /* EINVAL */ + } + + + /* DMA channel must be set if we are in system DMA mode */ + DBG("SatCAN: write: dma channel select is %x\n\r", priv->dmaen); + if (!priv->dmaen && priv->dmamode == SATCAN_DMA_MODE_SYSTEM) + return RTEMS_INVALID_NAME; /* EINVAL */ + + /* DMA must not be active */ + if (regs->satcan[SATCAN_DMA] & (DMA_EnTx1 | DMA_EnTx2 | DMA_AutoInitDmaTx)) { + DBG("SatCAN: write: DMA was active\n\r"); + rw_args->bytes_moved = 0; + return RTEMS_IO_ERROR; /* EIO */ + } + + doff = regs->satcan[SATCAN_CMD1] & Sel_2k_8kN ? DMA_2K_DATA_OFFSET : DMA_8K_DATA_OFFSET; + + for (msgindex = 0; msgindex < messages; msgindex++) { + /* Place header in DMA area */ + for (i = 0; i < SATCAN_HEADER_SIZE; i++) { + priv->alptr[priv->doff+8*msgindex+i] = msgs[msgindex].header[i]; + } + + /* Place data in DMA area */ + for (i = 0; i < SATCAN_PAYLOAD_SIZE; i++) + priv->alptr[priv->doff+doff+8*msgindex+i] = msgs[msgindex].payload[i]; + } + + if ((priv->dmaen & SATCAN_DMA_ENABLE_TX1) || priv->dmamode == SATCAN_DMA_MODE_USER) { + regs->satcan[SATCAN_DMA_TX_1_CUR] = 0; + regs->satcan[SATCAN_DMA_TX_1_END] = messages<<3; + } + + if ((priv->dmaen & SATCAN_DMA_ENABLE_TX2) || priv->dmamode == SATCAN_DMA_MODE_USER) { + regs->satcan[SATCAN_DMA_TX_2_CUR] = 0; + regs->satcan[SATCAN_DMA_TX_2_END] = messages<<3; + } + + /* If we are in DMA user mode we are done here, otherwise we block */ + if (priv->dmamode == SATCAN_DMA_MODE_SYSTEM) { + priv->txactive = 1; + + /* Enable DMA */ + regs->satcan[SATCAN_DMA] |= priv->dmaen << 1; + + /* Wait for TX interrupt */ + status = rtems_semaphore_obtain(priv->txsem, RTEMS_WAIT, priv->timeout); + + priv->txactive = 0; + + /* Disable activated Tx DMA */ + regs->satcan[SATCAN_DMA] &= ~(priv->dmaen << 1); + + if (status != RTEMS_SUCCESSFUL) { + rw_args->bytes_moved = 0; + return status; + } + } + + rw_args->bytes_moved = rw_args->count; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver satcan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + char *buf; + int i; + int canid; + int messages; + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t*)arg; + satcan_msg *ret; + + /* Check that there is room for the return */ + if (rw_args->count < sizeof(satcan_msg)) { + DBG("SatCAN: read: length of buffer must be at least %d, current is %d\n\r", + sizeof(satcan_msg) + sizeof(int), rw_args->count); + return RTEMS_INVALID_NAME; /* -EINVAL */ + } + + /* Check that size matches any number of satcan_msg */ + if (rw_args->count % sizeof(satcan_msg)) { + DBG("SatCAN: read: count can not be evenly divided with satcan_msg size\n\r"); + return RTEMS_INVALID_NAME; /* EINVAL */ + } + + messages = rw_args->count / sizeof(satcan_msg); + ret = (satcan_msg*)rw_args->buffer; + + DBG("SatCAN: read: reading %d messages to %p\n\r", messages, ret); + + for (i = 0; i < messages; i++) { + canid = (ret[i].header[1] << 8) | ret[i].header[0]; + + /* Copy message header from DMA header area to buffer */ + buf = (char*)((int)priv->alptr | (canid << 3)); + memcpy(ret[i].header, buf, SATCAN_HEADER_SIZE); + + DBG("SatCAN: read: copied header from %p to %p\n\r", buf, ret[i].header); + + /* Clear New Message Marker */ + buf[SATCAN_HEADER_NMM_POS] = 0; + + /* Copy message payload from DMA data area to buffer */ + buf = (char*)((int)buf | + (regs->satcan[SATCAN_CMD1] & Sel_2k_8kN ? DMA_2K_DATA_SELECT : DMA_8K_DATA_SELECT)); + memcpy(ret[i].payload, buf, SATCAN_PAYLOAD_SIZE); + + DBG("SatCAN: read: copied payload from %p to %p\n\r", buf, ret[i].payload); + } + rw_args->bytes_moved = rw_args->count; + + return RTEMS_SUCCESSFUL; +} + + +static rtems_device_driver satcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + DBG("SatCAN: Closing %d\n\r",minor); + + if (priv->open) { + regs->irqmask = 0; + regs->satcan[SATCAN_INT_EN] = 0; + regs->satcan[SATCAN_RX] = 0; + regs->satcan[SATCAN_DMA] = 0; + priv->open = 0; + priv->dmaen = 0; + priv->doff = 0; + priv->timeout = RTEMS_NO_TIMEOUT; + priv->dmamode = SATCAN_DMA_MODE_SYSTEM; + } + + return RTEMS_SUCCESSFUL; +} + + +static rtems_device_driver satcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + DBG("SatCAN: Opening %d\n\r",minor); + + rtems_semaphore_obtain(priv->devsem,RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (priv->open) { + rtems_semaphore_release(priv->devsem); + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + priv->open = 1; + rtems_semaphore_release(priv->devsem); + + /* Enable AHB and CAN IRQs in wrapper and EOD1, EOD2 and CAN critical IRQs in SatCAN core */ + regs->irqmask = MSK_AHB | MSK_CAN; + regs->satcan[SATCAN_INT_EN] = ((1 << SATCAN_IRQ_EOD1) | (1 << SATCAN_IRQ_EOD2) | + (1 << SATCAN_IRQ_CRITICAL)); + + /* Select can_int as IRQ source */ + regs->satcan[SATCAN_CMD0] = CAN_TODn_Int_sel; + /* CAN RX DMA Enable */ + regs->satcan[SATCAN_DMA] = 1; + /* CAN RX Enable */ + regs->satcan[SATCAN_RX] = 1; + + DBG("SatCAN: Opening %d success\n\r",minor); + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver satcan_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct ambapp_ahb_info d; + char fs_name[20]; + rtems_status_code status; + + DBG("SatCAN: Initialize..\n\r"); + + strcpy(fs_name, SATCAN_DEVNAME); + + /* Find core and initialize register pointer */ + if (!ambapp_find_ahbslv(&ambapp_plb, VENDOR_GAISLER, GAISLER_SATCAN, &d)) { + printk("SatCAN: Failed to find SatCAN core\n\r"); + return -1; + } + + status = rtems_io_register_name(fs_name, major, minor); + if (RTEMS_SUCCESSFUL != status) + rtems_fatal_error_occurred(status); + + regs = (struct satcan_regs*)d.start[0]; + + /* Set node number and DPS */ + regs->ctrl |= ((priv->cfg->nodeno & 0xf) << 5) | (priv->cfg->dps << 1); + + /* Reset core */ + regs->ctrl |= CTRL_RST; + + /* Allocate DMA area */ + almalloc(&priv->alptr, &priv->dmaptr, ALIGN_2KMEM); + if (priv->dmaptr == NULL) { + printk("SatCAN: Failed to allocate DMA memory\n\r"); + free(priv->cfg); + free(priv); + return -1; + } + + /* Wait until core reset has completed */ + while (regs->ctrl & CTRL_RST) + ; + + /* Initialize core registers, default is 2K messages */ + regs->membase = (unsigned int)priv->alptr; + regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> 15; + + DBG("regs->membase = %x\n\r", (unsigned int)priv->alptr); + DBG("regs->satcan[SATCAN_RAM_BASE] = %x\n\r", (unsigned int)priv->alptr >> 15); + + status = rtems_semaphore_create( + rtems_build_name('S', 'd', 'v', '0'), + 1, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->devsem); + if (status != RTEMS_SUCCESSFUL) { + printk("SatCAN: Failed to create dev semaphore (%d)\n\r", status); + free(priv->cfg); + free(priv); + return RTEMS_UNSATISFIED; + } + status = rtems_semaphore_create( + rtems_build_name('S', 't', 'x', '0'), + 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ + RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->txsem); + if (status != RTEMS_SUCCESSFUL) { + printk("SatCAN: Failed to create tx semaphore (%d)\n\r", status); + free(priv->cfg); + free(priv); + return RTEMS_UNSATISFIED; + } + + priv->txactive = 0; + priv->open = 0; + priv->dmaen = 0; + priv->doff = 0; + priv->timeout = RTEMS_NO_TIMEOUT; + priv->dmamode = SATCAN_DMA_MODE_SYSTEM; + + /* Register interrupt handler */ + set_vector(satcan_interrupt_handler, d.irq+0x10, 2); + + return RTEMS_SUCCESSFUL; +} + + + +#define SATCAN_DRIVER_TABLE_ENTRY { satcan_initialize, satcan_open, satcan_close, satcan_read, satcan_write, satcan_ioctl } + +static rtems_driver_address_table satcan_driver = SATCAN_DRIVER_TABLE_ENTRY; + +int satcan_register(satcan_config *conf) +{ + rtems_status_code r; + rtems_device_major_number m; + + DBG("SatCAN: satcan_register called\n\r"); + + /* Create private structure */ + if ((priv = malloc(sizeof(struct satcan_priv))) == NULL) { + printk("SatCAN driver could not allocate memory for priv structure\n\r"); + return -1; + } + + DBG("SatCAN: Creating local copy of config structure\n\r"); + if ((priv->cfg = malloc(sizeof(satcan_config))) == NULL) { + printk("SatCAN driver could not allocate memory for cfg structure\n\r"); + return 1; + } + memcpy(priv->cfg, conf, sizeof(satcan_config)); + + if ((r = rtems_io_register_driver(0, &satcan_driver, &m)) == RTEMS_SUCCESSFUL) { + DBG("SatCAN driver successfully registered, major: %d\n\r", m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("SatCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n\r"); break; + case RTEMS_INVALID_NUMBER: + printk("SatCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n\r"); break; + case RTEMS_RESOURCE_IN_USE: + printk("SatCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n\r"); break; + default: + printk("SatCAN rtems_io_register_driver failed\n\r"); + } + return 1; + } + + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/gpio/gpiolib.c b/c/src/lib/libbsp/sparc/shared/gpio/gpiolib.c new file mode 100644 index 0000000000..22f1baa5f3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/gpio/gpiolib.c @@ -0,0 +1,265 @@ +/* GPIOLIB interface implementation + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <gpiolib.h> + +struct gpiolib_port; + +struct gpiolib_port { + struct gpiolib_port *next; + int minor; + struct gpiolib_drv *drv; + void *handle; + + int open; +}; + +/* Root of GPIO Ports */ +struct gpiolib_port *gpiolib_ports; + +/* Number of GPIO ports registered */ +static int port_nr; + +/* 1 if libraray initialized */ +static int gpiolib_initied = 0; + +/* Insert a port first in ports list */ +void gpiolib_list_add(struct gpiolib_port *port) +{ + port->next = gpiolib_ports; + gpiolib_ports = port; +} + +struct gpiolib_port *gpiolib_find(int minor) +{ + struct gpiolib_port *p; + + p = gpiolib_ports; + while ( p && (p->minor != minor) ) { + p = p->next; + } + return p; +} + +struct gpiolib_port *gpiolib_find_by_name(char *name) +{ + struct gpiolib_port *p; + struct gpiolib_info info; + int (*get_info)(void *, struct gpiolib_info *); + + p = gpiolib_ports; + while ( p ) { + get_info = p->drv->ops->get_info; + if ( get_info && (get_info(p->handle, &info) == 0) ) { + if ( strncmp(name, (char *)&info.devName[0], 64) == 0 ) { + break; + } + } + p = p->next; + } + return p; +} + +void gpiolib_list_remove(struct gpiolib_port *port) +{ + +} + +int gpiolib_drv_register(struct gpiolib_drv *drv, void *handle) +{ + struct gpiolib_port *port; + + if ( !drv || !drv->ops ) + return -1; + + port = malloc(sizeof(*port)); + if ( port == NULL ) + return -1; + + memset(port, 0, sizeof(*port)); + port->handle = handle; + port->minor = port_nr++; + port->drv = drv; + + gpiolib_list_add(port); + + return 0; +} + +void gpiolib_show(int port, void *handle) +{ + struct gpiolib_port *p; + + if ( port == -1 ) { + p = gpiolib_ports; + while (p != NULL) { + if ( p->drv->ops->show ) + p->drv->ops->show(p->handle); + p = p->next; + } + } else { + if ( handle ) { + p = handle; + } else { + p = gpiolib_find(port); + } + if ( p == NULL ) { + printf("PORT %d NOT FOUND\n", port); + return; + } + if ( p->drv->ops->show ) + p->drv->ops->show(p->handle); + } +} + +void *gpiolib_open_internal(int port, char *devName) +{ + struct gpiolib_port *p; + + if ( gpiolib_initied == 0 ) + return NULL; + + /* Find */ + if ( port >= 0 ) { + p = gpiolib_find(port); + } else { + p = gpiolib_find_by_name(devName); + } + if ( p == NULL ) + return NULL; + + if ( p->open ) + return NULL; + + p->open = 1; + return p; +} + +void *gpiolib_open(int port) +{ + return gpiolib_open_internal(port, NULL); +} + +void *gpiolib_open_by_name(char *devName) +{ + return gpiolib_open_internal(-1, devName); +} + +void gpiolib_close(void *handle) +{ + struct gpiolib_port *p = handle; + + if ( p && p->open ) { + p->open = 0; + } +} + +int gpiolib_set_config(void *handle, struct gpiolib_config *cfg) +{ + struct gpiolib_port *port = handle; + + if ( !port || !cfg ) + return -1; + + if ( !port->drv->ops->config ) + return -1; + + return port->drv->ops->config(port->handle, cfg); +} + +int gpiolib_set(void *handle, int dir, int outval) +{ + struct gpiolib_port *port = handle; + + if ( !port ) + return -1; + + if ( !port->drv->ops->set ) + return -1; + + return port->drv->ops->set(port->handle, dir, outval); +} + +int gpiolib_get(void *handle, int *inval) +{ + struct gpiolib_port *port = handle; + + if ( !port || !inval) + return -1; + + if ( !port->drv->ops->get ) + return -1; + + return port->drv->ops->get(port->handle, inval); +} + +/*** IRQ Functions ***/ +int gpiolib_irq_register(void *handle, void *func, void *arg) +{ + struct gpiolib_port *port = handle; + + if ( !port ) + return -1; + + if ( !port->drv->ops->irq_register ) + return -1; + + return port->drv->ops->irq_register(port->handle, func, arg); +} + +int gpiolib_irq_opts(void *handle, unsigned int options) +{ + struct gpiolib_port *port = handle; + + if ( !port ) + return -1; + + if ( !port->drv->ops->irq_opts ) + return -1; + + return port->drv->ops->irq_opts(port->handle, options); +} + +int gpiolib_irq_clear(void *handle) +{ + return gpiolib_irq_opts(handle, GPIOLIB_IRQ_CLEAR); +} + +int gpiolib_irq_force(void *handle) +{ + return gpiolib_irq_opts(handle, GPIOLIB_IRQ_FORCE); +} + +int gpiolib_irq_enable(void *handle) +{ + return gpiolib_irq_opts(handle, GPIOLIB_IRQ_ENABLE); +} + +int gpiolib_irq_disable(void *handle) +{ + return gpiolib_irq_opts(handle, GPIOLIB_IRQ_DISABLE); +} + +/*** Initialization ***/ +int gpiolib_initialize(void) +{ + if ( gpiolib_initied != 0 ) + return 0; + + /* Initialize Libarary */ + port_nr = 0; + gpiolib_ports = 0; + gpiolib_initied = 1; + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/gpio/grgpio.c b/c/src/lib/libbsp/sparc/shared/gpio/grgpio.c new file mode 100644 index 0000000000..92e9657833 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/gpio/grgpio.c @@ -0,0 +1,437 @@ +/* GRGPIO GPIO Driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <assert.h> +#include <rtems/bspIo.h> +#include <string.h> +#include <stdio.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <grgpio.h> +#include <gpiolib.h> +#include <ambapp.h> +#include <grlib.h> + +/*#define DEBUG 1*/ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#define STATIC +#else +#define DBG(x...) +#define STATIC static +#endif + +struct grgpio_isr { + drvmgr_isr isr; + void *arg; +}; + +struct grgpio_priv { + struct drvmgr_dev *dev; + struct grgpio_regs *regs; + int irq; + int minor; + + /* Driver implementation */ + int port_cnt; + unsigned char port_handles[32]; + struct grgpio_isr isrs[32]; + struct gpiolib_drv gpiolib_desc; + unsigned int bypass; + unsigned int imask; +}; + +/******************* Driver Manager Part ***********************/ + +int grgpio_device_init(struct grgpio_priv *priv); + +int grgpio_init1(struct drvmgr_dev *dev); +int grgpio_init2(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops grgpio_ops = +{ + .init = {grgpio_init1, NULL, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grgpio_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GPIO}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grgpio_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRGPIO_ID, /* Driver ID */ + "GRGPIO_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grgpio_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grgpio_ids[0] +}; + +void grgpio_register_drv (void) +{ + DBG("Registering GRGPIO driver\n"); + drvmgr_drv_register(&grgpio_drv_info.general); +} + +/* Register GRGPIO pins as quick as possible to the GPIO library, + * other drivers may depend upon them in INIT LEVEL 2. + * Note that since IRQ may not be available in init1, it is assumed + * that the GPIOLibrary does not request IRQ routines until LEVEL 2. + */ +int grgpio_init1(struct drvmgr_dev *dev) +{ + struct grgpio_priv *priv; + int status, port; + + DBG("GRGPIO[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + /* This core will not find other cores, but other driver may depend upon + * the GPIO library to function. So, we set up GPIO right away. + */ + + /* Initialize library if not already done */ + status = gpiolib_initialize(); + if ( status < 0 ) + return DRVMGR_FAIL; + + priv = dev->priv = malloc(sizeof(struct grgpio_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + if ( grgpio_device_init(priv) ) { + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + /* Register all ports available on this core as GPIO port to + * upper layer + */ + for(port=0; port<priv->port_cnt; port++) { + priv->port_handles[port] = port; + gpiolib_drv_register(&priv->gpiolib_desc, + &priv->port_handles[port]); + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +/* Find port from handle, returns -1 if not found */ +int grgpio_find_port(void *handle, struct grgpio_priv **priv) +{ + unsigned char portnr; + + portnr = *(unsigned char *)handle; + if ( portnr > 31 ) + return -1; + *priv = (struct grgpio_priv *) + (((unsigned int)handle - portnr*sizeof(unsigned char)) - + offsetof(struct grgpio_priv, port_handles)); + return portnr; +} + +int grgpio_gpiolib_open(void *handle) +{ + struct grgpio_priv *priv; + int portnr; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + DBG("GRGPIO: FAILED OPENING HANDLE 0x%08x\n", handle); + return -1; + } + DBG("GRGPIO[0x%08x][%d]: OPENING\n", priv->regs, portnr); + + /* Open the device, nothing to be done... */ + + return 0; +} + +int grgpio_grpiolib_config(void *handle, struct gpiolib_config *cfg) +{ + struct grgpio_priv *priv; + int portnr; + unsigned int mask; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + return -1; + } + DBG("GRGPIO[0x%08x][%d]: CONFIG\n", priv->regs, portnr); + + /* Configure the device. And check that operation is supported, + * not all I/O Pins have IRQ support. + */ + mask = (1<<portnr); + + /* Return error when IRQ not supported by this I/O Line and it + * is beeing enabled by user. + */ + if ( ((mask & priv->imask) == 0) && cfg->mask ) + return -1; + + priv->regs->imask &= ~mask; /* Disable interrupt temporarily */ + + /* Configure settings before enabling interrupt */ + priv->regs->ipol = (priv->regs->ipol & ~mask) | (cfg->irq_polarity ? mask : 0); + priv->regs->iedge = (priv->regs->iedge & ~mask) | (cfg->irq_level ? 0 : mask); + priv->regs->imask |= cfg->mask ? mask : 0; + + return 0; +} + +int grgpio_grpiolib_get(void *handle, int *inval) +{ + struct grgpio_priv *priv; + int portnr; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + return -1; + } + DBG("GRGPIO[0x%08x][%d]: GET\n", priv->regs, portnr); + + /* Get current status of the port */ + if ( inval ) + *inval = (priv->regs->data >> portnr) & 0x1; + + return 0; +} + +int grgpio_grpiolib_irq_opts(void *handle, unsigned int options) +{ + struct grgpio_priv *priv; + int portnr; + drvmgr_isr isr; + void *arg; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + return -1; + } + DBG("GRGPIO[0x%08x][%d]: IRQ OPTS 0x%x\n", priv->regs, portnr, options); + + if ( options & GPIOLIB_IRQ_FORCE ) + return -1; + + isr = priv->isrs[portnr].isr; + arg = priv->isrs[portnr].arg; + + if ( options & GPIOLIB_IRQ_DISABLE ) { + /* Disable interrupt at interrupt controller */ + if ( drvmgr_interrupt_unregister(priv->dev, portnr, isr, arg) ) { + return -1; + } + } + if ( options & GPIOLIB_IRQ_CLEAR ) { + /* Clear interrupt at interrupt controller */ + if ( drvmgr_interrupt_clear(priv->dev, portnr) ) { + return -1; + } + } + if ( options & GPIOLIB_IRQ_ENABLE ) { + /* Enable interrupt at interrupt controller */ + if ( drvmgr_interrupt_register(priv->dev, portnr, "grgpio", isr, arg) ) { + return -1; + } + } + + return 0; +} + +int grgpio_grpiolib_irq_register(void *handle, void *func, void *arg) +{ + struct grgpio_priv *priv; + int portnr; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + DBG("GRGPIO: FAILED OPENING HANDLE 0x%08x\n", handle); + return -1; + } + DBG("GRGPIO: OPENING %d at [0x%08x]\n", portnr, priv->regs); + + /* Since the user doesn't provide the ISR and argument, we must... */ + priv->isrs[portnr].isr = func; + priv->isrs[portnr].arg = arg; + + return 0; +} + +int grgpio_grpiolib_set(void *handle, int dir, int outval) +{ + struct grgpio_priv *priv; + int portnr; + unsigned int mask; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + DBG("GRGPIO: FAILED OPENING HANDLE 0x%08x\n", handle); + return -1; + } + DBG("GRGPIO: OPENING %d at [0x%08x]\n", portnr, priv->regs); + + /* Set Direction and Output */ + mask = 1<<portnr; + priv->regs->dir = (priv->regs->dir & ~mask) | (dir ? mask : 0); + priv->regs->output = (priv->regs->output & ~mask) | (outval ? mask : 0); + + return 0; +} + +int grgpio_gpiolib_show(void *handle) +{ + struct grgpio_priv *priv; + int portnr, i, regs[7]; + volatile unsigned int *reg; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + DBG("GRGPIO: FAILED SHOWING HANDLE 0x%08x\n", handle); + return -1; + } + for (i=0, reg=&priv->regs->data; i<7; i++, reg++) { + regs[i] = ( *reg >> portnr) & 1; + } + printf("GRGPIO[%p] PORT[%d]: IN/OUT/DIR: [%d,%d,%d], MASK/POL/EDGE: [%d,%d,%d], BYPASS: %d\n", + priv->regs, portnr, regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6]); + return 0; +} + +int grgpio_gpiolib_get_info(void *handle, struct gpiolib_info *pinfo) +{ + struct grgpio_priv *priv; + int portnr; + char prefix[48]; + struct drvmgr_dev *dev; + + if ( !pinfo ) + return -1; + + portnr = grgpio_find_port(handle, &priv); + if ( portnr < 0 ) { + DBG("GRGPIO: FAILED GET_INFO HANDLE 0x%08x\n", handle); + return -1; + } + + /* Get Filesystem name prefix */ + dev = priv->dev; + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + snprintf(pinfo->devName, 64, "/dev/grgpio%d/%d", dev->minor_drv, portnr); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + snprintf(pinfo->devName, 64, "/dev/%sgrgpio%d/%d", prefix, dev->minor_bus, portnr); + } + + return 0; +} + +static struct gpiolib_drv_ops grgpio_gpiolib_ops = +{ + .config = grgpio_grpiolib_config, + .get = grgpio_grpiolib_get, + .irq_opts = grgpio_grpiolib_irq_opts, + .irq_register = grgpio_grpiolib_irq_register, + .open = grgpio_gpiolib_open, + .set = grgpio_grpiolib_set, + .show = grgpio_gpiolib_show, + .get_info = grgpio_gpiolib_get_info, +}; + +int grgpio_device_init(struct grgpio_priv *priv) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + unsigned int mask; + int port_cnt; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + priv->irq = pnpinfo->irq; + priv->regs = (struct grgpio_regs *)pnpinfo->apb_slv->start; + + DBG("GRGPIO: 0x%08x irq %d\n", (unsigned int)priv->regs, priv->irq); + + /* Mask all Interrupts */ + priv->regs->imask = 0; + + /* Make IRQ Rising edge triggered default */ + priv->regs->ipol = 0xfffffffe; + priv->regs->iedge = 0xfffffffe; + + /* Read what I/O lines have IRQ support */ + priv->imask = priv->regs->ipol; + + /* Let the user configure the port count, this might be needed + * when the GPIO lines must not be changed (assigned during bootup) + */ + value = drvmgr_dev_key_get(priv->dev, "nBits", KEY_TYPE_INT); + if ( value ) { + priv->port_cnt = value->i; + } else { + /* Auto detect number of GPIO ports */ + priv->regs->dir = 0; + priv->regs->output = 0xffffffff; + mask = priv->regs->output; + priv->regs->output = 0; + + for(port_cnt=0; port_cnt<32; port_cnt++) + if ( (mask & (1<<port_cnt)) == 0 ) + break; + priv->port_cnt = port_cnt; + } + + /* Let the user configure the BYPASS register, this might be needed + * to select which cores can do I/O on a pin. + */ + value = drvmgr_dev_key_get(priv->dev, "bypass", KEY_TYPE_INT); + if ( value ) { + priv->bypass = value->i; + } else { + priv->bypass = 0; + } + priv->regs->bypass = priv->bypass; + + /* Prepare GPIOLIB layer */ + priv->gpiolib_desc.ops = &grgpio_gpiolib_ops; + + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/include/ahbstat.h b/c/src/lib/libbsp/sparc/shared/include/ahbstat.h new file mode 100644 index 0000000000..8f0576c584 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/ahbstat.h @@ -0,0 +1,73 @@ +/* AHBSTAT driver interface + * + * COPYRIGHT (c) 2011. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __AHBSTAT_H__ +#define __AHBSTAT_H__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* AHBSTAT Registers layout */ +struct ahbstat_regs { + volatile uint32_t status; + volatile uint32_t failing; +}; + +/* AHB fail interrupt callback to user. This function is declared weak so that + * the user can define a function pointer variable containing the address + * responsible for handling errors + * + * minor Index of AHBSTAT hardware + * regs Register address of AHBSTAT + * status AHBSTAT status register at IRQ + * failing_address AHBSTAT Failing address register at IRQ + * + * * User return + * 0: print error onto terminal with printk and reenable AHBSTAT + * 1: just re-enable AHBSTAT + * 2: just print error + * 3: do nothing, let user do custom handling + */ +extern int (*ahbstat_error)( + int minor, + struct ahbstat_regs *regs, + uint32_t status, + uint32_t failing_address); + +/* Get Last received AHB Error + * + * \param minor Index used to indentify a specific AHBSTAT core + * \param status Status register at time of error IRQ was recevied + * \param address Failing address register at time of error IRQ + * + * Return + * 0: No error received + * 1: Error Received, last status and address stored into argument pointers + * -1: No such AHBSTAT device + */ +extern int ahbstat_last_error(int minor, uint32_t *status, uint32_t *address); + +/* Get AHBSTAT registers address from minor. Can also be used to check if + * AHBSTAT hardware is present. + * + * Return + * NULL returned if no such device + * non-zero Address to AHBSTAT register + */ +extern struct ahbstat_regs *ahbstat_get_regs(int minor); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/b1553rt.h b/c/src/lib/libbsp/sparc/shared/include/b1553rt.h new file mode 100644 index 0000000000..94b5afdbff --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/b1553rt.h @@ -0,0 +1,74 @@ +/* B1553RT driver interface + * + * COPYRIGHT (c) 2009. + * Aeroflex Gaisler AB + * + * 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. + */ + +#ifndef __B1553RT_H__ +#define __B1553RT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rt_reg { + volatile unsigned int stat; /* 0x00 */ + volatile unsigned int ctrl; /* 0x04 */ + volatile unsigned int vword; /* 0x08 */ + volatile unsigned int irq; /* 0x0C */ + volatile unsigned int addr; /* 0x10 */ + volatile unsigned int ipm; /* 0x14 */ +}; + + +struct rt_msg { + unsigned short miw; + unsigned short time; + unsigned short data[32]; + unsigned short desc; +}; + +#define RT_FREQ_12MHZ 0 +#define RT_FREQ_16MHZ 1 +#define RT_FREQ_20MHZ 2 +#define RT_FREQ_24MHZ 3 +#define RT_FREQ_MASK 0x3 + +/* IOCTLs */ +#define RT_SET_ADDR 3 +#define RT_SET_BCE 5 +#define RT_RX_BLOCK 8 +#define RT_CLR_STATUS 12 +#define RT_GET_STATUS 13 +#define RT_SET_EVENTID 14 + +#define RT_SET_VECTORW 32 +#define RT_SET_EXTMDATA 33 + +#define RT_ILLCMD_IRQ 128 +#define RT_MERR_IRQ 2048 +#define RT_DMAF_IRQ 32768 /* DMA Fail irq */ + +#define RT_TSW_OK (1<<14) +#define RT_TSW_BUS (1<<13) +#define RT_TSW_BC (1<<12) +#define RT_TSW_LPBKERRB (1<<11) +#define RT_TSW_LPBKERRA (1<<10) +#define RT_TSW_ILL (1<<9) +#define RT_TSW_MEM (1<<8) +#define RT_TSW_MAN (1<<7) +#define RT_TSW_PAR (1<<6) +#define RT_TSW_WC (1<<5) + +void b1553rt_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __RT_H__ */ + diff --git a/c/src/lib/libbsp/sparc/shared/include/canmux.h b/c/src/lib/libbsp/sparc/shared/include/canmux.h new file mode 100644 index 0000000000..476c3b8a6d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/canmux.h @@ -0,0 +1,32 @@ +/* + * Header file for RTEMS CAN_MUX driver + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __CANMUX_H__ +#define __CANMUX_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Driver interface */ +int canmux_register(void); + +/* ioctl calls */ +#define CANMUX_IOC_BUSA_SATCAN 1 +#define CANMUX_IOC_BUSA_OCCAN1 2 +#define CANMUX_IOC_BUSB_SATCAN 3 +#define CANMUX_IOC_BUSB_OCCAN2 4 + +#ifdef __cplusplus +} +#endif + +#endif /* __CANMUX_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h index c8fca2422f..687bafd792 100644 --- a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h @@ -1,7 +1,7 @@ /* General part of a AMBA Plug & Play bus driver. * * COPYRIGHT (c) 2008. - * Cobham Gaisler AB + * Cobham Gaisler AB. * * This is the general part of the different AMBA Plug & Play * drivers. The drivers are wrappers around this driver, making @@ -28,8 +28,36 @@ extern "C" { DRIVER_ID(DRVMGR_BUS_TYPE_AMBAPP, ((((vendor) & 0xff) << 16) | ((device) & 0xfff))) /*** Gaisler Hardware Device Driver IDs ***/ -#define DRIVER_AMBAPP_GAISLER_APBUART_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_APBUART) -#define DRIVER_AMBAPP_GAISLER_GPTIMER_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GPTIMER) +#define DRIVER_AMBAPP_GAISLER_AHBSTAT_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_AHBSTAT) +#define DRIVER_AMBAPP_GAISLER_APBUART_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_APBUART) +#define DRIVER_AMBAPP_GAISLER_B1553BRM_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_B1553BRM) +#define DRIVER_AMBAPP_GAISLER_B1553RT_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_B1553RT) +#define DRIVER_AMBAPP_GAISLER_GPTIMER_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GPTIMER) +#define DRIVER_AMBAPP_GAISLER_GR1553B_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GR1553B) +#define DRIVER_AMBAPP_GAISLER_GRADCDAC_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRADCDAC) +#define DRIVER_AMBAPP_GAISLER_GRAES_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRAESDMA) +#define DRIVER_AMBAPP_GAISLER_GRCAN_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRCAN) +#define DRIVER_AMBAPP_GAISLER_GRCTM_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRCTM) +#define DRIVER_AMBAPP_GAISLER_GRETH_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_ETHMAC) +#define DRIVER_AMBAPP_GAISLER_GRGPIO_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GPIO) +#define DRIVER_AMBAPP_GAISLER_GRPCI2_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRPCI2) +#define DRIVER_AMBAPP_GAISLER_GRPCI_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_PCIFBRG) +#define DRIVER_AMBAPP_GAISLER_GRPWM_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRPWM) +#define DRIVER_AMBAPP_GAISLER_GRPWRX_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_PW2APB) +#define DRIVER_AMBAPP_GAISLER_GRSPW_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPW) +#define DRIVER_AMBAPP_GAISLER_GRTC_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRTC) +#define DRIVER_AMBAPP_GAISLER_GRTM_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GRTM) +#define DRIVER_AMBAPP_GAISLER_I2CMST_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_I2CMST) +#define DRIVER_AMBAPP_GAISLER_OCCAN_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_CANAHB) +#define DRIVER_AMBAPP_GAISLER_PCIF_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_PCIF) +#define DRIVER_AMBAPP_GAISLER_PCITRACE_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_PCITRACE) +#define DRIVER_AMBAPP_GAISLER_SPICTRL_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPICTRL) +#define DRIVER_AMBAPP_GAISLER_SPWCUC_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPWCUC) +#define DRIVER_AMBAPP_GAISLER_SPW_ROUTER_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPW_ROUTER) + +/*** ESA Hardware Device Driver IDs ***/ +#define DRIVER_AMBAPP_ESA_MCTRL_ID DRIVER_AMBAPP_ID(VENDOR_ESA, ESA_MCTRL) +#define DRIVER_AMBAPP_MCTRL_ID DRIVER_AMBAPP_ESA_MCTRL_ID struct amba_dev_id { unsigned short vendor; diff --git a/c/src/lib/libbsp/sparc/shared/include/genirq.h b/c/src/lib/libbsp/sparc/shared/include/genirq.h new file mode 100644 index 0000000000..1b83698d57 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/genirq.h @@ -0,0 +1,107 @@ +/* General Shared Interrupt handling function interface + * + * The functions does not manipulate the IRQ controller or the + * interrupt level of the CPU. It simply helps the caller with + * managing shared interrupts where multiple interrupt routines + * share on interrupt vector/number. + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GENIRQ_H__ +#define __GENIRQ_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*genirq_handler)(void *arg); +typedef void* genirq_t; + +struct genirq_stats { + unsigned int irq_cnt; +}; + +/* Initialize the genirq interface. Must be the first function + * called. + * + * Returns zero on success, otherwise failure. + */ +extern genirq_t genirq_init(int number_of_irqs); + +/* Free the dynamically allocated memory that the genirq interface has + * allocated. + * + * Returns zero on success, otherwise failure. + */ +extern void genirq_destroy(genirq_t d); + +/* Check IRQ number validity + * + * Returns zero for valid IRQ numbers, -1 of invalid IRQ numbers. + */ +extern int genirq_check(genirq_t d, int irq); + +/* Register shared interrupt handler. + * + * \param irq The interrupt number to register ISR on + * \param isr The interrupt service routine called upon IRQ + * \param arg The argument given to isr() when called. + * + * Return Values + * -1 = Failed + * 0 = Handler registered Successfully, first handler on this IRQ + * 1 = Handler registered Successfully, _not_ first handler on this IRQ + */ +extern int genirq_register(genirq_t d, int irq, genirq_handler isr, void *arg); + +/* Unregister an previous registered interrupt handler + * + * Return Values + * -1 = ISR not registered before + * 0 = ISR unregistered + * 1 = Unable to unregister enabled ISR + */ +extern int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg); + +/* Enables IRQ only for this isr[arg] combination. Records if this + * is the first interrupt enable, only then must interrupts be enabled + * on the interrupt controller. + * + * IRQs must be disabled before entering this function. + * + * Return values + * -1 = Failure, for example isr[arg] not registered on this irq + * 0 = IRQ must be enabled, it is the first IRQ handler to be enabled + * 1 = IRQ has already been enabled, either by isr[arg] or by another handler + */ +extern int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg); + +/* Disables IRQ only for this isr[arg] combination. Records if this + * is the only interrupt handler that is enabled on this IRQ, only then + * must interrupts be disabled on the interrupt controller. + * + * IRQs must be disabled before entering this function. + * + * Return values + * -1 = Failure, for example isr[arg] not registered on this irq + * 0 = IRQ must be disabled, no ISR are enabled for this IRQ + * 1 = ISR has already been disabled, or other ISRs are still enabled + */ +extern int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg); + +/* Must be called by user when an IRQ has fired, the argument 'irq' + * is the IRQ number of the IRQ which was fired. + */ +extern void genirq_doirq(genirq_t d, int irq); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/gpiolib.h b/c/src/lib/libbsp/sparc/shared/include/gpiolib.h new file mode 100644 index 0000000000..466a131830 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gpiolib.h @@ -0,0 +1,90 @@ +/* GPIO Library interface + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GPIOLIB_H__ +#define __GPIOLIB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* GPIO Config of one GPIO port */ +struct gpiolib_config { + char mask; /* 0=Masked/1=Unmasked IRQ */ + char irq_level; /* Edge or Level triggered IRQ */ + char irq_polarity; /* Polarity of IRQ */ +}; + +#define GPIOLIB_IRQ_EDGE 0 +#define GPIOLIB_IRQ_LEVEL 1 + +#define GPIOLIB_IRQ_POL_LOW 0 +#define GPIOLIB_IRQ_POL_HIGH 1 + +/* Libarary initialize function must be called befor any other */ +extern int gpiolib_initialize(void); + +/*** User Interface ***/ + +extern void *gpiolib_open(int port); +extern void *gpiolib_open_by_name(char *devName); +extern void gpiolib_close(void *handle); + +/* Show the current status one or all GPIO ports in the system. + * Int port is port nunber, if port = -1 selects all ports. + * + * If port != -1, handle is used to get port. + * If port != -1, handle == NULL, then port is used as port number + */ +extern void gpiolib_show(int port, void *handle); + +extern int gpiolib_set_config(void *handle, struct gpiolib_config *cfg); +extern int gpiolib_set(void *handle, int dir, int val); +extern int gpiolib_get(void *handle, int *inval); +extern int gpiolib_irq_clear(void *handle); +extern int gpiolib_irq_enable(void *handle); +extern int gpiolib_irq_disable(void *handle); +extern int gpiolib_irq_force(void *handle); +extern int gpiolib_irq_register(void *handle, void *func, void *arg); + +/*** Driver Interface ***/ + +struct gpiolib_info { + char devName[64]; +}; + +struct gpiolib_drv_ops { + int (*config)(void *handle, struct gpiolib_config *cfg); + int (*get)(void *handle, int *val); + int (*irq_opts)(void *handle, unsigned int options); + int (*irq_register)(void *handle, void *func, void *arg); + int (*open)(void *handle); + int (*set)(void *handle, int dir, int outval); + int (*show)(void *handle); + int (*get_info)(void *handle, struct gpiolib_info *pinfo); +}; + +#define GPIOLIB_IRQ_ENABLE 0x01 +#define GPIOLIB_IRQ_DISABLE 0x02 +#define GPIOLIB_IRQ_CLEAR 0x04 +#define GPIOLIB_IRQ_FORCE 0x08 + +struct gpiolib_drv { + struct gpiolib_drv_ops *ops; +}; + +/* Register a GPIO port */ +extern int gpiolib_drv_register(struct gpiolib_drv *drv, void *handle); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/gr1553b.h b/c/src/lib/libbsp/sparc/shared/include/gr1553b.h new file mode 100644 index 0000000000..33a79bfdb6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553b.h @@ -0,0 +1,365 @@ +/* GR1553B driver, used by BC, RT and/or BM driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + * + * OVERVIEW + * ======== + * This driver controls the GR1553B device regardless of interfaces supported + * (BC, RT and/or BM). The device can be located at an on-chip AMBA or an + * AMBA-over-PCI bus. This driver provides an interface for the BC, RT and BM + * drivers to use. Since the different interfaces are accessed over the same + * register interface on the same core, the other drivers must share a GR1553B + * device. Any combination of interface functionality is supported, but the RT + * and BC functionality can nnot be used simultaneously due to hardware + * limitation. + * + */ + +#ifndef __GR1553B_H__ +#define __GR1553B_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* The GR1553B registers */ +struct gr1553b_regs { + /* Common Registers */ + volatile unsigned int irq; /* 0x00 IRQ register */ + volatile unsigned int imask; /* 0x04 IRQ enable mask */ + int unused0[(0x10-0x08)/4]; + volatile unsigned int hwcfg; /* 0x10 HW config register */ + + int unused1[(0x40-0x14)/4]; /* Padding */ + + /* BC Registers */ + volatile unsigned int bc_stat; /* 0x40 BC status */ + volatile unsigned int bc_ctrl; /* 0x44 BC Action register */ + volatile unsigned int bc_bd; /* 0x48 BC transfer list pointer */ + volatile unsigned int bc_abd; /* 0x4c BC async list pointer */ + volatile unsigned int bc_timer; /* 0x50 BC timer register */ + volatile unsigned int bc_wake; /* 0x54 BC wakeup control register */ + volatile unsigned int bc_irqptr;/* 0x58 BC transfer IRQ pointer */ + volatile unsigned int bc_busmsk;/* 0x5C BC per-RT bus mask register */ + + int unused2[(0x68-0x60)/4]; /* Padding */ + + volatile unsigned int bc_slot; /* 0x48 BC Current BD pointer */ + volatile unsigned int bc_aslot; /* 0x4c BC Current async BD pointer */ + + int unused3[(0x80-0x70)/4]; /* Padding */ + + /* RT Registers */ + volatile unsigned int rt_stat; /* 0x80 RT status */ + volatile unsigned int rt_cfg; /* 0x84 RT config register */ + volatile unsigned int rt_stat2; /* 0x88 RT bus status bits */ + volatile unsigned int rt_statw; /* 0x8c RT status words */ + volatile unsigned int rt_sync; /* 0x90 RT bus synchronize */ + volatile unsigned int rt_tab; /* 0x94 RT subaddress table base */ + volatile unsigned int rt_mcctrl;/* 0x98 RT valid mode code mask */ + int unused4[(0xa4-0x9c)/4]; + volatile unsigned int rt_ttag; /* 0xa4 RT time tag register */ + int unused5; /* 0xa8 RESERVED */ + volatile unsigned int rt_evsz; /* 0xac RT event log end pointer */ + volatile unsigned int rt_evlog; /* 0xb0 RT event log position */ + volatile unsigned int rt_evirq; /* 0xb4 RT event log IRQ position */ + + int unused6[(0xc0-0xb8)/4]; /* Padding */ + + /* BM Registers */ + volatile unsigned int bm_stat; /* 0xc0 BM status */ + volatile unsigned int bm_ctrl; /* 0xc4 BM control register */ + volatile unsigned int bm_adr; /* 0xc8 BM address filter */ + volatile unsigned int bm_subadr;/* 0xcc BM subaddress filter */ + volatile unsigned int bm_mc; /* 0xd0 BM mode code filter */ + volatile unsigned int bm_start; /* 0xd4 BM log start address */ + volatile unsigned int bm_end; /* 0xd8 BM log size/alignment mask */ + volatile unsigned int bm_pos; /* 0xdc BM log position */ + volatile unsigned int bm_ttag; /* 0xe0 BM time tag register */ +}; + +#define GR1553BC_KEY 0x15520000 +#define GR1553RT_KEY 0x15530000 + +/* IRQ Definitions */ +#define GR1553BC_IRQLOG_SIZE 64 +#define GR1553BC_IRQLOG_CNT (GR1553BC_IRQLOG_SIZE/sizeof(uint32_t)) + +/*** IRQ Flag Register ***/ +#define GR1553B_IRQ_BCEV_BIT 0 +#define GR1553B_IRQ_BCD_BIT 1 +#define GR1553B_IRQ_BCWK_BIT 2 +#define GR1553B_IRQ_RTEV_BIT 8 +#define GR1553B_IRQ_RTD_BIT 9 +#define GR1553B_IRQ_RTTE_BIT 10 +#define GR1553B_IRQ_BMD_BIT 16 +#define GR1553B_IRQ_BMTOF_BIT 17 + +#define GR1553B_IRQ_BCEV (1<<GR1553B_IRQ_BCEV_BIT) +#define GR1553B_IRQ_BCD (1<<GR1553B_IRQ_BCD_BIT) +#define GR1553B_IRQ_BCWK (1<<GR1553B_IRQ_BCWK_BIT) +#define GR1553B_IRQ_RTEV (1<<GR1553B_IRQ_RTEV_BIT) +#define GR1553B_IRQ_RTD (1<<GR1553B_IRQ_RTD_BIT) +#define GR1553B_IRQ_RTTE (1<<GR1553B_IRQ_RTTE_BIT) +#define GR1553B_IRQ_BMD (1<<GR1553B_IRQ_BMD_BIT) +#define GR1553B_IRQ_BMTOF (1<<GR1553B_IRQ_BMTOF_BIT) + +/*** IRQ Enable Register ***/ +#define GR1553B_IRQEN_BCEVE_BIT 0 +#define GR1553B_IRQEN_BCDE_BIT 1 +#define GR1553B_IRQEN_BCWKE_BIT 2 +#define GR1553B_IRQEN_RTEVE_BIT 8 +#define GR1553B_IRQEN_RTDE_BIT 9 +#define GR1553B_IRQEN_RTTEE_BIT 10 +#define GR1553B_IRQEN_BMDE_BIT 16 +#define GR1553B_IRQEN_BMTOE_BIT 17 + +#define GR1553B_IRQEN_BCEVE (1<<GR1553B_IRQEN_BCEVE_BIT) +#define GR1553B_IRQEN_BCDE (1<<GR1553B_IRQEN_BCDE_BIT) +#define GR1553B_IRQEN_BCWKE (1<<GR1553B_IRQEN_BCWKE_BIT) +#define GR1553B_IRQEN_RTEVE (1<<GR1553B_IRQEN_RTEVE_BIT) +#define GR1553B_IRQEN_RTDE (1<<GR1553B_IRQEN_RTDE_BIT) +#define GR1553B_IRQEN_RTTEE (1<<GR1553B_IRQEN_RTTEE_BIT) +#define GR1553B_IRQEN_BMDE (1<<GR1553B_IRQEN_BMDE_BIT) +#define GR1553B_IRQEN_BMTOE (1<<GR1553B_IRQEN_BMTOE_BIT) + +/*** BC Status Register ***/ +#define GR1553B_BC_STAT_SCST_BIT 0 +#define GR1553B_BC_STAT_SCADL_BIT 3 +#define GR1553B_BC_STAT_ASST_BIT 8 +#define GR1553B_BC_STAT_ASADL_BIT 11 +#define GR1553B_BC_STAT_BCSUP_BIT 31 + +#define GR1553B_BC_STAT_SCST (0x3<<GR1553B_BC_STAT_SCST_BIT) +#define GR1553B_BC_STAT_SCADL (0x1f<<GR1553B_BC_STAT_SCADL_BIT) +#define GR1553B_BC_STAT_ASST (0x3<<GR1553B_BC_STAT_ASST_BIT) +#define GR1553B_BC_STAT_ASADL (0x1f<<GR1553B_BC_STAT_ASADL_BIT) +#define GR1553B_BC_STAT_BCSUP (1<<GR1553B_BC_STAT_BCSUP_BIT) + +/*** BC Action Register ***/ +#define GR1553B_BC_ACT_SCSRT_BIT 0 +#define GR1553B_BC_ACT_SCSUS_BIT 1 +#define GR1553B_BC_ACT_SCSTP_BIT 2 +#define GR1553B_BC_ACT_SETT_BIT 3 +#define GR1553B_BC_ACT_CLRT_BIT 4 +#define GR1553B_BC_ACT_ASSRT_BIT 8 +#define GR1553B_BC_ACT_ASSTP_BIT 9 +#define GR1553B_BC_ACT_BCKEY_BIT 16 + +#define GR1553B_BC_ACT_SCSRT (1<<GR1553B_BC_ACT_SCSRT_BIT) +#define GR1553B_BC_ACT_SCSUS (1<<GR1553B_BC_ACT_SCSUS_BIT) +#define GR1553B_BC_ACT_SCSTP (1<<GR1553B_BC_ACT_SCSTP_BIT) +#define GR1553B_BC_ACT_SETT (1<<GR1553B_BC_ACT_SETT_BIT) +#define GR1553B_BC_ACT_CLRT (1<<GR1553B_BC_ACT_CLRT_BIT) +#define GR1553B_BC_ACT_ASSRT (1<<GR1553B_BC_ACT_ASSRT_BIT) +#define GR1553B_BC_ACT_ASSTP (1<<GR1553B_BC_ACT_ASSTP_BIT) +#define GR1553B_BC_ACT_BCKEY (0xffff<<GR1553B_BC_ACT_BCKEY_BIT) + +/*** BC Timer Register ***/ +#define GR1553B_BC_TIMER_SCTM_BIT 0 + +#define GR1553B_BC_TIMER_SCTM (0xffffff<<GR1553B_BC_TIMER_SCTM_BIT) + +/*** BC Wake-up control Register ***/ +#define GR1553B_BC_WAKE_TIME_BIT 0 +#define GR1553B_BC_WAKE_WKEN_BIT 31 + +#define GR1553B_BC_WAKE_TIME (0xffffff<<GR1553B_BC_WAKE_TIME_BIT) +#define GR1553B_BC_WAKE_WKEN (1<GR1553B_BC_WAKE_WKEN_BIT) + +/*** RT status Register ***/ +#define GR1553B_RT_STAT_RUN_BIT 0 +#define GR1553B_RT_STAT_SHDB_BIT 1 +#define GR1553B_RT_STAT_SHDA_BIT 2 +#define GR1553B_RT_STAT_ACT_BIT 3 +#define GR1553B_RT_STAT_RTSUP_BIT 31 + +#define GR1553B_RT_STAT_RUN (1<<GR1553B_RT_STAT_RUN_BIT) +#define GR1553B_RT_STAT_SHDB (1<<GR1553B_RT_STAT_SHDB_BIT) +#define GR1553B_RT_STAT_SHDA (1<<GR1553B_RT_STAT_SHDA_BIT) +#define GR1553B_RT_STAT_ACT (1<<GR1553B_RT_STAT_ACT_BIT) +#define GR1553B_RT_STAT_RTSUP (1<<GR1553B_RT_STAT_RTSUP_BIT) + + +/*** RT Config Register ***/ +#define GR1553B_RT_CFG_RTEN_BIT 0 +#define GR1553B_RT_CFG_RTADDR_BIT 1 +#define GR1553B_RT_CFG_RTKEY_BIT 16 + +#define GR1553B_RT_CFG_RTEN (1<<GR1553B_RT_CFG_RTEN_BIT) +#define GR1553B_RT_CFG_RTADDR (1<<GR1553B_RT_CFG_RTADDR_BIT) +#define GR1553B_RT_CFG_RTKEY (0xffff<<GR1553B_RT_CFG_RTKEY_BIT) + +/*** RT Bus Status Register ***/ +#define GR1553B_RT_STAT2_RTEN_BIT 0 +#define GR1553B_RT_STAT2_DBCA_BIT 1 +#define GR1553B_RT_STAT2_SSF_BIT 2 +#define GR1553B_RT_STAT2_BUSY_BIT 3 +#define GR1553B_RT_STAT2_SREQ_BIT 4 + +#define GR1553B_RT_STAT2_RTEN (1<<GR1553B_RT_STAT2_RTEN_BIT) +#define GR1553B_RT_STAT2_DBCA (1<<GR1553B_RT_STAT2_DBCA_BIT) +#define GR1553B_RT_STAT2_SSF (1<<GR1553B_RT_STAT2_SSF_BIT) +#define GR1553B_RT_STAT2_BUSY (1<<GR1553B_RT_STAT2_BUSY_BIT) +#define GR1553B_RT_STAT2_SREQ (1<<GR1553B_RT_STAT2_RTEN_BIT) + +/*** RT Status Words Register ***/ +#define GR1553B_RT_STATW_VECW_BIT 0 +#define GR1553B_RT_STATW_BITW_BIT 16 + +#define GR1553B_RT_STATW_VECW (0xffff<<GR1553B_RT_STATW_VECW_BIT) +#define GR1553B_RT_STATW_BITW (0xffff<<GR1553B_RT_STATW_BITW_BIT) + +/*** RT Sync Register ***/ +#define GR1553B_RT_SYNC_SYD_BIT 0 +#define GR1553B_RT_SYNC_SYTM_BIT 16 + +#define GR1553B_RT_SYNC_SYD (0xffff<<GR1553B_RT_SYNC_SYD_BIT) +#define GR1553B_RT_SYNC_SYTM (0xffff<<GR1553B_RT_SYNC_SYTM_BIT) + +/*** RT Sub adress table Register ***/ +#define GR1553B_RT_TAB_SATB_BIT 0 + +#define GR1553B_RT_TAB_SATB (0xffff<<GR1553B_RT_TAB_SATB_BIT) + +/*** RT Mode code control Register ***/ +#define GR1553B_RT_MCCTRL_S_BIT 0 +#define GR1553B_RT_MCCTRL_SB_BIT 2 +#define GR1553B_RT_MCCTRL_SD_BIT 4 +#define GR1553B_RT_MCCTRL_SDB_BIT 6 +#define GR1553B_RT_MCCTRL_TS_BIT 8 +#define GR1553B_RT_MCCTRL_TSB_BIT 10 +#define GR1553B_RT_MCCTRL_TVW_BIT 12 +#define GR1553B_RT_MCCTRL_TBW_BIT 14 +#define GR1553B_RT_MCCTRL_DBC_BIT 16 +#define GR1553B_RT_MCCTRL_IST_BIT 18 +#define GR1553B_RT_MCCTRL_ISTB_BIT 20 +#define GR1553B_RT_MCCTRL_ITF_BIT 22 +#define GR1553B_RT_MCCTRL_ITFB_BIT 24 +#define GR1553B_RT_MCCTRL_RRT_BIT 26 +#define GR1553B_RT_MCCTRL_RRTB_BIT 28 + +#define GR1553B_RT_MCCTRL_S (1<<GR1553B_RT_MCCTRL_S_BIT) +#define GR1553B_RT_MCCTRL_SB (1<<GR1553B_RT_MCCTRL_SB_BIT) +#define GR1553B_RT_MCCTRL_SD (1<<GR1553B_RT_MCCTRL_SD_BIT) +#define GR1553B_RT_MCCTRL_SDB (1<<GR1553B_RT_MCCTRL_SDB_BIT) +#define GR1553B_RT_MCCTRL_TS (1<<GR1553B_RT_MCCTRL_TS_BIT) +#define GR1553B_RT_MCCTRL_TSB (1<<GR1553B_RT_MCCTRL_TSB_BIT) +#define GR1553B_RT_MCCTRL_TVW (1<<GR1553B_RT_MCCTRL_TVW_BIT) +#define GR1553B_RT_MCCTRL_TBW (1<<GR1553B_RT_MCCTRL_TBW_BIT) +#define GR1553B_RT_MCCTRL_DBC (1<<GR1553B_RT_MCCTRL_DBC_BIT) +#define GR1553B_RT_MCCTRL_IST (1<<GR1553B_RT_MCCTRL_IST_BIT) +#define GR1553B_RT_MCCTRL_ISTB (1<<GR1553B_RT_MCCTRL_ISTB_BIT) +#define GR1553B_RT_MCCTRL_ITF (1<<GR1553B_RT_MCCTRL_ITF_BIT) +#define GR1553B_RT_MCCTRL_ITFB (1<<GR1553B_RT_MCCTRL_ITFB_BIT) +#define GR1553B_RT_MCCTRL_RRT (1<<GR1553B_RT_MCCTRL_RRT_BIT) +#define GR1553B_RT_MCCTRL_RRTB (1<<GR1553B_RT_MCCTRL_RRTB_BIT) + +/*** RT Time Tag control Register ***/ +#define GR1553B_RT_TTAG_TVAL_BIT 0 +#define GR1553B_RT_TTAG_TRES_BIT 16 + +#define GR1553B_RT_TTAG_TVAL (0xffff<<GR1553B_RT_TTAG_TVAL_BIT) +#define GR1553B_RT_TTAG_TRES (0xffff<<GR1553B_RT_TTAG_TRES_BIT) + +/*** BM Control Register ***/ +#define GR1553B_BM_STAT_BMSUP_BIT 31 + +#define GR1553B_BM_STAT_BMSUP (1<<GR1553B_BM_STAT_BMSUP_BIT) + +/*** BM Control Register ***/ +#define GR1553B_BM_CTRL_BMEN_BIT 0 +#define GR1553B_BM_CTRL_MANL_BIT 1 +#define GR1553B_BM_CTRL_UDWL_BIT 2 +#define GR1553B_BM_CTRL_IMCL_BIT 3 + +#define GR1553B_BM_CTRL_BMEN (1<<GR1553B_BM_CTRL_BMEN_BIT) +#define GR1553B_BM_CTRL_MANL (1<<GR1553B_BM_CTRL_MANL_BIT) +#define GR1553B_BM_CTRL_UDWL (1<<GR1553B_BM_CTRL_UDWL_BIT) +#define GR1553B_BM_CTRL_IMCL (1<<GR1553B_BM_CTRL_IMCL_BIT) + +/*** BM RT Mode code filter Register ***/ +#define GR1553B_BM_MC_S_BIT 0 +#define GR1553B_BM_MC_SB_BIT 1 +#define GR1553B_BM_MC_SD_BIT 2 +#define GR1553B_BM_MC_SDB_BIT 3 +#define GR1553B_BM_MC_TS_BIT 4 +#define GR1553B_BM_MC_TSB_BIT 5 +#define GR1553B_BM_MC_TVW_BIT 6 +#define GR1553B_BM_MC_TBW_BIT 7 +#define GR1553B_BM_MC_DBC_BIT 8 +#define GR1553B_BM_MC_IST_BIT 9 +#define GR1553B_BM_MC_ISTB_BIT 10 +#define GR1553B_BM_MC_ITF_BIT 11 +#define GR1553B_BM_MC_ITFB_BIT 12 +#define GR1553B_BM_MC_RRT_BIT 13 +#define GR1553B_BM_MC_RRTB_BIT 14 +#define GR1553B_BM_MC_TSW_BIT 15 +#define GR1553B_BM_MC_TLC_BIT 16 +#define GR1553B_BM_MC_STS_BIT 17 +#define GR1553B_BM_MC_STSB_BIT 18 + +#define GR1553B_BM_MC_S (1<<GR1553B_BM_MC_S_BIT) +#define GR1553B_BM_MC_SB (1<<GR1553B_BM_MC_SB_BIT) +#define GR1553B_BM_MC_SD (1<<GR1553B_BM_MC_SD_BIT) +#define GR1553B_BM_MC_SDB (1<<GR1553B_BM_MC_SDB_BIT) +#define GR1553B_BM_MC_TS (1<<GR1553B_BM_MC_TS_BIT) +#define GR1553B_BM_MC_TSB (1<<GR1553B_BM_MC_TSB_BIT) +#define GR1553B_BM_MC_TVW (1<<GR1553B_BM_MC_TVW_BIT) +#define GR1553B_BM_MC_TBW (1<<GR1553B_BM_MC_TBW_BIT) +#define GR1553B_BM_MC_DBC (1<<GR1553B_BM_MC_DBC_BIT) +#define GR1553B_BM_MC_IST (1<<GR1553B_BM_MC_IST_BIT) +#define GR1553B_BM_MC_ISTB (1<<GR1553B_BM_MC_ISTB_BIT) +#define GR1553B_BM_MC_ITF (1<<GR1553B_BM_MC_ITF_BIT) +#define GR1553B_BM_MC_ITFB (1<<GR1553B_BM_MC_ITFB_BIT) +#define GR1553B_BM_MC_RRT (1<<GR1553B_BM_MC_RRT_BIT) +#define GR1553B_BM_MC_RRTB (1<<GR1553B_BM_MC_RRTB_BIT) +#define GR1553B_BM_MC_TSW (1<<GR1553B_BM_MC_TSW_BIT) +#define GR1553B_BM_MC_TLC (1<<GR1553B_BM_MC_TLC_BIT) +#define GR1553B_BM_MC_STS (1<<GR1553B_BM_MC_STS_BIT) +#define GR1553B_BM_MC_STSB (1<<GR1553B_BM_MC_STSB_BIT) + +/*** BM RT Mode code filter Register ***/ +#define GR1553B_BM_TTAG_VAL_BIT 0 +#define GR1553B_BM_TTAG_RES_BIT 24 + +#define GR1553B_BM_TTAG_VAL (0xffffff<<GR1553B_BM_TTAG_VAL_BIT) +#define GR1553B_BM_TTAG_RES (0xff<<GR1553B_BM_TTAG_RES_BIT) + +/* Register GR1553B driver */ +extern void gr1553_register(void); + +/*** BC Device allocation ***/ +/* Allocate a BC device. Minor is assigned to a device in the order + * they are registered to the driver. + */ +extern struct drvmgr_dev **gr1553_bc_open(int minor); +/* Free a BC device previously allocated */ +extern void gr1553_bc_close(struct drvmgr_dev **dev); + +/*** RT Device allocation ***/ +/* Allocate a BC device. Minor is assigned to a device in the order + * they are registered to the driver. + */ +extern struct drvmgr_dev **gr1553_rt_open(int minor); +/* Free a BC device previously allocated */ +extern void gr1553_rt_close(struct drvmgr_dev **dev); + +/*** BM Device allocation ***/ +/* Allocate a BC device. Minor is assigned to a device in the order + * they are registered to the driver. + */ +extern struct drvmgr_dev **gr1553_bm_open(int minor); +/* Free a BC device previously allocated */ +extern void gr1553_bm_close(struct drvmgr_dev **dev); + +#ifdef __cplusplus +} +#endif + +#endif /* __GR1553B_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/gr1553bc.h b/c/src/lib/libbsp/sparc/shared/include/gr1553bc.h new file mode 100644 index 0000000000..cb71b92394 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553bc.h @@ -0,0 +1,250 @@ +/* GR1553B BC driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + * + * OVERVIEW + * ======== + * This driver controls the BC device, located at an on-chip AMBA or an + * AMBA-over-PCI bus. The driver operates the BC device and provides you + * with interrupt services and core control. The driver start execution of + * a synchronuos and/or an asynchronous BC descriptor List. The list contains + * a descriptor table and a software description to make some operations + * possible, for example translate descriptor-address into descriptor-number. + * + * BC descriptors are generated by the list API, available in gr1553bc_list.h. + * + * See gr1553bc_list.h for more information. + */ + +#ifndef __GR1553BC_H__ +#define __GR1553BC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +struct gr1553bc_list; +struct gr1553bc_major; +struct gr1553bc_minor; +struct gr1553bc_minor_cfg; +struct gr1553bc_major_cfg; + +#ifdef __cplusplus +} +#endif + +#include <stdint.h> +#include <gr1553bc_list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Register GR1553B driver needed by BC driver */ +extern void gr1553bc_register(void); + +/* A BC descriptor accessed as is */ +struct gr1553bc_bd_raw { + volatile uint32_t words[4]; +}; + +/* A BC descriptor accessed as a transfer descriptor */ +struct gr1553bc_bd_tr { + volatile uint32_t settings[2]; + volatile uint32_t dptr; + volatile uint32_t status; +}; + +/* A BC descriptor accessed as a conditional descriptor */ +struct gr1553bc_bd_cond { + volatile uint32_t cond; + volatile uint32_t bdptr; + volatile uint32_t padding[2]; +}; + +/* A BC descriptor accessed any way */ +union gr1553bc_bd { + struct gr1553bc_bd_raw raw; + struct gr1553bc_bd_tr tr; + struct gr1553bc_bd_cond cond; +}; + +/* Current state of the BC hardware */ +struct gr1553bc_status { + unsigned int status; + unsigned int time; +}; + +#define KEEP_TIMESLOT 0x10 +/* Initialize a BC descriptor. The words written is controllable by + * the flags argument. + * + * flags: + * bit[N=0..3]: 1 = set BD wordN according to argument wordN, + * 0 = do not modify BD wordN + * + * If bit KEEP_TIMESLOT is set the time slot of word0 is preserved, + * this bit only have an affect when the descriptor is a transfer + * descriptor. + */ +extern void gr1553bc_bd_init( + union gr1553bc_bd *bd, + unsigned int flags, + uint32_t word0, + uint32_t word1, + uint32_t word2, + uint32_t word3 + ); + +/* Initialize a Transfer descriptor + * + * Arguments: + * struct gr1553bc_bd_tr *bd + * uint32_t setting0 + * uint32_t setting1 + * uint32_t data + * uint32_t status + */ +#define gr1553bc_bd_tr_init(bd, set0, set1, data, status) \ + gr1553bc_bd_init((union gr1553bc_bd *)bd,\ + 0xf, set0, set1, data, status) +/* Initializa a Condition descriptor + * + * Arguments: + * struct gr1553bc_bd_cond *bd + * uint32_t cond + * uint32_t jump_adr + */ +#define gr1553bc_bd_cond_init(bd, cond, jump_adr) \ + gr1553bc_bd_init((union gr1553bc_bd *)bd, \ + 0xf, cond, jump_adr, 0, 0) + +/* Size of a descriptor */ +#define GR1553BC_BD_SIZE sizeof(struct gr1553bc_bd_raw) + +/* Alignment of a descriptor */ +#define GR1553BC_BD_ALIGN 16 + +/* End of list marker */ +#define GR1553BC_TR_EOL 0x80ffffff + +#define GR1553BC_BD_TYPE 0x80000000 + +/* Condition descriptor bits */ +#define GR1553BC_UNCOND_JMP 0x820000ff +#define GR1553BC_UNCOND_IRQ 0x860000ff +#define GR1553BC_UNCOND_NOJMP 0x82000000 + +/* Transfer descriptor bits */ +#define GR1553BC_TR_DUMMY_0 0x00000000 +#define GR1553BC_TR_DUMMY_1 0x80000000 + +#define GR1553BC_TR_TIME 0x0000ffff + +#define GR1553BC_TR_EXTTRIG 0x40000000 + +/* Take a GR1553BC hardware device identified by instance index (minor). + * A pointer is returned that is used internally by the GR1553BC + * driver, it is used as an input paramter 'bc' to all other + * functions that manipulate the hardware. + */ +extern void *gr1553bc_open(int minor); + +extern void gr1553bc_close(void *bc); + +/* Stores Current Major/Minor frame number and the Slot number executing + * into the location indicated by 'mid'. There may be two lists executing + * in "parallel", the 'async' argument select for which list the MID is + * looked up, the Syncronous (async=0) list or the Asynchronous (async=1) + * list. + * + */ +extern int gr1553bc_indication(void *bc, int async, int *mid); + +/* Trigger external time sync by writing to the BC action register. + * This may be good for debugging or if the time management is + * implemented in software. + * + * if trig=0 the external trigger memory is cleared. + * if trig!=0 the external trigger memory is set. + */ +extern void gr1553bc_ext_trig(void *bc, int trig); + +/* Configure the GR1553BC driver */ +/*extern int gr1553bc_config(struct gr1553bc_config *cfg);*/ + +/* Start major frame processing. At least one list pointer must be + * non-zero to affect BC operation. The BC communication is enabled + * depending on list and Interrupts are enabled. This function can + * be called multiple times. + * + * If a list is already executing it will be replaced with the new + * list. + * + * list - Schedule Transfer List + * list_async - Asynchronous list + */ +extern int gr1553bc_start + ( + void *bc, + struct gr1553bc_list *list, + struct gr1553bc_list *list_async + ); + +/* Pause GR1553B BC scheduled transfers. + * + * Does not affect asynchronous operation. + */ +extern int gr1553bc_pause(void *bc); + +/* Restart GR1553B BC scheduled transfers, after being paused + * + * Does not affect asynchronous operation. + */ +extern int gr1553bc_restart(void *bc); + +/* Stop BC transmission. + * + * OPTIONS + * bit0 - 1=STOP schedule list + * bit1 - 1=STOP asynchronous list + */ +extern int gr1553bc_stop(void *bc, int options); + +/* Standard IRQ function setup. IRQ can be generated by condition descriptors + * or by transfer descriptors or by errors. + * + * Condition descriptors are inserted into the list by user, each condition + * may have a custom function and data assigned to it, see + * gr1553bc_slot_irq_prepare(). IRQs generated by condition descriptors are + * not handled by this function. + * + * Transfer descriptors can generate IRQ if enabled by user. + * + * IRQs generated by transfer descriptors or by BC errors (DMA error etc.) + * is handled by this standard ISR handler. + */ +extern int gr1553bc_irq_setup + ( + void *bc, + bcirq_func_t func, + void *data + ); + +/* Get Current BC hardware state/status. The Status is stored into the + * area pointed to by status. See "struct gr1553bc_status" for more + * info. + */ +extern void gr1553bc_status(void *bc, struct gr1553bc_status *status); + +#ifdef __cplusplus +} +#endif + +#endif /* __GR1553BC_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/gr1553bc_list.h b/c/src/lib/libbsp/sparc/shared/include/gr1553bc_list.h new file mode 100644 index 0000000000..24f52815af --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553bc_list.h @@ -0,0 +1,707 @@ +/* + * GR1553B BC driver, Descriptor LIST handling + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GR1553BC_LIST_H__ +#define __GR1553BC_LIST_H__ + +/*!\file doc/gr1553bc_list.h + * \brief GR1553B BC driver + * + * \section OVERVIEW + * + * The BC device driver can schedule synchronous and asynchronous lists + * of descriptors. The list contains a descriptor table and a software + * description to make some operations possible, for example translate + * descriptor-address into descriptor-number. + * + * This is the LIST API. It provides functionality to create and manage + * a BC descriptor list. + * + * A list is built up by the following build blocks: + * - Major Frame (Consists of N Minor Frames) + * - Minor Frame (Consists of up to 32 1553 Message Slots) + * - Message Slot (Transfer/Condition BC descriptor) + * + * The user can configure lists with different configuration of number of + * Major Frames, Minor Frame and messages slots within a Minor Frame. The + * List manages a strait descriptor table (may be changed) and a Frame/Slot + * tree in order to easily find it's way through all descriptor created. + * + * Each Minor frame consist of up to 32 message slot and 2 message slots + * for time management and descriptor find operations. The list can manage + * time slots per minor frame, for example a minor frame may be programmed + * to take 8ms and when the user allocate a message slot within that Minor + * frame the time spcified will be subtracted from the 8ms, and when the + * message slot is freed the time will be returned to the Minor frame again. + * + * A Major, Minor and Message Slots are identified using a MID (Message-ID). + * The MID is a way for the user to avoid using pointers are talk with the + * list API in an easier way. For example a condition Slot that should jump + * to a transfer slot can be created by knowing "MID and Jump-To-MID". When + * allocating a Slot (with or without time) the user may specify a certain + * Slot or a Minor frame, when a Minor frame is given then the API will find + * a free Slot as early in the Minor Frame as possible and return it to the + * user. + * + * A MID can be created using the macros: + * GR1553BC_ID(major,minor,slot) - ID of a SLOT + * GR1553BC_MINOR_ID(major,minor) - ID of a MINOR (Slot=0xff) + * GR1553BC_MAJOR_ID(major) - ID of a Major (Minor=0xff,Slot=0xff) + * + * The typical approach create lists is in the following order: + * -# gr1553bc_list_alloc(&list, MAJOR_CNT) + * -# gr1553bc_list_config(list, &listcfg) + * -# Create all Major Frames and Minor frame, for each major frame: + * a) gr1553bc_major_alloc_skel(&major, &major_minor_cfg) + * b) gr1553bc_list_set_major(list, &major, MAJOR_NUM) + * -# link end Major Frames together: + * a) gr1553bc_list_set_major(&major7, &major0) // Connect Major frames + * -# gr1553bc_list_table_alloc() (Allocate Descriptor Table) + * -# gr1553bc_list_table_build() (Build Descriptor Table from Majors/Minors) + * -# Allocate and initialize Descriptors pre defined before starting: + * -## gr1553bc_slot_alloc(list, &MID, TIME_REQUIRED, ..) + * -## gr1553bc_slot_transfer(MID, ...) + * -# START BC HARDWARE BY SHCDULING ABOVE LIST + * -# Operate on List + * + * + * \section bc_list_update Changing a scheduled BC list (during BC-runtime) + * + * One can use the INDICATION service to avoid modifying + * a descriptor currently in use by the BC core. One can also in most cases + * do descriptor initialization in three steps: Init Descriptor as Dummy + * with and allocated time (often done before starting/scheduling list), + * then modify transfer options and data-pointers, then clear the Dummy + * bit in one atomic data store. This approach will avoid potential races + * between software has hardware. + * + * + * \section bc_memory_setup Custom Memory Setup + * + * For designs where dynamically memory is not an option, or the driver + * is used on a AMBA-over-PCI bus (where malloc() does not work), the + * API allows the user to provide custom addresses for descriptor table + * and object descriptions (lists, major frames, minor frames). Custom + * descriptor table is probably the most interesting thing for most, it + * is setup with gr1553bc_list_table_alloc(list, CUSTOM_ADDRESS). + * + * Object descriptions are normally allocated during initialization + * procedure by providing the API with a object configuration, for + * example a Major Frame configuration enables the API to allocate + * the software description of a Major Frame with all it's Minor frames. + * + * + * \section major Major Frame + * + * Consists of multiple Minor frames. A Major frame may be connected/linked + * with another Major frame, this will result in a Jump Slot from last + * Minor frame in the first Major to the first Minor in the second Major. + * + * + * \section minor Minor Frame + * + * Consists of up to 32 Message Slots. The services are Time-Management and + * Slot allocation. + * + * Time-Management is optional. + * + * Time-Slot-Management can be enabled per Minor frame. A Minor frame can be + * assigned a time in microseconds. The BC will not continue to the next + * Minor frame until the time has passed. It is managed by adding an extra + * Dummy Message Slot with the total minor frame time. Each time a message + * Slot is allocated (with a certain time: Slot-Time) the Slot-Time will + * be decremented from the total time of the Minor frame. This way the + * sum of the Message Slot will always sum up to the total time of the + * Minor configuration. When a message slot is freed, the Dymmy Message + * Slot's Slot-Time is incremented with the freed Slot-Time. + * + * A Message Slot can be allocated by identifying a specific free Slot + * by the MID (Message-ID) or by letting the API allocate the first free + * Slot in the Minor Frame (Set MID Slot-ID to 0xff to identify Minor + * Frame). + * + * + * \section slot Message Slot + * + * The GR1553B BC core supports two Slot (Descriptor) Types: + * - Transfer descriptor + * - Condition descriptor (Jump, unconditional-IRQ) + * + * See the hardware manual for a detail description of a descriptor (Slot). + * + * The BC Core is unaware of lists, it steps through executing each + * descriptor as the encountered, Conditionals resulting in jumps may + * let us to create more complex arrangements of buffer descriptos (BDs) + * which we call list. + * + * Transfer BDs (TBDs) may have a time slot assigned, the BC core will wait + * until the time has expired before executing the next descriptor. Time + * slots are handled by a Minor frame in the list. + * + * A Message Slot is allocated using the gr1553bc_slot_alloc() function, + * and configured by calling one of the below functions: + * - gr1553bc_slot_irq_prepare [unconditional IRQ slot] + * - gr1553bc_slot_jump [unconditional jump] + * - gr1553bc_slot_exttrig [Dummy transfer, wait for EXTERNAL-TRIGGER] + * - gr1553bc_slot_transfer [Transfer descriptor] + * - gr1553bc_slot_empty [Create Dummy Transfer descriptor] + * - gr1553bc_slot_raw [Custom Descriptor handling] + * + * - gr1553bc_slot_dummy [Set existing Transfer descriptor to Dummy] + * - gr1553bc_slot_update [Update DataPointer|Status of a TBD] + * + * + * \section bc_IRQ Interrupt Handling + * + * There are different types of interrupts, Error IRQs or transfer IRQs. The + * Error IRQs are handled by the driver can a callback function is called. + * + * Transfer Descriptors can be programmed to generate interrupt, and + * condition descriptors can be programmed to generate interrupt + * unconditionaly (there exists more conditional types). When a Transfer + * descriptor causes IRQ the general ISR callback of the BC driver is + * called to let the user handle the interrupt. When a condition descriptor + * causes an IRQ a custom IRQ handler is called (if assigned). + * + * Transfers descriptor IRQ is enabled by configuring the descriptor. + * + * The API provides functions for placing unconditional IRQ points anywhere + * in the list. The order: + * -# gr1553bc_slot_alloc(&MID, TIME=0, ..) + * -# gr1553bc_slot_irq_prepare(MID, funcISR, data) + * -# gr1553bc_slot_irq_enable(MID) + * + * \verbatim + * void funcISR(*bd, *data) + * { + * // HANDLE ONE OR MULTIPLE DESCRIPTORS (MULTIPLE IN THIS EXAMPLE): + * int MID; + * gr1553bc_mid_from_bd(bd,&MID,NULL); + * printf("IRQ ON %06x\n", MID); + * } + * \endverbatim + * + * \ingroup GR1553BC + */ + +#include <stdint.h> +#include <gr1553bc.h> + +/**** CONFIGURATION OPTIONS ****/ + +/* Define GR1553BC_TIMESLOT to make driver take care of time + * management of minor frames. + */ +#define GR1553BC_TIMESLOT + +#define GR1553BC_MINOR_MAX 256 +#define GR1553BC_SLOT_MAX 32 + +#ifdef __cplusplus +extern "C" { +#endif + +struct gr1553bc_list; +struct gr1553bc_major; +struct gr1553bc_minor; +struct gr1553bc_minor_cfg; +struct gr1553bc_major_cfg; + +struct gr1553bc_minor_cfg { + int slot_cnt; + int timeslot; /* Total time of minor frame in us */ +}; + +struct gr1553bc_major_cfg { + int minor_cnt; /* Number of Minor Frames */ + struct gr1553bc_minor_cfg minor_cfgs[1]; +}; + +struct gr1553bc_list_cfg { + unsigned char rt_timeout[31]; /* Number of us timeout tolerance per RT */ + unsigned char bc_timeout; /* Number of us timeout tolerance of + * broadcast transfers */ + int tropt_irq_on_err; /* Generate IRQ on transfer error */ + int tropt_pause_on_err; /* Pause list on transfer error */ + int async_list; /* Set to non-zero if asyncronous list*/ +}; + +/* Default Configuration */ +extern struct gr1553bc_list_cfg gr1553bc_def_cfg; + +/* Complete list of all major frames */ +struct gr1553bc_list { + void *_table_custom; /* Config option given by user */ + void *_table; /* address of allocated bd-table */ + unsigned int table_hw; /* Descriptor table base HW-ADR */ + unsigned int table_cpu; /* Descriptor table base CPU-ADR */ + int table_size; /* Descriptor Table Size */ + void *bc; /* BC HW, needed for adr translation */ + unsigned char rt_timeout[32]; /* Tolerance per RT, default 20us + * Note: 31 is for Broadcast */ + uint32_t tropts; /* Transfer descriptor options: + * On transfer error the following bits + * do affect: + * - bit28 1=Generate IRQ + * - bit26 1=Pause transfer list + * + */ + int async_list; /* async list or not */ + int major_cnt; /* Number of Major frames */ + struct gr1553bc_major *majors[1]; /* Var-Array of Major Pointers*/ +}; + +/* Alloc a List with a maximum number of Major frames supported */ +extern int gr1553bc_list_alloc(struct gr1553bc_list **list, int max_major); + +/* Free List if allocated with gr1553bc_list_alloc() */ +extern void gr1553bc_list_free(struct gr1553bc_list *list); + +/* Configure Global List parameters + * + * \param list List to be configured and initialized. + * \param cfg List Configuration + * \param bc The BC hardware device description + * (only needed for address translation) + */ +extern int gr1553bc_list_config + ( + struct gr1553bc_list *list, + struct gr1553bc_list_cfg *cfg, + void *bc + ); + +/* Link a 'major' Major frame with next major frame + * The links affected: + * - major->next + * - major->minor[LAST]->next + */ +extern void gr1553bc_list_link_major( + struct gr1553bc_major *major, + struct gr1553bc_major *next + ); + +/* Link in a Major frame into a BC list. + * Calls gr1553bc_list_link_major() to link major frame with major-1 and + * major+1. If ending or starting major frame the frame is wrapped around. + */ +extern int gr1553bc_list_set_major( + struct gr1553bc_list *list, + struct gr1553bc_major *major, + int no); + +/* Calculate the size required in the descriptor table by one minor frame. */ +extern int gr1553bc_minor_table_size(struct gr1553bc_minor *minor); + +/* Calculate the size required for the descriptor table. + */ +extern int gr1553bc_list_table_size(struct gr1553bc_list *list); + +/* Allocate an empty descriptor table from list description suitable for + * the BC given by 'bc'. + * + * \param bdtab_custom Custom Descriptor Allocation options: + * ZERO: Dynamically allocated by Driver (CPU near RAM) + * Non-Zero: Use provided address as BASE of BD-TABLE + * Non-Zero with LSB set: Same as Non-Zero but address + * is given as HW address (used with AMBA-over-PCI to + * to specify RAM location on PCI board). + */ +extern int gr1553bc_list_table_alloc + ( + struct gr1553bc_list *list, + void *bdtab_custom + ); + +/* Free descriptor table allocated with gr1553bc_list_table_alloc() */ +extern void gr1553bc_list_table_free(struct gr1553bc_list *list); + +/* Build an empty descriptor table from list description, + * the minor frames will be linked together. + */ +extern int gr1553bc_list_table_build(struct gr1553bc_list *list); + +/* Major Frame */ +struct gr1553bc_major { + struct gr1553bc_major *next; /* Next Major Frame */ + struct gr1553bc_major_cfg *cfg; /* User Config of Major frame */ + struct gr1553bc_minor *minors[1]; /* Minor frames */ +}; + +/* Minor Frame */ +struct gr1553bc_minor { + struct gr1553bc_minor *next; /* Next Minor Frame */ + struct gr1553bc_minor_cfg *cfg; /* User Config of Minor frame */ + uint32_t alloc; /* Descripts allocated */ + + /* Note: THIS POINTER MUST BE ALIGNED ON A 128-bit BOUNDARY */ + union gr1553bc_bd *bds; /* Descriptors for this minor frame (CPU ADRS)*/ +}; + +/* Alloc a Major/Minor frame skeleton according to the configuration structure. + * The descriptor table is not allocated. + */ +extern int gr1553bc_major_alloc_skel + ( + struct gr1553bc_major **major, + struct gr1553bc_major_cfg *cfg + ); + +/* Unique Message/Descriptor ID. Can be used to identify a Major or Minor + * Frame, or a Slot. + * + * - If minor_num is 0xff, the ID identifies a Major Frame + * - If slot_num is 0xff, the ID identifies a Minor Frame + * - If non of the above is true, the ID identifies a specific Slot + */ +#define GR1553BC_ID(major_num, minor_num, slot_num) \ + ((((major_num)<<16)&0xff0000) | (((minor_num)<<8)&0xff00) | \ + ((slot_num) & 0xff)) +#define GR1553BC_MINOR_ID(major_num, minor_num) \ + GR1553BC_ID(major_num, minor_num, 0xff) +#define GR1553BC_MAJOR_ID(major_num) \ + GR1553BC_ID(major_num, 0xff, 0xff) + +#define GR1553BC_MAJID_FROM_ID(mid) (((mid) >> 16) & 0xff) +#define GR1553BC_MINID_FROM_ID(mid) (((mid) >> 8) & 0xff) +#define GR1553BC_SLOTID_FROM_ID(mid) ((mid) & 0xff) +#define GR1553BC_ID_SET_SLOT(mid, slot_num) (((mid) & ~0xff) | ((slot_num) & 0xff)) + +extern struct gr1553bc_major *gr1553bc_major_from_id + ( + struct gr1553bc_list *list, + int mid + ); + +extern struct gr1553bc_minor *gr1553bc_minor_from_id + ( + struct gr1553bc_list *list, + int mid + ); + +/* Get free time left of minor frame identified by MID 'mid' */ +extern int gr1553bc_list_freetime(struct gr1553bc_list *list, int mid); + +/* Get free time left of minor frame */ +extern int gr1553bc_minor_freetime(struct gr1553bc_minor *minor); + +/* Allocate a time slot on a minor frame, major/minor frame is identified + * by MID. The 'mid' is a input/ouput parameter, the resulting slot taken + * will be placed in 'mid', a pointer to the allocated descriptor is stored + * into bd. + * + * Major/Minor must be specified by MID, if slot is specified that slot will + * be allocated, if slot is 0xff, then the first free slot is allocated. + * + * The function fails (return negative) if timeslot is longer than remaining + * time in minor frame, if no more slots are available in minor frame, if + * MID points to a bad major/minor or major/minor/slot. + */ +extern int gr1553bc_slot_alloc( + struct gr1553bc_list *list, + int *mid, + int timeslot, + union gr1553bc_bd **bd + ); +/* Same as gr1553bc_slot_alloc but identifies a minor instead of list. + * The major/minor part of MID is ignored. + */ +extern int gr1553bc_slot_alloc2( + struct gr1553bc_minor *minor, + int *mid, + int timeslot, + union gr1553bc_bd **bd + ); + +/* Free message slot and the time associated with it. The time taken by the + * message slot is added to the END TIME descriptor, if managed by the driver + * for this minor frame. The descriptor will be + */ +extern int gr1553bc_slot_free(struct gr1553bc_list *list, int mid); +extern int gr1553bc_slot_free2(struct gr1553bc_minor *minor, int mid); + +/* Find MID from Descriptor pointer + * + * In the end of each minor frame is a unconditional jump + * to next minor frame descriptor. The hardware does not + * use the last 8 bytes of conditional descriptors, in the + * padding area a MID is stored so that we can lookup the + * MID of a descriptor. This function finds the jump + * descriptor and subtracs the offset from it. + * + * A faster way of looking up can be implemented if the + * list is symertical, however in the current setup we + * allow different numbers of slots in minor frames, and + * different number of minor frames in a major frame. + * + * \param bd IN: Descriptor to lookup MID of (CPU address of BD) + * \param mid OUT: Pointer to where Message-ID (Slot-ID) will be stored + * \param async OUT: Function will store non-zero value if BD belogs to + * async list. + */ +extern int gr1553bc_mid_from_bd( + union gr1553bc_bd *bd, + int *mid, + int *async + ); + +/********** TRANSFER DESCRIPTOR MANIPULATION **********/ + +/* Get pointer to descriptor entry from MID. */ +extern union gr1553bc_bd *gr1553bc_slot_bd + ( + struct gr1553bc_list *list, + int mid + ); + +/* IRQ function */ +typedef void (*bcirq_func_t)(union gr1553bc_bd *bd, void *data); + +/* Create unconditional IRQ customly defined location. + * The IRQ is disabled, enable it with gr1553bc_slot_irq_enable(). + */ +extern int gr1553bc_slot_irq_prepare + ( + struct gr1553bc_list *list, + int mid, + bcirq_func_t func, + void *data + ); + +/* Enable previously prepared unconditional IRQ */ +extern int gr1553bc_slot_irq_enable(struct gr1553bc_list *list, int mid); + +/* Disable unconditional IRQ point, changed to unconditional JUMP + * to descriptor following. + * After disabling it it can be enabled again, or freed. + */ +extern int gr1553bc_slot_irq_disable(struct gr1553bc_list *list, int mid); + +/* Create custom jump to descriptor, conditional or unconditional, see + * hardware manual for conditions. + * + * set conditional to GR1553BC_UNCOND_JMP for unconditional jump. + */ +extern int gr1553bc_slot_jump + ( + struct gr1553bc_list *list, + int mid, + uint32_t condition, + int to_mid + ); + +/* Create a dummy transfer, paused until external trigger is set. The + * Slot is will have the dummy bit set, no transfer will take place. + */ +extern int gr1553bc_slot_exttrig(struct gr1553bc_list *list, int mid); + +/* Create a transfer on a previous allocated descriptor. It is assumed + * that the descriptor has been initialized empty before calling this + * function, this is to avoid races. + * + * The settings that are controlled on a global level (and not + * by this function): + * - IRQ after transfer error + * - IRQ after transfer (not supported, insert separate IRQ slot after this) + * - Pause schedule after transfer error + * - Pause schedule after transfer (not supported) + * - slot time optional (set when MID allocated), otherwise 0 + * - (OPTIONAL) Dummy Bit, set using slot_empty() or ..._TT_DUMMY + * - RT timeout tolerance (managed per RT) + * + * Input Parameters: + * - Retry Mode (options) + * - Number of retires (options) + * - Bus selection (A or B) (options) + * - dummy bit (options) + * - transfer type (tt) + * - rt src/dst address (tt) + * - RT subaddress (tt) + * - word count (tt) + * - mode code (tt) + * - data pointer (dptr) + * + * + * See macros defined in this header file for creating transfer types (tt) + * and word count etc. + * + * See macros defined in this header file for creating the mask of options. + * + * Note that if bit0 (LSB) of dptr is set, then the address is translated into + * hardware address, otherwise the dptr is assumed to be accessible from the + * 1553 core. This is an option only for AMBA-over-PCI. + */ +extern int gr1553bc_slot_transfer( + struct gr1553bc_list *list, + int mid, + int options, + int tt, + uint16_t *dptr); + +/* Remove or set dummy bit of a transfer descriptor + * Bit31 of *dummy is written to the dummy bit, the + * old descriptor value is stored into *dummy. + */ +extern int gr1553bc_slot_dummy( + struct gr1553bc_list *list, + int mid, + unsigned int *dummy); + +/* Make a slot empty (BC will not generate bus transfers), time slot + * allocated is untouched (if assigned). + */ +extern int gr1553bc_slot_empty(struct gr1553bc_list *list, int mid); + +/* Transfer descriptor status and/or update Transfer descriptor data pointer. + * + * Read and/or write Status of a slot. Writing the status word may be + * used by software to indicate that result has been handled, or bit 31 + * may be written 1 telling software that when it reaches 0, then BC + * has executed the request. + * + * Operation: + * bd->status = *stat & (bd->status 0xffffff) | (*stat & 0x80000000); + * *stat = Value of bd->status before rewrite. + * + * Note that the status word is not written when *stat is zero. + * + * Note that if bit0 (LSB) of dptr is set, then the address is translated into + * hardware address, otherwise the dptr is assumed to be accessible from the + * 1553 core. This is an option only for AMBA-over-PCI. + */ +extern int gr1553bc_slot_update( + struct gr1553bc_list *list, + int mid, + uint16_t *dptr, + unsigned int *stat); + +/* Modify a transfer descriptor in any way, + * + * flags: + * bit[N=0..3]: 1 = set BD wordN according to argument wordN, + * 0 = do not modify BD wordN + */ +extern int gr1553bc_slot_raw + ( + struct gr1553bc_list *list, + int mid, + unsigned int flags, + uint32_t word0, + uint32_t word1, + uint32_t word2, + uint32_t word3 + ); + + +/***** Macros to create BC Transfer Types (tt) for gr1553bc_slot_transfer() *****/ + +/* WRITE TO RT (BC-to-RT) */ +#define GR1553BC_BC2RT(rtadr, subadr, word_count) \ + ((rtadr<<11) | (subadr<<5) | (0x1f<<21) | (0<<10) | \ + ((word_count>=32) ? 0 : word_count)) + +/* READ FROM RT (RT-to-BC) */ +#define GR1553BC_RT2BC(rtadr, subadr, word_count) \ + ((rtadr<<11) | (subadr<<5) | (0x1f<<21) | (1<<10) | \ + ((word_count>=32) ? 0 : word_count)) + +/* RT(TX) WRITE TO RT(RX) (RT-to-RT) */ +#define GR1553BC_RT2RT(tx_rtadr, tx_subadr, rx_rtadr, rx_subadr, word_count) \ + ((rx_rtadr<<11) | (rx_subadr<<5) | \ + (tx_rtadr<<21) | (tx_subadr<<16) | \ + (0<<10) | \ + ((word_count>=32) ? 0 : word_count)) + +/* Mode command without data. (BC-to-RT) + * Mode code: 0,1,2,3,4,5,6,7 or 8. + */ +#define GR1553BC_MC_NODATA(rtadr, modecode) \ + ((rtadr<<11) | (0x1f<<5) | (0x1f<<21) | \ + (modecode<<0) | (1<<10)) + +/* Mode command with 4 byte data (RT-to-BC) + * Mode code: 16, 18 or 19. + */ +#define GR1553BC_MC_RT2BC(rtadr, modecode) \ + ((rtadr<<11) | (0x1f<<5) | (0x1f<<21) | \ + (modecode<<0) | (1<<10)) + +/* Mode command with 4 byte data (BC-to-RT) + * Mode code: 17, 20 or 21. + */ +#define GR1553BC_MC_BC2RT(rtadr, modecode) \ + ((rtadr<<11) | (0x1f<<5) | (0x1f<<21) | \ + (modecode<<0) | (0<<10)) + +/* Broadcast to all RTs, to a specific subaddress (BC-to-RTs) */ +#define GR1553BC_BC_BC2RT(subadr, word_count) \ + ((0x1f<<11) | (subadr<<5) | (0x1f<<21) | \ + (0<<10) | \ + ((word_count>=32) ? 0 : word_count)) + +/* Request RT to broadcast to all RTs, to a specific subaddress (RT-to-RTs) */ +#define GR1553BC_BC_RT2RT(tx_rtadr, tx_subadr, rx_subadr, word_count) \ + ((0x1f<<11) | (rx_subadr<<5) | \ + (tx_rtadr<<21) | (tx_subadr<<16) | \ + (0<<10) | \ + ((word_count>=32) ? 0 : word_count)) + +/* Broadcast mode command without data (BC-to-RTs) + * Mode code: 1,3,4,5,6,7 or 8 + */ +#define GR1553BC_BC_MC_NODATA(modecode) \ + ((0x1f<<11) | (0x1f<<5) | (0x1f<<21) | \ + ((modecode)<<0) | (1<<10)) + +/* Broadcast mode command with 4 byte data (BC-to-RTs) + * Mode code: 17, 20 or 21 + */ +#define GR1553BC_BC_MC_BC2RT(modecode) \ + ((0x1f<<11) | (0x1f<<5) | (0x1f<<21) | \ + ((modecode)<<0) | (0<<10)) + + +/***** Macros to create BC options (options) for gr1553bc_slot_transfer() *****/ + +/* Dummy (BC does no bus trasactions) */ +#define GR1553BC_OPT_DUMMY (1<<1) + +/* Retry modes */ +#define GR1553BC_RETRY_SAME 0x0 /* Retry on the same bus only */ +#define GR1553BC_RETRY_ALTER 0x1 /* Retry alternating on both busses */ +#define GR1553BC_RETRY_ATTEMPT 0x2 /* Many attepts first on original + * bus then on other bus */ +/* Number of retires supported */ +#define GR1553BC_RETRY_CNT_MAX 6 + +/* Dummy bit: No transfer + * Bus bit: 0=A, 1=B + * Exttrig bit: Wait for external trigger (used for timesync) + * Exclusive bit: 1=Don't allow other messages in this time slot. + */ +#define GR1553BC_OPTIONS(dummy, exttrig, exclusive, retrymode, nretry, bus) \ + ((((exttrig) & 0x1) << 30) | (((exclusive) & 0x1) << 29) | \ + ((retrymode) << 23) | ((nretry) << 20) | \ + ((bus) & 1) | (((dummy) & 0x1) << 1)) + +#define GR1553BC_OPTIONS_BUSA GR1553BC_OPTIONS(0,0,0,GR1553BC_RETRY_SAME,0,0) +#define GR1553BC_OPTIONS_BUSB GR1553BC_OPTIONS(0,0,0,GR1553BC_RETRY_SAME,0,1) +#define GR1553BC_OPTIONS_BUSA_DUM GR1553BC_OPTIONS(1,0,0,GR1553BC_RETRY_SAME,0,0) +#define GR1553BC_OPTIONS_BUSB_DUM GR1553BC_OPTIONS(1,0,0,GR1553BC_RETRY_SAME,0,1) + +/* Show parts of a list - this is for debugging only */ +extern void gr1553bc_show_list(struct gr1553bc_list *list, int options); + +#ifdef __cplusplus +} +#endif + +#endif /* __GR1553BC_LIST_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/gr1553bm.h b/c/src/lib/libbsp/sparc/shared/include/gr1553bm.h new file mode 100644 index 0000000000..3d1aecc12c --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553bm.h @@ -0,0 +1,204 @@ +/* GR1553B BM driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GR1553BM_H__ +#define __GR1553BM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Register GR1553B driver needed by BM driver */ +extern void gr1553bm_register(void); + +struct gr1553bm_entry { + uint32_t time; /* bit31=1, bit 30=0 */ + uint32_t data; /* bit31=0, bit 30=0 */ +}; + +#define GR1553BM_ERROPTS_MANL 0x02 +#define GR1553BM_ERROPTS_UDWL 0x04 +#define GR1553BM_ERROPTS_IMCL 0x08 +#define GR1553BM_ERROPTS_ALL 0x0e + +/* Function used to implement a custom copy routine. + * Returns number of bytes the desctionation address + * should be incremented with. + * + * \param dst Optional Destination address + * \param src Source DMA address + * \param nentires Number of entries to be processed. + * \param data Custom Data (set by config) + */ +typedef int (*bmcopy_func_t)( + unsigned int dst, + struct gr1553bm_entry *src, + int nentries, + void *data + ); + +/* IRQ function callback, called on BM DMA error */ +typedef void (*bmisr_func_t)(void *bm, void *data); + +/* BM driver configuration */ +struct gr1553bm_config { + + /*** Time options ***/ + + /* 8-bit time resolution, the BM will update the time according + * to this setting. 0 will make the time tag be of highest + * resolution (no division), 1 will make the BM increment the + * time tag once for two time ticks (div with 2), etc. + */ + uint8_t time_resolution; + + /* Enable Time Overflow IRQ handling. Setting this to 1 + * makes the driver to update the 64-bit time by it self, + * it will use time overflow IRQ to detect when the 64-bit + * time counter must be incremented. + * + * If set to zero, the driver expect the user to call + * gr1553bm_time() regularly, it must be called more often + * than the time overflows to avoid an incorrect time. + */ + int time_ovf_irq; + + + + /*** Filtering options ***/ + + /* Bus error log options + * + * bit0,4-31 = reserved, set to zero + * Bit1 = Enables logging of Invalid mode code errors + * Bit2 = Enables logging of Unexpected Data errors + * Bit3 = Enables logging of Manchester/parity errors + */ + unsigned int filt_error_options; + + /* RT Address filtering bit mask. Each bit enables (if set) + * logging of a certain RT sub address. Bit 31 enables logging + * of broadcast messages. + */ + unsigned int filt_rtadr; + + /* RT Subaddress filtering bit mask, bit definition: + * 31: Enables logging of mode commands on subadr 31 + * 1..30: BitN enables/disables logging of RT subadr N + * 0: Enables logging of mode commands on subadr 0 + */ + unsigned int filt_subadr; + + /* Mode code Filter, is written into "BM RT Mode code filter" + * register, please see hardware manual for bit declarations. + */ + unsigned int filt_mc; + + + + /*** Buffer options ***/ + + /* Size of buffer in bytes, must be aligned to 8-byte + * The size is limited to max 4Mb. + */ + unsigned int buffer_size; + + /* Custom buffer, must be aligned to 8-byte and be of buffer_size + * length. If NULL dynamic memory allocation is used. + */ + void *buffer_custom; + + /* Custom Copy function, may be used to implement a more + * effective way of copying the DMA buffer. For example + * the DMA log may need to be compressed before copied + * onto a storage, this function can be used to avoid an + * extra copy. + */ + bmcopy_func_t copy_func; + + /* Optional Custom Data passed on to copy_func() */ + void *copy_func_arg; + + + + /*** Interrupt options ***/ + + /* Custom DMA error function, note that this function is called + * from Interrupt Context. Set to NULL to disable this callback. + */ + bmisr_func_t dma_error_isr; + + /* Optional Custom Data passed on to dma_error_isr() */ + void *dma_error_arg; +}; + +/* Open BM device by instance number (minor) + * + * The return value is used as input parameter in all other function calls + * in the A + */ +extern void *gr1553bm_open(int minor); + +/* Close previously opened Bm device */ +extern void gr1553bm_close(void *bm); + +/* Configure the BM driver before starting */ +extern int gr1553bm_config(void *bm, struct gr1553bm_config *cfg); + +/* Start logging */ +extern int gr1553bm_start(void *bm); + +/* Get 64-bit 1553 Time. Low 24-bit time is acquired from BM hardware, + * the MSB is taken from a software counter internal to the driver. The + * counter is incremented every time the Time overflows by: + * - using "Time overflow" IRQ if enabled in user configuration + * - by checking IRQ flag (IRQ disabled), it is required that user + * calls this function before the next time overflow. + * + * The BM timer is limited to 24-bits, in order to handle overflows + * correctly and maintain a valid time an Interrupt handler is used + * or this function must be called when IRQ is not used. + * + * Update software time counters and return the current time. + */ +extern void gr1553bm_time(void *bm, uint64_t *time); + +/* Return zero when logging has not been started, non-zero when logging + * has been started + */ +extern int gr1553bm_started(void *bm); + +/* Check how many entries are currently stored in the BM Log DMA-area */ +extern int gr1553bm_available(void *bm, int *nentries); + +/* Stop logging */ +extern void gr1553bm_stop(void *bm); + +/* Read a maximum number of entries from LOG buffer. This function + * must be + * + * Arguments + * bm - Private pointer returned by gr1553bm_open() + * dst - Address where log data is written + * max - (IN/OUT) Maximum number of entires, when successfull + * the number of entries actually written is stored + * into the address of max. + * + * Result + * 0 = success + * -1 = fail. (may be due to BM logging not started) + */ +extern int gr1553bm_read(void *bm, struct gr1553bm_entry *dst, int *max); + +#ifdef __cplusplus +} +#endif + +#endif /* __GR1553BM_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/gr1553rt.h b/c/src/lib/libbsp/sparc/shared/include/gr1553rt.h new file mode 100644 index 0000000000..a1f174cba3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gr1553rt.h @@ -0,0 +1,434 @@ +/* GR1553B RT driver + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GR1553RT_H__ +#define __GR1553RT_H__ + +/* CONFIG OPTION: Maximum number of LIST IDs supported. + * There are two lists per RT subaddress, one for RX one + * for TX. + */ +#define RTLISTID_MAX 64 + +/* CONFIG OPTION: Maximum number of Interrupt handlers per device supported + * max is 256 supported, and minimum is 1. + */ +#define RTISR_MAX 64 + +/* CONFIG OPTION: Maximum number of transfer (RX/TX) descriptors supported. + * + * Set this option to zero to allow flexible number of descriptors, + * requires dynamically allocation of driver structures. + */ +/*#define RTBD_MAX 4096*/ +#define RTBD_MAX 0 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Register GR1553B driver needed by RT driver */ +extern void gr1553rt_register(void); + +struct gr1553rt_list; + +/* Descriptor read/written by hardware. + * + * Must be aligned to 16 byte boundary + */ +struct gr1553rt_bd { + volatile unsigned int ctrl; /* 0x00 Control/Status word */ + volatile unsigned int dptr; /* 0x04 Data Pointer */ + volatile unsigned int next; /* 0x08 Next Descriptor in list */ + volatile unsigned int unused; /* 0x0C UNUSED BY HARDWARE */ +}; + +/* Sub address table entry, the hardware access */ +struct gr1553rt_sa { + volatile unsigned int ctrl; /* 0x00 SUBADDRESS CONTROL WORD */ + volatile unsigned int txptr; /* 0x04 TRANSMIT BD POINTER */ + volatile unsigned int rxptr; /* 0x08 RECEIVE BD POINTER */ + volatile unsigned int unused; /* 0x0C UNUSED BY HARDWARE */ +}; + +/* Configuration of a RT-SubAddress-List */ +struct gr1553rt_list_cfg { + unsigned int bd_cnt; /* Number of hw-descriptors in list */ +}; + +/* A TX or RX subaddress descriptor list */ +struct gr1553rt_list { + short listid; /* ID/NUMBER of List. -1 unassigned */ + short subadr; /* SubAddress. -1 when not scheduled */ + void *rt; /* Scheduled on Device */ + struct gr1553rt_list_cfg *cfg; /* List configuration */ + int bd_cnt; /* Number of Descriptors */ + + /* !!Must be last in data structure!! + * !!Array must at least be of length bd_cnt!! + */ + unsigned short bds[1]; /* Array of BDIDs, -1 unused/end */ +}; + +/* GR1553B-RT Driver configuration options used when calling gr1553rt_config(). + * + * Note that if not custom addresses are given the driver will dynamically + * allocate memory for buffers. + * Note that if custom addresses with the LSB set, the address will be + * interpreted as a address accessible by hardware, and translated + * into an address used by CPU. + */ +struct gr1553rt_cfg { + unsigned char rtaddress; /* RT Address 0..30 */ + + /*** MODE CODE CONFIG ***/ + unsigned int modecode; /* Mode codes enable/disable/IRQ/EV-Log. + * Each modecode has a 2-bit cfg field. + * See Mode Code Control Register in + * hardware manual. + */ + + /*** TIME CONFIG ***/ + unsigned short time_res; /* Time tag resolution in us */ + + /*** SUBADDRESS TABLE CONFIG ***/ + void *satab_buffer; /* Optional Custom buffer. Must be + * At least 16*32 bytes, and be aligned + * to 10-bit (1KB) boundary. Set to NULL + * to make driver allocate buffer. + */ + + /*** EVENT LOG CONFIG ***/ + void *evlog_buffer; /* Optional Custom buffer */ + int evlog_size; /* Length, must be a multiple of 2. + * If set to ZERO event log is disabled + */ + + /*** TRANSFER DESCRIPTOR CONFIG ***/ + int bd_count; /* Number of transfer descriptors shared + * by all RX/TX sub-addresses */ + void *bd_buffer; /* Optional Custom descriptor area. + * Must hold bd_count*32 bytes. + * If NULL, descriptors will be + * allocated dynamically. */ +}; + +/* GR1553B-RT status indication, copied from the RT registers and stored + * here. Used when calling the gr1553rt_status() function. + */ +struct gr1553rt_status { + unsigned int status; /* RT Status word */ + unsigned int bus_status; /* BUS Status */ + unsigned short synctime; /* Time Tag of last sync with data */ + unsigned short syncword; /* Data of last mode code synchronize + * with data. */ + unsigned short time_res; /* Time resolution (set by config) */ + unsigned short time; /* Current Time Tag */ +}; + +/* ISR callback definition for ERRORs detected in the GR1553B-RT interrupt + * handler. + * + * \param err Inidicate Error type. The IRQ flag register bit mask: + * Bit 9 - RT DMA ERROR + * Bit 10 - RT Table access error + * \param data Custom data assigned by user + */ +typedef void (*gr1553rt_irqerr_t)(int err, void *data); + +/* ISR callback definition for modecodes that are configured to generate + * an IRQ. The callback is called from within the GR1553B-RT interrupt + * handler. + * + * \param mcode Mode code that caused this IRQ + * \param entry The raw Eventlog Entry + * \param data Custom data assigned by user + */ +typedef void (*gr1553rt_irqmc_t)(int mcode, unsigned int entry, void *data); + +/* Transfer ISR callback definition. Called from GR1553B-RT interrupt handler + * when an interrupt has been generated and a event logged due to a 1553 + * transfer to this RT. + * + * \param list List (Subaddress/TransferType) that caused IRQ + * \param entry The raw Eventlog Entry + * \param bd_next Next Descriptor-entry index in the list (Subaddress/tr-type) + * This can be used to process all descriptors upto entry_next. + * \param data Custom data assigned by user + */ +typedef void (*gr1553rt_irq_t)( + struct gr1553rt_list *list, + unsigned int entry, + int bd_next, + void *data + ); + +/* Configure a list according to configuration. Assign the list + * to a device, however not to a RT sub address yet. The rt + * is stored within list. + * + * \param rt RT Device driver identification, stored within list. + * \param list The list to configure + * \param cfg Configuration for list. Pointer to configuration is + * stored within list for later use. + */ +extern int gr1553rt_list_init + ( + void *rt, + struct gr1553rt_list **plist, + struct gr1553rt_list_cfg *cfg + ); + +/* Assign an Error Interrupt handler. Before the handler is called the + * RT hardware is stopped/disabled. The handler is optional, if not assigned + * the ISR will still stop the RT upon error. + * + * Errors detected by the interrupt handler: + * - DMA error + * - Subaddress table access error + * + * \param func ISR called when an error causes an interrupt. + * \param data Custom data given as an argument when calling ISR + */ +extern int gr1553rt_irq_err + ( + void *rt, + gr1553rt_irqerr_t func, + void *data + ); + +/* Assign a ModeCode Interrupt handler callback. Called when a 1553 modecode + * transfer is logged and cause an IRQ. The modecode IRQ generation is + * configured from "struct gr1553rt_cfg" when calling gr1553rt_config(). + * + * \param func ISR called when a modecode causes an interrupt. + * \param data Custom data given as an argument when calling ISR + */ +extern int gr1553rt_irq_mc + ( + void *rt, + gr1553rt_irqmc_t func, + void *data + ); + +/* Assign transfer interrupt handler callback. Called when a RX or TX + * transfer is logged and cause an interrupt, the function is called + * from the GR1553B-RT driver's ISR, in interrupt context. + * + * The callback can be installed per subaddress and transfer type. + * Subaddress 0 and 31 are not valid (gr1553rt_irq_mc() for modecodes). + * + * \param subadr Select subaddress (1-30) + * \param tx 1=TX subaddress, 0=RX subaddress + * \param func ISR called when subaddress of spcified transfer type + * causes an interrupt. + * \param data Custom data given as an argument when calling ISR + */ +extern int gr1553rt_irq_sa + ( + void *rt, + int subadr, + int tx, + gr1553rt_irq_t func, + void *data + ); + +#define GR1553RT_BD_FLAGS_IRQEN (1<<30) +/* Initialize a descriptor entry in a list. This is typically done + * prior to scheduling the list. + * + * \param entry_no Entry number in list (descriptor index in list) + * \param flags Enable IRQ when descriptor is accessed by setting + * argument GR1553RT_BD_FLAGS_IRQEN. Enabling IRQ on a + * descriptor basis will override SA-table IRQ config. + * \param dptr Data Pointer to RX or TX operation. The LSB indicate + * if the address must be translated into Hardware address + * - this is useful when a buffer close to CPU is used + * as a data pointer and the RT core is located over PCI. + * \param next Next Entry in list. Set to 0xffff for end of list. Set + * 0xfffe for next entry in list, wrap around to entry + * zero if entry_no is last descriptor in list (circular). + */ +extern int gr1553rt_bd_init( + struct gr1553rt_list *list, + unsigned short entry_no, + unsigned int flags, + uint16_t *dptr, + unsigned short next + ); + +/* Manipulate/Read Control/Status and Data Pointer words of a buffer descriptor. + * If status is zero, the control/status word is accessed. If dptr is non-zero + * the data pointer word is accessed. + * + * \param list The list that the descriptor is located at + * + * \param entry_no The descriptor number accessed + * + * \param status IN/OUT. If zero no effect. If pointer is non-zero the + * value pointed to: + * IN: Written to Control/Status + * OUT: the value of the Control/Status word before writing. + * + * \param dptr IN/OUT. If zero no effect. If pointer is non-zero, the + * value pointed to: + * IN: non-zero: Descriptor data pointer will be updated with + * this value. Note that the LSB indicate if the address + * must be translated into hardware-aware address. + * OUT: The old data pointer is stored here. + */ +extern int gr1553rt_bd_update( + struct gr1553rt_list *list, + int entry_no, + unsigned int *status, + uint16_t **dptr + ); + +/* Get the next/current descriptor processed of a RT sub-address. + * + * \param subadr RT Subaddress + * \param txeno Pointer to where TX descriptor number is stored. + * \param rxeno Pointer to where RX descriptor number is stored. + */ +extern int gr1553rt_indication(void *rt, int subadr, int *txeno, int *rxeno); + +/* Take a GR1553RT hardware device identified by minor. + * A pointer is returned that is used internally by the GR1553RT + * driver, it is used as an input parameter 'rt' to all other + * functions that manipulate the hardware. + * + * This function initializes the RT hardware to a stopped/disable level. + */ +extern void *gr1553rt_open(int minor); + +/* Close and stop/disable the RT hardware. */ +extern void gr1553rt_close(void *rt); + +/* Configure the RT. The RT device must be configured once before + * started. A started RT device can not be configured. + * + * \param rt The RT to configure + * \param cfg Configuration parameters + */ +extern int gr1553rt_config(void *rt, struct gr1553rt_cfg *cfg); + +/* Schedule a RX or TX list on a sub address. If a list has already been + * schduled on the subaddress and on the same transfer type (RX/TX), the + * old list is replaced with the list given here. + * + * \param subadr Subaddress to schedule list on + * \param tx Subaddress transfer type: 1=TX, 0=RX + * \param list Preconfigued RT list scheduled + */ +extern void gr1553rt_sa_schedule( + void *rt, + int subadr, + int tx, + struct gr1553rt_list *list + ); + +/* Set SubAdress options. One may for example Enable or Disable a sub + * address RX and/or TX. See hardware manual for SA-Table configuration + * options. + * + * \param subadr SubAddress to configure + * \param mask Bit mask of option-bits written to subaddress config + * \param options The new options written to subaddress config. + * + */ +extern void gr1553rt_sa_setopts( + void *rt, + int subadr, + unsigned int mask, + unsigned int options + ); + +/* Get The Subaddress and transfer type of a scheduled list. Normally the + * application knows which subaddress the list is for. + * + * \param list List to lookup information for + * \param subadr Pointer to where the subaddress is stored + * \param tx Transfer type is stored here. 1=TX, 0=RX. + */ +extern void gr1553rt_list_sa( + struct gr1553rt_list *list, + int *subadr, + int *tx + ); + +/* Start RT Communication + * + * Interrupts will be enabled. The RT enabled and the "RT-run-time" + * part of the API will be opened for the user and parts that need the + * RT to be stopped are no longer available. After the RT has been + * started the configuration function can not be called. + */ +extern int gr1553rt_start(void *rt); + +/* Get Status of the RT core. See data structure gr1553rt_status for more + * information about the result. It can be used to read out: + * - time information + * - sync information + * - bus & RT status + * + * \param status Pointer to where the status words will be stored. They + * are stored according to the gr1553rt_status data structure. + */ +extern void gr1553rt_status(void *rt, struct gr1553rt_status *status); + +/* Stop RT communication. Only possible to stop an already started RT device. + * Interrupts are disabled and the RT Enable bit cleared. + */ +extern void gr1553rt_stop(void *rt); + +/* Set RT vector and/or bit word. + * + * - Vector Word is used in response to "Transmit vector word" BC commands + * - Bit Word is used in response to "Transmit bit word" BC commands + * + * + * \param mask Bit-Mask, bits that are 1 will result in that bit in the + * words register being overwritten with the value of words + * \param words Bits 31..16: Bit Word. Bits 15..0: Vector Word. + * + * Operation: + * hw_words = (hw_words & ~mask) | (words & mask) + */ +extern void gr1553rt_set_vecword( + void *rt, + unsigned int mask, + unsigned int words + ); + +/* Set selectable bits of the "Bus Status Register". The bits written + * is determined by the "mask" bit-mask. Operation: + * + * bus_status = (bus_status & ~mask) | (sts & mask) + * + */ +extern void gr1553rt_set_bussts(void *rt, unsigned int mask, unsigned int sts); + +/* Read up to MAX number of entries in eventlog log. + * + * \param dst Destination address for event log entries + * \param max Maximal number of event log entries that an be stored into dst + * + * Return + * negative Failure + * zero No entries available at the moment + * positive Number of entries copied into dst + */ +extern int gr1553rt_evlog_read(void *rt, unsigned int *dst, int max); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/gradcdac.h b/c/src/lib/libbsp/sparc/shared/include/gradcdac.h new file mode 100644 index 0000000000..b0f246a40d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/gradcdac.h @@ -0,0 +1,227 @@ +/* ADC / DAC (GRADCDAC) interface +/* + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRADCDAC_H__ +#define __GRADCDAC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct gradcdac_regs { + volatile unsigned int config; /* 0x00 Configuration Register */ + volatile unsigned int status; /* 0x04 Status Register */ + int unused0[2]; + volatile unsigned int adc_din; /* 0x10 ADC Data Input Register */ + volatile unsigned int dac_dout; /* 0x14 DAC Data Output Register */ + int unused1[2]; + volatile unsigned int adrin; /* 0x20 Address Input Register */ + volatile unsigned int adrout; /* 0x24 Address Output Register */ + volatile unsigned int adrdir; /* 0x28 Address Direction Register */ + int unused2[1]; + volatile unsigned int data_in; /* 0x30 Data Input Register */ + volatile unsigned int data_out; /* 0x34 Data Output Register */ + volatile unsigned int data_dir; /* 0x38 Data Direction Register */ +}; + +#define GRADCDAC_CFG_DACWS 0x00f80000 +#define GRADCDAC_CFG_WRPOL 0x00040000 +#define GRADCDAC_CFG_DACDW 0x00030000 +#define GRADCDAC_CFG_ADCWS 0x0000f800 +#define GRADCDAC_CFG_RCPOL 0x00000400 +#define GRADCDAC_CFG_CSMODE 0x00000300 +#define GRADCDAC_CFG_CSPOL 0x00000080 +#define GRADCDAC_CFG_RDYMODE 0x00000040 +#define GRADCDAC_CFG_RDYPOL 0x00000020 +#define GRADCDAC_CFG_TRIGPOL 0x00000010 +#define GRADCDAC_CFG_TRIGMODE 0x0000000c +#define GRADCDAC_CFG_ADCDW 0x00000003 + +#define GRADCDAC_CFG_DACWS_BIT 19 +#define GRADCDAC_CFG_WRPOL_BIT 18 +#define GRADCDAC_CFG_DACDW_BIT 16 +#define GRADCDAC_CFG_ADCWS_BIT 11 +#define GRADCDAC_CFG_RCPOL_BIT 10 +#define GRADCDAC_CFG_CSMODE_BIT 8 +#define GRADCDAC_CFG_CSPOL_BIT 7 +#define GRADCDAC_CFG_RDYMODE_BIT 6 +#define GRADCDAC_CFG_RDYPOL_BIT 5 +#define GRADCDAC_CFG_TRIGPOL_BIT 4 +#define GRADCDAC_CFG_TRIGMODE_BIT 2 +#define GRADCDAC_CFG_ADCDW_BIT 0 + +#define GRADCDAC_STATUS_DACNO 0x40 +#define GRADCDAC_STATUS_DACRDY 0x20 +#define GRADCDAC_STATUS_DACON 0x10 +#define GRADCDAC_STATUS_ADCTO 0x08 +#define GRADCDAC_STATUS_ADCNO 0x04 +#define GRADCDAC_STATUS_ADCRDY 0x02 +#define GRADCDAC_STATUS_ADCON 0x01 + +#define GRADCDAC_STATUS_DACNO_BIT 6 +#define GRADCDAC_STATUS_DACRDY_BIT 5 +#define GRADCDAC_STATUS_DACON_BIT 4 +#define GRADCDAC_STATUS_ADCTO_BIT 3 +#define GRADCDAC_STATUS_ADCNO_BIT 2 +#define GRADCDAC_STATUS_ADCRDY_BIT 1 +#define GRADCDAC_STATUS_ADCON_BIT 0 + +#define GRADCDAC_IRQ_DAC 1 +#define GRADCDAC_IRQ_ADC 0 + +struct gradcdac_config { + unsigned char dac_ws; + char wr_pol; + unsigned char dac_dw; + unsigned char adc_ws; + char rc_pol; + unsigned char cs_mode; + char cs_pol; + char ready_mode; + char ready_pol; + char trigg_pol; + unsigned char trigg_mode; + unsigned char adc_dw; +}; + +extern void *gradcdac_open(char *devname); + +extern void gradcdac_set_config(void *cookie, struct gradcdac_config *cfg); + +extern void gradcdac_get_config(void *cookie, struct gradcdac_config *cfg); + +extern void gradcdac_set_cfg(void *cookie, unsigned int config); + +extern unsigned int gradcdac_get_cfg(void *cookie); + +extern unsigned int gradcdac_get_status(void *cookie); + +static int __inline__ gradcdac_DAC_ReqRej(unsigned int status) +{ + return (status & GRADCDAC_STATUS_DACNO); +} + +static int __inline__ gradcdac_DAC_isCompleted(unsigned int status) +{ + return (status & GRADCDAC_STATUS_DACRDY); +} + +static int __inline__ gradcdac_DAC_isOngoing(unsigned int status) +{ + return (status & GRADCDAC_STATUS_DACON); +} + +static int __inline__ gradcdac_ADC_isTimeouted(unsigned int status) +{ + return (status & GRADCDAC_STATUS_ADCTO); +} + +static int __inline__ gradcdac_ADC_ReqRej(unsigned int status) +{ + return (status & GRADCDAC_STATUS_ADCNO); +} + +static int __inline__ gradcdac_ADC_isCompleted(unsigned int status) +{ + return (status & GRADCDAC_STATUS_ADCRDY); +} + +static int __inline__ gradcdac_ADC_isOngoing(unsigned int status) +{ + return (status & GRADCDAC_STATUS_ADCON); +} + +#define GRADCDAC_ISR_BOTH 3 +#define GRADCDAC_ISR_DAC 2 +#define GRADCDAC_ISR_ADC 1 + +/* Install IRQ handler for ADC and/or DAC interrupt. + * The installed IRQ handler(ISR) must read the status + * register to clear the pending interrupt avoiding multiple + * entries to the ISR caused by the same IRQ. + * + * \param adc 1=ADC interrupt, 2=ADC interrupt, 3=ADC and DAC interrupt + * \param isr Interrupt service routine called when IRQ is fired + * \param arg custom argument passed to ISR when called. + */ +extern int gradcdac_install_irq_handler + (void *cookie, int adc, void (*isr)(void *cookie, void *arg), void *arg); + +extern void gradcdac_uninstall_irq_handler(void *cookie, int adc); + +/* Make the ADC circuitry initialize a analogue to digital + * conversion. The result can be read out by gradcdac_adc_convert_try + * or gradcdac_adc_convert. + */ +extern void gradcdac_adc_convert_start(void *cookie); + +/* Tries to read the conversion result. If the circuitry is busy + * converting the function return a non-zero value, if the conversion + * has successfully finished the function return zero. + * + * \param digital_value the resulting converted value is placed here + * \return zero = ADC conversion complete, digital_value contain current conversion result + * positive = ADC busy, digital_value contain previous conversion result + * negative = Conversion request failed. + */ +extern int gradcdac_adc_convert_try(void *cookie, unsigned short *digital_value); + +/* Waits until the ADC circuity has finished a digital to analogue + * conversion. The Waiting is implemented as a busy loop utilizing + * 100% CPU load. + * + * \return zero = Conversion ok + * negative = Conversion request failed. + */ +extern int gradcdac_adc_convert(void *cookie, unsigned short *digital_value); + +/* Try to make the DAC circuitry initialize a digital to analogue + * conversion. If the circuitry is busy by a previous conversion + * the function return a non-zero value, if the conversion is + * successfully initialized the function return zero. + */ +extern int gradcdac_dac_convert_try(void *cookie, unsigned short digital_value); + +/* Initializes a digital to analogue conversion by waiting until + * previous conversions is finished before procceding with the + * conversion. The Waiting is implemented as a busy loop utilizing + * 100% CPU load. + */ +extern void gradcdac_dac_convert(void *cookie, unsigned short digital_value); + +extern unsigned int gradcdac_get_adrinput(void *cookie); +extern void gradcdac_set_adrinput(void *cookie, unsigned int input); + +extern unsigned int gradcdac_get_adroutput(void *cookie); +extern void gradcdac_set_adroutput(void *cookie, unsigned int output); + +extern unsigned int gradcdac_get_adrdir(void *cookie); +extern void gradcdac_set_adrdir(void *cookie, unsigned int dir); + +extern unsigned int gradcdac_get_datainput(void *cookie); +extern void gradcdac_set_datainput(void *cookie, unsigned int input); + +extern unsigned int gradcdac_get_dataoutput(void *cookie); +extern void gradcdac_set_dataoutput(void *cookie, unsigned int output); + +extern unsigned int gradcdac_get_datadir(void *cookie); +extern void gradcdac_set_datadir(void *cookie, unsigned int dir); + +/* Show one or all GRADCDAC cores. If cookie is NULL all GRADCDAC's are shown */ +extern void grAdcDacShow(void *cookie); + +/* Register Driver routine */ +extern void gradcdac_register_drv (void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/grascs.h b/c/src/lib/libbsp/sparc/shared/include/grascs.h new file mode 100644 index 0000000000..5a082ce49d --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grascs.h @@ -0,0 +1,92 @@ +/* + * Header file for GRASCS RTEMS driver + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRASCS_H__ +#define __GRASCS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Minimum and maximum system frequency */ +#define GRASCS_MIN_SFREQ 10000 +#define GRASCS_MAX_SFREQ 255000 + +/* Default, minimum and maximum ETR pulse frequency */ +#define GRASCS_DEFAULT_ETRFREQ 10 +#define GRASCS_MIN_ETRFREQ 1 +#define GRASCS_MAX_ETRFREQ 100 + +/* Maximum number of external time markers */ +#define GRASCS_MAX_TMS 6 + +/* Error codes */ +#define GRASCS_ERROR_STARTSTOP 1 /* Serial/synch interface is running/stopped */ +#define GRASCS_ERROR_TRANSACTIVE 2 /* Busy with transaction */ +#define GRASCS_ERROR_CAPFAULT 3 /* Core capabilities prohibit request */ + +/* Command register */ +#define GRASCS_CMD_RESET (1 << 0) +#define GRASCS_CMD_STARTSTOP (1 << 1) +#define GRASCS_CMD_ESTARTSTOP (1 << 2) +#define GRASCS_CMD_SENDTM (1 << 3) +#define GRASCS_CMD_ETRCTRL (7 << 4) +#define GRASCS_CMD_ETRCTRL_BITS 4 +#define GRASCS_CMD_SLAVESEL (15 << 8) +#define GRASCS_CMD_SLAVESEL_BITS 8 +#define GRASCS_CMD_TCDONE (1 << 12) +#define GRASCS_CMD_TMDONE (1 << 13) +#define GRASCS_CMD_US1 (255 << 16) +#define GRASCS_CMD_US1_BITS 16 +#define GRASCS_CMD_US1C (1 << 24) + +/* Clock scale register */ +#define GRASCS_CLK_ETRFREQ_BITS 12 + +/* Status register */ +#define GRASCS_STS_RUNNING (1 << 0) +#define GRASCS_STS_ERUNNING (1 << 1) +#define GRASCS_STS_TCDONE (1 << 4) +#define GRASCS_STS_TMDONE (1 << 5) +#define GRASCS_STS_DBITS_BITS 8 +#define GRASCS_STS_NSLAVES_BITS 13 +#define GRASCS_STS_USCONF_BITS 18 +#define GRASCS_STS_TMCONF_BITS 19 + +extern int ASCS_init(); + +extern int ASCS_input_select(int slave); + +extern int ASCS_etr_select(int etr, int freq); + +extern void ASCS_start(void); + +extern void ASCS_stop(void); + +extern int ASCS_iface_status(void); + +extern int ASCS_TC_send(int *word); + +extern int ASCS_TC_send_block(int *block, int ntrans); + +extern void ASCS_TC_sync_start(void); + +extern void ASCS_TC_sync_stop(void); + +extern int ASCS_TM_recv(int *word); + +extern int ASCS_TM_recv_block(int *block, int ntrans); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/grctm.h b/c/src/lib/libbsp/sparc/shared/include/grctm.h new file mode 100644 index 0000000000..731e4c5b37 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grctm.h @@ -0,0 +1,168 @@ +/* GRCTM - CCSDS Time Manager - register driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB + * + * 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. + */ + +#ifndef __GRCTM_H__ +#define __GRCTM_H__ + +#define DAT0_IRQ 0x1 +#define DAT1_IRQ 0x2 +#define DAT2_IRQ 0x4 +#define PULSE0_IRQ 0x10 +#define PULSE1_IRQ 0x20 +#define PULSE2_IRQ 0x40 +#define PULSE3_IRQ 0x80 +#define PULSE4_IRQ 0x100 +#define PULSE5_IRQ 0x200 +#define PULSE6_IRQ 0x400 +#define PULSE7_IRQ 0x800 + +struct grctm_regs { + volatile unsigned int grr; + volatile unsigned int gcr; + volatile unsigned int gsr; + volatile unsigned int unused[2]; + volatile unsigned int pfr; + volatile unsigned int etcr; + volatile unsigned int etfr; + volatile unsigned int dcr0; + volatile unsigned int dfr0; + volatile unsigned int dcr1; + volatile unsigned int dfr1; + volatile unsigned int dcr2; + volatile unsigned int dfr2; + volatile unsigned int stcr; + volatile unsigned int stfr; + volatile unsigned int pdr[8]; + volatile unsigned int pimsr; + volatile unsigned int pimr; + volatile unsigned int pisr; + volatile unsigned int pir; + volatile unsigned int imr; + volatile unsigned int picr; + volatile unsigned int unused1[2]; + volatile unsigned int etir; + volatile unsigned int fsir; + volatile unsigned int serconf; + volatile unsigned int unused2; + volatile unsigned int twsc; + volatile unsigned int twadj; + volatile unsigned int twtx; + volatile unsigned int twrx; +}; + +struct grctm_stats { + + /* IRQ Stats */ + unsigned int nirqs; + unsigned int pulse; +}; + +/* Function ISR callback prototype */ +typedef void (*grctm_isr_t)(unsigned int pimr, void *data); + +/* Open a GRCTM device by minor number. */ +extern void *grctm_open(int minor); + +/* Close a previously opened GRCTM device */ +extern void grctm_close(void *spwcuc); + +/* Hardware Reset of GRCTM */ +extern int grctm_reset(void *grctm); + +/* Enable Interrupts at Interrupt controller */ +extern void grctm_int_enable(void *grctm); + +/* Disable Interrupts at Interrupt controller */ +extern void grctm_int_disable(void *grctm); + +/* Clear Statistics gathered by the driver */ +extern void grctm_clr_stats(void *grctm); + +/* Get Statistics gathered by the driver */ +extern void grctm_get_stats(void *grctm, struct grctm_stats *stats); + +/* Register an Interrupt handler and custom data, the function call is + * removed by setting func to NULL. + */ +extern void grctm_int_register(void *grctm, grctm_isr_t func, void *data); + +/* Enable external synchronisation (from spwcuc) */ +extern void grctm_enable_ext_sync(void *grctm); + +/* Disable external synchronisation (from spwcuc) */ +extern void grctm_disable_ext_sync(void *grctm); + +/* Enable TimeWire synchronisation */ +extern void grctm_enable_tw_sync(void *grctm); + +/* Disable TimeWire synchronisation */ +extern void grctm_disable_tw_sync(void *grctm); + +/* Disable frequency synthesizer from driving ET */ +extern void grctm_disable_fs(void *grctm); + +/* Enable frequency synthesizer to drive ET */ +extern void grctm_enable_fs(void *grctm); + +/* Return elapsed coarse time */ +extern unsigned int grctm_get_et_coarse(void *grctm); + +/* Return elapsed fine time */ +extern unsigned int grctm_get_et_fine(void *grctm); + +/* Return elapsed time (coarse and fine) */ +extern unsigned long long grctm_get_et(void *grctm); + +/* Return 1 if specified datation has been latched */ +extern int grctm_is_dat_latched(void *grctm, int dat); + +/* Set triggering edge of datation input */ +extern void grctm_set_dat_edge(void *grctm, int dat, int edge); + +/* Return latched datation coarse time */ +extern unsigned int grctm_get_dat_coarse(void *grctm, int dat); + +/* Return latched datation fine time */ +extern unsigned int grctm_get_dat_fine(void *grctm, int dat); + +/* Return latched datation ET */ +extern unsigned long long grctm_get_dat_et(void *grctm, int dat); + +/* Return current pulse configuration */ +extern unsigned int grctm_get_pulse_reg(void *grctm, int pulse); + +/* Set pulse register */ +extern void grctm_set_pulse_reg(void *grctm, int pulse, unsigned int val); + +/* Configure pulse: pp = period, pw = width, pl = level, en = enable */ +extern void grctm_cfg_pulse(void *grctm, int pulse, int pp, int pw, int pl, int en); + +/* Enable pulse output */ +extern void grctm_enable_pulse(void *grctm, int pulse); + +/* Disable pulse output */ +extern void grctm_disable_pulse(void *grctm, int pulse); + +/* Clear interrupts */ +extern void grctm_clear_irqs(void *grctm, int irqs); + +/* Enable interrupts */ +extern void grctm_enable_irqs(void *grctm, int irqs); + +/* Set Frequency synthesizer increment */ +void grctm_set_fs_incr(void *grctm, int incr); + +/* Set ET increment */ +void grctm_set_et_incr(void *grctm, int incr); + +/* Register the GRCTM driver to Driver Manager */ +extern void grctm_register(void); + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/greth.h b/c/src/lib/libbsp/sparc/shared/include/greth.h new file mode 100644 index 0000000000..c0c3b9a28b --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/greth.h @@ -0,0 +1,145 @@ +/* + * Cobham Gaisler ethernet MAC driver + * adapted from Opencores driver by Marko Isomaki + * + * 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. + */ + +#ifndef __GRETH_H__ +#define __GRETH_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Ethernet configuration registers */ + +typedef struct _greth_regs { + volatile uint32_t ctrl; /* Ctrl Register */ + volatile uint32_t status; /* Status Register */ + volatile uint32_t mac_addr_msb; /* Bit 47-32 of MAC address */ + volatile uint32_t mac_addr_lsb; /* Bit 31-0 of MAC address */ + volatile uint32_t mdio_ctrl; /* MDIO control and status */ + volatile uint32_t txdesc; /* Transmit descriptor pointer */ + volatile uint32_t rxdesc; /* Receive descriptor pointer */ +} greth_regs; + +#define GRETH_TOTAL_BD 128 +#define GRETH_MAXBUF_LEN 1520 + +/* Tx BD */ +#define GRETH_TXD_ENABLE 0x0800 /* Tx BD Enable */ +#define GRETH_TXD_WRAP 0x1000 /* Tx BD Wrap (last BD) */ +#define GRETH_TXD_IRQ 0x2000 /* Tx BD IRQ Enable */ +#define GRETH_TXD_MORE 0x20000 /* Tx BD More (more descs for packet) */ +#define GRETH_TXD_IPCS 0x40000 /* Tx BD insert ip chksum */ +#define GRETH_TXD_TCPCS 0x80000 /* Tx BD insert tcp chksum */ +#define GRETH_TXD_UDPCS 0x100000 /* Tx BD insert udp chksum */ + +#define GRETH_TXD_UNDERRUN 0x4000 /* Tx BD Underrun Status */ +#define GRETH_TXD_RETLIM 0x8000 /* Tx BD Retransmission Limit Status */ +#define GRETH_TXD_LATECOL 0x10000 /* Tx BD Late Collision */ + +#define GRETH_TXD_STATS (GRETH_TXD_UNDERRUN | \ + GRETH_TXD_RETLIM | \ + GRETH_TXD_LATECOL) + +#define GRETH_TXD_CS (GRETH_TXD_IPCS | \ + GRETH_TXD_TCPCS | \ + GRETH_TXD_UDPCS) + +/* Rx BD */ +#define GRETH_RXD_ENABLE 0x0800 /* Rx BD Enable */ +#define GRETH_RXD_WRAP 0x1000 /* Rx BD Wrap (last BD) */ +#define GRETH_RXD_IRQ 0x2000 /* Rx BD IRQ Enable */ + +#define GRETH_RXD_DRIBBLE 0x4000 /* Rx BD Dribble Nibble Status */ +#define GRETH_RXD_TOOLONG 0x8000 /* Rx BD Too Long Status */ +#define GRETH_RXD_CRCERR 0x10000 /* Rx BD CRC Error Status */ +#define GRETH_RXD_OVERRUN 0x20000 /* Rx BD Overrun Status */ +#define GRETH_RXD_LENERR 0x40000 /* Rx BD Length Error */ +#define GRETH_RXD_ID 0x40000 /* Rx BD IP Detected */ +#define GRETH_RXD_IR 0x40000 /* Rx BD IP Chksum Error */ +#define GRETH_RXD_UD 0x40000 /* Rx BD UDP Detected*/ +#define GRETH_RXD_UR 0x40000 /* Rx BD UDP Chksum Error */ +#define GRETH_RXD_TD 0x40000 /* Rx BD TCP Detected */ +#define GRETH_RXD_TR 0x40000 /* Rx BD TCP Chksum Error */ + + +#define GRETH_RXD_STATS (GRETH_RXD_OVERRUN | \ + GRETH_RXD_DRIBBLE | \ + GRETH_RXD_TOOLONG | \ + GRETH_RXD_CRCERR) + +/* CTRL Register */ +#define GRETH_CTRL_TXEN 0x00000001 /* Transmit Enable */ +#define GRETH_CTRL_RXEN 0x00000002 /* Receive Enable */ +#define GRETH_CTRL_TXIRQ 0x00000004 /* Transmit Enable */ +#define GRETH_CTRL_RXIRQ 0x00000008 /* Receive Enable */ +#define GRETH_CTRL_FULLD 0x00000010 /* Full Duplex */ +#define GRETH_CTRL_PRO 0x00000020 /* Promiscuous (receive all) */ +#define GRETH_CTRL_RST 0x00000040 /* Reset MAC */ + +/* Status Register */ +#define GRETH_STATUS_RXERR 0x00000001 /* Receive Error */ +#define GRETH_STATUS_TXERR 0x00000002 /* Transmit Error IRQ */ +#define GRETH_STATUS_RXIRQ 0x00000004 /* Receive Frame IRQ */ +#define GRETH_STATUS_TXIRQ 0x00000008 /* Transmit Error IRQ */ +#define GRETH_STATUS_RXAHBERR 0x00000010 /* Receiver AHB Error */ +#define GRETH_STATUS_TXAHBERR 0x00000020 /* Transmitter AHB Error */ + +/* MDIO Control */ +#define GRETH_MDIO_WRITE 0x00000001 /* MDIO Write */ +#define GRETH_MDIO_READ 0x00000002 /* MDIO Read */ +#define GRETH_MDIO_LINKFAIL 0x00000004 /* MDIO Link failed */ +#define GRETH_MDIO_BUSY 0x00000008 /* MDIO Link Busy */ +#define GRETH_MDIO_REGADR 0x000007C0 /* Register Address */ +#define GRETH_MDIO_PHYADR 0x0000F800 /* PHY address */ +#define GRETH_MDIO_DATA 0xFFFF0000 /* MDIO DATA */ + + +/* MII registers */ +#define GRETH_MII_EXTADV_1000FD 0x00000200 +#define GRETH_MII_EXTADV_1000HD 0x00000100 +#define GRETH_MII_EXTPRT_1000FD 0x00000800 +#define GRETH_MII_EXTPRT_1000HD 0x00000400 + +#define GRETH_MII_100T4 0x00000200 +#define GRETH_MII_100TXFD 0x00000100 +#define GRETH_MII_100TXHD 0x00000080 +#define GRETH_MII_10FD 0x00000040 +#define GRETH_MII_10HD 0x00000020 + + +/* Attach routine */ + +void greth_register_drv(void); + +/* PHY data */ +struct phy_device_info +{ + int vendor; + int device; + int rev; + + int adv; + int part; + + int extadv; + int extpart; +}; + +/* +#ifdef CPU_U32_FIX +void ipalign(struct mbuf *m); +#endif + +*/ +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/c/src/lib/libbsp/sparc/shared/include/grgpio.h b/c/src/lib/libbsp/sparc/shared/include/grgpio.h new file mode 100644 index 0000000000..8fcd1ff7fd --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grgpio.h @@ -0,0 +1,25 @@ +/* + * GRGPIO GPIO Driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRGPIO_H__ +#define __GRGPIO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void grgpio_register_drv (void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/grpwm.h b/c/src/lib/libbsp/sparc/shared/include/grpwm.h new file mode 100644 index 0000000000..c0c2fdda40 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grpwm.h @@ -0,0 +1,127 @@ +/* + * GRPWM PWM Driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRPWM_H__ +#define __GRPWM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void grpwm_register_drv (void); + +#define GRPWM_IOCTL_GET_CAP 1 /* Get Capabilities */ +#define GRPWM_IOCTL_SET_CONFIG 2 /* Configure one PWM Channel */ +#define GRPWM_IOCTL_SET_SCALER 3 /* Set one scaler */ +#define GRPWM_IOCTL_UPDATE 4 /* Set current period and compare value */ +#define GRPWM_IOCTL_IRQ 5 /* Set up IRQ handling */ + +/*** Argument for GRPWM_IOCTL_GET_CAP ***/ + +/* The Capability of the PWM core */ +struct grpwm_ioctl_cap { + int channel_cnt; /* Number of channels */ + unsigned int pwm; /* Capability1 register */ + unsigned int wave; /* Capability2 register, Wave form capabilities of last PWM channel, otherwise 0 */ +}; + +/*** Argument for GRPWM_IOCTL_GET_CONFIG and GRPWM_IOCTL_SET_CONFIG ***/ + +/* Config One PWM */ +struct grpwm_ioctl_config { + unsigned int channel; /* Select channel to configure */ + + /* Specific for one PWM channel */ + unsigned int options; /* PWM options */ + unsigned char dbscaler; /* value greater than 15 disable Dead band */ + unsigned char scaler_index; /* Select scaler used by PWM channel */ + + /* IRQ Setup */ + unsigned char irqscaler; /* IRQ scaler */ + void *isr_arg; /* Argument of IRQ handler */ + void (*isr)(int channel, void *arg); /* Interrupt service routine for this PWM Channel */ + + /* Waveform set up */ + int wave_activate; /* Enables Waveform functionality */ + unsigned int wave_synccfg; /* Bits [29,30,31] is written into Wave-Config register */ + unsigned int wave_sync; /* Sets sync compare register */ + unsigned int *wave_data; /* If not NULL, the Wave RAM is filled with data */ + unsigned int wave_data_length; /* Length of Wave RAM Data, Also used for wstopaddr */ +}; + +#define GRPWM_CONFIG_OPTION_FLIP 0x04000000 /* Set this to Flip PWM output pair */ +#define GRPWM_CONFIG_OPTION_DEAD_BAND 0x00200000 /* Dead Band enable */ +#define GRPWM_CONFIG_OPTION_SYMMETRIC 0x00000040 /* If not defined, asymmetric */ +#define GRPWM_CONFIG_OPTION_ASYMMERTIC 0 +#define GRPWM_CONFIG_OPTION_DUAL 0x00000020 /* Dual Compare Enable */ +#define GRPWM_CONFIG_OPTION_PAIR 0x00000004 /* PWM Pair Enable */ +#define GRPWM_CONFIG_OPTION_SINGLE 0x00000000 /* PWM Pair Disable */ +#define GRPWM_CONFIG_OPTION_POLARITY_HIGH 0x00000002 /* PWM Polarity HIGH */ +#define GRPWM_CONFIG_OPTION_POLARITY_LOW 0x00000000 /* PWM Polarity LOW */ + +#define GRPWM_CONFIG_OPTION_MASK ( \ + GRPWM_CONFIG_OPTION_DEAD_BAND | GRPWM_CONFIG_OPTION_SYMMETRIC | \ + GRPWM_CONFIG_OPTION_DUAL | GRPWM_CONFIG_OPTION_PAIR | \ + GRPWM_CONFIG_OPTION_POLARITY_HIGH \ + ) + +/*** Argument for GPPWM_IOCTL_SET_SCALER ***/ + +struct grpwm_ioctl_scaler { + unsigned int index_mask;/* Scaler update index mask, bit 0 = Scaler 0, bit 1 = Scaler 1 */ + unsigned int values[8]; /* Scaler update values, values[N] is stored into scaler N, if mask & 1<<N is set */ +}; + +/*** Argument for GRPWM_IOCTL_UPDATE ***/ + +#define GRPWM_UPDATE_OPTION_ENABLE 0x01 /* Enable the PWM core */ +#define GRPWM_UPDATE_OPTION_DISABLE 0x02 /* Disable the PWM core */ +#define GRPWM_UPDATE_OPTION_PERIOD 0x04 /* Update period register */ +#define GRPWM_UPDATE_OPTION_COMP 0x08 /* Update Compare register */ +#define GRPWM_UPDATE_OPTION_DBCOMP 0x10 /* Update Dead band register */ +#define GRPWM_UPDATE_OPTION_FIX 0x20 /* Update fix output pins (bypass PWM) */ + +/* FIX PIN bit-mask */ +#define GRPWM_UPDATE_FIX_ENABLE 1 /* Enable force ouput */ +#define GRPWM_UPDATE_FIX_DISABLE 0 /* Disable force ouput */ +#define GRPWM_UPDATE_FIX_0_LOW 0 /* PIN 0 OUPUT: LOW */ +#define GRPWM_UPDATE_FIX_0_HIGH 2 /* PIN 0 OUPUT: HIGH */ +#define GRPWM_UPDATE_FIX_1_LOW 0 /* PIN 1 OUPUT: LOW */ +#define GRPWM_UPDATE_FIX_1_HIGH 4 /* PIN 1 OUPUT: HIGH */ + +struct grpwm_ioctl_update_chan { + unsigned int options; /* Select what is updated */ + unsigned int period; /* Period register content */ + unsigned int compare; /* Compare register content */ + unsigned int dbcomp; /* Dead band register content */ + unsigned char fix; /* Bit-mask that select output on one or two PWM + * output pins. Depends on PAIR config value. + */ +}; +struct grpwm_ioctl_update { + unsigned char chanmask; /* Bit Mask select channels */ + struct grpwm_ioctl_update_chan channels[8]; /* */ +}; + +/*** Argument for GPPWM_IOCTL_IRQ ***/ + +#define GRPWM_IRQ_DISABLE 0 /* Disable IRQ */ +#define GRPWM_IRQ_PERIOD 1 /* Enable IRQ on period match */ +#define GRPWM_IRQ_COMPARE 3 /* Enable IRQ on Compare Match */ +#define GRPWM_IRQ_CLEAR 0x10 /* Clear any pending IRQ on GRPWM and IRQ controller */ + +#define GRPWM_IRQ_CHAN 0x100 /* Channel N is selected, by adding 0x100*N */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/grslink.h b/c/src/lib/libbsp/sparc/shared/include/grslink.h new file mode 100644 index 0000000000..840cc0b3a7 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grslink.h @@ -0,0 +1,148 @@ +/* + * Header file for RTEMS GRSLINK SLINK master driver + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRSLINK_H__ +#define __GRSLINK_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/**** Configuration ****/ +/* Collect statistics ? */ +#define SLINK_COLLECT_STATISTICS + +/* Frequency of SLINK SCLK */ +#define SLINK_FREQ_HZ 6000000 +/* Number of queues used in driver */ +#define SLINK_NUMQUEUES 4 + +/* The four values below are only used in the demo software */ +#define SLINK_CORE_REGBASE 0x80000600 +#define SLINK_CORE_IRQ 6 +#define IRQ_CNTRL_REG 0x80000200 +#define IRQ_CNTRL_MASK_OFFSET 0x40 + +/* + * Structure returned by SLINK_statistics if SLINK_COLLECT_STATISTCS has + * been defined + */ +typedef struct { + unsigned int parerr; /* Number of parity errors */ + unsigned int recov; /* Number of receive overflows */ + unsigned int reads; /* Number of completed READs */ + unsigned int writes; /* Number of performed WRITES */ + unsigned int sequences; /* Number of started SEQUENCEs */ + unsigned int seqcomp; /* Number of completed SEQUENCEs */ + unsigned int interrupts; /* Number of INTERRUPT transfers */ + unsigned int lostwords; /* Number of lost words due to full queue */ +} SLINK_stats; + +/**** SLINK status codes ****/ +#define SLINK_ABORTED 0 +#define SLINK_QFULL 1 +#define SLINK_ACTIVE 2 +#define SLINK_AMBAERR 3 +#define SLINK_COMPLETED 4 +#define SLINK_PARERR 5 +#define SLINK_ROV 6 /* Only used internally in driver */ + +/**** SLINK master register fields *****/ +/* Control register */ +#define SLINK_C_SLEN_POS 16 +#define SLINK_C_SRO (1 << 8) +#define SLINK_C_SCN_POS 4 +#define SLINK_C_PAR (1 << 3) +#define SLINK_C_AS (1 << 2) +#define SLINK_C_SE (1 << 1) +#define SLINK_C_SLE (1 << 0) + +/* Status register fields */ +#define SLINK_S_SI_POS 16 +#define SLINK_S_PERR (1 << 7) +#define SLINK_S_AERR (1 << 6) +#define SLINK_S_ROV (1 << 5) +#define SLINK_S_RNE (1 << 4) +#define SLINK_S_TNF (1 << 3) +#define SLINK_S_SC (1 << 2) +#define SLINK_S_SA (1 << 1) +#define SLINK_S_SRX (1 << 0) + +/* Mask register fields */ +#define SLINK_M_PERRE (1 << 7) +#define SLINK_M_AERRE (1 << 6) +#define SLINK_M_ROVE (1 << 5) +#define SLINK_M_RNEE (1 << 4) +#define SLINK_M_TNFE (1 << 3) +#define SLINK_M_SCE (1 << 2) +#define SLINK_M_SAE (1 << 1) +#define SLINK_M_SRXE (1 << 0) + +/**** Macros ****/ +/* Get channel field from received SLINK word */ +#define SLINK_WRD_CHAN(x) ((x >> 16) & 0xF) +/* Get IO card # from received SLINK word */ +#define SLINK_WRD_CARDNUM(x) ((x >> 21) & 0x3) +/* Get data part from SLINK word */ +#define SLINK_WRD_PAYLOAD(x) (x & 0xFFFF) + +/* Checks status value to see if transmit queue has free slot */ +#define SLINK_STS_TRANSFREE(x) (x & SLINK_S_TNF) +/* Get Sequence Index value */ +#define SLINK_STS_SI(x) ((x >> 16) & 0xFF) + +/**** Function declarations, driver interface ****/ +/* Initializes the SLINK core */ +int SLINK_init(unsigned int nullwrd, int parity, int qsize, + void (*interrupt_trans_handler)(int), + void (*sequence_callback)(int)); + +/* Enables the core */ +void SLINK_start(void); + +/* Disables the core */ +void SLINK_stop(void); + +/* Reads one word */ +int SLINK_read(int data, int channel, int *reply); + +/* Writes one word */ +int SLINK_write(int data, int channel); + +/* Peforms a SEQUENCE */ +int SLINK_seqstart(int *a, int *b, int n, int channel, int reconly); + +/* Aborts a SEQUENCE */ +void SLINK_seqabort(void); + +/* Status of current or last SEQUENCE */ +int SLINK_seqstatus(void); + +/* Number of words transferred in last SEQUENCE */ +int SLINK_seqwrds(void); + +/* Returns value of core's status register */ +int SLINK_hwstatus(void); + +/* Returns number of elements in queue associated with IO card */ +int SLINK_queuestatus(int iocard); + +/* Take first element from queue for IO card # 'iocard' */ +int SLINK_dequeue(int iocard, int *elem); + +/* Returns structure containing core driver statistics */ +SLINK_stats *SLINK_statistics(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __GRSLINK_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/grspw_router.h b/c/src/lib/libbsp/sparc/shared/include/grspw_router.h new file mode 100644 index 0000000000..c3b186fbc5 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grspw_router.h @@ -0,0 +1,104 @@ +/* + * GRSPW ROUTER APB-Register Driver. + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRSPW_ROUTER_H__ +#define __GRSPW_ROUTER_H__ + +/* Hardware Information */ +struct router_hw_info { + unsigned char nports_spw; + unsigned char nports_amba; + unsigned char nports_fifo; + char timers_avail; + char pnp_avail; + unsigned char ver_major; + unsigned char ver_minor; + unsigned char ver_patch; + unsigned char iid; +}; + +#define ROUTER_FLG_CFG 0x01 +#define ROUTER_FLG_IID 0x02 +#define ROUTER_FLG_IDIV 0x04 +#define ROUTER_FLG_TPRES 0x08 +#define ROUTER_FLG_TRLD 0x10 +#define ROUTER_FLG_ALL 0x1f /* All Above Flags */ + +struct router_config { + unsigned int flags; /* Determine what configuration should be updated */ + + /* Router Configuration Register */ + unsigned int config; + + /* Set Instance ID */ + unsigned char iid; + + /* SpaceWire Link Initialization Clock Divisor */ + unsigned char idiv; + + /* Timer Prescaler and Reload */ + unsigned int timer_prescaler; + unsigned int timer_reload[32]; +}; + +/* Logical routing table */ +struct router_routes { + unsigned int route[224]; +}; + +/* Port Setup, see register definitions for "Port setup register" */ +struct router_ps { + unsigned int ps[31]; /* Port Setup for ports 1-31 */ + unsigned int ps_logical[224]; /* Port setup for locgical addresses 32-255 */ +}; + +/* Set/Get Port Control/Status */ +#define ROUTER_PORTFLG_SET_CTRL 0x01 +#define ROUTER_PORTFLG_GET_CTRL 0x02 +#define ROUTER_PORTFLG_SET_STS 0x04 +#define ROUTER_PORTFLG_GET_STS 0x08 +struct router_port { + unsigned int flag; + int port; + unsigned int ctrl; + unsigned int sts; +}; + +/* Get Hardware support/information available */ +#define GRSPWR_IOCTL_HWINFO 0x01 /* OUT: struct router_hw_info */ + +/* Router Configuration */ +#define GRSPWR_IOCTL_CFG_SET 0x02 /* IN: struct router_config */ +#define GRSPWR_IOCTL_CFG_GET 0x03 /* OUT: struct router_config */ + +/* Routes */ +#define GRSPWR_IOCTL_ROUTES_SET 0x04 /* IN: struct router_routes */ +#define GRSPWR_IOCTL_ROUTES_GET 0x05 /* OUT: struct router_routes */ + +/* Port Setup */ +#define GRSPWR_IOCTL_PS_SET 0x06 /* IN: struct router_ps */ +#define GRSPWR_IOCTL_PS_GET 0x07 /* OUT: struct router_ps */ + +/* Set configuration write enable */ +#define GRSPWR_IOCTL_WE_SET 0x08 /* INT: int */ + +/* Set/Get Port Control/Status */ +#define GRSPWR_IOCTL_PORT 0x09 /* IN/OUT: struct router_port */ + +/* Set Router Configuration/Status Register */ +#define GRSPWR_IOCTL_CFGSTS_SET 0x0a /* IN: unsigned int */ +/* Get Router Configuration/Status Register */ +#define GRSPWR_IOCTL_CFGSTS_GET 0x0b /* OUT: unsigned int */ + +/* Get Current Time-Code Register */ +#define GRSPWR_IOCTL_TC_GET 0x0c /* OUT: unsigned int */ + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/grtc.h b/c/src/lib/libbsp/sparc/shared/include/grtc.h new file mode 100644 index 0000000000..ee1c806128 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grtc.h @@ -0,0 +1,152 @@ +/* GRTC Telecommand (TC) decoder driver interface + * + * COPYRIGHT (c) 2007. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRTC_H__ +#define __GRTC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define GRTC_IOC_UNUSED 0 + +/* Driver operation controlling commands */ +#define GRTC_IOC_START 1 +#define GRTC_IOC_STOP 2 +#define GRTC_IOC_ISSTARTED 3 +#define GRTC_IOC_SET_BLOCKING_MODE 4 /* Raw mode only */ +#define GRTC_IOC_SET_TIMEOUT 5 /* Raw mode only */ + +#define GRTC_IOC_ADD_BUFF 16 /* Frame mode only */ +#define GRTC_IOC_RECV 17 /* Frame mode only */ + +/* Available only in STOPPED mode */ +#define GRTC_IOC_SET_MODE 32 /* Set frame mode (ioctl) or raw mode (read) */ +#define GRTC_IOC_SET_BUF_PARAM 33 +#define GRTC_IOC_SET_CONFIG 34 +#define GRTC_IOC_POOLS_SETUP 35 /* Frame mode only */ + +/* Available in both running and stopped mode */ +#define GRTC_IOC_GET_CONFIG 64 +#define GRTC_IOC_GET_BUF_PARAM 65 +#define GRTC_IOC_GET_HW_STATUS 66 +#define GRTC_IOC_ASSIGN_FRM_POOL 67 +#define GRTC_IOC_GET_CLCW_ADR 68 /* Get address of CLCWRx1 */ +#define GRTC_IOC_GET_STATS 69 /* Get statistics, note that most of the stats are only avilable in FRAME mode */ +#define GRTC_IOC_CLR_STATS 70 /* Clear statistics */ + +/* Available only in RUNNING mode */ + +/* Args to GRTC_IOC_GET_BUF_PARAMS */ +#define GRTC_BUF_MAXLEN (0x100*1024) +#define GRTC_BUF_MASK 0xfffffc00 +struct grtc_ioc_buf_params { + unsigned int length; /* Length of new buffer in multiples of 1kbyte blocks */ + void *custom_buffer; /* If set zero driver will allocate with malloc, set LSB to 1 to indicate remote address */ +}; + +/* Args to GRTC_IOC_SET_BLOCKING_MODE */ +enum { + GRTC_BLKMODE_POLL = 0, /* Never block (polling mode) */ + GRTC_BLKMODE_BLK = 1, /* Block until at least 1 byte can be read */ + GRTC_BLKMODE_COMPLETE = 2 /* Block until all data requested has be read */ +}; + +/* Argument of GRTC_IOC_SET_CONFIG and GRTC_IOC_GET_CONFIG + * Pointer to: + */ +struct grtc_ioc_config { + int psr_enable; + int nrzm_enable; + int pss_enable; + int crc_calc; /* Enable Software CRC calculation (only Frame mode) */ +}; + +/* Argument of GRTC_IOC_GET_HW_STATUS: + * Pointer to a grtc_ioc_hw_status structure that will be filled + * in by driver. + */ +struct grtc_ioc_hw_status { + unsigned int sir; + unsigned int far; + unsigned int clcw1; + unsigned int clcw2; + unsigned int phir; + unsigned int str; +}; + +struct grtc_hdr { + unsigned short flags_scid; + unsigned short vc_len; + unsigned char seqnum; +} __attribute__((packed)); + +/* Frame pool, all frames in pool have the same buffer length (frame mode only) */ +struct grtc_frame { + struct grtc_frame *next; /* Next frame in list */ + unsigned short len; /* Length of frame extracted */ + unsigned short reserved; /* Reserved */ + struct grtc_frame_pool *pool; /* The frame pool this frame belongs to */ + + /* The Frame content */ + struct grtc_hdr hdr; /* Primary Header */ + unsigned char data[3]; /* Frame payload */ +} __attribute__((packed)); + +/* GRTC_IOC_RECV argument, single linked list of received frames */ +struct grtc_list { + struct grtc_frame *head; /* First frame in list */ + struct grtc_frame *tail; /* Last frame in list */ + int cnt; /* Number of frames in list */ +}; + +struct grtc_ioc_pools_setup { + unsigned int pool_cnt; /* Number of pools */ + unsigned int pool_frame_len[1]; /* Array of 'pool_cnt' length: Frame length of frames in a pool + * Lengths must be sorted, starting with the smallest frame pool. + */ +}; + +struct grtc_ioc_assign_frm_pool { + unsigned int frame_len; /* The length of the pool to insert the frame into */ + struct grtc_frame *frames; /* Frames to assign to a pool */ +}; + +enum { + GRTC_MODE_RAW = 0, + GRTC_MODE_FRAME = 1 +}; + +/* TC driver stats collected during receiving. The statistics is only available + * in FRAME mode. In RAW mode the user interprets the incoming frames and is + * therefore responsible for generating the staticstics. + */ +struct grtc_ioc_stats { + unsigned long long frames_recv; /* Total number of non-erroneous frames received */ + /* Errors related to incoming data */ + unsigned int err; /* total number of errors */ + unsigned int err_hdr; /* number of errors in Header */ + unsigned int err_payload; /* Number of errors in payload */ + unsigned int err_ending; /* Number of errors in end (Filler, end marker) */ + unsigned int err_abandoned; /* Number of abandoned frames, NOT IMPLEMENTED */ + /* Errors related to the handling of incoming frames */ + unsigned int dropped; /* Number of dropped frames TC driver */ + unsigned int dropped_no_buf; /* Number of dropped frame caused by no buffers were available */ + unsigned int dropped_too_long; /* Number of dropped frames that was larger than any buffer available for driver */ +}; + +/* Register GRTC driver at driver manager */ +void grtc_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __GRTC_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/grtm.h b/c/src/lib/libbsp/sparc/shared/include/grtm.h new file mode 100644 index 0000000000..c2cf9ef8e6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/grtm.h @@ -0,0 +1,241 @@ +/* GRTM Telemetry (TM) driver interface + * + * COPYRIGHT (c) 2007. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __GRTM_H__ +#define __GRTM_H__ + +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GRTM_IOC_UNUSED 0 + +/* Driver operation controlling commands */ +#define GRTM_IOC_START 1 +#define GRTM_IOC_STOP 2 +#define GRTM_IOC_ISSTARTED 3 +#define GRTM_IOC_SET_BLOCKING_MODE 4 +#define GRTM_IOC_SET_TIMEOUT 5 + +/* Available only in STOPPED mode */ +#define GRTM_IOC_SET_CONFIG 32 + +/* Available in both running and stopped mode */ +#define GRTM_IOC_RECLAIM 64 +#define GRTM_IOC_GET_CONFIG 65 +#define GRTM_IOC_GET_HW_IMPL 66 +#define GRTM_IOC_GET_HW_STATUS 67 /* Not implemented */ +#define GRTM_IOC_GET_OCFREG 68 +#define GRTM_IOC_GET_STATS 69 +#define GRTM_IOC_CLR_STATS 70 + +/* Available only in RUNNING mode */ +#define GRTM_IOC_SEND 96 + +/* Args to GRTC_IOC_SET_BLOCKING_MODE */ +enum { + GRTM_BLKMODE_POLL = 0, /* Never block (polling mode) */ + GRTM_BLKMODE_BLK = 1, /* Block until at least 1 byte can be read */ +}; + +/* Reed Solomon Encoder implemented */ +enum { + GRTM_RS_IMPL_NONE = 0, + GRTM_RS_IMPL_E16 = 1, /* E16 */ + GRTM_RS_IMPL_E8 = 2, /* E8 */ + GRTM_RS_IMPL_BOTH = 3 /* Both E8 and E16 */ + +}; + +struct grtm_ioc_hw { + char cs; /* Sub Carrier */ + char sp; /* Split-Phase Level */ + char ce; + char nrz; + char psr; + char te; + unsigned char rsdep; + unsigned char rs; + char aasm; + char fecf; + char ocf; + char evc; + char idle; + char fsh; + char mcg; + char iz; + char fhec; + char aos; + char cif; + char ocfb; + + unsigned short blk_size; /* Block Size */ + unsigned short fifo_size; /* FIFO Size */ + +}; + +/* Driver Mode */ +enum { + GRTM_MODE_TM = 0, /* TM */ + GRTM_MODE_AOS = 1 /* AOS */ +}; + +/* Physical layer Options */ +#define GRTM_IOC_PHY_SCF (1<<15) /* Sub Carrier Fall */ +#define GRTM_IOC_PHY_SF (1<<31) /* Symbol Fall */ + +/* Coding Sub-layer Options */ +#define GRTM_IOC_CODE_SC (1<<0) /* Enable Sub Carrier modulation */ +#define GRTM_IOC_CODE_SP (1<<1) /* Enable Split-Phase (SP) level modulation */ +#define GRTM_IOC_CODE_CE (1<<5) /* Enable Convolutional Encoding */ +#define GRTM_IOC_CODE_NRZ (1<<6) /* Enable Non-Return-to-Zero mark encoding */ +#define GRTM_IOC_CODE_PSR (1<<7) /* Enable Pseudo-Randomizer */ +#define GRTM_IOC_CODE_RS8 (1<<11) /* Reed-solomon Encoder to use: 0=E16, 1=E8 */ +#define GRTM_IOC_CODE_RS (1<<15) /* Enable Reed-Solomon Encoder */ +#define GRTM_IOC_CODE_AASM (1<<16) /* Enable Alternative attached synchronization marker */ +#define GRTM_IOC_CODE_ALL (GRTM_IOC_CODE_SC|GRTM_IOC_CODE_SP|GRTM_IOC_CODE_CE| \ + GRTM_IOC_CODE_NRZ|GRTM_IOC_CODE_PSR|GRTM_IOC_CODE_RS8|\ + GRTM_IOC_CODE_RS|GRTM_IOC_CODE_AASM) + +enum { + GRTM_CERATE_00 = 0, /* Rate 1/2, no puncturing */ + GRTM_CERATE_02 = 2, /* Rate 1/2, punctured */ + GRTM_CERATE_04 = 4, /* Rate 2/3, punctured */ + GRTM_CERATE_05 = 5, /* Rate 3/4, punctured */ + GRTM_CERATE_06 = 6, /* Rate 5/6, punctured */ + GRTM_CERATE_07 = 7, /* Rate 7/8, punctured */ +}; + +/* Options for Generating all frames */ +#define GRTM_IOC_ALL_FHEC 0x01 /* Enable Frame Header Error Control (Only AOS) */ +#define GRTM_IOC_ALL_FECF 0x02 /* Enable Transfer Frame CRC */ +#define GRTM_IOC_ALL_IZ 0x04 /* Enable Insert Zone */ +#define GRTM_IOC_ALL_ALL (GRTM_IOC_ALL_FHEC|GRTM_IOC_ALL_FECF|GRTM_IOC_ALL_IZ) + +/* Master Frame Generation Options */ +#define GRTM_IOC_MF_OW 0x01 /* Over Write OCF bits 16 and 17 */ +#define GRTM_IOC_MF_OCF 0x02 /* Enable Operation Control Field (OCF) for master channel */ +#define GRTM_IOC_MF_FSH 0x04 /* Enable MC_FSH for master channel */ +#define GRTM_IOC_MF_MC 0x08 /* Enable Master channel counter generation */ +#define GRTM_IOC_MF_ALL (GRTM_IOC_MF_OW|GRTM_IOC_MF_OCF|GRTM_IOC_MF_FSH|GRTM_IOC_MF_MC) + +/* Idle Frames Generation Options */ +#define GRTM_IOC_IDLE_MC 0x01 /* Enable Master Channel (MC) counter generation (TM Only) */ +#define GRTM_IOC_IDLE_VCC 0x02 /* Enable Virtual Channel counter cycle generation (AOS Only)*/ +#define GRTM_IOC_IDLE_FSH 0x04 /* Enable Frame Secondary Header (FSH) for idle frames (TM Only) */ +#define GRTM_IOC_IDLE_EVC 0x08 /* Enable Extended Virtual Channel Counter Generation */ +#define GRTM_IOC_IDLE_OCF 0x10 /* Enable OCF/CLCW in idle frame */ +#define GRTM_IOC_IDLE_EN 0x20 /* Enable Idle frame generation */ +#define GRTM_IOC_IDLE_ALL (GRTM_IOC_IDLE_MC|GRTM_IOC_IDLE_VCC|GRTM_IOC_IDLE_FSH| \ + GRTM_IOC_IDLE_EVC|GRTM_IOC_IDLE_OCF|GRTM_IOC_IDLE_EN) + +/* Argument of GRTM_IOC_SET_CONFIG and GRTM_IOC_GET_CONFIG. + * Driver and Hardware configuration. + * + * Pointer to: + */ +struct grtm_ioc_config { + + /* Mode AOS or TM */ + unsigned char mode; /* 0=TM, 1=AOS */ + + unsigned short frame_length; /* Length of every frame transmitted */ + unsigned short limit; /* Number of data bytes fetched by DMA before transmission starts */ + unsigned int as_marker; /* Attached Synchronization Marker */ + + /* Physical layer options */ + unsigned short phy_subrate; /* Sub Carrier rate - sub carrier devision factor - 1 */ + unsigned short phy_symbolrate; /* Symbol Rate division factor - 1 */ + unsigned char phy_opts; /* Mask of GRTM_IOC_PHY_XXXX */ + + /* Coding sub-layer Options */ + unsigned char code_rsdep; /* Coding sub-layer Reed-Solomon interleave depth (3-bit) */ + unsigned char code_ce_rate; /* Convolutional encoding rate, select one of GRTM_CERATE_00 ... GRTM_CERATE_07 */ + unsigned char code_csel; /* */ + unsigned int code_opts; /* Mask of GRTM_IOC_CODE_XXXX */ + + /* All Frames Generation */ + unsigned char all_izlen; /* FSH/IZ Length (5-bit) */ + unsigned char all_opts; /* Mask of GRTM_IOC_ALL_XXXX */ + + /* Master Frame Generation */ + unsigned char mf_opts; /* Mask of GRTM_IOC_MF_XXXX */ + + /* Idle frame Generation */ + unsigned short idle_scid; + unsigned char idle_vcid; + unsigned char idle_opts; /* Mask of GRTM_IOC_IDLE_XXXX */ + + /* Interrupt options */ + unsigned int enable_cnt; /* Number of frames in between Interrupt is generated, Zero disables interrupt */ + int isr_desc_proc; /* Enable ISR to process descriptors */ + int blocking; /* Blocking mode select (POLL,BLK..) */ + rtems_interval timeout; /* Blocking mode timeout */ +}; + +struct grtm_frame; + +struct grtm_list { + struct grtm_frame *head; /* First Frame in list */ + struct grtm_frame *tail; /* Last Frame in list */ +}; + +#define GRTM_FLAGS_SENT 0x01 +#define GRRM_FLAGS_ERR 0x02 + +#define GRTM_FLAGS_TRANSLATE (1<<31) /* Translate frame payload address from CPU address to remote bus (the bus GRTM is resident on) */ +#define GRTM_FLAGS_TRANSLATE_AND_REMEMBER (1<<30) /* As GRTM_FLAGS_TRANSLATE, however if the translated payload address equals the payload address + * the GRTM_FLAGS_TRANSLATE_AND_REMEMBER bit is cleared and the GRTM_FLAGS_TRANSLATE bit is set */ +#define GRTM_FLAGS_COPY_DATA (1<<29) /* Where available: Transfer Frame payload to target, may be used for SpaceWire, where the GRTM driver transfer + * the payload to a buffer on the SpaceWire target. + */ + +#define GRTM_FLAGS_TS (1<<9) +#define GRTM_FLAGS_MCB (1<<8) +#define GRTM_FLAGS_FSHB (1<<7) +#define GRTM_FLAGS_OCFB (1<<6) +#define GRTM_FLAGS_FHECB (1<<5) +#define GRTM_FLAGS_IZB (1<<4) +#define GRTM_FLAGS_FECFB (1<<3) + +#define GRTM_FLAGS_MASK (GRTM_FLAGS_TS|GRTM_FLAGS_MCB|GRTM_FLAGS_FSHB|\ + GRTM_FLAGS_OCFB|GRTM_FLAGS_FHECB|GRTM_FLAGS_IZB|\ + GRTM_FLAGS_FECFB) + +/* The GRTM software representation of a Frame */ +struct grtm_frame { + /* Options and status */ + unsigned int flags; /* bypass options, and sent/error status */ + + struct grtm_frame *next; /* Next packet in chain */ + + unsigned int *payload; /* The Headers and Payload, Frame data and header must be word aligned */ +}; + +#define FRAME_SIZE(payloadlen) (sizeof(struct grtm_frame)+payloadlen) + +struct grtm_ioc_stats { + unsigned long long frames_sent; + unsigned int err_underrun; + unsigned int err_tx; + unsigned int err_ahb; + unsigned int err_transfer_frame; +}; + +/* Register GRTM driver at driver manager */ +void grtm_register_drv(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __GRTM_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/satcan.h b/c/src/lib/libbsp/sparc/shared/include/satcan.h new file mode 100644 index 0000000000..3f2f0c4805 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/satcan.h @@ -0,0 +1,142 @@ +/* + * Header file for RTEMS SATCAN FPGA driver + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __SATCAN_H__ +#define __SATCAN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Config structure passed to SatCAN_init(..) */ +typedef struct { + /* Configuration */ + int nodeno; + int dps; + /* Callback functions */ + void (*ahb_irq_callback)(void); + void (*pps_irq_callback)(void); + void (*m5_irq_callback)(void); + void (*m4_irq_callback)(void); + void (*m3_irq_callback)(void); + void (*m2_irq_callback)(void); + void (*m1_irq_callback)(void); + void (*sync_irq_callback)(void); + void (*can_irq_callback)(unsigned int fifo); +} satcan_config; + +#define SATCAN_HEADER_SIZE 4 +#define SATCAN_HEADER_NMM_POS 3 +#define SATCAN_PAYLOAD_SIZE 8 + +/* SatCAN message */ +typedef struct { + unsigned char header[SATCAN_HEADER_SIZE]; /* Header of SatCAN message */ + unsigned char payload[SATCAN_PAYLOAD_SIZE]; /* Payload of SatCAN message */ +} satcan_msg; + +/* SatCAN modify register structure */ +typedef struct { + unsigned int reg; + unsigned int val; +} satcan_regmod; + +/* Driver interface */ +int satcan_register(satcan_config *conf); + +/* SatCAN interrupt IDs */ +#define SATCAN_IRQ_NONACT_TO_ACT 0 +#define SATCAN_IRQ_ACTIVE_TO_NONACT 1 +#define SATCAN_IRQ_STR1_TO_DPS 2 +#define SATCAN_IRQ_DPS_TO_STR1 3 +#define SATCAN_IRQ_STR2_TO_DPS 4 +#define SATCAN_IRQ_DPS_TO_STR2 5 +#define SATCAN_IRQ_STR3_TO_DPS 6 +#define SATCAN_IRQ_DPS_TO_STR3 7 +#define SATCAN_IRQ_PLD1_TO_DPS 8 +#define SATCAN_IRQ_DPS_TO_PLD1 9 +#define SATCAN_IRQ_PLD2_TO_DPS 10 +#define SATCAN_IRQ_DPS_TO_PLD2 11 +#define SATCAN_IRQ_SYNC 16 +#define SATCAN_IRQ_TIME_MARKER1 17 +#define SATCAN_IRQ_TIME_MARKER2 18 +#define SATCAN_IRQ_TIME_MARKER3 19 +#define SATCAN_IRQ_TIME_MARKER4 20 +#define SATCAN_IRQ_TIME_MARKER5 21 +#define SATCAN_IRQ_EOD1 22 +#define SATCAN_IRQ_EOD2 23 +#define SATCAN_IRQ_TOD 24 +#define SATCAN_IRQ_CRITICAL 25 + +/* IOC */ +#define SATCAN_IOC_DMA_2K 1 /* Use DMA area for 2K messages */ +#define SATCAN_IOC_DMA_8K 2 /* Use DMA area for 8K messages */ +#define SATCAN_IOC_GET_REG 3 /* Provides direct read access to all core registers */ +#define SATCAN_IOC_SET_REG 4 /* Provides direct write access to all core registers */ +#define SATCAN_IOC_OR_REG 5 /* Provides direct read access to all core registers */ +#define SATCAN_IOC_AND_REG 6 /* Provides direct write access to all core registers */ +#define SATCAN_IOC_EN_TX1_DIS_TX2 7 /* Enable DMA TX channel 1, Disable DMA TX channel 2 */ +#define SATCAN_IOC_EN_TX2_DIS_TX1 8 /* Enable DMA TX channel 2, Disable DMA TX channel 1 */ +#define SATCAN_IOC_GET_DMA_MODE 9 /* Returns the current DMA mode */ +#define SATCAN_IOC_SET_DMA_MODE 10 /* Sets the DMA mode */ +#define SATCAN_IOC_ACTIVATE_DMA 11 /* Directly activate DMA channel */ +#define SATCAN_IOC_DEACTIVATE_DMA 12 /* Directly deactivate DMA channel */ +#define SATCAN_IOC_DMA_STATUS 13 /* Returns status of directly activated DMA */ +#define SATCAN_IOC_GET_DOFFSET 14 /* Get TX DMA offset */ +#define SATCAN_IOC_SET_DOFFSET 15 /* Set TX DMA offset */ +#define SATCAN_IOC_GET_TIMEOUT 16 /* Set TX DMA timeout */ +#define SATCAN_IOC_SET_TIMEOUT 17 /* Get TX DMA timeout */ + + +/* Values used to select core register with IOC_SET_REG/IOC_GET_REG */ +#define SATCAN_SWRES 0 /* Software reset */ +#define SATCAN_INT_EN 1 /* Interrupt enable */ +#define SATCAN_FIFO 3 /* FIFO read */ +#define SATCAN_FIFO_RES 4 /* FIFO reset */ +#define SATCAN_TSTAMP 5 /* Current time stamp */ +#define SATCAN_CMD0 6 /* Command register 0 */ +#define SATCAN_CMD1 7 /* Command register 1 */ +#define SATCAN_START_CTC 8 /* Start cycle time counter */ +#define SATCAN_RAM_BASE 9 /* RAM offset address */ +#define SATCAN_STOP_CTC 10 /* Stop cycle time counter / DPS active status */ +#define SATCAN_DPS_ACT 10 /* Stop cycle time counter / DPS active status */ +#define SATCAN_PLL_RST 11 /* DPLL reset */ +#define SATCAN_PLL_CMD 12 /* DPLL command */ +#define SATCAN_PLL_STAT 13 /* DPLL status */ +#define SATCAN_PLL_OFF 14 /* DPLL offset */ +#define SATCAN_DMA 15 /* DMA channel enable */ +#define SATCAN_DMA_TX_1_CUR 16 /* DMA channel 1 TX current address */ +#define SATCAN_DMA_TX_1_END 17 /* DMA channel 1 TX end address */ +#define SATCAN_DMA_TX_2_CUR 18 /* DMA channel 2 TX current address */ +#define SATCAN_DMA_TX_2_END 19 /* DMA channel 2 TX end address */ +#define SATCAN_RX 20 /* CAN RX enable / Filter start ID */ +#define SATCAN_FILTER_START 20 /* CAN RX enable / Filter start ID */ +#define SATCAN_FILTER_SETUP 21 /* Filter setup / Filter stop ID */ +#define SATCAN_FILTER_STOP 21 /* Filter setup / Filter stop ID */ +#define SATCAN_WCTRL 32 /* Wrapper status/control register */ +#define SATCAN_WIPEND 33 /* Wrapper interrupt pending register */ +#define SATCAN_WIMASK 34 /* Wrapper interrupt mask register */ +#define SATCAN_WAHBADDR 35 /* Wrapper AHB address register */ + + +/* Values used to communicate DMA mode */ +#define SATCAN_DMA_MODE_USER 0 +#define SATCAN_DMA_MODE_SYSTEM 1 + +/* Values used to directly activate DMA channel */ +#define SATCAN_DMA_ENABLE_TX1 1 +#define SATCAN_DMA_ENABLE_TX2 2 + +#ifdef __cplusplus +} +#endif + +#endif /* __SATCAN_H__ */ diff --git a/c/src/lib/libbsp/sparc/shared/include/spictrl.h b/c/src/lib/libbsp/sparc/shared/include/spictrl.h new file mode 100644 index 0000000000..aba6dcfdc3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/spictrl.h @@ -0,0 +1,126 @@ +/* + * SPICTRL SPI Driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __SPICTRL_H__ +#define __SPICTRL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern void spictrl_register_drv (void); + +/*** REGISTER LAYOUT ***/ +struct spictrl_regs { + volatile unsigned int capability; /* 0x00 */ + volatile unsigned int resv[7]; /* 0x04 */ + volatile unsigned int mode; /* 0x20 */ + volatile unsigned int event; /* 0x24 */ + volatile unsigned int mask; /* 0x28 */ + volatile unsigned int command; /* 0x2c */ + volatile unsigned int tx; /* 0x30 */ + volatile unsigned int rx; /* 0x34 */ + volatile unsigned int slvsel; /* 0x38 */ + volatile unsigned int am_slvsel; /* 0x3c */ + volatile unsigned int am_cfg; /* 0x40 */ + volatile unsigned int am_period; /* 0x44 */ + int reserved0[2]; + volatile unsigned int am_mask[4]; /* 0x50-0x5C */ + int reserved1[(0x200-0x60)/4]; + volatile unsigned int am_tx[128]; /* 0x200-0x3FC */ + volatile unsigned int am_rx[128]; /* 0x400-0x5FC */ +}; + +/* -- About automated periodic transfer mode -- + * + * Core must support this feature. + * + * The SPI core must be configured in periodic mode before + * writing the data into the transfer FIFO which will be used + * mutiple times in different transfers, it will also make + * the receive FIFO to be updated. + * + * In periodic mode the following sequence is performed, + * 1. start() + * 2. ioctl(CONFIG, &config) - Enable periodic mode + * 3. set_address() + * 4. write() - Fills TX FIFO, this has some constraints + * 5. ioctl(START) - Starts the periodic transmission of the TX FIFO + * 6. read() - Read one response of the tranistted data. It will + * hang until data is available. If hanging is not an + * options use ioctl(STATUS) + * 7. go back to 6. + * + * 8. ioctl(STOP) - Stop to set up a new periodic or normal transfer + * 9. stop() + * + * Note that the the read length must equal the total write length. + */ + +/* Custom SPICTRL driver ioctl commands */ +#define SPICTRL_IOCTL_PERIOD_START 5000 /* Start automated periodic transfer mode */ +#define SPICTRL_IOCTL_PERIOD_STOP 5001 /* Stop to SPI core from doing periodic transfers */ +#define SPICTRL_IOCTL_CONFIG 5002 /* Configure Periodic transfer mode (before calling write() and START) */ +#define SPICTRL_IOCTL_STATUS 5003 /* Get status */ + +#define SPICTRL_IOCTL_PERIOD_READ 5005 /* Write transmit registers and mask register + * (only in automatic periodic mode) + * Note that it is probably prefferred to read + * the received words using the read() using + * operations instead. + */ +#define SPICTRL_IOCTL_PERIOD_WRITE 5006 /* Read receive registers and mask register + * (only in automatic periodic mode) */ +#define SPICTRL_IOCTL_REGS 5007 /* Get SPICTRL Register */ + +/* SPICTRL_IOCTL_CONFIG argument */ +struct spictrl_ioctl_config { + int clock_gap; /* Clock GAP between */ + unsigned int flags; /* Normal mode flags */ + int periodic_mode; /* 1=Enables Automated periodic transfers if supported by hardware */ + unsigned int period; /* Number of clocks between automated transfers are started */ + unsigned int period_flags; /* Options */ + unsigned int period_slvsel; /* Slave Select when transfer is not active, default is 0xffffffff */ +}; +#define SPICTRL_FLAGS_TAC 0x10 + +#define SPICTRL_PERIOD_FLAGS_ERPT 0x80 /* Trigger start-period from external signal */ +#define SPICTRL_PERIOD_FLAGS_SEQ 0x40 +#define SPICTRL_PERIOD_FLAGS_STRICT 0x20 +#define SPICTRL_PERIOD_FLAGS_OVTB 0x10 +#define SPICTRL_PERIOD_FLAGS_OVDB 0x08 +#define SPICTRL_PERIOD_FLAGS_ASEL 0x04 +#define SPICTRL_PERIOD_FLAGS_EACT 0x01 + +/* SPICTRL_IOCTL_PERIOD_READ and SPICTRL_IOCTL_PERIOD_WRITE Argument data structure + * + * Note that the order of reading the mask registers are different for read/write + * operation. See options notes. + */ +struct spictrl_period_io { + int options; /* READ: bit0=Read Mask Registers into masks[]. + * bit1=Read Receive registers according to masks[] + * (after reading masks). + * + * WRITE: bit0=Update Mask accoring to masks[]. + * bit1=Update Transmit registers according to masks[]. + * (before reading masks) + */ + unsigned int masks[4]; + + void *data; /* Data read sequentially according to masks[] bit. */ +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/include/spwcuc.h b/c/src/lib/libbsp/sparc/shared/include/spwcuc.h new file mode 100644 index 0000000000..dfef80e6ef --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/spwcuc.h @@ -0,0 +1,188 @@ +/* SPWCUC - SpaceWire - CCSDS unsegmented Code Transfer Protocol GRLIB core + * register driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#ifndef __SPWCUC_H__ +#define __SPWCUC_H__ + +#define PKT_INIT_IRQ 0x1 +#define PKT_ERR_IRQ 0x2 +#define PKT_RX_IRQ 0x4 +#define WRAP_ERR_IRQ 0x8 +#define WRAP_IRQ 0x10 +#define SYNC_ERR_IRQ 0x20 +#define SYNC_IRQ 0x40 +#define TOL_ERR_IRQ 0x80 +#define TICK_RX_ERR_IRQ 0x100 +#define TICK_RX_WRAP_IRQ 0x200 +#define TICK_RX_IRQ 0x400 +#define TICK_TX_WRAP_IRQ 0x800 +#define TICK_TX_IRQ 0x1000 + +/* SPWCUC Register layout */ +struct spwcuc_regs { + volatile unsigned int config; /* 00 */ + volatile unsigned int status; /* 04 */ + volatile unsigned int control; /* 08 */ + volatile unsigned int unused0; /* 0c */ + volatile unsigned int dla; /* 10 */ + volatile unsigned int pid; /* 14 */ + volatile unsigned int offset; /* 18 */ + volatile unsigned int unused1; /* 1c */ + volatile unsigned int pkt_ct; /* 20 */ + volatile unsigned int pkt_ft; /* 24 */ + volatile unsigned int pkt_pf_crc; /* 28 */ + volatile unsigned int unused2; /* 2c */ + volatile unsigned int etct; /* 30 */ + volatile unsigned int etft; /* 34 */ + volatile unsigned int etct_next; /* 38 */ + volatile unsigned int etft_next; /* 3c */ + volatile unsigned int unused3[8]; /* 40-5c */ + volatile unsigned int pimsr; /* 60 */ + volatile unsigned int pimr; /* 64 */ + volatile unsigned int pisr; /* 68 */ + volatile unsigned int pir; /* 6c */ + volatile unsigned int imr; /* 70 */ + volatile unsigned int picr; /* 74 */ +}; + +struct spwcuc_cfg { + unsigned char sel_out; /* Bits 3-0 enable time code transmission on respective output */ + unsigned char sel_in; /* Select SpW to receive time codes on, 0-3 */ + unsigned char mapping; /* Define mapping of time code time info into T-field, 0-31 */ + unsigned char tolerance; /* Define SpaceWire time code reception tolerance, 0-31 */ + unsigned char tid; /* Define CUC P-Field time code identification, 1 = Level 1, 2 = Level 2 */ + unsigned char ctf; /* If 1 check time code flags to be all zero */ + unsigned char cp; /* If 1 check P-Field time code id against tid */ + + unsigned char txen; /* Enable SpaceWire time code transmission */ + unsigned char rxen; /* Enable SpaceWire time code reception */ + unsigned char pktsyncen; /* Enable SpaceWire time CUC packet sync */ + unsigned char pktiniten; /* Enable SpaceWire time CUC packet init */ + unsigned char pktrxen; /* Enable SpaceWire time CUC packet reception */ + + unsigned char dla; /* SpaceWire destination logical address */ + unsigned char dla_mask; /* SpaceWire destination logical address mask */ + unsigned char pid; /* SpaceWire protocol ID */ + + unsigned int offset; /* Packet reception offset */ +}; + +/* SPWCUC Statistics gathered by driver */ +struct spwcuc_stats { + + /* IRQ Stats */ + unsigned int nirqs; + unsigned int tick_tx; + unsigned int tick_tx_wrap; + unsigned int tick_rx; + unsigned int tick_rx_wrap; + unsigned int tick_rx_error; + unsigned int tolerr; + unsigned int sync; + unsigned int syncerr; + unsigned int wrap; + unsigned int wraperr; + unsigned int pkt_rx; + unsigned int pkt_err; + unsigned int pkt_init; +}; + +/* Function ISR callback prototype + * + * pimr - PIMR/PIR register of the SPWCUC core read by ISR + * data - Custom data provided by user + */ +typedef void (*spwcuc_isr_t)(unsigned int pimr, void *data); + +/* Open a SPWCUC device by minor number. A SPWCUC device can only by opened + * once. The handle returned must be used as the input parameter 'spwcuc' in + * the rest of the calls in the function interface. + */ +extern void *spwcuc_open(int minor); + +/* Close a previously opened SPWCUC device */ +extern void spwcuc_close(void *spwcuc); + +/* Reset SPWCUC Core */ +extern int spwcuc_reset(void *spwcuc); + +/* Enable Interrupts at Interrupt controller */ +extern void spwcuc_int_enable(void *spwcuc); + +/* Disable Interrupts at Interrupt controller */ +extern void spwcuc_int_disable(void *spwcuc); + +/* Clear Statistics gathered by the driver */ +extern void spwcuc_clr_stats(void *spwcuc); + +/* Get Statistics gathered by the driver. The statistics are stored into + * the location pointed to by 'stats'. + */ +extern void spwcuc_get_stats(void *spwcuc, struct spwcuc_stats *stats); + +/* Register an Interrupt handler and custom data, the function call is + * removed by setting func to NULL. + * + * The driver's interrupt handler is installed on open(), however the user + * callback called from the driver's ISR is installed using this function. + */ +extern void spwcuc_int_register(void *spwcuc, spwcuc_isr_t func, void *data); + +/* Configure the spwcuc core. The configuration is taken from the data + * structure pointed to by 'cfg'. See data structure spwcuc_cfg fields. + */ +extern void spwcuc_config(void *spwcuc, struct spwcuc_cfg *cfg); + +/* Return elapsed coarse time */ +extern unsigned int spwcuc_get_et_coarse(void *spwcuc); + +/* Return elapsed fine time */ +extern unsigned int spwcuc_get_et_fine(void *spwcuc); + +/* Return elapsed time (coarse and fine) 64-bit value */ +extern unsigned long long spwcuc_get_et(void *spwcuc); + +/* Return next elapsed coarse time (for use when sending SpW time packet) */ +extern unsigned int spwcuc_get_next_et_coarse(void *spwcuc); + +/* Return next elapsed fine time (for use when sending SpW time packet) */ +extern unsigned int spwcuc_get_next_et_fine(void *spwcuc); + +/* Return next elapsed time (for use when sending SpW time packet) */ +extern unsigned long long spwcuc_get_next_et(void *spwcuc); + +/* Force/Set the elapsed time (coarse 32-bit and fine 24-bit) by writing the + * T-Field Time Packet Registers then the FORCE bit. + */ +extern void spwcuc_force_et(void *spwcuc, unsigned long long time); + +/* Return received (from time packet) elapsed coarse time */ +extern unsigned int spwcuc_get_tp_et_coarse(void *spwcuc); + +/* Return received (from time packet) elapsed fine time */ +extern unsigned int spwcuc_get_tp_et_fine(void *spwcuc); + +/* Return received (from time packet) elapsed time (coarse and fine) */ +extern unsigned long long spwcuc_get_tp_et(void *spwcuc); + +/* Clear interrupts */ +extern void spwcuc_clear_irqs(void *spwcuc, int irqs); + +/* Enable interrupts */ +extern void spwcuc_enable_irqs(void *spwcuc, int irqs); + +/* Get Register */ +extern struct spwcuc_regs *spwcuc_get_regs(void *spwcuc); + +/* Register the SPWCUC Driver to the Driver Manager */ +extern void spwcuc_register(void); + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/irq/genirq.c b/c/src/lib/libbsp/sparc/shared/irq/genirq.c new file mode 100644 index 0000000000..782ebf8a5f --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/irq/genirq.c @@ -0,0 +1,241 @@ +/* + * Generic interrupt helpers mainly for GRLIB PCI peripherals + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <rtems.h> +#include <stdlib.h> +#include <string.h> +#include <genirq.h> + +struct genirq_handler_entry { + struct genirq_handler_entry *next; /* Next ISR entry for this IRQ number */ + genirq_handler isr; /* ISR function called upon IRQ */ + void *arg; /* custom argument to ISR */ + int enabled; /* Inidicates if IRQ is enabled */ +}; + +struct genirq_irq_entry { + struct genirq_handler_entry *head; + struct genirq_stats stats; +}; + +struct genirq_priv { + /* Maximum number of interrupt */ + int genirq_max; + /* IRQ Table index N reflect IRQ number N */ + struct genirq_irq_entry genirq_table[1]; /* Length depends on */ +}; + +genirq_t genirq_init(int number_of_irqs) +{ + int size; + struct genirq_priv *priv; + + size = sizeof(int) + + number_of_irqs * sizeof(struct genirq_irq_entry); + + priv = (struct genirq_priv *)malloc(size); + if ( !priv ) + return NULL; + memset(priv, 0, size); + priv->genirq_max = number_of_irqs - 1; + return priv; +} + +void genirq_destroy(genirq_t d) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, *tmp; + int i; + + /* Free all registered interrupts */ + for ( i=0; i<priv->genirq_max; i++) { + irqentry = &priv->genirq_table[i]; + isrentry = irqentry->head; + while ( isrentry ) { + tmp = isrentry; + isrentry = isrentry->next; + free(tmp); + } + } + + free(priv); +} + +int genirq_check(genirq_t d, int irq) +{ + struct genirq_priv *priv = d; + + if ( (irq <= 0) || (irq > priv->genirq_max) ) + return -1; + else + return 0; +} + +int genirq_register(genirq_t d, int irq, genirq_handler isr, void *arg) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, *newentry; + rtems_interrupt_level level; + + if ( genirq_check(d, irq) ) + return -1; + + newentry = malloc(sizeof(*newentry)); + if ( !newentry ) + return -1; + + /* Initialize ISR entry */ + newentry->isr = isr; + newentry->arg = arg; + newentry->enabled = 0; + + rtems_interrupt_disable(level); + + /* Insert new ISR entry first into table */ + irqentry = &priv->genirq_table[irq]; + isrentry = irqentry->head; + irqentry->head = newentry; + newentry->next = isrentry; + + rtems_interrupt_enable(level); + + if ( isrentry ) + return 1; /* This is the first handler on this IRQ */ + return 0; +} + +int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, **prev; + rtems_interrupt_level level; + int ret; + + if ( genirq_check(d, irq) ) + return -1; + + /* Remove isr[arg] from ISR list */ + irqentry = &priv->genirq_table[irq]; + ret = -1; + + rtems_interrupt_disable(level); + + prev = &irqentry->head; + isrentry = irqentry->head; + while ( isrentry ) { + if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) { + /* Found ISR, remove it from list */ + if ( isrentry->enabled ) { + /* Can not remove enabled ISRs, disable first */ + ret = 1; + break; + } + *prev = isrentry->next; + ret = 0; + break; + } + prev = &isrentry->next; + isrentry = isrentry->next; + } + + rtems_interrupt_enable(level); + + return ret; +} + +/* Enables or Disables ISR handler. Internal function to reduce footprint + * of enable/disable functions. + * + * \param action 1=enable, 0=disable ISR + */ +int genirq_set_active(struct genirq_priv *priv, int irq, genirq_handler isr, void *arg, int action) +{ + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry, *e = NULL; + int enabled; + + if ( genirq_check(priv, irq) ) + return -1; + + /* Find isr[arg] in ISR list */ + irqentry = &priv->genirq_table[irq]; + enabled = 0; + + isrentry = irqentry->head; + while ( isrentry ) { + if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) { + /* Found ISR */ + if ( isrentry->enabled == action ) { + /* The ISR is already enabled or disabled + * depending on request, neccessary actions + * were taken last time the same action was + * requested. + */ + return 1; + } + e = isrentry; + } else { + enabled += isrentry->enabled; + } + isrentry = isrentry->next; + } + + if ( !e ) + return -1; + + e->enabled = action; + + return enabled; +} + +int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg) +{ + struct genirq_priv *priv = d; + return genirq_set_active(priv, irq, isr, arg, 1); +} + +int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg) +{ + struct genirq_priv *priv = d; + return genirq_set_active(priv, irq, isr, arg, 0); +} + +void genirq_doirq(genirq_t d, int irq) +{ + struct genirq_priv *priv = d; + struct genirq_irq_entry *irqentry; + struct genirq_handler_entry *isrentry; + int enabled; + + irqentry = &priv->genirq_table[irq]; + irqentry->stats.irq_cnt++; + + enabled = 0; + + isrentry = irqentry->head; + while ( isrentry ) { + if ( isrentry->enabled ) { + enabled = 1; + /* Call the ISR */ + isrentry->isr(isrentry->arg); + } + isrentry = isrentry->next; + } + + /* Was the IRQ an IRQ without source? */ + if ( enabled == 0 ) { + /* This should not happen */ + printk("Spurious IRQ happened on IRQ %d\n", irq); + } +} diff --git a/c/src/lib/libbsp/sparc/shared/mem/mctrl.c b/c/src/lib/libbsp/sparc/shared/mem/mctrl.c new file mode 100644 index 0000000000..b61d21828a --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/mem/mctrl.c @@ -0,0 +1,210 @@ +/* Memory Controller driver (FTMTRL, MCTRL) + * + * COPYRIGHT (c) 2008. + * Cobham Gaisler AB. + * + * This file contains the driver for the MCTRL memory controller. + * The driver sets the memory configuration registers (MCFG1, MCFG2, MCFG3) + * during driver initialization + * + * 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. + */ + +/******************* Driver manager interface ***********************/ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#define MEMSET(priv, start, c, length) memset((void *)start, c, length) + +#define DBG(args...) +/*#define DBG(args...) printk(args)*/ + +struct mctrl_regs { + unsigned int mcfg[8]; +}; + +struct mctrl_priv; + +struct mctrl_ops { + void (*mcfg_set)(struct mctrl_priv *priv, int index, void *regs, unsigned int regval); +}; + +struct mctrl_priv { + struct drvmgr_dev *dev; + void *regs; + unsigned int mcfg[8]; /* The wanted memory configuration */ + unsigned int configured; /* Determines what mcfgs was configured by user */ + struct mctrl_ops *ops; /* Operation may depend on hardware */ +}; + +static int mctrl_init1(struct drvmgr_dev *dev); +static int mctrl_remove(struct drvmgr_dev *dev); + +/* Standard MCFG registers */ +static void mctrl_set_std(struct mctrl_priv *priv, int index, void *regs, unsigned int regval); + +struct mctrl_ops std_mctrl_ops = +{ + mctrl_set_std +}; + +struct drvmgr_drv_ops mctrl_ops = +{ + .init = {mctrl_init1, NULL, NULL, NULL}, + .remove = mctrl_remove, + .info = NULL +}; + +struct amba_dev_id mctrl_ids[] = +{ + {VENDOR_ESA, ESA_MCTRL}, + {VENDOR_GAISLER, GAISLER_FTMCTRL}, + {VENDOR_GAISLER, GAISLER_FTSRCTRL}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info mctrl_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_MCTRL_ID, /* Driver ID */ + "MCTRL_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &mctrl_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &mctrl_ids[0] +}; + +void mctrl_register_drv (void) +{ + DBG("Registering MCTRL driver\n"); + drvmgr_drv_register(&mctrl_drv_info.general); +} + +static int mctrl_init1(struct drvmgr_dev *dev) +{ + struct mctrl_priv *priv; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int i; + char res_name[16]; + union drvmgr_key_value *value; + unsigned int start, length; + + DBG("MCTRL[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(struct mctrl_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if ( ambadev == NULL ) { + return DRVMGR_FAIL; + } + pnpinfo = &ambadev->info; + if ( pnpinfo->apb_slv == NULL ) { + /* LEON2 PnP systems are missing the APB interface */ + priv->regs = (void *)0x80000000; + } else { + priv->regs = (void *)pnpinfo->apb_slv->start; + } + + /* Depending on Hardware selection write/read routines */ + switch ( pnpinfo->vendor ) { + case VENDOR_ESA: + switch ( pnpinfo->device ) { + case ESA_MCTRL: + default: + priv->ops = &std_mctrl_ops; + } + break; + + case VENDOR_GAISLER: + switch ( pnpinfo->device ) { + case GAISLER_FTMCTRL: + case GAISLER_FTSRCTRL: + default: + priv->ops = &std_mctrl_ops; + } + break; + + default: + priv->ops = &std_mctrl_ops; + break; + } + + /* Find user configuration from bus resources */ + priv->configured = 0; + strcpy(res_name, "mcfgX"); + for(i=0; i<8; i++) { + res_name[4] = '1' + i; + value = drvmgr_dev_key_get(priv->dev, res_name, KEY_TYPE_INT); + if ( value ) { + priv->mcfg[i] = value->i; + priv->configured |= (1<<i); + } + } + + /* Init hardware registers right away, other devices may depend on it in init2(), also + * the washing depend on it. + */ + for ( i=0; i<8; i++) { + if ( priv->configured & (1<<i) ) { + DBG("Setting MCFG%d to 0x%08x\n", i+1, priv->mcfg[i]); + priv->ops->mcfg_set(priv, i, priv->regs, priv->mcfg[i]); + } + } + + /* Wash memory partitions if user wants */ + for (i=0; i<9; i++) { + strcpy(res_name, "washXStart"); + res_name[4] = '0' + i; + value = drvmgr_dev_key_get(priv->dev, res_name, KEY_TYPE_INT); + if ( value ) { + start = value->i; + strcpy(res_name, "washXLength"); + res_name[4] = '0' + i; + value = drvmgr_dev_key_get(priv->dev, res_name, KEY_TYPE_INT); + if ( value ) { + length = value->i; + + if ( length > 0 ) { + DBG("MCTRL: Washing 0x%08x-0x%08x\n", start, start+length-1); + + MEMSET(priv, (void *)start, 0, length); + } + } + } + } + + return DRVMGR_OK; +} + +static int mctrl_remove(struct drvmgr_dev *dev) +{ + /* Nothing to be done */ + DBG("Removing %s\n", dev->name); + return DRVMGR_OK; +} + +/* Standard Operations */ +static void mctrl_set_std(struct mctrl_priv *priv, int index, void *regs, unsigned int regval) +{ + struct mctrl_regs *pregs = regs; + + /* Store new value */ + pregs->mcfg[index] = regval; +} diff --git a/c/src/lib/libbsp/sparc/shared/net/README b/c/src/lib/libbsp/sparc/shared/net/README new file mode 100644 index 0000000000..3ef086f223 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/net/README @@ -0,0 +1,7 @@ +A non Driver Manager GRETH driver is located in libchip/network/greth.c. This +version requires the driver manager. + +network_interface_add is used to assign IP/NETMASK and MAC address to +GRETH interfaces dynamically according to in which order devices are +registered. The function takes the settings from the user defined +interface_configs[] array, defined in the project configuration. diff --git a/c/src/lib/libbsp/sparc/shared/net/greth.c b/c/src/lib/libbsp/sparc/shared/net/greth.c new file mode 100644 index 0000000000..31eeadeeb6 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/net/greth.c @@ -0,0 +1,1424 @@ +/* + * Gaisler Research ethernet MAC driver + * adapted from Opencores driver by Marko Isomaki + * + * 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. + * + * + * 2008-12-10, Converted to driver manager and added support for + * multiple GRETH cores. <daniel@gaisler.com> + * 2007-09-07, Ported GBIT support from 4.6.5 + */ +#include <rtems.h> +#define _KERNEL +#define CPU_U32_FIX +#include <bsp.h> + +#ifdef GRETH_SUPPORTED + +#include <inttypes.h> +#include <errno.h> +#include <rtems/bspIo.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> + +#include <greth.h> +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <ambapp.h> + +#include <sys/param.h> +#include <sys/mbuf.h> + +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#ifdef malloc +#undef malloc +#endif +#ifdef free +#undef free +#endif + +#if defined(__m68k__) +extern m68k_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int ); +#else +extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int ); +#endif + + +/* #define GRETH_DEBUG */ + +#ifdef GRETH_DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) +#endif + +#ifdef CPU_U32_FIX +extern void ipalign(struct mbuf *m); +#endif + +/* Used when reading from memory written by GRETH DMA unit */ +#ifndef GRETH_MEM_LOAD +#define GRETH_MEM_LOAD(addr) (*(volatile unsigned int *)(addr)) +#endif + +/* + * Number of OCs supported by this driver + */ +#define NOCDRIVER 1 + +/* + * Receive buffer size -- Allow for a full ethernet packet including CRC + */ +#define RBUF_SIZE 1518 + +#define ET_MINLEN 64 /* minimum message length */ + +/* + * RTEMS event used by interrupt handler to signal driver tasks. + * This must not be any of the events used by the network task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + + /* event to send when tx buffers become available */ +#define GRETH_TX_WAIT_EVENT RTEMS_EVENT_3 + +#if (MCLBYTES < RBUF_SIZE) +# error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +/* 4s Autonegotiation Timeout */ +#ifndef GRETH_AUTONEGO_TIMEOUT_MS +#define GRETH_AUTONEGO_TIMEOUT_MS 4000 +#endif +const struct timespec greth_tan = { + GRETH_AUTONEGO_TIMEOUT_MS/1000, + GRETH_AUTONEGO_TIMEOUT_MS*1000000 +}; + +/* For optimizing the autonegotiation time */ +#define GRETH_AUTONEGO_PRINT_TIME + +/* Ethernet buffer descriptor */ + +typedef struct _greth_rxtxdesc { + volatile uint32_t ctrl; /* Length and status */ + uint32_t *addr; /* Buffer pointer */ +} greth_rxtxdesc; + + +/* + * Per-device data + */ +struct greth_softc +{ + + struct arpcom arpcom; + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; + + greth_regs *regs; + int minor; + int phyaddr; /* PHY Address configured by user (or -1 to autodetect) */ + + int acceptBroadcast; + rtems_id daemonTid; + + unsigned int tx_ptr; + unsigned int tx_dptr; + unsigned int tx_cnt; + unsigned int rx_ptr; + unsigned int txbufs; + unsigned int rxbufs; + greth_rxtxdesc *txdesc; + greth_rxtxdesc *rxdesc; + unsigned int txdesc_remote; + unsigned int rxdesc_remote; + struct mbuf **rxmbuf; + struct mbuf **txmbuf; + rtems_vector_number vector; + + /* TX descriptor interrupt generation */ + int tx_int_gen; + int tx_int_gen_cur; + struct mbuf *next_tx_mbuf; + int max_fragsize; + + /*Status*/ + struct phy_device_info phydev; + int fd; + int sp; + int gb; + int gbit_mac; + int auto_neg; + struct timespec auto_neg_time; + + /* + * Statistics + */ + unsigned long rxInterrupts; + + unsigned long rxPackets; + unsigned long rxLengthError; + unsigned long rxNonOctet; + unsigned long rxBadCRC; + unsigned long rxOverrun; + + unsigned long txInterrupts; + + unsigned long txDeferred; + unsigned long txHeartbeat; + unsigned long txLateCollision; + unsigned long txRetryLimit; + unsigned long txUnderrun; + +}; + +int greth_process_tx_gbit(struct greth_softc *sc); +int greth_process_tx(struct greth_softc *sc); + +static char *almalloc(int sz, int alignment) +{ + char *tmp; + tmp = calloc(1, sz + (alignment-1)); + tmp = (char *) (((int)tmp+alignment) & ~(alignment -1)); + return(tmp); +} + +/* GRETH interrupt handler */ + +void greth_interrupt (void *arg) +{ + uint32_t status; + uint32_t ctrl; + rtems_event_set events = 0; + struct greth_softc *greth = arg; + + /* read and clear interrupt cause */ + status = greth->regs->status; + greth->regs->status = status; + ctrl = greth->regs->ctrl; + + /* Frame received? */ + if ((ctrl & GRETH_CTRL_RXIRQ) && (status & (GRETH_STATUS_RXERR | GRETH_STATUS_RXIRQ))) + { + greth->rxInterrupts++; + /* Stop RX-Error and RX-Packet interrupts */ + ctrl &= ~GRETH_CTRL_RXIRQ; + events |= INTERRUPT_EVENT; + } + + if ( (ctrl & GRETH_CTRL_TXIRQ) && (status & (GRETH_STATUS_TXERR | GRETH_STATUS_TXIRQ)) ) + { + greth->txInterrupts++; + ctrl &= ~GRETH_CTRL_TXIRQ; + events |= GRETH_TX_WAIT_EVENT; + } + + /* Clear interrupt sources */ + greth->regs->ctrl = ctrl; + + /* Send the event(s) */ + if ( events ) + rtems_event_send (greth->daemonTid, events); +} + +static uint32_t read_mii(struct greth_softc *sc, uint32_t phy_addr, uint32_t reg_addr) +{ + while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {} + sc->regs->mdio_ctrl = (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_READ; + while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {} + if (!(sc->regs->mdio_ctrl & GRETH_MDIO_LINKFAIL)) + return((sc->regs->mdio_ctrl >> 16) & 0xFFFF); + else { + printf("greth: failed to read mii\n"); + return (0); + } +} + +static void write_mii(struct greth_softc *sc, uint32_t phy_addr, uint32_t reg_addr, uint32_t data) +{ + while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {} + sc->regs->mdio_ctrl = + ((data & 0xFFFF) << 16) | (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_WRITE; + while (sc->regs->mdio_ctrl & GRETH_MDIO_BUSY) {} +} + +static void print_init_info(struct greth_softc *sc) +{ + printf("greth: driver attached\n"); + if ( sc->auto_neg == -1 ){ + printf("Auto negotiation timed out. Selecting default config\n"); + } + printf("**** PHY ****\n"); + printf("Vendor: %x Device: %x Revision: %d\n",sc->phydev.vendor, sc->phydev.device, sc->phydev.rev); + printf("Current Operating Mode: "); + if (sc->gb) { + printf("1000 Mbit "); + } else if (sc->sp) { + printf("100 Mbit "); + } else { + printf("10 Mbit "); + } + if (sc->fd) { + printf("Full Duplex\n"); + } else { + printf("Half Duplex\n"); + } +#ifdef GRETH_AUTONEGO_PRINT_TIME + if ( sc->auto_neg ) { + printf("Autonegotiation Time: %dms\n", sc->auto_neg_time.tv_sec * 1000 + + sc->auto_neg_time.tv_nsec / 1000000); + } +#endif +} + + +/* + * Initialize the ethernet hardware + */ +static void +greth_initialize_hardware (struct greth_softc *sc) +{ + struct mbuf *m; + int i; + int phyaddr; + int phyctrl; + int phystatus; + int tmp1; + int tmp2; + struct timespec tstart, tnow; + + greth_regs *regs; + + regs = sc->regs; + + /* Reset the controller. */ + sc->rxInterrupts = 0; + sc->rxPackets = 0; + + regs->ctrl = 0; + regs->ctrl = GRETH_CTRL_RST; /* Reset ON */ + regs->ctrl = 0; /* Reset OFF */ + + /* Check if mac is gbit capable*/ + sc->gbit_mac = (regs->ctrl >> 27) & 1; + + /* Get the phy address which assumed to have been set + correctly with the reset value in hardware*/ + if ( sc->phyaddr == -1 ) { + phyaddr = (regs->mdio_ctrl >> 11) & 0x1F; + } else { + phyaddr = sc->phyaddr; + } + + /* get phy control register default values */ + while ((phyctrl = read_mii(sc, phyaddr, 0)) & 0x8000) {} + + /* reset PHY and wait for completion */ + write_mii(sc, phyaddr, 0, 0x8000 | phyctrl); + + while ((read_mii(sc, phyaddr, 0)) & 0x8000) {} + + /* Check if PHY is autoneg capable and then determine operating mode, + otherwise force it to 10 Mbit halfduplex */ + sc->gb = 0; + sc->fd = 0; + sc->sp = 0; + sc->auto_neg = 0; + _Timespec_Set_to_zero(&sc->auto_neg_time); + if ((phyctrl >> 12) & 1) { + /*wait for auto negotiation to complete*/ + sc->auto_neg = 1; + if (rtems_clock_get_uptime(&tstart) != RTEMS_SUCCESSFUL) + printk("rtems_clock_get_uptime failed\n"); + while (!(((phystatus = read_mii(sc, phyaddr, 1)) >> 5) & 1)) { + if (rtems_clock_get_uptime(&tnow) != RTEMS_SUCCESSFUL) + printk("rtems_clock_get_uptime failed\n"); + _Timespec_Subtract(&tstart, &tnow, &sc->auto_neg_time); + if (_Timespec_Greater_than(&sc->auto_neg_time, &greth_tan)) { + sc->auto_neg = -1; /* Failed */ + tmp1 = read_mii(sc, phyaddr, 0); + sc->gb = ((phyctrl >> 6) & 1) && !((phyctrl >> 13) & 1); + sc->sp = !((phyctrl >> 6) & 1) && ((phyctrl >> 13) & 1); + sc->fd = (phyctrl >> 8) & 1; + goto auto_neg_done; + } + /* Wait about 30ms, time is PHY dependent */ + rtems_task_wake_after(rtems_clock_get_ticks_per_second()/32); + } + sc->phydev.adv = read_mii(sc, phyaddr, 4); + sc->phydev.part = read_mii(sc, phyaddr, 5); + if ((phystatus >> 8) & 1) { + sc->phydev.extadv = read_mii(sc, phyaddr, 9); + sc->phydev.extpart = read_mii(sc, phyaddr, 10); + if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000FD) && + (sc->phydev.extpart & GRETH_MII_EXTPRT_1000FD)) { + sc->gb = 1; + sc->fd = 1; + } + if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000HD) && + (sc->phydev.extpart & GRETH_MII_EXTPRT_1000HD)) { + sc->gb = 1; + sc->fd = 0; + } + } + if ((sc->gb == 0) || ((sc->gb == 1) && (sc->gbit_mac == 0))) { + if ( (sc->phydev.adv & GRETH_MII_100TXFD) && + (sc->phydev.part & GRETH_MII_100TXFD)) { + sc->sp = 1; + sc->fd = 1; + } + if ( (sc->phydev.adv & GRETH_MII_100TXHD) && + (sc->phydev.part & GRETH_MII_100TXHD)) { + sc->sp = 1; + sc->fd = 0; + } + if ( (sc->phydev.adv & GRETH_MII_10FD) && + (sc->phydev.part & GRETH_MII_10FD)) { + sc->fd = 1; + } + } + } +auto_neg_done: + sc->phydev.vendor = 0; + sc->phydev.device = 0; + sc->phydev.rev = 0; + phystatus = read_mii(sc, phyaddr, 1); + + /*Read out PHY info if extended registers are available */ + if (phystatus & 1) { + tmp1 = read_mii(sc, phyaddr, 2); + tmp2 = read_mii(sc, phyaddr, 3); + + sc->phydev.vendor = (tmp1 << 6) | ((tmp2 >> 10) & 0x3F); + sc->phydev.rev = tmp2 & 0xF; + sc->phydev.device = (tmp2 >> 4) & 0x3F; + } + + /* Force to 10 mbit half duplex if the 10/100 MAC is used with a 1000 PHY*/ + /*check if marvell 88EE1111 PHY. Needs special reset handling */ + if ((phystatus & 1) && (sc->phydev.vendor == 0x005043) && (sc->phydev.device == 0x0C)) { + if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) { + write_mii(sc, phyaddr, 0, sc->sp << 13); + write_mii(sc, phyaddr, 0, 0x8000); + sc->gb = 0; + sc->sp = 0; + sc->fd = 0; + } + } else { + if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) { + write_mii(sc, phyaddr, 0, sc->sp << 13); + sc->gb = 0; + sc->sp = 0; + sc->fd = 0; + } + } + while ((read_mii(sc, phyaddr, 0)) & 0x8000) {} + + regs->ctrl = 0; + regs->ctrl = GRETH_CTRL_RST; /* Reset ON */ + regs->ctrl = 0; + + /* Initialize rx/tx descriptor table pointers. Due to alignment we + * always allocate maximum table size. + */ + sc->txdesc = (greth_rxtxdesc *) almalloc(0x800, 0x400); + sc->rxdesc = (greth_rxtxdesc *) &sc->txdesc[128]; + sc->tx_ptr = 0; + sc->tx_dptr = 0; + sc->tx_cnt = 0; + sc->rx_ptr = 0; + + /* Translate the Descriptor DMA table base address into an address that + * the GRETH core can understand + */ + drvmgr_translate_check( + sc->dev, + CPUMEM_TO_DMA, + (void *)sc->txdesc, + (void **)&sc->txdesc_remote, + 0x800); + sc->rxdesc_remote = sc->txdesc_remote + 0x400; + regs->txdesc = (int) sc->txdesc_remote; + regs->rxdesc = (int) sc->rxdesc_remote; + + sc->rxmbuf = calloc(sc->rxbufs, sizeof(*sc->rxmbuf)); + sc->txmbuf = calloc(sc->txbufs, sizeof(*sc->txmbuf)); + + for (i = 0; i < sc->txbufs; i++) + { + sc->txdesc[i].ctrl = 0; + if (!(sc->gbit_mac)) { + drvmgr_translate_check( + sc->dev, + CPUMEM_TO_DMA, + (void *)malloc(GRETH_MAXBUF_LEN), + (void **)&sc->txdesc[i].addr, + GRETH_MAXBUF_LEN); + } +#ifdef GRETH_DEBUG + /* printf("TXBUF: %08x\n", (int) sc->txdesc[i].addr); */ +#endif + } + for (i = 0; i < sc->rxbufs; i++) + { + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + if (sc->gbit_mac) + m->m_data += 2; + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + sc->rxmbuf[i] = m; + drvmgr_translate_check( + sc->dev, + CPUMEM_TO_DMA, + (void *)mtod(m, uint32_t *), + (void **)&sc->rxdesc[i].addr, + GRETH_MAXBUF_LEN); + sc->rxdesc[i].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ; +#ifdef GRETH_DEBUG +/* printf("RXBUF: %08x\n", (int) sc->rxdesc[i].addr); */ +#endif + } + sc->rxdesc[sc->rxbufs - 1].ctrl |= GRETH_RXD_WRAP; + + /* set ethernet address. */ + regs->mac_addr_msb = + sc->arpcom.ac_enaddr[0] << 8 | sc->arpcom.ac_enaddr[1]; + regs->mac_addr_lsb = + sc->arpcom.ac_enaddr[2] << 24 | sc->arpcom.ac_enaddr[3] << 16 | + sc->arpcom.ac_enaddr[4] << 8 | sc->arpcom.ac_enaddr[5]; + + if ( sc->rxbufs < 10 ) { + sc->tx_int_gen = sc->tx_int_gen_cur = 1; + }else{ + sc->tx_int_gen = sc->tx_int_gen_cur = sc->txbufs/2; + } + sc->next_tx_mbuf = NULL; + + if ( !sc->gbit_mac ) + sc->max_fragsize = 1; + + /* clear all pending interrupts */ + regs->status = 0xffffffff; + + /* install interrupt handler */ + drvmgr_interrupt_register(sc->dev, 0, "greth", greth_interrupt, sc); + + regs->ctrl |= GRETH_CTRL_RXEN | (sc->fd << 4) | GRETH_CTRL_RXIRQ | (sc->sp << 7) | (sc->gb << 8); + + print_init_info(sc); +} + +#ifdef CPU_U32_FIX + +/* + * Routine to align the received packet so that the ip header + * is on a 32-bit boundary. Necessary for cpu's that do not + * allow unaligned loads and stores and when the 32-bit DMA + * mode is used. + * + * Transfers are done on word basis to avoid possibly slow byte + * and half-word writes. + */ + +void ipalign(struct mbuf *m) +{ + unsigned int *first, *last, data; + unsigned int tmp = 0; + + if ((((int) m->m_data) & 2) && (m->m_len)) { + last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3); + first = (unsigned int *) (((int) m->m_data) & ~3); + /* tmp = *first << 16; */ + asm volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(first) ); + tmp = tmp << 16; + first++; + do { + /* When snooping is not available the LDA instruction must be used + * to avoid the cache to return an illegal value. + ** Load with forced cache miss + * data = *first; + */ + asm volatile (" lda [%1] 1, %0\n" : "=r"(data) : "r"(first) ); + *first = tmp | (data >> 16); + tmp = data << 16; + first++; + } while (first <= last); + + m->m_data = (caddr_t)(((int) m->m_data) + 2); + } +} +#endif + +void +greth_Daemon (void *arg) +{ + struct ether_header *eh; + struct greth_softc *dp = (struct greth_softc *) arg; + struct ifnet *ifp = &dp->arpcom.ac_if; + struct mbuf *m; + unsigned int len, len_status, bad; + rtems_event_set events; + rtems_interrupt_level level; + int first; + int tmp; + unsigned int addr; + + for (;;) + { + rtems_bsdnet_event_receive (INTERRUPT_EVENT | GRETH_TX_WAIT_EVENT, + RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + + if ( events & GRETH_TX_WAIT_EVENT ){ + /* TX interrupt. + * We only end up here when all TX descriptors has been used, + * and + */ + if ( dp->gbit_mac ) + greth_process_tx_gbit(dp); + else + greth_process_tx(dp); + + /* If we didn't get a RX interrupt we don't process it */ + if ( (events & INTERRUPT_EVENT) == 0 ) + continue; + } + + +#ifdef GRETH_ETH_DEBUG + printf ("r\n"); +#endif + first=1; + /* Scan for Received packets */ +again: + while (!((len_status = + GRETH_MEM_LOAD(&dp->rxdesc[dp->rx_ptr].ctrl)) & GRETH_RXD_ENABLE)) + { + bad = 0; + if (len_status & GRETH_RXD_TOOLONG) + { + dp->rxLengthError++; + bad = 1; + } + if (len_status & GRETH_RXD_DRIBBLE) + { + dp->rxNonOctet++; + bad = 1; + } + if (len_status & GRETH_RXD_CRCERR) + { + dp->rxBadCRC++; + bad = 1; + } + if (len_status & GRETH_RXD_OVERRUN) + { + dp->rxOverrun++; + bad = 1; + } + if (len_status & GRETH_RXD_LENERR) + { + dp->rxLengthError++; + bad = 1; + } + if (!bad) + { + /* pass on the packet in the receive buffer */ + len = len_status & 0x7FF; + m = dp->rxmbuf[dp->rx_ptr]; +#ifdef GRETH_DEBUG + int i; + printf("RX: 0x%08x, Len: %d : ", (int) m->m_data, len); + for (i=0; i<len; i++) + printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff); + printf("\n"); +#endif + m->m_len = m->m_pkthdr.len = + len - sizeof (struct ether_header); + + eh = mtod (m, struct ether_header *); + + m->m_data += sizeof (struct ether_header); +#ifdef CPU_U32_FIX + if(!dp->gbit_mac) { + /* OVERRIDE CACHED ETHERNET HEADER FOR NON-SNOOPING SYSTEMS */ + addr = (unsigned int)eh; + asm volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(addr) ); + addr+=4; + asm volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(addr) ); + addr+=4; + asm volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(addr) ); + addr+=4; + asm volatile (" lda [%1] 1, %0\n" : "=r"(tmp) : "r"(addr) ); + + ipalign(m); /* Align packet on 32-bit boundary */ + } +#endif +/* + if(!(dp->gbit_mac) && !CPU_SPARC_HAS_SNOOPING) { + rtems_cache_invalidate_entire_data(); + } +*/ + ether_input (ifp, eh, m); + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + if (dp->gbit_mac) + m->m_data += 2; + dp->rxmbuf[dp->rx_ptr] = m; + m->m_pkthdr.rcvif = ifp; + drvmgr_translate_check( + dp->dev, + CPUMEM_TO_DMA, + (void *)mtod (m, uint32_t *), + (void **)&dp->rxdesc[dp->rx_ptr].addr, + GRETH_MAXBUF_LEN); + dp->rxPackets++; + } + if (dp->rx_ptr == dp->rxbufs - 1) { + dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ | GRETH_RXD_WRAP; + } else { + dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ; + } + rtems_interrupt_disable(level); + dp->regs->ctrl |= GRETH_CTRL_RXEN; + rtems_interrupt_enable(level); + dp->rx_ptr = (dp->rx_ptr + 1) % dp->rxbufs; + } + + /* Always scan twice to avoid deadlock */ + if ( first ){ + first=0; + rtems_interrupt_disable(level); + dp->regs->ctrl |= GRETH_CTRL_RXIRQ; + rtems_interrupt_enable(level); + goto again; + } + + } + +} + +static int inside = 0; +static int +sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct greth_softc *dp = ifp->if_softc; + unsigned char *temp; + struct mbuf *n; + unsigned int len; + rtems_interrupt_level level; + + /*printf("Send packet entered\n");*/ + if (inside) printf ("error: sendpacket re-entered!!\n"); + inside = 1; + + /* + * Is there a free descriptor available? + */ + if (GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].ctrl) & GRETH_TXD_ENABLE){ + /* No. */ + inside = 0; + return 1; + } + + /* Remember head of chain */ + n = m; + + len = 0; + temp = (unsigned char *) GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].addr); + drvmgr_translate(dp->dev, CPUMEM_FROM_DMA, (void *)temp, (void **)&temp); +#ifdef GRETH_DEBUG + printf("TXD: 0x%08x : BUF: 0x%08x\n", (int) m->m_data, (int) temp); +#endif + for (;;) + { +#ifdef GRETH_DEBUG + int i; + printf("MBUF: 0x%08x : ", (int) m->m_data); + for (i=0;i<m->m_len;i++) + printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff); + printf("\n"); +#endif + len += m->m_len; + if (len <= RBUF_SIZE) + memcpy ((void *) temp, (char *) m->m_data, m->m_len); + temp += m->m_len; + if ((m = m->m_next) == NULL) + break; + } + + m_freem (n); + + /* don't send long packets */ + + if (len <= GRETH_MAXBUF_LEN) { + if (dp->tx_ptr < dp->txbufs-1) { + dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_ENABLE | len; + } else { + dp->txdesc[dp->tx_ptr].ctrl = + GRETH_TXD_WRAP | GRETH_TXD_ENABLE | len; + } + dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs; + rtems_interrupt_disable(level); + dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN; + rtems_interrupt_enable(level); + + } + inside = 0; + + return 0; +} + + +int +sendpacket_gbit (struct ifnet *ifp, struct mbuf *m) +{ + struct greth_softc *dp = ifp->if_softc; + unsigned int len; + + unsigned int ctrl; + int frags; + struct mbuf *mtmp; + int int_en; + rtems_interrupt_level level; + + if (inside) printf ("error: sendpacket re-entered!!\n"); + inside = 1; + + len = 0; +#ifdef GRETH_DEBUG + printf("TXD: 0x%08x\n", (int) m->m_data); +#endif + /* Get number of fragments too see if we have enough + * resources. + */ + frags=1; + mtmp=m; + while(mtmp->m_next){ + frags++; + mtmp = mtmp->m_next; + } + + if ( frags > dp->max_fragsize ) + dp->max_fragsize = frags; + + if ( frags > dp->txbufs ){ + inside = 0; + printf("GRETH: MBUF-chain cannot be sent. Increase descriptor count.\n"); + return -1; + } + + if ( frags > (dp->txbufs-dp->tx_cnt) ){ + inside = 0; + /* Return number of fragments */ + return frags; + } + + + /* Enable interrupt from descriptor every tx_int_gen + * descriptor. Typically every 16 descriptor. This + * is only to reduce the number of interrupts during + * heavy load. + */ + dp->tx_int_gen_cur-=frags; + if ( dp->tx_int_gen_cur <= 0 ){ + dp->tx_int_gen_cur = dp->tx_int_gen; + int_en = GRETH_TXD_IRQ; + }else{ + int_en = 0; + } + + /* At this stage we know that enough descriptors are available */ + for (;;) + { + +#ifdef GRETH_DEBUG + int i; + printf("MBUF: 0x%08x, Len: %d : ", (int) m->m_data, m->m_len); + for (i=0; i<m->m_len; i++) + printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff); + printf("\n"); +#endif + len += m->m_len; + drvmgr_translate_check( + dp->dev, + CPUMEM_TO_DMA, + (void *)(uint32_t *)m->m_data, + (void **)&dp->txdesc[dp->tx_ptr].addr, + m->m_len); + + /* Wrap around? */ + if (dp->tx_ptr < dp->txbufs-1) { + ctrl = GRETH_TXD_ENABLE | GRETH_TXD_CS; + }else{ + ctrl = GRETH_TXD_ENABLE | GRETH_TXD_CS | GRETH_TXD_WRAP; + } + + /* Enable Descriptor */ + if ((m->m_next) == NULL) { + dp->txdesc[dp->tx_ptr].ctrl = ctrl | int_en | m->m_len; + break; + }else{ + dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_MORE | ctrl | int_en | m->m_len; + } + + /* Next */ + dp->txmbuf[dp->tx_ptr] = m; + dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs; + dp->tx_cnt++; + m = m->m_next; + } + dp->txmbuf[dp->tx_ptr] = m; + dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs; + dp->tx_cnt++; + + /* Tell Hardware about newly enabled descriptor */ + rtems_interrupt_disable(level); + dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN; + rtems_interrupt_enable(level); + + inside = 0; + + return 0; +} + +int greth_process_tx_gbit(struct greth_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_interrupt_level level; + int first=1; + + /* + * Send packets till queue is empty + */ + for (;;){ + /* Reap Sent packets */ + while((sc->tx_cnt > 0) && !(GRETH_MEM_LOAD(&sc->txdesc[sc->tx_dptr].ctrl) & GRETH_TXD_ENABLE)) { + m_free(sc->txmbuf[sc->tx_dptr]); + sc->tx_dptr = (sc->tx_dptr + 1) % sc->txbufs; + sc->tx_cnt--; + } + + if ( sc->next_tx_mbuf ){ + /* Get packet we tried but faild to transmit last time */ + m = sc->next_tx_mbuf; + sc->next_tx_mbuf = NULL; /* Mark packet taken */ + }else{ + /* + * Get the next mbuf chain to transmit from Stack. + */ + IF_DEQUEUE (&ifp->if_snd, m); + if (!m){ + /* Hardware has sent all schedule packets, this + * makes the stack enter at greth_start next time + * a packet is to be sent. + */ + ifp->if_flags &= ~IFF_OACTIVE; + break; + } + } + + /* Are there free descriptors available? */ + /* Try to send packet, if it a negative number is returned. */ + if ( (sc->tx_cnt >= sc->txbufs) || sendpacket_gbit(ifp, m) ){ + /* Not enough resources */ + + /* Since we have taken the mbuf out of the "send chain" + * we must remember to use that next time we come back. + * or else we have dropped a packet. + */ + sc->next_tx_mbuf = m; + + /* Not enough resources, enable interrupt for transmissions + * this way we will be informed when more TX-descriptors are + * available. + */ + if ( first ){ + first = 0; + rtems_interrupt_disable(level); + ifp->if_flags |= IFF_OACTIVE; + sc->regs->ctrl |= GRETH_CTRL_TXIRQ; + rtems_interrupt_enable(level); + + /* We must check again to be sure that we didn't + * miss an interrupt (if a packet was sent just before + * enabling interrupts) + */ + continue; + } + + return -1; + }else{ + /* Sent Ok, proceed to process more packets if available */ + } + } + return 0; +} + +int greth_process_tx(struct greth_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_interrupt_level level; + int first=1; + + /* + * Send packets till queue is empty + */ + for (;;){ + if ( sc->next_tx_mbuf ){ + /* Get packet we tried but failed to transmit last time */ + m = sc->next_tx_mbuf; + sc->next_tx_mbuf = NULL; /* Mark packet taken */ + }else{ + /* + * Get the next mbuf chain to transmit from Stack. + */ + IF_DEQUEUE (&ifp->if_snd, m); + if (!m){ + /* Hardware has sent all schedule packets, this + * makes the stack enter at greth_start next time + * a packet is to be sent. + */ + ifp->if_flags &= ~IFF_OACTIVE; + break; + } + } + + /* Try to send packet, failed if it a non-zero number is returned. */ + if ( sendpacket(ifp, m) ){ + /* Not enough resources */ + + /* Since we have taken the mbuf out of the "send chain" + * we must remember to use that next time we come back. + * or else we have dropped a packet. + */ + sc->next_tx_mbuf = m; + + /* Not enough resources, enable interrupt for transmissions + * this way we will be informed when more TX-descriptors are + * available. + */ + if ( first ){ + first = 0; + rtems_interrupt_disable(level); + ifp->if_flags |= IFF_OACTIVE; + sc->regs->ctrl |= GRETH_CTRL_TXIRQ; + rtems_interrupt_enable(level); + + /* We must check again to be sure that we didn't + * miss an interrupt (if a packet was sent just before + * enabling interrupts) + */ + continue; + } + + return -1; + }else{ + /* Sent Ok, proceed to process more packets if available */ + } + } + return 0; +} + +static void +greth_start (struct ifnet *ifp) +{ + struct greth_softc *sc = ifp->if_softc; + + if ( ifp->if_flags & IFF_OACTIVE ) + return; + + if ( sc->gbit_mac ){ + /* No use trying to handle this if we are waiting on GRETH + * to send the previously scheduled packets. + */ + + greth_process_tx_gbit(sc); + }else{ + greth_process_tx(sc); + } + +} + +/* + * Initialize and start the device + */ +static void +greth_init (void *arg) +{ + struct greth_softc *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->daemonTid == 0) + { + + /* + * Start driver tasks + */ + sc->daemonTid = rtems_bsdnet_newproc ("DCrxtx", 4096, + greth_Daemon, sc); + + /* + * Set up GRETH hardware + */ + greth_initialize_hardware (sc); + + } + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + +} + +/* + * Stop the device + */ +static void +greth_stop (struct greth_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + sc->regs->ctrl = 0; /* RX/TX OFF */ + sc->regs->ctrl = GRETH_CTRL_RST; /* Reset ON */ + sc->regs->ctrl = 0; /* Reset OFF */ + + sc->next_tx_mbuf = NULL; +} + + +/* + * Show interface statistics + */ +static void +greth_stats (struct greth_softc *sc) +{ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Rx Packets:%-8lu", sc->rxPackets); + printf (" Length:%-8lu", sc->rxLengthError); + printf (" Non-octet:%-8lu\n", sc->rxNonOctet); + printf (" Bad CRC:%-8lu", sc->rxBadCRC); + printf (" Overrun:%-8lu", sc->rxOverrun); + printf (" Tx Interrupts:%-8lu", sc->txInterrupts); + printf (" Maximal Frags:%-8d", sc->max_fragsize); + printf (" GBIT MAC:%-8d", sc->gbit_mac); +} + +/* + * Driver ioctl handler + */ +static int +greth_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) +{ + struct greth_softc *sc = ifp->if_softc; + int error = 0; + + switch (command) + { + case SIOCGIFADDR: + case SIOCSIFADDR: + ether_ioctl (ifp, command, data); + break; + + case SIOCSIFFLAGS: + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) + { + case IFF_RUNNING: + greth_stop (sc); + break; + + case IFF_UP: + greth_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + greth_stop (sc); + greth_init (sc); + break; + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + greth_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + + return error; +} + +/* + * Attach an GRETH driver to the system + */ +int +greth_interface_driver_attach ( + struct rtems_bsdnet_ifconfig *config, + int attach + ) +{ + struct greth_softc *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + + /* parse driver name */ + if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0) + return 0; + + sc = config->drv_ctrl; + ifp = &sc->arpcom.ac_if; +#ifdef GRETH_DEBUG + printf("GRETH[%d]: %s, sc %p, dev %p on %s\n", unitNumber, config->ip_address, sc, sc->dev, sc->dev->parent->dev->name); +#endif + if (config->hardware_address) + { + memcpy (sc->arpcom.ac_enaddr, config->hardware_address, + ETHER_ADDR_LEN); + } + else + { + memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN); + } + + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + + sc->acceptBroadcast = !config->ignore_broadcast; + + /* + * Set up network interface values + */ + ifp->if_softc = sc; + ifp->if_unit = unitNumber; + ifp->if_name = unitName; + ifp->if_mtu = mtu; + ifp->if_init = greth_init; + ifp->if_ioctl = greth_ioctl; + ifp->if_start = greth_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* + * Attach the interface + */ + if_attach (ifp); + ether_ifattach (ifp); + +#ifdef GRETH_DEBUG + printf ("GRETH : driver has been attached\n"); +#endif + return 1; +} + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +int greth_register_io(rtems_device_major_number *m); +int greth_device_init(struct greth_softc *sc); +int network_interface_add(struct rtems_bsdnet_ifconfig *interface); + +#ifdef GRETH_INFO_AVAIL +static int greth_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int argc, char *argv[]); +#define GRETH_INFO_FUNC greth_info +#else +#define GRETH_INFO_FUNC NULL +#endif + +int greth_init2(struct drvmgr_dev *dev); +int greth_init3(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops greth_ops = +{ + .init = + { + NULL, + greth_init2, + greth_init3, + NULL + }, + .remove = NULL, + .info = GRETH_INFO_FUNC, +}; + +struct amba_dev_id greth_ids[] = +{ + {VENDOR_GAISLER, GAISLER_ETHMAC}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info greth_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRETH_ID, /* Driver ID */ + "GRETH_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &greth_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &greth_ids[0] +}; + +void greth_register_drv (void) +{ + DBG("Registering GRETH driver\n"); + drvmgr_drv_register(&greth_drv_info.general); +} + +int greth_init2(struct drvmgr_dev *dev) +{ + struct greth_softc *priv; + + DBG("GRETH[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(struct greth_softc)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init3() */ + + return DRVMGR_OK; +} + +int greth_init3(struct drvmgr_dev *dev) +{ + struct greth_softc *sc; + struct rtems_bsdnet_ifconfig *ifp; + rtems_status_code status; + + sc = dev->priv; + sprintf(sc->devName, "gr_eth%d", (dev->minor_drv+1)); + + /* Init GRETH device */ + if ( greth_device_init(sc) ) { + printk("GRETH: Failed to init device\n"); + return DRVMGR_FAIL; + } + + /* Register GRETH device as an Network interface */ + ifp = malloc(sizeof(struct rtems_bsdnet_ifconfig)); + memset(ifp, 0, sizeof(*ifp)); + + ifp->name = sc->devName; + ifp->drv_ctrl = sc; + ifp->attach = greth_interface_driver_attach; + + status = network_interface_add(ifp); + if (status != 0) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +int greth_device_init(struct greth_softc *sc) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)sc->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + sc->regs = (greth_regs *)pnpinfo->apb_slv->start; + sc->minor = sc->dev->minor_drv; + + /* clear control register and reset NIC + * This should be done as quick as possible during startup, this is to + * stop DMA transfers after a reboot. + */ + sc->regs->ctrl = 0; + sc->regs->ctrl = GRETH_CTRL_RST; + sc->regs->ctrl = 0; + + /* Configure driver by overriding default config with the bus resources + * configured by the user + */ + sc->txbufs = 32; + sc->rxbufs = 32; + sc->phyaddr = -1; + + value = drvmgr_dev_key_get(sc->dev, "txDescs", KEY_TYPE_INT); + if ( value && (value->i <= 128) ) + sc->txbufs = value->i; + + value = drvmgr_dev_key_get(sc->dev, "rxDescs", KEY_TYPE_INT); + if ( value && (value->i <= 128) ) + sc->rxbufs = value->i; + + value = drvmgr_dev_key_get(sc->dev, "phyAdr", KEY_TYPE_INT); + if ( value && (value->i < 32) ) + sc->phyaddr = value->i; + + return 0; +} + +#ifdef GRETH_INFO_AVAIL +static int greth_info( + struct drvmgr_dev *dev, + void (*print_line)(void *p, char *str), + void *p, int argc, char *argv[]) +{ + struct greth_softc *sc; + char buf[64]; + + if (dev->priv == NULL) + return -DRVMGR_EINVAL; + sc = dev->priv; + + sprintf(buf, "IFACE NAME: %s", sc->devName); + print_line(p, buf); + sprintf(buf, "GBIT MAC: %s", sc->gbit_mac ? "YES" : "NO"); + print_line(p, buf); + + return DRVMGR_OK; +} +#endif + +#endif diff --git a/c/src/lib/libbsp/sparc/shared/pwm/grpwm.c b/c/src/lib/libbsp/sparc/shared/pwm/grpwm.c new file mode 100644 index 0000000000..753a6807f3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/pwm/grpwm.c @@ -0,0 +1,847 @@ +/* + * GRPWM PWM Driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB, + * + * 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. + */ + +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <assert.h> +#include <rtems/bspIo.h> +#include <string.h> +#include <stdio.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <grpwm.h> +#include <ambapp.h> + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#define STATIC +#else +#define DBG(x...) +#define STATIC static +#endif + +/*** REGISTER LAYOUT ***/ + +/* PWM Channel specific registers */ +struct grpwm_pwm_regs { + volatile unsigned int period; /* 0x00 */ + volatile unsigned int comp; /* 0x04 */ + volatile unsigned int dbcomp; /* 0x08 */ + volatile unsigned int ctrl; /* 0x0C */ +}; + +/* Core common registers */ +struct grpwm_regs { + volatile unsigned int ctrl; /* 0x00 */ + volatile unsigned int scaler; /* 0x04 */ + volatile unsigned int ipend; /* 0x08 */ + volatile unsigned int cap1; /* 0x0C */ + volatile unsigned int cap2; /* 0x10 */ + volatile unsigned int wctrl; /* 0x14 */ + int reserved0[2]; + struct grpwm_pwm_regs pwms[8]; /* 0x20 */ + int reserved1[(0x8000-0xA0)/4]; /* 0xA0-0x7FFC */ + volatile unsigned int wram[0x8000/4]; /* 0x8000-0xFFFC */ +}; + +/*** REGISTER BIT LAYOUT ***/ + +/* CTRL REGISTER - 0x0 */ +#define GRPWM_CTRL_EN_BIT 0 +#define GRPWM_CTRL_SCSEL_BIT 8 +#define GRPWM_CTRL_NOUP_BIT 12 +#define GRPWM_CTRL_EN (1<<GRPWM_CTRL_EN_BIT) +#define GRPWM_CTRL_SCSEL (0x7<<GRPWM_CTRL_SCSEL_BIT) +#define GRPWM_CTRL_NOUP (0xff<<GRPWM_CTRL_NOUP_BIT) + + +/* CAPABILITY1 REGISTER - 0x0C */ +#define GRPWM_CAP_NPWM_BIT 0 +#define GRPWM_CAP_PBITS_BIT 3 +#define GRPWM_CAP_SBITS_BIT 8 +#define GRPWM_CAP_NSC_BIT 13 +#define GRPWM_CAP_DBB_BIT 16 +#define GRPWM_CAP_DBSC_BIT 21 +#define GRPWM_CAP_ASY_BIT 22 +#define GRPWM_CAP_SYM_BIT 23 +#define GRPWM_CAP_SEP_BIT 25 +#define GRPWM_CAP_DCM_BIT 27 + +#define GRPWM_CAP_NPWM (0x7<<GRPWM_CAP_NPWM_BIT) +#define GRPWM_CAP_PBITS (0x1f<<GRPWM_CAP_PBITS_BIT) +#define GRPWM_CAP_SBITS (0x1f<<GRPWM_CAP_SBITS_BIT) +#define GRPWM_CAP_NSC (0x7<<GRPWM_CAP_NSC_BIT) +#define GRPWM_CAP_DBB (0x1f<<GRPWM_CAP_DBB_BIT) +#define GRPWM_CAP_DBSC (1<<GRPWM_CAP_DBSC_BIT) +#define GRPWM_CAP_ASY (1<<GRPWM_CAP_ASY_BIT) +#define GRPWM_CAP_SYM (1<<GRPWM_CAP_SYM_BIT) +#define GRPWM_CAP_SEP (0x3<<GRPWM_CAP_SEP_BIT) +#define GRPWM_CAP_DCM (1<<GRPWM_CAP_DCM_BIT) + +/* CAPABILITY2 REGISTER - 0x10 */ +#define GRPWM_CAP2_WPWM_BIT 0 +#define GRPWM_CAP2_WDBITS_BIT 1 +#define GRPWM_CAP2_WABITS_BIT 6 +#define GRPWM_CAP2_WSYNC_BIT 10 + +#define GRPWM_CAP2_WPWM (0x1<<GRPWM_CAP2_WPWM_BIT) +#define GRPWM_CAP2_WDBITS (0x1f<<GRPWM_CAP2_WDBITS_BIT) +#define GRPWM_CAP2_WABITS (0xf<<GRPWM_CAP2_WABITS_BIT) +#define GRPWM_CAP2_WSYNC (1<<GRPWM_CAP2_WSYNC_BIT) + +/* WAVE FORM CONFIG REGISTER - 0x14 */ +#define GRPWM_WCTRL_STOP_BIT 0 +#define GRPWM_WCTRL_WSYNC_BIT 16 +#define GRPWM_WCTRL_WSEN_BIT 29 +#define GRPWM_WCTRL_WSYNCCFG_BIT 30 + +#define GRPWM_WCTRL_STOP (0x1fff<<GRPWM_WCTRL_STOP_BIT) +#define GRPWM_WCTRL_WSYNC (0x1fff<<GRPWM_WCTRL_WSYNC_BIT) +#define GRPWM_WCTRL_WSEN (0x1<<GRPWM_WCTRL_WSEN_BIT) +#define GRPWM_WCTRL_WSYNCCFG (0x3<<GRPWM_WCTRL_WSYNCCFG_BIT) + + +/* PWM CONTROL REGISTER - 0x2C, 0x3C... */ +#define GRPWM_PCTRL_EN_BIT 0 +#define GRPWM_PCTRL_POL_BIT 1 +#define GRPWM_PCTRL_PAIR_BIT 2 +#define GRPWM_PCTRL_FIX_BIT 3 +#define GRPWM_PCTRL_METH_BIT 6 +#define GRPWM_PCTRL_DCEN_BIT 8 +#define GRPWM_PCTRL_WEN_BIT 9 +#define GRPWM_PCTRL_SCSEL_BIT 10 +#define GRPWM_PCTRL_IEN_BIT 13 +#define GRPWM_PCTRL_IT_BIT 14 +#define GRPWM_PCTRL_ISC_BIT 15 +#define GRPWM_PCTRL_DBEN_BIT 21 +#define GRPWM_PCTRL_DBSC_BIT 22 +#define GRPWM_PCTRL_FLIP_BIT 26 + +#define GRPWM_PCTRL_EN (0x1<<GRPWM_PCTRL_EN_BIT) +#define GRPWM_PCTRL_POL (0x1<<GRPWM_PCTRL_POL_BIT) +#define GRPWM_PCTRL_PAIR (0x1<<GRPWM_PCTRL_PAIR_BIT) +#define GRPWM_PCTRL_FIX (0x7<<GRPWM_PCTRL_FIX_BIT) +#define GRPWM_PCTRL_METH (0x1<<GRPWM_PCTRL_METH_BIT) +#define GRPWM_PCTRL_DCEN (0x1<<GRPWM_PCTRL_DCEN_BIT) +#define GRPWM_PCTRL_WEN (0x1<<GRPWM_PCTRL_WEN_BIT) +#define GRPWM_PCTRL_SCSEL (0x7<<GRPWM_PCTRL_SCSEL_BIT) +#define GRPWM_PCTRL_IEN (0x1<<GRPWM_PCTRL_IEN_BIT) +#define GRPWM_PCTRL_IT (0x1<<GRPWM_PCTRL_IT_BIT) +#define GRPWM_PCTRL_ISC (0x3f<<GRPWM_PCTRL_ISC_BIT) +#define GRPWM_PCTRL_DBEN (0x1<<GRPWM_PCTRL_DBEN_BIT) +#define GRPWM_PCTRL_DBSC (0xf<<GRPWM_PCTRL_DBSC_BIT) +#define GRPWM_PCTRL_FLIP (0xf<<GRPWM_PCTRL_FLIP_BIT) + +/*** DRIVER PRIVATE STRUCTURE ***/ +struct grpwm_priv { + struct drvmgr_dev *dev; + struct grpwm_regs *regs; + char devName[32]; + int irq; + int open; + + /* Driver implementation */ + char nscalers; /* Number of scalers */ + char wave; /* If Wave form is available */ + int wlength; /* Wave Form RAM Length */ + int channel_cnt; + struct grpwm_chan_priv *channels[8]; + rtems_id dev_sem; +}; + +struct grpwm_chan_priv { + struct grpwm_priv *common; + struct grpwm_pwm_regs *pwmregs; + /* IRQ */ + int irqindex; + void (*isr)(int channel, void *arg); + void *isr_arg; +}; + +/******************* Driver Manager Part ***********************/ + +int grpwm_device_init(struct grpwm_priv *priv); +int grpwm_register_io(rtems_device_major_number *m); +static int grpwm_driver_io_registered = 0; +static rtems_device_major_number grpwm_driver_io_major = 0; + +int grpwm_init2(struct drvmgr_dev *dev); +int grpwm_init3(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops grpwm_ops = +{ + .init = {NULL, grpwm_init2, grpwm_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grpwm_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRPWM}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grpwm_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRPWM_ID, /* Driver ID */ + "GRPWM_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grpwm_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grpwm_ids[0] +}; + +void grpwm_register_drv (void) +{ + DBG("Registering GRPWM driver\n"); + drvmgr_drv_register(&grpwm_drv_info.general); +} + +int grpwm_init2(struct drvmgr_dev *dev) +{ + struct grpwm_priv *priv; + + DBG("GRPWM[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + priv = dev->priv = malloc(sizeof(struct grpwm_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + +int grpwm_init3(struct drvmgr_dev *dev) +{ + struct grpwm_priv *priv = dev->priv; + char prefix[32]; + rtems_status_code status; + + if ( !priv ) + return DRVMGR_FAIL; + + if ( grpwm_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( grpwm_register_io(&grpwm_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + grpwm_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + if ( grpwm_device_init(priv) ) { + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/grpwm%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sgrpwm%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, grpwm_driver_io_major, + dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static rtems_device_driver grpwm_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grpwm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define GRPWM_DRIVER_TABLE_ENTRY { grpwm_initialize, grpwm_open, grpwm_close, grpwm_read, grpwm_write, grpwm_ioctl } + +static rtems_driver_address_table grpwm_driver = GRPWM_DRIVER_TABLE_ENTRY; + +int grpwm_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &grpwm_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("GRPWM driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + DBG("GRPWM rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + DBG("GRPWM rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + DBG("GRPWM rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + DBG("GRPWM rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +void grpwm_scaler_set(struct grpwm_regs *regs, int scaler, unsigned int value) +{ + /* Select scaler */ + regs->ctrl = (regs->ctrl & ~GRPWM_CTRL_SCSEL) | (scaler << GRPWM_CTRL_SCSEL_BIT); + /* Write scaler */ + regs->scaler = value; +} + +/* Write Wave form RAM */ +void grpwm_write_wram(struct grpwm_regs *regs, unsigned int *data, int length) +{ + unsigned int *end; + volatile unsigned int *pos; + + pos = ®s->wram[0]; + + /* Write RAM */ + if ( data ) { + end = data + length; + while ( data < end ) { + *pos++ = *data++; + } + } else { + while( length > 0 ) { + *pos++ = 0; + length -= 4; + } + } +} + +void grpwm_hw_reset(struct grpwm_priv *priv) +{ + int i; + struct grpwm_chan_priv *pwm; + struct grpwm_regs *regs = priv->regs; + + /* Disable Core */ + regs->ctrl = 0; + + /* Clear all registers */ + regs->ipend = 0xffffffff; + regs->wctrl = 0; + + /* Init all PWM channels */ + for (i=0; i<priv->channel_cnt; i++) { + pwm = priv->channels[i]; + pwm->pwmregs->ctrl = 0; + pwm->pwmregs->period = 0; + pwm->pwmregs->comp = 0; + pwm->pwmregs->dbcomp = 0; + pwm->pwmregs->ctrl = 0; /* Twice because METH and POL requires EN=0 */ + } + + /* Clear RAM */ + if ( priv->wave ) { + grpwm_write_wram(regs, NULL, priv->wlength); + } + + /* Set max scaler */ + for (i=0; i<priv->nscalers; i++) { + grpwm_scaler_set(regs, i, 0xffffffff); + } +} + +/* Update one Channel but leaves the "Hold update" bit set + * + * A bit mask of updated bits are returned. + */ +unsigned int grpwm_update_prepare_channel( + struct grpwm_priv *priv, + int channel, + struct grpwm_ioctl_update_chan *up + ) +{ + struct grpwm_chan_priv *pwm; + struct grpwm_pwm_regs *pwmregs; + unsigned int ctrl; + unsigned int ret; + + pwm = priv->channels[channel]; + pwmregs = pwm->pwmregs; + + /* Read channel control register */ + ctrl = pwmregs->ctrl; + ret = 0; + + if ( up->options & GRPWM_UPDATE_OPTION_DISABLE ) { + ctrl &= ~GRPWM_PCTRL_EN; + pwmregs->ctrl = ctrl; + ret |= GRPWM_PCTRL_EN; + } + + /* Hold the updates */ + if ( up->options & (GRPWM_UPDATE_OPTION_PERIOD| + GRPWM_UPDATE_OPTION_COMP|GRPWM_UPDATE_OPTION_DBCOMP) ) { + + if ( up->options & (GRPWM_UPDATE_OPTION_PERIOD) ) { + DBG("GRPWM: UPDATING 0x%x: 0x%x\n", &pwmregs->period, up->period); + pwmregs->period = up->period; + } + if ( up->options & (GRPWM_UPDATE_OPTION_COMP) ) { + DBG("GRPWM: UPDATING 0x%x: 0x%x\n", &pwmregs->comp, up->compare); + pwmregs->comp = up->compare; + } + if ( up->options & (GRPWM_UPDATE_OPTION_DBCOMP) ) { + DBG("GRPWM: UPDATING 0x%x: 0x%x\n", &pwmregs->dbcomp, up->dbcomp); + pwmregs->dbcomp = up->dbcomp; + } + } + + if ( up->options & GRPWM_UPDATE_OPTION_ENABLE ) { + ret |= GRPWM_PCTRL_EN; + pwmregs->ctrl = ctrl | GRPWM_PCTRL_EN; + } + return ret; +} + +void grpwm_update_active(struct grpwm_priv *priv, int enable) +{ + unsigned int ctrl; + int i; + + ctrl = priv->regs->ctrl; + + /* Make all "Update Hold" bits be cleared */ + ctrl &= ~GRPWM_CTRL_NOUP; + + /* A change in any of the Channel enable/disable bits? */ + if ( enable ) { + ctrl &= ~GRPWM_CTRL_EN; + for(i=0; i<priv->channel_cnt; i++) { + ctrl |= priv->regs->pwms[i].ctrl & GRPWM_CTRL_EN; + } + } + priv->regs->ctrl = ctrl; +} + +/* Configure the hardware of a channel according to this */ +rtems_status_code grpwm_config_channel( + struct grpwm_priv *priv, + int channel, + struct grpwm_ioctl_config *cfg + ) +{ + struct grpwm_chan_priv *pwm; + unsigned int pctrl, wctrl=0; + + pwm = priv->channels[channel]; + if ( pwm->pwmregs->ctrl & GRPWM_PCTRL_EN_BIT ) { + return RTEMS_RESOURCE_IN_USE; + } + if ( cfg->options & ~GRPWM_CONFIG_OPTION_MASK ) { + return RTEMS_INVALID_NAME; + } + if ( (cfg->options & GRPWM_CONFIG_OPTION_DUAL) && + ((priv->regs->cap1 & GRPWM_CAP_DCM) == 0) ) { + return RTEMS_INVALID_NAME; + } + /* IRQ set up */ + pwm->isr_arg = cfg->isr_arg; + pwm->isr = cfg->isr; + + pctrl = cfg->options | + (cfg->dbscaler << GRPWM_PCTRL_DBSC_BIT) | + (cfg->irqscaler << GRPWM_PCTRL_ISC_BIT) | + (cfg->scaler_index << GRPWM_PCTRL_SCSEL_BIT); + + /* Set Wave form gerneration if available */ + if ( !priv->wave || (priv->channel_cnt != (channel+1)) ) { + /* Wave Form not available for this channel (or core) */ + if ( cfg->wave_activate || cfg->wave_data || cfg->wave_data_length ) { + return RTEMS_INVALID_NAME; + } + } else if ( cfg->wave_activate ) { + /* Enable Wave form generation */ + DBG("GRPWM: ENABLING WAVE FORM GENERATION 0x%x\n", cfg->wave_data_length); + + if ( cfg->wave_data ) { + grpwm_write_wram(priv->regs, cfg->wave_data, cfg->wave_data_length); + } + + /* Write length register, and let user control Wave-Sync functionality */ + wctrl = (((cfg->wave_data_length-1) << GRPWM_WCTRL_STOP_BIT) & GRPWM_WCTRL_STOP); + wctrl |= cfg->wave_synccfg & (GRPWM_WCTRL_WSYNCCFG|GRPWM_WCTRL_WSEN); + wctrl |= (cfg->wave_sync << 16) & 0x1fff0000; + priv->regs->wctrl = wctrl; + + /* Enable Wave form */ + pctrl |= GRPWM_PCTRL_WEN; + } + + DBG("GRPWM: CONFIG: 0x%x, WAVE CONFIG: 0x%x\n", pctrl, wctrl); + + pwm->pwmregs->ctrl = pctrl; + + return RTEMS_SUCCESSFUL; +} + +static void grpwm_isr(void *arg) +{ + unsigned int ipend; + struct grpwm_chan_priv *pwm = arg; + struct grpwm_priv *priv = pwm->common; + int i; + + /* Get current pending interrupts */ + ipend = priv->regs->ipend; + + for (i=0; i<priv->channel_cnt; i++) { + if ( ipend & (1<<i) ) { + pwm = priv->channels[i]; + if ( pwm->isr ) { + pwm->isr(i, pwm->isr_arg); + } + } + } + priv->regs->ipend = ipend; +} + +static rtems_device_driver grpwm_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grpwm_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grpwm_priv *priv; + rtems_device_driver ret; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&grpwm_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + priv = (struct grpwm_priv *)dev->priv; + + /* Wait until we get semaphore */ + if ( rtems_semaphore_obtain(priv->dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != + RTEMS_SUCCESSFUL ){ + return RTEMS_INTERNAL_ERROR; + } + + /* is device busy/taken? */ + if ( priv->open ) { + ret=RTEMS_RESOURCE_IN_USE; + goto out; + } + + /* Mark device taken */ + priv->open = 1; + + ret = RTEMS_SUCCESSFUL; +out: + rtems_semaphore_release(priv->dev_sem); + return ret; +} + +static rtems_device_driver grpwm_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grpwm_priv *priv; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&grpwm_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + priv = (struct grpwm_priv *)dev->priv; + + /* Reset Hardware */ + grpwm_hw_reset(priv); + + /* Mark Device closed */ + priv->open = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grpwm_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_UNSATISFIED; +} + +static rtems_device_driver grpwm_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_UNSATISFIED; +} + +static rtems_device_driver grpwm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + + struct grpwm_priv *priv; + struct drvmgr_dev *dev; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; + + if ( drvmgr_get_dev(&grpwm_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NAME; + } + priv = (struct grpwm_priv *)dev->priv; + + if (!ioarg) + return RTEMS_INVALID_NAME; + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + default: /* Not a valid command */ + return RTEMS_NOT_DEFINED; + + case GRPWM_IOCTL_GET_CAP: + { + struct grpwm_ioctl_cap *cap = (void *)ioarg->buffer; + if ( cap == NULL ) + return RTEMS_INVALID_NAME; + + /* Copy Capability registers to user */ + cap->channel_cnt = priv->channel_cnt; + cap->pwm = priv->regs->cap1; + cap->wave = priv->regs->cap2; + break; + } + case GRPWM_IOCTL_SET_CONFIG: + { + struct grpwm_ioctl_config *cfg = (void *)ioarg->buffer; + if ( cfg == NULL ) + return RTEMS_INVALID_NAME; + if ( cfg->channel >= priv->channel_cnt ) + return RTEMS_INVALID_NAME; + + return grpwm_config_channel(priv, cfg->channel, cfg); + } + case GRPWM_IOCTL_SET_SCALER: + { + unsigned int invalid_mask; + int i; + struct grpwm_ioctl_scaler *sc = ioarg->buffer; + + if ( sc == NULL ) + return RTEMS_INVALID_NAME; + + /* Test if caller reqest to set a scaler not existing */ + invalid_mask = ~((1 << priv->nscalers) - 1); + if ( invalid_mask & sc->index_mask ) { + return RTEMS_INVALID_NAME; + } + + /* Set scalers requested */ + for (i=0; i<priv->nscalers; i++) { + if ( sc->index_mask & (1<<i) ) { + /* Update Scaler 'i' */ + grpwm_scaler_set(priv->regs, i, sc->values[i]); + } + } + break; + } + case GRPWM_IOCTL_UPDATE: + { + struct grpwm_ioctl_update *up = ioarg->buffer; + unsigned int invalid_mask, pctrl = 0; + int i; + + if ( up == NULL ) + return RTEMS_INVALID_NAME; + + /* Test if caller reqest to set a scaler not existing */ + invalid_mask = ~((1 << priv->channel_cnt) - 1); + if ( invalid_mask & up->chanmask ) { + return RTEMS_INVALID_NAME; + } + + /* In order for the changes to take effect at the same time, the "Hold update" + * bits is set for all PWM channels that will be updated. The hold update bits + * will be cleared at the same time for all channels. + */ + priv->regs->ctrl = (priv->regs->ctrl & ~GRPWM_CTRL_NOUP) | + (up->chanmask << GRPWM_CTRL_NOUP_BIT); + + for (i=0; i<priv->channel_cnt; i++) { + if ( up->chanmask & (1<<i) ) { + /* Prepare update channel 'i' */ + pctrl |= grpwm_update_prepare_channel(priv, i, &up->channels[i]); + } + } + + /* 1. Update all channels requested, + * 2. Enable the core if at least one channel is enabled + * 3. Disable the core if all channels are disabled + */ + grpwm_update_active(priv, (pctrl & GRPWM_PCTRL_EN)); + + break; + } + case GRPWM_IOCTL_IRQ: + { + unsigned int data = (unsigned int)ioarg->buffer; + int channel = (data >> 8) & 0x7; + struct grpwm_chan_priv *pwm; + unsigned int pctrl; + + pwm = priv->channels[channel]; + + if ( data & GRPWM_IRQ_CLEAR ) { + priv->regs->ipend |= (1<<channel); + drvmgr_interrupt_clear(priv->dev, pwm->irqindex); + } + if ( (data & 0x3) && !pwm->isr ) { + /* Enable IRQ but no ISR */ + return RTEMS_INVALID_NAME; + } + pctrl = pwm->pwmregs->ctrl & ~(GRPWM_PCTRL_IEN|GRPWM_PCTRL_IT); + pctrl |= ((data & 0x3) << GRPWM_PCTRL_IEN_BIT); + pwm->pwmregs->ctrl = pctrl; + break; + } + } + + return RTEMS_SUCCESSFUL; +} + +#define MAX_CHANNEL 8 +char grpwm_irqindex_lookup[8][MAX_CHANNEL] = +{ +/* Channel 1 2 3 4 5 6 7 8 */ +/* npwm 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* npwm 2 */ {0, 1, 0, 0, 0, 0, 0, 0}, +/* npwm 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* npwm 4 */ {0, 0, 0, 1, 0, 0, 0, 0}, +/* npwm 5 */ {0, 0, 0, 1, 2, 0, 0, 0}, +/* npwm 6 */ {0, 0, 0, 1, 1, 1, 0, 0}, +/* npwm 7 */ {0, 0, 0, 1, 1, 1, 2, 0}, +/* npwm 8 */ {0, 0, 0, 1, 1, 1, 2, 3} +}; + +int grpwm_device_init(struct grpwm_priv *priv) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + int mask, i, sepirq; + unsigned int wabits; + struct grpwm_chan_priv *pwm; + struct grpwm_regs *regs; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + priv->irq = pnpinfo->irq; + regs = priv->regs = (struct grpwm_regs *)pnpinfo->apb_slv->start; + + DBG("GRPWM: 0x%08x irq %d\n", (unsigned int)regs, priv->irq); + + /* Disable Core */ + regs->ctrl = 0; + + /* Clear all registers */ + regs->ipend = 0xffffffff; + regs->wctrl = 0; + + /* Find the number of PWM channels */ + priv->channel_cnt = 1 + ((regs->cap1 & GRPWM_CAP_NPWM) >> GRPWM_CAP_NPWM_BIT); + pwm = malloc(sizeof(*pwm)*priv->channel_cnt); + if ( !pwm ) + return -1; + memset(pwm, 0, sizeof(*pwm)*priv->channel_cnt); + + /* Init all PWM channels */ + sepirq = ((regs->cap1 & GRPWM_CAP_SEP) >> GRPWM_CAP_SEP_BIT); + for (i=0; i<priv->channel_cnt; i++, pwm++) { + priv->channels[i] = pwm; + pwm->common = priv; + pwm->pwmregs = ®s->pwms[i]; + if ( sepirq == 0 ) { + pwm->irqindex = 0; + } else if ( sepirq == 1 ) { + pwm->irqindex = i; + } else { + pwm->irqindex = grpwm_irqindex_lookup[priv->channel_cnt][i]; + } + } + + /* Detect if Wave Form capability is availble for last PWM channel */ + if ( regs->cap2 & GRPWM_CAP2_WPWM ) { + priv->wave = 1; + + /* Clear RAM */ + wabits = (regs->cap2 & GRPWM_CAP2_WABITS) >> GRPWM_CAP2_WABITS_BIT; + priv->wlength = 1 << wabits; + } + priv->nscalers = 1 + ((regs->cap1 & GRPWM_CAP_NSC) >> GRPWM_CAP_NSC_BIT); + + grpwm_hw_reset(priv); + + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'P', 'W', 'M'), + 1, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ + RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &priv->dev_sem) != RTEMS_SUCCESSFUL ) { + return -1; + } + + /* Register interrupt handler for all PWM channels */ + mask = 0; + for (i=0; i<priv->channel_cnt; i++) { + pwm = priv->channels[i]; + if ( (mask & (1 << pwm->irqindex)) == 0 ) { + /* Not registered interrupt handler for this IRQ index before, + * we do it now. + */ + mask |= (1 << pwm->irqindex); + drvmgr_interrupt_register( + priv->dev, + pwm->irqindex, + "grpwm", + grpwm_isr, + pwm); + } + } + + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/slink/grslink.c b/c/src/lib/libbsp/sparc/shared/slink/grslink.c new file mode 100644 index 0000000000..0c3d086a64 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/slink/grslink.c @@ -0,0 +1,661 @@ +/* + * This file contains the RTEMS GRSLINK SLINK master driver + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + * + * Comments concerning current driver implementation: + * + * The SLINK specification says that there are three IO cards that are capable + * of transmitting data. But these IO cards can have the address range 0 to 3, + * and an 'For information only' comment explains that the current + * implementation has receive buffers for ".. x 4 (IO cards)". + * Because of this the driver has four queues, one for each IO card 0 - 3. + * When the addressing convention used for the IO cards is known, the number of + * queues may be lowered to three. + * + */ + +#include <stdlib.h> + +#include <bsp.h> +#include <grslink.h> +#include <ambapp.h> + +#ifndef GAISLER_SLINK +#define GAISLER_SLINK 0x02F +#endif + +/* Enable debug output? */ +/* #define DEBUG */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* Bits and fields in SLINK transmit word */ +#define SLINK_RW (1 << 23) +#define SLINK_CHAN_POS 16 + +/* Local types */ +typedef struct { + volatile unsigned int clockscale; + volatile unsigned int ctrl; + volatile unsigned int nullwrd; + volatile unsigned int sts; + volatile unsigned int msk; + volatile unsigned int abase; + volatile unsigned int bbase; + volatile unsigned int td; + volatile unsigned int rd; +} SLINK_regs; + +typedef struct { + char readstat; /* Status of READ operation */ + char seqstat; /* Status of SEQUENCE operation */ + unsigned char scnt; /* Number of SEQUENCE words transferred */ +} SLINK_status; + +typedef struct { + int size; + unsigned int *buf; + unsigned int *first; + unsigned int *last; + unsigned int *max; + int full; +} SLINK_queue; + +typedef struct { + SLINK_regs *reg; /* Pointer to core registers */ + SLINK_status *status; /* Driver status information */ + void (*slink_irq_handler)(int); /* Handler for INTERRUPT */ + void (*slink_seq_change)(int); /* Callback on SEQUENCE change */ + int rword; /* Placeholder for READ response */ + rtems_id read_sem; /* Semaphore for blocking SLINK_read */ + SLINK_queue *queues; /* Receive queues */ +#ifdef SLINK_COLLECT_STATISTICS + SLINK_stats *stats; /* Core statistics, optional */ +#endif +} SLINK_cfg; + + +static SLINK_cfg *cfg = NULL; + +/**** SLINK driver queues for unsolicited and INTERRUPT requests ****/ + +/* Function: SLINK_createqueues + * Arguments: size: Number of elements in each queue + * Returns: 0 on success, -1 on failure + * Description: Creates SLINK_NUMQUEUES queues, one for each IO card + * that can send data. The pointers to the queues is saved in the driver + * config structure. + */ +static int SLINK_createqueues(int size) +{ + SLINK_queue *q; + int i, j; + + if ((q = malloc(SLINK_NUMQUEUES*sizeof(SLINK_queue))) == NULL) + goto slink_qiniterr1; + + for (i = 0; i < SLINK_NUMQUEUES; i++) { + q[i].size = size; + if ((q[i].buf = malloc(size*sizeof(int))) == NULL) + goto slink_qiniterr2; + q[i].first = q[i].last = q[i].buf; + q[i].max = q[i].buf + (size-1); + q[i].full = 0; + } + + cfg->queues = q; + + return 0; + + slink_qiniterr2: + for (j = 0; j < i; j++) + free(q[i].buf); + free(q); + slink_qiniterr1: + return -1; +} + +/* + * Function: SLINK_destroyqueues + * Arguments: None + * Returns: Nothing + * Description: Frees the memory occupied by the queues in cfg->queues + */ +/* + static void SLINK_destroyqueues(void) + { + int i; + + for(i = 0; i < SLINK_NUMQUEUES; i++) + free(cfg->queues[i].buf); + + free(cfg->queues); +} +*/ + +/* + * Function: SLINK_enqueue + * Arguments: Received SLINK word + * Returns: Nothing + * Description: + */ +static void SLINK_enqueue(unsigned int slink_wrd) +{ + SLINK_queue *ioq = cfg->queues + SLINK_WRD_CARDNUM(slink_wrd); + + if (!ioq->full && SLINK_WRD_CARDNUM(slink_wrd) < SLINK_NUMQUEUES) { + *ioq->last = slink_wrd; + ioq->last = (ioq->last >= ioq->max) ? ioq->buf : ioq->last+1; + ioq->full = ioq->last == ioq->first; + return; + } +#ifdef SLINK_COLLECT_STATISTICS + cfg->stats->lostwords++; +#endif +} + +/**** SLINK driver helper functions ****/ + +/* + * Function: SLINK_getaddr + * Arguments: amba_conf + * base: assigned to base of core registers + * irq: assigned to core irq lines + * Returns: Base address and IRQ via arguments, 0 if core is found, else -1 + * Description: See above. + */ +static int SLINK_getaddr(int *base, int *irq) +{ + struct ambapp_apb_info c; + + if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_SLINK,&c) == 1) { + *base = c.start; + *irq = c.irq; + return 0; + } + return -1; +} + +/* Function: SLINK_calcscaler + * Arguments: sysfreq: System frequency in Hz + * Returns: Clock scaler register value + * Description: Calculates value for SLINK clock scaler register to attain + * a SLINK bus frequency as close to 6 MHz as possible. Please see the IP core + * documentation for a description of how clock scaling is implemented. + */ +static int SLINK_calcscaler(int sysfreq) +{ + int fact = sysfreq / SLINK_FREQ_HZ; + return ((fact/2-1) << 16) | (fact % 2 ? fact/2 : fact/2-1); +} + + +/* + * Function: SLINK_getsysfreq + * Arguments: None + * Returns: System frequency in Hz, or 0 if system timer is not found. + * Description: Looks at the timer to determine system frequency. Makes use + * of AMBA Plug'n'Play. + */ +static int SLINK_getsysfreq(void) +{ + struct ambapp_apb_info t; + struct gptimer_regs *tregs; + + if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_GPTIMER,&t)==1) { + tregs = (struct gptimer_regs *)t.start; + DBG("SLINK_getsysfreq returning %d\n", + (tregs->scaler_reload+1)*1000*1000); + return (tregs->scaler_reload+1)*1000*1000; + } + return 0; +} + +/* + * Function: SLINK_interrupt_handler + * Arguments: v: not used + * Returns: Nothing + * Description: Interrupt handles checks RNE, SEQUENCE and error status + * bits. Reads word from receive queue and distinguishes between INTERRUPT, + * READ responses and SLAVE-WORD-SEND. When an INTERRUPT transfer is detected + * the handler calls the user specified slink_irq_handler with the received + * word. READ responses are saved and given to SLINK_read via a private + * variable. SLAVE-WORD-SEND transfers are placed in the IO card's receive + * queue. + */ +static rtems_isr SLINK_interrupt_handler(rtems_vector_number v) +{ + unsigned int sts; + unsigned int wrd; + + /* Read all words from Receive queue */ + while ((sts = cfg->reg->sts) & SLINK_S_RNE) { + + /* Read first word in receive queue */ + wrd = cfg->reg->rd; + + /* Check channel value to determine action */ + switch (SLINK_WRD_CHAN(wrd)) { + case 0: /* Interrupt */ + cfg->slink_irq_handler(wrd); +#ifdef SLINK_COLLECT_STATISTICS + cfg->stats->interrupts++; +#endif + break; + case 3: /* Read response, if no active READ, fall-through */ + if (cfg->status->readstat == SLINK_ACTIVE) { + rtems_semaphore_release(cfg->read_sem); + cfg->status->readstat = SLINK_COMPLETED; + cfg->rword = wrd; + break; + } + default: /* Unsolicited request */ + SLINK_enqueue(wrd); + break; + } + } + + /* Check sequence operation */ + if (sts & SLINK_S_SC) { + /* SEQUENCE completed */ + cfg->status->seqstat = SLINK_COMPLETED; + if (cfg->slink_seq_change) + cfg->slink_seq_change(SLINK_COMPLETED); +#ifdef SLINK_COLLECT_STATISTICS + cfg->stats->seqcomp++; +#endif + } else if (sts & SLINK_S_SA) { + /* SEQUENCE aborted */ + cfg->status->seqstat = SLINK_ABORTED; + cfg->status->scnt = (sts >> SLINK_S_SI_POS); + if (cfg->slink_seq_change) + cfg->slink_seq_change(SLINK_ABORTED); + } + + /* Check error conditions */ + if (sts & SLINK_S_PERR) { + /* + Parity error detected, set seqstat if there is an ongoing + sequence so that the calling application can decide if the + sequence should be aborted + */ + if (cfg->status->seqstat == SLINK_ACTIVE) { + cfg->status->seqstat = SLINK_PARERR; + if (cfg->slink_seq_change) + cfg->slink_seq_change(SLINK_PARERR); + } + /* Abort READ operation */ + if (cfg->status->readstat == SLINK_ACTIVE) { + cfg->status->readstat = SLINK_PARERR; + rtems_semaphore_release(cfg->read_sem); + } +#ifdef SLINK_COLLECT_STATISTICS + cfg->stats->parerr++; +#endif + } + if (sts & SLINK_S_AERR) { + /* AMBA error response, sequence aborted */ + cfg->status->seqstat = SLINK_AMBAERR; + cfg->status->scnt = sts >> SLINK_S_SI_POS; + if (cfg->slink_seq_change) + cfg->slink_seq_change(SLINK_AMBAERR); + } + if (sts & SLINK_S_ROV) { + /* Receive overflow, abort any ongoing READ */ + if (cfg->status->readstat == SLINK_ACTIVE) { + cfg->status->readstat = SLINK_ROV; + rtems_semaphore_release(cfg->read_sem); + } +#ifdef SLINK_COLLECT_STATISICS + cfg->status->recov++; +#endif + } + + /* Clear processed bits */ + cfg->reg->sts = sts; +} + +/**** SLINK driver interface starts here ****/ + +/* Function: SLINK_init + * Arguments: nullwrd: NULL word + * parity: Even (0) or Odd (1) parity + * interrupt_trans_handler: Function that handles interrupt requests + * sequence_callback: Callback on SEQUENCE status changes + * qsize: Size of each receive queue + * Returns: 0 on success, -1 on failure + * Description: Initializes the SLINK core + */ +int SLINK_init(unsigned int nullwrd, int parity, int qsize, + void (*interrupt_trans_handler)(int), + void (*sequence_callback)(int)) +{ + int base; + int irq; + rtems_status_code st; + + /* Allocate private config structure */ + if (cfg == NULL && (cfg = malloc(sizeof(SLINK_cfg))) == NULL) { + DBG("SLINK_init: Could not allocate cfg structure\n"); + goto slink_initerr1; + } + + /* Create simple binary semaphore for blocking SLINK_read */ + st = rtems_semaphore_create(rtems_build_name('S', 'L', 'R', '0'), 0, + (RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE| + RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL| + RTEMS_NO_PRIORITY_CEILING), 0, + &cfg->read_sem); + if (st != RTEMS_SUCCESSFUL) { + DBG("SLINK_init: Could not create semaphore\n"); + goto slink_initerr1; + } + + /* Initialize pointer to SLINK core registers and get IRQ line */ + if (SLINK_getaddr(&base, &irq) == -1) { + DBG("SLINK_init: Could not find core\n"); + goto slink_initerr2; + } + cfg->reg = (SLINK_regs*)base; + + /* Allocate status structure and initialize members */ + if ((cfg->status = calloc(1, sizeof(SLINK_status))) == NULL) { + DBG("SLINK_init: Could not allocate status structure\n"); + goto slink_initerr2; + } + cfg->status->seqstat = SLINK_COMPLETED; + cfg->status->readstat = SLINK_COMPLETED; + +#ifdef SLINK_COLLECT_STATISTICS + /* Allocate statistics structure and initialize members */ + if ((cfg->stats = calloc(1, sizeof(SLINK_stats))) == NULL) { + DBG("SLINK_init: Could not allocate statistics structure\n"); + goto slink_initerr3; + } +#endif + + /* Allocate and initialize queues */ + if (SLINK_createqueues(qsize) == -1) { + DBG("SLINK_init: Could not create queues\n"); + goto slink_initerr3; + } + + /* Configure core registers */ + cfg->reg->clockscale = SLINK_calcscaler(SLINK_getsysfreq()); + cfg->reg->ctrl = parity ? SLINK_C_PAR : 0; + cfg->reg->nullwrd = nullwrd; + cfg->reg->msk = (SLINK_M_PERRE | SLINK_M_AERRE | SLINK_M_ROVE | + SLINK_M_RNEE | SLINK_M_SAE | SLINK_M_SCE); + + /* Set-up INTERRUPT transfer handling */ + cfg->slink_irq_handler = interrupt_trans_handler; + + /* Save SEQUENCE callback */ + cfg->slink_seq_change = sequence_callback; + + /* Set-up IRQ handling */ + set_vector(SLINK_interrupt_handler,irq+0x10,2); + + return 0; + + slink_initerr3: + free(cfg->status); + slink_initerr2: + free(cfg); + slink_initerr1: + return -1; +} + +/* Function: SLINK_start + * Description: Enables the core + */ +void SLINK_start(void) +{ + if (cfg != NULL) + cfg->reg->ctrl |= SLINK_C_SLE; +} + +/* Function: SLINK_stop + * Description: Disables the core + */ +void SLINK_stop(void) +{ + if (cfg != NULL) + cfg->reg->ctrl &= ~SLINK_C_SLE; +} + +/* + * Function: SLINK_read + * Arguments: data: Payload of data word + * channel: - + * reply: Reply from IO card + * Returns: 0 on success + * -(SLINK_PARERR, SLINK_ROV) on error or -SLINK_QFULL if transmit queue + * is full and software should try again. + * Description: Reads one word and returns the response in *reply unless there + * is an error. This function blocks until the READ operation is + * completed or aborted. + */ +int SLINK_read(int data, int channel, int *reply) +{ + DBG("SLINK_read: called.."); + + if (cfg->reg->sts & SLINK_S_TNF) { + cfg->status->readstat = SLINK_ACTIVE; + cfg->reg->td = SLINK_RW | channel << SLINK_CHAN_POS | data; + } else { + DBG("queue FULL\n"); + return -SLINK_QFULL; /* Transmit queue full */ + } + + /* Block until the operation has completed or has been aborted */ + rtems_semaphore_obtain(cfg->read_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + + if (cfg->status->readstat == SLINK_COMPLETED) { + *reply = cfg->rword; +#ifdef SLINK_COLLECT_STATISTICS + cfg->stats->reads++; +#endif + DBG("returning 0\n"); + return 0; + } else { + DBG("returning error code\n"); + return -cfg->status->readstat; + } +} + +/* + * Function: SLINK_write + * Arguments: data: Payload of SLINK data word + * channel: Channel value (bits 22 downto 16) of receive + * register word + * Returns: 0 if command was placed in transmit queue + * -SLINK_QFULL if transmit queue was full (software should retry) + * Description: See above. + */ +int SLINK_write(int data, int channel) +{ + if (cfg->reg->sts & SLINK_S_TNF) { + cfg->reg->td = channel << SLINK_CHAN_POS | data; +#ifdef SLINK_COLLECT_STATISTICS + cfg->stats->writes++; +#endif + return 0; + } + + return -SLINK_QFULL; +} + +/* + * Function: SLINK_sequence + * Arguments: a: Array containing sequence commands + * b: Array where SEQUENCE responses will be stored + * n: Number of commands in a array + * channel: Sequence Channel Number + * reconly: Set to 1 if the SEQUENCE operation is receive only + * Returns: 0 if SEQUENCE could be started (SUCCESS) + * -1 if SEQUNCE was not started due to ongoing SEQUENCE + */ +int SLINK_seqstart(int *a, int *b, int n, int channel, int reconly) +{ + /* Only start a new SEQUENCE of the former SEQUENCE has completed */ + if (cfg->status->seqstat == SLINK_ACTIVE || + cfg->status->seqstat == SLINK_PARERR) + return -1; + + /* Tell core about arrays */ + cfg->reg->abase = (int)a; + cfg->reg->bbase = (int)b; + + /* As far as software is concerned the sequence is now active */ + cfg->status->seqstat = SLINK_ACTIVE; + + /* Enable SEQUENCE operation with SCN = channel and SLEN = n-1 */ + if (reconly == 1) { + cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) | SLINK_C_SRO | + (channel << SLINK_C_SCN_POS) | + SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F)); + } else { + cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) | + (channel << SLINK_C_SCN_POS) | + SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F)); + } + +#ifdef SLINK_COLLECT_STATISTICS + cfg->stats->sequences++; +#endif + + return 0; +} + + +/* Function: SLINK_seqabort + * Description: This function aborts an ongoing SEQUENCE. Software can tell + * when the SEQUENCE is aborted by polling SLINK_seqstat(). + */ +void SLINK_seqabort(void) +{ + cfg->reg->ctrl = cfg->reg->ctrl | SLINK_C_AS; +} + + +/* + * Function: SLINK_seqstatus + * Returns: The current or status of the SEQUENCE operation: + * SLINK_COMPLETED, SLINK_ACTIVE, SLINK_PARERR, SLINK_AMBAERR, + * SLINK_ABORTED (these are defined in grslink.h) + * Description: Meaning of returned values: + * SLINK_ABORTED: Aborted before all operations completed. + * SLINK_ACTIVE: The core is busy processing the SEQUENCE + * SLINK_AMBAERR: The last SEQUENCE was aborted by an AMBA ERROR + * SLINK_COMPLETED: All words were transferred in the last SEQUENCE + * SLINK_PARERR: Parity error detected. Software may want to abort + * + * If the SEQUENCE was aborted SLINK_seqwrds() can be used to + * determine the number of completed operations. + */ +int SLINK_seqstatus(void) +{ + return cfg->status->seqstat; +} + +/* + * Function: SLINK_seqwrds + * Returns: -1 for ongoing sequence + * 0 if all words were transferred in the last sequence + * number of words if the last SEQUENCE did not complete + * (SLINK_AMBAERR or SLINK_ABORTED is reported ny SLINK_seqstatus()) + */ +int SLINK_seqwrds(void) +{ + switch (cfg->status->seqstat) { + case SLINK_COMPLETED: return 0; + case SLINK_ACTIVE | SLINK_PARERR: return -1; + default: return cfg->status->scnt; + } +} + +/* + * Function: SLINK_hwstatus + * Returns: The SLINK core's status register. The register values can be + * interpreted with the help of macros defined in grslink.h. + */ +int SLINK_hwstatus(void) +{ + return cfg->reg->sts; +} + +/* + * Function: SLINK_queuestatus + * Arguments: iocard: Queue which to check status for + * Returns: Number of elements in queue or -1 on non-existent queue + * Description: SLINK_queuestatus(queue) returns the number of elements in + * queue 'iocard' + */ +int SLINK_queuestatus(int iocard) +{ + unsigned int first, last; + SLINK_queue *ioq; + + if (iocard >= SLINK_NUMQUEUES) + return -1; + + ioq = cfg->queues + iocard; + + if (ioq->full) + return ioq->size; + if (ioq->first == ioq->last) + return 0; + + first = ((unsigned int)ioq->first)/sizeof(unsigned int); + last = ((unsigned int)ioq->last)/sizeof(unsigned int); + + return first < last ? last - first : ioq->size - first + last; +} + +/* + * Function: SLINK_dequeue + * Arguments: iocard: IO card number + * elem: First element in IO card queue + * Returns: 0 on success or -1 on empty or non-existent queue + * Description: + */ +int SLINK_dequeue(int iocard, int *elem) +{ + if (iocard >= SLINK_NUMQUEUES) + return -1; + + SLINK_queue *ioq = cfg->queues + iocard; + + if (ioq->last != ioq->first || ioq->full) { + *elem = *ioq->first; + ioq->first = (ioq->first >= ioq->max) ? ioq->buf : ioq->first+1; + ioq->full = 0; + return 0; + } + return -1; +} + +/* + * Function: SLINK_statistics + * Returns: If the core has statistics colletion enabled this function returns + * a pointer to a struct containing statistics information, otherwise NULL. + */ +SLINK_stats *SLINK_statistics(void) +{ +#ifdef SLINK_COLLECT_STATISTICS + return cfg->stats; +#else + return NULL; +#endif +} diff --git a/c/src/lib/libbsp/sparc/shared/spi/spictrl.c b/c/src/lib/libbsp/sparc/shared/spi/spictrl.c new file mode 100644 index 0000000000..b149a95fb0 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spi/spictrl.c @@ -0,0 +1,1008 @@ +/* + * SPICTRL SPI driver implmenetation + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <assert.h> +#include <rtems/bspIo.h> +#include <string.h> +#include <stdio.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <spictrl.h> +#include <ambapp.h> + +#include <rtems/libi2c.h> + +/*#define DEBUG 1*/ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#define STATIC +#else +#define DBG(x...) +#define STATIC static +#endif + +/*** CAPABILITY REGISTER 0x00 ***/ +#define SPICTRL_CAP_SSSZ_BIT 24 +#define SPICTRL_CAP_AMODE_BIT 18 +#define SPICTRL_CAP_ASELA_BIT 17 +#define SPICTRL_CAP_SSEN_BIT 16 +#define SPICTRL_CAP_FDEPTH_BIT 8 +#define SPICTRL_CAP_REV_BIT 0 + +#define SPICTRL_CAP_SSSZ (0xff << SPICTRL_CAP_SSSZ_BIT) +#define SPICTRL_CAP_AMODE (1<<SPICTRL_CAP_AMODE_BIT) +#define SPICTRL_CAP_ASELA (1<<SPICTRL_CAP_ASELA_BIT) +#define SPICTRL_CAP_SSEN (1 << SPICTRL_CAP_SSEN_BIT) +#define SPICTRL_CAP_FDEPTH (0xff << SPICTRL_CAP_FDEPTH_BIT) +#define SPICTRL_CAP_REV (0xff << SPICTRL_CAP_REV_BIT) + +/*** MODE REGISTER 0x20 ***/ +#define SPICTRL_MODE_AMEN_BIT 31 +#define SPICTRL_MODE_LOOP_BIT 30 +#define SPICTRL_MODE_CPOL_BIT 29 +#define SPICTRL_MODE_CPHA_BIT 28 +#define SPICTRL_MODE_DIV16_BIT 27 +#define SPICTRL_MODE_REV_BIT 26 +#define SPICTRL_MODE_MS_BIT 25 +#define SPICTRL_MODE_EN_BIT 24 +#define SPICTRL_MODE_LEN_BIT 20 +#define SPICTRL_MODE_PM_BIT 16 +#define SPICTRL_MODE_ASEL_BIT 14 +#define SPICTRL_MODE_FACT_BIT 13 +#define SPICTRL_MODE_CG_BIT 7 +#define SPICTRL_MODE_TAC_BIT 4 + +#define SPICTRL_MODE_AMEN (1 << SPICTRL_MODE_AMEN_BIT) +#define SPICTRL_MODE_LOOP (1 << SPICTRL_MODE_LOOP_BIT) +#define SPICTRL_MODE_CPOL (1 << SPICTRL_MODE_CPOL_BIT) +#define SPICTRL_MODE_CPHA (1 << SPICTRL_MODE_CPHA_BIT) +#define SPICTRL_MODE_DIV16 (1 << SPICTRL_MODE_DIV16_BIT) +#define SPICTRL_MODE_REV (1 << SPICTRL_MODE_REV_BIT) +#define SPICTRL_MODE_MS (1 << SPICTRL_MODE_MS_BIT) +#define SPICTRL_MODE_EN (1 << SPICTRL_MODE_EN_BIT) +#define SPICTRL_MODE_LEN (0xf << SPICTRL_MODE_LEN_BIT) +#define SPICTRL_MODE_PM (0xf << SPICTRL_MODE_PM_BIT) +#define SPICTRL_MODE_ASEL (1 << SPICTRL_MODE_ASEL_BIT) +#define SPICTRL_MODE_FACT (1 << SPICTRL_MODE_FACT_BIT) +#define SPICTRL_MODE_CG (0x1f << SPICTRL_MODE_CG_BIT) +#define SPICTRL_MODE_TAC (0x1 << SPICTRL_MODE_TAC_BIT) + +/*** EVENT REGISTER 0x24 ***/ +#define SPICTRL_EVENT_AT_BIT 15 +#define SPICTRL_EVENT_LT_BIT 14 +#define SPICTRL_EVENT_OV_BIT 12 +#define SPICTRL_EVENT_UN_BIT 11 +#define SPICTRL_EVENT_MME_BIT 10 +#define SPICTRL_EVENT_NE_BIT 9 +#define SPICTRL_EVENT_NF_BIT 8 + +#define SPICTRL_EVENT_AT (1 << SPICTRL_EVENT_AT_BIT) +#define SPICTRL_EVENT_LT (1 << SPICTRL_EVENT_LT_BIT) +#define SPICTRL_EVENT_OV (1 << SPICTRL_EVENT_OV_BIT) +#define SPICTRL_EVENT_UN (1 << SPICTRL_EVENT_UN_BIT) +#define SPICTRL_EVENT_MME (1 << SPICTRL_EVENT_MME_BIT) +#define SPICTRL_EVENT_NE (1 << SPICTRL_EVENT_NE_BIT) +#define SPICTRL_EVENT_NF (1 << SPICTRL_EVENT_NF_BIT) + +/*** MASK REGISTER 0x28 ***/ +#define SPICTRL_MASK_ATE_BIT 15 +#define SPICTRL_MASK_LTE_BIT 14 +#define SPICTRL_MASK_OVE_BIT 12 +#define SPICTRL_MASK_UNE_BIT 11 +#define SPICTRL_MASK_MMEE_BIT 10 +#define SPICTRL_MASK_NEE_BIT 9 +#define SPICTRL_MASK_NFE_BIT 8 + +#define SPICTRL_MASK_ATE (1 << SPICTRL_MASK_ATE_BIT) +#define SPICTRL_MASK_LTE (1 << SPICTRL_MASK_LTE_BIT) +#define SPICTRL_MASK_OVE (1 << SPICTRL_MASK_OVE_BIT) +#define SPICTRL_MASK_UNE (1 << SPICTRL_MASK_UNE_BIT) +#define SPICTRL_MASK_MMEE (1 << SPICTRL_MASK_MMEE_BIT) +#define SPICTRL_MASK_NEE (1 << SPICTRL_MASK_NEE_BIT) +#define SPICTRL_MASK_NFE (1 << SPICTRL_MASK_NFE_BIT) + +/*** COMMAND REGISTER 0x2c ***/ +#define SPICTRL_CMD_LST_BIT 22 +#define SPICTRL_CMD_LST (1 << SPICTRL_CMD_LST_BIT) + +/*** TRANSMIT REGISTER 0x30 ***/ +#define SPICTRL_TX_TDATA_BIT 0 +#define SPICTRL_TX_TDATA 0xffffffff + +/*** RECEIVE REGISTER 0x34 ***/ +#define SPICTRL_RX_RDATA_BIT 0 +#define SPICTRL_RX_RDATA 0xffffffff + +/*** SLAVE SELECT REGISTER 0x38 - VARIABLE ***/ + +/*** AM CONFIGURATION REGISTER 0x40 ***/ +#define SPICTRL_AMCFG_ERPT_BIT 6 +#define SPICTRL_AMCFG_SEQ_BIT 5 +#define SPICTRL_AMCFG_STRICT_BIT 4 +#define SPICTRL_AMCFG_OVTB_BIT 3 +#define SPICTRL_AMCFG_OVDB_BIT 2 +#define SPICTRL_AMCFG_ACT_BIT 1 +#define SPICTRL_AMCFG_EACT_BIT 0 + +#define SPICTRL_AMCFG_ERPT (1<<SPICTRL_AMCFG_ERPT_BIT) +#define SPICTRL_AMCFG_SEQ (1<<SPICTRL_AMCFG_SEQ_BIT) +#define SPICTRL_AMCFG_STRICT (1<<SPICTRL_AMCFG_STRICT_BIT) +#define SPICTRL_AMCFG_OVTB (1<<SPICTRL_AMCFG_OVTB_BIT) +#define SPICTRL_AMCFG_OVDB (1<<SPICTRL_AMCFG_OVDB_BIT) +#define SPICTRL_AMCFG_ACT (1<<SPICTRL_AMCFG_ACT_BIT) +#define SPICTRL_AMCFG_EACT (1<<SPICTRL_AMCFG_EACT_BIT) + +struct spictrl_priv { + rtems_libi2c_bus_t i2clib_desc; + struct drvmgr_dev *dev; + struct spictrl_regs *regs; + int irq; + int minor; + unsigned int core_freq_hz; + + /* Driver */ + int fdepth; + int bits_per_char; + int lsb_first; + int txshift; + int rxshift; + unsigned int idle_char; + int (*slvSelFunc)(void *regs, uint32_t addr, int select); + + /* Automated Periodic transfers */ + int periodic_started; + struct spictrl_ioctl_config periodic_cfg; +}; + +/******************* Driver Manager Part ***********************/ + +int spictrl_device_init(struct spictrl_priv *priv); + +int spictrl_init2(struct drvmgr_dev *dev); +int spictrl_init3(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops spictrl_ops = +{ + .init = {NULL, spictrl_init2, spictrl_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id spictrl_ids[] = +{ + {VENDOR_GAISLER, GAISLER_SPICTRL}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info spictrl_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_SPICTRL_ID, /* Driver ID */ + "SPICTRL_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &spictrl_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &spictrl_ids[0] +}; + +void spictrl_register_drv (void) +{ + DBG("Registering SPICTRL driver\n"); + drvmgr_drv_register(&spictrl_drv_info.general); +} + +int spictrl_init2(struct drvmgr_dev *dev) +{ + struct spictrl_priv *priv; + + DBG("SPICTRL[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + priv = dev->priv = malloc(sizeof(struct spictrl_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + +int spictrl_init3(struct drvmgr_dev *dev) +{ + struct spictrl_priv *priv; + char prefix[32]; + char devName[32]; + int rc; + + priv = (struct spictrl_priv *)dev->priv; + + /* Do initialization */ + + /* Initialize i2c library */ + rc = rtems_libi2c_initialize(); + if (rc != 0) { + DBG("SPICTRL: rtems_libi2c_initialize failed, exiting...\n"); + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + /* Get frequency */ + if ( drvmgr_freq_get(dev, DEV_APB_SLV, &priv->core_freq_hz) ) { + return DRVMGR_FAIL; + } + + if ( spictrl_device_init(priv) ) { + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(devName, "/dev/spi%d", dev->minor_drv+1); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(devName, "/dev/%sspi%d", prefix, dev->minor_bus+1); + } + + /* Register Bus for this Device */ + rc = rtems_libi2c_register_bus(devName, &priv->i2clib_desc); + if (rc < 0) { + DBG("SPICTRL: rtems_libi2c_register_bus(%s) failed\n", devName); + free(dev->priv); + dev->priv = NULL; + return DRVMGR_FAIL; + } + priv->minor = rc; + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +STATIC rtems_status_code spictrl_libi2c_send_addr(rtems_libi2c_bus_t *bushdl, + uint32_t addr, int rw); + +/* Set as high frequency of SCK as possible but not higher than + * requested frequency (freq). + */ +int spictrl_set_freq(struct spictrl_priv *priv, unsigned int freq) +{ + unsigned int core_freq_hz = priv->core_freq_hz; + unsigned int lowest_freq_possible, result; + unsigned int div, div16, pm, fact; + + /* Lowest possible when DIV16 is set and PM is 0xf */ + lowest_freq_possible = core_freq_hz / (16 * 4 * (0xf + 1)); + + if ( freq < lowest_freq_possible ) { + DBG("SPICTRL: TOO LOW FREQ %u, CORE FREQ %u, LOWEST FREQ %u\n", + freq, core_freq_hz, lowest_freq_possible); + return -1; + } + + div = ((core_freq_hz / 2) + (freq-1)) / freq; + DBG("SPICTRL: DIV=%d, FREQ=%d\n", div, freq); + + /* Is DIV16 neccessary? */ + if ( div > 16 ) { + div = (div + (16 - 1)) / 16; + div16 = 1; + } else { + div16 = 0; + } + + if ( div > 0xf ) { + fact = 0; /* FACT adds an factor /2 */ + div = (div + (2 - 1)) / 2; + } else { + fact = 1; + } + + pm = div-1; + + /* Update hardware */ + priv->regs->mode = + (priv->regs->mode & ~(SPICTRL_MODE_PM|SPICTRL_MODE_DIV16|SPICTRL_MODE_FACT)) | + (pm << SPICTRL_MODE_PM_BIT) | (div16 << SPICTRL_MODE_DIV16_BIT) | + (fact << SPICTRL_MODE_FACT_BIT); + + result = core_freq_hz / (2 * (fact ? 1 : 2) * (div) * (div16 ? 16 : 1) ); + DBG("SPICTRL: Effective bit rate %u (requested %u), PM: %x, FACT: %d, div16: %x, core_freq: %u\n", result, freq, pm, fact, div16, core_freq_hz); + + return 0; +} + +/* Start Automated Periodic transfers, after this call read can be done */ +int spictrl_start_periodic(struct spictrl_priv *priv) +{ + struct spictrl_ioctl_config *cfg = &priv->periodic_cfg; + unsigned int am_cfg; + + /* Clear the events */ + priv->regs->event = 0xffffffff; + + /* Enable core */ + priv->regs->mode |= SPICTRL_MODE_EN | SPICTRL_MODE_MS; + + /* Update hardware config from flags and period */ + priv->regs->am_period = cfg->period; + + /* Remove SPICTRL_PERIOD_FLAGS_ASEL and ACT bit and shift into posistion */ + am_cfg = (cfg->period_flags & 0x1f8) >> 1; + priv->regs->am_cfg = am_cfg; + + /* Start automated periodic transfers */ + if ( cfg->period_flags & SPICTRL_PERIOD_FLAGS_EACT ) { + /* Enable external triggering */ + priv->regs->am_cfg = am_cfg | SPICTRL_AMCFG_EACT; + } else { + /* Activate periodic transfers */ + priv->regs->am_cfg = am_cfg | SPICTRL_AMCFG_ACT; + } + + return 0; +} + +/* Stop Automated Periodic transfers */ +void spictrl_stop_periodic(struct spictrl_priv *priv) +{ + priv->regs->am_cfg = 0; +} + +/* Return the status of the SPI controller (the event register), + * it may be needed in periodic mode to look at the Not Full bit (NF) + * in order not to hang in an infinte loop when read is called. + */ +unsigned int spictrl_status(struct spictrl_priv *priv) +{ + return priv->regs->event; +} + +int spictrl_read_periodic(struct spictrl_priv *priv, struct spictrl_period_io *rarg) +{ + int i, rxi, rxshift, bits_per_char, reg; + unsigned int rx_word, mask; + void *rxbuf; + + if ( rarg->options & 0x1 ) { + /* Read mask registers */ + for (i=0; i<4; i++) { + rarg->masks[i] = priv->regs->am_mask[i]; + } + } + + if ( rarg->options & 0x2 ) { + /* Read receive registers (after updating masks so that the caller can + * read current buffer without knowning of actual register mask). + */ + + /* If not started we could be hanging here forever. */ + if ( !priv->periodic_started ) + return -1; + + rxshift = priv->rxshift; + bits_per_char = priv->bits_per_char; + rx_word = 0; + + rxbuf = rarg->data; + if ( !rxbuf ) { + /* If no data pointer specified we cannot copy data... */ + return -1; + } + + /* Wait until all data is available (if started) */ + while ( (priv->regs->event & SPICTRL_EVENT_NE) == 0 ) { + ; + } + + rxi = 0; + for (i=0; i<4; i++) { + mask = rarg->masks[i]; + reg = 0; + while ( mask ) { + if ( mask & 1 ) { + /* Update Register */ + rx_word = priv->regs->am_rx[i*32 + reg] >> rxshift; + + if ( bits_per_char <= 8 ) { + *((unsigned char *)rxbuf + rxi) = rx_word; + } else if ( bits_per_char <= 16 ) { + *((unsigned short *)rxbuf + rxi) = rx_word; + } else { + *((unsigned int *)rxbuf + rxi) = rx_word; + } + rxi++; + } + + mask = mask>>1; + reg++; + } + } + } + + return 0; +} + +int spictrl_write_periodic(struct spictrl_priv *priv, struct spictrl_period_io *warg) +{ + int i, txi, txshift, bits_per_char, reg; + unsigned int tx_word, mask; + void *txbuf; + + if ( warg->options & 0x2 ) { + + /* Make sure core is enabled, otherwise TX registers writes are lost */ + priv->regs->mode |= SPICTRL_MODE_EN; + + /* Update Transmit registers (before updating masks so that we do not + * transmit invalid data) + */ + + txshift = priv->txshift; + bits_per_char = priv->bits_per_char; + tx_word = 0; + + txbuf = warg->data; + if ( !txbuf ) { + /* If no data pointer specified we fill up with + * idle chars. + */ + tx_word = priv->idle_char << txshift; + } + + txi = 0; + for (i=0; i<4; i++) { + mask = warg->masks[i]; + reg = 0; + while ( mask ) { + if ( mask & 1 ) { + if ( txbuf ) { + if ( bits_per_char <= 8 ) { + tx_word = *((unsigned char *)txbuf + txi); + } else if ( bits_per_char <= 16 ) { + tx_word = *((unsigned short *)txbuf + txi); + } else { + tx_word = *((unsigned int *)txbuf + txi); + } + tx_word = tx_word << txshift; + txi++; + } + + /* Update Register */ + DBG("WRITE 0x%08x to 0x%08x\n", tx_word, &priv->regs->am_tx[i*32 + reg]); + priv->regs->am_tx[i*32 + reg] = tx_word; + } + + mask = mask>>1; + reg++; + } + } + } + + if ( warg->options & 0x1 ) { + /* Update mask registers */ + for (i=0; i<4; i++) { + DBG("WRITE 0x%08x to 0x%08x (MSK%d)\n", warg->masks[i], &priv->regs->am_mask[i], i); + priv->regs->am_mask[i] = warg->masks[i]; + } + } + + return 0; +} + +int spictrl_read_write(struct spictrl_priv *priv, void *rxbuf, void *txbuf, int len) +{ + unsigned int tx_word, rx_word, tmp; + int txshift = priv->txshift; + int rxshift = priv->rxshift; + int txi, rxi, bits_per_char; + int length; + + /* Use IOCTL for periodic reads. The FIFO is not supported in automated + * periodic mode + */ + if ( priv->periodic_cfg.periodic_mode ) { + return -1; + } + + bits_per_char = priv->bits_per_char; + tx_word = 0; + if ( !txbuf ) { + tx_word = priv->idle_char << txshift; + } + + /* Clear the events */ + priv->regs->event = 0xffffffff; + + /* Enable core */ + priv->regs->mode |= SPICTRL_MODE_EN | SPICTRL_MODE_MS; + + length = len; + if ( bits_per_char > 8 ) { + length = length / 2; + if ( bits_per_char > 16 ) + length = length / 2; + } + DBG("SPICTRL: LENGTH = %d, Bits/Char: %d, Shift: %d, %d\n", length, bits_per_char, txshift, rxshift); + + txi=0; + rxi=0; + while ( (rxi < length) || (txi < length) ) { + /* Get transmit word */ + if ( length > txi ) { + if ( txbuf ) { + if ( bits_per_char <= 8 ) { + tx_word = *((unsigned char *)txbuf + txi); + } else if ( bits_per_char <= 16 ) { + tx_word = *((unsigned short *)txbuf + txi); + } else { + tx_word = *((unsigned int *)txbuf + txi); + } + tx_word = tx_word << txshift; + } + + /* Wait for SPICTRL to get ready for another TX char */ + while ( (priv->regs->event & SPICTRL_EVENT_NF) == 0 ) { + /* Wait for all chars to transmit */ +/* Could implement waiting for SPICTRL IRQ here */ + } + + DBG("SPICTRL: Writing 0x%x\n", tx_word); + + /* Transmit word */ + priv->regs->tx = tx_word; + txi++; + } + + /* Read */ + while ( priv->regs->event & SPICTRL_EVENT_NE ) { + /* Read to avoid overrun */ + tmp = priv->regs->rx; + DBG("SPICTRL: Read 0x%x\n", tmp); + + if ( rxbuf && (length > rxi) ) { + /* Copy word to user buffer */ + rx_word = (tmp >> rxshift); + + DBG("SPICTRL: Receiving 0x%x (0x%x, %d)\n", rx_word, tmp, rxshift); + + if ( bits_per_char <= 8 ) { + *((unsigned char *)rxbuf + rxi) = rx_word; + } else if ( bits_per_char <= 16 ) { + *((unsigned short *)rxbuf + rxi) = rx_word; + } else { + *((unsigned int *)rxbuf + rxi) = rx_word; + } + + } + rxi++; + } + } + + return len; +} + + +STATIC rtems_status_code spictrl_libi2c_init(rtems_libi2c_bus_t *bushdl) +{ + struct spictrl_priv *priv = (struct spictrl_priv *)bushdl; + + DBG("SPICTRL: spictrl_libi2c_init\n"); + + /* Disable SPICTTRL, Select Master mode */ + priv->regs->mode = SPICTRL_MODE_MS; + + /* Mask all Interrupts */ + priv->regs->mask = 0; + + /* Select no slave */ + priv->regs->slvsel = 0xffffffff; + + /* Clear all events */ + priv->regs->event = 0xffffffff; + + return 0; +} + +/* Nothing to be done in start */ +STATIC rtems_status_code spictrl_libi2c_send_start(rtems_libi2c_bus_t *bushdl) +{ + DBG("SPICTRL: spictrl_libi2c_send_start\n"); + + return 0; +} + +/* Inactivate all chip selects, indicates "End of command" */ +STATIC rtems_status_code spictrl_libi2c_send_stop(rtems_libi2c_bus_t *bushdl) +{ + struct spictrl_priv *priv = (struct spictrl_priv *)bushdl; + + priv->regs->slvsel = 0xffffffff; + + if ( priv->slvSelFunc ) { + /* unslect all */ + return priv->slvSelFunc(priv->regs, -1, 0); + } + + DBG("SPICTRL: spictrl_libi2c_send_stop\n"); + return 0; +} + +/* Select Slave address by selecting apropriate chip select */ +STATIC rtems_status_code spictrl_libi2c_send_addr(rtems_libi2c_bus_t *bushdl, + uint32_t addr, int rw) +{ + struct spictrl_priv *priv = (struct spictrl_priv *)bushdl; + + DBG("SPICTRL: spictrl_libi2c_send_addr, %d\n", addr); + + if ( priv->slvSelFunc ) { + /* Let user set spi select using for example GPIO */ + return priv->slvSelFunc(priv->regs, addr, 1); + } else if ( priv->regs->capability & SPICTRL_CAP_SSEN ) { + int slaves; + + /* Maximum number of slaves the core support */ + slaves = (priv->regs->capability & SPICTRL_CAP_SSSZ) >> SPICTRL_CAP_SSSZ_BIT; + + if ( addr > slaves ) + return -1; + + if ( (priv->regs->capability & SPICTRL_CAP_ASELA) && + (priv->periodic_cfg.period_flags & SPICTRL_PERIOD_FLAGS_ASEL) ) { + /* When automatic slave select is supported by hardware and + * enabled by configuration the SPI address is determined by + * the automatic slave select register and the "idle" slave + * select register is set by configuration. + */ + priv->regs->am_slvsel = ~(1<<(addr-1)); + priv->regs->slvsel = priv->periodic_cfg.period_slvsel; + /* Enable automatic slave select */ + priv->regs->mode |= SPICTRL_MODE_ASEL; + } else { + /* Normal mode */ + priv->regs->slvsel = ~(1<<(addr-1)); + } + } + + return 0; +} + +/* Read a number of bytes */ +STATIC int spictrl_libi2c_read_bytes(rtems_libi2c_bus_t *bushdl, + unsigned char *bytes, int nbytes) +{ + struct spictrl_priv *priv = (struct spictrl_priv *)bushdl; + int ret; + + DBG("SPICTRL: spictrl_libi2c_read_bytes %d\n", nbytes); + ret = spictrl_read_write(priv, bytes, NULL, nbytes); + if ( ret < 0 ) { + printf("SPICTRL: Error Reading\n"); + } +#ifdef DEBUG + else { + int i; + for(i=0; i<nbytes; i+=16) { + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x ", + bytes[0+i], bytes[1+i], bytes[2+i], bytes[3+i], bytes[4+i], bytes[5+i], bytes[6+i], bytes[7+i]); + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + bytes[8+i], bytes[9+i], bytes[10+i], bytes[11+i], bytes[12+i], bytes[13+i], bytes[14+i], bytes[15+i]); + } + } +#endif + return ret; +} + +/* Write a number of bytes */ +STATIC int spictrl_libi2c_write_bytes(rtems_libi2c_bus_t *bushdl, + unsigned char *bytes, int nbytes) +{ + struct spictrl_priv *priv = (struct spictrl_priv *)bushdl; + +#ifdef DEBUG + int i; + DBG("SPICTRL: spictrl_libi2c_write_bytes: %d\n", nbytes); + + for(i=0; i<nbytes; i+=16) { + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x ", + bytes[0+i], bytes[1+i], bytes[2+i], bytes[3+i], bytes[4+i], bytes[5+i], bytes[6+i], bytes[7+i]); + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + bytes[8+i], bytes[9+i], bytes[10+i], bytes[11+i], bytes[12+i], bytes[13+i], bytes[14+i], bytes[15+i]); + } +#endif + + return spictrl_read_write(priv, NULL, bytes, nbytes); +} + +/* Configure the interface and do simultaneous READ/WRITE operations */ +STATIC int spictrl_libi2c_ioctl( + rtems_libi2c_bus_t * bushdl, + int cmd, + void *buffer) +{ + struct spictrl_priv *priv = (struct spictrl_priv *)bushdl; + int ret; + + DBG("SPICTRL: spictrl_libi2c_ioctl(%d, 0x%x)\n", cmd, (unsigned int)buffer); + + switch (cmd) { + case RTEMS_LIBI2C_IOCTL_SET_TFRMODE: + { + rtems_libi2c_tfr_mode_t *trf_mode = buffer; + unsigned int mode; + + /* Must disable core to write new values */ + priv->regs->mode &= ~SPICTRL_MODE_EN; + + /* Change bit frequency */ + if ( spictrl_set_freq(priv, trf_mode->baudrate) ) { + /* Unable to set such a low frequency. */ + return -1; + } + + /* Set Clock Polarity, Clock Phase, Reverse mode and Word Length */ + mode = (priv->regs->mode & + ~(SPICTRL_MODE_CPOL|SPICTRL_MODE_CPHA|SPICTRL_MODE_REV|SPICTRL_MODE_LEN)); + if ( trf_mode->clock_inv ) + mode |= SPICTRL_MODE_CPOL; + if ( trf_mode->clock_phs ) + mode |= SPICTRL_MODE_CPHA; + if ( trf_mode->lsb_first == 0 ) + mode |= SPICTRL_MODE_REV; /* Set Reverse mode (MSB first) */ + + if ( (trf_mode->bits_per_char < 4) || + ((trf_mode->bits_per_char > 16) && (trf_mode->bits_per_char != 32)) ) + return -1; + if ( trf_mode->bits_per_char == 32 ) { + priv->txshift = 0; + priv->rxshift = 0; + } else { + mode |= (trf_mode->bits_per_char-1) << SPICTRL_MODE_LEN_BIT; + if ( trf_mode->lsb_first == 0 ) { + /* REV bit 1 */ + priv->txshift = 32 - trf_mode->bits_per_char; + priv->rxshift = 16; + } else { + /* REV bit 0 */ + priv->txshift = 0; + priv->rxshift = 16 - trf_mode->bits_per_char; + } + } + + priv->bits_per_char = trf_mode->bits_per_char; + priv->lsb_first = trf_mode->lsb_first; + priv->idle_char = trf_mode->idle_char; + + /* Update hardware */ + priv->regs->mode = mode; + + return 0; + } + + case RTEMS_LIBI2C_IOCTL_READ_WRITE: + { + rtems_libi2c_read_write_t *arg = buffer; + + DBG("SPICTRL: IOCTL READ/WRITE, RX: 0x%x, TX: 0x%x, len: %d\n", arg->rd_buf, arg->wr_buf, arg->byte_cnt); +#ifdef DEBUG + /* Printf out what is going to be transmitted */ + if ( arg->wr_buf ) { + unsigned char *bytes = (unsigned char *)arg->wr_buf; + int i; + for(i=0; i<arg->byte_cnt; i+=16) { + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x ", + bytes[0+i], bytes[1+i], bytes[2+i], bytes[3+i], bytes[4+i], bytes[5+i], bytes[6+i], bytes[7+i]); + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + bytes[8+i], bytes[9+i], bytes[10+i], bytes[11+i], bytes[12+i], bytes[13+i], bytes[14+i], bytes[15+i]); + } + } +#endif + + ret = spictrl_read_write(priv, arg->rd_buf, (unsigned char *)arg->wr_buf, + arg->byte_cnt); +#ifdef DEBUG + /* Printf out what was read */ + if ( arg->rd_buf ) { + unsigned char *bytes = (unsigned char *)arg->rd_buf; + int i; + for(i=0; i<arg->byte_cnt; i+=16) { + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x ", + bytes[0+i], bytes[1+i], bytes[2+i], bytes[3+i], bytes[4+i], bytes[5+i], bytes[6+i], bytes[7+i]); + DBG("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + bytes[8+i], bytes[9+i], bytes[10+i], bytes[11+i], bytes[12+i], bytes[13+i], bytes[14+i], bytes[15+i]); + } + } +#endif + return ret; + } + + /* Enable Periodic mode */ + case SPICTRL_IOCTL_CONFIG: + { + struct spictrl_ioctl_config *cfg; + + DBG("SPICTRL: Configuring Periodic mode\n"); + + if ( priv->periodic_started ) { + DBG("SPICTRL: Periodic mode already started, too late to configure\n"); + return -1; + } + + cfg = buffer; + if ( cfg == NULL ) { + memset(&priv->periodic_cfg, 0, sizeof(priv->periodic_cfg)); + } else { + priv->periodic_cfg = *cfg; + } + cfg = &priv->periodic_cfg; + if ( cfg->periodic_mode ) { + /* Enable Automated Periodic mode */ + priv->regs->mode |= SPICTRL_MODE_AMEN; + + /* Check that hardware has support for periodic mode */ + if ( (priv->regs->mode & SPICTRL_MODE_AMEN) == 0 ) { + priv->periodic_cfg.periodic_mode = 0; + DBG("SPICTRL: Periodic mode not supported by hardware\n"); + return -1; + } + } else { + /* Disable Periodic mode */ + priv->regs->mode &= ~SPICTRL_MODE_AMEN; + } + priv->periodic_started = 0; + + /* Set clockgap and TAC */ + priv->regs->mode = (priv->regs->mode & ~(SPICTRL_MODE_CG|SPICTRL_MODE_TAC)) | + (cfg->clock_gap << SPICTRL_MODE_CG_BIT) | + (cfg->flags & SPICTRL_MODE_TAC); + return 0; + } + case SPICTRL_IOCTL_PERIOD_START: + { + if ( !priv->periodic_cfg.periodic_mode || priv->periodic_started ) { + return -1; + } + if ( spictrl_start_periodic(priv) == 0 ) { + priv->periodic_started = 1; + return 0; + } else + return -1; + } + case SPICTRL_IOCTL_PERIOD_STOP: + { + if ( !priv->periodic_cfg.periodic_mode || !priv->periodic_started ) { + return -1; + } + spictrl_stop_periodic(priv); + priv->periodic_started = 0; + return 0; + } + case SPICTRL_IOCTL_STATUS: + { + if ( !buffer ) + return 0; + *(unsigned int *)buffer = spictrl_status(priv); + return 0; + } + + case SPICTRL_IOCTL_PERIOD_WRITE: + { + if ( !priv->periodic_cfg.periodic_mode || !buffer ) { + return -1; + } + if ( spictrl_write_periodic(priv, (struct spictrl_period_io *) + buffer) == 0 ) { + return 0; + } else + return -1; + } + + case SPICTRL_IOCTL_PERIOD_READ: + { + if ( !priv->periodic_cfg.periodic_mode || !buffer ) { + return -1; + } + if ( spictrl_read_periodic(priv, (struct spictrl_period_io *) + buffer) == 0 ) { + return 0; + } else + return -1; + } + + case SPICTRL_IOCTL_REGS: + { + /* Copy Register Base Address to user space */ + if ( !buffer ) { + return -1; + } + *(struct spictrl_regs **)buffer = priv->regs; + return 0; + } + + default: + /* Unknown IOCTL */ + return -1; + } + + return 0; +} + +STATIC rtems_libi2c_bus_ops_t spictrl_libi2c_ops = +{ + .init = spictrl_libi2c_init, + .send_start = spictrl_libi2c_send_start, + .send_stop = spictrl_libi2c_send_stop, + .send_addr = spictrl_libi2c_send_addr, + .read_bytes = spictrl_libi2c_read_bytes, + .write_bytes = spictrl_libi2c_write_bytes, + .ioctl = spictrl_libi2c_ioctl +}; + +int spictrl_device_init(struct spictrl_priv *priv) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + priv->irq = pnpinfo->irq; + priv->regs = (struct spictrl_regs *)pnpinfo->apb_slv->start; + priv->fdepth = (priv->regs->capability & SPICTRL_CAP_FDEPTH) >> SPICTRL_CAP_FDEPTH_BIT; + + DBG("SPCTRL: 0x%x irq %d, FIFO: %d\n", (unsigned int)priv->regs, priv->irq, priv->fdepth); + + /* Mask all Interrupts */ + priv->regs->mask = 0; + + /* Disable SPICTTRL */ + priv->regs->mode = 0; + + /* Get custom */ + value = drvmgr_dev_key_get(priv->dev, "slvSelFunc", KEY_TYPE_POINTER); + if ( value ) { + priv->slvSelFunc = value->ptr; + } + + /* Prepare I2C layer */ + priv->i2clib_desc.ops = &spictrl_libi2c_ops; + priv->i2clib_desc.size = sizeof(spictrl_libi2c_ops); + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/spw/grspw_router.c b/c/src/lib/libbsp/sparc/shared/spw/grspw_router.c new file mode 100644 index 0000000000..f33ad3756e --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/spw/grspw_router.c @@ -0,0 +1,549 @@ +/* GRSPW ROUTER APB-Register Driver. + * + * COPYRIGHT (c) 2010. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <rtems.h> +#include <rtems/libio.h> +#include <stdio.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <grspw_router.h> + +#define ROUTER_DBG(args...) + +#define REG_READ(adr) (*(volatile unsigned int *)(adr)) +#define REG_WRITE(adr, value) (*(volatile unsigned int *)(adr) = (value)) + +struct router_regs { + unsigned int resv1; /* 0x000 */ + unsigned int psetup[255]; /* 0x004 */ + unsigned int resv2[32]; /* 0x400 */ + unsigned int routes[224]; /* 0x480 */ + unsigned int pctrl[32]; /* 0x800 */ + unsigned int psts[32]; /* 0x880 */ + unsigned int treload[32]; /* 0x900 */ + unsigned int resv3[32]; /* 0x980 */ + unsigned int cfgsts; /* 0xA00 */ + unsigned int timecode; /* 0xA04 */ + unsigned int ver; /* 0xA08 */ + unsigned int idiv; /* 0xA0C */ + unsigned int cfgwe; /* 0xA10 */ + unsigned int tprescaler; /* 0xA14 */ + unsigned int resv4[123]; /* 0xA18 */ + unsigned int charo[31]; /* 0xC04 */ + unsigned int resv5; /* 0xC80 */ + unsigned int chari[31]; /* 0xC84 */ + unsigned int resv6; /* 0xD00 */ + unsigned int pkto[31]; /* 0xD04 */ + unsigned int resv7; /* 0xD80 */ + unsigned int pkti[31]; /* 0xD84 */ +}; + +struct router_priv { + char devName[32]; + struct drvmgr_dev *dev; + struct router_regs *regs; + int minor; + int open; + struct router_hw_info hwinfo; + int nports; +}; + +static rtems_device_driver router_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ); + +static rtems_device_driver router_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ); + +static rtems_device_driver router_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ); + +static rtems_device_driver router_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ); + +#define ROUTER_DRIVER_TABLE_ENTRY \ + { router_initialize, \ + router_open, \ + router_close, \ + NULL, \ + NULL, \ + router_control } + +void router_hwinfo(struct router_priv *priv, struct router_hw_info *hwinfo); + +static rtems_driver_address_table router_driver = ROUTER_DRIVER_TABLE_ENTRY; +static int router_driver_io_registered = 0; +static rtems_device_major_number router_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +int router_register_io(rtems_device_major_number *m); + +int router_init2(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops router_ops = +{ + .init = {NULL, router_init2, NULL, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id router_ids[] = +{ + {VENDOR_GAISLER, GAISLER_SPW_ROUTER}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info router_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_SPW_ROUTER_ID,/* Driver ID */ + "ROUTER_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &router_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct router_priv), /* Let DRVMGR allocate for us */ + }, + &router_ids[0], +}; + +void router_register_drv (void) +{ + drvmgr_drv_register(&router_drv_info.general); +} + +int router_init2(struct drvmgr_dev *dev) +{ + struct router_priv *priv = dev->priv; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + char prefix[32]; + rtems_status_code status; + + if ( priv == NULL ) + return DRVMGR_NOMEM; + priv->dev = dev; + + /* Do initialization */ + if ( router_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( router_register_io(&router_driver_io_major) ) { + /* Failed to register I/O driver */ + return DRVMGR_FAIL; + } + + router_driver_io_registered = 1; + } + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)priv->dev->businfo; + if ( ambadev == NULL ) { + return DRVMGR_FAIL; + } + pnpinfo = &ambadev->info; + priv->regs = (struct router_regs *)pnpinfo->ahb_slv->start[0]; + priv->minor = dev->minor_drv; + + /* Register character device in registered region */ + router_hwinfo(priv, &priv->hwinfo); + priv->open = 0; + priv->nports = priv->hwinfo.nports_spw + priv->hwinfo.nports_amba + + priv->hwinfo.nports_fifo; + if ( (priv->nports < 2) || (priv->nports > 32) ) + return DRVMGR_FAIL; + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/router%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%srouter%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, router_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +int router_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &router_driver, m)) == RTEMS_SUCCESSFUL) { + ROUTER_DBG("ROUTER driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("ROUTER rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("ROUTER rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("ROUTER rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("ROUTER rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +static rtems_device_driver router_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg + ) +{ + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver router_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + struct router_priv *priv; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&router_drv_info.general, minor, &dev) ) { + ROUTER_DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + priv = (struct router_priv *)dev->priv; + + if ( !priv || priv->open ) { + return RTEMS_RESOURCE_IN_USE; + } + + priv->open = 1; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver router_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + struct router_priv *priv; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&router_drv_info.general, minor, &dev) ) { + ROUTER_DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + priv = (struct router_priv *)dev->priv; + + priv->open = 0; + + return RTEMS_SUCCESSFUL; +} + +void router_hwinfo(struct router_priv *priv, struct router_hw_info *hwinfo) +{ + unsigned int tmp; + + tmp = REG_READ(&priv->regs->cfgsts); + hwinfo->nports_spw = (tmp >> 27) & 0x1f; + hwinfo->nports_amba = (tmp >> 22) & 0x1f; + hwinfo->nports_fifo = (tmp >> 17) & 0x1f; + hwinfo->timers_avail = (tmp >> 1) & 0x1; + hwinfo->pnp_avail = (tmp >> 0) & 0x1; + + tmp = REG_READ(&priv->regs->ver); + hwinfo->ver_major = (tmp >> 24) & 0xff; + hwinfo->ver_minor = (tmp >> 16) & 0xff; + hwinfo->ver_patch = (tmp >> 8) & 0xff; + hwinfo->iid = (tmp >> 0) & 0xff; +} + +int router_config_set(struct router_priv *priv, struct router_config *cfg) +{ + int i; + + if ( (cfg->flags & (ROUTER_FLG_TPRES|ROUTER_FLG_TRLD)) && + !priv->hwinfo.timers_avail ) { + return RTEMS_NOT_IMPLEMENTED; + } + + /* Write only configuration bits in Config register */ + if ( cfg->flags & ROUTER_FLG_CFG ) { + REG_WRITE(&priv->regs->cfgsts, cfg->config & ~0x4); + } + + /* Write Instance ID to Version Register */ + if ( cfg->flags & ROUTER_FLG_IID ) { + REG_WRITE(&priv->regs->ver, cfg->iid); + } + + /* Write startup-clock-divisor Register */ + if ( cfg->flags & ROUTER_FLG_IDIV ) { + REG_WRITE(&priv->regs->idiv, cfg->idiv); + } + + /* Write Timer Prescaler Register */ + if ( cfg->flags & ROUTER_FLG_TPRES ) { + REG_WRITE(&priv->regs->tprescaler, cfg->timer_prescaler); + } + + /* Write Timer Reload Register */ + if ( cfg->flags & ROUTER_FLG_TRLD ) { + for (i=0; i<=priv->nports; i++) + REG_WRITE(&priv->regs->treload[i], cfg->timer_reload[i]); + } + + return 0; +} + +int router_config_read(struct router_priv *priv, struct router_config *cfg) +{ + int i; + + cfg->config = REG_READ(&priv->regs->cfgsts) & ~0xffff0007; + cfg->iid = REG_READ(&priv->regs->ver) & 0xff; + cfg->idiv = REG_READ(&priv->regs->idiv) & 0xff; + cfg->timer_prescaler = REG_READ(&priv->regs->tprescaler); + for (i=0; i<=priv->nports; i++) + cfg->timer_reload[i] = REG_READ(&priv->regs->treload[i]); + + return 0; +} + +int router_routes_set(struct router_priv *priv, struct router_routes *routes) +{ + int i; + for (i=0; i<224; i++) + REG_WRITE(&priv->regs->routes[i], routes->route[i]); + return 0; +} + +int router_routes_read(struct router_priv *priv, struct router_routes *routes) +{ + int i; + for (i=0; i<224; i++) + routes->route[i] = REG_READ(&priv->regs->routes[i]); + return 0; +} + +int router_ps_set(struct router_priv *priv, struct router_ps *ps) +{ + int i; + unsigned int *p = &ps->ps[0]; + for (i=0; i<255; i++,p++) + REG_WRITE(&priv->regs->psetup[i], *p); + return 0; +} + +int router_ps_read(struct router_priv *priv, struct router_ps *ps) +{ + int i; + unsigned int *p = &ps->ps[0]; + for (i=0; i<255; i++,p++) + REG_WRITE(&priv->regs->psetup[i], *p); + return 0; +} + +int router_we_set(struct router_priv *priv, int we) +{ + REG_WRITE(&priv->regs->cfgwe, we & 0x1); + return 0; +} + +int router_port_ctrl(struct router_priv *priv, struct router_port *port) +{ + unsigned int ctrl, sts; + + if ( port->port > priv->nports ) + return RTEMS_INVALID_NAME; + + ctrl = port->ctrl; + if ( port->flag & ROUTER_PORTFLG_GET_CTRL ) { + ctrl = REG_READ(&priv->regs->pctrl[port->port]); + } + sts = port->sts; + if ( port->flag & ROUTER_PORTFLG_GET_STS ) { + sts = REG_READ(&priv->regs->psts[port->port]); + } + + if ( port->flag & ROUTER_PORTFLG_SET_CTRL ) { + REG_WRITE(&priv->regs->pctrl[port->port], port->ctrl); + } + if ( port->flag & ROUTER_PORTFLG_SET_STS ) { + REG_WRITE(&priv->regs->psts[port->port], port->sts); + } + + port->ctrl = ctrl; + port->sts = sts; + return 0; +} + +int router_cfgsts_set(struct router_priv *priv, unsigned int cfgsts) +{ + REG_WRITE(&priv->regs->cfgsts, cfgsts); + return 0; +} + +int router_cfgsts_read(struct router_priv *priv, unsigned int *cfgsts) +{ + *cfgsts = REG_READ(&priv->regs->cfgsts); + return 0; +} + +int router_tc_read(struct router_priv *priv, unsigned int *tc) +{ + *tc = REG_READ(&priv->regs->timecode); + return 0; +} + +static rtems_device_driver router_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void * arg + ) +{ + struct router_priv *priv; + struct drvmgr_dev *dev; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; + void *argp = (void *)ioarg->buffer; + + if ( drvmgr_get_dev(&router_drv_info.general, minor, &dev) ) { + ROUTER_DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NAME; + } + priv = (struct router_priv *)dev->priv; + + ioarg->ioctl_return = 0; + switch (ioarg->command) { + + /* Get Hardware support/information available */ + case GRSPWR_IOCTL_HWINFO: + { + struct router_hw_info *hwinfo = argp; + router_hwinfo(priv, hwinfo); + break; + } + + /* Set Router Configuration */ + case GRSPWR_IOCTL_CFG_SET: + { + struct router_config *cfg = argp; + return router_config_set(priv, cfg); + } + + /* Read Router Configuration */ + case GRSPWR_IOCTL_CFG_GET: + { + struct router_config *cfg = argp; + router_config_read(priv, cfg); + break; + } + + /* Routes */ + case GRSPWR_IOCTL_ROUTES_SET: + { + struct router_routes *routes = argp; + return router_routes_set(priv, routes); + } + + case GRSPWR_IOCTL_ROUTES_GET: + { + struct router_routes *routes = argp; + router_routes_read(priv, routes); + break; + } + + /* Port Setup */ + case GRSPWR_IOCTL_PS_SET: + { + struct router_ps *ps = argp; + return router_ps_set(priv, ps); + } + + case GRSPWR_IOCTL_PS_GET: + { + struct router_ps *ps = argp; + router_ps_read(priv, ps); + break; + } + + /* Set configuration write enable */ + case GRSPWR_IOCTL_WE_SET: + { + return router_we_set(priv, (int)argp); + } + + /* Set/Get Port Control/Status */ + case GRSPWR_IOCTL_PORT: + { + struct router_port *port = argp; + int result; + if ( (result=router_port_ctrl(priv, port)) ) + return result; + break; + } + + /* Set Router Configuration/Status Register */ + case GRSPWR_IOCTL_CFGSTS_SET: + { + return router_cfgsts_set(priv, (int)argp); + } + + /* Get Router Configuration/Status Register */ + case GRSPWR_IOCTL_CFGSTS_GET: + { + unsigned int *cfgsts = argp; + router_cfgsts_read(priv, cfgsts); + break; + } + + /* Get Current Time-Code Register */ + case GRSPWR_IOCTL_TC_GET: + { + unsigned int *tc = argp; + router_tc_read(priv, tc); + break; + } + + default: return RTEMS_NOT_IMPLEMENTED; + } + + return 0; +} diff --git a/c/src/lib/libbsp/sparc/shared/time/grctm.c b/c/src/lib/libbsp/sparc/shared/time/grctm.c new file mode 100644 index 0000000000..3185e13264 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/time/grctm.c @@ -0,0 +1,409 @@ +/* GRCTM - CCSDS Time Manager - register driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <stdlib.h> + +#include <grctm.h> + +/* Private structure of GRCTM driver */ +struct grctm_priv { + struct drvmgr_dev *dev; + struct grctm_regs *regs; + int open; + + grctm_isr_t user_isr; + void *user_isr_arg; + + struct grctm_stats stats; +}; + +void grctm_isr(void *data); + +struct amba_drv_info grctm_drv_info; + +void *grctm_open(int minor) +{ + struct grctm_priv *priv; + struct drvmgr_dev *dev; + + /* Get Device from Minor */ + if ( drvmgr_get_dev(&grctm_drv_info.general, minor, &dev) ) { + return NULL; + } + + priv = dev->priv; + if ( (priv == NULL) || priv->open ) + return NULL; + + /* Set initial state of software */ + priv->open = 1; + + /* Clear Statistics */ + grctm_clr_stats(priv); + priv->user_isr = NULL; + priv->user_isr_arg = NULL; + + return priv; +} + +void grctm_close(void *grctm) +{ + struct grctm_priv *priv = (struct grctm_priv *)grctm; + + if ( priv->open == 0 ) + return; + + /* Reset Hardware */ + grctm_reset(priv); + + priv->open = 0; +} + +/* Hardware Reset of GRCTM */ +int grctm_reset(void *grctm) +{ + struct grctm_priv *priv = grctm; + struct grctm_regs *r = priv->regs; + + r->grr = 0x55000001; + + int i = 1000; + while ((r->grr & 1) && i > 0) { + i--; + } + + return i ? 0 : -1; +} + +void grctm_int_enable(void *grctm) +{ + struct grctm_priv *priv = (struct grctm_priv *)grctm; + + /* Register and Enable Interrupt at Interrupt controller */ + drvmgr_interrupt_register(priv->dev, 0, "grctm", grctm_isr, priv); +} + +void grctm_int_disable(void *grctm) +{ + struct grctm_priv *priv = (struct grctm_priv *)grctm; + + /* Enable Interrupt at Interrupt controller */ + drvmgr_interrupt_unregister(priv->dev, 0, grctm_isr, priv); +} + +void grctm_clr_stats(void *grctm) +{ + struct grctm_priv *priv = (struct grctm_priv *)grctm; + + memset(&priv->stats, 0, sizeof(priv->stats)); +} + +void grctm_get_stats(void *grctm, struct grctm_stats *stats) +{ + struct grctm_priv *priv = (struct grctm_priv *)grctm; + + memcpy(stats, &priv->stats, sizeof(priv->stats)); +} + +/* Enable external synchronisation (from grctm) */ +void grctm_enable_ext_sync(void *grctm) +{ + struct grctm_priv *priv = grctm; + + priv->regs->gcr |= 0x55<<24 | 1<<9; +} + +/* Disable external synchronisation (from grctm) */ +void grctm_disable_ext_sync(void *grctm) +{ + struct grctm_priv *priv = grctm; + + priv->regs->gcr &= ~((0xAA<<24) | 1<<9); +} + +/* Enable TimeWire synchronisation */ +void grctm_enable_tw_sync(void *grctm) +{ + struct grctm_priv *priv = grctm; + + priv->regs->gcr |= 0x55<<24 | 1<<8; +} + +/* Disable TimeWire synchronisation */ +void grctm_disable_tw_sync(void *grctm) +{ + struct grctm_priv *priv = grctm; + + priv->regs->gcr &= ~((0xAA<<24) | 1<<8); +} + +/* Disable frequency synthesizer from driving ET */ +void grctm_disable_fs(void *grctm) +{ + struct grctm_priv *priv = grctm; + + priv->regs->gcr |= 0x55<<24 | 1<<7; +} + +/* Enable frequency synthesizer to drive ET */ +void grctm_enable_fs(void *grctm) +{ + struct grctm_priv *priv = grctm; + + priv->regs->gcr &= ~((0xAA<<24) | 1<<7); +} + +/* Return elapsed coarse time */ +unsigned int grctm_get_et_coarse(void *grctm) +{ + struct grctm_priv *priv = grctm; + + return priv->regs->etcr; +} + +/* Return elapsed fine time */ +unsigned int grctm_get_et_fine(void *grctm) +{ + struct grctm_priv *priv = grctm; + + return (priv->regs->etfr & 0xffffff00) >> 8; +} + +/* Return elapsed time (coarse and fine) */ +unsigned long long grctm_get_et(void *grctm) +{ + return (((unsigned long)grctm_get_et_coarse(grctm)) << 24) | grctm_get_et_fine(grctm); +} + + +/* Return 1 if specified datation has been latched */ +int grctm_is_dat_latched(void *grctm, int dat) +{ + struct grctm_priv *priv = grctm; + + return (priv->regs->gsr >> dat) & 1; +} + +/* Set triggering edge of datation input */ +void grctm_set_dat_edge(void *grctm, int dat, int edge) +{ + struct grctm_priv *priv = grctm; + + priv->regs->gcr &= ~((0xAA<<24) | 1 << (10+dat)); + priv->regs->gcr |= 0x55<<24 | (edge&1) << (10+dat); +} + +/* Return latched datation coarse time */ +unsigned int grctm_get_dat_coarse(void *grctm, int dat) +{ + struct grctm_priv *priv = grctm; + + switch (dat) { + case 0 : return priv->regs->dcr0; + case 1 : return priv->regs->dcr1; + case 2 : return priv->regs->dcr2; + default: return -1; + } +} + +/* Return latched datation fine time */ +unsigned int grctm_get_dat_fine(void *grctm, int dat) +{ + struct grctm_priv *priv = grctm; + + switch (dat) { + case 0 : return (priv->regs->dfr0 & 0xffffff00) >> 8; + case 1 : return (priv->regs->dfr1 & 0xffffff00) >> 8; + case 2 : return (priv->regs->dfr2 & 0xffffff00) >> 8; + default: return -1; + } +} + + +/* Return latched datation ET */ +unsigned long long grctm_get_dat_et(void *grctm, int dat) +{ + return (((unsigned long)grctm_get_dat_coarse(grctm, dat)) << 24) | + grctm_get_dat_fine(grctm, dat); +} + + +/* Return current pulse configuration */ +unsigned int grctm_get_pulse_reg(void *grctm, int pulse) +{ + struct grctm_priv *priv = grctm; + + return priv->regs->pdr[pulse]; +} + +/* Set pulse register */ +void grctm_set_pulse_reg(void *grctm, int pulse, unsigned int val) +{ + struct grctm_priv *priv = grctm; + + priv->regs->pdr[pulse] = val; +} + +/* Configure pulse: pp = period, pw = width, pl = level, en = enable */ +void grctm_cfg_pulse(void *grctm, int pulse, int pp, int pw, int pl, int en) +{ + grctm_set_pulse_reg(grctm, pulse, (pp&0xf)<<20 | (pw&0xf)<<16 | (pl&1)<<10 | (en&1)<<1); +} + +/* Enable pulse output */ +void grctm_enable_pulse(void *grctm, int pulse) +{ + struct grctm_priv *priv = grctm; + + priv->regs->pdr[pulse] |= 0x2; +} + +/* Disable pulse output */ +void grctm_disable_pulse(void *grctm, int pulse) +{ + struct grctm_priv *priv = grctm; + + priv->regs->pdr[pulse] &= ~0x2; +} + +/* Clear interrupts */ +void grctm_clear_irqs(void *grctm, int irqs) +{ + struct grctm_priv *priv = grctm; + + priv->regs->picr = irqs; +} + +/* Enable interrupts */ +void grctm_enable_irqs(void *grctm, int irqs) +{ + struct grctm_priv *priv = grctm; + + priv->regs->imr = irqs; +} + +/* Set Frequency synthesizer increment */ +void grctm_set_fs_incr(void *grctm, int incr) +{ + struct grctm_priv *priv = grctm; + + priv->regs->fsir = incr; +} + +/* Set ET increment */ +void grctm_set_et_incr(void *grctm, int incr) +{ + struct grctm_priv *priv = grctm; + + priv->regs->etir = incr; +} + + +void grctm_isr(void *data) +{ + struct grctm_priv *priv = data; + struct grctm_stats *stats = &priv->stats; + unsigned int pimr = priv->regs->pimr; + + if ( pimr == 0 ) + return; + + stats->nirqs++; + if (pimr & PULSE0_IRQ ) + stats->pulse++; + + /* Let user Handle Interrupt */ + if ( priv->user_isr ) + priv->user_isr(pimr, priv->user_isr_arg); +} + +struct grctm_regs *grctm_get_regs(void *grctm) +{ + struct grctm_priv *priv = (struct grctm_priv *)grctm; + + return priv->regs; +} + +void grctm_int_register(void *grctm, grctm_isr_t func, void *data) +{ + struct grctm_priv *priv = (struct grctm_priv *)grctm; + + priv->user_isr = func; + priv->user_isr_arg = data; +} + +/*** INTERFACE TO DRIVER MANAGER ***/ + +int grctm_init2(struct drvmgr_dev *dev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + struct grctm_priv *priv; + struct grctm_regs *regs; + + priv = (struct grctm_priv *)malloc(sizeof(*priv)); + if ( priv == NULL ) + return -1; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + dev->priv = priv; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + regs = (struct grctm_regs *)pnpinfo->ahb_slv->start[0]; + + priv->regs = regs; + + grctm_reset(priv); + + return 0; +} + +struct drvmgr_drv_ops grctm_ops = +{ + {NULL, grctm_init2, NULL, NULL}, + NULL, + NULL +}; + +struct amba_dev_id grctm_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRCTM}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grctm_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRCTM_ID, /* Driver ID */ + "GRCTM_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grctm_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grctm_ids[0] +}; + +/* Register the grctm Driver */ +void grctm_register(void) +{ + drvmgr_drv_register(&grctm_drv_info.general); +} diff --git a/c/src/lib/libbsp/sparc/shared/time/spwcuc.c b/c/src/lib/libbsp/sparc/shared/time/spwcuc.c new file mode 100644 index 0000000000..738ec1a277 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/time/spwcuc.c @@ -0,0 +1,369 @@ +/* SPWCUC - SpaceWire - CCSDS unsegmented Code Transfer Protocol GRLIB core + * register driver interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <stdlib.h> + +#include <spwcuc.h> + +/* Private structure of SPWCUC driver. */ +struct spwcuc_priv { + struct drvmgr_dev *dev; + struct spwcuc_regs *regs; + int open; + + spwcuc_isr_t user_isr; + void *user_isr_arg; + + struct spwcuc_stats stats; +}; + +void spwcuc_isr(void *data); + +struct amba_drv_info spwcuc_drv_info; + +/* Hardware Reset of SPWCUC */ +int spwcuc_hw_reset(struct spwcuc_priv *priv) +{ + struct spwcuc_regs *r = priv->regs; + int i = 1000; + + r->control = 1; + + while ((r->control & 1) && i > 0) { + i--; + } + + spwcuc_clear_irqs(priv, -1); + + return i ? 0 : -1; +} + +int spwcuc_reset(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return spwcuc_hw_reset(priv); +} + +void *spwcuc_open(int minor) +{ + struct spwcuc_priv *priv; + struct drvmgr_dev *dev; + + /* Get Device from Minor */ + if ( drvmgr_get_dev(&spwcuc_drv_info.general, minor, &dev) ) { + return NULL; + } + + priv = dev->priv; + if ( (priv == NULL) || priv->open ) + return NULL; + + /* Set initial state of software */ + priv->open = 1; + + /* Clear Statistics */ + spwcuc_clr_stats(priv); + priv->user_isr = NULL; + priv->user_isr_arg = NULL; + + return priv; +} + +void spwcuc_close(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + if ( priv->open == 0 ) + return; + + /* Reset Hardware */ + spwcuc_hw_reset(priv); + + priv->open = 0; +} + +void spwcuc_int_enable(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + /* Register and Enable Interrupt at Interrupt controller */ + drvmgr_interrupt_register(priv->dev, 0, "spwcuc", spwcuc_isr, priv); +} + +void spwcuc_int_disable(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + /* Enable Interrupt at Interrupt controller */ + drvmgr_interrupt_unregister(priv->dev, 0, spwcuc_isr, priv); +} + +void spwcuc_clr_stats(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + memset(&priv->stats, 0, sizeof(priv->stats)); +} + +void spwcuc_get_stats(void *spwcuc, struct spwcuc_stats *stats) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + memcpy(stats, &priv->stats, sizeof(priv->stats)); +} + +/* Configure the spwcuc core */ +void spwcuc_config(void *spwcuc, struct spwcuc_cfg *cfg) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + struct spwcuc_regs *r = priv->regs; + + r->config = (cfg->sel_out & 0x1f) << 28 | + (cfg->sel_in & 0x1f) << 24 | + (cfg->mapping & 0x1f) << 16 | + (cfg->tolerance & 0x1f) << 8 | + (cfg->tid & 0x7) << 4 | + (cfg->ctf & 1) << 1 | + (cfg->cp & 1); + + r->control = (cfg->txen & 1) << 1 | + (cfg->rxen & 1) << 2 | + (cfg->pktsyncen & 1) << 3 | + (cfg->pktiniten & 1) << 4 | + (cfg->pktrxen & 1) << 5; + + r->dla = (cfg->dla_mask & 0xff)<<8 | (cfg->dla & 0xff); + + r->pid = cfg->pid; + + r->offset = cfg->offset; +} + +/* Return elapsed coarse time */ +unsigned int spwcuc_get_et_coarse(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return priv->regs->etct; +} + +/* Return elapsed fine time */ +unsigned int spwcuc_get_et_fine(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return (priv->regs->etft & 0xffffff) >> 8; +} + +/* Return elapsed time (coarse and fine) */ +unsigned long long spwcuc_get_et(void *spwcuc) +{ + return (((unsigned long long)spwcuc_get_et_coarse(spwcuc)) << 24) | spwcuc_get_et_fine(spwcuc); +} + +/* Return next elapsed coarse time (for use when sending SpW time packet) */ +unsigned int spwcuc_get_next_et_coarse(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return priv->regs->etct_next; +} + +/* Return next elapsed fine time (for use when sending SpW time packet) */ +unsigned int spwcuc_get_next_et_fine(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return (priv->regs->etft_next & 0xffffff) >> 8; +} + +/* Return next elapsed time (for use when sending SpW time packet) */ +unsigned long long spwcuc_get_next_et(void *spwcuc) +{ + return (((unsigned long long)spwcuc_get_next_et_coarse(spwcuc)) << 24) | spwcuc_get_next_et_fine(spwcuc); +} + +/* Force/Set the elapsed time (coarse 32-bit and fine 24-bit) by writing the + * T-Field Time Packet Registers then the FORCE, NEW and INIT bits. + * The latter three are needed for the ET to be set with the new value. + */ +void spwcuc_force_et(void *spwcuc, unsigned long long time) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + struct spwcuc_regs *regs = priv->regs; + + regs->etft_next = (time & 0xffffff) << 8; + regs->etct_next = (time >> 24) & 0xffffffff; + regs->pkt_pf_crc = (1 << 29) | (1 << 30) | (1 << 31); +} + +/* Return received (from time packet) elapsed coarse time */ +unsigned int spwcuc_get_tp_et_coarse(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return priv->regs->pkt_ct; +} + +/* Return received (from time packet) elapsed fine time */ +unsigned int spwcuc_get_tp_et_fine(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return (priv->regs->pkt_ft & 0xffffff) >> 8; +} + +/* Return received (from time packet) elapsed time (coarse and fine) */ +unsigned long long spwcuc_get_tp_et(void *spwcuc) +{ + return (((unsigned long long)spwcuc_get_tp_et_coarse(spwcuc)) << 24) | spwcuc_get_tp_et_fine(spwcuc); +} + +/* Clear interrupts */ +void spwcuc_clear_irqs(void *spwcuc, int irqs) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + priv->regs->picr = irqs; +} + +/* Enable interrupts */ +void spwcuc_enable_irqs(void *spwcuc, int irqs) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + priv->regs->imr = irqs; +} + +struct spwcuc_regs *spwcuc_get_regs(void *spwcuc) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + return priv->regs; +} + +void spwcuc_int_register(void *spwcuc, spwcuc_isr_t func, void *data) +{ + struct spwcuc_priv *priv = (struct spwcuc_priv *)spwcuc; + + priv->user_isr = func; + priv->user_isr_arg = data; +} + +void spwcuc_isr(void *data) +{ + struct spwcuc_priv *priv = data; + struct spwcuc_stats *stats = &priv->stats; + unsigned int pimr = priv->regs->pimr; + + stats->nirqs++; + + if (pimr & PKT_INIT_IRQ) + stats->pkt_init++; + if (pimr & PKT_ERR_IRQ) + stats->pkt_err++; + if (pimr & PKT_RX_IRQ) + stats->pkt_rx++; + if (pimr & WRAP_ERR_IRQ) + stats->wraperr++; + if (pimr & WRAP_IRQ) + stats->wrap++; + if (pimr & SYNC_ERR_IRQ) + stats->syncerr++; + if (pimr & SYNC_IRQ) + stats->sync++; + if (pimr & TOL_ERR_IRQ) + stats->tolerr++; + if (pimr & TICK_RX_ERR_IRQ) + stats->tick_rx_error++; + if (pimr & TICK_RX_WRAP_IRQ) + stats->tick_rx_wrap++; + if (pimr & TICK_RX_IRQ) + stats->tick_rx++; + if (pimr & TICK_TX_WRAP_IRQ) + stats->tick_tx_wrap++; + if (pimr & TICK_TX_IRQ) + stats->tick_tx++; + + /* Let user Handle Interrupt */ + if ( priv->user_isr ) + priv->user_isr(pimr, priv->user_isr_arg); +} + +/*** INTERFACE TO DRIVER MANAGER ***/ + +int spwcuc_init2(struct drvmgr_dev *dev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + struct spwcuc_priv *priv; + struct spwcuc_regs *regs; + + priv = (struct spwcuc_priv *)malloc(sizeof(*priv)); + if ( priv == NULL ) + return -1; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + dev->priv = priv; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + regs = (struct spwcuc_regs *)pnpinfo->apb_slv->start; + + priv->regs = regs; + + spwcuc_hw_reset(priv); + + return 0; +} + +struct drvmgr_drv_ops spwcuc_ops = +{ + {NULL, spwcuc_init2, NULL, NULL}, + NULL, + NULL +}; + +struct amba_dev_id spwcuc_ids[] = +{ + {VENDOR_GAISLER, GAISLER_SPWCUC}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info spwcuc_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_SPWCUC_ID,/* Driver ID */ + "SPWCUC_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &spwcuc_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &spwcuc_ids[0] +}; + +/* Register the SPWCUC Driver */ +void spwcuc_register(void) +{ + drvmgr_drv_register(&spwcuc_drv_info.general); +} diff --git a/c/src/lib/libbsp/sparc/shared/tmtc/grtc.c b/c/src/lib/libbsp/sparc/shared/tmtc/grtc.c new file mode 100644 index 0000000000..3794e95d24 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/tmtc/grtc.c @@ -0,0 +1,1962 @@ +/* GRTC Telecommand decoder driver + * + * COPYRIGHT (c) 2007. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <malloc.h> +#include <rtems/bspIo.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> +#include <ambapp.h> +#include <grtc.h> + +#ifndef IRQ_GLOBAL_PREPARE + #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level +#endif + +#ifndef IRQ_GLOBAL_DISABLE + #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level) +#endif + +#ifndef IRQ_GLOBAL_ENABLE + #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level) +#endif + +/* +#define DEBUG +#define DEBUGFUNCS +*/ + +#include <debug_defs.h> + +#ifdef DEBUG_ERROR +#define DEBUG_ERR_LOG(device,error) grtc_log_error(device,error) +#else +#define DEBUG_ERR_LOG(device,error) +#endif + +/* GRTC register map */ +struct grtc_regs { + volatile unsigned int grst; /* Global Reset Register (GRR 0x00) */ + volatile unsigned int gctrl; /* Global Control Register (GCR 0x04) */ + int unused0; + volatile unsigned int sir; /* Spacecraft Identifier Register (SIR 0x0c) */ + volatile unsigned int far; /* Frame Acceptance Report Register (FAR 0x10) */ + + volatile unsigned int clcw1; /* CLCW Register 1 (CLCWR1 0x14) */ + volatile unsigned int clcw2; /* CLCW Register 2 (CLCWR2 0x18) */ + volatile unsigned int phir; /* Physical Interface Register (PHIR 0x1c) */ + volatile unsigned int cor; /* Control Register (COR 0x20) */ + + volatile unsigned int str; /* Status Register (STR 0x24) */ + volatile unsigned int asr; /* Address Space Register (ASR 0x28) */ + volatile unsigned int rp; /* Receive Read Pointer Register (RRP 0x2c) */ + volatile unsigned int wp; /* Receive Write Pointer Register (RWP 0x30) */ + + int unused1[(0x60-0x34)/4]; + + volatile unsigned int pimsr; /* Pending Interrupt Masked Status Register (PIMSR 0x60) */ + volatile unsigned int pimr; /* Pending Interrupt Masked Register (PIMR 0x64) */ + volatile unsigned int pisr; /* Pending Interrupt Status Register (PISR 0x68) */ + volatile unsigned int pir; /* Pending Interrupt Register (PIR 0x6c) */ + volatile unsigned int imr; /* Interrupt Mask Register (IMR 0x70) */ + volatile unsigned int picr; /* Pending Interrupt Clear Register (PICR 0x74) */ +}; + +/* Security Byte */ +#define GRTC_SEB 0x55000000 + +/* Global Reset Register (GRR 0x00) */ +#define GRTC_GRR_SRST 0x1 +#define GRTC_GRR_SRST_BIT 0 + +/* Global Control Register (GCR 0x04) */ +#define GRTC_GCR_PSR_BIT 10 +#define GRTC_GCR_NRZM_BIT 11 +#define GRTC_GCR_PSS_BIT 12 + +#define GRTC_GCR_PSR (1<<GRTC_GCR_PSR_BIT) +#define GRTC_GCR_NRZM (1<<GRTC_GCR_NRZM_BIT) +#define GRTC_GCR_PSS (1<<GRTC_GCR_PSS_BIT) + +/* Spacecraft Identifier Register (SIR 0x0c) */ + + +/* Frame Acceptance Report Register (FAR 0x10) */ +#define GRTC_FAR_SCI_BIT 10 +#define GRTC_FAR_CSEC_BIT 11 +#define GRTC_FAR_CAC_BIT 12 +#define GRTC_FAR_SSD_BIT 13 + +#define GRTC_FAR_SCI (0x7<<GRTC_FAR_SCI_BIT) +#define GRTC_FAR_CSEC (0x7<<GRTC_FAR_CSEC_BIT) +#define GRTC_FAR_CAC (0x3f<<GRTC_FAR_CAC_BIT) +#define GRTC_FAR_SSD (1<<GRTC_FAR_SSD_BIT) + +/* CLCW Register 1 (CLCWR1 0x14) */ +/* CLCW Register 2 (CLCWR2 0x18) */ +#define GRTC_CLCW_RVAL_BIT 0 +#define GRTC_CLCW_RTYPE_BIT 8 +#define GRTC_CLCW_FBCO_BIT 9 +#define GRTC_CLCW_RTMI_BIT 11 +#define GRTC_CLCW_WAIT_BIT 12 +#define GRTC_CLCW_LOUT_BIT 13 +#define GRTC_CLCW_NBLO_BIT 14 +#define GRTC_CLCW_NRFA_BIT 15 +#define GRTC_CLCW_VCI_BIT 18 +#define GRTC_CLCW_CIE_BIT 24 +#define GRTC_CLCW_STAF_BIT 26 +#define GRTC_CLCW_VNUM_BIT 29 +#define GRTC_CLCW_CWTY_BIT 31 + +#define GRTC_CLCW_RVAL (0xff<<GRTC_CLCW_RVAL_BIT) +#define GRTC_CLCW_RTYPE (1<<GRTC_CLCW_RTYPE_BIT) +#define GRTC_CLCW_FBCO (0x3<<GRTC_CLCW_FBCO_BIT) +#define GRTC_CLCW_RTMI (0x3<<GRTC_CLCW_RTMI_BIT) +#define GRTC_CLCW_WAIT (1<<GRTC_CLCW_WAIT_BIT) +#define GRTC_CLCW_LOUT (1<<GRTC_CLCW_LOUT_BIT) +#define GRTC_CLCW_NBLO (1<<GRTC_CLCW_NBLO_BIT) +#define GRTC_CLCW_NRFA (1<<GRTC_CLCW_NRFA_BIT) +#define GRTC_CLCW_VCI (0x3f<<GRTC_CLCW_VCI_BIT) +#define GRTC_CLCW_CIE (0x3<<GRTC_CLCW_CIE_BIT) +#define GRTC_CLCW_STAF (0x3<<GRTC_CLCW_STAF_BIT) +#define GRTC_CLCW_VNUM (0x3<<GRTC_CLCW_VNUM_BIT) +#define GRTC_CLCW_CWTY (1<<GRTC_CLCW_CWTY_BIT) + +/* Physical Interface Register (PIR 0x1c) */ +#define GRTC_PIR_BLO_BIT 0 +#define GRTC_PIR_RFA_BIT 8 + +#define GRTC_PIR_BLO (0xff<<GRTC_PIR_BLO_BIT) +#define GRTC_PIR_RFA (0xff<<GRTC_PIR_RFA_BIT) + +/* Control Register (COR 0x20) */ +#define GRTC_COR_RE_BIT 0 +#define GRTC_COR_CRST_BIT 9 + +#define GRTC_COR_RE (1<<GRTC_COR_RE_BIT) +#define GRTC_COR_CRST (1<<GRTC_COR_CRST_BIT) + +/* Status Register (STR 0x24) */ +#define GRTC_STR_CR_BIT 0 +#define GRTC_STR_OV_BIT 4 +#define GRTC_STR_RFF_BIT 7 +#define GRTC_STR_RBF_BIT 10 + +#define GRTC_STR_CR (1<<GRTC_STR_CR_BIT) +#define GRTC_STR_OV (1<<GRTC_STR_OV_BIT) +#define GRTC_STR_RFF (1<<GRTC_STR_RFF_BIT) +#define GRTC_STR_RBF (1<<GRTC_STR_RBF_BIT) + +/* Address Space Register (ASR 0x28) */ +#define GRTC_ASR_RXLEN_BIT 0 +#define GRTC_ASR_BUFST_BIT 10 + +#define GRTC_ASR_RXLEN (0xff<<GRTC_ASR_RXLEN_BIT) +#define GRTC_ASR_BUFST (0x3fffff<<GRTC_ASR_BUFST_BIT) + +/* Receive Read Pointer Register (RRP 0x2c) */ +#define GRTC_RRP_PTR_BIT 0 + +#define GRTC_RRP_PTR (0xffffff<<GRTC_RRP_PTR_BIT) + +/* Receive Write Pointer Register (RWP 0x30) */ +#define GRTC_RWP_PTR_BIT 0 + +#define GRTC_RWP_PTR (0xffffff<<GRTC_RWP_PTR_BIT) + +/* Pending Interrupt Masked Status Register (PIMSR 0x60) */ +/* Pending Interrupt Masked Register (PIMR 0x64) */ +/* Pending Interrupt Status Register (PISR 0x68) */ +/* Pending Interrupt Register (PIR 0x6c) */ +/* Interrupt Mask Register (IMR 0x70) */ +/* Pending Interrupt Clear Register (PICR 0x74) */ +#define GRTC_INT_RFA_BIT 0 +#define GRTC_INT_BLO_BIT 1 +#define GRTC_INT_FAR_BIT 2 +#define GRTC_INT_CR_BIT 3 +#define GRTC_INT_RBF_BIT 4 +#define GRTC_INT_OV_BIT 5 +#define GRTC_INT_CS_BIT 6 + +#define GRTC_INT_RFA (1<<GRTC_INT_RFA_BIT) +#define GRTC_INT_BLO (1<<GRTC_INT_BLO_BIT) +#define GRTC_INT_FAR (1<<GRTC_INT_FAR_BIT) +#define GRTC_INT_CR (1<<GRTC_INT_CR_BIT) +#define GRTC_INT_OV (1<<GRTC_INT_OV_BIT) +#define GRTC_INT_CS (1<<GRTC_INT_CS_BIT) + +#define GRTC_INT_ALL (GRTC_INT_RFA|GRTC_INT_BLO|GRTC_INT_FAR|GRTC_INT_CR|GRTC_INT_OV|GRTC_INT_CS) + +#define READ_REG(address) (*(volatile unsigned int *)address) + +/* Driver functions */ +static rtems_device_driver grtc_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtc_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtc_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtc_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtc_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtc_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define GRTC_DRIVER_TABLE_ENTRY { grtc_initialize, grtc_open, grtc_close, grtc_read, grtc_write, grtc_ioctl } + +static rtems_driver_address_table grtc_driver = GRTC_DRIVER_TABLE_ENTRY; + +enum { + FRM_STATE_NONE = 0, /* not started */ + FRM_STATE_HDR = 1, /* Reading Header (Frame length isn't known) */ + FRM_STATE_ALLOC = 2, /* Allocate Frame to hold data */ + FRM_STATE_PAYLOAD = 3, /* Reading Payload (Frame length is known) */ + FRM_STATE_FILLER = 4, /* Check filler */ + FRM_STATE_DROP = 5 /* error, drop data until end marker */ +}; + +/* Frame pool, all frames in pool have the same buffer length (frame mode only) */ +struct grtc_frame_pool { + unsigned int frame_len; /* Maximal length of frame (payload+hdr+crc..) */ + unsigned int frame_cnt; /* Current number of frames in pool (in frms) */ + struct grtc_frame *frms; /* Chain of frames in pool (this is the pool) */ +}; + +struct grtc_priv { + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + struct grtc_regs *regs; /* TC Hardware Register MAP */ + int irq; /* IRQ number of TC core */ + + int major; /* Driver major */ + int minor; /* Device Minor */ + + int open; /* Device has been opened by user */ + int running; /* TC receiver running */ + int mode; /* RAW or FRAME mode */ + int overrun_condition; /* Overrun condition */ + int blocking; /* Blocking/polling mode */ + rtems_interval timeout; /* Timeout in blocking mode */ + int wait_for_nbytes;/* Number of bytes to wait for in blocking mode */ + + struct grtc_ioc_config config; + +/* RAW MODE ONLY */ + /* Buffer allocation (user provided or driver allocated using malloc) */ + void *buf; + void *buf_remote; + void *_buf; + int buf_custom; /* 0=no custom buffer, 1=custom buffer (don't free it...) */ + unsigned int len; + +/* FRAME MODE ONLY */ + /* Frame management when user provides buffers. */ + int pool_cnt; /* Number of Pools */ + struct grtc_frame_pool *pools; /* Array of pools */ + + struct grtc_list ready; /* Ready queue (received frames) */ + + /* Frame read data (Frame mode only) */ + int frame_state; + int filler; + unsigned char hdr[5] __attribute__((aligned(2))); + struct grtc_frame *frm; /* Frame currently beeing copied */ + int frmlen; + + struct grtc_ioc_stats stats; /* Statistics */ + + rtems_id sem_rx; + +#ifdef DEBUG_ERROR + /* Buffer read/write state */ + unsigned int rp; + unsigned int wp; + + /* Debugging */ + int last_error[128]; + int last_error_cnt; +#endif +}; + +/* Prototypes */ +static void grtc_hw_reset(struct grtc_priv *priv); +static void grtc_interrupt(void *arg); + +/* Common Global Variables */ +static rtems_id grtc_dev_sem; +static int grtc_driver_io_registered = 0; +static rtems_device_major_number grtc_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +static int grtc_register_io(rtems_device_major_number *m); +static int grtc_device_init(struct grtc_priv *pDev); + +static int grtc_init2(struct drvmgr_dev *dev); +static int grtc_init3(struct drvmgr_dev *dev); + +static struct drvmgr_drv_ops grtc_ops = +{ + {NULL, grtc_init2, grtc_init3, NULL}, + NULL, + NULL, +}; + +static struct amba_dev_id grtc_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRTC}, + {0, 0} /* Mark end of table */ +}; + +static struct amba_drv_info grtc_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRTC_ID, /* Driver ID */ + "GRTC_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grtc_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct grtc_priv), + }, + &grtc_ids[0] +}; + +void grtc_register_drv (void) +{ + DBG("Registering GRTC driver\n"); + drvmgr_drv_register(&grtc_drv_info.general); +} + +static int grtc_init2(struct drvmgr_dev *dev) +{ + struct grtc_priv *priv; + + DBG("GRTC[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv; + if ( !priv ) + return DRVMGR_NOMEM; + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + +static int grtc_init3(struct drvmgr_dev *dev) +{ + struct grtc_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( grtc_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( grtc_register_io(&grtc_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + grtc_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + if ( grtc_device_init(priv) ) { + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/grtc%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sgrtc%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, grtc_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static int grtc_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &grtc_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("GRTC driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("GRTC rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("GRTC rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("GRTC rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("GRTC rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +static int grtc_device_init(struct grtc_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)pDev->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + pDev->irq = pnpinfo->irq; + pDev->regs = (struct grtc_regs *)pnpinfo->ahb_slv->start[0]; + pDev->minor = pDev->dev->minor_drv; + pDev->open = 0; + pDev->running = 0; + + /* Create Binary RX Semaphore with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'R', 'C', '0' + pDev->minor), + 0, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ + RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->sem_rx) != RTEMS_SUCCESSFUL ) { + return -1; + } + + /* Reset Hardware before attaching IRQ handler */ + grtc_hw_reset(pDev); + + return 0; +} + +static void grtc_hw_reset(struct grtc_priv *priv) +{ + /* Reset Core */ + priv->regs->grst = GRTC_SEB | GRTC_GRR_SRST; +} + +static void grtc_hw_get_defaults(struct grtc_priv *pDev, struct grtc_ioc_config *config) +{ + unsigned int gcr = READ_REG(&pDev->regs->gctrl); + + config->psr_enable = (gcr & GRTC_GCR_PSR) ? 1:0; + config->nrzm_enable = (gcr & GRTC_GCR_NRZM) ? 1:0; + config->pss_enable = (gcr & GRTC_GCR_PSS) ? 1:0; + + config->crc_calc = 0; +} + +/* bufsize is given in bytes */ +static int __inline__ grtc_hw_data_avail_upper(unsigned int rrp, unsigned rwp, unsigned int bufsize) +{ + if ( rrp == rwp ) + return 0; + + if ( rwp > rrp ) { + return rwp-rrp; + } + + return (bufsize-rrp); +} + +/* bufsize is given in bytes */ +static int __inline__ grtc_hw_data_avail_lower(unsigned int rrp, unsigned rwp, unsigned int bufsize) +{ + if ( rrp == rwp ) + return 0; + + if ( rwp > rrp ) { + return 0; + } + + return rwp; +} + +/* bufsize is given in bytes */ +static int __inline__ grtc_hw_data_avail(unsigned int rrp, unsigned rwp, unsigned int bufsize) +{ + if ( rrp == rwp ) + return 0; + + if ( rwp > rrp ) { + return rwp-rrp; + } + + return rwp+(bufsize-rrp); +} + +/* Reads as much as possiböe but not more than 'max' bytes from the TC receive buffer. + * Number of bytes put into 'buf' is returned. + */ +static int grtc_hw_read_try(struct grtc_priv *pDev, char *buf, int max) +{ + struct grtc_regs *regs = pDev->regs; + unsigned int rp, wp, asr, bufmax, rrp, rwp; + unsigned int upper, lower; + unsigned int count, cnt, left; + + FUNCDBG(); + + if ( max < 1 ) + return 0; + + rp = READ_REG(®s->rp); + asr = READ_REG(®s->asr); + bufmax = (asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + bufmax = (bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + wp = READ_REG(®s->wp); + + /* Relative rp and wp */ + rrp = rp - (asr & GRTC_ASR_BUFST); + rwp = wp - (asr & GRTC_ASR_BUFST); + + lower = grtc_hw_data_avail_lower(rrp,rwp,bufmax); + upper = grtc_hw_data_avail_upper(rrp,rwp,bufmax); + + DBG("grtc_hw_read_try: AVAIL: Lower: %d, Upper: %d\n",lower,upper); + DBG("grtc_hw_read_try: rp: 0x%x, rrp: 0x%x, wp: 0x%x, rwp: 0x%x, bufmax: %d\n, start: 0x%x\n", + rp,rrp,wp,rwp,bufmax,pDev->buffer); + + if ( (upper+lower) == 0 ) + return 0; + + /* Count bytes will be read */ + count = (upper+lower) > max ? max : (upper+lower); + left = count; + + /* Read from upper part of data buffer */ + if ( upper > 0 ){ + if ( left < upper ){ + cnt = left; + }else{ + cnt = upper; /* Read all upper data available */ + } + DBG("grtc_hw_read_try: COPYING %d from upper\n",cnt); + /* Convert from Remote address (RP) into CPU Local address */ + memcpy(buf, (void *)((rp - (unsigned int)pDev->buf_remote) + (unsigned int)pDev->buf), cnt); + buf += cnt; + left -= cnt; + } + + /* Read from lower part of data buffer */ + if ( left > 0 ){ + if ( left < lower ){ + cnt = left; + }else{ + cnt = lower; /* Read all lower data available */ + } + DBG("grtc_hw_read_try: COPYING %d from lower\n",cnt); + memcpy(buf, (void *)pDev->buf, cnt); + buf += cnt; + left -= cnt; + } + + /* Update hardware RP pointer to tell hardware about new space available */ + if ( (rp+count) >= ((asr&GRTC_ASR_BUFST)+bufmax) ){ + regs->rp = (rp+count-bufmax); + } else { + regs->rp = rp+count; + } + + return count; +} + +/* Reads as much as possiböe but not more than 'max' bytes from the TC receive buffer. + * Number of bytes put into 'buf' is returned. + */ +static int grtc_data_avail(struct grtc_priv *pDev) +{ + unsigned int rp, wp, asr, bufmax, rrp, rwp; + struct grtc_regs *regs = pDev->regs; + + FUNCDBG(); + + rp = READ_REG(®s->rp); + asr = READ_REG(®s->asr); + bufmax = (asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + bufmax = (bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + wp = READ_REG(®s->wp); + + /* Relative rp and wp */ + rrp = rp - (asr & GRTC_ASR_BUFST); + rwp = wp - (asr & GRTC_ASR_BUFST); + + return grtc_hw_data_avail(rrp,rwp,bufmax); +} + +static void *grtc_memalign(unsigned int boundary, unsigned int length, void *realbuf) +{ + *(int *)realbuf = (int)malloc(length+(~GRTC_ASR_BUFST)+1); + DBG("GRTC: Alloced %d (0x%x) bytes, requested: %d\n",length+(~GRTC_ASR_BUFST)+1,length+(~GRTC_ASR_BUFST)+1,length); + return (void *)(((*(unsigned int *)realbuf)+(~GRTC_ASR_BUFST)+1) & ~(boundary-1)); +} + +static int grtc_start(struct grtc_priv *pDev) +{ + struct grtc_regs *regs = pDev->regs; + unsigned int tmp; + + if ( !pDev->buf || (((unsigned int)pDev->buf & ~GRTC_ASR_BUFST) != 0) || + (pDev->len>(1024*0x100)) || (pDev->len<1024) || ((pDev->len & (1024-1)) != 0) + ) { + DBG("GRTC: start: buffer not properly allocated(0x%x,0x%x,0x%x,0x%x)\n",pDev->buf,pDev->len,((unsigned int)pDev->buf & ~GRTC_ASR_BUFST),(pDev->len & ~(1024-1))); + return RTEMS_NO_MEMORY; + } + + memset(pDev->buf,0,pDev->len); + + /* Software init */ + pDev->overrun_condition = 0; +#ifdef DEBUG_ERROR + pDev->last_error_cnt = 0; + memset(&pDev->last_error[0],0,128*sizeof(int)); +#endif + memset(&pDev->stats,0,sizeof(struct grtc_ioc_stats)); + + /* Reset the receiver */ + regs->cor = GRTC_SEB | GRTC_COR_CRST; + if ( READ_REG(®s->cor) & GRTC_COR_CRST ){ + /* Reset Failed */ + DBG("GRTC: start: Reseting receiver failed\n"); + return RTEMS_IO_ERROR; + } + + /* Set operating modes */ + tmp = 0; + if ( pDev->config.psr_enable ) + tmp |= GRTC_GCR_PSR; + if ( pDev->config.nrzm_enable ) + tmp |= GRTC_GCR_NRZM; + if ( pDev->config.pss_enable ) + tmp |= GRTC_GCR_PSS; + regs->gctrl = GRTC_SEB | tmp; + + /* Clear any pending interrupt */ + tmp = READ_REG(®s->pir); + regs->picr = GRTC_INT_ALL; + + /* Unmask only the Overrun interrupt */ + regs->imr = GRTC_INT_OV; + + /* Set up DMA registers + * 1. Let hardware know about our DMA area (size and location) + * 2. Set DMA read/write posistions to zero. + */ + regs->asr = (unsigned int)pDev->buf_remote | ((pDev->len>>10)-1); + regs->rp = (unsigned int)pDev->buf_remote; + + /* Mark running before enabling the receiver, we could receive + * an interrupt directly after enabling the receiver and it would + * then interpret the interrupt as spurious (see interrupt handler) + */ + pDev->running = 1; + + /* Enable receiver */ + regs->cor = GRTC_SEB | GRTC_COR_RE; + + DBG("GRTC: STARTED\n"); + + return 0; +} + +static void grtc_stop(struct grtc_priv *pDev) +{ + struct grtc_regs *regs = pDev->regs; + unsigned int tmp; + + /* Disable the receiver */ + regs->cor = GRTC_SEB; + + /* disable all interrupts and clear them */ + regs->imr = 0; + tmp = READ_REG(®s->pir); + regs->picr = GRTC_INT_ALL; + + DBG("GRTC: STOPPED\n"); + + /* Flush semaphores in case a thread is stuck waiting for CLTUs (RX data) */ + rtems_semaphore_flush(pDev->sem_rx); +} + +/* Wait until 'count' bytes are available in receive buffer, or until + * the timeout expires. + */ +static int grtc_wait_data(struct grtc_priv *pDev, int count, rtems_interval timeout) +{ + int avail; + int ret; + IRQ_GLOBAL_PREPARE(oldLevel); + + FUNCDBG(); + + if ( count < 1 ) + return 0; + + IRQ_GLOBAL_DISABLE(oldLevel); + + /* Enable interrupts when receiving CLTUs, Also clear old pending CLTUs store + * interrupts. + */ + pDev->regs->picr = GRTC_INT_CS; + pDev->regs->imr = READ_REG(&pDev->regs->imr) | GRTC_INT_CS; + + avail = grtc_data_avail(pDev); + if ( avail < count ) { + /* Wait for interrupt. */ + + IRQ_GLOBAL_ENABLE(oldLevel); + + if ( timeout == 0 ){ + timeout = RTEMS_NO_TIMEOUT; + } + ret = rtems_semaphore_obtain(pDev->sem_rx,RTEMS_WAIT,timeout); + /* RTEMS_SUCCESSFUL = interrupt signaled data is available + * RTEMS_TIMEOUT = timeout expired, probably not enough data available + * RTEMS_UNSATISFIED = driver has been closed or an error (overrun) occured + * which should cancel this operation. + * RTEMS_OBJECT_WAS_DELETED, RTEMS_INVALID_ID = driver error. + */ + IRQ_GLOBAL_DISABLE(oldLevel); + }else{ + ret = RTEMS_SUCCESSFUL; + } + + /* Disable interrupts when receiving CLTUs */ + pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRTC_INT_CS; + + IRQ_GLOBAL_ENABLE(oldLevel); + + return ret; +} + +static rtems_device_driver grtc_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + struct grtc_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grtc_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grtc_priv *)dev->priv; + + /* Wait until we get semaphore */ + if ( rtems_semaphore_obtain(grtc_dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL ){ + return RTEMS_INTERNAL_ERROR; + } + + /* Is device in use? */ + if ( pDev->open ){ + rtems_semaphore_release(grtc_dev_sem); + return RTEMS_RESOURCE_IN_USE; + } + + /* Mark device taken */ + pDev->open = 1; + + rtems_semaphore_release(grtc_dev_sem); + + DBG("grtc_open: OPENED minor %d (pDev: 0x%x)\n",pDev->minor,(unsigned int)pDev); + + /* Set defaults */ + pDev->buf = NULL; + pDev->_buf = NULL; + pDev->buf_custom = 0; + pDev->buf_remote = 0; + pDev->len = 0; + pDev->timeout = 0; /* no timeout */ + pDev->blocking = 0; /* polling mode */ + pDev->mode = GRTC_MODE_RAW; /* Always default to Raw mode */ + pDev->ready.head = NULL; + pDev->ready.tail = NULL; + pDev->ready.cnt = 0; + + pDev->running = 0; + + memset(&pDev->config,0,sizeof(pDev->config)); + + /* The core has been reset when we execute here, so it is possible + * to read out defualts from core. + */ + grtc_hw_get_defaults(pDev,&pDev->config); + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grtc_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grtc_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grtc_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grtc_priv *)dev->priv; + + if ( pDev->running ){ + grtc_stop(pDev); + pDev->running = 0; + } + + /* Reset core */ + grtc_hw_reset(pDev); + + /* Mark not open */ + pDev->open = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grtc_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grtc_priv *pDev; + struct drvmgr_dev *dev; + int count; + int left; + int timedout; + int err; + rtems_interval timeout; + rtems_libio_rw_args_t *rw_args; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grtc_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grtc_priv *)dev->priv; + + if ( !pDev->running && !pDev->overrun_condition ) { + return RTEMS_RESOURCE_IN_USE; + } + + if ( pDev->mode != GRTC_MODE_RAW ) { + return RTEMS_NOT_DEFINED; + } + + rw_args = (rtems_libio_rw_args_t *) arg; + left = rw_args->count; + timedout = 0; + timeout = pDev->timeout; + +read_from_buffer: + /* Read maximally rw_args->count bytes from receive buffer */ + count = grtc_hw_read_try(pDev,rw_args->buffer,left); + + left -= count; + + DBG("READ %d bytes from DMA, left: %d\n",count,left); + + if ( !timedout && !pDev->overrun_condition && ((count < 1) || ((count < rw_args->count) && (pDev->blocking == GRTC_BLKMODE_COMPLETE))) ){ + /* didn't read anything (no data available) or we want to wait for all bytes requested. + * + * Wait for data to arrive only in blocking mode + */ + if ( pDev->blocking ) { + if ( (err=grtc_wait_data(pDev,left,timeout)) != RTEMS_SUCCESSFUL ){ + /* Some kind of error, closed, overrun etc. */ + if ( err == RTEMS_TIMEOUT ){ + /* Got a timeout, we try to read as much as possible */ + timedout = 1; + goto read_from_buffer; + } + return err; + } + goto read_from_buffer; + } + /* Non-blocking mode and no data read. */ + return RTEMS_TIMEOUT; + } + + /* Tell caller how much was read. */ + + DBG("READ returning %d bytes, left: %d\n",rw_args->count-left,left); + + rw_args->bytes_moved = rw_args->count - left; + if ( rw_args->bytes_moved == 0 ){ + return RTEMS_TIMEOUT; + } + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grtc_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + FUNCDBG(); + return RTEMS_NOT_IMPLEMENTED; +} + +static int grtc_pool_add_frms(struct grtc_frame *frms) +{ + struct grtc_frame *frm, *next; + + /* Add frames to pools */ + frm = frms; + while(frm){ + + if ( !frm->pool ) { + /* */ + DBG("GRTC: Frame not assigned to a pool\n"); + return -1; + } + next = frm->next; /* Remember next frame to process */ + + DBG("GRTC: adding frame 0x%x to pool %d (%d)\n",frm,frm->pool->frame_len,frm->pool->frame_cnt); + + /* Insert Frame into pool */ + frm->next = frm->pool->frms; + frm->pool->frms = frm; + frm->pool->frame_cnt++; + + frm = next; + } + + return 0; +} + +static struct grtc_frame *grtc_pool_get_frm(struct grtc_priv *pDev, int frame_len, int *error) +{ + struct grtc_frame *frm; + struct grtc_frame_pool *pool; + int i; + + /* Loop through all pools until a pool is found + * with a matching (or larger) frame length + */ + pool = pDev->pools; + for (i=0; i<pDev->pool_cnt; i++,pool++) { + if ( pool->frame_len >= frame_len ) { + /* Found a good pool ==> get frame */ + frm = pool->frms; + if ( !frm ) { + /* not enough frames available for this + * frame length, we try next + * + * If this is a severe error add your handling + * code here. + */ +#if 0 + if ( error ) + *error = 0; + return 0; +#endif + continue; + } + + /* Got a frame, the frame is taken out of the + * pool for usage. + */ + pool->frms = frm->next; + pool->frame_cnt--; + return frm; + } + } + + if ( error ) + *error = 1; + + /* Didn't find any frames */ + return NULL; +} + +/* Return number of bytes processed, Stops at the first occurance + * of the pattern given in 'pattern' + */ +static int grtc_scan(unsigned short *src, int max, unsigned char pattern, int *found) +{ + unsigned short tmp = 0; + unsigned int left = max; + + while ( (left>1) && (((tmp=*src) & 0x00ff) != pattern) ) { + src++; + left-=2; + } + if ( (tmp & 0xff) == pattern ) { + *found = 1; + } else { + *found = 0; + } + return max-left; +} + +static int grtc_copy(unsigned short *src, unsigned char *buf, int cnt) +{ + unsigned short tmp; + int left = cnt; + + while ( (left>0) && ((((tmp=*src) & 0x00ff) == 0x00) || ((tmp & 0x00ff) == 0x01)) ) { + *buf++ = tmp>>8; + src++; + left--; + } + + return cnt-left; +} + + +static int grtc_hw_find_frm(struct grtc_priv *pDev) +{ + struct grtc_regs *regs = pDev->regs; + unsigned int rp, wp, asr, bufmax, rrp, rwp; + unsigned int upper, lower; + unsigned int count, cnt; + int found; + + FUNCDBG(); + + rp = READ_REG(®s->rp); + asr = READ_REG(®s->asr); + wp = READ_REG(®s->wp); + + /* Quick Check for most common case where Start of frame is at next + * data byte. + */ + if ( rp != wp ) { + /* At least 1 byte in buffer */ + if ( ((*(unsigned short *)((rp - (unsigned int)pDev->buf_remote) + (unsigned int)pDev->buf)) & 0x00ff) == 0x01 ) { + return 0; + } + } + + bufmax = (asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + bufmax = (bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + + /* Relative rp and wp */ + rrp = rp - (asr & GRTC_ASR_BUFST); + rwp = wp - (asr & GRTC_ASR_BUFST); + + lower = grtc_hw_data_avail_lower(rrp,rwp,bufmax); + upper = grtc_hw_data_avail_upper(rrp,rwp,bufmax); + + DBG("grtc_hw_find_frm: AVAIL: Lower: %d, Upper: %d\n",lower,upper); + DBG("grtc_hw_find_frm: rp: 0x%x, rrp: 0x%x, wp: 0x%x, rwp: 0x%x, bufmax: %d\n, start: 0x%x\n", + rp,rrp,wp,rwp,bufmax,pDev->buf_remote); + + if ( (upper+lower) == 0 ) + return 1; + + /* Count bytes will be read */ + count = 0; + found = 0; + + /* Read from upper part of data buffer */ + if ( upper > 0 ){ + cnt = grtc_scan((unsigned short *)((rp - (unsigned int)pDev->buf_remote) + (unsigned int)pDev->buf), upper, 0x01, &found); + count = cnt; + if ( found ) { + DBG("grtc_hw_find_frm: SCANNED upper %d bytes until found\n",cnt); + goto out; + } + + DBG("grtc_hw_find_frm: SCANNED all upper %d bytes, not found\n",cnt); + } + + /* Read from lower part of data buffer */ + if ( lower > 0 ){ + cnt = grtc_scan((unsigned short *)pDev->buf, lower, 0x01, &found); + count += cnt; + + if ( found ) { + DBG("grtc_hw_find_frm: SCANNED lower %d bytes until found\n",cnt); + goto out; + } + + DBG("grtc_hw_find_frm: SCANNED all lower %d bytes, not found\n",cnt); + } + +out: + /* Update hardware RP pointer to tell hardware about new space available */ + if ( count > 0 ) { + if ( (rp+count) >= ((asr&GRTC_ASR_BUFST)+bufmax) ){ + regs->rp = (rp+count-bufmax); + } else { + regs->rp = rp+count; + } + } + if ( found ) + return 0; + return 1; + +} + +static int grtc_check_ending(unsigned short *src, int max, int end) +{ + while ( max > 0 ) { + /* Check Filler */ + if ( *src != 0x5500 ) { + /* Filler is wrong */ + return -1; + } + src++; + max-=2; + } + + /* Check ending (at least */ + if ( end ) { + if ( (*src & 0x00ff) != 0x02 ) { + return -1; + } + } + + return 0; +} + +static int grtc_hw_check_ending(struct grtc_priv *pDev, int max) +{ + struct grtc_regs *regs = pDev->regs; + unsigned int rp, wp, asr, bufmax, rrp, rwp; + unsigned int upper, lower; + unsigned int count, cnt, left; + int tot; + + FUNCDBG(); + + if ( max < 1 ) + return 0; + max = max*2; + max += 2; /* Check ending also (2 byte extra) */ + + rp = READ_REG(®s->rp); + asr = READ_REG(®s->asr); + bufmax = (asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + bufmax = (bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + wp = READ_REG(®s->wp); + + /* Relative rp and wp */ + rrp = rp - (asr & GRTC_ASR_BUFST); + rwp = wp - (asr & GRTC_ASR_BUFST); + + lower = grtc_hw_data_avail_lower(rrp,rwp,bufmax); + upper = grtc_hw_data_avail_upper(rrp,rwp,bufmax); + + DBG("grtc_hw_check_ending: AVAIL: Lower: %d, Upper: %d\n",lower,upper); + DBG("grtc_hw_check_ending: rp: 0x%x, rrp: 0x%x, wp: 0x%x, rwp: 0x%x, bufmax: %d\n, start: 0x%x\n", + rp,rrp,wp,rwp,bufmax,pDev->buf_remote); + + if ( (upper+lower) < max ) + return 0; + + /* Count bytes will be read */ + count = max; + left = count; + tot = 0; + + /* Read from upper part of data buffer */ + if ( upper > 0 ){ + if ( left <= upper ){ + cnt = left; + if ( grtc_check_ending((unsigned short *)((rp-(unsigned int)pDev->buf_remote)+(unsigned int)pDev->buf), cnt-2, 1) ) { + return -1; + } + }else{ + cnt = upper; /* Read all upper data available */ + if ( grtc_check_ending((unsigned short *)((rp-(unsigned int)pDev->buf_remote)+(unsigned int)pDev->buf), cnt, 0) ) { + return -1; + } + } + left -= cnt; + } + + /* Read from lower part of data buffer */ + if ( left > 0 ){ + cnt = left; + if ( grtc_check_ending((unsigned short *)pDev->buf, cnt-2, 1) ) { + return -1; + } + left -= cnt; + } + + /* Update hardware RP pointer to tell hardware about new space available */ + if ( (rp+count) >= ((asr&GRTC_ASR_BUFST)+bufmax) ){ + regs->rp = (rp+count-bufmax); + } else { + regs->rp = rp+count; + } + + return 0; +} + +/* Copies Data from DMA area to buf, the control bytes are stripped. For + * every data byte, in the DMA area, one control byte is stripped. + */ +static int grtc_hw_copy(struct grtc_priv *pDev, unsigned char *buf, int max, int partial) +{ + struct grtc_regs *regs = pDev->regs; + unsigned int rp, wp, asr, bufmax, rrp, rwp; + unsigned int upper, lower; + unsigned int count, cnt, left; + int ret, tot, tmp; + + FUNCDBG(); + + if ( max < 1 ) + return 0; + + rp = READ_REG(®s->rp); + asr = READ_REG(®s->asr); + bufmax = (asr & GRTC_ASR_RXLEN) >> GRTC_ASR_RXLEN_BIT; + bufmax = (bufmax+1) << 10; /* Convert from 1kbyte blocks into bytes */ + wp = READ_REG(®s->wp); + + /* Relative rp and wp */ + rrp = rp - (asr & GRTC_ASR_BUFST); + rwp = wp - (asr & GRTC_ASR_BUFST); + + lower = grtc_hw_data_avail_lower(rrp,rwp,bufmax) >> 1; + upper = grtc_hw_data_avail_upper(rrp,rwp,bufmax) >> 1; + + DBG("grtc_hw_copy: AVAIL: Lower: %d, Upper: %d\n",lower,upper); + DBG("grtc_hw_copy: rp: 0x%x, rrp: 0x%x, wp: 0x%x, rwp: 0x%x, bufmax: %d\n, start: 0x%x\n", + rp,rrp,wp,rwp,bufmax,pDev->buf_remote); + + if ( (upper+lower) == 0 || (!partial && ((upper+lower)<max) ) ) + return 0; + + /* Count bytes will be read */ + count = (upper+lower) > max ? max : (upper+lower); + left = count; + tot = 0; + + /* Read from upper part of data buffer */ + if ( upper > 0 ){ + if ( left < upper ){ + cnt = left; + }else{ + cnt = upper; /* Read all upper data available */ + } + DBG("grtc_hw_copy: COPYING %d from upper\n",cnt); + if ( (tot=grtc_copy((unsigned short *)((rp-(unsigned int)pDev->buf_remote)+(unsigned int)pDev->buf), buf, cnt)) != cnt ) { + /* Failed to copy due to an receive error */ + DBG("grtc_hw_copy(upper): not all in DMA buffer (%d)\n",tot); + count = tot; + ret = -1; + goto out; + } + buf += cnt; + left -= cnt; + } + + /* Read from lower part of data buffer */ + if ( left > 0 ){ + if ( left < lower ){ + cnt = left; + }else{ + cnt = lower; /* Read all lower data available */ + } + DBG("grtc_hw_copy: COPYING %d from lower\n",cnt); + if ( (tmp=grtc_copy((unsigned short *)pDev->buf, buf, cnt)) != cnt ) { + /* Failed to copy due to an receive error */ + DBG("grtc_hw_copy(lower): not all in DMA buffer (%d)\n",tot); + count = tot+tmp; + ret = -1; + goto out; + } + buf += cnt; + left -= cnt; + } + ret = count; + +out: + count = count*2; + /* Update hardware RP pointer to tell hardware about new space available */ + if ( (rp+count) >= ((asr&GRTC_ASR_BUFST)+bufmax) ){ + regs->rp = (rp+count-bufmax); + } else { + regs->rp = rp+count; + } + + return ret; +} + +#ifdef DEBUG_ERROR +void grtc_log_error(struct grtc_priv *pDev, int err) +{ + /* Stop Receiver */ + *(volatile unsigned int *)&pDev->regs->cor = 0x55000000; + *(volatile unsigned int *)&pDev->regs->cor = 0x55000000; + pDev->last_error[pDev->last_error_cnt] = err; + if ( ++pDev->last_error_cnt > 128 ) + pDev->last_error_cnt = 0; +} +#endif + +/* Read one frame from DMA buffer + * + * Return Values + * Zero - nothing more to process + * 1 - more to process, no free frames + * 2 - more to process, frame received + * negative - more to process, frame dropped + */ +static int process_dma(struct grtc_priv *pDev) +{ + int ret, err; + int left, total_len; + unsigned char *dst; + struct grtc_frame *frm; + + switch( pDev->frame_state ) { + case FRM_STATE_NONE: + DBG2("FRAME_STATE_NONE\n"); + + /* Find Start of next frame by searching for 0x01 */ + ret = grtc_hw_find_frm(pDev); + if ( ret != 0 ) { + /* Frame start not found */ + return 0; + } + + /* Start of frame found, Try to copy header */ + pDev->frm = NULL; + pDev->frame_state = FRM_STATE_HDR; + + case FRM_STATE_HDR: + DBG2("FRAME_STATE_HDR\n"); + + /* Wait for all of header to be in place by setting partial to 0 */ + ret = grtc_hw_copy(pDev,pDev->hdr,5,0); + if ( ret < 0 ) { + /* Error copying header, restart scanning for new frame */ + DEBUG_ERR_LOG(pDev,1); + pDev->stats.err++; + pDev->stats.err_hdr++; + DBG("FRAME_STATE_HDR: copying failed %d\n",ret); + pDev->frame_state = FRM_STATE_NONE; + return -1; + } else if ( ret != 5 ) { + DBG("FRAME_STATE_HDR: no header (%d)\n",ret); + /* Not all bytes available, come back later */ + return 0; + } + + /* The complete header has been copied, parse it */ + pDev->frmlen = ((*(unsigned short *)&pDev->hdr[2]) & 0x3ff)+1; + if ( pDev->frmlen < 5 ) { + /* Error: frame length is not correct */ + pDev->stats.err++; + pDev->stats.err_hdr++; + DBG("FRAME_STATE_HDR: frame length error: %d\n", pDev->frmlen); + pDev->frame_state = FRM_STATE_NONE; + return -1; + } + pDev->frame_state = FRM_STATE_ALLOC; + + case FRM_STATE_ALLOC: + DBG2("FRAME_STATE_ALLOC\n"); + /* Header has been read, allocate a frame to put payload and header into */ + + /* Allocate Frame matching Frame length */ + err = 0; + frm = grtc_pool_get_frm(pDev,pDev->frmlen,&err); + if ( !frm ) { + /* Couldn't find frame */ + DEBUG_ERR_LOG(pDev,2); + pDev->stats.dropped++; + DBG2("No free frames\n"); + if ( err == 0 ){ + /* Frame length exist in pool configuration, but no + * frames are available for that frame length. + */ + DEBUG_ERR_LOG(pDev,3); + pDev->stats.dropped_no_buf++; + return 1; + } else { + /* Frame length of incoming frame is larger than the + * frame length in any of the configured frame pools. + * + * This may be because of an corrupt header. We simply + * scan for the end of frame marker in the DMA buffer + * so we can drop the frame. + */ + DEBUG_ERR_LOG(pDev,4); + pDev->stats.dropped_too_long++; + pDev->frame_state = FRM_STATE_NONE; + return -2; + } + } + frm->len = 5; /* Only header currenlty in frame */ + + /* Copy Frame Header into frame structure */ + *((unsigned char *)&frm->hdr + 0) = pDev->hdr[0]; + *((unsigned char *)&frm->hdr + 1) = pDev->hdr[1]; + *((unsigned char *)&frm->hdr + 2) = pDev->hdr[2]; + *((unsigned char *)&frm->hdr + 3) = pDev->hdr[3]; + *((unsigned char *)&frm->hdr + 4) = pDev->hdr[4]; + + /* Calc Total and Filler byte count in frame */ + total_len = pDev->frmlen / 7; + total_len = total_len * 7; + if ( pDev->frmlen != total_len ) + total_len += 7; + + pDev->filler = total_len - pDev->frmlen; + + pDev->frame_state = FRM_STATE_PAYLOAD; + pDev->frm = frm; + + case FRM_STATE_PAYLOAD: + DBG2("FRAME_STATE_PAYLOAD\n"); + /* Parts of payload and the complete header has been read */ + frm = pDev->frm; + + dst = (unsigned char *)&frm->data[frm->len-5]; + left = pDev->frmlen-frm->len; + + ret = grtc_hw_copy(pDev,dst,left,1); + if ( ret < 0 ) { + DEBUG_ERR_LOG(pDev,5); + /* Error copying header, restart scanning for new frame */ + pDev->frame_state = FRM_STATE_NONE; + frm->next = NULL; + grtc_pool_add_frms(frm); + pDev->frm = NULL; + pDev->stats.err++; + pDev->stats.err_payload++; + return -1; + } else if ( ret != left ) { + /* Not all bytes available, come back later */ + frm->len += ret; + return 0; + } + frm->len += ret; + pDev->frame_state = FRM_STATE_FILLER; + + case FRM_STATE_FILLER: + DBG2("FRAME_STATE_FILLER\n"); + /* check filler data */ + frm = pDev->frm; + + ret = grtc_hw_check_ending(pDev,pDev->filler); + if ( ret != 0 ) { + /* Error in frame, drop frame */ + DEBUG_ERR_LOG(pDev,6); + pDev->frame_state = FRM_STATE_NONE; + frm->next = NULL; + grtc_pool_add_frms(frm); + pDev->frm = NULL; + pDev->stats.err++; + pDev->stats.err_ending++; + return -1; + } + + /* A complete frame received, put it into received frame queue */ + if ( pDev->ready.head ) { + /* Queue not empty */ + pDev->ready.tail->next = frm; + } else { + /* Queue empty */ + pDev->ready.head = frm; + } + pDev->ready.tail = frm; + frm->next = NULL; + pDev->ready.cnt++; + pDev->stats.frames_recv++; + + pDev->frame_state = FRM_STATE_NONE; + frm->next = NULL; + return 2; + +#if 0 + case FRM_STATE_DROP: + DBG2("FRAME_STATE_DROP\n"); + break; +#endif + + default: + printk("GRTC: internal error\n"); + pDev->frame_state = FRM_STATE_NONE; + break; + } + + return 0; +} + +static rtems_device_driver grtc_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grtc_priv *pDev; + struct drvmgr_dev *dev; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; + unsigned int *data = ioarg->buffer; + int status,frm_len,i,ret; + struct grtc_ioc_buf_params *buf_arg; + struct grtc_ioc_config *cfg; + struct grtc_ioc_hw_status *hwregs; + struct grtc_ioc_pools_setup *pocfg; + struct grtc_ioc_assign_frm_pool *poassign; + struct grtc_frame *frm, *frms; + struct grtc_frame_pool *pool; + struct grtc_list *frmlist; + struct grtc_ioc_stats *stats; + unsigned int mem; + + IRQ_GLOBAL_PREPARE(oldLevel); + + FUNCDBG(); + + if ( drvmgr_get_dev(&grtc_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grtc_priv *)dev->priv; + + if (!ioarg) + return RTEMS_INVALID_NAME; + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case GRTC_IOC_START: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + if ( (status=grtc_start(pDev)) != RTEMS_SUCCESSFUL ){ + return status; + } + /* Register ISR and Unmask interrupt */ + drvmgr_interrupt_register(pDev->dev, 0, "grtc", grtc_interrupt, pDev); + + /* Read and write are now open... */ + break; + + case GRTC_IOC_STOP: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + drvmgr_interrupt_unregister(pDev->dev, 0, grtc_interrupt, pDev); + grtc_stop(pDev); + pDev->running = 0; + break; + + case GRTC_IOC_ISSTARTED: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + break; + + case GRTC_IOC_SET_BLOCKING_MODE: + if ( (unsigned int)data > GRTC_BLKMODE_COMPLETE ) { + return RTEMS_INVALID_NAME; + } + DBG("GRTC: Set blocking mode: %d\n",(unsigned int)data); + pDev->blocking = (unsigned int)data; + break; + + case GRTC_IOC_SET_TIMEOUT: + DBG("GRTC: Timeout: %d\n",(unsigned int)data); + pDev->timeout = (rtems_interval)data; + break; + + case GRTC_IOC_SET_BUF_PARAM: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + + buf_arg = (struct grtc_ioc_buf_params *)data; + if ( !buf_arg ) { + return RTEMS_INVALID_NAME; + } + + DBG("GRTC: IOC_SET_BUF_PARAM: Len: 0x%x, Custom Buffer: 0x%x\n",buf_arg->length,buf_arg->custom_buffer); + + /* Check alignment need, skip bit 0 since that bit only indicates remote address or not */ + if ( (unsigned int)buf_arg->custom_buffer & (~GRTC_BUF_MASK) & (~0x1) ) { + return RTEMS_INVALID_NAME; + } + + if ( buf_arg->length > 0x100 ){ + DBG("GRTC: Too big buffer requested\n"); + return RTEMS_INVALID_NAME; + } + + /* If current buffer allocated by driver we must free it */ + if ( !pDev->buf_custom && pDev->buf ){ + free(pDev->_buf); + pDev->_buf = NULL; + } + pDev->buf = NULL; + pDev->len = buf_arg->length*1024; + + if (pDev->len <= 0) + break; + mem = (unsigned int)buf_arg->custom_buffer; + pDev->buf_custom = mem; + + if (mem & 1) { + /* Remote address given, the address is as the GRTC + * core looks at it. Translate the base address into + * an address that the CPU can understand. + */ + pDev->buf_remote = (void *)(mem & ~0x1); + drvmgr_translate_check(pDev->dev, DMAMEM_TO_CPU, + (void *)pDev->buf_remote, + (void **)&pDev->buf, + pDev->len); + } else { + if (mem == 0) { + pDev->buf = grtc_memalign((~GRTC_ASR_BUFST)+1,pDev->len,&pDev->_buf); + DBG("grtc_ioctl: SETBUF: new buf: 0x%x(0x%x), Len: %d\n",pDev->buf,pDev->_buf,pDev->len); + if (!pDev->buf){ + pDev->len = 0; + pDev->buf_custom = 0; + pDev->_buf = NULL; + pDev->buf_remote = 0; + DBG("GRTC: Failed to allocate memory\n"); + return RTEMS_NO_MEMORY; + } + } else{ + pDev->buf = buf_arg->custom_buffer; + } + + /* Translate into a remote address so that GRTC core + * on a remote AMBA bus (for example over the PCI bus) + * gets a valid address + */ + drvmgr_translate_check(pDev->dev, CPUMEM_TO_DMA, + (void *)pDev->buf, + (void **)&pDev->buf_remote, + pDev->len); + } + break; + + case GRTC_IOC_GET_BUF_PARAM: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + + buf_arg = (struct grtc_ioc_buf_params *)data; + if ( !buf_arg ) { + return RTEMS_INVALID_NAME; + } + + buf_arg->length = pDev->len >> 10; /* Length in 1kByte blocks */ + if ( pDev->buf_custom ) + buf_arg->custom_buffer =(void *)pDev->buf; + else + buf_arg->custom_buffer = 0; /* Don't reveal internal driver buffer */ + break; + + case GRTC_IOC_SET_CONFIG: + cfg = (struct grtc_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + pDev->config = *cfg; + break; + + case GRTC_IOC_GET_CONFIG: + cfg = (struct grtc_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + *cfg = pDev->config; + break; + + case GRTC_IOC_GET_HW_STATUS: + hwregs = (struct grtc_ioc_hw_status *)data; + if ( !hwregs ) { + return RTEMS_INVALID_NAME; + } + /* We disable interrupt in order to get a snapshot of the registers */ + IRQ_GLOBAL_DISABLE(oldLevel); + hwregs->sir = READ_REG(&pDev->regs->sir); + hwregs->far = READ_REG(&pDev->regs->far); + hwregs->clcw1 = READ_REG(&pDev->regs->clcw1); + hwregs->clcw2 = READ_REG(&pDev->regs->clcw2); + hwregs->phir = READ_REG(&pDev->regs->phir); + hwregs->str = READ_REG(&pDev->regs->str); + IRQ_GLOBAL_ENABLE(oldLevel); + break; + + case GRTC_IOC_GET_STATS: + stats = (struct grtc_ioc_stats *)data; + if ( !stats ) { + return RTEMS_INVALID_NAME; + } + memcpy(stats,&pDev->stats,sizeof(struct grtc_ioc_stats)); + break; + + case GRTC_IOC_CLR_STATS: + memset(&pDev->stats,0,sizeof(struct grtc_ioc_stats)); + break; + + case GRTC_IOC_SET_MODE: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + if ( (int)data == GRTC_MODE_FRAME ) { + pDev->mode = GRTC_MODE_FRAME; + } else if ( (int)data == GRTC_MODE_RAW ) { + pDev->mode = GRTC_MODE_RAW; + } else { + return RTEMS_INVALID_NAME; + } + break; + + case GRTC_IOC_POOLS_SETUP: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + pocfg = (struct grtc_ioc_pools_setup *)data; + if ( (pDev->mode != GRTC_MODE_FRAME) || !pocfg ) { + return RTEMS_INVALID_NAME; + } + + /* Check that list is sorted */ + frm_len = 0; + for(i=0;i<pocfg->pool_cnt;i++){ + if ( pocfg->pool_frame_len[i] <= frm_len ) { + return RTEMS_INVALID_NAME; + } + frm_len = pocfg->pool_frame_len[i]; + } + + /* Ok, we trust user. The pool descriptions are allocated + * but not frames, that the user must do self. + */ + if ( pDev->pools ) { + free(pDev->pools); + } + pDev->pools = malloc(pocfg->pool_cnt * sizeof(struct grtc_frame_pool)); + if ( !pDev->pools ) { + pDev->pool_cnt = 0; + return RTEMS_NO_MEMORY; + } + pDev->pool_cnt = pocfg->pool_cnt; + for (i=0;i<pocfg->pool_cnt;i++) { + pDev->pools[i].frame_len = pocfg->pool_frame_len[i]; + pDev->pools[i].frame_cnt = 0; + pDev->pools[i].frms = NULL; + } + break; + + case GRTC_IOC_ASSIGN_FRM_POOL: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + if ( (pDev->mode != GRTC_MODE_FRAME) ) { + return RTEMS_INVALID_NAME; + } + + poassign = (struct grtc_ioc_assign_frm_pool *)data; + if ( !poassign ) { + return RTEMS_INVALID_NAME; + } + + /* Find pool to assign the frames to */ + pool = NULL; + for(i=0; i<pDev->pool_cnt; i++) { + if ( pDev->pools[i].frame_len == poassign->frame_len ) { + pool = &pDev->pools[i]; + break; + } + } + if ( !pool ) { + /* No Pool matching frame length */ + return RTEMS_INVALID_NAME; + } + + /* Assign frames to pool */ + frm = poassign->frames; + while(frm){ + frm->pool = pool; /* Assign Frame to pool */ + frm = frm->next; + } + break; + + case GRTC_IOC_ADD_BUFF: + frms = (struct grtc_frame *)data; + + if ( (pDev->mode != GRTC_MODE_FRAME) ) { + return RTEMS_NOT_DEFINED; + } + if ( !frms ) { + return RTEMS_INVALID_NAME; + } + + /* Add frames to respicative pools */ + if ( grtc_pool_add_frms(frms) ) { + return RTEMS_INVALID_NAME; + } + break; + + /* Try to read as much data as possible from DMA area and + * put it into free frames. + * + * If receiver is in stopped mode, let user only read previously + * received frames. + */ + case GRTC_IOC_RECV: + + if ( (pDev->mode != GRTC_MODE_FRAME) ) { + return RTEMS_NOT_DEFINED; + } + + while ( pDev->running && ((ret=process_dma(pDev) == 2) || (ret == -1)) ) { + /* Frame received or dropped, process next frame */ + } + + /* Take frames out from ready queue and put them to user */ + frmlist = (struct grtc_list *)data; + if ( !frmlist ) { + return RTEMS_INVALID_NAME; + } + + frmlist->head = pDev->ready.head; + frmlist->tail = pDev->ready.tail; + frmlist->cnt = pDev->ready.cnt; + + /* Empty list */ + pDev->ready.head = NULL; + pDev->ready.tail = NULL; + pDev->ready.cnt = 0; + break; + + case GRTC_IOC_GET_CLCW_ADR: + if ( !data ) { + return RTEMS_INVALID_NAME; + } + *data = (unsigned int)&pDev->regs->clcw1; + break; + + default: + return RTEMS_NOT_DEFINED; + } + return RTEMS_SUCCESSFUL; +} + +static void grtc_interrupt(void *arg) +{ + struct grtc_priv *pDev = arg; + struct grtc_regs *regs = pDev->regs; + unsigned int status; + + /* Clear interrupt by reading it */ + status = READ_REG(®s->pisr); + + /* Spurious Interrupt? */ + if ( !pDev->running ) + return; + + if ( status & GRTC_INT_OV ){ + + /* Stop core (Disable receiver, interrupts), set overrun condition, + * Flush semaphore if thread waiting for data in grtc_wait_data(). + */ + pDev->overrun_condition = 1; + + grtc_stop(pDev); + + /* No need to handle the reset of interrupts, we are still */ + goto out; + } + + if ( status & GRTC_INT_CS ){ + if ( (pDev->blocking==GRTC_BLKMODE_COMPLETE) && pDev->timeout ){ + /* Signal to thread only if enough data is available */ + if ( pDev->wait_for_nbytes > grtc_data_avail(pDev) ){ + /* Not enough data available */ + goto procceed_processing_interrupts; + } + + /* Enough data is available which means that we should wake + * up thread sleeping. + */ + } + + /* Disable further CLTUs Stored interrupts, no point until thread waiting for them + * say it want to wait for more. + */ + regs->imr = READ_REG(®s->imr) & ~GRTC_INT_CS; + + /* Signal Semaphore to wake waiting thread in read() */ + rtems_semaphore_release(pDev->sem_rx); + } + +procceed_processing_interrupts: + + if ( status & GRTC_INT_CR ){ + + } + + if ( status & GRTC_INT_FAR ){ + + } + + if ( status & GRTC_INT_BLO ){ + + } + + if ( status & GRTC_INT_RFA ){ + + } +out: + if ( status ) + regs->picr = status; +} + +static rtems_device_driver grtc_initialize( + rtems_device_major_number major, + rtems_device_minor_number unused, + void *arg + ) +{ + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'R', 'T', 'C'), + 1, + RTEMS_FIFO|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &grtc_dev_sem) != RTEMS_SUCCESSFUL ) { + return RTEMS_INTERNAL_ERROR; + } + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c b/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c new file mode 100644 index 0000000000..de1df727ad --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/tmtc/grtm.c @@ -0,0 +1,1587 @@ +/* GRTM CCSDS Telemetry Encoder driver + * + * COPYRIGHT (c) 2007. + * Cobham Gaisler AB. + * + * 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. + */ + +#include <bsp.h> +#include <rtems/libio.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <malloc.h> +#include <rtems/bspIo.h> + +#include <drvmgr/drvmgr.h> +#include <ambapp.h> +#include <drvmgr/ambapp_bus.h> +#include <grtm.h> + +#ifndef IRQ_GLOBAL_PREPARE + #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level +#endif + +#ifndef IRQ_GLOBAL_DISABLE + #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level) +#endif + +#ifndef IRQ_GLOBAL_ENABLE + #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level) +#endif + +/* +#define DEBUG +#define DEBUGFUNCS +*/ + +#include <debug_defs.h> + +/* GRTM register map */ +struct grtm_regs { + volatile unsigned int dma_ctrl; /* DMA Control Register (0x00) */ + volatile unsigned int dma_status; /* DMA Status Register (0x04) */ + volatile unsigned int dma_len; /* DMA Length Register (0x08) */ + volatile unsigned int dma_bd; /* DMA Descriptor Pointer Register (0x0c) */ + + volatile unsigned int dma_cfg; /* DMA Configuration Register (0x10) */ + volatile unsigned int revision; /* GRTM Revision Register (0x14) */ + + int unused0[(0x80-0x18)/4]; + + volatile unsigned int ctrl; /* TM Control Register (0x80) */ + volatile unsigned int status; /* TM Status Register (0x84) */ + volatile unsigned int cfg; /* TM Configuration Register (0x88) */ + volatile unsigned int size; /* TM Size Register (0x8c) */ + + volatile unsigned int phy; /* TM Physical Layer Register (0x90) */ + volatile unsigned int code; /* TM Coding Sub-Layer Register (0x94) */ + volatile unsigned int asmr; /* TM Attached Synchronization Marker Register (0x98) */ + + int unused1; + + volatile unsigned int all_frm; /* TM All Frames Generation Register (0xa0) */ + volatile unsigned int mst_frm; /* TM Master Channel Frame Generation Register (0xa4) */ + volatile unsigned int idle_frm; /* TM Idle Frame Generation Register (0xa8) */ + + int unused2[(0xc0-0xac)/4]; + + volatile unsigned int fsh[4]; /* TM FSH/Insert Zone Registers (0xc0..0xcc) */ + + volatile unsigned int ocf; /* TM Operational Control Field Register (0xd0) */ +}; + +/* DMA Control Register (0x00) */ +#define GRTM_DMA_CTRL_EN_BIT 0 +#define GRTM_DMA_CTRL_IE_BIT 1 +#define GRTM_DMA_CTRL_TXRST_BIT 2 +#define GRTM_DMA_CTRL_RST_BIT 3 +#define GRTM_DMA_CTRL_TFIE_BIT 4 + +#define GRTM_DMA_CTRL_EN (1<<GRTM_DMA_CTRL_EN_BIT) +#define GRTM_DMA_CTRL_IE (1<<GRTM_DMA_CTRL_IE_BIT) +#define GRTM_DMA_CTRL_TXRST (1<<GRTM_DMA_CTRL_TXRST_BIT) +#define GRTM_DMA_CTRL_RST (1<<GRTM_DMA_CTRL_RST_BIT) +#define GRTM_DMA_CTRL_TFIE (1<<GRTM_DMA_CTRL_TFIE_BIT) + +/* DMA Status Register (0x04) */ +#define GRTM_DMA_STS_TE_BIT 0 +#define GRTM_DMA_STS_TI_BIT 1 +#define GRTM_DMA_STS_TA_BIT 2 +#define GRTM_DMA_STS_TFF_BIT 3 +#define GRTM_DMA_STS_TFS_BIT 4 + +#define GRTM_DMA_STS_TE (1<<GRTM_DMA_STS_TE_BIT) +#define GRTM_DMA_STS_TI (1<<GRTM_DMA_STS_TI_BIT) +#define GRTM_DMA_STS_TA (1<<GRTM_DMA_STS_TA_BIT) +#define GRTM_DMA_STS_TFF (1<<GRTM_DMA_STS_TFF_BIT) +#define GRTM_DMA_STS_TFS (1<<GRTM_DMA_STS_TFS_BIT) +#define GRTM_DMA_STS_ALL 0x1f + +/* DMA Length Register (0x08) */ +#define GRTM_DMA_LEN_LEN_BIT 0 +#define GRTM_DMA_LEN_LIM_BIT 16 + +#define GRTM_DMA_LEN_LEN (0x7ff<<GRTM_DMA_LEN_LEN_BIT) +#define GRTM_DMA_LEN_LIM (0x3ff<<GRTM_DMA_LEN_LIM_BIT) + +/* DMA Descriptor Pointer Register (0x0c) */ +#define GRTM_DMA_BD_INDEX_BIT 0 +#define GRTM_DMA_BD_BASE_BIT 10 + +#define GRTM_DMA_BD_INDEX (0x3ff<<GRTM_DMA_BD_INDEX_BIT) +#define GRTM_DMA_BD_BASE (0xfffffc<<GRTM_DMA_BD_BASE_BIT) + +/* DMA Configuration Register (0x10) */ +#define GRTM_DMA_CFG_BLKSZ_BIT 0 +#define GRTM_DMA_CFG_FIFOSZ_BIT 16 + +#define GRTM_DMA_CFG_BLKSZ (0xffff<<GRTM_DMA_CFG_BLKSZ_BIT) +#define GRTM_DMA_CFG_FIFOSZ (0xffff<<GRTM_DMA_CFG_FIFOSZ_BIT) + +/* TM Control Register (0x80) */ +#define GRTM_CTRL_EN_BIT 0 + +#define GRTM_CTRL_EN (1<<GRTM_CTRL_EN_BIT) + +/* TM Status Register (0x84) - Unused */ + +/* TM Configuration Register (0x88) */ +#define GRTM_CFG_SC_BIT 0 +#define GRTM_CFG_SP_BIT 1 +#define GRTM_CFG_CE_BIT 2 +#define GRTM_CFG_NRZ_BIT 3 +#define GRTM_CFG_PSR_BIT 4 +#define GRTM_CFG_TE_BIT 5 +#define GRTM_CFG_RSDEP_BIT 6 +#define GRTM_CFG_RS_BIT 9 +#define GRTM_CFG_AASM_BIT 11 +#define GRTM_CFG_FECF_BIT 12 +#define GRTM_CFG_OCF_BIT 13 +#define GRTM_CFG_EVC_BIT 14 +#define GRTM_CFG_IDLE_BIT 15 +#define GRTM_CFG_FSH_BIT 16 +#define GRTM_CFG_MCG_BIT 17 +#define GRTM_CFG_IZ_BIT 18 +#define GRTM_CFG_FHEC_BIT 19 +#define GRTM_CFG_AOS_BIT 20 +#define GRTM_CFG_CIF_BIT 21 +#define GRTM_CFG_OCFB_BIT 22 + +#define GRTM_CFG_SC (1<<GRTM_CFG_SC_BIT) +#define GRTM_CFG_SP (1<<GRTM_CFG_SP_BIT) +#define GRTM_CFG_CE (1<<GRTM_CFG_CE_BIT) +#define GRTM_CFG_NRZ (1<<GRTM_CFG_NRZ_BIT) +#define GRTM_CFG_PSR (1<<GRTM_CFG_PSR_BIT) +#define GRTM_CFG_TE (1<<GRTM_CFG_TE_BIT) +#define GRTM_CFG_RSDEP (0x7<<GRTM_CFG_RSDEP_BIT) +#define GRTM_CFG_RS (0x3<<GRTM_CFG_RS_BIT) +#define GRTM_CFG_AASM (1<<GRTM_CFG_AASM_BIT) +#define GRTM_CFG_FECF (1<<GRTM_CFG_FECF_BIT) +#define GRTM_CFG_OCF (1<<GRTM_CFG_OCF_BIT) +#define GRTM_CFG_EVC (1<<GRTM_CFG_EVC_BIT) +#define GRTM_CFG_IDLE (1<<GRTM_CFG_IDLE_BIT) +#define GRTM_CFG_FSH (1<<GRTM_CFG_FSH_BIT) +#define GRTM_CFG_MCG (1<<GRTM_CFG_MCG_BIT) +#define GRTM_CFG_IZ (1<<GRTM_CFG_IZ_BIT) +#define GRTM_CFG_FHEC (1<<GRTM_CFG_FHEC_BIT) +#define GRTM_CFG_AOS (1<<GRTM_CFG_AOS_BIT) +#define GRTM_CFG_CIF (1<<GRTM_CFG_CIF_BIT) +#define GRTM_CFG_OCFB (1<<GRTM_CFG_OCFB_BIT) + +/* TM Size Register (0x8c) */ +#define GRTM_SIZE_BLKSZ_BIT 0 +#define GRTM_SIZE_FIFOSZ_BIT 8 +#define GRTM_SIZE_LEN_BIT 20 + +#define GRTM_SIZE_BLKSZ (0xff<<GRTM_SIZE_BLKSZ_BIT) +#define GRTM_SIZE_FIFOSZ (0xfff<<GRTM_SIZE_FIFOSZ_BIT) +#define GRTM_SIZE_LEN (0xfff<<GRTM_SIZE_LEN_BIT) + +/* TM Physical Layer Register (0x90) */ +#define GRTM_PHY_SUB_BIT 0 +#define GRTM_PHY_SCF_BIT 15 +#define GRTM_PHY_SYM_BIT 16 +#define GRTM_PHY_SF_BIT 31 + +#define GRTM_PHY_SUB (0x7fff<<GRTM_PHY_SUB_BIT) +#define GRTM_PHY_SCF (1<<GRTM_PHY_SCF_BIT) +#define GRTM_PHY_SYM (0x7fff<<GRTM_PHY_SYM_BIT) +#define GRTM_PHY_SF (1<<GRTM_PHY_SF_BIT) + +/* TM Coding Sub-Layer Register (0x94) */ +#define GRTM_CODE_SC_BIT 0 +#define GRTM_CODE_SP_BIT 1 +#define GRTM_CODE_CERATE_BIT 2 +#define GRTM_CODE_CE_BIT 5 +#define GRTM_CODE_NRZ_BIT 6 +#define GRTM_CODE_PSR_BIT 7 +#define GRTM_CODE_RS8_BIT 11 +#define GRTM_CODE_RSDEP_BIT 12 +#define GRTM_CODE_RS_BIT 15 +#define GRTM_CODE_AASM_BIT 16 +#define GRTM_CODE_CSEL_BIT 17 + +#define GRTM_CODE_SC (1<<GRTM_CODE_SC_BIT) +#define GRTM_CODE_SP (1<<GRTM_CODE_SP_BIT) +#define GRTM_CODE_CERATE (0x7<<GRTM_CODE_CERATE_BIT) +#define GRTM_CODE_CE (1<<GRTM_CODE_CE_BIT) +#define GRTM_CODE_NRZ (1<<GRTM_CODE_NRZ_BIT) +#define GRTM_CODE_PSR (1<<GRTM_CODE_PSR_BIT) +#define GRTM_CODE_RS8 (1<<GRTM_CODE_RS8_BIT) +#define GRTM_CODE_RSDEP (0x7<<GRTM_CODE_RSDEP_BIT) +#define GRTM_CODE_RS (1<<GRTM_CODE_RS_BIT) +#define GRTM_CODE_AASM (1<<GRTM_CODE_AASM_BIT) +#define GRTM_CODE_CSEL (0x3<<GRTM_CODE_CSEL_BIT) + +/* TM Attached Synchronization Marker Register (0x98) */ +#define GRTM_ASM_BIT 0 + +#define GRTM_ASM 0xffffffff + +/* TM All Frames Generation Register (0xa0) */ +#define GRTM_ALL_LEN_BIT 0 +#define GRTM_ALL_VER_BIT 12 +#define GRTM_ALL_FHEC_BIT 14 +#define GRTM_ALL_FECF_BIT 15 +#define GRTM_ALL_IZ_BIT 16 +#define GRTM_ALL_IZLEN_BIT 17 + +#define GRTM_ALL_LEN (0x7ff<<GRTM_ALL_LEN_BIT) +#define GRTM_ALL_VER (0x3<<GRTM_ALL_VER_BIT) +#define GRTM_ALL_FHEC (1<<GRTM_ALL_FHEC_BIT) +#define GRTM_ALL_FECF (1<<GRTM_ALL_FECF_BIT) +#define GRTM_ALL_IZ (1<<GRTM_ALL_IZ_BIT) +#define GRTM_ALL_IZLEN (0x1f<<GRTM_ALL_IZLEN_BIT) + +/* TM Master Channel Frame Generation Register (0xa4) */ +#define GRTM_MST_OW_BIT 0 +#define GRTM_MST_OCF_BIT 1 +#define GRTM_MST_FSH_BIT 2 +#define GRTM_MST_MC_BIT 3 +#define GRTM_MST_MCCNTR_BIT 24 + +#define GRTM_MST_OW (1<<GRTM_MST_OW_BIT) +#define GRTM_MST_OCF (1<<GRTM_MST_OCF_BIT) +#define GRTM_MST_FSH (1<<GRTM_MST_FSH_BIT) +#define GRTM_MST_MC (0xff<<GRTM_MST_MC_BIT) + +/* TM Idle Frame Generation Register (0xa8) */ +#define GRTM_IDLE_SCID_BIT 0 +#define GRTM_IDLE_VCID_BIT 10 +#define GRTM_IDLE_MC_BIT 16 +#define GRTM_IDLE_VCC_BIT 17 +#define GRTM_IDLE_FSH_BIT 18 +#define GRTM_IDLE_EVC_BIT 19 +#define GRTM_IDLE_OCF_BIT 20 +#define GRTM_IDLE_IDLE_BIT 21 +#define GRTM_IDLE_MCCNTR_BIT 24 + +#define GRTM_IDLE_SCID (0x3ff<<GRTM_IDLE_SCID_BIT) +#define GRTM_IDLE_VCID (0x3f<<GRTM_IDLE_VCID_BIT) +#define GRTM_IDLE_MC (1<<GRTM_IDLE_MC_BIT) +#define GRTM_IDLE_VCC (1<<GRTM_IDLE_VCC_BIT) +#define GRTM_IDLE_FSH (1<<GRTM_IDLE_FSH_BIT) +#define GRTM_IDLE_EVC (1<<GRTM_IDLE_EVC_BIT) +#define GRTM_IDLE_OCF (1<<GRTM_IDLE_OCF_BIT) +#define GRTM_IDLE_IDLE (1<<GRTM_IDLE_IDLE_BIT) +#define GRTM_IDLE_MCCNTR (0xff<<GRTM_IDLE_MCCNTR_BIT) + +/* TM FSH/Insert Zone Registers (0xc0..0xcc) */ +#define GRTM_FSH_DATA_BIT 0 + +#define GRTM_FSH_DATA 0xffffffff + + +/* TM Operational Control Field Register (0xd0) */ +#define GRTM_OCF_CLCW_BIT 0 + +#define GRTM_OCF_CLCW 0xffffffff + + +/* GRTM Revision 0 */ +#define GRTM_REV0_DMA_CTRL_TXRDY_BIT 5 +#define GRTM_REV0_DMA_CTRL_TXRDY (1<<GRTM_REV0_DMA_CTRL_TXRDY_BIT) + +/* GRTM Revision 1 */ +#define GRTM_REV1_DMA_STS_TXRDY_BIT 6 +#define GRTM_REV1_DMA_STS_TXSTAT_BIT 7 +#define GRTM_REV1_DMA_STS_TXRDY (1<<GRTM_REV1_DMA_STS_TXRDY_BIT) +#define GRTM_REV1_DMA_STS_TXSTAT (1<<GRTM_REV1_DMA_STS_TXSTAT_BIT) + +#define GRTM_REV1_REV_SREV_BIT 0 +#define GRTM_REV1_REV_MREV_BIT 8 +#define GRTM_REV1_REV_TIRQ_BIT 16 +#define GRTM_REV1_REV_SREV (0xff<<GRTM_REV1_REV_SREV_BIT) +#define GRTM_REV1_REV_MREV (0xff<<GRTM_REV1_REV_MREV_BIT) +#define GRTM_REV1_REV_TIRQ (1<<GRTM_REV1_REV_TIRQ_BIT) + + +/* GRTM transmit descriptor (0x400 Alignment need) */ +struct grtm_bd { + volatile unsigned int ctrl; + unsigned int address; +}; + +#define GRTM_BD_EN_BIT 0 +#define GRTM_BD_WR_BIT 1 +#define GRTM_BD_IE_BIT 2 +#define GRTM_BD_FECFB_BIT 3 +#define GRTM_BD_IZB_BIT 4 +#define GRTM_BD_FHECB_BIT 5 +#define GRTM_BD_OCFB_BIT 6 +#define GRTM_BD_FSHB_BIT 7 +#define GRTM_BD_MCB_BIT 8 +#define GRTM_BD_VCE_BIT 9 +#define GRTM_BD_TS_BIT 14 +#define GRTM_BD_UE_BIT 15 + +#define GRTM_BD_EN (1<<GRTM_BD_EN_BIT) +#define GRTM_BD_WR (1<<GRTM_BD_WR_BIT) +#define GRTM_BD_IE (1<<GRTM_BD_IE_BIT) +#define GRTM_BD_FECFB (1<<GRTM_BD_FECFB_BIT) +#define GRTM_BD_IZB (1<<GRTM_BD_IZB_BIT) +#define GRTM_BD_FHECB (1<<GRTM_BD_FHECB_BIT) +#define GRTM_BD_OCFB (1<<GRTM_BD_OCFB_BIT) +#define GRTM_BD_FSHB (1<<GRTM_BD_FSHB_BIT) +#define GRTM_BD_MCB (1<<GRTM_BD_MCB_BIT) +#define GRTM_BD_VCE (1<<GRTM_BD_VCE_BIT) +#define GRTM_BD_TS (1<<GRTM_BD_TS_BIT) +#define GRTM_BD_UE (1<<GRTM_BD_UE_BIT) + +/* Load register */ + +#define READ_REG(address) (*(volatile unsigned int *)address) + +/* Driver functions */ +static rtems_device_driver grtm_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtm_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtm_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtm_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtm_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define GRTM_DRIVER_TABLE_ENTRY { grtm_initialize, grtm_open, grtm_close, grtm_read, grtm_write, grtm_ioctl } + +static rtems_driver_address_table grtm_driver = GRTM_DRIVER_TABLE_ENTRY; + +/* Structure that connects BD with SoftWare Frame */ +struct grtm_ring { + struct grtm_ring *next; + struct grtm_bd *bd; + struct grtm_frame *frm; +}; + +struct grtm_priv { + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + struct grtm_regs *regs; + int irq; + int minor; + int subrev; /* GRTM Revision */ + + int open; + int running; + + struct grtm_bd *bds; + void *_bds; + + /* Interrupt generation */ + int enable_cnt_curr;/* Down counter, when 0 the interrupt bit is set for next descriptor */ + volatile int handling_transmission; /* Tells ISR if user are active changing descriptors/queues */ + + struct grtm_ring *_ring; /* Root of ring */ + struct grtm_ring *ring; /* Next ring to use for new frames to be transmitted */ + struct grtm_ring *ring_end; /* Oldest activated ring used */ + + /* Collections of frames Ready to sent/ Scheduled for transmission/Sent + * frames waiting for the user to reclaim + */ + struct grtm_list ready; /* Frames Waiting for free BDs */ + struct grtm_list scheduled; /* Frames in BDs beeing transmitted */ + struct grtm_list sent; /* Sent Frames waiting for user to reclaim and reuse */ + + /* Number of frames in the lists */ + int ready_cnt; /* Number of ready frames */ + int scheduled_cnt; /* Number of scheduled frames */ + int sent_cnt; /* Number of sent frames */ + + struct grtm_ioc_hw hw_avail; /* Hardware support available */ + struct grtm_ioc_config config; + struct grtm_ioc_stats stats; + + rtems_id sem_tx; +}; + +/* Prototypes */ +static void *grtm_memalign(unsigned int boundary, unsigned int length, void *realbuf); +static void grtm_hw_reset(struct grtm_priv *pDev); +static void grtm_interrupt(void *arg); + +/* Common Global Variables */ +static rtems_id grtm_dev_sem; +static int grtm_driver_io_registered = 0; +static rtems_device_major_number grtm_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +static int grtm_register_io(rtems_device_major_number *m); +static int grtm_device_init(struct grtm_priv *pDev); + +static int grtm_init2(struct drvmgr_dev *dev); +static int grtm_init3(struct drvmgr_dev *dev); + +static struct drvmgr_drv_ops grtm_ops = +{ + {NULL, grtm_init2, grtm_init3, NULL}, + NULL, + NULL +}; + +static struct amba_dev_id grtm_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRTM}, + {0, 0} /* Mark end of table */ +}; + +static struct amba_drv_info grtm_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRTM_ID, /* Driver ID */ + "GRTM_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grtm_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &grtm_ids[0] +}; + +void grtm_register_drv (void) +{ + DBG("Registering GRTM driver\n"); + drvmgr_drv_register(&grtm_drv_info.general); +} + +static int grtm_init2(struct drvmgr_dev *dev) +{ + struct grtm_priv *priv; + + DBG("GRTM[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(struct grtm_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + +static int grtm_init3(struct drvmgr_dev *dev) +{ + struct grtm_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( grtm_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( grtm_register_io(&grtm_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + grtm_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + if ( grtm_device_init(priv) ) { + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/grtm%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sgrtm%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, grtm_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +/******************* Driver Implementation ***********************/ + +static int grtm_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &grtm_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("GRTM driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("GRTM rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("GRTM rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("GRTM rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("GRTM rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +static int grtm_device_init(struct grtm_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)pDev->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + pDev->irq = pnpinfo->irq; + pDev->regs = (struct grtm_regs *)pnpinfo->apb_slv->start; + pDev->minor = pDev->dev->minor_drv; + pDev->open = 0; + pDev->running = 0; + + /* Create Binary RX Semaphore with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'R', 'M', '0' + pDev->minor), + 0, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ + RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->sem_tx) != RTEMS_SUCCESSFUL ) { + return -1; + } + + /* Allocate Memory for Buffer Descriptor Table, or let user provide a custom + * address. + */ + value = drvmgr_dev_key_get(pDev->dev, "bdTabAdr", KEY_TYPE_POINTER); + if ( value ) { + pDev->bds = (struct grtm_bd *)value->ptr; + pDev->_bds = (void *)value->ptr; + } else { + pDev->bds = (struct grtm_bd *)grtm_memalign(0x400, 0x400, &pDev->_bds); + } + if ( !pDev->bds ) { + DBG("GRTM: Failed to allocate descriptor table\n"); + return -1; + } + memset(pDev->bds, 0, 0x400); + + pDev->_ring = malloc(sizeof(struct grtm_ring) * 128); + if ( !pDev->_ring ) { + return -1; + } + + /* Reset Hardware before attaching IRQ handler */ + grtm_hw_reset(pDev); + + /* Read SUB revision number, ignore */ + pDev->subrev = (READ_REG(&pDev->regs->revision) & GRTM_REV1_REV_SREV) + >> GRTM_REV1_REV_SREV_BIT; + + return 0; +} + + +static inline void grtm_list_clr(struct grtm_list *list) +{ + list->head = NULL; + list->tail = NULL; +} + +static void grtm_hw_reset(struct grtm_priv *pDev) +{ + /* Reset Core */ + pDev->regs->dma_ctrl = GRTM_DMA_CTRL_RST; +} + +static void grtm_hw_get_implementation(struct grtm_priv *pDev, struct grtm_ioc_hw *hwcfg) +{ + unsigned int cfg = READ_REG(&pDev->regs->cfg); + + hwcfg->cs = (cfg & GRTM_CFG_SC) ? 1:0; + hwcfg->sp = (cfg & GRTM_CFG_SP) ? 1:0; + hwcfg->ce = (cfg & GRTM_CFG_CE) ? 1:0; + hwcfg->nrz = (cfg & GRTM_CFG_NRZ) ? 1:0; + hwcfg->psr = (cfg & GRTM_CFG_PSR) ? 1:0; + hwcfg->te = (cfg & GRTM_CFG_TE) ? 1:0; + hwcfg->rsdep = (cfg & GRTM_CFG_RSDEP)>>GRTM_CFG_RSDEP_BIT; + hwcfg->rs = (cfg & GRTM_CFG_RS)>>GRTM_CFG_RS_BIT; + hwcfg->aasm = (cfg & GRTM_CFG_AASM) ? 1:0; + hwcfg->fecf = (cfg & GRTM_CFG_FECF) ? 1:0; + hwcfg->ocf = (cfg & GRTM_CFG_OCF) ? 1:0; + hwcfg->evc = (cfg & GRTM_CFG_EVC) ? 1:0; + hwcfg->idle = (cfg & GRTM_CFG_IDLE) ? 1:0; + hwcfg->fsh = (cfg & GRTM_CFG_FSH) ? 1:0; + hwcfg->mcg = (cfg & GRTM_CFG_MCG) ? 1:0; + hwcfg->iz = (cfg & GRTM_CFG_IZ) ? 1:0; + hwcfg->fhec = (cfg & GRTM_CFG_FHEC) ? 1:0; + hwcfg->aos = (cfg & GRTM_CFG_AOS) ? 1:0; + hwcfg->cif = (cfg & GRTM_CFG_CIF) ? 1:0; + hwcfg->ocfb = (cfg & GRTM_CFG_OCFB) ? 1:0; + + cfg = READ_REG(&pDev->regs->dma_cfg); + hwcfg->blk_size = (cfg & GRTM_DMA_CFG_BLKSZ) >> GRTM_DMA_CFG_BLKSZ_BIT; + hwcfg->fifo_size= (cfg & GRTM_DMA_CFG_FIFOSZ) >> GRTM_DMA_CFG_FIFOSZ_BIT; +} + +#warning Extra: Implement proper default calculation from hardware configuration +static void grtm_hw_get_default_modes(struct grtm_ioc_config *cfg, struct grtm_ioc_hw *hwcfg) +{ + cfg->mode = GRTM_MODE_TM; + cfg->frame_length = 223; + cfg->limit = 0; /* Make driver auto configure it on START, user may override with non-zero value */ + cfg->as_marker = 0x1ACFFC1D; + + /* Physical */ + cfg->phy_subrate = 1; + cfg->phy_symbolrate = 1; + cfg->phy_opts = 0; + + /* Coding Layer */ + cfg->code_rsdep = 1; + cfg->code_ce_rate = 0; + cfg->code_csel = 0; + cfg->code_opts = 0; + + /* All Frame Generation */ + cfg->all_izlen = 0; + cfg->all_opts = GRTM_IOC_ALL_FECF; + + /* Master Channel Frame Generation */ + if ( hwcfg->mcg ) { + cfg->mf_opts = GRTM_IOC_MF_MC; + } else { + cfg->mf_opts = 0; + } + + /* Idle Frame Generation */ + cfg->idle_scid = 0; + cfg->idle_vcid = 0; + if ( hwcfg->idle ) { + cfg->idle_opts = GRTM_IOC_IDLE_EN; + } else { + cfg->idle_opts = 0; + } + + /* Interrupt options */ + cfg->blocking = 0; /* non-blocking mode is default */ + cfg->enable_cnt = 16; /* generate interrupt every 16 descriptor */ + cfg->isr_desc_proc = 1; /* Let interrupt handler do descriptor processing */ + cfg->timeout = RTEMS_NO_TIMEOUT; + +} + +static void *grtm_memalign(unsigned int boundary, unsigned int length, void *realbuf) +{ + *(int *)realbuf = (int)malloc(length+boundary); + DBG("GRTM: Alloced %d (0x%x) bytes, requested: %d\n",length+boundary,length+boundary,length); + return (void *)(((*(unsigned int *)realbuf)+boundary) & ~(boundary-1)); +} + +static int grtm_hw_set_config(struct grtm_priv *pDev, struct grtm_ioc_config *cfg, struct grtm_ioc_hw *hwcfg) +{ + struct grtm_regs *regs = pDev->regs; + unsigned int tmp; + unsigned int limit; + + if ( cfg->limit == 0 ) { + /* Calculate Limit */ + if ( cfg->frame_length > hwcfg->blk_size ) { + limit = hwcfg->blk_size*2; + } else { + limit = cfg->frame_length; + } + } else { + /* Use user configured limit */ + limit = cfg->limit; + } + + /* Frame Length and Limit */ + regs->dma_len = (((limit-1) << GRTM_DMA_LEN_LIM_BIT) & GRTM_DMA_LEN_LIM)| + (((cfg->frame_length-1) << GRTM_DMA_LEN_LEN_BIT) & GRTM_DMA_LEN_LEN); + + /* Physical layer options */ + tmp = (cfg->phy_opts & (GRTM_IOC_PHY_SCF|GRTM_IOC_PHY_SF)) | + (((cfg->phy_symbolrate-1)<<GRTM_PHY_SYM_BIT) & GRTM_PHY_SYM) | (((cfg->phy_subrate-1)<<GRTM_PHY_SUB_BIT) & GRTM_PHY_SUB); + regs->phy = tmp; + + /* Coding Sub-layer Options */ + tmp = (cfg->code_opts & GRTM_IOC_CODE_ALL) | ((cfg->code_csel<<GRTM_CODE_CSEL_BIT) & GRTM_CODE_CSEL) | + (((cfg->code_rsdep-1)<<GRTM_CODE_RSDEP_BIT) & GRTM_CODE_RSDEP) | ((cfg->code_ce_rate<<GRTM_CODE_CERATE_BIT) & GRTM_CODE_CERATE); + regs->code = tmp; + + /* Attached synchronization marker register */ + regs->asmr = cfg->as_marker; + + /* All Frames Generation */ + tmp = ((cfg->all_opts & GRTM_IOC_ALL_ALL)<<14) | + ((cfg->all_izlen<<GRTM_ALL_IZLEN_BIT) & GRTM_ALL_IZLEN) | + ((cfg->mode<<GRTM_ALL_VER_BIT) & GRTM_ALL_VER); + regs->all_frm = tmp; + + /* Master Frame Generation */ + regs->mst_frm = cfg->mf_opts & GRTM_IOC_MF_ALL; + + /* Idle frame Generation */ + tmp = ((cfg->idle_opts & GRTM_IOC_IDLE_ALL) << 16) | + ((cfg->idle_vcid << GRTM_IDLE_VCID_BIT) & GRTM_IDLE_VCID) | + ((cfg->idle_scid << GRTM_IDLE_SCID_BIT) & GRTM_IDLE_SCID); + regs->idle_frm = tmp; + + return 0; +} + +static int grtm_start(struct grtm_priv *pDev) +{ + struct grtm_regs *regs = pDev->regs; + int i; + struct grtm_ioc_config *cfg = &pDev->config; + volatile unsigned int *txrdy_reg; + unsigned int txrdy_mask; + + /* Clear Descriptors */ + memset(pDev->bds,0,0x400); + + /* Clear stats */ + memset(&pDev->stats,0,sizeof(struct grtm_ioc_stats)); + + /* Init Descriptor Ring */ + memset(pDev->_ring,0,sizeof(struct grtm_ring)*128); + for(i=0;i<127;i++){ + pDev->_ring[i].next = &pDev->_ring[i+1]; + pDev->_ring[i].bd = &pDev->bds[i]; + pDev->_ring[i].frm = NULL; + } + pDev->_ring[127].next = &pDev->_ring[0]; + pDev->_ring[127].bd = &pDev->bds[127]; + pDev->_ring[127].frm = NULL; + + pDev->ring = &pDev->_ring[0]; + pDev->ring_end = &pDev->_ring[0]; + + /* Clear Scheduled, Ready and Sent list */ + grtm_list_clr(&pDev->ready); + grtm_list_clr(&pDev->scheduled); + grtm_list_clr(&pDev->sent); + + /* Software init */ + pDev->handling_transmission = 0; + + /* Reset the transmitter */ + regs->dma_ctrl = GRTM_DMA_CTRL_TXRST; + regs->dma_ctrl = 0; /* Leave Reset */ + + /* Clear old interrupts */ + regs->dma_status = GRTM_DMA_STS_ALL; + + /* Set Descriptor Pointer Base register to point to first descriptor */ + drvmgr_translate_check(pDev->dev, CPUMEM_TO_DMA, (void *)pDev->bds, + (void **)®s->dma_bd, 0x400); + + /* Set hardware options as defined by config */ + if ( grtm_hw_set_config(pDev, cfg, &pDev->hw_avail) ) { + return RTEMS_IO_ERROR; + } + + /* Enable TM Transmitter */ + regs->ctrl = GRTM_CTRL_EN; + + /* Wait for TXRDY to be cleared */ + i=1000; + while( i > 0 ) { + asm volatile ("nop"::); + i--; + } + + /* Location of TXRDY Bit is different for different revisions */ + if ( pDev->subrev == 0 ) { + txrdy_reg = ®s->dma_ctrl; + txrdy_mask = GRTM_REV0_DMA_CTRL_TXRDY; + } else { + txrdy_reg = ®s->dma_status; + txrdy_mask = GRTM_REV1_DMA_STS_TXRDY; + } + + /* Check transmitter startup OK */ + i=0; + while( !(READ_REG(txrdy_reg) & txrdy_mask) && (i<1000000) ){ + i++; + } + if ( !(READ_REG(txrdy_reg) & txrdy_mask) ){ + /* Reset Failed */ + DBG("GRTM: start: Reseting transmitter failed (%d)\n",i); + return RTEMS_IO_ERROR; + } + DBG("GRTM: reset time %d\n",i); + + /* Everything is configured, the TM transmitter is started + * and idle frames has been sent. + */ + + /* Mark running before enabling the DMA transmitter */ + pDev->running = 1; + + /* Enable interrupts (Error and DMA TX) */ + regs->dma_ctrl = GRTM_DMA_CTRL_IE; + + DBG("GRTM: STARTED\n"); + + return RTEMS_SUCCESSFUL; +} + +static void grtm_stop(struct grtm_priv *pDev) +{ + struct grtm_regs *regs = pDev->regs; + + /* Disable the transmitter & Interrupts */ + regs->dma_ctrl = 0; + + /* Clear any pending interrupt */ + regs->dma_status = GRTM_DMA_STS_ALL; + + DBG("GRTM: STOPPED\n"); + + /* Flush semaphore in case a thread is stuck waiting for TX Interrupts */ + rtems_semaphore_flush(pDev->sem_tx); +} + +static rtems_device_driver grtm_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg) +{ + struct grtm_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grtm_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grtm_priv *)dev->priv; + + /* Wait until we get semaphore */ + if ( rtems_semaphore_obtain(grtm_dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL ){ + return RTEMS_INTERNAL_ERROR; + } + + /* Is device in use? */ + if ( pDev->open ){ + rtems_semaphore_release(grtm_dev_sem); + return RTEMS_RESOURCE_IN_USE; + } + + /* Mark device taken */ + pDev->open = 1; + + rtems_semaphore_release(grtm_dev_sem); + + DBG("grtm_open: OPENED minor %d (pDev: 0x%x)\n",pDev->minor,(unsigned int)pDev); + + /* Set defaults */ + pDev->config.timeout = RTEMS_NO_TIMEOUT; /* no timeout (wait forever) */ + pDev->config.blocking = 0; /* polling mode */ + + pDev->running = 0; /* not in running mode yet */ + + memset(&pDev->config,0,sizeof(pDev->config)); + + /* The core has been reset when we execute here, so it is possible + * to read out what HW is implemented from core. + */ + grtm_hw_get_implementation(pDev, &pDev->hw_avail); + + /* Get default modes */ + grtm_hw_get_default_modes(&pDev->config,&pDev->hw_avail); + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grtm_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grtm_priv *pDev; + struct drvmgr_dev *dev; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grtm_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grtm_priv *)dev->priv; + + if ( pDev->running ){ + grtm_stop(pDev); + pDev->running = 0; + } + + /* Reset core */ + grtm_hw_reset(pDev); + + /* Clear descriptor area just for sure */ + memset(pDev->bds, 0, 0x400); + + /* Mark not open */ + pDev->open = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver grtm_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + FUNCDBG(); + return RTEMS_NOT_IMPLEMENTED; +} + +static rtems_device_driver grtm_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + FUNCDBG(); + return RTEMS_NOT_IMPLEMENTED; +} + +/* Scans the desciptor table for scheduled frames that has been sent, + * and moves these frames from the head of the scheduled queue to the + * tail of the sent queue. + * + * Also, for all frames the status is updated. + * + * Return Value + * Number of frames freed. + */ +static int grtm_free_sent(struct grtm_priv *pDev) +{ + struct grtm_ring *curr; + struct grtm_frame *last_frm, *first_frm; + int freed_frame_cnt=0; + unsigned int ctrl; + + curr = pDev->ring_end; + + /* Step into TX ring to find sent frames */ + if ( !curr->frm ){ + /* No scheduled frames, abort */ + return 0; + } + + /* There has been messages scheduled ==> scheduled messages may have been + * transmitted and needs to be collected. + */ + + first_frm = curr->frm; + + /* Loop until first enabled unsent frame is found. + * A unused descriptor is indicated by an unassigned frm field + */ + while ( curr->frm && !((ctrl=READ_REG(&curr->bd->ctrl)) & GRTM_BD_EN) ){ + /* Handle one sent Frame */ + + /* Remember last handled frame so that insertion/removal from + * frames lists go fast. + */ + last_frm = curr->frm; + + /* 1. Set flags to indicate error(s) and other information */ + last_frm->flags |= GRTM_FLAGS_SENT; /* Mark sent */ + + /* Update Stats */ + pDev->stats.frames_sent++; + + /* Did packet encounter link error? */ + if ( ctrl & GRTM_BD_UE ) { + pDev->stats.err_underrun++; + last_frm->flags |= GRRM_FLAGS_ERR; + } + + curr->frm = NULL; /* Mark unused */ + + /* Increment */ + curr = curr->next; + freed_frame_cnt++; + } + + /* 1. Remove all handled frames from scheduled queue + * 2. Put all handled frames into sent queue + */ + if ( freed_frame_cnt > 0 ){ + + /* Save TX ring posistion */ + pDev->ring_end = curr; + + /* Remove all sent frames from scheduled list */ + if ( pDev->scheduled.tail == last_frm ){ + /* All scheduled frames sent... */ + pDev->scheduled.head = NULL; + pDev->scheduled.tail = NULL; + }else{ + pDev->scheduled.head = last_frm->next; + } + last_frm->next = NULL; + + /* Put all sent frames into "Sent queue" for user to + * collect, later on. + */ + if ( !pDev->sent.head ){ + /* Sent queue empty */ + pDev->sent.head = first_frm; + pDev->sent.tail = last_frm; + }else{ + pDev->sent.tail->next = first_frm; + pDev->sent.tail = last_frm; + } + } + return freed_frame_cnt; +} + + +/* Moves as many frames in the ready queue (as there are free descriptors for) + * to the scheduled queue. The free descriptors are then assigned one frame + * each and enabled for transmission. + * + * Return Value + * Returns number of frames moved from ready to scheduled queue + */ +static int grtm_schedule_ready(struct grtm_priv *pDev, int ints_off) +{ + int cnt; + unsigned int ctrl, dmactrl; + struct grtm_ring *curr_bd; + struct grtm_frame *curr_frm, *last_frm; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( !pDev->ready.head ){ + return 0; + } + + cnt=0; + curr_frm = pDev->ready.head; + curr_bd = pDev->ring; + while( !curr_bd->frm ){ + /* Assign frame to descriptor */ + curr_bd->frm = curr_frm; + + /* Prepare descriptor address. Three cases: + * - GRTM core on same bus as CPU ==> no translation (Address used by CPU = address used by GRTM) + * - GRTM core on remote bus, and payload address given as used by CPU ==> Translation needed + * - GRTM core on remote bus, and payload address given as used by GRTM ==> no translation [ USER does custom translation] + */ + if ( curr_frm->flags & (GRTM_FLAGS_TRANSLATE|GRTM_FLAGS_TRANSLATE_AND_REMEMBER) ) { + /* Do translation */ + drvmgr_translate(pDev->dev, CPUMEM_TO_DMA, (void *)curr_frm->payload, (void **)&curr_bd->bd->address); + if ( curr_frm->flags & GRTM_FLAGS_TRANSLATE_AND_REMEMBER ) { + if ( curr_frm->payload != curr_bd->bd->address ) { + /* Translation needed */ + curr_frm->flags &= ~GRTM_FLAGS_TRANSLATE_AND_REMEMBER; + curr_frm->flags |= GRTM_FLAGS_TRANSLATE; + } else { + /* No Trnaslation needed */ + curr_frm->flags &= ~(GRTM_FLAGS_TRANSLATE|GRTM_FLAGS_TRANSLATE_AND_REMEMBER); + } + } + } else { + /* Custom translation or no translation needed */ + curr_bd->bd->address = (unsigned int)curr_frm->payload; + } + + ctrl = GRTM_BD_EN; + if ( curr_bd->next == pDev->_ring ){ + ctrl |= GRTM_BD_WR; /* Wrap around */ + } + /* Apply user options/flags */ + ctrl |= (curr_frm->flags & GRTM_FLAGS_MASK); + + /* Is this Frame going to be an interrupt Frame? */ + if ( (--pDev->enable_cnt_curr) <= 0 ){ + if ( pDev->config.enable_cnt == 0 ){ + pDev->enable_cnt_curr = 0x3fffffff; + }else{ + pDev->enable_cnt_curr = pDev->config.enable_cnt; + ctrl |= GRTM_BD_IE; + } + } + + /* Enable descriptor */ + curr_bd->bd->ctrl = ctrl; + + last_frm = curr_frm; + curr_bd = curr_bd->next; + cnt++; + + /* Get Next Frame from Ready Queue */ + if ( curr_frm == pDev->ready.tail ){ + /* Handled all in ready queue. */ + curr_frm = NULL; + break; + } + curr_frm = curr_frm->next; + } + + /* Has frames have been scheduled? */ + if ( cnt > 0 ){ + /* Make last frame mark end of chain, probably pointless... */ + last_frm->next = NULL; + + /* Insert scheduled packets into scheduled queue */ + if ( !pDev->scheduled.head ){ + /* empty scheduled queue */ + pDev->scheduled.head = pDev->ready.head; + pDev->scheduled.tail = last_frm; + }else{ + pDev->scheduled.tail->next = pDev->ready.head; + pDev->scheduled.tail = last_frm; + } + + /* Remove scheduled packets from ready queue */ + pDev->ready.head = curr_frm; + if ( !curr_frm ){ + pDev->ready.tail = NULL; + } + + /* Update TX ring posistion */ + pDev->ring = curr_bd; + if ( !ints_off ) { + IRQ_GLOBAL_DISABLE(oldLevel); + } + + /* Make hardware aware of the newly enabled descriptors */ + dmactrl = READ_REG(&pDev->regs->dma_ctrl); + dmactrl &= ~(GRTM_DMA_CTRL_TXRST | GRTM_DMA_CTRL_RST); + dmactrl |= GRTM_DMA_CTRL_EN; + pDev->regs->dma_ctrl = dmactrl; + + if ( !ints_off ) { + IRQ_GLOBAL_ENABLE(oldLevel); + } + } + return cnt; +} + + +static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + struct grtm_priv *pDev; + struct drvmgr_dev *dev; + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; + unsigned int *data = ioarg->buffer; + int status; + struct grtm_ioc_config *cfg; + struct grtm_ioc_hw_status *hwregs; + IRQ_GLOBAL_PREPARE(oldLevel); + struct grtm_list *chain; + struct grtm_frame *curr; + struct grtm_ioc_hw *hwimpl; + struct grtm_ioc_stats *stats; + int num,ret; + + FUNCDBG(); + + if ( drvmgr_get_dev(&grtm_drv_info.general, minor, &dev) ) { + return RTEMS_INVALID_NUMBER; + } + pDev = (struct grtm_priv *)dev->priv; + + if (!ioarg) + return RTEMS_INVALID_NAME; + + ioarg->ioctl_return = 0; + switch(ioarg->command) { + case GRTM_IOC_START: + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + if ( (status=grtm_start(pDev)) != RTEMS_SUCCESSFUL ){ + return status; + } + /* Register ISR & Enable interrupt */ + drvmgr_interrupt_register(dev, 0, "grtm", grtm_interrupt, pDev); + + /* Read and write are now open... */ + break; + + case GRTM_IOC_STOP: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + /* Disable interrupts */ + drvmgr_interrupt_unregister(dev, 0, grtm_interrupt, pDev); + grtm_stop(pDev); + pDev->running = 0; + break; + + case GRTM_IOC_ISSTARTED: + if ( !pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + break; + + case GRTM_IOC_SET_BLOCKING_MODE: + if ( (unsigned int)data > GRTM_BLKMODE_BLK ) { + return RTEMS_INVALID_NAME; + } + DBG("GRTM: Set blocking mode: %d\n",(unsigned int)data); + pDev->config.blocking = (unsigned int)data; + break; + + case GRTM_IOC_SET_TIMEOUT: + DBG("GRTM: Timeout: %d\n",(unsigned int)data); + pDev->config.timeout = (rtems_interval)data; + break; + + case GRTM_IOC_SET_CONFIG: + cfg = (struct grtm_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + if ( pDev->running ) { + return RTEMS_RESOURCE_IN_USE; + } + + pDev->config = *cfg; + break; + + case GRTM_IOC_GET_STATS: + stats = (struct grtm_ioc_stats *)data; + if ( !stats ) { + return RTEMS_INVALID_NAME; + } + memcpy(stats,&pDev->stats,sizeof(struct grtm_ioc_stats)); + break; + + case GRTM_IOC_CLR_STATS: + memset(&pDev->stats,0,sizeof(struct grtm_ioc_stats)); + break; + + case GRTM_IOC_GET_CONFIG: + cfg = (struct grtm_ioc_config *)data; + if ( !cfg ) { + return RTEMS_INVALID_NAME; + } + + *cfg = pDev->config; + break; + + case GRTM_IOC_GET_OCFREG: + if ( !pDev->hw_avail.ocf ) { + /* Hardware does not implement the OCF register */ + return RTEMS_NOT_DEFINED; + } + if ( !data ) { + return RTEMS_INVALID_NAME; + } + *(unsigned int **)data = (unsigned int *)&pDev->regs->ocf; + break; + + case GRTM_IOC_GET_HW_IMPL: + hwimpl = (struct grtm_ioc_hw *)data; + if ( !hwimpl ) { + return RTEMS_INVALID_NAME; + } + *hwimpl = pDev->hw_avail; + break; + + case GRTM_IOC_GET_HW_STATUS: + hwregs = (struct grtm_ioc_hw_status *)data; + if ( !hwregs ) { + return RTEMS_INVALID_NAME; + } + /* We disable interrupt in order to get a snapshot of the registers */ + IRQ_GLOBAL_DISABLE(oldLevel); +#warning IMPLEMENT HWREGS + IRQ_GLOBAL_ENABLE(oldLevel); + break; + + /* Put a chain of frames at the back of the "Ready frames" queue. This + * triggers the driver to put frames from the Ready queue into unused + * available descriptors. (Ready -> Scheduled) + */ + + case GRTM_IOC_SEND: + if ( !pDev->running ){ + return RTEMS_RESOURCE_IN_USE; + } + num=0; + + /* Get pointer to frame chain wished be sent */ + chain = (struct grtm_list *)ioarg->buffer; + if ( !chain ){ + /* No new frames to send ==> just trigger hardware + * to send previously made ready frames to be sent. + */ + pDev->handling_transmission = 1; + goto trigger_transmission; + } + if ( !chain->tail || !chain->head ){ + return RTEMS_INVALID_NAME; + } + + DBG("GRTM_SEND: head: 0x%x, tail: 0x%x\n",chain->head,chain->tail); + + /* Mark ready frames unsent by clearing GRTM_FLAGS_SENT of all frames */ + + curr = chain->head; + while(curr != chain->tail){ + curr->flags = curr->flags & ~(GRTM_FLAGS_SENT|GRRM_FLAGS_ERR); + curr = curr->next; + num++; + } + curr->flags = curr->flags & ~(GRTM_FLAGS_SENT|GRRM_FLAGS_ERR); + num++; + + pDev->handling_transmission = 1; + /* 1. Put frames into ready queue + * (New Frames->READY) + */ + if ( pDev->ready.head ){ + /* Frames already on ready queue (no free descriptors previously) ==> + * Put frames at end of ready queue + */ + pDev->ready.tail->next = chain->head; + pDev->ready.tail = chain->tail; + chain->tail->next = NULL; + }else{ + /* All frames is put into the ready queue for later processing */ + pDev->ready.head = chain->head; + pDev->ready.tail = chain->tail; + chain->tail->next = NULL; + } + pDev->ready_cnt += num; /* Added 'num' frames to ready queue */ +trigger_transmission: + /* 2. Free used descriptors and put the sent frame into the "Sent queue" + * (SCHEDULED->SENT) + */ + num = grtm_free_sent(pDev); + pDev->scheduled_cnt -= num; + pDev->sent_cnt += num; + + /* 3. Use all available free descriptors there are frames for + * in the ready queue. + * (READY->SCHEDULED) + */ + num = grtm_schedule_ready(pDev,0); + pDev->ready_cnt -= num; + pDev->scheduled_cnt += num; + + pDev->handling_transmission = 0; + break; + + /* Take all available sent frames from the "Sent frames" queue. + * If no frames has been sent, the thread may get blocked if in blocking + * mode. The blocking mode is not available if driver is not in running mode. + * + * Note this ioctl may return success even if the driver is not in STARTED mode. + * This is because in case of a error (link error of similar) and the driver switch + * from START to STOP mode we must still be able to get our frames back. + * + * Note in case the driver fails to send a frame for some reason (link error), + * the sent flag is set to 0 indicating a failure. + * + */ + case GRTM_IOC_RECLAIM: + /* Get pointer to were to place reaped chain */ + chain = (struct grtm_list *)ioarg->buffer; + if ( !chain ){ + return RTEMS_INVALID_NAME; + } + + /* Lock out interrupt handler */ + pDev->handling_transmission = 1; + + do { + /* Move sent frames from descriptors to Sent queue. This makes more + * descriptors (BDs) available. + */ + num = grtm_free_sent(pDev); + pDev->scheduled_cnt -= num; + pDev->sent_cnt += num; + + + if ( pDev->running ){ + /* Fill descriptors with as many frames from the ready list + * as possible. + */ + num = grtm_schedule_ready(pDev,0); + pDev->ready_cnt -= num; + pDev->scheduled_cnt += num; + } + + /* Are there any frames on the sent queue waiting to be + * reclaimed? + */ + + if ( !pDev->sent.head ){ + /* No frames to reclaim - no frame in sent queue. + * Instead we block thread until frames have been sent + * if in blocking mode. + */ + if ( pDev->running && pDev->config.blocking ){ + ret = rtems_semaphore_obtain(pDev->sem_tx,RTEMS_WAIT,pDev->config.timeout); + if ( ret == RTEMS_TIMEOUT ) { + pDev->handling_transmission = 0; + return RTEMS_TIMEOUT; + } else if ( ret == RTEMS_SUCCESSFUL ) { + /* There might be frames available, go check */ + continue; + } else { + /* any error (driver closed, internal error etc.) */ + pDev->handling_transmission = 0; + return RTEMS_UNSATISFIED; + } + + }else{ + /* non-blocking mode, we quit */ + chain->head = NULL; + chain->tail = NULL; + /* do not lock out interrupt handler any more */ + pDev->handling_transmission = 0; + return RTEMS_TIMEOUT; + } + }else{ + /* Take all sent framess from sent queue to userspace queue */ + chain->head = pDev->sent.head; + chain->tail = pDev->sent.tail; + chain->tail->next = NULL; /* Just for sure */ + + /* Mark no Sent */ + grtm_list_clr(&pDev->sent); + pDev->sent_cnt = 0; + + DBG("TX_RECLAIM: head: 0x%x, tail: 0x%x\n",chain->head,chain->tail); + break; + } + + }while(1); + + /* do not lock out interrupt handler any more */ + pDev->handling_transmission = 0; + break; + + default: + return RTEMS_NOT_DEFINED; + } + return RTEMS_SUCCESSFUL; +} + +static void grtm_interrupt(void *arg) +{ + struct grtm_priv *pDev = arg; + struct grtm_regs *regs = pDev->regs; + unsigned int status; + int num; + + /* Clear interrupt by reading it */ + status = READ_REG(®s->dma_status); + + /* Spurious Interrupt? */ + if ( !pDev->running ) + return; + + if ( status ) + regs->dma_status = status; + + if ( status & GRTM_DMA_STS_TFF ){ + pDev->stats.err_transfer_frame++; + } + + if ( status & GRTM_DMA_STS_TA ){ + pDev->stats.err_ahb++; + } + + if ( status & GRTM_DMA_STS_TE ){ + pDev->stats.err_tx++; + } + + if ( status & GRTM_DMA_STS_TI ){ + + if ( pDev->config.isr_desc_proc && !pDev->handling_transmission ) { + /* Free used descriptors and put the sent frame into the "Sent queue" + * (SCHEDULED->SENT) + */ + num = grtm_free_sent(pDev); + pDev->scheduled_cnt -= num; + pDev->sent_cnt += num; + + /* Use all available free descriptors there are frames for + * in the ready queue. + * (READY->SCHEDULED) + */ + num = grtm_schedule_ready(pDev,1); + pDev->ready_cnt -= num; + pDev->scheduled_cnt += num; + +#if 0 + if ( (pDev->config.blocking==GRTM_BLKMODE_COMPLETE) && pDev->timeout ){ + /* Signal to thread only if enough data is available */ + if ( pDev->wait_for_frames > grtm_data_avail(pDev) ){ + /* Not enough data available */ + goto procceed_processing_interrupts; + } + + /* Enough number of frames has been transmitted which means that + * the waiting thread should be woken up. + */ + rtems_semaphore_release(pDev->sem_tx); + } +#endif + } + + if ( pDev->config.blocking == GRTM_BLKMODE_BLK ) { + /* Blocking mode */ + +#if 0 + /* Disable further Interrupts until handled by waiting task. */ + regs->dma_ctrl = READ_REG(®s->dma_ctrl) & ~GRTM_DMA_CTRL_IE; +#endif + + /* Signal Semaphore to wake waiting thread in ioctl(SEND|RECLAIM) */ + rtems_semaphore_release(pDev->sem_tx); + } + + } + +procceed_processing_interrupts: + ; +} + +static rtems_device_driver grtm_initialize( + rtems_device_major_number major, + rtems_device_minor_number unused, + void *arg + ) +{ + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('G', 'R', 'T', 'M'), + 1, + RTEMS_FIFO|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &grtm_dev_sem) != RTEMS_SUCCESSFUL ) { + return RTEMS_INTERNAL_ERROR; + } + + return RTEMS_SUCCESSFUL; +} |